www.xbdev.net
xbdev - software development
Thursday April 2, 2026
Home | Contact | Support | JSON File Formats JSON Can Be Used For 3D Data
>>
     
 

JSON File Formats

JSON Can Be Used For 3D Data

 

The json file format is a great! Not just for 3d data but for lots of things. It's very popular for web-based systems (storing and transfering data).

The file format itself is text-based and easy to read - but even greater - lots of languages (Java, Python, JavaScript, ..) all have builtin or free packages for parsing or creating json files! So you don't need to do the work of processing each character.

The file format is simple enough - as it's just text.

Blender Exporter (Vertices and Faces)


A few cool little things - in blender you can export your data as 'json'. Just open up Blender (3.x), select the object you want to export - select the
Scripting
tab at the top - and you can write/run small python programs.

The following is a juicy little python script for Blender that exports the mesh data.

t bpy import json def export_mesh_data(obj): mesh_data = {} vertices = [] for vertex in obj.data.vertices: vertices.append((vertex.co.x, vertex.co.y, vertex.co.z)) mesh_data['vertices'] = vertices faces = [] for face in obj.data.polygons: faces.append([v for v in face.vertices]) mesh_data['faces'] = faces return mesh_data def export_json(filename): scene = bpy.context.scene selected_objects = [obj for obj in scene.objects if obj.select_get() and obj.type == 'MESH'] exported_data = {} for obj in selected_objects: exported_data[obj.name] = export_mesh_data(obj) # Specify the full path where you have write permissions with open(filename, 'w') as file: json.dump(exported_data, file, indent=4) # Change the path to your desired export location export_json("C:/exported_temp.json")



The json file for a cube.json from the above exported script looks like:

n>{"Cube":{ "vertices":[[1,1,1],[1,1,-1],[1,-1,1],[1,-1,-1],[-1,1,1],[-1,1,-1],[-1,-1,1],[-1,-1,-1]], "faces": [[0,4,6,2],[3,2,6,7],[7,6,4,5],[5,1,3,7],[1,0,2,3],[5,4,0,1]]}}


The Blender window with the cube and the script should look something like this:


Full window screenshot showing Blender, script, cube model. Red circles show the cube mesh (selected), scripting tab, script an...
Full window screenshot showing Blender, script, cube model. Red circles show the cube mesh (selected), scripting tab, script and the run button.



Triangles - Only Triangles (NO QUADS)


The previous example worked - but the exported data kept the format that Blender was using - so if it was storing surfaces as quads the exported JSON contained quads! But most of the time we want triangles!

So a few extra lines - we convert any quads to triangles


t bpy import json def export_mesh_data(obj): mesh_data = {} vertices = [] for vertex in obj.data.vertices: vertices.append((vertex.co.x, vertex.co.y, vertex.co.z)) mesh_data['vertices'] = vertices faces = [] for face in obj.data.polygons: if len(face.vertices) == 4: # Check if the face is a quad # Convert quad to two triangles faces.append([face.vertices[0], face.vertices[1], face.vertices[2]]) faces.append([face.vertices[0], face.vertices[2], face.vertices[3]]) else: faces.append([v for v in face.vertices]) mesh_data['faces'] = faces return mesh_data def export_json(filename): scene = bpy.context.scene selected_objects = [obj for obj in scene.objects if obj.select_get() and obj.type == 'MESH'] exported_data = {} for obj in selected_objects: exported_data[obj.name] = export_mesh_data(obj) # Specify the full path where you have write permissions with open(filename, 'w') as file: json.dump(exported_data, file, indent=4) # Change the path to your desired export location export_json("C:/Pro2/exported_cube.json")



This is what the modified export look like now (3 indices per face - triangle):

n>{"Cube":{"vertices":[[1,1,1],[1,1,-1],[1,-1,1],[1,-1,-1],[-1,1,1],[-1,1,-1],[-1,-1,1],[-1,-1,-1]], "faces":[[0,4,6],[0,6,2],[3,2,6],[3,6,7],[7,6,4],[7,4,5],[5,1,3],[5,3,7],[1,0,2],[1,2,3],[5,4,0],[5,0,1]]}}



Blender Exporter (Vertices/Faces/UV/Color)


Taking the simple example from previously further - you can export lots of other mesh data (e.g. UVs and Colors).

