def ik_target_mat(name, index, src): mat = Matrix.Translation(Vector(src[0:3])) quat = Quaternion(src[3:7]) mat = mat @ quat.to_matrix().to_4x4() src = src[7:] ctl_mat = Matrix.Translation(Vector(src[0:3])) ctl_quat = Quaternion(src[3:7]) ctl_mat = ctl_mat @ ctl_quat.to_matrix().to_4x4() ctl_scale = Matrix.Scale(src[7], 4, (1, 0, 0)) ctl_scale @= Matrix.Scale(src[8], 4, (0, 1, 0)) ctl_scale @= Matrix.Scale(src[9], 4, (0, 0, 1)) ctl_mat = ctl_mat @ ctl_scale obj = bpy.data.objects[name] s = obj.animation_retarget_state limb = s.ik_limbs[index] mapping = s.get_mapping_for_target(limb.target_bone) src_arma, src_pose = s.get_pose_and_arma_bone('source', mapping.source) dest_arma, dest_pose = s.get_pose_and_arma_bone('target', mapping.target) src_world_mat = loc_mat(s.source.matrix_world).inverted() @ s.source.matrix_world src_ref_mat = s.source.matrix_world @ loc_mat(src_arma.matrix_local) src_rot_mat = rot_mat(s.source.matrix_world) @ rot_mat(src_arma.matrix_local) dest_rest_mat = data_to_matrix4x4(mapping.rest) dest_rot_mat = rot_mat(s.target.matrix_world) @ rot_mat(dest_rest_mat) diff_rot_mat = src_rot_mat.inverted() @ dest_rot_mat mat = src_world_mat @ src_ref_mat.inverted() @ loc_mat(dest_rest_mat) @ ctl_mat @ mat @ diff_rot_mat return mat
def createBones(bones, name): direction = Vector((.01, .01, .01)) origin = (0, 0, 0) bpy.ops.object.add(type='ARMATURE', enter_editmode=True, location=origin) ob = bpy.context.object ob.show_x_ray = True ob.name = name amt = ob.data amt.name = name + 'Amt' amt.show_axes = True bpy.ops.object.mode_set(mode='EDIT') for (idx, name, pidx, pos, rot_) in bones: rot = Quaternion(rot_) bone = amt.edit_bones.new(str(idx)) bone.head = Vector(origin) bone.tail = direction m = Matrix() if pidx != -1: pname = bones[pidx][1] parent = amt.edit_bones[str(pidx)] bone.parent = parent P = parent.matrix R = rot.to_matrix().to_4x4() T = Matrix().Translation(pos) m = P * T * R else: R = rot.to_matrix().to_4x4() T = Matrix().Translation(pos) m = T * R bone.matrix = m bpy.ops.object.mode_set(mode='OBJECT') return ob
def apply_rot(self, frame, bone): if bone is None: return values = self.getValue(frame) for i in range(len(self.Channels)): if isinstance(self.Channels[i], ChannelCachedQuaternion1): cached = self.Channels[i] val = cached.getValue(frame, values) if cached.quatIndex == 0: values = [ val, values[0], values[1], values[2], 0 ] elif cached.quatIndex == 1: values = [ values[0], val, values[1], values[2], 0 ] elif cached.quatIndex == 2: values = [ values[0], values[1], val, values[2], 0 ] elif cached.quatIndex == 3: values = [ values[0], values[1], values[2], val, 0 ] rotation = Quaternion((values[3], values[0], values[1], values[2])) if bone.bone.parent is not None: rotation = bone.bone.convert_local_to_pose(rotation.to_matrix().to_4x4(), bone.bone.matrix.to_4x4(), parent_matrix=bone.bone.parent.matrix_local.to_4x4(), invert=True) else: rotation = bone.bone.convert_local_to_pose(rotation.to_matrix().to_4x4(), bone.bone.matrix.to_4x4(), invert=True) bone.rotation_quaternion = rotation.to_quaternion() bone.keyframe_insert(data_path="rotation_quaternion", frame=frame)
def loadMhpFile(context, filepath): ob = context.object rig = ob.parent scn = context.scene if rig and rig.type == 'ARMATURE': (pname, ext) = os.path.splitext(filepath) mhppath = pname + ".mhp" fp = open(mhppath, "rU") for line in fp: words = line.split() if len(words) < 5: continue elif words[1] == "quat": q = Quaternion((float(words[2]), float(words[3]), float(words[4]), float(words[5]))) mat = q.to_matrix().to_4x4() pb = rig.pose.bones[words[0]] pb.matrix_basis = mat elif words[1] == "gquat": q = Quaternion((float(words[2]), float(words[3]), float(words[4]), float(words[5]))) mat = q.to_matrix().to_4x4() maty = mat[1].copy() matz = mat[2].copy() mat[1] = -matz mat[2] = maty pb = rig.pose.bones[words[0]] pb.matrix_basis = pb.bone.matrix_local.inverted() * mat fp.close() print("Mhp file %s loaded" % mhppath)
def set_bone_transforms(self, bone, node, parent): obj = bpy.data.objects[self.blender_armature_name] delta = Quaternion((0.7071068286895752, 0.7071068286895752, 0.0, 0.0)) mat = Matrix() if parent is None: transform = node.get_transforms() mat = transform * delta.to_matrix().to_4x4() else: if not self.gltf.scene.nodes[ parent].is_joint: # TODO if Node in another scene transform = node.get_transforms() mat = transform * delta.to_matrix().to_4x4() else: transform = node.get_transforms() parent_mat = obj.data.edit_bones[self.gltf.scene.nodes[ parent].blender_bone_name].matrix # Node in another scene mat = (parent_mat.to_quaternion() * delta.inverted() * transform.to_quaternion() * delta).to_matrix().to_4x4() mat = Matrix.Translation(parent_mat.to_translation() + (parent_mat.to_quaternion() * delta.inverted() * transform.to_translation())) * mat #TODO scaling of bones bone.matrix = mat return bone.matrix
def transform_rotation(rotation: Quaternion, transform: Matrix = Matrix.Identity(4), need_rotation_correction: bool = False) -> Quaternion: """Transform rotation.""" rotation.normalize() correction = Quaternion((2**0.5 / 2, -2**0.5 / 2, 0.0, 0.0)) m = rotation.to_matrix().to_4x4() if need_rotation_correction: m @= correction.to_matrix().to_4x4() m = transform @ m return m.to_quaternion()
def calc_pose_mats(iqmodel, iqpose, bone_axis): loc_pose_mat = [None] * len(iqmodel.bones) abs_pose_mat = [None] * len(iqmodel.bones) recalc = False # convert pose to local matrix and compute absolute matrix for n in range(len(iqmodel.bones)): iqbone = iqmodel.bones[n] pose_pos = iqpose[n].translate pose_rot = iqpose[n].rotate pose_scale = iqpose[n].scale local_pos = Vector(pose_pos) local_rot = Quaternion( (pose_rot[3], pose_rot[0], pose_rot[1], pose_rot[2])) local_scale = Vector(pose_scale) mat_pos = Matrix.Translation(local_pos) mat_rot = local_rot.to_matrix().to_4x4() mat_scale = Matrix.Scale(local_scale.x, 3).to_4x4() loc_pose_mat[n] = mat_pos * mat_rot * mat_scale if iqbone.parent >= 0: abs_pose_mat[n] = abs_pose_mat[iqbone.parent] * loc_pose_mat[n] else: abs_pose_mat[n] = loc_pose_mat[n] # Remove negative scaling from bones. # Due to numerical instabilities in blender's matrix <-> head/tail/roll math # this isn't always stable when the bones are in the X axis. If the bones # end up rotated 90 degrees from what they should be, that's the reason. for n in range(len(iqmodel.bones)): if abs_pose_mat[n].is_negative: if not hasattr(iqmodel, 'abs_bind_mat'): print("warning: removing negative scale in bone", iqmodel.bones[n].name) abs_pose_mat[n] = abs_pose_mat[n] * Matrix.Scale(-1, 4) recalc = True # flip bone axis (and recompute local matrix if needed) if bone_axis == 'X': axis_flip = Matrix.Rotation(math.radians(-90), 4, 'Z') abs_pose_mat = [m * axis_flip for m in abs_pose_mat] recalc = True if bone_axis == 'Z': axis_flip = Matrix.Rotation(math.radians(-90), 4, 'X') abs_pose_mat = [m * axis_flip for m in abs_pose_mat] recalc = True if recalc: inv_pose_mat = [m.inverted() for m in abs_pose_mat] for n in range(len(iqmodel.bones)): iqbone = iqmodel.bones[n] if iqbone.parent >= 0: loc_pose_mat[n] = inv_pose_mat[iqbone.parent] * abs_pose_mat[n] else: loc_pose_mat[n] = abs_pose_mat[n] return loc_pose_mat, abs_pose_mat
def get_matrix_world_at_frame(obj, frame_id): rotation_mode = get_rotation_mode_at_frame(obj, frame_id) if rotation_mode == 'AXIS_ANGLE': axis_angle = get_vector4_at_frame(obj, "rotation_axis_angle", frame_id) angle = axis_angle[0] axis = Vector(axis_angle[1], axis_angle[2], axis_angle[3]) rotation_matrix = Matrix.Rotation(angle, 4, axis) elif rotation_mode == 'QUATERNION': rotation_quat = get_vector4_at_frame(obj, "rotation_quaternion", frame_id) quat = Quaternion(rotation_quat) rotation_matrix = quat.to_matrix().to_4x4() else: rotation = get_vector3_at_frame(obj, "rotation_euler", frame_id) e = Euler(rotation, rotation_mode) rotation_matrix = e.to_matrix().to_4x4() location = get_vector3_at_frame(obj, "location", frame_id) location_matrix = Matrix.Translation(location).to_4x4() scale = get_vector3_at_frame(obj, "scale", frame_id) scale_matrix = Matrix.Identity(4) scale_matrix[0][0] = scale[0] scale_matrix[1][1] = scale[1] scale_matrix[2][2] = scale[2] return vcu.element_multiply( vcu.element_multiply(location_matrix, rotation_matrix), scale_matrix)
def transform_rotation( rotation: Quaternion, transform: Matrix = Matrix.Identity(4)) -> Quaternion: """Transform rotation.""" m = rotation.to_matrix().to_4x4() m = multiply(transform, m) return m.to_quaternion()
def build_armature(self): data_block = self.valve_file.get_data_block(block_name='DATA')[0] model_skeleton = data_block.data['m_modelSkeleton'] bone_names = model_skeleton['m_boneName'] bone_positions = model_skeleton['m_bonePosParent'] bone_rotations = model_skeleton['m_boneRotParent'] bone_parents = model_skeleton['m_nParent'] armature_obj = bpy.data.objects.new(self.name + "_ARM", bpy.data.armatures.new(self.name + "_ARM_DATA")) armature_obj.show_in_front = True self.main_collection.objects.link(armature_obj) bpy.ops.object.select_all(action="DESELECT") armature_obj.select_set(True) bpy.context.view_layer.objects.active = armature_obj armature_obj.rotation_euler = Euler([math.radians(180), 0, math.radians(90)]) armature = armature_obj.data bpy.ops.object.mode_set(mode='EDIT') bones = [] for bone_name in bone_names: # print("Creating bone", bone_name) bl_bone = armature.edit_bones.new(name=bone_name) bl_bone.tail = Vector([0, 0, 1]) + bl_bone.head bones.append((bl_bone, bone_name)) for n, bone_name in enumerate(bone_names): bl_bone = armature.edit_bones.get(bone_name) parent_id = bone_parents[n] if parent_id != -1: bl_parent, parent = bones[parent_id] bl_bone.parent = bl_parent bpy.ops.object.mode_set(mode='POSE') for n, (bl_bone, bone_name) in enumerate(bones): pose_bone = armature_obj.pose.bones.get(bone_name) if pose_bone is None: print("Missing", bone_name, 'bone') parent_id = bone_parents[n] bone_pos = bone_positions[n] bone_rot = bone_rotations[n] bone_pos = Vector([bone_pos.y, bone_pos.x, -bone_pos.z]) bone_rot = Quaternion([-bone_rot.w, -bone_rot.y, -bone_rot.x, bone_rot.z]) mat = (Matrix.Translation(bone_pos) @ bone_rot.to_matrix().to_4x4()) pose_bone.matrix_basis.identity() if parent_id != -1: parent_bone = armature_obj.pose.bones.get(bone_names[parent_id]) pose_bone.matrix = parent_bone.matrix @ mat else: pose_bone.matrix = mat bpy.ops.pose.armature_apply() bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_all(action="DESELECT") armature_obj.select_set(True) bpy.context.view_layer.objects.active = armature_obj bpy.ops.object.transform_apply(location=True, rotation=True, scale=False) return armature_obj
def create_armature(model: Model, collection): bpy.ops.object.armature_add(enter_editmode=True) armature_obj: bpy.types.Object = bpy.context.object try: bpy.context.scene.collection.objects.unlink(armature_obj) except: pass collection.objects.link(armature_obj) armature_obj.name = model.name + '_ARM' armature: bpy.types.Armature = armature_obj.data armature.name = model.name + "_ARM_DATA" armature_obj.select_set(True) bpy.context.view_layer.objects.active = armature_obj bpy.ops.object.mode_set(mode='EDIT') armature.edit_bones.remove(armature.edit_bones[0]) for root_bone in model.armature.roots: bone = armature.edit_bones.new(root_bone.name) create_child_bones(root_bone, bone, armature) for bone in model.armature.bones: # type: Bone bl_bone = armature.edit_bones.get(bone.name) bl_bone.tail = Vector([1, 0, 0]) + bl_bone.head pos = Vector(bone.position.values) rot = Quaternion(bone.rotation.values) bl_bone.transform(Matrix.Translation(pos) @ rot.to_matrix().to_4x4()) bpy.ops.object.mode_set(mode='OBJECT') return armature_obj
def rotationMatrix(theta, axis): """ Return the rotation matrix associated with counterclockwise rotation about the given axis by theta radians. """ quat_rot = Quaternion(axis, theta) return np.array(quat_rot.to_matrix())
def getStoredTPose(rig, useSetKeys): for pb in rig.pose.bones: quat = Quaternion(pb.McpQuat) pb.matrix_basis = quat.to_matrix().to_4x4() if useSetKeys: setKeys(pb) updateScene()
def loadTPose(rig, filename): if filename: filepath = os.path.join(os.path.dirname(__file__), filename) filepath = os.path.normpath(filepath) print("Loading %s" % filepath) struct = loadJson(filepath) rig.McpTPoseFile = filename else: return False unit = Matrix() for pb in rig.pose.bones: pb.matrix_basis = unit for name, value in struct: bname = getBoneName(rig, name) try: pb = rig.pose.bones[bname] except KeyError: continue quat = Quaternion(value) pb.matrix_basis = quat.to_matrix().to_4x4() setBoneTPose(pb, quat) rig.McpTPoseLoaded = True rig.McpRestTPose = False return True
def loadTPose(rig, filename): if filename: filepath = os.path.join(os.path.dirname(__file__), filename) filepath = os.path.normpath(filepath) print("Loading %s" % filepath) struct = loadJson(filepath) rig.McpTPoseFile = filename else: return False unit = Matrix() for pb in rig.pose.bones: pb.matrix_basis = unit for name,value in struct: bname = getBoneName(rig, name) try: pb = rig.pose.bones[bname] except KeyError: continue quat = Quaternion(value) pb.matrix_basis = quat.to_matrix().to_4x4() setBoneTPose(pb, quat) rig.McpTPoseLoaded = True rig.McpRestTPose = False return True
def bone_mat(name, bone, src): mat = Matrix.Translation(Vector(src[0:3])) quat = Quaternion(src[3:]) mat = mat @ quat.to_matrix().to_4x4() obj = bpy.data.objects[name] s = obj.animation_retarget_state mapping = s.get_mapping_for_target(bone) rest_mat = data_to_matrix4x4(mapping.rest) offset_mat = data_to_matrix4x4(mapping.offset) src_arma, src_pose = s.get_pose_and_arma_bone('source', mapping.source) dest_arma, dest_pose = s.get_pose_and_arma_bone('target', mapping.target) src_ref_mat = rot_mat(s.source.matrix_world) @ rot_mat(src_arma.matrix_local) dest_ref_mat = rot_mat(s.target.matrix_world) @ rot_mat(rest_mat) diff_mat = src_ref_mat. inverted() @ dest_ref_mat scale = s.source.scale[0] mat.translation *= scale mat = diff_mat.inverted() @ mat @ diff_mat mat = offset_mat @ mat if s.correct_root_pivot and s.root_bone != '' and dest_arma.name == s.root_bone: src_root_mat = s.source.matrix_world @ src_arma.matrix_local dest_root_mat = s.target.matrix_world @ rest_mat src_root_loc = src_root_mat.to_translation() dest_root_loc = dest_root_mat.to_translation() root_delta_mat = Matrix.Translation(Vector((0,0,(dest_root_loc[2] - src_root_loc[2])))) src_rot_matrix = rot_mat(dest_ref_mat @ rot_mat(mat) @ dest_ref_mat.inverted()) applied_delta_mat = loc_mat((dest_ref_mat.inverted() @ src_rot_matrix) @ root_delta_mat) mat = loc_mat(dest_ref_mat.inverted() @ root_delta_mat).inverted() @ applied_delta_mat @ mat return mat
def addBone(bone, armature, obj, parent=None): global bone_mapping bone_name = bone["attributes"][0] bone_mapping.append(bone_name) b_bone = armature.edit_bones.new(bone_name) b_bone.head = (0, 0, 0) b_bone.tail = (0, 0.05, 0) b_bone.use_inherit_rotation = True b_bone.use_local_location = True quad = Quaternion((float(bone["RotationQuaternion"][3]), float(bone["RotationQuaternion"][0]), float(bone["RotationQuaternion"][1]), float(bone["RotationQuaternion"][2]))) # quad = Quaternion(map(float, bone["RotationQuaternion"])) mat = quad.to_matrix().to_4x4() b_bone.matrix = mat position = Vector(map(float, bone["LocalOffset"])) b_bone.translate(position) if parent: b_bone.parent = parent b_bone.matrix = parent.matrix @ b_bone.matrix # add child bones for children in bone["members"]: for child in children["members"]: addBone(child, armature, obj, b_bone)
def execute(self, context): selected = bpy.context.selected_objects obj = selected[-1] surf = bpy.context.scene.objects['surface'] loc = bpy.context.scene.cursor_location bvh = BVHTree.FromObject(surf, bpy.context.scene) loc = surf.matrix_world.inverted() * loc (loc, normal, index, dist) = bvh.find_nearest(loc) if self.use_smooth: normal = smooth_normal(surf, loc, index) loc = surf.matrix_world * loc bpy.ops.object.duplicate() new_obj = bpy.context.selected_objects[-1] (unused, surf_rot, unused) = surf.matrix_world.decompose() (unused, obj_rot, scale) = obj.matrix_world.decompose() normal = surf_rot * normal vec = obj_rot * Vector((0.0, 0.0, 1.0)) q = vec.rotation_difference(normal) q = Quaternion().slerp(q, self.align_with_normal) mat_scale = Matrix() for i in range(3): mat_scale[i][i] = scale[i] new_obj.matrix_world = (Matrix.Translation(loc) * q.to_matrix().to_4x4() * obj_rot.to_matrix().to_4x4() * mat_scale) bpy.context.scene.objects.active = new_obj return {'FINISHED'}
def exportParticles(context, emitter, psys, oct_t): """Exports a particle system for the specified emitter""" octane = context.scene.octane_render export_path = bpath.abspath(octane.path) pset = psys.settings infostr = "Exporting PS '%s' (%s) on emitter '%s'" % (psys.name, pset.type, emitter.name) particles = [p for p in psys.particles] if pset.type == 'HAIR' else [ p for p in psys.particles if p.alive_state == 'ALIVE' ] if pset.render_type == "OBJECT": dupli_ob = pset.dupli_object if dupli_ob is not None and octane.instances_write_dupli: info(infostr + " with %i instances of '%s' objects" % (len(particles), dupli_ob.name)) filepath = "".join([bpath.abspath(octane.path), dupli_ob.name]) info("Writing dupli object to file '%s'" % (filepath + ".obj")) dupli_world = dupli_ob.matrix_world.copy() transl_inv = Matrix.Translation(-dupli_world.translation) dupli_ob.matrix_world = transl_inv * dupli_ob.matrix_world writeDupliObjects(context, [dupli_ob], filepath) dupli_ob.matrix_world = dupli_world # # elif pset.render_type == "GROUP": # duplig = pset.dupli_group # if duplig is not None: # objects = duplig.objects # infostr += " with %i instances from group '%s'" % (len(particles), duplig.name) # info(infostr + " {0}".format([o.name for o in objects])) # # TODO: separate group scatter per object else: warning("Invalid PS visualization type '%s'" % pset.render_type) return if not pset.use_rotation_dupli: warning( "'Use object rotation' should be on. Rotations wont conform to Blender veiwport" ) try: fh = open(export_path + psys.name + ".csv", "w") for p in particles: #if pset.type == 'HAIR' or not p.alive_state == 'DEAD': if (pset.type == "HAIR"): loc = Matrix.Translation(p.hair_keys[0].co) scale = Matrix.Scale(p.size, 4) * Matrix.Scale( pset.hair_length, 4) else: loc = Matrix.Translation(p.location) scale = Matrix.Scale(p.size, 4) rot = Quaternion.to_matrix(p.rotation).to_4x4() t = loc * rot * scale t = emitter.matrix_world * t if pset.type == "HAIR" else t t = oct_t[0] * t * oct_t[1] writeTransform(t, fh) fh.close() except IOError as err: msg = "IOError during file handling '{0}'".format(err) error(msg) raise ExportException(msg)
def CreateRigHandle(self, handleName, pos: SVector = None, rot: Quaternion = None, group=None, posControl=False, rotControl=False): obj = self.obj self.edit_mode() bone = obj.data.edit_bones.new(name=handleName) bone.tail = (Vector([0, 0, 1]) * 0.1) + bone.head if pos is not None: bone.matrix = bone.matrix @ obj.matrix_world.inverted( ) @ Matrix.Translation(pos.v) if rot is not None: bone.matrix = bone.matrix @ obj.matrix_world.inverted( ) @ rot.to_matrix().to_4x4() if group is not None: bone.bone_group = self._get_bone_group(group) dag = Dag(bone) self.pose_mode() bone = get_bone(obj, dag) if not posControl: bone.lock_location = [True, True, True] if not rotControl: bone.lock_rotation_w = True bone.lock_rotation[0] = [True, True, True] return dag
def getActionRotation(action, bone, frame=1): rot = Quaternion() rot.identity() if bone.rotation_mode != 'QUATERNION': rot = Euler(Vector(), bone.rotation_mode) for i in range(0, 3): fc = getCurve(action, bone, 'rotation_euler', index = i) if fc != None: rot[i] = fc.evaluate(frame) return rot.to_matrix() else: for i in range(0, 4): fc = getCurve(action, bone, 'rotation_quaternion', index = i) if fc != None: rot[i] = fc.evaluate(frame) return rot.to_matrix() return rot.to_matrix()
def transform_rotation( rotation: Quaternion, transform: Matrix = Matrix.Identity(4)) -> Quaternion: """Transform rotation.""" rotation.normalize() m = rotation.to_matrix().to_4x4() m = transform @ m return m.to_quaternion()
def transform_to_matrix(pos: Vector, rot: Quaternion, scale: Vector) -> Matrix: scale_matrix = Matrix.Diagonal(scale) scale_matrix.resize_4x4() rot_matrix = rot.to_matrix() rot_matrix.resize_4x4() pos_matrix = Matrix.Translation(pos.xyz) pos_matrix.resize_4x4() return pos_matrix @ rot_matrix @ scale_matrix
def calc_pose_mats(iqmodel, iqpose, bone_axis): loc_pose_mat = [None] * len(iqmodel.bones) abs_pose_mat = [None] * len(iqmodel.bones) recalc = False # convert pose to local matrix and compute absolute matrix for n in range(len(iqmodel.bones)): iqbone = iqmodel.bones[n] pose_pos = iqpose[n].translate pose_rot = iqpose[n].rotate pose_scale = iqpose[n].scale local_pos = Vector(pose_pos) local_rot = Quaternion((pose_rot[3], pose_rot[0], pose_rot[1], pose_rot[2])) local_scale = Vector(pose_scale) mat_pos = Matrix.Translation(local_pos) mat_rot = local_rot.to_matrix().to_4x4() mat_scale = Matrix.Scale(local_scale.x, 3).to_4x4() loc_pose_mat[n] = mat_pos * mat_rot * mat_scale if iqbone.parent >= 0: abs_pose_mat[n] = abs_pose_mat[iqbone.parent] * loc_pose_mat[n] else: abs_pose_mat[n] = loc_pose_mat[n] # Remove negative scaling from bones. # Due to numerical instabilities in blender's matrix <-> head/tail/roll math # this isn't always stable when the bones are in the X axis. If the bones # end up rotated 90 degrees from what they should be, that's the reason. for n in range(len(iqmodel.bones)): if abs_pose_mat[n].is_negative: if not hasattr(iqmodel, 'abs_bind_mat'): print("warning: removing negative scale in bone", iqmodel.bones[n].name) abs_pose_mat[n] = abs_pose_mat[n] * Matrix.Scale(-1, 4) recalc = True # flip bone axis (and recompute local matrix if needed) if bone_axis == 'X': axis_flip = Matrix.Rotation(math.radians(-90), 4, 'Z') abs_pose_mat = [m * axis_flip for m in abs_pose_mat] recalc = True if bone_axis == 'Z': axis_flip = Matrix.Rotation(math.radians(-90), 4, 'X') abs_pose_mat = [m * axis_flip for m in abs_pose_mat] recalc = True if recalc: inv_pose_mat = [m.inverted() for m in abs_pose_mat] for n in range(len(iqmodel.bones)): iqbone = iqmodel.bones[n] if iqbone.parent >= 0: loc_pose_mat[n] = inv_pose_mat[iqbone.parent] * abs_pose_mat[n] else: loc_pose_mat[n] = abs_pose_mat[n] return loc_pose_mat, abs_pose_mat
def get_trajectory(self, pose_cw): trans_cw = pose_cw[:, :3] quat_cw = pose_cw[:, 3:] pose_w = [] for i, _ in enumerate(pose_cw): q = Quaternion(quat_cw[i]) rot = np.array(q.to_matrix()) pose_w.append(rot @ trans_cw[i]) return np.array(pose_w)
def change_to_scs_quaternion_coordinates(rot): """Transposes quaternion rotation from Blender to SCS game engine. :param rot: Blender quaternion (or four floats) :type rot: Quaternion | list | tuple :return: Transposed quaternion rotation :rtype: Quaternion """ quat = Quaternion((rot[0], rot[1], rot[2], rot[3])) return (scs_to_blend_matrix().inverted() * quat.to_matrix().to_4x4() * scs_to_blend_matrix()).to_quaternion()
def transform_location(location: Vector, transform: Matrix = Matrix.Identity(4), need_rotation_correction: bool = False) -> Vector: """Transform location.""" correction = Quaternion((2**0.5 / 2, -2**0.5 / 2, 0.0, 0.0)) m = Matrix.Translation(location) if need_rotation_correction: m @= correction.to_matrix().to_4x4() m = transform @ m return m.to_translation()
def loadJsonVerts(filepath): from .load_json import loadJson objects = {} rigs = {} struct = loadJson(filepath) if ("application" not in struct.keys() or struct["application"] != "export_basic_data"): msg = ("The file\n" + filepath + " \n" + "does not contain basic data") raise DazError(msg) for figure in struct["figures"]: if "num verts" in figure.keys() and figure["num verts"] == 0: continue name = figure["name"] if name not in objects.keys(): objects[name] = [] if "vertices" in figure.keys(): verts = [d2b(vec) for vec in figure["vertices"]] objects[name].append(verts) if "bones" not in figure.keys(): continue locations = {} transforms = {} if name not in rigs.keys(): rigs[name] = [] rigs[name].append((locations, transforms)) for bone in figure["bones"]: head = Vector(bone["center_point"]) tail = Vector(bone["end_point"]) vec = tail - head if "ws_transform" in bone.keys(): ws = bone["ws_transform"] rmat = Matrix([ws[0:3], ws[3:6], ws[6:9]]) head = Vector(ws[9:12]) tail = head + Mult2(vec, rmat) else: head = Vector(bone["ws_pos"]) x, y, z, w = bone["ws_rot"] quat = Quaternion((w, x, y, z)) rmat = quat.to_matrix().to_3x3() ws = bone["ws_scale"] smat = Matrix([ws[0:3], ws[3:6], ws[6:9]]) tail = head + Mult3(vec, smat, rmat) rmat = Mult2(smat, rmat) locations[bone["name"]] = (head, tail) rmat = rmat.to_4x4() rmat.col[3][0:3] = theSettings.scale * head transforms[bone["name"]] = (rmat, head, rmat.to_euler(), (1, 1, 1)) return objects, rigs
def exportParticles(context, emitter, psys, oct_t): """Exports a particle system for the specified emitter""" octane = context.scene.octane_render export_path = bpath.abspath(octane.path) pset = psys.settings infostr = "Exporting PS '%s' (%s) on emitter '%s'" % (psys.name, pset.type, emitter.name) particles = [p for p in psys.particles] if pset.type == 'HAIR' else [p for p in psys.particles if p.alive_state == 'ALIVE'] if pset.render_type == "OBJECT": dupli_ob = pset.dupli_object if dupli_ob is not None and octane.instances_write_dupli: info(infostr + " with %i instances of '%s' objects" % (len(particles), dupli_ob.name)) filepath = "".join([bpath.abspath(octane.path), dupli_ob.name]) info("Writing dupli object to file '%s'" % (filepath + ".obj")) dupli_world = dupli_ob.matrix_world.copy() transl_inv = Matrix.Translation(-dupli_world.translation) dupli_ob.matrix_world = transl_inv * dupli_ob.matrix_world writeDupliObjects(context, [dupli_ob], filepath) dupli_ob.matrix_world = dupli_world # # elif pset.render_type == "GROUP": # duplig = pset.dupli_group # if duplig is not None: # objects = duplig.objects # infostr += " with %i instances from group '%s'" % (len(particles), duplig.name) # info(infostr + " {0}".format([o.name for o in objects])) # # TODO: separate group scatter per object else: warning("Invalid PS visualization type '%s'" % pset.render_type) return if not pset.use_rotation_dupli: warning("'Use object rotation' should be on. Rotations wont conform to Blender veiwport") try: fh = open(export_path + psys.name + ".csv", "w") for p in particles: #if pset.type == 'HAIR' or not p.alive_state == 'DEAD': if (pset.type == "HAIR"): loc = Matrix.Translation(p.hair_keys[0].co) scale = Matrix.Scale(p.size, 4) * Matrix.Scale(pset.hair_length, 4) else: loc = Matrix.Translation(p.location) scale = Matrix.Scale(p.size, 4) rot = Quaternion.to_matrix(p.rotation).to_4x4() t = loc * rot * scale t = emitter.matrix_world * t if pset.type == "HAIR" else t t = oct_t[0] * t * oct_t[1] writeTransform(t, fh) fh.close() except IOError as err: msg = "IOError during file handling '{0}'".format(err) error(msg) raise ExportException(msg)
class Transform(object): def __init__(self): self.location = Vector() self.rotation = Quaternion((1, 0, 0, 0)) @property def matrix(self): return Matrix.Translation( self.location) * self.rotation.to_matrix().to_4x4() @matrix.setter def matrix(self, m): self.location, self.rotation, _ = m.decompose()
def pose_bone(root_bone: Bone, armature): bl_bone = armature.pose.bones.get(root_bone.name) pos = Vector(root_bone.position.values) rot = Quaternion(root_bone.rotation.values) print(f'Posing "{root_bone.name}" POS:{pos} ROT:{rot}') mat = Matrix.Translation(pos) @ rot.to_matrix().to_4x4() bl_bone.matrix_basis.identity() if bl_bone.parent: bl_bone.matrix = bl_bone.parent.matrix @ mat else: bl_bone.matrix = mat for child in root_bone.childs: pose_bone(child, armature)
class Transform(object): world_scale = 0.1 world_scale_reciprocal = 10.0 # := 1/world_scale def __init__(self): self.translation = Vector((0, 0, 0)) self.rotation = Quaternion() self.scale = 1 def read(self, file): v = read_vector4(file) q = read_vector4(file) scale = read_vector4(file) self.translation = Vector(v.xyz) * self.world_scale self.rotation = Quaternion(q.wxyz) self.scale = scale.z def write(self, file): v = self.translation * self.world_scale_reciprocal v = (v.x, v.y, v.z, 0) q = self.rotation q = (q.x, q.y, q.z, q.w) scale = (self.scale, self.scale, self.scale, 0) write_vector4_raw(file, v) write_vector4_raw(file, q) write_vector4_raw(file, scale) def __mul__(self, other): t = Transform() v = Vector(other.translation) # dup v.rotate(self.rotation) t.translation = self.translation + v * self.scale t.rotation = self.rotation * other.rotation t.scale = self.scale * other.scale return t def to_matrix(self): m_rotation = self.rotation.to_matrix().to_4x4() # 3x3 to 4x4 m_scale = Matrix.Scale(self.scale, 4) m = m_rotation * m_scale m.translation = self.translation return m def copy(self): t = Transform() t.translation = self.translation.copy() t.rotation = self.rotation.copy() t.scale = self.scale return t
def vec_roll_to_mat3_normalized(nor, roll): THETA_SAFE = 1.0e-5 # theta above this value are always safe to use THETA_CRITICAL = 1.0e-9 # above this is safe under certain conditions assert nor.magnitude - 1.0 < 0.01 x = nor.x y = nor.y z = nor.z theta = 1.0 + y theta_alt = x * x + z * z # When theta is close to zero (nor is aligned close to negative Y Axis), # we have to check we do have non-null X/Z components as well. # Also, due to float precision errors, nor can be (0.0, -0.99999994, 0.0) which results # in theta being close to zero. This will cause problems when theta is used as divisor. bMatrix = Matrix().to_3x3() if theta > THETA_SAFE or ((x | z) and theta > THETA_CRITICAL): # nor is *not* aligned to negative Y-axis (0,-1,0). # We got these values for free... so be happy with it... ;) bMatrix[0][1] = -x bMatrix[1][0] = x bMatrix[1][1] = y bMatrix[1][2] = z bMatrix[2][1] = -z if theta > THETA_SAFE: # nor differs significantly from negative Y axis (0,-1,0): apply the general case. */ bMatrix[0][0] = 1 - x * x / theta bMatrix[2][2] = 1 - z * z / theta bMatrix[2][0] = bMatrix[0][2] = -x * z / theta else: # nor is close to negative Y axis (0,-1,0): apply the special case. */ bMatrix[0][0] = (x + z) * (x - z) / -theta_alt bMatrix[2][2] = -bMatrix[0][0] bMatrix[2][0] = bMatrix[0][2] = 2.0 * x * z / theta_alt else: # nor is very close to negative Y axis (0,-1,0): use simple symmetry by Z axis. */ bMatrix.identity() bMatrix[0][0] = bMatrix[1][1] = -1.0 # Make Roll matrix */ quat = Quaternion(nor, roll) rMatrix = quat.to_matrix() # Combine and output result */ return rMatrix @ bMatrix
def get_ground_align_matrix(self, norm, pos, asset): scale = bpy.data.objects[asset.ref_object_name].scale.to_tuple() m = Matrix() m[0][0], m[1][1], m[2][2] = scale scale = m surface_rotation = Vector( (0, 0, 1)).rotation_difference(norm).normalized() object_rotation = Quaternion().slerp(surface_rotation, asset.surface_affect) rot = object_rotation.to_matrix().normalized() trans = rot.to_4x4() trans.translation = (pos + Vector((0, 0, asset.z_offset))) trans *= Matrix.Rotation(asset.start_rot_z, 4, 'Z') return trans * scale
def get_3x4_RT_matrix(location, rotation): ''' :param location: :param rotation: :return: RT_matrix ''' rot = Quaternion(rotation) T = Vector(location) R = rot.to_matrix() # put into 3x4 matrix RT = Matrix((R[0][:] + (T[0], ), R[1][:] + (T[1], ), R[2][:] + (T[2], ))) return RT
def setTPose(context): rig = context.object scn = context.scene if not rig.McpHasTPose: print(("%s has no defined T-pose" % rig)) quat = Quaternion((1,0,0,0)) mat = quat.to_matrix().to_4x4() for pb in rig.pose.bones: try: qw = pb["McpRestW"] except KeyError: continue pb.matrix_basis = mat print("Set T-pose")
def clearTPose(context): rig = context.object scn = context.scene if not rig.McpHasTPose: print(("%s has no defined T-pose" % rig)) for pb in rig.pose.bones: try: qw = pb["McpRestW"] qx = pb["McpRestX"] qy = pb["McpRestY"] qz = pb["McpRestZ"] except KeyError: continue quat = Quaternion((qw,qx,qy,qz)) pb.matrix_basis = quat.to_matrix().to_4x4() print("Cleared T-pose")
def bake_path_offsets(context, cu_path, ob, action, specials): """ bake path offsets into an action """ channels = get_bone_channels(action) channels = topmost_level(channels, ob, specials) limits = (int(action.frame_range[0]), 2 + int(action.frame_range[1])) values = evaluate_curves(channels, limits) zero_offset = get_path_offset(context, cu_path, ob, 0).copy() for bone, groups in channels.items(): for data_path, curves in groups.items(): data = [(cu.data_path, cu.array_index, cu.group.name) for cu in curves] while curves: cu = curves.pop(-1) action.fcurves.remove(cu) for datum in data: cu = action.fcurves.new(datum[0], datum[1], datum[2]) curves.append(cu) for frame in range(limits[0], limits[1]): context.scene.frame_set(frame) current_offset = ob.matrix_world print(ob.name, current_offset.to_translation() , zero_offset.to_translation()) for bone, groups in channels.items(): for transforms in 'location', 'rotation_quaternion': if 'location' in groups: old_loc = values[bone]['location'][frame - limits[0]] else: old_loc = Vector((0,0,0)) if 'rotation_quaternion' in groups: old_rot = Quaternion(values[bone]['rotation_quaternion'][frame - limits[0]]) else: old_rot = Quaternion((1, 0, 0, 0)) old_trans = Matrix.Translation(old_loc).to_4x4() * old_rot.to_matrix().to_4x4() rest_mat = ob.data.bones[bone].matrix_local old_trans_world = current_offset * rest_mat * old_trans new_trans =\ rest_mat.inverted() * zero_offset.inverted() * old_trans_world new_loc, new_rot, sca = new_trans.decompose() for group, curves in groups.items(): for array_index, curve in enumerate(curves): if curve.data_path.endswith('location'): insert_keyframe_curve( curve, frame, new_loc[array_index], 'LINEAR') else: insert_keyframe_curve( curve, frame, new_rot[array_index], 'LINEAR')
def createTPose(context): rig = context.object scn = context.scene if rig.McpHasTPose: setTPose(context) return filepath = os.path.join(os.path.dirname(__file__), "t_pose.json") struct = loadJson(filepath) for name,value in struct: pb = rig.pose.bones[name] quat = Quaternion(value) pb.matrix_basis = quat.to_matrix().to_4x4() rest = quat.inverted() pb["McpRestW"] = rest.w pb["McpRestX"] = rest.x pb["McpRestY"] = rest.y pb["McpRestZ"] = rest.z children = [] for ob in scn.objects: if ob.type != 'MESH': continue for mod in ob.modifiers: if (mod.type == 'ARMATURE' and mod.object == rig): children.append((ob, mod.name)) scn.objects.active = ob bpy.ops.object.modifier_apply(apply_as='SHAPE', modifier=mod.name) ob.data.shape_keys.key_blocks[mod.name].value = 1 scn.objects.active = rig bpy.ops.pose.armature_apply() for ob,name in children: scn.objects.active = ob mod = ob.modifiers.new(name, 'ARMATURE') mod.object = rig mod.use_vertex_groups = True bpy.ops.object.modifier_move_up(modifier=name) setShapeKey(ob, name, 1.0) scn.objects.active = rig rig.McpHasTPose = True print("Created T-pose")
def getmatrix(self): # Rotating / panning / zooming 3D view is handled here. # Get matrix. if self.selobj.rotation_mode == 'AXIS_ANGLE': # when roataion mode is axisangle angle, x, y, z = self.selobj.rotation_axis_angle self.matrix = Matrix.Rotation(-angle, 4, Vector((x, y, z))) elif self.selobj.rotation_mode == 'QUATERNION': # when rotation on object is quaternion w, x, y, z = self.selobj.rotation_quaternion x = -x y = -y z = -z quat = Quaternion([w, x, y, z]) self.matrix = quat.to_matrix() self.matrix.resize_4x4() else: # when rotation of object is euler ax, ay, az = self.selobj.rotation_euler mat_rotX = Matrix.Rotation(-ax, 4, 'X') mat_rotY = Matrix.Rotation(-ay, 4, 'Y') mat_rotZ = Matrix.Rotation(-az, 4, 'Z') if self.selobj.rotation_mode == 'XYZ': self.matrix = mat_rotX * mat_rotY * mat_rotZ elif self.selobj.rotation_mode == 'XZY': self.matrix = mat_rotX * mat_rotZ * mat_rotY elif self.selobj.rotation_mode == 'YXZ': self.matrix = mat_rotY * mat_rotX * mat_rotZ elif self.selobj.rotation_mode == 'YZX': self.matrix = mat_rotY * mat_rotZ * mat_rotX elif self.selobj.rotation_mode == 'ZXY': self.matrix = mat_rotZ * mat_rotX * mat_rotY elif self.selobj.rotation_mode == 'ZYX': self.matrix = mat_rotZ * mat_rotY * mat_rotX # handle object scaling sx, sy, sz = self.selobj.scale mat_scX = Matrix.Scale(sx, 4, Vector([1, 0, 0])) mat_scY = Matrix.Scale(sy, 4, Vector([0, 1, 0])) mat_scZ = Matrix.Scale(sz, 4, Vector([0, 0, 1])) self.matrix = mat_scX * mat_scY * mat_scZ * self.matrix
def importSkeletonPiece(skel, tree, piece, parentName, depth=0): bone = skel.edit_bones.new(piece.name) track = piece.trackRef.Reference trans = Vector(track.translation()) rot = Quaternion(track.rotation()) mTrans = Matrix.Translation(trans) mRot = rot.to_matrix() mRot.resize_4x4() if parentName: parent = skel.edit_bones[parentName] bone.parent = parent bone.head = parent.tail bone.use_connect = False m = parent.matrix.copy() else: bone.head = (0, 0, 0) m = Matrix() m = m * mTrans m = m * mRot bone.tail = transform(m, bone.head) # recursively import the children bones for childID in piece.children: importSkeletonPiece(skel, tree, tree[childID], piece.name, depth+1)
def drawBone2(p1, p2, radiuses, material): length = dist(p1,p2) print('length :',length) v = Vector(diffv(p1, p2)) up = Vector((0,0,1)) if v!=-up: rot = up.rotation_difference(v) else: rot = Quaternion((1,0,0),math.pi) s1 = drawEllipsoid((0,0,-0.5*length),radiuses,material) s2 = drawEllipsoid((0,0,0.5*length),radiuses,material) c1 = drawCylinder(zero,radiuses,length,materials.blue) s1.select = True s2.select = True c1.select = True #bpy.ops.transform.translate(value=(0,0,length/2)) #bpy.ops.object.editmode_toggle() bpy.ops.transform.rotate(value=rot.angle, axis=rot.axis) #bpy.ops.object.editmode_toggle() #bpy.ops.transform.translate(value=Vector((0,0,-0.5*length))*rot.to_matrix()) rot.normalize(); bpy.ops.transform.translate(value=Vector((0,0,0.5*length))*rot.to_matrix()) bpy.ops.transform.translate(value=p1) return (s1,s2,c1)
def loadMhpFile(context, filepath): ob = context.object if ob.type == "ARMATURE": rig = ob else: rig = ob.parent unit = Matrix() for pb in rig.pose.bones: pb.matrix_basis = unit scn = context.scene if rig and rig.type == "ARMATURE": (pname, ext) = os.path.splitext(filepath) mhppath = pname + ".mhp" fp = open(mhppath, "rU") for line in fp: words = line.split() if len(words) < 4: continue try: pb = rig.pose.bones[words[0]] except KeyError: continue if isMuscleBone(pb): pass elif words[1] == "quat": q = Quaternion((float(words[2]), float(words[3]), float(words[4]), float(words[5]))) mat = q.to_matrix().to_4x4() pb.matrix_basis = mat elif words[1] == "gquat": q = Quaternion((float(words[2]), float(words[3]), float(words[4]), float(words[5]))) mat = q.to_matrix().to_4x4() maty = mat[1].copy() matz = mat[2].copy() mat[1] = -matz mat[2] = maty pb.matrix_basis = pb.bone.matrix_local.inverted() * mat elif words[1] == "matrix": rows = [] n = 2 for i in range(4): rows.append((float(words[n]), float(words[n + 1]), float(words[n + 2]), float(words[n + 3]))) n += 4 mat = Matrix(rows) if pb.parent: pb.matrix_basis = mat else: maty = mat[1].copy() matz = mat[2].copy() mat[1] = -matz mat[2] = maty pb.matrix_basis = pb.bone.matrix_local.inverted() * mat elif words[1] == "scale": pass else: raise MHError("Unknown line in mcp file:\n%s" % line) fp.close() print("Mhp file %s loaded" % mhppath)
def getStoredBonePose(pb): try: quat = Quaternion((pb.McpQuatW, pb.McpQuatX, pb.McpQuatY, pb.McpQuatZ)) except KeyError: quat = Quaternion() return quat.to_matrix().to_4x4()
def recursive_cook(buffer, obj, version, path_hasher, parent_name): buffer += struct.pack('>4s', obj.retro_widget_type[6:].encode()) buffer += obj.name.encode() + b'\0' buffer += parent_name.encode() + b'\0' buffer += struct.pack('>bbbbffffI', False, obj.retro_widget_default_visible, obj.retro_widget_default_active, obj.retro_widget_cull_faces, obj.retro_widget_color[0], obj.retro_widget_color[1], obj.retro_widget_color[2], obj.retro_widget_color[3], model_draw_flags_e[obj.retro_widget_model_draw_flags]) angle = Quaternion((1.0, 0.0, 0.0), 0) if obj.retro_widget_type == 'RETRO_CAMR': angle = Quaternion((1.0, 0.0, 0.0), math.radians(-90.0)) aspect = bpy.context.scene.render.resolution_x / bpy.context.scene.render.resolution_y if obj.data.type == 'PERSP': if aspect > 1.0: fov = math.degrees(math.atan(math.tan(obj.data.angle / 2.0) / aspect)) * 2.0 else: fov = math.degrees(obj.data.angle) buffer += struct.pack('>Iffff', 0, fov, aspect, obj.data.clip_start, obj.data.clip_end) elif obj.data.type == 'ORTHO': ortho_half = obj.data.ortho_scale / 2.0 buffer += struct.pack('>Iffffff', 1, -ortho_half, ortho_half, ortho_half / aspect, -ortho_half / aspect, obj.data.clip_start, obj.data.clip_end) elif obj.retro_widget_type == 'RETRO_MODL': if len(obj.children) == 0: raise RuntimeException('Model Widget must have a child model object') model_obj = obj.children[0] if model_obj.type != 'MESH': raise RuntimeException('Model Widget must have a child MESH') if not model_obj.data.library: raise RuntimeException('Model Widget must have a linked library MESH') path = bpy.path.abspath(model_obj.data.library.filepath) path_hash = path_hasher.hashpath32(path) buffer += struct.pack('>III', path_hash, 0, obj.retro_model_light_mask) elif obj.retro_widget_type == 'RETRO_PANE': buffer += struct.pack('>fffff', obj.retro_pane_dimensions[0], obj.retro_pane_dimensions[1], obj.retro_pane_scale_center[0], obj.retro_pane_scale_center[1], obj.retro_pane_scale_center[2]) elif obj.retro_widget_type == 'RETRO_TXPN': path_hash = path_hasher.hashpath32(obj.retro_textpane_font_path) buffer += struct.pack('>fffffIbbIIffffffffff', obj.retro_pane_dimensions[0], obj.retro_pane_dimensions[1], obj.retro_pane_scale_center[0], obj.retro_pane_scale_center[1], obj.retro_pane_scale_center[2], path_hash, obj.retro_textpane_word_wrap, obj.retro_textpane_horizontal, hjustifications[obj.retro_textpane_hjustification], vjustifications[obj.retro_textpane_vjustification], obj.retro_textpane_fill_color[0], obj.retro_textpane_fill_color[1], obj.retro_textpane_fill_color[2], obj.retro_textpane_fill_color[3], obj.retro_textpane_outline_color[0], obj.retro_textpane_outline_color[1], obj.retro_textpane_outline_color[2], obj.retro_textpane_outline_color[3], obj.retro_textpane_block_extent[0], obj.retro_textpane_block_extent[1]) if version >= 1: path_hash = path_hasher.hashpath32(obj.retro_textpane_jp_font_path) buffer += struct.pack('>III', path_hash, obj.retro_textpane_jp_font_scale[0], obj.retro_textpane_jp_font_scale[1]) elif obj.retro_widget_type == 'RETRO_TBGP': buffer += struct.pack('>HHIHHbbffbfHHHH', obj.retro_tablegroup_elem_count, 0, 0, obj.retro_tablegroup_elem_default, 0, obj.retro_tablegroup_wraparound, False, 0.0, 0.0, False, 0.0, 0, 0, 0, 0) elif obj.retro_widget_type == 'RETRO_GRUP': buffer += struct.pack('>Hb', obj.retro_group_default_worker, False) elif obj.retro_widget_type == 'RETRO_SLGP': buffer += struct.pack('>ffff', obj.retro_slider_min, obj.retro_slider_max, obj.retro_slider_default, obj.retro_slider_increment) elif obj.retro_widget_type == 'RETRO_ENRG': path_hash = path_hasher.hashpath32(obj.retro_energybar_texture_path) buffer += struct.pack('>I', path_hash) elif obj.retro_widget_type == 'RETRO_METR': buffer += struct.pack('>bbII', False, obj.retro_meter_no_round_up, obj.retro_meter_max_capacity, obj.retro_meter_worker_count) elif obj.retro_widget_type == 'RETRO_LITE': angle = Quaternion((1.0, 0.0, 0.0), math.radians(-90.0)) type_enum = 0 constant = 1.0 linear = 0.0 quadratic = 0.0 cutoff = 0.0 if obj.data.type == 'POINT': type_enum = 4 elif obj.data.type == 'HEMI': type_enum = 2 elif obj.data.type == 'SPOT': type_enum = 0 cutoff = obj.data.spot_size if obj.data.type == 'POINT' or obj.data.type == 'SPOT': constant = obj.data.constant_coefficient linear = obj.data.linear_coefficient quadratic = obj.data.quadratic_coefficient buffer += struct.pack('>IffffffI', type_enum, constant, linear, quadratic, obj.data.retro_light_angle_constant, obj.data.retro_light_angle_linear, obj.data.retro_light_angle_quadratic, obj.data.retro_light_index) if obj.data.type == 'SPOT': buffer += struct.pack('>f', cutoff) elif obj.retro_widget_type == 'RETRO_IMGP': if obj.type != 'MESH': raise RuntimeException('Imagepane Widget must be a MESH') if len(obj.data.loops) < 4: raise RuntimeException('Imagepane Widget must be a MESH with 4 verts') if len(obj.data.uv_layers) < 1: raise RuntimeException('Imagepane Widget must ba a MESH with a UV layer') path_hash = 0xffffffff if len(obj.data.materials): material = obj.data.materials[0] if len(material.texture_slots) and material.texture_slots[0]: tex_slot = material.texture_slots[0] if tex_slot.texture.type == 'IMAGE' and tex_slot.texture.image: image = tex_slot.texture.image path = bpy.path.abspath(image.filepath) path_hash = path_hasher.hashpath32(path) buffer += struct.pack('>IIII', path_hash, 0, 0, 4) for i in range(4): vi = obj.data.loops[i].vertex_index co = obj.data.vertices[vi].co buffer += struct.pack('>fff', co[0], co[1], co[2]) buffer += struct.pack('>I', 4) for i in range(4): co = obj.data.uv_layers[0].data[i].uv buffer += struct.pack('>ff', co[0], co[1]) if obj.retro_widget_is_worker: buffer += struct.pack('>bH', True, obj.retro_widget_worker_id) else: buffer += struct.pack('>b', False) angMtx = angle.to_matrix() * obj.matrix_local.to_3x3() buffer += struct.pack('>fffffffffffffffIH', obj.matrix_local[0][3], obj.matrix_local[1][3], obj.matrix_local[2][3], angMtx[0][0], angMtx[0][1], angMtx[0][2], angMtx[1][0], angMtx[1][1], angMtx[1][2], angMtx[2][0], angMtx[2][1], angMtx[2][2], 0.0, 0.0, 0.0, 0, 0) ch_list = [] for ch in obj.children: ch_list.append((ch.pass_index, ch.name)) for s_pair in sorted(ch_list): ch = bpy.data.objects[s_pair[1]] if ch.retro_widget_type != 'RETRO_NONE': recursive_cook(buffer, ch, version, path_hasher, obj.name)
def transform_rotation(rotation: Quaternion, transform: Matrix = Matrix.Identity(4)) -> Quaternion: """Transform rotation.""" rotation.normalize() m = rotation.to_matrix().to_4x4() m = multiply(transform, m) return m.to_quaternion()
class SelProject(bpy.types.Operator): bl_idname = "mesh.selproject" bl_label = "SelProject" bl_description = "Use object projection as selection tool" bl_options = {'REGISTER', 'UNDO'} def invoke(self, context, event): global started started = True self.area = context.area self.area.header_text_set(text="SelProject : Enter to confirm - ESC to exit") self.init_selproject(context) context.window_manager.modal_handler_add(self) self._handle = bpy.types.SpaceView3D.draw_handler_add(self.redraw, (), 'WINDOW', 'POST_PIXEL') return {'RUNNING_MODAL'} def modal(self, context, event): global started if event.type in {'RET', 'NUMPAD_ENTER'}: self.area.header_text_set() if self.obhide != None: bpy.ops.object.select_all(action = 'DESELECT') self.obF.select = True bpy.context.scene.objects.active = self.obF bpy.ops.object.delete() self.obhide.hide = False bpy.ops.object.select_all(action = 'DESELECT') self.empt.select = True bpy.context.scene.objects.active = self.empt bpy.ops.object.delete() self.obT.select = True bpy.context.scene.objects.active = self.obT started = False for v in self.vsellist: v.select = True for e in self.esellist: e.select = True for f in self.fsellist: f.select = True self.obF.location = self.originobF self.obT.location = self.originobT self.bmT.select_flush(1) self.bmT.to_mesh(self.meT) self.meT.update() self.bmF.free() self.bmT.free() bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') bpy.ops.object.editmode_toggle() return {'FINISHED'} elif event.type == 'ESC': self.area.header_text_set() if self.obhide != None: bpy.ops.object.select_all(action = 'DESELECT') self.obF.select = True bpy.context.scene.objects.active = self.obF bpy.ops.object.delete() self.obhide.hide = False bpy.ops.object.select_all(action = 'DESELECT') self.empt.select = True bpy.context.scene.objects.active = self.empt bpy.ops.object.delete() started = False self.obF.location = self.originobF self.obT.location = self.originobT self.bmF.free() self.bmT.free() for obj in self.oldobjlist: obj.select = True self.scn.objects.active = self.oldobj bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') if self.oldmode == 'EDIT': bpy.ops.object.editmode_toggle() return {'CANCELLED'} elif event.type in {'LEFTMOUSE', 'MIDDLEMOUSE', 'RIGHTMOUSE', 'WHEELDOWNMOUSE', 'WHEELUPMOUSE', 'G', 'S', 'R', 'X', 'Y', 'Z', 'MOUSEMOVE'}: context.region.tag_redraw() return {'PASS_THROUGH'} return {'RUNNING_MODAL'} def getmatrix(self, selobj): # Rotating / panning / zooming 3D view is handled here. # Creates a matrix. if selobj.rotation_mode == 'AXIS_ANGLE': # object rotation_quaternionmode axisangle ang, x, y, z = selobj.rotation_axis_angle matrix = Matrix.Rotation(-ang, 4, Vector((x, y, z))) elif selobj.rotation_mode == 'QUATERNION': # object rotation_quaternionmode euler w, x, y, z = selobj.rotation_quaternion x = -x y = -y z = -z self.quat = Quaternion([w, x, y, z]) matrix = self.quat.to_matrix() matrix.resize_4x4() else: # object rotation_quaternionmode euler ax, ay, az = selobj.rotation_euler mat_rotX = Matrix.Rotation(-ax, 4, 'X') mat_rotY = Matrix.Rotation(-ay, 4, 'Y') mat_rotZ = Matrix.Rotation(-az, 4, 'Z') if selobj.rotation_mode == 'XYZ': matrix = mat_rotX * mat_rotY * mat_rotZ elif selobj.rotation_mode == 'XZY': matrix = mat_rotX * mat_rotZ * mat_rotY elif selobj.rotation_mode == 'YXZ': matrix = mat_rotY * mat_rotX * mat_rotZ elif selobj.rotation_mode == 'YZX': matrix = mat_rotY * mat_rotZ * mat_rotX elif selobj.rotation_mode == 'ZXY': matrix = mat_rotZ * mat_rotX * mat_rotY elif selobj.rotation_mode == 'ZYX': matrix = mat_rotZ * mat_rotY * mat_rotX # handle object scaling sx, sy, sz = selobj.scale mat_scX = Matrix.Scale(sx, 4, Vector([1, 0, 0])) mat_scY = Matrix.Scale(sy, 4, Vector([0, 1, 0])) mat_scZ = Matrix.Scale(sz, 4, Vector([0, 0, 1])) matrix = mat_scX * mat_scY * mat_scZ * matrix return matrix def getscreencoords(self, vector): # calculate screencoords of given Vector vector = vector * self.matrixT vector = vector + self.obT.location svector = bpy_extras.view3d_utils.location_3d_to_region_2d(self.region, self.rv3d, vector) if svector == None: return [0, 0] else: return [svector[0], svector[1]] def checksel(self): self.selverts = [] self.matrixT = self.getmatrix(self.obT) self.matrixF = self.getmatrix(self.obF).inverted() direc1 = (self.obF.location - self.empt.location) * self.matrixF direc2 = (self.obF.location - self.empt.location) * self.matrixT.inverted() direc2.length = 10000 for v in self.bmT.verts: vno1 = v.normal vno1.length = 0.0001 vco1 = v.co + vno1 hit1 = self.obT.ray_cast(vco1, vco1 + direc2) vno2 = -v.normal vno2.length = 0.0001 vco2 = v.co + vno2 hit2 = self.obT.ray_cast(vco2, vco2 + direc2) if hit1[2] == -1 or hit2[2] == -1: vco = ((v.co * self.matrixT + self.obT.location) - self.obF.location) * self.matrixF hit = self.obF.ray_cast(vco, vco + direc1) if hit[2] != -1: v.select = True self.selverts.append(v) def init_selproject(self, context): self.obhide = None # main operation self.scn = context.scene self.region = context.region self.rv3d = context.space_data.region_3d self.oldobjlist = list(self.scn.objects) self.oldobj = context.active_object self.oldmode = self.oldobj.mode mesh = self.oldobj.data if self.scn.UseSel and context.mode == 'EDIT_MESH': self.obhide = context.active_object me = self.obhide.data bmundo = bmesh.new() bmundo.from_mesh(me) objlist = [] for obj in self.scn.objects: objlist.append(obj) bpy.ops.mesh.separate(type = 'SELECTED') for obj in self.scn.objects: if not(obj in objlist): self.obF = obj bmundo.to_mesh(me) bmundo.free() self.obhide.hide = True else: self.obF = bpy.data.objects.get(self.scn.FromObject) if context.mode == 'EDIT_MESH': bpy.ops.object.editmode_toggle() self.obF.select = True self.scn.objects.active = self.obF self.originobF = self.obF.location bpy.ops.object.origin_set(type = 'ORIGIN_GEOMETRY') self.meF = self.obF.to_mesh(self.scn, 1, 'PREVIEW') self.bmF = bmesh.new() self.bmF.from_mesh(self.meF) self.obT = bpy.data.objects.get(self.scn.ToObject) self.obT.select = True self.scn.objects.active = self.obT self.originobT = self.obT.location bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') self.meT = self.obT.data self.bmT = bmesh.new() self.bmT.from_mesh(self.meT) self.vsellist = [] for v in self.bmT.verts: if v.select: self.vsellist.append(v) self.esellist = [] for e in self.bmT.edges: if e.select: self.esellist.append(e) self.fsellist = [] for f in self.bmT.faces: if f.select: self.fsellist.append(f) bpy.ops.object.add(type='EMPTY', location=(self.obF.location + self.obT.location) / 2) self.empt = context.active_object self.empt.name = "SelProject_dir_empty" self.selverts = [] def redraw(self): if started: self.checksel() glColor3f(1.0, 1.0, 0) for v in self.selverts: glBegin(GL_QUADS) x, y = self.getscreencoords(v.co) glVertex2f(x-2, y-2) glVertex2f(x-2, y+2) glVertex2f(x+2, y+2) glVertex2f(x+2, y-2) glEnd()
def do_import(path, DELETE_TOP_BONE=True): # limits MAX_NUMMESHES = 1000 MAX_NUMVERTS = 100000 MAX_NUMNORMALS = 100000 MAX_NUMTRIS = 100000 MAX_NUMMATS = 16 MAX_NUMBONES = 1000 MAX_NUMPOSKEYS = 1000 MAX_NUMROTKEYS = 1000 # get scene scn = bpy.context.scene if scn==None: return "No scene to import to!" # open the file try: file = open(path, 'r') except IOError: return "Failed to open the file!" try: if not path.endswith(".nb2"): raise IOError except IOError: return "Must be an nb2 file!" # Read frame info try: lines = getNextLine(file).split() if len(lines) != 2 or lines[0] != "Frames:": raise ValueError lines = getNextLine(file).split() if len(lines) != 2 or lines[0] != "Frame:": raise ValueError except ValueError: return "Frame information is invalid!" # Create the mesh meshName = "Mesh Object" # Before adding any meshes or armatures go into Object mode. if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='OBJECT') # read the number of meshes try: lines = getNextLine(file).split() if len(lines)!=2 or lines[0]!="Meshes:": raise ValueError numMeshes = int(lines[1]) if numMeshes < 0 or numMeshes > MAX_NUMMESHES: raise ValueError except ValueError: return "Number of meshes is invalid!" # read meshes boneIds = [[],[],[],[]] boneWeights = [[],[],[],[]] meshVertexGroups = {} vCount = 0 meshes = [] meshObjects = [] for i in range(numMeshes): # read name, flags and material try: lines = re.findall(r'\".*\"|[^ ]+', getNextLine(file)) if len(lines)!=3: raise ValueError meshName = lines[0] meshName = meshName[1:-1] print ("processing mesh name:%s..." % meshName) material = int(lines[2]) except ValueError: return "Name, flags or material in mesh " + str(i+1) + " are invalid!" meshes.append(bpy.data.meshes.new(meshName)) meshObjects.append(bpy.data.objects.new(meshName + "Ob", meshes[i])) scn.objects.link(meshObjects[i]) # read the number of vertices try: numVerts = int(getNextLine(file)) if numVerts < 0 or numVerts > MAX_NUMVERTS: raise ValueError except ValueError: return "Number of vertices in mesh " + str(i+1) + " is invalid!" print ("Number of vertices in mesh:%d" % numVerts) # read vertices coords = [] uvs = [] for j in range(numVerts): try: lines = getNextLine(file).split() if len(lines)!=14: raise ValueError coords.append([float(lines[1]), float(lines[2]), float(lines[3])]) uvs.append([float(lines[4]), 1-float(lines[5])]) boneIds[0].append(int(lines[6])) boneWeights[0].append(float(lines[7])) boneIds[1].append(int(lines[8])) boneWeights[1].append(float(lines[9])) boneIds[2].append(int(lines[10])) boneWeights[2].append(float(lines[11])) boneIds[3].append(int(lines[12])) boneWeights[3].append(float(lines[13])) meshVertexGroups[vCount] = meshName # uses the long mesh name - may be > 21 chars vCount += 1 except ValueError: return "Vertex " + str(j+1) + " in mesh " + str(i+1) + " is invalid!" meshes[i].vertices.add(len(coords)) meshes[i].vertices.foreach_set("co", unpack_list(coords)) # read number of normals try: numNormals = int(getNextLine(file)) if numNormals < 0 or numNormals > MAX_NUMNORMALS: raise ValueError except ValueError: return "Number of normals in mesh " + str(i+1) + " is invalid!" print ("Number of normals in mesh:%d" % numNormals) # read normals normals = [] for j in range(numNormals): try: lines = getNextLine(file).split() if len(lines)!=3: raise ValueError normals.append([float(lines[0]), float(lines[1]), float(lines[2])]) except ValueError: return "Normal " + str(j+1) + " in mesh " + str(i+1) + " is invalid!" # read the number of triangles try: numTris = int(getNextLine(file)) if numTris < 0 or numTris > MAX_NUMTRIS: raise ValueError except ValueError: return "Number of triangles in mesh " + str(i+1) + " is invalid!" print ("Number of triangles in mesh:%d" % numTris) # read triangles faces = [] for j in range(numTris): # read the triangle try: lines = getNextLine(file).split() if len(lines) != 8: raise ValueError v1 = int(lines[1]) v2 = int(lines[2]) v3 = int(lines[3]) faces.append([v1,v2,v3,0]) except ValueError: return "Triangle " + str(j+1) + " in mesh " + str(i+1) + " is invalid!" meshes[i].tessfaces.add(len(faces)) for fi, fpol in enumerate(faces): vlen = len(fpol) if vlen == 3 or vlen == 4: for v in range(vlen): meshes[i].tessfaces[fi].vertices_raw[v]= fpol[v] # set texture coordinates and material meshes[i].tessface_uv_textures.new() for j, face in enumerate(meshes[i].tessfaces): face_uv = meshes[i].tessface_uv_textures[0].data[j] face_uv.uv1 = Vector(uvs[face.vertices[0]]); face_uv.uv2 = Vector(uvs[face.vertices[1]]); face_uv.uv3 = Vector(uvs[face.vertices[2]]); if material >= 0: face.material_index = material for mesh in meshes: mesh.update() # read the number of materials try: lines = getNextLine(file).split() if len(lines)!=2 or lines[0]!="Materials:": raise ValueError numMats = int(lines[1]) if numMats < 0 or numMats > MAX_NUMMATS: raise ValueError except ValueError: return "Number of materials is invalid!" # read the materials for i in range(numMats): # read name name = getNextLine(file)[1:-1] # create the material #mat = Blender.Material.New(name) #mesh.materials += [mat] # read ambient color try: lines = getNextLine(file).split() if len(lines)!=4: raise ValueError except ValueError: return "Ambient color in material " + str(i+1) + " is invalid!" # read diffuse color try: lines = getNextLine(file).split() if len(lines)!=4: raise ValueError except ValueError: return "Diffuse color in material " + str(i+1) + " is invalid!" # read specular color try: lines = getNextLine(file).split() if len(lines)!=4: raise ValueError except ValueError: return "Specular color in material " + str(i+1) + " is invalid!" # read emissive color try: lines = getNextLine(file).split() if len(lines)!=4: raise ValueError except ValueError: return "Emissive color in material " + str(i+1) + " is invalid!" # read shininess try: shi = float(getNextLine(file)) except ValueError: return "Shininess in material " + str(i+1) + " is invalid!" # read transparency try: alpha = float(getNextLine(file)) except ValueError: return "Transparency in material " + str(i+1) + " is invalid!" # read texturemap texturemap = getNextLine(file)[1:-1] alphamap = getNextLine(file)[1:-1] # read the number of bones try: lines = getNextLine(file).split() if len(lines)!=2 or lines[0]!="Bones:": raise ValueError numBones = int(lines[1]) if numBones < 0 or numBones > MAX_NUMBONES: raise ValueError except: return "Number of bones is invalid!" # create the armature armature = None armOb = None print ("numBones:") numBones if numBones > 0: armature = bpy.data.armatures.new("Armature") armOb = bpy.data.objects.new("ArmatureObject", armature) armature.draw_type = 'STICK' scn.objects.link(armOb) scn.objects.active = armOb # read bones posKeys = {} rotKeys = {} boneNames = [] bpy.ops.object.editmode_toggle() bpy.types.EditBone.rot_matrix = bpy.props.FloatVectorProperty(name="Rot Matrix", size=9) for i in range(numBones): # read name fullName = getNextLine(file)[1:-1] boneNames.append(fullName) bone = armature.edit_bones.new(fullName) # read parent parentBoneName = getNextLine(file)[1:-1] if len(parentBoneName) > 0: bone.parent = armature.bones.data.edit_bones[parentBoneName] # read position and rotation try: line = getNextLine(file) lines = line.split() if not (len(lines) == 8 or len(lines) == 24): raise ValueError pos = [float(lines[1]), float(lines[2]), float(lines[3])] quat = [float(lines[4]), float(lines[5]), float(lines[6]), float(lines[7])] #print 'Read bone: %s' % line except ValueError: return "Invalid position or orientation in a bone!" # # Granny Rotation Quaternions are stored X,Y,Z,W but Blender uses W,X,Y,Z quaternion = Quaternion((quat[3], quat[0], quat[1], quat[2])) rotMatrix = quaternion.to_matrix() rotMatrix.transpose() # Need to transpose to get same behaviour as 2.49 script print ("Bone Data") print (fullName) print (pos) print (rotMatrix) boneLength = 3 # set position and orientation if bone.parent: bone_parent_matrix = Matrix([[bone.parent.rot_matrix[0], bone.parent.rot_matrix[1], bone.parent.rot_matrix[2]], [bone.parent.rot_matrix[3], bone.parent.rot_matrix[4], bone.parent.rot_matrix[5]], [bone.parent.rot_matrix[6], bone.parent.rot_matrix[7], bone.parent.rot_matrix[8]]]) bone.head = Vector(pos) * bone_parent_matrix + bone.parent.head bone.tail = bone.head + Vector([boneLength,0,0]) tempM = rotMatrix * bone_parent_matrix bone.rot_matrix = [tempM[0][0], tempM[0][1], tempM[0][2], tempM[1][0], tempM[1][1], tempM[1][2], tempM[2][0], tempM[2][1], tempM[2][2]] bone.matrix = Matrix([[-bone.rot_matrix[3], bone.rot_matrix[0], bone.rot_matrix[6], bone.head[0]], [-bone.rot_matrix[4], bone.rot_matrix[1], bone.rot_matrix[7], bone.head[1]], [-bone.rot_matrix[5], bone.rot_matrix[2], bone.rot_matrix[8], bone.head[2]], [0, 0, 0, 1]]) else: bone.head = Vector(pos) bone.tail = bone.head + Vector([boneLength,0,0]) bone.rot_matrix = [rotMatrix[0][0], rotMatrix[0][1], rotMatrix[0][2], rotMatrix[1][0], rotMatrix[1][1], rotMatrix[1][2], rotMatrix[2][0], rotMatrix[2][1], rotMatrix[2][2]] bone.matrix = Matrix([[-bone.rot_matrix[3], bone.rot_matrix[0], bone.rot_matrix[6], bone.head[0]], [-bone.rot_matrix[4], bone.rot_matrix[1], bone.rot_matrix[7], bone.head[1]], [-bone.rot_matrix[5], bone.rot_matrix[2], bone.rot_matrix[8], bone.head[2]], [0, 0, 0, 1]]) print (bone.rot_matrix[0], bone.rot_matrix[1], bone.rot_matrix[2]) print (bone.rot_matrix[3], bone.rot_matrix[4], bone.rot_matrix[5]) print (bone.rot_matrix[6], bone.rot_matrix[7], bone.rot_matrix[8]) print (bone.head) print (bone.tail) print ("bone roll") print (bone.roll) # read the number of position key frames try: numPosKeys = int(getNextLine(file)) if numPosKeys < 0 or numPosKeys > MAX_NUMPOSKEYS: raise ValueError except ValueError: return "Invalid number of position key frames!" # read position key frames posKeys[name] = [] for j in range(numPosKeys): # read time and position try: lines = getNextLine(file).split() if len(lines) != 4: raise ValueError except ValueError: return "Invalid position key frame!" # read the number of rotation key frames try: numRotKeys = int(getNextLine(file)) if numRotKeys < 0 or numRotKeys > MAX_NUMROTKEYS: raise ValueError except ValueError: return "Invalid number of rotation key frames!" # read rotation key frames rotKeys[name] = [] for j in range(numRotKeys): # read time and rotation try: lines = getNextLine(file).split() if len(lines) != 4: raise ValueError except ValueError: return "Invalid rotation key frame!" # Roll Fix #for bone in armature.edit_bones: # if bone.parent: # roll = bone.roll # bone.roll = roll - math.radians(90.0) # Create Vertex Groups vi = 0 for meshOb in meshObjects: mesh = meshOb.data for mvi, vertex in enumerate(mesh.vertices): for bi in range(numBones): for j in range(4): if bi==boneIds[j][vi]: name = boneNames[bi] if not meshOb.vertex_groups.get(name): meshOb.vertex_groups.new(name) grp = meshOb.vertex_groups.get(name) grp.add([mvi], boneWeights[j][vi], 'ADD') vi = vi + 1 # Give mesh object an armature modifier, using vertex groups but # not envelopes mod = meshOb.modifiers.new('mod_' + mesh.name, 'ARMATURE') mod.object = armOb mod.use_bone_envelopes = False mod.use_vertex_groups = True if DELETE_TOP_BONE: # Adjust object names, remove top bone for Civ V - Deliverator #armature.makeEditable() bone = armature.bones.data.edit_bones[boneNames[0]] while not bone.parent is None: bone = bone.parent print ('Found World Bone: %s' % bone.name) name = bone.name armOb.name = name # Delete top bone unless that would leave zero bones if (len(armature.bones.data.edit_bones) > 1): bpy.ops.object.select_pattern(pattern=name) bpy.ops.armature.delete() #del armature.bones.data.edit_bones[name] # armature.update() bpy.ops.object.editmode_toggle() bpy.ops.object.editmode_toggle() bpy.ops.object.editmode_toggle() # The import was a success! return ""