Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/Shape.cpp
9913 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Collision/Shape/Shape.h>7#include <Jolt/Physics/Collision/Shape/ScaledShape.h>8#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>9#include <Jolt/Physics/Collision/TransformedShape.h>10#include <Jolt/Physics/Collision/PhysicsMaterial.h>11#include <Jolt/Physics/Collision/RayCast.h>12#include <Jolt/Physics/Collision/CastResult.h>13#include <Jolt/Physics/Collision/CollidePointResult.h>14#include <Jolt/Core/StreamIn.h>15#include <Jolt/Core/StreamOut.h>16#include <Jolt/Core/Factory.h>17#include <Jolt/ObjectStream/TypeDeclarations.h>1819JPH_NAMESPACE_BEGIN2021JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE(ShapeSettings)22{23JPH_ADD_BASE_CLASS(ShapeSettings, SerializableObject)2425JPH_ADD_ATTRIBUTE(ShapeSettings, mUserData)26}2728#ifdef JPH_DEBUG_RENDERER29bool Shape::sDrawSubmergedVolumes = false;30#endif // JPH_DEBUG_RENDERER3132ShapeFunctions ShapeFunctions::sRegistry[NumSubShapeTypes];3334const Shape *Shape::GetLeafShape([[maybe_unused]] const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const35{36outRemainder = inSubShapeID;37return this;38}3940TransformedShape Shape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const41{42// We have reached the leaf shape so there is no remainder43outRemainder = SubShapeID();4445// Just return the transformed shape for this shape46TransformedShape ts(RVec3(inPositionCOM), inRotation, this, BodyID());47ts.SetShapeScale(inScale);48return ts;49}5051void Shape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const52{53// Test shape filter54if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))55return;5657TransformedShape ts(RVec3(inPositionCOM), inRotation, this, TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator);58ts.SetShapeScale(inScale);59ioCollector.AddHit(ts);60}6162void Shape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const63{64Vec3 scale;65Mat44 transform = inCenterOfMassTransform.Decompose(scale);66TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator());67ts.SetShapeScale(MakeScaleValid(scale));68ioCollector.AddHit(ts);69}7071void Shape::SaveBinaryState(StreamOut &inStream) const72{73inStream.Write(mShapeSubType);74inStream.Write(mUserData);75}7677void Shape::RestoreBinaryState(StreamIn &inStream)78{79// Type hash read by sRestoreFromBinaryState80inStream.Read(mUserData);81}8283Shape::ShapeResult Shape::sRestoreFromBinaryState(StreamIn &inStream)84{85ShapeResult result;8687// Read the type of the shape88EShapeSubType shape_sub_type;89inStream.Read(shape_sub_type);90if (inStream.IsEOF() || inStream.IsFailed())91{92result.SetError("Failed to read type id");93return result;94}9596// Construct and read the data of the shape97Ref<Shape> shape = ShapeFunctions::sGet(shape_sub_type).mConstruct();98shape->RestoreBinaryState(inStream);99if (inStream.IsEOF() || inStream.IsFailed())100{101result.SetError("Failed to restore shape");102return result;103}104105result.Set(shape);106return result;107}108109void Shape::SaveWithChildren(StreamOut &inStream, ShapeToIDMap &ioShapeMap, MaterialToIDMap &ioMaterialMap) const110{111ShapeToIDMap::const_iterator shape_id_iter = ioShapeMap.find(this);112if (shape_id_iter == ioShapeMap.end())113{114// Write shape ID of this shape115uint32 shape_id = ioShapeMap.size();116ioShapeMap[this] = shape_id;117inStream.Write(shape_id);118119// Write the shape itself120SaveBinaryState(inStream);121122// Write the ID's of all sub shapes123ShapeList sub_shapes;124SaveSubShapeState(sub_shapes);125inStream.Write(uint32(sub_shapes.size()));126for (const Shape *shape : sub_shapes)127{128if (shape == nullptr)129inStream.Write(~uint32(0));130else131shape->SaveWithChildren(inStream, ioShapeMap, ioMaterialMap);132}133134// Write the materials135PhysicsMaterialList materials;136SaveMaterialState(materials);137StreamUtils::SaveObjectArray(inStream, materials, &ioMaterialMap);138}139else140{141// Known shape, just write the ID142inStream.Write(shape_id_iter->second);143}144}145146Shape::ShapeResult Shape::sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap)147{148ShapeResult result;149150// Read ID of this shape151uint32 shape_id;152inStream.Read(shape_id);153if (inStream.IsEOF() || inStream.IsFailed())154{155result.SetError("Failed to read shape id");156return result;157}158159// Check nullptr shape160if (shape_id == ~uint32(0))161{162result.Set(nullptr);163return result;164}165166// Check if we already read this shape167if (shape_id < ioShapeMap.size())168{169result.Set(ioShapeMap[shape_id]);170return result;171}172173// Read the shape174result = sRestoreFromBinaryState(inStream);175if (result.HasError())176return result;177JPH_ASSERT(ioShapeMap.size() == shape_id); // Assert that this is the next ID in the map178ioShapeMap.push_back(result.Get());179180// Read the sub shapes181uint32 len;182inStream.Read(len);183if (inStream.IsEOF() || inStream.IsFailed())184{185result.SetError("Failed to read stream");186return result;187}188ShapeList sub_shapes;189sub_shapes.reserve(len);190for (size_t i = 0; i < len; ++i)191{192ShapeResult sub_shape_result = sRestoreWithChildren(inStream, ioShapeMap, ioMaterialMap);193if (sub_shape_result.HasError())194return sub_shape_result;195sub_shapes.push_back(sub_shape_result.Get());196}197result.Get()->RestoreSubShapeState(sub_shapes.data(), (uint)sub_shapes.size());198199// Read the materials200Result mlresult = StreamUtils::RestoreObjectArray<PhysicsMaterialList>(inStream, ioMaterialMap);201if (mlresult.HasError())202{203result.SetError(mlresult.GetError());204return result;205}206const PhysicsMaterialList &materials = mlresult.Get();207result.Get()->RestoreMaterialState(materials.data(), (uint)materials.size());208209return result;210}211212Shape::Stats Shape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const213{214Stats stats = GetStats();215216// If shape is already visited, don't count its size again217if (!ioVisitedShapes.insert(this).second)218stats.mSizeBytes = 0;219220return stats;221}222223bool Shape::IsValidScale(Vec3Arg inScale) const224{225return !ScaleHelpers::IsZeroScale(inScale);226}227228Vec3 Shape::MakeScaleValid(Vec3Arg inScale) const229{230return ScaleHelpers::MakeNonZeroScale(inScale);231}232233Shape::ShapeResult Shape::ScaleShape(Vec3Arg inScale) const234{235const Vec3 unit_scale = Vec3::sOne();236237if (inScale.IsNearZero())238{239ShapeResult result;240result.SetError("Can't use zero scale!");241return result;242}243244// First test if we can just wrap this shape in a scaled shape245if (IsValidScale(inScale))246{247// Test if the scale is near unit248ShapeResult result;249if (inScale.IsClose(unit_scale))250result.Set(const_cast<Shape *>(this));251else252result.Set(new ScaledShape(this, inScale));253return result;254}255256// Collect the leaf shapes and their transforms257struct Collector : TransformedShapeCollector258{259virtual void AddHit(const ResultType &inResult) override260{261mShapes.push_back(inResult);262}263264Array<TransformedShape> mShapes;265};266Collector collector;267TransformShape(Mat44::sScale(inScale) * Mat44::sTranslation(GetCenterOfMass()), collector);268269// Construct a compound shape270StaticCompoundShapeSettings compound;271compound.mSubShapes.reserve(collector.mShapes.size());272for (const TransformedShape &ts : collector.mShapes)273{274const Shape *shape = ts.mShape;275276// Construct a scaled shape if scale is not unit277Vec3 scale = ts.GetShapeScale();278if (!scale.IsClose(unit_scale))279shape = new ScaledShape(shape, scale);280281// Add the shape282compound.AddShape(Vec3(ts.mShapePositionCOM) - ts.mShapeRotation * shape->GetCenterOfMass(), ts.mShapeRotation, shape);283}284285return compound.Create();286}287288void Shape::sCollidePointUsingRayCast(const Shape &inShape, Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter)289{290// First test if we're inside our bounding box291AABox bounds = inShape.GetLocalBounds();292if (bounds.Contains(inPoint))293{294// A collector that just counts the number of hits295class HitCountCollector : public CastRayCollector296{297public:298virtual void AddHit(const RayCastResult &inResult) override299{300// Store the last sub shape ID so that we can provide something to our outer hit collector301mSubShapeID = inResult.mSubShapeID2;302303++mHitCount;304}305306int mHitCount = 0;307SubShapeID mSubShapeID;308};309HitCountCollector collector;310311// Configure the raycast312RayCastSettings settings;313settings.SetBackFaceMode(EBackFaceMode::CollideWithBackFaces);314315// Cast a ray that's 10% longer than the height of our bounding box316inShape.CastRay(RayCast { inPoint, 1.1f * bounds.GetSize().GetY() * Vec3::sAxisY() }, settings, inSubShapeIDCreator, collector, inShapeFilter);317318// Odd amount of hits means inside319if ((collector.mHitCount & 1) == 1)320ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), collector.mSubShapeID });321}322}323324JPH_NAMESPACE_END325326327