t bpy import json def export_mesh_data(obj): print("Exporting mesh data for object:", obj.name) mesh_data = {} vertices = [] for vertex in obj.data.vertices: vertices.append((vertex.co.x, vertex.co.y, vertex.co.z)) mesh_data['vertices'] = vertices print("Vertices exported:", len(vertices)) faces = [] for face in obj.data.polygons: faces.append([v for v in face.vertices]) mesh_data['faces'] = faces print("Faces exported:", len(faces)) # Check if vertex colors are present if obj.data.vertex_colors: vertex_colors = {} for layer_name, color_layer in obj.data.vertex_colors.items(): vertex_colors[layer_name] = [] for loop_index, loop in enumerate(obj.data.loops): color = color_layer.data[loop_index].color vertex_colors[layer_name].append((color.r, color.g, color.b, color.a)) mesh_data['vertex_colors'] = vertex_colors print("Vertex colors exported for layers:", list(vertex_colors.keys())) # Check if UV coordinates are present if obj.data.uv_layers: uv_coordinates = {} for layer_name, uv_layer in obj.data.uv_layers.items(): uv_coordinates[layer_name] = [] for loop_index, loop in enumerate(obj.data.loops): uv_coordinate = uv_layer.data[loop_index].uv uv_coordinates[layer_name].append((uv_coordinate.x, uv_coordinate.y)) mesh_data['uv_coordinates'] = uv_coordinates print("UV coordinates exported for layers:", list(uv_coordinates.keys())) return mesh_data def export_json(filename): print("Starting JSON export...") scene = bpy.context.scene selected_objects = [obj for obj in scene.objects if obj.select_get() and obj.type == 'MESH'] print("Selected objects for export:", [obj.name for obj in selected_objects]) exported_data = {} for obj in selected_objects: exported_data[obj.name] = export_mesh_data(obj) with open(filename, 'w') as file: json.dump(exported_data, file, indent=4) print("JSON export completed.") # Change the path to your desired export location export_json("C:/modelwithvfuvc.json")


If yoyu open the json file in your favorite text editor (like notpad++) you can view the structure/layout. For example:


Exported json file - viewed in notepad++ but have minimized the numbers so you can see the overall structure.
Exported json file - viewed in notepad++ but have minimized the numbers so you can see the overall structure.



You need normals! Without them how do you add lighting?


So let's extend the json file exporter so it includes normal data.

t bpy import json def export_mesh_data(obj): mesh_data = {} vertices = [] normals = [] # New list to store vertex normals uv_coordinates = [] # New list to store UV coordinates vertex_colors = [] # New list to store vertex colors for vertex in obj.data.vertices: vertices.append((vertex.co.x, vertex.co.y, vertex.co.z)) # Extract vertex normals for loop in obj.data.loops: normals.append((loop.normal.x, loop.normal.y, loop.normal.z)) # Extract UV coordinates uv_layer = obj.data.uv_layers.active.data if obj.data.uv_layers.active else None if uv_layer: for loop in obj.data.loops: uv_coordinate = uv_layer[loop.index].uv uv_coordinates.append((uv_coordinate.x, uv_coordinate.y)) # Extract vertex colors color_layer = obj.data.vertex_colors.active.data if obj.data.vertex_colors.active else None if color_layer: for loop in obj.data.loops: color = color_layer[loop.index].color vertex_colors.append((color.r, color.g, color.b, color.a)) mesh_data['vertices'] = vertices mesh_data['normals'] = normals mesh_data['uv_coordinates'] = uv_coordinates mesh_data['vertex_colors'] = vertex_colors faces = [] for face in obj.data.polygons: if len(face.vertices) == 4: # Check if the face is a quad # Convert quad to two triangles faces.append([face.vertices[0], face.vertices[1], face.vertices[2]]) faces.append([face.vertices[0], face.vertices[2], face.vertices[3]]) else: faces.append([v for v in face.vertices]) mesh_data['faces'] = faces return mesh_data def export_json(filename): scene = bpy.context.scene selected_objects = [obj for obj in scene.objects if obj.select_get() and obj.type == 'MESH'] exported_data = {} for obj in selected_objects: exported_data[obj.name] = export_mesh_data(obj) # Specify the full path where you have write permissions with open(filename, 'w') as file: json.dump(exported_data, file, indent=4) # Change the path to your desired export location export_json("C:/modelwithvfuvcn.json")


Bringing it all together - Adding Export Button


