Merge pull request #1528 from marauder2k9-torque/Recast-Update

Recast refactor
This commit is contained in:
Brian Roberts 2025-08-04 08:50:11 -05:00 committed by GitHub
commit c7d966c0de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 3812 additions and 1639 deletions

View file

@ -168,11 +168,20 @@ bool AIController::getAIMove(Move* movePtr)
{
obj = getAIInfo()->mObj;
}
Point3F start = obj->getPosition();
Point3F end = start;
start.z = obj->getBoxCenter().z;
end.z -= mControllerData->mHeightTolerance;
obj->disableCollision();
// Only repath if not already adjusted and on risky ground
RayInfo info;
if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, mControllerData->mHeightTolerance), StaticShapeObjectType, &info))
if (obj->getContainer()->castRay(start, end, StaticShapeObjectType, &info))
{
getNav()->repath();
}
obj->enableCollision();
getGoal()->mInRange = false;
}
if (getGoal()->getDist() < mControllerData->mFollowTolerance )
@ -530,11 +539,15 @@ AIControllerData::AIControllerData()
mAttackRadius = 2.0f;
mMoveStuckTolerance = 0.01f;
mMoveStuckTestDelay = 30;
mHeightTolerance = 0.001f;
mHeightTolerance = 0.1f;
mFollowTolerance = 1.0f;
#ifdef TORQUE_NAVIGATION_ENABLED
mLinkTypes = LinkData(AllFlags);
mFilter.setIncludeFlags(mLinkTypes.getFlags());
mFilter.setExcludeFlags(0);
mAreaCosts.setSize(PolyAreas::NumAreas);
mAreaCosts.fill(1.0f);
mNavSize = AINavigation::Regular;
mFlocking.mChance = 90;
mFlocking.mMin = 1.0f;
@ -560,6 +573,8 @@ AIControllerData::AIControllerData(const AIControllerData& other, bool temp_clon
#ifdef TORQUE_NAVIGATION_ENABLED
mLinkTypes = other.mLinkTypes;
mFilter = other.mFilter;
mAreaCosts = other.mAreaCosts;
mNavSize = other.mNavSize;
mFlocking.mChance = other.mFlocking.mChance;
mFlocking.mMin = other.mFlocking.mMin;
@ -629,6 +644,8 @@ void AIControllerData::initPersistFields()
addFieldV("FlockSideStep", TypeRangedF32, Offset(mFlocking.mSideStep, AIControllerData), &CommonValidators::PositiveFloat,
"@brief Distance from destination before we stop moving out of the way.");
addField("areaCosts", TypeF32Vector, Offset(mAreaCosts, AIControllerData),
"Vector of costs for each PolyArea.");
addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, AIControllerData),
"Allow the character to walk on dry land.");
addField("allowJump", TypeBool, Offset(mLinkTypes.jump, AIControllerData),
@ -662,6 +679,10 @@ void AIControllerData::packData(BitStream* stream)
#ifdef TORQUE_NAVIGATION_ENABLED
//enums
stream->write(mAreaCosts.size());
for (U32 i = 0; i < mAreaCosts.size(); i++) {
stream->write(mAreaCosts[i]);
}
stream->write(mLinkTypes.getFlags());
stream->write((U32)mNavSize);
// end enums
@ -684,10 +705,23 @@ void AIControllerData::unpackData(BitStream* stream)
stream->read(&mFollowTolerance);
#ifdef TORQUE_NAVIGATION_ENABLED
U32 num;
stream->read(&num);
mAreaCosts.setSize(num);
for (U32 i = 0; i < num; i++)
{
stream->read(&mAreaCosts[i]);
}
//enums
U16 linkFlags;
stream->read(&linkFlags);
mLinkTypes = LinkData(linkFlags);
mFilter.setIncludeFlags(mLinkTypes.getFlags());
mFilter.setExcludeFlags(mLinkTypes.getExcludeFlags());
for (U32 i = 0; i < PolyAreas::NumAreas; i++)
{
mFilter.setAreaCost((PolyAreas)i, mAreaCosts[i]);
}
U32 navSize;
stream->read(&navSize);
mNavSize = (AINavigation::NavSize)(navSize);

View file

@ -168,6 +168,8 @@ public:
/// Types of link we can use.
LinkData mLinkTypes;
dtQueryFilter mFilter;
Vector<F32> mAreaCosts;
AINavigation::NavSize mNavSize;
#endif
Delegate<void(AIController* obj, Point3F location, Move* movePtr)> resolveYawPtr;

View file

@ -80,7 +80,7 @@ bool AIController::findCover(const Point3F& from, F32 radius)
if (s.point)
{
// Calling setPathDestination clears cover...
bool foundPath = getNav()->setPathDestination(s.point->getPosition());
bool foundPath = getNav()->setPathDestination(s.point->getPosition(), true);
setCover(s.point);
s.point->setOccupied(true);
return foundPath;

View file

@ -23,7 +23,7 @@
#include "AIController.h"
#include "T3D/shapeBase.h"
static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType | AIObjectType;
static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType;
AINavigation::AINavigation(AIController* controller)
{
@ -77,6 +77,7 @@ bool AINavigation::setPathDestination(const Point3F& pos, bool replace)
path->mAlwaysRender = true;
path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes;
path->mXray = true;
path->mFilter = getCtrl()->mControllerData->mFilter;
// Paths plan automatically upon being registered.
if (!path->registerObject())
{
@ -338,7 +339,11 @@ void AINavigation::repath()
if (mPathData.path.isNull() || !mPathData.owned)
return;
if (mRandI(0, 100) < getCtrl()->mControllerData->mFlocking.mChance && flock())
if (avoidObstacles())
{
mPathData.path->mTo = mMoveDestination;
}
else if (mRandI(0, 100) < getCtrl()->mControllerData->mFlocking.mChance && flock())
{
mPathData.path->mTo = mMoveDestination;
}
@ -379,6 +384,60 @@ void AINavigation::clearPath()
mPathData = PathData();
}
bool AINavigation::avoidObstacles()
{
SimObjectPtr<SceneObject> obj = getCtrl()->getAIInfo()->mObj;
obj->disableCollision();
Point3F pos = obj->getBoxCenter();
VectorF forward = obj->getTransform().getForwardVector();
forward.normalizeSafe();
// Generate forward-left and forward-right by rotating forward vector
VectorF right = mCross(forward, Point3F(0, 0, 1));
VectorF leftDir = forward + right * -0.5f; // front-left
VectorF rightDir = forward + right * 0.5f; // front-right
leftDir.normalizeSafe();
rightDir.normalizeSafe();
F32 rayLength = obj->getVelocity().lenSquared() * TickSec * 2 + getCtrl()->getAIInfo()->mRadius;
Point3F directions[3] = {
forward,
leftDir,
rightDir
};
bool hit[3] = { false, false, false };
RayInfo info;
for (int i = 0; i < 3; ++i)
{
Point3F end = pos + directions[i] * rayLength;
if (obj->getContainer()->castRay(pos, end, sAILoSMask, &info))
{
hit[i] = true;
}
}
Point3F avoidance = Point3F::Zero;
if (hit[0]) avoidance += right * 1.0f;
if (hit[1]) avoidance += right * 1.5f;
if (hit[2]) avoidance -= right * 1.5f;
if (!avoidance.isZero())
{
avoidance.normalizeSafe();
F32 clearance = getCtrl()->getAIInfo()->mRadius * 1.5f;
Point3F newDest = info.point + avoidance * rayLength;
mMoveDestination = newDest;
obj->enableCollision();
return true;
}
obj->enableCollision();
return false;
}
bool AINavigation::flock()
{
AIControllerData::Flocking flockingData = getCtrl()->mControllerData->mFlocking;
@ -386,9 +445,10 @@ bool AINavigation::flock()
obj->disableCollision();
Point3F pos = obj->getBoxCenter();
Point3F searchArea = Point3F(flockingData.mMin / 2, flockingData.mMax / 2, getCtrl()->getAIInfo()->mObj->getObjBox().maxExtents.z / 2);
F32 maxFlocksq = flockingData.mMax * flockingData.mMax;
Point3F searchArea = Point3F(maxFlocksq, maxFlocksq, getCtrl()->getAIInfo()->mObj->getObjBox().maxExtents.z / 2);
bool flocking = false;
U32 found = 0;
if (getCtrl()->getGoal())
@ -412,41 +472,35 @@ bool AINavigation::flock()
sql.mList.remove(obj);
Point3F avoidanceOffset = Point3F::Zero;
F32 avoidanceAmtSq = 0;
//avoid objects in the way
RayInfo info;
if (obj->getContainer()->castRay(pos, dest + Point3F(0, 0, obj->getObjBox().len_z() / 2), sAILoSMask, &info))
{
Point3F blockerOffset = (info.point - dest);
blockerOffset.z = 0;
avoidanceOffset += blockerOffset;
}
//avoid bots that are too close
for (U32 i = 0; i < sql.mList.size(); i++)
{
ShapeBase* other = dynamic_cast<ShapeBase*>(sql.mList[i]);
Point3F objectCenter = other->getBoxCenter();
F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin;
F32 sumMinRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin;
F32 separation = getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius;
sumRad += separation;
separation += sumMinRad;
Point3F offset = (pos - objectCenter);
F32 offsetLensq = offset.lenSquared(); //square roots are expensive, so use squared val compares
if ((flockingData.mMin > 0) && (offsetLensq < (sumRad * sumRad)))
if ((flockingData.mMin > 0) && (offsetLensq < (sumMinRad * sumMinRad)))
{
other->disableCollision();
if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask, &info))
if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask | AIObjectType, &info))
{
found++;
offset.normalizeSafe();
offset *= sumRad + separation;
offset *= separation;
avoidanceOffset += offset; //accumulate total group, move away from that
avoidanceAmtSq += offsetLensq;
}
other->enableCollision();
}
}
//if we don't have to worry about bumping into one another (nothing found lower than minFLock), see about grouping up
if (found == 0)
{
@ -455,20 +509,20 @@ bool AINavigation::flock()
ShapeBase* other = static_cast<ShapeBase*>(sql.mList[i]);
Point3F objectCenter = other->getBoxCenter();
F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin;
F32 sumMaxRad = flockingData.mMax + other->getAIController()->mControllerData->mFlocking.mMax;
F32 separation = getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius;
sumRad += separation;
separation += sumMaxRad;
Point3F offset = (pos - objectCenter);
if ((flockingData.mMin > 0) && ((sumRad * sumRad) < (maxFlocksq)))
F32 offsetLensq = offset.lenSquared(); //square roots are expensive, so use squared val compares
if ((flockingData.mMax > 0) && (offsetLensq < (sumMaxRad * sumMaxRad)))
{
other->disableCollision();
if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask, &info))
if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask | AIObjectType, &info))
{
found++;
offset.normalizeSafe();
offset *= sumRad + separation;
avoidanceOffset -= offset; // subtract total group, move toward it
avoidanceAmtSq -= offsetLensq;
}
other->enableCollision();
}
@ -476,27 +530,36 @@ bool AINavigation::flock()
}
if (found > 0)
{
//ephasize the *side* portion of sidestep to better avoid clumps
if (avoidanceOffset.x < avoidanceOffset.y)
avoidanceOffset.x *= 2.0;
else
avoidanceOffset.y *= 2.0;
//add fuzz to sidestepping
avoidanceOffset.z = 0;
avoidanceOffset.x = (mRandF() * avoidanceOffset.x) * 0.5 + avoidanceOffset.x * 0.75;
avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75;
if (avoidanceOffset.lenSquared() < (maxFlocksq))
avoidanceOffset.normalizeSafe();
avoidanceOffset *= avoidanceAmtSq;
if ((avoidanceAmtSq) > flockingData.mMin * flockingData.mMin)
{
dest += avoidanceOffset;
dest = obj->getPosition()+avoidanceOffset;
}
//if we're not jumping...
if (mJump == None)
{
dest.z = obj->getPosition().z;
//make sure we don't run off a cliff
Point3F zlen(0, 0, getCtrl()->mControllerData->mHeightTolerance);
if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info))
{
if ((mMoveDestination - dest).len() > getCtrl()->mControllerData->mMoveTolerance)
{
mMoveDestination = dest;
flocking = true;
}
mMoveDestination = dest;
flocking = true;
}
}
}

View file

@ -98,6 +98,7 @@ struct AINavigation
/// Move to the specified node in the current path.
void moveToNode(S32 node);
bool avoidObstacles();
bool flock();
#endif
};

View file

