Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability to store per triangle user data in mesh shape #1225

Merged
merged 10 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Jolt/AABBTree/AABBTreeToBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class AABBTreeToBuffer
static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize;

/// Convert AABB tree. Returns false if failed.
bool Convert(const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, const char *&outError)
bool Convert(const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, bool inStoreUserData, const char *&outError)
{
const typename NodeCodec::EncodingContext node_ctx;
typename TriangleCodec::EncodingContext tri_ctx(inVertices);
Expand All @@ -46,7 +46,7 @@ class AABBTreeToBuffer
uint tri_count = inRoot->GetTriangleCountInTree();
uint node_count = inRoot->GetNodeCount();
uint nodes_size = node_ctx.GetPessimisticMemoryEstimate(node_count);
uint total_size = HeaderSize + TriangleHeaderSize + nodes_size + tri_ctx.GetPessimisticMemoryEstimate(tri_count);
uint total_size = HeaderSize + TriangleHeaderSize + nodes_size + tri_ctx.GetPessimisticMemoryEstimate(tri_count, inStoreUserData);
mTree.reserve(total_size);

// Reset counters
Expand Down Expand Up @@ -157,7 +157,7 @@ class AABBTreeToBuffer
else
{
// Add triangles
node_data->mTriangleStart = tri_ctx.Pack(node_data->mNode->mTriangles, mTree, outError);
node_data->mTriangleStart = tri_ctx.Pack(node_data->mNode->mTriangles, inStoreUserData, mTree, outError);
if (node_data->mTriangleStart == uint(-1))
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ JPH_NAMESPACE_BEGIN
/// TriangleBlockHeader,
/// TriangleBlock (4 triangles and their flags in 16 bytes),
/// TriangleBlock...
/// [Optional] UserData (4 bytes per triangle)
///
/// Vertices are stored:
///
Expand Down Expand Up @@ -77,13 +78,22 @@ class TriangleCodecIndexed8BitPackSOA4Flags

static_assert(sizeof(TriangleBlock) == 16, "Compiler added padding");

enum ETriangleBlockHeaderFlags : uint32
{
OFFSET_TO_VERTICES_BITS = 29, ///< Offset from current block to start of vertices in bytes
OFFSET_TO_VERTICES_MASK = (1 << OFFSET_TO_VERTICES_BITS) - 1,
OFFSET_TO_USERDATA_BITS = 3, ///< When user data is stored, this is the number of blocks to skip to get to the user data (0 = no user data)
OFFSET_TO_USERDATA_MASK = (1 << OFFSET_TO_USERDATA_BITS) - 1,
};

/// A triangle header, will be followed by one or more TriangleBlocks
struct TriangleBlockHeader
{
const VertexData * GetVertexData() const { return reinterpret_cast<const VertexData *>(reinterpret_cast<const uint8 *>(this) + mOffsetToVertices); }
const VertexData * GetVertexData() const { return reinterpret_cast<const VertexData *>(reinterpret_cast<const uint8 *>(this) + (mFlags & OFFSET_TO_VERTICES_MASK)); }
const TriangleBlock * GetTriangleBlock() const { return reinterpret_cast<const TriangleBlock *>(reinterpret_cast<const uint8 *>(this) + sizeof(TriangleBlockHeader)); }
const uint32 * GetUserData() const { uint32 offset = mFlags >> OFFSET_TO_VERTICES_BITS; return offset == 0? nullptr : reinterpret_cast<const uint32 *>(GetTriangleBlock() + offset); }

uint32 mOffsetToVertices; ///< Offset from current block to start of vertices in bytes
uint32 mFlags;
};

static_assert(sizeof(TriangleBlockHeader) == 4, "Compiler added padding");
Expand Down Expand Up @@ -131,15 +141,15 @@ class TriangleCodecIndexed8BitPackSOA4Flags
}

/// Get an upper bound on the amount of bytes needed to store inTriangleCount triangles
uint GetPessimisticMemoryEstimate(uint inTriangleCount) const
uint GetPessimisticMemoryEstimate(uint inTriangleCount, bool inStoreUserData) const
{
// Worst case each triangle is alone in a block, none of the vertices are shared and we need to add 3 bytes to align the vertices
return inTriangleCount * (sizeof(TriangleBlockHeader) + sizeof(TriangleBlock) + 3 * sizeof(VertexData)) + 3;
return inTriangleCount * (sizeof(TriangleBlockHeader) + sizeof(TriangleBlock) + (inStoreUserData? sizeof(uint32) : 0) + 3 * sizeof(VertexData)) + 3;
}

/// Pack the triangles in inContainer to ioBuffer. This stores the mMaterialIndex of a triangle in the 8 bit flags.
/// Returns uint(-1) on error.
uint Pack(const IndexedTriangleList &inTriangles, ByteBuffer &ioBuffer, const char *&outError)
uint Pack(const IndexedTriangleList &inTriangles, bool inStoreUserData, ByteBuffer &ioBuffer, const char *&outError)
{
// Determine position of triangles start
uint offset = (uint)ioBuffer.size();
Expand All @@ -155,11 +165,20 @@ class TriangleCodecIndexed8BitPackSOA4Flags
uint start_vertex = Clamp((int)mVertices.size() - 256 + (int)tri_count * 3, 0, (int)mVertices.size());

// Store the start vertex offset, this will later be patched to give the delta offset relative to the triangle block
mOffsetsToPatch.push_back(uint((uint8 *)&header->mOffsetToVertices - &ioBuffer[0]));
header->mOffsetToVertices = start_vertex * sizeof(VertexData);
mOffsetsToPatch.push_back(uint((uint8 *)&header->mFlags - &ioBuffer[0]));
header->mFlags = start_vertex * sizeof(VertexData);
JPH_ASSERT(header->mFlags <= OFFSET_TO_VERTICES_MASK, "Offset to vertices doesn't fit");

// Pack vertices
// When we store user data we need to store the offset to the user data in TriangleBlocks
uint padded_triangle_count = AlignUp(tri_count, 4);
if (inStoreUserData)
{
uint32 num_blocks = padded_triangle_count >> 2;
JPH_ASSERT(num_blocks <= OFFSET_TO_USERDATA_MASK);
header->mFlags |= num_blocks << OFFSET_TO_VERTICES_BITS;
}

// Pack vertices
for (uint t = 0; t < padded_triangle_count; t += 4)
{
TriangleBlock *block = ioBuffer.Allocate<TriangleBlock>();
Expand Down Expand Up @@ -199,6 +218,14 @@ class TriangleCodecIndexed8BitPackSOA4Flags
}
}

// Store user data
if (inStoreUserData)
{
uint32 *user_data = ioBuffer.Allocate<uint32>(tri_count);
for (uint t = 0; t < tri_count; ++t)
user_data[t] = inTriangles[t].mUserData;
}

return offset;
}