In Blender, as you must already know, there is an 'export' option in the File menu. So let's take the 'json' exporter and build it into an export button.


JSON Exporter Button
JSON Exporter Button



t bpy import json def export_mesh_data(obj): mesh_data = {} vertices = [] normals = [] # New list to store vertex normals uv_coordinates = [] # New list to store UV coordinates vertex_colors = [] # New list to store vertex colors for vertex in obj.data.vertices: vertices.append((vertex.co.x, vertex.co.y, vertex.co.z)) # Extract vertex normals for loop in obj.data.loops: normals.append((loop.normal.x, loop.normal.y, loop.normal.z)) # Extract UV coordinates uv_layer = obj.data.uv_layers.active.data if obj.data.uv_layers.active else None if uv_layer: for loop in obj.data.loops: uv_coordinate = uv_layer[loop.index].uv uv_coordinates.append((uv_coordinate.x, uv_coordinate.y)) # Extract vertex colors color_layer = obj.data.vertex_colors.active.data if obj.data.vertex_colors.active else None if color_layer: for loop in obj.data.loops: color = color_layer[loop.index].color vertex_colors.append((color.r, color.g, color.b, color.a)) # Flatten the ararys - instead of = [ [2,3], [2,1], [2,4,5] ] - you have - [2,3,2,1,2,4,5] mesh_data['vertices'] = [ list(i) for i in vertices ] mesh_data['normals'] = [ list(i) for i in normals ] mesh_data['uv_coordinates'] = [ list(i) for i in uv_coordinates ] mesh_data['vertex_colors'] = [ list(i) for i in vertex_colors ] mesh_data['vertices'] = sum( mesh_data['vertices'], [] ) mesh_data['normals'] = sum( mesh_data['normals'], [] ) mesh_data['uv_coordinates'] = sum( mesh_data['uv_coordinates'], [] ) mesh_data['vertex_colors'] = sum( mesh_data['vertex_colors'], [] ) faces = [] for face in obj.data.polygons: if len(face.vertices) == 4: # Check if the face is a quad # Convert quad to two triangles faces.append([face.vertices[0], face.vertices[1], face.vertices[2]]) faces.append([face.vertices[0], face.vertices[2], face.vertices[3]]) else: faces.append([v for v in face.vertices]) mesh_data['faces'] = [ list(i) for i in faces ] mesh_data['faces'] = sum( mesh_data['faces'], [] ) return mesh_data def export_json(filename): scene = bpy.context.scene selected_objects = [obj for obj in scene.objects if obj.select_get() and obj.type == 'MESH'] exported_data = {} for obj in selected_objects: exported_data[obj.name] = export_mesh_data(obj) # Specify the full path where you have write permissions with open(filename, 'w') as file: json.dump(exported_data, file, indent=4) def write_some_data(context, filepath, use_some_setting): print("running write_some_data...") export_json( filepath ) return {'FINISHED'} # ExportHelper is a helper class, defines filename and # invoke() function which calls the file selector. from bpy_extras.io_utils import ExportHelper from bpy.props import StringProperty, BoolProperty, EnumProperty from bpy.types import Operator class ExportSomeData(Operator, ExportHelper): """This appears in the tooltip of the operator and in the generated docs""" bl_idname = "export_test.some_data" # important since its how bpy.ops.import_test.some_data is constructed bl_label = "Export Some Data" # ExportHelper mix-in class uses this. filename_ext = ".json" filter_glob: StringProperty( default="*.json", options={'HIDDEN'}, maxlen=255, # Max internal buffer length, longer would be clamped. ) # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. use_setting: BoolProperty( name="Example Boolean", description="Example Tooltip", default=True, ) type: EnumProperty( name="Example Enum", description="Choose between two items", items=( ('OPT_A', "First Option", "Description one"), ('OPT_B', "Second Option", "Description two"), ), default='OPT_A', ) def execute(self, context): return write_some_data(context, self.filepath, self.use_setting) # Only needed if you want to add into a dynamic menu def menu_func_export(self, context): self.layout.operator(ExportSomeData.bl_idname, text="JSON Exporter (XBDEV)") # Register and add to the "file selector" menu (required to use F3 search "JSON Exporter" for quick access). def register(): bpy.utils.register_class(ExportSomeData) bpy.types.TOPBAR_MT_file_export.append(menu_func_export) def unregister(): bpy.utils.unregister_class(ExportSomeData) bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) if __name__ == "__main__": register() # test call # bpy.ops.export_test.some_data('INVOKE_DEFAULT')


