Emptybox
Emptybox

Reputation: 121

Convert Rhino Mesh to BufferGeometry JSON

I am attempting to convert a rhino mesh in Grasshopper python to a BufferGeometry that will be read in Three.js/WebGL. Three.js does have converters available to go from .obj to three.js, but for my workflow, I need these files to be outputted from GH as a JSON without saving it as a .obj first.

I had an old converter that would input a rhino mesh then output a Geometry, but since Three.js r.99 that JSON format is now deprecated. I have created a script that inputs a mesh and creates a data structure like JSON Geometry Format 4. The geometry is being successfully loaded using OBJLoader, but I am getting the following error.

glDrawArrays: attempt to access out of range vertices in attribute 1

Thanks in Advance!

from System import Guid
import rhinoscriptsyntax as rs
import json

# initiate lists to fill
geometryList = []
childrenList = []
# loop through input meshes
for i, mesh in enumerate(meshes):
    # get UUID
    geometry_guid = str(Guid.NewGuid())
    # JSON BufferGeometry Structure
    geometryDict = {
        "uuid": geometry_guid,
        "type": "BufferGeometry",
        "data": {
            "attributes":{
              "position":{
                "itemSize": 3,
                "type": "Float32Array",
                "array":[],
                "normalized": False
              },
              "normal":{
                "itemSize": 3,
                "type": "Float32Array",
                "array":[],
                "normalized": False
              }
            }
        }
    }
    # children Dict
    # values such as name, visible are input values from GH
    childrenDict = {
        "uuid": str(Guid.NewGuid()),
        "type": "Mesh",
        "name": name,
        "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
        "geometry": geometry_guid,
        "material": material.d['uuid'],
        "visible":visible,
        "castShadow": cast_shadow,
        "receiveShadow": receive_shadow,
        "userData": {}
    }
    # start index
    vertexIndex = 0
    # vertex array creation
    vertexArray = [None] * (len(rs.MeshVertices(mesh)) *3)
    # loop through vertices and append to vertex array
    for vertex in rs.MeshVertices(mesh):

        vertexArray[vertexIndex*3] = vertex[0]
        vertexArray[vertexIndex*3+1] = vertex[1]
        vertexArray[vertexIndex*3+2] = vertex[2]

        vertexIndex+=1
    # add to geometry dictionary
    geometryDict['data']['attributes']['position']['array'] = vertexArray

    # loop through faces
    faceVertices = []
    for face in rs.MeshFaceVertices(mesh):

        faceVertices.append(face[0])
        faceVertices.append(face[1])
        faceVertices.append(face[2])

        if face[2] != face[3]:
            faceVertices.append(face[2])
            faceVertices.append(face[3])
            faceVertices.append(face[0])

    # normal index
    normalIndex = 0
    # normal array creation
    normalArray = [None] * (len(rs.MeshFaceNormals(mesh)) *3)

    for normal in rs.MeshFaceNormals(mesh):

        normalArray[normalIndex*3] = normal[0]
        normalArray[normalIndex*3+1] = normal[1]
        normalArray[normalIndex*3+2] = normal[2]

        normalIndex+=1

    # add normal array to geometry dictionary
    geometryDict['data']['attributes']['normal']['array'] = normalArray

    geometryList.append(geometryDict)
    childrenList.append(childrenDict)

# these meshes are later added to the parent JSON Structure
class GhPythonDictionary(object):
    def __init__(self, pythonDict=None):
        if pythonDict:
            self.d = pythonDict
        else:
            self.d = {
                "material": material.d,
                "geometries": geometryList,
                "children": childrenList
            }
    def ToString(self):
        return 'GhPythonDictionary object'

mesh_object = GhPythonDictionary()

# master dictionary. all scene data goes in here

featureDict = {
    "metadata": {
        "version": 4.5,
        "type": "Object",
        "generator": "Object3D.toJSON",
    },
    "geometries":[],
    "materials":[],
    "object":{
        "uuid": "378FAA8D-0888-4249-8701-92D1C1F37C51",
        "type": "Group",
        "name": file_name,
        "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
        "children": []
    }
}

for i in range(len(mesh_objects)):

    for j in range(len(mesh_objects[i].d['geometries'])):
        featureDict['geometries'].append(mesh_objects[i].d['geometries'][j])

    featureDict['materials'].append(mesh_objects[i].d['material'])

    for j in range(len(mesh_objects[i].d['children'])):
        featureDict['object']['children'].append(mesh_objects[i].d['children'][j])

# file path as a string input from GH
file_path = path + '\\' + file_name + '.json'

# write file to folder path
with open(file_path, 'wb') as outfile:
    json.dump(featureDict, outfile)




// example of a mesh object created

{"uuid":"77589f14-6476-4517-b371-846920b9464b","type":"BufferGeometry","data":{"attributes":{"position":{"array":[-99.910560607910156,7.6936740875244141,211,-99.910560607910156,7.6936740875244141,207.021728515625,-100.83158111572266,7.6936740875244141,211,-100.83158111572266,7.6936740875244141,210.00543212890625,-100.83158111572266,7.6936740875244141,209.0108642578125,-100.83158111572266,7.6936740875244141,208.01629638671875,-101.75260925292969,7.6936740875244141,211,-101.75260925292969,7.6936740875244141,210.00543212890625,-101.75260925292969,7.6936740875244141,209.0108642578125,-101.75260925292969,7.6936740875244141,208.01629638671875,-102.67362976074219,7.6936740875244141,211,-102.67362976074219,7.6936740875244141,210.00543212890625,-102.67362976074219,7.6936740875244141,209.0108642578125,-102.67362976074219,7.6936740875244141,208.01629638671875,-103.59465026855469,7.6936740875244141,211,-103.59465026855469,7.6936740875244141,207.021728515625,-99.910560607910156,7.6936740875244141,207.68478393554687,-99.910560607910156,7.6936740875244141,208.34782409667969,-99.910560607910156,7.6936740875244141,209.0108642578125,-99.910560607910156,7.6936740875244141,209.67390441894531,-99.910560607910156,7.6936740875244141,210.33694458007813,-103.59465026855469,7.6936740875244141,210.33694458007813,-103.59465026855469,7.6936740875244141,209.67390441894531,-103.59465026855469,7.6936740875244141,209.0108642578125,-103.59465026855469,7.6936740875244141,208.34782409667969,-103.59465026855469,7.6936740875244141,207.68478393554687,-102.85783386230469,7.6936740875244141,207.021728515625,-102.12101745605469,7.6936740875244141,207.021728515625,-101.38420104980469,7.6936740875244141,207.021728515625,-100.64737701416016,7.6936740875244141,207.021728515625],"normalized":false,"itemSize":3,"type":"Float32Array"},"normal":{"array":[0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,-1,-1],"normalized":false,"itemSize":3,"type":"Float32Array"}}}}

Upvotes: 0

Views: 737

Answers (1)

Meli Harvey
Meli Harvey

Reputation: 11

As you mentioned threejs JSON format 4 is deprecated and the ObjectLoader is not included with the most recent builds of threejs. One reason it's no longer supported is because glTF format has become the preferred option for web. To my knowledge, there is no glTF exporter for Rhino/GH available as of the time of this post.

You have a number of options:

  1. add this legacy JSON loader to your website
  2. choose a file type that can be both exported from Rhino/Grasshopper and has a loader for threejs (i.e. OBJ, FBX)
  3. export as an OBJ, FBX or some other file type from Rhino, and then convert it to glTF using another software or process
  4. create a glTF exporter from Rhino/GH
  5. revert to an older version of threejs (pre r99) that supports threejs JSON format 4

Upvotes: 1

Related Questions