def encode_baked_mesh(obj): """ Bake an object as a triangle mesh and encode it. """ stats_timer = share_data.current_stats_timer # Bake modifiers depsgraph = bpy.context.evaluated_depsgraph_get() obj = obj.evaluated_get(depsgraph) stats_timer.checkpoint("eval_depsgraph") # Triangulate mesh (before calculating normals) mesh = obj.data if obj.type == "MESH" else obj.to_mesh() if mesh is None: # This happens for empty curves return bytes() bm = bmesh.new() bm.from_mesh(mesh) bmesh.ops.triangulate(bm, faces=bm.faces) bm.to_mesh(mesh) bm.free() stats_timer.checkpoint("triangulate_mesh") # Calculate normals, necessary if auto-smooth option enabled mesh.calc_normals() mesh.calc_normals_split() # calc_loop_triangles resets normals so... don't use it stats_timer.checkpoint("calc_normals") # get active uv layer uvlayer = mesh.uv_layers.active vertices = [] normals = [] uvs = [] indices = [] material_indices = [] # array of triangle index, material index current_material_index = -1 current_face_index = 0 logger.debug("Writing %d polygons", len(mesh.polygons)) for f in mesh.polygons: for loop_id in f.loop_indices: index = mesh.loops[loop_id].vertex_index vertices.extend(mesh.vertices[index].co) normals.extend(mesh.loops[loop_id].normal) if uvlayer: uvs.extend([x for x in uvlayer.data[loop_id].uv]) indices.append(loop_id) if f.material_index != current_material_index: current_material_index = f.material_index material_indices.append(current_face_index) material_indices.append(current_material_index) current_face_index = current_face_index + 1 if obj.type != "MESH": obj.to_mesh_clear() stats_timer.checkpoint("make_buffers") # Vericex count + binary vertices buffer size = len(vertices) // 3 binary_vertices_buffer = common.int_to_bytes(size, 4) + struct.pack( f"{len(vertices)}f", *vertices) stats_timer.checkpoint("write_verts") # Normals count + binary normals buffer size = len(normals) // 3 binary_normals_buffer = common.int_to_bytes(size, 4) + struct.pack( f"{len(normals)}f", *normals) stats_timer.checkpoint("write_normals") # UVs count + binary uvs buffer size = len(uvs) // 2 binary_uvs_buffer = common.int_to_bytes(size, 4) + struct.pack( f"{len(uvs)}f", *uvs) stats_timer.checkpoint("write_uvs") # material indices + binary material indices buffer size = len(material_indices) // 2 binary_material_indices_buffer = common.int_to_bytes( size, 4) + struct.pack(f"{len(material_indices)}I", *material_indices) stats_timer.checkpoint("write_material_indices") # triangle indices count + binary triangle indices buffer size = len(indices) // 3 binary_indices_buffer = common.int_to_bytes(size, 4) + struct.pack( f"{len(indices)}I", *indices) stats_timer.checkpoint("write_tri_idx_buff") return (binary_vertices_buffer + binary_normals_buffer + binary_uvs_buffer + binary_material_indices_buffer + binary_indices_buffer)
def encode_base_mesh_geometry(mesh_data): stats_timer = share_data.current_stats_timer # We do not synchronize "select" and "hide" state of mesh elements # because we consider them user specific. bm = bmesh.new() bm.from_mesh(mesh_data) stats_timer.checkpoint("bmesh_from_mesh") binary_buffer = bytes() logger.debug("Writing %d vertices", len(bm.verts)) bm.verts.ensure_lookup_table() verts_array = [] for vert in bm.verts: verts_array.extend((*vert.co, )) stats_timer.checkpoint("make_verts_buffer") binary_buffer += struct.pack(f"1I{len(verts_array)}f", len(bm.verts), *verts_array) stats_timer.checkpoint("encode_verts_buffer") # Vertex layers # Ignored layers for now: # - skin (BMVertSkin) # - deform (BMDeformVert) # - paint_mask (float) # Other ignored layers: # - shape: shape keys are handled with Shape Keys at the mesh and object level # - float, int, string: don't really know their role binary_buffer += encode_bmesh_layer(bm.verts.layers.bevel_weight, bm.verts, extract_layer_float) stats_timer.checkpoint("verts_layers") logger.debug("Writing %d edges", len(bm.edges)) bm.edges.ensure_lookup_table() edges_array = [] for edge in bm.edges: edges_array.extend( (edge.verts[0].index, edge.verts[1].index, edge.smooth, edge.seam)) stats_timer.checkpoint("make_edges_buffer") binary_buffer += struct.pack(f"1I{len(edges_array)}I", len(bm.edges), *edges_array) stats_timer.checkpoint("encode_edges_buffer") # Edge layers # Ignored layers for now: None # Other ignored layers: # - freestyle: of type NotImplementedType, maybe reserved for future dev # - float, int, string: don't really know their role binary_buffer += encode_bmesh_layer(bm.edges.layers.bevel_weight, bm.edges, extract_layer_float) binary_buffer += encode_bmesh_layer(bm.edges.layers.crease, bm.edges, extract_layer_float) stats_timer.checkpoint("edges_layers") logger.debug("Writing %d faces", len(bm.faces)) bm.faces.ensure_lookup_table() faces_array = [] for face in bm.faces: faces_array.extend((face.material_index, face.smooth, len(face.verts))) faces_array.extend((vert.index for vert in face.verts)) stats_timer.checkpoint("make_faces_buffer") binary_buffer += struct.pack(f"1I{len(faces_array)}I", len(bm.faces), *faces_array) stats_timer.checkpoint("encode_faces_buffer") # Face layers # Ignored layers for now: None # Other ignored layers: # - freestyle: of type NotImplementedType, maybe reserved for future dev # - float, int, string: don't really know their role binary_buffer += encode_bmesh_layer(bm.faces.layers.face_map, bm.faces, extract_layer_int) stats_timer.checkpoint("faces_layers") # Loops layers # A loop is an edge attached to a face (so each edge of a manifold can have 2 loops at most). # Ignored layers for now: None # Other ignored layers: # - float, int, string: don't really know their role binary_buffer += encode_bmesh_layer(bm.loops.layers.uv, loops_iterator(bm), extract_layer_uv) binary_buffer += encode_bmesh_layer(bm.loops.layers.color, loops_iterator(bm), extract_layer_color) stats_timer.checkpoint("loops_layers") bm.free() return binary_buffer
def encode_baked_mesh(obj): """ Bake an object as a triangle mesh and encode it. """ stats_timer = share_data.current_stats_timer # Bake modifiers depsgraph = bpy.context.evaluated_depsgraph_get() obj = obj.evaluated_get(depsgraph) stats_timer.checkpoint("eval_depsgraph") # Triangulate mesh (before calculating normals) mesh = obj.data if obj.type == "MESH" else obj.to_mesh() if mesh is None: # This happens for empty curves return bytes() original_bm = None if obj.type == "MESH": # Mesh is restored later only if is has not been generated from a curve or something else original_bm = bmesh.new() original_bm.from_mesh(mesh) bm = bmesh.new() bm.from_mesh(mesh) bmesh.ops.triangulate(bm, faces=bm.faces) bm.to_mesh(mesh) bm.free() stats_timer.checkpoint("triangulate_mesh") # Calculate normals, necessary if auto-smooth option enabled mesh.calc_normals() mesh.calc_normals_split() # calc_loop_triangles resets normals so... don't use it stats_timer.checkpoint("calc_normals") # get active uv layer uvlayer = mesh.uv_layers.active vertices = array.array("d", (0.0, )) * len(mesh.vertices) * 3 mesh.vertices.foreach_get("co", vertices) normals = array.array("d", (0.0, )) * len(mesh.loops) * 3 mesh.loops.foreach_get("normal", normals) if uvlayer: uvs = array.array("d", (0.0, )) * len(mesh.loops) * 2 mesh.uv_layers[0].data.foreach_get("uv", uvs) else: uvs = [] indices = array.array("i", (0, )) * len(mesh.loops) mesh.loops.foreach_get("vertex_index", indices) if len(obj.material_slots) <= 1: material_indices = [] else: material_indices = array.array("i", (0, )) * len(mesh.polygons) mesh.polygons.foreach_get("material_index", material_indices) if obj.type != "MESH": obj.to_mesh_clear() else: original_bm.to_mesh(mesh) original_bm.free() stats_timer.checkpoint("make_buffers") # Vericex count + binary vertices buffer size = len(vertices) // 3 binary_vertices_buffer = common.int_to_bytes(size, 4) + struct.pack( f"{len(vertices)}f", *vertices) stats_timer.checkpoint("write_verts") # Normals count + binary normals buffer size = len(normals) // 3 binary_normals_buffer = common.int_to_bytes(size, 4) + struct.pack( f"{len(normals)}f", *normals) stats_timer.checkpoint("write_normals") # UVs count + binary uvs buffer size = len(uvs) // 2 binary_uvs_buffer = common.int_to_bytes(size, 4) + struct.pack( f"{len(uvs)}f", *uvs) stats_timer.checkpoint("write_uvs") # material indices + binary material indices buffer size = len(material_indices) binary_material_indices_buffer = common.int_to_bytes( size, 4) + struct.pack(f"{len(material_indices)}I", *material_indices) stats_timer.checkpoint("write_material_indices") # triangle indices count + binary triangle indices buffer size = len(indices) // 3 binary_indices_buffer = common.int_to_bytes(size, 4) + struct.pack( f"{len(indices)}I", *indices) stats_timer.checkpoint("write_tri_idx_buff") return (binary_vertices_buffer + binary_normals_buffer + binary_uvs_buffer + binary_material_indices_buffer + binary_indices_buffer)