@ -114,6 +114,10 @@ AIPlayer::AIPlayer()
mJump = None;
mNavSize = Regular;
mLinkTypes = LinkData(AllFlags);
mFilter.setIncludeFlags(mLinkTypes.getFlags());
mFilter.setExcludeFlags(0);
mAreaCosts.setSize(PolyAreas::NumAreas);
mAreaCosts.fill(1.0f);
#endif
mIsAiControlled = true;
@ -163,7 +167,8 @@ void AIPlayer::initPersistFields()
#ifdef TORQUE_NAVIGATION_ENABLED
addGroup("Pathfinding");
addField("areaCosts", TypeF32Vector, Offset(mAreaCosts, AIPlayer),
"Vector of costs for each PolyArea.");
addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, AIPlayer),
"Allow the character to walk on dry land.");
addField("allowJump", TypeBool, Offset(mLinkTypes.jump, AIPlayer),
@ -785,6 +790,7 @@ void AIPlayer::moveToNode(S32 node)
bool AIPlayer::setPathDestination(const Point3F &pos)
{
#ifdef TORQUE_NAVIGATION_ENABLED
// Pathfinding only happens on the server.
if(!isServerObject())
return false;
@ -799,6 +805,13 @@ bool AIPlayer::setPathDestination(const Point3F &pos)
return false;
}
mFilter.setIncludeFlags(mLinkTypes.getFlags());
mFilter.setExcludeFlags(mLinkTypes.getExcludeFlags());
for (U32 i = 0; i < PolyAreas::NumAreas; i++)
{
mFilter.setAreaCost((PolyAreas)i, mAreaCosts[i]);
}
// Create a new path.
NavPath *path = new NavPath();
@ -808,6 +821,7 @@ bool AIPlayer::setPathDestination(const Point3F &pos)
path->mFromSet = path->mToSet = true;
path->mAlwaysRender = true;
path->mLinkTypes = mLinkTypes;
path->mFilter = mFilter;
path->mXray = true;
// Paths plan automatically upon being registered.
if(!path->registerObject())
@ -839,6 +853,9 @@ bool AIPlayer::setPathDestination(const Point3F &pos)
path->deleteObject();
return false;
}
#else
setMoveDestination(pos, false);
#endif
}
DefineEngineMethod(AIPlayer, setPathDestination, bool, (Point3F goal),,

View file

@ -228,6 +228,8 @@ public:
/// Types of link we can use.
LinkData mLinkTypes;
dtQueryFilter mFilter;
Vector<F32> mAreaCosts;
/// @}
#endif // TORQUE_NAVIGATION_ENABLED

View file

@ -1486,69 +1486,73 @@ void GFXDrawUtil::_drawWireCapsule( const GFXStateBlockDesc &desc, const Point3F
void GFXDrawUtil::drawCone( const GFXStateBlockDesc &desc, const Point3F &basePnt, const Point3F &tipPnt, F32 baseRadius, const ColorI &color )
{
VectorF uvec = tipPnt - basePnt;
F32 height = uvec.len();
uvec.normalize();
MatrixF mat( true );
MathUtils::getMatrixFromUpVector( uvec, &mat );
mat.setPosition(basePnt);
VectorF dir = tipPnt - basePnt;
F32 height = dir.len();
dir.normalize();
Point3F scale( baseRadius, baseRadius, height );
mat.scale(scale);
MatrixF mat(true);
MathUtils::getMatrixFromUpVector(dir, &mat);
mat.setPosition(basePnt);
mat.scale(Point3F(baseRadius, baseRadius, height));
GFXTransformSaver saver;
mDevice->pushWorldMatrix();
mDevice->multWorld(mat);
S32 numPoints = sizeof(circlePoints)/sizeof(Point2F);
GFXVertexBufferHandle<GFXVertexPCT> verts(mDevice, numPoints * 3 + 2, GFXBufferTypeVolatile);
const S32 numPoints = sizeof(circlePoints) / sizeof(Point2F);
// Vertex index layout
const S32 baseCenterIdx = 0;
const S32 baseStartIdx = 1;
const S32 tipIdx = baseStartIdx + numPoints;
const S32 sideStartIdx = tipIdx + 1;
const S32 totalVerts = sideStartIdx + numPoints * 3;
GFXVertexBufferHandle<GFXVertexPCT> verts(mDevice, totalVerts, GFXBufferTypeVolatile);
verts.lock();
F32 sign = -1.f;
S32 indexDown = 0; //counting down from numPoints
S32 indexUp = 0; //counting up from 0
S32 index = 0; //circlePoints index for cap
for (S32 i = 0; i < numPoints + 1; i++)
// Base center vertex (at origin in local space)
verts[baseCenterIdx].point = Point3F(0, 0, 0);
verts[baseCenterIdx].color = color;
// Base circle vertices
for (S32 i = 0; i < numPoints; i++)
{
//Top cap
if (i != numPoints)
{
if (sign < 0)
index = indexDown;
else
index = indexUp;
verts[baseStartIdx + i].point = Point3F(circlePoints[i].x, circlePoints[i].y, 0);
verts[baseStartIdx + i].color = color;
}
verts[i].point = Point3F(circlePoints[index].x, circlePoints[index].y, 0);
verts[i].color = color;
// Tip vertex (pointing "up" in local Z)
verts[tipIdx].point = Point3F(0, 0, 1);
verts[tipIdx].color = color;
if (sign < 0)
indexUp += 1;
else
indexDown = numPoints - indexUp;
// Side triangles: one triangle per segment
for (S32 i = 0; i < numPoints; i++)
{
S32 triBase = sideStartIdx + i * 3;
// invert sign
sign *= -1.0f;
}
// Each triangle is (tip, base[i], base[(i+1)%numPoints])
verts[triBase + 0].point = verts[tipIdx].point;
verts[triBase + 1].point = verts[baseStartIdx + i].point;
verts[triBase + 2].point = verts[baseStartIdx + ((i + 1) % numPoints)].point;
//cone
S32 imod = i % numPoints;
S32 vertindex = 2 * i + numPoints;
verts[vertindex].point = Point3F(circlePoints[imod].x, circlePoints[imod].y, 0);
verts[vertindex].color = color;
verts[vertindex + 1].point = Point3F(0.0f, 0.0f, 1.0f);
verts[vertindex + 1].color = color;
verts[triBase + 0].color = color;
verts[triBase + 1].color = color;
verts[triBase + 2].color = color;
}
verts.unlock();
mDevice->setStateBlockByDesc( desc );
mDevice->setVertexBuffer( verts );
mDevice->setStateBlockByDesc(desc);
mDevice->setVertexBuffer(verts);
mDevice->setupGenericShaders();
mDevice->drawPrimitive(GFXTriangleStrip, 0, numPoints - 2);
mDevice->drawPrimitive(GFXTriangleStrip, numPoints, numPoints * 2);
// Draw base cap using triangle fan
mDevice->drawPrimitive(GFXTriangleList, baseCenterIdx, numPoints - 2);
// Draw sides using triangle list
mDevice->drawPrimitive(GFXTriangleList, sideStartIdx, numPoints);
mDevice->popWorldMatrix();
@ -1556,71 +1560,89 @@ void GFXDrawUtil::drawCone( const GFXStateBlockDesc &desc, const Point3F &basePn
void GFXDrawUtil::drawCylinder( const GFXStateBlockDesc &desc, const Point3F &basePnt, const Point3F &tipPnt, F32 radius, const ColorI &color )
{
VectorF uvec = tipPnt - basePnt;
F32 height = uvec.len();
uvec.normalize();
MatrixF mat( true );
MathUtils::getMatrixFromUpVector( uvec, &mat );
VectorF dir = tipPnt - basePnt;
F32 height = dir.len();
dir.normalize();
MatrixF mat(true);
MathUtils::getMatrixFromUpVector(dir, &mat);
mat.setPosition(basePnt);
mat.scale(Point3F(radius, radius, height));
Point3F scale( radius, radius, height * 2 );
mat.scale(scale);
GFXTransformSaver saver;
mDevice->pushWorldMatrix();
mDevice->multWorld(mat);
S32 numPoints = sizeof(circlePoints) / sizeof(Point2F);
GFXVertexBufferHandle<GFXVertexPCT> verts(mDevice, numPoints *4 + 2, GFXBufferTypeVolatile);
const S32 numPoints = sizeof(circlePoints) / sizeof(Point2F);
// Vertex index layout
const S32 baseCenterIdx = 0;
const S32 topCenterIdx = 1;
const S32 baseStartIdx = 2;
const S32 topStartIdx = baseStartIdx + numPoints;
const S32 sideStartIdx = topStartIdx + numPoints;
const S32 totalVerts = sideStartIdx + numPoints * 6;
GFXVertexBufferHandle<GFXVertexPCT> verts(mDevice, totalVerts, GFXBufferTypeVolatile);
verts.lock();
F32 sign = -1.f;
S32 indexDown = 0; //counting down from numPoints
S32 indexUp = 0; //counting up from 0
S32 index = 0; //circlePoints index for caps
for (S32 i = 0; i < numPoints + 1; i++)
// Base center
verts[baseCenterIdx].point = Point3F(0, 0, 0);
verts[baseCenterIdx].color = color;
// Top center
verts[topCenterIdx].point = Point3F(0, 0, 1);
verts[topCenterIdx].color = color;
// Base circle
for (S32 i = 0; i < numPoints; ++i)
{
//Top/Bottom cap
if (i != numPoints)
{
if (sign < 0)
index = indexDown;
else
index = indexUp;
verts[baseStartIdx + i].point = Point3F(circlePoints[i].x, circlePoints[i].y, 0);
verts[baseStartIdx + i].color = color;
}
verts[i].point = Point3F(circlePoints[index].x, circlePoints[index].y, 0);
verts[i].color = color;
verts[i + numPoints].point = Point3F(circlePoints[index].x, circlePoints[index].y, 0.5f);
verts[i + numPoints].color = color;
// Top circle
for (S32 i = 0; i < numPoints; ++i)
{
verts[topStartIdx + i].point = Point3F(circlePoints[i].x, circlePoints[i].y, 1.0f);
verts[topStartIdx + i].color = color;
}
if (sign < 0)
indexUp += 1;
else
indexDown = numPoints - indexUp;
// Side triangles
for (S32 i = 0; i < numPoints; ++i)
{
S32 next = (i + 1) % numPoints;
S32 idx = sideStartIdx + i * 6;
// invert sign
sign *= -1.0f;
}
// First triangle (base[i], base[next], top[i])
verts[idx + 0].point = verts[baseStartIdx + i].point;
verts[idx + 1].point = verts[baseStartIdx + next].point;
verts[idx + 2].point = verts[topStartIdx + i].point;
//cylinder
S32 imod = i % numPoints;
S32 vertindex = 2 * i + (numPoints * 2);
verts[vertindex].point = Point3F(circlePoints[imod].x, circlePoints[imod].y, 0);
verts[vertindex].color = color;
verts[vertindex + 1].point = Point3F(circlePoints[imod].x, circlePoints[imod].y, 0.5f);
verts[vertindex + 1].color = color;
// Second triangle (top[i], base[next], top[next])
verts[idx + 3].point = verts[topStartIdx + i].point;
verts[idx + 4].point = verts[baseStartIdx + next].point;
verts[idx + 5].point = verts[topStartIdx + next].point;
for (int j = 0; j < 6; ++j)
verts[idx + j].color = color;
}
verts.unlock();
mDevice->setStateBlockByDesc( desc );
mDevice->setVertexBuffer( verts );
mDevice->setStateBlockByDesc(desc);
mDevice->setVertexBuffer(verts);
mDevice->setupGenericShaders();
mDevice->drawPrimitive( GFXTriangleStrip, 0, numPoints-2 );
mDevice->drawPrimitive( GFXTriangleStrip, numPoints, numPoints - 2);
mDevice->drawPrimitive( GFXTriangleStrip, numPoints*2, numPoints * 2);
// Draw base cap
mDevice->drawPrimitive(GFXTriangleList, baseCenterIdx, numPoints - 2);
// Draw top cap
mDevice->drawPrimitive(GFXTriangleList, topCenterIdx, numPoints - 2);
// Draw sides (2 triangles per segment)
mDevice->drawPrimitive(GFXTriangleList, sideStartIdx, numPoints * 2);
mDevice->popWorldMatrix();
}

View file

@ -178,39 +178,59 @@ public:
U32 mSize = 0;
}_getBufferData;
void lock(const U32 size, U32 offsetAlign, U32 &outOffset, void* &outPtr)
void lock(const U32 size, U32 offsetAlign, U32& outOffset, void*& outPtr)
{
if( !size )
if (!size)
{
AssertFatal(0, "");
AssertFatal(0, "GLCircularVolatileBuffer::lock - size must be > 0");
outOffset = 0;
outPtr = NULL;
outPtr = nullptr;
return;
}
mLockManager.waitFirstRange( mBufferFreePos, (mBufferFreePos + size)-1 );
// Align free pos first (before wraparound check)
if (offsetAlign)
{
mBufferFreePos = ((mBufferFreePos + offsetAlign - 1) / offsetAlign) * offsetAlign;
}
if( mBufferFreePos + size > mBufferSize )
{
mUsedRanges.push_back( UsedRange( mBufferFreePos, mBufferSize-1 ) );
// If the size won't fit from current pos to end, wrap around
if (mBufferFreePos + size > mBufferSize)
{
// Protect the remaining space
if (mBufferFreePos < mBufferSize)
mUsedRanges.push_back(UsedRange(mBufferFreePos, mBufferSize - 1));
// Reset free pos
mBufferFreePos = 0;
}
// force offset buffer align
if( offsetAlign )
mBufferFreePos = ( (mBufferFreePos/offsetAlign) + 1 ) * offsetAlign;
// Realign after wrap
if (offsetAlign)
{
mBufferFreePos = ((mBufferFreePos + offsetAlign - 1) / offsetAlign) * offsetAlign;
}
// Now check for overlaps *after* wrapping
mLockManager.waitOverlapRanges(mBufferFreePos, mBufferFreePos + size - 1);
}
else
{
// Normal range wait
mLockManager.waitOverlapRanges(mBufferFreePos, mBufferFreePos + size - 1);
}
outOffset = mBufferFreePos;
if( GFXGL->mCapabilities.bufferStorage )
{
outPtr = (U8*)(mBufferPtr) + mBufferFreePos;
}
else if( GFXGL->glUseMap() )
if (GFXGL->mCapabilities.bufferStorage)
{
PRESERVE_BUFFER( mBinding );
outPtr = static_cast<U8*>(mBufferPtr) + mBufferFreePos;
}
else if (GFXGL->glUseMap())
{
PRESERVE_BUFFER(mBinding);
glBindBuffer(mBinding, mBufferName);
const GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
const GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT;
outPtr = glMapBufferRange(mBinding, outOffset, size, access);
}
else
@ -218,14 +238,13 @@ public:
_getBufferData.mOffset = outOffset;
_getBufferData.mSize = size;
outPtr = mFrameAllocator.lock( size );
}
outPtr = mFrameAllocator.lock(size);
}
//set new buffer pos
mBufferFreePos = mBufferFreePos + size;
mBufferFreePos += size;
//align 4bytes
mBufferFreePos = ( (mBufferFreePos/4) + 1 ) * 4;
mBufferFreePos = ((mBufferFreePos + 4 - 1) / 4) * 4;
}
void unlock()

View file

@ -0,0 +1,315 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include "ChunkyTriMesh.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct BoundsItem
{
float bmin[2];
float bmax[2];
int i;
};
static int compareItemX(const void* va, const void* vb)
{
const BoundsItem* a = (const BoundsItem*)va;
const BoundsItem* b = (const BoundsItem*)vb;
if (a->bmin[0] < b->bmin[0])
return -1;
if (a->bmin[0] > b->bmin[0])
return 1;
return 0;
}
static int compareItemY(const void* va, const void* vb)
{
const BoundsItem* a = (const BoundsItem*)va;
const BoundsItem* b = (const BoundsItem*)vb;
if (a->bmin[1] < b->bmin[1])
return -1;
if (a->bmin[1] > b->bmin[1])
return 1;
return 0;
}
static void calcExtends(const BoundsItem* items, const int /*nitems*/,
const int imin, const int imax,
float* bmin, float* bmax)
{
bmin[0] = items[imin].bmin[0];
bmin[1] = items[imin].bmin[1];
bmax[0] = items[imin].bmax[0];
bmax[1] = items[imin].bmax[1];
for (int i = imin+1; i < imax; ++i)
{
const BoundsItem& it = items[i];
if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0];
if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1];
if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0];
if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1];
}
}
inline int longestAxis(float x, float y)
{
return y > x ? 1 : 0;
}
static void subdivide(BoundsItem* items, int nitems, int imin, int imax, int trisPerChunk,
int& curNode, rcChunkyTriMeshNode* nodes, const int maxNodes,
int& curTri, int* outTris, const int* inTris)
{
int inum = imax - imin;
int icur = curNode;
if (curNode >= maxNodes)
return;
rcChunkyTriMeshNode& node = nodes[curNode++];
if (inum <= trisPerChunk)
{
// Leaf
calcExtends(items, nitems, imin, imax, node.bmin, node.bmax);
// Copy triangles.
node.i = curTri;
node.n = inum;
for (int i = imin; i < imax; ++i)
{
const int* src = &inTris[items[i].i*3];
int* dst = &outTris[curTri*3];
curTri++;
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
}
}
else
{
// Split
calcExtends(items, nitems, imin, imax, node.bmin, node.bmax);
int axis = longestAxis(node.bmax[0] - node.bmin[0],
node.bmax[1] - node.bmin[1]);
if (axis == 0)
{
// Sort along x-axis
qsort(items+imin, static_cast<size_t>(inum), sizeof(BoundsItem), compareItemX);
}
else if (axis == 1)
{
// Sort along y-axis
qsort(items+imin, static_cast<size_t>(inum), sizeof(BoundsItem), compareItemY);
}
int isplit = imin+inum/2;
// Left
subdivide(items, nitems, imin, isplit, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris);
// Right
subdivide(items, nitems, isplit, imax, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris);
int iescape = curNode - icur;
// Negative index means escape.
node.i = -iescape;
}
}
bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris,
int trisPerChunk, rcChunkyTriMesh* cm)
{
int nchunks = (ntris + trisPerChunk-1) / trisPerChunk;
cm->nodes = new rcChunkyTriMeshNode[nchunks*4];
if (!cm->nodes)
return false;
cm->tris = new int[ntris*3];
if (!cm->tris)
return false;
cm->ntris = ntris;
// Build tree
BoundsItem* items = new BoundsItem[ntris];
if (!items)
return false;
for (int i = 0; i < ntris; i++)
{
const int* t = &tris[i*3];
BoundsItem& it = items[i];
it.i = i;
// Calc triangle XZ bounds.
it.bmin[0] = it.bmax[0] = verts[t[0]*3+0];
it.bmin[1] = it.bmax[1] = verts[t[0]*3+2];
for (int j = 1; j < 3; ++j)
{
const float* v = &verts[t[j]*3];
if (v[0] < it.bmin[0]) it.bmin[0] = v[0];
if (v[2] < it.bmin[1]) it.bmin[1] = v[2];
if (v[0] > it.bmax[0]) it.bmax[0] = v[0];
if (v[2] > it.bmax[1]) it.bmax[1] = v[2];
}
}
int curTri = 0;
int curNode = 0;
subdivide(items, ntris, 0, ntris, trisPerChunk, curNode, cm->nodes, nchunks*4, curTri, cm->tris, tris);
delete [] items;
cm->nnodes = curNode;
// Calc max tris per node.
cm->maxTrisPerChunk = 0;
for (int i = 0; i < cm->nnodes; ++i)
{
rcChunkyTriMeshNode& node = cm->nodes[i];
const bool isLeaf = node.i >= 0;
if (!isLeaf) continue;
if (node.n > cm->maxTrisPerChunk)
cm->maxTrisPerChunk = node.n;
}
return true;
}
inline bool checkOverlapRect(const float amin[2], const float amax[2],
const float bmin[2], const float bmax[2])
{
bool overlap = true;
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
return overlap;
}
int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm,
float bmin[2], float bmax[2],
int* ids, const int maxIds)
{
// Traverse tree
int i = 0;
int n = 0;
while (i < cm->nnodes)
{
const rcChunkyTriMeshNode* node = &cm->nodes[i];
const bool overlap = checkOverlapRect(bmin, bmax, node->bmin, node->bmax);
const bool isLeafNode = node->i >= 0;
if (isLeafNode && overlap)
{
if (n < maxIds)
{
ids[n] = i;
n++;
}
}
if (overlap || isLeafNode)
i++;
else
{
const int escapeIndex = -node->i;
i += escapeIndex;
}
}
return n;
}
static bool checkOverlapSegment(const float p[2], const float q[2],
const float bmin[2], const float bmax[2])
{
static const float EPSILON = 1e-6f;
float tmin = 0;
float tmax = 1;
float d[2];
d[0] = q[0] - p[0];
d[1] = q[1] - p[1];
for (int i = 0; i < 2; i++)
{
if (fabsf(d[i]) < EPSILON)
{
// Ray is parallel to slab. No hit if origin not within slab
if (p[i] < bmin[i] || p[i] > bmax[i])
return false;
}
else
{
// Compute intersection t value of ray with near and far plane of slab
float ood = 1.0f / d[i];
float t1 = (bmin[i] - p[i]) * ood;
float t2 = (bmax[i] - p[i]) * ood;
if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; }
if (t1 > tmin) tmin = t1;
if (t2 < tmax) tmax = t2;
if (tmin > tmax) return false;
}
}
return true;
}
int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm,
float p[2], float q[2],
int* ids, const int maxIds)
{
// Traverse tree
int i = 0;
int n = 0;
while (i < cm->nnodes)
{
const rcChunkyTriMeshNode* node = &cm->nodes[i];
const bool overlap = checkOverlapSegment(p, q, node->bmin, node->bmax);
const bool isLeafNode = node->i >= 0;
if (isLeafNode && overlap)
{
if (n < maxIds)
{
ids[n] = i;
n++;
}
}
if (overlap || isLeafNode)
i++;
else
{
const int escapeIndex = -node->i;
i += escapeIndex;
}
}
return n;
}

View file

@ -0,0 +1,59 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef CHUNKYTRIMESH_H
#define CHUNKYTRIMESH_H
struct rcChunkyTriMeshNode
{
float bmin[2];
float bmax[2];
int i;
int n;
};
struct rcChunkyTriMesh
{
inline rcChunkyTriMesh() : nodes(0), nnodes(0), tris(0), ntris(0), maxTrisPerChunk(0) {}
inline ~rcChunkyTriMesh() { delete [] nodes; delete [] tris; }
rcChunkyTriMeshNode* nodes;
int nnodes;
int* tris;
int ntris;
int maxTrisPerChunk;
private:
// Explicitly disabled copy constructor and copy assignment operator.
rcChunkyTriMesh(const rcChunkyTriMesh&);
rcChunkyTriMesh& operator=(const rcChunkyTriMesh&);
};
/// Creates partitioned triangle mesh (AABB tree),
/// where each node contains at max trisPerChunk triangles.
bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris,
int trisPerChunk, rcChunkyTriMesh* cm);
/// Returns the chunk indices which overlap the input rectable.
int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, float bmin[2], float bmax[2], int* ids, const int maxIds);
/// Returns the chunk indices which overlap the input segment.
int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, float p[2], float q[2], int* ids, const int maxIds);
#endif // CHUNKYTRIMESH_H

View file