Expand All @@ -214,7 +241,13 @@ class TriangleCodecIndexed8BitPackSOA4Flags

// Patch the offsets
for (uint o : mOffsetsToPatch)
*ioBuffer.Get<uint32>(o) += vertices_idx - o;
{
uint32 *flags = ioBuffer.Get<uint32>(o);
uint32 delta = vertices_idx - o;
if ((*flags & OFFSET_TO_VERTICES_MASK) + delta > OFFSET_TO_VERTICES_MASK)
JPH_ASSERT(false, "Offset to vertices doesn't fit");
*flags += delta;
}

// Calculate bounding box
AABox bounds;
Expand Down Expand Up @@ -409,6 +442,14 @@ class TriangleCodecIndexed8BitPackSOA4Flags
outV3 = trans.GetAxisZ();
}

/// Get user data for a triangle
JPH_INLINE uint32 GetUserData(const void *inTriangleStart, uint32 inTriangleIdx) const
{
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
const uint32 *user_data = header->GetUserData();
return user_data != nullptr? user_data[inTriangleIdx] : 0;
}

/// Get flags for entire triangle block
JPH_INLINE static void sGetFlags(const void *inTriangleStart, uint32 inNumTriangles, uint8 *outTriangleFlags)
{
Expand Down
15 changes: 8 additions & 7 deletions Jolt/Geometry/IndexedTriangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ class IndexedTriangle : public IndexedTriangleNoMaterial
using IndexedTriangleNoMaterial::IndexedTriangleNoMaterial;

/// Constructor
constexpr IndexedTriangle(uint32 inI1, uint32 inI2, uint32 inI3, uint32 inMaterialIndex) : IndexedTriangleNoMaterial(inI1, inI2, inI3), mMaterialIndex(inMaterialIndex) { }
constexpr IndexedTriangle(uint32 inI1, uint32 inI2, uint32 inI3, uint32 inMaterialIndex, uint inUserData) : IndexedTriangleNoMaterial(inI1, inI2, inI3), mMaterialIndex(inMaterialIndex), mUserData(inUserData) { }

/// Check if two triangles are identical
bool operator == (const IndexedTriangle &inRHS) const
{
return mMaterialIndex == inRHS.mMaterialIndex && IndexedTriangleNoMaterial::operator==(inRHS);
return mMaterialIndex == inRHS.mMaterialIndex && mUserData == inRHS.mUserData && IndexedTriangleNoMaterial::operator==(inRHS);
}

/// Rotate the vertices so that the lowest vertex becomes the first. This does not change the represented triangle.
Expand All @@ -89,20 +89,21 @@ class IndexedTriangle : public IndexedTriangleNoMaterial
if (mIdx[0] < mIdx[1])
{
if (mIdx[0] < mIdx[2])
return IndexedTriangle(mIdx[0], mIdx[1], mIdx[2], mMaterialIndex); // 0 is smallest
return IndexedTriangle(mIdx[0], mIdx[1], mIdx[2], mMaterialIndex, mUserData); // 0 is smallest
else
return IndexedTriangle(mIdx[2], mIdx[0], mIdx[1], mMaterialIndex); // 2 is smallest
return IndexedTriangle(mIdx[2], mIdx[0], mIdx[1], mMaterialIndex, mUserData); // 2 is smallest
}
else
{
if (mIdx[1] < mIdx[2])
return IndexedTriangle(mIdx[1], mIdx[2], mIdx[0], mMaterialIndex); // 1 is smallest
return IndexedTriangle(mIdx[1], mIdx[2], mIdx[0], mMaterialIndex, mUserData); // 1 is smallest
else
return IndexedTriangle(mIdx[2], mIdx[0], mIdx[1], mMaterialIndex); // 2 is smallest
return IndexedTriangle(mIdx[2], mIdx[0], mIdx[1], mMaterialIndex, mUserData); // 2 is smallest
}
}

