def update_bounds(self, context): if self.sollum_type == SollumType.BOUND_BOX: create_box(self.data, 1, Matrix.Diagonal(Vector(self.bound_dimensions))) elif self.sollum_type == SollumType.BOUND_SPHERE or self.sollum_type == SollumType.BOUND_POLY_SPHERE: create_sphere(mesh=self.data, radius=self.bound_radius) elif self.sollum_type == SollumType.BOUND_CYLINDER: create_cylinder(mesh=self.data, radius=self.bound_radius, length=self.bound_length) elif self.sollum_type == SollumType.BOUND_POLY_CYLINDER: create_cylinder(mesh=self.data, radius=self.bound_radius, length=self.bound_length, rot_mat=Matrix()) elif self.sollum_type == SollumType.BOUND_DISC: create_disc(mesh=self.data, radius=self.bound_radius, length=self.margin * 2) elif self.sollum_type == SollumType.BOUND_CAPSULE: create_capsule(mesh=self.data, diameter=self.margin, length=self.bound_radius, use_rot=True) elif self.sollum_type == SollumType.BOUND_POLY_CAPSULE: create_capsule(mesh=self.data, diameter=self.bound_radius / 2, length=self.bound_length)
def boundaries() -> Tuple[Vector, Vector]: x = [] y = [] z = [] for perimeter in Perimeter.all(): reference = Reference(perimeter) user = reference.matrix() for obj in perimeter.objects(): scale = Matrix.Diagonal(perimeter.matrix().to_scale()).to_4x4() bb = obj.bound_box for p in range(8): v_local = perimeter.matrix_1() @ obj.matrix_world @ Vector( [bb[p][0], bb[p][1], bb[p][2]]) v = user @ scale @ v_local x.append(v[0]) y.append(v[1]) z.append(v[2]) minimum = Vector([min(x), min(y), min(z)]) maximum = Vector([max(x), max(y), max(z)]) return minimum, maximum
def obj_to_bone(obj, rig, bone_name, bone_transform_name=None): """ Places an object at the location/rotation/scale of the given bone. """ if bpy.context.mode == 'EDIT_ARMATURE': raise MetarigError("obj_to_bone(): does not work while in edit mode") bone = rig.pose.bones[bone_name] loc = bone.custom_shape_translation rot = bone.custom_shape_rotation_euler scale = Vector(bone.custom_shape_scale_xyz) if bone.use_custom_shape_bone_size: scale *= bone.length if bone_transform_name is not None: bone = rig.pose.bones[bone_transform_name] elif bone.custom_shape_transform: bone = bone.custom_shape_transform shape_mat = Matrix.Translation(loc) @ ( Euler(rot).to_matrix() @ Matrix.Diagonal(scale)).to_4x4() obj.rotation_mode = 'XYZ' obj.matrix_basis = rig.matrix_world @ bone.bone.matrix_local @ shape_mat
def compute(self, **kwargs): input_stage = self.get_input_link('Input', **kwargs) if not input_stage or not self.name: return None path = f'/{Tf.MakeValidIdentifier(self.name)}' stage = self.cached_stage.create() UsdGeom.SetStageMetersPerUnit(stage, 1) UsdGeom.SetStageUpAxis(stage, UsdGeom.Tokens.z) root_xform = UsdGeom.Xform.Define(stage, path) root_prim = root_xform.GetPrim() for prim in input_stage.GetPseudoRoot().GetAllChildren(): override_prim = stage.OverridePrim( root_xform.GetPath().AppendChild(prim.GetName())) override_prim.GetReferences().AddReference( input_stage.GetRootLayer().realPath, prim.GetPath()) translation = Matrix.Translation((self.translation[:3])) diagonal = Matrix.Diagonal((self.scale[:3])).to_4x4() rotation_x = Matrix.Rotation(self.rotation[0], 4, 'X') rotation_y = Matrix.Rotation(self.rotation[1], 4, 'Y') rotation_z = Matrix.Rotation(self.rotation[2], 4, 'Z') transform = translation @ rotation_x @ rotation_y @ rotation_z @ diagonal UsdGeom.Xform.Get(stage, root_xform.GetPath()).AddTransformOp() root_prim.GetAttribute('xformOp:transform').Set( Gf.Matrix4d(transform.transposed())) return stage
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 generate_ik_tweak_widget(self, i, ctrl, wgt_mch): set_bone_widget_transform(self.obj, ctrl, wgt_mch) obj = create_circle_widget(self.obj, ctrl, head_tail=0.0, with_line=False) adjust_widget_transform_mesh(obj, Matrix.Diagonal((1.1, 1, 0.9, 1)), local=True)
def update_gizmo_matrix(self, context): light = context.object data = light.data # Gizmo scale is dependent on UI scale ui_scale = context.preferences.system.ui_scale r = data.size / ui_scale * 0.5 s = (light.scale.y * data.size_y) / ui_scale * 0.5 smatrix = Matrix.Diagonal([r, s, r]).to_4x4() self.matrix_offset = smatrix
def get_matrices_single_bone(ob, pose_bone, edit_bone, frames): use_quaternions = pose_bone.rotation_mode == 'QUATERNION' transforms = { 'loc': { 'count': 3, 'path': 'location', 'object': pose_bone.location }, 'rot': { 'count': 4 if use_quaternions else 3, 'path': 'rotation_quaternion' if use_quaternions else 'rotation_euler', 'object': pose_bone.rotation_quaternion if use_quaternions else pose_bone.rotation_euler }, 'sca': { 'count': 3, 'path': 'scale', 'object': pose_bone.scale }, } if ob.animation_data == None: ob.animation_data_create() for key, value in transforms.items(): result = dict(zip(frames, [value['object'].copy() for f in frames])) for i in range(value['count']): if ob.animation_data.action: crv = ob.animation_data.action.fcurves.find( 'pose.bones["' + pose_bone.name + '"].' + value['path'], index=i) else: crv = None if crv != None: for f in frames: v = crv.evaluate(f) result[f][i] = crv.evaluate(f) transforms[key] = result if use_quaternions: for quat in transforms['rot'].values(): quat.normalize() loc, rot, sca = transforms.values() matrices = [ Matrix.Translation(loc[frame]) @ rot[frame].to_matrix().to_4x4() @ Matrix.Diagonal(sca[frame]).to_4x4() for frame in loc ] return matrices
def get_perm_transform(self, perm): if not math.isnan(perm.scale): #in amf files geometry is stored as 100x the halo units with the exception of instanced geometry #this scales up to match the rest of the geometry and corrects the translation to match the unit settings offset_scale = self.get_scale_multiplier() / 0.01 pos, rot, scale = perm.transform.decompose() return Matrix.Translation(pos * offset_scale) @ rot.to_matrix( ).to_4x4() @ Matrix.Diagonal(scale).to_4x4() @ Matrix.Scale( 100 * perm.scale, 4) else: return Matrix()
def _get_tmat(self, context, ob): if self.apply_to == 'LOC': tmat = Matrix.Translation(ob.matrix_local.translation) elif self.apply_to == 'ROT': tmat = ob.matrix_local.to_quaternion().to_matrix().to_4x4() elif self.apply_to == 'SCL': tmat = Matrix.Diagonal(ob.matrix_local.to_scale()).to_4x4() else: tmat = ob.matrix_local.copy() if self.to_cursor: return context.scene.cursor.matrix.inverted() @ tmat return tmat
def execute(self, context): ob = context.object loc, rot, scl = ob.matrix_local.decompose() # If no additional objects are selected in addition to the # active one, consider all objects in the scene selobs = context.selected_objects if not selobs or selobs == [ob]: selobs = context.scene.objects if self.apply_to == 'LOC': tmat = Matrix.Translation(loc) elif self.apply_to == 'ROT': tmat = rot.to_matrix().to_4x4() elif self.apply_to == 'SCL': tmat = Matrix.Diagonal(scl).to_4x4() else: tmat = ob.matrix_local if self.to_cursor: tmat = context.scene.cursor.matrix.inverted() @ tmat ob.data.transform(tmat) tmat_inv = tmat.inverted() # Update ob's children to let them visually stay in place for c in ob.children: c.matrix_local = tmat @ c.matrix_local # Update objects sharing the same mesh as 'ob' for o in selobs: if o.data is not ob.data: continue # Check if an object is a child AND an instance of 'ob'. # matrix_local seems to be updated only once per frame after # execution of this script, so objects that are children and # an instance, whose matrix has already been set in the first # loop, still return their transformation from before the loop # here if ob is o.parent: o.matrix_local = tmat @ o.matrix_local @ tmat_inv else: o.matrix_local = o.matrix_local @ tmat_inv # What does o.matrix_local turn into? # * no child, no instance: o.matrix_local # * child, no instance: tmat @ o.matrix_local # * no child, instance: o.matrix_local @ tmat_inv # * child, instance: tmat @ o.matrix_local @ tmat_inv return {'FINISHED'}
def _fit_lattice_to_selection(object, vertices, lattice_object): ob_mat = object.matrix_world ob_loc, ob_rot, ob_scale = ob_mat.decompose() vert_locs = [Matrix.Diagonal(ob_scale) @ v.co for v in vertices] avg_vert_loc = sum(vert_locs, Vector()) / len(vert_locs) lat_origin = _calc_lattice_origin(vert_locs, avg_vert_loc) lattice_object.matrix_world = (Matrix.Translation(ob_loc) @ ob_rot.to_matrix().to_4x4() @ Matrix.Translation(lat_origin)) dims = _calc_lattice_dimensions(vert_locs, avg_vert_loc) # Avoid setting dimensions of a lattice to 0; it causes problems. ensured_dims = [d if d > 0 else 0.1 for d in dims] lattice_object.dimensions = ensured_dims _set_lattice_points(lattice_object, dims)
def getRotatedMatrix(sourceMat, pivotPos, axis, angle): # sourceMat = source.matrix_world loc, rot, sca = sourceMat.decompose() pivOffset = loc - pivotPos locMat = Matrix.Translation(pivOffset) rotMat = rot.to_matrix().to_4x4() scaMat = Matrix.Diagonal(sca).to_4x4() pivMat = Matrix.Translation(pivotPos) mat_rotBy = Matrix.Rotation(math.radians(angle), 4, axis) mat_out = pivMat @ mat_rotBy @ locMat @ rotMat @ scaMat return mat_out
def __gather_trans_rot_scale(vnode, export_settings): if vnode.parent_uuid is None: # No parent, so matrix is world matrix trans, rot, sca = vnode.matrix_world.decompose() else: # calculate local matrix trans, rot, sca = ( export_settings['vtree'].nodes[vnode.parent_uuid].matrix_world. inverted_safe() @ vnode.matrix_world).decompose() # make sure the rotation is normalized rot.normalize() trans = __convert_swizzle_location(trans, export_settings) rot = __convert_swizzle_rotation(rot, export_settings) sca = __convert_swizzle_scale(sca, export_settings) if vnode.blender_object.instance_type == 'COLLECTION' and vnode.blender_object.instance_collection: offset = -__convert_swizzle_location( vnode.blender_object.instance_collection.instance_offset, export_settings) s = Matrix.Diagonal(sca).to_4x4() r = rot.to_matrix().to_4x4() t = Matrix.Translation(trans).to_4x4() o = Matrix.Translation(offset).to_4x4() m = t @ r @ s @ o trans = m.translation translation, rotation, scale = (None, None, None) trans[0], trans[1], trans[2] = gltf2_blender_math.round_if_near(trans[0], 0.0), gltf2_blender_math.round_if_near(trans[1], 0.0), \ gltf2_blender_math.round_if_near(trans[2], 0.0) rot[0], rot[1], rot[2], rot[3] = gltf2_blender_math.round_if_near(rot[0], 1.0), gltf2_blender_math.round_if_near(rot[1], 0.0), \ gltf2_blender_math.round_if_near(rot[2], 0.0), gltf2_blender_math.round_if_near(rot[3], 0.0) sca[0], sca[1], sca[2] = gltf2_blender_math.round_if_near(sca[0], 1.0), gltf2_blender_math.round_if_near(sca[1], 1.0), \ gltf2_blender_math.round_if_near(sca[2], 1.0) if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0: translation = [trans[0], trans[1], trans[2]] if rot[0] != 1.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 0.0: rotation = [rot[1], rot[2], rot[3], rot[0]] if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0: scale = [sca[0], sca[1], sca[2]] return translation, rotation, scale
def box(p: Point3d, vx: Vector3d, vy: Vector3d, dx: float, dy: float, dz: float, mat: MatId) -> Id: id, name = new_id() rot = quaternion_from_vx_vy(vx, vy) bm = bmesh.new() bmesh.ops.create_cube( bm, size=1, matrix=Matrix.Diagonal(Vector((dx, dy, dz, 1.0))), # AML Scale here? May influence UVs #matrix=Matrix.Translation(Vector((0, 0, -depth/2))), #calc_uvs=True ) obj = mesh_from_bmesh(name, bm) obj.rotation_euler = rot.to_euler() obj.location = p current_collection.objects.link(obj) assign_material(obj, mat) return id
def reset_pose_bone_rotation(obj, bone_name) -> None: entered, active, mode = enter_mode_if(MODE_POSE, obj) bone = obj.pose.bones[bone_name] rest_matrix = get_pose_bone_rest_matrix_object(obj, bone_name) current_matrix = bone.matrix _, rr, _ = rest_matrix.decompose() cl, cr, cs = current_matrix.decompose() ml = Matrix.Translation(cl) mr = rr.to_matrix().to_4x4() msc = Matrix.Diagonal(cs).to_4x4() ms = ml @ mr @ msc bone.matrix = ms exit_mode_if(entered, active, mode)
def adjust_widget_axis(obj, axis='y', offset=0.0): mesh = obj.data if axis[0] == '-': s = -1.0 axis = axis[1] else: s = 1.0 trans_matrix = Matrix.Translation((0.0, offset, 0.0)) rot_matrix = Matrix.Diagonal((1.0, s, 1.0, 1.0)) if axis == "x": rot_matrix = Matrix.Rotation(-s * math.pi / 2, 4, 'Z') trans_matrix = Matrix.Translation((offset, 0.0, 0.0)) elif axis == "z": rot_matrix = Matrix.Rotation(s * math.pi / 2, 4, 'X') trans_matrix = Matrix.Translation((0.0, 0.0, offset)) matrix = trans_matrix @ rot_matrix for vert in mesh.vertices: vert.co = matrix @ vert.co
def execute(self, context): space_data = context.space_data use_local_view = bool(space_data.local_view) collection = context.collection start = self.start end = self.end sizes = context.window_manager.jewelcraft.sizes view_layer_upd = context.view_layer.update # Prepare objects # --------------------------- obs = [] app = obs.append if self.is_distribute: if not sizes.values(): return {"FINISHED"} curve = context.object curve.select_set(False) ob = context.selected_objects[0] context.view_layer.objects.active = ob mat_sca = Matrix.Diagonal(ob.scale).to_4x4() ob.matrix_world = mat_sca if self.rot_x: mat_rot = Matrix.Rotation(self.rot_x, 4, "X") ob.matrix_world @= mat_rot if self.rot_z: mat_rot = Matrix.Rotation(self.rot_z, 4, "Z") ob.matrix_world @= mat_rot if self.loc_z: mat_loc = Matrix.Translation((0.0, 0.0, self.loc_z)) ob.matrix_world @= mat_loc first_cycle = True for item in sizes.values(): for _ in range(item.qty): if first_cycle: con = ob.constraints.new("FOLLOW_PATH") con.target = curve con.use_curve_follow = True con.forward_axis = "FORWARD_X" ob.scale *= item.size / ob.dimensions.y view_layer_upd() app((ob, con, None, item.size)) first_cycle = False continue ob_copy = ob.copy() collection.objects.link(ob_copy) if use_local_view: ob_copy.local_view_set(space_data, True) if ob.children: for child in ob.children: child_copy = child.copy() collection.objects.link(child_copy) child_copy.parent = ob_copy child_copy.matrix_parent_inverse = child.matrix_parent_inverse for con in ob_copy.constraints: if con.type == "FOLLOW_PATH": break ob_copy.scale *= item.size / ob_copy.dimensions.y app((ob_copy, con, None, item.size)) else: for ob in context.selected_objects: for con in ob.constraints: if con.type == "FOLLOW_PATH": if self.rot_x: ob_mat_rot = ob.matrix_basis.to_quaternion().to_matrix( ).to_4x4() mat_rot = Matrix.Rotation(self.rot_x, 4, "X") ob.matrix_basis @= ob_mat_rot.inverted( ) @ mat_rot @ ob_mat_rot if self.rot_z: mat_rot = Matrix.Rotation(self.rot_z, 4, "Z") ob.matrix_basis @= mat_rot if self.rot_x or self.loc_z: dist = ob.matrix_basis.translation.length mat_rot = ob.matrix_basis.to_quaternion().to_matrix() ob.matrix_basis.translation = mat_rot @ Vector( (0.0, 0.0, dist + self.loc_z)) app((ob, con, con.offset, ob.dimensions.y)) break obs.sort(key=operator.itemgetter(2), reverse=True) ob = context.object for con in ob.constraints: if con.type == "FOLLOW_PATH": break else: ob, con, _ = obs[0] curve = con.target curve.data.use_radius = False asset.apply_scale(curve) # Start offset # --------------------------- if self.use_absolute_offset: base_unit = 100.0 / self.curve_length else: ofst = 0.0 num = len(obs) if num > 1: closed_distribution = round(end - start, 1) == 100.0 if self.cyclic and closed_distribution: ofst = (end - start) / num else: if not self.cyclic: start = max(start, 0.0) end = min(end, 100.0) ofst = (end - start) / (num - 1) # (Re)Distribute # --------------------------- ofst_fac = start size_prev = 0.0 consecutive_cycle = False for ob, con, _, size in obs: if self.use_absolute_offset: ofst = base_unit * ((size + size_prev) / 2 + self.spacing) size_prev = size if consecutive_cycle: ofst_fac += ofst else: consecutive_cycle = True con.offset = -ofst_fac return {"FINISHED"}
def __gather_trans_rot_scale(blender_object, export_settings): if blender_object.matrix_parent_inverse == Matrix.Identity(4): trans = blender_object.location if blender_object.rotation_mode in ['QUATERNION', 'AXIS_ANGLE']: rot = blender_object.rotation_quaternion else: rot = blender_object.rotation_euler.to_quaternion() sca = blender_object.scale else: # matrix_local = matrix_parent_inverse*location*rotation*scale # Decomposing matrix_local gives less accuracy, but is needed if matrix_parent_inverse is not the identity. if blender_object.matrix_local[3][3] != 0.0: trans, rot, sca = blender_object.matrix_local.decompose() else: # Some really weird cases, scale is null (if parent is null when evaluation is done) print_console( 'WARNING', 'Some nodes are 0 scaled during evaluation. Result can be wrong' ) trans = blender_object.location if blender_object.rotation_mode in ['QUATERNION', 'AXIS_ANGLE']: rot = blender_object.rotation_quaternion else: rot = blender_object.rotation_euler.to_quaternion() sca = blender_object.scale # make sure the rotation is normalized rot.normalize() trans = __convert_swizzle_location(trans, export_settings) rot = __convert_swizzle_rotation(rot, export_settings) sca = __convert_swizzle_scale(sca, export_settings) if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection: offset = -__convert_swizzle_location( blender_object.instance_collection.instance_offset, export_settings) s = Matrix.Diagonal(sca).to_4x4() r = rot.to_matrix().to_4x4() t = Matrix.Translation(trans).to_4x4() o = Matrix.Translation(offset).to_4x4() m = t @ r @ s @ o trans = m.translation translation, rotation, scale = (None, None, None) trans[0], trans[1], trans[2] = gltf2_blender_math.round_if_near(trans[0], 0.0), gltf2_blender_math.round_if_near(trans[1], 0.0), \ gltf2_blender_math.round_if_near(trans[2], 0.0) rot[0], rot[1], rot[2], rot[3] = gltf2_blender_math.round_if_near(rot[0], 1.0), gltf2_blender_math.round_if_near(rot[1], 0.0), \ gltf2_blender_math.round_if_near(rot[2], 0.0), gltf2_blender_math.round_if_near(rot[3], 0.0) sca[0], sca[1], sca[2] = gltf2_blender_math.round_if_near(sca[0], 1.0), gltf2_blender_math.round_if_near(sca[1], 1.0), \ gltf2_blender_math.round_if_near(sca[2], 1.0) if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0: translation = [trans[0], trans[1], trans[2]] if rot[0] != 1.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 0.0: rotation = [rot[1], rot[2], rot[3], rot[0]] if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0: scale = [sca[0], sca[1], sca[2]] return translation, rotation, scale
def execute(self, context): # Set objects # --------------------------- sizes_list = context.window_manager.jewelcraft.sizes.values() if self.is_distribute: if not sizes_list: return {"FINISHED"} curve, ob = _get_obs() curve.select_set(False) context.view_layer.objects.active = ob mat_sca = Matrix.Diagonal(ob.scale).to_4x4() ob.matrix_world = mat_sca if self.rot_x: mat_rot = Matrix.Rotation(self.rot_x, 4, "X") ob.matrix_world @= mat_rot if self.rot_z: mat_rot = Matrix.Rotation(self.rot_z, 4, "Z") ob.matrix_world @= mat_rot if self.loc_z: mat_loc = Matrix.Translation((0.0, 0.0, self.loc_z)) ob.matrix_world @= mat_loc obs = _create_dstr(ob, curve, sizes_list) elif self.hash_sizes != _hash(sizes_list): for is_last, con in iterutils.spot_last(list(_get_cons())): ob = con.id_data if not is_last: for child in ob.children: bpy.data.objects.remove(child) bpy.data.objects.remove(ob) context.view_layer.objects.active = ob curve = con.target _deform_redstr(ob, self.rot_x, self.rot_z, self.loc_z) obs = _create_dstr(ob, curve, sizes_list, con_add=False) else: obs = [] app = obs.append for con in _get_cons(): ob = con.id_data _deform_redstr(ob, self.rot_x, self.rot_z, self.loc_z) app((con, con.offset, ob.dimensions.y)) obs.sort(key=operator.itemgetter(1), reverse=True) con = obs[0][0] curve = con.target curve.data.use_radius = False asset.apply_scale(curve) # Offset values # --------------------------- start = self.start end = self.end if not self.use_absolute_offset: ofst = 0.0 num = len(obs) if num > 1: closed_distribution = round(end - start, 1) == 100.0 if self.cyclic and closed_distribution: ofst = (end - start) / num else: if not self.cyclic: start = max(start, 0.0) end = min(end, 100.0) ofst = (end - start) / (num - 1) # Distribute # --------------------------- ofst_fac = start size_prev = 0.0 consecutive_cycle = False for con, _, size in obs: if self.use_absolute_offset: ofst = self.base_unit * ((size + size_prev) / 2 + self.spacing) size_prev = size if consecutive_cycle: ofst_fac += ofst else: consecutive_cycle = True con.offset = -ofst_fac return {"FINISHED"}
def execute(self, context): space_data = context.space_data use_local_view = bool(space_data.local_view) collection = context.collection start = self.start end = self.end # Init # --------------------------- if self.is_scatter: num = self.number - 1 curve = context.object curve.select_set(False) ob = context.selected_objects[0] context.view_layer.objects.active = ob else: obs = {} for ob in context.selected_objects: con = ob.constraints.get("Follow Path") if con: obs[ob] = con.offset obs_sorted = sorted(obs, key=obs.get, reverse=True) num = len(obs_sorted) - 1 ob = context.object if ob not in obs: ob = obs_sorted[0] curve = ob.constraints["Follow Path"].target curve.data.use_radius = False asset.apply_scale(curve) # Offset # --------------------------- ofst = 0.0 if num: if self.use_absolute_offset: ob_size = ob.dimensions[1] base_unit = 100.0 / self.curve_length ofst = base_unit * (ob_size + self.spacing) else: closed_scattering = True if round(end - start, 1) == 100.0 else False if self.cyclic and closed_scattering: ofst = (end - start) / (num + 1) else: if not self.cyclic: start = start if start >= 0.0 else 0.0 end = end if end <= 100.0 else 100.0 ofst = (end - start) / num # Scatter/Redistribute # --------------------------- if self.is_scatter: mat_sca = Matrix.Diagonal(ob.scale).to_4x4() ob.matrix_world = mat_sca if self.rot_y: mat_rot = Matrix.Rotation(self.rot_y, 4, "Y") ob.matrix_world @= mat_rot if self.rot_z: mat_rot = Matrix.Rotation(self.rot_z, 4, "Z") ob.matrix_world @= mat_rot if self.loc_z: mat_loc = Matrix.Translation((0.0, 0.0, self.loc_z)) ob.matrix_world @= mat_loc ofst_fac = start + ofst for _ in range(num): ob_copy = ob.copy() collection.objects.link(ob_copy) if use_local_view: ob_copy.local_view_set(space_data, True) con = ob_copy.constraints.new("FOLLOW_PATH") con.target = curve con.offset = -ofst_fac con.use_curve_follow = True con.forward_axis = "FORWARD_X" ofst_fac += ofst if ob.children: for child in ob.children: child_copy = child.copy() collection.objects.link(child_copy) child_copy.parent = ob_copy child_copy.matrix_parent_inverse = child.matrix_parent_inverse con = ob.constraints.new("FOLLOW_PATH") con.target = curve con.offset = -start con.use_curve_follow = True con.forward_axis = "FORWARD_X" else: ofst_fac = start for ob in obs_sorted: if self.rot_y: mat_rot = Matrix.Rotation(self.rot_y, 4, "Y") ob.matrix_basis @= mat_rot if self.rot_z: mat_rot = Matrix.Rotation(self.rot_z, 4, "Z") ob.matrix_basis @= mat_rot if self.loc_z: mat_loc = Matrix.Translation((0.0, 0.0, self.loc_z)) ob.matrix_basis @= mat_loc ob.constraints["Follow Path"].offset = -ofst_fac ofst_fac += ofst return {"FINISHED"}
def handle_draw_callback(): if not bpy.data.actions: return action = bpy.data.actions[bpy.context.scene.rw4_list_index] if not action.rw4.is_morph_handle or not is_anim_panel_showing(): return direction = Vector(action.rw4.final_pos) - Vector(action.rw4.initial_pos) length = direction.length if length == 0.0: return bbox = calc_global_bbox() width = (bbox[1] - bbox[0]).length * 0.02 scale_matrix = Matrix.Scale(direction.length, 3, Vector((0, 0, 1))) scale_matrix = scale_matrix @ Matrix.Scale(width, 3, Vector((0, 1, 0))) scale_matrix = scale_matrix @ Matrix.Scale(width, 3, Vector((1, 0, 0))) matrix = Vector((0, 0, 1)).rotation_difference(direction).to_matrix() matrix = Matrix.Translation( action.rw4.initial_pos) @ (matrix @ scale_matrix).to_4x4() bgl.glEnable(bgl.GL_BLEND) bgl.glEnable(bgl.GL_LINE_SMOOTH) bgl.glEnable(bgl.GL_POLYGON_SMOOTH) bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA) shader.bind() shader.uniform_float("color", (109 / 255.0, 141 / 255.0, 143 / 255.0, 0.4)) batch = batch_for_shader(shader, 'TRIS', {"pos": [matrix @ Vector(c) for c in BOX_COORDS]}, indices=BOX_INDICES) batch.draw(shader) # Draw initial handle pos handle_width = width * 1.25 matrix = Matrix.Translation( Vector(action.rw4.initial_pos) - Vector( (0, 0, 0.5 * handle_width))) # center it matrix = matrix @ Matrix.Diagonal( (handle_width, handle_width, handle_width, 1)) shader.uniform_float("color", (165 / 255.0, 195 / 255.0, 196 / 255.0, 0.4)) batch = batch_for_shader(shader, 'TRIS', {"pos": [matrix @ Vector(c) for c in BOX_COORDS]}, indices=BOX_INDICES) batch.draw(shader) # Draw initial handle pos handle_width = width * 1.25 matrix = Matrix.Translation( Vector(action.rw4.final_pos) - Vector( (0, 0, 0.5 * handle_width))) # center it matrix = matrix @ Matrix.Diagonal( (handle_width, handle_width, handle_width, 1)) shader.uniform_float("color", (165 / 255.0, 195 / 255.0, 196 / 255.0, 0.4)) batch = batch_for_shader(shader, 'TRIS', {"pos": [matrix @ Vector(c) for c in BOX_COORDS]}, indices=BOX_INDICES) batch.draw(shader)
def execute(self, context): import operator space_data = context.space_data use_local_view = bool(space_data.local_view) collection = context.collection start = self.start end = self.end # Init # --------------------------- if self.is_scatter: num = self.number - 1 curve = context.object curve.select_set(False) ob = context.selected_objects[0] context.view_layer.objects.active = ob else: obs = [] app = obs.append for ob in context.selected_objects: for con in ob.constraints: if con.type == "FOLLOW_PATH": app((ob, con, con.offset)) break obs.sort(key=operator.itemgetter(2), reverse=True) num = len(obs) - 1 ob = context.object for con in ob.constraints: if con.type == "FOLLOW_PATH": break else: ob, con, _ = obs[0] curve = con.target curve.data.use_radius = False asset.apply_scale(curve) # Offset # --------------------------- ofst = 0.0 if num: if self.use_absolute_offset: ob_size = ob.dimensions[1] base_unit = 100.0 / self.curve_length ofst = base_unit * (ob_size + self.spacing) else: closed_scattering = True if round(end - start, 1) == 100.0 else False if self.cyclic and closed_scattering: ofst = (end - start) / (num + 1) else: if not self.cyclic: start = start if start >= 0.0 else 0.0 end = end if end <= 100.0 else 100.0 ofst = (end - start) / num # Scatter/Redistribute # --------------------------- if self.is_scatter: mat_sca = Matrix.Diagonal(ob.scale).to_4x4() ob.matrix_world = mat_sca if self.rot_x: mat_rot = Matrix.Rotation(self.rot_x, 4, "X") ob.matrix_world @= mat_rot if self.rot_z: mat_rot = Matrix.Rotation(self.rot_z, 4, "Z") ob.matrix_world @= mat_rot if self.loc_z: mat_loc = Matrix.Translation((0.0, 0.0, self.loc_z)) ob.matrix_world @= mat_loc ofst_fac = start + ofst for _ in range(num): ob_copy = ob.copy() collection.objects.link(ob_copy) if use_local_view: ob_copy.local_view_set(space_data, True) con = ob_copy.constraints.new("FOLLOW_PATH") con.target = curve con.offset = -ofst_fac con.use_curve_follow = True con.forward_axis = "FORWARD_X" ofst_fac += ofst if ob.children: for child in ob.children: child_copy = child.copy() collection.objects.link(child_copy) child_copy.parent = ob_copy child_copy.matrix_parent_inverse = child.matrix_parent_inverse con = ob.constraints.new("FOLLOW_PATH") con.target = curve con.offset = -start con.use_curve_follow = True con.forward_axis = "FORWARD_X" else: ofst_fac = start for ob, con, _ in obs: if self.rot_x: ob_mat_rot = ob.matrix_basis.to_quaternion().to_matrix( ).to_4x4() mat_rot = Matrix.Rotation(self.rot_x, 4, "X") ob.matrix_basis @= ob_mat_rot.inverted( ) @ mat_rot @ ob_mat_rot if self.rot_z: mat_rot = Matrix.Rotation(self.rot_z, 4, "Z") ob.matrix_basis @= mat_rot if self.rot_x or self.loc_z: dist = ob.matrix_basis.translation.length mat_rot = ob.matrix_basis.to_quaternion().to_matrix() ob.matrix_basis.translation = mat_rot @ Vector( (0.0, 0.0, dist + self.loc_z)) con.offset = -ofst_fac ofst_fac += ofst return {"FINISHED"}
def sync_light(ainode, light): data = light.data _type = types.AI_LIGHT_TYPE[data.type] # Common properties AiNodeSetStr(ainode, "name", light.name) if not hasattr(data, "shape") or light.data.shape != 'RECTANGLE': # Set matrix for everything except cylinder lights AiNodeSetMatrix(ainode, "matrix", generate_aimatrix(light.matrix_world)) AiNodeSetRGB(ainode, "color", *data.color) AiNodeSetFlt(ainode, "intensity", data.arnold.intensity) AiNodeSetFlt(ainode, "exposure", data.arnold.exposure) AiNodeSetInt(ainode, "samples", data.arnold.samples) AiNodeSetBool(ainode, "normalize", data.arnold.normalize) AiNodeSetBool(ainode, "cast_shadows", data.arnold.cast_shadows) AiNodeSetBool(ainode, "cast_volumetric_shadows", data.arnold.cast_volumetric_shadows) AiNodeSetFlt(ainode, "shadow_density", data.arnold.shadow_density) AiNodeSetFlt(ainode, "camera", data.arnold.camera) AiNodeSetFlt(ainode, "diffuse", data.arnold.diffuse) AiNodeSetFlt(ainode, "specular", data.arnold.specular) AiNodeSetFlt(ainode, "transmission", data.arnold.transmission) AiNodeSetFlt(ainode, "sss", data.arnold.sss) AiNodeSetFlt(ainode, "indirect", data.arnold.indirect) AiNodeSetFlt(ainode, "volume", data.arnold.volume) AiNodeSetInt(ainode, "max_bounces", data.arnold.max_bounces) # shadow_color # Light data if _type in ('point_light', 'spot_light'): AiNodeSetFlt(ainode, "radius", data.shadow_soft_size) if _type == 'distant_light': AiNodeSetFlt(ainode, "angle", data.arnold.angle) if _type == 'spot_light': AiNodeSetFlt(ainode, "cone_angle", math.degrees(data.spot_size)) AiNodeSetFlt(ainode, "penumbra_angle", math.degrees(data.arnold.penumbra_angle)) AiNodeSetFlt(ainode, "roundness", data.arnold.spot_roundness) AiNodeSetFlt(ainode, "aspect_ratio", data.arnold.aspect_ratio) AiNodeSetFlt(ainode, "lens_radius", data.arnold.lens_radius) if _type == 'area_light': if data.shape == 'SQUARE': smatrix = Matrix.Diagonal( (data.size / 2, data.size / 2, data.size / 2)).to_4x4() tmatrix = light.matrix_world @ smatrix AiNodeSetMatrix(ainode, "matrix", generate_aimatrix(tmatrix)) elif data.shape == 'DISK': # Get largest scale value (disk_light can't be an ellipse) s = light.scale.x if light.scale.x > light.scale.y else light.scale.y AiNodeSetFlt(ainode, "radius", 0.5 * data.size * s) elif data.shape == 'RECTANGLE': # Cylinder light d = 0.5 * data.size_y * light.scale.y top = get_position_along_local_vector(light, d, 'Y') bottom = get_position_along_local_vector(light, -d, 'Y') AiNodeSetVec(ainode, "top", *top) AiNodeSetVec(ainode, "bottom", *bottom) s = light.scale.x if light.scale.x > light.scale.z else light.scale.z AiNodeSetFlt(ainode, "radius", 0.5 * data.size * s) AiNodeSetFlt(ainode, "roundness", data.arnold.area_roundness) AiNodeSetFlt(ainode, "spread", data.arnold.spread) AiNodeSetInt(ainode, "resolution", data.arnold.resolution) AiNodeSetFlt(ainode, "soft_edge", data.arnold.soft_edge)
def import_animation_channel( b_pose_bone, b_action, b_action_group, channel, index, channel_keyframes): import_locrot = channel.keyframe_class in (rw4_base.LocRotScaleKeyframe, rw4_base.LocRotKeyframe) import_scale = channel.keyframe_class == rw4_base.LocRotScaleKeyframe fcurves_qr = [] fcurves_vt = [] fcurves_vs = [] if import_locrot: data_path = b_pose_bone.path_from_id('rotation_quaternion') for i in range(4): fcurve = b_action.fcurves.new(data_path, index=i) fcurve.group = b_action_group fcurves_qr.append(fcurve) data_path = b_pose_bone.path_from_id('location') for i in range(3): fcurve = b_action.fcurves.new(data_path, index=i) fcurve.group = b_action_group fcurves_vt.append(fcurve) if import_scale: data_path = b_pose_bone.path_from_id('scale') for i in range(3): fcurve = b_action.fcurves.new(data_path, index=i) fcurve.group = b_action_group fcurves_vs.append(fcurve) for k, kf in enumerate(channel.keyframes): time = kf.time * rw4_base.KeyframeAnim.FPS bpy.context.scene.frame_set(time) # So that parent.matrix works transform = channel_keyframes[index][k] if import_locrot: # Rotation is in model space qr = transform.to_quaternion() if b_pose_bone.parent is not None: qr = b_pose_bone.parent.matrix.inverted().to_quaternion() @ qr for i in range(4): fcurves_qr[i].keyframe_points.insert(time, qr[i]) # The position, in world coordinates relative to origin vt = transform @ b_pose_bone.bone.head_local # Convert from WORLD position into LOCAL position # This only works because we import our bones with no rotation; for exporting this will require more if b_pose_bone.parent is not None: parent_matrix = b_pose_bone.parent.matrix parent_transform = (parent_matrix @ b_pose_bone.parent.bone.matrix_local.inverted()) # The position, in world coordinates relative to posed position world_pos_relative = vt - (parent_transform @ b_pose_bone.bone.head_local) vt = parent_matrix.to_3x3().inverted() @ world_pos_relative for i in range(3): fcurves_vt[i].keyframe_points.insert(time, vt[i]) if import_scale: # It's in the transform coordinate system; in Blender it's in the bone.matrix # Since we use an Identity rotation for the bone matrix, it's the same vs = transform.to_scale() # BUT we have to compensate for the parent scaling. if b_pose_bone.parent is not None: _, parent_rotation, parent_scale = b_pose_bone.parent.matrix.decompose() world_parent_scale = (parent_rotation.to_matrix() @ Matrix.Diagonal(parent_scale)).to_scale() # This assumes matrix_local is Identity _, rotation, _ = transform.decompose() # Parent scale in bone coordinate system bone_parent_scale = \ (rotation.inverted().to_matrix() @ Matrix.Diagonal(world_parent_scale)).to_scale() vs = Vector([vs[i] / bone_parent_scale[i] for i in range(3)]) for i in range(3): fcurves_vs[i].keyframe_points.insert(time, vs[i])
def compute_world_matrix(self): mat_loc = Matrix.Translation(self.cur_location).to_4x4() mat_rot = Euler(self.cur_rotation, 'XYZ').to_matrix().to_4x4() mat_sca = Matrix.Diagonal(self.cur_scale).to_4x4() mat = mat_loc @ mat_rot @ mat_sca return self.cur_link_matrix @ mat
def apply_scale(ob: Object) -> None: mat = Matrix.Diagonal(ob.scale).to_4x4() ob.data.transform(mat) ob.scale = (1.0, 1.0, 1.0)
def get_active_geo_rotation(self, context) -> str: '''Get and set the normal direction of the 3D Cursor based on the active geo''' return_msg = None ob = context.object me = ob.data self.bm = bmesh.from_edit_mesh(me) self.bm.faces.ensure_lookup_table() self.bm.edges.ensure_lookup_table() if 'VERT' in self.bm.select_mode: return_msg = 'This operator does not work on vertices' return return_msg active_geo = self.bm.select_history.active if active_geo is None: return_msg = "Could not find the active geo to copy rotation" return return_msg if isinstance(active_geo, bmesh.types.BMFace): direction_vec = active_geo.normal else: # Edge if self.edge_rot_type == 'face_1_angle': if len(active_geo.link_faces): direction_vec = active_geo.link_faces[0].normal else: return_msg = "There is no face to sample from, using edge angle as fallback" elif self.edge_rot_type == 'face_2_angle': if len(active_geo.link_faces) > 1: direction_vec = active_geo.link_faces[1].normal else: return_msg = "There is no second face to sample from, using edge angle as fallback" elif self.edge_rot_type == 'average_face_angle': if len(active_geo.link_faces) > 1: direction_vec = (active_geo.link_faces[0].normal + active_geo.link_faces[1].normal) / 2 else: return_msg = "There isn't 2 faces to average from, using edge angle as fallback" if self.edge_rot_type == 'edge_angle' or return_msg is not None: # Also use as fallback direction_vec = active_geo.verts[0].co - active_geo.verts[1].co _loc, rot, scl = ob.matrix_world.decompose() rot_mat = rot.to_matrix().to_4x4() scl_mat = Matrix.Diagonal(scl.to_4d()) direction_vec = rot_mat @ scl_mat @ direction_vec normal_quat = direction_vec.to_track_quat('Z', 'Y') normal_euler = normal_quat.to_euler('XYZ') normal_axis = normal_quat.to_axis_angle() if self.internal_align_type == 'cursor': cursor = context.scene.cursor if cursor.rotation_mode == 'QUATERNION': cursor.rotation_quaternion = normal_quat elif cursor.rotation_mode == 'AXIS_ANGLE': cursor.rotation_axis_angle[0] = normal_axis[1] cursor.rotation_axis_angle[1] = normal_axis[0][0] cursor.rotation_axis_angle[2] = normal_axis[0][1] cursor.rotation_axis_angle[3] = normal_axis[0][2] else: cursor.rotation_euler = normal_euler else: # origin ob_mat = ob.matrix_world bpy.ops.object.mode_set(mode='OBJECT') # Make local space = world space me.transform(ob_mat) # Faster way to calculate new matrix - without refreshing the View Layer loc, _rot, scl = ob_mat.decompose() ob.matrix_world = ( Matrix.Translation(loc) @ normal_euler.to_matrix().to_4x4() @ Matrix.Diagonal(scl.to_4d())) # Apply the inverted matrix on the mesh me.transform(ob.matrix_world.inverted()) bpy.ops.object.mode_set(mode='EDIT') self.bm = bmesh.from_edit_mesh(ob.data) # Wierd rounding errors without this, less accurate but looks nicer ob.rotation_euler[0] = round(ob.rotation_euler[0], 5) ob.rotation_euler[1] = round(ob.rotation_euler[1], 5) ob.rotation_euler[2] = round(ob.rotation_euler[2], 5) if return_msg is not None: self.report({'WARNING'}, return_msg)
def make_control_widget(self, ctrl, is_hip): obj = create_circle_widget(self.obj, ctrl, radius=1.0, head_tail=0.5) if is_hip: adjust_widget_transform_mesh(obj, Matrix.Diagonal((1, -1, 1, 1)), local=True)
def process_animation(self, animation): """ Process all the keyframes of the animation, computing the final transformation matrices used in the shader. The matrices are the model space transformation from the base pose to the animated pose. Returns a list of channels, where every channel is a list of Matrix4 keyframes with the transformation. :param animation: :return: [[channel0_keyframe0, channel0_keyframe1,...], [channel1_keyframe0,...],...] """ # 1. Clssify all keyframes by their time # [(time1, pose_bones), (time2, pose_bones), etc], one keyframe per channel per time keyframe_poses = {} for c, channel in enumerate(animation.channels): for kf in channel.keyframes: if kf.time not in keyframe_poses: keyframe_poses[kf.time] = [None] * len(animation.channels) if channel.keyframe_class == rw4_base.LocRotScaleKeyframe or \ channel.keyframe_class == rw4_base.LocRotKeyframe: r = kf.rot t = kf.loc else: r = Quaternion() t = Vector((0, 0, 0)) if channel.keyframe_class == rw4_base.LocRotScaleKeyframe: s = kf.scale else: s = Vector((1.0, 1.0, 1.0)) keyframe_poses[kf.time][c] = PoseBone(r=r, t=t, s=s) # 2. Process the transformation matrix # This is the same algorithm used by Spore; the result is what is sent to the DirectX shader # These are the transforms in model space from the rest pose to the animated pose # This assumes that parents will always be processed before their children # List of channels, which are list of PoseBone keyframes containing the transformation channel_keyframes = [[] for _ in animation.channels] # Process for every channel for every time # We must do it even if the channel didn't have a keyframe there, because it might be used by other channels for time, pose_bones in sorted(keyframe_poses.items()): branches = [] # Used as an stack parent_rot = Matrix.Identity(3) parent_loc = Vector((0, 0, 0)) parent_scale = Vector((1.0, 1.0, 1.0)) # inverse scale for c, (pose_bone, bone, skin) in enumerate(zip(pose_bones, self.bones, self.skin_data)): skip_bone = pose_bone is None if skip_bone: pose_bone = interpolate_pose(animation, time, c, keyframe_poses) # Apply the scale scale_matrix = Matrix.Diagonal(pose_bone.s) parent_inv_scale = \ Matrix.Diagonal((1.0 / parent_scale.x, 1.0 / parent_scale.y, 1.0 / parent_scale.z)) scaled_m = parent_inv_scale @ (pose_bone.r.to_matrix() @ scale_matrix) m = parent_rot @ scaled_m t = parent_rot @ pose_bone.t + parent_loc if not skip_bone: dst_r = m @ skin.matrix.inverted() dst_t = t + (m @ skin.translation) #for i in range(3): # print(f"skin_bones_data += struct.pack('ffff', {dst_r[i][0]}, {dst_r[i][1]}, {dst_r[i][2]}, {dst_t[i]})") channel_keyframes[c].append(Matrix.Translation(dst_t) @ dst_r.to_4x4()) if bone.flags == rw4_base.SkeletonBone.TYPE_ROOT: parent_rot = m parent_loc = t parent_scale = pose_bone.s elif bone.flags == rw4_base.SkeletonBone.TYPE_LEAF: if branches: parent_rot, parent_loc, parent_scale = branches.pop() elif bone.flags == rw4_base.SkeletonBone.TYPE_BRANCH: branches.append((parent_rot, parent_loc, parent_scale)) parent_rot = m parent_loc = t parent_scale = pose_bone.s return channel_keyframes