@ -31,6 +31,7 @@
#include "gfx/gfxDrawUtil.h"
#include "renderInstance/renderPassManager.h"
#include "console/engineAPI.h"
#include "T3D/gameBase/gameConnection.h"
extern bool gEditingMission;
@ -285,14 +286,22 @@ void CoverPoint::prepRenderImage(SceneRenderState *state)
void CoverPoint::render(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat)
{
initGFXResources();
if (!state->isDiffusePass()) return;
if(overrideMat)
GameConnection* conn = GameConnection::getConnectionToServer();
MatrixF camTrans;
conn->getControlCameraTransform(0, &camTrans);
if ((getPosition() - camTrans.getPosition()).lenSquared() > 2500) return; //50 unit clamp
if (overrideMat)
return;
initGFXResources();
if(smVertexBuffer[mSize].isNull())
return;
PROFILE_SCOPE(CoverPoint_Render);
// Set up a GFX debug event (this helps with debugging rendering events in external tools)

View file

@ -38,28 +38,64 @@
duDebugDrawTorque::duDebugDrawTorque()
{
VECTOR_SET_ASSOCIATION(mVertList);
VECTOR_SET_ASSOCIATION(mDrawCache);
mPrimType = 0;
mQuadsMode = false;
mVertCount = 0;
mGroup = 0;
mCurrColor = 0;
mOverrideColor = 0;
mOverride = false;
dMemset(&mStore, 0, sizeof(mStore));
mOverrideState = false;
}
duDebugDrawTorque::~duDebugDrawTorque()
{
clear();
}
void duDebugDrawTorque::depthMask(bool state)
{
mDesc.setZReadWrite(state, state);
if (mOverrideState)
return;
mDesc.setZReadWrite(state);
if (!state)
{
mDesc.setCullMode(GFXCullNone);
mDesc.setBlend(true);
}
else
{
mDesc.setCullMode(GFXCullCW);
mDesc.setBlend(false);
}
}
void duDebugDrawTorque::depthMask(bool state, bool isOverride)
{
depthMask(state);
mOverrideState = isOverride;
}
void duDebugDrawTorque::blend(bool blend)
{
mDesc.setBlend(true);
}
void duDebugDrawTorque::texture(bool state)
{
// need a checker texture?...... if(state is true) then set first slot to that texture.
}
unsigned int duDebugDrawTorque::areaToCol(unsigned int area)
{
switch (area)
{
// Ground (1) : light blue
case GroundArea: return duRGBA(0, 192, 255, 255);
// Water : blue
case WaterArea: return duRGBA(0, 0, 255, 255);
// Road : brown
case OffMeshArea: return duRGBA(50, 20, 12, 255);
// Unexpected : red
default: return duRGBA(255, 0, 0, 255);
}
}
/// Begin drawing primitives.
@ -67,27 +103,20 @@ void duDebugDrawTorque::texture(bool state)
/// @param size [in] size of a primitive, applies to point size and line width only.
void duDebugDrawTorque::begin(duDebugDrawPrimitives prim, float size)
{
mCurrColor = -1;
mQuadsMode = false;
if (!mVertList.empty())
mVertList.clear();
mVertCount = 0;
mPrimType = 0;
switch(prim)
{
case DU_DRAW_POINTS: mPrimType = GFXPointList; break;
case DU_DRAW_LINES: mPrimType = GFXLineList; break;
case DU_DRAW_TRIS: mPrimType = GFXTriangleList; break;
case DU_DRAW_QUADS: mPrimType = GFXTriangleList;
mQuadsMode = true; break;
}
mBuffers.push_back(Buffer(mPrimType));
mBuffers.last().group = mGroup;
mDesc.setCullMode(GFXCullNone);
mDesc.setBlend(true);
}
void duDebugDrawTorque::beginGroup(U32 group)
{
mGroup = group;
switch (prim)
{
case DU_DRAW_POINTS: mPrimType = DU_DRAW_POINTS; break;
case DU_DRAW_LINES: mPrimType = DU_DRAW_LINES; break;
case DU_DRAW_TRIS: mPrimType = DU_DRAW_TRIS; break;
case DU_DRAW_QUADS: mPrimType = DU_DRAW_QUADS; break;
}
}
/// Submit a vertex
@ -103,30 +132,7 @@ void duDebugDrawTorque::vertex(const float* pos, unsigned int color)
/// @param color [in] color of the verts.
void duDebugDrawTorque::vertex(const float x, const float y, const float z, unsigned int color)
{
if(mQuadsMode)
{
if(mVertCount == 3)
{
_vertex(x, -z, y, color);
_vertex(mStore[0][0], mStore[0][1], mStore[0][2], color);
_vertex(mStore[1][0], mStore[1][1], mStore[1][2], color);
_vertex(mStore[1][0], mStore[1][1], mStore[1][2], color);
_vertex(mStore[2][0], mStore[2][1], mStore[2][2], color);
_vertex(x, -z, y, color);
mVertCount = 0;
}
else
{
mStore[mVertCount][0] = x;
mStore[mVertCount][1] = -z;
mStore[mVertCount][2] = y;
mVertCount++;
}
}
else
{
_vertex(x, -z, y, color);
}
_vertex(x, -z, y, color);
}
/// Submit a vertex
@ -148,105 +154,325 @@ void duDebugDrawTorque::vertex(const float x, const float y, const float z, unsi
/// Push a vertex onto the buffer.
void duDebugDrawTorque::_vertex(const float x, const float y, const float z, unsigned int color)
{
// Use override color if we must.
//if(mOverride)
//color = mOverrideColor;
if(mCurrColor != color || !mBuffers.last().buffer.size())
{
U8 r, g, b, a;
// Convert color integer to components.
rcCol(color, r, g, b, a);
mBuffers.last().buffer.push_back(Instruction(r, g, b, a));
mCurrColor = color;
}
// Construct vertex data.
mBuffers.last().buffer.push_back(Instruction(x, y, z));
GFXVertexPCT vert;
vert.point.set(x, y, z);
U8 r, g, b, a;
// Convert color integer to components.
rcCol(color, r, g, b, a);
vert.color.set(r, g, b, a);
mVertList.push_back(vert);
}
/// End drawing primitives.
void duDebugDrawTorque::end()
{
}
if (mVertList.empty())
return;
void duDebugDrawTorque::overrideColor(unsigned int col)
{
mOverride = true;
mOverrideColor = col;
}
void duDebugDrawTorque::cancelOverride()
{
mOverride = false;
}
void duDebugDrawTorque::renderBuffer(Buffer &b)
{
PrimBuild::begin(b.primType, b.buffer.size());
Vector<Instruction> &buf = b.buffer;
for(U32 i = 0; i < buf.size(); i++)
const U32 maxVertsPerDraw = GFX_MAX_DYNAMIC_VERTS;
switch (mPrimType)
{
switch(buf[i].type)
case DU_DRAW_POINTS:
{
const U32 totalPoints = mVertList.size();
for (U32 p = 0; p < totalPoints;)
{
case Instruction::POINT:
PrimBuild::vertex3f(buf[i].data.point.x,
buf[i].data.point.y,
buf[i].data.point.z);
break;
const U32 pointsThisBatch = getMin(maxVertsPerDraw, totalPoints - p);
const U32 batchVerts = pointsThisBatch;
Box3F box;
box.minExtents.set(F32_MAX, F32_MAX, F32_MAX);
box.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX);
case Instruction::COLOR:
if(mOverride)
break;
PrimBuild::color4i(buf[i].data.color.r,
buf[i].data.color.g,
buf[i].data.color.b,
buf[i].data.color.a);
break;
default:
break;
GFXVertexBufferHandle<GFXVertexPCT> buffer;
buffer.set(GFX, batchVerts, GFXBufferTypeStatic);
GFXVertexPCT* verts = buffer.lock();
for (U32 i = 0; i < pointsThisBatch; ++i)
{
verts[i] = mVertList[p + i];
box.minExtents.setMin(verts[i].point);
box.maxExtents.setMax(verts[i].point);
}
buffer.unlock();
// --- Build index buffer
GFXPrimitiveBufferHandle pb;
pb.set(GFX, pointsThisBatch, pointsThisBatch, GFXBufferTypeStatic);
U16* indices = nullptr;
pb.lock(&indices);
for (U32 i = 0; i < pointsThisBatch; ++i)
{
indices[i] = i;
}
pb.unlock();
CachedDraw batch;
batch.primType = GFXPointList;
batch.buffer = buffer;
batch.vertexCount = batchVerts;
batch.primitiveBuffer = pb;
batch.primitiveCount = pointsThisBatch;
batch.state = mDesc;
batch.bounds = box;
mDrawCache.push_back(batch);
p += pointsThisBatch;
}
break;
}
PrimBuild::end();
case DU_DRAW_LINES:
{
AssertFatal(mVertList.size() % 2 == 0, "DU_DRAW_LINES given invalid vertex count.");
const U32 vertsPerLine = 2;
const U32 totalLines = mVertList.size() / vertsPerLine;
for (U32 l = 0; l < totalLines;)
{
const U32 linesThisBatch = getMin(maxVertsPerDraw / vertsPerLine, totalLines - l);
const U32 batchVerts = linesThisBatch * vertsPerLine;
Box3F box;
box.minExtents.set(F32_MAX, F32_MAX, F32_MAX);
box.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX);
GFXVertexBufferHandle<GFXVertexPCT> buffer;
buffer.set(GFX, batchVerts, GFXBufferTypeStatic);
GFXVertexPCT* verts = buffer.lock();
for (U32 i = 0; i < linesThisBatch * vertsPerLine; ++i)
{
verts[i] = mVertList[l * vertsPerLine + i];
box.minExtents.setMin(verts[i].point);
box.maxExtents.setMax(verts[i].point);
}
buffer.unlock();
// --- Build index buffer
GFXPrimitiveBufferHandle pb;
pb.set(GFX, linesThisBatch * 2, linesThisBatch, GFXBufferTypeStatic);
U16* indices = nullptr;
pb.lock(&indices);
for (U32 i = 0; i < linesThisBatch; ++i)
{
indices[i * 2 + 0] = i * 2;
indices[i * 2 + 1] = i * 2 + 1;
}
pb.unlock();
CachedDraw batch;
batch.primType = GFXLineList;
batch.buffer = buffer;
batch.vertexCount = batchVerts;
batch.primitiveBuffer = pb;
batch.primitiveCount = linesThisBatch;
batch.state = mDesc;
batch.bounds = box;
mDrawCache.push_back(batch);
l += linesThisBatch;
}
break;
}
case DU_DRAW_TRIS:
{
AssertFatal(mVertList.size() % 3 == 0, "DU_DRAW_TRIS given invalid vertex count.");
const U32 vertsPerTri = 3;
const U32 totalTris = mVertList.size() / vertsPerTri;
for (U32 t = 0; t < totalTris;)
{
const U32 trisThisBatch = getMin(maxVertsPerDraw / vertsPerTri, totalTris - t);
const U32 batchVerts = trisThisBatch * vertsPerTri;
Box3F box;
box.minExtents.set(F32_MAX, F32_MAX, F32_MAX);
box.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX);
GFXVertexBufferHandle<GFXVertexPCT> buffer;
buffer.set(GFX, batchVerts, GFXBufferTypeStatic);
GFXVertexPCT* verts = buffer.lock();
for (U32 i = 0; i < trisThisBatch * vertsPerTri; ++i)
{
verts[i] = mVertList[t * vertsPerTri + i];
box.minExtents.setMin(verts[i].point);
box.maxExtents.setMax(verts[i].point);
}
buffer.unlock();
// --- Build index buffer
GFXPrimitiveBufferHandle pb;
pb.set(GFX, trisThisBatch*3, trisThisBatch, GFXBufferTypeStatic);
U16* indices = nullptr;
pb.lock(&indices);
for (U32 i = 0; i < trisThisBatch; ++i)
{
indices[i * 3 + 0] = i * 3 + 0;
indices[i * 3 + 1] = i * 3 + 1;
indices[i * 3 + 2] = i * 3 + 2;
}
pb.unlock();
CachedDraw batch;
batch.primType = GFXTriangleList;
batch.buffer = buffer;
batch.vertexCount = batchVerts;
batch.primitiveBuffer = pb;
batch.primitiveCount = trisThisBatch;
batch.state = mDesc;
batch.bounds = box;
mDrawCache.push_back(batch);
t += trisThisBatch;
}
break;
}
case DU_DRAW_QUADS:
{
AssertFatal(mVertList.size() % 4 == 0, "DU_DRAW_QUADS given wrong number of vertices.");
const U32 vertsPerQuad = 4;
const U32 totalQuads = mVertList.size() / 4;
for (U32 q = 0; q < totalQuads;)
{
const U32 quadsThisBatch = getMin(maxVertsPerDraw / vertsPerQuad, totalQuads - q);
const U32 batchVerts = quadsThisBatch * vertsPerQuad;
const U32 batchIndices = quadsThisBatch * 6;
Box3F box;
box.minExtents.set(F32_MAX, F32_MAX, F32_MAX);
box.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX);
GFXVertexBufferHandle<GFXVertexPCT> buffer;
buffer.set(GFX, batchVerts, GFXBufferTypeStatic);
GFXVertexPCT* verts = buffer.lock();
U32 outIdx = 0;
for (U32 i = 0; i < quadsThisBatch; ++i)
{
const GFXVertexPCT& v0 = mVertList[(q + i) * 4 + 0];
const GFXVertexPCT& v1 = mVertList[(q + i) * 4 + 1];
const GFXVertexPCT& v2 = mVertList[(q + i) * 4 + 2];
const GFXVertexPCT& v3 = mVertList[(q + i) * 4 + 3];
verts[outIdx++] = v0;
verts[outIdx++] = v1;
verts[outIdx++] = v2;
verts[outIdx++] = v3;
}
buffer.unlock();
GFXPrimitiveBufferHandle pb;
pb.set(GFX, batchIndices, quadsThisBatch*2, GFXBufferTypeStatic);
U16* indices = nullptr;
pb.lock(&indices);
for (U32 i = 0; i < quadsThisBatch; ++i)
{
const U16 base = i * 4;
indices[i * 6 + 0] = base + 0;
indices[i * 6 + 1] = base + 1;
indices[i * 6 + 2] = base + 2;
indices[i * 6 + 3] = base + 0;
indices[i * 6 + 4] = base + 2;
indices[i * 6 + 5] = base + 3;
}
pb.unlock();
CachedDraw batch;
batch.primType = GFXTriangleList;
batch.buffer = buffer;
batch.vertexCount = batchVerts;
batch.primitiveBuffer = pb;
batch.primitiveCount = quadsThisBatch*2;
batch.state = mDesc;
mDrawCache.push_back(batch);
q += quadsThisBatch;
}
break;
}
}
mVertList.clear();
}
void duDebugDrawTorque::render()
void duDebugDrawTorque::clearCache()
{
GFXStateBlockRef sb = GFX->createStateBlock(mDesc);
GFX->setStateBlock(sb);
// Use override color for all rendering.
if(mOverride)
mDrawCache.clear();
}
void duDebugDrawTorque::render(SceneRenderState* state)
{
if (!state->isDiffusePass()) return;
const Frustum& frustum = state->getCameraFrustum();
for (U32 i = 0; i < mDrawCache.size(); ++i)
{
U8 r, g, b, a;
rcCol(mOverrideColor, r, g, b, a);
PrimBuild::color4i(r, g, b, a);
}
for(U32 b = 0; b < mBuffers.size(); b++)
{
renderBuffer(mBuffers[b]);
const CachedDraw& draw = mDrawCache[i];
if (!frustum.getBounds().isOverlapped(draw.bounds))
continue;
GFX->setPrimitiveBuffer(draw.primitiveBuffer);
GFX->setStateBlockByDesc(draw.state);
GFX->setupGenericShaders(GFXDevice::GSColor);
GFX->setVertexBuffer(draw.buffer);
GFX->drawIndexedPrimitive(
draw.primType,
0, // start vertex
0, // min vertex index
draw.vertexCount, // vertex count
0, // start index
draw.primitiveCount // primitive count
);
}
}
void duDebugDrawTorque::renderGroup(U32 group)
void duDebugDrawTorque::immediateRender()
{
GFXStateBlockRef sb = GFX->createStateBlock(mDesc);
GFX->setStateBlock(sb);
// Use override color for all rendering.
if(mOverride)
for (U32 i = 0; i < mDrawCache.size(); ++i)
{
U8 r, g, b, a;
rcCol(mOverrideColor, r, g, b, a);
PrimBuild::color4i(r, g, b, a);
}
for(U32 b = 0; b < mBuffers.size(); b++)
{
if(mBuffers[b].group == group)
renderBuffer(mBuffers[b]);
const CachedDraw& draw = mDrawCache[i];
GFX->setPrimitiveBuffer(draw.primitiveBuffer);
GFX->setStateBlockByDesc(draw.state);
GFX->setupGenericShaders(GFXDevice::GSColor);
GFX->setVertexBuffer(draw.buffer);
GFX->drawIndexedPrimitive(
draw.primType,
0, // start vertex
0, // min vertex index
draw.vertexCount, // vertex count
0, // start index
draw.primitiveCount // primitive count
);
}
}
void duDebugDrawTorque::clear()
{
for(U32 b = 0; b < mBuffers.size(); b++)
mBuffers[b].buffer.clear();
mBuffers.clear();
}

View file