The export example has some redundant options for the popup dialog - quick and dirty example to give you something that works and lets you focus on the JSON format for you cool demos! Feel free to use it and tidy it up - adding other features/options (e.g., add menu options to the exporter to enable/disable things in the output JSON file)

The cube example would look like this:

n>{"Cube":{ "vertices":[1,1,1,1,1,-1,1,-1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,1,-1,-1,-1], "normals":[0,0,1,0,0,1,0,0,1,0,0,1,0,-1,0,0,-1,0,0,-1,0,0,-1,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0], "uv_coordinates":[0.625,0.5,0.875,0.5,0.875,0.75,0.625,0.75,0.375,0.75,0.625,0.75,0.625,1,0.375,1,0.375,0,0.625,0,0.625,0.25,0.375,0.25,0.125,0.5,0.375,0.5,0.375,0.75,0.125,0.75,0.375,0.5,0.625,0.5,0.625,0.75,0.375,0.75,0.375,0.25,0.625,0.25,0.625,0.5,0.375,0.5], "vertex_colors":[], "faces":[0,4,6,0,6,2,3,2,6,3,6,7,7,6,4,7,4,5,5,1,3,5,3,7,1,0,2,1,2,3,5,4,0,5,0,1]}}


Example Loading/Using JSON File in JavaScript


To give you an example of how you might use the JSON 3D file format in WebGL or WebGPU API. Just the loading and getting the data ready.

n>// get some model data (use json as it's easy to load/setup) let response = await fetch('./var/scripts/cube.json'); // parse json file let json = await response.json(); console.log('vertices:', json.vertices.length ); console.log('faces:' , json.faces.length ); console.log('normals:' , json.normals.length ); const positions = new Float32Array( json.vertices ); let normals = new Float32Array( json.normals ); const indices = new Uint16Array( json.faces ); // ready to go! use vertices/faces in webl or webgpu api - so easy!!




Things to Try


Extra things to do:
• Add version and signiture to the json file (when the json file is loaded you can check which version/type it is - in case it's another json file that isn't supported by your loader)
• Add a dialog that pops up for the exporter to give extra options (e.g., triangles, UV coordinates, colors, ...)
• Debug information if no object is selected (e.g., dialog that informs the user) - basically extra help to make the export script more friendly :)


Other Resources


• Visit https://free3d.com/ for lots of free test models (load in Blender - import) [LINK]
• WebGPU Demo (WebGPULab) - loads json file and renders the model (in the Web Browser) [LINK]
• Example 'cow.json' [LINK]











JSON/Blender


• Triangles (Quads to Triangles)
• Vertices
• Faces
• Texture Coordinates


