def rotate(self, o): """ Realization of <Node.rotate(..)> """ # check if we need to perform rotation cos = self.edges[1][2] convex = self.edges[1][3] if convex and abs(cos) < zero2: return angle = acos(cos) bm = getBmesh(o) bmesh.ops.rotate( bm, cent = zeroVector, matrix = mathutils.Matrix.Rotation( angle-math.pi/2. if convex else 1.5*math.pi-angle, 3, self.n ), verts = getVertsForVertexGroup(o, bm, o.vertex_groups[ self._edges[1][1] ].name) ) setBmesh(o, bm) return angle
def getEdges(self, o, groupIndices): """ Get vectors that define node edges for the Blender object <o> Args: o: Blender object acting as a node groupIndices (list): A list of vertex group indices, each vertex group is assigned to vertices forming an open end of the node edge """ edges = [] bm = getBmesh(o) layer = bm.verts.layers.deform[0] # Iterate through vertices for v in bm.verts: edge = None for i in groupIndices: if i in v[layer]: # now find the vertex that doesn't have the group with the index <i> loop = v.link_loops[0] _v = (loop.link_loop_prev if i in loop.link_loop_next.vert[layer] else loop.link_loop_next).vert edge = (v.co - _v.co).normalized() # store also the group index edges.append([ edge, i ]) # we found the vertex belonging to the group with the index <i>, # we don't need to continue iteration through groupIndices break if edge: # we'll skip the other vertices belonging to the group with the index <i> groupIndices.remove(i) if not groupIndices: # nothing is left to search for break bm.free() return edges
def create(self, o): context = self.context obj = createMeshObject(self.name) #obj.hide_select = True context.scene.prk.areaName = obj.name obj["t"] = self.type group = o["g"] # remember the group for the first vertex obj["last"] = group bm = getBmesh(obj) # create a deform layer to store vertex groups layer = bm.verts.layers.deform.new() vert = bm.verts.new(self.getLocation(o)) assignGroupToVerts(obj, layer, group, vert) bm.to_mesh(obj.data) bm.free() # without scene.update() hook modifiers will not work correctly context.scene.update() # perform parenting self.parent_set(o.parent, obj) # one more update context.scene.update() addHookModifier(obj, group, o, group)
def assignUv(self, uvMap=None): if not uvMap: uvMap = defaultUvMap o = self.obj # create a new UV map if necessary if not uvMap in o: o.data.uv_textures.new(uvMap) bm = getBmesh(o) # the inital loop _loop = self.getInitialLoop(bm) # find the open end if the finish sequence isn't a closed sequence loop = _loop vert = loop.vert while True: if len(vert.link_loops) == 1: # found the open end _loop = loop break loop = (vert.link_loops[1] if vert.link_loops[0] == loop else vert.link_loops[0]).link_loop_prev vert = loop.vert if loop == _loop: break # finally, assign UV coordinates layer = bm.loops.layers.uv[uvMap] # the layer for vertex groups groupLayer = bm.verts.layers.deform[0] h = getLevelHeight(self.context, o) offsetU = 0. loop = _loop vert = loop.vert e1 = getControlEmptyFromLoop(loop, groupLayer, o) while True: # remember the base loop _vert = vert # remember, we are dealing with rectangles # left bottom loop[layer].uv = (offsetU, 0.) # left top loop.link_loop_prev[layer].uv = (offsetU, h) # right bottom loop = loop.link_loop_next e2 = getControlEmptyFromLoop(loop, groupLayer, o) offsetU += (e2.location - e1.location).length loop[layer].uv = (offsetU, 0.) # right top loop.link_loop_next[layer].uv = (offsetU, h) # the step below isn't necessary, we already came to the required loop #loop = loop.link_loop_next vert = loop.vert if len(vert.link_loops) == 1: # reached the opposite end (end if the finish sequence isn't a closed sequence) break loop = vert.link_loops[1] if vert.link_loops[0] == loop else vert.link_loops[0] if loop == _loop: break e1 = e2 setBmesh(o, bm)
def create(self, controls, parent, profile): context = self.context profile, closed, clockwise = self.getProfileData(profile) obj = createMeshObject("extruded") obj["t"] = "extruded" bm = getBmesh(obj) # vertex groups are in the deform layer, create one before any operation with bmesh: layer = bm.verts.layers.deform.new() numVerts = len(profile) numControls = len(controls) for i in range(numControls): c = controls[i] corner = Corner(c.location, pVert=controls[i - 1].location, nVert=controls[(i + 1) % numControls].location) group = c["g"] for p in profile: v = bm.verts.new(corner.inset(p[0], p[1])) assignGroupToVerts(obj, layer, group, v) # create faces bm.verts.ensure_lookup_table() for i in range(numControls): offset1 = (i - 1) * numVerts offset2 = offset1 + numVerts v1_1 = bm.verts[offset1] v2_1 = bm.verts[offset2] for p in range(1, numVerts): v1_2 = bm.verts[offset1 + p] v2_2 = bm.verts[offset2 + p] bm.faces.new((v2_2, v1_2, v1_1, v2_1) if clockwise else (v2_2, v2_1, v1_1, v1_2)) v1_1 = v1_2 v2_1 = v2_2 if closed: v1_2 = bm.verts[offset1] v2_2 = bm.verts[offset2] bm.faces.new((v2_2, v1_2, v1_1, v2_1) if clockwise else (v2_2, v2_1, v1_1, v1_2)) bm.to_mesh(obj.data) bm.free() # without scene.update() hook modifiers will not work correctly context.scene.update() # perform parenting parent_set(parent, obj) # one more update context.scene.update() # add hook modifiers for c in controls: group = c["g"] addHookModifier(obj, group, c, group)
def slice(obj, terrain, app): sliceSize = app.sliceSize bm = getBmesh(obj) def _slice(index, plane_no, terrainMin, terrainMax): # min and max value along the axis defined by <index> # 1) terrain # a simple conversion from the world coordinate system to the local one terrainMin = terrainMin - obj.location[index] terrainMax = terrainMax - obj.location[index] # 2) <bm>, i.e. Blender mesh minValue = min(obj.bound_box, key = lambda v: v[index])[index] maxValue = max(obj.bound_box, key = lambda v: v[index])[index] # cut everything off outside the terrain bounding box if minValue < terrainMin: minValue = terrainMin bmesh.ops.bisect_plane( bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(0., minValue, 0.) if index else (minValue, 0., 0.), plane_no=plane_no, clear_inner=True ) if maxValue > terrainMax: maxValue = terrainMax bmesh.ops.bisect_plane( bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(0., maxValue, 0.) if index else (maxValue, 0., 0.), plane_no=plane_no, clear_outer=True ) # now cut the slices width = maxValue - minValue if width > sliceSize: numSlices = math.ceil(width/sliceSize) _sliceSize = width/numSlices coord = minValue sliceIndex = 1 while sliceIndex < numSlices: coord += _sliceSize bmesh.ops.bisect_plane( bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(0., coord, 0.) if index else (coord, 0., 0.), plane_no=plane_no ) sliceIndex += 1 _slice(0, (1., 0., 0.), terrain.minX, terrain.maxX) _slice(1, (0., 1., 0.), terrain.minY, terrain.maxY) setBmesh(obj, bm)
def createFromArea(self, area): context = self.context controls = area.getControls() obj = createMeshObject(area.obj.name+"_finish") obj["t"] = self.type self.obj = obj bm = getBmesh(obj) # vertex groups are in the deform layer, create one before any operation with bmesh: layer = bm.verts.layers.deform.new() # a vector along z-axis with the length equal to the wall height height = getLevelHeight(context, area.obj)*zAxis numControls = len(controls) for c in controls: group = c["g"] # the vert at the bottom v_b = bm.verts.new(c.location) # the vert at the top v_t = bm.verts.new(c.location+height) assignGroupToVerts(obj, layer, group, v_b, v_t) # assign vertex group for the top vertex assignGroupToVerts(obj, layer, "t", v_t) # create faces bm.verts.ensure_lookup_table() v1_b = bm.verts[-2] v1_t = bm.verts[-1] for i in range(numControls): # <2> is the number of vertices (of the just created wall surface) per control point v2_b = bm.verts[i*2] v2_t = bm.verts[i*2+1] bm.faces.new((v1_b, v1_t, v2_t, v2_b)) v1_b = v2_b v1_t = v2_t setBmesh(obj, bm) # without scene.update() hook modifiers will not work correctly context.scene.update() # perform parenting parent_set(area.obj.parent, obj) # one more update context.scene.update() # add HOOK modifiers for c in controls: group = c["g"] addHookModifier(obj, group, c, group) # add a HOOK modifier controlling the top vertices addHookModifier(obj, "t", getNextLevelParent(context, obj), "t") # add a SOLIDIFY modifier addSolidifyModifier(obj, "solidify", thickness=0.001, offset=1.) self.treatInsertions(controls)
def createFromArea(self, area): context = self.context controls = area.getControls() obj = createMeshObject(area.obj.name + "_finish") obj["t"] = self.type self.obj = obj bm = getBmesh(obj) # vertex groups are in the deform layer, create one before any operation with bmesh: layer = bm.verts.layers.deform.new() # a vector along z-axis with the length equal to the wall height height = getLevelHeight(context, area.obj) * zAxis numControls = len(controls) for c in controls: group = c["g"] # the vert at the bottom v_b = bm.verts.new(c.location) # the vert at the top v_t = bm.verts.new(c.location + height) assignGroupToVerts(obj, layer, group, v_b, v_t) # assign vertex group for the top vertex assignGroupToVerts(obj, layer, "t", v_t) # create faces bm.verts.ensure_lookup_table() v1_b = bm.verts[-2] v1_t = bm.verts[-1] for i in range(numControls): # <2> is the number of vertices (of the just created wall surface) per control point v2_b = bm.verts[i * 2] v2_t = bm.verts[i * 2 + 1] bm.faces.new((v1_b, v1_t, v2_t, v2_b)) v1_b = v2_b v1_t = v2_t setBmesh(obj, bm) # without scene.update() hook modifiers will not work correctly context.scene.update() # perform parenting parent_set(area.obj.parent, obj) # one more update context.scene.update() # add HOOK modifiers for c in controls: group = c["g"] addHookModifier(obj, group, c, group) # add a HOOK modifier controlling the top vertices addHookModifier(obj, "t", getNextLevelParent(context, obj), "t") # add a SOLIDIFY modifier addSolidifyModifier(obj, "solidify", thickness=0.001, offset=1.) self.treatInsertions(controls)
def create(self, controls, parent, profile): context = self.context profile, closed, clockwise = self.getProfileData(profile) obj = createMeshObject("extruded") obj["t"] = "extruded" bm = getBmesh(obj) # vertex groups are in the deform layer, create one before any operation with bmesh: layer = bm.verts.layers.deform.new() numVerts = len(profile) numControls = len(controls) for i in range(numControls): c = controls[i] corner = Corner(c.location, pVert = controls[i-1].location, nVert = controls[(i+1)%numControls].location) group = c["g"] for p in profile: v = bm.verts.new(corner.inset(p[0], p[1])) assignGroupToVerts(obj, layer, group, v) # create faces bm.verts.ensure_lookup_table() for i in range(numControls): offset1 = (i-1)*numVerts offset2 = offset1+numVerts v1_1 = bm.verts[offset1] v2_1 = bm.verts[offset2] for p in range(1, numVerts): v1_2 = bm.verts[offset1+p] v2_2 = bm.verts[offset2+p] bm.faces.new((v2_2, v1_2, v1_1, v2_1) if clockwise else (v2_2, v2_1, v1_1, v1_2)) v1_1 = v1_2 v2_1 = v2_2 if closed: v1_2 = bm.verts[offset1] v2_2 = bm.verts[offset2] bm.faces.new((v2_2, v1_2, v1_1, v2_1) if clockwise else (v2_2, v2_1, v1_1, v1_2)) bm.to_mesh(obj.data) bm.free() # without scene.update() hook modifiers will not work correctly context.scene.update() # perform parenting parent_set(parent, obj) # one more update context.scene.update() # add hook modifiers for c in controls: group = c["g"] addHookModifier(obj, group, c, group)
def finish(self): obj = getAreaObject(self.context) bm = getBmesh(obj) bm.verts.ensure_lookup_table() face = bm.faces.new(bm.verts) bm.to_mesh(obj.data) if obj.data.polygons[0].normal[2]<-zero: bmesh.ops.reverse_faces(bm, faces = (face,)) bm.to_mesh(obj.data) bm.free() # perform cleanup del obj["last"] self.context.scene.prk.areaName = "" return obj
def getProfileData(self, profile): coords = [] bm = getBmesh(profile) bm.verts.ensure_lookup_table() # is profile a closed loop? closed = True if len(bm.verts) == len(bm.edges) else False start = bm.verts[0] # keep the edge visited in the previous pass of the cycle in the following variable e = start.link_edges[0] if not closed: # find an either open end of the profile while len(start.link_edges) != 1: e = start.link_edges[0] if start.link_edges[ 1] == e else start.link_edges[1] start = e.verts[0] if e.verts[1] == start else e.verts[1] vert = start # keep the edge visited in the previous pass of the cycle in the following variable e = vert.link_edges[0] while True: p = vert.co coords.append((p.x, p.z)) if not closed and len(vert.link_edges) == 1 and vert != start: break vert = e.verts[0] if e.verts[1] == vert else e.verts[1] if closed and vert == start: break e = vert.link_edges[-1] if vert.link_edges[ 0] == e else vert.link_edges[0] bm.free() # Now determine if the coords are in the clockwise or counterclockwise order. # See http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order # The method is based on the shoelace formula _sum = 0. # store the coordinates of the previous point in the following variable _c = None for c in coords: if not _c: _c = coords[-1] _sum += (c[0] - _c[0]) * (c[1] + _c[1]) _c = c clockwise = True if _sum > 0 else False return coords, closed, clockwise
def getProfileData(self, profile): coords = [] bm = getBmesh(profile) bm.verts.ensure_lookup_table() # is profile a closed loop? closed = True if len(bm.verts)==len(bm.edges) else False start = bm.verts[0] # keep the edge visited in the previous pass of the cycle in the following variable e = start.link_edges[0] if not closed: # find an either open end of the profile while len(start.link_edges)!=1: e = start.link_edges[0] if start.link_edges[1] == e else start.link_edges[1] start = e.verts[0] if e.verts[1]==start else e.verts[1] vert = start # keep the edge visited in the previous pass of the cycle in the following variable e = vert.link_edges[0] while True: p = vert.co coords.append((p.x, p.z)) if not closed and len(vert.link_edges) == 1 and vert != start: break vert = e.verts[0] if e.verts[1]==vert else e.verts[1] if closed and vert == start: break e = vert.link_edges[-1] if vert.link_edges[0] == e else vert.link_edges[0] bm.free() # Now determine if the coords are in the clockwise or counterclockwise order. # See http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order # The method is based on the shoelace formula _sum = 0. # store the coordinates of the previous point in the following variable _c = None for c in coords: if not _c: _c = coords[-1] _sum += (c[0]-_c[0])*(c[1]+_c[1]) _c = c clockwise = True if _sum>0 else False return coords, closed, clockwise
def assignUv(self, uvMap=None): if not uvMap: uvMap = defaultUvMap o = self.obj # create a new UV map if necessary if not uvMap in o: o.data.uv_textures.new(uvMap) bm = getBmesh(self.obj) bm.verts.ensure_lookup_table() layer = bm.loops.layers.uv[uvMap] # the initial loop origin = bm.verts[0] start = origin.link_loops[0] # vector along the x-Axis of the area coordinate system firstLoopVector = start.link_loop_next.vert.co - origin.co firstLoopVector.normalize() # Calculate the transformation matrix from the level coordinate system to the area coordinate system, # where the x-axis is oriented along the first loop of <bm>, # y-axis lies in the plane of the area, # z-axis is parallel to the z-axis of the level coordinate system # translationMatrix is inversed translation matrix from the origin of the global coordinate system to the shape origin translationMatrix = mathutils.Matrix.Translation(-origin.co) # now calculate the inversed rotation matrix # create a rotation matrix instance with default values (i.e. a unit matrix) rotationMatrix = mathutils.Matrix() # cosine and sine of the angle between <xAxis> and <firstLoopVector> cos = firstLoopVector[0] sin = firstLoopVector[1] # rotation matrix from <firstLoopVector> to <xAxis> with -<zAxis> as the rotation axis rotationMatrix[0][0:2] = cos, sin rotationMatrix[1][0:2] = -sin, cos # remember inversed(TRS) = inversed(S)*inversed(R)*inversed(T), so in our case: matrix = rotationMatrix*translationMatrix loop = start while True: loop[layer].uv = (matrix*loop.vert.co)[:2] loop = loop.link_loop_next if loop == start: break setBmesh(o, bm)
def getControls(self): """ Returns an ordered list of EMPTYs controlling the vertices of the area """ bm = getBmesh(self.obj) bm.verts.ensure_lookup_table() # All vertex groups are in the deform layer. # There can be only one deform layer layer = bm.verts.layers.deform[0] # building a list of control EMPTYs controls = [] start = bm.verts[0].link_loops[0] loop = start while True: controls.append( getControlEmptyFromLoop(loop, layer, self.obj) ) loop = loop.link_loop_next if loop == start: break bm.free() return controls
def invoke(self, context, event): # template Blender object o = context.object bpy.ops.object.mode_set(mode='OBJECT') # check if exactly one edge is selected bm = getBmesh(o) edge = [edge for edge in bm.edges if edge.select] if len(edge) != 1: bpy.ops.object.mode_set(mode='EDIT') self.report({'ERROR'}, "To assign an asset select exactly 2 vertices on the same edge") return {'CANCELLED'} bm.free() prk = context.scene.prk baseDir = prk.baseDirectory if baseDir: self.directory = os.path.join(baseDir, prk.item.window.assetType) context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'}
def invoke(self, context, event): # template Blender object o = context.object bpy.ops.object.mode_set(mode='OBJECT') # check if exactly one edge is selected bm = getBmesh(o) edge = [edge for edge in bm.edges if edge.select] if len(edge) != 1: bpy.ops.object.mode_set(mode='EDIT') self.report({ 'ERROR' }, "To assign an asset select exactly 2 vertices on the same edge") return {'CANCELLED'} bm.free() prk = context.scene.prk baseDir = prk.baseDirectory if baseDir: self.directory = os.path.join(baseDir, prk.item.window.assetType) context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'}
def makeFromEmpties(self, empties): context = self.context obj = createMeshObject(self.name) #obj.hide_select = True # type obj["t"] = self.type bm = getBmesh(obj) # create a deform layer to store vertex groups layer = bm.verts.layers.deform.new() for e in empties: vert = bm.verts.new(self.getLocation(e)) assignGroupToVerts(obj, layer, e["g"], vert) # the face face = bm.faces.new(bm.verts) bm.to_mesh(obj.data) if obj.data.polygons[0].normal[2]<-zero: bmesh.ops.reverse_faces(bm, faces = (face,)) bm.to_mesh(obj.data) bm.free() # without scene.update() hook modifiers will not work correctly context.scene.update() # perform parenting self.parent_set(empties[0].parent, obj) # one more update context.scene.update() # add HOOK modifiers for e in empties: group = e["g"] addHookModifier(obj, group, e, group) return obj
def getEdges(self, o, groupIndices): """ Get vectors that define node edges for the Blender object <o> Args: o: Blender object acting as a node groupIndices (list): A list of vertex group indices, each vertex group is assigned to vertices forming an open end of the node edge """ edges = [] bm = getBmesh(o) layer = bm.verts.layers.deform[0] # Iterate through vertices for v in bm.verts: edge = None for i in groupIndices: if i in v[layer]: # now find the vertex that doesn't have the group with the index <i> loop = v.link_loops[0] _v = (loop.link_loop_prev if i in loop.link_loop_next.vert[layer] else loop.link_loop_next).vert edge = (v.co - _v.co).normalized() # store also the group index edges.append([edge, i]) # we found the vertex belonging to the group with the index <i>, # we don't need to continue iteration through groupIndices break if edge: # we'll skip the other vertices belonging to the group with the index <i> groupIndices.remove(i) if not groupIndices: # nothing is left to search for break bm.free() return edges
def rotate(self, o): """ Realization of <Node.rotate(..)> """ # check if we need to perform rotation cos = self.edges[1][2] convex = self.edges[1][3] if convex and abs(cos) < zero2: return angle = acos(cos) bm = getBmesh(o) bmesh.ops.rotate( bm, cent=zeroVector, matrix=mathutils.Matrix.Rotation( angle - math.pi / 2. if convex else 1.5 * math.pi - angle, 3, self.n), verts=getVertsForVertexGroup( o, bm, o.vertex_groups[self._edges[1][1]].name)) setBmesh(o, bm) return angle
def assignUv(self, uvMap=None): if not uvMap: uvMap = defaultUvMap o = self.obj # create a new UV map if necessary if not uvMap in o: o.data.uv_textures.new(uvMap) bm = getBmesh(o) # the inital loop _loop = self.getInitialLoop(bm) # find the open end if the finish sequence isn't a closed sequence loop = _loop vert = loop.vert while True: if len(vert.link_loops) == 1: # found the open end _loop = loop break loop = (vert.link_loops[1] if vert.link_loops[0] == loop else vert.link_loops[0]).link_loop_prev vert = loop.vert if loop == _loop: break # finally, assign UV coordinates layer = bm.loops.layers.uv[uvMap] # the layer for vertex groups groupLayer = bm.verts.layers.deform[0] h = getLevelHeight(self.context, o) offsetU = 0. loop = _loop vert = loop.vert e1 = getControlEmptyFromLoop(loop, groupLayer, o) while True: # remember the base loop _vert = vert # remember, we are dealing with rectangles # left bottom loop[layer].uv = (offsetU, 0.) # left top loop.link_loop_prev[layer].uv = (offsetU, h) # right bottom loop = loop.link_loop_next e2 = getControlEmptyFromLoop(loop, groupLayer, o) offsetU += (e2.location - e1.location).length loop[layer].uv = (offsetU, 0.) # right top loop.link_loop_next[layer].uv = (offsetU, h) # the step below isn't necessary, we already came to the required loop #loop = loop.link_loop_next vert = loop.vert if len(vert.link_loops) == 1: # reached the opposite end (end if the finish sequence isn't a closed sequence) break loop = vert.link_loops[1] if vert.link_loops[ 0] == loop else vert.link_loops[0] if loop == _loop: break e1 = e2 setBmesh(o, bm)
def extend(self, empty): context = self.context # get Blender object for the area obj = getAreaObject(self.context) bm = getBmesh(obj) bm.verts.ensure_lookup_table() _vert = bm.verts[-1] # find the Blender object for the empty that controls the last created area vertex prevEmpty = obj.modifiers[obj["last"]].object # If empty and prevEmpty belong to the same wall, # check if we need to create in-between verts for the area, # i.e. empty and prevEmpty aren't adjacent inbetweens = [] if empty.parent == prevEmpty.parent and empty["m"] == prevEmpty["m"]: wall = getWallFromEmpty(context, self.op, empty) if not (wall.getNext(empty) == prevEmpty or wall.getPrevious(empty) == prevEmpty): # find Blender empty objects for <wall>, located between empty and prevEmpty empties = [] # first searching in the forward direction e = prevEmpty while True: e = wall.getNext(e) if e == empty or not e: break empties.append(e) isClosed = wall.isClosed() if isClosed: # keep list of empties _empties = empties if not e or isClosed: # now try in the backward direction empties = [] e = prevEmpty while True: e = wall.getPrevious(e) if e == empty: break empties.append(e) # for the closed wall check whick path is shorter, in the forward or backward directions if isClosed and len(empties) > len(_empties): empties = _empties # finally, create area verts for EMTPYs for e in empties: group = e["g"] vert = bm.verts.new(self.getLocation(e)) assignGroupToVerts(obj, bm.verts.layers.deform[0], group, vert) _vert = vert inbetweens.append((e, group)) group = empty["g"] obj["last"] = group vert = bm.verts.new(self.getLocation(empty)) assignGroupToVerts(obj, bm.verts.layers.deform[0], group, vert) bm.to_mesh(obj.data) bm.free() # without scene.update() hook modifiers will not work correctly # this step is probably optional here, however it's required in AreaBegin.execute() context.scene.update() if inbetweens: for e,g in inbetweens: addHookModifier(obj, g, e, g) addHookModifier(obj, group, empty, group)
def shear(self, o, angle): """ Realization of <Node.shear(..)> """ if angle is None or not "c" in o.vertex_groups: return _edges = self._edges convex = self.edges[1][3] bm = getBmesh(o) shearFactor = 1./math.tan(angle/2.) if convex: shearFactor = shearFactor - 1. else: shearFactor = -shearFactor - 1. # For the share transformation of the central part defined by the vertex group <c> # we may have to provide a space matrix, since the parameters of the share transformation are # defined under assumtions that the central part defined by the vertex group <c> is oriented # along the <baseBisector>. So the <space> matrix defines the rotation that orients # the actual bisector along the <baseBisector> bisector = (_edges[0][0] + _edges[1][0]).normalized() dot = bisector.dot(baseBisector) # check if <bisector> and <baseBisector> are already aligned if abs(1-dot) > zero2: _angle = acos(dot) if self.n.dot( bisector.cross(baseBisector) ) < 0.: _angle = -_angle spaceMatrix = mathutils.Matrix.Rotation(_angle, 4, self.n) else: spaceMatrix = mathutils.Matrix.Identity(4) bmesh.ops.transform( bm, matrix = mathutils.Matrix.Shear('XY', 4, (shearFactor, 0.)), verts = getVertsForVertexGroup(o, bm, "c"), space = spaceMatrix ) # Set BMesh here to get the correct result in the next section # of the code related to the shape key update setBmesh(o, bm) # update shape key data (if available) for the vertices of the vertex group <c> shape_keys = o.data.shape_keys if shape_keys: if shape_keys.key_blocks.get("frame_width", None): bm = getBmesh(o) shapeKey = bm.verts.layers.shape.get("frame_width") # the bisector of the edges after the shear transformation bisector = mathutils.Matrix.Rotation( angle/2. - math.pi/4. if convex else 0.75*math.pi - angle/2., 3, self.n ) * bisector # offset vector for the shape key offset = shapeKeyOffset / math.sin(angle/2.) * bisector for v in getVertsForVertexGroup(o, bm, "c"): # check if the vertex changes its location for the shape key if ( (v[shapeKey] - v.co).length > zero2): v[shapeKey] = v.co + offset setBmesh(o, bm)
def shear(self, o, angle): """ Realization of <Node.shear(..)> """ if angle is None or not "c" in o.vertex_groups: return _edges = self._edges convex = self.edges[1][3] bm = getBmesh(o) shearFactor = 1. / math.tan(angle / 2.) if convex: shearFactor = shearFactor - 1. else: shearFactor = -shearFactor - 1. # For the share transformation of the central part defined by the vertex group <c> # we may have to provide a space matrix, since the parameters of the share transformation are # defined under assumtions that the central part defined by the vertex group <c> is oriented # along the <baseBisector>. So the <space> matrix defines the rotation that orients # the actual bisector along the <baseBisector> bisector = (_edges[0][0] + _edges[1][0]).normalized() dot = bisector.dot(baseBisector) # check if <bisector> and <baseBisector> are already aligned if abs(1 - dot) > zero2: _angle = acos(dot) if self.n.dot(bisector.cross(baseBisector)) < 0.: _angle = -_angle spaceMatrix = mathutils.Matrix.Rotation(_angle, 4, self.n) else: spaceMatrix = mathutils.Matrix.Identity(4) bmesh.ops.transform(bm, matrix=mathutils.Matrix.Shear( 'XY', 4, (shearFactor, 0.)), verts=getVertsForVertexGroup(o, bm, "c"), space=spaceMatrix) # Set BMesh here to get the correct result in the next section # of the code related to the shape key update setBmesh(o, bm) # update shape key data (if available) for the vertices of the vertex group <c> shape_keys = o.data.shape_keys if shape_keys: if shape_keys.key_blocks.get("frame_width", None): bm = getBmesh(o) shapeKey = bm.verts.layers.shape.get("frame_width") # the bisector of the edges after the shear transformation bisector = mathutils.Matrix.Rotation( angle / 2. - math.pi / 4. if convex else 0.75 * math.pi - angle / 2., 3, self.n) * bisector # offset vector for the shape key offset = shapeKeyOffset / math.sin(angle / 2.) * bisector for v in getVertsForVertexGroup(o, bm, "c"): # check if the vertex changes its location for the shape key if ((v[shapeKey] - v.co).length > zero2): v[shapeKey] = v.co + offset setBmesh(o, bm)
def make(self, t, **kwargs): verts = t.bm.verts context = self.context hooksForNodes = kwargs["hooksForNodes"] # template Blender object _o = t.o # Create a Blender EMPTY object to serve as a parent for the window mesh; # its name doesn't have <T_> prefix name = _o.name[2:] # <pt> stands for parent template pt = t.parentTemplate if pt: p = createEmptyObject(name, _o.location - pt.o.location, False, empty_draw_size=0.01) else: # parent for the whole hierarchy of window Blender objects p = t.p t.meshParent = p # start a Blender object for the template o = createMeshObject(name + "_mesh") t.meshObject = o context.scene.update() # perform parenting parent_set(p, o) if t.parentTemplate: parent_set(pt.meshParent, p) context.scene.update() context.scene.objects.active = o t.prepareOffsets() # iterate through the vertices of the template Blender object numVerts = 0 for v in verts: # id of the vertex vid = t.getVid(v) if not (vid in _o and _o[vid] in bpy.data.objects): continue # Blender object for the node at the vertex j = bpy.data.objects[_o[vid]] t.setNode(v, j, o, context, hooksForNodes=hooksForNodes) numVerts += 1 # final operations: bridging or extruding edges loops of the nodes, making surfaces bm = getBmesh(o) t.bridgeOrExtendNodes(o, bm, kwargs["dissolveEndEdges"]) if numVerts == len(verts): t.makeSurfaces(o, bm) setBmesh(o, bm) # remove unneeded vertex group groups = [g for g in o.vertex_groups if g.name[0] in ("e", "s", "c")] for g in groups: o.vertex_groups.remove(g) # add Edge Split modifier if kwargs["addEdgeSplitModifier"]: addEdgeSplitModifier(o, o.name) # set maximum possible range for the shape keys if hooksForNodes and o.data.shape_keys: for kb in o.data.shape_keys.key_blocks: kb.slider_min = -10. kb.slider_max = 10. t.insertAssets(context) # hide the template Blender object and all its children hide(t.o, True)
def make(self, t, **kwargs): verts = t.bm.verts context = self.context hooksForNodes = kwargs["hooksForNodes"] # template Blender object _o = t.o # Create a Blender EMPTY object to serve as a parent for the window mesh; # its name doesn't have <T_> prefix name = _o.name[2:] # <pt> stands for parent template pt = t.parentTemplate if pt: p = createEmptyObject(name, _o.location-pt.o.location, False, empty_draw_size=0.01) else: # parent for the whole hierarchy of window Blender objects p = t.p t.meshParent = p # start a Blender object for the template o = createMeshObject(name + "_mesh") t.meshObject = o context.scene.update() # perform parenting parent_set(p, o) if t.parentTemplate: parent_set(pt.meshParent, p) context.scene.update() context.scene.objects.active = o t.prepareOffsets() # iterate through the vertices of the template Blender object numVerts = 0 for v in verts: # id of the vertex vid = t.getVid(v) if not (vid in _o and _o[vid] in bpy.data.objects): continue # Blender object for the node at the vertex j = bpy.data.objects[_o[vid]] t.setNode(v, j, o, context, hooksForNodes = hooksForNodes) numVerts += 1 # final operations: bridging or extruding edges loops of the nodes, making surfaces bm = getBmesh(o) t.bridgeOrExtendNodes(o, bm, kwargs["dissolveEndEdges"]) if numVerts == len(verts): t.makeSurfaces(o, bm) setBmesh(o, bm) # remove unneeded vertex group groups = [g for g in o.vertex_groups if g.name[0] in ("e", "s", "c")] for g in groups: o.vertex_groups.remove(g) # add Edge Split modifier if kwargs["addEdgeSplitModifier"]: addEdgeSplitModifier(o, o.name) # set maximum possible range for the shape keys if hooksForNodes and o.data.shape_keys: for kb in o.data.shape_keys.key_blocks: kb.slider_min = -10. kb.slider_max = 10. t.insertAssets(context) # hide the template Blender object and all its children hide(t.o, True)
def createEnvelope(self): terrain = self.terrain name = "%s_envelope" % terrain.name if name in bpy.data.objects: # use existing Blender object envelope = bpy.data.objects[name] # delete modifiers for m in reversed(envelope.modifiers): envelope.modifiers.remove(m) else: # create new envelope for the terrain envelope = createMeshObject(name, (0., 0., self.minZ), terrain.data.copy()) # flatten the terrain envelope envelope.scale[2] = 0. envelope.select = True bpy.context.scene.objects.active = envelope bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.mesh.select_mode(type='FACE') bpy.ops.object.mode_set(mode='OBJECT') for p in envelope.data.polygons: if p.normal[2] < 0.: p.select = True bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.delete(type='FACE') bpy.ops.mesh.select_mode(type='VERT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.remove_doubles() bpy.ops.mesh.region_to_loop() bpy.ops.mesh.select_all(action='INVERT') bpy.ops.mesh.dissolve_verts(use_face_split=False, use_boundary_tear=False) bpy.ops.mesh.select_all(action='SELECT') #bpy.ops.mesh.dissolve_limited(angle_limit=math.radians(0.1)) bpy.ops.object.mode_set(mode='OBJECT') bm = getBmesh(envelope) for f in bm.faces: f.smooth = False # ensure all normals point upward pointNormalUpward(f) # inset faces to avoid weird results of the BOOLEAN modifier insetFaces = bmesh.ops.inset_region(bm, faces=bm.faces, use_boundary=True, use_even_offset=True, use_interpolate=True, use_relative_offset=False, use_edge_rail=False, use_outset=False, thickness=self.envelopeInset, depth=0.)['faces'] bmesh.ops.delete(bm, geom=insetFaces, context=5) setBmesh(envelope, bm) self.envelope = envelope envelope.select = False envelope.hide_render = True # hide <envelope> after all Blender operator envelope.hide = True # SOLIDIFY modifier instead of BMesh extrude operator m = envelope.modifiers.new(name="Solidify", type='SOLIDIFY') m.offset = 1. m.thickness = self.maxZ - self.minZ + self.envelopeOffset