@ -23,12 +23,37 @@
#ifndef _DU_DEBUG_DRAW_TORQUE_H_
#define _DU_DEBUG_DRAW_TORQUE_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#include <DebugDraw.h>
#include "gfx/gfxStateBlock.h"
/// @brief Implements the duDebugDraw interface in Torque.
class duDebugDrawTorque: public duDebugDraw {
#ifndef _GFXSTATEBLOCK_H_
#include "gfx/gfxStateBlock.h"
#endif
#ifndef _GFXVERTEXTYPES_H_
#include "gfx/gfxVertexTypes.h"
#endif
#ifndef _GFXVERTEXBUFFER_H_
#include "gfx/gfxVertexBuffer.h"
#endif
#ifndef _SCENERENDERSTATE_H_
#include "scene/sceneRenderState.h"
#endif
/**
* @class duDebugDrawTorque
* @brief Implements the duDebugDraw interface in Torque.
*
* Every debug draw from recast goes through a process of begin, add vertex and then end
* just like our primbuilder class, but we need to catch these vertices
* and add them to a GFXVertexBuffer as recast supports GL_QUADS which is now
* deprecated.
*/
class duDebugDrawTorque : public duDebugDraw {
public:
duDebugDrawTorque();
~duDebugDrawTorque();
@ -36,23 +61,20 @@ public:
/// Enable/disable Z read.
void depthMask(bool state) override;
/// Enable/disable texturing. Not used.
void texture(bool state) override;
/// <summary>
/// Enable/disable Z read and overrides any setting that will come from detour.
/// </summary>
/// <param name="state">Z read state.</param>
/// <param name="isOverride">Set to true to override any future changes.</param>
void depthMask(bool state, bool isOverride);
/// Special colour overwrite for when I get picky about the colours Mikko chose.
void overrideColor(unsigned int col);
/// Stop the colour override.
void cancelOverride();
void blend(bool blend);
/// Begin drawing primitives.
/// @param prim [in] primitive type to draw, one of rcDebugDrawPrimitives.
/// @param size [in] size of a primitive, applies to point size and line width only.
void begin(duDebugDrawPrimitives prim, float size = 1.0f) override;
/// All new buffers go into this group.
void beginGroup(U32 group);
/// Submit a vertex
/// @param pos [in] position of the verts.
/// @param color [in] color of the verts.
@ -66,92 +88,57 @@ public:
/// Submit a vertex
/// @param pos [in] position of the verts.
/// @param color [in] color of the verts.
/// @param uv [in] the uv coordinates.
void vertex(const float* pos, unsigned int color, const float* uv) override;
/// Submit a vertex
/// @param x,y,z [in] position of the verts.
/// @param color [in] color of the verts.
/// @param u [in] the u coordinate.
/// @param v [in] the v coordinate.
void vertex(const float x, const float y, const float z, unsigned int color, const float u, const float v) override;
/// Set a texture
/// @param state, use a texture in this draw, usually a checker texture.
void texture(bool state) override;
/// <summary>
/// Assigns a colour to an area type.
/// </summary>
/// <param name="area">The area type.</param>
/// <returns>The colour in recast format for the area.</returns>
unsigned int areaToCol(unsigned int area) override;
/// End drawing primitives.
void end() override;
/// Render buffered primitive.
void render();
void clearCache();
void render(SceneRenderState* state);
void immediateRender();
/// Render buffered primitives in a group.
void renderGroup(U32 group);
/// Delete buffered primitive.
void clear();
private:
struct CachedDraw {
GFXPrimitiveType primType;
GFXVertexBufferHandle<GFXVertexPCT> buffer;
GFXPrimitiveBufferHandle primitiveBuffer;
U32 vertexCount;
U32 primitiveCount;
GFXStateBlockDesc state;
Box3F bounds;
};
Vector<CachedDraw> mDrawCache;
GFXStateBlockDesc mDesc;
Vector<GFXVertexPCT> mVertList; // Our vertex list for setting up vertexBuffer in the End function.
GFXVertexBufferHandle<GFXVertexPCT> mVertexBuffer; // our vertex buffer for drawing.
U32 mPrimType;
bool mQuadsMode;
U32 mVertCount;
F32 mStore[3][3];
U32 mGroup;
struct Instruction {
// Contain either a point or a color command.
union {
struct {
U8 r, g, b, a;
} color;
struct {
float x, y, z;
} point;
U32 primType;
} data;
// Which type of data do we store?
enum {
COLOR,
POINT,
PRIMTYPE,
} type;
// Construct as color instruction.
Instruction(U8 r, U8 g, U8 b, U8 a) {
type = COLOR;
data.color.r = r;
data.color.g = g;
data.color.b = b;
data.color.a = a;
}
// Construct as point.
Instruction(float x, float y, float z) {
type = POINT;
data.point.x = x;
data.point.y = y;
data.point.z = z;
}
Instruction(U32 t = 0) {
type = PRIMTYPE;
data.primType = t;
}
};
struct Buffer {
U32 group;
Vector<Instruction> buffer;
GFXPrimitiveType primType;
Buffer(U32 type = 0) {
primType = (GFXPrimitiveType)type;
group = 0;
}
};
Vector<Buffer> mBuffers;
U32 mCurrColor;
U32 mOverrideColor;
bool mOverride;
bool mOverrideState;
void _vertex(const float x, const float y, const float z, unsigned int color);
void renderBuffer(Buffer &b);
};
#endif

View file

@ -38,6 +38,7 @@
#include "gui/worldEditor/undoActions.h"
#include "T3D/gameBase/gameConnection.h"
#include "T3D/AI/AIController.h"
#include "navigation/navMeshTool.h"
IMPLEMENT_CONOBJECT(GuiNavEditorCtrl);
@ -47,24 +48,11 @@ ConsoleDocClass(GuiNavEditorCtrl,
"@internal"
);
// Each of the mode names directly correlates with the Nav Editor's tool palette.
const String GuiNavEditorCtrl::mSelectMode = "SelectMode";
const String GuiNavEditorCtrl::mLinkMode = "LinkMode";
const String GuiNavEditorCtrl::mCoverMode = "CoverMode";
const String GuiNavEditorCtrl::mTileMode = "TileMode";
const String GuiNavEditorCtrl::mTestMode = "TestMode";
GuiNavEditorCtrl::GuiNavEditorCtrl()
{
mMode = mSelectMode;
mIsDirty = false;
mStartDragMousePoint = InvalidMousePoint;
mMesh = NULL;
mCurTile = mTile = -1;
mPlayer = mCurPlayer = NULL;
mSpawnClass = mSpawnDatablock = "";
mLinkStart = Point3F::Max;
mLink = mCurLink = -1;
}
GuiNavEditorCtrl::~GuiNavEditorCtrl()
@ -101,25 +89,21 @@ void GuiNavEditorCtrl::initPersistFields()
docsURL;
addField("isDirty", TypeBool, Offset(mIsDirty, GuiNavEditorCtrl));
addField("spawnClass", TypeRealString, Offset(mSpawnClass, GuiNavEditorCtrl),
"Class of object to spawn in test mode.");
addField("spawnDatablock", TypeRealString, Offset(mSpawnDatablock, GuiNavEditorCtrl),
"Datablock to give new objects in test mode.");
Parent::initPersistFields();
}
void GuiNavEditorCtrl::onSleep()
{
Parent::onSleep();
//mMode = mSelectMode;
}
void GuiNavEditorCtrl::selectMesh(NavMesh *mesh)
{
mesh->setSelected(true);
mMesh = mesh;
if (mTool)
mTool->setActiveNavMesh(mMesh);
}
DefineEngineMethod(GuiNavEditorCtrl, selectMesh, void, (S32 id),,
@ -141,125 +125,6 @@ DefineEngineMethod(GuiNavEditorCtrl, getMesh, S32, (),,
return object->getMeshId();
}
S32 GuiNavEditorCtrl::getPlayerId()
{
return mPlayer.isNull() ? 0 : mPlayer->getId();
}
DefineEngineMethod(GuiNavEditorCtrl, getPlayer, S32, (),,
"@brief Select a NavMesh object.")
{
return object->getPlayerId();
}
void GuiNavEditorCtrl::deselect()
{
if(!mMesh.isNull())
mMesh->setSelected(false);
mMesh = NULL;
mPlayer = mCurPlayer = NULL;
mCurTile = mTile = -1;
mLinkStart = Point3F::Max;
mLink = mCurLink = -1;
}
DefineEngineMethod(GuiNavEditorCtrl, deselect, void, (),,
"@brief Deselect whatever is currently selected in the editor.")
{
object->deselect();
}
void GuiNavEditorCtrl::deleteLink()
{
if(!mMesh.isNull() && mLink != -1)
{
mMesh->selectLink(mLink, false);
mMesh->deleteLink(mLink);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
}
DefineEngineMethod(GuiNavEditorCtrl, deleteLink, void, (),,
"@brief Delete the currently selected link.")
{
object->deleteLink();
}
void GuiNavEditorCtrl::setLinkFlags(const LinkData &d)
{
if(mMode == mLinkMode && !mMesh.isNull() && mLink != -1)
{
mMesh->setLinkFlags(mLink, d);
}
}
DefineEngineMethod(GuiNavEditorCtrl, setLinkFlags, void, (U32 flags),,
"@Brief Set jump and drop properties of the selected link.")
{
object->setLinkFlags(LinkData(flags));
}
void GuiNavEditorCtrl::buildTile()
{
if(!mMesh.isNull() && mTile != -1)
mMesh->buildTile(mTile);
}
DefineEngineMethod(GuiNavEditorCtrl, buildTile, void, (),,
"@brief Build the currently selected tile.")
{
object->buildTile();
}
void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos)
{
SceneObject *obj = (SceneObject*)Sim::spawnObject(mSpawnClass, mSpawnDatablock);
if(obj)
{
MatrixF mat(true);
mat.setPosition(pos);
obj->setTransform(mat);
SimObject* cleanup = Sim::findObject("MissionCleanup");
if(cleanup)
{
SimGroup* missionCleanup = dynamic_cast<SimGroup*>(cleanup);
missionCleanup->addObject(obj);
}
mPlayer = obj;
#ifdef TORQUE_NAVIGATION_ENABLED
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(obj);
if (asAIPlayer) //try direct
{
Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags()));
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(obj);
if (sbo->getAIController())
{
if (sbo->getAIController()->mControllerData)
Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags()));
}
else
{
#endif
Con::executef(this, "onPlayerSelected");
#ifdef TORQUE_NAVIGATION_ENABLED
}
}
#endif
}
}
DefineEngineMethod(GuiNavEditorCtrl, spawnPlayer, void, (),,
"@brief Spawn an AIPlayer at the centre of the screen.")
{
Point3F c;
if(object->get3DCentre(c))
object->spawnPlayer(c);
}
void GuiNavEditorCtrl::get3DCursor(GuiCursor *&cursor,
bool &visible,
const Gui3DMouseEvent &event_)
@ -315,148 +180,12 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
{
mGizmo->on3DMouseDown(event);
if(!isFirstResponder())
setFirstResponder();
if (mTool)
mTool->on3DMouseDown(event);
mouseLock();
// Construct a LineSegment from the camera position to 1000 meters away in
// the direction clicked.
// If that segment hits the terrain, truncate the ray to only be that length.
Point3F startPnt = event.pos;
Point3F endPnt = event.pos + event.vec * 1000.0f;
RayInfo ri;
U8 keys = Input::getModifierKeys();
bool shift = keys & SI_LSHIFT;
bool ctrl = keys & SI_LCTRL;
if(mMode == mLinkMode && !mMesh.isNull())
{
if(gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
U32 link = mMesh->getLink(ri.point);
if(link != -1)
{
if(mLink != -1)
mMesh->selectLink(mLink, false);
mMesh->selectLink(link, true, false);
mLink = link;
LinkData d = mMesh->getLinkFlags(mLink);
Con::executef(this, "onLinkSelected", Con::getIntArg(d.getFlags()));
}
else
{
if(mLink != -1)
{
mMesh->selectLink(mLink, false);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
else
{
if(mLinkStart != Point3F::Max)
{
mMesh->addLink(mLinkStart, ri.point);
if(!shift)
mLinkStart = Point3F::Max;
}
else
{
mLinkStart = ri.point;
}
}
}
}
else
{
mMesh->selectLink(mLink, false);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
}
if(mMode == mTileMode && !mMesh.isNull())
{
if(gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri))
{
mTile = mMesh->getTile(ri.point);
dd.clear();
mMesh->renderTileData(dd, mTile);
}
}
if(mMode == mTestMode)
{
// Spawn new character
if(ctrl)
{
if(gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
spawnPlayer(ri.point);
}
// Deselect character
else if(shift)
{
mPlayer = NULL;
Con::executef(this, "onPlayerDeselected");
}
// Select/move character
else
{
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
{
if(ri.object)
{
mPlayer = ri.object;
#ifdef TORQUE_NAVIGATION_ENABLED
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
if (asAIPlayer) //try direct
{
Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags()));
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
if (sbo->getAIController())
{
if (sbo->getAIController()->mControllerData)
Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags()));
}
else
{
#endif
Con::executef(this, "onPlayerSelected");
}
#ifdef TORQUE_NAVIGATION_ENABLED
}
}
#endif
}
else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
if (asAIPlayer) //try direct
{
#ifdef TORQUE_NAVIGATION_ENABLED
asAIPlayer->setPathDestination(ri.point);
#else
asAIPlayer->setMoveDestination(ri.point,false);
#endif
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
if (sbo->getAIController())
{
if (sbo->getAIController()->mControllerData)
sbo->getAIController()->getNav()->setPathDestination(ri.point, true);
}
}
}
}
}
return;
}
void GuiNavEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
@ -464,64 +193,18 @@ void GuiNavEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
// Keep the Gizmo up to date.
mGizmo->on3DMouseUp(event);
if (mTool)
mTool->on3DMouseUp(event);
mouseUnlock();
}
void GuiNavEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
{
//if(mSelRiver != NULL && mSelNode != -1)
//mGizmo->on3DMouseMove(event);
if (mTool)
mTool->on3DMouseMove(event);
Point3F startPnt = event.pos;
Point3F endPnt = event.pos + event.vec * 1000.0f;
RayInfo ri;
if(mMode == mLinkMode && !mMesh.isNull())
{
if(gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
U32 link = mMesh->getLink(ri.point);
if(link != -1)
{
if(link != mLink)
{
if(mCurLink != -1)
mMesh->selectLink(mCurLink, false);
mMesh->selectLink(link, true, true);
}
mCurLink = link;
}
else
{
if(mCurLink != mLink)
mMesh->selectLink(mCurLink, false);
mCurLink = -1;
}
}
else
{
mMesh->selectLink(mCurLink, false);
mCurLink = -1;
}
}
// Select a tile from our current NavMesh.
if(mMode == mTileMode && !mMesh.isNull())
{
if(gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
mCurTile = mMesh->getTile(ri.point);
else
mCurTile = -1;
}
if(mMode == mTestMode)
{
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
mCurPlayer = ri.object;
else
mCurPlayer = NULL;
}
return;
}
void GuiNavEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
@ -549,6 +232,11 @@ void GuiNavEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event)
void GuiNavEditorCtrl::updateGuiInfo()
{
if (mTool)
{
if (mTool->updateGuiInfo())
return;
}
}
void GuiNavEditorCtrl::onRender(Point2I offset, const RectI &updateRect)
@ -559,22 +247,6 @@ void GuiNavEditorCtrl::onRender(Point2I offset, const RectI &updateRect)
return;
}
static void renderBoxOutline(const Box3F &box, const ColorI &col)
{
if(box != Box3F::Invalid)
{
GFXStateBlockDesc desc;
desc.setCullMode(GFXCullNone);
desc.setFillModeSolid();
desc.setZReadWrite(true, false);
desc.setBlend(true);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 20));
desc.setFillModeWireframe();
desc.setBlend(false);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 255));
}
}
void GuiNavEditorCtrl::renderScene(const RectI & updateRect)
{
GFX->setStateBlock(mZDisableSB);
@ -592,45 +264,15 @@ void GuiNavEditorCtrl::renderScene(const RectI & updateRect)
Point3F camPos;
mat.getColumn(3,&camPos);
if(mMode == mLinkMode)
{
if(mLinkStart != Point3F::Max)
{
GFXStateBlockDesc desc;
desc.setBlend(false);
desc.setZReadWrite(true ,true);
MatrixF linkMat(true);
linkMat.setPosition(mLinkStart);
Point3F scale(0.8f, 0.8f, 0.8f);
GFX->getDrawUtil()->drawTransform(desc, linkMat, &scale);
}
}
if(mMode == mTileMode && !mMesh.isNull())
{
renderBoxOutline(mMesh->getTileBox(mCurTile), ColorI::BLUE);
renderBoxOutline(mMesh->getTileBox(mTile), ColorI::GREEN);
if(Con::getBoolVariable("$Nav::Editor::renderVoxels", false)) dd.renderGroup(0);
if(Con::getBoolVariable("$Nav::Editor::renderInput", false))
{
dd.depthMask(false);
dd.renderGroup(1);
dd.depthMask(true);
}
}
if(mMode == mTestMode)
{
if(!mCurPlayer.isNull())
renderBoxOutline(mCurPlayer->getWorldBox(), ColorI::BLUE);
if(!mPlayer.isNull())
renderBoxOutline(mPlayer->getWorldBox(), ColorI::GREEN);
}
if (mTool)
mTool->onRender3D();
duDebugDrawTorque d;
if(!mMesh.isNull())
if (!mMesh.isNull())
{
mMesh->renderLinks(d);
d.render();
d.immediateRender();
}
// Now draw all the 2d stuff!
GFX->setClipRect(updateRect);
@ -652,15 +294,6 @@ bool GuiNavEditorCtrl::getStaticPos(const Gui3DMouseEvent & event, Point3F &tpos
return hit;
}
void GuiNavEditorCtrl::setMode(String mode, bool sourceShortcut = false)
{
mMode = mode;
Con::executef(this, "onModeSet", mode);
if(sourceShortcut)
Con::executef(this, "paletteSync", mode);
}
void GuiNavEditorCtrl::submitUndo(const UTF8 *name)
{
// Grab the mission editor undo manager.
@ -691,13 +324,41 @@ void GuiNavEditorCtrl::_prepRenderImage(SceneManager* sceneGraph, const SceneRen
}*/
}
DefineEngineMethod(GuiNavEditorCtrl, getMode, const char*, (), , "")
void GuiNavEditorCtrl::setActiveTool(NavMeshTool* tool)
{
return object->getMode();
if (mTool)
{
mTool->onDeactivated();
}
mTool = tool;
if (mTool)
{
mTool->setActiveEditor(this);
mTool->setActiveNavMesh(mMesh);
mTool->onActivated(mLastEvent);
}
}
DefineEngineMethod(GuiNavEditorCtrl, setMode, void, (String mode),, "setMode(String mode)")
void GuiNavEditorCtrl::setDrawMode(S32 id)
{
object->setMode(mode);
if (mMesh.isNull())
return;
mMesh->setDrawMode((NavMesh::DrawMode)id);
}
DefineEngineMethod(GuiNavEditorCtrl, setDrawMode, void, (S32 id), ,
"@brief Deselect whatever is currently selected in the editor.")
{
object->setDrawMode(id);
}
DefineEngineMethod(GuiNavEditorCtrl, setActiveTool, void, (const char* toolName), , "( NavMeshTool tool )")
{
NavMeshTool* tool = dynamic_cast<NavMeshTool*>(Sim::findObject(toolName));
object->setActiveTool(tool);
}
#endif

View file

@ -34,6 +34,10 @@
#include "gui/worldEditor/gizmo.h"
#endif
//#ifndef _NAVMESH_TOOL_H_
//#include "navigation/navMeshTool.h"
//#endif
#include "navMesh.h"
#include "T3D/aiPlayer.h"
@ -41,6 +45,7 @@ struct ObjectRenderInst;
class SceneManager;
class SceneRenderState;
class BaseMatInstance;
class NavMeshTool;
class GuiNavEditorCtrl : public EditTSCtrl
{
@ -51,7 +56,6 @@ public:
static const String mSelectMode;
static const String mLinkMode;
static const String mCoverMode;
static const String mTileMode;
static const String mTestMode;
GuiNavEditorCtrl();
@ -96,26 +100,14 @@ public:
bool getStaticPos(const Gui3DMouseEvent & event, Point3F &tpos);
void setMode(String mode, bool sourceShortcut);
String getMode() { return mMode; }
void selectMesh(NavMesh *mesh);
void deselect();
S32 getMeshId();
S32 getPlayerId();
String mSpawnClass;
String mSpawnDatablock;
void deleteLink();
void setLinkFlags(const LinkData &d);
void buildTile();
void spawnPlayer(const Point3F &pos);
/// @}
void setActiveTool(NavMeshTool* tool);
void setDrawMode(S32 id);
protected:
@ -128,38 +120,21 @@ protected:
bool mIsDirty;
String mMode;
/// Currently-selected NavMesh.
SimObjectPtr<NavMesh> mMesh;
/// @name Link mode
/// @{
Point3F mLinkStart;
S32 mCurLink;
S32 mLink;
/// The active tool in used by the editor.
SimObjectPtr<NavMeshTool> mTool;
/// @}
/// @name Tile mode
/// @{
S32 mCurTile;
S32 mTile;
duDebugDrawTorque dd;
/// @}
/// @name Test mode
/// @{
SimObjectPtr<SceneObject> mPlayer;
SimObjectPtr<SceneObject> mCurPlayer;
/// @}
Gui3DMouseEvent mLastMouseEvent;
#define InvalidMousePoint Point2I(-100,-100)

File diff suppressed because it is too large Load diff

View file

@ -102,6 +102,7 @@ public:
U32 mMergeRegionArea;
F32 mTileSize;
U32 mMaxPolysPerTile;
duDebugDrawTorque mDbgDraw;
/// @}
/// @name Water
@ -112,6 +113,29 @@ public:
Impassable
};
enum DrawMode
{
DRAWMODE_NAVMESH,
DRAWMODE_NAVMESH_TRANS,
DRAWMODE_NAVMESH_BVTREE,
DRAWMODE_NAVMESH_NODES,
DRAWMODE_NAVMESH_PORTALS,
DRAWMODE_NAVMESH_INVIS,
DRAWMODE_MESH,
DRAWMODE_VOXELS,
DRAWMODE_VOXELS_WALKABLE,
DRAWMODE_COMPACT,
DRAWMODE_COMPACT_DISTANCE,
DRAWMODE_COMPACT_REGIONS,
DRAWMODE_REGION_CONNECTIONS,
DRAWMODE_RAW_CONTOURS,
DRAWMODE_BOTH_CONTOURS,
DRAWMODE_CONTOURS,
DRAWMODE_POLYMESH,
DRAWMODE_POLYMESH_DETAIL,
MAX_DRAWMODE
};
WaterMethod mWaterMethod;
/// @}
@ -127,7 +151,7 @@ public:
/// @{
/// Add an off-mesh link.
S32 addLink(const Point3F &from, const Point3F &to, U32 flags = 0);
S32 addLink(const Point3F &from, const Point3F &to, bool biDir, F32 rad, U32 flags = 0);
/// Get the ID of the off-mesh link near the point.
S32 getLink(const Point3F &pos);
@ -144,15 +168,27 @@ public:
/// Get the flags used by a link.
LinkData getLinkFlags(U32 idx);
bool getLinkDir(U32 idx);
F32 getLinkRadius(U32 idx);
void setLinkDir(U32 idx, bool biDir);
void setLinkRadius(U32 idx, F32 rad);
/// Set flags used by a link.
void setLinkFlags(U32 idx, const LinkData &d);
void setDrawMode(DrawMode mode) { m_drawMode = mode; setMaskBits(LoadFlag); }
/// Set the selected state of a link.
void selectLink(U32 idx, bool select, bool hover = true);
/// Delete the selected link.
void deleteLink(U32 idx);
dtNavMeshQuery* getNavMeshQuery() { return mQuery; }
/// @}
/// Should small characters use this mesh?
@ -230,6 +266,7 @@ public:
void prepRenderImage(SceneRenderState *state) override;
void render(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat);
void renderLinks(duDebugDraw &dd);
void renderSearch(duDebugDraw& dd);
void renderTileData(duDebugDrawTorque &dd, U32 tile);
bool mAlwaysRender;
@ -249,15 +286,13 @@ public:
void inspectPostApply() override;
void createNewFile();
protected:
dtNavMesh const* getNavMesh() { return nm; }
private:
/// Generates a navigation mesh for the collection of objects in this
/// mesh. Returns true if successful. Stores the created mesh in tnm.
bool generateMesh();
/// Builds the next tile in the dirty list.
void buildNextTile();
@ -275,56 +310,45 @@ private:
/// Recast min and max points.
F32 bmin[3], bmax[3];
/// Default constructor.
Tile() : box(Box3F::Invalid), x(0), y(0)
Tile() : box(Box3F::Invalid), x(0), y(0), chf(0), solid(0), cset(0), pmesh(0), dmesh(0), triareas(nullptr)
{
bmin[0] = bmin[1] = bmin[2] = bmax[0] = bmax[1] = bmax[2] = 0.0f;
}
/// Value constructor.
Tile(const Box3F &b, U32 _x, U32 _y, const F32 *min, const F32 *max)
: box(b), x(_x), y(_y)
: box(b), x(_x), y(_y), chf(0), solid(0), cset(0), pmesh(0), dmesh(0), triareas(nullptr)
{
rcVcopy(bmin, min);
rcVcopy(bmax, max);
}
};
/// Intermediate data for tile creation.
struct TileData {
RecastPolyList geom;
rcHeightfield *hf;
rcCompactHeightfield *chf;
rcContourSet *cs;
rcPolyMesh *pm;
rcPolyMeshDetail *pmd;
TileData()
~Tile()
{
hf = NULL;
chf = NULL;
cs = NULL;
pm = NULL;
pmd = NULL;
}
void freeAll()
{
geom.clear();
rcFreeHeightField(hf);
rcFreeCompactHeightfield(chf);
rcFreeContourSet(cs);
rcFreePolyMesh(pm);
rcFreePolyMeshDetail(pmd);
}
~TileData()
{
freeAll();
if (chf)
delete chf;
if (cset)
delete cset;
if (solid)
delete solid;
if (pmesh)
delete pmesh;
if (dmesh)
delete dmesh;
if (triareas)
delete[] triareas;
}
unsigned char* triareas;
rcCompactHeightfield* chf;
rcHeightfield* solid;
rcContourSet* cset;
rcPolyMesh* pmesh;
rcPolyMeshDetail* dmesh;
};
/// List of tiles.
Vector<Tile> mTiles;
/// List of tile intermediate data.
Vector<TileData> mTileData;
/// List of indices to the tile array which are dirty.
Vector<U32> mDirtyTiles;
@ -332,7 +356,7 @@ private:
void updateTiles(bool dirty = false);
/// Generates navmesh data for a single tile.
unsigned char *buildTileData(const Tile &tile, TileData &data, U32 &dataSize);
unsigned char *buildTileData(const Tile &tile, U32 &dataSize);
/// @}
@ -363,19 +387,9 @@ private:
/// @}
/// @name Intermediate data
/// @{
/// Config struct.
rcConfig cfg;
/// Updates our config from console members.
void updateConfig();
dtNavMesh *nm;
rcContext *ctx;
/// @}
dtNavMeshQuery* mQuery;
/// @name Cover
/// @{
@ -408,8 +422,6 @@ private:
/// @name Rendering
/// @{
duDebugDrawTorque mDbgDraw;
void renderToDrawer();
/// @}
@ -419,6 +431,21 @@ private:
/// Use this object to manage update events.
static SimObjectPtr<EventManager> smEventManager;
protected:
RecastPolyList* m_geo;
unsigned char* m_triareas;
rcHeightfield* m_solid;
rcCompactHeightfield* m_chf;
rcContourSet* m_cset;
rcPolyMesh* m_pmesh;
rcPolyMeshDetail* m_dmesh;
rcConfig m_cfg;
DrawMode m_drawMode;
U32 mWaterVertStart;
U32 mWaterTriStart;
void cleanup();
};
typedef NavMesh::WaterMethod NavMeshWaterMethod;

