feat: modules moved and engine moved to submodule
This commit is contained in:
parent
dfb5e645cd
commit
c33d2130cc
5136 changed files with 225275 additions and 64485 deletions
|
|
@ -92,7 +92,12 @@ MassProperties CompoundShape::GetMassProperties() const
|
|||
|
||||
AABox CompoundShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const
|
||||
{
|
||||
if (mSubShapes.size() <= 10)
|
||||
if (mSubShapes.empty())
|
||||
{
|
||||
// If there are no sub-shapes, we must return an empty box to avoid overflows in the broadphase
|
||||
return AABox(inCenterOfMassTransform.GetTranslation(), inCenterOfMassTransform.GetTranslation());
|
||||
}
|
||||
else if (mSubShapes.size() <= 10)
|
||||
{
|
||||
AABox bounds;
|
||||
for (const SubShape &shape : mSubShapes)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,11 @@ public:
|
|||
RefConst<Shape> mShapePtr; ///< Sub shape (either this or mShape needs to be filled up)
|
||||
Vec3 mPosition; ///< Position of the sub shape
|
||||
Quat mRotation; ///< Rotation of the sub shape
|
||||
uint32 mUserData = 0; ///< User data value (can be used by the application for any purpose)
|
||||
|
||||
/// User data value (can be used by the application for any purpose).
|
||||
/// Note this value can be retrieved through GetSubShape(...).mUserData, not through GetSubShapeUserData(...) as that returns Shape::GetUserData() of the leaf shape.
|
||||
/// Use GetSubShapeIndexFromID get a shape index from a SubShapeID to pass to GetSubShape.
|
||||
uint32 mUserData = 0;
|
||||
};
|
||||
|
||||
using SubShapes = Array<SubShapeSettings>;
|
||||
|
|
@ -338,7 +342,7 @@ protected:
|
|||
}
|
||||
|
||||
Vec3 mCenterOfMass { Vec3::sZero() }; ///< Center of mass of the compound
|
||||
AABox mLocalBounds;
|
||||
AABox mLocalBounds { Vec3::sZero(), Vec3::sZero() };
|
||||
SubShapes mSubShapes;
|
||||
float mInnerRadius = FLT_MAX; ///< Smallest radius of GetInnerRadius() of child shapes
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public:
|
|||
// See: ShapeSettings
|
||||
virtual ShapeResult Create() const override;
|
||||
|
||||
Array<Vec3> mPoints; ///< Points to create the hull from
|
||||
Array<Vec3> mPoints; ///< Points to create the hull from. Note that these points don't need to be the vertices of the convex hull, they can contain interior points or points on faces/edges.
|
||||
float mMaxConvexRadius = 0.0f; ///< Convex radius as supplied by the constructor. Note that during hull creation the convex radius can be made smaller if the value is too big for the hull.
|
||||
float mMaxErrorConvexRadius = 0.05f; ///< Maximum distance between the shrunk hull + convex radius and the actual hull.
|
||||
float mHullTolerance = 1.0e-3f; ///< Points are allowed this far outside of the hull (increasing this yields a hull with less vertices). Note that the actual used value can be larger if the points of the hull are far apart.
|
||||
|
|
|
|||
|
|
@ -57,8 +57,9 @@ void ConvexShape::sCollideConvexVsConvex(const Shape *inShape1, const Shape *inS
|
|||
Mat44 transform_2_to_1 = inverse_transform1 * inCenterOfMassTransform2;
|
||||
|
||||
// Get bounding boxes
|
||||
float max_separation_distance = inCollideShapeSettings.mMaxSeparationDistance;
|
||||
AABox shape1_bbox = shape1->GetLocalBounds().Scaled(inScale1);
|
||||
shape1_bbox.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance));
|
||||
shape1_bbox.ExpandBy(Vec3::sReplicate(max_separation_distance));
|
||||
AABox shape2_bbox = shape2->GetLocalBounds().Scaled(inScale2);
|
||||
|
||||
// Check if they overlap
|
||||
|
|
@ -86,10 +87,10 @@ void ConvexShape::sCollideConvexVsConvex(const Shape *inShape1, const Shape *inS
|
|||
const Support *shape2_excl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, buffer2_excl_cvx_radius, inScale2);
|
||||
|
||||
// Transform shape 2 in the space of shape 1
|
||||
TransformedConvexObject<Support> transformed2_excl_cvx_radius(transform_2_to_1, *shape2_excl_cvx_radius);
|
||||
TransformedConvexObject transformed2_excl_cvx_radius(transform_2_to_1, *shape2_excl_cvx_radius);
|
||||
|
||||
// Perform GJK step
|
||||
status = pen_depth.GetPenetrationDepthStepGJK(*shape1_excl_cvx_radius, shape1_excl_cvx_radius->GetConvexRadius() + inCollideShapeSettings.mMaxSeparationDistance, transformed2_excl_cvx_radius, shape2_excl_cvx_radius->GetConvexRadius(), inCollideShapeSettings.mCollisionTolerance, penetration_axis, point1, point2);
|
||||
status = pen_depth.GetPenetrationDepthStepGJK(*shape1_excl_cvx_radius, shape1_excl_cvx_radius->GetConvexRadius() + max_separation_distance, transformed2_excl_cvx_radius, shape2_excl_cvx_radius->GetConvexRadius(), inCollideShapeSettings.mCollisionTolerance, penetration_axis, point1, point2);
|
||||
}
|
||||
|
||||
// Check result of collision detection
|
||||
|
|
@ -105,16 +106,22 @@ void ConvexShape::sCollideConvexVsConvex(const Shape *inShape1, const Shape *inS
|
|||
{
|
||||
// Need to run expensive EPA algorithm
|
||||
|
||||
// We know we're overlapping at this point, so we can set the max separation distance to 0.
|
||||
// Numerically it is possible that GJK finds that the shapes are overlapping but EPA finds that they're separated.
|
||||
// In order to avoid this, we clamp the max separation distance to 1 so that we don't excessively inflate the shape,
|
||||
// but we still inflate it enough to avoid the case where EPA misses the collision.
|
||||
max_separation_distance = min(max_separation_distance, 1.0f);
|
||||
|
||||
// Create support function
|
||||
SupportBuffer buffer1_incl_cvx_radius, buffer2_incl_cvx_radius;
|
||||
const Support *shape1_incl_cvx_radius = shape1->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer1_incl_cvx_radius, inScale1);
|
||||
const Support *shape2_incl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer2_incl_cvx_radius, inScale2);
|
||||
|
||||
// Add separation distance
|
||||
AddConvexRadius<Support> shape1_add_max_separation_distance(*shape1_incl_cvx_radius, inCollideShapeSettings.mMaxSeparationDistance);
|
||||
AddConvexRadius shape1_add_max_separation_distance(*shape1_incl_cvx_radius, max_separation_distance);
|
||||
|
||||
// Transform shape 2 in the space of shape 1
|
||||
TransformedConvexObject<Support> transformed2_incl_cvx_radius(transform_2_to_1, *shape2_incl_cvx_radius);
|
||||
TransformedConvexObject transformed2_incl_cvx_radius(transform_2_to_1, *shape2_incl_cvx_radius);
|
||||
|
||||
// Perform EPA step
|
||||
if (!pen_depth.GetPenetrationDepthStepEPA(shape1_add_max_separation_distance, transformed2_incl_cvx_radius, inCollideShapeSettings.mPenetrationTolerance, penetration_axis, point1, point2))
|
||||
|
|
@ -124,14 +131,14 @@ void ConvexShape::sCollideConvexVsConvex(const Shape *inShape1, const Shape *inS
|
|||
}
|
||||
|
||||
// Check if the penetration is bigger than the early out fraction
|
||||
float penetration_depth = (point2 - point1).Length() - inCollideShapeSettings.mMaxSeparationDistance;
|
||||
float penetration_depth = (point2 - point1).Length() - max_separation_distance;
|
||||
if (-penetration_depth >= ioCollector.GetEarlyOutFraction())
|
||||
return;
|
||||
|
||||
// Correct point1 for the added separation distance
|
||||
float penetration_axis_len = penetration_axis.Length();
|
||||
if (penetration_axis_len > 0.0f)
|
||||
point1 -= penetration_axis * (inCollideShapeSettings.mMaxSeparationDistance / penetration_axis_len);
|
||||
point1 -= penetration_axis * (max_separation_distance / penetration_axis_len);
|
||||
|
||||
// Convert to world space
|
||||
point1 = inCenterOfMassTransform1 * point1;
|
||||
|
|
@ -164,7 +171,7 @@ bool ConvexShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubSh
|
|||
|
||||
// Create support function
|
||||
SupportBuffer buffer;
|
||||
const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f));
|
||||
const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sOne());
|
||||
|
||||
// Cast ray
|
||||
GJKClosestPoint gjk;
|
||||
|
|
@ -234,7 +241,7 @@ void ConvexShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubSh
|
|||
{
|
||||
// Create support function
|
||||
SupportBuffer buffer;
|
||||
const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f));
|
||||
const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sOne());
|
||||
|
||||
// Create support function for point
|
||||
PointConvexSupport point { inPoint };
|
||||
|
|
@ -312,7 +319,7 @@ public:
|
|||
mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)),
|
||||
mIsInsideOut(ScaleHelpers::IsInsideOut(inScale))
|
||||
{
|
||||
mSupport = inShape->GetSupportFunction(ESupportMode::IncludeConvexRadius, mSupportBuffer, Vec3::sReplicate(1.0f));
|
||||
mSupport = inShape->GetSupportFunction(ESupportMode::IncludeConvexRadius, mSupportBuffer, Vec3::sOne());
|
||||
}
|
||||
|
||||
SupportBuffer mSupportBuffer;
|
||||
|
|
@ -446,7 +453,7 @@ void ConvexShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg in
|
|||
// Get the support function with convex radius
|
||||
SupportBuffer buffer;
|
||||
const Support *support = GetSupportFunction(ESupportMode::ExcludeConvexRadius, buffer, inScale);
|
||||
AddConvexRadius<Support> add_convex(*support, support->GetConvexRadius());
|
||||
AddConvexRadius add_convex(*support, support->GetConvexRadius());
|
||||
|
||||
// Draw the shape
|
||||
DebugRenderer::GeometryRef geometry = inRenderer->CreateTriangleGeometryForConvex([&add_convex](Vec3Arg inDirection) { return add_convex.GetSupport(inDirection); });
|
||||
|
|
@ -498,7 +505,7 @@ void ConvexShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inC
|
|||
SupportingFace face = ftd.first;
|
||||
|
||||
// Displace the face a little bit forward so it is easier to see
|
||||
Vec3 normal = face.size() >= 3? (face[2] - face[1]).Cross(face[0] - face[1]).Normalized() : Vec3::sZero();
|
||||
Vec3 normal = face.size() >= 3? (face[2] - face[1]).Cross(face[0] - face[1]).NormalizedOr(Vec3::sZero()) : Vec3::sZero();
|
||||
Vec3 displacement = 0.001f * normal;
|
||||
|
||||
// Transform face to world space and calculate center of mass
|
||||
|
|
|
|||
|
|
@ -200,13 +200,14 @@ void CylinderShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg in
|
|||
float scaled_radius = scale_xz * mRadius;
|
||||
|
||||
float x = inDirection.GetX(), y = inDirection.GetY(), z = inDirection.GetZ();
|
||||
float o = sqrt(Square(x) + Square(z));
|
||||
float xz_sq = Square(x) + Square(z);
|
||||
float y_sq = Square(y);
|
||||
|
||||
// If o / |y| > scaled_radius / scaled_half_height, we're hitting the side
|
||||
if (o * scaled_half_height > scaled_radius * abs(y))
|
||||
// Check which component is bigger
|
||||
if (xz_sq > y_sq)
|
||||
{
|
||||
// Hitting side
|
||||
float f = -scaled_radius / o;
|
||||
float f = -scaled_radius / sqrt(xz_sq);
|
||||
float vx = x * f;
|
||||
float vz = z * f;
|
||||
outVertices.push_back(inCenterOfMassTransform * Vec3(vx, scaled_half_height, vz));
|
||||
|
|
@ -215,8 +216,21 @@ void CylinderShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg in
|
|||
else
|
||||
{
|
||||
// Hitting top or bottom
|
||||
|
||||
// When the inDirection is more than 5 degrees from vertical, align the vertices so that 1 of the vertices
|
||||
// points towards inDirection in the XZ plane. This ensures that we always have a vertex towards max penetration depth.
|
||||
Mat44 transform = inCenterOfMassTransform;
|
||||
if (xz_sq > 0.00765427f * y_sq)
|
||||
{
|
||||
Vec4 base_x = Vec4(x, 0, z, 0) / sqrt(xz_sq);
|
||||
Vec4 base_z = base_x.Swizzle<SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X, SWIZZLE_W>() * Vec4(-1, 0, 1, 0);
|
||||
transform = transform * Mat44(base_x, Vec4(0, 1, 0, 0), base_z, Vec4(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
// Adjust for scale and height
|
||||
Vec3 multiplier = y < 0.0f? Vec3(scaled_radius, scaled_half_height, scaled_radius) : Vec3(-scaled_radius, -scaled_half_height, scaled_radius);
|
||||
Mat44 transform = inCenterOfMassTransform.PreScaled(multiplier);
|
||||
transform = transform.PreScaled(multiplier);
|
||||
|
||||
for (const Vec3 &v : cCylinderTopFace)
|
||||
outVertices.push_back(transform * v);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,119 +201,155 @@ uint32 HeightFieldShapeSettings::CalculateBitsPerSampleForError(float inMaxError
|
|||
|
||||
void HeightFieldShape::CalculateActiveEdges(uint inX, uint inY, uint inSizeX, uint inSizeY, const float *inHeights, uint inHeightsStartX, uint inHeightsStartY, intptr_t inHeightsStride, float inHeightsScale, float inActiveEdgeCosThresholdAngle, TempAllocator &inAllocator)
|
||||
{
|
||||
// Limit the block size so we don't allocate more than 64K memory from the temp allocator
|
||||
uint block_size_x = min(inSizeX, 44u);
|
||||
uint block_size_y = min(inSizeY, 44u);
|
||||
|
||||
// Allocate temporary buffer for normals
|
||||
uint normals_size = 2 * inSizeX * inSizeY * sizeof(Vec3);
|
||||
uint normals_size = 2 * (block_size_x + 1) * (block_size_y + 1) * sizeof(Vec3);
|
||||
Vec3 *normals = (Vec3 *)inAllocator.Allocate(normals_size);
|
||||
JPH_SCOPE_EXIT([&inAllocator, normals, normals_size]{ inAllocator.Free(normals, normals_size); });
|
||||
|
||||
// Calculate triangle normals and make normals zero for triangles that are missing
|
||||
Vec3 *out_normal = normals;
|
||||
for (uint y = 0; y < inSizeY; ++y)
|
||||
for (uint x = 0; x < inSizeX; ++x)
|
||||
// Update the edges in blocks
|
||||
for (uint block_y = 0; block_y < inSizeY; block_y += block_size_y)
|
||||
for (uint block_x = 0; block_x < inSizeX; block_x += block_size_x)
|
||||
{
|
||||
// Get height on diagonal
|
||||
const float *height_samples = inHeights + (inY - inHeightsStartY + y) * inHeightsStride + (inX - inHeightsStartX + x);
|
||||
float x1y1_h = height_samples[0];
|
||||
float x2y2_h = height_samples[inHeightsStride + 1];
|
||||
if (x1y1_h != cNoCollisionValue && x2y2_h != cNoCollisionValue)
|
||||
{
|
||||
// Calculate normal for lower left triangle (e.g. T1A)
|
||||
float x1y2_h = height_samples[inHeightsStride];
|
||||
if (x1y2_h != cNoCollisionValue)
|
||||
{
|
||||
Vec3 x2y2_minus_x1y2(mScale.GetX(), inHeightsScale * (x2y2_h - x1y2_h), 0);
|
||||
Vec3 x1y1_minus_x1y2(0, inHeightsScale * (x1y1_h - x1y2_h), -mScale.GetZ());
|
||||
out_normal[0] = x2y2_minus_x1y2.Cross(x1y1_minus_x1y2).Normalized();
|
||||
}
|
||||
else
|
||||
out_normal[0] = Vec3::sZero();
|
||||
// Calculate the bottom right corner of the block
|
||||
uint block_x_end = min(block_x + block_size_x, inSizeX);
|
||||
uint block_y_end = min(block_y + block_size_y, inSizeY);
|
||||
|
||||
// Calculate normal for upper right triangle (e.g. T1B)
|
||||
float x2y1_h = height_samples[1];
|
||||
if (x2y1_h != cNoCollisionValue)
|
||||
{
|
||||
Vec3 x1y1_minus_x2y1(-mScale.GetX(), inHeightsScale * (x1y1_h - x2y1_h), 0);
|
||||
Vec3 x2y2_minus_x2y1(0, inHeightsScale * (x2y2_h - x2y1_h), mScale.GetZ());
|
||||
out_normal[1] = x1y1_minus_x2y1.Cross(x2y2_minus_x2y1).Normalized();
|
||||
}
|
||||
else
|
||||
out_normal[1] = Vec3::sZero();
|
||||
// If we're not at the first block in x, we need one extra column of normals to the left
|
||||
uint normals_x_start, normals_x_skip;
|
||||
if (block_x > 0)
|
||||
{
|
||||
normals_x_start = block_x - 1;
|
||||
normals_x_skip = 2; // We need to skip over that extra column
|
||||
}
|
||||
else
|
||||
{
|
||||
out_normal[0] = Vec3::sZero();
|
||||
out_normal[1] = Vec3::sZero();
|
||||
normals_x_start = 0;
|
||||
normals_x_skip = 0;
|
||||
}
|
||||
|
||||
out_normal += 2;
|
||||
// If we're not at the last block in y, we need one extra row of normals at the bottom
|
||||
uint normals_y_end = block_y_end < inSizeY? block_y_end + 1 : inSizeY;
|
||||
|
||||
// Calculate triangle normals and make normals zero for triangles that are missing
|
||||
Vec3 *out_normal = normals;
|
||||
for (uint y = block_y; y < normals_y_end; ++y)
|
||||
{
|
||||
for (uint x = normals_x_start; x < block_x_end; ++x)
|
||||
{
|
||||
// Get height on diagonal
|
||||
const float *height_samples = inHeights + (inY - inHeightsStartY + y) * inHeightsStride + (inX - inHeightsStartX + x);
|
||||
float x1y1_h = height_samples[0];
|
||||
float x2y2_h = height_samples[inHeightsStride + 1];
|
||||
if (x1y1_h != cNoCollisionValue && x2y2_h != cNoCollisionValue)
|
||||
{
|
||||
// Calculate normal for lower left triangle (e.g. T1A)
|
||||
float x1y2_h = height_samples[inHeightsStride];
|
||||
if (x1y2_h != cNoCollisionValue)
|
||||
{
|
||||
Vec3 x2y2_minus_x1y2(mScale.GetX(), inHeightsScale * (x2y2_h - x1y2_h), 0);
|
||||
Vec3 x1y1_minus_x1y2(0, inHeightsScale * (x1y1_h - x1y2_h), -mScale.GetZ());
|
||||
out_normal[0] = x2y2_minus_x1y2.Cross(x1y1_minus_x1y2).Normalized();
|
||||
}
|
||||
else
|
||||
out_normal[0] = Vec3::sZero();
|
||||
|
||||
// Calculate normal for upper right triangle (e.g. T1B)
|
||||
float x2y1_h = height_samples[1];
|
||||
if (x2y1_h != cNoCollisionValue)
|
||||
{
|
||||
Vec3 x1y1_minus_x2y1(-mScale.GetX(), inHeightsScale * (x1y1_h - x2y1_h), 0);
|
||||
Vec3 x2y2_minus_x2y1(0, inHeightsScale * (x2y2_h - x2y1_h), mScale.GetZ());
|
||||
out_normal[1] = x1y1_minus_x2y1.Cross(x2y2_minus_x2y1).Normalized();
|
||||
}
|
||||
else
|
||||
out_normal[1] = Vec3::sZero();
|
||||
}
|
||||
else
|
||||
{
|
||||
out_normal[0] = Vec3::sZero();
|
||||
out_normal[1] = Vec3::sZero();
|
||||
}
|
||||
|
||||
out_normal += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Number of vectors to skip to get to the next row of normals
|
||||
uint normals_pitch = 2 * (block_x_end - normals_x_start);
|
||||
|
||||
// Calculate active edges
|
||||
const Vec3 *in_normal = normals;
|
||||
uint global_bit_pos = 3 * ((inY + block_y) * (mSampleCount - 1) + (inX + block_x));
|
||||
for (uint y = block_y; y < block_y_end; ++y)
|
||||
{
|
||||
in_normal += normals_x_skip; // If we have an extra column to the left, skip it here, we'll read it with in_normal[-1] below
|
||||
|
||||
for (uint x = block_x; x < block_x_end; ++x)
|
||||
{
|
||||
// Get vertex heights
|
||||
const float *height_samples = inHeights + (inY - inHeightsStartY + y) * inHeightsStride + (inX - inHeightsStartX + x);
|
||||
float x1y1_h = height_samples[0];
|
||||
float x1y2_h = height_samples[inHeightsStride];
|
||||
float x2y2_h = height_samples[inHeightsStride + 1];
|
||||
bool x1y1_valid = x1y1_h != cNoCollisionValue;
|
||||
bool x1y2_valid = x1y2_h != cNoCollisionValue;
|
||||
bool x2y2_valid = x2y2_h != cNoCollisionValue;
|
||||
|
||||
// Calculate the edge flags (3 bits)
|
||||
// See diagram in the next function for the edge numbering
|
||||
uint16 edge_mask = 0b111;
|
||||
uint16 edge_flags = 0;
|
||||
|
||||
// Edge 0
|
||||
if (x == 0)
|
||||
edge_mask &= 0b110; // We need normal x - 1 which we didn't calculate, don't update this edge
|
||||
else if (x1y1_valid && x1y2_valid)
|
||||
{
|
||||
Vec3 edge0_direction(0, inHeightsScale * (x1y2_h - x1y1_h), mScale.GetZ());
|
||||
if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[-1], edge0_direction, inActiveEdgeCosThresholdAngle))
|
||||
edge_flags |= 0b001;
|
||||
}
|
||||
|
||||
// Edge 1
|
||||
if (y == inSizeY - 1)
|
||||
edge_mask &= 0b101; // We need normal y + 1 which we didn't calculate, don't update this edge
|
||||
else if (x1y2_valid && x2y2_valid)
|
||||
{
|
||||
Vec3 edge1_direction(mScale.GetX(), inHeightsScale * (x2y2_h - x1y2_h), 0);
|
||||
if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[normals_pitch + 1], edge1_direction, inActiveEdgeCosThresholdAngle))
|
||||
edge_flags |= 0b010;
|
||||
}
|
||||
|
||||
// Edge 2
|
||||
if (x1y1_valid && x2y2_valid)
|
||||
{
|
||||
Vec3 edge2_direction(-mScale.GetX(), inHeightsScale * (x1y1_h - x2y2_h), -mScale.GetZ());
|
||||
if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[1], edge2_direction, inActiveEdgeCosThresholdAngle))
|
||||
edge_flags |= 0b100;
|
||||
}
|
||||
|
||||
// Store the edge flags in the array
|
||||
uint byte_pos = global_bit_pos >> 3;
|
||||
uint bit_pos = global_bit_pos & 0b111;
|
||||
JPH_ASSERT(byte_pos < mActiveEdgesSize);
|
||||
uint8 *edge_flags_ptr = &mActiveEdges[byte_pos];
|
||||
uint16 combined_edge_flags = uint16(edge_flags_ptr[0]) | uint16(uint16(edge_flags_ptr[1]) << 8);
|
||||
combined_edge_flags &= ~(edge_mask << bit_pos);
|
||||
combined_edge_flags |= edge_flags << bit_pos;
|
||||
edge_flags_ptr[0] = uint8(combined_edge_flags);
|
||||
edge_flags_ptr[1] = uint8(combined_edge_flags >> 8);
|
||||
|
||||
in_normal += 2;
|
||||
global_bit_pos += 3;
|
||||
}
|
||||
|
||||
global_bit_pos += 3 * (mSampleCount - 1 - (block_x_end - block_x));
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate active edges
|
||||
const Vec3 *in_normal = normals;
|
||||
uint global_bit_pos = 3 * (inY * (mSampleCount - 1) + inX);
|
||||
for (uint y = 0; y < inSizeY; ++y)
|
||||
{
|
||||
for (uint x = 0; x < inSizeX; ++x)
|
||||
{
|
||||
// Get vertex heights
|
||||
const float *height_samples = inHeights + (inY - inHeightsStartY + y) * inHeightsStride + (inX - inHeightsStartX + x);
|
||||
float x1y1_h = height_samples[0];
|
||||
float x1y2_h = height_samples[inHeightsStride];
|
||||
float x2y2_h = height_samples[inHeightsStride + 1];
|
||||
bool x1y1_valid = x1y1_h != cNoCollisionValue;
|
||||
bool x1y2_valid = x1y2_h != cNoCollisionValue;
|
||||
bool x2y2_valid = x2y2_h != cNoCollisionValue;
|
||||
|
||||
// Calculate the edge flags (3 bits)
|
||||
// See diagram in the next function for the edge numbering
|
||||
uint16 edge_mask = 0b111;
|
||||
uint16 edge_flags = 0;
|
||||
|
||||
// Edge 0
|
||||
if (x == 0)
|
||||
edge_mask &= 0b110; // We need normal x - 1 which we didn't calculate, don't update this edge
|
||||
else if (x1y1_valid && x1y2_valid)
|
||||
{
|
||||
Vec3 edge0_direction(0, inHeightsScale * (x1y2_h - x1y1_h), mScale.GetZ());
|
||||
if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[-1], edge0_direction, inActiveEdgeCosThresholdAngle))
|
||||
edge_flags |= 0b001;
|
||||
}
|
||||
|
||||
// Edge 1
|
||||
if (y == inSizeY - 1)
|
||||
edge_mask &= 0b101; // We need normal y + 1 which we didn't calculate, don't update this edge
|
||||
else if (x1y2_valid && x2y2_valid)
|
||||
{
|
||||
Vec3 edge1_direction(mScale.GetX(), inHeightsScale * (x2y2_h - x1y2_h), 0);
|
||||
if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[2 * inSizeX + 1], edge1_direction, inActiveEdgeCosThresholdAngle))
|
||||
edge_flags |= 0b010;
|
||||
}
|
||||
|
||||
// Edge 2
|
||||
if (x1y1_valid && x2y2_valid)
|
||||
{
|
||||
Vec3 edge2_direction(-mScale.GetX(), inHeightsScale * (x1y1_h - x2y2_h), -mScale.GetZ());
|
||||
if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[1], edge2_direction, inActiveEdgeCosThresholdAngle))
|
||||
edge_flags |= 0b100;
|
||||
}
|
||||
|
||||
// Store the edge flags in the array
|
||||
uint byte_pos = global_bit_pos >> 3;
|
||||
uint bit_pos = global_bit_pos & 0b111;
|
||||
JPH_ASSERT(byte_pos < mActiveEdgesSize);
|
||||
uint8 *edge_flags_ptr = &mActiveEdges[byte_pos];
|
||||
uint16 combined_edge_flags = uint16(edge_flags_ptr[0]) | uint16(uint16(edge_flags_ptr[1]) << 8);
|
||||
combined_edge_flags &= ~(edge_mask << bit_pos);
|
||||
combined_edge_flags |= edge_flags << bit_pos;
|
||||
edge_flags_ptr[0] = uint8(combined_edge_flags);
|
||||
edge_flags_ptr[1] = uint8(combined_edge_flags >> 8);
|
||||
|
||||
in_normal += 2;
|
||||
global_bit_pos += 3;
|
||||
}
|
||||
|
||||
global_bit_pos += 3 * (mSampleCount - 1 - inSizeX);
|
||||
}
|
||||
}
|
||||
|
||||
void HeightFieldShape::CalculateActiveEdges(const HeightFieldShapeSettings &inSettings)
|
||||
|
|
@ -1710,7 +1746,7 @@ public:
|
|||
JPH_INLINE explicit DecodingContext(const HeightFieldShape *inShape) :
|
||||
mShape(inShape)
|
||||
{
|
||||
static_assert(sizeof(sGridOffsets) / sizeof(uint) == cNumBitsXY + 1, "Offsets array is not long enough");
|
||||
static_assert(std::size(sGridOffsets) == cNumBitsXY + 1, "Offsets array is not long enough");
|
||||
|
||||
// Construct root stack entry
|
||||
mPropertiesStack[0] = 0; // level: 0, x: 0, y: 0
|
||||
|
|
@ -1869,8 +1905,8 @@ public:
|
|||
uint32 stride = block_size_plus_1 - size_x_plus_1;
|
||||
|
||||
// Start range with a very large inside-out box
|
||||
Vec3 value_min = Vec3::sReplicate(1.0e30f);
|
||||
Vec3 value_max = Vec3::sReplicate(-1.0e30f);
|
||||
Vec3 value_min = Vec3::sReplicate(cLargeFloat);
|
||||
Vec3 value_max = Vec3::sReplicate(-cLargeFloat);
|
||||
|
||||
// Loop over the samples to determine the min and max of this block
|
||||
for (uint32 block_y = 0; block_y < size_y_plus_1; ++block_y)
|
||||
|
|
|
|||
|
|
@ -69,14 +69,14 @@ public:
|
|||
/// The height field is a surface defined by: mOffset + mScale * (x, mHeightSamples[y * mSampleCount + x], y).
|
||||
/// where x and y are integers in the range x and y e [0, mSampleCount - 1].
|
||||
Vec3 mOffset = Vec3::sZero();
|
||||
Vec3 mScale = Vec3::sReplicate(1.0f);
|
||||
Vec3 mScale = Vec3::sOne();
|
||||
uint32 mSampleCount = 0;
|
||||
|
||||
/// Artificial minimal value of mHeightSamples, used for compression and can be used to update the terrain after creating with lower height values. If there are any lower values in mHeightSamples, this value will be ignored.
|
||||
float mMinHeightValue = FLT_MAX;
|
||||
float mMinHeightValue = cLargeFloat;
|
||||
|
||||
/// Artificial maximum value of mHeightSamples, used for compression and can be used to update the terrain after creating with higher height values. If there are any higher values in mHeightSamples, this value will be ignored.
|
||||
float mMaxHeightValue = -FLT_MAX;
|
||||
float mMaxHeightValue = -cLargeFloat;
|
||||
|
||||
/// When bigger than mMaterials.size() the internal material list will be preallocated to support this number of materials.
|
||||
/// This avoids reallocations when calling HeightFieldShape::SetMaterials with new materials later.
|
||||
|
|
@ -349,7 +349,7 @@ private:
|
|||
/// The height field is a surface defined by: mOffset + mScale * (x, mHeightSamples[y * mSampleCount + x], y).
|
||||
/// where x and y are integers in the range x and y e [0, mSampleCount - 1].
|
||||
Vec3 mOffset = Vec3::sZero();
|
||||
Vec3 mScale = Vec3::sReplicate(1.0f);
|
||||
Vec3 mScale = Vec3::sOne();
|
||||
|
||||
/// Height data
|
||||
uint32 mSampleCount = 0; ///< See HeightFieldShapeSettings::mSampleCount
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <Jolt/Geometry/Plane.h>
|
||||
#include <Jolt/Geometry/OrientedBox.h>
|
||||
#include <Jolt/TriangleSplitter/TriangleSplitterBinning.h>
|
||||
#include <Jolt/TriangleSplitter/TriangleSplitterMean.h>
|
||||
#include <Jolt/AABBTree/AABBTreeBuilder.h>
|
||||
#include <Jolt/AABBTree/AABBTreeToBuffer.h>
|
||||
#include <Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h>
|
||||
|
|
@ -55,6 +56,7 @@ JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MeshShapeSettings)
|
|||
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaxTrianglesPerLeaf)
|
||||
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mActiveEdgeCosThresholdAngle)
|
||||
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mPerTriangleUserData)
|
||||
JPH_ADD_ENUM_ATTRIBUTE(MeshShapeSettings, mBuildQuality)
|
||||
}
|
||||
|
||||
// Codecs this mesh shape is using
|
||||
|
|
@ -190,12 +192,36 @@ MeshShape::MeshShape(const MeshShapeSettings &inSettings, ShapeResult &outResult
|
|||
sFindActiveEdges(inSettings, indexed_triangles);
|
||||
|
||||
// Create triangle splitter
|
||||
TriangleSplitterBinning splitter(inSettings.mTriangleVertices, indexed_triangles);
|
||||
union Storage
|
||||
{
|
||||
Storage() { }
|
||||
~Storage() { }
|
||||
|
||||
TriangleSplitterBinning mBinning;
|
||||
TriangleSplitterMean mMean;
|
||||
};
|
||||
Storage storage;
|
||||
TriangleSplitter *splitter = nullptr;
|
||||
switch (inSettings.mBuildQuality)
|
||||
{
|
||||
case MeshShapeSettings::EBuildQuality::FavorRuntimePerformance:
|
||||
splitter = new (&storage.mBinning) TriangleSplitterBinning(inSettings.mTriangleVertices, indexed_triangles);
|
||||
break;
|
||||
|
||||
case MeshShapeSettings::EBuildQuality::FavorBuildSpeed:
|
||||
splitter = new (&storage.mMean) TriangleSplitterMean(inSettings.mTriangleVertices, indexed_triangles);
|
||||
break;
|
||||
|
||||
default:
|
||||
JPH_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Build tree
|
||||
AABBTreeBuilder builder(splitter, inSettings.mMaxTrianglesPerLeaf);
|
||||
AABBTreeBuilder builder(*splitter, inSettings.mMaxTrianglesPerLeaf);
|
||||
AABBTreeBuilderStats builder_stats;
|
||||
const AABBTreeBuilder::Node *root = builder.Build(builder_stats);
|
||||
splitter->~TriangleSplitter();
|
||||
|
||||
// Convert to buffer
|
||||
AABBTreeToBuffer<TriangleCodec, NodeCodec> buffer;
|
||||
|
|
@ -221,6 +247,14 @@ MeshShape::MeshShape(const MeshShapeSettings &inSettings, ShapeResult &outResult
|
|||
|
||||
void MeshShape::sFindActiveEdges(const MeshShapeSettings &inSettings, IndexedTriangleList &ioIndices)
|
||||
{
|
||||
// Check if we're requested to make all edges active
|
||||
if (inSettings.mActiveEdgeCosThresholdAngle < 0.0f)
|
||||
{
|
||||
for (IndexedTriangle &triangle : ioIndices)
|
||||
triangle.mMaterialIndex |= 0b111 << FLAGS_ACTIVE_EGDE_SHIFT;
|
||||
return;
|
||||
}
|
||||
|
||||
// A struct to hold the two vertex indices of an edge
|
||||
struct Edge
|
||||
{
|
||||
|
|
@ -349,7 +383,7 @@ MassProperties MeshShape::GetMassProperties() const
|
|||
// creating a Body:
|
||||
//
|
||||
// BodyCreationSettings::mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
|
||||
// BodyCreationSettings::mMassPropertiesOverride.SetMassAndInertiaOfSolidBox(Vec3::sReplicate(1.0f), 1000.0f);
|
||||
// BodyCreationSettings::mMassPropertiesOverride.SetMassAndInertiaOfSolidBox(Vec3::sOne(), 1000.0f);
|
||||
//
|
||||
// Note that for a mesh shape to simulate properly, it is best if the mesh is manifold
|
||||
// (i.e. closed, all edges shared by only two triangles, consistent winding order).
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ public:
|
|||
/// Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive).
|
||||
/// 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).
|
||||
/// Negative values will make all edges active and causes EActiveEdgeMode::CollideOnlyWithActive to behave as EActiveEdgeMode::CollideWithAll.
|
||||
/// This speeds up the build process but will require all bodies that can interact with the mesh to use BodyCreationSettings::mEnhancedInternalEdgeRemoval = true.
|
||||
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.
|
||||
|
|
@ -65,6 +67,15 @@ public:
|
|||
/// Can be retrieved using MeshShape::GetTriangleUserData.
|
||||
/// Turning this on increases the memory used by the MeshShape by roughly 25%.
|
||||
bool mPerTriangleUserData = false;
|
||||
|
||||
enum class EBuildQuality
|
||||
{
|
||||
FavorRuntimePerformance, ///< Favor runtime performance, takes more time to build the MeshShape but performs better
|
||||
FavorBuildSpeed, ///< Favor build speed, build the tree faster but the MeshShape will be slower
|
||||
};
|
||||
|
||||
/// Determines the quality of the tree building process.
|
||||
EBuildQuality mBuildQuality = EBuildQuality::FavorRuntimePerformance;
|
||||
};
|
||||
|
||||
/// A mesh shape, consisting of triangles. Mesh shapes are mostly used for static geometry.
|
||||
|
|
|
|||
|
|
@ -142,8 +142,8 @@ void MutableCompoundShape::CalculateLocalBounds()
|
|||
}
|
||||
else
|
||||
{
|
||||
// There are no subshapes, set the bounding box to invalid
|
||||
mLocalBounds.SetEmpty();
|
||||
// There are no subshapes, make the bounding box empty
|
||||
mLocalBounds.mMin = mLocalBounds.mMax = Vec3::sZero();
|
||||
}
|
||||
|
||||
// Cache the inner radius as it can take a while to recursively iterate over all sub shapes
|
||||
|
|
@ -181,7 +181,7 @@ void MutableCompoundShape::CalculateSubShapeBounds(uint inStartIdx, uint inNumbe
|
|||
Mat44 transform = Mat44::sRotationTranslation(sub_shape.GetRotation(), sub_shape.GetPositionCOM());
|
||||
|
||||
// Get the bounding box
|
||||
sub_shape_bounds = sub_shape.mShape->GetWorldSpaceBounds(transform, Vec3::sReplicate(1.0f));
|
||||
sub_shape_bounds = sub_shape.mShape->GetWorldSpaceBounds(transform, Vec3::sOne());
|
||||
}
|
||||
|
||||
// Put the bounds as columns in a matrix
|
||||
|
|
@ -206,29 +206,37 @@ void MutableCompoundShape::CalculateSubShapeBounds(uint inStartIdx, uint inNumbe
|
|||
CalculateLocalBounds();
|
||||
}
|
||||
|
||||
uint MutableCompoundShape::AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData)
|
||||
uint MutableCompoundShape::AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData, uint inIndex)
|
||||
{
|
||||
SubShape sub_shape;
|
||||
sub_shape.mShape = inShape;
|
||||
sub_shape.mUserData = inUserData;
|
||||
sub_shape.SetTransform(inPosition, inRotation, mCenterOfMass);
|
||||
mSubShapes.push_back(sub_shape);
|
||||
uint shape_idx = (uint)mSubShapes.size() - 1;
|
||||
|
||||
CalculateSubShapeBounds(shape_idx, 1);
|
||||
|
||||
return shape_idx;
|
||||
if (inIndex >= mSubShapes.size())
|
||||
{
|
||||
uint shape_idx = uint(mSubShapes.size());
|
||||
mSubShapes.push_back(sub_shape);
|
||||
CalculateSubShapeBounds(shape_idx, 1);
|
||||
return shape_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSubShapes.insert(mSubShapes.begin() + inIndex, sub_shape);
|
||||
CalculateSubShapeBounds(inIndex, uint(mSubShapes.size()) - inIndex);
|
||||
return inIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void MutableCompoundShape::RemoveShape(uint inIndex)
|
||||
{
|
||||
mSubShapes.erase(mSubShapes.begin() + inIndex);
|
||||
|
||||
// We always need to recalculate the bounds of the sub shapes as we test blocks
|
||||
// of 4 sub shapes at a time and removed shapes get their bounds updated
|
||||
// to repeat the bounds of the previous sub shape
|
||||
uint num_bounds = (uint)mSubShapes.size() - inIndex;
|
||||
if (num_bounds > 0)
|
||||
CalculateSubShapeBounds(inIndex, num_bounds);
|
||||
else
|
||||
CalculateLocalBounds();
|
||||
CalculateSubShapeBounds(inIndex, num_bounds);
|
||||
}
|
||||
|
||||
void MutableCompoundShape::ModifyShape(uint inIndex, Vec3Arg inPosition, QuatArg inRotation)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ public:
|
|||
/// Note: If you're using MutableCompoundShape and are querying data while modifying the shape you'll have a race condition.
|
||||
/// In this case it is best to create a new MutableCompoundShape using the Clone function. You replace the shape on a body using BodyInterface::SetShape.
|
||||
/// If a query is still working on the old shape, it will have taken a reference and keep the old shape alive until the query finishes.
|
||||
///
|
||||
/// When you modify a MutableCompoundShape, beware that the SubShapeIDs of all other shapes can change. So be careful when storing SubShapeIDs.
|
||||
class JPH_EXPORT MutableCompoundShape final : public CompoundShape
|
||||
{
|
||||
public:
|
||||
|
|
@ -66,8 +68,13 @@ public:
|
|||
|
||||
/// Adding a new shape.
|
||||
/// Beware this can create a race condition if you're running collision queries in parallel. See class documentation for more information.
|
||||
/// @param inPosition The position of the new shape
|
||||
/// @param inRotation The orientation of the new shape
|
||||
/// @param inShape The shape to add
|
||||
/// @param inUserData User data that will be stored with the shape and can be retrieved using GetCompoundUserData
|
||||
/// @param inIndex Index where to insert the shape, UINT_MAX to add to the end
|
||||
/// @return The index of the newly added shape
|
||||
uint AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData = 0);
|
||||
uint AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData = 0, uint inIndex = UINT_MAX);
|
||||
|
||||
/// Remove a shape by index.
|
||||
/// Beware this can create a race condition if you're running collision queries in parallel. See class documentation for more information.
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public:
|
|||
virtual MassProperties GetMassProperties() const override;
|
||||
|
||||
// See Shape::GetMaterial
|
||||
virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override { JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); return mMaterial != nullptr? mMaterial : PhysicsMaterial::sDefault; }
|
||||
virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override { JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); return GetMaterial(); }
|
||||
|
||||
// See Shape::GetSurfaceNormal
|
||||
virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override { JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); return mPlane.GetNormal(); }
|
||||
|
|
@ -114,6 +114,10 @@ public:
|
|||
// See Shape::GetVolume
|
||||
virtual float GetVolume() const override { return 0; }
|
||||
|
||||
/// Material of the shape
|
||||
void SetMaterial(const PhysicsMaterial *inMaterial) { mMaterial = inMaterial; }
|
||||
const PhysicsMaterial * GetMaterial() const { return mMaterial != nullptr? mMaterial : PhysicsMaterial::sDefault; }
|
||||
|
||||
// Register shape functions with the registry
|
||||
static void sRegister();
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace ScaleHelpers
|
|||
static constexpr float cScaleToleranceSq = 1.0e-8f;
|
||||
|
||||
/// Test if a scale is identity
|
||||
inline bool IsNotScaled(Vec3Arg inScale) { return inScale.IsClose(Vec3::sReplicate(1.0f), cScaleToleranceSq); }
|
||||
inline bool IsNotScaled(Vec3Arg inScale) { return inScale.IsClose(Vec3::sOne(), cScaleToleranceSq); }
|
||||
|
||||
/// Test if a scale is uniform
|
||||
inline bool IsUniformScale(Vec3Arg inScale) { return inScale.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>().IsClose(inScale, cScaleToleranceSq); }
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ Vec3 Shape::MakeScaleValid(Vec3Arg inScale) const
|
|||
|
||||
Shape::ShapeResult Shape::ScaleShape(Vec3Arg inScale) const
|
||||
{
|
||||
const Vec3 unit_scale = Vec3::sReplicate(1.0f);
|
||||
const Vec3 unit_scale = Vec3::sOne();
|
||||
|
||||
if (inScale.IsNearZero())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ void SphereShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3
|
|||
void SphereShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const
|
||||
{
|
||||
float scaled_radius = GetScaledRadius(inScale);
|
||||
new (&ioContext) GetTrianglesContextVertexList(inPositionCOM, inRotation, Vec3::sReplicate(1.0f), Mat44::sScale(scaled_radius), sUnitSphereTriangles.data(), sUnitSphereTriangles.size(), GetMaterial());
|
||||
new (&ioContext) GetTrianglesContextVertexList(inPositionCOM, inRotation, Vec3::sOne(), Mat44::sScale(scaled_radius), sUnitSphereTriangles.data(), sUnitSphereTriangles.size(), GetMaterial());
|
||||
}
|
||||
|
||||
int SphereShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ StaticCompoundShape::StaticCompoundShape(const StaticCompoundShapeSettings &inSe
|
|||
|
||||
// Transform the shape's bounds into our local space
|
||||
Mat44 transform = Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM());
|
||||
AABox shape_bounds = shape.mShape->GetWorldSpaceBounds(transform, Vec3::sReplicate(1.0f));
|
||||
AABox shape_bounds = shape.mShape->GetWorldSpaceBounds(transform, Vec3::sOne());
|
||||
|
||||
// Store bounds and body index for tree construction
|
||||
bounds[i] = shape_bounds;
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ void TaperedCapsuleShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMa
|
|||
if (mGeometry == nullptr)
|
||||
{
|
||||
SupportBuffer buffer;
|
||||
const Support *support = GetSupportFunction(ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f));
|
||||
const Support *support = GetSupportFunction(ESupportMode::IncludeConvexRadius, buffer, Vec3::sOne());
|
||||
mGeometry = inRenderer->CreateTriangleGeometryForConvex([support](Vec3Arg inDirection) { return support->GetSupport(inDirection); });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -265,24 +265,40 @@ void TaperedCylinderShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec
|
|||
outVertices.push_back(inCenterOfMassTransform * (normal_xz * top_radius + Vec3(0, top, 0)));
|
||||
outVertices.push_back(inCenterOfMassTransform * (normal_xz * bottom_radius + Vec3(0, bottom, 0)));
|
||||
}
|
||||
else if (inDirection.GetY() < 0.0f)
|
||||
{
|
||||
// Top of the cylinder
|
||||
if (top_radius > cMinRadius)
|
||||
{
|
||||
Vec3 top_3d(0, top, 0);
|
||||
for (Vec3 v : cTaperedCylinderFace)
|
||||
outVertices.push_back(inCenterOfMassTransform * (top_radius * v + top_3d));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bottom of the cylinder
|
||||
if (bottom_radius > cMinRadius)
|
||||
// When the inDirection is more than 5 degrees from vertical, align the vertices so that 1 of the vertices
|
||||
// points towards inDirection in the XZ plane. This ensures that we always have a vertex towards max penetration depth.
|
||||
Mat44 transform = inCenterOfMassTransform;
|
||||
Vec4 base_x = Vec4(inDirection.GetX(), 0, inDirection.GetZ(), 0);
|
||||
float xz_sq = base_x.LengthSq();
|
||||
float y_sq = Square(inDirection.GetY());
|
||||
if (xz_sq > 0.00765427f * y_sq)
|
||||
{
|
||||
Vec3 bottom_3d(0, bottom, 0);
|
||||
for (const Vec3 *v = cTaperedCylinderFace + std::size(cTaperedCylinderFace) - 1; v >= cTaperedCylinderFace; --v)
|
||||
outVertices.push_back(inCenterOfMassTransform * (bottom_radius * *v + bottom_3d));
|
||||
base_x /= sqrt(xz_sq);
|
||||
Vec4 base_z = base_x.Swizzle<SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X, SWIZZLE_W>() * Vec4(-1, 0, 1, 0);
|
||||
transform = transform * Mat44(base_x, Vec4(0, 1, 0, 0), base_z, Vec4(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
if (inDirection.GetY() < 0.0f)
|
||||
{
|
||||
// Top of the cylinder
|
||||
if (top_radius > cMinRadius)
|
||||
{
|
||||
Vec3 top_3d(0, top, 0);
|
||||
for (Vec3 v : cTaperedCylinderFace)
|
||||
outVertices.push_back(transform * (top_radius * v + top_3d));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bottom of the cylinder
|
||||
if (bottom_radius > cMinRadius)
|
||||
{
|
||||
Vec3 bottom_3d(0, bottom, 0);
|
||||
for (const Vec3 *v = cTaperedCylinderFace + std::size(cTaperedCylinderFace) - 1; v >= cTaperedCylinderFace; --v)
|
||||
outVertices.push_back(transform * (bottom_radius * *v + bottom_3d));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ MassProperties TriangleShape::GetMassProperties() const
|
|||
// creating a Body:
|
||||
//
|
||||
// BodyCreationSettings::mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
|
||||
// BodyCreationSettings::mMassPropertiesOverride.SetMassAndInertiaOfSolidBox(Vec3::sReplicate(1.0f), 1000.0f);
|
||||
// BodyCreationSettings::mMassPropertiesOverride.SetMassAndInertiaOfSolidBox(Vec3::sOne(), 1000.0f);
|
||||
//
|
||||
// Note that this makes the triangle shape behave the same as a mesh shape with a single triangle.
|
||||
// In practice there is very little use for a dynamic triangle shape as back side collisions will be ignored
|
||||
|
|
@ -413,6 +413,9 @@ void TriangleShape::sRegister()
|
|||
{
|
||||
CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Triangle, sCollideConvexVsTriangle);
|
||||
CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Triangle, sCastConvexVsTriangle);
|
||||
|
||||
CollisionDispatch::sRegisterCollideShape(EShapeSubType::Triangle, s, CollisionDispatch::sReversedCollideShape);
|
||||
CollisionDispatch::sRegisterCastShape(EShapeSubType::Triangle, s, CollisionDispatch::sReversedCastShape);
|
||||
}
|
||||
|
||||
// Specialized collision functions
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue