262 lines
7.6 KiB
C++
262 lines
7.6 KiB
C++
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include <Jolt/Jolt.h>
|
|
|
|
#include <Jolt/Physics/PhysicsScene.h>
|
|
#include <Jolt/Physics/PhysicsSystem.h>
|
|
#include <Jolt/Physics/Body/BodyLockMulti.h>
|
|
#include <Jolt/ObjectStream/TypeDeclarations.h>
|
|
|
|
JPH_NAMESPACE_BEGIN
|
|
|
|
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(PhysicsScene)
|
|
{
|
|
JPH_ADD_ATTRIBUTE(PhysicsScene, mBodies)
|
|
JPH_ADD_ATTRIBUTE(PhysicsScene, mConstraints)
|
|
JPH_ADD_ATTRIBUTE(PhysicsScene, mSoftBodies)
|
|
}
|
|
|
|
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(PhysicsScene::ConnectedConstraint)
|
|
{
|
|
JPH_ADD_ATTRIBUTE(PhysicsScene::ConnectedConstraint, mSettings)
|
|
JPH_ADD_ATTRIBUTE(PhysicsScene::ConnectedConstraint, mBody1)
|
|
JPH_ADD_ATTRIBUTE(PhysicsScene::ConnectedConstraint, mBody2)
|
|
}
|
|
|
|
void PhysicsScene::AddBody(const BodyCreationSettings &inBody)
|
|
{
|
|
mBodies.push_back(inBody);
|
|
}
|
|
|
|
void PhysicsScene::AddConstraint(const TwoBodyConstraintSettings *inConstraint, uint32 inBody1, uint32 inBody2)
|
|
{
|
|
mConstraints.emplace_back(inConstraint, inBody1, inBody2);
|
|
}
|
|
|
|
void PhysicsScene::AddSoftBody(const SoftBodyCreationSettings &inSoftBody)
|
|
{
|
|
mSoftBodies.push_back(inSoftBody);
|
|
}
|
|
|
|
bool PhysicsScene::FixInvalidScales()
|
|
{
|
|
const Vec3 unit_scale = Vec3::sOne();
|
|
|
|
bool success = true;
|
|
for (BodyCreationSettings &b : mBodies)
|
|
{
|
|
// Test if there is an invalid scale in the shape hierarchy
|
|
const Shape *shape = b.GetShape();
|
|
if (!shape->IsValidScale(unit_scale))
|
|
{
|
|
// Fix it up
|
|
Shape::ShapeResult result = shape->ScaleShape(unit_scale);
|
|
if (result.IsValid())
|
|
b.SetShape(result.Get());
|
|
else
|
|
success = false;
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool PhysicsScene::CreateBodies(PhysicsSystem *inSystem) const
|
|
{
|
|
BodyInterface &bi = inSystem->GetBodyInterface();
|
|
|
|
BodyIDVector body_ids;
|
|
body_ids.reserve(mBodies.size() + mSoftBodies.size());
|
|
|
|
// Create bodies
|
|
for (const BodyCreationSettings &b : mBodies)
|
|
{
|
|
const Body *body = bi.CreateBody(b);
|
|
if (body == nullptr)
|
|
break;
|
|
body_ids.push_back(body->GetID());
|
|
}
|
|
|
|
// Create soft bodies
|
|
for (const SoftBodyCreationSettings &b : mSoftBodies)
|
|
{
|
|
const Body *body = bi.CreateSoftBody(b);
|
|
if (body == nullptr)
|
|
break;
|
|
body_ids.push_back(body->GetID());
|
|
}
|
|
|
|
// Batch add bodies
|
|
BodyIDVector temp_body_ids = body_ids; // Body ID's get shuffled by AddBodiesPrepare
|
|
BodyInterface::AddState add_state = bi.AddBodiesPrepare(temp_body_ids.data(), (int)temp_body_ids.size());
|
|
bi.AddBodiesFinalize(temp_body_ids.data(), (int)temp_body_ids.size(), add_state, EActivation::Activate);
|
|
|
|
// If not all bodies are created, creating constraints will be unreliable
|
|
if (body_ids.size() != mBodies.size() + mSoftBodies.size())
|
|
return false;
|
|
|
|
// Create constraints
|
|
for (const ConnectedConstraint &cc : mConstraints)
|
|
{
|
|
BodyID body1_id = cc.mBody1 == cFixedToWorld? BodyID() : body_ids[cc.mBody1];
|
|
BodyID body2_id = cc.mBody2 == cFixedToWorld? BodyID() : body_ids[cc.mBody2];
|
|
Constraint *c = bi.CreateConstraint(cc.mSettings, body1_id, body2_id);
|
|
inSystem->AddConstraint(c);
|
|
}
|
|
|
|
// Everything was created
|
|
return true;
|
|
}
|
|
|
|
void PhysicsScene::SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const
|
|
{
|
|
BodyCreationSettings::ShapeToIDMap shape_to_id;
|
|
BodyCreationSettings::MaterialToIDMap material_to_id;
|
|
BodyCreationSettings::GroupFilterToIDMap group_filter_to_id;
|
|
SoftBodyCreationSettings::SharedSettingsToIDMap settings_to_id;
|
|
|
|
// Save bodies
|
|
inStream.Write((uint32)mBodies.size());
|
|
for (const BodyCreationSettings &b : mBodies)
|
|
b.SaveWithChildren(inStream, inSaveShapes? &shape_to_id : nullptr, inSaveShapes? &material_to_id : nullptr, inSaveGroupFilter? &group_filter_to_id : nullptr);
|
|
|
|
// Save constraints
|
|
inStream.Write((uint32)mConstraints.size());
|
|
for (const ConnectedConstraint &cc : mConstraints)
|
|
{
|
|
cc.mSettings->SaveBinaryState(inStream);
|
|
inStream.Write(cc.mBody1);
|
|
inStream.Write(cc.mBody2);
|
|
}
|
|
|
|
// Save soft bodies
|
|
inStream.Write((uint32)mSoftBodies.size());
|
|
for (const SoftBodyCreationSettings &b : mSoftBodies)
|
|
b.SaveWithChildren(inStream, &settings_to_id, &material_to_id, inSaveGroupFilter? &group_filter_to_id : nullptr);
|
|
}
|
|
|
|
PhysicsScene::PhysicsSceneResult PhysicsScene::sRestoreFromBinaryState(StreamIn &inStream)
|
|
{
|
|
PhysicsSceneResult result;
|
|
|
|
// Create scene
|
|
Ref<PhysicsScene> scene = new PhysicsScene();
|
|
|
|
BodyCreationSettings::IDToShapeMap id_to_shape;
|
|
BodyCreationSettings::IDToMaterialMap id_to_material;
|
|
BodyCreationSettings::IDToGroupFilterMap id_to_group_filter;
|
|
SoftBodyCreationSettings::IDToSharedSettingsMap id_to_settings;
|
|
|
|
// Reserve some memory to avoid frequent reallocations
|
|
id_to_shape.reserve(1024);
|
|
id_to_material.reserve(128);
|
|
id_to_group_filter.reserve(128);
|
|
|
|
// Read bodies
|
|
uint32 len = 0;
|
|
inStream.Read(len);
|
|
scene->mBodies.resize(len);
|
|
for (BodyCreationSettings &b : scene->mBodies)
|
|
{
|
|
// Read creation settings
|
|
BodyCreationSettings::BCSResult bcs_result = BodyCreationSettings::sRestoreWithChildren(inStream, id_to_shape, id_to_material, id_to_group_filter);
|
|
if (bcs_result.HasError())
|
|
{
|
|
result.SetError(bcs_result.GetError());
|
|
return result;
|
|
}
|
|
b = bcs_result.Get();
|
|
}
|
|
|
|
// Read constraints
|
|
len = 0;
|
|
inStream.Read(len);
|
|
scene->mConstraints.resize(len);
|
|
for (ConnectedConstraint &cc : scene->mConstraints)
|
|
{
|
|
ConstraintSettings::ConstraintResult c_result = ConstraintSettings::sRestoreFromBinaryState(inStream);
|
|
if (c_result.HasError())
|
|
{
|
|
result.SetError(c_result.GetError());
|
|
return result;
|
|
}
|
|
cc.mSettings = StaticCast<TwoBodyConstraintSettings>(c_result.Get());
|
|
inStream.Read(cc.mBody1);
|
|
inStream.Read(cc.mBody2);
|
|
}
|
|
|
|
// Read soft bodies
|
|
len = 0;
|
|
inStream.Read(len);
|
|
scene->mSoftBodies.resize(len);
|
|
for (SoftBodyCreationSettings &b : scene->mSoftBodies)
|
|
{
|
|
// Read creation settings
|
|
SoftBodyCreationSettings::SBCSResult sbcs_result = SoftBodyCreationSettings::sRestoreWithChildren(inStream, id_to_settings, id_to_material, id_to_group_filter);
|
|
if (sbcs_result.HasError())
|
|
{
|
|
result.SetError(sbcs_result.GetError());
|
|
return result;
|
|
}
|
|
b = sbcs_result.Get();
|
|
}
|
|
|
|
result.Set(scene);
|
|
return result;
|
|
}
|
|
|
|
void PhysicsScene::FromPhysicsSystem(const PhysicsSystem *inSystem)
|
|
{
|
|
// This map will track where each body went in mBodies
|
|
using BodyIDToIdxMap = UnorderedMap<BodyID, uint32>;
|
|
BodyIDToIdxMap body_id_to_idx;
|
|
|
|
// Map invalid ID
|
|
body_id_to_idx[BodyID()] = cFixedToWorld;
|
|
|
|
// Get all bodies
|
|
BodyIDVector body_ids;
|
|
inSystem->GetBodies(body_ids);
|
|
|
|
// Loop over all bodies
|
|
const BodyLockInterface &bli = inSystem->GetBodyLockInterface();
|
|
for (const BodyID &id : body_ids)
|
|
{
|
|
BodyLockRead lock(bli, id);
|
|
if (lock.Succeeded())
|
|
{
|
|
// Store location of body
|
|
body_id_to_idx[id] = (uint32)mBodies.size();
|
|
|
|
const Body &body = lock.GetBody();
|
|
|
|
// Convert to body creation settings
|
|
if (body.IsRigidBody())
|
|
AddBody(body.GetBodyCreationSettings());
|
|
else
|
|
AddSoftBody(body.GetSoftBodyCreationSettings());
|
|
}
|
|
}
|
|
|
|
// Loop over all constraints
|
|
Constraints constraints = inSystem->GetConstraints();
|
|
for (const Constraint *c : constraints)
|
|
if (c->GetType() == EConstraintType::TwoBodyConstraint)
|
|
{
|
|
// Cast to two body constraint
|
|
const TwoBodyConstraint *tbc = static_cast<const TwoBodyConstraint *>(c);
|
|
|
|
// Find the body indices
|
|
BodyIDToIdxMap::const_iterator b1 = body_id_to_idx.find(tbc->GetBody1()->GetID());
|
|
BodyIDToIdxMap::const_iterator b2 = body_id_to_idx.find(tbc->GetBody2()->GetID());
|
|
JPH_ASSERT(b1 != body_id_to_idx.end() && b2 != body_id_to_idx.end());
|
|
|
|
// Create constraint settings and add the constraint
|
|
Ref<ConstraintSettings> settings = c->GetConstraintSettings();
|
|
AddConstraint(StaticCast<TwoBodyConstraintSettings>(settings), b1->second, b2->second);
|
|
}
|
|
}
|
|
|
|
JPH_NAMESPACE_END
|