View file

@ -0,0 +1,40 @@
#include "platform/platform.h"
#include "navigation/navMeshTool.h"
#ifdef TORQUE_TOOLS
#include "util/undo.h"
#include "math/mMath.h"
#include "math/mathUtils.h"
IMPLEMENT_CONOBJECT(NavMeshTool);
ConsoleDocClass(NavMeshTool,
"@brief Base class for NavMesh Editor specific tools\n\n"
"Editor use only.\n\n"
"@internal"
);
void NavMeshTool::_submitUndo(UndoAction* action)
{
AssertFatal(action, "NavMeshTool::_submitUndo() - No undo action!");
// Grab the mission editor undo manager.
UndoManager* undoMan = NULL;
if (!Sim::findObject("EUndoManager", undoMan))
{
Con::errorf("NavMeshTool::_submitUndo() - EUndoManager not found!");
return;
}
undoMan->addAction(action);
}
NavMeshTool::NavMeshTool()
: mNavMesh(NULL)
{
}
NavMeshTool::~NavMeshTool()
{
}
#endif

View file

@ -0,0 +1,58 @@
#pragma once
#ifndef _NAVMESH_TOOL_H_
#define _NAVMESH_TOOL_H_
#ifdef TORQUE_TOOLS
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _NAVMESH_H_
#include "navigation/navMesh.h"
#endif
#ifndef _GUINAVEDITORCTRL_H_
#include "navigation/guiNavEditorCtrl.h"
#endif
class UndoAction;
class NavMeshTool : public SimObject
{
typedef SimObject Parent;
protected:
SimObjectPtr<NavMesh> mNavMesh;
SimObjectPtr<GuiNavEditorCtrl> mCurEditor;
void _submitUndo(UndoAction* action);
public:
NavMeshTool();
virtual ~NavMeshTool();
DECLARE_CONOBJECT(NavMeshTool);
virtual void setActiveNavMesh(NavMesh* nav_mesh) { mNavMesh = nav_mesh; }
virtual void setActiveEditor(GuiNavEditorCtrl* nav_editor) { mCurEditor = nav_editor; }
virtual void onActivated(const Gui3DMouseEvent& lastEvent) {}
virtual void onDeactivated() {}
virtual void on3DMouseDown(const Gui3DMouseEvent& evt) {}
virtual void on3DMouseUp(const Gui3DMouseEvent& evt) {}
virtual void on3DMouseMove(const Gui3DMouseEvent& evt) {}
virtual void on3DMouseDragged(const Gui3DMouseEvent& evt) {}
virtual void on3DMouseEnter(const Gui3DMouseEvent& evt) {}
virtual void on3DMouseLeave(const Gui3DMouseEvent& evt) {}
virtual bool onMouseWheel(const GuiEvent& evt) { return false; }
virtual void onRender3D() {}
virtual void onRender2D() {}
virtual void updateGizmo() {}
virtual bool updateGuiInfo() { return false; }
virtual void onUndoAction() {}
};
#endif
#endif // !_NAVMESH_TOOL_H_

View file

@ -0,0 +1,40 @@
#include "coverTool.h"
IMPLEMENT_CONOBJECT(CoverTool);
CoverTool::CoverTool()
{
}
void CoverTool::onActivated(const Gui3DMouseEvent& evt)
{
Con::executef(this, "onActivated");
}
void CoverTool::onDeactivated()
{
Con::executef(this, "onDeactivated");
}
void CoverTool::on3DMouseDown(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
}
void CoverTool::on3DMouseMove(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
}
void CoverTool::onRender3D()
{
if (mNavMesh.isNull())
return;
}
bool CoverTool::updateGuiInfo()
{
return false;
}

View file

@ -0,0 +1,33 @@
#ifndef _COVERTOOL_H_
#define _COVERTOOL_H_
#ifndef _NAVMESH_TOOL_H_
#include "navigation/navMeshTool.h"
#endif
#ifndef _NAVPATH_H_
#include "navigation/navPath.h"
#endif
class CoverTool : public NavMeshTool
{
typedef NavMeshTool Parent;
public:
DECLARE_CONOBJECT(CoverTool);
CoverTool();
virtual ~CoverTool() {}
void onActivated(const Gui3DMouseEvent& evt) override;
void onDeactivated() override;
void on3DMouseDown(const Gui3DMouseEvent& evt) override;
void on3DMouseMove(const Gui3DMouseEvent& evt) override;
void onRender3D() override;
bool updateGuiInfo() override;
};
#endif // !_COVERTOOL_H_

View file

@ -0,0 +1,121 @@
#include "navMeshSelectTool.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDrawUtil.h"
IMPLEMENT_CONOBJECT(NavMeshSelectTool);
static void renderBoxOutline(const Box3F& box, const ColorI& col)
{
if (box != Box3F::Invalid)
{
GFXStateBlockDesc desc;
desc.setCullMode(GFXCullNone);
desc.setFillModeSolid();
desc.setZReadWrite(true, false);
desc.setBlend(true);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 20));
desc.setFillModeWireframe();
desc.setBlend(false);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 255));
}
}
NavMeshSelectTool::NavMeshSelectTool()
{
mCurMesh = NULL;
}
void NavMeshSelectTool::onActivated(const Gui3DMouseEvent& evt)
{
Con::executef(this, "onActivated");
}
void NavMeshSelectTool::onDeactivated()
{
Con::executef(this, "onDeactivated");
}
void NavMeshSelectTool::on3DMouseDown(const Gui3DMouseEvent& evt)
{
if (mCurEditor.isNull())
return;
Point3F startPnt = evt.pos;
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
if (gServerContainer.collideBox(startPnt, endPnt, MarkerObjectType, &ri))
{
if (!ri.object)
return;
NavMesh* selNavMesh = dynamic_cast<NavMesh*>(ri.object);
if (selNavMesh)
{
mCurEditor->selectMesh(selNavMesh);
mSelMesh = selNavMesh;
Con::executef(this, "onNavMeshSelected");
return;
}
}
}
void NavMeshSelectTool::on3DMouseMove(const Gui3DMouseEvent& evt)
{
if (mCurEditor.isNull())
return;
Point3F startPnt = evt.pos;
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
if (gServerContainer.collideBox(startPnt, endPnt, MarkerObjectType, &ri))
{
NavMesh* selNavMesh = dynamic_cast<NavMesh*>(ri.object);
if (selNavMesh)
{
mCurMesh = selNavMesh;
}
else
{
mCurMesh = NULL;
}
}
else
{
mCurMesh = NULL;
}
}
void NavMeshSelectTool::onRender3D()
{
if (!mCurMesh.isNull())
renderBoxOutline(mCurMesh->getWorldBox(), ColorI::LIGHT);
if (!mSelMesh.isNull())
renderBoxOutline(mSelMesh->getWorldBox(), ColorI::LIGHT);
}
bool NavMeshSelectTool::updateGuiInfo()
{
SimObject* statusbar;
Sim::findObject("EditorGuiStatusBar", statusbar);
GuiTextCtrl* selectionBar;
Sim::findObject("EWorldEditorStatusBarSelection", selectionBar);
String text;
text = "LMB To select a NavMesh.";
if (statusbar)
Con::executef(statusbar, "setInfo", text.c_str());
text = "";
if(mSelMesh)
text = String::ToString("NavMesh Selected: %d", mSelMesh->getId());
if (selectionBar)
selectionBar->setText(text);
return true;
}

View file

@ -0,0 +1,32 @@
#ifndef _NAVMESHSELECTTOOL_H_
#define _NAVMESHSELECTTOOL_H_
#ifndef _NAVMESH_TOOL_H_
#include "navigation/navMeshTool.h"
#endif
class NavMeshSelectTool : public NavMeshTool
{
typedef NavMeshTool Parent;
protected:
SimObjectPtr<NavMesh> mCurMesh;
SimObjectPtr<NavMesh> mSelMesh;
public:
DECLARE_CONOBJECT(NavMeshSelectTool);
NavMeshSelectTool();
virtual ~NavMeshSelectTool() {}
void setActiveNavMesh(NavMesh* nav_mesh) { mNavMesh = nav_mesh; mSelMesh = nav_mesh; }
void onActivated(const Gui3DMouseEvent& evt) override;
void onDeactivated() override;
void on3DMouseDown(const Gui3DMouseEvent& evt) override;
void on3DMouseMove(const Gui3DMouseEvent& evt) override;
void onRender3D() override;
bool updateGuiInfo() override;
};
#endif

View file

