Explorar o código

updated drawmodes and rendering

DebugDraw for recast now caches the results
We now have a drawmode dropdown selector
drawmode changes come from the gui itself no longer from console values
all recast drawmodes are supported with the exception of drawmodes that add abilities like navqueries until the nav tester tool is imlpemented.
marauder2k7 hai 1 mes
pai
achega
d1771756c2

+ 261 - 32
Engine/source/navigation/duDebugDrawTorque.cpp

@@ -39,10 +39,9 @@
 duDebugDrawTorque::duDebugDrawTorque()
 {
    VECTOR_SET_ASSOCIATION(mVertList);
+   VECTOR_SET_ASSOCIATION(mDrawCache);
    mPrimType = 0;
-   mQuadsMode = false;
    mVertCount = 0;
-   dMemset(&mStore, 0, sizeof(mStore));
 }
 
 duDebugDrawTorque::~duDebugDrawTorque()
@@ -82,21 +81,19 @@ void duDebugDrawTorque::begin(duDebugDrawPrimitives prim, float size)
    if (!mVertList.empty())
       mVertList.clear();
 
-   mQuadsMode = false;
    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;
+   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;
    }
 
    mDesc.setCullMode(GFXCullCW);
    mDesc.setBlend(false);
-   mDesc.setZReadWrite(true);
 }
 
 /// Submit a vertex
@@ -153,41 +150,273 @@ void duDebugDrawTorque::end()
       return;
 
    const U32 maxVertsPerDraw = GFX_MAX_DYNAMIC_VERTS;
-
-   U32 totalVerts = mVertList.size();
-   U32 stride = 1;
-   U32 stripStart = 0;
+   Box3F box;
+   box.minExtents.set(F32_MAX, F32_MAX, F32_MAX);
+   box.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX);
 
    switch (mPrimType)
    {
-   case GFXLineList:     stride = 2; break;
-   case GFXTriangleList: stride = 3; break;
-   case GFXLineStrip:    stripStart = 1; stride = 1; break;
-   case GFXTriangleStrip:stripStart = 2; stride = 1; break;
-   default:              stride = 1; break;
+   case DU_DRAW_POINTS:
+   {
+      const U32 totalPoints = mVertList.size();
+
+      for (U32 p = 0; p < totalPoints;)
+      {
+         const U32 pointsThisBatch = getMin(maxVertsPerDraw, totalPoints - p);
+         const U32 batchVerts = pointsThisBatch;
+
+         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;
    }
 
-   GFX->setPrimitiveBuffer(NULL);
-   GFX->setStateBlockByDesc(mDesc);
-   GFX->setupGenericShaders(GFXDevice::GSColor);
-
-   for (U32 i = 0; i < totalVerts; i += maxVertsPerDraw)
+   case DU_DRAW_LINES:
    {
-      U32 batchSize = getMin(maxVertsPerDraw, totalVerts - i);
-
-      mVertexBuffer.set(GFX, batchSize, GFXBufferTypeDynamic);
-      GFXVertexPCT* verts = mVertexBuffer.lock();
+      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;
+
+         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;
+   }
 
-      if (verts)
-         dMemcpy(verts, &mVertList[i], sizeof(GFXVertexPCT) * batchSize);
-      mVertexBuffer.unlock();
+   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;
+
+         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;
+   }
 
-      GFX->setVertexBuffer(mVertexBuffer);
+   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;
+
+         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;
+   }
 
-      U32 numPrims = (batchSize / stride) - stripStart;
-      GFX->drawPrimitive((GFXPrimitiveType)mPrimType, 0, numPrims);
    }
 
    mVertList.clear();
 }
 
+void duDebugDrawTorque::clearCache()
+{
+   mDrawCache.clear();
+}
+
+void duDebugDrawTorque::render(SceneRenderState* state)
+{
+   const Frustum& frustum = state->getCameraFrustum();
+
+   for (U32 i = 0; i < mDrawCache.size(); ++i)
+   {
+      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
+      );
+   }
+}
+

+ 19 - 3
Engine/source/navigation/duDebugDrawTorque.h

@@ -40,6 +40,10 @@
 #include "gfx/gfxVertexBuffer.h"
 #endif
 