t bpy import json def export_mesh_data(obj): vertices = [] normals = [] coordinates = [] colors = [] faces = [] uv_layer = obj.data.uv_layers.active.data color_layer = obj.data.vertex_colors.active.data if obj.data.vertex_colors.active else None for face in obj.data.polygons: if len(face.vertices) == 4: # Check if the face is a quad # Convert quad to two triangles i0 = face.vertices[0] i1 = face.vertices[1] i2 = face.vertices[2] i3 = face.vertices[3] v = obj.data.vertices vertices.extend( [ v[i0].co.x, -v[i0].co.z, v[i0].co.y ] ); vertices.extend( [ v[i1].co.x, -v[i1].co.z, v[i1].co.y ] ); vertices.extend( [ v[i2].co.x, -v[i2].co.z, v[i2].co.y ] ); vertices.extend( [ v[i0].co.x, -v[i0].co.z, v[i0].co.y ] ); vertices.extend( [ v[i2].co.x, -v[i2].co.z, v[i2].co.y ] ); vertices.extend( [ v[i3].co.x, -v[i3].co.z, v[i3].co.y ] ); faces.extend( [ len(faces)+0, len(faces)+1, len(faces)+2 ] ); faces.extend( [ len(faces)+0, len(faces)+1, len(faces)+2 ] ); ti0 = face.loop_indices[0] ti1 = face.loop_indices[1] ti2 = face.loop_indices[2] ti3 = face.loop_indices[3] if 1: n0 = obj.data.loops[ti0].normal n1 = obj.data.loops[ti1].normal n2 = obj.data.loops[ti2].normal n3 = obj.data.loops[ti3].normal normals.extend( [ n0.x, -n0.z, n0.y ] ); normals.extend( [ n1.x, -n1.z, n1.y ] ); normals.extend( [ n2.x, -n2.z, n2.y ] ); normals.extend( [ n0.x, -n0.z, n0.y ] ); normals.extend( [ n2.x, -n2.z, n2.y ] ); normals.extend( [ n3.x, -n3.z, n3.y ] ); if uv_layer: t0 = uv_layer[ ti0 ].uv t1 = uv_layer[ ti1 ].uv t2 = uv_layer[ ti2 ].uv t3 = uv_layer[ ti3 ].uv coordinates.extend( [ t0.x, 1.0 - t0.y ] ) coordinates.extend( [ t1.x, 1.0 - t1.y ] ) coordinates.extend( [ t2.x, 1.0 - t2.y ] ) coordinates.extend( [ t0.x, 1.0 - t0.y ] ) coordinates.extend( [ t2.x, 1.0 - t2.y ] ) coordinates.extend( [ t3.x, 1.0 - t3.y ] ) if color_layer: c0 = color_layer[ ti0 ].color c1 = color_layer[ ti1 ].color c2 = color_layer[ ti2 ].color c3 = color_layer[ ti3 ].color colors.extend( [ c0.r, c0.g, c0.b ] ) colors.extend( [ c1.r, c1.g, c1,b ] ) colors.extend( [ c2.r, c2.g, c2.b ] ) colors.extend( [ c0.r, c0.g, c0.b ] ) colors.extend( [ c2.r, c2.g, c2.b ] ) colors.extend( [ c3.r, c3.g, c3.b ] ) else: i0 = face.vertices[0] i1 = face.vertices[1] i2 = face.vertices[2] v = obj.data.vertices vertices.extend( [ v[i0].co.x, -v[i0].co.z, v[i0].co.y ] ); vertices.extend( [ v[i1].co.x, -v[i1].co.z, v[i1].co.y ] ); vertices.extend( [ v[i2].co.x, -v[i2].co.z, v[i2].co.y ] ); faces.extend( [ len(faces)+0, len(faces)+1, len(faces)+2 ] ); ti0 = face.loop_indices[0] ti1 = face.loop_indices[1] ti2 = face.loop_indices[2] if 1: n0 = obj.data.loops[ti0].normal n1 = obj.data.loops[ti1].normal n2 = obj.data.loops[ti2].normal normals.extend( [ n0.x, -n0.z, n0.y ] ); normals.extend( [ n1.x, -n1.z, n1.y ] ); normals.extend( [ n2.x, -n2.z, n2.y ] ); if uv_layer: t0 = uv_layer[ ti0 ].uv t1 = uv_layer[ ti1 ].uv t2 = uv_layer[ ti2 ].uv coordinates.extend( [ t0.x, 1.0 - t0.y ] ) coordinates.extend( [ t1.x, 1.0 - t1.y ] ) coordinates.extend( [ t2.x, 1.0 - t2.y ] ) if color_layer: c0 = color_layer[ ti0 ].color c1 = color_layer[ ti1 ].color c2 = color_layer[ ti2 ].color colors.extend( [ c0.r, c0.g, c0.b ] ) colors.extend( [ c1.r, c1.g, c1,b ] ) colors.extend( [ c2.r, c2.g, c2.b ] ) return { 'vertices' : vertices, 'faces' : faces, 'coordinates' : coordinates, 'normals' : normals, 'colors' : colors } def export_json(filename): scene = bpy.context.scene selected_objects = [obj for obj in scene.objects if obj.select_get() and obj.type == 'MESH'] exported_data = {} for obj in selected_objects: exported_data[obj.name] = export_mesh_data(obj) # Specify the full path where you have write permissions with open(filename, 'w') as file: json.dump(exported_data, file, indent=4) def write_some_data(context, filepath, use_some_setting): print("running write_some_data...") export_json(filepath) return {'FINISHED'} if __name__ == "__main__": write_some_data( 0, 'C:/Pro2/crate.json' , 0 );


Y-Axis