@ -0,0 +1,426 @@
#include "navMeshTestTool.h"
#include "navigation/guiNavEditorCtrl.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDrawUtil.h"
#include "scene/sceneManager.h"
#include "math/mathUtils.h"
#include "T3D/gameBase/gameConnection.h"
#include "T3D/AI/AIController.h"
IMPLEMENT_CONOBJECT(NavMeshTestTool);
//// take control
// GameConnection* conn = GameConnection::getConnectionToServer();
// if (conn)
// conn->setControlObject(static_cast<GameBase*>(obj));
//}
static void renderBoxOutline(const Box3F& box, const ColorI& col)
{
if (box != Box3F::Invalid)
{
GFXStateBlockDesc desc;
desc.setCullMode(GFXCullNone);
desc.setFillModeSolid();
desc.setZReadWrite(true, false);
desc.setBlend(true);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 20));
desc.setFillModeWireframe();
desc.setBlend(false);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 255));
}
}
void NavMeshTestTool::spawnPlayer(const Point3F& position)
{
if (mSpawnClass.isEmpty() || mSpawnDatablock.isEmpty())
{
Con::warnf("NavMeshTestTool::spawnPlayer - spawn class/datablock not set!");
return;
}
SimObject* spawned = Sim::spawnObject(mSpawnClass, mSpawnDatablock);
SceneObject* obj = dynamic_cast<SceneObject*>(spawned);
if (!obj)
{
Con::warnf("NavMeshTestTool::spawnPlayer - spawn failed or not a SceneObject");
if (spawned)
spawned->deleteObject();
return;
}
obj->setPosition(position);
SimObject* cleanup = Sim::findObject("MissionCleanup");
if (cleanup)
{
SimGroup* missionCleanup = dynamic_cast<SimGroup*>(cleanup);
missionCleanup->addObject(obj);
}
mPlayer = obj;
Con::executef(this, "onPlayerSpawned", obj->getIdString());
#ifdef TORQUE_NAVIGATION_ENABLED
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
if (asAIPlayer)
{
Con::executef(this, "onPlayerSelected");
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
if (sbo && sbo->getAIController() && sbo->getAIController()->mControllerData)
{
Con::executef(this, "onPlayerSelected");
}
else
{
Con::executef(this, "onPlayerSelected");
}
}
#else
Con::executef(this, "onPlayerSelected");
#endif
}
void NavMeshTestTool::drawAgent(duDebugDrawTorque& dd, const F32* pos, F32 r, F32 h, F32 c, const U32 col)
{
dd.depthMask(false);
// Agent dimensions.
duDebugDrawCylinderWire(&dd, pos[0] - r, pos[1] + 0.02f, pos[2] - r, pos[0] + r, pos[1] + h, pos[2] + r, col, 2.0f);
duDebugDrawCircle(&dd, pos[0], pos[1] + c, pos[2], r, duRGBA(0, 0, 0, 64), 1.0f);
U32 colb = duRGBA(0, 0, 0, 196);
dd.begin(DU_DRAW_LINES);
dd.vertex(pos[0], pos[1] - c, pos[2], colb);
dd.vertex(pos[0], pos[1] + c, pos[2], colb);
dd.vertex(pos[0] - r / 2, pos[1] + 0.02f, pos[2], colb);
dd.vertex(pos[0] + r / 2, pos[1] + 0.02f, pos[2], colb);
dd.vertex(pos[0], pos[1] + 0.02f, pos[2] - r / 2, colb);
dd.vertex(pos[0], pos[1] + 0.02f, pos[2] + r / 2, colb);
dd.end();
dd.depthMask(true);
}
NavMeshTestTool::NavMeshTestTool()
{
mSpawnClass = String::EmptyString;
mSpawnDatablock = String::EmptyString;
mPlayer = NULL;
mCurPlayer = NULL;
mFollowObject = NULL;
mCurFollowObject = NULL;
mPathStart = Point3F::Max;
mPathEnd = Point3F::Max;
mTestPath = NULL;
mLinkTypes = LinkData(AllFlags);
mFilter.setIncludeFlags(mLinkTypes.getFlags());
mFilter.setExcludeFlags(0);
mSelectFollow = false;
}
void NavMeshTestTool::onActivated(const Gui3DMouseEvent& evt)
{
mSelectFollow = false;
Con::executef(this, "onActivated");
}
void NavMeshTestTool::onDeactivated()
{
if (mTestPath != NULL) {
mTestPath->deleteObject();
mTestPath = NULL;
}
if (mPlayer != NULL)
{
mPlayer = NULL;
Con::executef(this, "onPlayerDeselected");
}
mSelectFollow = false;
Con::executef(this, "onDeactivated");
}
void NavMeshTestTool::on3DMouseDown(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
Point3F startPnt = evt.pos;
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
bool shift = evt.modifier & SI_LSHIFT;
bool ctrl = evt.modifier & SI_LCTRL;
if (ctrl)
{
if (gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
spawnPlayer(ri.point);
}
return;
}
if (shift)
{
mPlayer = NULL;
Con::executef(this, "onPlayerDeselected");
return;
}
if (gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
{
if (!ri.object)
return;
if (mSelectFollow)
{
mFollowObject = ri.object;
Con::executef(this, "onFollowSelected");
mSelectFollow = false;
return;
}
else
{
mPlayer = ri.object;
}
#ifdef TORQUE_NAVIGATION_ENABLED
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
if (asAIPlayer)
{
Con::executef(this, "onPlayerSelected");
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
if (sbo && sbo->getAIController() && sbo->getAIController()->mControllerData)
{
Con::executef(this, "onPlayerSelected");
}
else
{
Con::executef(this, "onPlayerSelected");
}
}
#else
Con::executef(this, "onPlayerSelected");
#endif
}
else if (gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
if (mPlayer.isNull())
{
if (mPathStart != Point3F::Max && mPathEnd != Point3F::Max) // start a new path.
{
mPathStart = Point3F::Max;
mPathEnd = Point3F::Max;
}
if (mPathStart != Point3F::Max)
{
mPathEnd = ri.point;
mTestPath = new NavPath();
mTestPath->mMesh = mNavMesh;
mTestPath->mFrom = mPathStart;
mTestPath->mTo = mPathEnd;
mTestPath->mFromSet = mTestPath->mToSet = true;
mTestPath->mAlwaysRender = true;
mTestPath->mLinkTypes = mLinkTypes;
mTestPath->mFilter = mFilter;
mTestPath->mXray = true;
// Paths plan automatically upon being registered.
if (!mTestPath->registerObject())
{
delete mTestPath;
return;
}
}
else
{
mPathStart = ri.point;
if (mTestPath != NULL) {
mTestPath->deleteObject();
mTestPath = NULL;
}
}
}
else
{
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
if (asAIPlayer) //try direct
{
asAIPlayer->setPathDestination(ri.point);
mPathStart = mPlayer->getPosition();
mPathEnd = ri.point;
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
if (sbo->getAIController())
{
if (sbo->getAIController()->mControllerData)
{
sbo->getAIController()->getNav()->setPathDestination(ri.point, true);
mPathStart = mPlayer->getPosition();
mPathEnd = ri.point;
}
}
}
}
}
}
void NavMeshTestTool::on3DMouseMove(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
Point3F startPnt = evt.pos;
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
if (gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
{
if (mSelectFollow)
mCurFollowObject = ri.object;
else
mCurPlayer = ri.object;
}
else
{
mCurFollowObject = NULL;
mCurPlayer = NULL;
}
}
void NavMeshTestTool::onRender3D()
{
if (mNavMesh.isNull())
return;
static const U32 startCol = duRGBA(128, 25, 0, 192);
static const U32 endCol = duRGBA(51, 102, 0, 129);
const F32 agentRadius = mNavMesh->mWalkableRadius;
const F32 agentHeight = mNavMesh->mWalkableHeight;
const F32 agentClimb = mNavMesh->mWalkableClimb;
duDebugDrawTorque dd;
dd.depthMask(false);
if (mPathStart != Point3F::Max)
{
drawAgent(dd, DTStoRC(mPathStart), agentRadius, agentHeight, agentClimb, startCol);
}
if (mPathEnd != Point3F::Max)
{
drawAgent(dd, DTStoRC(mPathEnd), agentRadius, agentHeight, agentClimb, endCol);
}
dd.depthMask(true);
mNavMesh->renderSearch(dd);
dd.immediateRender();
if (!mCurFollowObject.isNull())
renderBoxOutline(mCurFollowObject->getWorldBox(), ColorI::LIGHT);
if (!mCurPlayer.isNull())
renderBoxOutline(mCurPlayer->getWorldBox(), ColorI::BLUE);
if (!mPlayer.isNull())
renderBoxOutline(mPlayer->getWorldBox(), ColorI::GREEN);
if (!mFollowObject.isNull())
renderBoxOutline(mFollowObject->getWorldBox(), ColorI::WHITE);
}
bool NavMeshTestTool::updateGuiInfo()
{
SimObject* statusbar;
Sim::findObject("EditorGuiStatusBar", statusbar);
GuiTextCtrl* selectionBar;
Sim::findObject("EWorldEditorStatusBarSelection", selectionBar);
String text;
if (mPlayer)
text = "LMB To Select move Destination. LSHIFT+LMB To Deselect Current Bot.";
if (mCurPlayer != NULL && mCurPlayer != mPlayer)
text = "LMB To select Bot.";
if (mPlayer == NULL)
{
text = "LMB To place start/end for test path.";
}
if (mSpawnClass != String::EmptyString && mSpawnDatablock != String::EmptyString)
text += " CTRL+LMB To spawn a new Bot.";
if (mSelectFollow)
text = "LMB To select Follow Target.";
if (statusbar)
Con::executef(statusbar, "setInfo", text.c_str());
text = "";
if (mPlayer)
text = String::ToString("Bot Selected: %d", mPlayer->getId());
if (selectionBar)
selectionBar->setText(text);
return true;
}
S32 NavMeshTestTool::getPlayerId()
{
return mPlayer.isNull() ? 0 : mPlayer->getId();
}
S32 NavMeshTestTool::getFollowObjectId()
{
return mFollowObject.isNull() ? 0 : mFollowObject->getId();
}
DefineEngineMethod(NavMeshTestTool, getPlayer, S32, (), ,
"@brief Return the current player id.")
{
return object->getPlayerId();
}
DefineEngineMethod(NavMeshTestTool, getFollowObject, S32, (), ,
"@brief Return the current follow object id.")
{
return object->getFollowObjectId();
}
DefineEngineMethod(NavMeshTestTool, setSpawnClass, void, (String className), , "")
{
object->setSpawnClass(className);
}
DefineEngineMethod(NavMeshTestTool, setSpawnDatablock, void, (String dbName), , "")
{
object->setSpawnDatablock(dbName);
}
DefineEngineMethod(NavMeshTestTool, followSelectMode, void, (), ,
"@brief Set NavMeshTool to select a follow object.")
{
return object->followSelectMode();
}

View file

@ -0,0 +1,60 @@
#ifndef _NAVMESHTESTTOOL_H_
#define _NAVMESHTESTTOOL_H_
#ifndef _NAVMESH_TOOL_H_
#include "navigation/navMeshTool.h"
#endif
#ifndef _NAVPATH_H_
#include "navigation/navPath.h"
#endif
class NavMeshTestTool : public NavMeshTool
{
typedef NavMeshTool Parent;
protected:
String mSpawnClass;
String mSpawnDatablock;
SimObjectPtr<SceneObject> mPlayer;
SimObjectPtr<SceneObject> mCurPlayer;
SimObjectPtr<SceneObject> mFollowObject;
SimObjectPtr<SceneObject> mCurFollowObject;
Point3F mPathStart;
Point3F mPathEnd;
NavPath* mTestPath;
LinkData mLinkTypes;
dtQueryFilter mFilter;
bool mSelectFollow;
public:
DECLARE_CONOBJECT(NavMeshTestTool);
void spawnPlayer(const Point3F& position);
void drawAgent(duDebugDrawTorque& dd, const F32* pos, F32 r, F32 h, F32 c, const U32 col);
NavMeshTestTool();
virtual ~NavMeshTestTool() {}
void onActivated(const Gui3DMouseEvent& evt) override;
void onDeactivated() override;
void on3DMouseDown(const Gui3DMouseEvent& evt) override;
void on3DMouseMove(const Gui3DMouseEvent& evt) override;
void onRender3D() override;
bool updateGuiInfo() override;
S32 getPlayerId();
S32 getFollowObjectId();
void setSpawnClass(String className) { mSpawnClass = className; }
void setSpawnDatablock(String dbName) { mSpawnDatablock = dbName; }
void followSelectMode() { mSelectFollow = true; }
};
#endif // !_NAVMESHTESTTOOL_H_

View file

@ -0,0 +1,200 @@
#include "offMeshConnTool.h"
#include "navigation/guiNavEditorCtrl.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDrawUtil.h"
#include "scene/sceneManager.h"
#include "math/mathUtils.h"
IMPLEMENT_CONOBJECT(OffMeshConnectionTool);
void OffMeshConnectionTool::onActivated(const Gui3DMouseEvent& evt)
{
Con::executef(this, "onActivated");
}
void OffMeshConnectionTool::onDeactivated()
{
Con::executef(this, "onDeactivated");
}
void OffMeshConnectionTool::on3DMouseDown(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
Point3F startPnt = evt.pos;
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
bool shift = evt.modifier & SI_LSHIFT;
bool ctrl = evt.modifier & SI_LCTRL;
if (gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
U32 link = mNavMesh->getLink(ri.point);
if (link != -1)
{
if (mLink != -1)
mNavMesh->selectLink(mLink, false);
mNavMesh->selectLink(link, true, false);
mLink = link;
if (ctrl)
{
mNavMesh->selectLink(mLink, false);
mNavMesh->deleteLink(mLink);
mLink = -1;
Con::executef(this, "onLinkDeselected");
return;
}
else
{
LinkData d = mNavMesh->getLinkFlags(mLink);
bool biDir = mNavMesh->getLinkDir(mLink);
F32 linkRad = mNavMesh->getLinkRadius(mLink);
Con::executef(this, "onLinkSelected", Con::getIntArg(d.getFlags()), Con::getBoolArg(biDir), Con::getFloatArg(linkRad));
}
}
else
{
if (mLink != -1)
{
mNavMesh->selectLink(mLink, false);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
if (mLinkStart != Point3F::Max)
{
mLink = mNavMesh->addLink(mLinkStart, ri.point, mBiDir, mLinkRadius);
mNavMesh->selectLink(mLink, true, false);
if (shift)
mLinkStart = ri.point;
else
mLinkStart = Point3F::Max;
Con::executef(this, "onLinkSelected", Con::getIntArg(mLinkCache.getFlags()), Con::getBoolArg(mBiDir), Con::getFloatArg(mLinkRadius));
}
else
{
mLinkStart = ri.point;
}
}
}
else
{
if (mLink != -1)
{
mNavMesh->selectLink(mLink, false);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
}
}
void OffMeshConnectionTool::on3DMouseMove(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
Point3F startPnt = evt.pos;
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
if (gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
U32 link = mNavMesh->getLink(ri.point);
if (link != -1)
{
if (link != mLink)
{
if (mCurLink != -1)
mNavMesh->selectLink(mCurLink, false);
mNavMesh->selectLink(link, true, true);
}
mCurLink = link;
}
else
{
if (mCurLink != mLink)
mNavMesh->selectLink(mCurLink, false);
mCurLink = -1;
}
}
else
{
mNavMesh->selectLink(mCurLink, false);
mCurLink = -1;
}
}
void OffMeshConnectionTool::onRender3D()
{
if (mNavMesh.isNull())
return;
duDebugDrawTorque dd;
if (mLinkStart != Point3F::Max)
{
Point3F rcFrom = DTStoRC(mLinkStart);
dd.begin(DU_DRAW_LINES);
dd.depthMask(false);
duAppendCircle(&dd, rcFrom.x, rcFrom.y, rcFrom.z, mLinkRadius, duRGBA(0, 255, 0, 255));
dd.end();
}
//mNavMesh->renderLinks(dd);
dd.immediateRender();
}
bool OffMeshConnectionTool::updateGuiInfo()
{
SimObject* statusbar;
Sim::findObject("EditorGuiStatusBar", statusbar);
GuiTextCtrl* selectionBar;
Sim::findObject("EWorldEditorStatusBarSelection", selectionBar);
String text;
text = "LMB To Select Link. CTRL+LMB To Delete Link";
if (mLinkStart != Point3F::Max)
text = "LinkStarted: LMB To place End Point. Hold Left Shift to start a new Link from the end point.";
if (statusbar)
Con::executef(statusbar, "setInfo", text.c_str());
if (mLink != -1)
text = String::ToString("Selected Link: %d", mLink);
else
text = "";
if (selectionBar)
selectionBar->setText(text);
return true;
}
void OffMeshConnectionTool::setLinkProperties(const LinkData& d, bool biDir, F32 rad)
{
if (!mNavMesh.isNull() && mLink != -1)
{
mNavMesh->setLinkFlags(mLink, d);
mNavMesh->setLinkDir(mLink, biDir);
mNavMesh->setLinkRadius(mLink, rad);
}
mLinkCache = d;
mBiDir = biDir;
mLinkRadius = rad;
}
DefineEngineMethod(OffMeshConnectionTool, setLinkProperties, void, (U32 flags, bool biDir, F32 rad), ,
"@Brief Set properties of the selected link.")
{
object->setLinkProperties(LinkData(flags), biDir, rad);
}

View file

@ -0,0 +1,53 @@
#ifndef _OFFMESHCONNTOOL_H_
#define _OFFMESHCONNTOOL_H_
#ifndef _NAVMESH_TOOL_H_
#include "navigation/navMeshTool.h"
#endif
class OffMeshConnectionTool : public NavMeshTool
{
typedef NavMeshTool Parent;
bool mStartPosSet;
bool mBiDir;
S32 mLink;
S32 mCurLink;
Point3F mLinkStart;
LinkData mLinkCache;
F32 mLinkRadius;
public:
DECLARE_CONOBJECT(OffMeshConnectionTool);
OffMeshConnectionTool() {
mStartPosSet = false;
mBiDir = false;
mLink = -1;
mCurLink = -1;
mLinkStart = Point3F::Max;
mLinkCache = LinkData(0);
mLinkRadius = 1.0;
}
virtual ~OffMeshConnectionTool() {}
void setActiveNavMesh(NavMesh* nav_mesh) override {
mNavMesh = nav_mesh;
if (!mNavMesh.isNull())
mLinkRadius = mNavMesh->mWalkableRadius;
}
void onActivated(const Gui3DMouseEvent& evt) override;
void onDeactivated() override;
void on3DMouseDown(const Gui3DMouseEvent& evt) override;
void on3DMouseMove(const Gui3DMouseEvent& evt) override;
void onRender3D() override;
bool updateGuiInfo() override;
void setLinkProperties(const LinkData& d, bool biDir, F32 rad);
};
#endif

View file

@ -0,0 +1,115 @@
#include "tileTool.h"
#include "navigation/guiNavEditorCtrl.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDrawUtil.h"
#include "scene/sceneManager.h"
#include "math/mathUtils.h"
IMPLEMENT_CONOBJECT(TileTool);
static void renderBoxOutline(const Box3F& box, const ColorI& col)
{
if (box != Box3F::Invalid)
{
GFXStateBlockDesc desc;
desc.setCullMode(GFXCullNone);
desc.setFillModeSolid();
desc.setZReadWrite(true, false);
desc.setBlend(true);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 20));
desc.setFillModeWireframe();
desc.setBlend(false);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 255));
}
}
void TileTool::onActivated(const Gui3DMouseEvent& lastEvent)
{
Con::executef(this, "onActivated");
}
void TileTool::onDeactivated()
{
Con::executef(this, "onDeactivated");
}
void TileTool::on3DMouseDown(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
Point3F start = evt.pos;
Point3F end = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
if (gServerContainer.castRay(start, end, StaticObjectType, &ri))
{
mSelTile = mNavMesh->getTile(ri.point);
}
}
void TileTool::on3DMouseMove(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
Point3F startPnt = evt.pos;
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
if (gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
mCurTile = mNavMesh->getTile(ri.point);
else
mCurTile = -1;
}
void TileTool::onRender3D()
{
if (mNavMesh.isNull())
return;
if(mCurTile != -1)
renderBoxOutline(mNavMesh->getTileBox(mCurTile), ColorI::BLUE);
if(mSelTile != -1)
renderBoxOutline(mNavMesh->getTileBox(mSelTile), ColorI::GREEN);
}
void TileTool::buildTile()
{
if (!mNavMesh.isNull() && mSelTile != -1)
mNavMesh->buildTile(mSelTile);
}
bool TileTool::updateGuiInfo()
{
SimObject* statusbar;
Sim::findObject("EditorGuiStatusBar", statusbar);
GuiTextCtrl* selectionBar;
Sim::findObject("EWorldEditorStatusBarSelection", selectionBar);
String text;
text = "LMB To select NavMesh Tile";
if (statusbar)
Con::executef(statusbar, "setInfo", text.c_str());
if (mSelTile != -1)
text = String::ToString("Selected Tile: %d", mSelTile);
else
text = "";
if (selectionBar)
selectionBar->setText(text);
return true;
}
DefineEngineMethod(TileTool, buildTile, void, (), ,
"@brief Build the currently selected tile.")
{
return object->buildTile();
}

View file

@ -0,0 +1,31 @@
#ifndef _TILETOOL_H_
#define _TILETOOL_H_
#ifndef _NAVMESH_TOOL_H_
#include "navigation/navMeshTool.h"
#endif
class TileTool : public NavMeshTool
{
typedef NavMeshTool Parent;
S32 mCurTile;
S32 mSelTile;
public:
DECLARE_CONOBJECT(TileTool);
TileTool() { mCurTile = -1; mSelTile = -1; }
virtual ~TileTool() {}
void onActivated(const Gui3DMouseEvent& evt) override;
void onDeactivated() override;
void on3DMouseDown(const Gui3DMouseEvent& evt) override;
void on3DMouseMove(const Gui3DMouseEvent& evt) override;
void onRender3D() override;
void buildTile();
bool updateGuiInfo() override;
};
#endif

View file

@ -69,14 +69,11 @@ NavPath::NavPath() :
mXray = false;
mRenderSearch = false;
mQuery = NULL;
mStatus = DT_FAILURE;
}
NavPath::~NavPath()
{
dtFreeNavMeshQuery(mQuery);
mQuery = NULL;
}
void NavPath::checkAutoUpdate()
@ -264,9 +261,6 @@ bool NavPath::onAdd()
if(isServerObject())
{
mQuery = dtAllocNavMeshQuery();
if(!mQuery)
return false;
checkAutoUpdate();
if(!plan())
setProcessTick(true);
@ -293,7 +287,8 @@ bool NavPath::init()
return false;
// Initialise our query.
if(dtStatusFailed(mQuery->init(mMesh->getNavMesh(), MaxPathLen)))
mQuery = mMesh->getNavMeshQuery();
if(!mQuery)
return false;
mPoints.clear();
@ -372,9 +367,6 @@ void NavPath::resize()
bool NavPath::plan()
{
PROFILE_SCOPE(NavPath_plan);
// Initialise filter.
mFilter.setIncludeFlags(mLinkTypes.getFlags());
// Initialise query and visit locations.
if(!init())
return false;
@ -640,9 +632,8 @@ void NavPath::renderSimple(ObjectRenderInst *ri, SceneRenderState *state, BaseMa
if(np->mQuery && !dtStatusSucceed(np->mStatus))
{
duDebugDrawTorque dd;
dd.overrideColor(duRGBA(250, 20, 20, 255));
duDebugDrawNavMeshNodes(&dd, *np->mQuery);
dd.render();
dd.immediateRender();
}
}
}

View file

@ -57,7 +57,7 @@ public:
/// What sort of link types are we allowed to move on?
LinkData mLinkTypes;
dtQueryFilter mFilter;
/// Plan the path.
bool plan();
@ -152,7 +152,6 @@ private:
dtNavMeshQuery *mQuery;
dtStatus mStatus;
dtQueryFilter mFilter;
S32 mCurIndex;
Vector<Point3F> mPoints;
Vector<U16> mFlags;

View file

@ -27,12 +27,16 @@
#include "gfx/primBuilder.h"
#include "gfx/gfxStateBlock.h"
RecastPolyList::RecastPolyList()
RecastPolyList::RecastPolyList() : mChunkyMesh(0)
{
nverts = 0;
verts = NULL;
vertcap = 0;
nnormals = 0;
normals = NULL;
normalcap = 0;
ntris = 0;
tris = NULL;
tricap = 0;
@ -44,6 +48,28 @@ RecastPolyList::~RecastPolyList()
clear();
}
rcChunkyTriMesh* RecastPolyList::getChunkyMesh()
{
if (!mChunkyMesh)
{
mChunkyMesh = new rcChunkyTriMesh;
if (!mChunkyMesh)
{
Con::errorf("Build tile navigation: out of memory");
return NULL;
}
if (!rcCreateChunkyTriMesh(getVerts(), getTris(), getTriCount(), 256, mChunkyMesh))
{
Con::errorf("Build tile navigation: out of memory");
return NULL;
}
}
return mChunkyMesh;
}
void RecastPolyList::clear()
{
nverts = 0;
@ -51,6 +77,11 @@ void RecastPolyList::clear()
verts = NULL;
vertcap = 0;
nnormals = 0;
delete[] normals;
normals = NULL;
normalcap = 0;
ntris = 0;
delete[] tris;
tris = NULL;
@ -134,6 +165,39 @@ void RecastPolyList::vertex(U32 vi)
void RecastPolyList::end()
{
// Fetch current triangle indices
const U32 i0 = tris[ntris * 3 + 0];
const U32 i1 = tris[ntris * 3 + 1];
const U32 i2 = tris[ntris * 3 + 2];
// Rebuild vertices
Point3F v0(verts[i0 * 3 + 0], verts[i0 * 3 + 1], verts[i0 * 3 + 2]);
Point3F v1(verts[i1 * 3 + 0], verts[i1 * 3 + 1], verts[i1 * 3 + 2]);
Point3F v2(verts[i2 * 3 + 0], verts[i2 * 3 + 1], verts[i2 * 3 + 2]);
// Compute normal
Point3F edge1 = v1 - v0;
Point3F edge2 = v2 - v0;
Point3F normal = mCross(edge1, edge2);
normal.normalizeSafe();
// Allocate/resize normal buffer if needed
if (nnormals == normalcap)
{
normalcap = (normalcap == 0) ? 16 : normalcap * 2;
F32* newNormals = new F32[normalcap * 3];
if (normals)
dMemcpy(newNormals, normals, nnormals * 3 * sizeof(F32));
delete[] normals;
normals = newNormals;
}
// Store normal
normals[nnormals * 3 + 0] = normal.x;
normals[nnormals * 3 + 1] = normal.y;
normals[nnormals * 3 + 2] = normal.z;
nnormals++;
ntris++;
}
@ -147,6 +211,11 @@ const F32 *RecastPolyList::getVerts() const
return verts;
}
const F32* RecastPolyList::getNormals() const
{
return normals;
}
U32 RecastPolyList::getTriCount() const
{
return ntris;

View file

@ -26,6 +26,10 @@
#include "collision/abstractPolyList.h"
#include "core/util/tVector.h"
#ifndef CHUNKYTRIMESH_H
#include "ChunkyTriMesh.h"
#endif
/// Represents polygons in the same manner as the .obj file format. Handy for
/// padding data to Recast, since it expects this data format. At the moment,
/// this class only accepts triangles.
@ -57,6 +61,8 @@ public:
U32 getVertCount() const;
const F32 *getVerts() const;
const F32* getNormals() const;
U32 getTriCount() const;
const S32 *getTris() const;
@ -70,6 +76,9 @@ public:
/// Default destructor.
~RecastPolyList();
rcChunkyTriMesh* getChunkyMesh();
protected:
/// Number of vertices defined.
U32 nverts;
@ -78,6 +87,13 @@ protected:
/// Size of vertex array.
U32 vertcap;
// Number of normals defined.
U32 nnormals;
// Array of normals (xyz in float array)
F32* normals;
// Size of normal array (matches verts)
U32 normalcap;
/// Number of triangles defined.
U32 ntris;
/// Array of triangle vertex indices. Size ntris*3
@ -93,6 +109,8 @@ protected:
/// Another inherited utility function.
const PlaneF& getIndexedPlane(const U32 index) override { return planes[index]; }
rcChunkyTriMesh* mChunkyMesh;
private:
};

View file

@ -52,6 +52,7 @@ inline void rcCol(unsigned int col, U8 &r, U8 &g, U8 &b, U8 &a)
}
enum PolyAreas {
NullArea = 0,
GroundArea,
WaterArea,
OffMeshArea,
@ -99,6 +100,10 @@ struct LinkData {
(climb ? ClimbFlag : 0) |
(teleport ? TeleportFlag : 0);
}
U16 getExcludeFlags() const
{
return AllFlags & ~getFlags();
}
};
#endif

View file

@ -4,3 +4,58 @@ datablock ItemData(PrototypeItemData)
ShapeAsset = "Prototyping:TorusPrimitive_shape";
cameraMaxDist = "0.75";
};
datablock PlayerData( ProtoPlayer ) {
// Third person shape
ShapeAsset = "Prototyping:Playerbot_shape";
controlMap = "playerKeyMap";
AIControllerData = "aiPlayerControl";
};
datablock WheeledVehicleTire(ProtoCarTire)
{
// Tires act as springs and generate lateral and longitudinal
// forces to move the vehicle. These distortion/spring forces
// are what convert wheel angular velocity into forces that
// act on the rigid body.
shapeAsset = "Prototyping:carwheel_shape";
staticFriction = 1;
kineticFriction = 4.2;
// Spring that generates lateral tire forces
lateralForce = 150000;
lateralDamping = 30000;
lateralRelaxation = 0.1;
// Spring that generates longitudinal tire forces
longitudinalForce = 600;
longitudinalDamping = 1600;
longitudinalRelaxation = 0.1;
};
datablock WheeledVehicleSpring(ProtoCarSpring)
{
// Wheel suspension properties
length = "0.6"; // Suspension travel
force = 3600; // Spring force
damping = 2800; // Spring damping
antiSwayForce = 300; // Lateral anti-sway force
};
datablock WheeledVehicleData(ProtoCar)
{
category = "Vehicles";
shapeAsset = "Prototyping:car_shape";
collisionMul = 0;
impactMul = 0;
controlMap = "vehicleKeyMap";
AIControllerData = "aiCarControl";
cameraMaxDist = "2.81993";
ShapeFile = "data/Prototyping/shapes/Vehicles/car.dae";
mass = "1000";
originalAssetName = "ProtoCar";
massCenter = "0 0.75 0";
dragForce = "0.1";
};

View file

@ -0,0 +1,47 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
// This file contains script methods unique to the WheeledVehicle class. All
// other necessary methods are contained in "../server/scripts/vehicle.cs" in
// which the "generic" Vehicle class methods that are shared by all vehicles,
// (flying, hover, and wheeled) can be found.
function ProtoCar::onAdd(%this, %obj)
{
Parent::onAdd(%this, %obj);
// Setup the car with some tires & springs
for (%i = %obj.getWheelCount() - 1; %i >= 0; %i--)
{
%obj.setWheelTire(%i, ProtoCarTire);
%obj.setWheelSpring(%i, ProtoCarSpring);
%obj.setWheelPowered(%i, false);
}
// Steer with the front tires
%obj.setWheelSteering(0, 1);
%obj.setWheelSteering(1, 1);
// Only power the two rear wheels... assuming there are only 4 wheels.
%obj.setWheelPowered(2, true);
%obj.setWheelPowered(3, true);
}

