def make_node(self, parent_node, obj=None): node = None if obj is None: node = parent_node else: if obj.type == 'EMPTY': node = self.make_empty(parent_node, obj) elif obj.type == 'ARMATURE': node = self.make_armature(parent_node, obj) elif obj.type == 'MESH': node = self.make_mesh(parent_node, obj) elif obj.type in ('LIGHT', 'LAMP'): if obj.data.type in ('SPOT', 'POINT'): node = self.make_light(parent_node, obj) if node is None: return # make children of the current node if obj is None: # root objects children = filter(lambda o: not o.parent, bpy.data.objects) else: # children on current object children = obj.children for child in children: # print(child) if not is_object_visible(child) and not is_collision(child): continue if self._export_type == 'collision': if child.type in ('ARMATURE', 'LIGHT', 'LAMP'): continue if child.type == 'MESH' and not is_collision(child): continue self.make_node(node, child)
def can_merge(self, obj): if not self._merge: return False collection = get_object_collection(obj) if not collection: return False if is_collision(obj): return False obj_props = get_object_properties(obj) if obj_props.get('type') in NOT_MERGED_TYPES: return False return True
def _setup_node(self, node, obj=None, can_merge=False): if obj is None: return armature = get_armature(obj) obj_matrix = self._transform(get_object_matrix(obj, armature=armature)) # get custom object properties obj_props = get_object_properties(obj) if not can_merge and not armature: if self._geom_scale == 1: node.update({ 'rotation': quat_to_list(obj_matrix.to_quaternion()), 'scale': list(obj_matrix.to_scale()), 'translation': list(obj_matrix.to_translation()), # 'matrix': matrix_to_list(obj_matrix), }) else: x, y, z = list(obj_matrix.to_translation()) x *= self._geom_scale y *= self._geom_scale z *= self._geom_scale node.update({ 'rotation': quat_to_list(obj_matrix.to_quaternion()), 'scale': list(obj_matrix.to_scale()), 'translation': [x, y, z], }) # setup collisions if not can_merge and is_collision( obj) and obj_props.get('type') != 'Portal': collision = {} node['extensions'] = { 'BLENDER_physics': collision, } # collision shape shape = { 'shapeType': obj.rigid_body.collision_shape, 'boundingBox': [ obj.dimensions[i] / obj_matrix.to_scale()[i] * self._geom_scale for i in range(3) ], } if obj.rigid_body.collision_shape == 'MESH': shape['mesh'] = node.pop('mesh') collision['collisionShapes'] = [shape] collision['static'] = obj.rigid_body.type == 'PASSIVE' # don't actually collide (ghost) if (not obj.collision or not obj.collision.use): collision['intangible'] = True if self._set_origin: obj.select_set(state=True) set_active_object(obj) x1, y1, z1 = obj.location # set origin to the center of bounds bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS') x2, y2, z2 = obj.location if 'extras' not in node: node['extras'] = {} node['extras']['origin'] = [x2 - x1, y2 - y1, z2 - z1] # setup custom properties with tags if (obj_props or can_merge) and 'extras' not in node: node['extras'] = {} for k, v in obj_props.items(): if node['extras'].get(k): # tag exists tag = node['extras'].get(k) if type(v) in (tuple, list, dict): tag = json.dumps(v) else: tag = '{}'.format(v) node['extras'][k] = tag # if can_merge and 'type' not in obj_props: if can_merge: node['extras']['type'] = 'Merged'
def _setup_node(self, node, obj=None, can_merge=False): if obj is None: return armature = get_armature(obj) obj_matrix = get_object_matrix(obj, armature=armature) # get custom object properties obj_props = get_object_properties(obj) if not can_merge and not armature: node.add_matrix4(matrix_to_panda(obj_matrix)) if obj_props.get('type') == 'Portal': node.set_portal_flag(True) # setup collisions if not can_merge and is_collision(obj): # collision name node.set_collision_name(obj.name) # collision solid type shape = { 'BOX': EggGroup.CST_box, 'SPHERE': EggGroup.CST_sphere, 'CAPSULE': EggGroup.CST_tube, 'MESH': EggGroup.CST_polyset, }.get(obj.rigid_body.collision_shape, EggGroup.CST_polyset) # custom shape if obj.rigid_body.collision_shape == 'CONVEX_HULL': # trying to guess the best shape polygons = list( filter(lambda x: isinstance(x, EggPolygon), node.get_children())) if len(polygons) == 1 and polygons[0].is_planar(): # shape = EggGroup.CST_plane # <- is it infinite? shape = EggGroup.CST_polygon node.set_cs_type(shape) # collision flags # inherit collision by children nodes? flags = EggGroup.CF_descend if (not obj.collision or not obj.collision.use): # don't actually collide (ghost) flags |= EggGroup.CF_intangible node.set_collide_flags(flags) # setup custom properties with tags for k, v in obj_props.items(): if node.get_tag(k): # tag exists tag = node.get_tag(k) if type(v) in (tuple, list, dict): tag = json.dumps(v) else: tag = '{}'.format(v) node.set_tag(k, tag) if can_merge and 'type' not in obj_props: node.set_tag('type', 'Merged')
def make_geom(self, node, obj, can_merge=False): triangulate = not is_collision(obj) if self._geom_scale != 1: obj.scale.x = self._geom_scale obj.scale.y = self._geom_scale obj.scale.z = self._geom_scale apply_modifiers(obj, triangulate=triangulate) mesh = obj2mesh(obj, triangulate=triangulate) # get or create materials and textures egg_materials = {} egg_textures = {} egg_material_textures = {} if not self._no_materials and not is_collision(obj): for material in mesh.materials.values(): # material for child in self._root.get_children(): # existing material if (isinstance(child, EggMaterial) and child.get_name() == material.name): egg_materials[material.name] = child break else: # new material egg_material = self.make_material(material) self._root.add_child(egg_material) egg_materials[material.name] = egg_material # material -> textures if material.name not in egg_material_textures: egg_material_textures[material.name] = {} # textures if not self._no_textures: for type_, _, egg_texture in self.make_textures(material): tname = egg_texture.get_name() for child in self._root.get_children(): # existing texture if (isinstance(child, EggTexture) and child.get_name() == tname): egg_textures[tname] = child egg_material_textures[material.name][tname] = child break else: # new texture self._root.add_child(egg_texture) egg_textures[tname] = egg_texture egg_material_textures[material.name][tname] = egg_texture # get or create vertex pool egg_vertex_pool = None egg_vertex_id = 0 if can_merge: for child in node.get_children(): # existing vertex pool if isinstance(child, EggVertexPool): egg_vertex_pool = child egg_vertex_id = egg_vertex_pool.get_highest_index() break if egg_vertex_pool is None: # new vertex pool egg_vertex_pool = EggVertexPool(node.get_name()) egg_vertex_id = egg_vertex_pool.get_highest_index() node.add_child(egg_vertex_pool) # get armature and joints armature = get_armature(obj) egg_joints = {} if armature: for child in self._root.get_children(): if (isinstance(child, EggGroup) and child.get_dart_type() == EggGroup.DT_structured and child.get_name() == armature.name): egg_joints = self._get_joints(child) sharp_vertices = {} uv_tb = {} if not is_collision(obj): sharp_vertices = self.get_sharp_vertices(mesh) uv_tb = self.get_tangent_bitangent(mesh) egg_vertices = {} obj_matrix = get_object_matrix(obj, armature) parent_obj_matrix = obj_matrix if armature: parent_obj_matrix = get_object_matrix(armature) for polygon in mesh.polygons: # <-- polygon material = None mname = None if not self._no_materials: try: material = mesh.materials[polygon.material_index] mname = material.name except IndexError: pass # make polygon egg_polygon = EggPolygon(mname or node.get_name()) # set material and textures if material and not self._no_materials and not is_collision(obj): if mname in egg_materials: egg_polygon.set_material(egg_materials[mname]) # set textures if mname in egg_material_textures and not self._no_textures: for egg_texture in egg_material_textures[mname].values(): egg_polygon.add_texture(egg_texture) # vertices for i, vertex_id in enumerate(polygon.vertices): # i is vertex counter inside a polygon # (0, 1, 2) for triangle # vertex_id is reusable id, # because multiple polygons can share the same vertices # <-- vertex vertex = mesh.vertices[vertex_id] use_smooth = ( polygon.use_smooth and vertex_id not in sharp_vertices and not is_collision(obj)) # try to reuse shared vertices if (polygon.use_smooth and vertex_id in egg_vertices and not is_collision(obj)): shared = False for egg_vertex in egg_vertices[vertex_id]: loop_id = polygon.loop_indices[i] egg_vertex_uv = egg_vertex.get_uv_obj( self._get_uv_name(mesh.uv_layers.active)) if not egg_vertex_uv: egg_polygon.add_vertex(egg_vertex) shared = True break if self.can_share_vertex(mesh, loop_id, egg_vertex_uv.get_uv()): egg_polygon.add_vertex(egg_vertex) shared = True break if shared: continue # make new vertex data egg_vertex = self.make_vertex( parent_obj_matrix, obj_matrix, polygon, vertex, use_smooth=use_smooth) # uv layers if not is_collision(obj): for uv_name, uv_layer in mesh.uv_layers.items(): # <-- vertex uv loop_id = polygon.loop_indices[i] uv_loop = uv_layer.data[loop_id] # not active layer and extra UV disabled if not uv_layer.active and self._no_extra_uv: continue egg_vertex_uv = self.make_vertex_uv(uv_layer, uv_loop.uv) if uv_name in uv_tb: t, b, s = uv_tb[uv_name][loop_id] tangent = parent_obj_matrix @ t binormal = parent_obj_matrix @ b egg_vertex_uv.set_tangent(tuple(tangent)) egg_vertex_uv.set_binormal(tuple(binormal)) egg_vertex.set_uv_obj(egg_vertex_uv) # vertex uv --> # generate new ID, add vertex and save last ID egg_vertex_id += 1 egg_vertex_pool.add_vertex(egg_vertex, egg_vertex_id) egg_vertex_pool.set_highest_index(egg_vertex_id) egg_polygon.add_vertex(egg_vertex) # save vertex data for sharing if vertex_id not in egg_vertices: egg_vertices[vertex_id] = [] egg_vertices[vertex_id].append(egg_vertex) # attach joints to vertex if armature: for vertex_group in vertex.groups: obj_vertex_group = obj.vertex_groups[vertex_group.group] if obj_vertex_group.name in egg_joints: egg_joint = egg_joints[obj_vertex_group.name] egg_joint.set_vertex_membership( egg_vertex, vertex_group.weight) # vertex --> node.add_child(egg_polygon)
def make_geom(self, gltf_node, gltf_mesh, obj, can_merge=False): triangulate = True if self._geom_scale != 1: obj.scale.x = self._geom_scale obj.scale.y = self._geom_scale obj.scale.z = self._geom_scale apply_modifiers(obj, triangulate=triangulate) mesh = obj2mesh(obj, triangulate=triangulate) # get or create materials and textures gltf_materials = {} if not self._no_materials and not is_collision(obj): for material in mesh.materials.values(): # material for i, child in enumerate(self._root['materials']): # existing material if child['name'] == material.name: gltf_materials[material.name] = i break else: # new material gltf_material = self.make_material(material) self._root['materials'].append(gltf_material) gltf_materials[material.name] = len(self._root['materials']) - 1 # textures if not self._no_textures: for type_, gltf_sampler, gltf_image in self.make_textures(material): tname = gltf_image['name'] for i, child in enumerate(self._root['images']): # existing texture if child['name'] == tname: texid = i break else: # new texture self._root['samplers'].append(gltf_sampler) self._root['images'].append(gltf_image) gltf_texture = { 'sampler': len(self._root['samplers']) - 1, 'source': len(self._root['images']) - 1, } self._root['textures'].append(gltf_texture) texid = len(self._root['textures']) - 1 matid = gltf_materials[material.name] if type(type_) == tuple and len(type_) == 2: type_l1, type_l2 = type_ self._root['materials'][matid][type_l1][type_l2] = {'index': texid} else: self._root['materials'][matid][type_] = {'index': texid} # get primitives gltf_primitives = {} gltf_primitive_indices = {} if can_merge: for i, gltf_primitive in enumerate(gltf_mesh['primitives']): mname = None if 'material' in gltf_primitive: matid = gltf_primitive['material'] mname = self._root['materials'][matid]['name'] gltf_primitives[mname] = gltf_primitive gltf_primitive_indices[mname] = gltf_primitive['extra']['highest_index'] # get armature and joints armature = get_armature(obj) max_joints = 0 # get max joints per vertex gltf_joints = {} if armature: max_joints = 1 for polygon in mesh.polygons: for vertex_id in polygon.vertices: vertex = mesh.vertices[vertex_id] joints = 0 for vertex_group in vertex.groups: obj_vertex_group = obj.vertex_groups[vertex_group.group] if vertex_group.weight > 0: joints += 1 max_joints = max(max_joints, joints) if 'skin' in gltf_node: gltf_skin = self._root['skins'][gltf_node['skin']] # for i, child in enumerate(self._root['skins']): # if child['name'] == armature.name: # gltf_joints = self._get_joints(child) # break gltf_joints = self._get_joints(gltf_skin) # get max joint layers (4 bones per layer) max_joint_layers = math.ceil(max_joints / 4) # max_joint_layers = 1 sharp_vertices = self.get_sharp_vertices(mesh) uv_tb = self.get_tangent_bitangent(mesh) gltf_vertices = {} obj_matrix = self._matrix @ get_object_matrix(obj, armature) for polygon in mesh.polygons: # <-- polygon material = None mname = None if not self._no_materials: try: material = mesh.materials[polygon.material_index] mname = material.name except IndexError: pass # get or create primitive if mname in gltf_primitives: gltf_primitive = gltf_primitives[mname] else: gltf_primitive = self._make_primitive() gltf_primitives[mname] = gltf_primitive gltf_primitive_indices[mname] = -1 gltf_mesh['primitives'].append(gltf_primitive) # set material if material and not self._no_materials and not is_collision(obj): if material.name in gltf_materials: gltf_primitive['material'] = gltf_materials[mname] # else: # gltf_primitive['material'] = 0 # vertices for i, vertex_id in enumerate(polygon.vertices): # i is vertex counter inside a polygon # (0, 1, 2) for triangle # vertex_id is reusable id, # because multiple polygons can share the same vertices # <-- vertex vertex = mesh.vertices[vertex_id] use_smooth = ( polygon.use_smooth and vertex_id not in sharp_vertices and not is_collision(obj)) # try to reuse shared vertices if (polygon.use_smooth and vertex_id in gltf_vertices and not is_collision(obj)): shared = False for gltf_vertex in gltf_vertices[vertex_id]: loop_id = polygon.loop_indices[i] gltf_vertex_uv = gltf_vertex[1] if self.can_share_vertex(mesh, loop_id, gltf_vertex_uv): self._buffer.write( gltf_primitive['indices'], gltf_vertex[0]) shared = True break if shared: continue # make new vertex data self.make_vertex( obj_matrix, gltf_primitive, polygon, vertex, use_smooth=use_smooth, can_merge=can_merge) # uv layers, active first active_uv = 0, 0 if not is_collision(obj): uv_layers = sorted( mesh.uv_layers.items(), key=lambda x: not x[1].active) for uv_id, (uv_name, uv_layer) in enumerate(uv_layers): # <-- vertex uv loop_id = polygon.loop_indices[i] uv_loop = uv_layer.data[loop_id] # not active layer and extra UV disabled if not uv_layer.active and self._no_extra_uv: continue u, v = uv_loop.uv.to_2d() if uv_layer.active: active_uv = u, v self._write_uv(gltf_primitive, uv_id, u, v) if uv_name in uv_tb and uv_layer.active: self._write_tbs( obj_matrix, gltf_primitive, *uv_tb[uv_name][loop_id], can_merge=can_merge) # vertex uv --> # generate new ID, add vertex and save last ID gltf_primitive_indices[mname] += 1 self._buffer.write( gltf_primitive['indices'], gltf_primitive_indices[mname]) gltf_primitive['extra']['highest_index'] = gltf_primitive_indices[mname] # save vertex data for sharing if vertex_id not in gltf_vertices: gltf_vertices[vertex_id] = [] gltf_vertices[vertex_id].append(( gltf_primitive_indices[mname], active_uv, )) # attach joints to vertex if gltf_joints: joints_weights = [] # list of vec4 vertex_groups = reversed(sorted( vertex.groups, key=lambda vg: vg.weight)) for vertex_group in vertex_groups: obj_vertex_group = obj.vertex_groups[vertex_group.group] if (obj_vertex_group.name in gltf_joints and vertex_group.weight > 0): if not joints_weights or len(joints_weights[-1]) >= 4: if len(joints_weights) >= max_joint_layers: break joints_weights.append([]) joint_id = gltf_joints[obj_vertex_group.name] joint_weight = joint_id, vertex_group.weight joints_weights[-1].append(joint_weight) # push to vec4 # padding if joints_weights: while len(joints_weights[-1]) < 4: # up to vec4 joints_weights[-1].append((0, 0)) # push to vec4 while len(joints_weights) < max_joint_layers: # up to max joints vec4 = [(0, 0)] * 4 # make empty vec4 joints_weights.append(vec4) assert len(joints_weights) == max_joint_layers self._write_joints_weights( gltf_primitive, len(tuple(gltf_joints.keys())), joints_weights)
def _setup_node(self, node, obj=None, can_merge=False): if obj is None: return armature = get_armature(obj) obj_matrix = get_object_matrix(obj, armature=armature) # get custom object properties obj_props = get_object_properties(obj) if not can_merge and not armature: if self._geom_scale == 1: node.update({ 'rotation': quat_to_list(obj_matrix.to_quaternion()), 'scale': list(obj_matrix.to_scale()), 'translation': list(obj_matrix.to_translation()), }) else: x, y, z = list(obj_matrix.to_translation()) x *= self._geom_scale y *= self._geom_scale z *= self._geom_scale node.update({ 'rotation': quat_to_list(obj_matrix.to_quaternion()), 'scale': list(obj_matrix.to_scale()), 'translation': [x, y, z], }) # setup collisions if not can_merge and is_collision(obj) and obj_props.get('type') != 'Portal': collision = {} node['extensions'] = { 'BLENDER_physics': collision, } # collision shape shape = { 'shapeType': obj.rigid_body.collision_shape, 'boundingBox': [ obj.dimensions[i] / obj_matrix.to_scale()[i] for i in range(3) ], } if obj.rigid_body.collision_shape == 'MESH': shape['mesh'] = node.pop('mesh') collision['collisionShapes'] = [shape] collision['static'] = obj.rigid_body.type == 'PASSIVE' # don't actually collide (ghost) if (not obj.collision or not obj.collision.use): collision['intangible'] = True # setup custom properties with tags if (obj_props or can_merge) and 'extras' not in node: node['extras'] = {} for k, v in obj_props.items(): if node['extras'].get(k): # tag exists tag = node['extras'].get(k) if type(v) in (tuple, list, dict): tag = json.dumps(v) else: tag = '{}'.format(v) node['extras'][k] = tag if can_merge and 'type' not in obj_props: node['extras']['type'] = 'Merged'
def make_geom(self, gltf_node, gltf_mesh, obj, can_merge=False): triangulate = True if self._geom_scale != 1: scale = obj.scale obj.scale.x, obj.scale.y, obj.scale.z = [self._geom_scale] * 3 apply_modifiers(obj, triangulate=triangulate, apply_scale=True) obj.scale = scale else: apply_modifiers(obj, triangulate=triangulate) mesh = obj2mesh(obj, triangulate=triangulate) # setup shape key names for the primitives if mesh.shape_keys: for sk_name in sorted(mesh.shape_keys.key_blocks.keys()): if sk_name.lower() == 'basis': continue gltf_mesh['extras']['targetNames'].append(sk_name) # get or create materials and textures gltf_materials = {} if not self._no_materials and not is_collision(obj): for material in mesh.materials.values(): # material for i, child in enumerate( self._root['materials']): # existing material if child['name'] == material.name: gltf_materials[material.name] = i break else: # new material gltf_material = self.make_material(material) self._root['materials'].append(gltf_material) gltf_materials[material.name] = len( self._root['materials']) - 1 # textures if not self._no_textures: for type_, gltf_sampler, gltf_image in self.make_textures( material): tname = gltf_image['name'] for i, child in enumerate( self._root['images']): # existing texture if child['name'] == tname: texid = i break else: # new texture self._root['samplers'].append(gltf_sampler) self._root['images'].append(gltf_image) gltf_texture = { 'sampler': len(self._root['samplers']) - 1, 'source': len(self._root['images']) - 1, } self._root['textures'].append(gltf_texture) texid = len(self._root['textures']) - 1 matid = gltf_materials[material.name] if type(type_) == tuple and len(type_) == 2: type_l1, type_l2 = type_ self._root['materials'][matid][type_l1][ type_l2] = { 'index': texid, 'texCoord': 0, } else: self._root['materials'][matid][type_] = { 'index': texid, 'texCoord': 0, } # get primitives gltf_primitives = {} gltf_primitive_indices = {} # splitted vertex buffers gltf_mesh_vertices_index = -1 # reusable vertex buffer if can_merge: for i, gltf_primitive in enumerate(gltf_mesh['primitives']): mname = None if 'material' in gltf_primitive: matid = gltf_primitive['material'] mname = self._root['materials'][matid]['name'] gltf_primitives[mname] = gltf_primitive gltf_primitive_indices[mname] = gltf_primitive['extras'][ 'highest_index'] gltf_vertices = {} # get armature and joints armature = get_armature(obj) max_joints = 0 # get max joints per vertex gltf_joints = {} if armature: # max_joints = 1 # for polygon in mesh.polygons: # for vertex_id in polygon.vertices: # vertex = mesh.vertices[vertex_id] # joints = 0 # for vertex_group in vertex.groups: # obj_vertex_group = obj.vertex_groups[vertex_group.group] # if vertex_group.weight > 0: # joints += 1 # max_joints = max(max_joints, joints) if 'skin' in gltf_node: gltf_skin = self._root['skins'][gltf_node['skin']] # for i, child in enumerate(self._root['skins']): # if child['name'] == armature.name: # gltf_joints = self._get_joints(child) # break gltf_joints = self._get_joints(gltf_skin) # get max joint layers (4 bones per layer) # max_joint_layers = math.ceil(max_joints / 4) # panda3d-gltf is limited to 1 single layer only (up to 4 bones) max_joint_layers = 1 sharp_vertices = self.get_sharp_vertices(mesh) uv_tb = self.get_tangent_bitangent(mesh) obj_matrix = self._transform(get_object_matrix(obj, armature=armature)) for polygon in mesh.polygons: # <-- polygon material = None mname = None if not self._no_materials: try: material = mesh.materials[polygon.material_index] mname = material.name except IndexError: pass # get or create primitive if mname in gltf_primitives: gltf_primitive = gltf_primitives[mname] else: gltf_primitive = self._make_primitive(gltf_mesh, mesh) gltf_primitives[mname] = gltf_primitive gltf_primitive_indices[mname] = -1 gltf_mesh['primitives'].append(gltf_primitive) # set material if material and not self._no_materials and not is_collision(obj): if material.name in gltf_materials: gltf_primitive['material'] = gltf_materials[mname] # vertices for i, vertex_id in enumerate(polygon.vertices): # i is vertex counter inside a polygon # (0, 1, 2) for triangle # vertex_id is reusable id, # because multiple polygons can share the same vertices loop_id = polygon.loop_indices[i] # <-- vertex vertex = mesh.vertices[vertex_id] use_smooth = ( polygon.use_smooth and # vertex_id not in sharp_vertices and not is_collision(obj)) # try to reuse shared vertices if mname not in gltf_vertices: gltf_vertices[mname] = {} if (polygon.use_smooth and vertex_id in gltf_vertices[mname] and not is_collision(obj)): shared = False for gltf_vertex_index, gltf_vertex_uv, gltf_vertex_normal in gltf_vertices[ mname][vertex_id]: if self.can_share_vertex(mesh, vertex, loop_id, gltf_vertex_uv, gltf_vertex_normal): self._buffer.write(gltf_primitive['indices'], gltf_vertex_index) shared = True break if shared: continue # make new vertex data can_merge_vertices = can_merge if armature: can_merge_vertices = True elif is_collision(obj): can_merge_vertices = False self.make_vertex(obj_matrix, gltf_primitive, mesh, polygon, vertex, vertex_id, loop_id, use_smooth=use_smooth, can_merge=can_merge_vertices) # uv layers, active first active_uv = 0, 0 if not is_collision(obj): uv_layers = sorted(mesh.uv_layers.items(), key=lambda x: not x[1].active) for uv_id, (uv_name, uv_layer) in enumerate(uv_layers): # <-- vertex uv uv_loop = uv_layer.data[loop_id] # not active layer and extra UV disabled if not uv_layer.active and self._no_extra_uv: continue u, v = uv_loop.uv.to_2d() if uv_layer.active: active_uv = u, v self._write_uv(gltf_primitive, uv_id, u, v) if uv_name in uv_tb and uv_layer.active: self._write_tbs(obj_matrix, gltf_primitive, *uv_tb[uv_name][loop_id], can_merge=can_merge_vertices) # vertex uv --> # generate new ID, add vertex and save last ID gltf_primitive_indices[mname] += 1 gltf_mesh_vertices_index += 1 if self._split_primitives: idx = gltf_primitive_indices[mname] else: idx = gltf_mesh_vertices_index self._buffer.write(gltf_primitive['indices'], idx) gltf_primitive['extras'][ 'highest_index'] = gltf_primitive_indices[mname] # save vertex data for sharing if vertex_id not in gltf_vertices[mname]: gltf_vertices[mname][vertex_id] = [] gltf_vertices[mname][vertex_id].append(( idx, active_uv, mesh.loops[loop_id].normal if use_smooth else polygon.normal, )) # attach joints to vertex if gltf_joints: joints_weights = [] vertex_groups = reversed( sorted(vertex.groups, key=lambda vg: vg.weight)) for vertex_group in vertex_groups: obj_vertex_group = obj.vertex_groups[ vertex_group.group] # no bones with vertex group's name if obj_vertex_group.name not in gltf_joints: continue # weight is zero if vertex_group.weight <= 0: continue joint_id = gltf_joints[obj_vertex_group.name] joints_weights.append([joint_id, vertex_group.weight]) # padding while ((len(joints_weights) % 4 != 0) or (len(joints_weights) < max_joint_layers * 4)): joints_weights.append((0, 0)) # limit by max joints joints_weights = joints_weights[:max_joint_layers * 4] imax = -1 wmax = 0 for i, (joint, weight) in enumerate(joints_weights): if weight > wmax: imax = i wmax = weight # if imax >= 0: # joints_weights[imax][1] += 1 - sum(list(zip(*joints_weights))[1]) # group by 4 joint-weight pairs joints_weights_groups = [] for i in range(len(joints_weights) // 4): group = joints_weights[i * 4:i * 4 + 4] joints_weights_groups.append(group) self._write_joints_weights(gltf_primitive, len(tuple(gltf_joints.keys())), joints_weights_groups)