当前位置:   article > 正文

blender插件笔记

blender插件笔记

目录

文件拖拽导入

smpl导入导出

好像可以导入动画

smpl_blender_addon导入一帧

保存pose

导入导出完整代码


文件拖拽导入

https://github.com/mika-f/blender-drag-and-drop

支持格式:

  • *.abc
  • *.bvh
  • *.dae
  • *.fbx
  • *.glb
  • *.gltf
  • *.obj
  • *.ply
  • *.stl
  • *.svg
  • *.usd
  • *.usda
  • *.usdc
  • *.vrm (Required VRM Add-on for Blender)
  • *.x3d
  • *.wrl

smpl导入导出

好像可以导入动画

https://github.com/vltmedia/QuickMocap-BlenderAddon

smpl_blender_addon导入一帧

这个也是一次只能导入一帧,不能导入动画

https://github.com/Meshcapade/SMPL_blender_addon

这个可以写pose,就是把旋转角度保存下来

  1. class SMPLSnapGroundPlane(bpy.types.Operator):
  2. bl_idname = "object.smpl_snap_ground_plane"
  3. bl_label = "Snap To Ground Plane"
  4. bl_description = ("Snaps mesh to the XY ground plane")
  5. bl_options = {'REGISTER', 'UNDO'}
  6. @classmethod
  7. def poll(cls, context):
  8. try:
  9. # Enable button only if mesh or armature is active object
  10. return ((context.object.type == 'MESH') or (context.object.type == 'ARMATURE'))
  11. except: return False
  12. def execute(self, context):
  13. bpy.ops.object.mode_set(mode='OBJECT')
  14. obj = bpy.context.object
  15. if obj.type == 'ARMATURE':
  16. armature = obj
  17. obj = bpy.context.object.children[0]
  18. else:
  19. armature = obj.parent
  20. # Get vertices with applied skin modifier in object coordinates
  21. depsgraph = context.evaluated_depsgraph_get()
  22. object_eval = obj.evaluated_get(depsgraph)
  23. mesh_from_eval = object_eval.to_mesh()
  24. # Get vertices in world coordinates
  25. matrix_world = obj.matrix_world
  26. vertices_world = [matrix_world @ vertex.co for vertex in mesh_from_eval.vertices]
  27. z_min = (min(vertices_world, key=lambda item: item.z)).z
  28. object_eval.to_mesh_clear() # Remove temporary mesh
  29. # Translate armature edit bones
  30. context.view_layer.objects.active = armature
  31. bpy.ops.object.mode_set(mode='EDIT')
  32. for edit_bone in armature.data.edit_bones:
  33. if edit_bone.name != "root":
  34. edit_bone.translate(Vector((0.0, 0.0, -z_min)))
  35. # Translate skinned mesh and apply translation
  36. bpy.ops.object.mode_set(mode='OBJECT')
  37. context.view_layer.objects.active = obj
  38. obj.location = (0.0, 0.0, -z_min)
  39. bpy.ops.object.transform_apply(location = True)
  40. return {'FINISHED'}

保存pose

  1. class SMPLWritePose(bpy.types.Operator):
  2. bl_idname = "object.smpl_write_pose"
  3. bl_label = "Write Pose1"
  4. bl_description = ("Writes SMPL pose thetas to console window")
  5. bl_options = {'REGISTER', 'UNDO'}
  6. @classmethod
  7. def poll(cls, context):
  8. try:
  9. # Enable button only if mesh or armature is active object
  10. return (context.object.type == 'MESH') or (context.object.type == 'ARMATURE')
  11. except: return False
  12. def execute(self, context):
  13. obj = bpy.context.object
  14. if obj.type == 'MESH':
  15. armature = obj.parent
  16. else:
  17. armature = obj
  18. # Get armature pose in rodrigues representation
  19. pose = [0.0] * (len(SMPL_JOINT_NAMES) * 3)
  20. for index in range(len(SMPL_JOINT_NAMES)):
  21. joint_name = SMPL_JOINT_NAMES[index]
  22. joint_pose = rodrigues_from_pose(armature, joint_name)
  23. pose[index*3 + 0] = joint_pose[0]
  24. pose[index*3 + 1] = joint_pose[1]
  25. pose[index*3 + 2] = joint_pose[2]
  26. print("pose = " + str(pose))
  27. npz_file="1234.npz"
  28. np.savez_compressed(npz_file, joints_3d={"data": pose})
  29. return {'FINISHED'}

导入导出完整代码