t bpy import json def export_mesh_data(obj): vertices = [] normals = [] coordinates = [] colors = [] faces = [] uv_layer = obj.data.uv_layers.active.data color_layer = obj.data.vertex_colors.active.data if obj.data.vertex_colors.active else None for face in obj.data.polygons: if len(face.vertices) == 4: # Check if the face is a quad # Convert quad to two triangles i0 = face.vertices[0] i1 = face.vertices[1] i2 = face.vertices[2] i3 = face.vertices[3] v = obj.data.vertices vertices.extend( [ v[i0].co.x, v[i0].co.z, v[i0].co.y ] ); vertices.extend( [ v[i1].co.x, v[i1].co.z, v[i1].co.y ] ); vertices.extend( [ v[i2].co.x, v[i2].co.z, v[i2].co.y ] ); vertices.extend( [ v[i0].co.x, v[i0].co.z, v[i0].co.y ] ); vertices.extend( [ v[i2].co.x, v[i2].co.z, v[i2].co.y ] ); vertices.extend( [ v[i3].co.x, v[i3].co.z, v[i3].co.y ] ); faces.extend( [ len(faces)+0, len(faces)+1, len(faces)+2 ] ); faces.extend( [ len(faces)+0, len(faces)+1, len(faces)+2 ] ); ti0 = face.loop_indices[0] ti1 = face.loop_indices[1] ti2 = face.loop_indices[2] ti3 = face.loop_indices[3] if 1: n0 = obj.data.loops[ti0].normal n1 = obj.data.loops[ti1].normal n2 = obj.data.loops[ti2].normal n3 = obj.data.loops[ti3].normal normals.extend( [ n0.x, n0.z, n0.y ] ); normals.extend( [ n1.x, n1.z, n1.y ] ); normals.extend( [ n2.x, n2.z, n2.y ] ); normals.extend( [ n0.x, n0.z, n0.y ] ); normals.extend( [ n2.x, n2.z, n2.y ] ); normals.extend( [ n3.x, n3.z, n3.y ] ); if uv_layer: t0 = uv_layer[ ti0 ].uv t1 = uv_layer[ ti1 ].uv t2 = uv_layer[ ti2 ].uv t3 = uv_layer[ ti3 ].uv coordinates.extend( [ t0.x, 1.0 - t0.y ] ) coordinates.extend( [ t1.x, 1.0 - t1.y ] ) coordinates.extend( [ t2.x, 1.0 - t2.y ] ) coordinates.extend( [ t0.x, 1.0 - t0.y ] ) coordinates.extend( [ t2.x, 1.0 - t2.y ] ) coordinates.extend( [ t3.x, 1.0 - t3.y ] ) if color_layer: c0 = color_layer[ ti0 ].color c1 = color_layer[ ti1 ].color c2 = color_layer[ ti2 ].color c3 = color_layer[ ti3 ].color colors.extend( [ c0.r, c0.g, c0.b ] ) colors.extend( [ c1.r, c1.g, c1,b ] ) colors.extend( [ c2.r, c2.g, c2.b ] ) colors.extend( [ c0.r, c0.g, c0.b ] ) colors.extend( [ c2.r, c2.g, c2.b ] ) colors.extend( [ c3.r, c3.g, c3.b ] ) else: i0 = face.vertices[0] i1 = face.vertices[1] i2 = face.vertices[2] v = obj.data.vertices vertices.extend( [ v[i0].co.x, v[i0].co.z, v[i0].co.y ] ); vertices.extend( [ v[i1].co.x, v[i1].co.z, v[i1].co.y ] ); vertices.extend( [ v[i2].co.x, v[i2].co.z, v[i2].co.y ] ); faces.extend( [ len(faces)+0, len(faces)+1, len(faces)+2 ] ); ti0 = face.loop_indices[0] ti1 = face.loop_indices[1] ti2 = face.loop_indices[2] if 1: n0 = obj.data.loops[ti0].normal n1 = obj.data.loops[ti1].normal n2 = obj.data.loops[ti2].normal normals.extend( [ n0.x, n0.z, n0.y ] ); normals.extend( [ n1.x, n1.z, n1.y ] ); normals.extend( [ n2.x, n2.z, n2.y ] ); if uv_layer: t0 = uv_layer[ ti0 ].uv t1 = uv_layer[ ti1 ].uv t2 = uv_layer[ ti2 ].uv coordinates.extend( [ t0.x, 1.0 - t0.y ] ) coordinates.extend( [ t1.x, 1.0 - t1.y ] ) coordinates.extend( [ t2.x, 1.0 - t2.y ] ) if color_layer: c0 = color_layer[ ti0 ].color c1 = color_layer[ ti1 ].color c2 = color_layer[ ti2 ].color colors.extend( [ c0.r, c0.g, c0.b ] ) colors.extend( [ c1.r, c1.g, c1,b ] ) colors.extend( [ c2.r, c2.g, c2.b ] ) return { 'vertices' : vertices, 'faces' : faces, 'coordinates' : coordinates, 'normals' : normals, 'colors' : colors } def export_json(filename): scene = bpy.context.scene selected_objects = [obj for obj in scene.objects if obj.select_get() and obj.type == 'MESH'] exported_data = {} for obj in selected_objects: exported_data[obj.name] = export_mesh_data(obj) # Specify the full path where you have write permissions with open(filename, 'w') as file: json.dump(exported_data, file, indent=4) def write_some_data(context, filepath, use_some_setting): print("running write_some_data...") export_json(filepath) return {'FINISHED'} if __name__ == "__main__": write_some_data( 0, 'C:/Pro2/torus.json' , 0 );



