def nautical_euler_from_axes(forward, right): x = Vector(right) y = Vector(forward) world_x = Vector((1, 0, 0)) world_z = Vector((0, 0, 1)) if abs(y.z) > (1 - 1e-12): # sufficiently close to vertical roll = 0.0 xdir = x.copy() else: xdir = y.cross(world_z) rollPos = angle_signed(-y, x, xdir, 0.0) rollNeg = angle_signed(-y, x, -xdir, 0.0) if abs(rollNeg) < abs(rollPos): roll = rollNeg xdir = -xdir else: roll = rollPos xdir = Vector((xdir.x, xdir.y, 0)).normalized() yaw = angle_signed(-world_z, xdir, world_x, 0.0) zdir = xdir.cross(y).normalized() pitch = angle_signed(-xdir, zdir, world_z, 0.0) return Euler((pitch, roll, yaw), 'YXZ')
def arbitrary_x_axis(extrusion_normal): world_y = Vector((0, 1, 0)) world_z = Vector((0, 0, 1)) if abs(extrusion_normal[0]) < 1 / 64 and abs(extrusion_normal[1]) < 1 / 64: a_x = world_y.cross(extrusion_normal) else: a_x = world_z.cross(extrusion_normal) a_x.normalize() return a_x, extrusion_normal.cross(a_x)
def _gimbal_xyz(self, context, am): rotobj = (context.active_pose_bone if context.mode == 'POSE' else None) or context.active_object if (not rotobj) or (rotobj.rotation_mode == 'QUATERNION'): if not am: am = self.calc_active_matrix(context) xyz = am.col[:3] elif rotobj.rotation_mode == 'AXIS_ANGLE': aa = rotobj.rotation_axis_angle z = Vector(aa[1:]).normalized() q = Vector((0,0,1)).rotation_difference(z) x = (q * Vector((1,0,0))).normalized() y = z.cross(x) else: e = rotobj.rotation_euler m = Matrix.Identity(3) for e_ax in rotobj.rotation_mode: m = Matrix.Rotation(getattr(e, e_ax.lower()), 3, e_ax) * m x, y, z = m.col[:3] if not xyz: m = BlUtil.Object.matrix_parent(rotobj) x.rotate(m) y.rotate(m) z.rotate(m) xyz = x, y, z return xyz
def rotate(self,v): up_axis = Vector((0.0, 0.0, 1.0)) angle = v.angle(up_axis, 0) rotation_axis = up_axis.cross(v) bpy.ops.transform.rotate(value=angle,axis=rotation_axis) return
def __get(self): # in object axes world_x = Vector((1, 0, 0)) world_z = Vector((0, 0, 1)) x = self.right # right y = self.forward # forward z = self.up # up if abs(y.z) > (1 - 1e-12): # sufficiently close to vertical roll = 0.0 xdir = x.copy() else: xdir = y.cross(world_z) rollPos = angle_signed(-y, x, xdir, 0.0) rollNeg = angle_signed(-y, x, -xdir, 0.0) if abs(rollNeg) < abs(rollPos): roll = rollNeg xdir = -xdir else: roll = rollPos xdir = Vector((xdir.x, xdir.y, 0)).normalized() yaw = angle_signed(-world_z, xdir, world_x, 0.0) zdir = xdir.cross(y).normalized() pitch = angle_signed(-xdir, zdir, world_z, 0.0) return Euler((pitch, roll, yaw), 'YXZ')
def focus_view_on(region_3d, location): r3d = region_3d a = r3d.view_location.copy() b = location mm = r3d.view_matrix.inverted() vr = mm.to_3x3() loc = mm.translation n = (a-loc).cross(b-loc).normalized() alp = math.acos( max(-1.0,min(1.0, (a-loc).normalized().dot( (b-loc).normalized() ) ))) zero = Vector() u0,v0,w0 = vr.transposed() u = rot_on( zero, n, alp, u0 ) v = rot_on( zero, n, alp, v0 ) w = rot_on( zero, n, alp, w0 ) if bpy.context.user_preferences.inputs.view_rotate_method == 'TURNTABLE': ez = Vector((0,0,1)) u2 = ez.cross(w) v2 = w.cross(u2) u,v = u2,v2 vr2 = Matrix((u,v,w)).transposed() mm2 = vr2.to_4x4() mm2[0][3] = loc[0] mm2[1][3] = loc[1] mm2[2][3] = loc[2] dist0 = (loc-location).length r3d.view_distance = dist0 r3d.view_matrix = mm2.inverted()
def add_f_curve_modifiers(armature_object, strength, speed): wind_vector = Vector((1, 0, 0)) * strength fcurves = armature_object.animation_data.action.fcurves for f in fcurves: for m in f.modifiers: f.modifiers.remove(m) bones = organize_bones(armature_object) for b in bones: mass = b.bone.tail_radius ** 2 * b.length barycenter = b.tail * mass for c in b.children: mass += c["mass"] barycenter += Vector(c["barycenter"]) b["mass"] = mass b["barycenter"] = barycenter barycenter /= mass b.rotation_mode = 'XYZ' b.keyframe_insert('rotation_euler', frame=0, index=0) b.keyframe_insert('rotation_euler', frame=0, index=2) fcurves = armature_object.animation_data.action.fcurves for i in range(len(bones)): f0 = fcurves[2 * i] f1 = fcurves[2 * i + 1] b = bones[i] i_base = b.matrix.to_3x3().inverted() bone_vector = b.tail - b.head inertia_moment = bone_vector.length ** 2 * bones[i]["mass"] / 10000 damping = 0.5 * b.bone.tail_radius stiffness = b.bone.tail_radius ** 2 / b.length * 800 if b.parent is not None and len(b.parent.children) > 1: stiffness *= 2 # torque /= 3 # else: # torque = Vector((0, 0, 0)) torque = i_base * wind_vector.cross(bone_vector) / (b.bone.tail_radius) / 1000 f = sqrt(abs(damping ** 2 - 4 * inertia_moment * stiffness)) / (5*b.bone.tail_radius) * speed x_amplitude = torque.x z_amplitude = torque.z m0 = f0.modifiers.new(type='FNGENERATOR') m1 = f1.modifiers.new(type='FNGENERATOR') m0.function_type = 'SIN' m1.function_type = 'SIN' m0.amplitude = x_amplitude m1.amplitude = z_amplitude m0.phase_multiplier = f m1.phase_multiplier = f m0.value_offset = x_amplitude * 3 m1.value_offset = z_amplitude * 3
def vec_roll_to_mat3(axis, roll): """Computes 3x3 Matrix from rotation axis and its roll. :param axis: Rotation :type axis: Vector :param roll: Roll :type roll: float :return: 3x3 Matrix :rtype: Matrix """ nor = axis.normalized() target = Vector((0, 1, 0)) axis = target.cross(nor) if axis.dot(axis) > 1.0e-9: axis.normalize() theta = _math_utils.angle_normalized_v3v3(target, nor) b_matrix = Matrix.Rotation(theta, 4, axis) else: if target.dot(nor) > 0: up_or_down = 1.0 else: up_or_down = -1.0 b_matrix = Matrix() b_matrix[0] = (up_or_down, 0, 0, 0) b_matrix[1] = (0, up_or_down, 0, 0) b_matrix[2] = (0, 0, 1, 0) b_matrix[3] = (0, 0, 0, 1) roll_matrix = Matrix.Rotation(roll, 4, nor) return (roll_matrix * b_matrix).to_3x3()
def get_align_matrix(location, normal): up = Vector((0, 0, 1)) angle = normal.angle(up) axis = up.cross(normal) mat_rot = Matrix.Rotation(angle, 4, axis) mat_loc = Matrix.Translation(location) mat_align = mat_rot * mat_loc return mat_align
def find_norm_vectors(mesh, vert_dict): vertices = list(vert_dict.keys()) for vert in vertices: edge_vectors = [] for edge in vert_dict[vert].edges: if vert == edge[0]: vec = mesh.vertices[edge[0]].co.copy() - mesh.vertices[edge[1]].co.copy() else: vec = mesh.vertices[edge[1]].co.copy() - mesh.vertices[edge[0]].co.copy() edge_vectors.append(vec/vec.length) vec1 = Vector.cross(edge_vectors[0], edge_vectors[1]) edges_vec = edge_vectors[1] + edge_vectors[0] norm_vec = Vector.cross(vec1, edges_vec) if norm_vec.length < 1e-12: #hackish fix for flat surfaces and straight edges norm_vec = edge_vectors[0] vert_dict[vert].new_index = len(mesh.vertices) + vertices.index(vert) vert_dict[vert].norm_vec = norm_vec vert_dict[vert].edge_vec = edge_vectors[0]
def set_bonded(atom,b): """Sets the positions of all atoms bonded to atom. atom: Atom object rotate: Boolean defining whether or not to rotate tetrahedron around the z-axis b: bond length """ x = 0 y = 0 z = 0 b = b*sqrt(atom.radius) bonds = atom.bonds #Differences in position between central atom and bonded atom for item in bonds: if isinstance(item, Atom): x = atom.pos[0] - item.pos[0] y = atom.pos[1] - item.pos[1] z = atom.pos[2] - item.pos[2] break n = math.radians(109.5) n2 = pi*2/3 #Represents tetrahedron base centered at (0,0,0) v1 = Vector((b*sin(n),0,b*cos(n))) v2 = Vector((b*cos(n2),b*sin(n2),b*cos(n))) v3 = Vector((b*cos(2*n2),b*sin(2*n2),b*cos(n))) offset = (Vector((atom.pos))) top= Vector((0,0,-b)) diff = Vector((x,y,z)) axis = top.cross(diff) a = -(top.angle(diff, 0)) m = Matrix.Rotation(a,3,axis) #Rotates the tetrahedron base to align itself with the starting vector v1 = v1*m v2 = v2*m v3 = v3*m #Sets the positions of bonded atoms to locations in the tetrahedron, scales bond lengths and moves tetrahedron if len(bonds) >1: if isinstance(bonds[1], Atom): bonds[1].pos = v1*sqrt(bonds[1].radius)+offset if len(bonds) >2: if isinstance(bonds[2], Atom): bonds[2].pos = v2*sqrt(bonds[2].radius)+offset if len(bonds)>3: if isinstance(bonds[3],Atom): bonds[3].pos = v3*sqrt(bonds[3].radius)+offset
def make_strut(v1, v2, id, od, n, solid, loops): v1 = Vector(v1) v2 = Vector(v2) axis = v2 - v1 pos = [(0, od / 2)] if loops: pos += [((od - id) / 2, od / 2), (axis.length - (od - id) / 2, od / 2)] pos += [(axis.length, od / 2)] if solid: pos += [(axis.length, id / 2)] if loops: pos += [(axis.length - (od - id) / 2, id / 2), ((od - id) / 2, id / 2)] pos += [(0, id / 2)] vps = len(pos) fps = vps if not solid: fps -= 1 fw = axis.copy() fw.normalize() if (abs(axis[0] / axis.length) < 1e-5 and abs(axis[1] / axis.length) < 1e-5): up = Vector((-1, 0, 0)) else: up = Vector((0, 0, 1)) lf = up.cross(fw) lf.normalize() up = fw.cross(lf) mat = Matrix((fw, lf, up)) mat.transpose() verts = [None] * n * vps faces = [None] * n * fps for i in range(n): base = (i - 1) * vps x = cossin[i][0] y = cossin[i][1] for j in range(vps): p = Vector((pos[j][0], pos[j][1] * x, pos[j][1] * y)) p = mat * p verts[i * vps + j] = p + v1 if i: for j in range(fps): f = (i - 1) * fps + j faces[f] = [base + j, base + vps + j, base + vps + (j + 1) % vps, base + (j + 1) % vps] base = len(verts) - vps i = n for j in range(fps): f = (i - 1) * fps + j faces[f] = [base + j, j, (j + 1) % vps, base + (j + 1) % vps] #print(verts,faces) return verts, faces
def calc_average_area(mdl): frame = mdl.frames[0] if frame.type: frame = frame.frames[0] totalarea = 0.0 for tri in mdl.tris: verts = tuple(map(lambda i: frame.verts[i], tri.verts)) a = Vector(verts[0].r) - Vector(verts[1].r) b = Vector(verts[2].r) - Vector(verts[1].r) c = a.cross(b) totalarea += (c * c) ** 0.5 / 2.0 return totalarea / len(mdl.tris)
def calcRotAngle(self, axis, nor_x, nor_y, nor_z): theta = 0 vector_z = Vector((0.0, 0.0, 1.0)) vector_n = None vector_cross = None if axis == 'X': vector_n = Vector((0.0, nor_y, nor_z)) theta = vector_z.angle(vector_n, 999) vector_cross = vector_n.cross(vector_z) if vector_cross.x < 0: theta = -(theta) elif axis == 'Y': vector_n = Vector((nor_x, 0.0, nor_z)) theta = vector_z.angle(vector_n, 999) vector_cross = vector_n.cross(vector_z) if vector_cross.y < 0: theta = -(theta) else: pass return theta
def vec_roll_to_mat3(vec, roll): target = Vector((0,1,0)) nor = vec.normalized() axis = target.cross(nor) if axis.dot(axis) > 0.000001: axis.normalize() theta = target.angle(nor) bMatrix = Matrix.Rotation(theta, 3, axis) else: updown = 1 if target.dot(nor) > 0 else -1 bMatrix = Matrix.Scale(updown, 3) rMatrix = Matrix.Rotation(roll, 3, nor) mat = rMatrix * bMatrix return mat
def convert_bump(pixels, width, height): outp = list(pixels) for y in range(1, height - 1): for x in range(1, width - 1): i = ((y * width + x) * 4, (y * width + x - 1) * 4, (y * width + x + 1) * 4, ((y - 1) * width + x) * 4, ((y + 1) * width + x) * 4) dx = Vector((1, 0, (pixels[i[2]] - pixels[i[1]]) / 2.0)) dy = Vector((0, 1, (pixels[i[4]] - pixels[i[3]]) / 2.0)) n = dx.cross(dy) n.normalize() outp[i[0]:i[0]+3] = map(lambda x: int(x * 127) + 128, list(n)) return outp
def matchPoleTarget(pb, above, below): x = Vector(above.matrix.col[1][:3]) y = Vector(below.matrix.col[1][:3]) p0 = Vector(below.matrix.col[3][:3]) n = x.cross(y) if abs(n.length) > 1e-4: z = x - y n.normalize() z -= z.dot(n)*n z.normalize() p = p0 + 6*pb.length*z else: p = p0 gmat = Matrix.Translation(p) pmat = getPoseMatrix(gmat, pb) insertLocation(pb, pmat)
def fencehop(): cont = bge.logic.getCurrentController() own = cont.owner auto_action = cont.sensors['auto_action'] if not auto_action: return False elif own['isSpaceon']: #Gather info on the fence fence_obj = auto_action.hitObject fence_pos = fence_obj.worldPosition #get the normal of the fence hit_norm = Vector(auto_action.hitNormal) hit_norm.z = 0.0 #Vector axis of Sintel own_negative_y = Vector(own.getAxisVect((0.0, -1.0, 0.0))) own_negative_y.z = 0.0 #Cross it, then get the absolute vaule cross = hit_norm.cross(own_negative_y) cross = math.fabs(cross[2]) #print (cross) #Check the angle of approach if cross <=.5: new_pos = (fence_pos[2] + 2) own.worldPosition[2] = new_pos own['Leaping']=True CURRENT_SPEED = own.getLinearVelocity(True) if CURRENT_SPEED[1] < 11: CURRENT_SPEED[1] = 11 CURRENT_SPEED[2] = 10 #print (CURRENT_SPEED) own.setLinearVelocity(CURRENT_SPEED ,True) own['Tracking']=False return True else: return False
def matchIkLeg(legIk, toeFk, mBall, mToe, mHeel): rmat = toeFk.matrix.to_3x3() tHead = Vector(toeFk.matrix.col[3][:3]) ty = rmat.col[1] tail = tHead + ty * toeFk.bone.length zBall = mBall.matrix.col[3][2] zToe = mToe.matrix.col[3][2] zHeel = mHeel.matrix.col[3][2] x = Vector(rmat.col[0]) y = Vector(rmat.col[1]) z = Vector(rmat.col[2]) if zHeel > zBall and zHeel > zToe: # 1. foot.ik is flat if abs(y[2]) > abs(z[2]): y = -z y[2] = 0 else: # 2. foot.ik starts at heel hHead = Vector(mHeel.matrix.col[3][:3]) y = tail - hHead y.normalize() x -= x.dot(y)*y x.normalize() if abs(x[2]) < 0.7: x[2] = 0 x.normalize() z = x.cross(y) head = tail - y * legIk.bone.length # Create matrix gmat = Matrix() gmat.col[0][:3] = x gmat.col[1][:3] = y gmat.col[2][:3] = z gmat.col[3][:3] = head pmat = getPoseMatrix(gmat, legIk) insertLocation(legIk, pmat) insertRotation(legIk, pmat)
def polygon_area(verts, poly): ''' The area of the given polygon ''' if len(poly) < 3: # not a plane - no area return 0 total = Vector([0, 0, 0]) N = len(poly) for i in range(N): vi1 = Vector(verts[poly[i]]) vi2 = Vector(verts[poly[(i + 1) % N]]) prod = vi1.cross(vi2) total[0] += prod[0] total[1] += prod[1] total[2] += prod[2] normal = Vector(polygon_normal(verts, poly)) area = abs(total.dot(normal)) / 2 return area
def area_pol(poly): if len(poly) < 3: # not a plane - no area return 0 total = Vector((0, 0, 0)) for i in range(len(poly)): vi1 = Vector(poly[i]) if i is len(poly)-1: vi2 = Vector(poly[0]) else: vi2 = Vector(poly[i+1]) prod = vi1.cross(vi2)[:] total[0] += prod[0] total[1] += prod[1] total[2] += prod[2] result = total.dot(unit_normal(poly[0], poly[1], poly[2])) return abs(result/2)
def main(): cont = bge.logic.getCurrentController() own = cont.owner wall_ray = cont.sensors["wall_ray"] wall_normal = Vector(wall_ray.hitNormal) wall_normal.z = 0.0 own_negative_y = Vector(own.getAxisVect((0.0, -1.0, 0.0))) own_negative_y.z = 0.0 cross = wall_normal.cross(own_negative_y) if cross.z > 0.0: new_dir = Matrix.Rotation(-90.0, 3, 'X') * wall_normal else: new_dir = Matrix.Rotation(90.0, 3, 'X') * wall_normal #print (new_dir) #own.alignAxisToVect(new_dir, 0, .1)
def trackball(p1x, p1y, p2x, p2y, TRACKBALLSIZE=1.0): #""" #if (p1x == p2x) and (p1y == p2y): # return Quaternion() # Zero rotation # First, figure out z-coordinates for projection of P1 and P2 to deformed sphere p1 = Vector((p1x, p1y, tb_project_to_sphere(TRACKBALLSIZE, p1x, p1y))) p2 = Vector((p2x, p2y, tb_project_to_sphere(TRACKBALLSIZE, p2x, p2y))) # Now, we want the cross product of P1 and P2 a = p2.cross(p1) # vcross(p2,p1,a); # Axis of rotation # Figure out how much to rotate around that axis. d = p1 - p2 t = d.magnitude / (2.0*TRACKBALLSIZE) # Avoid problems with out-of-control values... t = min(max(t, -1.0), 1.0) #phi = 2.0 * math.asin(t) # how much to rotate about axis phi = 2.0 * t # how much to rotate about axis return Quaternion(a, phi)
def set_orientation(self, ori, local=False): vector = True try: len(ori[0]) vector = False except TypeError: pass if vector: ori_vec = [float(i) for i in ori] y = Vector((ori_vec[0], ori_vec[1], ori_vec[2])) z = Vector((0.0, 0.0, 1.0)) x = y.cross(z) ori = ([ [x[0], y[0], z[0]], [x[1], y[1], z[1]], [x[2], y[2], z[2]] ]) if local: self.gameobj.localOrientation = ori[:] else: self.gameobj.worldOrientation = ori[:]
def modal_rotate_tool(self, context, eventd): cx,cy = self.action_center mx,my = eventd['mouse'] px,py = self.prev_pos #mode_pos if eventd['press'] in {'RET', 'NUMPAD_ENTER', 'LEFTMOUSE'}: self.tool_fn('commit', eventd) return 'main' if eventd['press'] in {'ESC', 'RIGHTMOUSE'}: self.tool_fn('undo', eventd) return 'main' if eventd['type'] == 'MOUSEMOVE': vp = Vector((px-cx,py-cy,0)) vm = Vector((mx-cx,my-cy,0)) ang = vp.angle(vm) * (-1 if vp.cross(vm).z<0 else 1) self.tool_rot += ang self.tool_fn(self.tool_rot, eventd) self.prev_pos = (mx,my) return '' return ''
def _arc_segment(v_1, v_2): ELorigin = bpy.context.scene.objects['ELorigin'] ELground = bpy.context.scene.objects['ELground'] v = v_2 - v_1 d = v.length ELorigin.location = Vector((0, 0, 0)) ELground.location = Vector((0, 0, -d)) v_L = ELground.location - ELorigin.location q = Quaternion() c = Vector.cross(v_L, v) q.x = c.x q.y = c.y q.z = c.z q.w = sqrt((v_L.length ** 2) * (v.length ** 2)) + \ Vector.dot(v_L, v) q.normalize() euler = q.to_euler() bpy.ops.object.runfslg_operator() laALL = bpy.context.scene.objects['laALL'] laALL.name = 'lARC' laALL.rotation_euler = euler laALL.location = v_1 bpy.context.active_object.select = False laALL.select = True bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) laALL.select = False bpy.context.active_object.select = True return laALL
def camera_light_source(use_camera, use_light, object_center_vec, object_size): camera_factor = 15.0 # If chosen a camera is put into the scene. if use_camera == True: # Assume that the object is put into the global origin. Then, the # camera is moved in x and z direction, not in y. The object has its # size at distance sqrt(object_size) from the origin. So, move the # camera by this distance times a factor of camera_factor in x and z. # Then add x, y and z of the origin of the object. object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0, sqrt(object_size) * camera_factor)) camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera camera_data = bpy.data.cameras.new("A_camera") camera_data.lens = 45 camera_data.clip_end = 500.0 camera = bpy.data.objects.new("A_camera", camera_data) camera.location = camera_xyz_vec bpy.context.collection.objects.link(camera) # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis z_axis_vec = Vector((0.0, 0.0, 1.0)) # The angle between the last two vectors angle = object_camera_vec.angle(z_axis_vec, 0) # The cross-product of z_axis_vec and object_camera_vec axis_vec = z_axis_vec.cross(object_camera_vec) # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. # 4 is the size of the matrix. camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() # Rotate the camera around its axis by 90° such that we have a nice # camera position and view onto the object. bpy.ops.object.select_all(action='DESELECT') camera.select_set(True) # Rotate the camera around its axis 'object_camera_vec' by 90° such # that we have a nice camera view onto the object. matrix_rotation = Matrix.Rotation(90 / 360 * 2 * pi, 4, object_camera_vec) rotate_object(matrix_rotation, camera) # Here a lamp is put into the scene, if chosen. if use_light == True: # This is the distance from the object measured in terms of % # of the camera distance. It is set onto 50% (1/2) distance. lamp_dl = sqrt(object_size) * 15 * 0.5 # This is a factor to which extend the lamp shall go to the right # (from the camera point of view). lamp_dy_right = lamp_dl * (3.0 / 4.0) # Create x, y and z for the lamp. object_lamp_vec = Vector((lamp_dl, lamp_dy_right, lamp_dl)) lamp_xyz_vec = object_center_vec + object_lamp_vec length = lamp_xyz_vec.length # As a lamp we use a point source. lamp_data = bpy.data.lights.new(name="A_lamp", type="POINT") # We now determine the emission strength of the lamp. Note that the # intensity depends on 1/r^2. For this we use a value of 100000.0 at a # distance of 58. This value was determined manually inside Blender. lamp_data.energy = 500000.0 * ((length * length) / (58.0 * 58.0)) lamp = bpy.data.objects.new("A_lamp", lamp_data) lamp.location = lamp_xyz_vec bpy.context.collection.objects.link(lamp) # Some settings for the World: a bit ambient occlusion bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.1
def read(self): tree = et.parse(self.filepath) # Clear the scene for ob in bpy.context.scene.objects: ob.select = True bpy.ops.object.delete() camera = tree.find("./camera") bpy.ops.object.camera_add() camera_obj = bpy.context.selected_objects[0] nearClip = 1e-4 farClip = 1e4 fov = 30.0 width = 1280 height = 720 for parameter in camera: name = parameter.get('name') if name == 'nearClip': nearClip = float(parameter.get('value')) elif name == 'farClip': farClip = float(parameter.get('value')) elif name == 'fov': fov = float(parameter.get('value')) elif name == 'width': width = int(parameter.get('value')) elif name == 'height': height = int(parameter.get('value')) elif name == 'toWorld': lookat = parameter.find("./lookat") target = tuple( float(x) for x in self.__str2float(lookat.get('target'))) origin = tuple( float(x) for x in self.__str2float(lookat.get('origin'))) up = tuple( float(x) for x in self.__str2float(lookat.get('up'))) vtarget = Vector(target) vorigin = Vector(origin) vup = Vector(up) vdir = vtarget - vorigin vdir.normalize() vup.normalize() vleft = vup.cross(vdir) vleft.normalize() newup = vdir.cross(vleft) newup.normalize() lookat_transform = Matrix([ (vleft.x, newup.x, vdir.x, vorigin.x), (vleft.y, newup.y, vdir.y, vorigin.y), (vleft.z, newup.z, vdir.z, vorigin.z), (0, 0, 0, 1) ]) euler = lookat_transform.to_euler() euler.z = euler.z camera_obj.location = vorigin camera_obj.rotation_euler = euler camera_obj.data.clip_end = farClip camera_obj.data.clip_start = nearClip camera_obj.data.lens_unit = 'FOV' camera_obj.data.angle = fov / 180.0 * math.pi bpy.context.scene.render.resolution_x = width bpy.context.scene.render.resolution_y = height camera_obj.select = False meshes = tree.findall("./mesh") # import meshes for mesh in meshes: obj = mesh.find("./string[@name='filename']") obj_path = obj.get("value") bpy.ops.import_scene.obj( filepath=os.path.join(self.workingDir, obj_path)) me = bpy.context.selected_objects[0].data bm = bmesh.new() bm.from_mesh(me) bmesh.ops.scale(bm, vec=(1, -1, -1)) transforms = mesh.find("./transform") for transform in transforms: transform_type = transform.tag values = [ float(x) for x in re.split('[,\s]+', transform.get('value')) ] if transform_type == 'matrix': bmatrix = list(zip(*([iter(values)] * 4))) bmesh.ops.transform(bm, matrix=Matrix(bmatrix)) elif transform_type == 'scale': bmesh.ops.scale(bm, vec=values) elif transform_type == 'translate': bmesh.ops.translate(bm, vec=values) bm.to_mesh(me) bm.free() mat = bpy.data.materials.new( name=bpy.context.selected_objects[0].name) bsdf = mesh.find("./bsdf") bsdf_type = bsdf.get("type") if bsdf_type == 'diffuse': albedo = bsdf.find("./color[@name='albedo']") color = tuple( float(x) for x in self.__str2float(albedo.get('value'))) bpy.context.selected_objects[0].data.materials.append(mat) mat.diffuse_color = color emitter = mesh.find("./emitter") if emitter is not None: radiance = emitter.find("./color[@name='radiance']") color = max( float(x) for x in re.split('[,\s]+', radiance.get('value'))) mat.emit = color / 1000 bpy.context.selected_objects[0].select = False
class GlLine(GlBaseLine): """ 2d/3d Line """ def __init__(self, d=3, p=None, v=None, p0=None, p1=None, z_axis=None): """ d=3 use 3d coords, d=2 use 2d pixels coords Init by either p: Vector or tuple origin v: Vector or tuple size and direction or p0: Vector or tuple 1 point location p1: Vector or tuple 2 point location Will convert any into Vector 3d both optionnals """ if p is not None and v is not None: self.p = Vector(p) self.v = Vector(v) elif p0 is not None and p1 is not None: self.p = Vector(p0) self.v = Vector(p1) - self.p else: self.p = Vector((0, 0, 0)) self.v = Vector((0, 0, 0)) if z_axis is not None: self.z_axis = z_axis else: self.z_axis = Vector((0, 0, 1)) GlBaseLine.__init__(self, d) @property def p0(self): return self.p @property def p1(self): return self.p + self.v @p0.setter def p0(self, p0): """ Note: setting p0 move p0 only """ p1 = self.p1 self.p = Vector(p0) self.v = p1 - p0 @p1.setter def p1(self, p1): """ Note: setting p1 move p1 only """ self.v = Vector(p1) - self.p @property def length(self): return self.v.length @property def angle(self): return atan2(self.v.y, self.v.x) @property def cross(self): """ Vector perpendicular on plane defined by z_axis lie on the right side p1 |--x p0 """ return self.v.cross(self.z_axis) def normal(self, t=0): """ Line perpendicular on plane defined by z_axis lie on the right side p1 |--x p0 """ n = GlLine() n.p = self.lerp(t) n.v = self.cross return n def sized_normal(self, t, size): """ GlLine perpendicular on plane defined by z_axis and of given size positionned at t in current line lie on the right side p1 |--x p0 """ n = GlLine() n.p = self.lerp(t) n.v = size * self.cross.normalized() return n def lerp(self, t): """ Interpolate along segment t parameter [0, 1] where 0 is start of arc and 1 is end """ return self.p + self.v * t def offset(self, offset): """ offset > 0 on the right part """ self.p += offset * self.cross.normalized() def point_sur_segment(self, pt): """ point_sur_segment (2d) point: Vector 3d t: param t de l'intersection sur le segment courant d: distance laterale perpendiculaire positif a droite """ dp = (pt - self.p).to_2d() v2d = self.v.to_2d() dl = v2d.length d = (self.v.x * dp.y - self.v.y * dp.x) / dl t = (v2d * dp) / (dl * dl) return t > 0 and t < 1, d, t @property def pts(self): return [self.p0, self.p1]
def extract_primitives(glTF, blender_mesh, blender_object, blender_vertex_groups, modifiers, export_settings): """ Extract primitives from a mesh. Polygons are triangulated and sorted by material. Furthermore, primitives are split up, if the indices range is exceeded. Finally, triangles are also split up/duplicated, if face normals are used instead of vertex normals. """ print_console('INFO', 'Extracting primitive: ' + blender_mesh.name) if blender_mesh.has_custom_normals: # Custom normals are all (0, 0, 0) until calling calc_normals_split() or calc_tangents(). blender_mesh.calc_normals_split() use_tangents = False if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0: try: blender_mesh.calc_tangents() use_tangents = True except Exception: print_console( 'WARNING', 'Could not calculate tangents. Please try to triangulate the mesh first.' ) # material_map = {} # # Gathering position, normal and tex_coords. # no_material_attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: no_material_attributes[TANGENT_ATTRIBUTE] = [] # # Directory of materials with its primitive. # no_material_primitives = { MATERIAL_ID: 0, INDICES_ID: [], ATTRIBUTES_ID: no_material_attributes } material_idx_to_primitives = {0: no_material_primitives} # vertex_index_to_new_indices = {} material_map[0] = vertex_index_to_new_indices # # Create primitive for each material. # for (mat_idx, _) in enumerate(blender_mesh.materials): attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: attributes[TANGENT_ATTRIBUTE] = [] primitive = { MATERIAL_ID: mat_idx, INDICES_ID: [], ATTRIBUTES_ID: attributes } material_idx_to_primitives[mat_idx] = primitive # vertex_index_to_new_indices = {} material_map[mat_idx] = vertex_index_to_new_indices tex_coord_max = 0 if blender_mesh.uv_layers.active: tex_coord_max = len(blender_mesh.uv_layers) # vertex_colors = {} color_index = 0 for vertex_color in blender_mesh.vertex_colors: vertex_color_name = COLOR_PREFIX + str(color_index) vertex_colors[vertex_color_name] = vertex_color color_index += 1 if color_index >= GLTF_MAX_COLORS: break color_max = color_index # bone_max = 0 for blender_polygon in blender_mesh.polygons: for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index bones_count = len(blender_mesh.vertices[vertex_index].groups) if bones_count > 0: if bones_count % 4 == 0: bones_count -= 1 bone_max = max(bone_max, bones_count // 4 + 1) # morph_max = 0 blender_shape_keys = [] if blender_mesh.shape_keys is not None: for blender_shape_key in blender_mesh.shape_keys.key_blocks: if blender_shape_key != blender_shape_key.relative_key: if blender_shape_key.mute is False: morph_max += 1 blender_shape_keys.append( ShapeKey( blender_shape_key, blender_shape_key.normals_vertex_get( ), # calculate vertex normals for this shape key blender_shape_key.normals_polygon_get()) ) # calculate polygon normals for this shape key armature = None if modifiers is not None: modifiers_dict = {m.type: m for m in modifiers} if "ARMATURE" in modifiers_dict: modifier = modifiers_dict["ARMATURE"] armature = modifier.object # # Convert polygon to primitive indices and eliminate invalid ones. Assign to material. # for blender_polygon in blender_mesh.polygons: export_color = True # if export_settings['gltf_materials'] is False: primitive = material_idx_to_primitives[0] vertex_index_to_new_indices = material_map[0] elif not blender_polygon.material_index in material_idx_to_primitives: primitive = material_idx_to_primitives[0] vertex_index_to_new_indices = material_map[0] else: primitive = material_idx_to_primitives[ blender_polygon.material_index] vertex_index_to_new_indices = material_map[ blender_polygon.material_index] # attributes = primitive[ATTRIBUTES_ID] face_normal = blender_polygon.normal face_tangent = Vector((0.0, 0.0, 0.0)) face_bitangent = Vector((0.0, 0.0, 0.0)) if use_tangents: for loop_index in blender_polygon.loop_indices: temp_vertex = blender_mesh.loops[loop_index] face_tangent += temp_vertex.tangent face_bitangent += temp_vertex.bitangent face_tangent.normalize() face_bitangent.normalize() # indices = primitive[INDICES_ID] loop_index_list = [] if len(blender_polygon.loop_indices) == 3: loop_index_list.extend(blender_polygon.loop_indices) elif len(blender_polygon.loop_indices) > 3: # Triangulation of polygon. Using internal function, as non-convex polygons could exist. polyline = [] for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index v = blender_mesh.vertices[vertex_index].co polyline.append(Vector((v[0], v[1], v[2]))) triangles = tessellate_polygon((polyline, )) for triangle in triangles: for triangle_index in triangle: loop_index_list.append( blender_polygon.loop_indices[triangle_index]) else: continue for loop_index in loop_index_list: vertex_index = blender_mesh.loops[loop_index].vertex_index if vertex_index_to_new_indices.get(vertex_index) is None: vertex_index_to_new_indices[vertex_index] = [] # v = None n = None t = None b = None uvs = [] colors = [] joints = [] weights = [] target_positions = [] target_normals = [] target_tangents = [] vertex = blender_mesh.vertices[vertex_index] v = convert_swizzle_location(vertex.co, armature, blender_object, export_settings) if blender_polygon.use_smooth or blender_mesh.use_auto_smooth: if blender_mesh.has_custom_normals: n = convert_swizzle_normal( blender_mesh.loops[loop_index].normal, armature, blender_object, export_settings) else: n = convert_swizzle_normal(vertex.normal, armature, blender_object, export_settings) if use_tangents: t = convert_swizzle_tangent( blender_mesh.loops[loop_index].tangent, armature, blender_object, export_settings) b = convert_swizzle_location( blender_mesh.loops[loop_index].bitangent, armature, blender_object, export_settings) else: n = convert_swizzle_normal(face_normal, armature, blender_object, export_settings) if use_tangents: t = convert_swizzle_tangent(face_tangent, armature, blender_object, export_settings) b = convert_swizzle_location(face_bitangent, armature, blender_object, export_settings) if use_tangents: tv = Vector((t[0], t[1], t[2])) bv = Vector((b[0], b[1], b[2])) nv = Vector((n[0], n[1], n[2])) if (nv.cross(tv)).dot(bv) < 0.0: t[3] = -1.0 if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): uv = blender_mesh.uv_layers[tex_coord_index].data[ loop_index].uv uvs.append([uv.x, 1.0 - uv.y]) # if color_max > 0 and export_color: for color_index in range(0, color_max): color_name = COLOR_PREFIX + str(color_index) color = vertex_colors[color_name].data[loop_index].color colors.append([ color_srgb_to_scene_linear(color[0]), color_srgb_to_scene_linear(color[1]), color_srgb_to_scene_linear(color[2]), color[3] ]) # bone_count = 0 if blender_vertex_groups is not None and vertex.groups is not None and len( vertex.groups) > 0 and export_settings[ gltf2_blender_export_keys.SKINS]: joint = [] weight = [] vertex_groups = vertex.groups if not export_settings['gltf_all_vertex_influences']: # sort groups by weight descending vertex_groups = sorted(vertex.groups, key=attrgetter('weight'), reverse=True) for group_element in vertex_groups: if len(joint) == 4: bone_count += 1 joints.append(joint) weights.append(weight) joint = [] weight = [] # joint_weight = group_element.weight if joint_weight <= 0.0: continue # vertex_group_index = group_element.group vertex_group_name = blender_vertex_groups[ vertex_group_index].name joint_index = None if armature: skin = gltf2_blender_gather_skins.gather_skin( armature, export_settings) for index, j in enumerate(skin.joints): if j.name == vertex_group_name: joint_index = index break # if joint_index is not None: joint.append(joint_index) weight.append(joint_weight) if len(joint) > 0: bone_count += 1 for fill in range(0, 4 - len(joint)): joint.append(0) weight.append(0.0) joints.append(joint) weights.append(weight) for fill in range(0, bone_max - bone_count): joints.append([0, 0, 0, 0]) weights.append([0.0, 0.0, 0.0, 0.0]) # if morph_max > 0 and export_settings[ gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): blender_shape_key = blender_shape_keys[morph_index] v_morph = convert_swizzle_location( blender_shape_key.shape_key.data[vertex_index].co, armature, blender_object, export_settings) # Store delta. v_morph -= v target_positions.append(v_morph) # n_morph = None if blender_polygon.use_smooth: temp_normals = blender_shape_key.vertex_normals n_morph = (temp_normals[vertex_index * 3 + 0], temp_normals[vertex_index * 3 + 1], temp_normals[vertex_index * 3 + 2]) else: temp_normals = blender_shape_key.polygon_normals n_morph = (temp_normals[blender_polygon.index * 3 + 0], temp_normals[blender_polygon.index * 3 + 1], temp_normals[blender_polygon.index * 3 + 2]) n_morph = convert_swizzle_normal(Vector(n_morph), armature, blender_object, export_settings) # Store delta. n_morph -= n target_normals.append(n_morph) # if use_tangents: rotation = n_morph.rotation_difference(n) t_morph = Vector((t[0], t[1], t[2])) t_morph.rotate(rotation) target_tangents.append(t_morph) # # create = True for current_new_index in vertex_index_to_new_indices[vertex_index]: found = True for i in range(0, 3): if attributes[POSITION_ATTRIBUTE][current_new_index * 3 + i] != v[i]: found = False break if attributes[NORMAL_ATTRIBUTE][current_new_index * 3 + i] != n[i]: found = False break if use_tangents: for i in range(0, 4): if attributes[TANGENT_ATTRIBUTE][current_new_index * 4 + i] != t[i]: found = False break if not found: continue for tex_coord_index in range(0, tex_coord_max): uv = uvs[tex_coord_index] tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) for i in range(0, 2): if attributes[tex_coord_id][current_new_index * 2 + i] != uv[i]: found = False break if export_color: for color_index in range(0, color_max): color = colors[color_index] color_id = COLOR_PREFIX + str(color_index) for i in range(0, 3): # Alpha is always 1.0 - see above. current_color = attributes[color_id][ current_new_index * 4 + i] if color_srgb_to_scene_linear( current_color) != color[i]: found = False break if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint = joints[bone_index] weight = weights[bone_index] joint_id = JOINTS_PREFIX + str(bone_index) weight_id = WEIGHTS_PREFIX + str(bone_index) for i in range(0, 4): if attributes[joint_id][current_new_index * 4 + i] != joint[i]: found = False break if attributes[weight_id][current_new_index * 4 + i] != weight[i]: found = False break if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position = target_positions[morph_index] target_normal = target_normals[morph_index] if use_tangents: target_tangent = target_tangents[morph_index] target_position_id = MORPH_POSITION_PREFIX + str( morph_index) target_normal_id = MORPH_NORMAL_PREFIX + str( morph_index) target_tangent_id = MORPH_TANGENT_PREFIX + str( morph_index) for i in range(0, 3): if attributes[target_position_id][ current_new_index * 3 + i] != target_position[i]: found = False break if attributes[target_normal_id][ current_new_index * 3 + i] != target_normal[i]: found = False break if use_tangents: if attributes[target_tangent_id][ current_new_index * 3 + i] != target_tangent[i]: found = False break if found: indices.append(current_new_index) create = False break if not create: continue new_index = 0 if primitive.get('max_index') is not None: new_index = primitive['max_index'] + 1 primitive['max_index'] = new_index vertex_index_to_new_indices[vertex_index].append(new_index) # # indices.append(new_index) # attributes[POSITION_ATTRIBUTE].extend(v) attributes[NORMAL_ATTRIBUTE].extend(n) if use_tangents: attributes[TANGENT_ATTRIBUTE].extend(t) if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) if attributes.get(tex_coord_id) is None: attributes[tex_coord_id] = [] attributes[tex_coord_id].extend(uvs[tex_coord_index]) if export_color: for color_index in range(0, color_max): color_id = COLOR_PREFIX + str(color_index) if attributes.get(color_id) is None: attributes[color_id] = [] attributes[color_id].extend(colors[color_index]) if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint_id = JOINTS_PREFIX + str(bone_index) if attributes.get(joint_id) is None: attributes[joint_id] = [] attributes[joint_id].extend(joints[bone_index]) weight_id = WEIGHTS_PREFIX + str(bone_index) if attributes.get(weight_id) is None: attributes[weight_id] = [] attributes[weight_id].extend(weights[bone_index]) if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position_id = MORPH_POSITION_PREFIX + str( morph_index) if attributes.get(target_position_id) is None: attributes[target_position_id] = [] attributes[target_position_id].extend( target_positions[morph_index]) target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index) if attributes.get(target_normal_id) is None: attributes[target_normal_id] = [] attributes[target_normal_id].extend( target_normals[morph_index]) if use_tangents: target_tangent_id = MORPH_TANGENT_PREFIX + str( morph_index) if attributes.get(target_tangent_id) is None: attributes[target_tangent_id] = [] attributes[target_tangent_id].extend( target_tangents[morph_index]) # # Add non-empty primitives # result_primitives = [ primitive for primitive in material_idx_to_primitives.values() if len(primitive[INDICES_ID]) != 0 ] print_console('INFO', 'Primitives created: ' + str(len(result_primitives))) return result_primitives
def blend_rec(graph, tcoords, gcoords, colors, label, node_size=3, edge_thickness=0.25): # edge obj bpy.data.materials.new(name="light_gray") bpy.data.materials["light_gray"].diffuse_color = (0.4627450980392157, 0.4627450980392157, 0.4627450980392157) bpy.data.materials["light_gray"].specular_intensity = 0.5 # sphere obj for color, aa in zip(colors, label): bpy.data.materials.new(name=aa) bpy.data.materials[aa].diffuse_color = color bpy.data.materials[aa].specular_intensity = 0.5 # Transparency parameters bpy.data.materials[aa].use_transparency = True bpy.data.materials[aa].transparency_method = "RAYTRACE" bpy.data.materials[aa].alpha = 0.95 bpy.data.materials[aa].raytrace_transparency.fresnel = 0.1 bpy.data.materials[aa].raytrace_transparency.ior = 1.15 # sphere rec obj bpy.data.materials.new(name="red") bpy.data.materials["red"].diffuse_color = (1, 0, 0) bpy.data.materials["red"].specular_intensity = 0.5 # text obj text = bpy.data.objects.new("label", bpy.data.curves.new(type="FONT", name="curve")) bpy.data.materials.new(name="black") bpy.data.materials["black"].diffuse_color = (0, 0, 0) bpy.data.materials["black"].specular_intensity = 0.5 # Set scene, light and alpha_mode scene = bpy.context.scene scene.render.engine = 'BLENDER_RENDER' # 'CYCLE' scene.render.alpha_mode = 'TRANSPARENT' # remove background area = next(area for area in bpy.context.screen.areas if area.type == 'VIEW_3D') area.spaces[0].region_3d.view_perspective = 'CAMERA' bpy.data.worlds["World"].light_settings.use_ambient_occlusion = True bpy.data.worlds["World"].light_settings.samples = 10 # camera position if (len(bpy.data.cameras) == 1): obj = bpy.data.objects['Camera'] # bpy.types.Camera mean_x = np.mean([el[0] for el in tcoords.values()]) mean_y = np.mean([el[1] for el in tcoords.values()]) mean_z = np.mean([el[2] for el in tcoords.values()]) obj.location.x = mean_x obj.location.y = mean_y min_x = min([el[0] for el in tcoords.values()]) max_x = max([el[0] for el in tcoords.values()]) min_y = min([el[1] for el in tcoords.values()]) max_y = max([el[1] for el in tcoords.values()]) obj.location.z = mean_z + max( max_x - min_x, max_y - min_y) / (2 * np.tan(np.radians(20) / 2)) obj.rotation_euler = (0.0, 0.0, np.radians(90)) obj.keyframe_insert(data_path="location", frame=10.0) # Add some mesh primitives bpy.ops.object.select_all(action='DESELECT') bpy.ops.mesh.primitive_uv_sphere_add() sphere = bpy.context.object bpy.ops.mesh.primitive_cylinder_add() cylinder = bpy.context.object cylinder.active_material = bpy.data.materials["light_gray"] bpy.ops.mesh.primitive_uv_sphere_add() gsphere = bpy.context.object gsphere.active_material = bpy.data.materials["red"] # Keep references to all nodes and edges shapes = [] # Keep separate references to shapes to be smoothed shapes_to_smooth = [] # Draw nodes nx.set_node_attributes(graph, [], "color") for node, aa in zip(graph.nodes(), label): # Coloring rule for nodes. Edit this to suit your needs! col = colors[node] # Copy mesh primitive and edit to make node # (You can change the shape of drawn nodes here) node_sphere = sphere.copy() node_sphere.data = sphere.data.copy() node_sphere.location = tcoords[node] node_sphere.dimensions = [node_size] * 3 node_sphere.active_material = bpy.data.materials[aa] bpy.context.scene.objects.link(node_sphere) shapes.append(node_sphere) shapes_to_smooth.append(node_sphere) #lbl = text.copy() #lbl.data = text.data.copy() #lbl.data.body = label[node] #lbl.rotation_mode = "AXIS_ANGLE" #lbl.rotation_euler = (0.0, 0.0, np.radians(90)) #lbl.active_material = bpy.data.materials["black"] #lbl.dimensions = [.00000001] * 3 #lbl.location = np.asarray(tcoords[node]) + [0., 0., node_size/2] #bpy.context.scene.objects.link(lbl) # guess atom gues_sphere = gsphere.copy() gues_sphere.data = gsphere.data.copy() gues_sphere.dimensions = [node_size] * 3 gues_sphere.location = gcoords[node] gues_sphere.active_material = bpy.data.materials["red"] bpy.context.scene.objects.link(gues_sphere) shapes.append(gues_sphere) shapes_to_smooth.append(gues_sphere) # Draw edges for source, target in graph.edges(): # Get source and target locations by drilling down into data structure source_loc = tcoords[source] target_loc = tcoords[target] diff = [c2 - c1 for c2, c1 in zip(source_loc, target_loc)] cent = [(c2 + c1) / 2 for c2, c1 in zip(source_loc, target_loc)] mag = sum([(c2 - c1)**2 for c1, c2 in zip(source_loc, target_loc)])**0.5 # Euler rotation calculation v_axis = Vector(diff).normalized() v_obj = Vector((0, 0, 1)) v_rot = v_obj.cross(v_axis) angle = acos(v_obj.dot(v_axis)) # Copy mesh primitive to create edge edge_cylinder = cylinder.copy() edge_cylinder.data = cylinder.data.copy() edge_cylinder.dimensions = [edge_thickness] * 2 + [mag - node_size] edge_cylinder.location = cent edge_cylinder.rotation_mode = "AXIS_ANGLE" edge_cylinder.rotation_axis_angle = [angle] + list(v_rot) bpy.context.scene.objects.link(edge_cylinder) shapes.append(edge_cylinder) shapes_to_smooth.append(edge_cylinder) # Remove primitive meshes bpy.ops.object.select_all(action='DESELECT') sphere.select = True gsphere.select = True cylinder.select = True # If the starting cube is there, remove it if "Cube" in bpy.data.objects.keys(): bpy.data.objects.get("Cube").select = True bpy.ops.object.delete() # Smooth specified shapes for shape in shapes_to_smooth: shape.select = True bpy.context.scene.objects.active = shapes_to_smooth[0] bpy.ops.object.shade_smooth() # Join shapes for shape in shapes: shape.select = True bpy.context.scene.objects.active = shapes[0] bpy.ops.object.join() # Center object origin to geometry bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN") # Refresh scene bpy.context.scene.update()
class GlHandle(GlPolygon): def __init__(self, sensor_size, size, draggable=False, selectable=False, d=3): """ sensor_size : 2d size in pixels of sensor area size : 3d size of handle """ GlPolygon.__init__(self, d=d) self.colour_active = (1.0, 0.0, 0.0, 1.0) self.colour_hover = (1.0, 1.0, 0.0, 1.0) self.colour_normal = (1.0, 1.0, 1.0, 1.0) self.colour_selected = (0.0, 0.0, 0.7, 1.0) self.size = size self.sensor_width = sensor_size self.sensor_height = sensor_size self.pos_3d = Vector((0, 0, 0)) self.up_axis = Vector((0, 0, 0)) self.c_axis = Vector((0, 0, 0)) self.hover = False self.active = False self.draggable = draggable self.selectable = selectable self.selected = False def set_pos(self, context, pos_3d, direction, normal=Vector((0, 0, 1))): self.up_axis = direction.normalized() self.c_axis = self.up_axis.cross(normal) self.pos_3d = pos_3d self.pos_2d = self.position_2d_from_coord(context, self.sensor_center) def check_hover(self, pos_2d): if self.draggable: dp = pos_2d - self.pos_2d self.hover = abs(dp.x) < self.sensor_width and abs( dp.y) < self.sensor_height @property def sensor_center(self): pts = self.pts n = len(pts) x, y, z = 0, 0, 0 for pt in pts: x += pt.x y += pt.y z += pt.z return Vector((x / n, y / n, z / n)) @property def pts(self): raise NotImplementedError @property def colour(self): if self.render: return self.colour_inactive elif self.draggable: if self.active: return self.colour_active elif self.hover: return self.colour_hover elif self.selected: return self.colour_selected return self.colour_normal else: return self.colour_inactive
def tp_insert_mesh_geometry(self, context): wm = context.window_manager object = wm.TP_Tileable_Mesh_Previews obj_list = [] second_obj = "" if bpy.context.mode == 'OBJECT': tp_desc_mesh_insert(object) ###objectlist elif bpy.context.mode == 'EDIT': bpy.ops.object.mode_set(mode='OBJECT') ref_obj = bpy.context.active_object if len(context.selected_objects) == 2: obj1, obj2 = context.selected_objects second_obj = obj1 if obj2 == ref_obj else obj2 bpy.data.objects[second_obj.name].select = False bpy.ops.object.duplicate_move() bpy.context.active_object.name = "Dummy" obj = context.active_object 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.context.scene.cursor_location = loc_world_space tp_desc_mesh_insert(object) ###objectlist bpy.ops.transform.rotate(value=angle, axis=axis) obj_list.append(context.object.name) bm.to_mesh(obj.data) bm.free() bpy.context.scene.cursor_location = copy_cursor bpy.ops.object.select_all(action='DESELECT') bpy.context.scene.objects.active = bpy.data.objects["Dummy"] bpy.data.objects["Dummy"].select = True bpy.ops.object.delete(use_global=False) bpy.context.scene.objects.active = bpy.data.objects[obj_list[0]] for obj in obj_list: bpy.data.objects[obj].select = True bpy.ops.make.link() # custom link called from operators module bpy.ops.object.select_all(action='DESELECT') bpy.context.scene.objects.active = bpy.data.objects[ref_obj.name] if second_obj: bpy.data.objects[second_obj.name].select = True bpy.data.objects[ref_obj.name].select = True bpy.ops.object.mode_set(mode='EDIT') del (obj_list[:])
def execute(self, context): obj_list = [] bpy.ops.object.mode_set(mode='OBJECT') ref_obj = bpy.context.active_object obj1, obj2 = context.selected_objects second_obj = obj1 if obj2 == ref_obj else obj2 obj_list.append(second_obj.name) bpy.data.objects[second_obj.name].select = False bpy.ops.object.duplicate_move() bpy.context.active_object.name = "Dummy" obj = context.active_object 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') bpy.context.scene.objects.active = bpy.data.objects[ second_obj.name] bpy.data.objects[second_obj.name].select = True bpy.ops.object.duplicate() bpy.context.scene.cursor_location = loc_world_space bpy.ops.view3d.snap_selected_to_cursor() bpy.ops.transform.rotate(value=angle, axis=axis) obj_list.append(context.object.name) bm.to_mesh(obj.data) bm.free() bpy.context.scene.cursor_location = copy_cursor bpy.ops.object.select_all(action='DESELECT') bpy.context.scene.objects.active = bpy.data.objects["Dummy"] bpy.data.objects["Dummy"].select = True bpy.ops.object.delete(use_global=False) bpy.context.scene.objects.active = bpy.data.objects[obj_list[0]] for obj in obj_list: bpy.data.objects[obj].select = True bpy.ops.make.link() # custom link called from operators module bpy.ops.object.select_all(action='DESELECT') bpy.context.scene.objects.active = bpy.data.objects[ref_obj.name] bpy.data.objects[second_obj.name].select = True bpy.data.objects[ref_obj.name].select = True bpy.ops.object.mode_set(mode='EDIT') del (obj_list[:]) return {'FINISHED'}
def create_object_to_selection(self, context): if not bpy.context.object: add_primitive() # bpy.ops.object.orientationvariable(variable="LOCAL") elif context.object.mode == 'EDIT': #Duplicate the mesh, apply his transform and perform the code to add object on it bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.duplicate_move() bpy.context.active_object.name = "Dummy" bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM') bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) list_insert_meshes = [] obj = bpy.context.active_object saved_location = 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.context.scene.cursor_location = loc_world_space add_primitive() bpy.ops.transform.rotate(value=angle, axis=axis) list_insert_meshes.append(context.active_object.name) bm.to_mesh(obj.data) bm.free() bpy.context.scene.cursor_location = saved_location #Deselect all the objects, select the dummy object and delete it bpy.ops.object.select_all(action='DESELECT') bpy.context.scene.objects.active = bpy.data.objects["Dummy"] bpy.context.object.select = True bpy.ops.object.delete(use_global=False) #Select inserted meshes for obj in list_insert_meshes: bpy.context.scene.objects.active = bpy.data.objects[obj] bpy.data.objects[obj].select = True if len(list_insert_meshes) > 1: bpy.ops.object.make_links_data(type='OBDATA') #Make link del (list_insert_meshes[:]) # bpy.ops.object.orientationvariable(variable="LOCAL") else: saved_location = bpy.context.scene.cursor_location.copy() bpy.ops.view3d.snap_cursor_to_selected() add_primitive() bpy.context.scene.cursor_location = saved_location
def modal(self, context, event): context.area.tag_redraw() lin_def_settings = context.scene.mi_ldeformer_settings region = context.region rv3d = context.region_data m_coords = event.mouse_region_x, event.mouse_region_y active_obj = context.scene.objects.active bm = bmesh.from_edit_mesh(active_obj.data) # update check if lin_def_settings.manual_update is True: if event.type == 'U': if event.value == 'PRESS': self.do_update = True else: self.do_update = False else: self.do_update = True # tooltip tooltip_text = None if lin_def_settings.manual_update is True and self.tool_mode not in { 'IDLE', 'MOVE_POINT' }: tooltip_text = "Press U key to udate!" else: tooltip_text = "I:Invert, Z:Z-Constraint, X:X-Constraint, S:Scale, Shift-S:ScaleForward, G:Move, R:Rotate, B:Bend, Shift-B:BendSpiral, T:Tape, Shift-T:Twist, Ctrl+Z:Undo, Ctrl+Shift+Z:Redo" context.area.header_text_set(tooltip_text) # key pressed if self.tool_mode == 'IDLE' and event.value == 'PRESS': if event.type in {'LEFTMOUSE', 'SELECTMOUSE'}: if self.lw_tool: # pick linear widget point picked_point = l_widget.pick_lw_point( context, m_coords, self.lw_tool) if picked_point: self.deform_mouse_pos = Vector(m_coords) self.active_lw_point = picked_point self.tool_mode = 'MOVE_POINT' else: picked_point = ut_base.get_mouse_on_plane( context, self.start_work_center, None, m_coords) if picked_point: self.lw_tool = l_widget.MI_Linear_Widget() self.lw_tool.start_point = l_widget.MI_LW_Point( picked_point.copy()) self.lw_tool.middle_point = l_widget.MI_LW_Point( picked_point.copy()) self.lw_tool.end_point = l_widget.MI_LW_Point( picked_point) self.active_lw_point = self.lw_tool.end_point self.tool_mode = 'MOVE_POINT' elif event.type in {'S', 'G', 'R', 'B', 'T'}: # set tool type if event.type == 'S': if event.shift: self.tool_mode = 'SCALE_FRONT' else: self.tool_mode = 'SCALE_ALL' elif event.type == 'R': self.tool_mode = 'ROTATE_ALL' elif event.type == 'G': self.tool_mode = 'MOVE_ALL' elif event.type == 'B': if event.shift: self.tool_mode = 'BEND_SPIRAL' else: self.tool_mode = 'BEND_ALL' elif event.type == 'T': if event.shift: self.tool_mode = 'TWIST' else: self.tool_mode = 'TAPE' # get tool verts if self.tool_mode in {'SCALE_FRONT', 'TAPE'}: # do not clamp for SCALE_FRONT mode self.apply_tool_verts = l_widget.get_tool_verts( self.lw_tool, self.work_verts, bm, active_obj, False, True) else: self.apply_tool_verts = l_widget.get_tool_verts( self.lw_tool, self.work_verts, bm, active_obj, True, True) # set some settings for tools if self.tool_mode in {'SCALE_ALL', 'SCALE_FRONT', 'TAPE'}: self.deform_mouse_pos = Vector(m_coords) elif self.tool_mode == 'MOVE_ALL': mouse_pos_3d = ut_base.get_mouse_on_plane( context, self.lw_tool.start_point.position, None, m_coords) self.deform_vec_pos = mouse_pos_3d # 3d location elif self.tool_mode in { 'ROTATE_ALL', 'TWIST', 'BEND_ALL', 'BEND_SPIRAL' }: start_2d = view3d_utils.location_3d_to_region_2d( region, rv3d, self.lw_tool.start_point.position) self.deform_vec_pos = ( Vector(m_coords) - start_2d).normalized() # 2d direction self.deform_mouse_pos = 0.0 # we will use it as angle counter if self.tool_mode in {'BEND_SPIRAL', 'BEND_ALL'}: self.bend_scale_len = (Vector(m_coords) - start_2d).length #return {'RUNNING_MODAL'} elif event.type in {'Z', 'X'} and self.lw_tool: if event.type == 'Z' and event.ctrl: if event.shift: redo_history(bm, self.h_undo, self.h_redo, active_obj) else: undo_history(bm, self.h_undo, self.h_redo, active_obj) else: pre_verts = [bm.verts[v_id] for v_id in self.work_verts] if event.type == 'X': if self.lw_tool_axis: if self.lw_tool_axis == 'X': l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'X_Left', 1.0) self.lw_tool_axis = 'X_Left' elif self.lw_tool_axis == 'X_Left': l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'X_Right', 1.0) ## revert direction #stp = self.lw_tool.start_point.position.copy() #self.lw_tool.start_point.position = self.lw_tool.end_point.position #self.lw_tool.end_point.position = stp self.lw_tool_axis = 'X_Right' elif self.lw_tool_axis == 'X_Right': l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'X', 1.0) self.lw_tool_axis = 'X' else: l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'X', 1.0) self.lw_tool_axis = 'X' else: l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'X', 1.0) self.lw_tool_axis = 'X' else: if self.lw_tool_axis: if self.lw_tool_axis == 'Z': l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'Z_Top', 1.0) self.lw_tool_axis = 'Z_Top' elif self.lw_tool_axis == 'Z_Top': l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'Z_Bottom', 1.0) ## revert direction #stp = self.lw_tool.start_point.position.copy() #self.lw_tool.start_point.position = self.lw_tool.end_point.position #self.lw_tool.end_point.position = stp self.lw_tool_axis = 'Z_Bottom' elif self.lw_tool_axis == 'Z_Bottom': l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'Z', 1.0) self.lw_tool_axis = 'Z' else: l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'Z', 1.0) self.lw_tool_axis = 'Z' else: l_widget.setup_lw_tool(rv3d, self.lw_tool, active_obj, pre_verts, 'Z', 1.0) self.lw_tool_axis = 'Z' elif event.type == 'I' and self.lw_tool: start_copy = self.lw_tool.start_point.position.copy() self.lw_tool.start_point.position = self.lw_tool.end_point.position self.lw_tool.end_point.position = start_copy # TOOL WORK! if self.tool_mode == 'MOVE_POINT': if event.value == 'RELEASE': self.tool_mode = 'IDLE' return {'RUNNING_MODAL'} else: # move points new_point_pos = ut_base.get_mouse_on_plane( context, self.active_lw_point.position, None, m_coords) if self.active_lw_point.position == self.lw_tool.start_point.position or self.active_lw_point.position == self.lw_tool.end_point.position: self.active_lw_point.position = new_point_pos l_widget.update_middle_point(self.lw_tool) elif self.active_lw_point.position == self.lw_tool.middle_point.position: self.lw_tool.start_point.position += new_point_pos - self.active_lw_point.position self.lw_tool.end_point.position += new_point_pos - self.active_lw_point.position self.lw_tool.middle_point.position = new_point_pos return {'RUNNING_MODAL'} elif self.tool_mode in {'SCALE_ALL', 'SCALE_FRONT', 'TAPE'}: if event.value == 'RELEASE' and event.type in { 'LEFTMOUSE', 'SELECTMOUSE' }: bm.normal_update() # add to undo history self.h_redo.clear() pre_work_verts = [bm.verts[v_id] for v_id in self.work_verts] add_history(pre_work_verts, self.h_undo) self.tool_mode = 'IDLE' elif self.do_update: # move points start_point_2d = view3d_utils.location_3d_to_region_2d( region, rv3d, self.lw_tool.start_point.position) if start_point_2d: tool_dist = (start_point_2d - self.deform_mouse_pos).length now_dist = (start_point_2d - Vector(m_coords)).length apply_value = (now_dist - tool_dist) / tool_dist if apply_value != 0.0: tool_orig = active_obj.matrix_world.inverted( ) * self.lw_tool.start_point.position tool_end = active_obj.matrix_world.inverted( ) * self.lw_tool.end_point.position tool_vec = tool_end - tool_orig tool_dir = (tool_end - tool_orig).normalized() for vert_data in self.apply_tool_verts: scale_vec = None scale_value = vert_data[1] if self.tool_mode == 'SCALE_ALL': scale_vec = (vert_data[2] - tool_orig) elif self.tool_mode == 'SCALE_FRONT': scale_vec = (tool_end - tool_orig) else: # TAPE scale_vec = vert_data[2] - ( tool_orig + (tool_dir * vert_data[1] * (tool_vec).length)) scale_value = min(1.0, vert_data[1]) bm.verts[vert_data[0]].co = vert_data[2] + ( scale_vec * (scale_value * apply_value)) #bm.normal_update() bmesh.update_edit_mesh(active_obj.data) self.do_update = False return {'RUNNING_MODAL'} elif self.tool_mode == 'MOVE_ALL': if event.value == 'RELEASE' and event.type in { 'LEFTMOUSE', 'SELECTMOUSE' }: bm.normal_update() # add to undo history self.h_redo.clear() pre_work_verts = [bm.verts[v_id] for v_id in self.work_verts] add_history(pre_work_verts, self.h_undo) self.tool_mode = 'IDLE' elif self.do_update: mouse_pos_3d = ut_base.get_mouse_on_plane( context, self.lw_tool.start_point.position, None, m_coords) mouse_pos_3d = active_obj.matrix_world.inverted( ) * mouse_pos_3d start_pos = active_obj.matrix_world.inverted( ) * self.lw_tool.start_point.position orig_pos = active_obj.matrix_world.inverted( ) * self.deform_vec_pos orig_vec = orig_pos - start_pos move_vec = (mouse_pos_3d - start_pos) - orig_vec for vert_data in self.apply_tool_verts: move_value = vert_data[1] bm.verts[vert_data[0]].co = vert_data[2] + (move_vec * move_value) #bm.normal_update() bmesh.update_edit_mesh(active_obj.data) self.do_update = False return {'RUNNING_MODAL'} elif self.tool_mode in { 'ROTATE_ALL', 'TWIST', 'BEND_ALL', 'BEND_SPIRAL' }: if event.value == 'RELEASE' and event.type in { 'LEFTMOUSE', 'SELECTMOUSE' }: bm.normal_update() # add to undo history self.h_redo.clear() pre_work_verts = [bm.verts[v_id] for v_id in self.work_verts] add_history(pre_work_verts, self.h_undo) self.tool_mode = 'IDLE' elif self.do_update: m_coords = Vector( m_coords) # convert into vector for operations start_2d = view3d_utils.location_3d_to_region_2d( region, rv3d, self.lw_tool.start_point.position) new_vec_dir = (m_coords - start_2d).normalized() rot_angle = new_vec_dir.angle(self.deform_vec_pos) start_3d = self.lw_tool.start_point.position end_3d = self.lw_tool.end_point.position if rot_angle != 0.0: # check for left or right direction to rotate vec_check_1 = Vector((new_vec_dir[0], new_vec_dir[1], 0)) vec_check_2 = Vector( (new_vec_dir[0] - self.deform_vec_pos[0], new_vec_dir[1] - self.deform_vec_pos[1], 0)) checker_side_dir = vec_check_1.cross( vec_check_2).normalized()[2] if checker_side_dir > 0.0: rot_angle = -rot_angle start_pos = self.lw_tool.start_point.position rot_dir = None if self.tool_mode == 'ROTATE_FRONT' or self.tool_mode == 'TWIST': # ROTATE_FRONT code rot_dir = (end_3d - start_3d).normalized() else: rot_dir = (rv3d.view_rotation * Vector( (0.0, 0.0, -1.0))).normalized() rot_angle += self.deform_mouse_pos # add rot angle bend_side_dir = None faloff_len = None spiral_value = 0.0 # only for BEND_SPIRAL bend_scale_value = 1.0 # only for BEND_ALL if self.tool_mode == 'BEND_ALL' or self.tool_mode == 'BEND_SPIRAL': bend_side_dir = (((end_3d - start_3d).normalized() ).cross(rot_dir)).normalized() faloff_len = end_3d - start_3d if self.tool_mode == 'BEND_SPIRAL': val_scale = None if rot_angle > 0.0: val_scale = (1.0 - ((m_coords - start_2d).length / self.bend_scale_len)) else: val_scale = (((m_coords - start_2d).length / self.bend_scale_len)) spiral_value = 1.0 - (faloff_len.length * val_scale) else: val_scale = (((m_coords - start_2d).length / self.bend_scale_len)) bend_scale_value = ((val_scale)) do_bend = False if self.tool_mode == 'BEND_ALL' or self.tool_mode == 'BEND_SPIRAL': do_bend = True for vert_data in self.apply_tool_verts: apply_value = vert_data[1] final_apply_value = rot_angle * apply_value # do rotation if final_apply_value != 0.0: rot_mat = Matrix.Rotation(final_apply_value, 3, rot_dir) vert = bm.verts[vert_data[0]] if do_bend: vert_temp = (active_obj.matrix_world * vert_data[2]) - ( (faloff_len) * apply_value) back_offset = ( ((faloff_len).length / (final_apply_value)) + spiral_value) * ( apply_value * bend_scale_value) vert_temp += bend_side_dir * back_offset vert.co = vert_temp else: # set original position vert.co[0] = vert_data[2][0] vert.co[1] = vert_data[2][1] vert.co[2] = vert_data[2][2] # ROTATE VERTS! if do_bend: vert.co = rot_mat * ( (vert.co) - start_pos) + start_pos back_offset = ( (faloff_len).length / (final_apply_value)) * (apply_value * bend_scale_value) vert.co = active_obj.matrix_world.inverted( ) * (vert.co - (bend_side_dir * back_offset)) else: vert.co = active_obj.matrix_world.inverted( ) * (rot_mat * ((active_obj.matrix_world * vert.co) - start_pos) + start_pos) self.deform_vec_pos = new_vec_dir self.deform_mouse_pos = rot_angle # set new angle rotation for next step #bm.normal_update() bmesh.update_edit_mesh(active_obj.data) self.do_update = False return {'RUNNING_MODAL'} else: if event.value == 'RELEASE' and event.type in { 'LEFTMOUSE', 'SELECTMOUSE' }: self.tool_mode = 'IDLE' return {'RUNNING_MODAL'} # main stuff if event.type in {'RIGHTMOUSE', 'ESC'}: context.space_data.show_manipulator = self.manipulator # bpy.types.SpaceView3D.draw_handler_remove(self.lin_deform_handle_3d, 'WINDOW') bpy.types.SpaceView3D.draw_handler_remove( self.lin_deform_handle_2d, 'WINDOW') context.area.header_text_set() return {'FINISHED'} elif event.type in self.pass_keys: # allow navigation return {'PASS_THROUGH'} return {'RUNNING_MODAL'}
def create_mesh(data_list, AFMdata, use_smooth, scale_size, scale_height, use_camera, use_lamp): # This is for the image name. path_list = AFMdata.datfile.strip('/').split('/') number_img = len(data_list) image_x_offset_gap = 10.0 * scale_size image_x_all = sum(AFMdata.x_size) * scale_size image_x_offset = -(image_x_all + image_x_offset_gap * (number_img - 1)) / 2.0 # For each image do: for k, data in enumerate(data_list): size_x = AFMdata.x_pixel[k] size_y = AFMdata.y_pixel[k] image_scale = AFMdata.x_size[k] / float(AFMdata.x_pixel[k]) image_scale = image_scale * scale_size image_x_size = AFMdata.x_size[k] * scale_size image_x_offset += image_x_size / 2.0 image_name = path_list[-1] + "_" + AFMdata.channel[k][1] data_mesh = [] data_faces = [] #print("passed - create_mesh ---- 1") for i, line in enumerate(data): for j, pixel in enumerate(line): # The vertices data_mesh.append( Vector((float(i) * image_scale, float(j) * image_scale, float(pixel) * scale_height))) # The faces if i < size_y - 1 and j < size_x - 1: data_faces.append([ size_x * i + j, size_x * (i + 1) + j, size_x * (i + 1) + j + 1, size_x * i + j + 1 ]) #print("passed - create_mesh ---- 2") # Build the mesh surface_mesh = bpy.data.meshes.new("Mesh") surface_mesh.from_pydata(data_mesh, [], data_faces) surface_mesh.update() surface = bpy.data.objects.new(image_name, surface_mesh) bpy.context.scene.objects.link(surface) bpy.ops.object.select_all(action='DESELECT') surface.select = True bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') # sum((v.co for v in mesh.vertices), Vector()) / len(mesh.vertices) if use_smooth: for polygon in surface.data.polygons: polygon.use_smooth = True surface.location = Vector((0.0, image_x_offset, 0.0)) image_x_offset += image_x_size / 2.0 + image_x_offset_gap #print("passed - create_mesh ---- 3") object_center_vec = Vector((0.0, 0.0, 0.0)) object_size = (sum(AFMdata.x_size) * scale_size + image_x_offset_gap * (len(data_list) - 1)) # ------------------------------------------------------------------------ # CAMERA AND LAMP camera_factor = 20.0 # If chosen a camera is put into the scene. if use_camera == True: # Assume that the object is put into the global origin. Then, the # camera is moved in x and z direction, not in y. The object has its # size at distance sqrt(object_size) from the origin. So, move the # camera by this distance times a factor of camera_factor in x and z. # Then add x, y and z of the origin of the object. object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0, sqrt(object_size) * camera_factor)) camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera current_layers = bpy.context.scene.layers camera_data = bpy.data.cameras.new("A_camera") camera_data.lens = 45 camera_data.clip_end = 50000.0 camera = bpy.data.objects.new("A_camera", camera_data) camera.location = camera_xyz_vec camera.layers = current_layers bpy.context.scene.objects.link(camera) # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis z_axis_vec = Vector((0.0, 0.0, 1.0)) # The angle between the last two vectors angle = object_camera_vec.angle(z_axis_vec, 0) # The cross-product of z_axis_vec and object_camera_vec axis_vec = z_axis_vec.cross(object_camera_vec) # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. # 4 is the size of the matrix. camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() # Rotate the camera around its axis by 90° such that we have a nice # camera position and view onto the object. bpy.ops.object.select_all(action='DESELECT') camera.select = True bpy.ops.transform.rotate(value=(90.0 * 2 * pi / 360.0), axis=object_camera_vec, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False) # Here a lamp is put into the scene, if chosen. if use_lamp == True: # This is the distance from the object measured in terms of % # of the camera distance. It is set onto 50% (1/2) distance. lamp_dl = sqrt(object_size) * 15 * 0.5 # This is a factor to which extend the lamp shall go to the right # (from the camera point of view). lamp_dy_right = lamp_dl * (3.0 / 4.0) # Create x, y and z for the lamp. object_lamp_vec = Vector((lamp_dl, lamp_dy_right, lamp_dl)) lamp_xyz_vec = object_center_vec + object_lamp_vec # Create the lamp current_layers = bpy.context.scene.layers lamp_data = bpy.data.lamps.new(name="A_lamp", type="POINT") lamp_data.distance = 5000.0 lamp_data.energy = 3.0 lamp_data.shadow_method = 'RAY_SHADOW' lamp = bpy.data.objects.new("A_lamp", lamp_data) lamp.location = lamp_xyz_vec lamp.layers = current_layers bpy.context.scene.objects.link(lamp) bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.1
def create_poly_box(polyline, allverts, materials, geocenter): materialindex = int(polyline.attrib["m"]) idxs = [] idxs.append(int(polyline.attrib["v1"])) idxs.append(int(polyline.attrib["v2"])) idxs.append(int(polyline.attrib["v3"])) idxs.append(int(polyline.attrib["v4"])) verts = [] for idx in idxs: verts.append(allverts[idx]) p1 = verts[0] p2 = verts[1] p3 = verts[2] p4 = verts[3] a, b, c, d = p1, p2, p3, p4 cf1 = (a + b) / 2 cf2 = (c + d) / 2 cf3 = (a + d) / 2 cf4 = (b + c) / 2 cf5 = (a + c) / 2 cf6 = (b + d) / 2 # caclulate box center center = (cf3 + cf4) / 2 rightest = get_closest_axis_point(Vector((1, 0, 0)), center, [cf1, cf2, cf3, cf4, cf5, cf6]) upest = get_closest_axis_point(Vector((0, 0, 1)), center, [cf1, cf2, cf3, cf4, cf5, cf6]) right = (rightest - center).normalized() up = (upest - center).normalized() forward = Vector.cross(right, up).normalized() mat = Matrix.Identity(4) mat[0] = (right.x, right.y, right.z, 0) mat[1] = (up.x, up.y, up.z, 0) mat[2] = (forward.x, forward.y, forward.z, 0) mat[3] = (0, 0, 0, 1) mat.normalize() rotation = mat.to_quaternion().inverted().normalized().to_euler('XYZ') # calculate scale seq = [cf1, cf2, cf3, cf4, cf5, cf6] _cf1 = get_closest_axis_point(right, center, seq) _cf2 = get_closest_axis_point(-right, center, seq) _cf3 = get_closest_axis_point(-up, center, seq) _cf4 = get_closest_axis_point(up, center, seq) _cf5 = get_closest_axis_point(-forward, center, seq) _cf6 = get_closest_axis_point(forward, center, seq) W = (_cf2 - _cf1).length L = (_cf3 - _cf4).length H = (_cf5 - _cf6).length scale = Vector((W, L, H)) # blender stuff mesh = bpy.data.meshes.new("box") bm = bmesh.new() bmesh.ops.create_cube(bm, size=1) bm.to_mesh(mesh) bm.free() mat = materials[materialindex] mesh.materials.append(mat) obj = bpy.data.objects.new("box", mesh) #obj.data.materials.append(material) #location = center + geocenter obj.location = center #location #boxes dont use geocenter to offset? obj.rotation_euler = rotation obj.scale = scale obj.sollumtype = "Bound Box" return obj
class GlHandle(GlPolygon): def __init__(self, sensor_size, size, draggable=False, selectable=False, d=3): """ sensor_size : 2d size in pixels of sensor area size : 3d size of handle """ GlPolygon.__init__(self, d=d) self.colour_active = (1.0, 0.0, 0.0, 1.0) self.colour_hover = (1.0, 1.0, 0.0, 1.0) self.colour_normal = (1.0, 1.0, 1.0, 1.0) self.colour_selected = (0.0, 0.0, 0.7, 1.0) self.size = size self.sensor_width = sensor_size self.sensor_height = sensor_size self.pos_3d = Vector((0, 0, 0)) self.up_axis = Vector((0, 0, 0)) self.c_axis = Vector((0, 0, 0)) self.hover = False self.active = False self.draggable = draggable self.selectable = selectable self.selected = False def set_pos(self, context, pos_3d, direction, normal=Vector((0, 0, 1))): self.up_axis = direction.normalized() self.c_axis = self.up_axis.cross(normal) self.pos_3d = pos_3d self.pos_2d = self.position_2d_from_coord(context, self.sensor_center) def check_hover(self, pos_2d): if self.draggable: dp = pos_2d - self.pos_2d self.hover = abs(dp.x) < self.sensor_width and abs(dp.y) < self.sensor_height @property def sensor_center(self): pts = self.pts n = len(pts) x, y, z = 0, 0, 0 for pt in pts: x += pt.x y += pt.y z += pt.z return Vector((x / n, y / n, z / n)) @property def pts(self): raise NotImplementedError @property def colour(self): if self.render: return self.colour_inactive elif self.draggable: if self.active: return self.colour_active elif self.hover: return self.colour_hover elif self.selected: return self.colour_selected return self.colour_normal else: return self.colour_inactive
def add_f_curve_modifiers(armature_object, strength, speed): wind_vector = Vector((1, 0, 0)) * strength fcurves = armature_object.animation_data.action.fcurves for f in fcurves: for m in f.modifiers: f.modifiers.remove(m) bones = organize_bones(armature_object) for b in bones: mass = b.bone.tail_radius**2 * b.length barycenter = b.tail * mass for c in b.children: mass += c["mass"] barycenter += Vector(c["barycenter"]) b["mass"] = mass b["barycenter"] = barycenter barycenter /= mass b.rotation_mode = 'XYZ' b.keyframe_insert('rotation_euler', frame=0, index=0) b.keyframe_insert('rotation_euler', frame=0, index=2) fcurves = armature_object.animation_data.action.fcurves for i in range(len(bones)): f0 = fcurves[2 * i] f1 = fcurves[2 * i + 1] b = bones[i] i_base = b.matrix.to_3x3().inverted() bone_vector = b.tail - b.head inertia_moment = bone_vector.length**2 * bones[i]["mass"] / 10000 damping = 0.5 * b.bone.tail_radius stiffness = b.bone.tail_radius**2 / b.length * 800 if b.parent is not None and len(b.parent.children) > 1: stiffness *= 2 # torque /= 3 # else: # torque = Vector((0, 0, 0)) torque = i_base * wind_vector.cross(bone_vector) / ( b.bone.tail_radius) / 1000 f = sqrt(abs(damping**2 - 4 * inertia_moment * stiffness)) / ( 5 * b.bone.tail_radius) * speed x_amplitude = torque.x z_amplitude = torque.z m0 = f0.modifiers.new(type='FNGENERATOR') m1 = f1.modifiers.new(type='FNGENERATOR') m0.function_type = 'SIN' m1.function_type = 'SIN' m0.amplitude = x_amplitude m1.amplitude = z_amplitude m0.phase_multiplier = f m1.phase_multiplier = f m0.value_offset = x_amplitude * 3 m1.value_offset = z_amplitude * 3
def execute(self, context): if self.reset_values is True: self.reset_all_values() active_obj = context.scene.objects.active bm = bmesh.from_edit_mesh(active_obj.data) bm.verts.ensure_lookup_table() #verts = [v for v in bm.verts if v.select] # get loops loops = loop_t.get_connected_input(bm) loops = loop_t.check_loops(loops, bm) if not loops: self.report({'WARNING'}, "No Loops!") return {'CANCELLED'} first_indexes = [] if isinstance(bm.select_history[0], bmesh.types.BMVert): for element in bm.select_history: first_indexes.append(element.index) elif isinstance(bm.select_history[0], bmesh.types.BMEdge): for element in bm.select_history: el_verts = element.verts first_indexes.append(el_verts[0].index) first_indexes.append(el_verts[1].index) for loop in loops: if loop[1] is True: continue loop_verts = [] for ind in loop[0]: loop_verts.append(bm.verts[ind]) # for the case if we need to reverse it if loop[0][-1] in first_indexes: loop_verts = list(reversed(loop_verts)) # reverse again for the direction if self.reverse_direction is True: loop_verts = list(reversed(loop_verts)) # positions first_vert_pos = active_obj.matrix_world * loop_verts[0].co last_vert_pos = active_obj.matrix_world * loop_verts[-1].co loop_centr_orig = first_vert_pos.lerp(last_vert_pos, 0.5) relative_dist = (first_vert_pos - loop_centr_orig).length sidevec = (first_vert_pos - last_vert_pos).normalized() obj_matrix = active_obj.matrix_world obj_matrix_inv = obj_matrix.inverted() if self.direction_vector == 'Custom': rot_dir = Vector((self.rotate_axis[0], self.rotate_axis[1], self.rotate_axis[2])).normalized() elif self.direction_vector == 'MiddleCrossed': middle_nor = loop_verts[int(len(loop_verts) / 2)].normal.copy().normalized() middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix, obj_matrix_inv) rot_dir = middle_nor.cross(sidevec).normalized() # fix only for MiddleCrossed if not self.reverse_direction: rot_dir.negate() else: middle_nor = loop_verts[int(len(loop_verts) / 2)].normal.copy().normalized() middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix, obj_matrix_inv) middle_nor = middle_nor.cross(sidevec).normalized() rot_dir = middle_nor.cross(sidevec).normalized() rot_dir.negate() upvec = rot_dir.cross(sidevec).normalized() loop_centr = (self.upvec_offset * upvec * relative_dist) + loop_centr_orig loop_angle = (first_vert_pos - loop_centr).normalized().angle( (last_vert_pos - loop_centr).normalized()) if self.upvec_offset > 0: loop_angle = math.radians((360 - math.degrees(loop_angle))) # even spread line_data = None if self.spread_mode == 'Even': world_verts = [ active_obj.matrix_world * vert.co for vert in loop_verts ] line_data = [] line_length = 0.0 for i, vec in enumerate(world_verts): if i == 0: line_data.append(0) else: line_length += (vec - world_verts[i - 1]).length line_data.append(line_length) # make arc! for i, vert in enumerate(loop_verts): if i != 0 and i != len(loop_verts) - 1: if self.spread_mode == 'Normal': rot_angle = loop_angle * (i / (len(loop_verts) - 1)) else: rot_angle = loop_angle * ( line_data[i] / line_data[len(loop_verts) - 1]) rot_mat = Matrix.Rotation(rot_angle, 3, rot_dir) vert_pos = (rot_mat * (first_vert_pos - loop_centr)) + loop_centr if self.scale_arc != 0: vert_rel_dist = mathu.geometry.distance_point_to_plane( vert_pos, loop_centr_orig, upvec) vert_rel_dist_max = self.upvec_offset + relative_dist if vert_rel_dist != 0 and vert_rel_dist_max != 0: vert_pos_offset = vert_rel_dist / vert_rel_dist_max vert_pos += (self.scale_arc * upvec * vert_pos_offset * vert_rel_dist_max) # rotate arc if self.rotate_arc_axis != 0: rot_mat_2 = Matrix.Rotation( math.radians(self.rotate_arc_axis), 3, sidevec) vert_pos = ( rot_mat_2 * (vert_pos - loop_centr_orig)) + loop_centr_orig vert.co = active_obj.matrix_world.inverted() * vert_pos bm.normal_update() bmesh.update_edit_mesh(active_obj.data) return {'FINISHED'}
def draw_molecule(molecule, center=(0, 0, 0), show_bonds=True, join=True): """Draws a JSON-formatted molecule in Blender. This method uses a couple of tricks from [1] to improve rendering speed. In particular, it minimizes the amount of unique meshes and materials, and doesn't draw until all objects are initialized. [1] https://blenderartists.org/forum/showthread.php ?273149-Generating-a-large-number-of-mesh-primitives Args: molecule: The molecule to be drawn, as a python object following the JSON convention set in this project. center: (Optional, default (0, 0, 0)) Cartesian center of molecule. Use to draw multiple molecules in different locations. show_bonds: (Optional, default True) Draws a ball-and-stick model if True, and a space-filling model if False. join: (Optional, default True) Joins the molecule into a single object. Set to False if you want to individually manipulate atoms/bonds. Returns: If run in a blender context, will return a visual object of the molecule. """ shapes = [] # If using space-filling model, scale up atom size and remove bonds # Add atom primitive bpy.ops.object.select_all(action='DESELECT') bpy.ops.mesh.primitive_uv_sphere_add() sphere = bpy.context.object # Initialize bond material if it's going to be used. if show_bonds: bpy.data.materials.new(name='bond') # print(bpy.data.materials['bond']) bpy.data.materials[ 'bond'].diffuse_color = atom_data['bond']['color'] + [1.0] bpy.data.materials['bond'].specular_intensity = 0.2 bpy.ops.mesh.primitive_cylinder_add() cylinder = bpy.context.object cylinder.active_material = bpy.data.materials['bond'] # draw atoms for atom in molecule['atoms']: if atom['element'] not in atom_data: atom['element'] = 'undefined' if atom['element'] not in bpy.data.materials: key = atom['element'] bpy.data.materials.new(name=key) bpy.data.materials[key].diffuse_color = atom_data[key]['color'] + [ 1.0 ] bpy.data.materials[key].specular_intensity = 0.2 atom_sphere = sphere.copy() atom_sphere.data = sphere.data.copy() atom_sphere.location = [ l + c for l, c in zip(atom['location'], center) ] scale = 1 if show_bonds else 2.5 atom_sphere.dimensions = [ atom_data[atom['element']]['radius'] * scale * 2 ] * 3 atom_sphere.active_material = bpy.data.materials[atom['element']] bpy.context.collection.objects.link(atom_sphere) shapes.append(atom_sphere) # draw bonds for bond in (molecule['bonds'] if show_bonds else []): start = molecule['atoms'][bond['atoms'][0]]['location'] end = molecule['atoms'][bond['atoms'][1]]['location'] diff = [c2 - c1 for c2, c1 in zip(start, end)] cent = [(c2 + c1) / 2 for c2, c1 in zip(start, end)] mag = sum([(c2 - c1)**2 for c1, c2 in zip(start, end)])**0.5 v_axis = Vector(diff).normalized() v_obj = Vector((0, 0, 1)) v_rot = v_obj.cross(v_axis) # This check prevents gimbal lock (ie. weird behavior when v_axis is # close to (0, 0, 1)) if v_rot.length > 0.01: v_rot = v_rot.normalized() axis_angle = [acos(v_obj.dot(v_axis))] + list(v_rot) else: v_rot = Vector((1, 0, 0)) axis_angle = [0] * 4 if bond['order'] not in range(1, 4): sys.stderr.write("Improper number of bonds! Defaulting to 1.\n") bond['order'] = 1 if bond['order'] == 1: trans = [[0] * 3] elif bond['order'] == 2: trans = [[1.4 * atom_data['bond']['radius'] * x for x in v_rot], [-1.4 * atom_data['bond']['radius'] * x for x in v_rot]] elif bond['order'] == 3: trans = [[0] * 3, [2.2 * atom_data['bond']['radius'] * x for x in v_rot], [-2.2 * atom_data['bond']['radius'] * x for x in v_rot]] for i in range(bond['order']): bond_cylinder = cylinder.copy() bond_cylinder.data = cylinder.data.copy() bond_cylinder.dimensions = [ atom_data['bond']['radius'] * scale * 2 ] * 2 + [mag] bond_cylinder.location = [ c + scale * v for c, v in zip(cent, trans[i]) ] bond_cylinder.rotation_mode = 'AXIS_ANGLE' bond_cylinder.rotation_axis_angle = axis_angle bpy.context.collection.objects.link(bond_cylinder) shapes.append(bond_cylinder) # Remove primitive meshes bpy.ops.object.select_all(action='DESELECT') sphere.select_set(state=True) if show_bonds: cylinder.select_set(state=True) # If the starting cube is there, remove it if 'Cube' in bpy.data.objects.keys(): bpy.data.objects.get('Cube').select_set(state=True) bpy.ops.object.delete() for shape in shapes: shape.select_set(state=True) bpy.context.view_layer.objects.active = shapes[0] bpy.ops.object.shade_smooth() if join: bpy.ops.object.join() bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN') bpy.context.view_layer.update()
def lookFrom(p): y = Vector([0,0,1]) z = p x = y.cross(z).normalized() y = z.cross(x).normalized() return Matrix.Translation(p) @ Matrix([x, y, z]).transposed().to_4x4()
def execute(self, context): self.hcol = context.preferences.addons[ 'kekit'].preferences.modal_color_header self.tcol = context.preferences.addons[ 'kekit'].preferences.modal_color_text self.scol = context.preferences.addons[ 'kekit'].preferences.modal_color_subtext if self.ke_fitprim_option == "BOX": self.boxmode, self.world = True, False elif self.ke_fitprim_option == "CYL": self.boxmode, self.world = False, False elif self.ke_fitprim_option == "SPHERE": self.boxmode, self.world, self.sphere = False, False, True elif self.ke_fitprim_option == "QUADSPHERE": self.boxmode, self.world, self.sphere = False, False, True else: self.boxmode, self.world, self.sphere = False, False, False if bpy.context.scene.kekit.fitprim_item: self.itemize = True else: self.itemize = self.ke_fitprim_itemize self.modal = bpy.context.scene.kekit.fitprim_modal self.cyl_sides = bpy.context.scene.kekit.fitprim_sides self.select = bpy.context.scene.kekit.fitprim_select self.unit = round(bpy.context.scene.kekit.fitprim_unit, 4) self.sphere_ring = bpy.context.scene.kekit.fitprim_sphere_ring self.sphere_seg = bpy.context.scene.kekit.fitprim_sphere_seg self.quadsphere_seg = bpy.context.scene.kekit.fitprim_quadsphere_seg self.edit_mode = bpy.context.mode sel_verts = [] sel_verts2 = [] multi_object_mode = False cursor = context.scene.cursor self.og_cloc = cursor.location.copy() self.og_crot = cursor.rotation_euler.copy() og_orientation = str(context.scene.transform_orientation_slots[0].type) if self.itemize or self.edit_mode == "OBJECT" and context.object is not None: # make sure the new object is in the same layer as context object objc = context.object.users_collection[0] layer_collection = context.view_layer.layer_collection layer_coll = get_layer_collection(layer_collection, objc.name) alc = context.view_layer.active_layer_collection if objc.name != alc.name and alc.name != "Master Collection": context.view_layer.active_layer_collection = layer_coll elif objc.name != "Master Collection" and objc.name != alc.name: context.view_layer.active_layer_collection = layer_coll # ----------------------------------------------------------------------------------------- # MULTI OBJECT CHECK & SETUP # ----------------------------------------------------------------------------------------- if self.edit_mode == "EDIT_MESH": sel_mode = [b for b in bpy.context.tool_settings.mesh_select_mode] other_side = [] sel_obj = [ o for o in bpy.context.selected_objects if o.type == "MESH" ] if len(sel_obj) == 2: multi_object_mode = True obj = bpy.context.active_object obj_mtx = obj.matrix_world.copy() second_obj = [] bm = bmesh.from_edit_mesh(obj.data) bm.faces.ensure_lookup_table() bm.verts.ensure_lookup_table() sel_verts = [v for v in bm.verts if v.select] if sel_mode[2]: sel_poly = [p for p in bm.faces if p.select] active_face = bm.faces.active if active_face not in sel_poly: active_face = None if multi_object_mode: second_obj = [o for o in sel_obj if o != obj][0] bm2 = bmesh.from_edit_mesh(second_obj.data) obj_mtx2 = second_obj.matrix_world.copy() sel_poly2 = [p for p in bm2.faces if p.select] if sel_poly2: active_face2 = bm2.faces.active if active_face2 not in sel_poly2: active_face2 = None if active_face2: sel_verts2 = active_face2.verts else: sel_verts2 = sel_poly2[0].verts active_face2 = sel_poly2[0] # haxxfixxx else: sel_verts2 = [v for v in bm2.verts if v.select] if not sel_verts2: multi_object_mode = False elif sel_mode[0]: # Just for 2-vert mode in multiobj mode ole = sel_verts2[0].link_edges[:] other_side = get_shortest(obj_mtx2, ole) side = None distance = 0 vps = [] normal, setpos, setrot, center = None, None, None, None first_island = None island_mode = False # ----------------------------------------------------------------------------------------- # NO SELECTION MODE # ----------------------------------------------------------------------------------------- if not sel_verts or self.edit_mode == "OBJECT": # Check mouse over target if not self.edit_mode == "OBJECT": bpy.ops.object.mode_set(mode="OBJECT") hit_obj, hit_wloc, hit_normal, hit_face = mouse_raycast( context, self.mouse_pos, evaluated=True) if not self.edit_mode == "OBJECT": bpy.ops.object.mode_set(mode="EDIT") if self.edit_mode != "OBJECT" and hit_obj is not None: if len(hit_obj.modifiers) > 0: self.report({ "INFO" }, "FitPrim: Selected elements required in Edit Mode if Modifiers exist" ) return {"CANCELLED"} if hit_obj and hit_face is not None: # mouse over placement on face self.world = False mtx = hit_obj.matrix_world eks = hit_obj.data.polygons[hit_face].edge_keys vecs = [] for vp in eks: vc1 = mtx @ hit_obj.data.vertices[vp[0]].co vc2 = mtx @ hit_obj.data.vertices[vp[1]].co vecs.append(Vector(vc1 - vc2).normalized()) evps = [] for vp in eks: v1, v2 = hit_obj.data.vertices[ vp[0]], hit_obj.data.vertices[vp[1]] evps.append([v1, v2]) side, center, start_vec = get_sides(mtx, vecs, evps) if self.ke_fitprim_option == "PLANE" and self.edit_mode == "OBJECT": setpos = center # setpos = mtx @ hit_obj.data.polygons[hit_face].center side *= 2 else: offset = hit_normal * (side / 2) setpos = center + offset # setpos = mtx @ hit_obj.data.polygons[hit_face].center + offset setrot = rotation_from_vector(hit_normal, start_vec) else: # view placement compensation & Z0 drop view_vec = region_2d_to_vector_3d(context.region, context.space_data.region_3d, self.mouse_pos) view_pos = context.space_data.region_3d.view_matrix.inverted( ).translation if get_view_type() != "ORTHO": ground = ((0, 0, 0), (0, 1, 0), (1, 0, 0)) raypos = intersect_ray_tri(ground[0], ground[1], ground[2], view_vec, view_pos, False) snap_val = str(self.unit).split('.') if int(snap_val[0]) > 0: snap = 0 else: snap = len(snap_val[1]) setpos = Vector((round(raypos[0], snap), round(raypos[1], snap), self.unit / 2)) else: setpos = region_2d_to_location_3d( context.region, context.space_data.region_3d, self.mouse_pos, view_vec) self.world = True side = self.unit if self.ke_fitprim_option == "PLANE" and self.edit_mode == "OBJECT": side *= 2 distance = side sel_mode = (True, False, False) # ----------------------------------------------------------------------------------------- # VERT MODE(s) # ----------------------------------------------------------------------------------------- elif sel_mode[0]: if len(sel_verts) == 1 and not multi_object_mode: # 1-VERT MODE self.world = True side = get_shortest(obj_mtx, sel_verts[0].link_edges[:]) distance = side setpos = obj_mtx @ sel_verts[0].co elif len(sel_verts) == 2 and not multi_object_mode or \ multi_object_mode and len(sel_verts2) == 1 and len(sel_verts) == 1: # 2 Vert mode if multi_object_mode: p1 = obj_mtx @ sel_verts[0].co p2 = obj_mtx2 @ sel_verts2[0].co side = [other_side] con_edges = sel_verts[0].link_edges[:] side.append(get_shortest(obj_mtx, con_edges)) side = sorted(side)[0] else: p1 = obj_mtx @ sel_verts[0].co p2 = obj_mtx @ sel_verts[1].co con_edges = sel_verts[0].link_edges[:] + sel_verts[ 1].link_edges[:] side = get_shortest(obj_mtx, con_edges) v_1 = Vector(p1 - p2).normalized() v_2 = Vector((0, 0, 1)) if abs(v_1.dot(v_2)) == 1: v_2 = Vector((1, 0, 0)) n_v = v_1.cross(v_2).normalized() u_v = n_v.cross(v_1).normalized() t_v = u_v.cross(n_v).normalized() setrot = Matrix((u_v, n_v, t_v)).to_4x4().inverted() distance = get_distance(p1, p2) setpos = Vector(get_midpoint(p1, p2)) elif len(sel_verts) == 4 or multi_object_mode and len( sel_verts2) + len(sel_verts) <= 4: # 4-vert Rectangle mode # holy brute force batman if not second_obj: second_obj = obj tri = tri_order(((obj, sel_verts), (second_obj, sel_verts2))) q = tri[-1] # just placing a unit prim with min side in the center (for this one) so disregarding other vecs v1 = Vector(tri[0][0].matrix_world @ tri[0][1].co - tri[1][0].matrix_world @ tri[1][1].co) v2 = Vector(tri[0][0].matrix_world @ tri[0][1].co - tri[2][0].matrix_world @ tri[2][1].co) # v3 = Vector(q[0].matrix_world @ q[1].co - tri[1][0].matrix_world @ tri[1][1].co) # v4 = Vector(q[0].matrix_world @ q[1].co - tri[2][0].matrix_world @ tri[2][1].co) d1 = get_distance(tri[0][0].matrix_world @ tri[0][1].co, tri[1][0].matrix_world @ tri[1][1].co) # d2 = get_distance(tri[0][0].matrix_world @ tri[0][1].co, tri[2][0].matrix_world @ tri[2][1].co) # d3 = get_distance(q[0].matrix_world @ q[1].co, tri[1][0].matrix_world @ tri[1][1].co) d4 = get_distance(q[0].matrix_world @ q[1].co, tri[2][0].matrix_world @ tri[2][1].co) if d1 < d4: side = d1 else: side = d4 distance = side ap1 = average_vector([obj_mtx @ v.co for v in sel_verts]) if sel_verts2: ap2 = average_vector([obj_mtx2 @ v.co for v in sel_verts2]) setpos = average_vector((ap1, ap2)) else: setpos = ap1 n = v1.normalized().cross(v2.normalized()) u = n.cross(v1.normalized()) t = u.cross(n) setrot = Matrix((u, n, t)).to_4x4().inverted() else: self.report({ "INFO" }, "FitPrim: Invalid Vert Mode Selection: Select 1, 2 or 4 verts" ) return {"CANCELLED"} # ----------------------------------------------------------------------------------------- # EDGE MODE # ----------------------------------------------------------------------------------------- elif sel_mode[1]: one_line, loops, loops2 = False, [], [] sel_edges = [e for e in bm.edges if e.select] vps = [e.verts[:] for e in sel_edges] active_edge = bm.select_history.active if active_edge: active_edge_facenormals = [ correct_normal(obj_mtx, p.normal) for p in active_edge.link_faces ] active_edge_normal = Vector( average_vector(active_edge_facenormals)).normalized() # GET ISLANDS if multi_object_mode and active_edge: # print("multi obj mode") # todo: limited multiobj mode, rework one-for-all? sel_edges2 = [e for e in bm2.edges if e.select] vps2 = [e.verts for e in sel_edges2] p1 = get_loops(vps, legacy=True) p2 = get_loops(vps2, legacy=True) if p1 and p2: a1, a2 = obj_mtx @ p1[0][0].co, obj_mtx @ p1[0][-1].co b1, b2 = obj_mtx2 @ p2[0][0].co, obj_mtx2 @ p2[0][-1].co else: a1, a2 = obj_mtx @ sel_verts[0].co, obj_mtx @ sel_verts[ -1].co b1, b2 = obj_mtx2 @ sel_verts2[ 0].co, obj_mtx2 @ sel_verts2[-1].co b_avg = get_midpoint(b1, b2) spacing, mp = get_closest_midpoint(a1, a2, b_avg) u_v = Vector(a1 - b1).normalized() t_v = Vector(a1 - a2).normalized() n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=True) setpos = mp side = spacing distance = spacing elif active_edge: # same (active) obj island selections if len(sel_edges) > 1: if len(sel_edges) == 2 and not bool( set(sel_edges[0].verts).intersection( sel_edges[1].verts)): a1p, a2p = sel_edges[0].verts, sel_edges[1].verts a1, a2 = obj_mtx @ a1p[0].co, obj_mtx @ a1p[1].co b_avg = average_vector( [obj_mtx @ a2p[0].co, obj_mtx @ a2p[1].co]) lf1 = sel_edges[0].link_faces[:] lf = [ f for f in sel_edges[1].link_faces[:] if f in lf1 ] v1 = Vector((obj_mtx @ a1p[0].co - obj_mtx @ a1p[1].co)).normalized() v2 = Vector((obj_mtx @ a2p[0].co - obj_mtx @ a2p[1].co)).normalized() if not lf: u_v = Vector(a1 - (obj_mtx @ a2p[0].co)).normalized() t_v = Vector(a1 - a2).normalized() n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=True) else: n = correct_normal(obj_mtx, lf[0].normal) # t = average_vector((v1, v2)) # if sum(t) == 0: # print("AVG FAIL") t = v1 setrot = rotation_from_vector(n, t, rotate90=True) spacing, mp = get_closest_midpoint(a1, a2, b_avg) setpos = mp side = spacing distance = spacing else: loops = get_loops(vps, legacy=True) if len(loops) > 1: # print ("single obj - multi loop", len(loops)) # Check for closed loops a_ep1, a_ep2 = loops[0][0], loops[0][-1] b_ep1, b_ep2 = loops[1][0], loops[1][-1] if a_ep1 == a_ep2: a_ep2 = loops[0][-2] if b_ep1 == b_ep2: b_ep2 = loops[1][-2] # get coords & set vals a1, a2 = obj_mtx @ a_ep1.co, obj_mtx @ a_ep2.co b1, b2 = obj_mtx @ b_ep1.co, obj_mtx @ b_ep2.co b_avg = get_midpoint(b1, b2) spacing, mp = get_closest_midpoint(a1, a2, b_avg) u_v = Vector((0, 0, 1)) t_v = Vector(a1 - a2).normalized() if abs(u_v.dot(t_v)) == 1: u_v = Vector((1, 0, 0)) n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=False) setpos = mp side = spacing distance = spacing elif len(loops) == 1 or len(sel_edges) == 1: # print ("single obj - single loop") single_line = False if len(sel_edges) != 1: if loops[0][0] != loops[0][-1]: single_line = True if len(sel_edges) != 1 and not single_line: vecs = [ Vector((obj_mtx @ vp[0].co) - (obj_mtx @ vp[1].co)).normalized() for vp in vps ] normal = average_vector([ correct_normal(obj_mtx, v.normal) for v in flatten(vps) ]) side, center, start_vec = get_sides(obj_mtx, vecs, vps) distance = side setpos = center setrot = rotation_from_vector(normal, start_vec) elif len(sel_edges) == 1 or single_line: # print("1 edge --> one line", one_line) p1, p2 = obj_mtx @ sel_verts[ 0].co, obj_mtx @ sel_verts[1].co t_v = Vector(p1 - p2).normalized() n_v = active_edge_normal setrot = rotation_from_vector(n_v, t_v) distance = get_distance(p1, p2) setpos = get_midpoint(p1, p2) side = distance else: print("Unexpected: Aborting operation.") return {'CANCELLED'} # ----------------------------------------------------------------------------------------- # FACE MODE # ----------------------------------------------------------------------------------------- elif sel_mode[2] and active_face and sel_poly: fail_island = False # GET ISLANDS if multi_object_mode and active_face: first_island = sel_verts point = obj_mtx @ active_face.calc_center_median() firstcos = [obj_mtx @ v.co for v in active_face.verts] secondcos = [obj_mtx2 @ v.co for v in active_face2.verts] if firstcos and secondcos: island_mode = True distance = point_to_plane(point, firstcos, secondcos) if distance: distance = abs(distance) else: # print("Multi obj Point to plane failed for some reason - using single island mode.") # island_mode = False fail_island = True else: # same (active) obj island selections first_island, second_island = get_selection_islands( sel_poly, active_face) # Ofc, needs correct order for point to plane, and I'll just rely on face.verts for that: calc_island_1 = active_face.verts[:] calc_island_2 = [] for p in sel_poly: verts = p.verts[:] for v in verts: if v in second_island: calc_island_2 = verts break if len(first_island) != 0 and len(second_island) != 0: firstcos = [obj_mtx @ v.co for v in calc_island_1] secondcos = [obj_mtx @ v.co for v in calc_island_2] distance = point_to_plane( obj_mtx @ active_face.calc_center_median(), firstcos, secondcos) if distance: distance = abs(distance) island_mode = True else: # print("Point to plane failed for some reason - using single island mode.") fail_island = True # print(distance) else: # Ngon mode first_island = sel_verts # GETVALUES if island_mode or fail_island: bpy.ops.mesh.select_all(action='DESELECT') for v in first_island: bm.verts[v.index].select = True bmesh.update_edit_mesh(obj.data) bpy.ops.mesh.select_mode(type='VERT') bpy.ops.mesh.select_mode(type='FACE') bm.faces.ensure_lookup_table() faces = [f for f in bm.faces if f.select] normal = average_vector( [correct_normal(obj_mtx, f.normal) for f in faces]) bpy.ops.mesh.region_to_loop() sel_edges = [e for e in bm.edges if e.select] vps = [e.verts[:] for e in sel_edges] vecs = [ Vector((obj_mtx @ vp[0].co) - (obj_mtx @ vp[1].co)).normalized() for vp in vps ] side, center, start_vec = get_sides(obj_mtx, vecs, vps) setrot = rotation_from_vector(normal, start_vec) # ----------------------------------------------------------------------------------------- # PROCESS # ----------------------------------------------------------------------------------------- if side: if self.ke_fitprim_option == "PLANE" and self.edit_mode != "OBJECT": if sel_mode[2]: setpos = center elif len(sel_verts) == 0 or sel_mode[0] or sel_mode[1]: side *= .5 distance = distance / 2 if self.sphere and sel_mode[0]: if not len(sel_verts) == 0: side = distance elif sel_mode[2]: if island_mode: side *= .5 offset = normal * distance * .5 distance *= .5 if self.sphere: side = distance else: side *= .5 distance = side offset = normal * side setpos = center + offset if not island_mode and self.sphere: setpos = setpos - offset # SET FINAL ROTATION if self.world: setrot = (0, 0, 0) if self.ke_fitprim_option == "PLANE" and not sel_verts: setpos[2] = 0 else: setrot = setrot.to_euler() # RUN OP if not self.edit_mode == "OBJECT": bpy.ops.mesh.select_mode(type='FACE') if self.itemize: if self.edit_mode != "OBJECT": bpy.ops.object.mode_set(mode="OBJECT") bpy.ops.transform.select_orientation(orientation='GLOBAL') cursor.location = setpos cursor.rotation_euler = setrot # ----------------------------------------------------------------------------------------- # CUBE # ----------------------------------------------------------------------------------------- if self.boxmode: bpy.ops.mesh.primitive_box_add(width=side, depth=side, height=distance, align='WORLD', location=setpos, rotation=setrot) if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True # ----------------------------------------------------------------------------------------- # PLANE # ----------------------------------------------------------------------------------------- elif self.ke_fitprim_option == "PLANE": # side *= 2 enter = True if self.edit_mode == 'OBJECT' or self.itemize: enter = False bpy.ops.mesh.primitive_plane_add(enter_editmode=enter, align='WORLD', location=setpos, rotation=setrot, size=side, scale=(1, 1, 1)) if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True # ----------------------------------------------------------------------------------------- # SPHERE # ----------------------------------------------------------------------------------------- elif self.sphere and not self.ke_fitprim_option == "QUADSPHERE": bpy.ops.mesh.primitive_uv_sphere_add( segments=self.sphere_seg, ring_count=self.sphere_ring, radius=side, align='WORLD', location=setpos, rotation=setrot) if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True # ----------------------------------------------------------------------------------------- # QUADSPHERE # ----------------------------------------------------------------------------------------- elif self.ke_fitprim_option == "QUADSPHERE": cutnr = self.quadsphere_seg # calc compensated subd radius from cube v1_pos = setpos[0] + side, setpos[1] + side, setpos[2] + side rad = sqrt(sum([(a - b)**2 for a, b in zip(setpos, v1_pos)])) diff = side / rad side = (side * diff) distance = side bpy.ops.mesh.primitive_box_add(width=side, depth=side, height=distance, align='WORLD', location=setpos, rotation=setrot, name="QuadSphere") if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.mode_set(mode="EDIT") bpy.ops.mesh.subdivide(number_cuts=cutnr, smoothness=1) bpy.ops.mesh.faces_shade_smooth() bpy.ops.object.mode_set(mode="OBJECT") else: bpy.ops.mesh.subdivide(number_cuts=cutnr, smoothness=1) bpy.ops.mesh.faces_shade_smooth() bpy.ops.ed.undo_push() # bpy.context.object.data.use_auto_smooth = as_check if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True # ----------------------------------------------------------------------------------------- # CYLINDER # ----------------------------------------------------------------------------------------- else: bpy.ops.mesh.primitive_cylinder_add(vertices=self.cyl_sides, radius=side, depth=distance * 2, enter_editmode=False, align='WORLD', location=setpos, rotation=setrot) if self.modal: if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.mode_set(mode="EDIT") self.settings = (side, distance, setpos, setrot) context.window_manager.modal_handler_add(self) self._timer = context.window_manager.event_timer_add( 0.5, window=context.window) args = (self, context, self.screen_x) self._handle = bpy.types.SpaceView3D.draw_handler_add( draw_callback_px, args, 'WINDOW', 'POST_PIXEL') bpy.context.space_data.overlay.show_cursor = False return {'RUNNING_MODAL'} if multi_object_mode and not self.itemize: second_obj.select_set(state=True) obj.select_set(state=True) bpy.ops.object.editmode_toggle() bpy.ops.object.editmode_toggle() else: self.report({"INFO"}, "FitPrim: Invalid Selection / No Active Element?") if not self.select and not self.itemize and not self.edit_mode == "OBJECT": bpy.ops.mesh.select_all(action='DESELECT') if self.itemize: bpy.ops.transform.select_orientation(orientation=og_orientation) cursor.location = self.og_cloc cursor.rotation_euler = self.og_crot bpy.context.active_object.select_set(state=True) self.ke_fitprim_itemize = False return {"FINISHED"}
def import_arc(rcurve, bcurve, scale): ''' Parse arc data from Speckle object dictionary ''' #plane = find_key_case_insensitive(rcurve, "plane") plane = rcurve.plane if not plane: return origin = plane.origin normal = Vector(plane.normal.value) #origin = find_key_case_insensitive(plane, "origin") #normal = find_key_case_insensitive(plane, "normal") #normal = Vector(find_key_case_insensitive(normal, "value")) xaxis = plane.xdir yaxis = plane.ydir radius = rcurve.radius * scale startAngle = rcurve.startAngle endAngle = rcurve.endAngle #xaxis = find_key_case_insensitive(plane, "xdir") #yaxis = find_key_case_insensitive(plane, "ydir") #radius = find_key_case_insensitive(rcurve, "radius", 0) * scale #startAngle = find_key_case_insensitive(rcurve, "startAngle", 0) #endAngle = find_key_case_insensitive(rcurve, "endAngle", 0) startQuat = Quaternion(normal, startAngle) endQuat = Quaternion(normal, endAngle) ''' Get start and end vectors, centre point, angles, etc. ''' r1 = Vector(plane.xdir.value) r1.rotate(startQuat) r2 = Vector(plane.xdir.value) r2.rotate(endQuat) c = Vector(plane.origin.value) * scale spt = c + r1 * radius ept = c + r2 * radius #d = radius * (endAngle - startAngle) angle = endAngle - startAngle t1 = normal.cross(r1) #t2 = normal.cross(r2) ''' Initialize arc data and calculate subdivisions ''' arc = bcurve.splines.new('NURBS') arc.use_cyclic_u = False Ndiv = max(int(math.floor(angle / 0.3)), 2) step = angle / float(Ndiv) stepQuat = Quaternion(normal, step) tan = math.tan(step / 2) * radius arc.points.add(Ndiv + 1) ''' Set start and end points ''' arc.points[0].co = (spt.x, spt.y, spt.z, 1) arc.points[Ndiv + 1].co = (ept.x, ept.y, ept.z, 1) ''' Set intermediate points ''' for i in range(Ndiv): t1 = normal.cross(r1) pt = c + r1 * radius + t1 * tan arc.points[i + 1].co = (pt.x, pt.y, pt.z, 1) r1.rotate(stepQuat) ''' Set curve settings ''' arc.use_endpoint_u = True arc.order_u = 3 return arc
def lp_left(bm, lp, wid, vm, wm): # pass size = len(lp) up = wm.inverted() * vm.inverted() * Vector((0, 0, 1)) lp_off = [] faces = [] for i in range(size - 1): if i == 0: pt = bm.verts[lp[i]].co pre = bm.verts[lp[-2]].co nxt = bm.verts[lp[1]].co pre_ind = lp[size - 2] nxt_ind = lp[1] else: bm.verts.ensure_lookup_table() pt = bm.verts[lp[i]].co pre = bm.verts[lp[i - 1]].co nxt = bm.verts[lp[i + 1]].co pre_ind = lp[i - 1] nxt_ind = lp[i + 1] vec1 = pt - pre vec2 = pt - nxt mid = vec1.normalized() + vec2.normalized() if mid.length < 10e-4: up2 = Vector((0, 0, 1)) mid = up2.cross(vec1) else: xx = mid.cross(vec1).dot(up) if xx > 0: mid.negate() mid.normalize() if pre_ind == nxt_ind: mid = (pt - pre).normalized() q_a = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(90.0)) q_b = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(-180.0)) mid.rotate(q_a) pt1 = pt + mid * wid mid.rotate(q_b) pt2 = pt + mid * wid new_vert_1 = bm.verts.new(pt1) new_vert_2 = bm.verts.new(pt2) lp_off.append([new_vert_1, new_vert_2]) else: ang = mid.angle(pre - pt) vec_len = wid / (math.sin(ang)) # print(wid) pt = pt + mid * vec_len new_vert = bm.verts.new(pt) lp_off.append(new_vert) lp_off.append(lp_off[0]) bm.verts.index_update() for i in range(len(lp_off) - 1): bm.verts.ensure_lookup_table() p1 = bm.verts[lp[i]] p2 = bm.verts[lp[i + 1]] p3 = lp_off[i + 1] p4 = lp_off[i] if isinstance(p3, list): faces.append((p1, p2, p3[0], p4)) #faces.append((p3[0],p2,p3[1])) elif isinstance(p4, list): faces.append((p1, p2, p3, p4[1])) else: faces.append((p1, p2, p3, p4)) return faces
def execute(self, context): automerge = False self.used_modal = False sel_check, place, noloc, place, dupe = False, False, False, False, False normal, tangent, place_quat = None, None, None mode = bpy.context.mode[:] sel_mode = bpy.context.tool_settings.mesh_select_mode[:] obj = bpy.context.active_object if not obj: self.report({"INFO"}, "Unrotator: No Active Object?") return {"CANCELLED"} if self.ke_unrotator_option == "DUPE": dupe, noloc = True, False elif self.ke_unrotator_option == "NO_LOC": noloc, dupe = True, False connect = bpy.context.scene.kekit.unrotator_connect nolink = bpy.context.scene.kekit.unrotator_nolink nosnap = bpy.context.scene.kekit.unrotator_nosnap actual_invert = bpy.context.scene.kekit.unrotator_invert self.center = bpy.context.scene.kekit.unrotator_center if self.center_override: self.center = True # Check mouse over target ----------------------------------------------------------------------- bpy.ops.object.mode_set(mode="OBJECT") hit_obj, hit_wloc, hit_normal, hit_face = mouse_raycast(context, self.mouse_pos, evaluated=True) # print("HIT:", hit_obj, hit_face, hit_normal) if mode == "OBJECT" and not nosnap: self.rot_offset = 0 self.center = False if mode == "EDIT_MESH": bpy.ops.object.mode_set(mode="EDIT") automerge = bool(context.scene.tool_settings.use_mesh_automerge) if automerge: context.scene.tool_settings.use_mesh_automerge = False # Get selected obj bm obj_mtx = obj.matrix_world.copy() od = obj.data bm = bmesh.from_edit_mesh(od) # Check selections sel_poly = [p for p in bm.faces if p.select] sel_edges = [e for e in bm.edges if e.select] sel_verts = [v for v in bm.verts if v.select] active_h = bm.select_history.active active_point = None # Making sure an active element is in the selection if sel_mode[1] and sel_edges: if active_h not in sel_edges: bm.select_history.add(sel_edges[-1]) active_h = bm.select_history.active active_point = active_h.verts[0] elif sel_mode[0] and sel_verts: if active_h not in sel_verts: bm.select_history.add(sel_verts[-1]) active_h = bm.select_history.active active_point = active_h elif sel_mode[2] and sel_poly: if active_h not in sel_poly: bm.select_history.add(sel_poly[-1]) active_h = bm.select_history.active active_point = active_h.verts[0] # Verify valid selections if len(sel_verts) >= 3 and active_h: if sel_mode[0]: sel_check = True elif sel_mode[1] and len(sel_edges) >= 2: sel_check = True elif sel_mode[2] and len(sel_poly): sel_check = True if not sel_check: self.report({ "INFO" }, "Unrotator: Selection Error - Incorrect/No geo Selected? Active Element?" ) return {"CANCELLED"} # Expand Linked, or not ------------------------------------------------------------------ if connect: bpy.ops.mesh.select_linked() linked_verts = [v for v in bm.verts if v.select] elif not connect: linked_verts = sel_verts # fall-back # still calc the linked avg pos islands = get_islands(bm, sel_verts) for island in islands: if any(v in sel_verts for v in island): linked_verts = island break # Linked Selection center pos bm.verts.ensure_lookup_table() linked_verts_co = [v.co for v in linked_verts] avg_pos = obj_mtx @ Vector(average_vector(linked_verts_co)) sel_cos = [v.co for v in sel_verts] avg_sel_pos = obj_mtx @ Vector(average_vector(sel_cos)) center_d = Vector(avg_sel_pos - avg_pos).normalized() # bmdupe if dupe: bmesh.ops.duplicate(bm, geom=([v for v in bm.verts if v.select] + [e for e in bm.edges if e.select] + [p for p in bm.faces if p.select])) # ----------------------------------------------------------------------------------------- # Check mouse over target - place check # ----------------------------------------------------------------------------------------- if type(hit_face) == int: if hit_obj.name == obj.name: bm.faces.ensure_lookup_table() hit_face_verts = bm.faces[hit_face].verts[:] if any(i in hit_face_verts for i in linked_verts): # print("Mouse over same Obj and selected geo - Unrotate only mode") place = False else: # print("Mouse over same Obj but unselected geo - Unrotate & Place in same mesh mode") place = True n, t = self.calc_face_vectors(bm.faces[hit_face], obj_mtx, obj, len(hit_face_verts)) n.negate() place_quat = rotation_from_vector(n, t).to_quaternion() if self.center: bm.faces.ensure_lookup_table() hit_wloc = average_vector( [obj_mtx @ v.co for v in hit_face_verts]) elif hit_obj.name != obj.name: # print("Mouse over different Object - Unrotate & Place mode") place = True hit_face_verts = hit_obj.data.polygons[ hit_face].vertices[:] n, t = self.calc_face_vectors( hit_obj.data.polygons[hit_face], hit_obj.matrix_world, hit_obj, len(hit_face_verts)) n.negate() place_quat = rotation_from_vector(n, t).to_quaternion() if self.center: hit_vcos = [ hit_obj.matrix_world @ hit_obj.data.vertices[i].co for i in hit_face_verts ] hit_wloc = average_vector(hit_vcos) # ----------------------------------------------------------------------------------------- # Selection processing for normal and tangent vecs # ----------------------------------------------------------------------------------------- if sel_mode[0]: # VERT MODE --------------------------------------------------------------------------- h = tri_points_order( [sel_verts[0].co, sel_verts[1].co, sel_verts[2].co]) # Vectors from 3 points, hypoT ignored p1, p2, p3 = obj_mtx @ sel_verts[h[0]].co, obj_mtx @ sel_verts[ h[1]].co, obj_mtx @ sel_verts[h[2]].co v2 = p3 - p1 v1 = p2 - p1 normal = v1.cross(v2).normalized() tangent = Vector(v1).normalized() # Direction flip check against center mass d = normal.dot(center_d) if d <= 0: normal.negate() elif sel_mode[1]: # EDGE MODE --------------------------------------------------------------------------- bm.edges.ensure_lookup_table() # Active edge is tangent ev = active_h.verts[:] ev1, ev2 = ev[0].co, ev[1].co tangent = correct_normal(obj_mtx, Vector((ev1 - ev2)).normalized()) # B-tangent is shortest vec to v1 c = [v for v in sel_verts if v not in ev] vecs = [Vector((ev1 - v.co).normalized()) for v in c] v2 = correct_normal(obj_mtx, sorted(vecs)[0]) # Normal is crossp normal = tangent.cross(v2).normalized() # Direction flip check against center mass d = normal.dot(center_d) if d <= 0: normal.negate() elif sel_mode[2]: # POLY MODE --------------------------------------------------------------------------- normal, tangent = self.calc_face_vectors( active_h, obj_mtx, obj, len(sel_verts)) # ----------------------------------------------------------------------------------------- # Process & Execute transforms! # ----------------------------------------------------------------------------------------- if actual_invert: normal.negate() if self.inv: normal.negate() rotm = rotation_from_vector(normal, tangent) rq = rotm.to_quaternion() if self.rot_offset != 0: rq @= Quaternion((0, 0, 1), self.rot_offset) if place_quat is not None: qd = place_quat @ rq.inverted() else: # rot to place at bottom rq @= Quaternion((1, 0, 0), radians(-180.0)) qd = rq.rotation_difference(Quaternion()) rot = qd.to_euler("XYZ") rx, ry, rz = rot.x * -1, rot.y * -1, rot.z * -1 # Old macro method to avoid non-uniform scale issues...slow, but works. bpy.ops.transform.rotate(value=rx, orient_axis='X', orient_type='GLOBAL', use_proportional_edit=False, snap=False, orient_matrix_type='GLOBAL') bpy.ops.transform.rotate(value=ry, orient_axis='Y', orient_type='GLOBAL', use_proportional_edit=False, snap=False, orient_matrix_type='GLOBAL') bpy.ops.transform.rotate(value=rz, orient_axis='Z', orient_type='GLOBAL', use_proportional_edit=False, snap=False, orient_matrix_type='GLOBAL') # Calc offset nv = [v.co for v in linked_verts] npos = obj_mtx @ Vector(average_vector(nv)) if place and not noloc: # Move & offset placement d = distance_point_to_plane(obj_mtx @ active_point.co, npos, hit_normal) offset = hit_normal * d hit_vec = hit_wloc - (npos + offset) bmesh.ops.translate(bm, vec=hit_vec, space=obj_mtx, verts=linked_verts) bmesh.update_edit_mesh(obj.data) else: # Compensate z pos to rot-in-place-ish comp = (npos - avg_pos) * -1 bpy.ops.transform.translate(value=comp) bmesh.update_edit_mesh(obj.data) elif mode == 'OBJECT': # Unrotate only if not hit_obj: obj.select_set(True) obj.rotation_euler = (0, 0, self.rot_offset) elif hit_obj.name == obj.name: obj.select_set(True) obj.rotation_euler = (0, 0, self.rot_offset) # Place! elif hit_obj.name != obj.name: obj.select_set(True) self.og_pos = Vector(obj.location) if dupe: if nolink: bpy.ops.object.duplicate(linked=False) else: bpy.ops.object.duplicate(linked=True) obj = bpy.context.active_object obj.select_set(True) mtx = hit_obj.matrix_world eks = hit_obj.data.polygons[hit_face].edge_keys vecs = [] for vp in eks: vc1 = mtx @ hit_obj.data.vertices[vp[0]].co vc2 = mtx @ hit_obj.data.vertices[vp[1]].co vecs.append(vc1 - vc2) short_sort = sorted(vecs) start_vec = short_sort[0] self.setrot = rotation_from_vector(hit_normal, start_vec, rotate90=True, rw=True).to_quaternion() self.setrot @= Quaternion((0, 0, 1), self.rot_offset) if noloc or nosnap: obj.rotation_euler = self.setrot.to_euler() if not noloc: if self.center and nosnap: hit_wloc = hit_obj.matrix_world @ Vector( hit_obj.data.polygons[hit_face].center) obj.location = hit_wloc # no snapping place only obj.location = hit_wloc if not nosnap: obj.rotation_euler = self.setrot.to_euler() self.f_center = hit_obj.matrix_world @ Vector( hit_obj.data.polygons[hit_face].center) self.used_modal = True self.og_snaps = self.get_snap_settings(context) self.set_snap_settings(context, self.modal_snap) context.window_manager.modal_handler_add(self) bpy.ops.transform.translate('INVOKE_DEFAULT') return {'RUNNING_MODAL'} # Selection revert for non-face selections if not sel_mode[2] and mode != "OBJECT": bpy.ops.mesh.select_all(action='DESELECT') if sel_mode[1]: bm.edges.ensure_lookup_table() for v in sel_edges: bm.edges[v.index].select = True elif sel_mode[0]: bm.verts.ensure_lookup_table() for v in sel_verts: bm.verts[v.index].select = True bm.select_history.clear() bm.select_history.add(active_h) bmesh.update_edit_mesh(obj.data) if automerge: context.scene.tool_settings.use_mesh_automerge = True return {"FINISHED"}
class Turtle: def __init__( self, tropism=(0,0,0), tropismsize=0, angle=radians(30), iseed=42 ): self.tropism = Vector(tropism).normalized() self.magnitude = tropismsize self.forward = Vector((1,0,0)) self.up = Vector((0,0,1)) self.right = self.forward.cross(self.up) self.stack = [] self.position = Vector((0,0,0)) self.angle = angle self.radius = 0.1 self.__init_terminals() seed(iseed) def __init_terminals(self): """ Initialize a map of predefined terminals. """ self.terminals = { '+': self.term_plus, '-': self.term_minus, '[': self.term_push, ']': self.term_pop, '/': self.term_slash, '\\': self.term_backslash, '<': self.term_less, '>': self.term_greater, '&': self.term_amp, '!': self.term_expand, '@': self.term_shrink, '#': self.term_fatten, '%': self.term_slink, 'F': self.term_edge, 'Q': self.term_quad, # '{': self.term_object } def apply_tropism(self): # tropism is a normalized vector t = self.tropism * self.magnitude tf=self.forward + t tf.normalize() q = tf.rotation_difference(self.forward) self.forward.rotate(q) self.up.rotate(q) self.right.rotate(q) def term_plus(self, value=None): val = radians(value) if not value is None else self.angle r = Matrix.Rotation(val, 4, self.right) self.forward.rotate(r) self.up.rotate(r) def term_minus(self, value=None): val = radians(value) if not value is None else self.angle r = Matrix.Rotation(-val, 4, self.right) self.forward.rotate(r) self.up.rotate(r) def term_amp(self, value=30): k = (random()-0.5) * value self.term_plus(value=k) k = (random()-0.5) * value self.term_slash(value=k) def term_slash(self, value=None): r = Matrix.Rotation(radians(value) if not value is None else self.angle, 4, self.up) self.forward.rotate(r) self.right.rotate(r) def term_backslash(self, value=None): r = Matrix.Rotation(-radians(value) if not value is None else -self.angle, 4, self.up) self.forward.rotate(r) self.right.rotate(r) def term_less(self, value=None): r = Matrix.Rotation(radians(value) if not value is None else self.angle, 4, self.forward) self.up.rotate(r) self.right.rotate(r) def term_greater(self, value=None): r = Matrix.Rotation(-radians(value) if not value is None else -self.angle, 4, self.forward) self.up.rotate(r) self.right.rotate(r) def term_pop(self, value=None): t = self.stack.pop() ( self.forward, self.up, self.right, self.position, self.radius ) = t def term_push(self, value=None): t = ( self.forward.copy(), self.up.copy(), self.right.copy(), self.position.copy(), self.radius ) self.stack.append(t) def term_expand(self, value=1.1): self.forward *= value self.up *= value self.right *= value def term_shrink(self, value=0.9): self.forward *= value self.up *= value self.right *= value def term_fatten(self, value=1.1): self.radius *= value def term_slink(self, value=0.9): self.radius *= value def term_edge(self, value=None): s = self.position.copy() self.apply_tropism() self.position += self.forward e = self.position.copy() return Edge(start=s, end=e, radius=self.radius) def term_quad(self, value=0.5): return Quad(pos=self.position, right=self.right, up=self.up, forward=self.forward ) def term_object(self, value=None, name=None): s = self.position.copy() self.apply_tropism() self.position += self.forward return BObject(name=name, pos=s, right=self.right, up=self.up, forward=self.forward ) def interpret(self, s): """ interpret the iterable s, yield Quad, Edge or Object named tuples. """ print('interpret:',s) name='' for c in s: t = None print(c,name) if c == '}': t = self.term_object(name=name[1:]) name='' elif c == '{' or name != '': name += c continue elif name != '': continue elif c in self.terminals: t = self.terminals[c]() print('yield',t) if not t is None: yield t
def execute(self, context): if self.ke_fitprim_option == "BOX": self.boxmode, self.world = True, False elif self.ke_fitprim_option == "CYL": self.boxmode, self.world = False, False elif self.ke_fitprim_option == "SPHERE": self.boxmode, self.world, self.sphere = False, False, True elif self.ke_fitprim_option == "QUADSPHERE": self.boxmode, self.world, self.sphere = False, False, True if bpy.context.scene.kekit.fitprim_item: self.itemize = True else: self.itemize = self.ke_fitprim_itemize self.modal = bpy.context.scene.kekit.fitprim_modal self.cyl_sides = bpy.context.scene.kekit.fitprim_sides self.select = bpy.context.scene.kekit.fitprim_select self.unit = bpy.context.scene.kekit.fitprim_unit self.sphere_ring = bpy.context.scene.kekit.fitprim_sphere_ring self.sphere_seg = bpy.context.scene.kekit.fitprim_sphere_seg self.quadsphere_seg = bpy.context.scene.kekit.fitprim_quadsphere_seg self.edit_mode = bpy.context.mode sel_verts = [] multi_object_mode = False # ----------------------------------------------------------------------------------------- # MULTI OBJECT CHECK & SETUP # ----------------------------------------------------------------------------------------- if self.edit_mode == "EDIT_MESH": sel_mode = [b for b in bpy.context.tool_settings.mesh_select_mode] multi_object_mode = False other_side = [] sel_obj = [ o for o in bpy.context.objects_in_mode if o.type == "MESH" ] if len(sel_obj) == 2: multi_object_mode = True obj = bpy.context.active_object if multi_object_mode: second_obj = [o for o in sel_obj if o != obj][0] bm2 = bmesh.from_edit_mesh(second_obj.data) obj_mtx2 = second_obj.matrix_world.copy() # selection double check - just to get some manual selection control w. 2nd active face sel_poly2 = [p for p in bm2.faces if p.select] if sel_poly2: active_face2 = bm2.faces.active if active_face2 not in sel_poly2: active_face2 = None if active_face2: sel_verts2 = active_face2.verts else: sel_verts2 = sel_poly2[0].verts else: sel_verts2 = [v for v in bm2.verts if v.select] if not sel_verts2: multi_object_mode = False elif sel_mode[0]: # Just for 2-vert mode in multiobj mode ole = sel_verts2[0].link_edges[:] other_side = get_shortest(obj_mtx2, ole) obj_mtx = obj.matrix_world.copy() bm = bmesh.from_edit_mesh(obj.data) bm.faces.ensure_lookup_table() bm.verts.ensure_lookup_table() sel_verts = [v for v in bm.verts if v.select] if sel_mode[2]: sel_poly = [p for p in bm.faces if p.select] active_face = bm.faces.active if active_face not in sel_poly: active_face = None side = None distance = 0 vps = [] normal, setpos, setrot, center = None, None, None, None first_island = None island_mode = False # ----------------------------------------------------------------------------------------- # NO SELECTION MODE # ----------------------------------------------------------------------------------------- if not sel_verts or self.edit_mode == "OBJECT": self.world = True screenpos = region_2d_to_location_3d(context.region, context.space_data.region_3d, self.mouse_pos, (0, 0, 0)) setpos = screenpos side = self.unit distance = side sel_mode = (True, False, False) # ----------------------------------------------------------------------------------------- # VERT MODE(s) # ----------------------------------------------------------------------------------------- elif sel_mode[0] and len(sel_verts) == 1 and not multi_object_mode: # 1-VERT MODE self.world = True side = get_shortest(obj_mtx, sel_verts[0].link_edges[:]) distance = side setpos = obj_mtx @ sel_verts[0].co elif sel_mode[0]: # 2+ Vert mode if multi_object_mode: p1 = obj_mtx @ sel_verts[0].co p2 = obj_mtx2 @ sel_verts2[0].co side = [other_side] con_edges = sel_verts[0].link_edges[:] side.append(get_shortest(obj_mtx, con_edges)) side = sorted(side)[0] else: p1 = obj_mtx @ sel_verts[0].co p2 = obj_mtx @ sel_verts[1].co con_edges = sel_verts[0].link_edges[:] + sel_verts[ 1].link_edges[:] side = get_shortest(obj_mtx, con_edges) v_1 = p1 - p2 v_2 = Vector((0, 0, 1)) if v_1.dot(v_2) < 0: v_2 = Vector((1, 0, 0)) n_v = v_1.cross(v_2).normalized() u_v = n_v.cross(v_1).normalized() t_v = u_v.cross(n_v).normalized() setrot = Matrix((u_v, n_v, t_v)).to_4x4().inverted() distance = get_distance(p1, p2) setpos = Vector(get_midpoint(p1, p2)) # ----------------------------------------------------------------------------------------- # EDGE MODE # ----------------------------------------------------------------------------------------- elif sel_mode[1]: one_line, loops, loops2 = False, [], [] sel_edges = [e for e in bm.edges if e.select] vps = [e.verts for e in sel_edges] active_edge = bm.select_history.active if active_edge: active_edge_facenormals = [ correct_normal(obj_mtx, p.normal) for p in active_edge.link_faces ] active_edge_normal = Vector( average_vector(active_edge_facenormals)).normalized() # GET ISLANDS if multi_object_mode and active_edge: # print("multi obj mode") # todo: limited multiobj mode, rework one-for-all? sel_edges2 = [e for e in bm2.edges if e.select] vps2 = [e.verts for e in sel_edges2] p1 = get_loops(vps, legacy=True) p2 = get_loops(vps2, legacy=True) if p1 and p2: a1, a2 = obj_mtx @ p1[0][0].co, obj_mtx @ p1[0][-1].co b1, b2 = obj_mtx2 @ p2[0][0].co, obj_mtx2 @ p2[0][-1].co else: a1, a2 = obj_mtx @ sel_verts[0].co, obj_mtx @ sel_verts[ -1].co b1, b2 = obj_mtx2 @ sel_verts2[ 0].co, obj_mtx2 @ sel_verts2[-1].co b_avg = get_midpoint(b1, b2) spacing, mp = get_closest_midpoint(a1, a2, b_avg) u_v = Vector(a1 - b1).normalized() t_v = Vector(a1 - a2).normalized() n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=True) setpos = mp side = spacing distance = spacing elif active_edge: # same (active) obj island selections if len(sel_edges) > 1: if len(sel_edges) == 2 and not bool( set(sel_edges[0].verts).intersection( sel_edges[1].verts)): a1p, a2p = sel_edges[0].verts, sel_edges[1].verts a1, a2 = obj_mtx @ a1p[0].co, obj_mtx @ a1p[1].co # b1, b2 = obj_mtx @ a2p[0].co, obj_mtx @ a2p[1].co b_avg = average_vector( [obj_mtx @ a2p[0].co, obj_mtx @ a2p[1].co]) u_v = Vector(a1 - (obj_mtx @ a2p[0].co)).normalized() t_v = Vector(a1 - a2).normalized() n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=True) spacing, mp = get_closest_midpoint(a1, a2, b_avg) setpos = mp side = spacing distance = spacing else: loops = get_loops(vps, legacy=True) if len(loops) > 1: # print ("single obj - multi loop", len(loops)) # Check for closed loops a_ep1, a_ep2 = loops[0][0], loops[0][-1] b_ep1, b_ep2 = loops[1][0], loops[1][-1] if a_ep1 == a_ep2: a_ep2 = loops[0][-2] if b_ep1 == b_ep2: b_ep2 = loops[1][-2] # get coords & set vals a1, a2 = obj_mtx @ a_ep1.co, obj_mtx @ a_ep2.co b1, b2 = obj_mtx @ b_ep1.co, obj_mtx @ b_ep2.co b_avg = get_midpoint(b1, b2) spacing, mp = get_closest_midpoint(a1, a2, b_avg) u_v = Vector((0, 0, 1)) t_v = Vector(a1 - a2).normalized() if u_v.dot(t_v) > 0: u_v = Vector((1, 0, 0)) n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=False) setpos = mp side = spacing distance = spacing elif len(loops) == 1 or len(sel_edges) == 1: # print ("single obj - single loop") vecs = [(obj_mtx @ vp[0].co) - (obj_mtx @ vp[1].co) for vp in vps] start_vec = vecs[-1] side, sides, center = get_sides(obj_mtx, start_vec, vecs, vps) # if len(sel_edges) == 4: # print("FIX") # ONE LINE (ONE EDGE or SINGLE STRAIGHT LINE) MODE------------------------------ if len(sides) == 1 and len(sides[0]) == len(sel_verts): # print("1 side --> one line:", sides[0]) one_line = sides[0][0], sides[0][-1] elif len(sel_edges) == 1: # print("1 edge --> one line", one_line) one_line = sel_verts if one_line: # print ("one line. center, points, normal:", center, one_line, active_edge_normal) p1, p2 = obj_mtx @ one_line[0].co, obj_mtx @ one_line[ 1].co t_v = Vector(p1 - p2).normalized() n_v = active_edge_normal setrot = rotation_from_vector(n_v, t_v) distance = get_distance(p1, p2) setpos = get_midpoint(p1, p2) side = distance else: # RECT CHECK SIDES OR NGON MODE-------------------------------------------- # print("Edge Rect/Ngon mode. Sides:", len(sides)) b_candidate = sides[0][0].link_edges b_candidate = [e.verts for e in b_candidate] b = [ v for vp in b_candidate for v in vp if v not in sides[0] and v in sel_verts ] if b: b = obj_mtx @ b[0].co a = obj_mtx @ sides[0][0].co - obj_mtx @ sides[0][ 1].co b = obj_mtx @ sides[0][0].co - b t_v = Vector(a).normalized() u_v = Vector(b).normalized() n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=True) distance = side setpos = center else: side = None # ----------------------------------------------------------------------------------------- # FACE MODE # ----------------------------------------------------------------------------------------- elif sel_mode[2] and active_face and sel_poly: # GET ISLANDS if multi_object_mode and active_face: first_island = sel_verts point = obj_mtx @ active_face.calc_center_median() firstcos = [obj_mtx @ v.co for v in sel_verts] secondcos = [obj_mtx2 @ v.co for v in sel_verts2] if firstcos and secondcos: island_mode = True distance = point_to_plane(point, firstcos, secondcos) if distance: distance = abs(distance) else: # print("Multi obj Point to plane failed for some reason - using single island mode.") island_mode = False else: # same (active) obj island selections first_island, second_island = get_selection_islands( sel_poly, active_face) if len(first_island) != 0 and len(second_island) != 0: firstcos = [obj_mtx @ v.co for v in first_island] # needs order sort secondcos = [obj_mtx @ v.co for v in second_island] distance = point_to_plane( obj_mtx @ active_face.calc_center_median(), firstcos, secondcos) if distance: distance = abs(distance) island_mode = True else: # print("Point to plane failed for some reason - using single island mode.") island_mode = False else: # Ngon mode first_island = sel_verts # GET PRIMARY SELECTION VALUES ngoncheck = len(active_face.verts) if sel_mode[2] and len(sel_verts) < 4: ngoncheck = 5 # todo: better solution for tris... bpy.ops.mesh.select_all(action='DESELECT') for v in first_island: bm.verts[v.index].select = True bmesh.update_edit_mesh(obj.data, True) bpy.ops.mesh.select_mode(type='VERT') bpy.ops.mesh.select_mode(type='FACE') bm.faces.ensure_lookup_table() # sel_poly = [p for p in bm.faces if p.select] bpy.ops.mesh.region_to_loop() sel_edges = [e for e in bm.edges if e.select] vps = [e.verts for e in sel_edges] vecs = [(obj_mtx @ vp[0].co) - (obj_mtx @ vp[1].co) for vp in vps] normal = correct_normal(obj_mtx, active_face.normal) start_vec = vecs[-1] setrot = rotation_from_vector(normal, start_vec) # print("Rectangle or Ngon mode",ngoncheck) if ngoncheck <= 4: side, sides, center = get_sides(obj_mtx, start_vec, vecs, vps, legacy=False) else: side, sides, center = get_sides(obj_mtx, start_vec, vecs, vps, legacy=True) # ----------------------------------------------------------------------------------------- # PROCESS # ----------------------------------------------------------------------------------------- if side: if len(sel_verts) == 0 or sel_mode[0] or sel_mode[1]: side *= .5 distance = distance / 2 if self.sphere and sel_mode[0]: if not len(sel_verts) == 0: side = distance elif sel_mode[2]: if island_mode: side *= .5 offset = normal * distance * .5 distance *= .5 if self.sphere: side = distance else: side *= .5 distance = side offset = normal * side setpos = center + offset if not island_mode and self.sphere: setpos = setpos - offset # SET FINAL ROTATION if self.world: setrot = (0, 0, 0) else: setrot = setrot.to_euler() # RUN OP if not self.edit_mode == "OBJECT": bpy.ops.mesh.select_mode(type='FACE') if self.itemize: bpy.ops.object.mode_set(mode="OBJECT") cursor = bpy.context.scene.cursor bpy.ops.transform.select_orientation(orientation='GLOBAL') cursor.location = setpos cursor.rotation_euler = setrot if self.boxmode: bpy.ops.mesh.primitive_box_add(width=side, depth=side, height=distance, align='WORLD', location=setpos, rotation=setrot) if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True elif self.sphere and not self.ke_fitprim_option == "QUADSPHERE": bpy.ops.mesh.primitive_uv_sphere_add( segments=self.sphere_seg, ring_count=self.sphere_ring, radius=side, align='WORLD', location=setpos, rotation=setrot) if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True elif self.ke_fitprim_option == "QUADSPHERE": bpy.ops.mesh.primitive_round_cube_add( align='WORLD', location=setpos, rotation=setrot, radius=side, size=(0, 0, 0), arc_div=self.quadsphere_seg, lin_div=0, div_type='CORNERS') if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True else: bpy.ops.mesh.primitive_cylinder_add(vertices=self.cyl_sides, radius=side, depth=distance * 2, enter_editmode=False, align='WORLD', location=setpos, rotation=setrot) if self.modal: if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.mode_set(mode="EDIT") self.settings = (side, distance, setpos, setrot) context.window_manager.modal_handler_add(self) args = (self, context, self.screen_x) self._handle = bpy.types.SpaceView3D.draw_handler_add( draw_callback_px, args, 'WINDOW', 'POST_PIXEL') bpy.context.space_data.overlay.show_cursor = False return {'RUNNING_MODAL'} if multi_object_mode and not self.itemize: second_obj.select_set(state=True) obj.select_set(state=True) bpy.ops.object.editmode_toggle() bpy.ops.object.editmode_toggle() else: self.report({ "INFO" }, "FitPrim: Incorrect Selection? / Edit Mode? / No Active Element?" ) if not self.select and not self.itemize and not self.edit_mode == "OBJECT": bpy.ops.mesh.select_all(action='DESELECT') if self.itemize: bpy.ops.view3d.snap_cursor_to_center() bpy.context.active_object.select_set(state=True) self.ke_fitprim_itemize = False return {"FINISHED"}
def calvn(pas, alfa, ray, vn): vr = Vector((cos(alfa), sin(alfa), 0.0)) vt = Vector((-ray * sin(alfa), ray * cos(alfa), pas)) vn = vr.cross(vt) vn.normalize() return vn
class Line3d(Line): """ 3d Line mostly a gl enabled for future use in manipulators coords are in world space """ def __init__(self, p=None, v=None, p0=None, p1=None, z_axis=None): """ Init by either p: Vector or tuple origin v: Vector or tuple size and direction or p0: Vector or tuple 1 point location p1: Vector or tuple 2 point location Will convert any into Vector 3d both optionnals """ if p is not None and v is not None: self.p = Vector(p).to_3d() self.v = Vector(v).to_3d() elif p0 is not None and p1 is not None: self.p = Vector(p0).to_3d() self.v = Vector(p1).to_3d() - self.p else: self.p = Vector((0, 0, 0)) self.v = Vector((0, 0, 0)) if z_axis is not None: self.z_axis = z_axis else: self.z_axis = Vector((0, 0, 1)) @property def p0(self): return self.p @property def p1(self): return self.p + self.v @p0.setter def p0(self, p0): """ Note: setting p0 move p0 only """ p1 = self.p1 self.p = Vector(p0).to_3d() self.v = p1 - p0 @p1.setter def p1(self, p1): """ Note: setting p1 move p1 only """ self.v = Vector(p1).to_3d() - self.p @property def cross_z(self): """ 3d Vector perpendicular on plane xy lie on the right side p1 |--x p0 """ return self.v.cross(Vector((0, 0, 1))) @property def cross(self): """ 3d Vector perpendicular on plane defined by z_axis lie on the right side p1 |--x p0 """ return self.v.cross(self.z_axis) def normal(self, t=0): """ 3d Vector perpendicular on plane defined by z_axis lie on the right side p1 |--x p0 """ n = Line3d() n.p = self.lerp(t) n.v = self.cross return n def sized_normal(self, t, size): """ 3d Line perpendicular on plane defined by z_axis and of given size positionned at t in current line lie on the right side p1 |--x p0 """ p = self.lerp(t) v = size * self.cross.normalized() return Line3d(p, v, z_axis=self.z_axis) def offset(self, offset): """ offset > 0 on the right part """ return Line3d(self.p + offset * self.cross.normalized(), self.v) # unless override, 2d methods should raise NotImplementedError def intersect(self, line): raise NotImplementedError def point_sur_segment(self, pt): raise NotImplementedError def tangeant(self, t, da, radius): raise NotImplementedError
def camera_light_source(use_camera, use_light, object_center_vec, object_size): camera_factor = 15.0 # If chosen a camera is put into the scene. if use_camera == True: # Assume that the object is put into the global origin. Then, the # camera is moved in x and z direction, not in y. The object has its # size at distance sqrt(object_size) from the origin. So, move the # camera by this distance times a factor of camera_factor in x and z. # Then add x, y and z of the origin of the object. object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0, sqrt(object_size) * camera_factor)) camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera camera_data = bpy.data.cameras.new("A_camera") camera_data.lens = 45 camera_data.clip_end = 500.0 camera = bpy.data.objects.new("A_camera", camera_data) camera.location = camera_xyz_vec bpy.context.collection.objects.link(camera) # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis z_axis_vec = Vector((0.0, 0.0, 1.0)) # The angle between the last two vectors angle = object_camera_vec.angle(z_axis_vec, 0) # The cross-product of z_axis_vec and object_camera_vec axis_vec = z_axis_vec.cross(object_camera_vec) # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. # 4 is the size of the matrix. camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() # Rotate the camera around its axis by 90° such that we have a nice # camera position and view onto the object. bpy.ops.object.select_all(action='DESELECT') camera.select_set(True) # Rotate the camera around its axis 'object_camera_vec' by 90° such # that we have a nice camera view onto the object. matrix_rotation = Matrix.Rotation(90 / 360 * 2 * pi, 4, object_camera_vec) rotate_object(matrix_rotation, camera) # Here a lamp is put into the scene, if chosen. if use_light == True: # This is the distance from the object measured in terms of % # of the camera distance. It is set onto 50% (1/2) distance. light_dl = sqrt(object_size) * 15 * 0.5 # This is a factor to which extend the lamp shall go to the right # (from the camera point of view). light_dy_right = light_dl * (3.0 / 4.0) # Create x, y and z for the lamp. object_light_vec = Vector((light_dl, light_dy_right, light_dl)) light_xyz_vec = object_center_vec + object_light_vec # Create the lamp light_data = bpy.data.lights.new(name="A_light", type="SUN") light_data.distance = 500.0 light_data.energy = 3.0 lamp = bpy.data.objects.new("A_light", light_data) lamp.location = light_xyz_vec bpy.context.collection.objects.link(lamp) # Some settings for the World: a bit ambient occlusion bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.2 # Some properties for cycles lamp.data.use_nodes = True lmp_P_BSDF = lamp.data.node_tree.nodes['Emission'] lmp_P_BSDF.inputs['Strength'].default_value = 5
def get_axes(mmd_local_axis_x, mmd_local_axis_z): x_axis = Vector(mmd_local_axis_x).normalized().xzy z_axis = Vector(mmd_local_axis_z).normalized().xzy y_axis = z_axis.cross(x_axis).normalized() z_axis = x_axis.cross(y_axis).normalized() # correction return (x_axis, y_axis, z_axis)
def extract_primitives(glTF, blender_mesh, blender_vertex_groups, modifiers, export_settings): """ Extract primitives from a mesh. Polygons are triangulated and sorted by material. Furthermore, primitives are split up, if the indices range is exceeded. Finally, triangles are also split up/duplicated, if face normals are used instead of vertex normals. """ print_console('INFO', 'Extracting primitive: ' + blender_mesh.name) use_tangents = False if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0: try: blender_mesh.calc_tangents() use_tangents = True except Exception: print_console( 'WARNING', 'Could not calculate tangents. Please try to triangulate the mesh first.' ) # material_map = {} # # Gathering position, normal and tex_coords. # no_material_attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: no_material_attributes[TANGENT_ATTRIBUTE] = [] # # Directory of materials with its primitive. # no_material_primitives = { MATERIAL_ID: '', INDICES_ID: [], ATTRIBUTES_ID: no_material_attributes } material_name_to_primitives = {'': no_material_primitives} # vertex_index_to_new_indices = {} material_map[''] = vertex_index_to_new_indices # # Create primitive for each material. # for blender_material in blender_mesh.materials: if blender_material is None: continue attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: attributes[TANGENT_ATTRIBUTE] = [] primitive = { MATERIAL_ID: blender_material.name, INDICES_ID: [], ATTRIBUTES_ID: attributes } material_name_to_primitives[blender_material.name] = primitive # vertex_index_to_new_indices = {} material_map[blender_material.name] = vertex_index_to_new_indices tex_coord_max = 0 if blender_mesh.uv_layers.active: tex_coord_max = len(blender_mesh.uv_layers) # vertex_colors = {} color_index = 0 for vertex_color in blender_mesh.vertex_colors: vertex_color_name = COLOR_PREFIX + str(color_index) vertex_colors[vertex_color_name] = vertex_color color_index += 1 if color_index >= GLTF_MAX_COLORS: break color_max = color_index # bone_max = 0 for blender_polygon in blender_mesh.polygons: for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index bones_count = len(blender_mesh.vertices[vertex_index].groups) if bones_count > 0: if bones_count % 4 == 0: bones_count -= 1 bone_max = max(bone_max, bones_count // 4 + 1) # morph_max = 0 blender_shape_keys = [] if blender_mesh.shape_keys is not None: morph_max = len(blender_mesh.shape_keys.key_blocks) - 1 for blender_shape_key in blender_mesh.shape_keys.key_blocks: if blender_shape_key != blender_shape_key.relative_key: blender_shape_keys.append( ShapeKey( blender_shape_key, blender_shape_key.normals_vertex_get( ), # calculate vertex normals for this shape key blender_shape_key.normals_polygon_get()) ) # calculate polygon normals for this shape key # # Convert polygon to primitive indices and eliminate invalid ones. Assign to material. # for blender_polygon in blender_mesh.polygons: export_color = True # if blender_polygon.material_index < 0 or blender_polygon.material_index >= len(blender_mesh.materials) or \ blender_mesh.materials[blender_polygon.material_index] is None: primitive = material_name_to_primitives[''] vertex_index_to_new_indices = material_map[''] else: primitive = material_name_to_primitives[blender_mesh.materials[ blender_polygon.material_index].name] vertex_index_to_new_indices = material_map[blender_mesh.materials[ blender_polygon.material_index].name] # attributes = primitive[ATTRIBUTES_ID] face_normal = blender_polygon.normal face_tangent = Vector((0.0, 0.0, 0.0)) face_bitangent = Vector((0.0, 0.0, 0.0)) if use_tangents: for loop_index in blender_polygon.loop_indices: temp_vertex = blender_mesh.loops[loop_index] face_tangent += temp_vertex.tangent face_bitangent += temp_vertex.bitangent face_tangent.normalize() face_bitangent.normalize() # indices = primitive[INDICES_ID] loop_index_list = [] if len(blender_polygon.loop_indices) == 3: loop_index_list.extend(blender_polygon.loop_indices) elif len(blender_polygon.loop_indices) > 3: # Triangulation of polygon. Using internal function, as non-convex polygons could exist. polyline = [] for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index v = blender_mesh.vertices[vertex_index].co polyline.append(Vector((v[0], v[1], v[2]))) triangles = tessellate_polygon((polyline, )) for triangle in triangles: loop_index_list.append( blender_polygon.loop_indices[triangle[0]]) loop_index_list.append( blender_polygon.loop_indices[triangle[2]]) loop_index_list.append( blender_polygon.loop_indices[triangle[1]]) else: continue for loop_index in loop_index_list: vertex_index = blender_mesh.loops[loop_index].vertex_index if vertex_index_to_new_indices.get(vertex_index) is None: vertex_index_to_new_indices[vertex_index] = [] # v = None n = None t = None b = None uvs = [] colors = [] joints = [] weights = [] target_positions = [] target_normals = [] target_tangents = [] vertex = blender_mesh.vertices[vertex_index] v = convert_swizzle_location(vertex.co, export_settings) if blender_polygon.use_smooth: n = convert_swizzle_location(vertex.normal, export_settings) if use_tangents: t = convert_swizzle_tangent( blender_mesh.loops[loop_index].tangent, export_settings) b = convert_swizzle_location( blender_mesh.loops[loop_index].bitangent, export_settings) else: n = convert_swizzle_location(face_normal, export_settings) if use_tangents: t = convert_swizzle_tangent(face_tangent, export_settings) b = convert_swizzle_location(face_bitangent, export_settings) if use_tangents: tv = Vector((t[0], t[1], t[2])) bv = Vector((b[0], b[1], b[2])) nv = Vector((n[0], n[1], n[2])) if (nv.cross(tv)).dot(bv) < 0.0: t[3] = -1.0 if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): uv = blender_mesh.uv_layers[tex_coord_index].data[ loop_index].uv uvs.append([uv.x, 1.0 - uv.y]) # if color_max > 0 and export_color: for color_index in range(0, color_max): color_name = COLOR_PREFIX + str(color_index) color = vertex_colors[color_name].data[loop_index].color colors.append([ color_srgb_to_scene_linear(color[0]), color_srgb_to_scene_linear(color[1]), color_srgb_to_scene_linear(color[2]), 1.0 ]) # bone_count = 0 if blender_vertex_groups is not None and vertex.groups is not None and len( vertex.groups) > 0 and export_settings[ gltf2_blender_export_keys.SKINS]: joint = [] weight = [] vertex_groups = vertex.groups if not export_settings['gltf_all_vertex_influences']: # sort groups by weight descending vertex_groups = sorted(vertex.groups, key=attrgetter('weight'), reverse=True) for group_element in vertex_groups: if len(joint) == 4: bone_count += 1 joints.append(joint) weights.append(weight) joint = [] weight = [] # joint_weight = group_element.weight if joint_weight <= 0.0: continue # vertex_group_index = group_element.group vertex_group_name = blender_vertex_groups[ vertex_group_index].name joint_index = None if modifiers is not None: modifiers_dict = {m.type: m for m in modifiers} if "ARMATURE" in modifiers_dict: armature = modifiers_dict["ARMATURE"].object skin = gltf2_blender_gather_skins.gather_skin( armature, export_settings) for index, j in enumerate(skin.joints): if j.name == vertex_group_name: joint_index = index break # if joint_index is not None: joint.append(joint_index) weight.append(joint_weight) if len(joint) > 0: bone_count += 1 for fill in range(0, 4 - len(joint)): joint.append(0) weight.append(0.0) joints.append(joint) weights.append(weight) for fill in range(0, bone_max - bone_count): joints.append([0, 0, 0, 0]) weights.append([0.0, 0.0, 0.0, 0.0]) # if morph_max > 0 and export_settings[ gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): blender_shape_key = blender_shape_keys[morph_index] v_morph = convert_swizzle_location( blender_shape_key.shape_key.data[vertex_index].co, export_settings) # Store delta. v_morph -= v target_positions.append(v_morph) # n_morph = None if blender_polygon.use_smooth: temp_normals = blender_shape_key.vertex_normals n_morph = (temp_normals[vertex_index * 3 + 0], temp_normals[vertex_index * 3 + 1], temp_normals[vertex_index * 3 + 2]) else: temp_normals = blender_shape_key.polygon_normals n_morph = (temp_normals[blender_polygon.index * 3 + 0], temp_normals[blender_polygon.index * 3 + 1], temp_normals[blender_polygon.index * 3 + 2]) n_morph = convert_swizzle_location(n_morph, export_settings) # Store delta. n_morph -= n target_normals.append(n_morph) # if use_tangents: rotation = n_morph.rotation_difference(n) t_morph = Vector((t[0], t[1], t[2])) t_morph.rotate(rotation) target_tangents.append(t_morph) # # create = True for current_new_index in vertex_index_to_new_indices[vertex_index]: found = True for i in range(0, 3): if attributes[POSITION_ATTRIBUTE][current_new_index * 3 + i] != v[i]: found = False break if attributes[NORMAL_ATTRIBUTE][current_new_index * 3 + i] != n[i]: found = False break if use_tangents: for i in range(0, 4): if attributes[TANGENT_ATTRIBUTE][current_new_index * 4 + i] != t[i]: found = False break if not found: continue for tex_coord_index in range(0, tex_coord_max): uv = uvs[tex_coord_index] tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) for i in range(0, 2): if attributes[tex_coord_id][current_new_index * 2 + i] != uv[i]: found = False break if export_color: for color_index in range(0, color_max): color = colors[color_index] color_id = COLOR_PREFIX + str(color_index) for i in range(0, 3): # Alpha is always 1.0 - see above. current_color = attributes[color_id][ current_new_index * 4 + i] if color_srgb_to_scene_linear( current_color) != color[i]: found = False break if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint = joints[bone_index] weight = weights[bone_index] joint_id = JOINTS_PREFIX + str(bone_index) weight_id = WEIGHTS_PREFIX + str(bone_index) for i in range(0, 4): if attributes[joint_id][current_new_index * 4 + i] != joint[i]: found = False break if attributes[weight_id][current_new_index * 4 + i] != weight[i]: found = False break if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position = target_positions[morph_index] target_normal = target_normals[morph_index] if use_tangents: target_tangent = target_tangents[morph_index] target_position_id = MORPH_POSITION_PREFIX + str( morph_index) target_normal_id = MORPH_NORMAL_PREFIX + str( morph_index) target_tangent_id = MORPH_TANGENT_PREFIX + str( morph_index) for i in range(0, 3): if attributes[target_position_id][ current_new_index * 3 + i] != target_position[i]: found = False break if attributes[target_normal_id][ current_new_index * 3 + i] != target_normal[i]: found = False break if use_tangents: if attributes[target_tangent_id][ current_new_index * 3 + i] != target_tangent[i]: found = False break if found: indices.append(current_new_index) create = False break if not create: continue new_index = 0 if primitive.get('max_index') is not None: new_index = primitive['max_index'] + 1 primitive['max_index'] = new_index vertex_index_to_new_indices[vertex_index].append(new_index) # # indices.append(new_index) # attributes[POSITION_ATTRIBUTE].extend(v) attributes[NORMAL_ATTRIBUTE].extend(n) if use_tangents: attributes[TANGENT_ATTRIBUTE].extend(t) if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) if attributes.get(tex_coord_id) is None: attributes[tex_coord_id] = [] attributes[tex_coord_id].extend(uvs[tex_coord_index]) if export_color: for color_index in range(0, color_max): color_id = COLOR_PREFIX + str(color_index) if attributes.get(color_id) is None: attributes[color_id] = [] attributes[color_id].extend(colors[color_index]) if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint_id = JOINTS_PREFIX + str(bone_index) if attributes.get(joint_id) is None: attributes[joint_id] = [] attributes[joint_id].extend(joints[bone_index]) weight_id = WEIGHTS_PREFIX + str(bone_index) if attributes.get(weight_id) is None: attributes[weight_id] = [] attributes[weight_id].extend(weights[bone_index]) if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position_id = MORPH_POSITION_PREFIX + str( morph_index) if attributes.get(target_position_id) is None: attributes[target_position_id] = [] attributes[target_position_id].extend( target_positions[morph_index]) target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index) if attributes.get(target_normal_id) is None: attributes[target_normal_id] = [] attributes[target_normal_id].extend( target_normals[morph_index]) if use_tangents: target_tangent_id = MORPH_TANGENT_PREFIX + str( morph_index) if attributes.get(target_tangent_id) is None: attributes[target_tangent_id] = [] attributes[target_tangent_id].extend( target_tangents[morph_index]) # # Add primitive plus split them if needed. # result_primitives = [] for material_name, primitive in material_name_to_primitives.items(): export_color = True # indices = primitive[INDICES_ID] if len(indices) == 0: continue position = primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE] normal = primitive[ATTRIBUTES_ID][NORMAL_ATTRIBUTE] if use_tangents: tangent = primitive[ATTRIBUTES_ID][TANGENT_ATTRIBUTE] tex_coords = [] for tex_coord_index in range(0, tex_coord_max): tex_coords.append(primitive[ATTRIBUTES_ID][TEXCOORD_PREFIX + str(tex_coord_index)]) colors = [] if export_color: for color_index in range(0, color_max): tex_coords.append(primitive[ATTRIBUTES_ID][COLOR_PREFIX + str(color_index)]) joints = [] weights = [] if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joints.append(primitive[ATTRIBUTES_ID][JOINTS_PREFIX + str(bone_index)]) weights.append(primitive[ATTRIBUTES_ID][WEIGHTS_PREFIX + str(bone_index)]) target_positions = [] target_normals = [] target_tangents = [] if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_positions.append( primitive[ATTRIBUTES_ID][MORPH_POSITION_PREFIX + str(morph_index)]) target_normals.append( primitive[ATTRIBUTES_ID][MORPH_NORMAL_PREFIX + str(morph_index)]) if use_tangents: target_tangents.append( primitive[ATTRIBUTES_ID][MORPH_TANGENT_PREFIX + str(morph_index)]) # count = len(indices) if count == 0: continue max_index = max(indices) # # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed. # Specifically, the value 65535 (in UINT16) cannot be used as a vertex index. # https://github.com/KhronosGroup/glTF/issues/1142 # https://github.com/KhronosGroup/glTF/pull/1476/files range_indices = 65535 # if max_index >= range_indices: # # Splitting result_primitives. # # At start, all indices are pending. pending_attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: pending_attributes[TANGENT_ATTRIBUTE] = [] pending_primitive = { MATERIAL_ID: material_name, INDICES_ID: [], ATTRIBUTES_ID: pending_attributes } pending_primitive[INDICES_ID].extend(indices) pending_attributes[POSITION_ATTRIBUTE].extend(position) pending_attributes[NORMAL_ATTRIBUTE].extend(normal) if use_tangents: pending_attributes[TANGENT_ATTRIBUTE].extend(tangent) tex_coord_index = 0 for tex_coord in tex_coords: pending_attributes[TEXCOORD_PREFIX + str(tex_coord_index)] = tex_coord tex_coord_index += 1 if export_color: color_index = 0 for color in colors: pending_attributes[COLOR_PREFIX + str(color_index)] = color color_index += 1 if export_settings[gltf2_blender_export_keys.SKINS]: joint_index = 0 for joint in joints: pending_attributes[JOINTS_PREFIX + str(joint_index)] = joint joint_index += 1 weight_index = 0 for weight in weights: pending_attributes[WEIGHTS_PREFIX + str(weight_index)] = weight weight_index += 1 if export_settings[gltf2_blender_export_keys.MORPH]: morph_index = 0 for target_position in target_positions: pending_attributes[MORPH_POSITION_PREFIX + str(morph_index)] = target_position morph_index += 1 morph_index = 0 for target_normal in target_normals: pending_attributes[MORPH_NORMAL_PREFIX + str(morph_index)] = target_normal morph_index += 1 if use_tangents: morph_index = 0 for target_tangent in target_tangents: pending_attributes[MORPH_TANGENT_PREFIX + str(morph_index)] = target_tangent morph_index += 1 pending_indices = pending_primitive[INDICES_ID] # Continue until all are processed. while len(pending_indices) > 0: process_indices = pending_primitive[INDICES_ID] max_index = max(process_indices) pending_indices = [] # # all_local_indices = [] for i in range(0, (max_index // range_indices) + 1): all_local_indices.append([]) # # # For all faces ... for face_index in range(0, len(process_indices), 3): written = False face_min_index = min(process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]) face_max_index = max(process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]) # ... check if it can be but in a range of maximum indices. for i in range(0, (max_index // range_indices) + 1): offset = i * range_indices # Yes, so store the primitive with its indices. if face_min_index >= offset and face_max_index < offset + range_indices: all_local_indices[i].extend([ process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2] ]) written = True break # If not written, the triangle face has indices from different ranges. if not written: pending_indices.extend([ process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2] ]) # Only add result_primitives, which do have indices in it. for local_indices in all_local_indices: if len(local_indices) > 0: current_primitive = extract_primitive_floor( pending_primitive, local_indices, use_tangents) result_primitives.append(current_primitive) print_console( 'DEBUG', 'Adding primitive with splitting. Indices: ' + str(len(current_primitive[INDICES_ID])) + ' Vertices: ' + str( len(current_primitive[ATTRIBUTES_ID] [POSITION_ATTRIBUTE]) // 3)) # Process primitive faces having indices in several ranges. if len(pending_indices) > 0: pending_primitive = extract_primitive_pack( pending_primitive, pending_indices, use_tangents) print_console( 'DEBUG', 'Creating temporary primitive for splitting') else: # # No splitting needed. # result_primitives.append(primitive) print_console( 'DEBUG', 'Adding primitive without splitting. Indices: ' + str(len(primitive[INDICES_ID])) + ' Vertices: ' + str(len(primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]) // 3)) print_console('INFO', 'Primitives created: ' + str(len(result_primitives))) return result_primitives
def draw_molecule(molecule, center=(0, 0, 0), max_molecule_size=5, show_bonds=True): """Draw a molecule to blender. Uses loaded json molecule data.""" # Get scale factor - only scales large molecules down max_coord = 1E-6 for atom in molecule["atoms"]: max_coord = max(max_coord, *[abs(a) for a in atom["location"]]) scale = min(max_molecule_size / max_coord, 1) # Scale location coordinates and add specified center for atom in molecule["atoms"]: atom["location"] = [ c + x * scale for c, x in zip(center, atom["location"]) ] # Keep references to all atoms and bonds shapes = [] # If using space-filling model, scale up atom size and remove bonds if not show_bonds: scale *= 2.5 molecule["bonds"] = [] # Add atom primitive bpy.ops.object.select_all(action='DESELECT') bpy.ops.mesh.primitive_uv_sphere_add() sphere = bpy.context.object # Add bond material and primitive if it's going to be used if molecule["bonds"]: key = "bond" bpy.data.materials.new(name=key) bpy.data.materials[key].diffuse_color = atom_data[key]["color"] bpy.data.materials[key].specular_intensity = 0.2 bpy.ops.mesh.primitive_cylinder_add() cylinder = bpy.context.object cylinder.active_material = bpy.data.materials["bond"] # Draw atoms for atom in molecule["atoms"]: # If element is not in dictionary, use undefined values if atom["element"] not in atom_data: atom["element"] = "undefined" # If material for atom type has not yet been defined, do so if atom["element"] not in bpy.data.materials: key = atom["element"] bpy.data.materials.new(name=key) bpy.data.materials[key].diffuse_color = atom_data[key]["color"] bpy.data.materials[key].specular_intensity = 0.2 # Copy mesh primitive and edit to make atom atom_sphere = sphere.copy() atom_sphere.data = sphere.data.copy() atom_sphere.location = atom["location"] atom_sphere.dimensions = [ atom_data[atom["element"]]["radius"] * scale * 2 ] * 3 atom_sphere.active_material = bpy.data.materials[atom["element"]] bpy.context.scene.objects.link(atom_sphere) shapes.append(atom_sphere) # Draw bonds for bond in molecule["bonds"]: # Extracting locations first_loc = molecule["atoms"][bond["atoms"][0]]["location"] second_loc = molecule["atoms"][bond["atoms"][1]]["location"] diff = [c2 - c1 for c2, c1 in zip(first_loc, second_loc)] cent = [(c2 + c1) / 2 for c2, c1 in zip(first_loc, second_loc)] mag = sum([(c2 - c1)**2 for c1, c2 in zip(first_loc, second_loc)])**0.5 # Euler rotation calculation v_axis = Vector(diff).normalized() v_obj = Vector((0, 0, 1)) v_rot = v_obj.cross(v_axis) angle = acos(v_obj.dot(v_axis)) # Check that the number of bonds is logical if bond["order"] not in range(1, 4): print("Improper number of bonds! Defaulting to 1.") bond["order"] = 1 # Specify locations of each bond in every scenario if bond["order"] == 1: trans = [[0] * 3] elif bond["order"] == 2: trans = [[1.4 * atom_data["bond"]["radius"] * x for x in v_obj], [-1.4 * atom_data["bond"]["radius"] * x for x in v_obj]] elif bond["order"] == 3: trans = [[0] * 3, [2.2 * atom_data["bond"]["radius"] * x for x in v_obj], [-2.2 * atom_data["bond"]["radius"] * x for x in v_obj]] # Draw bonds for i in range(bond["order"]): bond_cylinder = cylinder.copy() bond_cylinder.data = cylinder.data.copy() bond_cylinder.dimensions = [ atom_data["bond"]["radius"] * scale * 2 ] * 2 + [mag] bond_cylinder.location = [ c + scale * v for c, v in zip(cent, trans[i]) ] bond_cylinder.rotation_mode = "AXIS_ANGLE" bond_cylinder.rotation_axis_angle = [angle] + list(v_rot) bpy.context.scene.objects.link(bond_cylinder) shapes.append(bond_cylinder) # Remove primitive meshes bpy.ops.object.select_all(action='DESELECT') sphere.select = True if molecule["bonds"]: cylinder.select = True # If the starting cube is there, remove it if "Cube" in bpy.data.objects.keys(): bpy.data.objects.get("Cube").select = True bpy.ops.object.delete() # Smooth and join molecule shapes for shape in shapes: shape.select = True bpy.context.scene.objects.active = shapes[0] bpy.ops.object.shade_smooth() bpy.ops.object.join() # Center object origin to geometry bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN") # Refresh scene bpy.context.scene.update()
def import_xyz(Ball_type, Ball_azimuth, Ball_zenith, Ball_radius_factor, radiustype, Ball_distance_factor, put_to_center, put_to_center_all, use_camera, use_lamp, filepath_xyz): # List of materials atom_material_list = [] # ------------------------------------------------------------------------ # INITIALIZE THE ELEMENT LIST read_elements() # ------------------------------------------------------------------------ # READING DATA OF ATOMS Number_of_total_atoms = read_xyz_file(filepath_xyz, radiustype) # We show the atoms of the first frame. first_frame = ALL_FRAMES[0] # ------------------------------------------------------------------------ # MATERIAL PROPERTIES FOR ATOMS # Create first a new list of materials for each type of atom # (e.g. hydrogen) for atoms_of_one_type in first_frame: # Take the first atom atom = atoms_of_one_type[0] material = bpy.data.materials.new(atom.name) material.name = atom.name material.diffuse_color = atom.color atom_material_list.append(material) # Now, we go through all atoms and give them a material. For all atoms ... for atoms_of_one_type in first_frame: for atom in atoms_of_one_type: # ... and all materials ... for material in atom_material_list: # ... select the correct material for the current atom via # comparison of names ... if atom.name in material.name: # ... and give the atom its material properties. # However, before we check if it is a vacancy # The vacancy is represented by a transparent cube. if atom.name == "Vacancy": material.transparency_method = 'Z_TRANSPARENCY' material.alpha = 1.3 material.raytrace_transparency.fresnel = 1.6 material.raytrace_transparency.fresnel_factor = 1.6 material.use_transparency = True # The atom gets its properties. atom.material = material # ------------------------------------------------------------------------ # TRANSLATION OF THE STRUCTURE TO THE ORIGIN # It may happen that the structure in a XYZ file already has an offset # If chosen, the structure is put into the center of the scene # (only the first frame). if put_to_center == True and put_to_center_all == False: sum_vec = Vector((0.0,0.0,0.0)) # Sum of all atom coordinates for atoms_of_one_type in first_frame: sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec) # Then the average is taken sum_vec = sum_vec / Number_of_total_atoms # After, for each atom the center of gravity is substracted for atoms_of_one_type in first_frame: for atom in atoms_of_one_type: atom.location -= sum_vec # If chosen, the structure is put into the center of the scene # (all frames). if put_to_center_all == True: # For all frames for frame in ALL_FRAMES: sum_vec = Vector((0.0,0.0,0.0)) # Sum of all atom coordinates for (i, atoms_of_one_type) in enumerate(frame): # This is a guarantee that only the total number of atoms of the # first frame is used. Condition is, so far, that the number of # atoms in a xyz file is constant. However, sometimes the number # may increase (or decrease). If it decreases, the addon crashes. # If it increases, only the tot number of atoms of the first frame # is used. # By time, I will allow varying atom numbers ... but this takes # some time ... if i >= Number_of_total_atoms: break sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec) # Then the average is taken sum_vec = sum_vec / Number_of_total_atoms # After, for each atom the center of gravity is substracted for atoms_of_one_type in frame: for atom in atoms_of_one_type: atom.location -= sum_vec # ------------------------------------------------------------------------ # SCALING # Take all atoms and adjust their radii and scale the distances. for atoms_of_one_type in first_frame: for atom in atoms_of_one_type: atom.location *= Ball_distance_factor # ------------------------------------------------------------------------ # DETERMINATION OF SOME GEOMETRIC PROPERTIES # In the following, some geometric properties of the whole object are # determined: center, size, etc. sum_vec = Vector((0.0,0.0,0.0)) # First the center is determined. All coordinates are summed up ... for atoms_of_one_type in first_frame: sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec) # ... and the average is taken. This gives the center of the object. object_center_vec = sum_vec / Number_of_total_atoms # Now, we determine the size.The farthest atom from the object center is # taken as a measure. The size is used to place well the camera and light # into the scene. object_size_vec = [] for atoms_of_one_type in first_frame: object_size_vec += [atom.location - object_center_vec for atom in atoms_of_one_type] object_size = 0.0 object_size = max(object_size_vec).length # ------------------------------------------------------------------------ # CAMERA AND LAMP camera_factor = 20.0 # If chosen a camera is put into the scene. if use_camera == True: # Assume that the object is put into the global origin. Then, the # camera is moved in x and z direction, not in y. The object has its # size at distance sqrt(object_size) from the origin. So, move the # camera by this distance times a factor of camera_factor in x and z. # Then add x, y and z of the origin of the object. object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0, sqrt(object_size) * camera_factor)) camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera current_layers=bpy.context.scene.layers camera_data = bpy.data.cameras.new("A_camera") camera_data.lens = 45 camera_data.clip_end = 500.0 camera = bpy.data.objects.new("A_camera", camera_data) camera.location = camera_xyz_vec camera.layers = current_layers bpy.context.scene.objects.link(camera) # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis z_axis_vec = Vector((0.0, 0.0, 1.0)) # The angle between the last two vectors angle = object_camera_vec.angle(z_axis_vec, 0) # The cross-product of z_axis_vec and object_camera_vec axis_vec = z_axis_vec.cross(object_camera_vec) # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. # 4 is the size of the matrix. camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() # Rotate the camera around its axis by 90° such that we have a nice # camera position and view onto the object. bpy.ops.object.select_all(action='DESELECT') camera.select = True bpy.ops.transform.rotate(value=(90.0*2*pi/360.0), axis=object_camera_vec, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False) # Here a lamp is put into the scene, if chosen. if use_lamp == True: # This is the distance from the object measured in terms of % # of the camera distance. It is set onto 50% (1/2) distance. lamp_dl = sqrt(object_size) * 15 * 0.5 # This is a factor to which extend the lamp shall go to the right # (from the camera point of view). lamp_dy_right = lamp_dl * (3.0/4.0) # Create x, y and z for the lamp. object_lamp_vec = Vector((lamp_dl,lamp_dy_right,lamp_dl)) lamp_xyz_vec = object_center_vec + object_lamp_vec # Create the lamp current_layers=bpy.context.scene.layers lamp_data = bpy.data.lamps.new(name="A_lamp", type="POINT") lamp_data.distance = 500.0 lamp_data.energy = 3.0 lamp_data.shadow_method = 'RAY_SHADOW' lamp = bpy.data.objects.new("A_lamp", lamp_data) lamp.location = lamp_xyz_vec lamp.layers = current_layers bpy.context.scene.objects.link(lamp) bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.2 # ------------------------------------------------------------------------ # DRAWING THE ATOMS bpy.ops.object.select_all(action='DESELECT') # For each list of atoms of ONE type (e.g. Hydrogen) for atoms_of_one_type in first_frame: # Create first the vertices composed of the coordinates of all # atoms of one type atom_vertices = [] for atom in atoms_of_one_type: # In fact, the object is created in the World's origin. # This is why 'object_center_vec' is substracted. At the end # the whole object is translated back to 'object_center_vec'. atom_vertices.append( atom.location - object_center_vec ) # Build the mesh atom_mesh = bpy.data.meshes.new("Mesh_"+atom.name) atom_mesh.from_pydata(atom_vertices, [], []) atom_mesh.update() new_atom_mesh = bpy.data.objects.new(atom.name, atom_mesh) bpy.context.scene.objects.link(new_atom_mesh) # Now, build a representative sphere (atom) current_layers=bpy.context.scene.layers if atom.name == "Vacancy": bpy.ops.mesh.primitive_cube_add( view_align=False, enter_editmode=False, location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0), layers=current_layers) else: # NURBS balls if Ball_type == "0": bpy.ops.surface.primitive_nurbs_surface_sphere_add( view_align=False, enter_editmode=False, location=(0,0,0), rotation=(0.0, 0.0, 0.0), layers=current_layers) # UV balls elif Ball_type == "1": bpy.ops.mesh.primitive_uv_sphere_add( segments=Ball_azimuth, ring_count=Ball_zenith, size=1, view_align=False, enter_editmode=False, location=(0,0,0), rotation=(0, 0, 0), layers=current_layers) # Meta balls elif Ball_type == "2": bpy.ops.object.metaball_add(type='BALL', view_align=False, enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0), layers=current_layers) ball = bpy.context.scene.objects.active ball.scale = (atom.radius*Ball_radius_factor,) * 3 if atom.name == "Vacancy": ball.name = "Cube_"+atom.name else: ball.name = "Ball (NURBS)_"+atom.name ball.active_material = atom.material ball.parent = new_atom_mesh new_atom_mesh.dupli_type = 'VERTS' # The object is back translated to 'object_center_vec'. new_atom_mesh.location = object_center_vec STRUCTURE.append(new_atom_mesh) # ------------------------------------------------------------------------ # SELECT ALL LOADED OBJECTS bpy.ops.object.select_all(action='DESELECT') obj = None for obj in STRUCTURE: obj.select = True # activate the last selected object (perhaps another should be active?) if obj: bpy.context.scene.objects.active = obj
def execute(self, context): curvepoints = [] curveobj = context.active_object bpy.ops.object.empty_add(type='PLAIN_AXES') parentobj = context.active_object parentobj.name = 'CurveForce' if len(curveobj.data.splines[0].bezier_points) > 1: curvepoints = [v.co for v in curveobj.data.splines[0].bezier_points] else: curvepoints = [v.co for v in curveobj.data.splines[0].points] curveobj.parent = parentobj context.active_object.select = False curveobj.select = True previousnormal = Vector([0.0,0.0,0.0]) lastStrength = context.window_manager.curveForce_strength lastDistance = context.window_manager.curveForce_maxDist for i in range(len(curvepoints)): cpoint = Vector([curvepoints[i][0],curvepoints[i][1],curvepoints[i][2]]) bpy.ops.object.empty_add(type='SINGLE_ARROW',location=(cpoint)) context.active_object.name = 'ForceObj' # turn into forcefield bpy.ops.object.forcefield_toggle() context.active_object.field.type = 'WIND' if context.window_manager.curveForce_trailout: if i > 0: lastStrength = lastStrength * 0.9 lastDistance = lastDistance * 0.9 context.active_object.field.strength = lastStrength context.active_object.field.use_max_distance = True context.active_object.field.distance_max = lastDistance context.active_object.field.falloff_power = context.window_manager.curveForce_falloffPower # get the curve's direction between points tempnorm = Vector([0,0,0]) if (i < len(curvepoints) - 1): cpoint2 = Vector([curvepoints[i + 1][0],curvepoints[i + 1][1],curvepoints[i + 1][2]]) tempnorm = cpoint - cpoint2 if i > 0: if abs(previousnormal.length) > 0.0: tempnorm = (tempnorm + previousnormal) / 2.0 previousnormal = tempnorm else: if curveobj.data.splines[0].use_cyclic_u or curveobj.data.splines[0].use_cyclic_u: cpoint2 = Vector([curvepoints[0][0],curvepoints[0][1],curvepoints[0][2]]) tempnorm = cpoint - cpoint2 if abs(previousnormal.length) > 0.0: tempnorm = (tempnorm + previousnormal) / 2.0 previousnormal = tempnorm else: cpoint2 = Vector([curvepoints[i - 1][0],curvepoints[i - 1][1],curvepoints[i - 1][2]]) tempnorm = cpoint2 - cpoint if abs(previousnormal.length) > 0.0: tempnorm = (tempnorm + previousnormal) / 2.0 previousnormal = tempnorm if abs(tempnorm.length) > 0.0: z = Vector((0,0,1)) angle = tempnorm.angle(z) axis = z.cross(tempnorm) mat = Matrix.Rotation(angle, 4, axis) mat_world = context.active_object.matrix_world * mat context.active_object.matrix_world = mat_world context.active_object.parent = parentobj return {'FINISHED'}
def grow(self): from numpy import sum, all, ones self.itt += 1 stp = self.stp killzone = self.killzone noise = self.noise v_xyz,s_xyz = self.get_positions() dvv,dvs = self.get_distances(v_xyz,s_xyz) vs_map,sv_map = self.make_maps(dvv,dvs,killzone) nv,ns = dvs.shape self.select_seed() bm = self.get_bmesh() nodes = [v for v in bm.verts] for i,jj in vs_map.items(): v = Vector(sum(s_xyz[jj,:]-v_xyz[i,:],axis=0)) v.normalize() # this is flawed. particularly for "flat" geometries p,pn = self.closest_point_on_geom(nodes[i].co) projected = pn.cross(v.cross(pn)) projected += random_unit_vector()*noise projected.normalize() new = nodes[i].co + projected*stp verts = [nodes[i]] new_vert = bmesh.ops.extrude_vert_indiv( bm, verts=verts)['verts'].pop() new_vert.co = new ## mask out dead sources mask = ones(ns,'bool') for j,ii in sv_map.items(): if all(dvs[ii,j]<=killzone): mask[j] = False self.num_sources -= 1 print('merging:',len(ii),'deleted source:',j) new_verts = [] for i in ii: new = bmesh.ops.extrude_vert_indiv( bm, verts=[nodes[i]])['verts'].pop() new.co = self.sources[j] new_verts.append(new) bmesh.ops.remove_doubles(bm,verts=new_verts,dist=0.01) self.dead_sources.append(self.sources[j]) self.to_mesh() self.sources = self.sources[mask,:] return
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) > 1: 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 bpy.data.objects[second_obj.name].select = False if context.mode == 'EDIT_MESH': print(self) self.report({'INFO'}, "Please select a source and a target object") else: bpy.ops.object.duplicate_move() for i in range(self.set_origin): bpy.ops.object.cubefront_side_minus_z() bpy.context.active_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') 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' }) for i in range(self.set_origin): bpy.ops.tp_ops.cubefront_side_minus_z() 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') 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') bpy.context.scene.objects.active = bpy.data.objects[ second_obj.name] bpy.data.objects[second_obj.name].select = True for i in range(self.set_edit): bpy.context.scene.objects.active = bpy.data.objects[ first_obj.name] bpy.ops.object.mode_set(mode='EDIT') print(self) self.report({'INFO'}, "Editmode") else: print(self) self.report({'INFO'}, "Please select a source and a target object") return {"FINISHED"}