Merge pull request #1281 from marauder2k9-torque/expanded-options-for-vhacd

Expanded options for vhacd
This commit is contained in:
Brian Roberts 2024-05-17 02:31:39 -05:00 committed by GitHub
commit b7c04c5734
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 806 additions and 1368 deletions

View file

@ -95,3 +95,4 @@ jobs:
path: "**/My Projects/Torque3D/game/test_detail.xml"
reporter: java-junit
fail-on-error: false
if: github.event_name != 'pull_request'

View file

@ -76,3 +76,4 @@ jobs:
path: "**/My Projects/Torque3D/game/test_detail.xml"
reporter: java-junit
fail-on-error: false
if: github.event_name != 'pull_request'

View file

@ -72,3 +72,4 @@ jobs:
path: "**/My Projects/Torque3D/game/test_detail.xml"
reporter: java-junit
fail-on-error: false
if: github.event_name != 'pull_request'

View file

@ -165,15 +165,15 @@ public:
// Box
void addBox( const Point3F& sides, const MatrixF& mat );
void fitOBB();
void fitOBB(const char* target);
// Sphere
void addSphere( F32 radius, const Point3F& center );
void fitSphere();
void fitSphere(const char* target);
// Capsule
void addCapsule( F32 radius, F32 height, const MatrixF& mat );
void fitCapsule();
void fitCapsule(const char* target);
// k-DOP
void fit10_DOP_X();
@ -183,7 +183,7 @@ public:
void fit26_DOP();
// Convex Hulls
void fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts,
void fitConvexHulls( const char* target, U32 depth, U32 fillType, F32 minPercentage, U32 maxHulls, U32 maxHullVerts,
F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError );
};
@ -404,6 +404,7 @@ void MeshFit::addBox( const Point3F& sides, const MatrixF& mat )
{
Point3F v = mesh->mVerts[i];
v.convolve(sides);
mat.mulP(v);
mesh->mVerts[i] = v;
}
@ -416,23 +417,27 @@ void MeshFit::addBox( const Point3F& sides, const MatrixF& mat )
TSMesh::__TSMeshVertexBase &vdata = mesh->mVertexData.getBase(i);
Point3F v = vdata.vert();
v.convolve(sides);
mat.mulP(v);
vdata.vert(v);
}
}
mesh->computeBounds();
mMeshes.increment();
mMeshes.last().type = MeshFit::Box;
mMeshes.last().transform = mat;
mMeshes.last().tsmesh = mesh;
}
void MeshFit::fitOBB()
void MeshFit::fitOBB(const char* target)
{
mMeshes.increment();
MatrixF worldtrans;
worldtrans.identity();
mShape->getNodeWorldTransform(mShape->findNode(target), &worldtrans);
mMeshes.last().transform = worldtrans;
PrimFit primFitter;
primFitter.fitBox( mVerts.size(), (F32*)mVerts.address() );
addBox( primFitter.mBoxSides, primFitter.mBoxTransform );
addBox( primFitter.mBoxSides, worldtrans.inverse() * primFitter.mBoxTransform );
}
//---------------------------
@ -443,11 +448,15 @@ void MeshFit::addSphere( F32 radius, const Point3F& center )
if ( !mesh )
return;
MatrixF sphereMat(true);
sphereMat.setPosition(center);
if (mesh->mVerts.size() > 0)
{
for (S32 i = 0; i < mesh->mVerts.size(); i++)
{
Point3F v = mesh->mVerts[i];
sphereMat.mulP(v);
mesh->mVerts[i] = v * radius;
}
@ -459,26 +468,30 @@ void MeshFit::addSphere( F32 radius, const Point3F& center )
{
TSMesh::__TSMeshVertexBase& vdata = mesh->mVertexData.getBase(i);
Point3F v = vdata.vert();
sphereMat.mulP(v);
vdata.vert(v * radius);
}
}
mesh->computeBounds();
mMeshes.increment();
MeshFit::Mesh& lastMesh = mMeshes.last();
lastMesh.type = MeshFit::Sphere;
lastMesh.transform.identity();
lastMesh.transform.setPosition(center);
lastMesh.tsmesh = mesh;
mMeshes.last().type = MeshFit::Sphere;
mMeshes.last().tsmesh = mesh;
}
void MeshFit::fitSphere()
void MeshFit::fitSphere(const char* target)
{
mMeshes.increment();
MatrixF worldtrans;
worldtrans.identity();
mShape->getNodeWorldTransform(mShape->findNode(target), &worldtrans);
mMeshes.last().transform = worldtrans;
PrimFit primFitter;
primFitter.fitSphere( mVerts.size(), (F32*)mVerts.address() );
addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter );
worldtrans.inverse();
worldtrans.mulP(primFitter.mSphereCenter);
addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter);
}
//---------------------------
@ -489,6 +502,7 @@ void MeshFit::addCapsule( F32 radius, F32 height, const MatrixF& mat )
if ( !mesh )
return;
MatrixF capTrans = mMeshes.last().transform * mat;
// Translate and scale the mesh verts
height = mMax( 0, height );
F32 offset = ( height / ( 2 * radius ) ) - 0.5f;
@ -498,6 +512,7 @@ void MeshFit::addCapsule( F32 radius, F32 height, const MatrixF& mat )
{
Point3F v = mesh->mVerts[i];
v.y += ((v.y > 0) ? offset : -offset);
capTrans.mulP(v);
mesh->mVerts[i] = v * radius;
}
@ -510,22 +525,29 @@ void MeshFit::addCapsule( F32 radius, F32 height, const MatrixF& mat )
TSMesh::__TSMeshVertexBase& vdata = mesh->mVertexData.getBase(i);
Point3F v = vdata.vert();
v.y += ((v.y > 0) ? offset : -offset);
capTrans.mulP(v);
vdata.vert(v * radius);
}
}
mesh->computeBounds();
mMeshes.increment();
mMeshes.last().type = MeshFit::Capsule;
mMeshes.last().transform = mat;
mMeshes.last().tsmesh = mesh;
}
void MeshFit::fitCapsule()
void MeshFit::fitCapsule(const char* target)
{
mMeshes.increment();
MatrixF worldtrans;
worldtrans.identity();
mShape->getNodeWorldTransform(mShape->findNode(target), &worldtrans);
mMeshes.last().transform = worldtrans;
PrimFit primFitter;
primFitter.fitCapsule( mVerts.size(), (F32*)mVerts.address() );
addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
}
@ -691,17 +713,17 @@ void MeshFit::fitK_DOP( const Vector<Point3F>& planes )
//---------------------------
// Best-fit set of convex hulls
void MeshFit::fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts,
void MeshFit::fitConvexHulls(const char* target, U32 depth, U32 fillType, F32 minPercentage, U32 maxHulls, U32 maxHullVerts,
F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError )
{
VHACD::IVHACD::Parameters p;
p.m_fillMode = VHACD::FillMode::FLOOD_FILL;
p.m_fillMode = (VHACD::FillMode)fillType;
p.m_maxNumVerticesPerCH = maxHullVerts;
p.m_shrinkWrap = true;
p.m_maxRecursionDepth = 64;
p.m_minimumVolumePercentErrorAllowed = 10;
p.m_maxRecursionDepth = depth;
p.m_minimumVolumePercentErrorAllowed = minPercentage;
p.m_resolution = 10000;
p.m_maxConvexHulls = depth;
p.m_maxConvexHulls = maxHulls;
VHACD::IVHACD* iface = VHACD::CreateVHACD_ASYNC();
@ -718,29 +740,37 @@ void MeshFit::fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThresh
{
VHACD::IVHACD::ConvexHull ch;
iface->GetConvexHull(i, ch);
mMeshes.increment();
MeshFit::Mesh& lastMesh = mMeshes.last();
eMeshType meshType = MeshFit::Hull;
MatrixF worldtrans;
worldtrans.identity();
mShape->getNodeWorldTransform(mShape->findNode(target), &worldtrans);
lastMesh.transform = worldtrans;
worldtrans.inverse();
// Compute error between actual mesh and fitted primitives
F32* points = new F32[ch.m_points.size() * 3];
for (U32 pt = 0; pt < ch.m_points.size(); pt++)
{
Point3F point(ch.m_points[pt].mX, ch.m_points[pt].mY, ch.m_points[pt].mZ);
worldtrans.mulP(point);
points[pt * 3 + 0] = point.x;
points[pt * 3 + 1] = point.y;
points[pt * 3 + 2] = point.z;
}
U32* indices = new U32[ch.m_triangles.size() * 3];
for (U32 ind = 0; ind < ch.m_triangles.size(); ind++)
{
indices[ind * 3 + 0] = ch.m_triangles[ind].mI0;
indices[ind * 3 + 1] = ch.m_triangles[ind].mI1;
indices[ind * 3 + 2] = ch.m_triangles[ind].mI2;
}
// Check if we can use a box, sphere or capsule primitive for this hull
if (( boxMaxError > 0 ) || ( sphereMaxError > 0 ) || ( capsuleMaxError > 0 ))
{
// Compute error between actual mesh and fitted primitives
F32* points = new F32[ch.m_points.size() * 3];
for (U32 pt = 0; pt < ch.m_points.size(); pt++)
{
points[pt * 3 + 0] = ch.m_points[pt].mX;
points[pt * 3 + 1] = ch.m_points[pt].mY;
points[pt * 3 + 2] = ch.m_points[pt].mZ;
}
U32* indices = new U32[ch.m_triangles.size() * 3];
for (U32 ind = 0; ind < ch.m_triangles.size(); ind++)
{
indices[ind * 3 + 0] = ch.m_triangles[ind].mI0;
indices[ind * 3 + 1] = ch.m_triangles[ind].mI1;
indices[ind * 3 + 2] = ch.m_triangles[ind].mI2;
}
F32 meshVolume = FLOAT_MATH::fm_computeMeshVolume(points, ch.m_triangles.size(), indices);
PrimFit primFitter;
@ -787,42 +817,19 @@ void MeshFit::fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThresh
else if ( meshType == MeshFit::Capsule )
addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
// else fall through to Hull processing
// cleanup
delete[] points;
delete[] indices;
}
if ( meshType == MeshFit::Hull )
{
// Create TSMesh from convex hull
mMeshes.increment();
MeshFit::Mesh& lastMesh = mMeshes.last();
lastMesh.type = MeshFit::Hull;
lastMesh.transform.identity();
U32* indices = new U32[ch.m_triangles.size() * 3];
for (U32 ind = 0; ind < ch.m_triangles.size(); ind++)
{
indices[ind * 3 + 0] = ch.m_triangles[ind].mI0;
indices[ind * 3 + 1] = ch.m_triangles[ind].mI1;
indices[ind * 3 + 2] = ch.m_triangles[ind].mI2;
}
F32* points = new F32[ch.m_points.size() * 3];
for (U32 pt = 0; pt < ch.m_points.size(); pt++)
{
points[pt * 3 + 0] = ch.m_points[pt].mX;
points[pt * 3 + 1] = ch.m_points[pt].mY;
points[pt * 3 + 2] = ch.m_points[pt].mZ;
}
lastMesh.tsmesh = createTriMesh(points, ch.m_points.size(), indices, ch.m_triangles.size());
lastMesh.tsmesh->computeBounds();
delete[] points;
delete[] indices;
}
delete[] points;
delete[] indices;
}
iface->Release();
@ -929,8 +936,8 @@ DefineTSShapeConstructorMethod( addPrimitive, bool, ( const char* meshName, cons
return true;
}}
DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char* type, const char* target, S32 depth, F32 merge, F32 concavity, S32 maxVerts, F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError ), ( 4, 30, 30, 32, 0, 0, 0 ),
( size, type, target, depth, merge, concavity, maxVerts, boxMaxError, sphereMaxError, capsuleMaxError ), false,
DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char* type, const char* target, S32 depth, F32 minPercentage, S32 maxHulls, S32 maxVerts, F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError, const char* fillMode), ( "bounds", 4, 10, 30, 32, 0, 0, 0, "flood fill"),
( size, type, target, depth, minPercentage, maxHulls, maxVerts, boxMaxError, sphereMaxError, capsuleMaxError, fillMode), false,
"Autofit a mesh primitive or set of convex hulls to the shape geometry. Hulls "
"may optionally be converted to boxes, spheres and/or capsules based on their "
"volume.\n"
@ -938,23 +945,20 @@ DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char
"@param type one of: box, sphere, capsule, 10-dop x, 10-dop y, 10-dop z, 18-dop, "
"26-dop, convex hulls. See the Shape Editor documentation for more details "
"about these types.\n"
"@param target geometry to fit collision mesh(es) to; either \"bounds\" (for the "
"whole shape), or the name of an object in the shape\n"
"@param target geometry to fit collision mesh(es) to; either \"bounds\" (for the whole shape), or the name of an object in the shape\n"
"@param depth maximum split recursion depth (hulls only)\n"
"@param merge volume % threshold used to merge hulls together (hulls only)\n"
"@param concavity volume % threshold used to detect concavity (hulls only)\n"
"@param minPercentage volume % error threshold (hulls only)\n"
"@param maxHulls allowed to be generated (hulls only)\n"
"@param maxVerts maximum number of vertices per hull (hulls only)\n"
"@param boxMaxError max % volume difference for a hull to be converted to a "
"box (hulls only)\n"
"@param sphereMaxError max % volume difference for a hull to be converted to "
"a sphere (hulls only)\n"
"@param capsuleMaxError max % volume difference for a hull to be converted to "
"a capsule (hulls only)\n"
"@param boxMaxError max % volume difference for a hull to be converted to a box (hulls only)\n"
"@param sphereMaxError max % volume difference for a hull to be converted to a sphere (hulls only)\n"
"@param capsuleMaxError max % volume difference for a hull to be converted to a capsule (hulls only)\n"
"@param fillMode method for filling the voxels in the volume (hulls only)\n"
"@return true if successful, false otherwise\n\n"
"@tsexample\n"
"%this.addCollisionDetail( -1, \"box\", \"bounds\" );\n"
"%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 0, 0, 0 );\n"
"%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 50, 50, 50 );\n"
"%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 10, 30, 32, 0, 0, 0 );\n"
"%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\",\"flood fill\", 4, 10, 30, 32, 50, 50, 50 );\n"
"@endtsexample\n" )
{
MeshFit fit( mShape );
@ -967,11 +971,11 @@ DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char
}
if ( !dStricmp( type, "box" ) )
fit.fitOBB();
fit.fitOBB(target);
else if ( !dStricmp( type, "sphere" ) )
fit.fitSphere();
fit.fitSphere(target);
else if ( !dStricmp( type, "capsule" ) )
fit.fitCapsule();
fit.fitCapsule(target);
else if ( !dStricmp( type, "10-dop x" ) )
fit.fit10_DOP_X();
else if ( !dStricmp( type, "10-dop y" ) )
@ -984,7 +988,13 @@ DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char
fit.fit26_DOP();
else if ( !dStricmp( type, "convex hulls" ) )
{
fit.fitConvexHulls( depth, merge, concavity, maxVerts,
U32 fillType = 0;
if (!dStricmp(fillMode, "surface only"))
fillType = 1;
if (!dStricmp(fillMode, "raycast fill"))
fillType = 2;
fit.fitConvexHulls( target, depth, fillType, minPercentage, maxHulls, maxVerts,
boxMaxError, sphereMaxError, capsuleMaxError );
}
else
@ -1015,6 +1025,9 @@ DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char
mShape->getNodeWorldTransform( nodeIndex, &mat );
if ( !mat.isIdentity() )
setNodeTransform( colNodeName, TransformF::Identity );
// clean node commands that are related to this target.
cleanTargetNodes(colNodeName, target);
}
// Add the meshes to the shape =>
@ -1032,22 +1045,32 @@ DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char
default: objName = "ColConvex"; break;
}
for ( S32 suffix = i; suffix != 0; suffix /= 26 )
objName += ('A' + ( suffix % 26 ) );
String meshName = objName + String::ToString( "%d", size );
S32 suffix = i;
while (true)
{
String tempName = objName;
for (S32 s = suffix; s != 0; s /= 26) {
tempName += ('A' + (s % 26));
}
if (mShape->findName(tempName) == -1)
break;
suffix++;
}
for (S32 s = suffix; s != 0; s /= 26) {
objName += ('A' + (s % 26));
}
String meshName = objName + String::ToString("%d", size);
mShape->addMesh( mesh->tsmesh, meshName );
// Add a node for this object if needed (non-identity transform)
if ( mesh->transform.isIdentity() )
{
mShape->setObjectNode( objName, colNodeName );
}
else
{
addNode( meshName, colNodeName, TransformF( mesh->transform ) );
mShape->setObjectNode( objName, meshName );
}
addNode( meshName, colNodeName, TransformF( mesh->transform ), false, target);
mShape->setObjectNode( objName, meshName );
}
mShape->init();