uint32 mMaterialIndex = 0;
uint32 mUserData = 0; ///< User data that can be used for anything by the application, e.g. for tracking the original index of the triangle
};

using IndexedTriangleNoMaterialList = Array<IndexedTriangleNoMaterial>;
Expand All @@ -112,4 +113,4 @@ JPH_NAMESPACE_END

// Create a std::hash for IndexedTriangleNoMaterial and IndexedTriangle
JPH_MAKE_HASHABLE(JPH::IndexedTriangleNoMaterial, t.mIdx[0], t.mIdx[1], t.mIdx[2])
JPH_MAKE_HASHABLE(JPH::IndexedTriangle, t.mIdx[0], t.mIdx[1], t.mIdx[2], t.mMaterialIndex)
JPH_MAKE_HASHABLE(JPH::IndexedTriangle, t.mIdx[0], t.mIdx[1], t.mIdx[2], t.mMaterialIndex, t.mUserData)
8 changes: 6 additions & 2 deletions Jolt/Geometry/Indexify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ void Indexify(const TriangleList &inTriangles, VertexList &outVertices, IndexedT
{
IndexedTriangle it;
it.mMaterialIndex = inTriangles[t].mMaterialIndex;
it.mUserData = inTriangles[t].mUserData;
for (int v = 0; v < 3; ++v)
it.mIdx[v] = welded_vertices[t * 3 + v];
if (!it.IsDegenerate(outVertices))
Expand All @@ -209,9 +210,12 @@ void Deindexify(const VertexList &inVertices, const IndexedTriangleList &inTrian
outTriangles.resize(inTriangles.size());
for (size_t t = 0; t < inTriangles.size(); ++t)
{
outTriangles[t].mMaterialIndex = inTriangles[t].mMaterialIndex;
const IndexedTriangle &in = inTriangles[t];
Triangle &out = outTriangles[t];
out.mMaterialIndex = in.mMaterialIndex;
out.mUserData = in.mUserData;
for (int v = 0; v < 3; ++v)
outTriangles[t].mV[v] = inVertices[inTriangles[t].mIdx[v]];
out.mV[v] = inVertices[in.mIdx[v]];
}
}

Expand Down
6 changes: 3 additions & 3 deletions Jolt/Geometry/Triangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ class Triangle

/// Constructor
Triangle() = default;
Triangle(const Float3 &inV1, const Float3 &inV2, const Float3 &inV3) : mV { inV1, inV2, inV3 } { }
Triangle(const Float3 &inV1, const Float3 &inV2, const Float3 &inV3, uint32 inMaterialIndex) : Triangle(inV1, inV2, inV3) { mMaterialIndex = inMaterialIndex; }
Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) { inV1.StoreFloat3(&mV[0]); inV2.StoreFloat3(&mV[1]); inV3.StoreFloat3(&mV[2]); }
Triangle(const Float3 &inV1, const Float3 &inV2, const Float3 &inV3, uint32 inMaterialIndex = 0, uint32 inUserData = 0) : mV { inV1, inV2, inV3 }, mMaterialIndex(inMaterialIndex), mUserData(inUserData) { }
Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, uint32 inMaterialIndex = 0, uint32 inUserData = 0) : mMaterialIndex(inMaterialIndex), mUserData(inUserData) { inV1.StoreFloat3(&mV[0]); inV2.StoreFloat3(&mV[1]); inV3.StoreFloat3(&mV[2]); }