这个可以导入导出,代码没有报错了,但是加载后没有显示出来,而且只能保存一帧,不能保存动画

  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software Foundation,
  15. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. import logging
  19. bl_info = {
  20. "name": "SMPL for Blender",
  21. "author": "Joachim Tesch, Max Planck Institute for Intelligent Systems",
  22. "version": (2021, 6, 11),
  23. "blender": (2, 80, 0),
  24. "location": "Viewport > Right panel",
  25. "description": "SMPL for Blender",
  26. "wiki_url": "https://smpl.is.tue.mpg.de/",
  27. "category": "SMPL"}
  28. import bpy
  29. import bmesh
  30. from bpy_extras.io_utils import ExportHelper # ExportHelper is a helper class, defines filename and invoke() function which calls the file selector.
  31. from mathutils import Vector, Quaternion
  32. from math import radians
  33. import numpy as np
  34. import os
  35. import pickle
  36. from bpy.props import ( BoolProperty, EnumProperty, FloatProperty, PointerProperty )
  37. from bpy.types import ( PropertyGroup )
  38. # SMPL globals
  39. SMPL_JOINT_NAMES = {
  40. 0: 'Pelvis',
  41. 1: 'L_Hip', 4: 'L_Knee', 7: 'L_Ankle', 10: 'L_Foot',
  42. 2: 'R_Hip', 5: 'R_Knee', 8: 'R_Ankle', 11: 'R_Foot',
  43. 3: 'Spine1', 6: 'Spine2', 9: 'Spine3', 12: 'Neck', 15: 'Head',
  44. 13: 'L_Collar', 16: 'L_Shoulder', 18: 'L_Elbow', 20: 'L_Wrist', 22: 'L_Hand',
  45. 14: 'R_Collar', 17: 'R_Shoulder', 19: 'R_Elbow', 21: 'R_Wrist', 23: 'R_Hand',
  46. }
  47. smpl_joints = len(SMPL_JOINT_NAMES)
  48. # End SMPL globals
  49. def rodrigues_from_pose(armature, bone_name):
  50. # Ensure that rotation mode is AXIS_ANGLE so the we get a correct readout of current pose
  51. armature.pose.bones[bone_name].rotation_mode = 'AXIS_ANGLE'
  52. axis_angle = armature.pose.bones[bone_name].rotation_axis_angle
  53. angle = axis_angle[0]
  54. rodrigues = Vector((axis_angle[1], axis_angle[2], axis_angle[3]))
  55. rodrigues.normalize()
  56. rodrigues = rodrigues * angle
  57. return rodrigues
  58. def update_corrective_poseshapes(self, context):
  59. if self.smpl_corrective_poseshapes:
  60. bpy.ops.object.smpl_set_poseshapes('EXEC_DEFAULT')
  61. else:
  62. bpy.ops.object.smpl_reset_poseshapes('EXEC_DEFAULT')
  63. # Property groups for UI
  64. class PG_SMPLProperties(PropertyGroup):
  65. smpl_gender: EnumProperty(
  66. name = "Model",
  67. description = "SMPL model",
  68. items = [ ("female", "Female", ""), ("male", "Male", "") ]
  69. )
  70. smpl_texture: EnumProperty(
  71. name = "",
  72. description = "SMPL model texture",
  73. items = [ ("NONE", "None", ""), ("UV_GRID", "UV Grid", ""), ("COLOR_GRID", "Color Grid", "") ]
  74. )
  75. smpl_corrective_poseshapes: BoolProperty(
  76. name = "Corrective Pose Shapes",
  77. description = "Enable/disable corrective pose shapes of SMPL model",
  78. update = update_corrective_poseshapes
  79. )
  80. smpl_export_setting_shape_keys: EnumProperty(
  81. name = "",
  82. description = "Blend shape export settings",
  83. items = [ ("SHAPE_POSE", "All: Shape + Posecorrectives", "Export shape keys for body shape and pose correctives"), ("SHAPE", "Reduced: Shape space only", "Export only shape keys for body shape"), ("NONE", "None: Apply shape space", "Do not export any shape keys, shape keys for body shape will be baked into mesh") ],
  84. )
  85. class SMPLAddGender(bpy.types.Operator):
  86. bl_idname = "scene.smpl_add_gender"
  87. bl_label = "Add"
  88. bl_description = ("Add SMPL model of selected gender to scene")
  89. bl_options = {'REGISTER', 'UNDO'}
  90. @classmethod
  91. def poll(cls, context):
  92. try:
  93. # Enable button only if in Object Mode
  94. if (context.active_object is None) or (context.active_object.mode == 'OBJECT'):
  95. return True
  96. else:
  97. return False
  98. except: return False
  99. def execute(self, context):
  100. gender = context.window_manager.smpl_tool.smpl_gender
  101. print("Adding gender: " + gender)
  102. path = os.path.dirname(os.path.realpath(__file__))
  103. objects_path = os.path.join(path, "data", "smpl-model-20200803.blend", "Object")
  104. object_name = "SMPL-mesh-" + gender
  105. bpy.ops.wm.append(filename=object_name, directory=str(objects_path))
  106. # Select imported mesh
  107. object_name = context.selected_objects[0].name
  108. bpy.ops.object.select_all(action='DESELECT')
  109. context.view_layer.objects.active = bpy.data.objects[object_name]
  110. bpy.data.objects[object_name].select_set(True)
  111. return {'FINISHED'}
  112. class SMPLSetTexture(bpy.types.Operator):
  113. bl_idname = "scene.smpl_set_texture"
  114. bl_label = "Set"
  115. bl_description = ("Set selected texture")
  116. bl_options = {'REGISTER', 'UNDO'}
  117. @classmethod
  118. def poll(cls, context):
  119. try:
  120. # Enable button only if in active object is mesh
  121. if (context.object.type == 'MESH'):
  122. return True
  123. else:
  124. return False
  125. except: return False
  126. def execute(self, context):
  127. texture = context.window_manager.smpl_tool.smpl_texture
  128. print("Setting texture: " + texture)
  129. obj = bpy.context.object
  130. if (len(obj.data.materials) == 0) or (obj.data.materials[0] is None):
  131. self.report({'WARNING'}, "Selected mesh has no material: %s" % obj.name)
  132. return {'CANCELLED'}
  133. mat = obj.data.materials[0]
  134. links = mat.node_tree.links
  135. nodes = mat.node_tree.nodes
  136. # Find texture node
  137. node_texture = None
  138. for node in nodes:
  139. if node.type == 'TEX_IMAGE':
  140. node_texture = node
  141. break
  142. # Find shader node
  143. node_shader = None
  144. for node in nodes:
  145. if node.type.startswith('BSDF'):
  146. node_shader = node
  147. break
  148. if texture == 'NONE':
  149. # Unlink texture node
  150. if node_texture is not None:
  151. for link in node_texture.outputs[0].links:
  152. links.remove(link)
  153. nodes.remove(node_texture)
  154. # 3D Viewport still shows previous texture when texture link is removed via script.
  155. # As a workaround we trigger desired viewport update by setting color value.
  156. node_shader.inputs[0].default_value = node_shader.inputs[0].default_value
  157. else:
  158. if node_texture is None:
  159. node_texture = nodes.new(type="ShaderNodeTexImage")
  160. if texture == 'UV_GRID':
  161. if texture not in bpy.data.images:
  162. bpy.ops.image.new(name=texture, generated_type='UV_GRID')
  163. image = bpy.data.images[texture]
  164. else:
  165. if texture not in bpy.data.images:
  166. bpy.ops.image.new(name=texture, generated_type='COLOR_GRID')
  167. image = bpy.data.images[texture]
  168. node_texture.image = image
  169. # Link texture node to shader node if not already linked
  170. if len(node_texture.outputs[0].links) == 0:
  171. links.new(node_texture.outputs[0], node_shader.inputs[0])
  172. return {'FINISHED'}
  173. class SMPLRandomShapes(bpy.types.Operator):
  174. bl_idname = "object.smpl_random_shapes"
  175. bl_label = "Random Shapes"
  176. bl_description = ("Sets all shape blend shape keys to a random value")
  177. bl_options = {'REGISTER', 'UNDO'}
  178. @classmethod
  179. def poll(cls, context):
  180. try:
  181. # Enable button only if mesh is active object
  182. return context.object.type == 'MESH'
  183. except: return False
  184. def execute(self, context):
  185. obj = bpy.context.object
  186. bpy.ops.object.mode_set(mode='OBJECT')
  187. for key_block in obj.data.shape_keys.key_blocks:
  188. if key_block.name.startswith("Shape"):
  189. key_block.value = np.random.normal(0.0, 1.0)
  190. bpy.ops.object.smpl_update_joint_locations('EXEC_DEFAULT')
  191. return {'FINISHED'}
  192. class SMPLResetShapes(bpy.types.Operator):
  193. bl_idname = "object.smpl_reset_shapes"
  194. bl_label = "Reset"
  195. bl_description = ("Resets all blend shape keys for shape")
  196. bl_options = {'REGISTER', 'UNDO'}
  197. @classmethod
  198. def poll(cls, context):
  199. try:
  200. # Enable button only if mesh is active object
  201. return context.object.type == 'MESH'
  202. except: return False
  203. def execute(self, context):
  204. obj = bpy.context.object
  205. bpy.ops.object.mode_set(mode='OBJECT')
  206. for key_block in obj.data.shape_keys.key_blocks:
  207. if key_block.name.startswith("Shape"):
  208. key_block.value = 0.0
  209. bpy.ops.object.smpl_update_joint_locations('EXEC_DEFAULT')
  210. return {'FINISHED'}
  211. class SMPLSnapGroundPlane(bpy.types.Operator):
  212. bl_idname = "object.smpl_snap_ground_plane"
  213. bl_label = "Snap To Ground Plane"
  214. bl_description = ("Snaps mesh to the XY ground plane")
  215. bl_options = {'REGISTER', 'UNDO'}
  216. @classmethod
  217. def poll(cls, context):
  218. try:
  219. # Enable button only if mesh or armature is active object
  220. return ((context.object.type == 'MESH') or (context.object.type == 'ARMATURE'))
  221. except: return False
  222. def execute(self, context):
  223. bpy.ops.object.mode_set(mode='OBJECT')
  224. obj = bpy.context.object
  225. if obj.type == 'ARMATURE':
  226. armature = obj
  227. obj = bpy.context.object.children[0]
  228. else:
  229. armature = obj.parent
  230. # Get vertices with applied skin modifier in object coordinates
  231. depsgraph = context.evaluated_depsgraph_get()
  232. object_eval = obj.evaluated_get(depsgraph)
  233. mesh_from_eval = object_eval.to_mesh()
  234. # Get vertices in world coordinates
  235. matrix_world = obj.matrix_world
  236. vertices_world = [matrix_world @ vertex.co for vertex in mesh_from_eval.vertices]
  237. z_min = (min(vertices_world, key=lambda item: item.z)).z
  238. object_eval.to_mesh_clear() # Remove temporary mesh
  239. # Translate armature edit bones
  240. context.view_layer.objects.active = armature
  241. bpy.ops.object.mode_set(mode='EDIT')
  242. for edit_bone in armature.data.edit_bones:
  243. if edit_bone.name != "root":
  244. edit_bone.translate(Vector((0.0, 0.0, -z_min)))
  245. # Translate skinned mesh and apply translation
  246. bpy.ops.object.mode_set(mode='OBJECT')
  247. context.view_layer.objects.active = obj
  248. obj.location = (0.0, 0.0, -z_min)
  249. bpy.ops.object.transform_apply(location = True)
  250. return {'FINISHED'}
  251. class SMPLUpdateJointLocations(bpy.types.Operator):
  252. bl_idname = "object.smpl_update_joint_locations"
  253. bl_label = "Update Joint Locations"
  254. bl_description = ("Update joint locations after shape/expression changes")
  255. bl_options = {'REGISTER', 'UNDO'}
  256. j_regressor_male = None
  257. j_regressor_female = None
  258. @classmethod
  259. def poll(cls, context):
  260. try:
  261. # Enable button only if mesh is active object
  262. return ((context.object.type == 'MESH') and (context.object.parent.type == 'ARMATURE'))
  263. except: return False
  264. def execute(self, context):
  265. obj = bpy.context.object
  266. bpy.ops.object.mode_set(mode='OBJECT')
  267. if self.j_regressor_female is None:
  268. path = os.path.dirname(os.path.realpath(__file__))
  269. regressor_path = os.path.join(path, "data", "smpl_joint_regressor_female.npz")
  270. with np.load(regressor_path) as data:
  271. self.j_regressor_female = data['joint_regressor']
  272. if self.j_regressor_male is None:
  273. path = os.path.dirname(os.path.realpath(__file__))
  274. regressor_path = os.path.join(path, "data", "smpl_joint_regressor_male.npz")
  275. with np.load(regressor_path) as data:
  276. self.j_regressor_male = data['joint_regressor']
  277. if "female" in obj.name:
  278. j_regressor = self.j_regressor_female
  279. else:
  280. j_regressor = self.j_regressor_male
  281. # Store current bone rotations
  282. armature = obj.parent
  283. bone_rotations = {}
  284. for pose_bone in armature.pose.bones:
  285. pose_bone.rotation_mode = 'AXIS_ANGLE'
  286. axis_angle = pose_bone.rotation_axis_angle
  287. bone_rotations[pose_bone.name] = (axis_angle[0], axis_angle[1], axis_angle[2], axis_angle[3])
  288. # Set model in default pose
  289. for bone in armature.pose.bones:
  290. bpy.ops.object.smpl_reset_poseshapes('EXEC_DEFAULT')
  291. bone.rotation_mode = 'AXIS_ANGLE'
  292. bone.rotation_axis_angle = (0, 0, 1, 0)
  293. # Reset corrective poseshapes if used
  294. if context.window_manager.smpl_tool.smpl_corrective_poseshapes:
  295. bpy.ops.object.smpl_reset_poseshapes('EXEC_DEFAULT')
  296. # Get vertices with applied skin modifier
  297. depsgraph = context.evaluated_depsgraph_get()
  298. object_eval = obj.evaluated_get(depsgraph)
  299. mesh_from_eval = object_eval.to_mesh()
  300. # Get Blender vertices as numpy matrix
  301. vertices_np = np.zeros((len(mesh_from_eval.vertices)*3), dtype=np.float)
  302. mesh_from_eval.vertices.foreach_get("co", vertices_np)
  303. vertices_matrix = np.reshape(vertices_np, (len(mesh_from_eval.vertices), 3))
  304. object_eval.to_mesh_clear() # Remove temporary mesh
  305. # Note: Current joint regressor uses 6890 vertices as input which is slow numpy operation
  306. joint_locations = j_regressor @ vertices_matrix
  307. # Set new bone joint locations
  308. bpy.context.view_layer.objects.active = armature
  309. bpy.ops.object.mode_set(mode='EDIT')
  310. for index in range(smpl_joints):
  311. bone = armature.data.edit_bones[SMPL_JOINT_NAMES[index]]
  312. bone.head = (0.0, 0.0, 0.0)
  313. bone.tail = (0.0, 0.0, 0.1)
  314. bone_start = Vector(joint_locations[index])
  315. bone.translate(bone_start)
  316. bpy.ops.object.mode_set(mode='OBJECT')
  317. bpy.context.view_layer.objects.active = obj
  318. # Restore pose
  319. for pose_bone in armature.pose.bones:
  320. pose_bone.rotation_mode = 'AXIS_ANGLE'
  321. pose_bone.rotation_axis_angle = bone_rotations[pose_bone.name]
  322. # Restore corrective poseshapes if used
  323. if context.window_manager.smpl_tool.smpl_corrective_poseshapes:
  324. bpy.ops.object.smpl_set_poseshapes('EXEC_DEFAULT')
  325. return {'FINISHED'}
  326. class SMPLSetPoseshapes(bpy.types.Operator):
  327. bl_idname = "object.smpl_set_poseshapes"
  328. bl_label = "Set Pose Shapes"
  329. bl_description = ("Sets corrective poseshapes for current pose")
  330. bl_options = {'REGISTER', 'UNDO'}
  331. @classmethod
  332. def poll(cls, context):
  333. try:
  334. # Enable button only if mesh is active object and parent is armature
  335. return ( ((context.object.type == 'MESH') and (context.object.parent.type == 'ARMATURE')) or (context.object.type == 'ARMATURE'))
  336. except: return False
  337. # https://github.com/gulvarol/surreal/blob/master/datageneration/main_part1.py
  338. # Computes rotation matrix through Rodrigues formula as in cv2.Rodrigues
  339. def rodrigues_to_mat(self, rotvec):
  340. theta = np.linalg.norm(rotvec)
  341. r = (rotvec/theta).reshape(3, 1) if theta > 0. else rotvec
  342. cost = np.cos(theta)
  343. mat = np.asarray([[0, -r[2], r[1]],
  344. [r[2], 0, -r[0]],
  345. [-r[1], r[0], 0]])
  346. return(cost*np.eye(3) + (1-cost)*r.dot(r.T) + np.sin(theta)*mat)
  347. # https://github.com/gulvarol/surreal/blob/master/datageneration/main_part1.py
  348. # Calculate weights of pose corrective blend shapes
  349. # Input is pose of all 24 joints, output is weights for all joints except pelvis (23)
  350. def rodrigues_to_posecorrective_weight(self, pose):
  351. joints_posecorrective = smpl_joints
  352. rod_rots = np.asarray(pose).reshape(joints_posecorrective, 3)
  353. mat_rots = [self.rodrigues_to_mat(rod_rot) for rod_rot in rod_rots]
  354. bshapes = np.concatenate([(mat_rot - np.eye(3)).ravel() for mat_rot in mat_rots[1:]])
  355. return(bshapes)
  356. def execute(self, context):
  357. obj = bpy.context.object
  358. # Get armature pose in rodrigues representation
  359. if obj.type == 'ARMATURE':
  360. armature = obj
  361. obj = bpy.context.object.children[0]
  362. else:
  363. armature = obj.parent
  364. pose = [0.0] * (smpl_joints * 3)
  365. for index in range(smpl_joints):
  366. joint_name = SMPL_JOINT_NAMES[index]
  367. joint_pose = rodrigues_from_pose(armature, joint_name)
  368. pose[index*3 + 0] = joint_pose[0]
  369. pose[index*3 + 1] = joint_pose[1]
  370. pose[index*3 + 2] = joint_pose[2]
  371. # print("Current pose: " + str(pose))
  372. poseweights = self.rodrigues_to_posecorrective_weight(pose)
  373. # Set weights for pose corrective shape keys
  374. for index, weight in enumerate(poseweights):
  375. obj.data.shape_keys.key_blocks["Pose%03d" % index].value = weight
  376. # Set checkbox without triggering update function
  377. context.window_manager.smpl_tool["smpl_corrective_poseshapes"] = True
  378. return {'FINISHED'}
  379. class SMPLResetPoseshapes(bpy.types.Operator):
  380. bl_idname = "object.smpl_reset_poseshapes"
  381. bl_label = "Reset"
  382. bl_description = ("Resets corrective poseshapes for current pose")
  383. bl_options = {'REGISTER', 'UNDO'}
  384. @classmethod
  385. def poll(cls, context):
  386. try:
  387. # Enable button only if mesh is active object and parent is armature
  388. return ( ((context.object.type == 'MESH') and (context.object.parent.type == 'ARMATURE')) or (context.object.type == 'ARMATURE'))
  389. except: return False
  390. def execute(self, context):
  391. obj = bpy.context.object
  392. if obj.type == 'ARMATURE':
  393. obj = bpy.context.object.children[0]
  394. for key_block in obj.data.shape_keys.key_blocks:
  395. if key_block.name.startswith("Pose"):
  396. key_block.value = 0.0
  397. return {'FINISHED'}
  398. def set_pose_from_rodrigues(armature, bone_name, rodrigues, rodrigues_reference=None, frame=1): # I wish frame=bpy.data.scenes[0].frame_current worked here, but it doesn't
  399. rod = Vector((rodrigues[0], rodrigues[1], rodrigues[2]))
  400. angle_rad = rod.length
  401. axis = rod.normalized()
  402. pbone = armature.pose.bones[bone_name]
  403. pbone.rotation_mode = 'QUATERNION'
  404. quat = Quaternion(axis, angle_rad)
  405. if rodrigues_reference is None:
  406. pbone.rotation_quaternion = quat
  407. else:
  408. # SMPL-X is adding the reference rodrigues rotation to the
  409. # relaxed hand rodrigues rotation, so we have to do the same here.
  410. # This means that pose values for relaxed hand model cannot be
  411. # interpreted as rotations in the local joint coordinate system of the relaxed hand.
  412. # https://github.com/vchoutas/smplx/blob/f4206853a4746139f61bdcf58571f2cea0cbebad/smplx/body_models.py#L1190
  413. # full_pose += self.pose_mean
  414. rod_reference = Vector((rodrigues_reference[0], rodrigues_reference[1], rodrigues_reference[2]))
  415. rod_result = rod + rod_reference
  416. angle_rad_result = rod_result.length
  417. axis_result = rod_result.normalized()
  418. quat_result = Quaternion(axis_result, angle_rad_result)
  419. pbone.rotation_quaternion = quat_result
  420. pbone.keyframe_insert(data_path="rotation_quaternion", frame=frame)
  421. if bone_name == 'pelvis':
  422. pbone.keyframe_insert('location', frame=frame)
  423. return
  424. class SMPLLoadPose(bpy.types.Operator):
  425. bl_idname = "object.smpl_load_pose"
  426. bl_label = "Load Pose"
  427. bl_description = ("Load SMPL pose thetas to console window")
  428. bl_options = {'REGISTER', 'UNDO'}
  429. @classmethod
  430. def poll(cls, context):
  431. try:
  432. # Enable button only if mesh or armature is active object
  433. return (context.object.type == 'MESH') or (context.object.type == 'ARMATURE')
  434. except: return False
  435. def execute(self, context):
  436. self.frame_number=5
  437. obj = bpy.context.object
  438. if obj.type == 'MESH':
  439. armature = obj.parent
  440. else:
  441. armature = obj
  442. obj = armature.children[0]
  443. context.view_layer.objects.active = obj # mesh needs to be active object for recalculating joint locations
  444. joint_names = SMPL_JOINT_NAMES
  445. obj = bpy.context.object
  446. npz_path = r"C:\Program Files\Blender Foundation\Blender 4.0\1234.npz"
  447. npz_data = np.load(npz_path, allow_pickle=True)
  448. if 'joints_3d' not in npz_data:
  449. print('joints_3d not find')
  450. return
  451. data = npz_data['joints_3d'].item()['data']
  452. body_pose = data.reshape(( 24, 3))
  453. logging.error("np.array(data):"+str(len(np.array(data))))
  454. # pose_index = max(0, min(self.frame_number, (len(np.array(data))))) # clamp the frame they give you from 0 and the max number of frames in this poses array
  455. # body_pose = np.array(data[pose_index]).reshape(len(joint_names), 3)
  456. # pose the entire body
  457. for index in range(len(joint_names)):
  458. pose_rodrigues = body_pose[index]
  459. bone_name = joint_names[index]
  460. set_pose_from_rodrigues(armature, bone_name, pose_rodrigues, frame=bpy.data.scenes[0].frame_current)
  461. return {'FINISHED'}
  462. class SMPLWritePose(bpy.types.Operator):
  463. bl_idname = "object.smpl_write_pose"
  464. bl_label = "Write Pose1"
  465. bl_description = ("Writes SMPL pose thetas to console window")
  466. bl_options = {'REGISTER', 'UNDO'}
  467. @classmethod
  468. def poll(cls, context):
  469. try:
  470. # Enable button only if mesh or armature is active object
  471. return (context.object.type == 'MESH') or (context.object.type == 'ARMATURE')
  472. except: return False
  473. def execute(self, context):
  474. obj = bpy.context.object
  475. if obj.type == 'MESH':
  476. armature = obj.parent
  477. else:
  478. armature = obj
  479. # Get armature pose in rodrigues representation
  480. pose = [0.0] * (len(SMPL_JOINT_NAMES) * 3)
  481. for index in range(len(SMPL_JOINT_NAMES)):
  482. joint_name = SMPL_JOINT_NAMES[index]
  483. joint_pose = rodrigues_from_pose(armature, joint_name)
  484. pose[index*3 + 0] = joint_pose[0]
  485. pose[index*3 + 1] = joint_pose[1]
  486. pose[index*3 + 2] = joint_pose[2]
  487. print("pose = " + str(pose))
  488. npz_file="1234.npz"
  489. np.savez_compressed(npz_file, joints_3d={"data": np.array([pose])})
  490. return {'FINISHED'}
  491. class SMPLResetPose(bpy.types.Operator):
  492. bl_idname = "object.smpl_reset_pose"
  493. bl_label = "Reset Pose"
  494. bl_description = ("Resets pose to default zero pose")
  495. bl_options = {'REGISTER', 'UNDO'}
  496. @classmethod
  497. def poll(cls, context):
  498. try:
  499. # Enable button only if mesh is active object
  500. return ( ((context.object.type == 'MESH') and (context.object.parent.type == 'ARMATURE')) or (context.object.type == 'ARMATURE'))
  501. except: return False
  502. def execute(self, context):
  503. obj = bpy.context.object
  504. if obj.type == 'MESH':
  505. armature = obj.parent
  506. else:
  507. armature = obj
  508. for bone in armature.pose.bones:
  509. bone.rotation_mode = 'AXIS_ANGLE'
  510. bone.rotation_axis_angle = (0, 0, 1, 0)
  511. # Reset corrective pose shapes
  512. bpy.ops.object.smpl_reset_poseshapes('EXEC_DEFAULT')
  513. return {'FINISHED'}
  514. class SMPLExportUnityFBX(bpy.types.Operator, ExportHelper):
  515. bl_idname = "object.smpl_export_unity_fbx"
  516. bl_label = "Export Unity FBX"
  517. bl_description = ("Export skinned mesh to Unity in FBX format")
  518. bl_options = {'REGISTER', 'UNDO'}
  519. # ExportHelper mixin class uses this
  520. filename_ext = ".fbx"
  521. @classmethod
  522. def poll(cls, context):
  523. try:
  524. # Enable button only if mesh is active object
  525. return (context.object.type == 'MESH')
  526. except: return False
  527. def execute(self, context):
  528. obj = bpy.context.object
  529. export_shape_keys = context.window_manager.smpl_tool.smpl_export_setting_shape_keys
  530. armature_original = obj.parent
  531. skinned_mesh_original = obj
  532. # Operate on temporary copy of skinned mesh and armature
  533. bpy.ops.object.select_all(action='DESELECT')
  534. skinned_mesh_original.select_set(True)
  535. armature_original.select_set(True)
  536. bpy.context.view_layer.objects.active = skinned_mesh_original
  537. bpy.ops.object.duplicate()
  538. skinned_mesh = bpy.context.object
  539. armature = skinned_mesh.parent
  540. # Reset pose
  541. bpy.ops.object.smpl_reset_pose('EXEC_DEFAULT')
  542. if export_shape_keys != 'SHAPE_POSE':
  543. # Remove pose corrective shape keys
  544. print('Removing pose corrective shape keys')
  545. num_shape_keys = len(skinned_mesh.data.shape_keys.key_blocks.keys())
  546. current_shape_key_index = 0
  547. for index in range(0, num_shape_keys):
  548. bpy.context.object.active_shape_key_index = current_shape_key_index
  549. if bpy.context.object.active_shape_key is not None:
  550. if bpy.context.object.active_shape_key.name.startswith('Pose'):
  551. bpy.ops.object.shape_key_remove(all=False)
  552. else:
  553. current_shape_key_index = current_shape_key_index + 1
  554. if export_shape_keys == 'NONE':
  555. # Bake and remove shape keys
  556. print("Baking shape and removing shape keys for shape")
  557. # Create shape mix for current shape
  558. bpy.ops.object.shape_key_add(from_mix=True)
  559. num_shape_keys = len(skinned_mesh.data.shape_keys.key_blocks.keys())
  560. # Remove all shape keys except newly added one
  561. bpy.context.object.active_shape_key_index = 0
  562. for count in range(0, num_shape_keys):
  563. bpy.ops.object.shape_key_remove(all=False)
  564. # Model (skeleton and skinned mesh) needs to have rotation of (90, 0, 0) when exporting so that it will have rotation (0, 0, 0) when imported into Unity
  565. bpy.ops.object.mode_set(mode='OBJECT')
  566. bpy.ops.object.select_all(action='DESELECT')
  567. skinned_mesh.select_set(True)
  568. skinned_mesh.rotation_euler = (radians(-90), 0, 0)
  569. bpy.context.view_layer.objects.active = skinned_mesh
  570. bpy.ops.object.transform_apply(rotation = True)
  571. skinned_mesh.rotation_euler = (radians(90), 0, 0)
  572. skinned_mesh.select_set(False)
  573. armature.select_set(True)
  574. armature.rotation_euler = (radians(-90), 0, 0)
  575. bpy.context.view_layer.objects.active = armature
  576. bpy.ops.object.transform_apply(rotation = True)
  577. armature.rotation_euler = (radians(90), 0, 0)
  578. # Select armature and skinned mesh for export
  579. skinned_mesh.select_set(True)
  580. # Rename armature and skinned mesh to not contain Blender copy suffix
  581. if "female" in skinned_mesh.name:
  582. gender = "female"
  583. else:
  584. gender = "male"
  585. target_mesh_name = "SMPL-mesh-%s" % gender
  586. target_armature_name = "SMPL-%s" % gender
  587. if target_mesh_name in bpy.data.objects:
  588. bpy.data.objects[target_mesh_name].name = "SMPL-temp-mesh"
  589. skinned_mesh.name = target_mesh_name
  590. if target_armature_name in bpy.data.objects:
  591. bpy.data.objects[target_armature_name].name = "SMPL-temp-armature"
  592. armature.name = target_armature_name
  593. bpy.ops.export_scene.fbx(filepath=self.filepath, use_selection=True, apply_scale_options="FBX_SCALE_ALL", add_leaf_bones=False)
  594. print("Exported: " + self.filepath)
  595. # Remove temporary copies of armature and skinned mesh
  596. bpy.ops.object.select_all(action='DESELECT')
  597. skinned_mesh.select_set(True)
  598. armature.select_set(True)
  599. bpy.ops.object.delete()
  600. bpy.ops.object.select_all(action='DESELECT')
  601. skinned_mesh_original.select_set(True)
  602. bpy.context.view_layer.objects.active = skinned_mesh_original
  603. if "SMPL-temp-mesh" in bpy.data.objects:
  604. bpy.data.objects["SMPL-temp-mesh"].name = target_mesh_name
  605. if "SMPL-temp-armature" in bpy.data.objects:
  606. bpy.data.objects["SMPL-temp-armature"].name = target_armature_name
  607. return {'FINISHED'}
  608. class SMPL_PT_Model(bpy.types.Panel):
  609. bl_label = "SMPL Model"
  610. bl_category = "SMPL"
  611. bl_space_type = "VIEW_3D"
  612. bl_region_type = "UI"
  613. def draw(self, context):
  614. layout = self.layout
  615. col = layout.column(align=True)
  616. row = col.row(align=True)
  617. col.prop(context.window_manager.smpl_tool, "smpl_gender")
  618. col.operator("scene.smpl_add_gender", text="Add")
  619. col.separator()
  620. col.label(text="Texture:")
  621. row = col.row(align=True)
  622. split = row.split(factor=0.75, align=True)
  623. split.prop(context.window_manager.smpl_tool, "smpl_texture")
  624. split.operator("scene.smpl_set_texture", text="Set")
  625. class SMPL_PT_Shape(bpy.types.Panel):
  626. bl_label = "Shape"
  627. bl_category = "SMPL"
  628. bl_space_type = "VIEW_3D"
  629. bl_region_type = "UI"
  630. def draw(self, context):
  631. layout = self.layout
  632. col = layout.column(align=True)
  633. row = col.row(align=True)
  634. split = row.split(factor=0.75, align=True)
  635. split.operator("object.smpl_random_shapes")
  636. split.operator("object.smpl_reset_shapes")
  637. col.separator()
  638. col.operator("object.smpl_snap_ground_plane")
  639. col.separator()
  640. col.operator("object.smpl_update_joint_locations")
  641. class SMPL_PT_Pose(bpy.types.Panel):
  642. bl_label = "Pose"
  643. bl_category = "SMPL"
  644. bl_space_type = "VIEW_3D"
  645. bl_region_type = "UI"
  646. def draw(self, context):
  647. layout = self.layout
  648. col = layout.column(align=True)
  649. col.prop(context.window_manager.smpl_tool, "smpl_corrective_poseshapes")
  650. col.separator()
  651. col.operator("object.smpl_set_poseshapes")
  652. col.separator()
  653. col.operator("object.smpl_load_pose")
  654. col.separator()
  655. col.operator("object.smpl_write_pose")
  656. col.separator()
  657. class SMPL_PT_Export(bpy.types.Panel):
  658. bl_label = "Export"
  659. bl_category = "SMPL"
  660. bl_space_type = "VIEW_3D"
  661. bl_region_type = "UI"
  662. def draw(self, context):
  663. layout = self.layout
  664. col = layout.column(align=True)
  665. col.label(text="Shape Keys (Blend Shapes):")
  666. col.prop(context.window_manager.smpl_tool, "smpl_export_setting_shape_keys")
  667. col.separator()
  668. col.separator()
  669. col.operator("object.smpl_export_unity_fbx")
  670. col.separator()
  671. # export_button = col.operator("export_scene.obj", text="Export OBJ [m]", icon='EXPORT')
  672. # export_button.global_scale = 1.0
  673. # export_button.use_selection = True
  674. # col.separator()
  675. row = col.row(align=True)
  676. row.operator("ed.undo", icon='LOOP_BACK')
  677. row.operator("ed.redo", icon='LOOP_FORWARDS')
  678. col.separator()
  679. (year, month, day) = bl_info["version"]
  680. col.label(text="Version: %s-%s-%s" % (year, month, day))
  681. classes = [
  682. PG_SMPLProperties,
  683. SMPLAddGender,
  684. SMPLSetTexture,
  685. SMPLRandomShapes,
  686. SMPLResetShapes,
  687. SMPLSnapGroundPlane,
  688. SMPLUpdateJointLocations,
  689. SMPLSetPoseshapes,
  690. SMPLResetPoseshapes,
  691. SMPLLoadPose,
  692. SMPLWritePose,
  693. SMPLResetPose,
  694. SMPLExportUnityFBX,
  695. SMPL_PT_Model,
  696. SMPL_PT_Shape,
  697. SMPL_PT_Pose,
  698. SMPL_PT_Export
  699. ]
  700. def register():
  701. from bpy.utils import register_class
  702. for cls in classes:
  703. bpy.utils.register_class(cls)
  704. # Store properties under WindowManager (not Scene) so that they are not saved in .blend files and always show default values after loading
  705. bpy.types.WindowManager.smpl_tool = PointerProperty(type=PG_SMPLProperties)
  706. def unregister():
  707. from bpy.utils import unregister_class
  708. for cls in classes:
  709. bpy.utils.unregister_class(cls)
  710. del bpy.types.WindowManager.smpl_tool
  711. if __name__ == "__main__":
  712. register()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/332543
推荐阅读
相关标签
  

闽ICP备14008679号