View file

@ -1111,8 +1111,8 @@ DefineTSShapeConstructorMethod(renameNode, bool, (const char* oldName, const cha
return true;
}}
DefineTSShapeConstructorMethod(addNode, bool, (const char* name, const char* parentName, TransformF txfm, bool isWorld), (TransformF::Identity, false),
(name, parentName, txfm, isWorld), false,
DefineTSShapeConstructorMethod(addNode, bool, (const char* name, const char* parentName, TransformF txfm, bool isWorld, const char* target), (TransformF::Identity, false, ""),
(name, parentName, txfm, isWorld, target), false,
"Add a new node.\n"
"@param name name for the new node (must not already exist)\n"
"@param parentName name of an existing node to be the parent of the new node. "
@ -1125,7 +1125,7 @@ DefineTSShapeConstructorMethod(addNode, bool, (const char* name, const char* par
"@tsexample\n"
"%this.addNode( \"Nose\", \"Bip01 Head\", \"0 2 2 0 0 1 0\" );\n"
"%this.addNode( \"myRoot\", \"\", \"0 0 4 0 0 1 1.57\" );\n"
"%this.addNode( \"Nodes\", \"Bip01 Head\", \"0 2 0 0 0 1 0\", true );\n"
"%this.addNode( \"Nodes\", \"Bip01 Head\", \"0 2 0 0 0 1 0\", true,\"Bounds\");\n"
"@endtsexample\n")
{
Point3F pos(txfm.getPosition());
@ -2433,6 +2433,7 @@ void TSShapeConstructor::ChangeSet::add( TSShapeConstructor::ChangeSet::Command&
// Detail level commands
case CmdRenameDetailLevel: addCommand = addCmd_renameDetailLevel(cmd); break;
case CmdRemoveDetailLevel: addCommand = addCmd_removeDetailLevel(cmd); break;
case CmdAddCollisionDetail: addCommand = addCmd_addDetailLevel(cmd); break;
case CmdSetDetailLevelSize: addCommand = addCmd_setDetailSize(cmd); break;
case CmdAddImposter: addCommand = addCmd_addImposter(cmd); break;
case CmdRemoveImposter: addCommand = addCmd_removeImposter(cmd); break;
@ -2538,14 +2539,6 @@ bool TSShapeConstructor::ChangeSet::addCmd_renameNode(const TSShapeConstructor::
Command& cmd = mCommands[index];
switch (cmd.type)
{
case CmdAddNode:
if (namesEqual(cmd.argv[0], newCmd.argv[0]))
{
cmd.argv[0] = newCmd.argv[1]; // Replace initial name argument
return false;
}
break;
case CmdRenameNode:
if (namesEqual(cmd.argv[1], newCmd.argv[0]))
{
@ -3302,6 +3295,28 @@ bool TSShapeConstructor::ChangeSet::addCmd_renameDetailLevel(const TSShapeConstr
return true;
}
bool TSShapeConstructor::ChangeSet::addCmd_addDetailLevel(const TSShapeConstructor::ChangeSet::Command& newCmd)
{
const char* targ = newCmd.argv[2];
for (S32 index = mCommands.size() - 1; index >= 0; index--)
{
Command& cmd = mCommands[index];
switch (cmd.type)
{
case CmdAddCollisionDetail:
if (!dStricmp(targ, cmd.argv[2]))
{
mCommands.erase(index);
}
break;
default:
break;
}
}
return true;
}
bool TSShapeConstructor::ChangeSet::addCmd_removeDetailLevel(const TSShapeConstructor::ChangeSet::Command& newCmd)
{
// Remove any previous command that references the detail, but stop if a mesh
@ -3480,3 +3495,32 @@ void TSShapeConstructor::onActionPerformed()
}
}
}
void TSShapeConstructor::cleanTargetNodes(const char* detail, const char* target)
{
for (S32 index = mChangeSet.mCommands.size() - 1; index >= 0; index--)
{
ChangeSet::Command& cmd = mChangeSet.mCommands[index];
switch (cmd.type)
{
case ChangeSet::eCommandType::CmdAddNode:
// node name starts with col
if (dStrStartsWith(cmd.argv[0], "Col"))
{
// node has the same detail and same target
if (!dStricmp(detail, cmd.argv[1]) && !dStricmp(target, cmd.argv[4]))
{
// now remove it from shape
mShape->removeMesh(cmd.argv[0]);
mShape->removeNode(cmd.argv[0]);
// erase the command
mChangeSet.mCommands.erase(index);
}
}
break;
default:
break;
}
}
}

View file

@ -100,7 +100,7 @@ public:
{
eCommandType type; // Command type
StringTableEntry name; // Command name
static const U32 MAX_ARGS = 10;
static const U32 MAX_ARGS = 11;
String argv[MAX_ARGS]; // Command arguments
S32 argc; // Number of arguments
Command() : type(CmdInvalid), name(0), argc(0) { }
@ -142,6 +142,7 @@ public:
bool addCmd_setBounds(const Command& newCmd);
bool addCmd_renameDetailLevel(const Command& newCmd);
bool addCmd_addDetailLevel(const Command& newCmd);
bool addCmd_removeDetailLevel(const Command& newCmd);
bool addCmd_setDetailSize(const Command& newCmd);
bool addCmd_addImposter(const Command& newCmd);
@ -253,6 +254,7 @@ public:
/// @name Nodes
///@{
void cleanTargetNodes(const char* detail, const char* target);
S32 getNodeCount();
S32 getNodeIndex(const char* name);
const char* getNodeName(S32 index);
@ -265,7 +267,7 @@ public:
TransformF getNodeTransform(const char* name, bool isWorld = false);
bool setNodeTransform(const char* name, TransformF txfm, bool isWorld = false);
bool renameNode(const char* oldName, const char* newName);
bool addNode(const char* name, const char* parentName, TransformF txfm = TransformF::Identity, bool isWorld = false);
bool addNode(const char* name, const char* parentName, TransformF txfm = TransformF::Identity, bool isWorld = false, const char* target = "");
bool removeNode(const char* name);
///@}
@ -315,7 +317,7 @@ public:
const char* getImposterSettings(S32 index);
S32 addImposter(S32 size, S32 equatorSteps, S32 polarSteps, S32 dl, S32 dim, bool includePoles, F32 polarAngle);
bool removeImposter();
bool addCollisionDetail(S32 size, const char* type, const char* target, S32 depth = 4, F32 merge = 30.0f, F32 concavity = 30.0f, S32 maxVerts = 32, F32 boxMaxError = 0, F32 sphereMaxError = 0, F32 capsuleMaxError = 0);
bool addCollisionDetail(S32 size, const char* type, const char* target, S32 depth = 4, F32 minPercentage = 10.0f, S32 maxHull = 30, S32 maxVerts = 32, F32 boxMaxError = 0, F32 sphereMaxError = 0, F32 capsuleMaxError = 0, const char* fillMode = "flood fill");
///@}
/// @name Sequences

View file

@ -2935,11 +2935,18 @@ function ShapeEdColWindow::onWake( %this )
%this-->colType.add( "18-DOP" );
%this-->colType.add( "26-DOP" );
%this-->colType.add( "Convex Hulls" );
%this-->fillMode.clear();
%this-->fillMode.add("Flood fill");
%this-->fillMode.add("Surface only");
%this-->fillMode.add("Raycast Fill");
%this-->fillMode.setSelected( %this-->fillMode.findText( "Flood fill" ), false );
}
function ShapeEdColWindow::update_onShapeSelectionChanged( %this )
{
%this.lastColSettings = "" TAB "Bounds";
%this.lastColSettings = "" TAB "Bounds" TAB "Flood fill";
// Initialise collision mesh target list
%this-->colTarget.clear();
@ -2979,6 +2986,8 @@ function ShapeEdColWindow::update_onCollisionChanged( %this )
%this-->hullMaxSphereErrorText.setText( mFloor( %this-->hullMaxSphereError.getValue() ) );
%this-->hullMaxCapsuleError.setValue( getField( %colData, 8 ) );
%this-->hullMaxCapsuleErrorText.setText( mFloor( %this-->hullMaxCapsuleError.getValue() ) );
%fillModeID = %this-->fillMode.findText( getField( %colData, 9 ) );
%this-->fillMode.setSelected( %fillModeID, false );
}
else
{
@ -3007,6 +3016,7 @@ function ShapeEdColWindow::editCollisionOK( %this )
{
%type = %this-->colType.getText();
%target = %this-->colTarget.getText();
%fillMode = %this-->fillMode.getText();
%depth = %this-->hullDepth.getValue();
%merge = %this-->hullMergeThreshold.getValue();
%concavity = %this-->hullConcaveThreshold.getValue();
@ -3015,7 +3025,7 @@ function ShapeEdColWindow::editCollisionOK( %this )
%maxSphere = %this-->hullMaxSphereError.getValue();
%maxCapsule = %this-->hullMaxCapsuleError.getValue();
ShapeEditor.doEditCollision( %type, %target, %depth, %merge, %concavity, %maxVerts,
ShapeEditor.doEditCollision( %type, %target, %fillMode, %depth, %merge, %concavity, %maxVerts,
%maxBox, %maxSphere, %maxCapsule );
}

View file

@ -1046,7 +1046,7 @@ function ActionAddMeshFromFile::undo( %this )
//------------------------------------------------------------------------------
// Add/edit collision geometry
function ShapeEditor::doEditCollision( %this, %type, %target, %depth, %merge, %concavity,
function ShapeEditor::doEditCollision( %this, %type, %target, %fillMode, %depth, %merge, %concavity,
%maxVerts, %boxMax, %sphereMax, %capsuleMax )
{
%colData = ShapeEdColWindow.lastColSettings;
@ -1062,6 +1062,7 @@ function ShapeEditor::doEditCollision( %this, %type, %target, %depth, %merge, %c
%action.oldBoxMax = getField( %colData, 6 );
%action.oldSphereMax = getField( %colData, 7 );
%action.oldCapsuleMax = getField( %colData, 8 );
%action.oldFillMode = getField(%colData, 9);
%action.newType = %type;
%action.newTarget = %target;
@ -1072,50 +1073,33 @@ function ShapeEditor::doEditCollision( %this, %type, %target, %depth, %merge, %c
%action.newBoxMax = %boxMax;
%action.newSphereMax = %sphereMax;
%action.newCapsuleMax = %capsuleMax;
%action.newFillMode = %fillMode;
%this.doAction( %action );
}
function ActionEditCollision::updateCollision( %this, %type, %target, %depth, %merge, %concavity,
function ActionEditCollision::updateCollision( %this, %type, %target, %fillMode, %depth, %merge, %concavity,
%maxVerts, %boxMax, %sphereMax, %capsuleMax )
{
%colDetailSize = -1;
%colNode = "Col" @ %colDetailSize;
%colData = ShapeEdColWindow.lastColSettings;
%oldTarget = getField( %colData, 1 );
// TreeView items are case sensitive, but TSShape names are not, so fixup case
// if needed
%index = ShapeEditor.shape.getNodeIndex( %colNode );
if ( %index != -1 )
%colNode = ShapeEditor.shape.getNodeName( %index );
// First remove the old detail and collision nodes
%meshList = ShapeEditor.getDetailMeshList( %colDetailSize );
%meshCount = getFieldCount( %meshList );
if ( %meshCount > 0 )
{
ShapeEditor.shape.removeDetailLevel( %colDetailSize );
for ( %i = 0; %i < %meshCount; %i++ )
ShapeEdPropWindow.update_onMeshRemoved( getField( %meshList, %i ) );
}
%nodeList = ShapeEditor.getNodeNames( %colNode, "" );
%nodeCount = getFieldCount( %nodeList );
if ( %nodeCount > 0 )
{
for ( %i = 0; %i < %nodeCount; %i++ )
ShapeEditor.shape.removeNode( getField( %nodeList, %i ) );
ShapeEdPropWindow.update_onNodeRemoved( %nodeList, %nodeCount );
}
// Add the new node and geometry
if ( %type $= "" )
return;
if ( !ShapeEditor.shape.addCollisionDetail( %colDetailSize, %type, %target,
%depth, %merge, %concavity, %maxVerts,
%boxMax, %sphereMax, %capsuleMax ) )
%boxMax, %sphereMax, %capsuleMax, %fillMode) )
return false;
// Update UI
%meshList = ShapeEditor.getDetailMeshList( %colDetailSize );
ShapeEdPropWindow.update_onNodeAdded( %colNode, ShapeEditor.shape.getNodeCount() ); // will also add child nodes
@ -1124,7 +1108,7 @@ function ActionEditCollision::updateCollision( %this, %type, %target, %depth, %m
ShapeEdPropWindow.update_onMeshAdded( getField( %meshList, %i ) );
ShapeEdColWindow.lastColSettings = %type TAB %target TAB %depth TAB %merge TAB
%concavity TAB %maxVerts TAB %boxMax TAB %sphereMax TAB %capsuleMax;
%concavity TAB %maxVerts TAB %boxMax TAB %sphereMax TAB %capsuleMax TAB %fillMode ;
ShapeEdColWindow.update_onCollisionChanged();
return true;
@ -1133,7 +1117,7 @@ function ActionEditCollision::updateCollision( %this, %type, %target, %depth, %m
function ActionEditCollision::doit( %this )
{
ShapeEdWaitGui.show( "Generating collision geometry..." );
%success = %this.updateCollision( %this.newType, %this.newTarget, %this.newDepth, %this.newMerge,
%success = %this.updateCollision( %this.newType, %this.newTarget, %this.newFillMode, %this.newDepth, %this.newMerge,
%this.newConcavity, %this.newMaxVerts, %this.newBoxMax,
%this.newSphereMax, %this.newCapsuleMax );
ShapeEdWaitGui.hide();
@ -1146,7 +1130,7 @@ function ActionEditCollision::undo( %this )
Parent::undo( %this );
ShapeEdWaitGui.show( "Generating collision geometry..." );
%this.updateCollision( %this.oldType, %this.oldTarget, %this.oldDepth, %this.oldMerge,
%this.updateCollision( %this.oldType, %this.oldTarget, %this.oldFillMode, %this.oldDepth, %this.oldMerge,
%this.oldConcavity, %this.oldMaxVerts, %this.oldBoxMax,
%this.oldSphereMax, %this.oldCapsuleMax );
ShapeEdWaitGui.hide();