/// Get center of triangle
Vec3 GetCentroid() const
Expand All @@ -27,6 +26,7 @@ class Triangle
/// Vertices
Float3 mV[3];
uint32 mMaterialIndex = 0; ///< Follows mV[3] so that we can read mV as 4 vectors
uint32 mUserData = 0; ///< User data that can be used for anything by the application, e.g. for tracking the original index of the triangle
};

using TriangleList = Array<Triangle>;
Expand Down
2 changes: 2 additions & 0 deletions Jolt/ObjectStream/TypeDeclarations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(Triangle)
{
JPH_ADD_ATTRIBUTE(Triangle, mV)
JPH_ADD_ATTRIBUTE(Triangle, mMaterialIndex)
JPH_ADD_ATTRIBUTE(Triangle, mUserData)
}

JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(IndexedTriangle)
{
JPH_ADD_ATTRIBUTE(IndexedTriangle, mIdx)
JPH_ADD_ATTRIBUTE(IndexedTriangle, mMaterialIndex)
JPH_ADD_ATTRIBUTE(IndexedTriangle, mUserData)
}

JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(Plane)
Expand Down
16 changes: 16 additions & 0 deletions Jolt/Physics/Collision/Shape/CompoundShape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,22 @@ const PhysicsMaterial *CompoundShape::GetMaterial(const SubShapeID &inSubShapeID
return mSubShapes[index].mShape->GetMaterial(remainder);
}

