# The goal of this addon is to export all of the actions for the active # armature into a JSON file bl_info = { "name": "Export Mesh to JSON", "category": "Import-Export" } import bpy import json # import sys # Write our JSON to stdout by default or to a file if specified. # Stdout mesh JSON is wrapped in a start and end indicators # to more easily distinguish it from other Blender output. # # START_MESH_JSON $BLENDER_FILEPATH $MESH_NAME # ... mesh json ... # END_MESH_JSON $BLENDER_FILEPATH $MESH_NAME class MeshToJSON(bpy.types.Operator): """Given an active armature, export it's actions and keyframed bone pose information to a JSON file""" # Unique identifier for the addon bl_idname = 'import_export.mesh2json' # Display name in the interface bl_label = 'Export Mesh to JSON' bl_options = {'REGISTER'} bl_category = 'Import-Export' # The filepath to write out JSON to # filepath = bpy.props.StringProperty(name='filepath') def execute(self, context): mesh = bpy.context.active_object mesh_json = { 'vertex_positions': [], 'num_vertices_in_each_face': [], 'vertex_position_indices': [], 'vertex_normals': [], 'vertex_normal_indices': [], 'armature_name': None, 'vertex_group_indices': [], 'vertex_group_weights': [], 'num_groups_for_each_vertex': [] } if mesh.parent != None and mesh.parent.type == 'ARMATURE': mesh_json['armature_name'] = mesh.parent.name # TODO: Handle triangular polygons, not just quads # cube.data.polygons[1].vertices[0]. Check if length # of face is 4... Use a triangular face in Blender to unit test. index = 0 for face in mesh.data.polygons: num_vertices_in_face = len(face.vertices) mesh_json['num_vertices_in_each_face'].append(num_vertices_in_face) for i in range(num_vertices_in_face): mesh_json['vertex_position_indices'].append(face.vertices[i]) # TODO: Maintain a dictionary with (x, y, z) => normal index # for normals that we've already run into. # Re-use an existing normal index wherever possible. Especially important # for smoothed models that mostly re-use the same normals. Test this by # making a cube with to faces that have the same normal mesh_json['vertex_normal_indices'].append(index) # TODO: Don't append normals if we've already encountered them mesh_json['vertex_normals'].append(face.normal.x) mesh_json['vertex_normals'].append(face.normal.y) mesh_json['vertex_normals'].append(face.normal.z) index += 1 # TODO: Breadcrumb - iterate over the vertices and add them to mesh_json # TODO: Option for # of decimal places to round positions / normals / etc to. # Potentially just one option or a separate option for each for vert in mesh.data.vertices: mesh_json['vertex_positions'].append(vert.co.x) mesh_json['vertex_positions'].append(vert.co.y) mesh_json['vertex_positions'].append(vert.co.z) # TODO: Only include num groups if there is a parent armature. Otherwise the # number of groups (bones) per vertex probably doesn't matter...? num_groups = len(list(vert.groups)) for group in vert.groups: mesh_json['vertex_group_indices'].append(group.group) mesh_json['vertex_group_weights'].append(group.weight) # groupName = mesh.vertex_groups[group.group].name if mesh_json['armature_name'] != None: mesh_json['num_groups_for_each_vertex'].append(num_groups) if mesh_json['armature_name'] == None: mesh_json['vertex_group_indices'] = None mesh_json['vertex_group_weights'] = None mesh_json['num_groups_for_each_vertex'] = None # TODO: Add unit test for no mesh currently selected # if mesh == None or mesh.type != 'MESH': # print("__NO_MESH_SELECTED__", file=sys.stderr) # return {'FINISHED'} # Iterate over all of the polygons and get the face data print("START_MESH_JSON " + bpy.data.filepath + " " + mesh.name) print(json.dumps(mesh_json)) print("END_MESH_JSON " + bpy.data.filepath + " " + mesh.name) # START_EXPORT_MESH $BLENDER_FILEPATH $MESH_NAME # ... mesh json ... # FINISH_EXPORT_MESH $BLENDER_FILEPATH $MESH_NAME return {'FINISHED'} def register(): bpy.utils.register_class(MeshToJSON) def unregister(): bpy.utils.unregister_class(MeshToJSON) if __name__ == "__main__": register()