Files
Yusarina a929f68ad4 Holy shit this was a pain
- Truly fixes PMX Import lol, i messed up completely
- Updated MMD Tools to use Cats One
2025-11-19 06:35:06 +00:00

295 lines
8.1 KiB
Python

# Copyright 2014 MMD Tools authors
# This file is part of MMD Tools.
"""Properties for rigid bodies and joints"""
import bpy
from .. import bpyutils
from ..core import rigid_body
from ..core.model import FnModel
from ..core.rigid_body import FnRigidBody, RigidBodyMaterial
from . import patch_library_overridable
def _updateCollisionGroup(prop, _context):
obj = prop.id_data
materials = obj.data.materials
if len(materials) == 0:
materials.append(RigidBodyMaterial.getMaterial(prop.collision_group_number))
else:
obj.material_slots[0].material = RigidBodyMaterial.getMaterial(prop.collision_group_number)
def _updateType(prop, _context):
obj = prop.id_data
rb = obj.rigid_body
if rb:
rb.kinematic = int(prop.type) == rigid_body.MODE_STATIC
def _updateShape(prop, _context):
obj = prop.id_data
if len(obj.data.vertices) > 0:
size = prop.size
prop.size = size # update mesh
rb = obj.rigid_body
if rb:
rb.collision_shape = prop.shape
def _get_bone(prop):
obj = prop.id_data
relation = obj.constraints.get("mmd_tools_rigid_parent", None)
if relation:
arm = relation.target
bone_name = relation.subtarget
if arm is not None and bone_name in arm.data.bones:
return bone_name
return prop.get("bone", "")
def _set_bone(prop, value):
bone_name = value
obj = prop.id_data
relation = obj.constraints.get("mmd_tools_rigid_parent", None)
if relation is None:
relation = obj.constraints.new("CHILD_OF")
relation.name = "mmd_tools_rigid_parent"
relation.mute = True
arm = relation.target
if arm is None:
root = FnModel.find_root_object(obj)
if root:
arm = relation.target = FnModel.find_armature_object(root)
if arm is not None and bone_name in arm.data.bones:
relation.subtarget = bone_name
else:
relation.subtarget = bone_name = ""
prop["bone"] = bone_name
def _get_size(prop):
if prop.id_data.mmd_type != "RIGID_BODY":
return (0, 0, 0)
return FnRigidBody.get_rigid_body_size(prop.id_data)
def _set_size(prop, value):
obj = prop.id_data
assert obj.mode == "OBJECT" # not support other mode yet
shape = prop.shape
mesh = obj.data
rb = obj.rigid_body
current_size = FnRigidBody.get_rigid_body_size(obj)
is_zero_size = all(abs(s) < 1e-6 for s in current_size)
if len(mesh.vertices) == 0 or rb is None or rb.collision_shape != shape or is_zero_size:
if shape == "SPHERE":
bpyutils.makeSphere(
radius=value[0],
target_object=obj,
)
elif shape == "BOX":
bpyutils.makeBox(
size=value,
target_object=obj,
)
elif shape == "CAPSULE":
bpyutils.makeCapsule(
radius=value[0],
height=value[1],
target_object=obj,
)
mesh.update()
if rb:
rb.collision_shape = shape
else:
if shape == "SPHERE":
radius = max(value[0], 1e-3)
for v in mesh.vertices:
vec = v.co.normalized()
v.co = vec * radius
elif shape == "BOX":
x = max(value[0], 1e-3)
y = max(value[1], 1e-3)
z = max(value[2], 1e-3)
for v in mesh.vertices:
x0, y0, z0 = v.co
x0 = -x if x0 < 0 else x
y0 = -y if y0 < 0 else y
z0 = -z if z0 < 0 else z
v.co = [x0, y0, z0]
elif shape == "CAPSULE":
r0, h0, xx = FnRigidBody.get_rigid_body_size(prop.id_data)
h0 *= 0.5
radius = max(value[0], 1e-3)
height = max(value[1], 1e-3) * 0.5
scale = radius / max(r0, 1e-3)
for v in mesh.vertices:
x0, y0, z0 = v.co
x0 *= scale
y0 *= scale
if z0 < 0:
z0 = (z0 + h0) * scale - height
else:
z0 = (z0 - h0) * scale + height
v.co = [x0, y0, z0]
mesh.update()
def _get_rigid_name(prop):
return prop.get("name", "")
def _set_rigid_name(prop, value):
prop["name"] = value
class MMDRigidBody(bpy.types.PropertyGroup):
name_j: bpy.props.StringProperty(
name="Name",
description="Japanese Name",
default="",
get=_get_rigid_name,
set=_set_rigid_name,
)
name_e: bpy.props.StringProperty(
name="Name(Eng)",
description="English Name",
default="",
)
collision_group_number: bpy.props.IntProperty(
name="Collision Group",
description="The collision group of the object",
min=0,
max=15,
default=1,
update=_updateCollisionGroup,
)
collision_group_mask: bpy.props.BoolVectorProperty(
name="Collision Group Mask",
description="The groups the object can not collide with",
size=16,
subtype="LAYER",
)
type: bpy.props.EnumProperty(
name="Rigid Type",
description="Select rigid type",
items=[
(str(rigid_body.MODE_STATIC), "Bone", "Rigid body's orientation completely determined by attached bone", 1),
(str(rigid_body.MODE_DYNAMIC), "Physics", "Attached bone's orientation completely determined by rigid body", 2),
(str(rigid_body.MODE_DYNAMIC_BONE), "Physics + Bone", "Bone determined by combination of parent and attached rigid body", 3),
],
update=_updateType,
)
shape: bpy.props.EnumProperty(
name="Shape",
description="Select the collision shape",
items=[
("SPHERE", "Sphere", "", 1),
("BOX", "Box", "", 2),
("CAPSULE", "Capsule", "", 3),
],
update=_updateShape,
)
bone: bpy.props.StringProperty(
name="Bone",
description="Target bone",
default="",
get=_get_bone,
set=_set_bone,
)
size: bpy.props.FloatVectorProperty(
name="Size",
description="Size of the object",
subtype="XYZ",
size=3,
min=0,
step=0.1,
get=_get_size,
set=_set_size,
)
@staticmethod
def register():
bpy.types.Object.mmd_rigid = patch_library_overridable(bpy.props.PointerProperty(type=MMDRigidBody))
@staticmethod
def unregister():
del bpy.types.Object.mmd_rigid
def _updateSpringLinear(prop, context):
obj = prop.id_data
rbc = obj.rigid_body_constraint
if rbc:
rbc.spring_stiffness_x = prop.spring_linear[0]
rbc.spring_stiffness_y = prop.spring_linear[1]
rbc.spring_stiffness_z = prop.spring_linear[2]
def _updateSpringAngular(prop, context):
obj = prop.id_data
rbc = obj.rigid_body_constraint
if rbc and hasattr(rbc, "use_spring_ang_x"):
rbc.spring_stiffness_ang_x = prop.spring_angular[0]
rbc.spring_stiffness_ang_y = prop.spring_angular[1]
rbc.spring_stiffness_ang_z = prop.spring_angular[2]
class MMDJoint(bpy.types.PropertyGroup):
name_j: bpy.props.StringProperty(
name="Name",
description="Japanese Name",
default="",
)
name_e: bpy.props.StringProperty(
name="Name(Eng)",
description="English Name",
default="",
)
spring_linear: bpy.props.FloatVectorProperty(
name="Spring(Linear)",
description="Spring constant of movement",
subtype="XYZ",
size=3,
min=0,
step=0.1,
update=_updateSpringLinear,
)
spring_angular: bpy.props.FloatVectorProperty(
name="Spring(Angular)",
description="Spring constant of rotation",
subtype="XYZ",
size=3,
min=0,
step=0.1,
update=_updateSpringAngular,
)
@staticmethod
def register():
bpy.types.Object.mmd_joint = patch_library_overridable(bpy.props.PointerProperty(type=MMDJoint))
@staticmethod
def unregister():
del bpy.types.Object.mmd_joint