def process(self): if not self.outputs['Quaternions'].is_linked: return inputs = self.inputs quaternion_list = [] if self.mode == "WXYZ": I = [inputs[n].sv_get()[0] for n in "WXYZ"] params = match_long_repeat(I) for wxyz in zip(*params): q = Quaternion(wxyz) if self.normalize: q.normalize() quaternion_list.append(q) elif self.mode == "SCALARVECTOR": I = [inputs[n].sv_get()[0] for n in ["Scalar", "Vector"]] params = match_long_repeat(I) for scalar, vector in zip(*params): q = Quaternion([scalar, *vector]) if self.normalize: q.normalize() quaternion_list.append(q) elif self.mode == "EULER": I = [inputs["Angle " + n].sv_get()[0] for n in "XYZ"] params = match_long_repeat(I) # conversion factor from the current angle units to radians au = self.radians_conversion_factor() for angleX, angleY, angleZ in zip(*params): euler = Euler((angleX * au, angleY * au, angleZ * au), self.euler_order) q = euler.to_quaternion() if self.normalize: q.normalize() quaternion_list.append(q) elif self.mode == "AXISANGLE": I = [inputs[n].sv_get()[0] for n in ["Axis", "Angle"]] params = match_long_repeat(I) # conversion factor from the current angle units to radians au = self.radians_conversion_factor() for axis, angle in zip(*params): q = Quaternion(axis, angle * au) if self.normalize: q.normalize() quaternion_list.append(q) elif self.mode == "MATRIX": input_M = inputs["Matrix"].sv_get(default=id_mat) for m in input_M: q = Matrix(m).to_quaternion() if self.normalize: q.normalize() quaternion_list.append(q) self.outputs['Quaternions'].sv_set(quaternion_list)
def mapping_node_order_flip(orientation): """ Flip euler order of mapping shader node see: Blender #a1ffb49 """ rot = Euler(orientation) rot.order = 'ZYX' quat = rot.to_quaternion() return quat.to_euler('XYZ')
def process(self): if not self.outputs['Quaternions'].is_linked: return inputs = self.inputs quaternionList = [] if self.mode == "WXYZ": I = [inputs[n].sv_get()[0] for n in "WXYZ"] params = match_long_repeat(I) for wxyz in zip(*params): q = Quaternion(wxyz) if self.normalize: q.normalize() quaternionList.append(q) elif self.mode == "SCALARVECTOR": I = [inputs[n].sv_get()[0] for n in ["Scalar", "Vector"]] params = match_long_repeat(I) for scalar, vector in zip(*params): q = Quaternion([scalar, *vector]) if self.normalize: q.normalize() quaternionList.append(q) elif self.mode == "EULER": I = [inputs["Angle " + n].sv_get()[0] for n in "XYZ"] params = match_long_repeat(I) au = angleConversion[self.angleUnits] for angleX, angleY, angleZ in zip(*params): euler = Euler((angleX * au, angleY * au, angleZ * au), self.eulerOrder) q = euler.to_quaternion() if self.normalize: q.normalize() quaternionList.append(q) elif self.mode == "AXISANGLE": I = [inputs[n].sv_get()[0] for n in ["Axis", "Angle"]] params = match_long_repeat(I) au = angleConversion[self.angleUnits] for axis, angle in zip(*params): q = Quaternion(axis, angle * au) if self.normalize: q.normalize() quaternionList.append(q) elif self.mode == "MATRIX": input_M = inputs["Matrix"].sv_get(default=idMat) for m in input_M: q = Matrix(m).to_quaternion() if self.normalize: q.normalize() quaternionList.append(q) self.outputs['Quaternions'].sv_set([quaternionList])
def process(self): if not any(socket.is_linked for socket in self.outputs): return location_s = self.inputs['Location'].sv_get() month_s = self.inputs['Month'].sv_get() day_s = self.inputs['Day'].sv_get() hour_s = self.inputs['Hour'].sv_get() altitude_s, azimuth_s = [], [] sun_pos = [] for location, months, days, hours in zip_long_repeat( location_s, month_s, day_s, hour_s): sp = Sunpath.from_location(location) # sp = Sunpath.from_location(location, north_angle=40) altitude, azimuth = [], [] for mo, day, ho in zip_long_repeat(months, days, hours): sun = sp.calculate_sun(month=mo, day=day, hour=ho) alt_l = sun.altitude azi_l = sun.azimuth altitude.append(sun.altitude) azimuth.append(sun.azimuth) angles = (alt_l * pi / 180, 0, -azi_l * pi / 180 + self.north_angle * pi / 180) euler = Euler(angles, 'XYZ') mat_r = euler.to_quaternion().to_matrix().to_4x4() pos = mat_r @ Vector((0, self.sun_dist, 0)) mat_t = Matrix.Translation(pos) angles = ((90 - alt_l) * pi / 180, 0, (180 - azi_l) * pi / 180 + self.north_angle * pi / 180) euler = Euler(angles, 'XYZ') mat_r = euler.to_quaternion().to_matrix().to_4x4() m = mat_t @ mat_r sun_pos.append(m) altitude_s.append(altitude) azimuth_s.append(azimuth) self.outputs['Altitude'].sv_set(altitude_s) self.outputs['Azimuth'].sv_set(azimuth_s) self.outputs['Sun Position'].sv_set(sun_pos)
def main(_self, entity_object, plugin_data): # Restore Euler structure new_orientation = [0, 0, 0] for value in plugin_data: new_orientation[value[1]] = value[0] euler_orientation = Euler(new_orientation) # Interpolate between data and use for extrapolation interpolator = setdefaultdict(entity_object, "interpolate_orientation", interpolate([entity_object.worldOrientation.to_quaternion(), 0.0])) orientation = [euler_orientation.to_quaternion(), time.time()] interpolator.update(orientation) entity_object.worldOrientation = euler_orientation.to_matrix()
def update_rotations_to_quat(obj, path_check, new_data_path_prefix=""): obj.rotation_mode = "QUATERNION" actions = [ action for action in bpy.data.actions if action.name.startswith(obj.name) ] for action in actions: data_paths = set([ fcurve.data_path for fcurve in action.fcurves if path_check(fcurve.data_path) ]) for data_path in data_paths: fcurves = [ fcurve for fcurve in action.fcurves if fcurve.data_path == data_path ] group = next((fcurve.group for fcurve in fcurves if fcurve.group is not None), None) if not fcurves: continue frames = fcurves.frames_matching(action, data_path) euler = Euler((0.0, 0.0, 0.0), "XYZ") degrees = [0.0, 0.0, 0.0] for fr in frames: for fc in fcurves: euler[fc.array_index] = fc.evaluate(fr) degrees[fc.array_index] = math.degrees( euler[fc.array_index]) quat = euler.to_quaternion() fcurves.add_keyframe_quat( action, quat, fr, "{0}rotation_quaternion".format(new_data_path_prefix), group, ) for fcurve in fcurves: action.fcurves.remove(fcurve)
def test_loc_rot_scale(self): euler = Euler((math.radians(90), 0, math.radians(90)), 'ZYX') expected = Matrix( ((0, -5, 0, 1), (0, 0, -6, 2), (4, 0, 0, 3), (0, 0, 0, 1))) result = Matrix.LocRotScale((1, 2, 3), euler, (4, 5, 6)) self.assertAlmostEqualMatrix(result, expected, 4) result = Matrix.LocRotScale((1, 2, 3), euler.to_quaternion(), (4, 5, 6)) self.assertAlmostEqualMatrix(result, expected, 4) result = Matrix.LocRotScale((1, 2, 3), euler.to_matrix(), (4, 5, 6)) self.assertAlmostEqualMatrix(result, expected, 4)
def process(self): if not self.outputs['Quaternions'].is_linked: return inputs = self.inputs quaternionList = [] if self.mode == "WXYZ": I = [inputs[n].sv_get()[0] for n in "WXYZ"] params = match_long_repeat(I) for wxyz in zip(*params): q = Quaternion(wxyz) if self.normalize: q.normalize() quaternionList.append(q) elif self.mode == "EULER": I = [inputs["Angle " + n].sv_get()[0] for n in "XYZ"] params = match_long_repeat(I) au = angleConversion[self.angleUnits] for angleX, angleY, angleZ in zip(*params): euler = Euler((angleX * au, angleY * au, angleZ * au), self.eulerOrder) q = euler.to_quaternion() if self.normalize: q.normalize() quaternionList.append(q) elif self.mode == "AXISANGLE": I = [inputs[n].sv_get()[0] for n in {"Axis", "Angle"}] params = match_long_repeat(I) au = angleConversion[self.angleUnits] for axis, angle in zip(*params): q = Quaternion(axis, angle * au) if self.normalize: q.normalize() quaternionList.append(q) elif self.mode == "MATRIX": input_M = inputs["Matrix"].sv_get(default=idMat) for m in input_M: q = Matrix(m).to_quaternion() if self.normalize: q.normalize() quaternionList.append(q) self.outputs['Quaternions'].sv_set([quaternionList])
def draw_callback_px(self, context): font_id = 0 region = context.region UIColor = (0.992, 0.5518, 0.0, 1.0) # Cut Type RECTANGLE = 0 LINE = 1 CIRCLE = 2 self.carver_prefs = context.preferences.addons[__package__].preferences # Color color1 = (1.0, 1.0, 1.0, 1.0) color2 = UIColor #The mouse is outside the active region if not self.in_view_3d: color1 = color2 = (1.0, 0.2, 0.1, 1.0) # Primitives type PrimitiveType = "Rectangle" if self.CutType == CIRCLE: PrimitiveType = "Circle" if self.CutType == LINE: PrimitiveType = "Line" # Width screen overlap = context.preferences.system.use_region_overlap t_panel_width = 0 if overlap: for region in context.area.regions: if region.type == 'TOOLS': t_panel_width = region.width # Initial position region_width = int(region.width / 2.0) y_txt = 10 # Draw the center command from bottom to top # Get the size of the text text_size = 18 if region.width >= 850 else 12 blf.size(0, int(round(text_size * bpy.context.preferences.view.ui_scale, 0)), 72) # Help Display if (self.ObjectMode is False) and (self.ProfileMode is False): # Depth Cursor TypeStr = "Cursor Depth [" + self.carver_prefs.Key_Depth + "]" BoolStr = "(ON)" if self.snapCursor else "(OFF)" help_txt = [[TypeStr, BoolStr]] # Close poygonal shape if self.CreateMode and self.CutType == LINE: TypeStr = "Close [" + self.carver_prefs.Key_Close + "]" BoolStr = "(ON)" if self.Closed else "(OFF)" help_txt += [[TypeStr, BoolStr]] if self.CreateMode is False: # Apply Booleans TypeStr = "Apply Operations [" + self.carver_prefs.Key_Apply + "]" BoolStr = "(OFF)" if self.dont_apply_boolean else "(ON)" help_txt += [[TypeStr, BoolStr]] #Auto update for bevel TypeStr = "Bevel Update [" + self.carver_prefs.Key_Update + "]" BoolStr = "(ON)" if self.Auto_BevelUpdate else "(OFF)" help_txt += [[TypeStr, BoolStr]] # Circle subdivisions if self.CutType == CIRCLE: TypeStr = "Subdivisions [" + self.carver_prefs.Key_Subrem + "][" + self.carver_prefs.Key_Subadd + "]" BoolStr = str((int(360 / self.stepAngle[self.step]))) help_txt += [[TypeStr, BoolStr]] if self.CreateMode: help_txt += [["Type [Space]", PrimitiveType]] else: help_txt += [["Cut Type [Space]", PrimitiveType]] else: # Instantiate TypeStr = "Instantiate [" + self.carver_prefs.Key_Instant + "]" BoolStr = "(ON)" if self.Instantiate else "(OFF)" help_txt = [[TypeStr, BoolStr]] # Random rotation if self.alt: TypeStr = "Random Rotation [" + self.carver_prefs.Key_Randrot + "]" BoolStr = "(ON)" if self.RandomRotation else "(OFF)" help_txt += [[TypeStr, BoolStr]] # Thickness if self.BrushSolidify: TypeStr = "Thickness [" + self.carver_prefs.Key_Depth + "]" if self.ProfileMode: BoolStr = str( round(self.ProfileBrush.modifiers["CT_SOLIDIFY"].thickness, 2)) if self.ObjectMode: BoolStr = str( round(self.ObjectBrush.modifiers["CT_SOLIDIFY"].thickness, 2)) help_txt += [[TypeStr, BoolStr]] # Brush depth if (self.ObjectMode): TypeStr = "Carve Depth [" + self.carver_prefs.Key_Depth + "]" BoolStr = str(round(self.ObjectBrush.data.vertices[0].co.z, 2)) help_txt += [[TypeStr, BoolStr]] TypeStr = "Brush Depth [" + self.carver_prefs.Key_BrushDepth + "]" BoolStr = str(round(self.BrushDepthOffset, 2)) help_txt += [[TypeStr, BoolStr]] help_txt, bloc_height, max_option, max_key, comma = get_text_info( self, context, help_txt) xCmd = region_width - (max_option + max_key + comma) / 2 draw_string(self, color1, color2, xCmd, y_txt, help_txt, max_option, divide=2) # Separator (Line) LineWidth = (max_option + max_key + comma) / 2 if region.width >= 850: LineWidth = 140 LineWidth = (max_option + max_key + comma) coords = [(int(region_width - LineWidth/2), y_txt + bloc_height + 8), \ (int(region_width + LineWidth/2), y_txt + bloc_height + 8)] draw_shader(self, UIColor, 1, 'LINES', coords, self.carver_prefs.LineWidth) # Command Display if self.CreateMode and ((self.ObjectMode is False) and (self.ProfileMode is False)): BooleanMode = "Create" else: if self.ObjectMode or self.ProfileMode: BooleanType = "Difference) [T]" if self.BoolOps == self.difference else "Union) [T]" BooleanMode = \ "Object Brush (" + BooleanType if self.ObjectMode else "Profil Brush (" + BooleanType else: BooleanMode = \ "Difference" if (self.shift is False) and (self.ForceRebool is False) else "Rebool" # Display boolean mode text_size = 40 if region.width >= 850 else 20 blf.size(0, int(round(text_size * bpy.context.preferences.view.ui_scale, 0)), 72) draw_string(self, color2, color2, region_width - (blf.dimensions(0, BooleanMode)[0]) / 2, \ y_txt + bloc_height + 16, BooleanMode, 0, divide = 2) if region.width >= 850: if self.AskHelp is False: # "H for Help" text blf.size(0, int(round(13 * bpy.context.preferences.view.ui_scale, 0)), 72) help_txt = "[" + self.carver_prefs.Key_Help + "] for help" txt_width = blf.dimensions(0, help_txt)[0] txt_height = (blf.dimensions(0, "gM")[1] * 1.45) # Draw a rectangle and put the text "H for Help" xrect = 40 yrect = 40 rect_vertices = [(xrect - 5, yrect - 5), (xrect + txt_width + 5, yrect - 5), \ (xrect + txt_width + 5, yrect + txt_height + 5), (xrect - 5, yrect + txt_height + 5)] draw_shader(self, (0.0, 0.0, 0.0), 0.3, 'TRI_FAN', rect_vertices, self.carver_prefs.LineWidth) draw_string(self, color1, color2, xrect, yrect, help_txt, 0) else: #Draw the help text xHelp = 30 + t_panel_width yHelp = 10 if self.ObjectMode or self.ProfileMode: if self.ProfileMode: help_txt = [["Object Mode", self.carver_prefs.Key_Brush]] else: help_txt = [["Cut Mode", self.carver_prefs.Key_Brush]] else: help_txt =[ ["Profil Brush", self.carver_prefs.Key_Brush],\ ["Move Cursor", "Ctrl + LMB"] ] if (self.ObjectMode is False) and (self.ProfileMode is False): if self.CreateMode is False: help_txt +=[ ["Create geometry", self.carver_prefs.Key_Create],\ ] else: help_txt +=[ ["Cut", self.carver_prefs.Key_Create],\ ] if self.CutMode == RECTANGLE: help_txt +=[ ["Dimension", "MouseMove"],\ ["Move all", "Alt"],\ ["Validate", "LMB"],\ ["Rebool", "Shift"] ] elif self.CutMode == CIRCLE: help_txt +=[ ["Rotation and Radius", "MouseMove"],\ ["Move all", "Alt"],\ ["Subdivision", self.carver_prefs.Key_Subrem + " " + self.carver_prefs.Key_Subadd],\ ["Incremental rotation", "Ctrl"],\ ["Rebool", "Shift"] ] elif self.CutMode == LINE: help_txt +=[ ["Dimension", "MouseMove"],\ ["Move all", "Alt"],\ ["Validate", "Space"],\ ["Rebool", "Shift"],\ ["Snap", "Ctrl"],\ ["Scale Snap", "WheelMouse"],\ ] else: # ObjectMode help_txt +=[ ["Difference", "Space"],\ ["Rebool", "Shift + Space"],\ ["Duplicate", "Alt + Space"],\ ["Scale", self.carver_prefs.Key_Scale],\ ["Rotation", "LMB + Move"],\ ["Step Angle", "CTRL + LMB + Move"],\ ] if self.ProfileMode: help_txt += [[ "Previous or Next Profile", self.carver_prefs.Key_Subadd + " " + self.carver_prefs.Key_Subrem ]] help_txt +=[ ["Create / Delete rows", chr(8597)],\ ["Create / Delete cols", chr(8596)],\ ["Gap for rows or columns", self.carver_prefs.Key_Gapy + " " + self.carver_prefs.Key_Gapx] ] blf.size(0, int(round(15 * bpy.context.preferences.view.ui_scale, 0)), 72) help_txt, bloc_height, max_option, max_key, comma = get_text_info( self, context, help_txt) draw_string(self, color1, color2, xHelp, yHelp, help_txt, max_option) if self.ProfileMode: xrect = region.width - t_panel_width - 80 yrect = 80 coords = [(xrect, yrect), (xrect + 60, yrect), (xrect + 60, yrect - 60), (xrect, yrect - 60)] # Draw rectangle background in the lower right draw_shader(self, (0.0, 0.0, 0.0), 0.3, 'TRI_FAN', coords, size=self.carver_prefs.LineWidth) # Use numpy to get the vertices and indices of the profile object to draw WidthProfil = 50 location = Vector((region.width - t_panel_width - WidthProfil, 50, 0)) ProfilScale = 20.0 coords = [] mesh = bpy.data.meshes[self.Profils[self.nProfil][0]] mesh.calc_loop_triangles() vertices = np.empty((len(mesh.vertices), 3), 'f') indices = np.empty((len(mesh.loop_triangles), 3), 'i') mesh.vertices.foreach_get("co", np.reshape(vertices, len(mesh.vertices) * 3)) mesh.loop_triangles.foreach_get( "vertices", np.reshape(indices, len(mesh.loop_triangles) * 3)) for idx, vals in enumerate(vertices): coords.append([ vals[0] * ProfilScale + location.x, vals[1] * ProfilScale + location.y, vals[2] * ProfilScale + location.z ]) #Draw the silhouette of the mesh draw_shader(self, UIColor, 0.5, 'TRIS', coords, size=self.carver_prefs.LineWidth, indices=indices) if self.CutMode: if len(self.mouse_path) > 1: x0 = self.mouse_path[0][0] y0 = self.mouse_path[0][1] x1 = self.mouse_path[1][0] y1 = self.mouse_path[1][1] # Cut rectangle if self.CutType == RECTANGLE: coords = [ (x0 + self.xpos, y0 + self.ypos), (x1 + self.xpos, y0 + self.ypos), \ (x1 + self.xpos, y1 + self.ypos), (x0 + self.xpos, y1 + self.ypos) ] indices = ((0, 1, 2), (2, 0, 3)) self.rectangle_coord = coords draw_shader(self, UIColor, 1, 'LINE_LOOP', coords, size=self.carver_prefs.LineWidth) #Draw points draw_shader(self, UIColor, 1, 'POINTS', coords, size=3) if self.shift or self.CreateMode: draw_shader(self, UIColor, 0.5, 'TRIS', coords, size=self.carver_prefs.LineWidth, indices=indices) # Draw grid (based on the overlay options) to show the incremental snapping if self.ctrl: mini_grid(self, context, UIColor) # Cut Line elif self.CutType == LINE: coords = [] indices = [] top_grid = False for idx, vals in enumerate(self.mouse_path): coords.append([vals[0] + self.xpos, vals[1] + self.ypos]) indices.append([idx]) # Draw lines if self.Closed: draw_shader(self, UIColor, 1.0, 'LINE_LOOP', coords, size=self.carver_prefs.LineWidth) else: draw_shader(self, UIColor, 1.0, 'LINE_STRIP', coords, size=self.carver_prefs.LineWidth) # Draw points draw_shader(self, UIColor, 1.0, 'POINTS', coords, size=3) # Draw polygon if (self.shift) or (self.CreateMode and self.Closed): draw_shader(self, UIColor, 0.5, 'TRI_FAN', coords, size=self.carver_prefs.LineWidth) # Draw grid (based on the overlay options) to show the incremental snapping if self.ctrl: mini_grid(self, context, UIColor) # Circle Cut elif self.CutType == CIRCLE: # Create a circle using a tri fan tris_coords, indices = draw_circle(self, x0, y0) # Remove the vertex in the center to get the outer line of the circle line_coords = tris_coords[1:] draw_shader(self, UIColor, 1.0, 'LINE_LOOP', line_coords, size=self.carver_prefs.LineWidth) if self.shift or self.CreateMode: draw_shader(self, UIColor, 0.5, 'TRIS', tris_coords, size=self.carver_prefs.LineWidth, indices=indices) if (self.ObjectMode or self.ProfileMode) and len(self.CurrentSelection) > 0: if self.ShowCursor: region = context.region rv3d = context.space_data.region_3d if self.ObjectMode: ob = self.ObjectBrush if self.ProfileMode: ob = self.ProfileBrush mat = ob.matrix_world # 50% alpha, 2 pixel width line bgl.glEnable(bgl.GL_BLEND) bbox = [mat @ Vector(b) for b in ob.bound_box] objBBDiagonal = objDiagonal(self.CurrentSelection[0]) if self.shift: gl_size = 4 UIColor = (0.5, 1.0, 0.0, 1.0) else: gl_size = 2 UIColor = (1.0, 0.8, 0.0, 1.0) line_coords = [] idx = 0 CRadius = ((bbox[7] - bbox[0]).length) / 2 for i in range(int(len(self.CLR_C) / 3)): vector3d = (self.CLR_C[idx * 3] * CRadius + self.CurLoc.x, \ self.CLR_C[idx * 3 + 1] * CRadius + self.CurLoc.y, \ self.CLR_C[idx * 3 + 2] * CRadius + self.CurLoc.z) vector2d = bpy_extras.view3d_utils.location_3d_to_region_2d( region, rv3d, vector3d) if vector2d is not None: line_coords.append((vector2d[0], vector2d[1])) idx += 1 if len(line_coords) > 0: draw_shader(self, UIColor, 1.0, 'LINE_LOOP', line_coords, size=gl_size) # Object display if self.quat_rot is not None: ob.location = self.CurLoc v = Vector() v.x = v.y = 0.0 v.z = self.BrushDepthOffset ob.location += self.quat_rot @ v e = Euler() e.x = 0.0 e.y = 0.0 e.z = self.aRotZ / 25.0 qe = e.to_quaternion() qRot = self.quat_rot @ qe ob.rotation_mode = 'QUATERNION' ob.rotation_quaternion = qRot ob.rotation_mode = 'XYZ' if self.ProfileMode: if self.ProfileBrush is not None: self.ProfileBrush.location = self.CurLoc self.ProfileBrush.rotation_mode = 'QUATERNION' self.ProfileBrush.rotation_quaternion = qRot self.ProfileBrush.rotation_mode = 'XYZ' # Opengl defaults bgl.glLineWidth(1) bgl.glDisable(bgl.GL_BLEND)
def duplicateObject(self): if self.Instantiate: bpy.ops.object.duplicate_move_linked( OBJECT_OT_duplicate={ "linked": True, "mode": 'TRANSLATION', }, TRANSFORM_OT_translate={ "value": (0, 0, 0), }, ) else: bpy.ops.object.duplicate_move( OBJECT_OT_duplicate={ "linked": False, "mode": 'TRANSLATION', }, TRANSFORM_OT_translate={ "value": (0, 0, 0), }, ) ob_new = bpy.context.active_object ob_new.location = self.CurLoc v = Vector() v.x = v.y = 0.0 v.z = self.BrushDepthOffset ob_new.location += self.qRot * v if self.ObjectMode: ob_new.scale = self.ObjectBrush.scale if self.ProfileMode: ob_new.scale = self.ProfileBrush.scale e = Euler() e.x = e.y = 0.0 e.z = self.aRotZ / 25.0 # If duplicate with a grid, no random rotation (each mesh in the grid is already rotated randomly) if (self.alt is True) and ((self.nbcol + self.nbrow) < 3): if self.RandomRotation: e.z += random.random() qe = e.to_quaternion() qRot = self.qRot * qe ob_new.rotation_mode = 'QUATERNION' ob_new.rotation_quaternion = qRot ob_new.rotation_mode = 'XYZ' if (ob_new.display_type == "WIRE") and (self.BrushSolidify is False): ob_new.hide_viewport = True if self.BrushSolidify: ob_new.display_type = "SOLID" ob_new.show_in_front = False for o in bpy.context.selected_objects: UndoAdd(self, "DUPLICATE", o) if len(bpy.context.selected_objects) > 0: bpy.ops.object.select_all(action='TOGGLE') for o in self.all_sel_obj_list: o.select_set(True) bpy.context.view_layer.objects.active = self.OpsObj
def bvh_node_dict2armature(context, bvh_name, bvh_nodes, rotate_mode='XYZ', frame_start=1, IMPORT_LOOP=False, global_matrix=None, ): if frame_start < 1: frame_start = 1 # Add the new armature, scene = context.scene for obj in scene.objects: obj.select = False arm_data = bpy.data.armatures.new(bvh_name) arm_ob = bpy.data.objects.new(bvh_name, arm_data) scene.objects.link(arm_ob) arm_ob.select = True scene.objects.active = arm_ob bpy.ops.object.mode_set(mode='OBJECT', toggle=False) bpy.ops.object.mode_set(mode='EDIT', toggle=False) # Get the average bone length for zero length bones, we may not use this. average_bone_length = 0.0 nonzero_count = 0 for bvh_node in bvh_nodes.values(): l = (bvh_node.rest_head_local - bvh_node.rest_tail_local).length if l: average_bone_length += l nonzero_count += 1 # Very rare cases all bones couldbe zero length??? if not average_bone_length: average_bone_length = 0.1 else: # Normal operation average_bone_length = average_bone_length / nonzero_count # XXX, annoying, remove bone. while arm_data.edit_bones: arm_ob.edit_bones.remove(arm_data.edit_bones[-1]) ZERO_AREA_BONES = [] for name, bvh_node in bvh_nodes.items(): # New editbone bone = bvh_node.temp = arm_data.edit_bones.new(name) bone.head = bvh_node.rest_head_world bone.tail = bvh_node.rest_tail_world # Zero Length Bones! (an exceptional case) if (bone.head - bone.tail).length < 0.001: print("\tzero length bone found:", bone.name) if bvh_node.parent: ofs = bvh_node.parent.rest_head_local - bvh_node.parent.rest_tail_local if ofs.length: # is our parent zero length also?? unlikely bone.tail = bone.tail - ofs else: bone.tail.y = bone.tail.y + average_bone_length else: bone.tail.y = bone.tail.y + average_bone_length ZERO_AREA_BONES.append(bone.name) for bvh_node in bvh_nodes.values(): if bvh_node.parent: # bvh_node.temp is the Editbone # Set the bone parent bvh_node.temp.parent = bvh_node.parent.temp # Set the connection state if not bvh_node.has_loc and\ bvh_node.parent and\ bvh_node.parent.temp.name not in ZERO_AREA_BONES and\ bvh_node.parent.rest_tail_local == bvh_node.rest_head_local: bvh_node.temp.use_connect = True # Replace the editbone with the editbone name, # to avoid memory errors accessing the editbone outside editmode for bvh_node in bvh_nodes.values(): bvh_node.temp = bvh_node.temp.name # Now Apply the animation to the armature # Get armature animation data bpy.ops.object.mode_set(mode='OBJECT', toggle=False) pose = arm_ob.pose pose_bones = pose.bones if rotate_mode == 'NATIVE': for bvh_node in bvh_nodes.values(): bone_name = bvh_node.temp # may not be the same name as the bvh_node, could have been shortened. pose_bone = pose_bones[bone_name] pose_bone.rotation_mode = bvh_node.rot_order_str elif rotate_mode != 'QUATERNION': for pose_bone in pose_bones: pose_bone.rotation_mode = rotate_mode else: # Quats default pass context.scene.update() arm_ob.animation_data_create() action = bpy.data.actions.new(name=bvh_name) arm_ob.animation_data.action = action # Replace the bvh_node.temp (currently an editbone) # With a tuple (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv) for bvh_node in bvh_nodes.values(): bone_name = bvh_node.temp # may not be the same name as the bvh_node, could have been shortened. pose_bone = pose_bones[bone_name] rest_bone = arm_data.bones[bone_name] bone_rest_matrix = rest_bone.matrix_local.to_3x3() bone_rest_matrix_inv = Matrix(bone_rest_matrix) bone_rest_matrix_inv.invert() bone_rest_matrix_inv.resize_4x4() bone_rest_matrix.resize_4x4() bvh_node.temp = (pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv) # Make a dict for fast access without rebuilding a list all the time. # KEYFRAME METHOD, SLOW, USE IPOS DIRECT # TODO: use f-point samples instead (Aligorith) if rotate_mode != 'QUATERNION': prev_euler = [Euler() for i in range(len(bvh_nodes))] # Animate the data, the last used bvh_node will do since they all have the same number of frames for frame_current in range(len(bvh_node.anim_data) - 1): # skip the first frame (rest frame) # print frame_current # if frame_current==40: # debugging # break scene.frame_set(frame_start + frame_current) # Dont neet to set the current frame for i, bvh_node in enumerate(bvh_nodes.values()): pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv = bvh_node.temp lx, ly, lz, rx, ry, rz = bvh_node.anim_data[frame_current + 1] if bvh_node.has_rot: # apply rotation order and convert to XYZ # note that the rot_order_str is reversed. bone_rotation_matrix = Euler((rx, ry, rz), bvh_node.rot_order_str[::-1]).to_matrix().to_4x4() bone_rotation_matrix = bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix if rotate_mode == 'QUATERNION': pose_bone.rotation_quaternion = bone_rotation_matrix.to_quaternion() else: euler = bone_rotation_matrix.to_euler(pose_bone.rotation_mode, prev_euler[i]) pose_bone.rotation_euler = euler prev_euler[i] = euler if bvh_node.has_loc: pose_bone.location = (bone_rest_matrix_inv * Matrix.Translation(Vector((lx, ly, lz)) - bvh_node.rest_head_local)).to_translation() if bvh_node.has_loc: pose_bone.keyframe_insert("location") if bvh_node.has_rot: if rotate_mode == 'QUATERNION': pose_bone.keyframe_insert("rotation_quaternion") else: pose_bone.keyframe_insert("rotation_euler") for cu in action.fcurves: if IMPORT_LOOP: pass # 2.5 doenst have cyclic now? for bez in cu.keyframe_points: bez.interpolation = 'LINEAR' # finally apply matrix arm_ob.matrix_world = global_matrix bpy.ops.object.transform_apply(rotation=True) return arm_ob
def bvh_node_dict2armature( context, bvh_name, bvh_nodes, rotate_mode='XYZ', frame_start=1, IMPORT_LOOP=False, global_matrix=None, ): if frame_start < 1: frame_start = 1 # Add the new armature, scene = context.scene for obj in scene.objects: obj.select = False arm_data = bpy.data.armatures.new(bvh_name) arm_ob = bpy.data.objects.new(bvh_name, arm_data) scene.objects.link(arm_ob) arm_ob.select = True scene.objects.active = arm_ob bpy.ops.object.mode_set(mode='OBJECT', toggle=False) bpy.ops.object.mode_set(mode='EDIT', toggle=False) bvh_nodes_list = sorted_nodes(bvh_nodes) # Get the average bone length for zero length bones, we may not use this. average_bone_length = 0.0 nonzero_count = 0 for bvh_node in bvh_nodes_list: l = (bvh_node.rest_head_local - bvh_node.rest_tail_local).length if l: average_bone_length += l nonzero_count += 1 # Very rare cases all bones could be zero length??? if not average_bone_length: average_bone_length = 0.1 else: # Normal operation average_bone_length = average_bone_length / nonzero_count # XXX, annoying, remove bone. while arm_data.edit_bones: arm_ob.edit_bones.remove(arm_data.edit_bones[-1]) ZERO_AREA_BONES = [] for bvh_node in bvh_nodes_list: # New editbone bone = bvh_node.temp = arm_data.edit_bones.new(bvh_node.name) bone.head = bvh_node.rest_head_world bone.tail = bvh_node.rest_tail_world # Zero Length Bones! (an exceptional case) if (bone.head - bone.tail).length < 0.001: print("\tzero length bone found:", bone.name) if bvh_node.parent: ofs = bvh_node.parent.rest_head_local - bvh_node.parent.rest_tail_local if ofs.length: # is our parent zero length also?? unlikely bone.tail = bone.tail - ofs else: bone.tail.y = bone.tail.y + average_bone_length else: bone.tail.y = bone.tail.y + average_bone_length ZERO_AREA_BONES.append(bone.name) for bvh_node in bvh_nodes_list: if bvh_node.parent: # bvh_node.temp is the Editbone # Set the bone parent bvh_node.temp.parent = bvh_node.parent.temp # Set the connection state if ((not bvh_node.has_loc) and (bvh_node.parent.temp.name not in ZERO_AREA_BONES) and (bvh_node.parent.rest_tail_local == bvh_node.rest_head_local)): bvh_node.temp.use_connect = True # Replace the editbone with the editbone name, # to avoid memory errors accessing the editbone outside editmode for bvh_node in bvh_nodes_list: bvh_node.temp = bvh_node.temp.name # Now Apply the animation to the armature # Get armature animation data bpy.ops.object.mode_set(mode='OBJECT', toggle=False) pose = arm_ob.pose pose_bones = pose.bones if rotate_mode == 'NATIVE': for bvh_node in bvh_nodes_list: bone_name = bvh_node.temp # may not be the same name as the bvh_node, could have been shortened. pose_bone = pose_bones[bone_name] pose_bone.rotation_mode = bvh_node.rot_order_str elif rotate_mode != 'QUATERNION': for pose_bone in pose_bones: pose_bone.rotation_mode = rotate_mode else: # Quats default pass context.scene.update() arm_ob.animation_data_create() action = bpy.data.actions.new(name=bvh_name) arm_ob.animation_data.action = action # Replace the bvh_node.temp (currently an editbone) # With a tuple (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv) for bvh_node in bvh_nodes_list: bone_name = bvh_node.temp # may not be the same name as the bvh_node, could have been shortened. pose_bone = pose_bones[bone_name] rest_bone = arm_data.bones[bone_name] bone_rest_matrix = rest_bone.matrix_local.to_3x3() bone_rest_matrix_inv = Matrix(bone_rest_matrix) bone_rest_matrix_inv.invert() bone_rest_matrix_inv.resize_4x4() bone_rest_matrix.resize_4x4() bvh_node.temp = (pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv) # Make a dict for fast access without rebuilding a list all the time. # KEYFRAME METHOD, SLOW, USE IPOS DIRECT # TODO: use f-point samples instead (Aligorith) if rotate_mode != 'QUATERNION': prev_euler = [Euler() for i in range(len(bvh_nodes))] # Animate the data, the last used bvh_node will do since they all have the same number of frames for frame_current in range(len(bvh_node.anim_data) - 1): # skip the first frame (rest frame) # print frame_current # if frame_current==40: # debugging # break scene.frame_set(frame_start + frame_current) # Dont neet to set the current frame for i, bvh_node in enumerate(bvh_nodes_list): pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv = bvh_node.temp lx, ly, lz, rx, ry, rz = bvh_node.anim_data[frame_current + 1] if bvh_node.has_rot: # apply rotation order and convert to XYZ # note that the rot_order_str is reversed. bone_rotation_matrix = Euler( (rx, ry, rz), bvh_node.rot_order_str[::-1]).to_matrix().to_4x4() bone_rotation_matrix = bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix if rotate_mode == 'QUATERNION': pose_bone.rotation_quaternion = bone_rotation_matrix.to_quaternion( ) else: euler = bone_rotation_matrix.to_euler( pose_bone.rotation_mode, prev_euler[i]) pose_bone.rotation_euler = euler prev_euler[i] = euler if bvh_node.has_loc: pose_bone.location = ( bone_rest_matrix_inv * Matrix.Translation( Vector((lx, ly, lz)) - bvh_node.rest_head_local)).to_translation() if bvh_node.has_loc: pose_bone.keyframe_insert("location") if bvh_node.has_rot: if rotate_mode == 'QUATERNION': pose_bone.keyframe_insert("rotation_quaternion") else: pose_bone.keyframe_insert("rotation_euler") for cu in action.fcurves: if IMPORT_LOOP: pass # 2.5 doenst have cyclic now? for bez in cu.keyframe_points: bez.interpolation = 'LINEAR' # finally apply matrix arm_ob.matrix_world = global_matrix bpy.ops.object.transform_apply(rotation=True) return arm_ob
def process(self): if not self.outputs['Matrices'].is_linked: return inputs = self.inputs matrix_list = [] add_matrix = matrix_list.extend if self.flat_output else matrix_list.append if self.mode == "QUATERNION": input_l = inputs["Location"].sv_get(deepcopy=False) input_s = inputs["Scale"].sv_get(deepcopy=False) input_q = inputs["Quaternion"].sv_get(deepcopy=False) if inputs["Quaternion"].is_linked: input_q = [input_q] else: input_q = [[Quaternion(input_q[0][0])]] I = [input_l, input_q, input_s] params1 = match_long_repeat(I) for ll, ql, sl in zip(*params1): params2 = match_long_repeat([ll, ql, sl]) matrices = [] for location, quaternion, scale in zip(*params2): # translation mat_t[0][3] = location[0] mat_t[1][3] = location[1] mat_t[2][3] = location[2] # rotation mat_r = quaternion.to_matrix().to_4x4() # scale mat_s[0][0] = scale[0] mat_s[1][1] = scale[1] mat_s[2][2] = scale[2] # composite matrix m = mat_t @ mat_r @ mat_s matrices.append(m) add_matrix(matrices) elif self.mode == "EULER": socket_names = [ "Location", "Angle X", "Angle Y", "Angle Z", "Scale" ] I = [inputs[name].sv_get(deepcopy=False) for name in socket_names] params1 = match_long_repeat(I) auc = angle_unit_conversion[self.angle_units][ "RAD"] # convert to radians for ll, axl, ayl, azl, sl in zip(*params1): params2 = match_long_repeat([ll, axl, ayl, azl, sl]) matrices = [] for location, angleX, angleY, angleZ, scale in zip(*params2): # translation mat_t[0][3] = location[0] mat_t[1][3] = location[1] mat_t[2][3] = location[2] # rotation angles = (angleX * auc, angleY * auc, angleZ * auc) euler = Euler(angles, self.euler_order) mat_r = euler.to_quaternion().to_matrix().to_4x4() # scale mat_s[0][0] = scale[0] mat_s[1][1] = scale[1] mat_s[2][2] = scale[2] # composite matrix m = mat_t @ mat_r @ mat_s matrices.append(m) add_matrix(matrices) elif self.mode == "AXISANGLE": socket_names = ["Location", "Axis", "Angle", "Scale"] I = [inputs[name].sv_get(deepcopy=False) for name in socket_names] params1 = match_long_repeat(I) auc = angle_unit_conversion[self.angle_units][ "RAD"] # convert to radians for ll, xl, al, sl in zip(*params1): params2 = match_long_repeat([ll, xl, al, sl]) matrices = [] for location, axis, angle, scale in zip(*params2): # translation mat_t[0][3] = location[0] mat_t[1][3] = location[1] mat_t[2][3] = location[2] # rotation mat_r = Quaternion(axis, angle * auc).to_matrix().to_4x4() # scale mat_s[0][0] = scale[0] mat_s[1][1] = scale[1] mat_s[2][2] = scale[2] # composite matrix m = mat_t @ mat_r @ mat_s matrices.append(m) add_matrix(matrices) self.outputs['Matrices'].sv_set(matrix_list)
def add_rotation_keyframe(object, group, translations, pivot, global_matrix): from mathutils import Vector from mathutils import Euler c1 = group.channels[0] # x c2 = group.channels[1] # y c3 = group.channels[2] # z totalTime = 0.0 type = 1 prevX = 0.0 prevY = 0.0 prevZ = 0.0 keys = setup_keyframe_list([c1, c2, c3]) #print(len(keys)) #print(keys) # be sure to offset by the first frame's rotation for key in keys: if key['frame'] == 1.0: prevX = key['data'][0] prevY = key['data'][1] prevZ = key['data'][2] break for key in keys: x = key['data'][0] y = key['data'][1] z = key['data'][2] rot = Euler((x - prevX, y - prevY, z - prevZ)) #rotDelta = rot #rotDelta.x -= prevRotation.x #rotDelta.y -= prevRotation.y #rotDelta.z -= prevRotation.z quat = rot.to_quaternion() axisAngle = quat.to_axis_angle() angle = -axisAngle[1] axis = axisAngle[0] start = key['frame'] axis = Vector(global_matrix * axis) axis.normalize() axis.z = -axis.z translations.append( { "start": totalTime / 60.0, "duration": (start - totalTime) / 60.0, "type": type, "axis": axis, "angle": angle, "origin": Vector(global_matrix * pivot), "local": 0 }) prevX = x prevY = y prevZ = z totalTime = start
def grow_spline(tree_settings: TreeSettings, lvl, stem, num_split, spline_list, spline_to_bone, close_tip, kp, bone_step): out_att = tree_settings.attractOut[lvl] handle_type = tree_settings.handles # Curv at base stem_curv = stem.curv if (lvl == 0) and (kp <= tree_settings.splitHeight): stem_curv = 0.0 curve_angle = stem_curv + (uniform(0, stem.curvV) * kp * stem.curvSignx) curve_var = uniform(0, stem.curvV) * kp * stem.curvSigny stem.curvSignx *= -1 stem.curvSigny *= -1 curve_var_mat = Matrix.Rotation(curve_var, 3, 'Y') # First find the current direction of the stem current_direction = stem.quat() # Length taperCrown if lvl == 0: dec = declination(current_direction) / 180 dec = dec**2 tf = 1 - (dec * tree_settings.taperCrown * 30) tf = max(.1, tf) else: tf = 1.0 tf = 1.0 # disabled # Outward attraction if (lvl >= 0) and (kp > 0) and (out_att > 0): p = stem.p.co.copy() d = atan2(p[0], -p[1]) # + tau e_dir = current_direction.to_euler('XYZ', Euler((0, 0, d), 'XYZ')) d = angle_mean(e_dir[2], d, (kp * out_att)) dir_v = Euler((e_dir[0], e_dir[1], d), 'XYZ') current_direction = dir_v.to_quaternion() if lvl == 0: current_direction = convert_quat(current_direction) if lvl != 0: split_length = 0 # If the stem splits, we need to add new splines etc if num_split > 0: dir_vec, split_r2 = add_splines_on_split(bone_step, close_tip, curve_angle, curve_var_mat, current_direction, lvl, num_split, spline_list, spline_to_bone, stem, tf, tree_settings) else: curve_var_mat = Matrix.Rotation(curve_var, 3, 'Y') # If there are no splits then generate the growth direction without accounting for spreading of stems dir_vec = z_axis.copy() div_rot_mat = Matrix.Rotation(-curve_angle, 3, 'X') dir_vec.rotate(div_rot_mat) # Horizontal curvature variation dir_vec.rotate(curve_var_mat) dir_vec.rotate(current_direction) stem.splitlast = 0 # numSplit #keep track of numSplit for next stem # Introduce upward curvature up_rot_axis = x_axis.copy() up_rot_axis.rotate(dir_vec.to_track_quat('Z', 'Y')) curve_up_ang = curve_up(stem.vertAtt, dir_vec.to_track_quat('Z', 'Y'), stem.segMax) up_rot_mat = Matrix.Rotation(-curve_up_ang, 3, up_rot_axis) dir_vec.rotate(up_rot_mat) dir_vec.normalize() dir_vec *= stem.segL * tf # Get the end point position end_co = stem.p.co.copy() + dir_vec stem.spline.bezier_points.add(1) new_point = stem.spline.bezier_points[-1] (new_point.co, new_point.handle_left_type, new_point.handle_right_type) = (end_co, handle_type, handle_type) new_radius = stem.radS * (1 - (stem.seg + 1) / stem.segMax) + stem.radE * ( (stem.seg + 1) / stem.segMax) if num_split > 0: new_radius = max(new_radius * split_r2, tree_settings.minRadius) stem.radS = max(stem.radS * split_r2, tree_settings.minRadius) stem.radE = max(stem.radE * split_r2, tree_settings.minRadius) new_radius = max(new_radius, stem.radE) if (stem.seg == stem.segMax - 1) and close_tip: new_radius = 0.0 new_point.radius = new_radius # Set bezier handles for first point. if len(stem.spline.bezier_points) == 2: temp_point = stem.spline.bezier_points[0] if handle_type is 'AUTO': dir_vec = z_axis.copy() dir_vec.rotate(current_direction) dir_vec = dir_vec * stem.segL * 0.33 (temp_point.handle_left_type, temp_point.handle_right_type) = ('ALIGNED', 'ALIGNED') temp_point.handle_right = temp_point.co + dir_vec temp_point.handle_left = temp_point.co - dir_vec elif handle_type is 'VECTOR': (temp_point.handle_left_type, temp_point.handle_right_type) = ('VECTOR', 'VECTOR') # Update the last point in the spline to be the newly added one stem.updateEnd()
def write_action_channels(writebuf, action): # Set of frame indices frame_set = set() # Set of unique bone names bone_set = [] # Scan through all fcurves to build animated bone set for fcurve in action.fcurves: data_path = fcurve.data_path scale_match = scale_matcher.match(data_path) rotation_match = rotation_matcher.match(data_path) location_match = location_matcher.match(data_path) if scale_match: if scale_match.group(1) not in bone_set: bone_set.append(scale_match.group(1)) elif rotation_match: if rotation_match.group(1) not in bone_set: bone_set.append(rotation_match.group(1)) elif location_match: if location_match.group(1) not in bone_set: bone_set.append(location_match.group(1)) else: continue # Count unified keyframes for interleaving channel data for key in fcurve.keyframe_points: frame_set.add(int(key.co[0])) # Build bone table bone_list = [] for bone in bone_set: fc_dict = dict() rotation_mode = None property_bits = 0 for fcurve in action.fcurves: if fcurve.data_path == 'pose.bones["' + bone + '"].scale': if 'scale' not in fc_dict: fc_dict['scale'] = [None, None, None] property_bits |= 4 fc_dict['scale'][fcurve.array_index] = fcurve elif fcurve.data_path == 'pose.bones["' + bone + '"].rotation_euler': if 'rotation_euler' not in fc_dict: fc_dict['rotation_euler'] = [None, None, None] rotation_mode = 'rotation_euler' property_bits |= 1 fc_dict['rotation_euler'][fcurve.array_index] = fcurve elif fcurve.data_path == 'pose.bones["' + bone + '"].rotation_quaternion': if 'rotation_quaternion' not in fc_dict: fc_dict['rotation_quaternion'] = [None, None, None, None] rotation_mode = 'rotation_quaternion' property_bits |= 1 fc_dict['rotation_quaternion'][fcurve.array_index] = fcurve elif fcurve.data_path == 'pose.bones["' + bone + '"].rotation_axis_angle': if 'rotation_axis_angle' not in fc_dict: fc_dict['rotation_axis_angle'] = [None, None, None, None] rotation_mode = 'rotation_axis_angle' property_bits |= 1 fc_dict['rotation_axis_angle'][fcurve.array_index] = fcurve elif fcurve.data_path == 'pose.bones["' + bone + '"].location': if 'location' not in fc_dict: fc_dict['location'] = [None, None, None] property_bits |= 2 fc_dict['location'][fcurve.array_index] = fcurve bone_list.append((bone, rotation_mode, fc_dict, property_bits)) # Write out frame indices sorted_frames = sorted(frame_set) writebuf(struct.pack('I', len(sorted_frames))) for frame in sorted_frames: writebuf(struct.pack('i', frame)) # Interleave / interpolate keyframe data writebuf(struct.pack('I', len(bone_list))) for bone in bone_list: bone_name = bone[0] rotation_mode = bone[1] fc_dict = bone[2] property_bits = bone[3] writebuf(struct.pack('I', len(bone_name))) writebuf(bone_name.encode()) writebuf(struct.pack('I', property_bits)) writebuf(struct.pack('I', len(sorted_frames))) for frame in sorted_frames: # Rotation curves if rotation_mode == 'rotation_quaternion': quat = [0.0] * 4 for comp in range(4): if fc_dict['rotation_quaternion'][comp]: quat[comp] = fc_dict['rotation_quaternion'][ comp].evaluate(frame) quat = Quaternion(quat).normalized() writebuf( struct.pack('ffff', quat[0], quat[1], quat[2], quat[3])) elif rotation_mode == 'rotation_euler': euler = [0.0] * 3 for comp in range(3): if fc_dict['rotation_euler'][comp]: euler[comp] = fc_dict['rotation_euler'][comp].evaluate( frame) euler_o = Euler(euler, 'XYZ') quat = euler_o.to_quaternion() writebuf( struct.pack('ffff', quat[0], quat[1], quat[2], quat[3])) elif rotation_mode == 'rotation_axis_angle': axis_angle = [0.0] * 4 for comp in range(4): if fc_dict['rotation_axis_angle'][comp]: axis_angle[comp] = fc_dict['rotation_axis_angle'][ comp].evaluate(frame) quat = Quaternion(axis_angle[1:4], axis_angle[0]) writebuf( struct.pack('ffff', quat[0], quat[1], quat[2], quat[3])) # Location curves if 'location' in fc_dict: writevec = [0.0] * 3 for comp in range(3): if fc_dict['location'][comp]: writevec[comp] = fc_dict['location'][comp].evaluate( frame) writebuf( struct.pack('fff', writevec[0], writevec[1], writevec[2])) # Scale curves if 'scale' in fc_dict: writevec = [1.0] * 3 for comp in range(3): if fc_dict['scale'][comp]: writevec[comp] = fc_dict['scale'][comp].evaluate(frame) writebuf( struct.pack('fff', writevec[0], writevec[1], writevec[2]))
def execute(self, context): """SETTINGS""" keep_breasts = True keep_twist_weights = True belly_locators = True arm_bend = 45 finger_bend = -15 scene = bpy.context.scene obj = bpy.context.object meshes = obj.children #gyaz stamp obj.data['GYAZ_rig'] = True #remove all modifiers except for armature modifier for mesh in meshes: for m in mesh.modifiers: if m.type != 'ARMATURE': mesh.modifiers.remove(m) ################################################################################# #raycast function my_tree = BVHTree.FromObject(scene.objects[meshes[0].name], bpy.context.depsgraph) rig = obj def cast_ray_from_bone(start_bone, head_tail, ebone_pbone, direction, distance): #set ray start and direction if ebone_pbone == 'ebone': bpy.ops.object.mode_set(mode='EDIT') if head_tail == 'head': ray_start = rig.data.edit_bones[start_bone].head elif head_tail == 'tail': ray_start = rig.data.edit_bones[start_bone].tail elif ebone_pbone == 'pbone': bpy.ops.object.mode_set(mode='POSE') if head_tail == 'head': ray_start = rig.pose.bones[start_bone].head elif head_tail == 'tail': ray_start = rig.pose.bones[start_bone].tail ray_direction = direction ray_distance = 10 #cast ray hit_loc, hit_nor, hit_index, hit_dist = my_tree.ray_cast( ray_start, ray_direction, ray_distance) return (hit_loc, hit_nor, hit_index, hit_dist) ################################################################################# if obj.type == 'ARMATURE': rig = obj sides = [['_L', '_l'], ['_R', '_r']] names_central = [['root', 'root'], ['pelvis', 'hips'], ['spine01', 'spine_1'], ['spine02', 'spine_2'], ['spine03', 'spine_3'], ['neck', 'neck'], ['head', 'head']] names_side = [ ['clavicle', 'shoulder'], ['upperarm', 'upperarm'], ['lowerarm', 'forearm'], ['hand', 'hand'], ['thigh', 'thigh'], ['calf', 'shin'], ['foot', 'foot'], ['toes', 'toes'], # ['breast', 'spring_chest'] ] finger_names = [['thumb', 'thumb'], ['index', 'pointer'], ['middle', 'middle'], ['ring', 'ring'], ['pinky', 'pinky']] counters = [['01', '_1'], ['02', '_2'], ['03', '_3']] twist_names = ['upperarm', 'lowerarm', 'thigh', 'calf'] new_twist_names = ['upperarm', 'forearm', 'thigh', 'shin'] metacarpal_names = ['index', 'middle', 'ring', 'pinky'] extra_names = ['root'] breast_names = ['breast', 'spring_chest'] # old name, new name bpy.ops.object.mode_set(mode='OBJECT') """WEIGHTS""" def merge_and_remove_weight(weight_to_merge, weight_to_merge_to): for mesh in meshes: bpy.ops.object.select_all(action='DESELECT') mesh.select_set(True) bpy.context.view_layer.objects.active = mesh #mix weights if mesh has those weights vgroups = mesh.vertex_groups if vgroups.get(weight_to_merge) != None: if vgroups.get(weight_to_merge_to) == None: vgroups.new(name=weight_to_merge_to) m = mesh.modifiers.new(type='VERTEX_WEIGHT_MIX', name="Mix Twist Weight") m.mix_mode = 'ADD' m.mix_set = 'ALL' m.vertex_group_a = weight_to_merge_to m.vertex_group_b = weight_to_merge bpy.ops.object.modifier_apply( apply_as='DATA', modifier="Mix Twist Weight") #delete surplus weights vgroups = mesh.vertex_groups vgroups.remove(vgroups[weight_to_merge]) if keep_twist_weights == False: for old_side, new_side in sides: for name in twist_names: weight_to_merge = name + '_twist' + old_side weight_to_merge_to = name + old_side merge_and_remove_weight(weight_to_merge, weight_to_merge_to) for old_side, new_side in sides: for name in metacarpal_names: weight_to_merge = name + '00' + old_side weight_to_merge_to = 'hand' + old_side merge_and_remove_weight(weight_to_merge, weight_to_merge_to) if keep_breasts == False: for old_side, new_side in sides: weight_to_merge = breast_names[0] + old_side weight_to_merge_to = 'spine03' merge_and_remove_weight(weight_to_merge, weight_to_merge_to) for mesh in meshes: mesh.data.update() bpy.ops.object.select_all(action='DESELECT') rig.select_set(True) bpy.context.view_layer.objects.active = rig bpy.ops.object.mode_set(mode='EDIT') ebones = rig.data.edit_bones """RENAME""" for old_name, new_name in names_central: ebones[old_name].name = new_name for old_side, new_side in sides: for old_name, new_name in names_side: ebones[old_name + old_side].name = new_name + new_side for old_side, new_side in sides: for old_finger, new_finger in finger_names: for old_counter, new_counter in counters: ebones[ old_finger + old_counter + old_side].name = new_finger + new_counter + new_side for old_side, new_side in sides: ebone = ebones[breast_names[0] + old_side].name = breast_names[1] + new_side for old_side, new_side in sides: for index, name in enumerate(twist_names): ebone = ebones[name + '_twist' + old_side] ebone.name = 'twist_1_' + new_twist_names[index] + new_side """REMOVE BONES""" for old_side, new_side in sides: for name in new_twist_names: ebone = ebones['twist_1_' + name + new_side] ebones.remove(ebone) for old_side, new_side in sides: for name in metacarpal_names: ebone = ebones[name + '00' + old_side] ebones.remove(ebone) for name in extra_names: ebone = ebones[name] ebones.remove(ebone) if keep_breasts == False: for old_side, new_side in sides: ebone = ebones[breast_names[1] + new_side] ebones.remove(ebone) """POSITION BONES""" ebones = rig.data.edit_bones for ebone in ebones: ebone.use_connect = False # for old_side, new_side in sides: # foot = ebones['foot'+new_side] # foot.tail = foot.head[0], foot.tail[1], foot.head[2] # # toes = ebones['toes'+new_side] # toes.head = foot.head[0], toes.head[1], toes.head[2] # toes.tail = foot.head[0], toes.tail[1], toes.head[2] # # foot.roll = 0 # toes.roll = 0 ebones['hips'].head = (ebones['thigh_l'].head + ebones['thigh_r'].head) / 2 ebones['spine_3'].tail = ebones['neck'].head for old_side, new_side in sides: ebone = ebones['shoulder' + new_side] ebone.head = ebone.head[0], ebone.tail[1], ebone.tail[2] ebone = ebones['head'] ebone.tail = ebone.head[0], ebone.head[1], ebone.tail[2] spine_names = [ 'hips', 'spine_1', 'spine_2', 'spine_3', 'neck', 'head' ] for name in spine_names: ebones[name].roll = 0 for old_side, new_side in sides: ebone = ebones['hand' + new_side] roll = ebone.roll print(degrees(roll)) ebone.tail = (ebone.tail - ebone.head) + ebone.tail ebone.roll = roll """REST POSE""" bpy.ops.object.mode_set(mode='POSE') pbones = rig.pose.bones for old_side, side in sides: pbone = pbones['forearm' + side] eu = Euler((radians(arm_bend), 0, 0), 'XYZ') qu = eu.to_quaternion() pbone.rotation_quaternion = qu for old_name, name in finger_names: for n in range(1, 4): pbone = pbones[name + '_' + str(n) + side] if 'thumb_1' not in pbone.name: eu = Euler((radians(finger_bend), 0, 0), 'XYZ') qu = eu.to_quaternion() pbone.rotation_quaternion = qu if len(meshes) > 0: for mesh in meshes: bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_all(action='DESELECT') mesh.select_set(True) bpy.context.view_layer.objects.active = mesh #remove shape keys try: bpy.ops.object.shape_key_remove(all=True) except: 'do nothing' #apply armature modifier old_mesh = mesh.data new_mesh = mesh.to_mesh(bpy.context.depsgraph, apply_modifiers=True, calc_undeformed=False) mesh.data = new_mesh bpy.data.meshes.remove(old_mesh) bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_all(action='DESELECT') rig.select_set(True) bpy.context.view_layer.objects.active = rig #apply pose bpy.ops.object.mode_set(mode='POSE') bpy.ops.pose.select_all(action='SELECT') bpy.ops.pose.armature_apply() #adjust spine #spine_1, spine_2 bpy.ops.object.mode_set(mode='EDIT') ebones = rig.data.edit_bones loc = (ebones['spine_1'].head + ebones['spine_2'].tail) / 2 ebones['spine_1'].tail = loc ebones['spine_2'].head = loc #spine chain def adjust_spine_point(lower_bone, upper_bone): forward = (0, -1, 0) backward = (0, 1, 0) hit_loc, hit_nor, hit_index, hit_dist = cast_ray_from_bone( lower_bone, 'tail', 'ebone', forward, 10) front = hit_loc hit_loc, hit_nor, hit_index, hit_dist = cast_ray_from_bone( lower_bone, 'tail', 'ebone', backward, 10) back = hit_loc middle = (front + back) / 2 bpy.ops.object.mode_set(mode='EDIT') ebones = rig.data.edit_bones ebones[lower_bone].tail = middle ebones[upper_bone].head = middle return front loc_pelvis_front = adjust_spine_point('hips', 'spine_1') adjust_spine_point('spine_1', 'spine_2') loc_sternum_lower = adjust_spine_point('spine_2', 'spine_3') adjust_spine_point('spine_3', 'neck') #additional locator bones if belly_locators == True: ebone = rig.data.edit_bones.new(name='loc_pelvis_front') ebone.head = loc_pelvis_front ebone.tail = loc_pelvis_front + Vector((0, 0, 0.05)) ebone.parent = rig.data.edit_bones['hips'] ebone = rig.data.edit_bones.new(name='loc_sternum_lower') ebone.head = loc_sternum_lower ebone.tail = loc_sternum_lower + Vector((0, 0, 0.05)) ebone.parent = rig.data.edit_bones['spine_3'] #enforce roll values bpy.ops.object.mode_set(mode='EDIT') ebones = rig.data.edit_bones bpy.ops.armature.select_all(action='SELECT') bpy.ops.armature.symmetrize() #finalize bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_all(action='DESELECT') rig.select_set(True) bpy.context.view_layer.objects.active = rig #GYAZ stamp rig.data['GYAZ_game_rig'] = True return {'FINISHED'}
def execute(self, context): data = [] objId = 0 for ob in bpy.data.objects: if (ob.physacq.is_actor): print(ob.name) o = {} o['name'] = ob.name o['objId'] = objId o['shape'] = ob.physacq.shape o['size'] = { "x": ob.dimensions[0], "y": ob.dimensions[1], "z": ob.dimensions[2] } o['mass'] = ob.physacq.mass log = {} if not ob.animation_data: continue action = ob.animation_data.action for fcu in action.fcurves: #print( "fcu: ", fcu ) #print( "prop: ", fcu.data_path ) #print( "propId: ", fcu.array_index ) if fcu.data_path == 'location': logKey = 'pos' elif fcu.data_path == 'rotation_euler': logKey = 'pose' elif fcu.data_path == 'rotation_quaternion': logKey = 'pose' elif fcu.data_path == 'scale': continue else: print("Can't interpret keyframe type, attention!!!", fcu.data_path) continue if fcu.array_index == 0: subKey = 'x' elif fcu.array_index == 1: subKey = 'y' elif fcu.array_index == 2: subKey = 'z' elif fcu.array_index == 3: subKey = 'w' else: print("Unprepared!") keyframe_points = fcu.keyframe_points for entry in keyframe_points: #print( entry.co ) #print( "key:", str(int(entry.co[0])) ) frameId = str(int(entry.co[0])) if frameId not in log: log[frameId] = {} if logKey not in log[frameId]: log[frameId][logKey] = {} log[frameId][logKey][subKey] = entry.co[1] prevKey = None prevQ = None for key, frame in log.items(): if 'pos' in log[key]: pos = Vector( coordinatesUtil.jsonVector3ToTuple( log[key]['pos'])) log[key]['pos'] = coordinatesUtil.vector3ToJson( self.coordChangeM * pos) if 'pose' not in log[key]: continue #print( 'ob.rotation_mode:', ob.rotation_mode) if ob.rotation_mode == 'XYZ': v = Vector( coordinatesUtil.jsonVector3ToTuple( log[key]['pose'])) eul = Euler(v, ob.rotation_mode) q = eul.to_quaternion() elif ob.rotation_mode == 'QUATERNION': entry = log[key]['pose'] print(entry) q = Quaternion(( entry['x'], entry['y'], entry['z'], entry['w'])) # yes, array_index==3 was parsed to w if prevQ: dotProd = q.dot(prevQ) if dotProd < 0: print("dot: ", dotProd) #q.negate() #print( "q: %s" % (q.__repr__()) ) #log[ key ] ['pose'] = { 'x' : q[1], 'y' : -q[3], 'z': q[2], 'w' : q[0] } log[key]['pose'] = coordinatesUtil.quatToJson( coordinatesUtil.quatFromBlender(q)) if prevKey: #print( "check: ", log[prevKey]['pose'], "\n",log[key]['pose'] ) q2 = Quaternion(q) q2.negate() #print( "q:", q, "negative: ", q2 ) prevKey = key prevQ = q o['log'] = log data.append(o) objId += 1 if not len(data): self.report({'WARNING'}, "No keyframes, not saving") return {'FINISHED'} fp = context.scene.physacq.savePath if len(bpy.path.basename(fp)) == 0: fp = "//cuboids.json" if fp.find("//") >= 0: fp = bpy.path.abspath(fp) self.report({'INFO'}, "Saving poses to %s" % (fp)) f = open(fp, 'w') json.dump(data, f) f.close() return {'FINISHED'}