View file

@ -279,24 +279,22 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
HorizSizing = "width";
VertSizing = "bottom";
Position = "4 24";
Extent = "202 85";
Extent = "202 136";
Docking = "Top";
Margin = "3 3 3 3";
internalName = "ActionsBox";
new GuiTextCtrl(){
Profile = "EditorTextProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = "5 0";
Extent = "86 18";
text = "Actions";
new GuiPopUpMenuCtrl(DrawModeSelector) {
position = "7 0";
extent = "190 20";
profile = "ToolsGuiPopUpMenuProfile";
tooltipProfile = "GuiToolTipProfile";
};
new GuiStackControl()
{
internalName = "SelectActions";
position = "7 21";
extent = "190 64";
extent = "190 136";
new GuiButtonCtrl() {
Profile = "ToolsGuiButtonProfile";
@ -372,7 +370,7 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
{
internalName = "LinkActions";
position = "7 21";
extent = "190 64";
extent = "190 136";
new GuiButtonCtrl() {
Profile = "ToolsGuiButtonProfile";
@ -388,7 +386,7 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
{
internalName = "CoverActions";
position = "7 21";
extent = "190 64";
extent = "190 136";
new GuiButtonCtrl() {
Profile = "ToolsGuiButtonProfile";
@ -413,7 +411,7 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
{
internalName = "TileActions";
position = "7 21";
extent = "190 64";
extent = "190 136";
new GuiButtonCtrl() {
Profile = "ToolsGuiButtonProfile";
@ -422,23 +420,54 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
VertSizing = "bottom";
Extent = "182 18";
text = "Rebuild tile";
command = "NavEditorGui.buildTile();";
command = "NavMeshTools->TileTool.buildTile();";
};
};
new GuiStackControl()
{
internalName = "TestActions";
position = "7 21";
extent = "190 64";
new GuiButtonCtrl() {
Profile = "ToolsGuiButtonProfile";
buttonType = "PushButton";
HorizSizing = "right";
VertSizing = "bottom";
Extent = "180 18";
text = "Spawn";
command = "NavEditorGui.spawnPlayer();";
extent = "190 136";
new GuiControl() {
profile = "GuiDefaultProfile";
Extent = "190 20";
new GuiTextCtrl(){
Profile = "EditorTextProfile";
extent = "89 20";
text = "Spawn Class";
};
new GuiTextCtrl(){
Profile = "EditorTextProfile";
Position = "100 0";
extent = "89 20";
text = "Spawn Datablock";
};
};
new GuiControl() {
profile = "GuiDefaultProfile";
Extent = "190 20";
new GuiPopUpMenuCtrl(SpawnClassSelector) {
extent = "89 20";
profile = "ToolsGuiPopUpMenuProfile";
tooltipProfile = "GuiToolTipProfile";
};
new GuiPopUpMenuCtrl(SpawnDatablockSelector) {
position = "100 0";
extent = "89 20";
profile = "ToolsGuiPopUpMenuProfile";
tooltipProfile = "GuiToolTipProfile";
};
};
new GuiControl() {
profile = "GuiDefaultProfile";
Extent = "190 20";
new GuiTextCtrl(){
Profile = "EditorTextProfile";
extent = "89 20";
text = "AI Actions";
};
};
new GuiControl() {
profile = "GuiDefaultProfile";
@ -451,7 +480,9 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
VertSizing = "bottom";
Extent = "90 18";
text = "Delete";
command = "NavEditorGui.getPlayer().delete();";
tooltipProfile = "GuiToolTipProfile";
tooltip = "Delete Selected Bot.";
command = "NavMeshTools->TestTool.getPlayer().delete();NavInspector.inspect();";
};
new GuiButtonCtrl() {
position = "100 0";
@ -460,8 +491,8 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
HorizSizing = "right";
VertSizing = "bottom";
Extent = "90 18";
text = "Find cover";
command = "NavEditorGui.findCover();";
text = "Stop";
command = "NavMeshTools->TestTool.stop();";
};
};
new GuiControl() {
@ -474,8 +505,8 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
HorizSizing = "right";
VertSizing = "bottom";
Extent = "90 18";
text = "Follow";
command = "NavEditorGui.followObject();";
text = "Select Follow";
command = "NavMeshTools->TestTool.followObject();";
};
new GuiButtonCtrl() {
position = "100 0";
@ -484,8 +515,39 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
HorizSizing = "right";
VertSizing = "bottom";
Extent = "90 18";
text = "Stop";
command = "NavEditorGui.stop();";
text = "Find cover";
command = "NavMeshTools->TestTool.findCover();";
};
};
new GuiControl() {
profile = "GuiDefaultProfile";
Extent = "190 18";
new GuiButtonCtrl() {
Profile = "ToolsGuiButtonProfile";
buttonType = "PushButton";
HorizSizing = "right";
VertSizing = "bottom";
Extent = "90 18";
text = "Toggle Follow";
command = "NavMeshTools->TestTool.toggleFollow();";
};
new GuiTextEditSliderCtrl(CoverRadius) {
position = "100 0";
extent = "90 18";
format = "%3.2f";
range = "0 1e+03";
increment = "0.1";
focusOnMouseWheel = "0";
historySize = "0";
password = "0";
tabComplete = "0";
sinkAllKeyEvents = "0";
hovertime = "1000";
profile = "ToolsGuiTextEditProfile";
tooltipProfile = "GuiToolTipProfile";
toolTip = "The radius to search for cover";
};
};
};
@ -567,7 +629,45 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
position = "7 21";
extent = "186 64";
padding = "2 2 2 2";
new GuiTextEditSliderCtrl() {
internalName = "LinkRadius";
class = "NavMeshLinkRadius";
extent = "50 15";
format = "%3.2f";
range = "0 1e+03";
increment = "0.1";
focusOnMouseWheel = "0";
historySize = "0";
password = "0";
tabComplete = "0";
sinkAllKeyEvents = "0";
hovertime = "1000";
profile = "ToolsGuiTextEditProfile";
tooltipProfile = "GuiToolTipProfile";
toolTip = "The radius for this link.";
AltCommand = "NavMeshTools->LinkTool.updateRadius();";
};
new GuiCheckBoxCtrl() {
internalName = "LinkBiDirection";
class = "NavMeshLinkBiDirection";
text = " Link Bi-Directional";
buttonType = "ToggleButton";
useMouseEvents = "0";
extent = "159 15";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "0";
tooltipProfile = "GuiToolTipProfile";
toolTip = "This link is bidirectional.";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
internalName = "LinkWalkFlag";
class = "NavMeshLinkFlagButton";
@ -732,194 +832,6 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
variable = "$Nav::Editor::renderVoxels";
};
};
new GuiStackControl() {
internalName = "TestProperties";
position = "7 21";
extent = "186 64";
padding = "2 2 2 2";
new GuiTextCtrl() {
text = "Cover";
profile = "ToolsGuiTextProfile";
extent = "180 20";
minExtent = "8 2";
visible = "1";
};
new GuiTextEditCtrl() {
internalName = "CoverRadius";
text = "10";
profile = "ToolsGuiTextEditProfile";
extent = "40 20";
minExtent = "8 2";
visible = "1";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Radius for cover-finding.";
};
new GuiTextEditCtrl() {
internalName = "CoverPosition";
text = "LocalClientConnection.getControlObject().getPosition();";
profile = "ToolsGuiTextEditProfile";
extent = "140 20";
minExtent = "8 2";
visible = "1";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Position to find cover from.";
};
new GuiTextCtrl() {
text = "Follow";
profile = "ToolsuiTextProfile";
extent = "180 20";
minExtent = "8 2";
visible = "1";
};
new GuiTextEditCtrl() {
internalName = "FollowRadius";
text = "1";
profile = "ToolsGuiTextEditProfile";
extent = "40 20";
minExtent = "8 2";
visible = "1";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Radius for following.";
};
new GuiTextEditCtrl() {
internalName = "FollowObject";
text = "LocalClientConnection.player";
profile = "ToolsGuiTextEditProfile";
extent = "140 20";
minExtent = "8 2";
visible = "1";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Object to follow.";
};
new GuiTextCtrl() {
text = "Movement";
profile = "ToolsGuiTextProfile";
extent = "180 20";
minExtent = "8 2";
visible = "1";
};
new GuiCheckBoxCtrl() {
internalName = "LinkWalkFlag";
class = "NavMeshTestFlagButton";
text = " Walk";
buttonType = "ToggleButton";
useMouseEvents = "0";
extent = "159 15";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "0";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Can this character walk on ground?";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
internalName = "LinkJumpFlag";
class = "NavMeshTestFlagButton";
text = " Jump";
buttonType = "ToggleButton";
useMouseEvents = "0";
extent = "159 15";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "0";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Can this character jump?";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
internalName = "LinkDropFlag";
class = "NavMeshTestFlagButton";
text = " Drop";
buttonType = "ToggleButton";
useMouseEvents = "0";
extent = "159 15";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "0";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Can this character drop over edges?";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
internalName = "LinkLedgeFlag";
class = "NavMeshTestFlagButton";
text = " Ledge";
buttonType = "ToggleButton";
useMouseEvents = "0";
extent = "159 15";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "0";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Can this character jump from ledges?";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
internalName = "LinkClimbFlag";
class = "NavMeshTestFlagButton";
text = " Climb";
buttonType = "ToggleButton";
useMouseEvents = "0";
extent = "159 15";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "0";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Can this character climb?";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
internalName = "LinkTeleportFlag";
class = "NavMeshTestFlagButton";
text = " Teleport";
buttonType = "ToggleButton";
useMouseEvents = "0";
extent = "159 15";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "0";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Can this character teleport?";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
};
};
new GuiMLTextCtrl(NavFieldInfoControl) {
canSaveDynamicFields = "0";

View file

@ -77,68 +77,5 @@ $guiContent = new GuiControl(NavEditorToolbar,EditorGuiGroup) {
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
text = "Mesh";
groupNum = "-1";
buttonType = "ToggleButton";
useMouseEvents = "0";
position = "167 1";
extent = "50 30";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "1";
variable = "$Nav::Editor::renderMesh";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
internalName = "MeshButton";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
text = "Portals";
groupNum = "-1";
buttonType = "ToggleButton";
useMouseEvents = "0";
position = "224 1";
extent = "54 30";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "1";
variable = "$Nav::Editor::renderPortals";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
internalName = "PortalButton";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
text = "BV tree";
groupNum = "-1";
buttonType = "ToggleButton";
useMouseEvents = "0";
position = "286 1";
extent = "140 30";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "1";
variable = "$Nav::Editor::renderBVTree";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
internalName = "BVTreeButton";
canSave = "1";
canSaveDynamicFields = "0";
};
};
//--- OBJECT WRITE END ---

View file

@ -57,6 +57,44 @@ function initializeNavEditor()
editorGui = NavEditorGui;
};
new SimSet(NavMeshTools)
{
new NavMeshSelectTool()
{
internalName = "SelectTool";
toolTip = "Edit NavMesh";
buttonImage = "ToolsModule:visibility_toggle_n_image";
};
new OffMeshConnectionTool()
{
internalName = "LinkTool";
toolTip = "Link tool";
buttonImage = "ToolsModule:nav_link_n_image";
};
new CoverTool()
{
internalName = "NavCoverTool";
toolTip = "Cover Tool";
buttonImage = "ToolsModule:nav_cover_n_image";
};
new TileTool()
{
internalName = "TileTool";
toolTip = "Tile selection tool";
buttonImage = "ToolsModule:select_bounds_n_image";
};
new NavMeshTestTool()
{
internalName = "TestTool";
toolTip = "PathFinding Test tool";
buttonImage = "ToolsModule:3rd_person_camera_n_image";
};
};
// Bind shortcuts for the nav editor.
%map = new ActionMap();
%map.bindCmd(keyboard, "1", "ENavEditorSelectModeBtn.performClick();", "");
@ -118,12 +156,12 @@ function EditorGui::SetNavPalletBar()
EWToolsPaletteWindow.setActionMap(WorldEditorInspectorPlugin.map);
//Adds a button to the pallete stack
//Name Icon Click Command Tooltip text Keybind
EWToolsPaletteWindow.addButton("ViewNavMesh", "ToolsModule:visibility_toggle_n_image", "NavEditorGui.prepSelectionMode();", "", "View NavMesh", "1");
EWToolsPaletteWindow.addButton("LinkMode", "ToolsModule:nav_link_n_image", "NavEditorGui.setMode(\"LinkMode\");", "", "Create off-mesh links", "2");
EWToolsPaletteWindow.addButton("CoverMode", "ToolsModule:nav_cover_n_image", "NavEditorGui.setMode(\"CoverMode\");", "", "Edit cover", "3");
EWToolsPaletteWindow.addButton("TileMode", "ToolsModule:select_bounds_n_image", "NavEditorGui.setMode(\"TileMode\");", "", "View tiles", "4");
EWToolsPaletteWindow.addButton("TestMode", "ToolsModule:3rd_person_camera_n_image", "NavEditorGui.setMode(\"TestMode\");", "", "Test pathfinding", "5");
//Name Icon Click Command Tooltip text Keybind
EWToolsPaletteWindow.addButton("EditMode", "ToolsModule:visibility_toggle_n_image", "NavEditorGui.setActiveTool(NavMeshTools->SelectTool);", "", "Edit NavMesh", "1");
EWToolsPaletteWindow.addButton("LinkMode", "ToolsModule:nav_link_n_image", "NavEditorGui.setActiveTool(NavMeshTools->LinkTool);", "", "Create off-mesh links", "2");
EWToolsPaletteWindow.addButton("CoverMode","ToolsModule:nav_cover_n_image", "NavEditorGui.setActiveTool(NavMeshTools->NavCoverTool);", "", "Create Cover Points.", "3");
EWToolsPaletteWindow.addButton("TileMode", "ToolsModule:select_bounds_n_image", "NavEditorGui.setActiveTool(NavMeshTools->TileTool);" , "", "View and Edit Tiles", "4");
EWToolsPaletteWindow.addButton("TestMode", "ToolsModule:3rd_person_camera_n_image", "NavEditorGui.setActiveTool(NavMeshTools->TestTool);", "", "Test pathfinding", "5");
EWToolsPaletteWindow.refresh();
}
@ -135,7 +173,22 @@ function NavEditorPlugin::onActivated(%this)
$Nav::EditorOpen = true;
// Start off in Select mode.
ToolsPaletteArray->NavEditorSelectMode.performClick();
// Callback when the nav editor changes mode. Set the appropriate dynamic
// GUI contents in the properties/actions boxes.
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->SelectActions.setVisible(false);
%actions->LinkActions.setVisible(false);
%actions->CoverActions.setVisible(false);
%actions->TileActions.setVisible(false);
%actions->TestActions.setVisible(false);
%properties = NavEditorOptionsWindow->PropertiesBox;
%properties->LinkProperties.setVisible(false);
%properties->TileProperties.setVisible(false);
ENavEditorSelectModeBtn.performClick();
EditorGui.bringToFront(NavEditorGui);
NavEditorGui.setVisible(true);
@ -177,6 +230,9 @@ function NavEditorPlugin::onActivated(%this)
Parent::onActivated(%this);
EditorGui.SetNavPalletBar();
DrawModeSelector.init();
DrawModeSelector.selectDefault();
}
function NavEditorPlugin::onDeactivated(%this)
@ -242,7 +298,7 @@ function NavEditorPlugin::initSettings(%this)
EditorSettings.beginGroup("NavEditor", true);
EditorSettings.setDefaultValue("SpawnClass", "AIPlayer");
EditorSettings.setDefaultValue("SpawnDatablock", "DefaultPlayerData");
EditorSettings.setDefaultValue("SpawnDatablock", "ProtoPlayer");
EditorSettings.endGroup();
}
@ -252,9 +308,6 @@ function NavEditorPlugin::readSettings(%this)
EditorSettings.beginGroup("NavEditor", true);
// Currently these are globals because of the way they are accessed in navMesh.cpp.
$Nav::Editor::renderMesh = EditorSettings.value("RenderMesh");
$Nav::Editor::renderPortals = EditorSettings.value("RenderPortals");
$Nav::Editor::renderBVTree = EditorSettings.value("RenderBVTree");
NavEditorGui.spawnClass = EditorSettings.value("SpawnClass");
NavEditorGui.spawnDatablock = EditorSettings.value("SpawnDatablock");
NavEditorGui.backgroundBuild = EditorSettings.value("BackgroundBuild");
@ -274,9 +327,6 @@ function NavEditorPlugin::writeSettings(%this)
{
EditorSettings.beginGroup("NavEditor", true);
EditorSettings.setValue("RenderMesh", $Nav::Editor::renderMesh);
EditorSettings.setValue("RenderPortals", $Nav::Editor::renderPortals);
EditorSettings.setValue("RenderBVTree", $Nav::Editor::renderBVTree);
EditorSettings.setValue("SpawnClass", NavEditorGui.spawnClass);
EditorSettings.setValue("SpawnDatablock", NavEditorGui.spawnDatablock);
EditorSettings.setValue("BackgroundBuild", NavEditorGui.backgroundBuild);

View file

@ -296,79 +296,314 @@ function NavEditorGui::showSidePanel()
%parent.panelHidden = false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------
// NAVMESHSELECTTOOL
//------------------------------------------------------
function NavEditorGui::onModeSet(%this, %mode)
function NavMeshSelectTool::onActivated(%this)
{
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
NavInspector.setVisible(true);
%actions->SelectActions.setVisible(true);
NavInspector.inspect(NavEditorGui.getMesh());
}
function NavMeshSelectTool::onDeactivated(%this)
{
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->SelectActions.setVisible(false);
}
function NavMeshSelectTool::onNavMeshSelected(%this)
{
NavTreeView.clearSelection();
if(isObject(NavEditorGui.getMesh()))
NavTreeView.selectItem(NavEditorGui.getMesh());
// we set the naveditorgui navmesh in source so just get it
// and update here.
NavInspector.inspect(NavEditorGui.getMesh());
}
//------------------------------------------------------
// OffMeshConnectionTool
//------------------------------------------------------
function OffMeshConnectionTool::onActivated(%this)
{
// Callback when the nav editor changes mode. Set the appropriate dynamic
// GUI contents in the properties/actions boxes.
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->SelectActions.setVisible(false);
%actions->LinkActions.setVisible(false);
%actions->CoverActions.setVisible(false);
%actions->TileActions.setVisible(false);
%actions->TestActions.setVisible(false);
%properties = NavEditorOptionsWindow->PropertiesBox;
%actions->LinkActions.setVisible(true);
%properties->LinkProperties.setVisible(true);
}
function OffMeshConnectionTool::onDeactivated(%this)
{
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->LinkActions.setVisible(false);
%properties = NavEditorOptionsWindow->PropertiesBox;
%properties->LinkProperties.setVisible(false);
%properties->TileProperties.setVisible(false);
%properties->TestProperties.setVisible(false);
}
switch$(%mode)
function OffMeshConnectionTool::updateLinkFlags(%this)
{
%properties = NavEditorOptionsWindow-->LinkProperties;
%this.setLinkProperties(getLinkFlags(%properties), %properties->LinkBiDirection.isStateOn(), %properties->LinkRadius.getValue());
}
function updateLinkData(%control, %flags, %biDir, %radius)
{
%control->LinkRadius.setActive(true);
%control->LinkBiDirection.setActive(true);
%control->LinkWalkFlag.setActive(true);
%control->LinkJumpFlag.setActive(true);
%control->LinkDropFlag.setActive(true);
%control->LinkLedgeFlag.setActive(true);
%control->LinkClimbFlag.setActive(true);
%control->LinkTeleportFlag.setActive(true);
%control->LinkRadius.setValue(%radius);
%control->LinkBiDirection.setStateOn(%biDir);
%control->LinkWalkFlag.setStateOn(%flags & $Nav::WalkFlag);
%control->LinkJumpFlag.setStateOn(%flags & $Nav::JumpFlag);
%control->LinkDropFlag.setStateOn(%flags & $Nav::DropFlag);
%control->LinkLedgeFlag.setStateOn(%flags & $Nav::LedgeFlag);
%control->LinkClimbFlag.setStateOn(%flags & $Nav::ClimbFlag);
%control->LinkTeleportFlag.setStateOn(%flags & $Nav::TeleportFlag);
}
function getLinkFlags(%control)
{
return (%control->LinkWalkFlag.isStateOn() ? $Nav::WalkFlag : 0) |
(%control->LinkJumpFlag.isStateOn() ? $Nav::JumpFlag : 0) |
(%control->LinkDropFlag.isStateOn() ? $Nav::DropFlag : 0) |
(%control->LinkLedgeFlag.isStateOn() ? $Nav::LedgeFlag : 0) |
(%control->LinkClimbFlag.isStateOn() ? $Nav::ClimbFlag : 0) |
(%control->LinkTeleportFlag.isStateOn() ? $Nav::TeleportFlag : 0);
}
function disableLinkData(%control)
{
%control->LinkRadius.setActive(false);
%control->LinkBiDirection.setActive(false);
%control->LinkWalkFlag.setActive(false);
%control->LinkJumpFlag.setActive(false);
%control->LinkDropFlag.setActive(false);
%control->LinkLedgeFlag.setActive(false);
%control->LinkClimbFlag.setActive(false);
%control->LinkTeleportFlag.setActive(false);
}
function OffMeshConnectionTool::onLinkSelected(%this, %flags, %biDir, %radius)
{
updateLinkData(NavEditorOptionsWindow-->LinkProperties, %flags, %biDir, %radius);
}
function OffMeshConnectionTool::onLinkDeselected(%this)
{
disableLinkData(NavEditorOptionsWindow-->LinkProperties);
}
function OffMeshConnectionTool::updateRadius(%this)
{
%this.updateLinkFlags();
}
function NavMeshLinkFlagButton::onClick(%this)
{
NavMeshTools->LinkTool.updateLinkFlags();
}
function NavMeshLinkBiDirection::onClick(%this)
{
NavMeshTools->LinkTool.updateLinkFlags();
}
//------------------------------------------------------
// CoverTool
//------------------------------------------------------
function CoverTool::onActivated(%this)
{
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->CoverActions.setVisible(true);
}
function CoverTool::onDeactivated(%this)
{
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->CoverActions.setVisible(false);
}
//------------------------------------------------------
// NAVMESHTESTTOOL
//------------------------------------------------------
function NavMeshTestTool::onActivated(%this)
{
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->TestActions.setVisible(true);
%classList = enumerateConsoleClasses("Player") TAB enumerateConsoleClasses("Vehicle");
//echo(%classList);
SpawnClassSelector.clear();
foreach$(%class in %classList)
{
case "SelectMode":
if(%class !$= "Vehicle") // vehicle doesnt work, purely virtual class.
SpawnClassSelector.add(%class);
}
SpawnClassSelector.setFirstSelected(true);
}
function NavMeshTestTool::onDeactivated(%this)
{
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->TestActions.setVisible(false);
}
function NavMeshTestTool::onPlayerSelected(%this)
{
if (!isObject(%this.getPlayer().aiController))
{
%this.getPlayer().aiController = new AIController(){ ControllerData = %this.getPlayer().getDatablock().aiControllerData; };
%this.getPlayer().setAIController(%this.getPlayer().aiController);
}
if(%this.getPlayer().isMemberOfClass("AIPlayer"))
{
NavInspector.inspect(%this.getPlayer());
NavInspector.setVisible(true);
%actions->SelectActions.setVisible(true);
case "LinkMode":
%actions->LinkActions.setVisible(true);
%properties->LinkProperties.setVisible(true);
case "CoverMode":
//
%actions->CoverActions.setVisible(true);
case "TileMode":
%actions->TileActions.setVisible(true);
%properties->TileProperties.setVisible(true);
case "TestMode":
%actions->TestActions.setVisible(true);
%properties->TestProperties.setVisible(true);
}
else
{
NavInspector.inspect(%this.getPlayer().getDatablock().aiControllerData);
NavInspector.setVisible(true);
}
NavMeshIgnore(%this.getPlayer(), true);
%this.getPlayer().setDamageState("Enabled");
}
function NavMeshTestTool::onPlayerDeselected(%this)
{
NavInspector.inspect();
}
function NavMeshTestTool::stop(%this)
{
if (isObject(%this.getPlayer().aiController))
%this.getPlayer().aiController.stop();
else
{
%this.getPlayer().stop();
}
}
function NavMeshTestTool::toggleFollow(%this)
{
if(isObject(%this.getFollowObject()) && isObject(%this.getPlayer()))
{
%player = %this.getPlayer();
if(%player.isMemberOfClass("AIPlayer"))
%player.followObject(%this.getFollowObject(), "2.0");
else
%player.getAIController().followObject(%this.getFollowObject(), %player.getDatablock().aiControllerData.mFollowTolerance);
}
}
function NavEditorGui::paletteSync(%this, %mode)
function NavMeshTestTool::followObject(%this)
{
// Synchronise the palette (small buttons on the left) with the actual mode
// the nav editor is in.
%evalShortcut = "ToolsPaletteArray-->" @ %mode @ ".setStateOn(1);";
eval(%evalShortcut);
}
%this.followSelectMode();
}
function NavMeshTestTool::findCover(%this)
{
if(isObject(%this.getPlayer()))
{
%player = %this.getPlayer();
%pos = %player.getPosition();
if(%player.isMemberOfClass("AIPlayer"))
%player.findCover(%pos, CoverRadius.getText());
else
%player.getAIController().findCover(%pos, CoverRadius.getText());
}
}
function SpawnClassSelector::onSelect(%this, %id)
{
%className = %this.getTextById(%id);
NavMeshTools->TestTool.setSpawnClass(%className);
SpawnDatablockSelector.clear();
%classData = %className @ "Data";
if(%className $= "AIPlayer")
{
%classData = "PlayerData";
}
// add the datablocks
for(%i = 0; %i < DataBlockGroup.getCount(); %i++)
{
%obj = DataBlockGroup.getObject(%i);
if( isMemberOfClass( %obj.getClassName(), %classData ))
SpawnDatablockSelector.add(%obj.getName());
}
SpawnDatablockSelector.setFirstSelected(true);
}
function SpawnDatablockSelector::onSelect(%this, %id)
{
%className = %this.getTextById(%id);
NavMeshTools->TestTool.setSpawnDatablock(%className);
}
//------------------------------------------------------
// TILETOOL
//------------------------------------------------------
function TileTool::onActivated(%this)
{
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%properties = NavEditorOptionsWindow->PropertiesBox;
%actions->TileActions.setVisible(true);
%properties->TileProperties.setVisible(true);
}
function TileTool::onDeactivated(%this)
{
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->TileActions.setVisible(false);
%properties = NavEditorOptionsWindow->PropertiesBox;
%properties->TileProperties.setVisible(false);
}
function NavEditorGui::onEscapePressed(%this)
{
return false;
}
function NavEditorGui::selectObject(%this, %obj)
{
NavTreeView.clearSelection();
if(isObject(%obj))
NavTreeView.selectItem(%obj);
%this.onObjectSelected(%obj);
}
function NavEditorGui::onObjectSelected(%this, %obj)
{
if(isObject(%this.selectedObject))
%this.deselect();
%this.selectedObject = %obj;
if(isObject(%obj))
{
%this.selectMesh(%obj);
NavInspector.inspect(%obj);
}
}
function NavEditorGui::deleteMesh(%this)
{
if(isObject(%this.selectedObject))
@ -388,12 +623,12 @@ function NavEditorGui::deleteSelected(%this)
toolsMessageBoxYesNo("Warning",
"Are you sure you want to delete" SPC NavEditorGui.selectedObject.getName(),
"NavEditorGui.deleteMesh();");
case "TestMode":
%this.getPlayer().delete();
%this.onPlayerDeselected();
case "LinkMode":
%this.deleteLink();
%this.isDirty = true;
// case "TestMode":
// %this.getPlayer().delete();
// %this.onPlayerDeselected();
// case "LinkMode":
// %this.deleteLink();
// %this.isDirty = true;
}
}
@ -415,99 +650,6 @@ function NavEditorGui::buildLinks(%this)
}
}
function updateLinkData(%control, %flags)
{
%control->LinkWalkFlag.setActive(true);
%control->LinkJumpFlag.setActive(true);
%control->LinkDropFlag.setActive(true);
%control->LinkLedgeFlag.setActive(true);
%control->LinkClimbFlag.setActive(true);
%control->LinkTeleportFlag.setActive(true);
%control->LinkWalkFlag.setStateOn(%flags & $Nav::WalkFlag);
%control->LinkJumpFlag.setStateOn(%flags & $Nav::JumpFlag);
%control->LinkDropFlag.setStateOn(%flags & $Nav::DropFlag);
%control->LinkLedgeFlag.setStateOn(%flags & $Nav::LedgeFlag);
%control->LinkClimbFlag.setStateOn(%flags & $Nav::ClimbFlag);
%control->LinkTeleportFlag.setStateOn(%flags & $Nav::TeleportFlag);
}
function getLinkFlags(%control)
{
return (%control->LinkWalkFlag.isStateOn() ? $Nav::WalkFlag : 0) |
(%control->LinkJumpFlag.isStateOn() ? $Nav::JumpFlag : 0) |
(%control->LinkDropFlag.isStateOn() ? $Nav::DropFlag : 0) |
(%control->LinkLedgeFlag.isStateOn() ? $Nav::LedgeFlag : 0) |
(%control->LinkClimbFlag.isStateOn() ? $Nav::ClimbFlag : 0) |
(%control->LinkTeleportFlag.isStateOn() ? $Nav::TeleportFlag : 0);
}
function disableLinkData(%control)
{
%control->LinkWalkFlag.setActive(false);
%control->LinkJumpFlag.setActive(false);
%control->LinkDropFlag.setActive(false);
%control->LinkLedgeFlag.setActive(false);
%control->LinkClimbFlag.setActive(false);
%control->LinkTeleportFlag.setActive(false);
}
function NavEditorGui::onLinkSelected(%this, %flags)
{
updateLinkData(NavEditorOptionsWindow-->LinkProperties, %flags);
}
function NavEditorGui::onPlayerSelected(%this, %flags)
{
if (!isObject(%this.getPlayer().aiController) || (isObject(%this.getPlayer().aiController) && !(%this.getPlayer().isMemberOfClass("AIPlayer"))))
{
%this.getPlayer().aiController = new AIController(){ ControllerData = %this.getPlayer().getDatablock().aiControllerData; };
%this.getPlayer().setAIController(%this.getPlayer().aiController);
}
NavMeshIgnore(%this.getPlayer(), true);
%this.getPlayer().setDamageState("Enabled");
updateLinkData(NavEditorOptionsWindow-->TestProperties, %flags);
}
function NavEditorGui::updateLinkFlags(%this)
{
if(isObject(%this.getMesh()))
{
%properties = NavEditorOptionsWindow-->LinkProperties;
%this.setLinkFlags(getLinkFlags(%properties));
%this.isDirty = true;
}
}
function NavEditorGui::updateTestFlags(%this)
{
if(isObject(%this.getPlayer()))
{
%properties = NavEditorOptionsWindow-->TestProperties;
%player = %this.getPlayer();
%player.allowWwalk = %properties->LinkWalkFlag.isStateOn();
%player.allowJump = %properties->LinkJumpFlag.isStateOn();
%player.allowDrop = %properties->LinkDropFlag.isStateOn();
%player.allowLedge = %properties->LinkLedgeFlag.isStateOn();
%player.allowClimb = %properties->LinkClimbFlag.isStateOn();
%player.allowTeleport = %properties->LinkTeleportFlag.isStateOn();
%this.isDirty = true;
}
}
function NavEditorGui::onLinkDeselected(%this)
{
disableLinkData(NavEditorOptionsWindow-->LinkProperties);
}
function NavEditorGui::onPlayerDeselected(%this)
{
disableLinkData(NavEditorOptionsWindow-->TestProperties);
}
function NavEditorGui::createCoverPoints(%this)
{
if(isObject(%this.getMesh()))
@ -526,48 +668,6 @@ function NavEditorGui::deleteCoverPoints(%this)
}
}
function NavEditorGui::findCover(%this)
{
if(%this.getMode() $= "TestMode" && isObject(%this.getPlayer()))
{
%pos = LocalClientConnection.getControlObject().getPosition();
%text = NavEditorOptionsWindow-->TestProperties->CoverPosition.getText();
if(%text !$= "")
%pos = eval("return " @ %text);
%this.getPlayer().getAIController().findCover(%pos, NavEditorOptionsWindow-->TestProperties->CoverRadius.getText());
}
}
function NavEditorGui::followObject(%this)
{
if(%this.getMode() $= "TestMode" && isObject(%this.getPlayer()))
{
%obj = LocalClientConnection.player;
%text = NavEditorOptionsWindow-->TestProperties->FollowObject.getText();
if(%text !$= "")
{
%command = "return " @ %text;
if(!endsWith(%command, ";"))
%command = %command @ ";";
%obj = eval(%command);
if(!isObject(%obj))
toolsMessageBoxOk("Error", "Cannot find object" SPC %text);
}
if(isObject(%obj))
%this.getPlayer().getAIController().followObject(%obj, NavEditorOptionsWindow-->TestProperties->FollowRadius.getText());
}
}
function NavEditorGui::stop(%this)
{
if (isObject(%this.getPlayer().aiController))
%this.getPlayer().aiController.stop();
else
{
NavEditorGui.getPlayer().stop();
}
}
function NavInspector::inspect(%this, %obj)
{
%name = "";
@ -598,13 +698,13 @@ function NavTreeView::onInspect(%this, %obj)
function NavTreeView::onSelect(%this, %obj)
{
NavInspector.inspect(%obj);
NavEditorGui.onObjectSelected(%obj);
NavEditorGui.selectMesh(%obj);
}
function NavEditorGui::prepSelectionMode(%this)
{
%this.setMode("SelectMode");
ToolsPaletteArray-->NavEditorSelectMode.setStateOn(1);
NavEditorGui.setActiveTool(NavMeshTools->SelectTool);
}
//-----------------------------------------------------------------------------
@ -618,16 +718,6 @@ function ENavEditorPaletteButton::onClick(%this)
//-----------------------------------------------------------------------------
function NavMeshLinkFlagButton::onClick(%this)
{
NavEditorGui.updateLinkFlags();
}
function NavMeshTestFlagButton::onClick(%this)
{
NavEditorGui.updateTestFlags();
}
singleton GuiControlProfile(NavEditorProfile)
{
canKeyFocus = true;
@ -635,3 +725,38 @@ singleton GuiControlProfile(NavEditorProfile)
fillColor = "192 192 192 192";
category = "Editor";
};
function DrawModeSelector::init(%this)
{
%this.clear();
%this.add("Draw NavMesh", 0);
%this.add("Draw NavMesh Transparent", 1);
%this.add("Draw NavMesh BVTree", 2);
%this.add("Draw NavMesh Nodes", 3);
%this.add("Draw NavMesh Portals", 4);
%this.add("Draw NavMesh Invis", 5);
%this.add("Draw Mesh", 6);
%this.add("Draw Voxels", 7);
%this.add("Draw Walkable Voxels", 8);
%this.add("Draw Compact Heightfield", 9);
%this.add("Draw Compact Distance", 10);
%this.add("Draw Compact Regions", 11);
%this.add("Draw Region Connections", 12);
%this.add("Draw Raw Contours", 13);
%this.add("Draw Both Contours", 14);
%this.add("Draw Contours", 15);
%this.add("Draw PolyMesh", 16);
%this.add("Draw PolyMesh Detail", 17);
}
function DrawModeSelector::selectDefault(%this)
{
%this.setSelected(0);
}
function DrawModeSelector::onSelect(%this, %id)
{
NavEditorGui.setDrawMode(%id);
}

View file

@ -11,7 +11,6 @@ $paletteId = new GuiControl(NavEditorPalette,EditorGuiGroup) {
canSave = "1";
Visible = "1";
hovertime = "1000";
new GuiBitmapButtonCtrl(ENavEditorSelectModeBtn) {
canSaveDynamicFields = "1";
class = ENavEditorPaletteButton;
@ -26,9 +25,9 @@ $paletteId = new GuiControl(NavEditorPalette,EditorGuiGroup) {
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Command = "NavEditorGui.prepSelectionMode();";
Command = "NavEditorGui.setActiveTool(NavMeshTools->SelectTool);";
tooltipprofile = "GuiToolTipProfile";
ToolTip = "View NavMesh (1).";
ToolTip = "Edit NavMesh (1).";
DetailedDesc = "";
hovertime = "1000";
bitmapAsset = "ToolsModule:visibility_toggle_n_image";
@ -49,10 +48,10 @@ $paletteId = new GuiControl(NavEditorPalette,EditorGuiGroup) {
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Command = "NavEditorGui.setMode(\"LinkMode\");";
Command = "NavEditorGui.setActiveTool(NavMeshTools->LinkTool);";
tooltipprofile = "GuiToolTipProfile";
ToolTip = "Create off-mesh links (2).";
DetailedDesc = "Click to select/add. Shift-click to add multiple end points.";
ToolTip = "Edit Links (2).";
DetailedDesc = "";
hovertime = "1000";
bitmapAsset = "ToolsModule:nav_link_n_image";
buttonType = "RadioButton";
@ -72,7 +71,7 @@ $paletteId = new GuiControl(NavEditorPalette,EditorGuiGroup) {
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Command = "NavEditorGui.setMode(\"CoverMode\");";
Command = "NavEditorGui.setActiveTool(NavMeshTools->NavCoverTool);";
tooltipprofile = "GuiToolTipProfile";
ToolTip = "Edit cover (3).";
DetailedDesc = "";
@ -95,7 +94,7 @@ $paletteId = new GuiControl(NavEditorPalette,EditorGuiGroup) {
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Command = "NavEditorGui.setMode(\"TileMode\");";
Command = "NavEditorGui.setActiveTool(NavMeshTools->TileTool);";
tooltipprofile = "GuiToolTipProfile";
ToolTip = "View tiles (4).";
DetailedDesc = "Click to select.";
@ -118,7 +117,7 @@ $paletteId = new GuiControl(NavEditorPalette,EditorGuiGroup) {
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Command = "NavEditorGui.setMode(\"TestMode\");";
Command = "NavEditorGui.setActiveTool(NavMeshTools->TestTool);";
tooltipprofile = "GuiToolTipProfile";
ToolTip = "Test pathfinding (5).";
DetailedDesc = "Click to select/move character, CTRL-click to spawn, SHIFT-click to deselect.";

View file

@ -4,12 +4,15 @@ option(TORQUE_NAVIGATION "Enable Navigation module" ON)
if(TORQUE_NAVIGATION)
message("Enabling Navigation Module")
file(GLOB_RECURSE TORQUE_NAV_SOURCES "${CMAKE_SOURCE_DIR}/Engine/source/navigation/*.cpp" "${CMAKE_SOURCE_DIR}/Engine/source/navigation/*.h" )
set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_NAV_SOURCES})
file(GLOB TORQUE_NAV_SOURCES "${CMAKE_SOURCE_DIR}/Engine/source/navigation/*.cpp" "${CMAKE_SOURCE_DIR}/Engine/source/navigation/*.h")
if(TORQUE_TOOLS)
file(GLOB_RECURSE TORQUE_NAV_TOOLS "${CMAKE_SOURCE_DIR}/Engine/source/navigation/navMeshTools/*.cpp" "${CMAKE_SOURCE_DIR}/Engine/source/navigation/navMeshTools/*.h")
endif()
set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_NAV_SOURCES} ${TORQUE_NAV_TOOLS})
set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} recast)
set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} recast TORQUE_NAVIGATION_ENABLED)
# Since recast lives elsewhere we need to ensure it is known to Torque when providing a link to it
add_subdirectory("${TORQUE_LIB_ROOT_DIRECTORY}/recast" ${TORQUE_LIB_TARG_DIRECTORY}/recast EXCLUDE_FROM_ALL)
source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source/navigation/" PREFIX "Modules/NAVIGATION" FILES ${TORQUE_NAV_SOURCES})
source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source/navigation/" PREFIX "Modules/NAVIGATION" FILES ${TORQUE_NAV_SOURCES} ${TORQUE_NAV_TOOLS})
endif(TORQUE_NAVIGATION)