const Shape *CompoundShape::GetLeafShape(const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const
{
// Decode sub shape index
SubShapeID remainder;
uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);
if (index >= mSubShapes.size())
{
// No longer valid index
outRemainder = SubShapeID();
return nullptr;
}

// Pass call on
return mSubShapes[index].mShape->GetLeafShape(remainder, outRemainder);
}

uint64 CompoundShape::GetSubShapeUserData(const SubShapeID &inSubShapeID) const
{
// Decode sub shape index
Expand Down
3 changes: 3 additions & 0 deletions Jolt/Physics/Collision/Shape/CompoundShape.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class JPH_EXPORT CompoundShape : public Shape
// See Shape::GetMaterial
virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override;

// See Shape::GetLeafShape
virtual const Shape * GetLeafShape(const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const override;

// See Shape::GetSubShapeUserData
virtual uint64 GetSubShapeUserData(const SubShapeID &inSubShapeID) const override;

Expand Down
3 changes: 3 additions & 0 deletions Jolt/Physics/Collision/Shape/DecoratedShape.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class JPH_EXPORT DecoratedShape : public Shape
// See Shape::GetSubShapeIDBitsRecursive
virtual uint GetSubShapeIDBitsRecursive() const override { return mInnerShape->GetSubShapeIDBitsRecursive(); }

// See Shape::GetLeafShape
virtual const Shape * GetLeafShape(const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const override { return mInnerShape->GetLeafShape(inSubShapeID, outRemainder); }

// See Shape::GetMaterial
virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override;

Expand Down
15 changes: 14 additions & 1 deletion Jolt/Physics/Collision/Shape/MeshShape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MeshShapeSettings)
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaterials)
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaxTrianglesPerLeaf)
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mActiveEdgeCosThresholdAngle)
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mPerTriangleUserData)
}

// Codecs this mesh shape is using
Expand Down Expand Up @@ -199,7 +200,7 @@ MeshShape::MeshShape(const MeshShapeSettings &inSettings, ShapeResult &outResult
// Convert to buffer
AABBTreeToBuffer<TriangleCodec, NodeCodec> buffer;
const char *error = nullptr;
if (!buffer.Convert(inSettings.mTriangleVertices, root, error))
if (!buffer.Convert(inSettings.mTriangleVertices, root, inSettings.mPerTriangleUserData, error))
{
outResult.SetError(error);
delete root;
Expand Down Expand Up @@ -1221,6 +1222,18 @@ Shape::Stats MeshShape::GetStats() const
return Stats(sizeof(*this) + mMaterials.size() * sizeof(Ref<PhysicsMaterial>) + mTree.size() * sizeof(uint8), visitor.mNumTriangles);
}

uint32 MeshShape::GetTriangleUserData(const SubShapeID &inSubShapeID) const
{
// Decode ID
const void *block_start;
uint32 triangle_idx;
DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);

// Decode triangle
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
return triangle_ctx.GetUserData(block_start, triangle_idx);
}

void MeshShape::sRegister()
{
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Mesh);
Expand Down
9 changes: 9 additions & 0 deletions Jolt/Physics/Collision/Shape/MeshShape.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ class JPH_EXPORT MeshShapeSettings final : public ShapeSettings
/// Setting this value too small can cause ghost collisions with edges, setting it too big can cause depenetration artifacts (objects not depenetrating quickly).
/// Valid ranges are between cos(0 degrees) and cos(90 degrees). The default value is cos(5 degrees).
float mActiveEdgeCosThresholdAngle = 0.996195f; // cos(5 degrees)

/// When true, we store the user data coming from Triangle::mUserData or IndexedTriangle::mUserData in the mesh shape.
/// This can be used to store additional data like the original index of the triangle in the mesh.
/// Can be retrieved using MeshShape::GetTriangleUserData.
/// Turning this on increases the memory used by the MeshShape by roughly 25%.
bool mPerTriangleUserData = false;
};

