def get_node_transforms(nodes): node_transforms = [None]*len(nodes) # create a collection of absolute translations and rotations # to move and rotate each nodes collision model into place. for node_i in range(len(nodes)): node = nodes[node_i] trans = node.translation rot = node.rotation x, y, z = trans.x, trans.y, trans.z this_rot = quaternion_to_matrix(rot.i, rot.j, rot.k, rot.w) if node.parent_node >= 0: parent = node_transforms[node.parent_node] trans = Matrix((x, y, z)) parent_rot = parent[4] this_rot = parent_rot * this_rot trans = parent_rot * trans x = trans[0][0] + parent[0] y = trans[1][0] + parent[1] z = trans[2][0] + parent[2] else: parent_rot = quaternion_to_matrix(0, 0, 0, 1) node_transforms[node_i] = [x, y, z, parent_rot, this_rot] return node_transforms
def calc_internal_data(self): ''' For each node, this method recalculates the rotation matrix from the quaternion, and the translation to the root bone. ''' HekTag.calc_internal_data(self) nodes = self.data.tagdata.nodes.STEPTREE for node in nodes: rotation = quaternion_to_matrix(*node.rotation) trans = Matrix([node.translation]) * -1 parent = None # add the parents translation to this ones if node.parent_node > -1: trans += Matrix([nodes[node.parent_node].translation_to_root]) # rotate the trans_to_root by this node's rotation trans *= rotation # combine this nodes rotation with its parents rotation if parent is not None: this_trans = node.translation node.distance_from_parent = sqrt(this_trans.x**2 + this_trans.y**2 + this_trans.z**2) parent_rot = Matrix( [parent.rot_jj_kk, parent.rot_kk_ii, parent.rot_ii_jj]) rotation *= parent_rot # apply the changes to the node node.translation_to_root[:] = trans[0][:] node.rot_jj_kk[:] = rotation[0] node.rot_kk_ii[:] = rotation[1] node.rot_ii_jj[:] = rotation[2]
def extract_physics(tagdata, tag_path="", **kw): do_write_jms = kw.get('write_jms', True) filepath = "" if do_write_jms: filepath = os.path.join(kw['out_dir'], os.path.dirname(tag_path), "physics", "physics.jms") if not kw.get('overwrite', True) and os.path.isfile(filepath): return jms_model = JmsModel() child_node_ct = 1 for mp in tagdata.mass_points.STEPTREE: child_node_ct = max(child_node_ct, mp.model_node + 1) fi, fj, fk = mp.forward ui, uj, uk = mp.up si, sj, sk = uj * fk - fj * uk, uk * fi - fk * ui, ui * fj - fi * uj matrix = Matrix(((fi, fj, fk), (si, sj, sk), (ui, uj, uk))) i, j, k, w = matrix_to_quaternion(matrix) # no idea why I have to invert these w = -w if w < 0: i, j, k, w = -i, -j, -k, -w jms_model.markers.append( JmsMarker( mp.name, "physics", -1, mp.model_node, i, j, k, w, mp.position.x * 100, mp.position.y * 100, mp.position.z * 100, mp.radius * 100, )) jms_model.nodes = generate_fake_nodes(child_node_ct) if do_write_jms: write_jms(filepath, jms_model) else: return jms_model
def make_raw_verts_block(node_bsp, node_transforms, node_i): raw_verts = raw_block_def.build() # create the uncompressed vertices bytearray that # all the parts will reuse. This is wasteful, but # i dont care to convert the vertex indices, so w/e uncomp_verts = bytearray(68*len(node_bsp.vertices.STEPTREE)) raw_verts.data = uncomp_verts transform = node_transforms[node_i] dx, dy, dz = transform[0], transform[1], transform[2] rotation = transform[4] pos = 0 for vert in node_bsp.vertices.STEPTREE: # rotate the vertices to match the nodes orientation trans = rotation * Matrix(vert[:3]) # also translate the vertices to match the nodes position pack_into('>14f2h2f', uncomp_verts, pos, trans[0][0]+dx, trans[1][0]+dy, trans[2][0]+dz, 1,0,0,0,1,0,0,0,1,0,0,0,0,1,0) pos += 68 return raw_verts
def compile_physics(phys_tag, jms_model_markers, updating=True): tagdata = phys_tag.data.tagdata mass_points = tagdata.mass_points.STEPTREE if not updating: # making fresh physics tag. use default values tagdata.radius = -1.0 tagdata.moment_scale = 0.3 tagdata.mass = 1.0 tagdata.density = 1.0 tagdata.gravity_scale = 1.0 tagdata.ground_friction = 0.2 tagdata.ground_depth = 0.2 tagdata.ground_damp_fraction = 0.05 tagdata.ground_normal_k1 = 0.7071068 tagdata.ground_normal_k0 = 0.5 tagdata.water_friction = 0.05 tagdata.water_depth = 0.25 tagdata.water_density = 1.0 tagdata.air_friction = 0.001 existing_mp_names = {} for i in range(len(mass_points)): existing_mp_names[mass_points[i].name.lower()] = i mass_points_to_update = {} for marker in jms_model_markers: name = marker.name.lower() if name in existing_mp_names: mass_points_to_update[name] = mass_points[existing_mp_names[name]] del mass_points[:] # update the mass points and/or make new ones for marker in jms_model_markers: rotation = quaternion_to_matrix(marker.rot_i, marker.rot_j, marker.rot_k, marker.rot_w) name = marker.name if name in existing_mp_names: mass_points.append(mass_points_to_update[name]) else: mass_points.append() mp = mass_points[-1] if name not in existing_mp_names: # set default values mp.relative_mass = 1.0 mp.relative_density = 1.0 mp.friction_parallel_scale = 1.0 mp.friction_perpendicular_scale = 1.0 forward = rotation * Matrix(((1, ), (0, ), (0, ))) up = rotation * Matrix(((0, ), (0, ), (1, ))) mp.up[:] = up[0][0], up[1][0], up[2][0] mp.forward[:] = forward[0][0], forward[1][0], forward[2][0] mp.position[:] = marker.pos_x / 100, marker.pos_y / 100, marker.pos_z / 100 mp.name = name mp.radius = marker.radius / 100 mp.model_node = marker.parent phys_tag.calc_internal_data()
def calc_intertia_matrices(self): data = self.data.tagdata scale = data.moment_scale matrices = data.inertia_matrices.STEPTREE com = data.center_of_mass mass_points = data.mass_points.STEPTREE # make sure the matrix array is only 2 long matrices.extend(2 - len(matrices)) del matrices[2:] reg = matrices.regular inv = matrices.inverse reg_yy_zz, reg_zz_xx, reg_xx_yy = reg.yy_zz, reg.zz_xx, reg.xx_yy xx = yy = zz = float('1e-30') # prevent division by 0 neg_zx = neg_xy = neg_yz = 0 # calculate the moments for each mass point and add them up for mp in mass_points: pos = mp.position dist_xx = (com[1] - pos[1])**2 + (com[2] - pos[2])**2 dist_yy = (com[0] - pos[0])**2 + (com[2] - pos[2])**2 dist_zz = (com[0] - pos[0])**2 + (com[1] - pos[1])**2 dist_zx = (com[0] - pos[0])*(com[2] - pos[2]) dist_xy = (com[0] - pos[0])*(com[1] - pos[1]) dist_yz = (com[1] - pos[1])*(com[2] - pos[2]) if mp.radius > 0: radius_term = 4*pow(10, (2*log(mp.radius, 10) - 1)) else: radius_term = 0 xx += (dist_xx + radius_term) * mp.mass yy += (dist_yy + radius_term) * mp.mass zz += (dist_zz + radius_term) * mp.mass neg_zx -= dist_zx * mp.mass neg_xy -= dist_xy * mp.mass neg_yz -= dist_yz * mp.mass xx, yy, zz = xx*scale, yy*scale, zz*scale neg_zx, neg_xy, neg_yz = neg_zx*scale, neg_xy*scale, neg_yz*scale # place the calculated values into the matrix reg_yy_zz[:] = xx, neg_xy, neg_zx reg_zz_xx[:] = neg_xy, yy, neg_yz reg_xx_yy[:] = neg_zx, neg_yz, zz # calculate the inverse inertia matrix regular = Matrix((reg_yy_zz, reg_zz_xx, reg_xx_yy)) try: inverse = regular.inverse except ZeroDivisionError: inverse = Matrix((1, 0, 0), (0, 1, 0), (0, 0, 1)) print("Could not calculate inertia matrix inverse.") # place the inverse matrix into the tag inv.yy_zz[:] = inverse[0][:] inv.zz_xx[:] = inverse[1][:] inv.xx_yy[:] = inverse[2][:] # copy the xx, yy, and zz moments form the matrix into the tag body data.xx_moment = reg_yy_zz[0] data.yy_moment = reg_zz_xx[1] data.zz_moment = reg_xx_yy[2]