Skip to content

Instantly share code, notes, and snippets.

@ranjian0
Created September 12, 2022 00:01
Show Gist options
  • Save ranjian0/3aa0ebeb54c2075ea5408828235b29ba to your computer and use it in GitHub Desktop.
Save ranjian0/3aa0ebeb54c2075ea5408828235b29ba to your computer and use it in GitHub Desktop.
Blender Extract Unique Indices from GLTF export
"""
Blender's GLTF exporter does some splitting of blender verts based on loops i.e in my case a mesh with 386 verts ends up with
441 verts after gltf export.
As explained at https://blender.stackexchange.com/questions/167372/gltf-export-has-twice-the-vertices-it-should
glTF is a last mile format and stores mesh data in a "Ready to load on GPU" state.
This script basically predetermines how the export will split the vertices and produces all the vertex
indices that will exist in the final gltf data.
XXX Caution
This script only considers the influence of the UVs (which is unique to my use case). If you are exporting other attributes such as
normals and tangents please add the necessary `dot_fields` and `dot` data as has been done at
https://git.blender.org/gitweb/gitweb.cgi/blender-addons.git/blob/HEAD:/io_scene_gltf2/blender/exp/gltf2_blender_extract.py in the
`extract_primitives` function.
"""
import bpy
import bmesh
import numpy as np
def get_gltf_export_indices(obj):
def __get_uvs(blender_mesh, uv_i):
layer = blender_mesh.uv_layers[uv_i]
uvs = np.empty(len(blender_mesh.loops) * 2, dtype=np.float32)
layer.data.foreach_get('uv', uvs)
uvs = uvs.reshape(len(blender_mesh.loops), 2)
# Blender UV space -> glTF UV space
# u,v -> u,1-v
uvs[:, 1] *= -1
uvs[:, 1] += 1
return uvs
# Get the active mesh
me = obj.data
tex_coord_max = len(me.uv_layers)
dot_fields = [('vertex_index', np.uint32)]
for uv_i in range(tex_coord_max):
dot_fields += [('uv%dx' % uv_i, np.float32), ('uv%dy' % uv_i, np.float32)]
dots = np.empty(len(me.loops), dtype=np.dtype(dot_fields))
vidxs = np.empty(len(me.loops))
me.loops.foreach_get('vertex_index', vidxs)
dots['vertex_index'] = vidxs
del vidxs
for uv_i in range(tex_coord_max):
uvs = __get_uvs(me, uv_i)
dots['uv%dx' % uv_i] = uvs[:, 0]
dots['uv%dy' % uv_i] = uvs[:, 1]
del uvs
# Calculate triangles and sort them into primitives.
me.calc_loop_triangles()
loop_indices = np.empty(len(me.loop_triangles) * 3, dtype=np.uint32)
me.loop_triangles.foreach_get('loops', loop_indices)
prim_indices = {} # maps material index to TRIANGLES-style indices into dots
# Bucket by material index.
tri_material_idxs = np.empty(len(me.loop_triangles), dtype=np.uint32)
me.loop_triangles.foreach_get('material_index', tri_material_idxs)
loop_material_idxs = np.repeat(tri_material_idxs, 3) # material index for every loop
unique_material_idxs = np.unique(tri_material_idxs)
del tri_material_idxs
for material_idx in unique_material_idxs:
prim_indices[material_idx] = loop_indices[loop_material_idxs == material_idx]
prim_dots = dots[prim_indices[0]]
prim_dots, indices = np.unique(prim_dots, return_inverse=True)
result = [d[0] for d in prim_dots]
return result
get_gltf_export_indices(bpy.context.object)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment