def wireframe( obj: bpy.types.Object, evaluated: bool) -> typing.List[typing.Tuple[float, float, float]]: ''' Get coords of verts for edges of an object. Args: obj: An object that can be converted to mesh. evaluated: Whether to apply modifiers. Returns: points: A list of coordinates. ''' points = [] if evaluated: deps = bpy.context.view_layer.depsgraph obj = obj.evaluated_get(deps) mesh = obj.to_mesh() bm = bmesh.new() bm.from_mesh(mesh) bm.transform(obj.matrix_world) for edge in bm.edges: for vert in edge.verts: points.append(vert.co.xyz) bm.free() obj.to_mesh_clear() return points
def bake_frame(ob: bpy.types.Object, frame: int, export_obj=None): scene = bpy.context.scene shape_name = 'cache__F{:04d}'.format(frame) data_path = 'key_blocks["{}"].value'.format(shape_name) ## clean out the old keys if ob.data.shape_keys.animation_data and ob.data.shape_keys.animation_data.action: action = ob.data.shape_keys.animation_data.action for curve in action.fcurves: if curve.data_path == data_path: action.fcurves.remove(curve) break scene.frame_set(frame) mesh = ob.to_mesh(scene, apply_modifiers=True, settings="RENDER") shape = add_shape_key(ob, shape_name) for index in range(len(ob.data.vertices)): shape.data[index].co = mesh.vertices[index].co ## this keys them on for the duration of the animation shape.value = 0.0 ob.data.shape_keys.keyframe_insert(data_path, frame=frame - 1) shape.value = 1.0 ob.data.shape_keys.keyframe_insert(data_path, frame=frame) shape.value = 0.0 ob.data.shape_keys.keyframe_insert(data_path, frame=frame + 1) if export_obj: ## the blender OBJ importer adjusts for Y up in other packages ## so rotate the mesh here before export rotate_mat = mathutils.Matrix.Rotation(-math.pi / 2, 4, 'X') mesh.transform(rotate_mat) ## man the OBJ format is simple. No wonder people love it. print('+ Exporting frame {} to "{}"'.format(frame, export_obj)) with open(export_obj, 'w') as fp: for v in mesh.vertices: fp.write('v {:6f} {:6f} {:6f}\n'.format( v.co[0], v.co[1], v.co[2])) ## smoothing # fp.write( 's 1\n' ) for f in mesh.polygons: msg = "f" for v in f.vertices: msg += ' {:d}'.format(v + 1) msg += '\n' fp.write(msg) ## one extra line at the end fp.write('\n') bpy.data.meshes.remove(mesh)
def temporary_mesh_object(source : bpy.types.Object) -> bpy.types.Object: """Creates a temporary mesh object from a nonmesh object that will only exist for the duration of the context.""" assert source.type != "MESH" obj = bpy.data.objects.new(source.name, source.to_mesh(bpy.context.scene, True, "RENDER")) obj.draw_type = "WIRE" obj.matrix_basis, obj.matrix_world = source.matrix_basis, source.matrix_world obj.parent = source.parent bpy.context.scene.objects.link(obj) try: yield obj finally: bpy.data.objects.remove(obj)
def sync(rpr_context, obj: bpy.types.Object, **kwargs): """ Converts object into blender's mesh and exports it as mesh """ try: # This operation adds new mesh into bpy.data.meshes, that's why it should be removed after usage. # obj.to_mesh() could also return None for META objects. new_mesh = obj.to_mesh() log("sync", obj, new_mesh) if new_mesh: mesh.sync(rpr_context, obj, mesh=new_mesh, **kwargs) return True return False finally: # it's important to clear created mesh obj.to_mesh_clear()
def normalize_skinning_weights(obj: bpy.types.Object) -> bpy.types.Mesh: bpy.context.view_layer.objects.active = obj bpy.ops.object.mode_set(mode="EDIT") bpy.ops.mesh.select_mode(type="VERT") bpy.ops.mesh.select_all(action="DESELECT") bm = bmesh.from_edit_mesh(obj.data) bm.verts.ensure_lookup_table() for idx in range(len(bm.verts)): bm.select_history.add(bm.verts[idx]) bpy.ops.object.vertex_weight_normalize_active_vertex() bpy.ops.mesh.select_all(action="DESELECT") mesh = obj.to_mesh() bm.to_mesh(mesh) bm.free() bpy.ops.object.mode_set(mode="OBJECT") return mesh
def adjust_shape_key(obj: bpy.types.Object, name: str, blend_weight: float, vertices: np.ndarray = None): bpy.context.view_layer.objects.active = obj key_block = bpy.data.shape_keys["Key"].key_blocks[name] key_block.value = blend_weight if vertices is not None: bpy.ops.object.mode_set(mode="EDIT") bm = bmesh.from_edit_mesh(obj.data) bm.verts.ensure_lookup_table() for idx in range(len(bm.verts)): bm.verts[idx].co = (vertices[idx][0], vertices[idx][1], vertices[idx][2]) mesh = obj.to_mesh() bm.to_mesh(mesh) bm.free() bpy.ops.object.mode_set(mode="OBJECT")
def _get_camera_space_bounding_box(context: bpy.types.Context, camera_obj: bpy.types.Object, target_obj: bpy.types.Object) -> Bounds2D: # TODO support more than just meshes (esp. metaballs) if target_obj.type != 'MESH': raise Exception(f"Target object {target_obj} is not a mesh") # Get latest version of target object with modifiers such as armature applied depsgraph = context.evaluated_depsgraph_get() target_obj = target_obj.evaluated_get(depsgraph) m_obj_to_world = target_obj.matrix_world m_world_to_cam = camera_obj.rotation_euler.to_matrix().inverted() obj_verts = target_obj.to_mesh().vertices cam_verts = [m_world_to_cam @ (m_obj_to_world @ v.co) for v in obj_verts] return Bounds2D.from_points(cam_verts)
def obj2np(obj: bpy.types.Object, dtype: type = np.float32, apply_modifier: bool = False, frame: int = bpy.context.scene.frame_current, geo_type: str = "position", is_local: bool = False, as_homogeneous: bool = False, mode: str = "dynamic") -> np.ndarray: # Input: obj(bpy.types.Object), Output: positions or normals bpy.context.scene.frame_set(frame) if type(obj.data) == bpy.types.Mesh : if apply_modifier: depsgraph = bpy.context.evaluated_depsgraph_get() obj = obj.evaluated_get(depsgraph) mesh = obj.to_mesh() return np.array([vec2np(v.co) for v in mesh.vertices], dtype=dtype) world_matrix = world_matrix2np(obj, dtype=dtype) # (4, 4) return mesh2np(obj.data, world_matrix=world_matrix, geo_type=geo_type, dtype=dtype, is_local=is_local, frame=frame, as_homogeneous=as_homogeneous) elif type(obj.data) == bpy.types.Armature: return armature2np(obj.data, dtype=dtype, mode=mode, frame=frame) else: raise NotImplementedError( f"{type(obj.data)} is not supported with obj2np")
def cache_blur_data(rpr_context, obj: bpy.types.Object): if obj.rpr.deformation_blur and isinstance(rpr_context, RPRContext2): try: # This operation adds new mesh into bpy.data.meshes, that's why it should be removed # after usage. obj.to_mesh() could also return None for META objects. new_mesh = obj.to_mesh() log("sync", obj, new_mesh) if new_mesh: mesh.cache_blur_data(rpr_context, obj, new_mesh) return True return False finally: # it's important to clear created mesh obj.to_mesh_clear() else: mesh.cache_blur_data(rpr_context, obj)
def __init__(self, obj: bpy.types.Object, depsgraph: bpy.types.Depsgraph, ARMATURE=False, ANIMATION=False ): """Constructor that pulls geometry data from bpy""" # ## ORIG CONSTRUCTOR ## self.vertexlist = list() self.submesh_list = list() self.submesh_material = "" self.vertexcount = 0 self.attr_dict = dict() self.attr_dict["binormals"] = True self.attr_dict["colours_diffuse"] = False self.attr_dict["normals"] = True self.attr_dict["positions"] = True self.attr_dict["tangent_dimensions"] = 3 self.attr_dict["tangents"] = True self.attr_dict["texture_coords"] = 1 # ## END ## self.name = obj.data.name # obj to mesh # to_mesh(depsgraph, apply_modifiers, calc_undeformed=False) -> Mesh bpymesh = obj.to_mesh(depsgraph, True) # collect mesh vertices to ogre_types.Vertex # triangulate self.bmesh_triangulate(bpymesh) # update normals, tangents, bitangents bpymesh.calc_tangents() bpy_uvdata = bpymesh.uv_layers.active.data if bpymesh.vertex_colors.active is not None: bpy_color = bpymesh.vertex_colors.active.data else: bpy_color = None # Loop through the tris in the triangulated mesh for poly in bpymesh.polygons: # confirm that what we have is a tri. self.is_valid_tri(poly) # for tri reference tri = list() # for every vertex, create an ogre Vertex! for index in range(3): # get the index of the vertex and the loop vert_ref = poly.vertices[index] loop_ref = poly.loop_indices[index] # get the vertex coords, normals, tangent and bitangent vc = cc(bpymesh.vertices[vert_ref].co) vn = cc(bpymesh.vertices[vert_ref].normal) vt = cc(bpymesh.loops[loop_ref].tangent) # TODO: confirm this works as binormal vb = cc(bpymesh.loops[vert_ref].bitangent) uv = bpy_uvdata[loop_ref].uv if bpy_color: rgb = bpy_color[loop_ref].color rgba = [rgb[0], rgb[1], rgb[2], 1] else: rgba = [1, 1, 1, 1] # TODO: better rgba # get the bone / vertexgroup influences # TODO: confirm this works bw = [] if ARMATURE: for group in bpymesh.vertices[vert_ref].groups: if group.weight > 0.01: vg = obj.vertex_groups[group.group] bw[vg.name] = group.weight # create vertex vert = Vertex(vc, vn, uv=uv, tangent=vt, binormal=vb, rgba=rgba, boneweights=bw) # add the Vertex to our list in the Mesh, and to the tri tri.append(self.add_vertex(vert)) # for every poly (tri) add a tri to our submesh list self.add_sm_tri(tri) # we go back to our object # and get that material! self.pull_sm_material(obj) if ANIMATION: # TODO: export poses # ... and anims pass
def sync(obj_prim, obj: bpy.types.Object, mesh: bpy.types.Mesh = None, **kwargs): """ Creates pyrpr.Shape from obj.data:bpy.types.Mesh """ from .object import sdf_name if not mesh: mesh = obj.data log("sync", mesh, obj) data = MeshData.init_from_mesh(mesh, obj=obj) if not data: return stage = obj_prim.GetStage() usd_mesh = UsdGeom.Mesh.Define( stage, obj_prim.GetPath().AppendChild(Tf.MakeValidIdentifier(mesh.name))) usd_mesh.CreateDoubleSidedAttr(True) usd_mesh.CreateFaceVertexIndicesAttr(data.vertex_indices) usd_mesh.CreateFaceVertexCountsAttr(data.num_face_vertices) usd_mesh.CreateSubdivisionSchemeAttr(UsdGeom.Tokens.none) usd_mesh.SetNormalsInterpolation(UsdGeom.Tokens.faceVarying) points_attr = usd_mesh.CreatePointsAttr(data.vertices) normals_attr = usd_mesh.CreateNormalsAttr(data.normals) # here we can't just call mesh.calc_loop_triangles to update loops because Blender crashes armature = obj.find_armature() if armature and kwargs.get('is_use_animation', False): scene = kwargs.get('scene') frame_current = scene.frame_current frame_start = kwargs.get('frame_start') if kwargs.get( 'is_restrict_frames') else scene.frame_start frame_end = kwargs.get('frame_end') if kwargs.get( 'is_restrict_frames') else scene.frame_end for frame in range(frame_start, frame_end + 1): scene.frame_set(frame) new_mesh = obj.to_mesh() new_data = MeshData.init_from_mesh(new_mesh, obj=obj) points_attr.Set(new_data.vertices, frame) normals_attr.Set(new_data.normals, frame) obj.to_mesh_clear() scene.frame_set(frame_current) for name, uv_layer in data.uv_layers.items(): uv_primvar = usd_mesh.CreatePrimvar( "st", # default name, later we'll use sdf_path(name) Sdf.ValueTypeNames.TexCoord2fArray, UsdGeom.Tokens.faceVarying) uv_primvar.Set(uv_layer[0]) uv_primvar.SetIndices(Vt.IntArray.FromNumpy(uv_layer[1])) break # currently we use only first UV layer _assign_materials(obj_prim, obj.original, usd_mesh)