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 make_tube(self, mats, verts): edges_out = [] verts_out = [] faces_out = [] vID = 0 nring = len(verts[0]) # end face faces_out.append(list(range(nring))) for i,m in enumerate(mats): for j,v in enumerate(verts[0]): vout = Matrix(m) * Vector(v) verts_out.append(vout.to_tuple()) vID = j + i*nring # rings if j != 0: edges_out.append([vID, vID - 1]) else: edges_out.append([vID, vID + nring-1]) # lines if i != 0: edges_out.append([vID, vID - nring]) # faces if j != 0: faces_out.append([vID, vID - nring, vID - nring - 1, vID-1,]) else: faces_out.append([vID, vID - nring, vID-1, vID + nring-1]) # end face # reversing list fixes face normal direction keeps mesh manifold f = list(range(vID, vID-nring, -1)) faces_out.append(f) return verts_out, edges_out, faces_out
def print_mat(label, matrix, column=4): if isinstance(matrix[0], (float, int)): # buffer用 if len(matrix) == 16: mat = [matrix[:4], matrix[4:8], matrix[8:12], matrix[12:16]] matrix = Matrix(mat) elif len(matrix) == 9: matrix = Matrix([matrix[:3], matrix[3:6], matrix[6:9]]) elif len(matrix) == 4: matrix = Matrix([matrix[:2], matrix[2:4]]) print(label) t2 = 'row{0} [{1:>{5}.{6}f}, {2:>{5}.{6}f}]' t3 = 'row{0} [{1:>{5}.{6}f}, {2:>{5}.{6}f}, {3:>{5}.{6}f}]' t4 = 'row{0} [{1:>{5}.{6}f}, {2:>{5}.{6}f}, {3:>{5}.{6}f}, {4:>{5}.{6}f}]' m = matrix.transposed() for cnt, row in enumerate(m): if len(row) == 2: print(t2.format(cnt, row[0], row[1], 0, 0, column + 3, column)) elif len(row) == 3: print(t3.format(cnt, row[0], row[1], row[2], 0, column + 3, column)) else: print(t4.format(cnt, row[0], row[1], row[2], row[3], column + 3, column))
def copyto(scene, source_obj, pos, xdir, zdir, axes, scale=None): """ copy the source_obj to pos, so its primary axis points in zdir and its secondary axis points in xdir """ copy_obj = source_obj.copy() scene.objects.link(copy_obj) xdir = xdir.normalized() zdir = zdir.normalized() #rotation first z_axis = zdir x_axis = xdir y_axis = z_axis.cross(x_axis) #use axes_dict to assign the axis as chosen in panel A, B, C = axes_dict[axes] rot_mat = Matrix() rot_mat[A].xyz = x_axis rot_mat[B].xyz = y_axis rot_mat[C].xyz = z_axis rot_mat.transpose() #rotate object copy_obj.matrix_world = rot_mat #move object into position copy_obj.location = pos #scale object if scale != None: copy_obj.scale = scale return copy_obj
def get_coconuts_mesh(context, prefs): me = context.blend_data.meshes.new('temp_mesh') bm = bmesh.new() bm.from_mesh(me) mat = Matrix() trunk_length = prefs.palm_stage_length * prefs.palm_stages coconutX = (0.29, -0.29, 0, 0) coconutY = (0, 0, 0.29, -0.29) coconutZ = trunk_length - 0.2 coconuts = get_random(prefs.lp_Tree_Palm_Top_Coconuts_Min, prefs.lp_Tree_Palm_Top_Coconuts_Max) for i in range(0, coconuts): mat.translation = (coconutX[i], coconutY[i], coconutZ) bmesh.ops.create_icosphere( bm, subdivisions=1, diameter=0.15, matrix=mat) bm.to_mesh(me) return me
def get_bone_matrix(armature, bone, relative=True): pose_bone = armature.pose.bones[bone.name] m = Matrix() ### inverted posebone origin matrix m.row[0] = [0, 0, 1, 0] m.row[1] = [1, 0, 0, 0] m.row[2] = [0, 1, 0, 0] m.row[3] = [0, 0, 0, 1] if bone.parent == None: mat_bone_space = m * pose_bone.matrix.copy() else: if relative: mat_bone_space = pose_bone.parent.matrix.inverted() * pose_bone.matrix else: mat_bone_space = m * pose_bone.matrix #### remap matrix loc, rot, scale = mat_bone_space.decompose() if not bone.use_inherit_scale: scale = (m * pose_bone.matrix).decompose()[2] loc_mat = Matrix.Translation(loc) rot_mat = rot.inverted().to_matrix().to_4x4() scale_mat = Matrix() scale_mat[0][0] = scale[1] scale_mat[1][1] = scale[0] mat_bone_space = loc_mat * rot_mat * scale_mat return mat_bone_space
def parseTree(tree, parentName): # print("parsetree") armName = bpy.context.active_object.name armatures.createBone(armName, tree.name, parentName) bpy.ops.roboteditor.select_segment(segment_name=tree.name) # print(tree.name) boneProp = bpy.context.active_bone.RobotEditor m = Matrix() # print(tree.transformations) for i in tree.transformations: # We expect a matrix here! # Todo accept rotation and translations too! if type(i[0]) is list: m = m * Matrix(i) elif len(i) == 3: # TODO pass elif len(i) == 4: # TODO pass else: raise Exception("ParsingError") # print(m) bpy.context.active_bone.RobotEditor.Euler.x.value = m.translation[0] / 1000 bpy.context.active_bone.RobotEditor.Euler.y.value = m.translation[1] / 1000 bpy.context.active_bone.RobotEditor.Euler.z.value = m.translation[2] / 1000 bpy.context.active_bone.RobotEditor.Euler.gamma.value = degrees(m.to_euler().z) bpy.context.active_bone.RobotEditor.Euler.beta.value = degrees(m.to_euler().y) bpy.context.active_bone.RobotEditor.Euler.alpha.value = degrees(m.to_euler().x) if tree.axis_type == 'revolute': bpy.context.active_bone.RobotEditor.jointMode = 'REVOLUTE' # boneProp.theta.value = float(tree.initalValue) bpy.context.active_bone.RobotEditor.theta.max = float(tree.max) bpy.context.active_bone.RobotEditor.theta.min = float(tree.min) else: bpy.context.active_bone.RobotEditor.jointMode = 'PRISMATIC' # boneProp.d.value = float(tree.initialValue) bpy.context.active_bone.RobotEditor.d.max = float(tree.max) bpy.context.active_bone.RobotEditor.d.min = float(tree.min) if tree.axis is not None: for i, axis in enumerate(tree.axis): if axis == -1.0: bpy.context.active_bone.RobotEditor.axis_revert = True tree.axis[i] = 1.0 if tree.axis == [1.0, 0.0, 0.0]: bpy.context.active_bone.RobotEditor.axis = 'X' elif tree.axis == [0.0, 1.0, 0.0]: bpy.context.active_bone.RobotEditor.axis = 'Y' elif tree.axis == [0.0, 0.0, 1.0]: bpy.context.active_bone.RobotEditor.axis = 'Z' # print("parsetree done") for child in tree.children: parseTree(child, tree.name)
def fit_cubicbezier(l_v, l_t): ######################################################### # http://nbviewer.ipython.org/gist/anonymous/5688579 # make the summation functions for A (16 of them) A_fns = [ lambda l_t: sum([2*t**0*(t-1)**6 for t in l_t]), lambda l_t: sum([-6*t**1*(t-1)**5 for t in l_t]), lambda l_t: sum([6*t**2*(t-1)**4 for t in l_t]), lambda l_t: sum([-2*t**3*(t-1)**3 for t in l_t]), lambda l_t: sum([-6*t**1*(t-1)**5 for t in l_t]), lambda l_t: sum([18*t**2*(t-1)**4 for t in l_t]), lambda l_t: sum([-18*t**3*(t-1)**3 for t in l_t]), lambda l_t: sum([6*t**4*(t-1)**2 for t in l_t]), lambda l_t: sum([6*t**2*(t-1)**4 for t in l_t]), lambda l_t: sum([-18*t**3*(t-1)**3 for t in l_t]), lambda l_t: sum([18*t**4*(t-1)**2 for t in l_t]), lambda l_t: sum([-6*t**5*(t-1)**1 for t in l_t]), lambda l_t: sum([-2*t**3*(t-1)**3 for t in l_t]), lambda l_t: sum([6*t**4*(t-1)**2 for t in l_t]), lambda l_t: sum([-6*t**5*(t-1)**1 for t in l_t]), lambda l_t: sum([2*t**6*(t-1)**0 for t in l_t]) ] # make the summation functions for b (4 of them) b_fns = [ lambda l_t, l_v: sum(v * (-2 * (t**0) * ((t-1)**3)) for t, v in zip(l_t, l_v)), lambda l_t, l_v: sum(v * (6 * (t**1) * ((t-1)**2)) for t, v in zip(l_t, l_v)), lambda l_t, l_v: sum(v * (-6 * (t**2) * ((t-1)**1)) for t, v in zip(l_t, l_v)), lambda l_t, l_v: sum(v * (2 * (t**3) * ((t-1)**0)) for t, v in zip(l_t, l_v)), ] # compute the data we will put into matrix A A_values = [fn(l_t) for fn in A_fns] # fill the A matrix with data A_matrix = Matrix(tuple(zip(*[iter(A_values)]*4))) try: A_inv = A_matrix.inverted() except: return (float('inf'), l_v[0], l_v[0], l_v[0], l_v[0]) # compute the data we will put into the b vector b_values = [fn(l_t, l_v) for fn in b_fns] # fill the b vector with data b_vector = Vector(b_values) # solve for the unknowns in vector x v0, v1, v2, v3 = A_inv * b_vector err = compute_cubic_error(v0, v1, v2, v3, l_v, l_t) / len(l_v) return (err, v0, v1, v2, v3)
def matchPoseReverse(pb, src): gmat = src.matrix tail = gmat.col[3] + src.length * gmat.col[1] rmat = Matrix((gmat.col[0], -gmat.col[1], -gmat.col[2], tail)) rmat.transpose() pmat = getPoseMatrix(rmat, pb) pb.matrix_basis = pmat insertRotation(pb, pmat)
def _convertMatrixTo4x4(self, value): matrix = Matrix() matrix[0] = value[0:4] matrix[1] = value[4:8] matrix[2] = value[8:12] matrix[3] = value[12:16] return matrix.transposed()
def test_matrix_to_3x3(self): # mat = # [ 1 2 3 4 ] # [ 2 4 6 8 ] # [ 3 6 9 12 ] # [ 4 8 12 16 ] mat = Matrix(tuple((i, 2 * i, 3 * i, 4 * i) for i in range(1, 5))) mat_correct = Matrix(((1, 2, 3), (2, 4, 6), (3, 6, 9))) self.assertEqual(mat.to_3x3(), mat_correct)
def _parseVertices( self, mesh ): ''' Extract the vertices from a blender mesh ''' transform = Matrix().to_4x4() transform.identity() if not self.export_config.export_rot: transform = self.export_config.global_matrix.to_4x4() * self.matrix_world self.vertices = [transform * v.co for v in mesh.vertices]
def parse_rotation_channel(gltf, node, obj, bone, channel, animation): """Manage rotation animation.""" blender_path = "pose.bones[" + json.dumps(bone.name) + "].rotation_quaternion" group_name = bone.name keys = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].input) values = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].output) bind_rotation = node.blender_bone_matrix.to_quaternion() if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE": # TODO manage tangent? quat_keyframes = [ quaternion_gltf_to_blender(values[idx * 3 + 1]) for idx in range(0, len(keys)) ] else: quat_keyframes = [quaternion_gltf_to_blender(vals) for vals in values] if node.parent is None: final_rots = [ bind_rotation.inverted() @ quat_keyframe for quat_keyframe in quat_keyframes ] else: if not gltf.data.nodes[node.parent].is_joint: parent_mat = Matrix() else: parent_mat = gltf.data.nodes[node.parent].blender_bone_matrix if parent_mat != parent_mat.inverted(): final_rots = [ bind_rotation.rotation_difference( (parent_mat @ quat_keyframe.to_matrix().to_4x4()).to_quaternion() ) for quat_keyframe in quat_keyframes ] else: final_rots = [ bind_rotation.rotation_difference(quat_keyframe) for quat_keyframe in quat_keyframes ] # Manage antipodal quaternions for i in range(1, len(final_rots)): if final_rots[i].dot(final_rots[i-1]) < 0: final_rots[i] = -final_rots[i] BlenderBoneAnim.fill_fcurves( obj.animation_data.action, keys, final_rots, group_name, blender_path, animation.samplers[channel.sampler].interpolation )
def sparse(values): """ constructs a sparse matrix from a list of tuples (col, row, value) """ result = Matrix() result.zero() result[W][W] = 1 for cell in values: result.col[cell[0]][cell[1]] = cell[2] return result
def _transform_mesh_coordinate_system(mesh): # This transformation swaps Y and Z axes, turning coordinate system from # right-handed to left-handed. transformation = Matrix() transformation.zero() transformation[0][0] = 1 transformation[1][2] = 1 transformation[2][1] = 1 transformation[3][3] = 1 mesh.transform(transformation)
def cubic_bezier_fit_value(l_v, l_t): def compute_error(v0,v1,v2,v3,l_v,l_t): return math.sqrt(sum((cubic_bezier_blend_t(v0,v1,v2,v3,t)-v)**2 for v,t in zip(l_v,l_t))) ######################################################### # http://nbviewer.ipython.org/gist/anonymous/5688579 # make the summation functions for A (16 of them) A_fns = [ lambda l_t: sum([ 2*t**0*(t-1)**6 for t in l_t]), lambda l_t: sum([ -6*t**1*(t-1)**5 for t in l_t]), lambda l_t: sum([ 6*t**2*(t-1)**4 for t in l_t]), lambda l_t: sum([ -2*t**3*(t-1)**3 for t in l_t]), lambda l_t: sum([ -6*t**1*(t-1)**5 for t in l_t]), lambda l_t: sum([ 18*t**2*(t-1)**4 for t in l_t]), lambda l_t: sum([-18*t**3*(t-1)**3 for t in l_t]), lambda l_t: sum([ 6*t**4*(t-1)**2 for t in l_t]), lambda l_t: sum([ 6*t**2*(t-1)**4 for t in l_t]), lambda l_t: sum([-18*t**3*(t-1)**3 for t in l_t]), lambda l_t: sum([ 18*t**4*(t-1)**2 for t in l_t]), lambda l_t: sum([ -6*t**5*(t-1)**1 for t in l_t]), lambda l_t: sum([ -2*t**3*(t-1)**3 for t in l_t]), lambda l_t: sum([ 6*t**4*(t-1)**2 for t in l_t]), lambda l_t: sum([ -6*t**5*(t-1)**1 for t in l_t]), lambda l_t: sum([ 2*t**6*(t-1)**0 for t in l_t]) ] # make the summation functions for b (4 of them) b_fns = [ lambda l_t,l_v: sum([-2*v*t**0*(t-1)**3 for t,v in zip(l_t,l_v)]), lambda l_t,l_v: sum([ 6*v*t**1*(t-1)**2 for t,v in zip(l_t,l_v)]), lambda l_t,l_v: sum([-6*v*t**2*(t-1)**1 for t,v in zip(l_t,l_v)]), lambda l_t,l_v: sum([ 2*v*t**3*(t-1)**0 for t,v in zip(l_t,l_v)]) ] # compute the data we will put into matrix A A_values = [fn(l_t) for fn in A_fns] # fill the A matrix with data A_matrix = Matrix(tuple(zip(*[iter(A_values)]*4))) A_inv = A_matrix.inverted() # compute the data we will put into the b vector b_values = [fn(l_t, l_v) for fn in b_fns] # fill the b vector with data b_vector = Vector(b_values) # solve for the unknowns in vector x v0,v1,v2,v3 = A_inv * b_vector err = compute_error(v0,v1,v2,v3,l_v,l_t) / len(l_v) return (err,v0,v1,v2,v3)
def calculate_best_plane(locs): # calculating the center of masss com = Vector() for loc in locs: com += loc com /= len(locs) x, y, z = com # creating the covariance matrix mat = Matrix(((0.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 0.0), )) for loc in locs: mat[0][0] += (loc[0]-x)**2 mat[1][0] += (loc[0]-x)*(loc[1]-y) mat[2][0] += (loc[0]-x)*(loc[2]-z) mat[0][1] += (loc[1]-y)*(loc[0]-x) mat[1][1] += (loc[1]-y)**2 mat[2][1] += (loc[1]-y)*(loc[2]-z) mat[0][2] += (loc[2]-z)*(loc[0]-x) mat[1][2] += (loc[2]-z)*(loc[1]-y) mat[2][2] += (loc[2]-z)**2 # calculating the normal to the plane normal = False try: mat.invert() except: if sum(mat[0]) == 0.0: normal = Vector((1.0, 0.0, 0.0)) elif sum(mat[1]) == 0.0: normal = Vector((0.0, 1.0, 0.0)) elif sum(mat[2]) == 0.0: normal = Vector((0.0, 0.0, 1.0)) if not normal: # warning! this is different from .normalize() itermax = 500 iter = 0 vec = Vector((1.0, 1.0, 1.0)) vec2 = (mat * vec)/(mat * vec).length while vec != vec2 and iter<itermax: iter+=1 vec = vec2 vec2 = mat * vec if vec2.length != 0: vec2 /= vec2.length if vec2.length == 0: vec2 = Vector((1.0, 1.0, 1.0)) normal = vec2 return(com, normal)
def pointInTri2D(v, v1, v2, v3): global dict_matrix key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y # Commented because its slower to do teh bounds check, we should realy cache the bounds info for each face. ''' # BOUNDS CHECK xmin= 1000000 ymin= 1000000 xmax= -1000000 ymax= -1000000 for i in (0,2,4): x= key[i] y= key[i+1] if xmax<x: xmax= x if ymax<y: ymax= y if xmin>x: xmin= x if ymin>y: ymin= y x= v.x y= v.y if x<xmin or x>xmax or y < ymin or y > ymax: return False # Done with bounds check ''' try: mtx = dict_matrix[key] if not mtx: return False except: side1 = v2 - v1 side2 = v3 - v1 nor = side1.cross(side2) mtx = Matrix((side1, side2, nor)) # Zero area 2d tri, even tho we throw away zerop area faces # the projection UV can result in a zero area UV. if not mtx.determinant(): dict_matrix[key] = None return False mtx.invert() dict_matrix[key] = mtx uvw = (v - v1) * mtx return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
def unit_normal(a, b, c): mat_x = Matrix(((1, a[1], a[2]), (1, b[1], b[2]), (1, c[1], c[2]))) mat_y = Matrix(((a[0], 1, a[2]), (b[0], 1, b[2]), (c[0], 1, c[2]))) mat_z = Matrix(((a[0], a[1], 1), (b[0], b[1], 1), (c[0], c[1], 1))) x = Matrix.determinant(mat_x) y = Matrix.determinant(mat_y) z = Matrix.determinant(mat_z) magnitude = (x**2 + y**2 + z**2)**.5 return (x/magnitude, y/magnitude, z/magnitude)
def to_Scene(self): if self.hasOpened: # Load table # #Get the tag objects from the scene by filtering objects with # hexadecimal strings as the first 8 chars of their name tag_objs = {obj.name[:8]:obj for obj in bpy.data.objects if isHex(obj.name[:8])} # # Load placements # #Create a parent object to contain all placement objects for i, p in enumerate(self.placements): mesh = None # the mesh for the object #The placement's index references a table item that has a tag index if p.index >= 0 and p.index < self.n_table_items: # the index must be in the range of the table items palette_tag_idx = self.table_items[p.index].index try: #apply the data if found mesh = tag_objs[str('%08x' % palette_tag_idx).upper()].data except KeyError: print("Could not find tag '%08x' for placement %d" % (palette_tag_idx, i)) object = bpy.data.objects.new("Placement.%03d" % i, mesh) #link the object to the active scene (ususally scene 0) bpy.context.scene.objects.link(object) for j in range(3): object.lock_scale[j] = True #objects in forge can be only moved or rotated # # Apply a matrix to the object to place it correctly in the scene # #Recreate col1 using cross product of column vectors: col0 # and col2 that are perpendicular to one another col2 = p.col2 col0 = p.col0 col1 = col0.cross(col2) pos = p.pos #Construct 3x3 matrix with column vectors for rows mat = Matrix((col0, col1, col2)) mat.transpose() # Matrix is now correct #Increase size from 3x3 to 4x4 to move 3D points # as well as to rotate/scale them like a 3x3 could mat = mat.to_4x4() for i in range(3): mat[i][3] = pos[i] object.matrix_local = mat #object. #Assign 'Custom Properties' to the object with values from the Placement # in order to serialize the placement later pd = vars(p) for key in pd.keys(): object[key] = pd[key] #Assign the item table to the scene in order to serialize later bpy.data.scenes[0]['item_table'] = [l.to_list() for l in self.table_items] #Assign the entire 60k file to the scene bpy.data.scenes[0]['usermap_data'] = struct.unpack("B" * len(self.data), self.data)
def test_matrix_inverse(self): mat = Matrix(((1, 4, 0, -1), (2, -1, 2, -2), (0, 3, 8, 3), (-2, 9, 1, 0))) inv_mat = (1 / 285) * Matrix(((195, -57, 27, -102), (50, -19, 4, 6), (-60, 57, 18, 27), (110, -133, 43, -78))) self.assertEqual(mat.inverted(), inv_mat)
def get_trunk_mesh(context, prefs): me = context.blend_data.meshes.new("temp_mesh") bm = bmesh.new() bm.from_mesh(me) mat = Matrix() tt = prefs.lp_Tree_Type if tt == "lp_Tree_Oak" or tt == "lp_Tree_Pine": segments = get_random(prefs.lp_Tree_Trunk_Segments_Min, prefs.lp_Tree_Trunk_Segments_Max) trunklen = uniform(prefs.lp_Tree_Trunk_Length_Min, prefs.lp_Tree_Trunk_Length_Max) prefs.trunk_depth = 2.0 * trunklen mat.translation = (0, 0, prefs.trunk_depth / 2) trunk_diameter1 = 0.15 * uniform(prefs.lp_Tree_Trunk_Diameter1_Min, prefs.lp_Tree_Trunk_Diameter1_Max) trunk_diameter2 = 0.15 * uniform(prefs.lp_Tree_Trunk_Diameter2_Min, prefs.lp_Tree_Trunk_Diameter2_Max) bmesh.ops.create_cone( bm, cap_ends=False, cap_tris=True, segments=segments, diameter1=trunk_diameter1, diameter2=trunk_diameter2, depth=prefs.trunk_depth, matrix=mat, ) elif tt == "lp_Tree_Palm": prefs.palm_stages = get_random(prefs.lp_Tree_Palm_Trunk_Stages_Min, prefs.lp_Tree_Palm_Trunk_Stages_Max) segments = get_random(prefs.lp_Tree_Palm_Trunk_Segments_Min, prefs.lp_Tree_Palm_Trunk_Segments_Max) prefs.palm_stage_length = uniform( prefs.lp_Tree_Palm_Trunk_Stage_Length_Min, prefs.lp_Tree_Palm_Trunk_Stage_Length_Max ) stage_length = prefs.palm_stage_length diameter1 = uniform(prefs.lp_Tree_Palm_Trunk_Diameter1_Min, prefs.lp_Tree_Palm_Trunk_Diameter1_Max) diameter2 = uniform(prefs.lp_Tree_Palm_Trunk_Diameter2_Min, prefs.lp_Tree_Palm_Trunk_Diameter1_Max) for i in range(0, prefs.palm_stages): scale = 1 mat[0][0], mat[1][1], mat[2][2] = (scale, scale, scale) # e = Euler((0, uniform(0, 0.2), 0), 'XYZ') # mat = mat * e.to_matrix().to_4x4() mat.translation = (0, 0, (stage_length / 2 + i * stage_length)) bmesh.ops.create_cone( bm, cap_ends=True, cap_tris=True, segments=segments, diameter1=diameter1, diameter2=diameter2, depth=stage_length, matrix=mat, ) # mat = Matrix() bm.to_mesh(me) return me
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 mirrorPlane(vertex, matrix): vert = [] a = Matrix(matrix) eul = a.to_euler() normal = Vector((0.0, 0.0, 1.0)) normal.rotate(eul) tras = Matrix.Translation(2*a.to_translation()) for i in vertex: v = Vector(i) r = v.reflect(normal) vert.append((tras*r)[:]) return vert
def rotation_from_matrix(m00, m01, m02, m10, m11, m12, m20, m21, m22): mat = Matrix() mat[0][0] = m00 mat[0][1] = m01 mat[0][2] = m02 mat[1][0] = m10 mat[1][1] = m11 mat[1][2] = m12 mat[2][0] = m20 mat[2][1] = m21 mat[2][2] = m22 return mat.to_quaternion()
def update(self): if 'vertices' in self.inputs and self.inputs['vertices'].links \ and self.inputs['edg_pol'].links \ and self.inputs['cut_matrix'].links: verts_ob = Vector_generate(SvGetSocketAnyType(self,self.inputs['vertices'])) edg_pols_ob = SvGetSocketAnyType(self,self.inputs['edg_pol']) if self.inputs['matrix'].links: matrixs = SvGetSocketAnyType(self,self.inputs['matrix']) else: matrixs = [] for le in verts_ob: matrixs.append(Matrix()) cut_mats = SvGetSocketAnyType(self,self.inputs['cut_matrix']) verts_out = [] edges_out = [] for cut_mat in cut_mats: cut_mat = Matrix(cut_mat) pp = Vector((0.0, 0.0, 0.0)) * cut_mat.transposed() pno = Vector((0.0, 0.0, 1.0)) * cut_mat.to_3x3().transposed() verts_pre_out = [] edges_pre_out = [] for idx_mob, matrix in enumerate(matrixs): idx_vob = min(idx_mob, len(verts_ob)-1) idx_epob = min(idx_mob, len(edg_pols_ob)-1) matrix = Matrix(matrix) x_me = section(verts_ob[idx_vob], edg_pols_ob[idx_epob], matrix, pp, pno, self.fill_check, self.tri) if x_me: verts_pre_out.append(x_me['Verts']) edges_pre_out.append(x_me['Edges']) if verts_pre_out: verts_out.extend(verts_pre_out) edges_out.extend(edges_pre_out) if 'vertices' in self.outputs and self.outputs['vertices'].links: output = Vector_degenerate(verts_out) SvSetSocketAnyType(self,'vertices',output) if 'edges' in self.outputs and self.outputs['edges'].links: SvSetSocketAnyType(self,'edges',edges_out) else: pass
def matrix_inverted_safe(m): try: return m.inverted() except ValueError: pass m = Matrix() m.col[0][0] += 1e-6 m.col[1][1] += 1e-6 m.col[2][2] += 1e-6 m.col[3][3] += 1e-6 try: return m.inverted() except ValueError: return Matrix()
def getWorld(self): t = Vector(self.translation).to_4d() mr = Matrix() for row in range(3): mr[row][0:3] = self.rotation[row] mr.transpose() # = Inverse rotation p = -(mr * t) # Camera position in world coordinates p[3] = 1.0 m = mr.copy() m.col[3] = p # Set translation to camera position return m
def draw_cloud(bm, prefs, translation=(0, 0, 0)): mat = Matrix() mat.translation = translation smin = prefs.lp_Cloud_Scale_Min smax = prefs.lp_Cloud_Scale_Max sx = uniform(smin[0], smax[0]) sy = uniform(smin[1], smax[1]) sz = uniform(smin[2], smax[2]) scale = (sx, sy, sz) mat[0][0], mat[1][1], mat[2][2] = scale[0], scale[1], scale[2] e = Euler((uniform(0, 3.14), uniform(0, 3.14), uniform(0, 3.14)), 'XYZ') mat = mat * e.to_matrix().to_4x4() bmesh.ops.create_icosphere(bm, subdivisions=prefs.lp_Cloud_Subdivisions, diameter=1.0, matrix=mat) return scale
def __getAxisAngleBasedRotation(self, rotate, translate): euler = Euler(rotate) self.logger.debug("Injecting rotation: '%s'", str(euler)) vector_translate = Vector((translate[0], translate[1], translate[2])) # actually the translation is also needed to be passed here rotate_mtx = Matrix.to_4x4(euler.to_matrix()) translate_mtx = Matrix.Translation(vector_translate) cameraMatrix = translate_mtx * rotate_mtx # global matrix rotate (guess it is for world coordinate system rotating) mtx = Matrix.Rotation(-(math.pi / 2.0), 4, 'X') mtx = mtx * cameraMatrix (loc, quat, _) = mtx.decompose() # get the values the way that in x3d exporter does quat = quat.normalized() # some weird stuff axises = list(quat.axis.to_tuple()) angle = quat.angle orientation = self.__getStringRepresentation(axises) + " " + str(angle) translation = self.__getStringRepresentation(loc) return translation, orientation
def execute(self, context): from .model import SelectModel from .rigid_bodies import SelectGeometry, AssignGeometry from .segments import SelectSegment C = bpy.context D = bpy.data model = C.active_object bone_name = C.active_bone.name pose_bone = C.active_object.pose.bones[bone_name] if not C.active_bone.parent: self.report({"ERROR"}, "Does not work for root segments") return {'CANCELLED'} parent_bone = C.active_object.pose.bones[C.active_bone.parent.name] parent_name = parent_bone.name parent_frame = model.matrix_world * parent_bone.matrix bone_world = model.matrix_world * pose_bone.matrix bone_to_parent = bone_world.inverted() * parent_frame parent_to_bone = parent_frame.inverted() * bone_world l = bone_to_parent.translation.length v1 = bone_to_parent.translation max_v1 = max(abs(i) for i in v1) v2 = parent_to_bone.translation max_v2 = max(abs(i) for i in v2) if max_v1 != 0: bpy.ops.curve.primitive_bezier_circle_add(radius=l / 20) print(l) bevel = C.active_object bezier = bpy.ops.curve.primitive_bezier_curve_add() bezier = C.active_object bezier.data.bevel_object = bevel bezier.matrix_world = bone_world print(bezier.matrix_world) # e= C.active_bone.RobotEditor.Euler bpy.ops.object.mode_set(mode="EDIT", toggle=False) a = bezier.data.splines[0].bezier_points[0] b = bezier.data.splines[0].bezier_points[1] a.co = (0, 0, 0) b.co = bone_to_parent.translation print(v1, max_v1) v1 = Vector( [0.1 * i / max_v1 if abs(i) == max_v1 else 0.0 for i in v1]) v2 = Vector( [0.1 * i / max_v2 if abs(i) == max_v2 else 0.0 for i in v2]) # v2 = Vector(v2)#.to_4d() # v2[3]=0.0 a.handle_right = v1 a.handle_left = -1 * v1 print(v1, a.handle_right, a.handle_left) m = Matrix() m.translation = v2 # m[3][3] = 0 # b.co = (bone_to_parent *m).translation print(m, bone_to_parent.inverted() * parent_frame * m) b.handle_left = (bone_to_parent * m).translation m.translation = -1 * v2 b.handle_right = (bone_to_parent * m).translation print(v2, b.handle_right, b.handle_left) # print(a.co,b.co) bpy.ops.object.mode_set(mode="OBJECT", toggle=False) bpy.ops.object.convert(target='MESH') bpy.ops.object.select_all(action='DESELECT') bevel.select = True bpy.ops.object.delete() SelectModel.run(model_name=model.name) SelectGeometry.run(mesh_name=bezier.name) # SelectSegment.run(segment_name=parent_name) AssignGeometry.run() SelectSegment.run(segment_name=bone_name) GenerateMeshFromJoint.run() return {'FINISHED'}
def execute(self, model): # Set the first node as removable model.nodes[0].is_removable = True # First node seems to be off... model.nodes[0].bind_matrix = Matrix() model.nodes[0].bind_matrix[0][0] = -1.0 model.nodes[0].bind_matrix[0][1] = 0.0 model.nodes[0].bind_matrix[0][2] = 0.0 model.nodes[0].bind_matrix[0][3] = 0.0 model.nodes[0].bind_matrix[1][0] = 0.0 model.nodes[0].bind_matrix[1][1] = -1.0 model.nodes[0].bind_matrix[1][2] = 0.0 model.nodes[0].bind_matrix[1][3] = -1.543487 model.nodes[0].bind_matrix[2][0] = 0.0 model.nodes[0].bind_matrix[2][1] = 0.0 model.nodes[0].bind_matrix[2][2] = 1.0 model.nodes[0].bind_matrix[2][3] = 0.0 model.nodes[0].bind_matrix[3][0] = 0.0 model.nodes[0].bind_matrix[3][1] = 0.0 model.nodes[0].bind_matrix[3][2] = 0.0 model.nodes[0].bind_matrix[3][3] = 1.0 # Fix specular power, scale, and lod weight for i in range(len(model.pieces)): model.pieces[i].specular_power = 5.0 model.pieces[i].specular_scale = 1.0 model.pieces[i].lod_weight = 1.0 ''' This function will just create fake parts of the model Because LithTech needs at least something in every section! ''' animation = Animation() animation.name = 'ConvertedFromPS2' animation.extents = Vector((0, 0, 0)) animation.keyframes.append(Animation.Keyframe()) for node_index, (node) in enumerate(model.nodes): transforms = list() for _ in animation.keyframes: transform = Animation.Keyframe.Transform() transform.matrix = node.bind_matrix transforms.append(transform) animation.node_keyframe_transforms.append(transforms) model.animations.append(animation) ''' ChildModels ''' child_model = ChildModel() for _ in model.nodes: # This number seems to have no basis on reality, so therefore it's now a teapot. child_model.build_number = 418 child_model.transforms.append(Animation.Keyframe.Transform()) model.child_models.append(child_model) ''' AnimBindings ''' anim_binding = AnimBinding() anim_binding.name = 'ConvertedFromPS2' anim_binding.origin = Vector((0, 0, 0)) model.anim_bindings.append(anim_binding) # Save me some time renaming stuff.. # PS2 doesn't have names for sockets human_socket_list = [ "RightHand", "Head", "Eyes", "Back", "Nose", "Chin", "LeftHand", "LeftFoot", "RightFoot", "Snowmobile", "Motorcycle" ] print(len(model.sockets), len(human_socket_list)) if len(model.sockets) == len(human_socket_list): for i in range(len(model.sockets)): model.sockets[i].name = human_socket_list[i] print(model.sockets[0]) return model
def get_matrix_local(self): if self._tag == 'OB': return self.bdata.matrix_local.copy() elif self._tag == 'DP': return self._ref.matrix_world.inverted_safe() * self._dupli_matrix else: # 'BO', current pose # PoseBone.matrix is in armature space, bring in back in real local one! par = self.bdata.parent par_mat_inv = self._ref.pose.bones[par.name].matrix.inverted_safe() if par else Matrix() return par_mat_inv * self._ref.pose.bones[self.bdata.name].matrix
def import_motion(reader, context, bonesmap, reported, motions_filter=MOTIONS_FILTER_ALL): bpy_armature = context.armature name = reader.gets() if not motions_filter(name): skip = _skip_motion_rest(reader.getv(), 0) reader.skip(skip) return act = bpy.data.actions.new(name=name) act.use_fake_user = True xray = act.xray reader.getf('II') # range fps, ver = reader.getf('fH') xray.fps = fps if ver < 6: raise AppError('unsupported motions version', log_props(version=ver)) motion = bpy_armature.xray.motions_collection.add() motion.name = act.name if context.use_motion_prefix_name: bpy_armature.xray.use_custom_motion_names = True motion.export_name = name # cut extension filename = context.filename[0:-len(context.filename.split('.')[-1]) - 1] name = '{0}_{1}'.format(filename, name) act.name = name motion.name = name if name != act.name and not context.use_motion_prefix_name: bpy_armature.xray.use_custom_motion_names = True motion.export_name = name xray.flags, xray.bonepart = reader.getf('<BH') xray.speed, xray.accrue, xray.falloff, xray.power = reader.getf('<ffff') for _bone_idx in range(reader.getf('H')[0]): tmpfc = [act.fcurves.new('temp', index=i) for i in range(6)] try: times = {} bname = reader.gets() flags = reader.getf('B')[0] if flags != 0: warn('bone has non-zero flags', bone=bname, flags=flags) for fcurve in tmpfc: behaviors = reader.getf('BB') if (behaviors[0] != 1) or (behaviors[1] != 1): warn('bone has different behaviors', bode=bname, behaviors=behaviors) for _keyframe_idx in range(reader.getf('H')[0]): val = reader.getf('f')[0] time = reader.getf('f')[0] * fps times[time] = True key_frame = fcurve.keyframe_points.insert(time, val) shape = Shape(reader.getf('B')[0]) if shape != Shape.STEPPED: reader.getf('HHH') reader.getf('HHHH') else: key_frame.interpolation = 'CONSTANT' bpy_bone = bpy_armature.data.bones.get(bname, None) if bpy_bone is None: bpy_bone = bonesmap.get(bname.lower(), None) if bpy_bone is None: if bname not in reported: warn('bone is not found', bone=bname) reported.add(bname) continue if bname not in reported: warn('bone\'s reference will be replaced', bone=bname, replacement=bpy_bone.name) reported.add(bname) bname = bpy_bone.name data_path = 'pose.bones["' + bname + '"]' fcs = [ act.fcurves.new(data_path + '.location', index=0, action_group=bname), act.fcurves.new(data_path + '.location', index=1, action_group=bname), act.fcurves.new(data_path + '.location', index=2, action_group=bname), act.fcurves.new(data_path + '.rotation_euler', index=0, action_group=bname), act.fcurves.new(data_path + '.rotation_euler', index=1, action_group=bname), act.fcurves.new(data_path + '.rotation_euler', index=2, action_group=bname) ] xmat = bpy_bone.matrix_local.inverted() real_parent = find_bone_exportable_parent(bpy_bone) if real_parent: xmat = multiply(xmat, real_parent.matrix_local) else: xmat = multiply(xmat, MATRIX_BONE) for time in times: mat = multiply( xmat, Matrix.Translation(( +tmpfc[0].evaluate(time), +tmpfc[1].evaluate(time), -tmpfc[2].evaluate(time), )), Euler(( -tmpfc[4].evaluate(time), -tmpfc[3].evaluate(time), +tmpfc[5].evaluate(time), ), 'ZXY').to_matrix().to_4x4()) trn = mat.to_translation() rot = mat.to_euler('ZXY') for i in range(3): fcs[i + 0].keyframe_points.insert(time, trn[i]) for i in range(3): fcs[i + 3].keyframe_points.insert(time, rot[i]) finally: for fcurve in tmpfc: act.fcurves.remove(fcurve) if ver >= 7: for _bone_idx in range(reader.getf('I')[0]): name = reader.gets_a() reader.skip((4 + 4) * reader.getf('I')[0]) warn('markers are not supported yet', name=name) return act
def __matrix_compose(loc, rot, scale): return (Matrix.Translation(loc) * rot.to_matrix().to_4x4() * Matrix.Scale(scale[0], 4, (1, 0, 0)) * Matrix.Scale(scale[1], 4, (0, 1, 0)) * Matrix.Scale(scale[2], 4, (0, 0, 1)))
FBX_DEFORMER_SHAPE_VERSION = 100 FBX_DEFORMER_SHAPECHANNEL_VERSION = 100 FBX_POSE_BIND_VERSION = 100 FBX_DEFORMER_SKIN_VERSION = 101 FBX_DEFORMER_CLUSTER_VERSION = 100 FBX_MATERIAL_VERSION = 102 FBX_TEXTURE_VERSION = 202 FBX_ANIM_KEY_VERSION = 4008 FBX_NAME_CLASS_SEP = b"\x00\x01" FBX_ANIM_PROPSGROUP_NAME = "d" FBX_KTIME = 46186158000 # This is the number of "ktimes" in one second (yep, precision over the nanosecond...) MAT_CONVERT_LAMP = Matrix.Rotation(math.pi / 2.0, 4, 'X') # Blender is -Z, FBX is -Y. MAT_CONVERT_CAMERA = Matrix.Rotation(math.pi / 2.0, 4, 'Y') # Blender is -Z, FBX is +X. # XXX I can't get this working :( # MAT_CONVERT_BONE = Matrix.Rotation(math.pi / 2.0, 4, 'Z') # Blender is +Y, FBX is -X. MAT_CONVERT_BONE = Matrix() BLENDER_OTHER_OBJECT_TYPES = {'CURVE', 'SURFACE', 'FONT', 'META'} BLENDER_OBJECT_TYPES_MESHLIKE = {'MESH'} | BLENDER_OTHER_OBJECT_TYPES # Lamps. FBX_LIGHT_TYPES = { 'POINT': 0, # Point. 'SUN': 1, # Directional. 'SPOT': 2, # Spot.
("Y", -45.0), ("Y", -22.5), # ("Y", 0.0), 0 rotation, default to x ("Y", 22.5), ("Y", 45.0), ("Z", -45.0), ("Z", -22.5), # ("Z", 0.0), 0 rotation, default to x ("Z", 22.5), ("Z", 45.0), ] # generate all rotation matrices (15x3x3) MAT_ROTATIONS = np.zeros((len(ROTATIONS), 3, 3)) for i, r in enumerate(ROTATIONS): MAT_ROTATIONS[i,:,:] = np.array(Matrix.Rotation(math.radians(r[1]), 3, r[0])) # direction names for minecraft cube face UVs DIRECTIONS = np.array([ "north", "east", "west", "south", "up", "down", ]) # normals for minecraft directions in BLENDER world space # e.g. blender (-1, 0, 0) is minecraft north (0, 0, -1) # shape (f,n) = (6,3) # f = 6: number of cuboid faces to test
import bpy from mathutils import Matrix, Euler, Quaternion from .utils import is_exportable_bone, find_bone_exportable_parent, AppError from .xray_envelope import Behavior, Shape, KF, EPSILON, refine_keys, export_keyframes from .xray_io import PackedWriter, FastBytes as fb from .log import warn, with_context, props as log_props from .version_utils import multiply MATRIX_BONE = Matrix(((1.0, 0.0, 0.0, 0.0), (0.0, 0.0, -1.0, 0.0), (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0))).freeze() MATRIX_BONE_INVERTED = MATRIX_BONE.inverted().freeze() MOTIONS_FILTER_ALL = lambda name: True @with_context('import-motion') def import_motion(reader, context, bonesmap, reported, motions_filter=MOTIONS_FILTER_ALL): bpy_armature = context.armature name = reader.gets() if not motions_filter(name): skip = _skip_motion_rest(reader.getv(), 0) reader.skip(skip) return act = bpy.data.actions.new(name=name) act.use_fake_user = True
def setOrigin(self, origin): terrain = self.terrain # see http://blender.stackexchange.com/questions/35825/changing-object-origin-to-arbitrary-point-without-origin-set offset = origin - terrain.matrix_world.translation terrain.data.transform(Matrix.Translation(-offset)) terrain.matrix_world.translation += offset
def load(self, context, **options): """load a sketchup file""" self.context = context self.reuse_material = options['reuse_material'] self.reuse_group = options['reuse_existing_groups'] self.max_instance = options['max_instance'] self.render_engine = options['render_engine'] self.component_stats = defaultdict(list) self.component_skip = proxy_dict() self.component_depth = proxy_dict() self.group_written = {} ren_res_x = context.scene.render.resolution_x ren_res_y = context.scene.render.resolution_y self.aspect_ratio = ren_res_x / ren_res_y skp_log(f'Importing: {self.filepath}') addon_name = __name__.split('.')[0] self.prefs = context.preferences.addons[addon_name].preferences _time_main = time.time() try: self.skp_model = sketchup.Model.from_file(self.filepath) except Exception as e: skp_log(f'Error reading input file: {self.filepath}') skp_log(e) return {'FINISHED'} if options['import_scene']: for s in self.skp_model.scenes: if s.name == options['import_scene']: skp_log(f"Importing Scene '{s.name}'") self.scene = s self.layers_skip = [l for l in s.layers] for l in s.layers: skp_log(f"SKIP: {l.name}") if not self.layers_skip: skp_log('Could not find scene: {}, importing default.'.format( options['import_scene'])) if not self.layers_skip: self.layers_skip = [ l for l in self.skp_model.layers if not l.visible ] skp_log('Skipping Layers ... ') for l in sorted([l.name for l in self.layers_skip]): skp_log(l) self.skp_components = proxy_dict( self.skp_model.component_definition_as_dict) skp_log(f'Parsed in {(time.time() - _time_main):.4f} sec.') if options['scenes_as_camera']: for s in self.skp_model.scenes: self.write_camera(s.camera, s.name) if options['import_camera']: if self.scene: active_cam = self.write_camera(self.scene.camera, name=self.scene.name) context.scene.camera = active_cam else: active_cam = self.write_camera(self.skp_model.camera) context.scene.camera = active_cam _t1 = time.time() self.write_materials(self.skp_model.materials) skp_log(f'Materials imported in {(time.time() - _t1):.4f} sec.') _t1 = time.time() D = SKP_util() SKP_util.layers_skip = self.layers_skip for c in self.skp_model.component_definitions: self.component_depth[c.name] = D.component_deps(c.entities) skp_log(f'Component depths analyzed in {(time.time() - _t1):.4f} sec.') self.write_duplicateable_groups() if options["dedub_only"]: return {'FINISHED'} self.component_stats = defaultdict(list) self.write_entities(self.skp_model.entities, "Sketchup", Matrix.Identity(4)) for k, _v in self.component_stats.items(): name, mat = k if options['dedub_type'] == "VERTEX": self.instance_group_dupli_vert(name, mat, self.component_stats) else: self.instance_group_dupli_face(name, mat, self.component_stats) skp_log(f'Entities imported in {(time.time() - _t1):.4f} sec.') skp_log('Finished importing in %.4f sec.\n' % (time.time() - _time_main)) return {'FINISHED'}
def instance_group_dupli_face(self, name, default_material, component_stats): def get_orientations(v): orientations = defaultdict(list) for transform in v: _loc, _rot, scale = Matrix(transform).decompose() scale = (scale[0], scale[1], scale[2]) orientations[scale].append(transform) for scale, transforms in orientations.items(): yield scale, transforms for _scale, transforms in get_orientations( component_stats[(name, default_material)]): main_loc, _, real_scale = Matrix(transforms[0]).decompose() verts = [] faces = [] f_count = 0 for c in transforms: l_loc, l_rot, _l_scale = Matrix(c).decompose() mat = Matrix.Translation(l_loc) * l_rot.to_matrix().to_4x4() verts.append( Vector((mat * Vector((-0.05, -0.05, 0, 1.0)))[0:3]) - main_loc) verts.append( Vector((mat * Vector((0.05, -0.05, 0, 1.0)))[0:3]) - main_loc) verts.append( Vector((mat * Vector((0.05, 0.05, 0, 1.0)))[0:3]) - main_loc) verts.append( Vector((mat * Vector((-0.05, 0.05, 0, 1.0)))[0:3]) - main_loc) faces.append( (f_count + 0, f_count + 1, f_count + 2, f_count + 3)) f_count += 4 dme = bpy.data.meshes.new('DUPLI_' + name) dme.vertices.add(len(verts)) dme.vertices.foreach_set("co", unpack_list(verts)) dme.tessfaces.add(f_count / 4) dme.tessfaces.foreach_set("vertices_raw", unpack_face_list(faces)) dme.update(calc_edges=True) # Update mesh with new data dme.validate() dob = bpy.data.objects.new("DUPLI_" + name, dme) dob.dupli_type = 'FACES' dob.location = main_loc #dob.use_dupli_faces_scale = True #dob.dupli_faces_scale = 10 ob = self.instance_object_or_group(name, default_material) ob.scale = real_scale ob.parent = dob self.context.scene.objects.link(ob) self.context.scene.objects.link(dob) skp_log("Complex group {} {} instanced {} times".format( name, default_material, f_count / 4)) return
def deserializePoseVector(vec4): m = Matrix.Identity(4) for i in range(4): m[i][3] = vec4[i] return m
def _get_bone_channels(scs_root_obj, armature, scs_animation, action, export_scale): """Takes armature and action and returns bone channels. bone_channels structure example: [("Bone", [("_TIME", [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]), ("_MATRIX", [])])]""" bone_channels = [] frame_start = scs_animation.anim_start frame_end = scs_animation.anim_end anim_export_step = action.scs_props.anim_export_step total_frames = (frame_end - frame_start) / anim_export_step # armature matrix stores transformation of armature object against scs root # and has to be added to all bones as they only armature space transformations armature_mat = scs_root_obj.matrix_world.inverted() @ armature.matrix_world invalid_data = False # flag to indicate invalid data state curves_per_bone = OrderedDict( ) # store all the curves we are interested in per bone names for bone in armature.data.bones: for fcurve in action.fcurves: # check if curve belongs to bone if '["' + bone.name + '"]' in fcurve.data_path: data_path = fcurve.data_path array_index = fcurve.array_index if data_path.endswith("location"): curve_type = "location" elif data_path.endswith("rotation_euler"): curve_type = "euler_rotation" elif data_path.endswith("rotation_quaternion"): curve_type = "quat_rotation" elif data_path.endswith("scale"): curve_type = "scale" else: curve_type = None # write only recognized curves if curve_type is not None: if bone.name not in curves_per_bone: curves_per_bone[bone.name] = { "location": {}, "euler_rotation": {}, "quat_rotation": {}, "scale": {} } curves_per_bone[ bone.name][curve_type][array_index] = fcurve for bone_name, bone_curves in curves_per_bone.items(): bone = armature.data.bones[bone_name] pose_bone = armature.pose.bones[bone_name] loc_curves = bone_curves["location"] euler_rot_curves = bone_curves["euler_rotation"] quat_rot_curves = bone_curves["quat_rotation"] sca_curves = bone_curves["scale"] bone_rest_mat = armature_mat @ bone.matrix_local if bone.parent: parent_bone_rest_mat = ( Matrix.Scale(export_scale, 4) @ _convert_utils.scs_to_blend_matrix().inverted() @ armature_mat @ bone.parent.matrix_local) else: parent_bone_rest_mat = Matrix() # GO THOUGH FRAMES actual_frame = frame_start timings_stream = [] matrices_stream = [] while actual_frame <= frame_end: mat_loc = Matrix() mat_rot = Matrix() mat_sca = Matrix() # LOCATION MATRIX if len(loc_curves) > 0: location = Vector() for index in range(3): if index in loc_curves: location[index] = loc_curves[index].evaluate( actual_frame) mat_loc = Matrix.Translation(location) # ROTATION MATRIX if len(euler_rot_curves) > 0: rotation = Euler() for index in range(3): if index in euler_rot_curves: rotation[index] = euler_rot_curves[index].evaluate( actual_frame) mat_rot = Euler(rotation, pose_bone.rotation_mode).to_matrix( ).to_4x4() # calc rotation by pose rotation mode elif len(quat_rot_curves) > 0: rotation = Quaternion() for index in range(4): if index in quat_rot_curves: rotation[index] = quat_rot_curves[index].evaluate( actual_frame) mat_rot = rotation.to_matrix().to_4x4() # SCALE MATRIX if len(sca_curves) > 0: scale = Vector((1.0, 1.0, 1.0)) for index in range(3): if index in sca_curves: scale[index] = sca_curves[index].evaluate(actual_frame) if scale[index] < 0: lprint( str("E Negative scale detected on bone %r:\n\t " "(Action: %r, keyframe no.: %s, SCS Animation: %r)." ), (bone_name, action.name, actual_frame, scs_animation.name)) invalid_data = True mat_sca = Matrix() mat_sca[0] = (scale[0], 0, 0, 0) mat_sca[1] = (0, scale[1], 0, 0) mat_sca[2] = (0, 0, scale[2], 0) mat_sca[3] = (0, 0, 0, 1) # BLENDER FRAME MATRIX mat = mat_loc @ mat_rot @ mat_sca # SCALE REMOVAL MATRIX rest_location, rest_rotation, rest_scale = bone_rest_mat.decompose( ) # print(' BONES rest_scale: %s' % str(rest_scale)) rest_scale = rest_scale * export_scale scale_removal_matrix = Matrix() scale_removal_matrix[0] = (1.0 / rest_scale[0], 0, 0, 0) scale_removal_matrix[1] = (0, 1.0 / rest_scale[1], 0, 0) scale_removal_matrix[2] = (0, 0, 1.0 / rest_scale[2], 0) scale_removal_matrix[3] = (0, 0, 0, 1) # SCALE MATRIX scale_matrix = Matrix.Scale(export_scale, 4) # COMPUTE SCS FRAME MATRIX frame_matrix = (parent_bone_rest_mat.inverted() @ _convert_utils.scs_to_blend_matrix().inverted() @ scale_matrix.inverted() @ bone_rest_mat @ mat @ scale_removal_matrix.inverted()) # print(' actual_frame: %s - value: %s' % (actual_frame, frame_matrix)) timings_stream.append( ("__time__", scs_animation.length / total_frames), ) matrices_stream.append(("__matrix__", frame_matrix.transposed()), ) actual_frame += anim_export_step anim_timing = ("_TIME", timings_stream) anim_matrices = ("_MATRIX", matrices_stream) bone_anim = (anim_timing, anim_matrices) bone_data = (bone_name, bone_anim) bone_channels.append(bone_data) # return empty bone channels if data are invalid if invalid_data: return [] return bone_channels
def __gather_children(blender_object, blender_scene, export_settings): children = [] # standard children for child_object in blender_object.children: if child_object.parent_bone: # this is handled further down, # as the object should be a child of the specific bone, # not the Armature object continue node = gather_node(child_object, blender_scene, export_settings) if node is not None: children.append(node) # blender dupli objects if bpy.app.version < (2, 80, 0): if blender_object.dupli_type == 'GROUP' and blender_object.dupli_group: for dupli_object in blender_object.dupli_group.objects: node = gather_node(dupli_object, blender_scene, export_settings) if node is not None: children.append(node) else: if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection: for dupli_object in blender_object.instance_collection.objects: node = gather_node(dupli_object, blender_scene, export_settings) if node is not None: children.append(node) # blender bones if blender_object.type == "ARMATURE": root_joints = [] for blender_bone in blender_object.pose.bones: if not blender_bone.parent: joint = gltf2_blender_gather_joints.gather_joint(blender_bone, export_settings) children.append(joint) root_joints.append(joint) # handle objects directly parented to bones direct_bone_children = [child for child in blender_object.children if child.parent_bone] def find_parent_joint(joints, name): for joint in joints: if joint.name == name: return joint parent_joint = find_parent_joint(joint.children, name) if parent_joint: return parent_joint return None for child in direct_bone_children: # find parent joint parent_joint = find_parent_joint(root_joints, child.parent_bone) if not parent_joint: continue child_node = gather_node(child, None, export_settings) if child_node is None: continue blender_bone = blender_object.pose.bones[parent_joint.name] # fix rotation if export_settings[gltf2_blender_export_keys.YUP]: rot = child_node.rotation if rot is None: rot = [0, 0, 0, 1] rot_quat = Quaternion(rot) axis_basis_change = Matrix( ((1.0, 0.0, 0.0, 0.0), (0.0, 0.0, -1.0, 0.0), (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0))) mat = gltf2_blender_math.multiply(axis_basis_change, child.matrix_basis) mat = gltf2_blender_math.multiply(child.matrix_parent_inverse, mat) _, rot_quat, _ = mat.decompose() child_node.rotation = [rot_quat[1], rot_quat[2], rot_quat[3], rot_quat[0]] # fix translation (in blender bone's tail is the origin for children) trans, _, _ = child.matrix_local.decompose() if trans is None: trans = [0, 0, 0] # bones go down their local y axis bone_tail = [0, blender_bone.length, 0] child_node.translation = [trans[idx] + bone_tail[idx] for idx in range(3)] parent_joint.children.append(child_node) return children
def array_to_matrix4(arr): """Convert a single 16-len tuple into a valid 4D Blender matrix""" # Blender matrix is row major, fbx is col major so transpose on read return Matrix(tuple(zip(*[iter(arr)]*4))).transposed()
def __matrix_compose(loc, rot, scale): return matmul( matmul(Matrix.Translation(loc), rot.to_matrix().to_4x4()), Matrix([(scale[0], 0, 0, 0), (0, scale[1], 0, 0), (0, 0, scale[2], 0), (0, 0, 0, 1)]))
def fbx_object_matrix(self, scene_data, rest=False, local_space=False, global_space=False): """ Generate object transform matrix (*always* in matching *FBX* space!). If local_space is True, returned matrix is *always* in local space. Else if global_space is True, returned matrix is always in world space. If both local_space and global_space are False, returned matrix is in parent space if parent is valid, else in world space. Note local_space has precedence over global_space. If rest is True and object is a Bone, returns matching rest pose transform instead of current pose one. Applies specific rotation to bones, lamps and cameras (conversion Blender -> FBX). """ # Objects which are not bones and do not have any parent are *always* in global space # (unless local_space is True!). is_global = (not local_space and (global_space or not (self._tag in {'DP', 'BO'} or self.has_valid_parent(scene_data.objects)))) # Objects (meshes!) parented to armature are not parented to anything in FBX, hence we need them # in global space, which is their 'virtual' local space... is_global = is_global or self.parented_to_armature # Since we have to apply corrections to some types of object, we always need local Blender space here... matrix = self.matrix_rest_local if rest else self.matrix_local parent = self.parent # Bones, lamps and cameras need to be rotated (in local space!). if self._tag == 'BO': # If we have a bone parent we need to undo the parent correction. if not is_global and scene_data.settings.bone_correction_matrix_inv and parent and parent.is_bone: matrix = scene_data.settings.bone_correction_matrix_inv * matrix # Apply the bone correction. if scene_data.settings.bone_correction_matrix: matrix = matrix * scene_data.settings.bone_correction_matrix elif self.bdata.type == 'LAMP': matrix = matrix * MAT_CONVERT_LAMP elif self.bdata.type == 'CAMERA': matrix = matrix * MAT_CONVERT_CAMERA if self._tag in {'DP', 'OB'} and parent: if parent._tag == 'BO': # In bone parent case, we get transformation in **bone tip** space (sigh). # Have to bring it back into bone root, which is FBX expected value. matrix = Matrix.Translation((0, (parent.bdata.tail - parent.bdata.head).length, 0)) * matrix # Our matrix is in local space, time to bring it in its final desired space. if parent: if is_global: # Move matrix to global Blender space. matrix = (parent.matrix_rest_global if rest else parent.matrix_global) * matrix elif parent.use_bake_space_transform(scene_data): # Blender's and FBX's local space of parent may differ if we use bake_space_transform... # Apply parent's *Blender* local space... matrix = (parent.matrix_rest_local if rest else parent.matrix_local) * matrix # ...and move it back into parent's *FBX* local space. par_mat = parent.fbx_object_matrix(scene_data, rest=rest, local_space=True) matrix = par_mat.inverted_safe() * matrix if self.use_bake_space_transform(scene_data): # If we bake the transforms we need to post-multiply inverse global transform. # This means that the global transform will not apply to children of this transform. matrix = matrix * scene_data.settings.global_matrix_inv if is_global: # In any case, pre-multiply the global matrix to get it in FBX global space! matrix = scene_data.settings.global_matrix * matrix return matrix
def execute(self, context): from .model import SelectModel from .rigid_bodies import SelectGeometry, AssignGeometry from .segments import SelectSegment C = bpy.context D = bpy.data model = C.active_object bone_name = C.active_bone.name axis = C.active_bone.RobotEditor.axis pose_bone = C.active_object.pose.bones[bone_name] parent_bone = pose_bone.parent bone_to_parent = pose_bone.matrix.inverted() * parent_bone.matrix bone_world = model.matrix_world * pose_bone.matrix segment_length = bone_to_parent.translation.length distance_to_children = [ (child.matrix.inverted() * pose_bone.matrix).translation.length for child in pose_bone.children ] self.logger.debug("%s, %s", segment_length, distance_to_children) # if there is no translation to parent, the parent (or its parent) draws the joint if bone_to_parent.translation.length > 0.001: max_length = max(distance_to_children + [segment_length]) # If there is only one children, and its a distance 0, we have a ball joint if len(pose_bone.children ) == 1 and distance_to_children[0] < 0.001: bpy.ops.mesh.primitive_uv_sphere_add(size=segment_length / 15.0) C.active_object.matrix_world = bone_world # if there IS a child, at distance >0 (or more than one child), draw a hinge joint elif len(pose_bone.children): bpy.ops.mesh.primitive_cylinder_add(radius=max_length / 15, depth=max_length / 5) if axis == 'X': m = Euler((0, 0, pi / 4)).to_matrix().to_4x4() elif axis == 'Y': m = Euler((0, 0, pi / 4)).to_matrix().to_4x4() else: m = Matrix() C.active_object.matrix_world = bone_world * m else: bpy.ops.mesh.primitive_cone_add(radius1=segment_length / 10, radius2=segment_length / 10) C.active_object.name = bone_name + '_axis' new_name = C.active_object.name SelectModel.run(model_name=model.name) SelectSegment.run(bone_name) SelectGeometry.run(new_name) AssignGeometry.run() return {'FINISHED'}
def conponent_def_as_group(self, entities, name, parent_tranform, default_material="Material", etype=None, group=None): if etype == EntityType.outer: if (name, default_material) in self.component_skip: return else: skp_log("Write instance definition as group {} {}".format( group.name, default_material)) self.component_skip[(name, default_material)] = True if etype == EntityType.component and ( name, default_material) in self.component_skip: ob = self.instance_object_or_group(name, default_material) ob.matrix_world = parent_tranform self.context.scene.objects.link(ob) ob.layers = 18 * [False] + [True] + [False] group.objects.link(ob) return else: me, alpha = self.write_mesh_data(entities=entities, name=name, default_material=default_material) if me: ob = bpy.data.objects.new(name, me) ob.matrix_world = parent_tranform if alpha: ob.show_transparent = True me.update(calc_edges=True) self.context.scene.objects.link(ob) ob.layers = 18 * [False] + [True] + [False] group.objects.link(ob) for g in entities.groups: if self.layers_skip and g.layer in self.layers_skip: continue self.conponent_def_as_group(g.entities, "G-" + g.name, parent_tranform @ Matrix(g.transform), default_material=inherent_default_mat( g.material, default_material), etype=EntityType.group, group=group) for instance in entities.instances: if self.layers_skip and instance.layer in self.layers_skip: continue cdef = self.skp_components[instance.definition.name] self.conponent_def_as_group( cdef.entities, cdef.name, parent_tranform @ Matrix(instance.transform), default_material=inherent_default_mat(instance.material, default_material), etype=EntityType.component, group=group)
def render(self, context): # needed??? if context.region.id == self.region_id: # just hide the cursor... context.space_data.cursor_location = [0.0, 0.0, 10.0] context.space_data.region_3d.view_matrix = Matrix.Identity(4) w = context.region.width h = context.region.height x1, x2, x3, x4, y1, y2, y3, y4 = self.grid(w, h) bgl.glMatrixMode(bgl.GL_PROJECTION) bgl.glLoadIdentity() bgl.gluOrtho2D(0, w, 0, h) bgl.glMatrixMode(bgl.GL_MODELVIEW) bgl.glLoadIdentity() bgl.glColor3f(*self.COLOR_BOARD) bgl.glLineWidth(2.0) bgl.glBegin(bgl.GL_LINES) for x in (x1, x2, x3, x4): bgl.glVertex3f(x, y1, 0.0) bgl.glVertex3f(x, y4, 0.0) for y in (y1, y2, y3, y4): bgl.glVertex3f(x1, y, 0.0) bgl.glVertex3f(x4, y, 0.0) bgl.glEnd() xs = (x1, x2, x3, x4) ys = (y1, y2, y3, y4) bgl.glLineWidth(8.0) bgl.glBegin(bgl.GL_LINES) for i in range(3): for j in range(3): c = self.board[i][j] xa = xs[i] xb = xs[i + 1] ya = ys[j] yb = ys[j + 1] if c == self.PLAYER: if self.computer_has_x: self.drawO(xa, xb, ya, yb) else: self.drawX(xa, xb, ya, yb) elif c == self.COMPUTER: if self.computer_has_x: self.drawX(xa, xb, ya, yb) else: self.drawO(xa, xb, ya, yb) bgl.glEnd() bgl.glLineWidth(1.0) bgl.glColor3f(*self.COLOR_FONT) xp, yp = self.fontPosition(w, h) blf.position(0, xp, yp, 0) blf.size(0, 50, 72) if self.winner == self.COMPUTER: blf.draw(0, "You loose!") elif self.winner == self.PLAYER: blf.draw(0, "You win!") elif self.round == self.NONE: blf.draw(0, "Draw.")
def rotate(co): """ Set the human orientation in reference to the camera orientation. """ ow = co.owner # get the suffix of the human to reference the right objects suffix = ow.name[-4:] if ow.name[-4] == "." else "" keyboard = co.sensors['All_Keys'] scene = blenderapi.scene() human_pos = co.owner pos = scene.objects['POS_EMPTY' + suffix] active_camera = scene.active_camera # if the human is external, do nothing if human_pos.get( 'External_Robot_Tag') or human_pos['disable_keyboard_control']: return if human_pos['move_cameraFP'] and active_camera.name != ('Human_Camera' + suffix): return keylist = keyboard.events k = [] #initiate a list with all currently pressed keys for key in keylist: if key[1] == blenderapi.input_active(): k.append(key[0]) # add all pressed keys to a list - as ASCII CODES pos.worldPosition = ow.worldPosition # Get active camera scene = blenderapi.scene() active_camera = scene.active_camera if ow['Manipulate']: ow.worldOrientation = pos.worldOrientation # lock camera to head in Manipulation Mode else: if FORWARDS in k and not (LEFT in k or RIGHT in k): if active_camera.name == ("Human_Camera" + suffix): applyrotate(pos.worldOrientation, ow) else: applyrotate(human_pos.worldOrientation, ow) elif LEFT in k and not (FORWARDS in k or BACKWARDS in k): if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi / 2, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi / 2, 3, 'Z'), ow) # turn around 90 deg elif RIGHT in k and not (FORWARDS in k or BACKWARDS in k): if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi * 3 / 2, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi * 3 / 2, 3, 'Z'), ow) # turn around 270 deg elif LEFT in k and FORWARDS in k: if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi / 4, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi / 4, 3, 'Z'), ow) # turn around 45 deg elif RIGHT in k and FORWARDS in k: if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi * 7 / 4, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi * 7 / 4, 3, 'Z'), ow) # turn around 315 deg elif BACKWARDS in k and not (LEFT in k or RIGHT in k): if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi, 3, 'Z'), ow) # turn around 180 deg if in game-mode elif LEFT in k and BACKWARDS in k: if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi * 3 / 4, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi / 4, 3, 'Z'), ow) # turn around 135 deg if in game-mode, else turn 45 deg elif RIGHT in k and BACKWARDS in k: if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi * 5 / 4, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi * 7 / 4, 3, 'Z'), ow)
def import_dXtree(field, lvl=0): tab = ' ' * lvl * 2 if field == []: if show_geninfo: print('%s>> no childs, return False' % (tab)) return False ob = False mat = False is_root = False frames = [] obs = [] parentname = tokens[field[0]]['parent'] if show_geninfo: print('%s>>childs in frame %s :' % (tab, parentname)) for tokenname in field: tokentype = tokens[tokenname]['type'] # frames can contain more than one mesh if tokentype == 'mesh': # object and mesh naming : # if parent frame has several meshes : obname = meshname = mesh token name, # if parent frame has only one mesh : obname = parent frame name, meshname = mesh token name. if parentname: meshcount = 0 for child in getChilds(parentname): if tokens[child]['type'] == 'mesh': meshcount += 1 if meshcount == 2: parentname = tokenname break else: parentname = tokenname ob = getMesh(parentname, tokenname) obs.append(ob) if show_geninfo: print('%smesh : %s' % (tab, tokenname)) # frames contain one matrix (empty or bone) elif tokentype == 'frametransformmatrix': [mat] = readToken(tokenname) if show_geninfo: print('%smatrix : %s' % (tab, tokenname)) # frames can contain 0 or more frames elif tokentype == 'frame': frames.append(tokenname) if show_geninfo: print('%sframe : %s' % (tab, tokenname)) # matrix is used for mesh transform if some mesh(es) exist(s) if ob: is_root = True if mat == False: mat = Matrix() if show_geninfo: print( '%smesh token without matrix, set it to default\n%splease report in bug tracker if you read this !' % (tab, tab)) if parentname == '': mat = mat * global_matrix if len(obs) == 1: ob.matrix_world = mat else: ob = bel.ob.new(parentname, None, naming_method) ob.matrix_world = mat for child in obs: child.parent = ob # matrix only, store it as a list as we don't know if # it's a bone or an empty yet elif mat: ob = [parentname, mat, []] # nothing case ? else: ob = [parentname, Matrix() * global_matrix, []] if show_geninfo: print('%snothing here' % (tab)) childs = [] for tokenname in frames: if show_geninfo: print('%s<Begin %s :' % (tab, tokenname)) # child is either False, empty, object, or a list or undefined name matrices hierarchy child = import_dXtree(getChilds(tokenname), lvl + 1) if child and type(child) != list: is_root = True childs.append([tokenname, child]) if show_geninfo: print('%sEnd %s>' % (tab, tokenname)) if is_root and parentname != '': if show_geninfo: print('%send of tree a this point' % (tab)) if type(ob) == list: mat = ob[1] ob = bel.ob.new(parentname, None, naming_method) ob.matrix_world = mat for tokenname, child in childs: if show_geninfo: print('%sbegin2 %s>' % (tab, tokenname)) # returned a list of object(s) or matrice(s) if child: # current frame is an object or an empty, we parent this frame to it #if eot or (ob and ( type(ob.data) == type(None) or type(ob.data) == bpy.types.Mesh ) ) : if is_root: # this branch is an armature, convert it if type(child) == list: if show_geninfo: print('%sconvert to armature %s' % (tab, tokenname)) child = buildArm(tokenname, child) # parent the obj/empty/arm to current # or apply the global user defined matrix to the object root if parentname != '': child.parent = ob else: child.matrix_world = global_matrix # returned a list of parented matrices. append it in childs list elif type(child[0]) == str: ob[2].append(child) # child is an empty or a mesh, so current frame is an empty, not an armature elif ob and (type(child.data) == type(None) or type(child.data) == bpy.types.Mesh): #print(' child data type: %s'%type(child.data)) child.parent = ob #print('%s parented to %s'%(child.name,ob.name)) # returned False else: if show_geninfo: print('%sreturned %s, nothing' % (tab, child)) #print('>> %s return %s'%(field,ob)) return ob # if ob else False
def get_mats_for_segment(self, context, segment, splinetype): ''' Creates the Matrizies for a spline segment. ''' #printd('Creating transforms for segment', splinetype) mats = [] p1 = segment[0] p2 = segment[1] count = self.leafs_per_segment if self.bvh: count *= 2 ### TRANSLATION ################# if splinetype == 'BEZIER': coordsb = interpolate_bezier(p1.co, p1.handle_right, p2.handle_left, p2.co, count + 1) coords = [ coordsb[i].lerp(coordsb[i + 1], random() * self.loc_random) for i in range(len(coordsb) - 1) ] elif splinetype == 'POLY': if count > 1: positions = [i / count for i in range(count)] else: positions = [0.5] #printd('POLY-segment: Lerp-positions', positions) coords = [ p1.co.lerp(p2.co, positions[i] + (random() * self.loc_random)).to_3d() for i in range(count) ] mat_locs = [Matrix.Translation(co) for co in coords] ### SCALE ################# mat_scales = [ Matrix.Scale(self.scale + (random() * self.scale_random), 4) for i in range(len(mat_locs)) ] ### ROTATION ################# if splinetype == 'BEZIER': directions = [(coordsb[i + 1] - coordsb[i]).normalized() for i in range(len(coordsb) - 1)] elif splinetype == 'POLY': directions = [(p2.co - p1.co).to_3d().normalized()] * len(coords) # COLLISION -> align leaves to surface if self.bvh: # printd('COLLISION') mat_rots = get_collider_aligned_rots(self, context, coords[:int(count / 2)], directions) # NO COLLISION else: vecs_target = directions.copy() # direction + random for y alignment vecs_target = [ vt.lerp(vt + noise.random_unit_vector(), self.rot_random).normalized() for vt in vecs_target ] # up versus random for z alignment vecs_alignz = [ Vector((0, 0, 1)).lerp(noise.random_unit_vector(), self.rot_align_normal_random).normalized() for i in range(len(directions)) ] # return alignz towards the direction vector vecs_alignz = [ va.lerp(d, self.rot_align_normal).normalized() for va, d in zip(vecs_alignz, directions) ] mat_rots = [align(vt, va) for vt, va in zip(vecs_target, vecs_alignz)] #printd('FREE ROTATION Mats:', len(mat_rots)) # COMBINED mats = [l * s * r for l, s, r in zip(mat_locs, mat_scales, mat_rots)] mats = [m for m in mats if not random() > self.leafs_per_segment_random] #printd('MATS: ', len(mats), len(mat_locs), len(mat_scales), len(mat_rots)) return mats
def dataCorrect2(self, destination, obj): if destination: return dataCorrect(destination) return [Matrix() for v in obj]
def import_model(model, options): # utils.delete_all_objects() # This seems to be the rage these days Context = bpy.context Data = bpy.data Ops = bpy.ops # Create our new collection. This will help us later on.. collection = Data.collections.new(model.name) # Add our collection to the scene Context.scene.collection.children.link(collection) # TODO: clear the orphan list for ultra name purity sun = Data.lights.new(name="Sun", type='SUN') sun_object = Data.objects.new("Sun", sun) collection.objects.link(sun_object) # Create the armature armature = bpy.data.armatures.new(model.name) armature_object = bpy.data.objects.new(model.name, armature) armature_object.data.display_type = 'STICK' armature_object.show_in_front = True collection.objects.link(armature_object) armature_object.select_set(True) #armature_object.edit_bones() Context.view_layer.objects.active = armature_object Ops.object.mode_set(mode='EDIT') for node in model.nodes: bone = armature.edit_bones.new(node.name) ''' We can be assured that the parent will always be found because the node list is in the same order as a depth-first traversal of the node hierarchy. ''' bone.parent = armature.edit_bones[ node.parent.name] if node.parent else None bone.tail = (1, 0, 0) bone.transform(node.bind_matrix) if bone.parent is not None: bone.use_connect = bone.parent.tail == bone.head print(bone.use_connect, node.is_removable) ''' Get the forward, left, and up vectors of the bone (used later for determining the roll). ''' ''' Lithtech uses a left-handed coordinate system where: X+: Right Y+: Up Z+: Forward ''' (forward, right, up) = map(lambda x: -x.xyz, node.bind_matrix.col[0:3]) if node.children: ''' This bone has children. To determine the tail position of this bone, we check if all child bone head locations are sufficiently collinear to this direction vector of this bone. ''' is_collinear = True collinearity_epsilon = 0.00001 for child in node.children: child_dir = child.bind_matrix.translation - bone.head dot_product = forward.dot(child_dir.normalized()) if (1.0 - dot_product) > collinearity_epsilon: is_collinear = False break if is_collinear: ''' All child bone head locations are collinear to the directionality of the bone. Set the tail of this bone to the nearest child bone whose head is not concurrent with the head of the current bone (i.e. we shouldn't have zero-length bones!) ''' sorted_children = node.children[:] sorted_children.sort(key=lambda c: ( bone.head - c.bind_matrix.translation).length_squared) bone.tail = sorted_children[0].bind_matrix.translation # TODO: this could still be generating zero-length bones else: ''' One or more child bone head locations are not collinear. Simply project the bone out along it's forward vector a reasonable distance. ''' dot_products = map( lambda x: forward.dot(child.bind_matrix.translation - bone. head), node.children) dot_products = list(filter(lambda x: x > 0, dot_products)) bone_length = max( options.bone_length_min, min(dot_products) if dot_products else options.bone_length_min) bone.tail = bone.head + bone_length * forward else: ''' There is no child node to connect to, so we make a guess as to an appropriate length of the bone (half the length of the previous bone). ''' if bone.parent is not None: bone_length = max(options.bone_length_min, bone.parent.length * 0.5) else: bone_length = options.bone_length_min bone.tail = bone.head + bone_length * forward # TODO: Now calculate the roll of the bone. Ops.object.mode_set(mode='OBJECT') ''' Add sockets as empties with a child-of constraint to the appropriate bone. ''' if options.should_import_sockets: for socket in model.sockets: empty_object = Data.objects.new(socket.name, None) empty_object.location = socket.location empty_object.rotation_quaternion = socket.rotation empty_object.empty_display_type = 'PLAIN_AXES' child_of_constraint = empty_object.constraints.new('CHILD_OF') child_of_constraint.target = armature_object child_of_constraint.subtarget = model.nodes[socket.node_index].name empty_object.parent = armature_object collection.objects.link(empty_object) ''' Determine the amount of LODs to import. ''' lod_import_count = model.lod_count if options.should_import_lods else 1 ''' Create materials. ''' materials = [] for i, piece in enumerate(model.pieces): while len(materials) <= piece.material_index: ''' Create a material for the new piece. ''' material = Data.materials.new(piece.name) material.specular_intensity = piece.specular_power / 100 material.use_nodes = True # TODO: maybe put something in here for the specular scale? materials.append(material) ''' Create texture. ''' # Swapped over to nodes bsdf = material.node_tree.nodes["Principled BSDF"] texImage = material.node_tree.nodes.new('ShaderNodeTexImage') material.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color']) texture = Data.textures.new(piece.name, type='IMAGE') # Note: Texture image names are stored in ModelButes.txt if options.image is not None: texture.image = bpy.data.images.new( piece.name, width=options.image.width, height=options.image.height, alpha=True) # TODO: get real name texture.image.pixels[:] = options.image.pixels[:] texImage.image = texture.image ''' Create a mesh for each piece of each LOD level that we are importing. ''' for lod_index in range(lod_import_count): for piece_index, piece in enumerate(model.pieces): lod = piece.lods[lod_index] ''' Create the object and mesh. ''' mesh_name = piece.name if options.should_import_lods: mesh_name += '.LOD{0!s}'.format(lod_index) mesh = Data.meshes.new(model.name) mesh_object = Data.objects.new(mesh_name, mesh) ''' Add materials to mesh. ''' for material in materials: ''' Create UV map. ''' ''' uv_texture = mesh.uv_textures.new() mesh.materials.append(material) material.texture_slots[0].uv_layer = uv_texture.name ''' uv_texture = mesh.uv_layers.new() mesh.materials.append(material) # material.uv_layers[0].name = uv_texture.name ''' Create a vertex group for each node. ''' for node in model.nodes: mesh_object.vertex_groups.new(name=node.name) # TODO: these need to be reset for each mesh vertex_offset = 0 face_offset = 0 ''' Populate the actual mesh data. ''' bm = bmesh.new() bm.from_mesh(mesh) for vertex in lod.vertices: bm.verts.new(vertex.location) bm.verts.ensure_lookup_table() duplicate_face_indices = [] for face_index, face in enumerate(lod.faces): face = [ bm.verts[vertex_offset + vertex.vertex_index] for vertex in face.vertices ] try: bmface = bm.faces.new(face) except ValueError: ''' This face is a duplicate of another face, which is disallowed by Blender. Mark this face for deletion after iteration. ''' duplicate_face_indices.append(face_index) continue ''' Assign the material index of face based on the piece's material index. ''' bmface.material_index = model.pieces[ piece_index].material_index bmface.smooth = True bm.faces.ensure_lookup_table() ''' Warn the user of the number of duplicate faces detected, if any. ''' if len(duplicate_face_indices) > 0: print('WARNING: {} duplicate faces detected.'.format( len(duplicate_face_indices))) ''' Delete any of the duplicate faces from the mesh. ''' for face_index in reversed(sorted(duplicate_face_indices)): del lod.faces[face_index] vertex_offset += len(lod.vertices) face_offset += len(lod.faces) bm.to_mesh(mesh) ''' Assign texture coordinates. ''' material_face_offsets = [0] * len(mesh.materials) uv_texture = mesh.uv_layers[piece.material_index] # Set the correct UV as active uv_texture.active_render = True for face_index, face in enumerate(lod.faces): material_face_offset = material_face_offsets[ 0] # TODO: is this right? texcoords = [vertex.texcoord for vertex in face.vertices] for i in range(3): uv = texcoords[i][0], 1.0 - texcoords[i][1] uv_texture.data[(material_face_offset + face_index) * 3 + i].uv = uv material_face_offsets[0] += len(lod.faces) ''' Assign normals ''' face_offset = 0 polygons = mesh.polygons[face_offset:face_offset + len(lod.faces)] for face_index, (face, polygon) in enumerate(zip(lod.faces, polygons)): vertices = lod.get_face_vertices(face_index) for vertex, loop_index in zip(vertices, polygon.loop_indices): # TODO: this might not actually set the normal properly n = Vector(vertex.normal) mesh.loops[loop_index].normal = n face_offset += len(lod.faces) mesh.validate(clean_customdata=False) mesh.update(calc_edges=False) # add it to our collection c: collection.objects.link(mesh_object) if Ops.object.mode_set.poll(): Ops.object.mode_set(mode='OBJECT') Ops.object.select_all(action='DESELECT') ''' Add an armature modifier. ''' armature_modifier = mesh_object.modifiers.new(name='Armature', type='ARMATURE') armature_modifier.object = armature_object ''' Assign vertex weighting. ''' vertex_offset = 0 for (vertex_index, vertex) in enumerate(lod.vertices): for weight in vertex.weights: vertex_group_name = model.nodes[weight.node_index].name vertex_group = mesh_object.vertex_groups[vertex_group_name] vertex_group.add([vertex_offset + vertex_index], weight.bias, 'REPLACE') vertex_offset += len(lod.vertices) ''' Parent the mesh to the armature. ''' mesh_object.parent = armature_object ''' Animations ''' if options.should_import_animations: for ob in bpy.context.scene.objects: ob.animation_data_clear() assert (len(armature.bones) == len(model.nodes)) armature_object.animation_data_create() actions = [] index = 0 for animation in model.animations: print("Processing ", animation.name) if (index > 0): break index = index + 1 # Create a new action with the animation name action = bpy.data.actions.new(name=animation.name) # Temp set armature_object.animation_data.action = action # For every keyframe for keyframe_index, keyframe in enumerate(animation.keyframes): # Set keyframe time - Scale it down because it's way too slow for testing Context.scene.frame_set(keyframe.time * 0.01) ''' Recursively apply transformations to a nodes children Notes: It carries everything (nodes, pose_bones..) with it, because I expected it to not be a child of this scope...oops! ''' def recursively_apply_transform(nodes, node_index, pose_bones, parent_matrix): # keyframe_index = 0 node = nodes[node_index] pose_bone = pose_bones[node_index] original_index = node_index #print("[",node_index,"] Applying transform to : ", node.name) # Get the current transform transform = animation.node_keyframe_transforms[node_index][ keyframe_index] # Correct-ish # rotation = Quaternion( (transform.rotation.w, -transform.rotation.z, -transform.rotation.x, transform.rotation.y) ) rotation = Quaternion( (transform.rotation.w, -transform.rotation.z, -transform.rotation.x, transform.rotation.y)) matrix = rotation.to_matrix().to_4x4( ) # transform.rotation.to_matrix().to_4x4() # Apply the translation matrix.Translation(transform.location.xzy) # Use a matrix instead! pose_bone.matrix = parent_matrix @ matrix # Recursively apply our transform to our children! # print("[",node_index,"] Found children count : ", node.child_count) #if (node.child_count): # print("[",original_index,"] Recurse Start --- ",node.name) for index in range(0, node.child_count): node_index = node_index + 1 #print("[",original_index,"] Applying transform to child : ", nodes[node_index].name) node_index = recursively_apply_transform( nodes, node_index, pose_bones, pose_bone.matrix) #if (node.child_count): # print("[",original_index,"] Recurse End --- ",node.name) return node_index ''' Func End ''' recursively_apply_transform(model.nodes, 0, armature_object.pose.bones, Matrix()) # For every bone for bone, node in zip(armature_object.pose.bones, model.nodes): bone.keyframe_insert('location') bone.keyframe_insert('rotation_quaternion') # Add to actions array actions.append(action) # Add our actions to animation data armature_object.animation_data.action = actions[0] # Set our keyframe time to 0 Context.scene.frame_set(0) # TODO: make an option to convert to blender coordinate system # armature_object.rotation_euler.x = math.radians(90) # armature_object.scale.x = -1.0 return {'FINISHED'}
def texdata(self, face, mesh, obj): mat = None width = height = 64 if obj.material_slots: mat = obj.material_slots[face.material_index].material if mat: for node in mat.node_tree.nodes: if node.type == 'TEX_IMAGE': width, height = node.image.size break texstring = mat.name else: texstring = self.option_skip V = [loop.vert.co for loop in face.loops] uv_layer = mesh.loops.layers.uv.active T = [loop[uv_layer].uv for loop in face.loops] # UV handling ported from: https://bitbucket.org/khreathor/obj-2-map if self.option_format == 'Valve': # [ Ux Uy Uz Uoffs ] [ Vx Vy Vz Voffs ] rotation scaleU scaleV dummy = ' [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1\n' height = -height # workaround for flipped v # Set up "2d world" coordinate system with the 01 edge along X world01 = V[1] - V[0] world02 = V[2] - V[0] world01_02Angle = world01.angle(world02) if face.normal.dot(world01.cross(world02)) < 0: world01_02Angle = -world01_02Angle world01_2d = Vector((world01.length, 0.0)) world02_2d = Vector((math.cos(world01_02Angle), math.sin(world01_02Angle))) * world02.length # Get 01 and 02 vectors in UV space and scale them tex01 = T[1] - T[0] tex02 = T[2] - T[0] tex01.x *= width tex02.x *= width tex01.y *= height tex02.y *= height ''' a = world01_2d b = world02_2d p = tex01 q = tex02 [ px ] [ m11 m12 0 ] [ ax ] [ py ] = [ m21 m22 0 ] [ ay ] [ 1 ] [ 0 0 1 ] [ 1 ] [ qx ] [ m11 m12 0 ] [ bx ] [ qy ] = [ m21 m22 0 ] [ by ] [ 1 ] [ 0 0 1 ] [ 1 ] px = ax * m11 + ay * m12 py = ax * m21 + ay * m22 qx = bx * m11 + by * m12 qy = bx * m21 + by * m22 [ px ] [ ax ay 0 0 ] [ m11 ] [ py ] = [ 0 0 ax ay ] [ m12 ] [ qx ] [ bx by 0 0 ] [ m21 ] [ qy ] [ 0 0 bx by ] [ m22 ] ''' # Find an affine transformation to convert # world01_2d and world02_2d to their respective UV coords texCoordsVec = Vector((tex01.x, tex01.y, tex02.x, tex02.y)) world2DMatrix = Matrix(((world01_2d.x, world01_2d.y, 0, 0), (0, 0, world01_2d.x, world01_2d.y), (world02_2d.x, world02_2d.y, 0, 0), (0, 0, world02_2d.x, world02_2d.y))) try: mCoeffs = solve(world2DMatrix, texCoordsVec) except: return texstring + dummy right_2dworld = Vector(mCoeffs[0:2]) up_2dworld = Vector(mCoeffs[2:4]) # These are the final scale values # (avoid division by 0 for degenerate or missing UVs) scalex = 1 / max(0.00001, right_2dworld.length) scaley = 1 / max(0.00001, up_2dworld.length) scale = Vector((scalex, scaley)) # Get the angles of the texture axes. These are in the 2d world # coordinate system, so they're relative to the 01 vector right_2dworld_angle = math.atan2(right_2dworld.y, right_2dworld.x) up_2dworld_angle = math.atan2(up_2dworld.y, up_2dworld.x) # Recreate the texture axes in 3d world coordinates, # using the angles from the 01 edge rt = world01.normalized() up = rt.copy() rt.rotate(Matrix.Rotation(right_2dworld_angle, 3, face.normal)) up.rotate(Matrix.Rotation(up_2dworld_angle, 3, face.normal)) # Now we just need the offsets rt_full = rt.to_4d() up_full = up.to_4d() test_s = V[0].dot(rt) / (width * scale.x) test_t = V[0].dot(up) / (height * scale.y) rt_full[3] = (T[0].x - test_s) * width up_full[3] = (T[0].y - test_t) * height texstring += f" [ {self.printvec(rt_full)} ]"\ f"[ {self.printvec(up_full)} ]"\ f" 0 {self.printvec(scale)}\n" elif self.option_format == 'Quake': # offsetU offsetV rotation scaleU scaleV dummy = ' 0 0 0 1 1\n' # 01 and 02 in 3D space world01 = V[1] - V[0] world02 = V[2] - V[0] # 01 and 02 projected along the closest axis maxn = max(abs(round(crd, 5)) for crd in face.normal) for i in [2, 0, 1]: # axis priority for 45 degree angles if round(abs(face.normal[i]), 5) == maxn: axis = i break world01_2d = Vector((world01[:axis] + world01[(axis + 1):])) world02_2d = Vector((world02[:axis] + world02[(axis + 1):])) # 01 and 02 in UV space (scaled to texture size) tex01 = T[1] - T[0] tex02 = T[2] - T[0] tex01.x *= width tex02.x *= width tex01.y *= height tex02.y *= height # Find affine transformation between 2D and UV texCoordsVec = Vector((tex01.x, tex01.y, tex02.x, tex02.y)) world2DMatrix = Matrix(((world01_2d.x, world01_2d.y, 0, 0), (0, 0, world01_2d.x, world01_2d.y), (world02_2d.x, world02_2d.y, 0, 0), (0, 0, world02_2d.x, world02_2d.y))) try: mCoeffs = solve(world2DMatrix, texCoordsVec) except: return texstring + dummy # Build the transformation matrix and decompose it tformMtx = Matrix(((mCoeffs[0], mCoeffs[1], 0), (mCoeffs[2], mCoeffs[3], 0), (0, 0, 1))) t0 = Vector((T[0].x * width, T[0].y * height)).to_3d() v0 = Vector((V[0][:axis] + V[0][(axis + 1):])).to_3d() offset = t0 - (tformMtx @ v0) rotation = math.degrees(tformMtx.inverted_safe().to_euler().z) scale = tformMtx.inverted_safe().to_scale() # always positive # Compare normals between UV and projection to get the scale sign tn = tex01.to_3d().cross(tex02.to_3d()) vn = world01_2d.to_3d().cross(world02_2d.to_3d()) if tn.dot(vn) < 0: scale.x *= -1 # fudge offset.x += width offset.y *= -1 finvals = [offset.x, offset.y, rotation, scale.x, scale.y] texstring += f" {self.printvec(finvals)}\n" return texstring