mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-03 11:35:11 +00:00
Merge pull request #1281 from marauder2k9-torque/expanded-options-for-vhacd
Expanded options for vhacd
This commit is contained in:
commit
b7c04c5734
9 changed files with 806 additions and 1368 deletions
1
.github/workflows/build-linux-gcc.yml
vendored
1
.github/workflows/build-linux-gcc.yml
vendored
|
|
@ -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'
|
||||
|
|
|
|||
1
.github/workflows/build-macos-clang.yml
vendored
1
.github/workflows/build-macos-clang.yml
vendored
|
|
@ -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'
|
||||
|
|
|
|||
1
.github/workflows/build-windows-msvc.yml
vendored
1
.github/workflows/build-windows-msvc.yml
vendored
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue