物理系统
This commit is contained in:
parent
b8b2dd5520
commit
5ddfd76430
@ -210,9 +210,9 @@ public:
|
||||
if (collider.Shape == MetaCoreColliderShape::Box) return std::make_unique<btBoxShape>(Bt(size * 0.5F));
|
||||
if (collider.Shape == MetaCoreColliderShape::Sphere) return std::make_unique<btSphereShape>(std::max(0.001F, collider.Radius * std::max({absoluteScale.x, absoluteScale.y, absoluteScale.z})));
|
||||
if (collider.Shape == MetaCoreColliderShape::Capsule) {
|
||||
const float radius = std::max(0.001F, collider.Radius * std::max(absoluteScale.x, absoluteScale.z));
|
||||
const float cylinderHeight = std::max(0.0F, collider.Height * absoluteScale.y - 2.0F * radius);
|
||||
return std::make_unique<btCapsuleShape>(radius, cylinderHeight);
|
||||
const float radius = std::max(0.001F, collider.Radius * std::max(absoluteScale.x, absoluteScale.y));
|
||||
const float cylinderHeight = std::max(0.0F, collider.Height * absoluteScale.z - 2.0F * radius);
|
||||
return std::make_unique<btCapsuleShapeZ>(radius, cylinderHeight);
|
||||
}
|
||||
if (!MeshProvider || !collider.MeshAssetGuid.IsValid()) { Diagnostics.push_back("对象 " + std::to_string(objectId) + " 缺少网格碰撞载荷"); return {}; }
|
||||
const auto mesh = MeshProvider(collider.MeshAssetGuid);
|
||||
@ -405,7 +405,7 @@ void MetaCorePhysicsWorld::Step(double fixedDeltaSeconds) {
|
||||
(void)id;
|
||||
if (!entry->Character) continue;
|
||||
btVector3 velocity = entry->Body->getLinearVelocity();
|
||||
velocity.setY(std::max(velocity.y(), -entry->CharacterMaxFallSpeed));
|
||||
velocity.setZ(std::max(velocity.z(), -entry->CharacterMaxFallSpeed));
|
||||
entry->Body->setLinearVelocity(velocity);
|
||||
}
|
||||
Impl_->World->stepSimulation(static_cast<btScalar>(fixedDeltaSeconds), 0);
|
||||
@ -434,7 +434,7 @@ std::optional<MetaCorePhysicsHit> MetaCorePhysicsWorld::Raycast(glm::vec3 origin
|
||||
|
||||
static std::unique_ptr<btConvexShape> MetaCoreMakeQueryShape(const MetaCorePhysicsQueryShape& shape) {
|
||||
if (shape.Type == MetaCorePhysicsQueryShapeType::Sphere && shape.Radius > 0.0F) return std::make_unique<btSphereShape>(shape.Radius);
|
||||
if (shape.Type == MetaCorePhysicsQueryShapeType::Capsule && shape.Radius > 0.0F && shape.Height >= shape.Radius * 2.0F) return std::make_unique<btCapsuleShape>(shape.Radius, shape.Height - shape.Radius * 2.0F);
|
||||
if (shape.Type == MetaCorePhysicsQueryShapeType::Capsule && shape.Radius > 0.0F && shape.Height >= shape.Radius * 2.0F) return std::make_unique<btCapsuleShapeZ>(shape.Radius, shape.Height - shape.Radius * 2.0F);
|
||||
if (shape.Type == MetaCorePhysicsQueryShapeType::Box && shape.HalfExtents.x > 0.0F && shape.HalfExtents.y > 0.0F && shape.HalfExtents.z > 0.0F) return std::make_unique<btBoxShape>(Bt(shape.HalfExtents));
|
||||
return {};
|
||||
}
|
||||
@ -480,8 +480,8 @@ bool MetaCorePhysicsWorld::AddImpulse(MetaCoreId id, glm::vec3 value) { const au
|
||||
bool MetaCorePhysicsWorld::SetLinearVelocity(MetaCoreId id, glm::vec3 value) { const auto it = Impl_->Bodies.find(id); if (it == Impl_->Bodies.end()) return false; it->second->Body->setLinearVelocity(Bt(value)); it->second->Body->activate(true); return true; }
|
||||
std::optional<glm::vec3> MetaCorePhysicsWorld::GetLinearVelocity(MetaCoreId id) const { const auto it = Impl_->Bodies.find(id); return it == Impl_->Bodies.end() ? std::nullopt : std::optional<glm::vec3>(Glm(it->second->Body->getLinearVelocity())); }
|
||||
bool MetaCorePhysicsWorld::WakeUp(MetaCoreId id) { const auto it = Impl_->Bodies.find(id); if (it == Impl_->Bodies.end()) return false; it->second->Body->activate(true); return true; }
|
||||
bool MetaCorePhysicsWorld::CharacterMove(MetaCoreId id, glm::vec3 displacement) { const auto it = Impl_->Bodies.find(id); if (it == Impl_->Bodies.end() || !Impl_->Scene.FindGameObject(id).HasComponent<MetaCoreCharacterControllerComponent>()) return false; auto velocity=it->second->Body->getLinearVelocity(); velocity.setX(displacement.x/static_cast<float>(Impl_->Settings.FixedTimeStep)); velocity.setZ(displacement.z/static_cast<float>(Impl_->Settings.FixedTimeStep)); it->second->Body->setLinearVelocity(velocity); it->second->Body->activate(true); return true; }
|
||||
bool MetaCorePhysicsWorld::CharacterJump(MetaCoreId id, float speed) { if (!IsCharacterGrounded(id)) return false; auto value=GetLinearVelocity(id); if(!value)return false; value->y=speed; return SetLinearVelocity(id,*value); }
|
||||
bool MetaCorePhysicsWorld::CharacterMove(MetaCoreId id, glm::vec3 displacement) { const auto it = Impl_->Bodies.find(id); if (it == Impl_->Bodies.end() || !Impl_->Scene.FindGameObject(id).HasComponent<MetaCoreCharacterControllerComponent>()) return false; auto velocity=it->second->Body->getLinearVelocity(); velocity.setX(displacement.x/static_cast<float>(Impl_->Settings.FixedTimeStep)); velocity.setY(displacement.y/static_cast<float>(Impl_->Settings.FixedTimeStep)); it->second->Body->setLinearVelocity(velocity); it->second->Body->activate(true); return true; }
|
||||
bool MetaCorePhysicsWorld::CharacterJump(MetaCoreId id, float speed) { if (!IsCharacterGrounded(id)) return false; auto value=GetLinearVelocity(id); if(!value)return false; value->z=speed; return SetLinearVelocity(id,*value); }
|
||||
bool MetaCorePhysicsWorld::SetConstraintEnabled(MetaCoreId id, bool enabled) {
|
||||
const auto iterator = std::find_if(Impl_->Constraints.begin(), Impl_->Constraints.end(), [id](const auto& value) { return value.Owner == id; });
|
||||
if (iterator == Impl_->Constraints.end() || !iterator->Value) return false;
|
||||
@ -500,7 +500,7 @@ bool MetaCorePhysicsWorld::SetConstraintMotor(MetaCoreId id, bool enabled, float
|
||||
bool MetaCorePhysicsWorld::IsCharacterGrounded(MetaCoreId id) const {
|
||||
const auto it = Impl_->Bodies.find(id); if (it == Impl_->Bodies.end() || !it->second->Character) return false;
|
||||
const btVector3 from = it->second->Body->getWorldTransform().getOrigin();
|
||||
const float distance = it->second->CharacterHalfHeight * it->second->Scale.y + it->second->CharacterSkinWidth + 0.08F;
|
||||
const float distance = it->second->CharacterHalfHeight * it->second->Scale.z + it->second->CharacterSkinWidth + 0.08F;
|
||||
struct Callback final : btCollisionWorld::ClosestRayResultCallback {
|
||||
const btCollisionObject* Ignored;
|
||||
Callback(const btVector3& start, const btVector3& end, const btCollisionObject* ignored)
|
||||
@ -508,9 +508,9 @@ bool MetaCorePhysicsWorld::IsCharacterGrounded(MetaCoreId id) const {
|
||||
bool needsCollision(btBroadphaseProxy* proxy) const override {
|
||||
return proxy && proxy->m_clientObject != Ignored && ClosestRayResultCallback::needsCollision(proxy);
|
||||
}
|
||||
} callback(from, from + btVector3(0.0F, -distance, 0.0F), it->second->Body.get());
|
||||
} callback(from, from + btVector3(0.0F, 0.0F, -distance), it->second->Body.get());
|
||||
Impl_->World->rayTest(callback.m_rayFromWorld, callback.m_rayToWorld, callback);
|
||||
return callback.hasHit() && callback.m_hitNormalWorld.y() >= 0.5F;
|
||||
return callback.hasHit() && callback.m_hitNormalWorld.z() >= 0.5F;
|
||||
}
|
||||
|
||||
std::vector<MetaCorePhysicsDebugLine> MetaCorePhysicsWorld::BuildDebugLines() const {
|
||||
|
||||
@ -20,6 +20,9 @@ MetaCorePhysicsSettingsDocument MetaCoreBuildDefaultPhysicsSettings() {
|
||||
bool MetaCoreValidatePhysicsSettings(MetaCorePhysicsSettingsDocument& document, std::vector<std::string>* issues) {
|
||||
bool valid = true;
|
||||
const auto report = [&](std::string value) { valid = false; if (issues) issues->push_back(std::move(value)); };
|
||||
if (!std::isfinite(document.Gravity.x) || !std::isfinite(document.Gravity.y) || !std::isfinite(document.Gravity.z)) {
|
||||
report("Gravity 必须为有限值"); document.Gravity = glm::vec3(0.0F, 0.0F, -9.81F);
|
||||
}
|
||||
if (!std::isfinite(document.FixedTimeStep) || document.FixedTimeStep < 0.001 || document.FixedTimeStep > 1.0) {
|
||||
report("FixedTimeStep 必须位于 [0.001, 1.0]"); document.FixedTimeStep = 1.0 / 60.0;
|
||||
}
|
||||
@ -55,6 +58,12 @@ std::optional<MetaCorePhysicsSettingsDocument> MetaCoreReadPhysicsSettings(const
|
||||
if (size && !input.read(reinterpret_cast<char*>(bytes.data()), static_cast<std::streamsize>(size))) return std::nullopt;
|
||||
MetaCorePhysicsSettingsDocument result;
|
||||
if (!MetaCoreDeserializeFromBytes(std::span<const std::byte>(bytes.data(), bytes.size()), result, registry)) return std::nullopt;
|
||||
// Version 1 used Bullet's Y-up convention. MetaCore is Z-up, so migrate the
|
||||
// complete gravity vector instead of only replacing the old default value.
|
||||
if (result.Version < 2U) {
|
||||
result.Gravity = glm::vec3(result.Gravity.x, result.Gravity.z, result.Gravity.y);
|
||||
result.Version = 2U;
|
||||
}
|
||||
(void)MetaCoreValidatePhysicsSettings(result); return result;
|
||||
}
|
||||
|
||||
|
||||
@ -46,9 +46,9 @@ MC_STRUCT()
|
||||
struct MetaCorePhysicsSettingsDocument {
|
||||
MC_GENERATED_BODY()
|
||||
MC_PROPERTY()
|
||||
std::uint32_t Version = 1U;
|
||||
std::uint32_t Version = 2U;
|
||||
MC_PROPERTY()
|
||||
glm::vec3 Gravity{0.0F, -9.81F, 0.0F};
|
||||
glm::vec3 Gravity{0.0F, 0.0F, -9.81F};
|
||||
MC_PROPERTY()
|
||||
double FixedTimeStep = 1.0 / 60.0;
|
||||
MC_PROPERTY()
|
||||
|
||||
@ -28,21 +28,22 @@ int main() {
|
||||
{
|
||||
auto settings = MetaCoreBuildDefaultPhysicsSettings();
|
||||
Expect(settings.LayerNames.size() == 32U && settings.CollisionMasks.size() == 32U, "default settings expose 32 layers");
|
||||
Expect(settings.Version == 2U && settings.Gravity == glm::vec3(0.0F, 0.0F, -9.81F), "default settings use MetaCore Z-up gravity");
|
||||
settings.FixedTimeStep = -1.0; std::vector<std::string> issues;
|
||||
Expect(!MetaCoreValidatePhysicsSettings(settings, &issues) && !issues.empty(), "invalid settings are repaired and diagnosed");
|
||||
}
|
||||
{
|
||||
MetaCoreScene scene;
|
||||
auto ground = AddBox(scene, "Ground", {0.0F, -1.0F, 0.0F}, MetaCoreRigidBodyType::Static);
|
||||
ground.GetComponent<MetaCoreColliderComponent>().Size = {20.0F, 1.0F, 20.0F};
|
||||
const auto falling = AddBox(scene, "Falling", {0.0F, 4.0F, 0.0F}, MetaCoreRigidBodyType::Dynamic);
|
||||
auto ground = AddBox(scene, "Ground", {0.0F, 0.0F, -1.0F}, MetaCoreRigidBodyType::Static);
|
||||
ground.GetComponent<MetaCoreColliderComponent>().Size = {20.0F, 20.0F, 1.0F};
|
||||
const auto falling = AddBox(scene, "Falling", {0.0F, 0.0F, 4.0F}, MetaCoreRigidBodyType::Dynamic);
|
||||
MetaCorePhysicsWorld world(scene, MetaCoreBuildDefaultPhysicsSettings());
|
||||
auto hit = world.Raycast({0.0F, 10.0F, 0.0F}, {0.0F, -1.0F, 0.0F}, 30.0F);
|
||||
auto hit = world.Raycast({0.0F, 0.0F, 10.0F}, {0.0F, 0.0F, -1.0F}, 30.0F);
|
||||
Expect(hit.has_value() && hit->ColliderObjectId == falling.GetId(), "raycast returns closest collider");
|
||||
const auto invalid = world.Raycast(glm::vec3(0.0F), glm::vec3(0.0F), 10.0F); Expect(!invalid.has_value(), "zero direction is rejected");
|
||||
for (int i = 0; i < 180; ++i) { scene.IncrementRevision(); world.Step(1.0 / 60.0); }
|
||||
const float y = scene.FindGameObject(falling.GetId()).GetComponent<MetaCoreTransformComponent>().Position.y;
|
||||
Expect(y > -0.1F && y < 1.0F, "dynamic body falls and rests on static body");
|
||||
const float z = scene.FindGameObject(falling.GetId()).GetComponent<MetaCoreTransformComponent>().Position.z;
|
||||
Expect(z > -0.1F && z < 1.0F, "dynamic body falls along -Z and rests on static body");
|
||||
Expect(world.GetStatistics().BodyCount == 2U, "world reports body statistics");
|
||||
}
|
||||
{
|
||||
@ -71,9 +72,9 @@ int main() {
|
||||
}
|
||||
{
|
||||
MetaCoreScene scene;
|
||||
auto ground = AddBox(scene, "CharacterGround", {0.0F, -0.5F, 0.0F}, MetaCoreRigidBodyType::Static);
|
||||
ground.GetComponent<MetaCoreColliderComponent>().Size = {20.0F, 1.0F, 20.0F};
|
||||
auto character=scene.CreateGameObject("Character"); character.GetComponent<MetaCoreTransformComponent>().Position={0.0F,2.0F,0.0F}; character.AddComponent<MetaCoreCharacterControllerComponent>();
|
||||
auto ground = AddBox(scene, "CharacterGround", {0.0F, 0.0F, -0.5F}, MetaCoreRigidBodyType::Static);
|
||||
ground.GetComponent<MetaCoreColliderComponent>().Size = {20.0F, 20.0F, 1.0F};
|
||||
auto character=scene.CreateGameObject("Character"); character.GetComponent<MetaCoreTransformComponent>().Position={0.0F,0.0F,2.0F}; character.AddComponent<MetaCoreCharacterControllerComponent>();
|
||||
MetaCorePhysicsWorld world(scene,MetaCoreBuildDefaultPhysicsSettings()); Expect(world.GetStatistics().BodyCount==2U,"character controller creates an implicit capsule body");
|
||||
Expect(world.CharacterMove(character.GetId(),{0.1F,0.0F,0.0F}),"character accepts movement commands");
|
||||
for (int index = 0; index < 120; ++index) world.Step(1.0 / 60.0);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user