Material IDs


Extend the previous version but also include at global material index list - each face has a material index. Shapes with the same material have the same index.

t bpy import json allmatnames = {} def export_mesh_data(obj): vertices = [] normals = [] coordinates = [] colors = [] faces = [] material_ids = [] uv_layer = 0 if obj.data.uv_layers.active: uv_layer = obj.data.uv_layers.active.data color_layer = obj.data.vertex_colors.active.data if obj.data.vertex_colors.active else None for face in obj.data.polygons: mat_id = face.material_index # Material index for the current face mat_name = 'none' if obj.data.materials: mat_name = obj.data.materials[mat_id].name if mat_name not in allmatnames: allmatnames[ mat_name ] = len( allmatnames.keys()); all_mat_id = allmatnames[ mat_name ] if len(face.vertices) == 4: # Check if the face is a quad # Convert quad to two triangles i0 = face.vertices[0] i1 = face.vertices[1] i2 = face.vertices[2] i3 = face.vertices[3] v = obj.data.vertices vertices.extend( [ v[i0].co.x, v[i0].co.z, v[i0].co.y ] ); vertices.extend( [ v[i1].co.x, v[i1].co.z, v[i1].co.y ] ); vertices.extend( [ v[i2].co.x, v[i2].co.z, v[i2].co.y ] ); vertices.extend( [ v[i0].co.x, v[i0].co.z, v[i0].co.y ] ); vertices.extend( [ v[i2].co.x, v[i2].co.z, v[i2].co.y ] ); vertices.extend( [ v[i3].co.x, v[i3].co.z, v[i3].co.y ] ); faces.extend( [ len(faces)+0, len(faces)+1, len(faces)+2 ] ); faces.extend( [ len(faces)+0, len(faces)+1, len(faces)+2 ] ); material_ids.append(all_mat_id) material_ids.append(all_mat_id) ti0 = face.loop_indices[0] ti1 = face.loop_indices[1] ti2 = face.loop_indices[2] ti3 = face.loop_indices[3] if 1: n0 = obj.data.loops[ti0].normal n1 = obj.data.loops[ti1].normal n2 = obj.data.loops[ti2].normal n3 = obj.data.loops[ti3].normal normals.extend( [ n0.x, n0.z, n0.y ] ); normals.extend( [ n1.x, n1.z, n1.y ] ); normals.extend( [ n2.x, n2.z, n2.y ] ); normals.extend( [ n0.x, n0.z, n0.y ] ); normals.extend( [ n2.x, n2.z, n2.y ] ); normals.extend( [ n3.x, n3.z, n3.y ] ); if uv_layer: t0 = uv_layer[ ti0 ].uv t1 = uv_layer[ ti1 ].uv t2 = uv_layer[ ti2 ].uv t3 = uv_layer[ ti3 ].uv coordinates.extend( [ t0.x, 1.0 - t0.y ] ) coordinates.extend( [ t1.x, 1.0 - t1.y ] ) coordinates.extend( [ t2.x, 1.0 - t2.y ] ) coordinates.extend( [ t0.x, 1.0 - t0.y ] ) coordinates.extend( [ t2.x, 1.0 - t2.y ] ) coordinates.extend( [ t3.x, 1.0 - t3.y ] ) if color_layer: c0 = color_layer[ ti0 ].color c1 = color_layer[ ti1 ].color c2 = color_layer[ ti2 ].color c3 = color_layer[ ti3 ].color colors.extend( [ c0.r, c0.g, c0.b ] ) colors.extend( [ c1.r, c1.g, c1,b ] ) colors.extend( [ c2.r, c2.g, c2.b ] ) colors.extend( [ c0.r, c0.g, c0.b ] ) colors.extend( [ c2.r, c2.g, c2.b ] ) colors.extend( [ c3.r, c3.g, c3.b ] ) else: i0 = face.vertices[0] i1 = face.vertices[1] i2 = face.vertices[2] v = obj.data.vertices vertices.extend( [ v[i0].co.x, v[i0].co.z, v[i0].co.y ] ); vertices.extend( [ v[i1].co.x, v[i1].co.z, v[i1].co.y ] ); vertices.extend( [ v[i2].co.x, v[i2].co.z, v[i2].co.y ] ); faces.extend( [ len(faces)+0, len(faces)+1, len(faces)+2 ] ); material_ids.append(all_mat_id) ti0 = face.loop_indices[0] ti1 = face.loop_indices[1] ti2 = face.loop_indices[2] if 1: n0 = obj.data.loops[ti0].normal n1 = obj.data.loops[ti1].normal n2 = obj.data.loops[ti2].normal normals.extend( [ n0.x, n0.z, n0.y ] ); normals.extend( [ n1.x, n1.z, n1.y ] ); normals.extend( [ n2.x, n2.z, n2.y ] ); if uv_layer: t0 = uv_layer[ ti0 ].uv t1 = uv_layer[ ti1 ].uv t2 = uv_layer[ ti2 ].uv coordinates.extend( [ t0.x, 1.0 - t0.y ] ) coordinates.extend( [ t1.x, 1.0 - t1.y ] ) coordinates.extend( [ t2.x, 1.0 - t2.y ] ) if color_layer: c0 = color_layer[ ti0 ].color c1 = color_layer[ ti1 ].color c2 = color_layer[ ti2 ].color colors.extend( [ c0.r, c0.g, c0.b ] ) colors.extend( [ c1.r, c1.g, c1,b ] ) colors.extend( [ c2.r, c2.g, c2.b ] ) return { 'vertices' : vertices, 'faces' : faces, 'coordinates' : coordinates, 'normals' : normals, 'colors' : colors, 'material_ids': material_ids } def export_json(filename): scene = bpy.context.scene selected_objects = [obj for obj in scene.objects if obj.select_get() and obj.type == 'MESH'] exported_data = {} for obj in selected_objects: exported_data[obj.name] = export_mesh_data(obj) # Specify the full path where you have write permissions with open(filename, 'w') as file: json.dump(exported_data, file, indent=4) def write_some_data(context, filepath, use_some_setting): print("running write_some_data...") export_json(filepath) return {'FINISHED'} if __name__ == "__main__": write_some_data( 0, 'C:/Pro2/xxxxxx.json' , 0 );




