Convert Rhino Mesh to Three.js BufferGeometry

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.

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[1]

        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)

A little off topic, but figured worth mentioning.
The latest version of rhino3dm.js supports directly creating threejs BufferGeometry json from Mesh instances.

Here is a basic sample
https://mcneel.github.io/rhino3dm/javascript/samples/viewer/01_basic/index.html

Hi,

Thanks for the guidance - in this case would you save out your geometries as an .obj and materials as an .mtl then combine them before render?

are you familiar with the spectacles exporter?

In the sample that I posted, I just saved a 3dm with meshes and then directly read the 3dm file instance in javascript. I then called a function on each mesh to get the BufferGeometry json representation of that mesh.

@Rickson, the Spectacles exporter is now out of date. The objects exported are Geometries which are now deprecated as of Three.js r.99.