2025-07-01
This commit is contained in:
@@ -0,0 +1,559 @@
|
||||
# Copyright (C) 2021 Victor Soupday
|
||||
# This file is part of CC/iC Blender Tools <https://github.com/soupday/cc_blender_tools>
|
||||
#
|
||||
# CC/iC Blender Tools is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# CC/iC Blender Tools is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with CC/iC Blender Tools. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
from . import rigidbody, utils, bones, vars
|
||||
|
||||
|
||||
HEAD_RIG_NAME = "RLS_Hair_Rig_Head"
|
||||
JAW_RIG_NAME = "RLS_Hair_Rig_Jaw"
|
||||
HAIR_BONE_PREFIX = "Hair"
|
||||
BEARD_BONE_PREFIX = "Beard"
|
||||
HEAD_BONE_NAMES = ["ORG-spine.006", "CC_Base_Head", "RL_Head", "Head", "head"]
|
||||
JAW_BONE_NAMES = ["ORG-jaw", "CC_Base_JawRoot", "RL_JawRoot", "JawRoot", "teeth.B"]
|
||||
EYE_BONE_NAMES = ["ORG-eye.R", "ORG-eye.L", "CC_Base_R_Eye", "CC_Base_L_Eye", "CC_Base_R_Eye", "CC_Base_L_Eye"]
|
||||
ROOT_BONE_NAMES = HEAD_BONE_NAMES.copy().extend(JAW_BONE_NAMES.copy())
|
||||
|
||||
AVAILABLE_SPRING_RIG_LIST = []
|
||||
|
||||
|
||||
def get_all_parent_modes(chr_cache, arm):
|
||||
return ["HEAD", "JAW"]
|
||||
|
||||
|
||||
def get_spring_rig_name(arm, parent_mode):
|
||||
if parent_mode == "JAW":
|
||||
spring_rig_name = JAW_RIG_NAME
|
||||
else:
|
||||
spring_rig_name = HEAD_RIG_NAME
|
||||
|
||||
# fix any old spring bone rig names
|
||||
old_spring_rig_name = "RL_" + spring_rig_name[4:]
|
||||
if old_spring_rig_name in arm.data.bones:
|
||||
return old_spring_rig_name
|
||||
|
||||
return spring_rig_name
|
||||
|
||||
|
||||
def has_spring_rig(chr_cache, arm, parent_mode):
|
||||
spring_rig_name = get_spring_rig_name(arm, parent_mode)
|
||||
spring_rig = bones.get_bone(arm, spring_rig_name)
|
||||
return spring_rig is not None
|
||||
|
||||
|
||||
def has_spring_rigs(chr_cache, arm):
|
||||
parent_modes = get_all_parent_modes(chr_cache, arm)
|
||||
for parent_mode in parent_modes:
|
||||
if has_spring_rig(chr_cache, arm, parent_mode):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def has_spring_systems(chr_cache):
|
||||
if chr_cache:
|
||||
arm = chr_cache.get_armature()
|
||||
if arm:
|
||||
parent_modes = get_all_parent_modes(chr_cache, arm)
|
||||
for parent_mode in parent_modes:
|
||||
rig_prefix = get_spring_rig_prefix(parent_mode)
|
||||
rigid_body_system = rigidbody.get_spring_rigid_body_system(arm, rig_prefix)
|
||||
if rigid_body_system:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_spring_systems(chr_cache):
|
||||
spring_systems = []
|
||||
if chr_cache:
|
||||
arm = chr_cache.get_armature()
|
||||
if arm:
|
||||
parent_modes = get_all_parent_modes(chr_cache, arm)
|
||||
for parent_mode in parent_modes:
|
||||
rig_prefix = get_spring_rig_prefix(parent_mode)
|
||||
rigid_body_system = rigidbody.get_spring_rigid_body_system(arm, rig_prefix)
|
||||
if rigid_body_system:
|
||||
spring_systems.append(rigid_body_system)
|
||||
return spring_systems
|
||||
|
||||
|
||||
def rigidbody_state():
|
||||
has_rigidbody = False
|
||||
is_baked = False
|
||||
is_baking = False
|
||||
point_cache = None
|
||||
rigidbody_world = bpy.context.scene.rigidbody_world
|
||||
if rigidbody_world:
|
||||
has_rigidbody = True
|
||||
point_cache = rigidbody_world.point_cache
|
||||
is_baked = point_cache.is_baked
|
||||
is_baking = point_cache.is_baking
|
||||
return has_rigidbody, is_baked, is_baking, point_cache
|
||||
|
||||
|
||||
def get_spring_rigs(chr_cache, arm, parent_modes : list = None, mode = "POSE"):
|
||||
"""Returns { parent_mode: {
|
||||
"name": rig_name,
|
||||
"bone_name": rig_root.name,
|
||||
"bone": rig_root
|
||||
} }
|
||||
The bone will be either the edit bone, pose bone or bone depending on which mode Blender is in.
|
||||
(or the pose if preferred)
|
||||
"""
|
||||
if not parent_modes:
|
||||
parent_modes = get_all_parent_modes(chr_cache, arm)
|
||||
spring_rigs = {}
|
||||
for parent_mode in parent_modes:
|
||||
spring_rig_name = get_spring_rig_name(arm, parent_mode)
|
||||
spring_rig_bone = get_spring_rig(chr_cache, arm, parent_mode, mode)
|
||||
if spring_rig_bone:
|
||||
spring_rigs[parent_mode] = { "name": spring_rig_name,
|
||||
"bone_name" : spring_rig_bone.name,
|
||||
"bone": spring_rig_bone }
|
||||
return spring_rigs
|
||||
|
||||
|
||||
def get_spring_rig_names(chr_cache, arm, parent_modes = None, mode = "POSE"):
|
||||
spring_rigs = get_spring_rigs(chr_cache, arm, parent_modes, mode)
|
||||
return [v["bone_name"] for v in spring_rigs.values()]
|
||||
|
||||
|
||||
def get_spring_rig_from_child(chr_cache, arm, bone_name, prefer_pose = True):
|
||||
|
||||
try:
|
||||
if prefer_pose or utils.get_mode() == "POSE":
|
||||
bone = arm.pose.bones[bone_name]
|
||||
elif utils.get_mode() == "EDIT":
|
||||
bone = arm.data.edit_bones[bone_name]
|
||||
else:
|
||||
bone = arm.data.bones[bone_name]
|
||||
except:
|
||||
bone = None
|
||||
|
||||
if bone:
|
||||
|
||||
spring_rigs = get_spring_rigs(chr_cache, arm, mode = "POSE")
|
||||
|
||||
while bone.parent:
|
||||
for parent_mode in spring_rigs:
|
||||
if spring_rigs[parent_mode]["bone"] == bone.parent:
|
||||
return spring_rigs[parent_mode], bone.name, parent_mode
|
||||
bone = bone.parent
|
||||
|
||||
return None, None, None
|
||||
|
||||
|
||||
def get_spring_rig(chr_cache, arm, parent_mode, mode = "POSE", create_if_missing = False):
|
||||
"""This will return either the edit bone, pose bone or bone depending on which mode Blender is in.
|
||||
(or the pose if preferred)
|
||||
"""
|
||||
if parent_mode and chr_cache and arm:
|
||||
spring_rig_name = get_spring_rig_name(arm, parent_mode)
|
||||
spring_rig = None
|
||||
if mode == "EDIT" and utils.get_mode() != "EDIT":
|
||||
utils.edit_mode_to(arm)
|
||||
if mode == "POSE" or utils.get_mode() == "POSE":
|
||||
if spring_rig_name in arm.pose.bones:
|
||||
return arm.pose.bones[spring_rig_name]
|
||||
elif mode == "EDIT" and utils.get_mode() == "EDIT":
|
||||
if spring_rig_name in arm.data.edit_bones:
|
||||
spring_rig = arm.data.edit_bones[spring_rig_name]
|
||||
if not spring_rig and create_if_missing:
|
||||
anchor_bone_name = get_spring_anchor_name(chr_cache, arm, parent_mode)
|
||||
center_position = get_spring_rig_position(chr_cache, arm, parent_mode)
|
||||
spring_rig = bones.new_edit_bone(arm, spring_rig_name, anchor_bone_name)
|
||||
spring_rig.head = arm.matrix_world.inverted() @ center_position
|
||||
spring_rig.tail = arm.matrix_world.inverted() @ (center_position + Vector((0,1/32,0)))
|
||||
spring_rig.align_roll(Vector((0,0,1)))
|
||||
bones.set_bone_collection(arm, spring_rig, "Spring (Root)", None, vars.SPRING_ROOT_LAYER)
|
||||
bones.set_bone_collection_visibility(arm, "Spring (Root)", vars.SPRING_ROOT_LAYER, False)
|
||||
# TODO spring roots are put in the DEF bones by Rigify...
|
||||
return spring_rig
|
||||
else:
|
||||
if spring_rig_name in arm.data.bones:
|
||||
return arm.data.bones[spring_rig_name]
|
||||
return None
|
||||
|
||||
|
||||
def get_spring_rig_prefix(parent_mode):
|
||||
if parent_mode == "HEAD":
|
||||
return HAIR_BONE_PREFIX
|
||||
elif parent_mode == "JAW":
|
||||
return BEARD_BONE_PREFIX
|
||||
else:
|
||||
return "NONE"
|
||||
|
||||
|
||||
def get_spring_anchor_name(chr_cache, arm, parent_mode):
|
||||
if parent_mode == "HEAD":
|
||||
possible_head_bones = HEAD_BONE_NAMES
|
||||
for name in possible_head_bones:
|
||||
if name in arm.data.bones:
|
||||
return name
|
||||
return None
|
||||
elif parent_mode == "JAW":
|
||||
possible_jaw_bones = JAW_BONE_NAMES
|
||||
for name in possible_jaw_bones:
|
||||
if name in arm.data.bones:
|
||||
return name
|
||||
return None
|
||||
|
||||
|
||||
def get_spring_rig_position(chr_cache, arm, root_mode):
|
||||
"""Returns the approximate position inside the head between the ears at nose height."""
|
||||
|
||||
head_edit_bone = get_spring_anchor_edit_bone(chr_cache, arm, "HEAD")
|
||||
|
||||
if head_edit_bone:
|
||||
head_pos = arm.matrix_world @ head_edit_bone.head
|
||||
|
||||
eye_pos = Vector((0,0,0))
|
||||
count = 0
|
||||
for eye_bone_name in EYE_BONE_NAMES:
|
||||
eye_edit_bone = bones.get_edit_bone(arm, eye_bone_name)
|
||||
if eye_edit_bone:
|
||||
count += 1
|
||||
eye_pos += arm.matrix_world @ eye_edit_bone.head
|
||||
|
||||
if count > 0:
|
||||
eye_pos /= count
|
||||
|
||||
if root_mode == "HEAD":
|
||||
return Vector((head_pos[0], head_pos[1], eye_pos[2]))
|
||||
elif root_mode == "JAW":
|
||||
return Vector((head_pos[0], (head_pos[1] + 2 * eye_pos[1]) / 3, head_pos[2]))
|
||||
else:
|
||||
return head_pos
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_spring_anchor_edit_bone(chr_cache, arm, parent_mode):
|
||||
try:
|
||||
return arm.data.edit_bones[get_spring_anchor_name(chr_cache, arm, parent_mode)]
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def is_hair_bone(bone_name):
|
||||
if bone_name.startswith(HAIR_BONE_PREFIX) or bone_name.startswith(BEARD_BONE_PREFIX):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def is_hair_rig_bone(bone_name):
|
||||
if bone_name.startswith(HEAD_RIG_NAME) or bone_name.startswith(JAW_RIG_NAME):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def convert_spring_rig_to_accessory(chr_cache, arm, parent_mode):
|
||||
"""Removes all none hair rig vertex groups from objects so that CC4 recognizes them as accessories
|
||||
and not cloth or hair.\n\n
|
||||
Accessories are categorized by:\n
|
||||
1. A bone representing the accessory parented to a CC Base bone. (This is the spring rig root bone)
|
||||
2. Child accessory deformation bone(s) parented to the accessory bone in 1.
|
||||
3. Object(s) with vertex weights to ONLY these accessory deformation bones in 2.
|
||||
4. All vertices in the accessory must be weighted.
|
||||
"""
|
||||
groups_to_remove = []
|
||||
|
||||
# get a list of all bones in the spring rig
|
||||
spring_rig_bone = get_spring_rig(chr_cache, arm, parent_mode)
|
||||
spring_bones = bones.get_bone_children(spring_rig_bone)
|
||||
spring_bone_names = [ bone.name for bone in spring_bones ]
|
||||
|
||||
# find all character objects with vertex groups for these bones
|
||||
accessory_objects = set()
|
||||
objects = chr_cache.get_all_objects(include_armature=False,
|
||||
include_children=True,
|
||||
of_type="MESH")
|
||||
for obj in objects:
|
||||
for vg in obj.vertex_groups:
|
||||
if vg.name in spring_bone_names:
|
||||
accessory_objects.add(obj)
|
||||
|
||||
# in these objects remove all vertex groups not from these bones
|
||||
for obj in accessory_objects:
|
||||
groups_to_remove = []
|
||||
for vg in obj.vertex_groups:
|
||||
if vg.name not in spring_bone_names:
|
||||
groups_to_remove.append(vg)
|
||||
for vg in groups_to_remove:
|
||||
obj.vertex_groups.remove(vg)
|
||||
|
||||
|
||||
def is_rigified(chr_cache, rig, parent_mode):
|
||||
if chr_cache and rig and parent_mode:
|
||||
spring_rig = get_spring_rig(chr_cache, rig, parent_mode)
|
||||
if spring_rig:
|
||||
pose_bone = rig.pose.bones[spring_rig.name]
|
||||
if "rigified" in pose_bone and pose_bone["rigified"]:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def realign_spring_bones_axis(chr_cache, arm):
|
||||
|
||||
utils.edit_mode_to(arm, True)
|
||||
|
||||
# align z-axis away from the spring roots
|
||||
spring_rigs = get_spring_rigs(chr_cache, arm, mode = "EDIT")
|
||||
for parent_mode in spring_rigs:
|
||||
spring_root = spring_rigs[parent_mode]["bone"]
|
||||
spring_bones = bones.get_bone_children(spring_root, include_root=False)
|
||||
for bone in spring_bones:
|
||||
head = arm.matrix_world @ bone.head
|
||||
tail = arm.matrix_world @ bone.tail
|
||||
origin = arm.matrix_world @ spring_root.head
|
||||
z_axis = (((head + tail) * 0.5) - origin).normalized()
|
||||
bone.align_roll(z_axis)
|
||||
if bone.parent != spring_root:
|
||||
bone.use_connect = True
|
||||
|
||||
# save edit mode changes
|
||||
utils.object_mode_to(arm)
|
||||
|
||||
|
||||
def enumerate_spring_rigs(self, context):
|
||||
global AVAILABLE_SPRING_RIG_LIST
|
||||
props = vars.props()
|
||||
chr_cache = props.get_context_character_cache(context)
|
||||
|
||||
if chr_cache:
|
||||
arm = chr_cache.get_armature()
|
||||
|
||||
spring_rigs = get_spring_rigs(chr_cache, arm, mode = "POSE")
|
||||
AVAILABLE_SPRING_RIG_LIST.clear()
|
||||
for i, parent_mode in enumerate(spring_rigs):
|
||||
list_entry = (parent_mode, f"{parent_mode} Rig", f"{parent_mode} Rig")
|
||||
AVAILABLE_SPRING_RIG_LIST.append(list_entry)
|
||||
|
||||
if not spring_rigs:
|
||||
AVAILABLE_SPRING_RIG_LIST.append(("NONE", "No Rig", "No Rig"))
|
||||
|
||||
return AVAILABLE_SPRING_RIG_LIST
|
||||
|
||||
|
||||
def show_spring_bone_edit_layer(chr_cache, arm, show):
|
||||
if arm:
|
||||
if show:
|
||||
bones.set_bone_collection_visibility(arm, "Spring (Edit)", vars.SPRING_EDIT_LAYER, True, only=True)
|
||||
arm.show_in_front = True
|
||||
arm.display_type = 'SOLID'
|
||||
#arm.data.display_type = 'STICK'
|
||||
|
||||
else:
|
||||
bones.set_bone_collection_visibility(arm, "Spring (Edit)", vars.SPRING_EDIT_LAYER, False, only=True)
|
||||
arm.show_in_front = False
|
||||
if chr_cache.rigified:
|
||||
arm.display_type = 'WIRE'
|
||||
else:
|
||||
arm.display_type = 'SOLID'
|
||||
#arm.data.display_type = 'OCTAHEDRAL'
|
||||
|
||||
|
||||
def show_spring_bone_rig_layers(chr_cache, arm, show):
|
||||
if arm:
|
||||
if show:
|
||||
bones.set_bone_collection_visibility(arm, "Spring (FK)", vars.SPRING_FK_LAYER, True)
|
||||
arm.show_in_front = False
|
||||
|
||||
else:
|
||||
bones.set_bone_collection_visibility(arm, "Spring (FK)", vars.SPRING_FK_LAYER, False)
|
||||
arm.show_in_front = False
|
||||
if chr_cache.rigified:
|
||||
arm.display_type = 'WIRE'
|
||||
else:
|
||||
arm.display_type = 'SOLID'
|
||||
#arm.data.display_type = 'OCTAHEDRAL'
|
||||
|
||||
|
||||
def stop_spring_animation(context):
|
||||
# stop any playing animation
|
||||
if context.screen.is_animation_playing:
|
||||
bpy.ops.screen.animation_cancel(restore_frame=False)
|
||||
|
||||
# reset the animation (it is very unstable if we don't do this)
|
||||
bpy.ops.screen.frame_jump(end = False)
|
||||
|
||||
|
||||
def reset_spring_physics(context):
|
||||
props = vars.props()
|
||||
chr_cache = props.get_context_character_cache(context)
|
||||
if chr_cache:
|
||||
arm = chr_cache.get_armature()
|
||||
if arm:
|
||||
arm.data.pose_position = "POSE"
|
||||
|
||||
# reset the physics cache
|
||||
bpy.context.scene.frame_current = bpy.context.scene.frame_current + 1
|
||||
rigidbody.reset_cache(context)
|
||||
|
||||
# reset the animation again for good measure...
|
||||
bpy.ops.screen.frame_jump(end = True)
|
||||
bpy.ops.screen.frame_jump(end = False)
|
||||
|
||||
|
||||
def add_spring_colliders(chr_cache):
|
||||
arm = chr_cache.get_armature()
|
||||
if not rigidbody.has_rigid_body_colliders(arm):
|
||||
json_data = chr_cache.get_json_data()
|
||||
bone_mapping = None
|
||||
if chr_cache.rigified:
|
||||
bone_mapping = chr_cache.get_rig_bone_mapping()
|
||||
rigidbody.build_rigid_body_colliders(chr_cache, json_data, bone_mapping=bone_mapping)
|
||||
|
||||
|
||||
def toggle_show_spring_bones(chr_cache):
|
||||
if chr_cache:
|
||||
arm = chr_cache.get_armature()
|
||||
else:
|
||||
arm = utils.get_armature_from_objects(bpy.context.selected_objects)
|
||||
if arm:
|
||||
if bones.is_bone_collection_visible(arm, "Spring (Edit)", vars.SPRING_EDIT_LAYER):
|
||||
show_spring_bone_edit_layer(chr_cache, arm, False)
|
||||
else:
|
||||
show_spring_bone_edit_layer(chr_cache, arm, True)
|
||||
|
||||
|
||||
class CC3OperatorSpringBones(bpy.types.Operator):
|
||||
"""Blender Spring Bone Functions"""
|
||||
bl_idname = "cc3.springbones"
|
||||
bl_label = "Spring Bone Simulation"
|
||||
#bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
|
||||
param: bpy.props.StringProperty(
|
||||
name = "param",
|
||||
default = ""
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
props = vars.props()
|
||||
prefs = vars.prefs()
|
||||
|
||||
mode_selection = utils.store_mode_selection_state()
|
||||
|
||||
chr_cache = props.get_context_character_cache(context)
|
||||
arm = None
|
||||
if chr_cache:
|
||||
arm = chr_cache.get_armature()
|
||||
|
||||
if self.param == "MAKE_RIGID_BODY_SYSTEM":
|
||||
stop_spring_animation(context)
|
||||
|
||||
if arm:
|
||||
parent_mode = chr_cache.available_spring_rigs
|
||||
spring_rig_name = get_spring_rig_name(arm, parent_mode)
|
||||
spring_rig_prefix = get_spring_rig_prefix(parent_mode)
|
||||
|
||||
rigidbody.build_spring_rigid_body_system(chr_cache, spring_rig_prefix, spring_rig_name)
|
||||
add_spring_colliders(chr_cache)
|
||||
|
||||
reset_spring_physics(context)
|
||||
|
||||
utils.restore_mode_selection_state(mode_selection)
|
||||
|
||||
if self.param == "REMOVE_RIGID_BODY_SYSTEM":
|
||||
stop_spring_animation(context)
|
||||
|
||||
if arm:
|
||||
parent_mode = props.hair_rig_bone_root
|
||||
spring_rig_name = get_spring_rig_name(arm, parent_mode)
|
||||
spring_rig_prefix = get_spring_rig_prefix(parent_mode)
|
||||
|
||||
rigidbody.remove_existing_rigid_body_system(arm, spring_rig_prefix, spring_rig_name)
|
||||
|
||||
reset_spring_physics(context)
|
||||
|
||||
if self.param == "ENABLE_RIGID_BODY_COLLISION":
|
||||
stop_spring_animation(context)
|
||||
|
||||
objects = utils.get_selected_meshes(context)
|
||||
for body in objects:
|
||||
rigidbody.enable_rigid_body_collision_mesh(chr_cache, body)
|
||||
|
||||
reset_spring_physics(context)
|
||||
|
||||
utils.restore_mode_selection_state(mode_selection)
|
||||
|
||||
if self.param == "DISABLE_RIGID_BODY_COLLISION":
|
||||
stop_spring_animation(context)
|
||||
|
||||
objects = utils.get_selected_meshes(context)
|
||||
for obj in objects:
|
||||
rigidbody.disable_rigid_body_collision_mesh(chr_cache, obj)
|
||||
|
||||
reset_spring_physics(context)
|
||||
|
||||
utils.restore_mode_selection_state(mode_selection)
|
||||
|
||||
if self.param == "RESET_PHYSICS":
|
||||
stop_spring_animation(context)
|
||||
reset_spring_physics(context)
|
||||
|
||||
utils.restore_mode_selection_state(mode_selection)
|
||||
|
||||
elif self.param == "BUILD_COLLIDERS":
|
||||
stop_spring_animation(context)
|
||||
reset_spring_physics(context)
|
||||
add_spring_colliders(chr_cache)
|
||||
rigidbody.toggle_show_colliders(arm)
|
||||
utils.restore_mode_selection_state(mode_selection)
|
||||
|
||||
elif self.param == "REMOVE_COLLIDERS":
|
||||
stop_spring_animation(context)
|
||||
reset_spring_physics(context)
|
||||
rigidbody.remove_rigid_body_colliders(arm)
|
||||
#utils.restore_mode_selection_state(mode_selection)
|
||||
|
||||
elif self.param == "TOGGLE_SHOW_COLLIDERS":
|
||||
rigidbody.toggle_show_colliders(arm)
|
||||
#utils.restore_mode_selection_state(mode_selection)
|
||||
|
||||
if self.param == "BAKE_PHYSICS":
|
||||
utils.object_mode_to(arm)
|
||||
reset_spring_physics(context)
|
||||
utils.log_info("Baking rigid body world point cache...")
|
||||
bpy.ops.ptcache.bake({"point_cache": bpy.context.scene.rigidbody_world.point_cache},
|
||||
"INVOKE_DEFAULT", bake=True)
|
||||
# as py.ops.ptcache.bake is a modal operator, don't do *anything* afterwards,
|
||||
# or Blender will crash...
|
||||
return {"FINISHED"}
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
props = vars.props()
|
||||
|
||||
if properties.param == "MAKE_RIGID_BODY_SYSTEM":
|
||||
return "Build the rigid body simulation for the selected spring rig and sets contraints to copy the simulation to the spring bones"
|
||||
elif properties.param == "REMOVE_RIGID_BODY_SYSTEM":
|
||||
return "Removes the rigid body simulation for the selected spring rig and removes all constraints"
|
||||
elif properties.param == "ENABLE_RIGID_BODY_COLLISION":
|
||||
return "Enables rigid body collision for the selected mesh (or it's collision proxy mesh), so it can interact with the spring bone simulation"
|
||||
elif properties.param == "DISABLE_RIGID_BODY_COLLISION":
|
||||
return "Removes rigid body collision for the selected mesh (or it's collision proxy mesh), so it can interact with the spring bone simulation"
|
||||
elif properties.param == "RESET_PHYSICS":
|
||||
return "Resets the spring bone physics rigid body world point cache and synchronizes the cache range with the current scene or preview range"
|
||||
elif properties.param == "BAKE_PHYSICS":
|
||||
return "Bakes the rigid body world point cache for all spring bone simulations"
|
||||
return ""
|
||||
Reference in New Issue
Block a user