Don
Don't forget - you flatten the transforms when exporting. Shortcut key 'cntl+a'


















Ray-Tracing with WebGPU kenwright WebGPU Development Cookbook - coding recipes for all your webgpu needs! WebGPU by Example: Fractals, Image Effects, Ray-Tracing, Procedural Geometry, 2D/3D, Particles, Simulations WebGPU Games WGSL 2d 3d interactive web-based fun learning WebGPU Compute WebGPU API - Owners WebGPU & WGSL Essentials: A Hands-On Approach to Interactive Graphics, Games, 2D Interfaces, 3D Meshes, Animation, Security and Production Kenwright graphics and animations using the webgpu api 12 week course kenwright learn webgpu api kenwright programming compute and graphics applications with html5 and webgpu api kenwright real-time 3d graphics with webgpu kenwright webgpu for dummies kenwright webgpu api develompent a quick start guide kenwright webgpu by example 2022 kenwright webgpu gems kenwright webgpu interactive compute and graphics visualization cookbook kenwright wgsl webgpu shading language cookbook kenwright WebGPU Shader Language Development: Vertex, Fragment, Compute Shaders for Programmers Kenwright wgsl webgpugems shading language cookbook kenwright WGSL Fundamentals book kenwright WebGPU Data Visualization Cookbook kenwright Special Effects Programming with WebGPU kenwright WebGPU Programming Guide: Interactive Graphics and Compute Programming with WebGPU & WGSL kenwright



 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2026 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.