def bmesh_copy_from_object(ob, transform=True, triangulate=True, apply_modifiers=True): assert(ob.type == 'MESH') if apply_modifiers and ob.modifiers: import bpy me = ob.to_mesh(bpy.context.scene, True, 'PREVIEW', calc_tessface=False) bm = bmesh.new() bm.from_mesh(me) bpy.data.meshes.remove(me) del bpy else: me = ob.data if ob.mode == 'EDIT': bm_orig = bmesh.from_edit_mesh(me) bm = bm_orig.copy() else: bm = bmesh.new() bm.from_mesh(me) if transform: bm.transform(ob.matrix_world) if triangulate: bmesh.ops.triangulate(bm, faces=bm.faces) return bm
def preRender(self, element, layerIndex=None): op = self.op # <li> stands for 'layer index' li = element.li if layerIndex is None else layerIndex self.layerIndex = li if op.singleObject: if op.layered: mesh = Renderer.layerMeshes[li] obj = Renderer.layerObjects[li] materialIndices = Renderer.materialIndices[li] if not mesh: mesh = bmesh.new() Renderer.layerMeshes[li] = mesh obj = self.createBlenderObject( self.getLayerName(li, op), self.parent ) Renderer.layerObjects[li] = obj materialIndices = {} Renderer.materialIndices[li] = materialIndices self.bm = mesh self.obj = obj self.materialIndices = materialIndices else: self.bm = Renderer.bm self.obj = Renderer.obj self.materialIndices = Renderer.materialIndices else: self.obj = self.createBlenderObject( self.getName(element), self.getLayerParent() if op.layered else Renderer.parent ) self.bm = bmesh.new() self.materialIndices = {}
def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): """ Returns a transformed, triangulated copy of the mesh """ assert obj.type == 'MESH' if apply_modifiers and obj.modifiers: import bpy me = obj.to_mesh(bpy.context.scene, apply_modifiers=True, settings='PREVIEW') bm = bmesh.new() bm.from_mesh(me) bpy.data.meshes.remove(me) del bpy else: me = obj.data if obj.mode == 'EDIT': bm_orig = bmesh.from_edit_mesh(me) bm = bm_orig.copy() else: bm = bmesh.new() bm.from_mesh(me) if transform: bm.transform(obj.matrix_world) if triangulate: bmesh.ops.triangulate(bm, faces=bm.faces) return bm
def setSpriteFrame(src, dst): for vert in src.data.vertices: dst.data.vertices[vert.index].co = vert.co sbm = bmesh.new() dbm = bmesh.new() sbm.from_mesh(src.data) dbm.from_mesh(dst.data) suv_layer = sbm.loops.layers.uv.verify() duv_layer = dbm.loops.layers.uv.verify() dbm.faces.layers.tex.verify() # adjust UVs for f in dbm.faces: for l in f.loops: luv = l[duv_layer] luv.uv = sbm.faces[f.index].loops[l.index][suv_layer].uv dbm.to_mesh(dst.data) sbm.free() dbm.free() mat = src.data.materials[0] tex = mat.texture_slots[0].texture img = tex.image if len(dst.data.materials) > 0: dst.data.materials[0] = mat else: dst.data.materials.append(mat) # set uv faces image for uv_face in dst.data.uv_textures.active.data: uv_face.image = img dst['sprite'] = src.parent['name']+'|'+src['name']
def ControlPoints(ctrlnum, CtrlSeg): #_______________________________________________________ #Creates 4 point bezier curves to act as guides for fibers #Parameters: ctrlnum- the number of control curves to create #_______________________________________________________ global mat, grav, tempbm grav = 0 objects = bpy.context.selected_objects for num in range(len(objects)): i = 0 original = objects[num].data if ctrlnum > len(original.polygons): ctnm = len(original.polygons) else: ctnm = ctrlnum mat = adapt(objects[num]) vertex = [0,0,0] bm = bmesh.new() bm.from_mesh(original) newbm = bmesh.new() tempbm = bmesh.new() for fs in range(0, len(original.polygons), int(len(original.polygons) / ctnm)): bm.faces.ensure_lookup_table() f = bm.faces[fs] normal = renormal(multmat(mat, f.verts[0]),multmat(mat, f.verts[1]),multmat(mat, f.verts[2])) for z in range(3): vertex[z] = normal[z] #centerpoint coordinates vertex = [0,0,0] for va in f.verts: v = multmat(mat, va) for z in range(3): #z is the coordinate plane (x,y,or z) vertex[z] += v.co[z] for z in range(3): vertex[z] /= len(f.verts) for z in range(CtrlSeg): i += 1 v =newbm.verts.new((vertex[0] + (z * normal[0]), vertex[1] + (z * normal[1]), vertex[2] + (z * normal[2]))) for z in range(CtrlSeg-1): newbm.edges.ensure_lookup_table() newbm.verts.ensure_lookup_table() newbm.edges.new((newbm.verts[i-1-z], newbm.verts[i-2-z])) GuideMesh = bpy.data.meshes.new(original.name+"_Fiber.C") newbm.to_mesh(GuideMesh) obj = bpy.data.objects.new(name=original.name+"_Fiber.C", object_data=GuideMesh) scene = bpy.context.scene scene.objects.link(obj)
def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): """ Returns a transformed, triangulated copy of the mesh """ assert(obj.type == 'MESH') if apply_modifiers and obj.modifiers: import bpy me = obj.to_mesh(bpy.context.scene, True, 'PREVIEW', calc_tessface=False) bm = bmesh.new() bm.from_mesh(me) bpy.data.meshes.remove(me) del bpy else: me = obj.data if obj.mode == 'EDIT': bm_orig = bmesh.from_edit_mesh(me) bm = bm_orig.copy() else: bm = bmesh.new() bm.from_mesh(me) # TODO. remove all customdata layers. # would save ram if transform: bm.transform(obj.matrix_world) if triangulate: bmesh.ops.triangulate(bm, faces=bm.faces) return bm
def save_object(fw, global_matrix, scene, obj, use_mesh_modifiers): assert(obj.type == 'MESH') if use_mesh_modifiers: is_editmode = (obj.mode == 'EDIT') if is_editmode: bpy.ops.object.editmode_toggle() me = obj.to_mesh(scene, True, 'PREVIEW', calc_tessface=False) bm = bmesh.new() bm.from_mesh(me) if is_editmode: bpy.ops.object.editmode_toggle() else: me = obj.data if obj.mode == 'EDIT': bm_orig = bmesh.from_edit_mesh(me) bm = bm_orig.copy() else: bm = bmesh.new() bm.from_mesh(me) # Blender 2.74 fails triangulation if we do it after transform. # If we do it before, it's ok. bmesh.ops.triangulate(bm, faces=bm.faces) bm.transform(global_matrix * obj.matrix_world) save_bmesh(fw, bm, me.materials) bm.free()
def update_references(self): """ This method tries to update references at bmesh, when old bmesh was removed """ if self.bmesh is None: if bpy.context.edit_object is not None and \ bpy.context.edit_object.data == self.mesh: self.bmesh = bmesh.from_edit_mesh(self.mesh) self.bm_from_edit_mesh = True else: self.bmesh = bmesh.new() self.bmesh.from_mesh(self.mesh) self.bm_from_edit_mesh = False else: try: self.bmesh.verts except ReferenceError: if bpy.context.edit_object is not None and \ bpy.context.edit_object.data == self.mesh: self.bmesh = bmesh.from_edit_mesh(self.mesh) self.bm_from_edit_mesh = True else: self.bmesh = bmesh.new() self.bmesh.from_mesh(self.mesh) self.bm_from_edit_mesh = False self.clear_ID_cache()
def setMeshData(self, mesh, meshData): # clear existing mesh bmesh.new().to_mesh(mesh) if meshData.isValid(): self.setValidMeshData(mesh, meshData.vertices, meshData.edges, meshData.polygons) else: self.errorMessage = "The mesh data is invalid"
def cb_receive_layer_set_value(cls, session, node_id, layer_id, item_id, value): """ This method is called, when new value of verse layer was set """ face_layer = super(VerseFaces, cls).cb_receive_layer_set_value(session, node_id, layer_id, item_id, value) # Update mesh only in situation, when it was changed by someone else if face_layer.node.locked_by_me is False: vert_layer = face_layer.node.vertices edge_layer = face_layer.node.edges if face_layer.node.bmesh is None: face_layer.node.bmesh = bmesh.new() face_layer.node.bmesh.from_mesh(face_layer.node.mesh) face_layer.node.bm_from_edit_mesh = False else: try: face_layer.node.bmesh.faces except ReferenceError: face_layer.node.bmesh = bmesh.new() face_layer.node.bmesh.from_mesh(face_layer.node.mesh) vert_layer.id_cache = {} edge_layer.id_cache = {} face_layer.id_cache = {} _bmesh = face_layer.node.bmesh b3d_face = face_layer.find_b3d_face(item_id) # When face already exists, then remove the face if b3d_face is not None: try: _bmesh.faces.remove(b3d_face) except ReferenceError: # Face was already removed pass # Add new one if value[3] == 0: b3d_face = _bmesh.faces.new([vert_layer.b3d_vertex(vert_id) for vert_id in value[0:3]]) else: b3d_face = _bmesh.faces.new([vert_layer.b3d_vertex(vert_id) for vert_id in value]) # Try to update last face ID if face_layer.node.last_face_ID is None or \ face_layer.node.last_face_ID < item_id: face_layer.node.last_face_ID = item_id face_layer.id_cache[item_id] = b3d_face id_layer = _bmesh.faces.layers.int.get('FaceIDs') b3d_face[id_layer] = item_id # Update Blender mesh _bmesh.to_mesh(face_layer.node.mesh) face_layer.node.mesh.update() return face_layer
def project_decal_old(mesh_src, mesh_dst): if isinstance(mesh_src, str): mesh_src = bpy.data.meshes.get(mesh_src) print (mesh_src) if isinstance(mesh_dst, str): mesh_dst = bpy.data.meshes.get(mesh_dst) print (mesh_dst) if mesh_src is None or mesh_dst is None: print ("src or dst is None") return bm_src = bmesh.new() bm_src.from_mesh(mesh_src) bm_dst = bmesh.new() bm_dst.from_mesh(mesh_dst) bm_out = bmesh.new() for face_src in bm_src.faces: verts_src = [] for loop_src in face_src.loops: verts_src.append(loop_src.vert.co) for face_dst in bm_dst.faces: verts_dst = [] for loop_dst in face_dst.loops: verts_dst.append(loop_dst.vert.co) # 目前做了这个假设,所以先测试到这一步 if len(verts_src) != 4 or len(verts_dst) != 3: continue # adapt arguments && run rect = [] for co in verts_src: rect.append((co.x, co.y, co.z)) tri = [] for co in verts_dst: tri.append((co.x, co.y, co.z)) res = decal_algo.decal(rect, tri) # add verts for x, y, z in res: bm_out.verts.new((x, y, z + 0.01)) # add face (all these verts will be on the same plane, so let's blender do triangulate for us) if res: bm_out.faces.new(bm_out.verts[-len(res):]) print(len(bm_out.verts)) if len(bm_out.verts) >= 0: mesh = bpy.data.meshes.new(name="_decal") bm_out.to_mesh(mesh) obj = bpy.context.scene.objects.get("_decal") if obj is None: obj = bpy.data.objects.new("_decal", mesh) bpy.context.scene.objects.link(obj) else: obj.data = mesh
def from_object(ob, apply_modifiers=True, settings='PREVIEW', modifier_types=None, layer_name='original', add_verts_layers=True, add_edges_layers=False, add_faces_layers=False): """対象のObjectがEditModeでも正常に動作する""" if ob.mode == 'EDIT': bm = bmesh.from_edit_mesh(ob.data) else: bm = bmesh.new() bm.from_mesh(ob.data) # カスタムレイヤの追加先 def layer_add_to(bm): ls = [] if add_verts_layers: ls.append(bm.verts) if add_edges_layers: ls.append(bm.edges) if add_faces_layers: ls.append(bm.faces) return ls for elem_seq in layer_add_to(bm): layer = elem_seq.layers.int.new(layer_name) for i, elem in enumerate(elem_seq): elem[layer] = i + 1 mesh = bpy.data.meshes.new("tmp") bm.to_mesh(mesh) if ob.mode == 'EDIT': for elem_seq in layer_add_to(bm): layer = elem_seq.layers.int[layer_name] elem_seq.layers.int.remove(layer) obj = ob.copy() obj.data = mesh if apply_modifiers and modifier_types is not None: for mod in obj.modifiers: if mod.type not in modifier_types: mod.show_viewport = False dmesh = obj.to_mesh(bpy.context.scene, apply_modifiers, settings) bm = bmesh.new() bm.from_mesh(dmesh) for elem_seq in layer_add_to(bm): layer = elem_seq.layers.int[layer_name] for elem in elem_seq: elem[layer] -= 1 bpy.data.objects.remove(obj) bpy.data.meshes.remove(mesh) bpy.data.meshes.remove(dmesh) return bm
def save_object(fw, global_matrix, scene, obj, use_mesh_modifiers, use_color, color_type, use_uv, path_mode, copy_set): assert obj.type == "MESH" if use_mesh_modifiers: is_editmode = obj.mode == "EDIT" if is_editmode: bpy.ops.object.editmode_toggle() me = obj.to_mesh(scene, True, "PREVIEW", calc_tessface=False) bm = bmesh.new() bm.from_mesh(me) if is_editmode: bpy.ops.object.editmode_toggle() else: me = obj.data if obj.mode == "EDIT": bm_orig = bmesh.from_edit_mesh(me) bm = bm_orig.copy() else: bm = bmesh.new() bm.from_mesh(me) bm.transform(global_matrix * obj.matrix_world) bmesh.ops.triangulate(bm, faces=bm.faces, use_beauty=True) # default empty material_colors = [] uv_image = None if use_color: if color_type == "VERTEX": if bm.loops.layers.color.active is None: # fallback to material color_type = "MATERIAL" if color_type == "MATERIAL": if not me.materials: use_color = False else: material_colors = [ "%.2f %.2f %.2f " % (m.diffuse_color[:] if m else (1.0, 1.0, 1.0)) for m in me.materials ] assert color_type in {"VERTEX", "MATERIAL"} if use_uv: if bm.loops.layers.uv.active is None: use_uv = False uv_image = object_utils.object_image_guess(obj, bm=bm) if uv_image is None: use_uv = False save_bmesh(fw, bm, use_color, color_type, material_colors, use_uv, uv_image, path_mode, copy_set) bm.free()
def cb_receive_layer_set_value(cls, session, node_id, layer_id, item_id, value): """ This method is called, when new value of verse layer was set """ edge_layer = super(VerseEdges, cls).cb_receive_layer_set_value(session, node_id, layer_id, item_id, value) # Update mesh only in situation, when it was changed by someone else if edge_layer.node.locked_by_me is False: vert_layer = edge_layer.node.vertices face_layer = edge_layer.node.quads if edge_layer.node.bmesh is None: edge_layer.node.bmesh = bmesh.new() edge_layer.node.bmesh.from_mesh(edge_layer.node.mesh) edge_layer.node.bm_from_edit_mesh = False else: try: edge_layer.node.bmesh.edges except ReferenceError: edge_layer.node.bmesh = bmesh.new() edge_layer.node.bmesh.from_mesh(edge_layer.node.mesh) vert_layer.id_cache = {} edge_layer.id_cache = {} face_layer.id_cache = {} _bmesh = edge_layer.node.bmesh b3d_edge = edge_layer.b3d_edge(item_id) # Try to update last vertex ID if edge_layer.node.last_edge_ID is None or \ edge_layer.node.last_edge_ID < item_id: edge_layer.node.last_edge_ID = item_id # Does edge with same id exist? if b3d_edge is not None: # Delete edge try: _bmesh.edges.remove(b3d_edge) except ReferenceError: # Edge was already removed pass # Create new edge b3d_edge = _bmesh.edges.new([vert_layer.b3d_vertex(vert_id) for vert_id in value]) edge_layer.id_cache[item_id] = b3d_edge id_layer = _bmesh.edges.layers.int.get('EdgeIDs') b3d_edge[id_layer] = item_id # Update Blender mesh _bmesh.to_mesh(edge_layer.node.mesh) edge_layer.node.mesh.update() return edge_layer
def execute(self, meshData): try: bm = getBMeshFromMeshData(meshData) self.errorMessage = "" except IndexError as e: bm = bmesh.new() self.errorMessage = "Missing vertices" except ValueError as e: bm = bmesh.new() self.errorMessage = "Multiple identical edges or polygons" return bm
def setMeshData(self, mesh, meshData): # clear existing mesh bmesh.new().to_mesh(mesh) isValidData = meshData.isValid( checkTupleLengths = self.checkTupleLengths, checkIndices = self.checkIndices) if isValidData: mesh.from_pydata(meshData.vertices, meshData.edges, meshData.polygons) else: self.errorMessage = "The mesh data is invalid"
def get_sceneColldersBVH(forObj, allowSelf): matrix_world_inv = None if forObj is not None: matrix_world = forObj.matrix_world matrix_world_inv = matrix_world.inverted() objs2checkAll = [obj for obj in bpy.data.objects] bvh2collides = [] for obj in objs2checkAll: if allowSelf == False and forObj is not None and obj.name == forObj.name: continue if obj.hide == True: continue isColl = False if "_collider" in obj.name: isColl = True for md in obj.modifiers: if md.type == 'COLLISION': isColl = True break if isColl: print("- Collider found:",obj.name) bm_collide = None if obj.type != 'MESH': sel_mesh = None try: sel_mesh = obj.to_mesh(bpy.context.scene, True, 'PREVIEW') except: pass if sel_mesh is not None: bm_collide = bmesh.new() bm_collide.from_mesh(sel_mesh) bpy.data.meshes.remove(sel_mesh) else: bm_collide = bmesh.new() bm_collide.from_object(obj, bpy.context.scene) if bm_collide is not None: hiddenFaces = [] for bm2f in bm_collide.faces: if bm2f.hide and bm2f not in hiddenFaces: hiddenFaces.append(bm2f) if len(hiddenFaces)>0: bmesh.ops.delete(bm_collide,geom=hiddenFaces,context=5) bm_collide.transform(obj.matrix_world) if matrix_world_inv is not None: bm_collide.transform(matrix_world_inv) bmesh.ops.recalc_face_normals(bm_collide, faces=bm_collide.faces) #??? strange results!!! bm_collide.verts.ensure_lookup_table() bm_collide.faces.ensure_lookup_table() bm_collide.verts.index_update() bvh_collide = BVHTree.FromBMesh(bm_collide, epsilon = kRaycastEpsilonCCL) bm_collide.free() bvh2collides.append(bvh_collide) return bvh2collides
def ConvertBackTo3D(self): bmUVS = bmesh.new() bm3DS = bmesh.new() bm3DD = bmesh.new() bmUVS.from_mesh(self.oMeshUVSO.data) bm3DS.from_mesh(self.oMesh3DSO.data) #=== Create new 3D-domain mesh so we can cut with a flattened mesh that doesn't move with user morphs === oMesh3DDD = bpy.data.meshes.new("CMeshUV-3DD") ###TODO#16 self.oMesh3DDO = bpy.data.objects.new(oMesh3DDD.name, oMesh3DDD) bpy.context.scene.objects.link(self.oMesh3DDO) SetParent(self.oMesh3DDO.name, G.C_NodeFolder_Game) bm3DD.from_mesh(self.oMesh3DDO.data) #=== Create the verts in the destination 3D mesh === aMapVertsUV2Verts3DD = {} bm3DS.verts.ensure_lookup_table() oLayVert3D = bmUVS.verts.layers.int[G.C_DataLayer_VertsSrc] for oVertUV in bmUVS.verts: nVert3DS = oVertUV[oLayVert3D] if nVert3DS >= G.C_OffsetVertIDs: oVert3DS = bm3DS.verts[nVert3DS - G.C_OffsetVertIDs] vecVert3D = oVert3DS.co else: # New vert that was created by Boolean cuts... we must interpolate its 3D position vecLoc, nPoly, nDist = self.oTreeKD.find(oVertUV.co) ###LEARN: How to extract multiple arguments out oPoly = self.oMesh3DSO.data.polygons[nPoly] aUV = [self.oMesh3DSO.data.uv_layers.active.data[nLoopIndex].uv for nLoopIndex in oPoly.loop_indices] #print("Loc search = ", vecLoc, nPoly, nDist) vecUV0 = Vector((aUV[0].x, 0, aUV[0].y)) # Expand 2D UV coordinate into a 3D vector with z = 0 so we can invoke barycentric_transform() below vecUV1 = Vector((aUV[1].x, 0, aUV[1].y)) vecUV2 = Vector((aUV[2].x, 0, aUV[2].y)) vecPoly0 = self.oMesh3DSO.data.vertices[oPoly.vertices[0]].co vecPoly1 = self.oMesh3DSO.data.vertices[oPoly.vertices[1]].co vecPoly2 = self.oMesh3DSO.data.vertices[oPoly.vertices[2]].co vecVert3D = geometry.barycentric_transform(oVertUV.co, vecUV0, vecUV1, vecUV2, vecPoly0, vecPoly1, vecPoly2) ###LEARN: How to convert from a point in one triangle space to another triangle space aMapVertsUV2Verts3DD[oVertUV.index] = len(bm3DD.verts) bm3DD.verts.new(vecVert3D) ###TODO#16: Duplicate verts along seams?? #=== Create the polygons in the destination 3D mesh === bm3DD.verts.ensure_lookup_table() for oFace in bmUVS.faces: aVertsNewFace3D = [] for oVert in oFace.verts: nVert3DD = aMapVertsUV2Verts3DD[oVert.index] oVert3DD = bm3DD.verts[nVert3DD] aVertsNewFace3D.append(oVert3DD) # Traverse from UV-domain to 3D domain using map we created in loop above bm3DD.faces.new(aVertsNewFace3D) bm3DD.to_mesh(self.oMesh3DDO.data)
def cb_receive_layer_unset_value(cls, session, node_id, layer_id, item_id): """ This method is called, when some vertex was deleted """ edge_layer = super(VerseEdges, cls).cb_receive_layer_unset_value(session, node_id, layer_id, item_id) # Update mesh only in situation, when it was changed by someone else if edge_layer.node.locked_by_me is False: vert_layer = edge_layer.node.vertices face_layer = edge_layer.node.quads if edge_layer.node.bmesh is None: edge_layer.node.bmesh = bmesh.new() edge_layer.node.bmesh.from_mesh(edge_layer.node.mesh) edge_layer.node.bm_from_edit_mesh = False else: try: edge_layer.node.bmesh.edges except ReferenceError: edge_layer.node.bmesh = bmesh.new() edge_layer.node.bmesh.from_mesh(edge_layer.node.mesh) vert_layer.id_cache = {} edge_layer.id_cache = {} face_layer.id_cache = {} _bmesh = edge_layer.node.bmesh b3d_edge = edge_layer.b3d_edge(item_id) # Try to update last vertex ID if edge_layer.node.last_vert_ID is None or \ edge_layer.node.last_edge_ID < item_id: edge_layer.node.last_edge_ID = item_id if b3d_edge is not None: # Delete edge try: _bmesh.edges.remove(b3d_edge) except ReferenceError: # Edge was already removed? edge_layer.id_cache.pop(item_id) else: # Update Blender mesh _bmesh.to_mesh(edge_layer.node.mesh) edge_layer.node.mesh.update() edge_layer.id_cache.pop(item_id) return edge_layer
def process(self): objm = bpy.data.objects[self.object_ref].data objm.update() if not objm.vertex_colors: objm.vertex_colors.new(name='Sv_VColor') if self.vertex_color not in objm.vertex_colors: return ovgs = objm.vertex_colors.get(self.vertex_color) Ind, Col = self.inputs if Col.is_linked: sm, colors = self.mode, Col.sv_get()[0] idxs = Ind.sv_get()[0] if Ind.is_linked else [i.index for i in getattr(objm,sm)] idxs, colors = second_as_first_cycle(idxs, colors) bm = bmesh.new() bm.from_mesh(objm) if self.clear: for i in ovgs.data: i.color = self.clear_c if sm == 'vertices': bv = bm.verts[:] for i, i2 in zip(idxs, colors): for i in bv[i].link_loops: ovgs.data[i.index].color = i2 elif sm == 'polygons': bf = bm.faces[:] for i, i2 in zip(idxs, colors): for i in bf[i].loops: ovgs.data[i.index].color = i2 bm.free() if self.outputs["OutColor"].is_linked: out = [] sm= self.mode bm = bmesh.new() bm.from_mesh(objm) if sm == 'vertices': #output one color per vertex for v in bm.verts[:]: c = ovgs.data[v.link_loops[0].index].color out.append(list(c)) elif sm == 'polygons': #output one color per face for f in bm.faces[:]: c = ovgs.data[f.loops[0].index].color out.append(list(c)) self.outputs["OutColor"].sv_set([out]) bm.free()
def joinBoundaryVertexNormals(self, context, destobjs, INFL=0.0, MAXDIST=0.01): ''' Average smoothing over boundary verts, usually same-location. destobjs = list, generally context.selected_objects INFL = float, influence strength MAXDIST = float, distance to influence... probably not necessary ''' bms = {} bmsrc = bmesh.new() scene = context.scene for obj in destobjs: # These type != 'MESH' checks could be alleviated by removing # non-mesh objects in execute(), but, may wish to # support non-mesh objects one day if obj.type != 'MESH': continue bms[obj.name] = bmesh.new() bm = bms[obj.name] bm.from_mesh(obj.to_mesh(scene, False, 'PREVIEW')) bm.transform(obj.matrix_world) destverts = bm.verts for otherobj in destobjs: if otherobj.type != 'MESH' or obj == otherobj: continue gatherSourceVerts(bmsrc, otherobj, scene, 'ONLY') sourceverts = bmsrc.verts for vert in destverts: near = nearestVertexNormal(sourceverts, vert, MAXDIST) if near: offset = near * INFL vert.normal = (vert.normal + offset) * 0.5 vert.normal.normalize() bmsrc.clear() for name in bms: # Everything's been modified by everything else's original state, # time to apply the modified data to the original objects bm = bms[name] for obj in destobjs: if obj.name == name: bm.transform(obj.matrix_world.inverted()) bm.to_mesh(obj.data) bm.free() bmsrc.free()
def get_snap_target(context, axis_index): bm = bmesh.new() bm.from_mesh(context.object.data) # bm = bmesh.from_edit_mesh(context.object.data) snap_target = None if not bm.select_history: bm.free() return None vert = bm.select_history[-1] if isinstance(vert, bmesh.types.BMVert): snap_target = vert.co[axis_index] if snap_target == None: face = bm.select_history[-1] if isinstance(face, bmesh.types.BMFace): center = face.calc_center_median() snap_target = center[axis_index] if snap_target == None: edge = bm.select_history[-1] if isinstance(edge, bmesh.types.BMEdge): pos1 = edge.verts[0].co pos2 = edge.verts[1].co midpoint = pos1.lerp(pos2, 0.5) snap_target = midpoint[axis_index] bm.free() return snap_target
def draw(self, context): ob = bpy.context.object if ob is None: return if ob.type != 'MESH': raise TypeError("Active object is not a Mesh") me = ob.data if me.is_editmode: # Gain direct access to the mesh bm = bmesh.from_edit_mesh(me) else: # Create a bmesh from mesh # (won't affect mesh, unless explicitly written back) bm = bmesh.new() bm.from_mesh(me) # Get active face face = bm.faces.active if face is None: return arxFaceType = bm.faces.layers.int.get('arx_facetype') arxTransVal = bm.faces.layers.float.get('arx_transval') faceType = PolyTypeFlag() faceType.asUInt = face[arxFaceType] transval = face[arxTransVal] obj = bpy.context.active_object layout = self.layout layout.label(text="transval: " + str(transval)) layout.label(text="POLY_NO_SHADOW: " + str(faceType.POLY_NO_SHADOW)) layout.label(text="POLY_DOUBLESIDED: " + str(faceType.POLY_DOUBLESIDED)) layout.label(text="POLY_TRANS: " + str(faceType.POLY_TRANS)) layout.label(text="POLY_WATER: " + str(faceType.POLY_WATER)) layout.label(text="POLY_GLOW: " + str(faceType.POLY_GLOW)) layout.label(text="POLY_IGNORE: " + str(faceType.POLY_IGNORE)) layout.label(text="POLY_QUAD: " + str(faceType.POLY_QUAD)) layout.label(text="POLY_METAL: " + str(faceType.POLY_METAL)) layout.label(text="POLY_HIDE: " + str(faceType.POLY_HIDE)) layout.label(text="POLY_STONE: " + str(faceType.POLY_STONE)) layout.label(text="POLY_WOOD: " + str(faceType.POLY_WOOD)) layout.label(text="POLY_GRAVEL: " + str(faceType.POLY_GRAVEL)) layout.label(text="POLY_EARTH: " + str(faceType.POLY_EARTH)) layout.label(text="POLY_NOCOL: " + str(faceType.POLY_NOCOL)) layout.label(text="POLY_LAVA: " + str(faceType.POLY_LAVA)) layout.label(text="POLY_CLIMB: " + str(faceType.POLY_CLIMB)) layout.label(text="POLY_FALL: " + str(faceType.POLY_FALL)) layout.label(text="POLY_NOPATH: " + str(faceType.POLY_NOPATH)) layout.label(text="POLY_NODRAW: " + str(faceType.POLY_NODRAW)) layout.label(text="POLY_PRECISE_PATH: " + str(faceType.POLY_PRECISE_PATH)) layout.label(text="POLY_LATE_MIP: " + str(faceType.POLY_LATE_MIP))
def draw_dashed_line(mesh, start, end): '''Draw a dashed line.''' # Create a bmesh representation bm = bmesh.new() # create an empty bmesh bm.from_mesh(mesh) # fill it with the above mesh # modify the mesh here w = 0.04 step = w * (end - start).normalized() n = len(bm.verts) for i in range(int(1 + 0.5 * (end - start).length / w)): a = start + 2 * i * step b = a + step if (b - end).length < step.length: b = end bm.verts.new(a) bm.verts.new(b) # use before accessing bm.verts[] with blender 2.73 if hasattr(bm.verts, "ensure_lookup_table"): bm.verts.ensure_lookup_table() bm.edges.new([bm.verts[n + 2 * i], bm.verts[n + 2 * i + 1]]) # write the bmesh back to the mesh bm.to_mesh(mesh) mesh.update()
def mesh_triangulate(me): import bmesh bm = bmesh.new() bm.from_mesh(me) bmesh.ops.triangulate(bm, faces=bm.faces)#, use_beauty=False) bm.to_mesh(me) bm.free()
def mesh_triangulate(mesh): import bmesh bm = bmesh.new() bm.from_mesh(mesh) bmesh.ops.triangulate(bm, faces=bm.faces) bm.to_mesh(mesh) bm.free()
def do_export(fileName, correctionMatrix): mats = [m for m in bpy.data.materials if any([m in [ms.material for ms in o.material_slots] for o in bpy.data.objects])] objs = [o for o in bpy.data.objects if o.type == 'MESH' and not o.hide] if not objs: return ("Nothing to export.",{'CANCELLED'}) groups = prep_groups(objs) actions = [( o.name, (correctionMatrix * o.matrix_world.to_translation())[:]) for o in bpy.data.objects if o.type == 'EMPTY' and o.parent in objs] bm = bmesh.new() for o in objs: bm = append_object(bm, o, mats, groups) bm.transform(correctionMatrix) bm.normal_update() model = Model(bm, [m.name for m in mats], groups, actions) bm.free() f = open(fileName, 'w+b') f.write(build_ftl(model)) f.close() msg = "File \"" + fileName + "\" written successfully." result = {'FINISHED'} return (msg,result)
def draw_circle(mesh, numpoints, radius, matrix): '''Draw a circle.''' # bmesh repr. bm = bmesh.new() bm.from_mesh(mesh) # get the number of verts already in the mesh n = len(bm.verts) # make the verts for i in range(numpoints): angle = 2.0 * math.pi * i / numpoints v = Vector((radius * math.cos(angle), radius * math.sin(angle), 0)) bm.verts.new((v * matrix)) # use before accessing bm.verts[] with blender 2.73 if hasattr(bm.verts, "ensure_lookup_table"): bm.verts.ensure_lookup_table() # make the edges for i in range(numpoints): i1 = (i + 1) % numpoints bm.edges.new([bm.verts[n + i], bm.verts[n + i1]]) # write the bmesh back to the mesh bm.to_mesh(mesh) mesh.update()
def add_mesh(meshes, m): mesh = meshes.add() mesh.name = m.name mesh.type = 'gregory' bm = bmesh.new() bm.from_mesh(m) positions = [] for f in bm.faces: fs = [f] if is_border_face(f): continue if len(f.loops) != 4: continue # Throw away #_,fs = local_subdivide(f) for g in fs: P = make_patch(g) positions += P mesh.init('positions', len(positions)*3) for i,pos in enumerate(positions): mesh.positions[i*3+0] = pos.x mesh.positions[i*3+1] = pos.y mesh.positions[i*3+2] = pos.z
def execute(self, context): A = 6.283185307179586476925286766559 / 3 verts = [(sin(A * 1), 0.0, cos(A * 1)), (sin(A * 2), 0.0, cos(A * 2)), (sin(A * 3), 0.0, cos(A * 3)), ] faces = [(0, 1, 2)] mesh = bpy.data.meshes.new("Cube") bm = bmesh.new() for v_co in verts: bm.verts.new(v_co) for f_idx in faces: bm.faces.new([bm.verts[i] for i in f_idx]) bm.to_mesh(mesh) mesh.update() object_utils.object_data_add(context, mesh) return{'FINISHED'}
def add_solid(self, solid): ''' Prune faces based on material names. ''' # TODO: have this list be part of the settings! textures_to_ignore = {'NULL', 'AAATRIGGER', 'CLIP', 'SKY', '{BLUE'} faces = list( filter(lambda f: f.texture_name not in textures_to_ignore, solid.faces)) if len(faces) == 0: return None mesh_name = 'Solid.000' mesh = bpy.data.meshes.new(mesh_name) mesh_object = bpy.data.objects.new(mesh_name, mesh) ''' Create or reuse materials ''' textures = [] for f in faces: material = self.load_material(f.texture_name) if f.texture_name not in textures: textures.append(f.texture_name) mesh.materials.append(material) bm = bmesh.new() vertex_offset = 0 for f in faces: for vertex in f.vertices: bm.verts.new(tuple(vertex)) bm.verts.ensure_lookup_table() # The face order is reversed because of differences in winding order face = reversed( [bm.verts[vertex_offset + x] for x in range(len(f.vertices))]) bmface = bm.faces.new(face) bmface.material_index = textures.index(f.texture_name) vertex_offset += len(f.vertices) bm.to_mesh(mesh) collection = self.get_collection_for_solid(solid) collection.objects.link(mesh_object) default_texture_size = 256, 256 if self.should_import_textures: ''' Assign texture coordinates ''' uv_layer = mesh.uv_layers.new() j = 0 for face_index, face in enumerate(faces): try: texture_size = self.get_texture_size(face.texture_name) except LookupError: # If we don't have the texture, just assume a texture size of 256x256 texture_size = default_texture_size uvs = convert_rmf_face_texture_coordinates_to_uvs( face, texture_size) # NOTE: the UV order has to be reversed to match the reversed vertices due to winding order for uv in reversed(uvs): uv[1] = -uv[1] uv_layer.data[j].uv = uv.tolist() j += 1 return mesh_object
def cycle(ot, context, event, index=1, custom=None): global current_index global sum_index preference = addon.preference() bc = context.window_manager.bc bc.lattice.hide_set(False) original_active = context.active_object context.view_layer.objects.active = bc.shape bc.shape.select_set(True) matrix = bc.shape.matrix_world.copy() dimension = Vector(bc.shape.dimensions) if not custom and not bc.shape.bc.applied and not bc.shape.bc.applied_cycle: bc.shape.bc.applied_cycle = True keep_modifiers = [ type for type in ['ARRAY', 'BEVEL', 'SOLIDIFY', 'SCREW', 'MIRROR'] if getattr(preference.behavior, F'keep_{type.lower()}') ] modifier.apply(obj=bc.shape, exclude=[ mod for mod in bc.shape.modifiers if mod.type in keep_modifiers ]) for obj in ot.datablock['targets']: modifier.shape_bool(ot, obj).object = None for obj in ot.datablock['slices']: modifier.shape_bool(ot, obj).object = None objects = [] for obj in bc.collection.objects: holdout = obj.bc.applied_cycle and not ( sum_index > len(bc.collection.objects) - 1) or obj.bc.copy if obj.type == 'MESH' and obj != bc.shape and not holdout: objects.append(obj) obj = None if not custom: next_index = current_index + index if next_index > len(objects) - 1: next_index = 0 elif next_index < -1: next_index = len(objects) - 1 current_index = next_index sum_index += 1 if objects: obj = objects[next_index if next_index < len(objects) - 1 else 0] else: return del objects if bc.shape.bc.copy: bpy.data.objects.remove(bc.shape) obj = obj if obj else custom if custom: bpy.data.objects.remove(bc.shape) bc.shape = None bc.shape = obj.copy() bc.collection.objects.link(bc.shape) context.view_layer.objects.active = bc.shape bc.shape.bc.copy = True bc.shape.data = obj.data.copy() bc.shape.data.use_auto_smooth = True del obj modifier.apply( bc.shape, exclude=[mod for mod in bc.shape.modifiers if mod.type == 'BEVEL']) scale = bc.shape.matrix_world.to_scale() bc.shape.data.transform(Matrix.Scale(scale.x, 4, Vector((1, 0, 0)))) bc.shape.data.transform(Matrix.Scale(scale.y, 4, Vector((0, 1, 0)))) bc.shape.data.transform(Matrix.Scale(scale.z, 4, Vector((0, 0, 1)))) bc.shape.scale = Vector((1, 1, 1)) bc.shape.location = Vector() bc.shape.rotation_euler = Vector() center = 0.125 * sum( (Vector(point) for point in bc.shape.bound_box), Vector()) bc.shape.data.transform(Matrix.Translation(-center)) scale_x = 1 / bc.shape.dimensions[0] scale_y = 1 / bc.shape.dimensions[1] scale_z = 1 / bc.shape.dimensions[2] if bc.shape.bc.shape and len(ot.datablock['targets']): scale_x = -scale_x if not bc.shape.bc.shape and ot.shape_type == 'CUSTOM': scale_y = -scale_y bc.shape.data.transform(Matrix.Scale(scale_x, 4, Vector((1, 0, 0)))) bc.shape.data.transform(Matrix.Scale(scale_y, 4, Vector((0, 1, 0)))) bc.shape.data.transform(Matrix.Scale(-scale_z, 4, Vector((0, 0, 1)))) bm = bmesh.new() bm.from_mesh(bc.shape.data) bm.faces.ensure_lookup_table() bmesh.ops.recalc_face_normals(bm, faces=bm.faces) for f in bm.faces: f.smooth = True bm.to_mesh(bc.shape.data) bm.free() for obj in ot.datablock['targets']: for mod in obj.modifiers: if mod.type == 'BOOLEAN' and not mod.object: mod.object = bc.shape for obj in ot.datablock['slices']: for mod in obj.modifiers: if mod.type == 'BOOLEAN' and not mod.object: mod.object = bc.shape bc.lattice.matrix_world = Matrix() points = [Vector(point.co_deform) for point in bc.lattice.data.points] bc.lattice.data.points[0].co_deform = Vector((-0.5, -0.5, -0.5)) bc.lattice.data.points[1].co_deform = Vector((0.5, -0.5, -0.5)) bc.lattice.data.points[2].co_deform = Vector((-0.5, 0.5, -0.5)) bc.lattice.data.points[3].co_deform = Vector((0.5, 0.5, -0.5)) bc.lattice.data.points[4].co_deform = Vector((-0.5, -0.5, 0.5)) bc.lattice.data.points[5].co_deform = Vector((0.5, -0.5, 0.5)) bc.lattice.data.points[6].co_deform = Vector((-0.5, 0.5, 0.5)) bc.lattice.data.points[7].co_deform = Vector((0.5, 0.5, 0.5)) mod = bc.shape.modifiers.new(name='Lattice', type='LATTICE') mod.object = bc.lattice modifier.sort(ot, bc.shape, force=True) bc.lattice.data.transform(Matrix.Translation(Vector((0, 0, -0.5)))) if ot.origin == 'CORNER': bc.lattice.data.transform(Matrix.Translation(Vector((0.5, 0.5, 0)))) for pair in zip(points, bc.lattice.data.points): pair[1].co_deform = pair[0] bc.shape.display_type = 'WIRE' bc.lattice.matrix_world = matrix bc.shape.matrix_world = matrix bc.shape.hide_set(True) bc.lattice.hide_set(True) context.view_layer.objects.active = original_active
def apply(self): if self.name is None: Reporter.fatal("Missing nametag in model") if self.shapes is None: Reporter.fatal("Missing shapes in model {name}", name=self.name) if self.obj_scale is None: Reporter.warning( "Missing GlScale in model {name}, assuming Identity", name=self.name) if self.uv_scale is None: Reporter.warning( "Missing texture-size in model {name}. Computing from texture anyway", name=self.name) elif self.img is not None and ( self.uv_scale[0][0] != 1 / self.img.size[0] or self.uv_scale[1][1] != 1 / self.img.size[1]): Reporter.warning( "In Model {name}: Texture is ({x}, {y}) but .tcn file said ({x_xml}, {y_xml})", name=self.name, x=self.img.size[0], y=self.img.size[1], x_xml=self.uv_scale[0], y_xml=self.uv_scale[1]) # make mesh mesh = bpy.data.meshes.new(self.name) bm = bmesh.new() uv = bm.loops.layers.uv.new('UVLayer') tex = bm.faces.layers.tex.new('UVLayer') mc_loop = bm.faces.layers.int.new('MCRenderGroupIndex') for shape_nbr, shape in enumerate(self.shapes): # points is a list of lists of points (representing a list of # faces) points, idx_uvs = shape.apply(True) bpoints = [] for p in points: co = self.obj_scale * p[0] norm = self.obj_scale * p[1] co.y = 24 - co.y co = Matrix( ((-1 / 16, 0, 0), (0, 0, -1 / 16), (0, 1 / 16, 0))) * co norm = Matrix(((-1, 0, 0), (0, 0, -1), (0, 1, 0))) * norm bp = bm.verts.new(co) bp.normal = norm bpoints.append(bp) for face in idx_uvs: bface = bm.faces.new([bpoints[l[0]] for l in face]) for loop, (_, uvco) in zip(bface.loops, face): uvco = self.uv_scale * uvco uvco[1] = 1 - uvco[1] loop[uv].uv = uvco bface[tex].image = self.img if mc_loop is not None: bface[mc_loop] = shape_nbr + 1 bm.verts.index_update() bm.faces.index_update() bm.to_mesh(mesh) # make obj obj = bpy.data.objects.new(self.name, mesh) props = mesh.mcprops props.name = self.name props.uv_layer = uv.name if self.img is not None: props.default_group.image = self.img.name for shape in self.shapes: group = props.render_groups.add() group.name = shape.name if self.img is not None: group.image = self.img.name return obj
def get_bm(self, obj): bm = bmesh.new() bm.from_mesh(obj.data) bm.normal_update() bm.verts.ensure_lookup_table() return bm
def execute(self, context): obj = context.active_object bpy.context.space_data.pivot_point = 'INDIVIDUAL_ORIGINS' bpy.ops.object.mode_set(mode='OBJECT') if len(bpy.context.selected_objects) == 2: first_obj = bpy.context.active_object obj_a, obj_b = context.selected_objects second_obj = obj_a if obj_b == first_obj else obj_b ### origin to top for i in range(self.set_plus_z): # active: second bpy.context.scene.objects.active = bpy.data.objects[ second_obj.name] #["YourName"] bpy.data.objects[second_obj.name].select = True bpy.ops.tp_ops.origin_plus_z() # active: first bpy.context.scene.objects.active = bpy.data.objects[ first_obj.name] bpy.data.objects[first_obj.name].select = True ### origin to bottom for i in range(self.set_minus_z): # active: second bpy.context.scene.objects.active = bpy.data.objects[ second_obj.name] bpy.data.objects[second_obj.name].select = True bpy.ops.tp_ops.origin_minus_z() # active: first bpy.context.scene.objects.active = bpy.data.objects[ first_obj.name] bpy.data.objects[first_obj.name].select = True # active: first bpy.data.objects[second_obj.name].select = False if context.mode == 'EDIT_MESH': print(self) self.report({'INFO'}, "need a source and a target") else: bpy.ops.object.duplicate_move() bpy.context.object.name = "Dummy" bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM') bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) copy_cursor = bpy.context.scene.cursor_location.copy() bm = bmesh.new() bm.from_mesh(obj.data) selected_faces = [f for f in bm.faces if f.select] for face in selected_faces: face_location = face.calc_center_median() loc_world_space = obj.matrix_world * Vector(face_location) z = Vector((0, 0, 1)) angle = face.normal.angle(z) axis = z.cross(face.normal) bpy.ops.object.select_all(action='DESELECT') # active: second bpy.context.scene.objects.active = bpy.data.objects[ second_obj.name] bpy.data.objects[second_obj.name].select = True for i in range(self.dupli_linked): bpy.ops.object.duplicate_move_linked( OBJECT_OT_duplicate={ "linked": True, "mode": 'TRANSLATION' }) for i in range(self.dupli): bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={ "linked": False, "mode": 'TRANSLATION' }) bpy.context.scene.cursor_location = loc_world_space bpy.ops.view3d.snap_selected_to_cursor() bpy.ops.transform.rotate(value=angle, axis=axis) bm.to_mesh(obj.data) bm.free() bpy.context.scene.cursor_location = copy_cursor bpy.ops.object.select_all(action='DESELECT') # active: dummy bpy.context.scene.objects.active = bpy.data.objects["Dummy"] bpy.data.objects["Dummy"].select = True bpy.ops.object.delete(use_global=False) #bpy.ops.object.select_all(action='DESELECT') for i in range(self.set_edit_source): # active: second bpy.context.scene.objects.active = bpy.data.objects[ second_obj.name] bpy.data.objects[second_obj.name].select = True bpy.ops.object.mode_set(mode='EDIT') for i in range(self.set_edit_target): # active: first bpy.context.scene.objects.active = bpy.data.objects[ first_obj.name] bpy.data.objects[first_obj.name].select = True bpy.ops.object.mode_set(mode='EDIT') print(self) self.report({'INFO'}, "Editmode") else: print(self) self.report({'INFO'}, "need a source and a target") return {"FINISHED"}
def cell_fracture_boolean(context, collection, obj, objects, use_debug_bool=False, clean=True, use_island_split=False, use_interior_hide=False, use_debug_redraw=False, level=0, remove_doubles=True ): objects_boolean = [] scene = context.scene view_layer = context.view_layer if use_interior_hide and level == 0: # only set for level 0 obj.data.polygons.foreach_set("hide", [False] * len(obj.data.polygons)) for obj_cell in objects: mod = obj_cell.modifiers.new(name="Boolean", type='BOOLEAN') mod.object = obj mod.operation = 'INTERSECT' if not use_debug_bool: if use_interior_hide: obj_cell.data.polygons.foreach_set("hide", [True] * len(obj_cell.data.polygons)) # Calculates all booleans at once (faster). depsgraph = context.evaluated_depsgraph_get() for obj_cell in objects: if not use_debug_bool: obj_cell_eval = obj_cell.evaluated_get(depsgraph) mesh_new = bpy.data.meshes.new_from_object(obj_cell_eval) mesh_old = obj_cell.data obj_cell.data = mesh_new obj_cell.modifiers.remove(obj_cell.modifiers[-1]) # remove if not valid if not mesh_old.users: bpy.data.meshes.remove(mesh_old) if not mesh_new.vertices: collection.objects.unlink(obj_cell) if not obj_cell.users: bpy.data.objects.remove(obj_cell) obj_cell = None if not mesh_new.users: bpy.data.meshes.remove(mesh_new) mesh_new = None # avoid unneeded bmesh re-conversion if mesh_new is not None: bm = None if clean: if bm is None: # ok this will always be true for now... bm = bmesh.new() bm.from_mesh(mesh_new) bm.normal_update() try: bmesh.ops.dissolve_limit(bm, verts=bm.verts, edges=bm.edges, angle_limit=0.001) except RuntimeError: import traceback traceback.print_exc() if remove_doubles: if bm is None: bm = bmesh.new() bm.from_mesh(mesh_new) bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.005) if bm is not None: bm.to_mesh(mesh_new) bm.free() del mesh_new del mesh_old if obj_cell is not None: objects_boolean.append(obj_cell) if use_debug_redraw: _redraw_yasiamevil() if (not use_debug_bool) and use_island_split: # this is ugly and Im not proud of this - campbell for ob in view_layer.objects: ob.select_set(False) for obj_cell in objects_boolean: obj_cell.select_set(True) bpy.ops.mesh.separate(type='LOOSE') objects_boolean[:] = [obj_cell for obj_cell in view_layer.objects if obj_cell.select_get()] context.view_layer.update() return objects_boolean
def execute(self, context): if (self.filepath.endswith(".vmf")): print("filepath=" + self.filepath) map = vmf.ValveMap() for ob in bpy.data.objects: if ob.type == 'MESH': mesh = ob.to_mesh() bm = bmesh.new() bm.from_mesh(mesh) # data = bmesh.ops.triangulate(bm, faces=bm.faces) edges = bm.edges #data['edges'] faces = bm.faces #data['faces'] blk = Solid() for f in faces: mat = bpy.data.materials[f.material_index + 1] verts = f.verts pos = [] norms = [] for v in f.verts: pos.append(ob.matrix_world @ v.co) norms.append(ob.matrix_world @ v.normal) if len(verts) >= 3: scale = 102.4 vert0 = Vertex(pos[0].x * scale, pos[0].y * scale, pos[0].z * scale) vert1 = Vertex(pos[1].x * scale, pos[1].y * scale, pos[1].z * scale) vert2 = Vertex(pos[2].x * scale, pos[2].y * scale, pos[2].z * scale) plane = Plane(vert2, vert1, vert0) side = Side(plane, mat.name) vertx = VmfVertex() vertx.properties["count"] = len(pos) for i in range(0, len(pos)): p = -i + len(pos) - 1 vertx.properties["vertex" + str(p)] = Vertex( pos[i].x * scale, pos[i].y * scale, pos[i].z * scale) # vertx.properties["vertex0"] = vert2 # vertx.properties["vertex1"] = vert1 # vertx.properties["vertex2"] = vert0 side.children.append(vertx) # side.uaxis, side.vaxis = plane.sensible_axes() # side.uaxis, side.vaxis = self.axis_calc(plane) matrix = ob.matrix_world matrix2 = ob.matrix_world.inverted() ## world space d = f.normal.copy() d.normalize() d = mathutils.Vector( (abs(d.x), abs(d.y), abs(d.z))) d = self.nearest_axis(d) ua = mathutils.Vector((1.0, 0.0, 0.0)) if (d.x != 0.0): ua = mathutils.Vector((0.0, 1.0, 0.0)) va = mathutils.Vector((0.0, 0.0, -1.0)) if (d.z != 0.0): va = mathutils.Vector((0.0, -1.0, 0.0)) ## face space # ua = f.normal.copy() # ua.normalize() # ua = mathutils.Vector((abs(ua.x), abs(ua.y), abs(ua.z))) # ua = self.nearest_axis(ua) # va = mathutils.Vector((0.0, 0.0, -1.0)) # if (ua.z == 1.0): # va = mathutils.Vector((0.0, -1.0, 0.0)) # ua = f.normal.copy() # ua.normalize() # ua.cross(va) # va = f.normal.copy() # va.normalize() # va.cross(ua) side.uaxis = Axis(ua.x, ua.y, ua.z) side.vaxis = Axis(va.x, va.y, va.z) uv_layer = 0 xmin = min( min(f.loops[0][bm.loops.layers.uv.active].uv.x, f.loops[1][ bm.loops.layers.uv.active].uv.x), f.loops[2][bm.loops.layers.uv.active].uv.x) xmax = max( max(f.loops[0][bm.loops.layers.uv.active].uv.x, f.loops[1][ bm.loops.layers.uv.active].uv.x), f.loops[2][bm.loops.layers.uv.active].uv.x) ymin = min( min(f.loops[0][bm.loops.layers.uv.active].uv.y, f.loops[1][ bm.loops.layers.uv.active].uv.y), f.loops[2][bm.loops.layers.uv.active].uv.y) ymax = max( max(f.loops[0][bm.loops.layers.uv.active].uv.y, f.loops[1][ bm.loops.layers.uv.active].uv.y), f.loops[2][bm.loops.layers.uv.active].uv.y) side.uaxis.scale = xmax - xmin side.uaxis.translate = xmin side.vaxis.scale = ymax - ymin side.vaxis.translate = ymin xs = f.loops[0][ bm.loops.layers.uv.active].uv.x + f.loops[1][ bm.loops.layers.uv.active].uv.x + f.loops[ 2][bm.loops.layers.uv.active].uv.x ys = f.loops[0][ bm.loops.layers.uv.active].uv.y + f.loops[1][ bm.loops.layers.uv.active].uv.y + f.loops[ 2][bm.loops.layers.uv.active].uv.y # side.uaxis.scale = xs / 3.0 # side.vaxis.scale = ys / 3.0 blk.children.append(side) else: print("a face was missed! " + str(f)) map.world.children.append(blk) bm.free() map.children.append(VersionInfo()) map.world.skyname = None map.world.properties["mapversion"] = 1 map.cordon.maxs.x = 1024.0 map.cordon.maxs.y = 1024.0 map.cordon.maxs.z = 1024.0 map.cordon.mins.x = -1024.0 map.cordon.mins.y = -1024.0 map.cordon.mins.z = -1024.0 # write to disk map.write_vmf(str(self.filepath)) return {'FINISHED'}
def export_object(file, ob, apply_modifiers): # create temp mesh temp_mesh = None if apply_modifiers: dg = bpy.context.evaluated_depsgraph_get() eval_obj = ob.evaluated_get(dg) temp_mesh = eval_obj.to_mesh() else: temp_mesh = ob.to_mesh() # get bmesh bm = bmesh.new() bm.from_mesh(temp_mesh) # get and sort triangles triangles_unsorted = bm.calc_loop_triangles() triangles = [] triangles_unwrapped = [] triangles_buckets = {} for luple in triangles_unsorted: face = luple[0].face material_index = face.material_index bucket = [] if not material_index in triangles_buckets: triangles_buckets[material_index] = bucket else: bucket = triangles_buckets[material_index] bucket.append(luple) for key in sorted(triangles_buckets): triangles += triangles_buckets[key] for luple in triangles: triangles_unwrapped += luple # vars uv_layer = bm.loops.layers.uv.active vc_layer = bm.loops.layers.color.active num_materials = len(ob.material_slots) max_dimension = max(ob.dimensions) center = translate_vertex(ob.location) triangles_loops_len = len(triangles) * 3 triangles_count = len(triangles) # calculate offsets submesh_offset = 64 vertex_offset = submesh_offset + (16 * num_materials) normals_offset = vertex_offset + (44 * triangles_loops_len) # header file.write(struct.pack('L', 259)) file.write(struct.pack('LL', num_materials, triangles_loops_len)) file.write(struct.pack('f', max_dimension)) file.write(struct.pack('fff', *center)) file.write(struct.pack('LLLL', 0, 0, 0, 0)) file.write(struct.pack('LLL', submesh_offset, vertex_offset, normals_offset)) file.write(struct.pack('LL', 0, 0)) # submeshes for submesh in range(num_materials): submesh_triangles_count = len(triangles_buckets[submesh]) material = ob.material_slots[submesh].material texnum = submesh if material is not None and "TD5TextureNumber" in material: texnum = int(material["TD5TextureNumber"]) file.write(struct.pack('H', 0)) file.write(struct.pack('H', texnum)) file.write(struct.pack('L', 0)) file.write(struct.pack('HH', submesh_triangles_count, 0)) file.write(struct.pack('L', 0)) # 'vertices' for loop in triangles_unwrapped: color = (1,1,1,1) uv = (0, 0) co = translate_vertex(loop.vert.co) if uv_layer is not None: uv = translate_uv(loop[uv_layer].uv) if vc_layer is not None: color = loop[vc_layer] file.write(struct.pack('fff', *co)) # position file.write(struct.pack('LLLL', 0, 0, 0, 0)) # unknown file.write(struct.pack('ff', *uv)) # uv file.write(struct.pack('L', 0)) # unknown cr = int(max(min(color[0], 1.0), 0.0) * 255) cg = int(max(min(color[1], 1.0), 0.0) * 255) cb = int(max(min(color[2], 1.0), 0.0) * 255) ca = int(max(min(color[3], 1.0), 0.0) * 255) file.write(struct.pack('BBBB', cr, cg, cb, ca)) # color # 'normals' for loop in triangles_unwrapped: normal = translate_normal(loop.vert.normal) file.write(struct.pack('fff', *normal)) file.write(struct.pack('L', 0)) # finish off bm.free() file.close() return
def execute(self, context): cleaning = True #cleaning loop while cleaning: #{ bpy.ops.object.mode_set(mode='OBJECT') me = bpy.context.object.data bm = bmesh.new() bm.from_mesh(me) # Find average edge size average_lenght = 0 edgecount = 0 for e in bm.edges: average_lenght += e.calc_length() edgecount += 1 average_lenght = average_lenght / edgecount # Collapse short edges bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.remove_doubles(threshold=(average_lenght / 3)) # Count non-manifold verts nmvcount = 0 for v in bm.verts: if not v.is_manifold: nmvcount += 1 if nmvcount is 0: cleaning = False else: cleaning = True # MACRO - dissolve and patch up broken vertices bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='DESELECT') context.tool_settings.mesh_select_mode = (True, False, False) bpy.ops.mesh.select_non_manifold() bpy.ops.mesh.dissolve_verts() bpy.ops.mesh.poke() bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.mesh.select_non_manifold() bpy.ops.mesh.delete(type='VERT') bpy.ops.mesh.fill_holes(sides=0) #fix non planar faces bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.mesh.vert_connect_nonplanar(angle_limit=0.333) bpy.ops.mesh.select_all(action='SELECT') context.tool_settings.mesh_select_mode = (False, False, True) bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY') #} bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.mode_set(mode='OBJECT') return {'FINISHED'}
def getDefaultValue(cls): return bmesh.new()
def do_import(self, b_model, armature, container): print('Importing group: \'', b_model.name, '\'', sep='') # Create object and mesh mesh = bpy.data.meshes.new(b_model.name) mesh.use_auto_smooth = True mesh.auto_smooth_angle = 3.14 object = bpy.data.objects.new(b_model.name, mesh) object.parent = container bpy.context.collection.objects.link(object) bpy.context.view_layer.objects.active = object # Assign custom properties self.__add_property(obj=object, key="opacity", value=b_model.opacity_amount, range=[-1, 255], description="Opacity of this mesh.") self.__add_property(obj=object, key="is_shadow", value="shadow" in object.name, range=[0, 1], description="Is this a shadow mesh?") do_tangents = object.get("is_shadow") == False self.__add_property( obj=object, key="calc_tangents", value=do_tangents, range=[0, 1], description="Should tangents be calculated on export?") self.__add_property(obj=object, key="neck_fix", value=-1, range=[-1, 5], description="Neck fix to apply (See GMDC Panel)") # Load vertices and faces mesh.from_pydata(b_model.vertices, [], b_model.faces) # Load normals for i, vert in enumerate(mesh.vertices): vert.normal = b_model.normals[i] # Create UV layer and load UV coordinates mesh.uv_layers.new(name='UVMap') for i, polygon in enumerate(mesh.polygons): for j, loopindex in enumerate(polygon.loop_indices): meshuvloop = mesh.uv_layers.active.data[loopindex] vertex_index = b_model.faces[i][j] meshuvloop.uv = b_model.uvs[vertex_index] # Load bone assignments and weights # Check for mismatches in index counts if armature: # Create vertex groups for bone assignments for b in armature.data.bones: object.vertex_groups.new(name=b.name) if len(b_model.vertices) != len(b_model.bone_assign) or \ len(b_model.vertices) != len(b_model.bone_weight): #Assign armature modifier anyway. object.modifiers.new("Armature", 'ARMATURE') object.modifiers["Armature"].object = armature object.modifiers["Armature"].use_deform_preserve_volume = False error = 'ERROR: Group ' + b_model.name + '\'s vertex index counts don\'t match.' return error print('Applying bone weights...') for i in range(len(b_model.bone_assign)): remainder = 1.0 # Used for an implied 4th bone weight # print(i, b_model.bone_assign[i]) for j in range(len(b_model.bone_assign[i])): # If it's a sim skeleton, use boneparent table # Otherwise use bone names if len(armature.data.bones) == 65: grpname = BoneData.bone_parent_table[ b_model.bone_assign[i][j]][0] else: # print(b_model.bone_assign[i][j]) grpname = armature.data.bones[b_model.bone_assign[i] [j]].name vertgroup = object.vertex_groups[grpname] if j != 3: weight = b_model.bone_weight[i][j] remainder -= weight vertgroup.add([i], weight, 'ADD') else: vertgroup.add([i], remainder, 'ADD') # Add Armature modifier object.modifiers.new("Armature", 'ARMATURE') object.modifiers["Armature"].object = armature object.modifiers["Armature"].use_deform_preserve_volume = False # Apply Morphs(if any) as shape keys print('Loading morphs as shape keys...') if b_model.morphs: # Blender always needs a base shape key shpkey = object.shape_key_add(from_mix=False) shpkey.name = "Basis" try: for morph in b_model.morphs: if morph.name == ', ': continue shpkey = object.shape_key_add(from_mix=False) shpkey.name = morph.name for i, vert in enumerate(mesh.vertices): shpkey.data[vert.index].co = Vector( (vert.co[0] + morph.deltas[i][0], vert.co[1] + morph.deltas[i][1], vert.co[2] + morph.deltas[i][2])) except: print("Additional morphs were discarded.") # Add vertex group to control which verts keep their old normals # object.vertex_groups.new("__NORMALS__") # vertgroup = object.vertex_groups['__NORMALS__'] ## Should be done manually by user before exporting # for i in range (len(mesh.vertices)): # vertgroup.add( [i], 1, 'ADD' ) # Import Original normals as vertex colors mesh.vertex_colors.new(name="__NORMALS__") color_map = mesh.vertex_colors['__NORMALS__'] for poly in mesh.polygons: for vert_idx, loop_idx in zip(poly.vertices, poly.loop_indices): # Convert Vertex normal to a valid Color normal = Vector(b_model.normals[vert_idx]) normal.normalize() rgb = (normal + Vector((1.0, 1.0, 1.0))) * 0.5 rgba = rgb.to_4d() # Set Vertex color to new color color_map.data[loop_idx].color = rgba print( "Original normals imported as vertex colors on layer '__NORMALS__'" ) print() # After all that, merge doubles and make originally hard edges sharp bm = bmesh.new() bm.from_mesh(mesh) edges = self.get_sharp(b_model) bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001) # Check mesh edges against edge dictionary, mark hard edges sharp after removing doubles numedges = 0 for e in bm.edges: edgemid = tuple((e.verts[0].co + e.verts[1].co) / 2) if edgemid in edges and len(edges[edgemid]) > 2: numedges += 1 e.smooth = False print(numedges, 'Hard edges found.') print() bm.to_mesh(mesh) bm.free() object.select_set(state=True) bpy.ops.object.shade_smooth() # Delete loose geometry in case of Maxis object imports bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.delete_loose() bpy.ops.object.mode_set(mode='OBJECT') return 'Group \'' + b_model.name + '\' imported.\n'
def write_mesh(self, obj, parent): """ Internal method to process a mesh during the export process """ char = None armature = None restore_armature_modifier = None for modifier in obj.modifiers: if modifier.type == "PARTICLE_SYSTEM": particle_system = modifier.particle_system self.writer.handle_particle_system(obj, parent, particle_system) elif modifier.type == "ARMATURE": # The geometry modified by the armature needs to be # parented under the character node. armature = modifier.object.data char = self.writer.characters[armature] parent = char if modifier.show_viewport: # Prevent the modifier from being applied. modifier.show_viewport = False restore_armature_modifier = modifier # Check if we alrady have the geom node cached key = (obj.data.name, armature) if key in self.geom_cache: virtual_geom_node = self.geom_cache[key] else: # Create a new geom node to store all geoms virtual_geom_node = GeomNode(obj.data.name) # Convert the object to a mesh, so we can read the polygons mesh = obj.to_mesh(self.writer.context.scene, apply_modifiers=True, settings='PREVIEW', calc_tessface=True, calc_undeformed=True) # Get a BMesh representation b_mesh = bmesh.new() b_mesh.from_mesh(mesh) # Triangulate the mesh. This makes stuff simpler, and panda can't handle # polygons with more than 3 vertices. bmesh.ops.triangulate(b_mesh, faces=b_mesh.faces) # Copy the bmesh back to the original mesh b_mesh.to_mesh(mesh) # Find the active uv layer and its name, in case there is one. if mesh.uv_layers.active: active_uv_layer = mesh.uv_layers.active.data active_uv_name = mesh.uv_layers.active.name else: active_uv_layer = None active_uv_name = "" # Group the polygons by their material index. We have to perform this # operation, because we have to create a single geom for each material polygons_by_material = self._group_mesh_faces_by_material(mesh) # Calculate the per-vertex normals, in case blender did not do that yet. mesh.calc_normals() # Extract material slots, but ensure there is always one slot, so objects # with no actual material get exported, too material_slots = obj.material_slots if len(material_slots) == 0: material_slots = [None] # Create the different geoms, 1 per material for index, slot in enumerate(material_slots): # Skip the material slot if no polygon references it if len(polygons_by_material[index]) < 1: continue # Create a virtual material if the slot contains a material. Otherwise # just use an empty material if slot: render_state = self.writer.material_writer.create_state_from_material( slot.material) else: render_state = RenderState.empty # Extract the per-material polygon list polygons = polygons_by_material[index] # Create a geom from those polygons virtual_geom = self._create_geom_from_polygons(obj, mesh, polygons, active_uv_layer, char=char) # Add that geom to the geom node virtual_geom_node.add_geom(virtual_geom, render_state) bpy.data.meshes.remove(mesh) self.geom_cache[key] = virtual_geom_node parent.add_child(virtual_geom_node) if restore_armature_modifier: restore_armature_modifier.show_viewport = True
def dot_mesh(ob, path, force_name=None, ignore_shape_animation=False, normals=True, tangents=4, isLOD=False, **kwargs): """ export the vertices of an object into a .mesh file ob: the blender object path: the path to save the .mesh file to. path MUST exist force_name: force a different name for this .mesh kwargs: * material_prefix - string. (optional) * overwrite - bool. (optional) default False """ obj_name = force_name or ob.data.name obj_name = clean_object_name(obj_name) target_file = os.path.join(path, '%s.mesh.xml' % obj_name) material_prefix = kwargs.get('material_prefix', '') overwrite = kwargs.get('overwrite', False) if os.path.isfile(target_file) and not overwrite: return [] if not os.path.isdir(path): os.makedirs(path) start = time.time() cleanup = False if ob.modifiers: cleanup = True copy = ob.copy() #bpy.context.scene.collection.objects.link(copy) rem = [] for mod in copy.modifiers: # remove armature and array modifiers before collaspe if mod.type in 'ARMATURE ARRAY'.split(): rem.append(mod) for mod in rem: copy.modifiers.remove(mod) else: copy = ob # bake mesh mesh = copy.to_mesh() # collaspe mesh.update() # Blender by default does not calculate these. # When querying the quads/tris of the object blender would crash if calc_tessface was not updated mesh.calc_loop_triangles() Report.meshes.append(obj_name) Report.faces += len(mesh.loop_triangles) Report.orig_vertices += len(mesh.vertices) logger.info('* Generating: %s.mesh.xml' % obj_name) try: with open(target_file, 'w') as f: f.flush() except Exception as e: show_dialog("Invalid mesh object name: %s" % obj_name) return with open(target_file, 'w') as f: doc = SimpleSaxWriter(f, 'mesh', {}) # Very ugly, have to replace number of vertices later doc.start_tag('sharedgeometry', {'vertexcount': '__TO_BE_REPLACED_VERTEX_COUNT__'}) logger.info('* Writing shared geometry') # Textures dotextures = False if mesh.uv_layers.active: dotextures = True else: tangents = 0 doc.start_tag( 'vertexbuffer', { 'positions': 'true', 'normals': 'true', 'tangents': str(bool(tangents)), 'tangent_dimensions': str(tangents), 'colours_diffuse': str(bool(mesh.vertex_colors)), 'texture_coords': '%s' % len(mesh.uv_layers) * dotextures }) # Materials # saves tuples of material name and material obj (or None) materials = [] # a material named 'vertex.color.<yourname>' will overwrite # the diffuse color in the mesh file! for mat in ob.data.materials: mat_name = "_missing_material_" if mat is not None: mat_name = mat.name mat_name = material_name(mat_name, prefix=material_prefix) extern = False if mat_name.startswith("extern."): mat_name = mat_name[len("extern."):] extern = True if mat: materials.append((mat_name, extern, mat)) else: logger.warn('Bad material data in: %s' % ob.name) materials.append(('_missing_material_', True, None)) # fixed dec22, keep proper index if not materials: materials.append(('_missing_material_', True, None)) vertex_groups = {} material_faces = [] for matidx, mat in enumerate(materials): material_faces.append([]) shared_vertices = {} _remap_verts_ = [] _remap_normals_ = [] _face_indices_ = [] numverts = 0 # Create bmesh to help obtain custom vertex normals bm = bmesh.new() if mesh.has_custom_normals: mesh.calc_normals_split() bm.from_mesh(mesh) bm.verts.ensure_lookup_table() else: bm.from_mesh(mesh) # Ogre only supports triangles bmesh_return = bmesh.ops.triangulate(bm, faces=bm.faces) bm.to_mesh(mesh) # Map the original face indices to the tesselated ones face_map = bmesh_return['face_map'] _tess_polygon_face_map_ = {} for tess_face in face_map: #print("tess_face.index : %s <---> polygon_face.index : %s" % (tess_face.index, face_map[tess_face].index)) _tess_polygon_face_map_[ tess_face.index] = face_map[tess_face].index # Vertex colors vertex_color_lookup = VertexColorLookup(mesh) if tangents: mesh.calc_tangents(uvmap=mesh.uv_layers.active.name) progressScale = 1.0 / len(mesh.polygons) # Process mesh after triangulation for F in mesh.polygons: percent = (F.index + 1) * progressScale sys.stdout.write("\r + Faces [" + '=' * int(percent * 50) + '>' + '.' * int(50 - percent * 50) + "] " + str(int(percent * 10000) / 100.0) + "% ") sys.stdout.flush() smooth = F.use_smooth tri = (F.vertices[0], F.vertices[1], F.vertices[2]) face = [] for loop_idx, idx in zip(F.loop_indices, tri): v = mesh.vertices[idx] if smooth: n = mathutils.Vector() if mesh.has_custom_normals: for loop in bm.verts[idx].link_loops: n += mesh.loops[loop.index].normal n.normalize() nx, ny, nz = swap(n) # when no custom normals or mesh.loops[...].normal is zero vector # use normal vector from vertex if n.length_squared == 0: nx, ny, nz = swap(v.normal) # fixed june 17th 2011 n = mathutils.Vector([nx, ny, nz]) elif tangents: nx, ny, nz = swap(mesh.loops[loop_idx].normal) n = mathutils.Vector([nx, ny, nz]) else: nx, ny, nz = swap(F.normal) n = mathutils.Vector([nx, ny, nz]) if tangents: tx, ty, tz = swap(mesh.loops[loop_idx].tangent) tw = mesh.loops[loop_idx].bitangent_sign r, g, b, ra = vertex_color_lookup.get(loop_idx) # Texture maps vert_uvs = [] if dotextures: for layer in mesh.uv_layers: vert_uvs.append(layer.data[loop_idx].uv) ''' Check if we already exported that vertex with same normal, do not export in that case, (flat shading in blender seems to work with face normals, so we copy each flat face' vertices, if this vertex with same normals was already exported, todo: maybe not best solution, check other ways (let blender do all the work, or only support smooth shading, what about seems, smoothing groups, materials, ...) ''' vert = VertexNoPos(numverts, nx, ny, nz, r, g, b, ra, vert_uvs) alreadyExported = False if idx in shared_vertices: for vert2 in shared_vertices[idx]: #does not compare ogre_vidx (and position at the moment) if vert == vert2: face.append(vert2.ogre_vidx) alreadyExported = True #print(idx,numverts, nx,ny,nz, r,g,b,ra, vert_uvs, "already exported") break if not alreadyExported: face.append(vert.ogre_vidx) shared_vertices[idx].append(vert) #print(numverts, nx,ny,nz, r,g,b,ra, vert_uvs, "appended") else: face.append(vert.ogre_vidx) shared_vertices[idx] = [vert] #print(idx, numverts, nx,ny,nz, r,g,b,ra, vert_uvs, "created") if alreadyExported: continue numverts += 1 _remap_verts_.append(v) _remap_normals_.append(n) # Use mapping from tesselated face to polygon face if the mapping exists if F.index in _tess_polygon_face_map_: _face_indices_.append(_tess_polygon_face_map_[F.index]) else: _face_indices_.append(F.index) x, y, z = swap(v.co) # xz-y is correct! doc.start_tag('vertex', {}) doc.leaf_tag('position', { 'x': '%6f' % x, 'y': '%6f' % y, 'z': '%6f' % z }) doc.leaf_tag('normal', { 'x': '%6f' % nx, 'y': '%6f' % ny, 'z': '%6f' % nz }) if tangents: doc.leaf_tag( 'tangent', { 'x': '%6f' % tx, 'y': '%6f' % ty, 'z': '%6f' % tz, 'w': '%6f' % tw }) if vertex_color_lookup.has_color_data: doc.leaf_tag('colour_diffuse', {'value': '%6f %6f %6f %6f' % (r, g, b, ra)}) # Texture maps if dotextures: for uv in vert_uvs: doc.leaf_tag('texcoord', { 'u': '%6f' % uv[0], 'v': '%6f' % (1.0 - uv[1]) }) doc.end_tag('vertex') append_triangle_in_vertex_group(mesh, ob, vertex_groups, face, tri) material_faces[F.material_index].append(face) Report.vertices += numverts doc.end_tag('vertexbuffer') doc.end_tag('sharedgeometry') print("") logger.info('- Done at %s seconds' % timer_diff_str(start)) logger.info('* Writing submeshes') doc.start_tag('submeshes', {}) for matidx, (mat_name, extern, mat) in enumerate(materials): if not len(material_faces[matidx]): Report.warnings.append( 'BAD SUBMESH "%s": material %r, has not been applied to any faces - not exporting as submesh.' % (obj_name, mat_name)) continue # fixes corrupt unused materials submesh_attributes = { 'usesharedvertices': 'true', # Maybe better look at index of all faces, if one over 65535 set to true; # Problem: we know it too late, postprocessing of file needed "use32bitindexes": str(bool(numverts > 65535)), "operationtype": "triangle_list" } if mat_name != "_missing_material_": submesh_attributes['material'] = mat_name doc.start_tag('submesh', submesh_attributes) doc.start_tag('faces', {'count': str(len(material_faces[matidx]))}) for fidx, (v1, v2, v3) in enumerate(material_faces[matidx]): doc.leaf_tag('face', { 'v1': str(v1), 'v2': str(v2), 'v3': str(v3) }) doc.end_tag('faces') doc.end_tag('submesh') Report.triangles += len(material_faces[matidx]) for name, ogre_indices in vertex_groups.items(): if len(ogre_indices) <= 0: continue submesh_attributes = { 'usesharedvertices': 'true', "use32bitindexes": str(bool(numverts > 65535)), "operationtype": "triangle_list", "material": "none", } doc.start_tag('submesh', submesh_attributes) doc.start_tag('faces', {'count': len(ogre_indices)}) for (v1, v2, v3) in ogre_indices: doc.leaf_tag('face', { 'v1': str(v1), 'v2': str(v2), 'v3': str(v3) }) doc.end_tag('faces') doc.end_tag('submesh') del material_faces del shared_vertices doc.end_tag('submeshes') # Submesh names # todo: why is the submesh name taken from the material # when we have the blender object name available? doc.start_tag('submeshnames', {}) for matidx, (mat_name, extern, mat) in enumerate(materials): doc.leaf_tag('submesh', {'name': mat_name, 'index': str(matidx)}) idx = len(materials) for name in vertex_groups.keys(): name = name[len('ogre.vertex.group.'):] doc.leaf_tag('submesh', {'name': name, 'index': idx}) idx += 1 doc.end_tag('submeshnames') logger.info('- Done at %s seconds' % timer_diff_str(start)) # Generate lod levels if isLOD == False and ob.type == 'MESH' and config.get( 'LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == False: lod_levels = config.get('LOD_LEVELS') lod_distance = config.get('LOD_DISTANCE') lod_ratio = config.get('LOD_PERCENT') / 100.0 lod_pre_mesh_count = len(bpy.data.meshes) # Cap lod levels to something sensible (what is it?) if lod_levels > 10: lod_levels = 10 def duplicate_object(scene, name, copyobj): # Create new mesh mesh = bpy.data.meshes.new(name) # Create new object associated with the mesh ob_new = bpy.data.objects.new(name, mesh) # Copy data block from the old object into the new object ob_new.data = copyobj.data.copy() ob_new.location = copyobj.location ob_new.rotation_euler = copyobj.rotation_euler ob_new.scale = copyobj.scale # Link new object to the given scene and select it scene.collection.objects.link(ob_new) ob_new.select_set(True) return ob_new, mesh # Create a temporary duplicate ob_copy, ob_copy_mesh = duplicate_object( bpy.context.scene, obj_name + "_LOD_TEMP_COPY", ob) ob_copy_meshes = [ob_copy.data, ob_copy_mesh] # Activate clone for modifier manipulation decimate = ob_copy.modifiers.new(name="Ogre-LOD_Decimate", type='DECIMATE') if decimate is not None: decimate.decimate_type = 'COLLAPSE' decimate.show_viewport = True decimate.show_render = True lod_generated = [] lod_ratio_multiplier = 1.0 - lod_ratio lod_current_ratio = 1.0 * lod_ratio_multiplier lod_current_distance = lod_distance lod_current_vertice_count = len(mesh.vertices) lod_min_vertice_count = 12 for level in range(lod_levels + 1)[1:]: decimate.ratio = lod_current_ratio # https://docs.blender.org/api/current/bpy.types.Depsgraph.html depsgraph = bpy.context.evaluated_depsgraph_get() object_eval = ob_copy.evaluated_get(depsgraph) lod_mesh = bpy.data.meshes.new_from_object(object_eval) ob_copy_meshes.append(lod_mesh) # Check min vertice count and that the vertice count got reduced from last iteration lod_mesh_vertices = len(lod_mesh.vertices) if lod_mesh_vertices < lod_min_vertice_count: logger.info( '- LOD level: %s, vertice count: %s too small. Ignoring LOD.' % (level, lod_mesh_vertices)) break if lod_mesh_vertices >= lod_current_vertice_count: logger.info( '- LOD level: %s, vertice count: %s cannot be decimated any longer. Ignoring LOD.' % (level - 1, lod_mesh_vertices)) break # todo: should we check if the ratio gets too small? although its up to the user to configure from the export panel lod_generated.append({ 'level': level, 'distance': lod_current_distance, 'ratio': lod_current_ratio, 'mesh': lod_mesh }) lod_current_distance += lod_distance lod_current_vertice_count = lod_mesh_vertices lod_current_ratio *= lod_ratio_multiplier # Create lod .mesh files and generate LOD XML to the original .mesh.xml if len(lod_generated) > 0: # 'manual' means if the geometry gets loaded from a # different file that this LOD list references # NOTE: This is the approach at the moment. Another option would be to # references to the same vertex indexes in the shared geometry. But the # decimate approach wont work with this as it generates a fresh geometry. doc.start_tag( 'levelofdetail', { 'strategy': 'default', 'numlevels': str( len(lod_generated) + 1 ), # The main mesh is + 1 (kind of weird Ogre logic) 'manual': "true" }) logger.info( '- Generating: %s LOD meshes. Original: vertices %s, faces: %s' % (len(lod_generated), len( mesh.vertices), len(mesh.loop_triangles))) for lod in lod_generated: ratio_percent = round(lod['ratio'] * 100.0, 0) logger.info( "- Writing LOD %s for distance %s and ratio %s/100, with %s vertices, %s faces" % (lod['level'], lod['distance'], str(ratio_percent), len(lod['mesh'].vertices), len(lod['mesh'].loop_triangles))) lod_ob_temp = bpy.data.objects.new( obj_name, lod['mesh']) lod_ob_temp.data.name = obj_name + '_LOD_' + str( lod['level']) dot_mesh(lod_ob_temp, path, lod_ob_temp.data.name, ignore_shape_animation, normals, tangents, isLOD=True) # 'value' is the distance this LOD kicks in for the 'Distance' strategy. doc.leaf_tag( 'lodmanual', { 'value': str(lod['distance']), 'meshname': lod_ob_temp.data.name + ".mesh" }) # Delete temporary LOD object. # The clone meshes will be deleted later. lod_ob_temp.user_clear() logger.debug("Removing temporary LOD object: %s" % lod_ob_temp.name) bpy.data.objects.remove(lod_ob_temp, do_unlink=True) del lod_ob_temp doc.end_tag('levelofdetail') # Delete temporary LOD object logger.debug("Removing temporary LOD object: %s" % ob_copy.name) bpy.data.objects.remove(ob_copy, do_unlink=True) del ob_copy # Delete temporary data/mesh objects bpy.context.evaluated_depsgraph_get().update() for mesh_iter in ob_copy_meshes: mesh_iter.user_clear() logger.debug("Removing temporary LOD mesh: %s" % mesh_iter.name) bpy.data.meshes.remove(mesh_iter) del mesh_iter ob_copy_meshes = [] if lod_pre_mesh_count != len(bpy.data.meshes): logger.warn( '- After LOD generation, cleanup failed to erase all temporary data!' ) arm = ob.find_armature() if arm: doc.leaf_tag('skeletonlink', {'name': '%s.skeleton' % obj_name}) doc.start_tag('boneassignments', {}) boneOutputEnableFromName = {} boneIndexFromName = {} for bone in arm.pose.bones: boneOutputEnableFromName[bone.name] = True if config.get('ONLY_DEFORMABLE_BONES'): # if we found a deformable bone, if bone.bone.use_deform: # visit all ancestor bones and mark them "output enabled" parBone = bone.parent while parBone: boneOutputEnableFromName[parBone.name] = True parBone = parBone.parent else: # non-deformable bone, no output boneOutputEnableFromName[bone.name] = False boneIndex = 0 for bone in arm.pose.bones: boneIndexFromName[bone.name] = boneIndex if boneOutputEnableFromName[bone.name]: boneIndex += 1 badverts = 0 for vidx, v in enumerate(_remap_verts_): check = 0 for vgroup in v.groups: if vgroup.weight > config.get('TRIM_BONE_WEIGHTS'): groupIndex = vgroup.group if groupIndex < len(copy.vertex_groups): vg = copy.vertex_groups[groupIndex] if vg.name in boneIndexFromName: # allows other vertex groups, not just armature vertex groups bnidx = boneIndexFromName[ vg. name] # find_bone_index(copy,arm,vgroup.group) doc.leaf_tag( 'vertexboneassignment', { 'vertexindex': str(vidx), 'boneindex': str(bnidx), 'weight': '%6f' % vgroup.weight }) check += 1 else: logger.warn( 'Mesh: %s vertex groups not in sync with armature %s (groupIndex = %s)' % (mesh.name, arm.name, groupIndex)) if check > 4: badverts += 1 logger.warn( '<%s> vertex %s is in more than 4 vertex groups (bone weights). This maybe Ogre incompatible' % (mesh.name, vidx)) if badverts: Report.warnings.append( 'Mesh "%s" has %s vertices weighted to too many bones (Ogre limits a vertex to 4 bones). Try increasing the Trim-Weights threshold option' % (mesh.name, badverts)) doc.end_tag('boneassignments') # Updated June3 2011 - shape animation works if config.get('SHAPE_ANIMATIONS') and ob.data.shape_keys and len( ob.data.shape_keys.key_blocks): logger.info('* Writing shape keys') doc.start_tag('poses', {}) for sidx, skey in enumerate(ob.data.shape_keys.key_blocks): if sidx == 0: continue if len(skey.data) != len(mesh.vertices): failure = 'FAILED to save shape animation - you can not use a modifier that changes the vertex count! ' failure += '[ mesh : %s ]' % mesh.name Report.warnings.append(failure) logger.error(failure) break doc.start_tag( 'pose', { 'name': skey.name, # If target is 'mesh', no index needed, if target is submesh then submesh identified by 'index' #'index' : str(sidx-1), #'index' : '0', 'target': 'mesh' }) snormals = None if config.get('SHAPE_NORMALS'): if smooth: snormals = skey.normals_vertex_get() else: snormals = skey.normals_polygon_get() for vidx, v in enumerate(_remap_verts_): pv = skey.data[v.index] x, y, z = swap(pv.co - v.co) if config.get('SHAPE_NORMALS'): n = _remap_normals_[vidx] if smooth: pn = mathutils.Vector([ snormals[v.index * 3 + 0], snormals[v.index * 3 + 1], snormals[v.index * 3 + 2] ]) else: vindex = _face_indices_[vidx] pn = mathutils.Vector([ snormals[vindex * 3 + 0], snormals[vindex * 3 + 1], snormals[vindex * 3 + 2] ]) if mesh.has_custom_normals: nx, ny, nz = n else: nx, ny, nz = swap(pn) #for i,p in enumerate( skey.data ): #x,y,z = p.co - ob.data.vertices[i].co #x,y,z = swap( ob.data.vertices[i].co - p.co ) #if x==.0 and y==.0 and z==.0: continue # the older exporter optimized this way, is it safe? if config.get('SHAPE_NORMALS'): doc.leaf_tag( 'poseoffset', { 'x': '%6f' % x, 'y': '%6f' % y, 'z': '%6f' % z, 'nx': '%6f' % nx, 'ny': '%6f' % ny, 'nz': '%6f' % nz, 'index': str(vidx) # is this required? }) else: doc.leaf_tag( 'poseoffset', { 'x': '%6f' % x, 'y': '%6f' % y, 'z': '%6f' % z, 'index': str(vidx) # is this required? }) doc.end_tag('pose') doc.end_tag('poses') logger.info('- Done at %s seconds' % timer_diff_str(start)) if ob.data.shape_keys.animation_data and len( ob.data.shape_keys.animation_data.nla_tracks): logger.info('* Writing shape animations') doc.start_tag('animations', {}) _fps = float(bpy.context.scene.render.fps) for nla in ob.data.shape_keys.animation_data.nla_tracks: for idx, strip in enumerate(nla.strips): doc.start_tag( 'animation', { 'name': strip.name, 'length': str((strip.frame_end - strip.frame_start) / _fps) }) doc.start_tag('tracks', {}) doc.start_tag( 'track', { 'type': 'pose', 'target': 'mesh' # If target is 'mesh', no index needed, if target is submesh then submesh identified by 'index' #'index' : str(idx) #'index' : '0' }) doc.start_tag('keyframes', {}) for frame in range( int(strip.frame_start), int(strip.frame_end) + 1, bpy.context.scene.frame_step): #thanks to Vesa bpy.context.scene.frame_set(frame) doc.start_tag( 'keyframe', { 'time': str( (frame - strip.frame_start) / _fps) }) for sidx, skey in enumerate( ob.data.shape_keys.key_blocks): if sidx == 0: continue doc.leaf_tag( 'poseref', { 'poseindex': str(sidx - 1), 'influence': str(skey.value) }) doc.end_tag('keyframe') doc.end_tag('keyframes') doc.end_tag('track') doc.end_tag('tracks') doc.end_tag('animation') doc.end_tag('animations') logger.info('- Done at %s seconds' % timer_diff_str(start)) ## Clean up and save if cleanup: #bpy.context.collection.objects.unlink(copy) copy.user_clear() logger.debug("Removing temporary object: %s" % copy.name) bpy.data.objects.remove(copy) del copy del _remap_verts_ del _remap_normals_ del _face_indices_ doc.close() # reported by Reyn f.close() logger.info('- Created %s.mesh.xml at %s seconds' % (obj_name, timer_diff_str(start))) # todo: Very ugly, find better way def replaceInplace(f, searchExp, replaceExp): import fileinput for line in fileinput.input(f, inplace=1): if searchExp in line: line = line.replace(searchExp, replaceExp) sys.stdout.write(line) fileinput.close() # reported by jakob replaceInplace(target_file, '__TO_BE_REPLACED_VERTEX_COUNT__' + '"', str(numverts) + '"') #+ ' ' * (ls - lr)) del (replaceInplace) # Start .mesh.xml to .mesh convertion tool util.xml_convert(target_file, has_uvs=dotextures) logger.info('- Created %s.mesh in total time %s seconds' % (obj_name, timer_diff_str(start))) # If requested by the user, generate LOD levels through OgreMeshUpgrader if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True: target_mesh_file = os.path.join(path, '%s.mesh' % obj_name) util.lod_create(target_mesh_file) # Note that exporting the skeleton does not happen here anymore # It was moved to the function dot_skeleton in its own module (skeleton.py) mats = [] for mat_name, extern, mat in materials: # _missing_material_ is marked as extern if not extern: mats.append(mat_name) else: logger.info("Extern material: %s" % mat_name) return mats
def makeInvertedSlope(dimensions: dict, brickSize: list, brickType: str, loopCut: bool, direction: str = None, circleVerts: int = None, detail: str = "LOW", stud: bool = True, bme: bmesh = None): # TODO: Add support for loopCut """ create slope brick with bmesh NOTE: brick created with slope facing +X direction, then translated/rotated as necessary Keyword Arguments: dimensions -- dictionary containing brick dimensions brickSize -- size of brick (e.g. 2x3 slope -> [2, 3, 3]) brickType -- cm.brickType loopCut -- loop cut cylinders so bevels can be cleaner direction -- direction slant faces in ("X+", "X-", "Y+", "Y-") circleVerts -- number of vertices per circle of cylinders detail -- level of brick detail (options: ("FLAT", "LOW", "MEDIUM", "HIGH")) stud -- create stud on top of brick bme -- bmesh object in which to create verts """ # create new bmesh object bme = bmesh.new() if not bme else bme # set direction to longest side if None (defaults to X if sides are the same) maxIdx = brickSize.index(max(brickSize[:2])) directions = ["X+", "Y+", "X-", "Y-"] # default to "X+" if X is larger, "Y+" if Y is larger direction = direction or directions[maxIdx] # verify direction is valid assert direction in directions # get halfScale bAndPBrick = flatBrickType(brickType) and brickSize[2] == 3 height = dimensions["height"] * (3 if bAndPBrick else 1) d = Vector((dimensions["width"] / 2, dimensions["width"] / 2, height / 2)) # get scalar for d in positive xyz directions adjustedBrickSize = (brickSize[:2] if "X" in direction else brickSize[1::-1]) + brickSize[2:] scalar = Vector( (adjustedBrickSize[0] * 2 - 1, adjustedBrickSize[1] * 2 - 1, 1)) # get thickness of brick from inside to outside thick = Vector([dimensions["thickness"]] * 3) # make brick body cube coord1 = -d coord2 = vec_mult(d, [1, scalar.y, 1]) v1, v2, v3, v4, v5, v6, v7, v8 = makeCube( coord1, coord2, [0 if stud else 1, 1 if detail == "FLAT" else 0, 0, 0, 1, 1], bme=bme) if adjustedBrickSize[0] > 1: # remove bottom verts on slope side bme.verts.remove(v6) bme.verts.remove(v7) # add face to opposite side from slope bme.faces.new((v1, v5, v8, v2)) # make square at end of slope coord1 = vec_mult(d, [scalar.x, -1, 1]) coord2 = vec_mult(d, [scalar.x, scalar.y, 1]) coord1.z -= thick.z v9, v10, v11, v12 = makeSquare(coord1, coord2, bme=bme) # connect square to body cube bme.faces.new([v8, v11, v10, v3, v2]) bme.faces.new([v9, v12, v5, v1, v4]) if max(brickSize[:2]) == 2 or detail in ["FLAT", "LOW"]: bme.faces.new((v4, v3, v10, v9)) else: pass # TODO: Draw inset half-cylinder # add details on top if not stud: bme.faces.new((v12, v11, v8, v5)) else: if adjustedBrickSize[0] > 1: # make upper square over slope coord1 = Vector( (d.x, -d.y + thick.y / 2, -d.z * (0.5 if max(adjustedBrickSize[:2]) == 2 else 0.625))) coord2 = Vector( (d.x * scalar.x - thick.x, d.y * scalar.y - thick.y / 2, d.z)) # v13, v14, v15, v16, v17, v18, v19, v20 = makeCube(coord1, coord2, [0, 0, 1, 0 if sum(adjustedBrickSize[:2]) == 5 else 1, 1, 1], flipNormals=True, bme=bme) # TODO: replace the following line with line above to add support details later print() print(coord1) print(coord2) print(scalar) v13, v14, v15, v16, v17, v18, v19, v20 = makeCube( coord1, coord2, [0, 0, 1, 1, 1, 1], flipNormals=True, bme=bme) v15.co.z += (d.z * 2 - thick.z) * (0.9 if max( adjustedBrickSize[:2]) == 3 else 0.8) v16.co.z = v15.co.z # make faces on edges of new square bme.faces.new((v18, v17, v5, v12)) bme.faces.new((v19, v18, v12, v11)) bme.faces.new((v20, v19, v11, v8)) addSlopeStuds(dimensions, height, adjustedBrickSize, brickType, circleVerts, bme, edgeXp=[v14, v13], edgeXn=[v16, v15], edgeYp=[v13, v16], edgeYn=[v15, v14], loopCut=loopCut) else: v17, v20 = v6, v7 addStuds(dimensions, height, [1, adjustedBrickSize[1], adjustedBrickSize[2]], brickType, circleVerts, bme, edgeXp=[v20, v17], edgeXn=[v8, v5], edgeYp=[v20, v8], edgeYn=[v17, v5], hollow=False, loopCut=loopCut) pass # add details underneath if detail != "FLAT": # making verts for hollow portion coord1 = -d + Vector((thick.x, thick.y, 0)) coord2 = Vector( (d.x + (dimensions["tick_depth"] if detail == "HIGH" else 0), d.y * scalar.y, d.z * scalar.z)) - thick sides = [ 1 if detail == "LOW" else 0, 0, 0 if detail == "HIGH" else 1, 1, 1, 1 ] v21, v22, v23, v24, v25, v26, v27, v28 = makeCube(coord1, coord2, sides, flipNormals=True, bme=bme) # make faces on bottom edges of brick bme.faces.new((v1, v21, v24, v4)) bme.faces.new((v1, v2, v22, v21)) bme.faces.new((v23, v22, v2, v3)) # make tick marks inside if detail == "HIGH": bottomVertsD = addTickMarks( dimensions, [1, min(adjustedBrickSize[:2]), adjustedBrickSize[2]], circleVerts, detail, d, thick, bme, nno=v1, npo=v2, ppo=v3, pno=v4, nni=v21, npi=v22, ppi=v23, pni=v24, nnt=v25, npt=v28, ppt=v27, pnt=v26, inverted_slope=True, sideMarks=False) bottomVerts = bottomVertsD["X+"][::-1] else: bme.faces.new((v23, v3, v4, v24)) bottomVerts = [] # add supports if detail in ("MEDIUM", "HIGH") and min(adjustedBrickSize[:2]) == 2: addOblongSupport( dimensions, height, loopCut, circleVerts, "SLOPE_INVERTED", detail, d, scalar, thick, bme ) # [v27] + bottomVerts + [v26], [v28, v25], [v27, v28], [v26, v25], bme) # add small inner cylinders inside brick if detail in ("MEDIUM", "HIGH"): addInnerCylinders( dimensions, [1, min(adjustedBrickSize[:2]), adjustedBrickSize[2]], circleVerts, d, [v27] + bottomVerts + [v26], [v28, v25], [v27, v28], [v26, v25], bme, loopCut=loopCut) # add half-cylinder insets on slope underside if detail in ("MEDIUM", "HIGH") and max(adjustedBrickSize[:2]) == 3: # TODO: Rewrite this as dedicated function # TODO: Add loopCut functionality for half-cylinder insets addSlopeStuds( dimensions, height, [2, min(adjustedBrickSize[:2]), adjustedBrickSize[2]], brickType, circleVerts, bme, edgeXp=[v3, v4], edgeXn=[v9, v10], edgeYp=[v4, v9], edgeYn=[v10, v3], underside=True, loopCut=loopCut) # translate slope to adjust for flipped brick for v in bme.verts: v.co.y -= d.y * (scalar.y - 1) if direction in ("X-", "Y+") else 0 v.co.x -= d.x * (scalar.x - 1) if direction in ("X-", "Y-") else 0 # rotate slope to the appropriate orientation mult = directions.index(direction) bmesh.ops.rotate(bme, verts=bme.verts, cent=(0, 0, 0), matrix=Matrix.Rotation(math.radians(90) * mult, 3, 'Z')) return bme
def draw_callback1_px(self, context): # Getting the first object and face that raycast hits: mode = self.mode np_print('mode:', mode) region = self.region np_print('region', region) rv3d = self.rv3d np_print('rv3d', rv3d) co2d = self.co2d np_print('self.co2d', co2d) co3d = 1 center = Vector((0, 0, 0)) normal = Vector((0.0, 0.0, 1.0)) scenecast = scene_cast(region, rv3d, co2d) np_print('scenecast', scenecast) hitob = scenecast[4] np_print('hitob', hitob) if mode == 0: instruct = 'paint material on object / objects' elif mode == 1: instruct = 'pick material to paint with' elif mode == 2: instruct = 'pick material to paint with' elif mode == 3: instruct = 'paint material on single face' elif mode == 4: instruct = 'paint material on object / objects' if hitob is not None: acob = bpy.context.view_layer.objects.active bpy.context.view_layer.objects.active = hitob slots = hitob.material_slots for i, s in enumerate(slots): hitob.active_material_index = i bpy.ops.object.material_slot_remove() if self.shader is not None: hitob.data.materials.append(self.shader) np_print(hitob.select) if hitob.select_get() is True: np_print('true') for ob in self.selob: bpy.context.view_layer.objects.active = ob slots = ob.material_slots for i, s in enumerate(slots): ob.active_material_index = i bpy.ops.object.material_slot_remove() if self.shader is not None: ob.data.materials.append(self.shader) bpy.context.view_layer.objects.active = acob #bpy.context.scene.update() np_print('040') elif mode == 5: instruct = 'pick material to paint with' if hitob is not None: mat = None findex = scenecast[3] np_print('findex', findex) me = hitob.data np_print('me', me) bme = bmesh.new() bme.from_mesh(me) bme.faces.ensure_lookup_table() np_print('faces', bme.faces) np_print('face', bme.faces[findex]) bmeface = bme.faces[findex] matindex = bmeface.material_index np_print('material_index', matindex) slots = list(hitob.material_slots) np_print('slots', slots) np_print('len slots', len(slots)) np_print('slots', slots) if len(slots) == 0: self.shader = None self.shadername = 'None' elif slots[matindex].material is not None: self.shader = slots[matindex].material self.shadername = slots[matindex].material.name else: self.shader = None self.shadername = 'None' bpy.context.view_layer.objects.active = hitob hitob.active_material_index = matindex np_print('self.shader', self.shader) np_print('050') else: self.shader = None self.shadername = 'None' elif mode == 6: instruct = 'pick material to paint with' elif mode == 7: instruct = 'paint material on single face' if hitob is not None: acob = bpy.context.view_layer.objects.active bpy.context.view_layer.objects.active = hitob findex = scenecast[3] np_print('findex', findex) me = hitob.data np_print('me', me) bpy.ops.object.mode_set(mode='EDIT') bme = bmesh.from_edit_mesh(me) bme.faces.ensure_lookup_table() np_print('faces', bme.faces) np_print('face', bme.faces[findex]) bmeface = bme.faces[findex] slots = list(hitob.material_slots) np_print('slots', list(slots)) m = 0 if list(slots) == []: hitob.data.materials.append(None) slots = list(hitob.material_slots) for i, s in enumerate(slots): np_print('s', s) if s.name == self.shadername: m = 1 matindex = i elif s.material == None and self.shader == None: m = 1 matindex = i if m == 0: hitob.data.materials.append(self.shader) matindex = len(slots) - 1 hitob.active_material_index = matindex bpy.context.tool_settings.mesh_select_mode = False, False, True bpy.ops.mesh.select_all(action='DESELECT') bmeface.select = True bmesh.update_edit_mesh(me, True) bpy.ops.object.material_slot_assign() bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.mode_set(mode='OBJECT') bpy.context.view_layer.objects.active = acob # ON-SCREEN INSTRUCTIONS: keys_aff = 'LMB - paint object / objects, CTRL - take material, ALT - paint single face' keys_nav = 'MMB, SCROLL - navigate' keys_neg = 'ESC - quit' display_instructions(region, rv3d, instruct, keys_aff, keys_nav, keys_neg) col_bg_fill_main_run = addon_settings_graph('col_bg_fill_main_run') col_bg_fill_main_nav = addon_settings_graph('col_bg_fill_main_nav') # Drawing the small icon near the cursor and the shader name: # First color is for lighter themes, second for default and darker themes: if mode in {1, 2, 5, 6}: square = [[17, 30], [17, 39], [26, 39], [26, 30]] cur = [[17, 36], [18, 35], [14, 31], [14, 30], [15, 30], [19, 34], [18, 35], [20, 37], [21, 37], [21, 36], [19, 34], [20, 33]] curpipe = [[18, 35], [14, 31], [14, 30], [15, 30], [19, 34], [18, 35]] curcap = [[18, 35], [20, 37], [21, 37], [21, 36], [19, 34], [18, 35]] scale = 2.8 col_square = col_bg_fill_main_run for co in square: co[0] = round((co[0] * scale), 0) - 35 + co2d[0] co[1] = round((co[1] * scale), 0) - 88 + co2d[1] for co in cur: co[0] = round((co[0] * scale), 0) - 25 + co2d[0] co[1] = round((co[1] * scale), 0) - 86 + co2d[1] for co in curcap: co[0] = round((co[0] * scale), 0) - 25 + co2d[0] co[1] = round((co[1] * scale), 0) - 86 + co2d[1] for co in curpipe: co[0] = round((co[0] * scale), 0) - 25 + co2d[0] co[1] = round((co[1] * scale), 0) - 86 + co2d[1] bgl.glColor4f(*col_square) bgl.glBegin(bgl.GL_TRIANGLE_FAN) for x, y in square: bgl.glVertex2f(x, y) bgl.glEnd() #bgl.glColor4f(1.0, 1.0, 1.0, 1.0) bgl.glColor4f(1.0, 1.0, 1.0, 0.8) bgl.glBegin(bgl.GL_LINE_STRIP) for x, y in cur: bgl.glVertex2f(x, y) bgl.glEnd() if mode in {2, 6}: bgl.glColor4f(1.0, 0.5, 0.5, 1.0) bgl.glBegin(bgl.GL_TRIANGLE_FAN) for x, y in curcap: bgl.glVertex2f(x, y) bgl.glEnd() else: square = [[17, 30], [17, 39], [26, 39], [26, 30]] cur = [[18, 30], [21, 33], [18, 36], [14, 32], [16, 32], [18, 30]] curtip = [[14, 32], [16, 32], [15, 33], [14, 32]] curbag = [[18, 30], [15, 33], [21, 33], [18, 30]] scale = 2.8 if mode in (3, 7): col_square = col_bg_fill_main_nav else: col_square = (0.25, 0.35, 0.4, 0.87) for co in square: co[0] = round((co[0] * scale), 0) - 35 + co2d[0] co[1] = round((co[1] * scale), 0) - 88 + co2d[1] for co in cur: co[0] = round((co[0] * scale), 0) - 25 + co2d[0] co[1] = round((co[1] * scale), 0) - 84 + co2d[1] for co in curtip: co[0] = round((co[0] * scale), 0) - 25 + co2d[0] co[1] = round((co[1] * scale), 0) - 84 + co2d[1] for co in curbag: co[0] = round((co[0] * scale), 0) - 25 + co2d[0] co[1] = round((co[1] * scale), 0) - 84 + co2d[1] bgl.glColor4f(*col_square) bgl.glBegin(bgl.GL_TRIANGLE_FAN) for x, y in square: bgl.glVertex2f(x, y) bgl.glEnd() #bgl.glColor4f(1.0, 1.0, 1.0, 1.0) bgl.glColor4f(1.0, 1.0, 1.0, 0.8) bgl.glBegin(bgl.GL_LINE_STRIP) for x, y in cur: bgl.glVertex2f(x, y) bgl.glEnd() ''' if mode in {3, 7}: bgl.glBegin(bgl.GL_TRIANGLE_FAN) for x,y in curtip: bgl.glVertex2f(x,y) bgl.glEnd() bgl.glLineWidth(2) bgl.glBegin(bgl.GL_LINE_STRIP) for x,y in curtip: bgl.glVertex2f(x,y) bgl.glEnd() bgl.glLineWidth(1) ''' if self.shader is not None: bgl.glBegin(bgl.GL_TRIANGLE_FAN) for x, y in curbag: bgl.glVertex2f(x, y) bgl.glEnd() bgl.glColor4f(0.25, 0.35, 0.4, 0.87) font_id = 0 blf.size(font_id, 15, 72) blf.position(font_id, co2d[0] + 47, co2d[1] - 3, 0) blf.draw(font_id, self.shadername) bgl.glColor4f(1.0, 1.0, 1.0, 0.8) font_id = 0 blf.size(font_id, 15, 72) blf.position(font_id, co2d[0] + 46, co2d[1] - 2, 0) blf.draw(font_id, self.shadername) # restore opengl defaults bgl.glLineWidth(1) bgl.glDisable(bgl.GL_BLEND) bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
def execute(self, context): #Set cursor representation to 'loading' icon w = context.window w.cursor_set('WAIT') t0 = time.clock() #Toogle object mode and deselect all try: bpy.ops.object.mode_set(mode='OBJECT') except: pass bpy.ops.object.select_all(action='DESELECT') #Path shpName = os.path.basename(self.filepath)[:-4] #Get shp reader print("Read shapefile...") try: shp = shpReader(self.filepath) except: self.report({'ERROR'}, "Unable to read shapefile") return {'FINISHED'} #Check shape type shpType = featureType[shp.shapeType] print('Feature type : ' + shpType) if shpType not in [ 'Point', 'PolyLine', 'Polygon', 'PointZ', 'PolyLineZ', 'PolygonZ' ]: self.report({ 'ERROR' }, "Cannot process multipoint, multipointZ, pointM, polylineM, polygonM and multipatch feature type" ) return {'FINISHED'} #Get fields fields = [field for field in shp.fields if field[0] != 'DeletionFlag' ] #ignore default DeletionFlag field fieldsNames = [field[0] for field in fields] #print("DBF fields : "+str(fieldsNames)) if self.fieldElevName or (self.fieldObjName and self.separateObjects ) or self.fieldExtrudeName: self.useDbf = True else: self.useDbf = False if self.fieldObjName and self.separateObjects: try: nameFieldIdx = fieldsNames.index(self.fieldObjName) except: self.report({'ERROR'}, "Unable to find name field") return {'FINISHED'} if self.fieldElevName: try: zFieldIdx = fieldsNames.index(self.fieldElevName) except: self.report({'ERROR'}, "Unable to find elevation field") return {'FINISHED'} if fields[zFieldIdx][1] not in ['N', 'F', 'L']: self.report({'ERROR'}, "Elevation field do not contains numeric values") return {'FINISHED'} if self.fieldExtrudeName: try: extrudeFieldIdx = fieldsNames.index(self.fieldExtrudeName) except ValueError: self.report({'ERROR'}, "Unable to find extrusion field") return {'FINISHED'} if fields[extrudeFieldIdx][1] not in ['N', 'F', 'L']: self.report({'ERROR'}, "Extrusion field do not contains numeric values") return {'FINISHED'} #Get shp and scene georef infos shpCRS = self.shpCRS geoscn = GeoScene() if geoscn.isBroken: self.report({'ERROR'}, "Scene georef is broken, please fix it beforehand") return {'FINISHED'} scale = geoscn.scale #TODO if not geoscn.hasCRS: try: geoscn.crs = shpCRS except Exception as e: self.report({'ERROR'}, str(e)) return {'FINISHED'} #Init reprojector class if geoscn.crs != shpCRS: print("Data will be reprojected from " + shpCRS + " to " + geoscn.crs) try: rprj = Reproj(shpCRS, geoscn.crs) except Exception as e: self.report({'ERROR'}, "Unable to reproject data. " + str(e)) return {'FINISHED'} if rprj.iproj == 'EPSGIO': if shp.numRecords > 100: self.report({ 'ERROR' }, "Reprojection through online epsg.io engine is limited to 100 features. \nPlease install GDAL or pyproj module." ) return {'FINISHED'} #Get bbox bbox = BBOX(shp.bbox) if geoscn.crs != shpCRS: bbox = rprj.bbox(bbox) #Get or set georef dx, dy if not geoscn.isGeoref: dx, dy = bbox.center geoscn.setOriginPrj(dx, dy) else: dx, dy = geoscn.getOriginPrj() #Tag if z will be extracted from shp geoms if shpType[-1] == 'Z' and not self.fieldElevName: self.useZGeom = True else: self.useZGeom = False #Get reader iterator (using iterator avoids loading all data in memory) #warn, shp with zero field will return an empty shapeRecords() iterator #to prevent this issue, iter only on shapes if there is no field required if self.useDbf: #Note: using shapeRecord solve the issue where number of shapes does not match number of table records #because it iter only on features with geom and record shpIter = shp.iterShapeRecords() else: shpIter = shp.iterShapes() nbFeats = shp.numRecords #Create an empty BMesh bm = bmesh.new() #Extrusion is exponentially slow with large bmesh #it's fastest to extrude a small bmesh and then join it to a final large bmesh if not self.separateObjects and self.fieldExtrudeName: finalBm = bmesh.new() progress = -1 #Main iteration over features for i, feat in enumerate(shpIter): if self.useDbf: shape = feat.shape record = feat.record else: shape = feat #Progress infos pourcent = round(((i + 1) * 100) / nbFeats) if pourcent in list(range(0, 110, 10)) and pourcent != progress: progress = pourcent if pourcent == 100: print(str(pourcent) + '%') else: print(str(pourcent), end="%, ") sys.stdout.flush( ) #we need to flush or it won't print anything until after the loop has finished #Deal with multipart features #If the shape record has multiple parts, the 'parts' attribute will contains the index of #the first point of each part. If there is only one part then a list containing 0 is returned if (shpType == 'PointZ' or shpType == 'Point'): #point layer has no attribute 'parts' partsIdx = [0] else: try: #prevent "_shape object has no attribute parts" error partsIdx = shape.parts except: partsIdx = [0] nbParts = len(partsIdx) #Get list of shape's points pts = shape.points nbPts = len(pts) #Skip null geom if nbPts == 0: continue #go to next iteration of the loop #Reproj geom if geoscn.crs != shpCRS: pts = rprj.pts(pts) #Get extrusion offset if self.fieldExtrudeName: try: offset = float(record[extrudeFieldIdx]) except: offset = 0 #null values will be set to zero #Iter over parts for j in range(nbParts): # EXTRACT 3D GEOM geom = [] #will contains a list of 3d points #Find first and last part index idx1 = partsIdx[j] if j + 1 == nbParts: idx2 = nbPts else: idx2 = partsIdx[j + 1] #Build 3d geom for k, pt in enumerate(pts[idx1:idx2]): if self.fieldElevName: try: z = float(record[zFieldIdx]) except: z = 0 #null values will be set to zero elif self.useZGeom: z = shape.z[idx1:idx2][k] else: z = 0 geom.append((pt[0], pt[1], z)) #Shift coords geom = [(pt[0] - dx, pt[1] - dy, pt[2]) for pt in geom] # BUILD BMESH # POINTS if (shpType == 'PointZ' or shpType == 'Point'): vert = [bm.verts.new(pt) for pt in geom] #Extrusion if self.fieldExtrudeName and offset > 0: vect = (0, 0, offset) #along Z result = bmesh.ops.extrude_vert_indiv(bm, verts=vert) verts = result['verts'] bmesh.ops.translate(bm, verts=verts, vec=vect) # LINES if (shpType == 'PolyLine' or shpType == 'PolyLineZ'): #Split polyline to lines n = len(geom) lines = [(geom[i], geom[i + 1]) for i in range(n) if i < n - 1] #Build edges edges = [] for line in lines: verts = [bm.verts.new(pt) for pt in line] edge = bm.edges.new(verts) edges.append(edge) #Extrusion if self.fieldExtrudeName and offset > 0: vect = (0, 0, offset) # along Z result = bmesh.ops.extrude_edge_only(bm, edges=edges) verts = [ elem for elem in result['geom'] if isinstance(elem, bmesh.types.BMVert) ] bmesh.ops.translate(bm, verts=verts, vec=vect) # NGONS if (shpType == 'Polygon' or shpType == 'PolygonZ'): #According to the shapefile spec, polygons points are clockwise and polygon holes are counterclockwise #in Blender face is up if points are in anticlockwise order geom.reverse() #face up geom.pop( ) #exlude last point because it's the same as first pt if len(geom) >= 3: #needs 3 points to get a valid face verts = [bm.verts.new(pt) for pt in geom] face = bm.faces.new(verts) #update normal to avoid null vector face.normal_update() if face.normal.z < 0: #this is a polygon hole, bmesh cannot handle polygon hole pass #TODO #Extrusion if self.fieldExtrudeName and offset > 0: #build translate vector if self.extrusionAxis == 'NORMAL': normal = face.normal vect = normal * offset elif self.extrusionAxis == 'Z': vect = (0, 0, offset) faces = bmesh.ops.extrude_discrete_faces( bm, faces=[face]) #return {'faces': [BMFace]} verts = faces['faces'][0].verts ##result = bmesh.ops.extrude_face_region(bm, geom=[face]) #return dict {"geom":[BMVert, BMEdge, BMFace]} ##verts = [elem for elem in result['geom'] if isinstance(elem, bmesh.types.BMVert)] #geom type filter bmesh.ops.translate(bm, verts=verts, vec=vect) if self.separateObjects: if self.fieldObjName: try: name = record[nameFieldIdx] except: name = '' # null values will return a bytes object containing a blank string of length equal to fields length definition if isinstance(name, bytes): name = '' else: name = str(name) else: name = shpName #Calc bmesh bbox _bbox = BBOX.fromBmesh(bm) #Calc bmesh geometry origin and translate coords according to it #then object location will be set to initial bmesh origin #its a work around to bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') ox, oy, oz = _bbox.center oz = _bbox.zmin bmesh.ops.translate(bm, verts=bm.verts, vec=(-ox, -oy, -oz)) #Create new mesh from bmesh mesh = bpy.data.meshes.new(name) bm.to_mesh(mesh) bm.clear() #Validate new mesh mesh.validate(verbose=False) #Place obj obj = bpy.data.objects.new(name, mesh) context.scene.objects.link(obj) context.scene.objects.active = obj obj.select = True obj.location = (ox, oy, oz) # bpy operators can be very cumbersome when scene contains lot of objects # because it cause implicit scene updates calls # so we must avoid using operators when created many objects with the 'separate objects' option) ##bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') elif self.fieldExtrudeName: #Join to final bmesh (use from_mesh method hack) buff = bpy.data.meshes.new(".temp") bm.to_mesh(buff) finalBm.from_mesh(buff) bpy.data.meshes.remove(buff) bm.clear() #Write back the whole mesh if not self.separateObjects: mesh = bpy.data.meshes.new(shpName) if self.fieldExtrudeName: bm.free() bm = finalBm bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001) bm.to_mesh(mesh) #Finish #mesh.update(calc_edges=True) mesh.validate( verbose=False) #return true if the mesh has been corrected obj = bpy.data.objects.new(shpName, mesh) context.scene.objects.link(obj) context.scene.objects.active = obj obj.select = True bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') #free the bmesh bm.free() t = time.clock() - t0 print('Build in %f seconds' % t) #Adjust grid size bbox.shift(-dx, -dy) #convert shapefile bbox in 3d view space adjust3Dview(context, bbox) return {'FINISHED'}
def cell_fracture_objects(context, collection, obj, source={'PARTICLE_OWN'}, source_limit=0, source_noise=0.0, clean=True, # operator options use_smooth_faces=False, use_data_match=False, use_debug_points=False, margin=0.0, material_index=0, use_debug_redraw=False, cell_scale=(1.0, 1.0, 1.0), ): from . import fracture_cell_calc depsgraph = context.evaluated_depsgraph_get() scene = context.scene view_layer = context.view_layer # ------------------------------------------------------------------------- # GET POINTS points = _points_from_object(depsgraph, scene, obj, source) if not points: # print using fallback points = _points_from_object(depsgraph, scene, obj, {'VERT_OWN'}) if not points: print("no points found") return [] # apply optional clamp if source_limit != 0 and source_limit < len(points): import random random.shuffle(points) points[source_limit:] = [] # saddly we cant be sure there are no doubles from mathutils import Vector to_tuple = Vector.to_tuple points = list({to_tuple(p, 4): p for p in points}.values()) del to_tuple del Vector # end remove doubles # ------------------ if source_noise > 0.0: from random import random # boundbox approx of overall scale from mathutils import Vector matrix = obj.matrix_world.copy() bb_world = [matrix @ Vector(v) for v in obj.bound_box] scalar = source_noise * ((bb_world[0] - bb_world[6]).length / 2.0) from mathutils.noise import random_unit_vector points[:] = [p + (random_unit_vector() * (scalar * random())) for p in points] if use_debug_points: bm = bmesh.new() for p in points: bm.verts.new(p) mesh_tmp = bpy.data.meshes.new(name="DebugPoints") bm.to_mesh(mesh_tmp) bm.free() obj_tmp = bpy.data.objects.new(name=mesh_tmp.name, object_data=mesh_tmp) collection.objects.link(obj_tmp) del obj_tmp, mesh_tmp mesh = obj.data matrix = obj.matrix_world.copy() verts = [matrix @ v.co for v in mesh.vertices] cells = fracture_cell_calc.points_as_bmesh_cells(verts, points, cell_scale, margin_cell=margin) # some hacks here :S cell_name = obj.name + "_cell" objects = [] for center_point, cell_points in cells: # --------------------------------------------------------------------- # BMESH # create the convex hulls bm = bmesh.new() # WORKAROUND FOR CONVEX HULL BUG/LIMIT # XXX small noise import random def R(): return (random.random() - 0.5) * 0.001 # XXX small noise for i, co in enumerate(cell_points): # XXX small noise co.x += R() co.y += R() co.z += R() # XXX small noise bm_vert = bm.verts.new(co) import mathutils bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.005) try: bmesh.ops.convex_hull(bm, input=bm.verts) except RuntimeError: import traceback traceback.print_exc() if clean: bm.normal_update() try: bmesh.ops.dissolve_limit(bm, verts=bm.verts, angle_limit=0.001) except RuntimeError: import traceback traceback.print_exc() # Smooth faces will remain only inner faces, after applying boolean modifier. if use_smooth_faces: for bm_face in bm.faces: bm_face.smooth = True if material_index != 0: for bm_face in bm.faces: bm_face.material_index = material_index # --------------------------------------------------------------------- # MESH mesh_dst = bpy.data.meshes.new(name=cell_name) bm.to_mesh(mesh_dst) bm.free() del bm if use_data_match: # match materials and data layers so boolean displays them # currently only materials + data layers, could do others... mesh_src = obj.data for mat in mesh_src.materials: mesh_dst.materials.append(mat) for lay_attr in ("vertex_colors", "uv_layers"): lay_src = getattr(mesh_src, lay_attr) lay_dst = getattr(mesh_dst, lay_attr) for key in lay_src.keys(): lay_dst.new(name=key) # --------------------------------------------------------------------- # OBJECT obj_cell = bpy.data.objects.new(name=cell_name, object_data=mesh_dst) collection.objects.link(obj_cell) # scene.objects.active = obj_cell obj_cell.location = center_point objects.append(obj_cell) # support for object materials if use_data_match: for i in range(len(mesh_dst.materials)): slot_src = obj.material_slots[i] slot_dst = obj_cell.material_slots[i] slot_dst.link = slot_src.link slot_dst.material = slot_src.material if use_debug_redraw: view_layer.update() _redraw_yasiamevil() view_layer.update() return objects
def scene_to_ge1(context, scene): # TODO use BMesh """Export scene geometry in FDS GE1 notation.""" # Cursor w = context.window_manager.windows[0] w.cursor_modal_set("WAIT") # Get GE1 appearances from materials appearances = list() ma_to_appearance = dict() for index, ma in enumerate(bpy.data.materials): ma_to_appearance[ma.name] = index appearances.append( "{desc}\n{i} {r} {g} {b} 0. 0. {alpha:.3f} 0. 0. 0.\n\n".format( desc=ma.name, i=index, r=int(ma.diffuse_color[0] * 255), g=int(ma.diffuse_color[1] * 255), b=int(ma.diffuse_color[2] * 255), alpha=0.0, # FIXME alpha=ma.alpha, )) # Append dummy material for holes: BF_HOLE ma_to_appearance["BF_HOLE"] = index + 1 appearances.append( "{desc}\n{i} {r} {g} {b} 0. 0. {alpha:.3f} 0. 0. 0.\n\n".format( desc="BF_HOLE", i=index + 1, r=150, g=150, b=150, alpha=0.5)) # Select GE1 objects obs = ( ob for ob in context.scene.objects if ob.type == "MESH" and not ob.hide_render # hide some objects if requested and not ob.bf_is_tmp # do not show temporary objects and ob.bf_export # show only exported objects and ob.bf_namelist_cls in ("ON_OBST", "ON_GEOM", "ON_VENT", "ON_HOLE") # show only some namelists and getattr(ob.active_material, "name", None) != "OPEN" # do not show open VENTs ) # Get GE1 faces from selected objects gefaces = list() for ob in obs: # Get the new bmesh from the Object, apply modifiers, set in world coordinates, and triangulate bm = bmesh.new() bm.from_object(ob, context.scene, deform=True, render=False, cage=False, face_normals=True) bm.transform(ob.matrix_world) bmesh.ops.triangulate(bm, faces=bm.faces) # Get ob material_slots material_slots = ob.material_slots # Get default_material_name if ob.bf_namelist_cls == "ON_HOLE": default_material_name = "BF_HOLE" elif ob.bf_namelist_cls == "ON_GEOM": default_material_name = None elif ob.active_material: default_material_name = ob.active_material.name else: default_material_name = "INERT" scale_length = context.scene.unit_settings.scale_length for f in bm.faces: # Grab ordered vertices coordinates coos = [co for v in f.verts for co in v.co] coos.extend((coos[-3], coos[-2], coos[-1])) # tri to quad items = ["{:.6f}".format(coo * scale_length) for coo in coos] # Get appearance_index if default_material_name: material_name = default_material_name else: material_name = material_slots[f.material_index].material.name appearance_index = str(ma_to_appearance.get(material_name, 0)) + "\n" items.append(appearance_index) # Append GE1 face gefaces.append(" ".join(items)) # Prepare GE1 file and return ge1_file_a = "[APPEARANCE]\n{}\n{}".format(len(appearances), "".join(appearances)) ge1_file_f = "[FACES]\n{}\n{}".format(len(gefaces), "".join(gefaces)) w.cursor_modal_restore() return "".join((ge1_file_a, ge1_file_f))
def cell_boolean(context, original, cells, use_debug_bool=False, clean=True, use_island_split=False, use_interior_hide=False, use_debug_redraw=False, level=0, remove_doubles=True): cells_boolean = [] collection = context.collection scene = context.scene view_layer = context.view_layer if use_interior_hide and level == 0: # only set for level 0 original.data.polygons.foreach_set("hide", [False] * len(original.data.polygons)) # The first object can't be applied by bool, so it is used as a no-effect first straw-man. bpy.ops.mesh.primitive_cube_add(enter_editmode=False, location=(original.location.x + 10000000000.0, 0, 0)) temp_cell = bpy.context.active_object cells.insert(0, temp_cell) bpy.ops.object.select_all(action='DESELECT') for i, cell in enumerate(cells): mod = cell.modifiers.new(name="Boolean", type='BOOLEAN') mod.object = original mod.operation = 'INTERSECT' if not use_debug_bool: if use_interior_hide: cell.data.polygons.foreach_set("hide", [True] * len(cell.data.polygons)) # mesh_old should be made before appling boolean modifier. mesh_old = cell.data original.select_set(True) cell.select_set(True) bpy.context.view_layer.objects.active = cell bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Boolean") if i == 0: bpy.data.objects.remove(cell, do_unlink=True) continue cell = bpy.context.active_object cell.select_set(False) # depsgraph sould be gotten after applied boolean modifier, for new_mesh. depsgraph = context.evaluated_depsgraph_get() cell_eval = cell.evaluated_get(depsgraph) mesh_new = bpy.data.meshes.new_from_object(cell_eval) cell.data = mesh_new ''' check_hide = [11] * len(cell.data.polygons) cell.data.polygons.foreach_get("hide", check_hide) print(check_hide) ''' # remove if not valid if not mesh_old.users: bpy.data.meshes.remove(mesh_old) if not mesh_new.vertices: collection.objects.unlink(cell) if not cell.users: bpy.data.objects.remove(cell) cell = None if not mesh_new.users: bpy.data.meshes.remove(mesh_new) mesh_new = None # avoid unneeded bmesh re-conversion if mesh_new is not None: bm = None if clean: if bm is None: # ok this will always be true for now... bm = bmesh.new() bm.from_mesh(mesh_new) bm.normal_update() try: bmesh.ops.dissolve_limit(bm, verts=bm.verts, edges=bm.edges, angle_limit=0.001) except RuntimeError: import traceback traceback.print_exc() if remove_doubles: if bm is None: bm = bmesh.new() bm.from_mesh(mesh_new) bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.005) if bm is not None: bm.to_mesh(mesh_new) bm.free() del mesh_new del mesh_old if cell is not None: cells_boolean.append(cell) if use_debug_redraw: _redraw_yasiamevil() bpy.context.view_layer.objects.active = original if (not use_debug_bool) and use_island_split: # this is ugly and Im not proud of this - campbell for ob in view_layer.objects: ob.select_set(False) for cell in cells_boolean: cell.select_set(True) # If new separated meshes are made, selected objects is increased. if cells_boolean: bpy.ops.mesh.separate(type='LOOSE') cells_boolean[:] = [ cell for cell in scene.objects if cell.select_get() ] context.view_layer.update() return cells_boolean
def import_gnd(filepath, options: GndImportOptions): gnd = GndReader.from_file(filepath) name = os.path.splitext(os.path.basename(filepath))[0] directory_name = os.path.dirname(filepath) mesh = bpy.data.meshes.new(name) mesh_object = bpy.data.objects.new(name, mesh) if options.should_import_lightmaps: ''' Generate light map image. ''' lightmap_size = int( math.ceil(math.sqrt(len(gnd.lightmaps) * 64) / 8) * 8) lightmap_tiles_per_dimension = lightmap_size / 8 pixel_count = lightmap_size * lightmap_size pixels = [0.0] * (pixel_count * 4) for i, lightmap in enumerate(gnd.lightmaps): x, y = int(i % lightmap_tiles_per_dimension) * 8, int( i / lightmap_tiles_per_dimension) * 8 for y2 in range(8): for x2 in range(8): idx = y2 * 8 + x2 lum = lightmap.luminosity[idx] j = int(((y + y2) * lightmap_size) + (x + x2)) * 4 r = lum / 255.0 pixels[j + 0] = r pixels[j + 1] = r pixels[j + 2] = r pixels[j + 3] = 1.0 lightmap_image = bpy.data.images.new('lightmap', lightmap_size, lightmap_size) lightmap_image.pixels = pixels ''' Create light map texture. ''' lightmap_texture = bpy.data.textures.new('lightmap', type='IMAGE') lightmap_texture.image = lightmap_image ''' Create materials. ''' materials = [] for i, texture in enumerate(gnd.textures): texture_path = texture.path material = bpy.data.materials.new(texture_path) material.diffuse_intensity = 1.0 material.specular_intensity = 0.0 materials.append(material) ''' Load diffuse texture. ''' diffuse_texture = bpy.data.textures.new(texture_path, type='IMAGE') data_path = get_data_path(directory_name) texpath = os.path.join(data_path, 'texture', texture_path) try: diffuse_texture.image = bpy.data.images.load( texpath, check_existing=True) except RuntimeError: pass ''' Add diffuse texture slot to material. ''' diffuse_texture_slot = material.texture_slots.add() diffuse_texture_slot.texture = diffuse_texture if options.should_import_lightmaps: ''' Add light map texture slot to material.''' lightmap_texture_slot = material.texture_slots.add() lightmap_texture_slot.texture = lightmap_texture lightmap_texture_slot.diffuse_color_factor = options.lightmap_factor lightmap_texture_slot.blend_type = 'MULTIPLY' bm = bmesh.new() bm.from_mesh(mesh) for y in range(gnd.height): for x in range(gnd.width): tile_index = y * gnd.width + x tile = gnd.tiles[tile_index] if tile.face_indices[0] != -1: # +Z bm.verts.new( ((x + 0) * gnd.scale, (y + 0) * gnd.scale, -tile[0])) bm.verts.new( ((x + 1) * gnd.scale, (y + 0) * gnd.scale, -tile[1])) bm.verts.new( ((x + 1) * gnd.scale, (y + 1) * gnd.scale, -tile[3])) bm.verts.new( ((x + 0) * gnd.scale, (y + 1) * gnd.scale, -tile[2])) if tile.face_indices[1] != -1: # +Y adjacent_tile = gnd.tiles[tile_index + gnd.width] bm.verts.new( ((x + 0) * gnd.scale, (y + 1) * gnd.scale, -tile[2])) bm.verts.new( ((x + 1) * gnd.scale, (y + 1) * gnd.scale, -tile[3])) bm.verts.new(((x + 1) * gnd.scale, (y + 1) * gnd.scale, -adjacent_tile[1])) bm.verts.new(((x + 0) * gnd.scale, (y + 1) * gnd.scale, -adjacent_tile[0])) if tile.face_indices[2] != -1: # +X adjacent_tile = gnd.tiles[tile_index + 1] bm.verts.new( ((x + 1) * gnd.scale, (y + 1) * gnd.scale, -tile[3])) bm.verts.new( ((x + 1) * gnd.scale, (y + 0) * gnd.scale, -tile[1])) bm.verts.new(((x + 1) * gnd.scale, (y + 0) * gnd.scale, -adjacent_tile[0])) bm.verts.new(((x + 1) * gnd.scale, (y + 1) * gnd.scale, -adjacent_tile[2])) bm.verts.ensure_lookup_table() vertex_offset = 0 for y in range(gnd.height): for x in range(gnd.width): tile_index = y * gnd.width + x tile = gnd.tiles[tile_index] for face_index in filter(lambda x: x >= 0, tile.face_indices): face = gnd.faces[face_index] vertex_indices = [vertex_offset + i for i in range(4)] bmface = bm.faces.new( [bm.verts[x] for x in vertex_indices]) bmface.material_index = face.texture_index vertex_offset += 4 bm.faces.ensure_lookup_table() bm.to_mesh(mesh) ''' Add materials to mesh. ''' uv_texture = mesh.uv_textures.new() lightmap_uv_texture = mesh.uv_textures.new() for material in materials: ''' Create UV map. ''' mesh.materials.append(material) material.texture_slots[0].uv_layer = uv_texture.name if options.should_import_lightmaps: material.texture_slots[1].uv_layer = lightmap_uv_texture.name ''' Assign texture coordinates. ''' uv_texture = mesh.uv_layers[0] lightmap_uv_layer = mesh.uv_layers[1] for face_index, face in enumerate(gnd.faces): uvs = list(face.uvs) ''' Since we are adding quads and not triangles, we need to add the UVs in quad clockwise winding order. ''' uvs = [uvs[x] for x in [0, 1, 3, 2]] for i, uv in enumerate(uvs): # UVs have to be V-flipped uv = uv[0], 1.0 - uv[1] uv_texture.data[face_index * 4 + i].uv = uv if options.should_import_lightmaps: x1 = (face.lightmap_index % lightmap_tiles_per_dimension ) / lightmap_tiles_per_dimension y1 = int(face.lightmap_index / lightmap_tiles_per_dimension ) / lightmap_tiles_per_dimension x2 = x1 + (1.0 / lightmap_tiles_per_dimension) y2 = y1 + (1.0 / lightmap_tiles_per_dimension) lightmap_uvs = [(x1, y1), (x2, y1), (x2, y2), (x1, y2)] for i, uv in enumerate(lightmap_uvs): lightmap_uv_layer.data[face_index * 4 + i].uv = uv bpy.context.scene.objects.link(mesh_object) return mesh_object
def geometry(self): bm = bmesh.new() verts = self.__class__.verts edges = self.__class__.edges faces = self.__class__.faces for n, v in enumerate(verts): bm.verts.new(v) bm.verts.ensure_lookup_table() # ensures bm.verts can be indexed bm.verts.index_update( ) # ensures all bm.verts have an index (= different thing!) if hasattr(self.__class__, 'vert_attributes'): for attr, values in self.__class__.vert_attributes.items(): for v, val in zip(bm.verts, values): setattr(v, attr, val) for n, e in enumerate(edges): edge = bm.edges.new(bm.verts[v] for v in e) bm.edges.ensure_lookup_table() bm.edges.index_update() if hasattr(self.__class__, 'edge_attributes'): for attr, values in self.__class__.edge_attributes.items(): for e, val in zip(bm.edges, values): setattr(e, attr, val) for n, f in enumerate(faces): bm.faces.new(bm.verts[v] for v in f) bm.faces.ensure_lookup_table() bm.faces.index_update() if hasattr(self.__class__, 'face_attributes'): for attr, values in self.__class__.face_attributes.items(): for f, val in zip(bm.faces, values): setattr(f, attr, val) for etype in ('verts', 'edges', 'faces'): seq = getattr(bm, etype) bmlayers = seq.layers if hasattr(self.__class__, etype + '_layers'): for layertype, layers in getattr(self.__class__, etype + '_layers').items(): bmlayertype = getattr(bmlayers, layertype) for layername, values in layers.items(): bmlayer = bmlayertype.new( layername ) # fresh object so we don't check if the layer already exists valmap = self.valmap(seq[0][bmlayer]) for n, ele in enumerate(seq): if valmap is None: ele[bmlayer] = values[n] else: valmap(ele[bmlayer], values[n]) bmlayers = bm.loops.layers if hasattr(self.__class__, 'loops_layers'): for layertype, layers in getattr(self.__class__, 'loops_layers').items(): bmlayertype = getattr(bmlayers, layertype) attrname = self.__class__.attributemapping[ layertype] if layertype in self.__class__.attributemapping else None for layername, values in layers.items(): bmlayer = bmlayertype.new( layername ) # fresh object so we don't check if the layer already exists # we assume all loops will be numbered in ascending order # bm.faces[i].loops.index_update() is of no use, since it # start numbering again at 0 for this set of loops, so there # is no way to update the indices of all loop in in go, # except by converting the bm to a regular mesh, in which # case it happens automagically. loopindex = 0 for face in bm.faces: for loop in face.loops: val = values[face.index][loopindex] valmap = self.valmap(loop[bmlayer]) if valmap is None: loop[bmlayer] = val else: valmap(loop[bmlayer], val) loopindex += 1 return bm
def execute(self, context): objectselection_props = context.window_manager.objectselection_props obj = context.active_object #go in EDIT MODE to see the results as it's a mesh operation bpy.ops.object.mode_set(mode='EDIT') cpobj = objectselection_props.cuttingplane #only accept mesh if cpobj.type != 'MESH': return {'CANCELED'} bm = bmesh.new() bm.from_mesh(cpobj.data) bm.faces.ensure_lookup_table() bm.faces[0].select = True if len(bm.faces) > 1: return {'CANCELED'} bm.verts.ensure_lookup_table() v1 = cpobj.matrix_world @ bm.verts[0].co v2 = cpobj.matrix_world @ bm.verts[1].co v3 = cpobj.matrix_world @ bm.verts[2].co v4 = cpobj.matrix_world @ bm.verts[3].co nv2 = v4 - v3 nv3 = v3 - v2 vn = nv2.cross(nv3) vn.normalize() face = bm.faces[0] origin = cpobj.matrix_world @ face.calc_center_median() normal = vn #keep the manual selection saved in a vertex group if objectselection_props.rememberselection: obj.vertex_groups.new(name="prebisectselection") bpy.ops.object.vertex_group_assign() #only works in Object Mode bpy.ops.object.mode_set(mode='OBJECT') if objectselection_props.selectionoverride: #all vertices need to be selected for v in obj.data.vertices: v.select = True bpy.ops.object.mode_set(mode='EDIT') #call bisect with the selected plane bpy.ops.mesh.bisect( plane_co=origin, plane_no=normal, use_fill=objectselection_props.fill, clear_inner=objectselection_props.clearinner, clear_outer=objectselection_props.clearouter, threshold=objectselection_props.axisthreshold, ) obj.vertex_groups.new(name="bisectionloop") bpy.ops.object.vertex_group_assign() mat = obj.matrix_world sideA = obj.vertex_groups["bisectionloop"] sideA = obj.vertex_groups.new(name="FrontSide") sideB = obj.vertex_groups["bisectionloop"] sideB = obj.vertex_groups.new(name="BackSide") indexarrayA = [] for vertex in obj.data.vertices: pos = mat @ vertex.co distance = mathutils.geometry.distance_point_to_plane( pos, origin, normal) if distance > objectselection_props.axisthreshold: indexarrayA.append(vertex.index) indexarrayB = [] for vertex in obj.data.vertices: pos = mat @ vertex.co distance = mathutils.geometry.distance_point_to_plane( pos, origin, normal) if distance < objectselection_props.axisthreshold: indexarrayB.append(vertex.index) #only works in Object Mode bpy.ops.object.mode_set(mode='OBJECT') sideA.add(indexarrayA, 1.0, 'REPLACE') sideB.add(indexarrayB, 1.0, 'REPLACE') bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.vertex_group_set_active(group='bisectionloop') bpy.ops.object.vertex_group_select() bpy.ops.object.vertex_group_set_active(group='FrontSide') bpy.ops.object.vertex_group_select() bpy.ops.object.vertex_group_assign() bpy.ops.object.vertex_group_deselect() bpy.ops.object.vertex_group_set_active(group='bisectionloop') bpy.ops.object.vertex_group_select() bpy.ops.object.vertex_group_set_active(group='BackSide') bpy.ops.object.vertex_group_select() bpy.ops.object.vertex_group_assign() bpy.ops.object.vertex_group_deselect() if objectselection_props.rememberselection: bpy.ops.object.vertex_group_set_active(group='prebisectselection') bpy.ops.object.vertex_group_select() bpy.ops.object.vertex_group_set_active(group='bisectionloop') bpy.ops.object.vertex_group_deselect() bpy.ops.object.vertex_group_set_active(group='prebisectselection') bpy.ops.object.vertex_group_remove() if objectselection_props.clearouter: bpy.ops.object.vertex_group_set_active(group='FrontSide') bpy.ops.object.vertex_group_remove() bpy.ops.object.vertex_group_set_active(group='BackSide') #Object Mode needed for the selection of the vertices bpy.ops.object.mode_set(mode='OBJECT') for v in obj.data.vertices: v.select = True bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.vertex_group_assign() if objectselection_props.clearinner: bpy.ops.object.vertex_group_set_active(group='BackSide') bpy.ops.object.vertex_group_remove() bpy.ops.object.vertex_group_set_active(group='FrontSide') #Object Mode needed for the selection of the vertices bpy.ops.object.mode_set(mode='OBJECT') for v in obj.data.vertices: v.select = True bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.vertex_group_assign() #clean up bm.free() return {'FINISHED'}
def execute(self, context): obj = bpy.context.object om = obj.mode bpy.context.tool_settings.mesh_select_mode = [False, False, True] origin = Vector([0.0, 0.0, 0.0]) # bmesh operations bpy.ops.object.mode_set() bm = bmesh.new() bm.from_mesh(obj.data) sel = [f for f in bm.faces if f.select] after = [] # faces loop for i, of in enumerate(sel): nro = nrot(self, of.normal) off = vloc(self, i) loc = gloc(self, i) of.normal_update() # initial rotation noise if self.opt3 is False: rot = vrot(self, i) # initial scale noise if self.opt4 is False: s = vsca(self, i) # extrusion loop for r in range(self.num): # random probability % for extrusions if self.var4 > int(random.random() * 100): nf = of.copy() nf.normal_update() no = nf.normal.copy() # face/obj coordinates if self.opt1 is True: ce = nf.calc_center_bounds() else: ce = origin # per step rotation noise if self.opt3 is True: rot = vrot(self, i + r) # per step scale noise if self.opt4 is True: s = vsca(self, i + r) # proportional, scale * offset if self.opt2 is True: off = s * off for v in nf.verts: v.co -= ce v.co.rotate(nro) v.co.rotate(rot) v.co += ce + loc + no * off v.co = v.co.lerp(ce, 1 - s) # extrude code from TrumanBlending for a, b in zip(of.loops, nf.loops): sf = bm.faces.new((a.vert, a.link_loop_next.vert, b.link_loop_next.vert, b.vert)) sf.normal_update() bm.faces.remove(of) of = nf after.append(of) for v in bm.verts: v.select = False for e in bm.edges: e.select = False for f in after: if f not in sel: f.select = True else: f.select = False bm.to_mesh(obj.data) obj.data.update() # restore user settings bpy.ops.object.mode_set(mode=om) if not len(sel): self.report( {"WARNING"}, "No suitable Face selection found. Operation cancelled") return {'CANCELLED'} return {'FINISHED'}
def execute(self, context): if self.option_sel: objects = context.selected_objects else: objects = context.scene.objects objects = [obj for obj in objects if obj.type == 'MESH'] geo = [] fw = geo.append if not self.option_dest == 'Append': fw('// Game: Quake\n') if self.option_format == 'Valve': fw('// Format: Valve\n') fw('"mapversion" "220"\n') else: fw('// Format: Quake\n') fw('{\n"classname" "worldspawn"\n') fw('}\n') group_id = uuid.uuid4().int if self.option_geo == 'Faces' and objects != []: for obj in objects: ob = obj.evaluated_get(bpy.context.evaluated_depsgraph_get()) bm = bmesh.new() bm.from_mesh(ob.data) bmesh.ops.connect_verts_concave(bm, faces=bm.faces) if self.option_triangulate: bmesh.ops.triangulate(bm, faces=bm.faces) else: bmesh.ops.connect_verts_concave(bm, faces=bm.faces) bmesh.ops.transform(bm, matrix=obj.matrix_world * self.option_scale, verts=bm.verts) for vert in bm.verts: vert.co = self.gridsnap(vert.co) bm.faces.ensure_lookup_table() islands = [ island for island in self.get_islands(bm, verts=bm.verts) ["islands"] ] print(ob.name, "Islands:", len(islands)) facegroups = [] for i in islands: faces = [] for f in bm.faces: is_in = True for v in f.verts: if v not in i: is_in = False if is_in: faces.append(f) if len(faces) > 0: facegroups.append(faces) if len(facegroups) < 1: facegroups = [bm.faces] for num, facegroup in enumerate(facegroups): fw("{\n") fw('"classname" "func_group"\n') fw('"_phong" "1"\n') fw('"_tb_type" "_tb_group"\n') fw('"_tb_name" "' + ob.name + '_' + str(num) + '"\n') fw('"_tb_id" "' + str(group_id) + '"\n') for face in facegroup[:]: if face.calc_area() < 0.0000001: continue fw('//brush from face from object: ' + obj.name + ' sub: ' + str(num) + '\n') fw('{\n') for vert in reversed(face.verts[0:3]): fw(f'( {self.printvec(vert.co)} ) ') fw(self.texdata(face, bm, obj)) pyr = bmesh.ops.poke(bm, faces=[face], offset=-self.option_depth) apex = pyr['verts'][0].co pyr['verts'][0].co = self.gridsnap(apex) for pyrface in pyr['faces']: for vert in pyrface.verts[0:]: # backfacing fw(f'( {self.printvec(vert.co)} ) ') pyrface.material_index = len( obj.data.materials) - 1 fw(self.texdata(pyrface, bm, obj, skip=True)) fw('}\n') # end face group_id += 1 fw('}\n') # end group group_id += 1 ob.to_mesh_clear() bm.free() elif self.option_geo == 'Brushes': fw("{\n") fw('"classname" "func_group"\n') fw('"_phong" "1"\n') fw('"_tb_type" "_tb_group"\n') fw('"_tb_name" "' + os.path.basename(bpy.data.filepath.split(".")[0]) + '"\n') fw('"_tb_id" "' + str(group_id) + '"\n') for obj in objects: ob = obj.evaluated_get(bpy.context.evaluated_depsgraph_get()) bm = bmesh.new() bm.from_mesh(ob.data) #if self.option_tm: #bmesh.ops.transform(bm, matrix=obj.matrix_world, # verts=bm.verts) bm.transform(obj.matrix_world * self.option_scale) if len(bm.verts) < 3: continue for vert in bm.verts: vert.co = self.gridsnap(vert.co) hull = bmesh.ops.convex_hull(bm, input=bm.verts, use_existing_faces=True) geom = hull['geom'] + hull['geom_holes'] oldfaces = [face for face in bm.faces if face not in geom] bmesh.ops.delete(bm, geom=oldfaces, context='FACES') bmesh.ops.recalc_face_normals(bm, faces=bm.faces) bmesh.ops.join_triangles(bm, faces=bm.faces, angle_face_threshold=0.01, angle_shape_threshold=0.7) bmesh.ops.connect_verts_nonplanar(bm, faces=bm.faces, angle_limit=0.0) fw('{\n') for face in bm.faces: for vert in reversed(face.verts[0:3]): fw(f'( {self.printvec(vert.co)} ) ') fw(self.texdata(face, bm, obj)) fw('}\n') bm.free() fw('}\n') if self.option_dest == 'File': self.prevent_overwrite(self.filepath) with open(self.filepath, 'w') as file: file.write(''.join(geo)) elif self.option_dest == 'Append': self.prevent_overwrite(self.filepath) with open(self.filepath, 'a') as file: file.write(''.join(geo)) else: bpy.context.window_manager.clipboard = ''.join(geo) return {'FINISHED'}
def points_to_cells(context, original, original_xyz_minmax, points, source_limit=0, source_noise=0.0, use_smooth_faces=False, use_data_match=False, use_debug_points=False, margin=0.0, material_index=0, use_debug_redraw=False, cell_scale=(1.0, 1.0, 1.0), clean=True): from . import cell_calc collection = context.collection view_layer = context.view_layer # apply optional clamp if source_limit != 0 and source_limit < len(points): points = _limit_source(points, source_limit) # saddly we cant be sure there are no doubles from mathutils import Vector to_tuple = Vector.to_tuple # To remove doubles, round the values. points = [(Vector(to_tuple(p[0], 4)), p[1]) for p in points] del to_tuple del Vector if source_noise > 0.0: from random import random # boundbox approx of overall scale from mathutils import Vector matrix = original.matrix_world.copy() bb_world = [matrix @ Vector(v) for v in original.bound_box] scalar = source_noise * ((bb_world[0] - bb_world[6]).length / 2.0) from mathutils.noise import random_unit_vector points[:] = [(p[0] + (random_unit_vector() * (scalar * random())), p[1]) for p in points] if use_debug_points: bm = bmesh.new() for p in points: bm.verts.new(p[0]) mesh_tmp = bpy.data.meshes.new(name="DebugPoints") bm.to_mesh(mesh_tmp) bm.free() obj_tmp = bpy.data.objects.new(name=mesh_tmp.name, object_data=mesh_tmp) collection.objects.link(obj_tmp) del obj_tmp, mesh_tmp cells_verts = cell_calc.points_to_verts(original_xyz_minmax, points, cell_scale, margin_cell=margin) # some hacks here :S cell_name = original.name + "_cell" cells = [] for center_point, cell_verts in cells_verts: # --------------------------------------------------------------------- # BMESH # create the convex hulls bm = bmesh.new() # WORKAROUND FOR CONVEX HULL BUG/LIMIT # XXX small noise import random def R(): return (random.random() - 0.5) * 0.001 for i, co in enumerate(cell_verts): co.x += R() co.y += R() co.z += R() bm_vert = bm.verts.new(co) import mathutils bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.005) try: # Making cell meshes as convex full here! bmesh.ops.convex_hull(bm, input=bm.verts) except RuntimeError: import traceback traceback.print_exc() if clean: bm.normal_update() try: bmesh.ops.dissolve_limit(bm, verts=bm.verts, angle_limit=0.001) except RuntimeError: import traceback traceback.print_exc() # smooth faces will remain only inner faces, after appling boolean modifier. if use_smooth_faces: for bm_face in bm.faces: bm_face.smooth = True if material_index != 0: for bm_face in bm.faces: bm_face.material_index = material_index # --------------------------------------------------------------------- # MESH mesh_dst = bpy.data.meshes.new(name=cell_name) bm.to_mesh(mesh_dst) bm.free() del bm if use_data_match: # match materials and data layers so boolean displays them # currently only materials + data layers, could do others... mesh_src = original.data for mat in mesh_src.materials: mesh_dst.materials.append(mat) for lay_attr in ("vertex_colors", "uv_layers"): lay_src = getattr(mesh_src, lay_attr) lay_dst = getattr(mesh_dst, lay_attr) for key in lay_src.keys(): lay_dst.new(name=key) # --------------------------------------------------------------------- # OBJECT cell = bpy.data.objects.new(name=cell_name, object_data=mesh_dst) collection.objects.link(cell) cell.location = center_point cells.append(cell) # support for object materials if use_data_match: for i in range(len(mesh_dst.materials)): slot_src = original.material_slots[i] slot_dst = cell.material_slots[i] slot_dst.link = slot_src.link slot_dst.material = slot_src.material if use_debug_redraw: view_layer.update() _redraw_yasiamevil() view_layer.update() # move this elsewhere... # Blender 2.8: BGE integration was disabled, -- # -- because BGE was deleted in Blender 2.8. ''' for cell in cells: game = cell.game game.physics_type = 'RIGID_BODY' game.use_collision_bounds = True game.collision_bounds_type = 'CONVEX_HULL' ''' return cells
def execute(self, context): bool_flip = True min_iso, max_iso = 0.25, 0.75 start_time = timeit.default_timer() try: check = bpy.context.object.vertex_groups[0] except: self.report({'ERROR'}, "The object doesn't have Vertex Groups") return {'CANCELLED'} ob0 = bpy.context.object group_id = ob0.vertex_groups.active_index vertex_group_name = ob0.vertex_groups[group_id].name bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.object.mode_set(mode='OBJECT') me0 = ob0.data # generate new bmesh bm = bmesh.new() bm.from_mesh(me0) bm.verts.ensure_lookup_table() bm.edges.ensure_lookup_table() bm.faces.ensure_lookup_table() # store weight values weight = [] ob = bpy.data.objects.new("temp", me0) for g in ob0.vertex_groups: ob.vertex_groups.new(name=g.name) for v in me0.vertices: try: weight.append(ob.vertex_groups[vertex_group_name].weight( v.index)) except: weight.append(0) # define iso values iso_values = [] n_cuts = 24 for i_cut in range(n_cuts): delta_iso = abs(max_iso - min_iso) min_iso = min(min_iso, max_iso) if delta_iso == 0: iso_val = min_iso else: iso_val = i_cut * delta_iso / (n_cuts - 1) + min_iso iso_values.append(iso_val) # Start Cuts Iterations filtered_edges = bm.edges for iso_val in iso_values: delete_edges = [] faces_mask = [] for f in bm.faces: w_min = 2 w_max = 2 for v in f.verts: w = weight[v.index] if w_min == 2: w_max = w_min = w if w > w_max: w_max = w if w < w_min: w_min = w if w_min < iso_val and w_max > iso_val: faces_mask.append(f) break #print("selected faces:" + str(len(faces_mask))) #link_faces = [[f for f in e.link_faces] for e in bm.edges] #faces_todo = [f.select for f in bm.faces] #faces_todo = [True for f in bm.faces] verts = [] edges = [] edges_id = {} _filtered_edges = [] n_verts = len(bm.verts) count = n_verts for e in filtered_edges: #id0 = e.vertices[0] #id1 = e.vertices[1] id0 = e.verts[0].index id1 = e.verts[1].index w0 = weight[id0] w1 = weight[id1] if w0 == w1: continue elif w0 > iso_val and w1 > iso_val: _filtered_edges.append(e) continue elif w0 < iso_val and w1 < iso_val: continue elif w0 == iso_val or w1 == iso_val: continue else: v0 = bm.verts[id0].co v1 = bm.verts[id1].co v = v0.lerp(v1, (iso_val - w0) / (w1 - w0)) if e not in delete_edges: delete_edges.append(e) verts.append(v) edges_id[str(id0) + "_" + str(id1)] = count edges_id[str(id1) + "_" + str(id0)] = count count += 1 _filtered_edges.append(e) filtered_edges = _filtered_edges #print("creating faces") splitted_faces = [] switch = False # splitting faces for f in faces_mask: # create sub-faces slots. Once a new vertex is reached it will # change slot, storing the next vertices for a new face. build_faces = [[], []] #switch = False verts0 = [v.index for v in f.verts] verts1 = list(verts0) verts1.append(verts1.pop(0)) # shift list for id0, id1 in zip(verts0, verts1): # add first vertex to active slot build_faces[switch].append(id0) # try to split edge try: # check if the edge must be splitted new_vert = edges_id[str(id0) + "_" + str(id1)] # add new vertex build_faces[switch].append(new_vert) # if there is an open face on the other slot if len(build_faces[not switch]) > 0: # store actual face splitted_faces.append(build_faces[switch]) # reset actual faces and switch build_faces[switch] = [] # change face slot switch = not switch # continue previous face build_faces[switch].append(new_vert) except: pass if len(build_faces[not switch]) == 2: build_faces[not switch].append(id0) if len(build_faces[not switch]) > 2: splitted_faces.append(build_faces[not switch]) # add last face splitted_faces.append(build_faces[switch]) #del_faces.append(f.index) #print("generate new bmesh") # adding new vertices new_verts = [] for v in verts: new_verts.append(bm.verts.new(v)) #verts = [0]*len(verts) + verts bm.verts.index_update() bm.verts.ensure_lookup_table() # adding new faces missed_faces = [] added_faces = [] for f in splitted_faces: try: face_verts = [bm.verts[i] for i in f] new_face = bm.faces.new(face_verts) for e in new_face.edges: filtered_edges.append(e) except: missed_faces.append(f) #print("missed " + str(len(missed_faces)) + " faces") bm.faces.ensure_lookup_table() # updating weight values weight = weight + [iso_val] * len(verts) # deleting old edges/faces bm.edges.ensure_lookup_table() for e in delete_edges: bm.edges.remove(e) _filtered_edges = [] for e in filtered_edges: if e not in delete_edges: _filtered_edges.append(e) filtered_edges = _filtered_edges #print("creating curve") name = ob0.name + '_Contour' me = bpy.data.meshes.new(name) bm.to_mesh(me) ob = bpy.data.objects.new(name, me) # Link object to scene and make active bpy.context.collection.objects.link(ob) bpy.context.view_layer.objects.active = ob ob.select_set(True) ob0.select_set(False) # generate new vertex group for g in ob0.vertex_groups: ob.vertex_groups.new(name=g.name) ob.vertex_groups.new(name="Smooth") #ob.vertex_groups.new(name=vertex_group_name) #print("doing weight") all_weight = weight + [iso_val] * len(verts) #mult = 1/(1-iso_val) for id in range(len(all_weight)): #if False: w = (all_weight[id]-iso_val)*mult w = all_weight[id] direction = bool_flip for i in range(len(iso_values) - 1): val0, val1 = iso_values[0], iso_values[-1] #val0, val1 = iso_values[i], iso_values[i+1] if val0 < w <= val1: if direction: w1 = (w - val0) / (val1 - val0) else: w1 = (val1 - w) / (val1 - val0) direction = not direction if w < iso_values[0]: w1 = not bool_flip if w > iso_values[-1]: w1 = not direction w2 = w1 if w == iso_values[0]: w2 = 1 ob.vertex_groups[vertex_group_name].add([id], w1, 'REPLACE') #ob.vertex_groups["Smooth"].add([id], w2, 'REPLACE') #ob.vertex_groups["Smooth"].add([id], w>min_iso, 'REPLACE') #print("weight done") #for id in range(len(weight), len(ob.data.vertices)): # ob.vertex_groups[vertex_group_name].add([id], iso_val*0, 'ADD') ob.vertex_groups.active_index = group_id # materials bpy.ops.object.material_slot_add() bpy.ops.object.material_slot_add() bpy.ops.object.material_slot_add() try: body_mat = bpy.data.materials["Body"] except: body_mat = bpy.data.materials.new("Body") try: border_mat = bpy.data.materials["Border"] except: border_mat = bpy.data.materials.new("Border") try: corset_mat = bpy.data.materials["Corset"] except: corset_mat = bpy.data.materials.new("Corset") corset_mat.diffuse_color = (0.31, 0.737, 0.792, 1) border_mat.diffuse_color = (0.31 * 0.6, 0.737 * 0.6, 0.792 * 0.6, 1) ob.material_slots[0].material = body_mat ob.material_slots[1].material = border_mat ob.material_slots[2].material = corset_mat for p in ob.data.polygons: bool_corset = True bool_body = True for v in p.vertices: #w = ob.vertex_groups["Group"].weight(v) w = ob.vertex_groups.active.weight(v) if w < 1: bool_corset = False if w > 0: bool_body = False if bool_corset: p.material_index = 2 elif not bool_body: p.material_index = 1 if not bool_body: for id in p.vertices: ob.vertex_groups["Smooth"].add([id], 1, 'REPLACE') # align new object ob.matrix_world = ob0.matrix_world # Displace Modifier ob.modifiers.new(type='VERTEX_WEIGHT_EDIT', name='Profile') group_name = ob.vertex_groups.active.name bpy.context.object.modifiers["Profile"].vertex_group = group_name ob.modifiers.new(type='SOLIDIFY', name='Solidify') mod = ob.modifiers["Solidify"] mod.thickness = self.max_thickness mod.vertex_group = vertex_group_name mod.thickness_vertex_group = self.min_thickness / self.max_thickness mod.offset = 1 bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_linked(delimit=set()) bpy.ops.mesh.select_all(action='INVERT') bpy.ops.mesh.delete(type='VERT') bpy.ops.object.mode_set(mode='OBJECT') #bpy.ops.paint.weight_paint_toggle() #bpy.context.space_data.viewport_shade = 'WIREFRAME' ob.data.update() print("Contour Displace time: " + str(timeit.default_timer() - start_time) + " sec") return {'FINISHED'}
def execute(self, context): CurFile = open(self.filepath, "rb") CurCollection = bpy.data.collections.new("New Mesh") bpy.context.scene.collection.children.link(CurCollection) tmpRead = CurFile.read() mshBytes = re.compile(b"\x33\xEA\x00\x00....\x2D\x00\x02\x1C", re.DOTALL) iter = 0 for x in mshBytes.finditer( tmpRead): # Files can have multiple mesh containers CurFile.seek(x.end() + 4) FaceDataOff = int.from_bytes(CurFile.read(4), byteorder='little') MeshDataSize = int.from_bytes(CurFile.read(4), byteorder='little') MeshChunkStart = CurFile.tell() CurFile.seek(0x14, 1) mDataTableCount = int.from_bytes(CurFile.read(4), byteorder='big') mDataSubCount = int.from_bytes(CurFile.read(4), byteorder='big') mDataOffsets = [] # This section isn't used right now... probably not needed for i in range(mDataTableCount): CurFile.seek(4, 1) mDataOffsets.append( int.from_bytes(CurFile.read(4), byteorder='big')) mDataSubStart = CurFile.tell() for i in range( mDataSubCount): # Containers can have multiple sub-meshes CurFile.seek(mDataSubStart + i * 0xc + 8) offset = int.from_bytes(CurFile.read(4), byteorder='big') chunkHead = CurFile.seek(offset + MeshChunkStart + 0xC) VertCountDataOff = int.from_bytes( CurFile.read(4), byteorder='big') + MeshChunkStart CurFile.seek(VertCountDataOff) VertChunkTotalSize = int.from_bytes(CurFile.read(4), byteorder='big') VertChunkSize = int.from_bytes(CurFile.read(4), byteorder='big') VertCount = int(VertChunkTotalSize / VertChunkSize) CurFile.seek(8, 1) VertexStart = int.from_bytes( CurFile.read(4), byteorder='big') + FaceDataOff + MeshChunkStart CurFile.seek(0x14, 1) FaceCount = int( int.from_bytes(CurFile.read(4), byteorder='big') / 2) CurFile.seek(4, 1) FaceStart = int.from_bytes( CurFile.read(4), byteorder='big') + FaceDataOff + MeshChunkStart CurFile.seek(FaceStart) StripList = [] tmpList = [] for f in range(FaceCount): Indice = int.from_bytes(CurFile.read(2), byteorder='big') if Indice == 65535: StripList.append(tmpList.copy()) tmpList.clear() else: tmpList.append(Indice) FaceTable = [] for f in StripList: for f2 in strip2face(f): FaceTable.append(f2) VertTable = [] UVTable = [] CMTable = [] for v in range(VertCount): CurFile.seek(VertexStart + v * VertChunkSize) TempVert = struct.unpack('>fff', CurFile.read(4 * 3)) VertTable.append(TempVert) # UV data for the textures CurFile.seek(VertexStart + v * VertChunkSize + VertChunkSize - 16) TempUV = struct.unpack('>ff', CurFile.read(4 * 2)) UVTable.append((TempUV[0], 1 - TempUV[1])) # Color map data for the palette CurFile.seek(VertexStart + v * VertChunkSize + VertChunkSize - 8) TempCM = struct.unpack('>ff', CurFile.read(4 * 2)) CMTable.append((TempCM[0], 1 - TempCM[1])) #build mesh mesh1 = bpy.data.meshes.new("Mesh") mesh1.use_auto_smooth = True obj = bpy.data.objects.new("Mesh_" + str(iter) + "_" + str(i), mesh1) CurCollection.objects.link(obj) bpy.context.view_layer.objects.active = obj obj.select_set(True) mesh = bpy.context.object.data bm = bmesh.new() for v in VertTable: bm.verts.new((v[0], v[1], v[2])) list = [v for v in bm.verts] for f in FaceTable: try: bm.faces.new((list[f[0]], list[f[1]], list[f[2]])) except: continue bm.to_mesh(mesh) uv_layer = bm.loops.layers.uv.verify() cm_layer = bm.loops.layers.uv.new() for f in bm.faces: f.smooth = True for l in f.loops: luv = l[uv_layer] lcm = l[cm_layer] try: luv.uv = UVTable[l.vert.index] lcm.uv = CMTable[l.vert.index] except: continue bm.to_mesh(mesh) bm.free() obj.rotation_euler = (1.5707963705062866, 0, 0) iter += 1 CurFile.close() del CurFile return {'FINISHED'}
def load(properties, data, *args, **kwargs): """ Imports photogrammetry data into the current scene. """ scene = kwargs.get('scene', None) if not scene: raise Exception('Scene required to load data in blender.load') collection = set_active_collection(**kwargs) camera_collection = set_active_collection(name='Cameras', parent=collection, **kwargs) if properties.update_render_size: resolution = data.get('resolution', None) if not resolution and len(data['cameras']): resolution = get_image_size( next(c for c in data['cameras'].values())['filename']) scene.render.resolution_x, scene.render.resolution_y = resolution for cid, camera in data['cameras'].items(): # rotation in file needs to be transposed to work properly mrot = Matrix(camera['R']) mrot.transpose() rotation = mrot.to_euler('XYZ') # https://github.com/simonfuhrmann/mve/wiki/Math-Cookbook # t = -R * c # where c is the real world position as I've calculated, and t is the camera location stored in bundle.out translation = -1 * mrot @ Vector(camera['t']) # create and link camera name = os.path.splitext(os.path.basename(camera['filename']))[0] cdata = bpy.data.cameras.new(name) cam = bpy.data.objects.new(name, cdata) camera_collection.objects.link(cam) # add background images per camera! cdata.show_background_images = True bg = cdata.background_images.new() image_path = camera['filename'] if properties.relative_paths: image_path = bpy.path.relpath(image_path) img = bpy.data.images.load(image_path, check_existing=True) bg.image = img bg.alpha = properties.camera_alpha bg.display_depth = properties.camera_display_depth # set parameters cam.location = translation cam.rotation_euler = rotation cdata.sensor_width = 35 cdata.lens = (camera['f'] * 35) / scene.render.resolution_x coords = [] for tracker in data['trackers'].values(): coords.append(Vector(tracker['co'])) mesh = bpy.data.meshes.new("PhotogrammetryPoints") obj = bpy.data.objects.new("PhotogrammetryPoints", mesh) collection.objects.link(obj) scene.view_layers[0].objects.active = obj obj.select_set(True) # add all coords from bundler points as vertices bm = bmesh.new() for v in coords: bm.verts.new(v) # make the bmesh the object's mesh bm.to_mesh(mesh) bm.free()
def export(filepath): file = open(filepath, 'w') fscanf = file.write objList = [object for object in bpy.data.objects if object.type == 'MESH'] for obj in objList: mesh = obj.data bm = bmesh.new() bm.from_mesh(mesh) bmesh.ops.triangulate(bm, faces=bm.faces) bm.to_mesh(mesh) mesh.calc_loop_triangles() bm.free() fscanf('blendWeightsX blendWeightsY blendWeightsZ blendWeightsW ') fscanf('blendIndicesX blendIndicesY blendIndicesZ blendIndicesW ') fscanf('\n') fscanf(' \n') vert = mesh.vertices for face in mesh.polygons: t = obj.data.loop_triangles t_1 = vert[face.vertices[0]] count_1 = 0 for g in t_1.groups: fscanf('%.3f ' % g.weight) count_1 = count_1 + 1 while count_1 < 4: fscanf('%.3f ' % 0.0) count_1 = count_1 + 1 count_1 = 0 for g in t_1.groups: fscanf('%d ' % g.group) count_1 = count_1 + 1 while count_1 < 4: fscanf('%d ' % -1) count_1 = count_1 + 1 fscanf('\n') #========================================================== t_2 = vert[face.vertices[2]] count_2 = 0 for g in t_2.groups: fscanf('%.3f ' % g.weight) count_2 = count_2 + 1 while count_2 < 4: fscanf('%.3f ' % 0.0) count_2 = count_2 + 1 count_2 = 0 for g in t_2.groups: fscanf('%d ' % g.group) count_2 = count_2 + 1 while count_2 < 4: fscanf('%d ' % -1) count_2 = count_2 + 1 fscanf('\n') #========================================================== t_3 = vert[face.vertices[1]] count_3 = 0 for g in t_3.groups: fscanf('%.3f ' % g.weight) count_3 = count_3 + 1 while count_3 < 4: fscanf('%.3f ' % 0.0) count_3 = count_3 + 1 count_3 = 0 for g in t_3.groups: fscanf('%d ' % g.group) count_3 = count_3 + 1 while count_3 < 4: fscanf('%d ' % -1) count_3 = count_3 + 1 fscanf('\n') file.close() return {'FINISHED'}
def import_tabula4(tblzip, jsonmodel, animations_only, scene): with ExitStack() as stack: bm = bmesh.new() stack.callback(bm.free) model_name = jsonmodel['modelName'] author = jsonmodel['authorName'] mesh = bpy.data.meshes.new(model_name) mesh.mcprops.artist = author uvmap = mesh.uv_textures.new('UVMap') bm.from_mesh(mesh) uvloop = bm.loops.layers.uv[uvmap.name] identifier_to_cube = set() tex_width = jsonmodel['textureWidth'] tex_height = jsonmodel['textureHeight'] texture_matrix = Matrix.Identity(3) texture_matrix[0][0] = 1 / tex_width texture_matrix[1][1] = -1 / tex_height texture_matrix[1][2] = 1 print(texture_matrix) texture_name = 'texture.png' try: tblzip.extract(texture_name, path=bpy.app.tempdir) texture_file = os.path.join(bpy.app.tempdir, texture_name) except KeyError: Reporter.error("Missing texture file", texname=texture_file) else: image = bpy.data.images.load(texture_file) image.pack() image.use_alpha = True os.remove(texture_file) def Position(vec): s = Matrix.Identity(4) s.row[0][3], s.row[1][3], s.row[2][3] = vec return s def Scale(vec): s = Matrix.Identity(4) s.row[0][0], s.row[1][1], s.row[2][2] = vec return s def Rotation(rot): return Euler(rot, 'XYZ').to_matrix().to_4x4() def emit_cube(cube, local_to_global): offset = Position(cube['offset']) # Offset dim_x, dim_y, dim_z = cube['dimensions'] dimensions = Scale((dim_x, dim_y, dim_z)) matrix = local_to_global * offset * dimensions tx_transform = Matrix.Identity(3) tx_transform.col[2] =\ cube['txOffset'][0], cube['txOffset'][1], 1 tx_transform = texture_matrix * tx_transform print(tx_transform) v0 = bm.verts.new((matrix * Vector((0, 0, 0, 1)))[0:3]) v1 = bm.verts.new((matrix * Vector((0, 0, 1, 1)))[0:3]) v2 = bm.verts.new((matrix * Vector((0, 1, 0, 1)))[0:3]) v3 = bm.verts.new((matrix * Vector((0, 1, 1, 1)))[0:3]) v4 = bm.verts.new((matrix * Vector((1, 0, 0, 1)))[0:3]) v5 = bm.verts.new((matrix * Vector((1, 0, 1, 1)))[0:3]) v6 = bm.verts.new((matrix * Vector((1, 1, 0, 1)))[0:3]) v7 = bm.verts.new((matrix * Vector((1, 1, 1, 1)))[0:3]) bm.edges.new((v0, v1)) bm.edges.new((v0, v2)) bm.edges.new((v0, v4)) bm.edges.new((v1, v3)) bm.edges.new((v1, v5)) bm.edges.new((v2, v3)) bm.edges.new((v2, v6)) bm.edges.new((v3, v7)) bm.edges.new((v4, v5)) bm.edges.new((v4, v6)) bm.edges.new((v5, v7)) bm.edges.new((v6, v7)) f0 = bm.faces.new((v0, v1, v3, v2)) f1 = bm.faces.new((v0, v4, v5, v1)) f2 = bm.faces.new((v0, v2, v6, v4)) f3 = bm.faces.new((v4, v6, v7, v5)) f4 = bm.faces.new((v2, v3, v7, v6)) f5 = bm.faces.new((v1, v5, v7, v3)) f0.loops[0][uvloop].uv = (tx_transform * Vector( (dim_z, dim_z, 1)))[0:2] f0.loops[1][uvloop].uv = (tx_transform * Vector( (0, dim_z, 1)))[0:2] f0.loops[2][uvloop].uv = (tx_transform * Vector( (0, dim_z + dim_y, 1)))[0:2] f0.loops[3][uvloop].uv = (tx_transform * Vector( (dim_z, dim_z + dim_y, 1)))[0:2] f1.loops[0][uvloop].uv = (tx_transform * Vector( (dim_z, dim_z, 1)))[0:2] f1.loops[1][uvloop].uv = (tx_transform * Vector( (dim_z + dim_x, dim_z, 1)))[0:2] f1.loops[2][uvloop].uv = (tx_transform * Vector( (dim_z + dim_x, 0, 1)))[0:2] f1.loops[3][uvloop].uv = (tx_transform * Vector( (dim_z, 0, 1)))[0:2] f2.loops[0][uvloop].uv = (tx_transform * Vector( (dim_z, dim_z, 1)))[0:2] f2.loops[1][uvloop].uv = (tx_transform * Vector( (dim_z, dim_z + dim_y, 1)))[0:2] f2.loops[2][uvloop].uv = (tx_transform * Vector( (dim_z + dim_x, dim_z + dim_y, 1)))[0:2] f2.loops[3][uvloop].uv = (tx_transform * Vector( (dim_z + dim_x, dim_z, 1)))[0:2] f3.loops[0][uvloop].uv = (tx_transform * Vector( (2 * dim_z + 2 * dim_x, dim_z, 1)))[0:2] f3.loops[1][uvloop].uv = (tx_transform * Vector( (dim_z + 2 * dim_x, dim_z, 1)))[0:2] f3.loops[2][uvloop].uv = (tx_transform * Vector( (dim_z + 2 * dim_x, dim_z + dim_y, 1)))[0:2] f3.loops[3][uvloop].uv = (tx_transform * Vector( (2 * dim_z + 2 * dim_x, dim_z + dim_y, 1)))[0:2] f4.loops[0][uvloop].uv = (tx_transform * Vector( (dim_z + 2 * dim_x, dim_z, 1)))[0:2] f4.loops[1][uvloop].uv = (tx_transform * Vector( (dim_z + 2 * dim_x, 0, 1)))[0:2] f4.loops[2][uvloop].uv = (tx_transform * Vector( (dim_z + dim_x, 0, 1)))[0:2] f4.loops[3][uvloop].uv = (tx_transform * Vector( (dim_z + dim_x, dim_z, 1)))[0:2] f5.loops[0][uvloop].uv = (tx_transform * Vector( (dim_z + dim_x, dim_z, 1)))[0:2] f5.loops[1][uvloop].uv = (tx_transform * Vector( (dim_z + dim_x, dim_z + dim_y, 1)))[0:2] f5.loops[2][uvloop].uv = (tx_transform * Vector( (dim_z + 2 * dim_x, dim_z + dim_y, 1)))[0:2] f5.loops[3][uvloop].uv = (tx_transform * Vector( (dim_z + 2 * dim_x, dim_z, 1)))[0:2] bm.verts.index_update() bm.edges.index_update() bm.faces.index_update() bm.normal_update() return def make_cube(cube, to_global): identifier = cube['identifier'] name = cube['name'] position = Position(cube['position']) # Rotation point scale = Scale(cube['scale']) rotation = Rotation(cube['rotation']) local_to_global = to_global * position * rotation * scale if identifier in identifier_to_cube: Reporter.warning("Identifier reused in the model: {id}", id=identifier) identifier_to_cube.add(identifier) emit_cube(cube, local_to_global) for child in cube['children']: make_cube(child, local_to_global) def make_group(group, to_global): for cube in group['cubes']: make_cube(cube, to_global) for child in group['cubeGroups']: make_cube(child, to_global) model_transform = Matrix.Scale(1 / 16, 4) model_transform[1][2] = model_transform[2][2] model_transform[2][1] = -model_transform[1][1] model_transform[1][1] = model_transform[2][2] = 0 model_transform[2][3] = 51 / 32 model_transform *= Scale(jsonmodel['scale']) for group in jsonmodel['cubeGroups']: make_group(group, model_transform) for cube in jsonmodel['cubes']: make_cube(cube, model_transform) bm.to_mesh(mesh) object = bpy.data.objects.new(model_name, mesh) scene.objects.link(object) scene.update() def make_animation(*args): pass # TODO: import animations aswell? for animation in jsonmodel['anims']: make_animation(animation, model_transform)