#include "KingSystem/Physics/RigidBody/physRigidBodyParam.h" #include #include #include "KingSystem/Physics/RigidBody/Shape/Box/physBoxRigidBody.h" #include "KingSystem/Physics/RigidBody/Shape/Box/physBoxShape.h" #include "KingSystem/Physics/RigidBody/Shape/BoxWater/physBoxWaterRigidBody.h" #include "KingSystem/Physics/RigidBody/Shape/BoxWater/physBoxWaterShape.h" #include "KingSystem/Physics/RigidBody/Shape/Capsule/physCapsuleRigidBody.h" #include "KingSystem/Physics/RigidBody/Shape/Capsule/physCapsuleShape.h" #include "KingSystem/Physics/RigidBody/Shape/Cylinder/physCylinderRigidBody.h" #include "KingSystem/Physics/RigidBody/Shape/Cylinder/physCylinderShape.h" #include "KingSystem/Physics/RigidBody/Shape/CylinderWater/physCylinderWaterRigidBody.h" #include "KingSystem/Physics/RigidBody/Shape/CylinderWater/physCylinderWaterShape.h" #include "KingSystem/Physics/RigidBody/Shape/List/physListShape.h" #include "KingSystem/Physics/RigidBody/Shape/List/physListShapeRigidBody.h" #include "KingSystem/Physics/RigidBody/Shape/Polytope/physPolytopeRigidBody.h" #include "KingSystem/Physics/RigidBody/Shape/Polytope/physPolytopeShape.h" #include "KingSystem/Physics/RigidBody/Shape/Sphere/physSphereRigidBody.h" #include "KingSystem/Physics/RigidBody/Shape/Sphere/physSphereShape.h" #include "KingSystem/Physics/RigidBody/Shape/physShapeParamObj.h" #include "KingSystem/Physics/System/physEntityGroupFilter.h" #include "KingSystem/Utils/SafeDelete.h" namespace ksys::phys { RigidBodyParam::RigidBodyParam() = default; RigidBodyParam::~RigidBodyParam() = default; RigidBodyParam::Info::Info() : rigid_body_name("RigidBody", "rigid_body_name", this), mass(1.0, "mass", this), volume(0.0, "volume", this), toi(false, "toi", this), mEnableAutoAddWorld(false, "mEnableAutoAddWorld", this), navmesh("NOT_USE", "navmesh", this), navmesh_sub_material("Others", "navmesh_sub_material", this), inertia(sead::Vector3f::ones, "inertia", this), center_of_mass(sead::Vector3f::zero, "center_of_mass", this), bounding_center(sead::Vector3f::zero, "bounding_center", this), bounding_extents(sead::Vector3f::zero, "bounding_extents", this), contact_point_info(sead::SafeString::cEmptyString, "contact_point_info", this), collision_info(sead::SafeString::cEmptyString, "collision_info", this), max_linear_velocity(200.0, "max_linear_velocity", this), linear_damping(0.0, "linear_damping", this), max_angular_velocity_rad(200.0, "max_angular_velocity_rad", this), angular_damping(0.05, "angular_damping", this), max_impulse(-1.0, "max_impulse", this), col_impulse_scale(1.0, "col_impulse_scale", this), ignore_normal_for_impulse(false, "ignore_normal_for_impulse", this), always_character_mass_scaling(false, "always_character_mass_scaling", this), friction_scale(1.0, "friction_scale", this), restitution_scale(1.0, "restitution_scale", this), water_buoyancy_scale(1.0, "water_buoyancy_scale", this), water_flow_effective_rate(1.0, "water_flow_effective_rate", this), magne_mass_scaling_factor(1.0, "magne_mass_scaling_factor", this), motion_type("Fixed", "motion_type", this), layer(sead::SafeString::cEmptyString, "layer", this), groundhit(sead::SafeString::cEmptyString, "groundhit", this), use_ground_hit_type_mask(false, "use_ground_hit_type_mask", this), ground_hit_type_mask(sead::SafeString::cEmptyString, "ground_hit_type_mask", this), receiver_type(sead::SafeString::cEmptyString, "receiver_type", this), no_hit_ground(false, "no_hit_ground", this), no_hit_water(false, "no_hit_water", this), no_char_standing_on(false, "no_char_standing_on", this), contact_mask(0, "contact_mask", this), link_matrix(sead::SafeString::cEmptyString, "link_matrix", this), link_entity_set(sead::SafeString::cEmptyString, "link_entity_set", this), link_entity_body(sead::SafeString::cEmptyString, "link_entity_body", this), use_entity_shape(false, "use_entity_shape", this), shape_num(0, "shape_num", this) {} RigidBodyParam::Info::~Info() = default; bool RigidBodyParam::parse(const agl::utl::ResParameterList& res_list, sead::Heap* heap) { if (!res_list) return false; info.applyResParameterObj(res_list.getResParameterObj(0)); const int num_shapes = info.shape_num.ref(); if (num_shapes < 1) return true; shapes.allocBufferAssert(num_shapes, heap); for (int i = 0; i < num_shapes; ++i) { if (!shapes[i].parse(res_list.getResParameterObj(i + 1), heap)) return false; } return true; } void RigidBodyParam::makeInstanceParam(RigidBodyInstanceParam* param) const { param->mass = *info.mass; param->name = info.rigid_body_name->cstr(); param->inertia = *info.inertia; param->center_of_mass = *info.center_of_mass; param->max_linear_velocity = *info.max_linear_velocity; param->max_angular_velocity_rad = *info.max_angular_velocity_rad; param->toi = *info.toi; param->motion_type = getMotionType(); param->linear_damping = *info.linear_damping; param->angular_damping = *info.angular_damping; param->max_impulse = *info.max_impulse; param->col_impulse_scale = *info.col_impulse_scale; param->ignore_normal_for_impulse = *info.ignore_normal_for_impulse; param->always_character_mass_scaling = *info.always_character_mass_scaling; param->friction_scale = *info.friction_scale; param->restitution_scale = *info.restitution_scale; param->water_buoyancy_scale = *info.water_buoyancy_scale; param->water_flow_effective_rate = *info.water_flow_effective_rate; param->magne_mass_scaling_factor = *info.magne_mass_scaling_factor; param->contact_layer = getContactLayer(); param->groundhit = getGroundHit(); param->groundhit_mask = info.ground_hit_mask; param->contact_mask = *info.contact_mask; param->no_hit_ground = *info.no_hit_ground; param->no_hit_water = *info.no_hit_water; param->no_char_standing_on = *info.no_char_standing_on; receiverMaskGetSensorLayerMaskForType(¶m->receiver_mask, *info.receiver_type); } template static bool makeParamImpl(const RigidBodyParam& param, RigidBodyInstanceParam* out, ShapeType shape_type) { if (param.getShapeType() != shape_type) return false; param.makeInstanceParam(out); std::invoke(Getter, param.shapes[0], static_cast(out)); return true; } template static bool makeParam(const RigidBodyParam& param, ParamT* out, ShapeType shape_type) { return makeParamImpl(param, out, shape_type); } static bool makeSphereParam(const RigidBodyParam& param, SphereParam* out) { return makeParam<&ShapeParamObj::getSphere>(param, out, ShapeType::Sphere); } static bool makeCapsuleParam(const RigidBodyParam& param, CapsuleParam* out) { return makeParam<&ShapeParamObj::getCapsule>(param, out, ShapeType::Capsule); } static bool makeBoxParam(const RigidBodyParam& param, BoxParam* out) { return makeParam<&ShapeParamObj::getBox>(param, out, ShapeType::Box); } static bool makeCylinderParam(const RigidBodyParam& param, CylinderParam* out) { return makeParam<&ShapeParamObj::getCylinder>(param, out, ShapeType::Cylinder); } static bool makePolytopeParam(const RigidBodyParam& param, PolytopeParam* out) { return makeParam<&ShapeParamObj::getPolytope>(param, out, ShapeType::Polytope); } RigidBody* RigidBodyParam::createRigidBody(SystemGroupHandler* group_handler, sead::Heap* heap, RigidBodyParam::CreateFixedBoxWithNoCollision no_collision) const { if (no_collision == CreateFixedBoxWithNoCollision::Yes) { BoxParam param; makeInstanceParam(¶m); if (getContactLayerType(param.contact_layer) == ContactLayerType::Entity) param.contact_layer = ContactLayer::EntityNoHit; else param.contact_layer = ContactLayer::SensorNoHit; param.motion_type = MotionType::Fixed; param.translate = *info.bounding_center; param.extents = *info.bounding_extents; param.system_group_handler = group_handler; auto* body = BoxRigidBody::make(¶m, heap); body->setFlag20(); return body; } const int num_shapes = *info.shape_num; if (num_shapes == 1) { switch (getShapeType()) { case ShapeType::Sphere: { SphereParam param; makeSphereParam(*this, ¶m); param.system_group_handler = group_handler; return SphereRigidBody::make(¶m, heap); } case ShapeType::Capsule: { CapsuleParam param; makeCapsuleParam(*this, ¶m); param.system_group_handler = group_handler; return CapsuleRigidBody::make(¶m, heap); } case ShapeType::Box: { BoxParam param; makeBoxParam(*this, ¶m); param.system_group_handler = group_handler; if (param.contact_layer == ContactLayer::EntityWater) return BoxWaterRigidBody::make(¶m, heap); return BoxRigidBody::make(¶m, heap); } case ShapeType::Cylinder: { CylinderParam param; makeCylinderParam(*this, ¶m); param.system_group_handler = group_handler; if (param.contact_layer == ContactLayer::EntityWater) return CylinderWaterRigidBody::make(¶m, heap); return CylinderRigidBody::make(¶m, heap); } case ShapeType::Polytope: { PolytopeParam param; makePolytopeParam(*this, ¶m); param.system_group_handler = group_handler; auto* body = PolytopeRigidBody::make(¶m, heap); for (int i = 0, n = *shapes[0].vertex_num; i < n; ++i) body->setVertex(i, *shapes[0].vertices[i]); body->setVolume(*info.volume); body->updateShape(); return body; } case ShapeType::List: case ShapeType::CharacterPrism: case ShapeType::BoxWater: case ShapeType::CylinderWater: case ShapeType::Unknown: break; } SEAD_ASSERT_MSG(false, "unexpected shape type (shape_num=1)"); return nullptr; } // Multiple shapes. Use a ListShape. ListShapeRigidBodyParam list_param; makeInstanceParam(&list_param); list_param.system_group_handler = group_handler; list_param.num_shapes = num_shapes; auto* list_body = ListShapeRigidBody::make(&list_param, heap); list_body->setUpdateRequestedFlag(); for (int i = 0; i < num_shapes; ++i) { switch (shapes[i].getShapeType()) { case ShapeType::Sphere: { SphereShapeParam param; shapes[i].getSphere(¶m); list_body->replaceWithNewSphere(i, param, heap); continue; } case ShapeType::Capsule: { CapsuleShapeParam param; shapes[i].getCapsule(¶m); list_body->replaceWithNewCapsule(i, param, heap); continue; } case ShapeType::Box: { BoxShapeParam param; shapes[i].getBox(¶m); list_body->replaceWithNewBox(i, param, heap); continue; } case ShapeType::Cylinder: { CylinderShapeParam param; shapes[i].getCylinder(¶m); list_body->replaceWithNewCylinder(i, param, heap); continue; } case ShapeType::Polytope: { PolytopeShapeParam param; shapes[i].getPolytope(¶m); auto* polytope = static_cast(list_body->replaceWithNewPolytope(i, param, heap)); for (int vertex_idx = 0, n = *shapes[i].vertex_num; vertex_idx < n; ++vertex_idx) polytope->setVertex(vertex_idx, *shapes[i].vertices[vertex_idx]); continue; } case ShapeType::List: case ShapeType::CharacterPrism: case ShapeType::BoxWater: case ShapeType::CylinderWater: case ShapeType::Unknown: break; } SEAD_ASSERT_MSG(false, "unexpected shape type for list shape"); util::safeDelete(list_body); } if (!list_body) return nullptr; list_body->updateShape(); list_body->processUpdateRequests(); return list_body; } RigidBody* RigidBodyParam::createEntityShapeBody(RigidBody* linked_body, SystemGroupHandler* group_handler, sead::Heap* heap) const { auto* linked_shape_body = sead::DynamicCast(linked_body); if (!linked_shape_body) return nullptr; auto* body = RigidBodyFromShape::createEntityShapeBody(*info.rigid_body_name, getContactLayer(), linked_shape_body, heap, group_handler); if (!body) return nullptr; body->setUpdateRequestedFlag(); body->setMaxLinearVelocity(*info.max_linear_velocity); body->setMaxAngularVelocity(*info.max_angular_velocity_rad); body->setCenterOfMassInLocal(*info.center_of_mass); body->updateCollidableQualityType(*info.toi); body->processUpdateRequests(); return body; } ContactLayer RigidBodyParam::getContactLayer() const { return contactLayerFromText(*info.layer); } GroundHit RigidBodyParam::getGroundHit() const { return groundHitFromText(*info.groundhit); } MotionType RigidBodyParam::getMotionType() const { return motionTypeFromText(*info.motion_type); } ShapeType RigidBodyParam::getShapeType() const { if (*info.shape_num > 1) return ShapeType::List; return shapes[0].getShapeType(); } namespace { constexpr const char* navmesh_types[] = { "NOT_USE", "STATIC_CUTTING", "STATIC_WALKABLE_AND_CUTTING", "STATIC_WALKABLE_AND_CUTTING_CGO", "STATIC_DEPENDS_ON_WALL_CODE", "STATIC_ENEMY_ONLY", "STATIC_NPC_ONLY", "STATIC_LOW_CEILING", "STATIC_GIANT_NO_ENTRY_PAINT", "STATIC_CUTTING_CARVER", "STATIC_AIR_WALL_FOR_HORSE", "STATIC_FENCE_FOR_HORSE", "STATIC_CUTTING_LARGE", "STATIC_CURSE", "DYNAMIC_OBSTACLE", "DYNAMIC_SILHOUETTE_AND_OBSTACLE", "DYNAMIC_WITHOUT_TOLERANCE", "MATERIAL_PAINT_AND_OBSTACLE", "MATERIAL_PAINT", "MATERIAL_WITHOUT_TOLERANCE", }; struct NavMeshSubMaterialInfo { sead::SafeString name; NavMeshSubMaterial value; }; } // namespace void RigidBodyParam::Info::postRead_() { for (int i = 0; i < int(std::size(navmesh_types)); ++i) { if (*navmesh == navmesh_types[i]) { navmesh_val = NavMeshType(i); break; } } navmesh_sub_material_val = NavMeshSubMaterial::None; const NavMeshSubMaterialInfo sub_material_info[] = { {"LiftableByEnemy", NavMeshSubMaterial::LiftableByEnemy}, {"Fire", NavMeshSubMaterial::Fire}, {"Curse", NavMeshSubMaterial::Curse}, {"NPCOnly", NavMeshSubMaterial::NPCOnly}, {"GiantNoEntry", NavMeshSubMaterial::GiantNoEntry}, {"Pass", NavMeshSubMaterial::Pass}, }; for (int i = 0; i < int(std::size(sub_material_info)); ++i) { if (*navmesh_sub_material == sub_material_info[i].name) { navmesh_sub_material_val = sub_material_info[i].value; break; } } if (*use_ground_hit_type_mask && !ground_hit_type_mask->isEmpty()) { ground_hit_mask = 0; sead::FixedSafeString<64> type_str; auto it = ground_hit_type_mask->tokenBegin(","); const auto end = ground_hit_type_mask->tokenEnd(","); while (end != it) { it.getAndForward(&type_str); if (!type_str.isEmpty()) ground_hit_mask = orEntityGroundHitMask(ground_hit_mask, type_str); } } else { ground_hit_mask = 0; } } } // namespace ksys::phys