/// A mesh shape, consisting of triangles. Mesh shapes are mostly used for static geometry.
Expand Down Expand Up @@ -141,6 +147,9 @@ class JPH_EXPORT MeshShape final : public Shape
// See Shape::GetVolume
virtual float GetVolume() const override { return 0; }

// When MeshShape::mPerTriangleUserData is true, this function can be used to retrieve the user data that was stored in the mesh shape.
uint32 GetTriangleUserData(const SubShapeID &inSubShapeID) const;

#ifdef JPH_DEBUG_RENDERER
// Settings
static bool sDrawTriangleGroups;
Expand Down
6 changes: 6 additions & 0 deletions Jolt/Physics/Collision/Shape/Shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ bool Shape::sDrawSubmergedVolumes = false;

ShapeFunctions ShapeFunctions::sRegistry[NumSubShapeTypes];

const Shape *Shape::GetLeafShape([[maybe_unused]] const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const
{
outRemainder = inSubShapeID;
return this;
}

TransformedShape Shape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const
{
// We have reached the leaf shape so there is no remainder
Expand Down
8 changes: 7 additions & 1 deletion Jolt/Physics/Collision/Shape/Shape.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,12 @@ class JPH_EXPORT Shape : public RefTarget<Shape>, public NonCopyable
/// Calculate the mass and inertia of this shape
virtual MassProperties GetMassProperties() const = 0;

/// Get the leaf shape for a particular sub shape ID.
/// @param inSubShapeID The full sub shape ID that indicates the path to the leaf shape
/// @param outRemainder What remains of the sub shape ID after removing the path to the leaf shape (could e.g. refer to a triangle within a MeshShape)
/// @return The shape or null if the sub shape ID is invalid
virtual const Shape * GetLeafShape([[maybe_unused]] const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const;

/// Get the material assigned to a particular sub shape ID
virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const = 0;

Expand All @@ -256,7 +262,7 @@ class JPH_EXPORT Shape : public RefTarget<Shape>, public NonCopyable
/// @param outVertices Resulting face. The returned face can be empty if the shape doesn't have polygons to return (e.g. because it's a sphere). The face will be returned in world space.
virtual void GetSupportingFace([[maybe_unused]] const SubShapeID &inSubShapeID, [[maybe_unused]] Vec3Arg inDirection, [[maybe_unused]] Vec3Arg inScale, [[maybe_unused]] Mat44Arg inCenterOfMassTransform, [[maybe_unused]] SupportingFace &outVertices) const { /* Nothing */ }

/// Get the user data of a particular sub shape ID
/// Get the user data of a particular sub shape ID. Corresponds with the value stored in Shape::GetUserData of the leaf shape pointed to by inSubShapeID.
virtual uint64 GetSubShapeUserData([[maybe_unused]] const SubShapeID &inSubShapeID) const { return mUserData; }

/// Get the direct child sub shape and its transform for a sub shape ID.
Expand Down
2 changes: 2 additions & 0 deletions Samples/Samples.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ set(SAMPLES_SRC_FILES
${SAMPLES_ROOT}/Tests/Shapes/HeightFieldShapeTest.h
${SAMPLES_ROOT}/Tests/Shapes/MeshShapeTest.cpp
${SAMPLES_ROOT}/Tests/Shapes/MeshShapeTest.h
${SAMPLES_ROOT}/Tests/Shapes/MeshShapeUserDataTest.cpp
${SAMPLES_ROOT}/Tests/Shapes/MeshShapeUserDataTest.h
${SAMPLES_ROOT}/Tests/Shapes/SphereShapeTest.cpp
${SAMPLES_ROOT}/Tests/Shapes/SphereShapeTest.h
${SAMPLES_ROOT}/Tests/Shapes/PlaneShapeTest.cpp
Expand Down
Loading