+#ifndef _SCENERENDERSTATE_H_
+#include "scene/sceneRenderState.h"
+#endif
+
 /**
 * @class duDebugDrawTorque
 *  @brief Implements the duDebugDraw interface in Torque.
@@ -99,17 +103,29 @@ public:
    /// End drawing primitives.
    void end() override;
 
+   void clearCache();
+   void render(SceneRenderState* state);
+
 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];
 
    void _vertex(const float x, const float y, const float z, unsigned int color);
 };

+ 14 - 0
Engine/source/navigation/guiNavEditorCtrl.cpp

@@ -686,6 +686,20 @@ void GuiNavEditorCtrl::setActiveTool(NavMeshTool* tool)
    }
 }
 
+void GuiNavEditorCtrl::setDrawMode(S32 id)
+{
+   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));

+ 2 - 0
Engine/source/navigation/guiNavEditorCtrl.h

@@ -118,6 +118,8 @@ public:
    /// @}
    void setActiveTool(NavMeshTool* tool);
 
+   void setDrawMode(S32 id);
+
 protected:
 
    void _prepRenderImage(SceneManager* sceneGraph, const SceneRenderState* sceneState);

+ 127 - 14
Engine/source/navigation/navMesh.cpp

@@ -179,7 +179,8 @@ NavMesh::NavMesh()
    m_chf(0),
    m_cset(0),
    m_pmesh(0),
-   m_dmesh(0)
+   m_dmesh(0),
+   m_drawMode(DRAWMODE_NAVMESH)
 {
    mTypeMask |= StaticShapeObjectType | MarkerObjectType;
    mFileName = StringTable->EmptyString();
@@ -1526,6 +1527,119 @@ bool NavMesh::testEdgeCover(const Point3F &pos, const VectorF &dir, CoverPointDa
 
 void NavMesh::renderToDrawer()
 {
+   mDbgDraw.clearCache();
+   // Recast debug draw
+   NetObject* no = getServerObject();
+   if (no)
+   {
+      NavMesh* n = static_cast<NavMesh*>(no);
+
+      mDbgDraw.depthMask(false);
+
+      if (n->nm && m_drawMode != DRAWMODE_NAVMESH_TRANS)
+      {
+         if (m_drawMode != DRAWMODE_NAVMESH_INVIS)
+            duDebugDrawNavMesh(&mDbgDraw, *n->nm, 0);
+
+         ////const F32 texScale = 1.0f / (n->mCellSize * 10.0f); this draw mode is useless for us.
+         ////duDebugDrawNavMesh(&mDbgDraw, *n->nm, 0);
+         //if (n->m_geo != NULL)
+         //{
+         //   duDebugDrawTriMeshSlope(&mDbgDraw, n->m_geo->getVerts(), n->m_geo->getVertCount(),
+         //      n->m_geo->getTris(), n->m_geo->getNormals(), n->m_geo->getTriCount(), n->mWalkableSlope, texScale);
+         //}
+      }
+
+      if (n->nm &&
+         (m_drawMode == DRAWMODE_NAVMESH ||
+            m_drawMode == DRAWMODE_NAVMESH_TRANS ||
+            m_drawMode == DRAWMODE_NAVMESH_BVTREE ||
+            m_drawMode == DRAWMODE_NAVMESH_NODES ||
+            m_drawMode == DRAWMODE_NAVMESH_PORTALS ||
+            m_drawMode == DRAWMODE_NAVMESH_INVIS))
+      {
+         if(m_drawMode == DRAWMODE_NAVMESH_BVTREE)
+            duDebugDrawNavMeshBVTree(&mDbgDraw, *n->nm);
+         if(m_drawMode == DRAWMODE_NAVMESH_PORTALS)
+            duDebugDrawNavMeshPortals(&mDbgDraw, *n->nm);
+      }
+
+      mDbgDraw.depthMask(true);
+
+      for (Tile& tile : n->mTiles)
+      {
+         if (tile.chf && m_drawMode == DRAWMODE_COMPACT)
+         {
+            duDebugDrawCompactHeightfieldSolid(&mDbgDraw, *tile.chf);
+         }
+
+         if (tile.chf && m_drawMode == DRAWMODE_COMPACT_DISTANCE)
+         {
+            duDebugDrawCompactHeightfieldDistance(&mDbgDraw, *tile.chf);
+         }
+
+         if (tile.chf && m_drawMode == DRAWMODE_COMPACT_REGIONS)
+         {
+            duDebugDrawCompactHeightfieldRegions(&mDbgDraw, *tile.chf);
+         }
+
+         if (tile.solid && m_drawMode == DRAWMODE_VOXELS)
+         {
+            duDebugDrawHeightfieldSolid(&mDbgDraw, *tile.solid);
+         }
+
+         if (tile.solid && m_drawMode == DRAWMODE_VOXELS_WALKABLE)
+         {
+            duDebugDrawHeightfieldWalkable(&mDbgDraw, *tile.solid);
+         }
+
+         if (tile.cset && m_drawMode == DRAWMODE_RAW_CONTOURS)
+         {
+            mDbgDraw.depthMask(false);
+            duDebugDrawRawContours(&mDbgDraw, *tile.cset);
+            mDbgDraw.depthMask(true);
+         }
+
+         if (tile.cset && m_drawMode == DRAWMODE_BOTH_CONTOURS)
+         {
+            mDbgDraw.depthMask(false);
+            duDebugDrawRawContours(&mDbgDraw, *tile.cset);
+            duDebugDrawContours(&mDbgDraw, *tile.cset);
+            mDbgDraw.depthMask(true);
+         }
+
+         if (tile.cset && m_drawMode == DRAWMODE_CONTOURS)
+         {
+            mDbgDraw.depthMask(false);
+            duDebugDrawContours(&mDbgDraw, *tile.cset);
+            mDbgDraw.depthMask(true);
+         }
+
+         if (tile.chf && tile.cset && m_drawMode == DRAWMODE_REGION_CONNECTIONS)
+         {
+            duDebugDrawCompactHeightfieldRegions(&mDbgDraw, *tile.chf);
+
+            mDbgDraw.depthMask(false);
+            duDebugDrawRegionConnections(&mDbgDraw, *tile.cset);
+            mDbgDraw.depthMask(true);
+         }
+
+         if (tile.pmesh && m_drawMode == DRAWMODE_POLYMESH)
+         {
+            mDbgDraw.depthMask(false);
+            duDebugDrawPolyMesh(&mDbgDraw, *tile.pmesh);
+            mDbgDraw.depthMask(true);
+         }
+
+         if (tile.dmesh && m_drawMode == DRAWMODE_POLYMESH_DETAIL)
+         {
+            mDbgDraw.depthMask(false);
+            duDebugDrawPolyMeshDetail(&mDbgDraw, *tile.dmesh);
+            mDbgDraw.depthMask(true);
+         }
+
+      }
+   }
 }
 
 void NavMesh::cleanup()
@@ -1570,16 +1684,9 @@ void NavMesh::render(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInsta
    {
       NavMesh *n = static_cast<NavMesh*>(no);
 
-      if ((!gEditingMission && n->mAlwaysRender) || (gEditingMission && Con::getBoolVariable("$Nav::Editor::renderMesh", 1)))
+      if ((!gEditingMission && n->mAlwaysRender) || gEditingMission)
       {
-         if (n->nm)
-         {
-            duDebugDrawNavMesh(&mDbgDraw, *n->nm, 0);
-            if (Con::getBoolVariable("$Nav::Editor::renderPortals"))
-               duDebugDrawNavMeshPortals(&mDbgDraw, *n->nm);
-            if (Con::getBoolVariable("$Nav::Editor::renderBVTree"))
-               duDebugDrawNavMeshBVTree(&mDbgDraw, *n->nm);
-         }
+         mDbgDraw.render(state);
       }
    }
 }
@@ -1617,11 +1724,14 @@ void NavMesh::renderLinks(duDebugDraw &dd)
 
 void NavMesh::renderTileData(duDebugDrawTorque &dd, U32 tile)
 {
+   if (tile > mTiles.size())
+      return;
+
    if(nm)
    {
-      duDebugDrawNavMesh(&dd, *nm, 0);
-      if(m_chf)
-         duDebugDrawCompactHeightfieldSolid(&dd, *m_chf);
+      //duDebugDrawNavMesh(&dd, *nm, 0);
+      if(mTiles[tile].chf)
+         duDebugDrawCompactHeightfieldSolid(&dd, *mTiles[tile].chf);
 
       duDebugDrawNavMeshPortals(&dd, *nm);
 
@@ -1669,6 +1779,7 @@ U32 NavMesh::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
    mathWrite(*stream, getTransform());
    mathWrite(*stream, getScale());
    stream->writeFlag(mAlwaysRender);
+   stream->write((U32)m_drawMode);
 
    return retMask;
 }
@@ -1680,8 +1791,10 @@ void NavMesh::unpackUpdate(NetConnection *conn, BitStream *stream)
    mathRead(*stream, &mObjToWorld);
    mathRead(*stream, &mObjScale);
    mAlwaysRender = stream->readFlag();
-
    setTransform(mObjToWorld);
+   U32 draw;
+   stream->read(&draw);
+   m_drawMode = (DrawMode)draw;
 
    renderToDrawer();
 }

+ 26 - 0
Engine/source/navigation/navMesh.h

@@ -113,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;
    /// @}
 
@@ -148,6 +171,8 @@ public:
    /// 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);
 
@@ -409,6 +434,7 @@ protected:
    rcPolyMesh* m_pmesh;
    rcPolyMeshDetail* m_dmesh;
    rcConfig m_cfg;
+   DrawMode m_drawMode;
 
    void cleanup();
 };

+ 0 - 5
Engine/source/navigation/navMeshTools/tileTool.cpp

@@ -45,11 +45,6 @@ void TileTool::on3DMouseDown(const Gui3DMouseEvent& evt)
    if (gServerContainer.castRay(start, end, StaticObjectType, &ri))
    {
       mSelTile = mNavMesh->getTile(ri.point);
-      if (mSelTile != -1)
-      {
-         mNavMesh->renderTileData(mNavMesh->mDbgDraw, mSelTile);
-         //mNavMesh->buildTile(tile); // Immediate rebuild
-      }
    }
 }
 

+ 47 - 0
Engine/source/navigation/recastPolyList.cpp

@@ -33,6 +33,10 @@ RecastPolyList::RecastPolyList() : mChunkyMesh(0)
    verts = NULL;
    vertcap = 0;
 
+   nnormals = 0;
+   normals = NULL;
+   normalcap = 0;
+
    ntris = 0;
    tris = NULL;
    tricap = 0;
@@ -73,6 +77,11 @@ void RecastPolyList::clear()
    verts = NULL;
    vertcap = 0;
 
+   nnormals = 0;
+   delete[] normals;
+   normals = NULL;
+   normalcap = 0;
+
    ntris = 0;
    delete[] tris;
    tris = NULL;
@@ -156,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++;
 }
 
@@ -169,6 +211,11 @@ const F32 *RecastPolyList::getVerts() const
    return verts;
 }
 
+const F32* RecastPolyList::getNormals() const
+{
+   return normals;
+}
+
 U32 RecastPolyList::getTriCount() const
 {
    return ntris;

+ 9 - 0
Engine/source/navigation/recastPolyList.h

@@ -61,6 +61,8 @@ public:
    U32 getVertCount() const;
    const F32 *getVerts() const;
 
+   const F32* getNormals() const;
+
    U32 getTriCount() const;
    const S32 *getTris() const;
 
@@ -85,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

+ 6 - 0
Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui

@@ -292,6 +292,12 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
             Extent = "86 18";
             text = "Actions";
          };
+         new GuiPopUpMenuCtrl(DrawModeSelector) {
+            position = "155 0";
+            extent = "189 20";
+            profile = "ToolsGuiPopUpMenuProfile";
+            tooltipProfile = "GuiToolTipProfile";
+         };
          new GuiStackControl()
          {
             internalName = "SelectActions";

+ 3 - 0
Templates/BaseGame/game/tools/navEditor/main.tscript

@@ -188,6 +188,9 @@ function NavEditorPlugin::onActivated(%this)
 
    Parent::onActivated(%this);
    EditorGui.SetNavPalletBar();
+
+   DrawModeSelector.init();
+   DrawModeSelector.selectDefault();
 }
 
 function NavEditorPlugin::onDeactivated(%this)

+ 35 - 0
Templates/BaseGame/game/tools/navEditor/navEditor.tscript

@@ -673,3 +673,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);
+}