Skip to content

Instantly share code, notes, and snippets.

@SymbolixDEV
Created May 14, 2013 16:14
Show Gist options
  • Save SymbolixDEV/5577226 to your computer and use it in GitHub Desktop.
Save SymbolixDEV/5577226 to your computer and use it in GitHub Desktop.
MMAPS.PATCH
commit eb20f56ab7a0969099cfab84a65219d251790140
Author: Venugh <venugh@gmx.net>
Date: Fri Aug 3 06:37:10 2012 +0200
mmaps patch v 1.0.1
diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt
index d658ba4..48a2fd5 100644
--- a/dep/CMakeLists.txt
+++ b/dep/CMakeLists.txt
@@ -34,6 +34,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows")
endif()
add_subdirectory(g3dlite)
+add_subdirectory(recastnavigation)
if(SERVERS)
add_subdirectory(gsoap)
diff --git a/dep/PackageList.txt b/dep/PackageList.txt
index e5003ec..a787eb0 100644
--- a/dep/PackageList.txt
+++ b/dep/PackageList.txt
@@ -39,3 +39,7 @@ zlib (A Massively Spiffy Yet Delicately Unobtrusive Compression Library)
gSOAP (a portable development toolkit for C and C++ XML Web services and XML data bindings)
http://gsoap2.sourceforge.net/
Version: 2.8.2
+
+recastnavigation (Recast is state of the art navigation mesh construction toolset for games)
+ http://code.google.com/p/recastnavigation/
+ Version: 1.4
diff --git a/dep/recastnavigation/CMakeLists.txt b/dep/recastnavigation/CMakeLists.txt
new file mode 100644
index 0000000..4a406f7
--- /dev/null
+++ b/dep/recastnavigation/CMakeLists.txt
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2005-2011 MaNGOS project <http://getmangos.com/>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+add_subdirectory(Detour)
+add_subdirectory(Recast)
diff --git a/dep/recastnavigation/Detour/CMakeLists.txt b/dep/recastnavigation/Detour/CMakeLists.txt
new file mode 100644
index 0000000..c09e0e3
--- /dev/null
+++ b/dep/recastnavigation/Detour/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+set(Detour_STAT_SRCS
+ DetourAlloc.cpp
+ DetourCommon.cpp
+ DetourNavMesh.cpp
+ DetourNavMeshBuilder.cpp
+ DetourNavMeshQuery.cpp
+ DetourNode.cpp
+)
+
+if(WIN32)
+ include_directories(
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ )
+endif()
+
+add_library(Detour STATIC ${Detour_STAT_SRCS})
+
+target_link_libraries(Detour ${ZLIB_LIBRARIES})
diff --git a/dep/recastnavigation/Detour/DetourAlloc.cpp b/dep/recastnavigation/Detour/DetourAlloc.cpp
new file mode 100644
index 0000000..5f671df
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourAlloc.cpp
@@ -0,0 +1,50 @@
+//
+// 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 <stdlib.h>
+#include "DetourAlloc.h"
+
+static void *dtAllocDefault(int size, dtAllocHint)
+{
+ return malloc(size);
+}
+
+static void dtFreeDefault(void *ptr)
+{
+ free(ptr);
+}
+
+static dtAllocFunc* sAllocFunc = dtAllocDefault;
+static dtFreeFunc* sFreeFunc = dtFreeDefault;
+
+void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc)
+{
+ sAllocFunc = allocFunc ? allocFunc : dtAllocDefault;
+ sFreeFunc = freeFunc ? freeFunc : dtFreeDefault;
+}
+
+void* dtAlloc(int size, dtAllocHint hint)
+{
+ return sAllocFunc(size, hint);
+}
+
+void dtFree(void* ptr)
+{
+ if (ptr)
+ sFreeFunc(ptr);
+}
diff --git a/dep/recastnavigation/Detour/DetourAlloc.h b/dep/recastnavigation/Detour/DetourAlloc.h
new file mode 100644
index 0000000..8693475
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourAlloc.h
@@ -0,0 +1,36 @@
+//
+// 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 DETOURALLOCATOR_H
+#define DETOURALLOCATOR_H
+
+enum dtAllocHint
+{
+ DT_ALLOC_PERM, // Memory persist after a function call.
+ DT_ALLOC_TEMP // Memory used temporarily within a function.
+};
+
+typedef void* (dtAllocFunc)(int size, dtAllocHint hint);
+typedef void (dtFreeFunc)(void* ptr);
+
+void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc);
+
+void* dtAlloc(int size, dtAllocHint hint);
+void dtFree(void* ptr);
+
+#endif
diff --git a/dep/recastnavigation/Detour/DetourAssert.h b/dep/recastnavigation/Detour/DetourAssert.h
new file mode 100644
index 0000000..709ebd9
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourAssert.h
@@ -0,0 +1,33 @@
+//
+// 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 DETOURASSERT_H
+#define DETOURASSERT_H
+
+// Note: This header file's only purpose is to include define assert.
+// Feel free to change the file and include your own implementation instead.
+
+#ifdef NDEBUG
+// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
+# define dtAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false)
+#else
+# include <assert.h>
+# define dtAssert assert
+#endif
+
+#endif // DETOURASSERT_H
diff --git a/dep/recastnavigation/Detour/DetourCommon.cpp b/dep/recastnavigation/Detour/DetourCommon.cpp
new file mode 100644
index 0000000..c0b973e
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourCommon.cpp
@@ -0,0 +1,329 @@
+//
+// 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 <math.h>
+#include "DetourCommon.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+float dtSqrt(float x)
+{
+ return sqrtf(x);
+}
+
+void dtClosestPtPointTriangle(float* closest, const float* p,
+ const float* a, const float* b, const float* c)
+{
+ // Check if P in vertex region outside A
+ float ab[3], ac[3], ap[3];
+ dtVsub(ab, b, a);
+ dtVsub(ac, c, a);
+ dtVsub(ap, p, a);
+ float d1 = dtVdot(ab, ap);
+ float d2 = dtVdot(ac, ap);
+ if (d1 <= 0.0f && d2 <= 0.0f)
+ {
+ // barycentric coordinates (1,0,0)
+ dtVcopy(closest, a);
+ return;
+ }
+
+ // Check if P in vertex region outside B
+ float bp[3];
+ dtVsub(bp, p, b);
+ float d3 = dtVdot(ab, bp);
+ float d4 = dtVdot(ac, bp);
+ if (d3 >= 0.0f && d4 <= d3)
+ {
+ // barycentric coordinates (0,1,0)
+ dtVcopy(closest, b);
+ return;
+ }
+
+ // Check if P in edge region of AB, if so return projection of P onto AB
+ float vc = d1*d4 - d3*d2;
+ if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
+ {
+ // barycentric coordinates (1-v,v,0)
+ float v = d1 / (d1 - d3);
+ closest[0] = a[0] + v * ab[0];
+ closest[1] = a[1] + v * ab[1];
+ closest[2] = a[2] + v * ab[2];
+ return;
+ }
+
+ // Check if P in vertex region outside C
+ float cp[3];
+ dtVsub(cp, p, c);
+ float d5 = dtVdot(ab, cp);
+ float d6 = dtVdot(ac, cp);
+ if (d6 >= 0.0f && d5 <= d6)
+ {
+ // barycentric coordinates (0,0,1)
+ dtVcopy(closest, c);
+ return;
+ }
+
+ // Check if P in edge region of AC, if so return projection of P onto AC
+ float vb = d5*d2 - d1*d6;
+ if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
+ {
+ // barycentric coordinates (1-w,0,w)
+ float w = d2 / (d2 - d6);
+ closest[0] = a[0] + w * ac[0];
+ closest[1] = a[1] + w * ac[1];
+ closest[2] = a[2] + w * ac[2];
+ return;
+ }
+
+ // Check if P in edge region of BC, if so return projection of P onto BC
+ float va = d3*d6 - d5*d4;
+ if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
+ {
+ // barycentric coordinates (0,1-w,w)
+ float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
+ closest[0] = b[0] + w * (c[0] - b[0]);
+ closest[1] = b[1] + w * (c[1] - b[1]);
+ closest[2] = b[2] + w * (c[2] - b[2]);
+ return;
+ }
+
+ // P inside face region. Compute Q through its barycentric coordinates (u,v,w)
+ float denom = 1.0f / (va + vb + vc);
+ float v = vb * denom;
+ float w = vc * denom;
+ closest[0] = a[0] + ab[0] * v + ac[0] * w;
+ closest[1] = a[1] + ab[1] * v + ac[1] * w;
+ closest[2] = a[2] + ab[2] * v + ac[2] * w;
+}
+
+bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
+ const float* verts, int nverts,
+ float& tmin, float& tmax,
+ int& segMin, int& segMax)
+{
+ static const float EPS = 0.00000001f;
+
+ tmin = 0;
+ tmax = 1;
+ segMin = -1;
+ segMax = -1;
+
+ float dir[3];
+ dtVsub(dir, p1, p0);
+
+ for (int i = 0, j = nverts-1; i < nverts; j=i++)
+ {
+ float edge[3], diff[3];
+ dtVsub(edge, &verts[i*3], &verts[j*3]);
+ dtVsub(diff, p0, &verts[j*3]);
+ const float n = dtVperp2D(edge, diff);
+ const float d = dtVperp2D(dir, edge);
+ if (fabsf(d) < EPS)
+ {
+ // S is nearly parallel to this edge
+ if (n < 0)
+ return false;
+ else
+ continue;
+ }
+ const float t = n / d;
+ if (d < 0)
+ {
+ // segment S is entering across this edge
+ if (t > tmin)
+ {
+ tmin = t;
+ segMin = j;
+ // S enters after leaving polygon
+ if (tmin > tmax)
+ return false;
+ }
+ }
+ else
+ {
+ // segment S is leaving across this edge
+ if (t < tmax)
+ {
+ tmax = t;
+ segMax = j;
+ // S leaves before entering polygon
+ if (tmax < tmin)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t)
+{
+ float pqx = q[0] - p[0];
+ float pqz = q[2] - p[2];
+ float dx = pt[0] - p[0];
+ float dz = pt[2] - p[2];
+ float d = pqx*pqx + pqz*pqz;
+ t = pqx*dx + pqz*dz;
+ if (d > 0) t /= d;
+ if (t < 0) t = 0;
+ else if (t > 1) t = 1;
+ dx = p[0] + t*pqx - pt[0];
+ dz = p[2] + t*pqz - pt[2];
+ return dx*dx + dz*dz;
+}
+
+void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts)
+{
+ tc[0] = 0.0f;
+ tc[1] = 0.0f;
+ tc[2] = 0.0f;
+ for (int j = 0; j < nidx; ++j)
+ {
+ const float* v = &verts[idx[j]*3];
+ tc[0] += v[0];
+ tc[1] += v[1];
+ tc[2] += v[2];
+ }
+ const float s = 1.0f / nidx;
+ tc[0] *= s;
+ tc[1] *= s;
+ tc[2] *= s;
+}
+
+bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h)
+{
+ float v0[3], v1[3], v2[3];
+ dtVsub(v0, c,a);
+ dtVsub(v1, b,a);
+ dtVsub(v2, p,a);
+
+ const float dot00 = dtVdot2D(v0, v0);
+ const float dot01 = dtVdot2D(v0, v1);
+ const float dot02 = dtVdot2D(v0, v2);
+ const float dot11 = dtVdot2D(v1, v1);
+ const float dot12 = dtVdot2D(v1, v2);
+
+ // Compute barycentric coordinates
+ const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
+ const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ const float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // The (sloppy) epsilon is needed to allow to get height of points which
+ // are interpolated along the edges of the triangles.
+ static const float EPS = 1e-4f;
+
+ // If point lies inside the triangle, return interpolated ycoord.
+ if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
+ {
+ h = a[1] + v0[1]*u + v1[1]*v;
+ return true;
+ }
+
+ return false;
+}
+
+bool dtPointInPolygon(const float* pt, const float* verts, const int nverts)
+{
+ // TODO: Replace pnpoly with triArea2D tests?
+ int i, j;
+ bool c = false;
+ for (i = 0, j = nverts-1; i < nverts; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
+ (pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ }
+ return c;
+}
+
+bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
+ float* ed, float* et)
+{
+ // TODO: Replace pnpoly with triArea2D tests?
+ int i, j;
+ bool c = false;
+ for (i = 0, j = nverts-1; i < nverts; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
+ (pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ ed[j] = dtDistancePtSegSqr2D(pt, vj, vi, et[j]);
+ }
+ return c;
+}
+
+static void projectPoly(const float* axis, const float* poly, const int npoly,
+ float& rmin, float& rmax)
+{
+ rmin = rmax = dtVdot2D(axis, &poly[0]);
+ for (int i = 1; i < npoly; ++i)
+ {
+ const float d = dtVdot2D(axis, &poly[i*3]);
+ rmin = dtMin(rmin, d);
+ rmax = dtMax(rmax, d);
+ }
+}
+
+inline bool overlapRange(const float amin, const float amax,
+ const float bmin, const float bmax,
+ const float eps)
+{
+ return ((amin+eps) > bmax || (amax-eps) < bmin) ? false : true;
+}
+
+bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
+ const float* polyb, const int npolyb)
+{
+ const float eps = 1e-4f;
+
+ for (int i = 0, j = npolya-1; i < npolya; j=i++)
+ {
+ const float* va = &polya[j*3];
+ const float* vb = &polya[i*3];
+ const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
+ float amin,amax,bmin,bmax;
+ projectPoly(n, polya, npolya, amin,amax);
+ projectPoly(n, polyb, npolyb, bmin,bmax);
+ if (!overlapRange(amin,amax, bmin,bmax, eps))
+ {
+ // Found separating axis
+ return false;
+ }
+ }
+ for (int i = 0, j = npolyb-1; i < npolyb; j=i++)
+ {
+ const float* va = &polyb[j*3];
+ const float* vb = &polyb[i*3];
+ const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
+ float amin,amax,bmin,bmax;
+ projectPoly(n, polya, npolya, amin,amax);
+ projectPoly(n, polyb, npolyb, bmin,bmax);
+ if (!overlapRange(amin,amax, bmin,bmax, eps))
+ {
+ // Found separating axis
+ return false;
+ }
+ }
+ return true;
+}
+
diff --git a/dep/recastnavigation/Detour/DetourCommon.h b/dep/recastnavigation/Detour/DetourCommon.h
new file mode 100644
index 0000000..3cee3f6
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourCommon.h
@@ -0,0 +1,248 @@
+//
+// 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 DETOURCOMMON_H
+#define DETOURCOMMON_H
+
+template<class T> inline void dtSwap(T& a, T& b) { T t = a; a = b; b = t; }
+template<class T> inline T dtMin(T a, T b) { return a < b ? a : b; }
+template<class T> inline T dtMax(T a, T b) { return a > b ? a : b; }
+template<class T> inline T dtAbs(T a) { return a < 0 ? -a : a; }
+template<class T> inline T dtSqr(T a) { return a*a; }
+template<class T> inline T dtClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
+
+float dtSqrt(float x);
+
+inline void dtVcross(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+inline float dtVdot(const float* v1, const float* v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+inline void dtVmad(float* dest, const float* v1, const float* v2, const float s)
+{
+ dest[0] = v1[0]+v2[0]*s;
+ dest[1] = v1[1]+v2[1]*s;
+ dest[2] = v1[2]+v2[2]*s;
+}
+
+inline void dtVlerp(float* dest, const float* v1, const float* v2, const float t)
+{
+ dest[0] = v1[0]+(v2[0]-v1[0])*t;
+ dest[1] = v1[1]+(v2[1]-v1[1])*t;
+ dest[2] = v1[2]+(v2[2]-v1[2])*t;
+}
+
+inline void dtVadd(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]+v2[0];
+ dest[1] = v1[1]+v2[1];
+ dest[2] = v1[2]+v2[2];
+}
+
+inline void dtVsub(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]-v2[0];
+ dest[1] = v1[1]-v2[1];
+ dest[2] = v1[2]-v2[2];
+}
+
+inline void dtVscale(float* dest, const float* v, const float t)
+{
+ dest[0] = v[0]*t;
+ dest[1] = v[1]*t;
+ dest[2] = v[2]*t;
+}
+
+inline void dtVmin(float* mn, const float* v)
+{
+ mn[0] = dtMin(mn[0], v[0]);
+ mn[1] = dtMin(mn[1], v[1]);
+ mn[2] = dtMin(mn[2], v[2]);
+}
+
+inline void dtVmax(float* mx, const float* v)
+{
+ mx[0] = dtMax(mx[0], v[0]);
+ mx[1] = dtMax(mx[1], v[1]);
+ mx[2] = dtMax(mx[2], v[2]);
+}
+
+inline void dtVset(float* dest, const float x, const float y, const float z)
+{
+ dest[0] = x; dest[1] = y; dest[2] = z;
+}
+
+inline void dtVcopy(float* dest, const float* a)
+{
+ dest[0] = a[0];
+ dest[1] = a[1];
+ dest[2] = a[2];
+}
+
+inline float dtVlen(const float* v)
+{
+ return dtSqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
+}
+
+inline float dtVlenSqr(const float* v)
+{
+ return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
+}
+
+inline float dtVdist(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1];
+ const float dz = v2[2] - v1[2];
+ return dtSqrt(dx*dx + dy*dy + dz*dz);
+}
+
+inline float dtVdistSqr(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1];
+ const float dz = v2[2] - v1[2];
+ return dx*dx + dy*dy + dz*dz;
+}
+
+inline float dtVdist2D(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dz = v2[2] - v1[2];
+ return dtSqrt(dx*dx + dz*dz);
+}
+
+inline float dtVdist2DSqr(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dz = v2[2] - v1[2];
+ return dx*dx + dz*dz;
+}
+
+inline void dtVnormalize(float* v)
+{
+ float d = 1.0f / dtSqrt(dtSqr(v[0]) + dtSqr(v[1]) + dtSqr(v[2]));
+ v[0] *= d;
+ v[1] *= d;
+ v[2] *= d;
+}
+
+inline bool dtVequal(const float* p0, const float* p1)
+{
+ static const float thr = dtSqr(1.0f/16384.0f);
+ const float d = dtVdistSqr(p0, p1);
+ return d < thr;
+}
+
+inline unsigned int dtNextPow2(unsigned int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v;
+}
+
+inline unsigned int dtIlog2(unsigned int v)
+{
+ unsigned int r;
+ unsigned int shift;
+ r = (v > 0xffff) << 4; v >>= r;
+ shift = (v > 0xff) << 3; v >>= shift; r |= shift;
+ shift = (v > 0xf) << 2; v >>= shift; r |= shift;
+ shift = (v > 0x3) << 1; v >>= shift; r |= shift;
+ r |= (v >> 1);
+ return r;
+}
+
+inline int dtAlign4(int x) { return (x+3) & ~3; }
+
+inline int dtOppositeTile(int side) { return (side+4) & 0x7; }
+
+inline float dtVdot2D(const float* u, const float* v)
+{
+ return u[0]*v[0] + u[2]*v[2];
+}
+
+inline float dtVperp2D(const float* u, const float* v)
+{
+ return u[2]*v[0] - u[0]*v[2];
+}
+
+inline float dtTriArea2D(const float* a, const float* b, const float* c)
+{
+ const float abx = b[0] - a[0];
+ const float abz = b[2] - a[2];
+ const float acx = c[0] - a[0];
+ const float acz = c[2] - a[2];
+ return acx*abz - abx*acz;
+}
+
+inline bool dtOverlapQuantBounds(const unsigned short amin[3], const unsigned short amax[3],
+ const unsigned short bmin[3], const unsigned short bmax[3])
+{
+ 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;
+ overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
+ return overlap;
+}
+
+inline bool dtOverlapBounds(const float* amin, const float* amax,
+ const float* bmin, const float* bmax)
+{
+ 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;
+ overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
+ return overlap;
+}
+
+void dtClosestPtPointTriangle(float* closest, const float* p,
+ const float* a, const float* b, const float* c);
+
+bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h);
+
+bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
+ const float* verts, int nverts,
+ float& tmin, float& tmax,
+ int& segMin, int& segMax);
+
+bool dtPointInPolygon(const float* pt, const float* verts, const int nverts);
+
+bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
+ float* ed, float* et);
+
+float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t);
+
+void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts);
+
+bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
+ const float* polyb, const int npolyb);
+
+#endif // DETOURCOMMON_H
diff --git a/dep/recastnavigation/Detour/DetourNavMesh.cpp b/dep/recastnavigation/Detour/DetourNavMesh.cpp
new file mode 100644
index 0000000..e139e3f
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMesh.cpp
@@ -0,0 +1,1235 @@
+//
+// 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 <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include "DetourNavMesh.h"
+#include "DetourNode.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <new>
+
+
+inline bool overlapSlabs(const float* amin, const float* amax,
+ const float* bmin, const float* bmax,
+ const float px, const float py)
+{
+ // Check for horizontal overlap.
+ // The segment is shrunken a little so that slabs which touch
+ // at end points are not connected.
+ const float minx = dtMax(amin[0]+px,bmin[0]+px);
+ const float maxx = dtMin(amax[0]-px,bmax[0]-px);
+ if (minx > maxx)
+ return false;
+
+ // Check vertical overlap.
+ const float ad = (amax[1]-amin[1]) / (amax[0]-amin[0]);
+ const float ak = amin[1] - ad*amin[0];
+ const float bd = (bmax[1]-bmin[1]) / (bmax[0]-bmin[0]);
+ const float bk = bmin[1] - bd*bmin[0];
+ const float aminy = ad*minx + ak;
+ const float amaxy = ad*maxx + ak;
+ const float bminy = bd*minx + bk;
+ const float bmaxy = bd*maxx + bk;
+ const float dmin = bminy - aminy;
+ const float dmax = bmaxy - amaxy;
+
+ // Crossing segments always overlap.
+ if (dmin*dmax < 0)
+ return true;
+
+ // Check for overlap at endpoints.
+ const float thr = dtSqr(py*2);
+ if (dmin*dmin <= thr || dmax*dmax <= thr)
+ return true;
+
+ return false;
+}
+
+static void calcSlabEndPoints(const float* va, const float* vb, float* bmin, float* bmax, const int side)
+{
+ if (side == 0 || side == 4)
+ {
+ if (va[2] < vb[2])
+ {
+ bmin[0] = va[2];
+ bmin[1] = va[1];
+ bmax[0] = vb[2];
+ bmax[1] = vb[1];
+ }
+ else
+ {
+ bmin[0] = vb[2];
+ bmin[1] = vb[1];
+ bmax[0] = va[2];
+ bmax[1] = va[1];
+ }
+ }
+ else if (side == 2 || side == 6)
+ {
+ if (va[0] < vb[0])
+ {
+ bmin[0] = va[0];
+ bmin[1] = va[1];
+ bmax[0] = vb[0];
+ bmax[1] = vb[1];
+ }
+ else
+ {
+ bmin[0] = vb[0];
+ bmin[1] = vb[1];
+ bmax[0] = va[0];
+ bmax[1] = va[1];
+ }
+ }
+}
+
+inline int computeTileHash(int x, int y, const int mask)
+{
+ const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
+ const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
+ unsigned int n = h1 * x + h2 * y;
+ return (int)(n & mask);
+}
+
+inline unsigned int allocLink(dtMeshTile* tile)
+{
+ if (tile->linksFreeList == DT_NULL_LINK)
+ return DT_NULL_LINK;
+ unsigned int link = tile->linksFreeList;
+ tile->linksFreeList = tile->links[link].next;
+ return link;
+}
+
+inline void freeLink(dtMeshTile* tile, unsigned int link)
+{
+ tile->links[link].next = tile->linksFreeList;
+ tile->linksFreeList = link;
+}
+
+
+dtNavMesh* dtAllocNavMesh()
+{
+ void* mem = dtAlloc(sizeof(dtNavMesh), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtNavMesh;
+}
+
+void dtFreeNavMesh(dtNavMesh* navmesh)
+{
+ if (!navmesh) return;
+ navmesh->~dtNavMesh();
+ dtFree(navmesh);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNavMesh::dtNavMesh() :
+ m_tileWidth(0),
+ m_tileHeight(0),
+ m_maxTiles(0),
+ m_tileLutSize(0),
+ m_tileLutMask(0),
+ m_posLookup(0),
+ m_nextFree(0),
+ m_tiles(0),
+ m_saltBits(0),
+ m_tileBits(0),
+ m_polyBits(0)
+{
+ m_orig[0] = 0;
+ m_orig[1] = 0;
+ m_orig[2] = 0;
+}
+
+dtNavMesh::~dtNavMesh()
+{
+ for (int i = 0; i < m_maxTiles; ++i)
+ {
+ if (m_tiles[i].flags & DT_TILE_FREE_DATA)
+ {
+ dtFree(m_tiles[i].data);
+ m_tiles[i].data = 0;
+ m_tiles[i].dataSize = 0;
+ }
+ }
+ dtFree(m_posLookup);
+ dtFree(m_tiles);
+}
+
+dtStatus dtNavMesh::init(const dtNavMeshParams* params)
+{
+ memcpy(&m_params, params, sizeof(dtNavMeshParams));
+ dtVcopy(m_orig, params->orig);
+ m_tileWidth = params->tileWidth;
+ m_tileHeight = params->tileHeight;
+
+ // Init tiles
+ m_maxTiles = params->maxTiles;
+ m_tileLutSize = dtNextPow2(params->maxTiles/4);
+ if (!m_tileLutSize) m_tileLutSize = 1;
+ m_tileLutMask = m_tileLutSize-1;
+
+ m_tiles = (dtMeshTile*)dtAlloc(sizeof(dtMeshTile)*m_maxTiles, DT_ALLOC_PERM);
+ if (!m_tiles)
+ return DT_FAILURE;
+ m_posLookup = (dtMeshTile**)dtAlloc(sizeof(dtMeshTile*)*m_tileLutSize, DT_ALLOC_PERM);
+ if (!m_posLookup)
+ return DT_FAILURE;
+ memset(m_tiles, 0, sizeof(dtMeshTile)*m_maxTiles);
+ memset(m_posLookup, 0, sizeof(dtMeshTile*)*m_tileLutSize);
+ m_nextFree = 0;
+ for (int i = m_maxTiles-1; i >= 0; --i)
+ {
+ m_tiles[i].salt = 1;
+ m_tiles[i].next = m_nextFree;
+ m_nextFree = &m_tiles[i];
+ }
+
+ // Init ID generator values.
+ m_tileBits = STATIC_TILE_BITS; //dtIlog2(dtNextPow2((unsigned int)params->maxTiles));
+ m_polyBits = STATIC_POLY_BITS; //dtIlog2(dtNextPow2((unsigned int)params->maxPolys));
+ m_saltBits = STATIC_SALT_BITS; //sizeof(dtPolyRef)*8 - m_tileBits - m_polyBits;
+ //if (m_saltBits < SALT_MIN_BITS)
+ //return DT_FAILURE;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::init(unsigned char* data, const int dataSize, const int flags)
+{
+ // Make sure the data is in right format.
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ if (header->magic != DT_NAVMESH_MAGIC)
+ return DT_FAILURE;
+ if (header->version != DT_NAVMESH_VERSION)
+ return DT_FAILURE;
+
+ dtNavMeshParams params;
+ dtVcopy(params.orig, header->bmin);
+ params.tileWidth = header->bmax[0] - header->bmin[0];
+ params.tileHeight = header->bmax[2] - header->bmin[2];
+ params.maxTiles = 1;
+ params.maxPolys = header->polyCount;
+
+ dtStatus res = init(&params);
+ if (res != DT_SUCCESS)
+ return res;
+
+ return addTile(data, dataSize, flags, 0, 0);
+}
+
+const dtNavMeshParams* dtNavMesh::getParams() const
+{
+ return &m_params;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+int dtNavMesh::findConnectingPolys(const float* va, const float* vb,
+ const dtMeshTile* tile, int side,
+ dtPolyRef* con, float* conarea, int maxcon) const
+{
+ if (!tile) return 0;
+
+ float amin[2], amax[2];
+ calcSlabEndPoints(va,vb, amin,amax, side);
+
+ // Remove links pointing to 'side' and compact the links array.
+ float bmin[2], bmax[2];
+ unsigned short m = DT_EXT_LINK | (unsigned short)side;
+ int n = 0;
+
+ dtPolyRef base = getPolyRefBase(tile);
+
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+ const int nv = poly->vertCount;
+ for (int j = 0; j < nv; ++j)
+ {
+ // Skip edges which do not point to the right side.
+ if (poly->neis[j] != m) continue;
+ // Check if the segments touch.
+ const float* vc = &tile->verts[poly->verts[j]*3];
+ const float* vd = &tile->verts[poly->verts[(j+1) % nv]*3];
+ calcSlabEndPoints(vc,vd, bmin,bmax, side);
+
+ if (!overlapSlabs(amin,amax, bmin,bmax, 0.01f, tile->header->walkableClimb)) continue;
+
+ // Add return value.
+ if (n < maxcon)
+ {
+ conarea[n*2+0] = dtMax(amin[0], bmin[0]);
+ conarea[n*2+1] = dtMin(amax[0], bmax[0]);
+ con[n] = base | (dtPolyRef)i;
+ n++;
+ }
+ break;
+ }
+ }
+ return n;
+}
+
+void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, int side)
+{
+ if (!tile) return;
+
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+ unsigned int j = poly->firstLink;
+ unsigned int pj = DT_NULL_LINK;
+ while (j != DT_NULL_LINK)
+ {
+ if (tile->links[j].side == side)
+ {
+ // Revove link.
+ unsigned int nj = tile->links[j].next;
+ if (pj == DT_NULL_LINK)
+ poly->firstLink = nj;
+ else
+ tile->links[pj].next = nj;
+ freeLink(tile, j);
+ j = nj;
+ }
+ else
+ {
+ // Advance
+ pj = j;
+ j = tile->links[j].next;
+ }
+ }
+ }
+}
+
+void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side)
+{
+ if (!tile) return;
+
+ // Connect border links.
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+
+ // Create new links.
+ unsigned short m = DT_EXT_LINK | (unsigned short)side;
+ const int nv = poly->vertCount;
+ for (int j = 0; j < nv; ++j)
+ {
+ // Skip edges which do not point to the right side.
+ if (poly->neis[j] != m) continue;
+
+ // Create new links
+ const float* va = &tile->verts[poly->verts[j]*3];
+ const float* vb = &tile->verts[poly->verts[(j+1) % nv]*3];
+ dtPolyRef nei[4];
+ float neia[4*2];
+ int nnei = findConnectingPolys(va,vb, target, dtOppositeTile(side), nei,neia,4);
+ for (int k = 0; k < nnei; ++k)
+ {
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &tile->links[idx];
+ link->ref = nei[k];
+ link->edge = (unsigned char)j;
+ link->side = (unsigned char)side;
+
+ link->next = poly->firstLink;
+ poly->firstLink = idx;
+
+ // Compress portal limits to a byte value.
+ if (side == 0 || side == 4)
+ {
+ float tmin = (neia[k*2+0]-va[2]) / (vb[2]-va[2]);
+ float tmax = (neia[k*2+1]-va[2]) / (vb[2]-va[2]);
+ if (tmin > tmax)
+ dtSwap(tmin,tmax);
+ link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
+ link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
+ }
+ else if (side == 2 || side == 6)
+ {
+ float tmin = (neia[k*2+0]-va[0]) / (vb[0]-va[0]);
+ float tmax = (neia[k*2+1]-va[0]) / (vb[0]-va[0]);
+ if (tmin > tmax)
+ dtSwap(tmin,tmax);
+ link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
+ link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
+ }
+ }
+ }
+ }
+ }
+}
+
+void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side)
+{
+ if (!tile) return;
+
+ // Connect off-mesh links.
+ // We are interested on links which land from target tile to this tile.
+ const unsigned char oppositeSide = (unsigned char)dtOppositeTile(side);
+
+ for (int i = 0; i < target->header->offMeshConCount; ++i)
+ {
+ dtOffMeshConnection* targetCon = &target->offMeshCons[i];
+ if (targetCon->side != oppositeSide)
+ continue;
+
+ dtPoly* targetPoly = &target->polys[targetCon->poly];
+
+ const float ext[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
+
+ // Find polygon to connect to.
+ const float* p = &targetCon->pos[3];
+ float nearestPt[3];
+ dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
+ if (!ref) continue;
+ // findNearestPoly may return too optimistic results, further check to make sure.
+ if (dtSqr(nearestPt[0]-p[0])+dtSqr(nearestPt[2]-p[2]) > dtSqr(targetCon->rad))
+ continue;
+ // Make sure the location is on current mesh.
+ float* v = &target->verts[targetPoly->verts[1]*3];
+ dtVcopy(v, nearestPt);
+
+ // Link off-mesh connection to target poly.
+ unsigned int idx = allocLink(target);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &target->links[idx];
+ link->ref = ref;
+ link->edge = (unsigned char)1;
+ link->side = oppositeSide;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = targetPoly->firstLink;
+ targetPoly->firstLink = idx;
+ }
+
+ // Link target poly to off-mesh connection.
+ if (targetCon->flags & DT_OFFMESH_CON_BIDIR)
+ {
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
+ dtPoly* landPoly = &tile->polys[landPolyIdx];
+ dtLink* link = &tile->links[idx];
+ link->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly);
+ link->edge = 0xff;
+ link->side = (unsigned char)side;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = landPoly->firstLink;
+ landPoly->firstLink = idx;
+ }
+ }
+ }
+
+}
+
+void dtNavMesh::connectIntLinks(dtMeshTile* tile)
+{
+ if (!tile) return;
+
+ dtPolyRef base = getPolyRefBase(tile);
+
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+ poly->firstLink = DT_NULL_LINK;
+
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Build edge links backwards so that the links will be
+ // in the linked list from lowest index to highest.
+ for (int j = poly->vertCount-1; j >= 0; --j)
+ {
+ // Skip hard and non-internal edges.
+ if (poly->neis[j] == 0 || (poly->neis[j] & DT_EXT_LINK)) continue;
+
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &tile->links[idx];
+ link->ref = base | (dtPolyRef)(poly->neis[j]-1);
+ link->edge = (unsigned char)j;
+ link->side = 0xff;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = poly->firstLink;
+ poly->firstLink = idx;
+ }
+ }
+ }
+}
+
+void dtNavMesh::connectIntOffMeshLinks(dtMeshTile* tile)
+{
+ if (!tile) return;
+
+ dtPolyRef base = getPolyRefBase(tile);
+
+ // Find Off-mesh connection end points.
+ for (int i = 0; i < tile->header->offMeshConCount; ++i)
+ {
+ dtOffMeshConnection* con = &tile->offMeshCons[i];
+ dtPoly* poly = &tile->polys[con->poly];
+
+ const float ext[3] = { con->rad, tile->header->walkableClimb, con->rad };
+
+ for (int j = 0; j < 2; ++j)
+ {
+ unsigned char side = j == 0 ? 0xff : con->side;
+
+ if (side == 0xff)
+ {
+ // Find polygon to connect to.
+ const float* p = &con->pos[j*3];
+ float nearestPt[3];
+ dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
+ if (!ref) continue;
+ // findNearestPoly may return too optimistic results, further check to make sure.
+ if (dtSqr(nearestPt[0]-p[0])+dtSqr(nearestPt[2]-p[2]) > dtSqr(con->rad))
+ continue;
+ // Make sure the location is on current mesh.
+ float* v = &tile->verts[poly->verts[j]*3];
+ dtVcopy(v, nearestPt);
+
+ // Link off-mesh connection to target poly.
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &tile->links[idx];
+ link->ref = ref;
+ link->edge = (unsigned char)j;
+ link->side = 0xff;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = poly->firstLink;
+ poly->firstLink = idx;
+ }
+
+ // Start end-point is always connect back to off-mesh connection,
+ // Destination end-point only if it is bidirectional link.
+ if (j == 0 || (j == 1 && (con->flags & DT_OFFMESH_CON_BIDIR)))
+ {
+ // Link target poly to off-mesh connection.
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
+ dtPoly* landPoly = &tile->polys[landPolyIdx];
+ dtLink* link = &tile->links[idx];
+ link->ref = base | (dtPolyRef)(con->poly);
+ link->edge = 0xff;
+ link->side = 0xff;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = landPoly->firstLink;
+ landPoly->firstLink = idx;
+ }
+ }
+
+ }
+ }
+ }
+}
+
+dtStatus dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip,
+ const float* pos, float* closest) const
+{
+ const dtPoly* poly = &tile->polys[ip];
+
+ float closestDistSqr = FLT_MAX;
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float pt[3];
+ dtClosestPtPointTriangle(pt, pos, v[0], v[1], v[2]);
+ float d = dtVdistSqr(pos, pt);
+ if (d < closestDistSqr)
+ {
+ dtVcopy(closest, pt);
+ closestDistSqr = d;
+ }
+ }
+
+ return DT_SUCCESS;
+}
+
+dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
+ const float* center, const float* extents,
+ float* nearestPt) const
+{
+ float bmin[3], bmax[3];
+ dtVsub(bmin, center, extents);
+ dtVadd(bmax, center, extents);
+
+ // Get nearby polygons from proximity grid.
+ dtPolyRef polys[128];
+ int polyCount = queryPolygonsInTile(tile, bmin, bmax, polys, 128);
+
+ // Find nearest polygon amongst the nearby polygons.
+ dtPolyRef nearest = 0;
+ float nearestDistanceSqr = FLT_MAX;
+ for (int i = 0; i < polyCount; ++i)
+ {
+ dtPolyRef ref = polys[i];
+ float closestPtPoly[3];
+ if (closestPointOnPolyInTile(tile, decodePolyIdPoly(ref), center, closestPtPoly) != DT_SUCCESS)
+ continue;
+ float d = dtVdistSqr(center, closestPtPoly);
+ if (d < nearestDistanceSqr)
+ {
+ if (nearestPt)
+ dtVcopy(nearestPt, closestPtPoly);
+ nearestDistanceSqr = d;
+ nearest = ref;
+ }
+ }
+
+ return nearest;
+}
+
+int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+ dtPolyRef* polys, const int maxPolys) const
+{
+ if (tile->bvTree)
+ {
+ const dtBVNode* node = &tile->bvTree[0];
+ const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount];
+ const float* tbmin = tile->header->bmin;
+ const float* tbmax = tile->header->bmax;
+ const float qfac = tile->header->bvQuantFactor;
+
+ // Calculate quantized box
+ unsigned short bmin[3], bmax[3];
+ // dtClamp query box to world box.
+ float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
+ float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
+ // Quantize
+ bmin[0] = (unsigned short)(qfac * minx) & 0xfffe;
+ bmin[1] = (unsigned short)(qfac * miny) & 0xfffe;
+ bmin[2] = (unsigned short)(qfac * minz) & 0xfffe;
+ bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
+ bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
+ bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;
+
+ // Traverse tree
+ dtPolyRef base = getPolyRefBase(tile);
+ int n = 0;
+ while (node < end)
+ {
+ const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);
+ const bool isLeafNode = node->i >= 0;
+
+ if (isLeafNode && overlap)
+ {
+ if (n < maxPolys)
+ polys[n++] = base | (dtPolyRef)node->i;
+ }
+
+ if (overlap || isLeafNode)
+ node++;
+ else
+ {
+ const int escapeIndex = -node->i;
+ node += escapeIndex;
+ }
+ }
+
+ return n;
+ }
+ else
+ {
+ float bmin[3], bmax[3];
+ int n = 0;
+ dtPolyRef base = getPolyRefBase(tile);
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ // Calc polygon bounds.
+ dtPoly* p = &tile->polys[i];
+ const float* v = &tile->verts[p->verts[0]*3];
+ dtVcopy(bmin, v);
+ dtVcopy(bmax, v);
+ for (int j = 1; j < p->vertCount; ++j)
+ {
+ v = &tile->verts[p->verts[j]*3];
+ dtVmin(bmin, v);
+ dtVmax(bmax, v);
+ }
+ if (dtOverlapBounds(qmin,qmax, bmin,bmax))
+ {
+ if (n < maxPolys)
+ polys[n++] = base | (dtPolyRef)i;
+ }
+ }
+ return n;
+ }
+}
+
+dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
+ dtTileRef lastRef, dtTileRef* result)
+{
+ // Make sure the data is in right format.
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ if (header->magic != DT_NAVMESH_MAGIC)
+ return DT_FAILURE_DATA_MAGIC;
+ if (header->version != DT_NAVMESH_VERSION)
+ return DT_FAILURE_DATA_VERSION;
+
+ // Make sure the location is free.
+ if (getTileAt(header->x, header->y))
+ return DT_FAILURE;
+
+ // Allocate a tile.
+ dtMeshTile* tile = 0;
+ if (!lastRef)
+ {
+ if (m_nextFree)
+ {
+ tile = m_nextFree;
+ m_nextFree = tile->next;
+ tile->next = 0;
+ }
+ }
+ else
+ {
+ // Try to relocate the tile to specific index with same salt.
+ int tileIndex = (int)decodePolyIdTile((dtPolyRef)lastRef);
+ if (tileIndex >= m_maxTiles)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ // Try to find the specific tile id from the free list.
+ dtMeshTile* target = &m_tiles[tileIndex];
+ dtMeshTile* prev = 0;
+ tile = m_nextFree;
+ while (tile && tile != target)
+ {
+ prev = tile;
+ tile = tile->next;
+ }
+ // Could not find the correct location.
+ if (tile != target)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ // Remove from freelist
+ if (!prev)
+ m_nextFree = tile->next;
+ else
+ prev->next = tile->next;
+
+ // Restore salt.
+ tile->salt = decodePolyIdSalt((dtPolyRef)lastRef);
+ }
+
+ // Make sure we could allocate a tile.
+ if (!tile)
+ return DT_FAILURE_OUT_OF_MEMORY;
+
+ // Insert tile into the position lut.
+ int h = computeTileHash(header->x, header->y, m_tileLutMask);
+ tile->next = m_posLookup[h];
+ m_posLookup[h] = tile;
+
+ // Patch header pointers.
+ const int headerSize = dtAlign4(sizeof(dtMeshHeader));
+ const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount);
+ const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount);
+ const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount));
+ const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
+ const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount);
+ const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
+ const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
+ const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
+
+ unsigned char* d = data + headerSize;
+ tile->verts = (float*)d; d += vertsSize;
+ tile->polys = (dtPoly*)d; d += polysSize;
+ tile->links = (dtLink*)d; d += linksSize;
+ tile->detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
+ tile->detailVerts = (float*)d; d += detailVertsSize;
+ tile->detailTris = (unsigned char*)d; d += detailTrisSize;
+ tile->bvTree = (dtBVNode*)d; d += bvtreeSize;
+ tile->offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
+
+ // Build links freelist
+ tile->linksFreeList = 0;
+ tile->links[header->maxLinkCount-1].next = DT_NULL_LINK;
+ for (int i = 0; i < header->maxLinkCount-1; ++i)
+ tile->links[i].next = i+1;
+
+ // Init tile.
+ tile->header = header;
+ tile->data = data;
+ tile->dataSize = dataSize;
+ tile->flags = flags;
+
+ connectIntLinks(tile);
+ connectIntOffMeshLinks(tile);
+
+ // Create connections connections.
+ for (int i = 0; i < 8; ++i)
+ {
+ dtMeshTile* nei = getNeighbourTileAt(header->x, header->y, i);
+ if (nei)
+ {
+ connectExtLinks(tile, nei, i);
+ connectExtLinks(nei, tile, dtOppositeTile(i));
+ connectExtOffMeshLinks(tile, nei, i);
+ connectExtOffMeshLinks(nei, tile, dtOppositeTile(i));
+ }
+ }
+
+ if (result)
+ *result = getTileRef(tile);
+
+ return DT_SUCCESS;
+}
+
+const dtMeshTile* dtNavMesh::getTileAt(int x, int y) const
+{
+ // Find tile based on hash.
+ int h = computeTileHash(x,y,m_tileLutMask);
+ dtMeshTile* tile = m_posLookup[h];
+ while (tile)
+ {
+ if (tile->header && tile->header->x == x && tile->header->y == y)
+ return tile;
+ tile = tile->next;
+ }
+ return 0;
+}
+
+dtMeshTile* dtNavMesh::getNeighbourTileAt(int x, int y, int side) const
+{
+ switch (side)
+ {
+ case 0: x++; break;
+ case 1: x++; y++; break;
+ case 2: y++; break;
+ case 3: x--; y++; break;
+ case 4: x--; break;
+ case 5: x--; y--; break;
+ case 6: y--; break;
+ case 7: x++; y--; break;
+ };
+
+ // Find tile based on hash.
+ int h = computeTileHash(x,y,m_tileLutMask);
+ dtMeshTile* tile = m_posLookup[h];
+ while (tile)
+ {
+ if (tile->header && tile->header->x == x && tile->header->y == y)
+ return tile;
+ tile = tile->next;
+ }
+ return 0;
+}
+
+dtTileRef dtNavMesh::getTileRefAt(int x, int y) const
+{
+ // Find tile based on hash.
+ int h = computeTileHash(x,y,m_tileLutMask);
+ dtMeshTile* tile = m_posLookup[h];
+ while (tile)
+ {
+ if (tile->header && tile->header->x == x && tile->header->y == y)
+ return getTileRef(tile);
+ tile = tile->next;
+ }
+ return 0;
+}
+
+const dtMeshTile* dtNavMesh::getTileByRef(dtTileRef ref) const
+{
+ if (!ref)
+ return 0;
+ unsigned int tileIndex = decodePolyIdTile((dtPolyRef)ref);
+ unsigned int tileSalt = decodePolyIdSalt((dtPolyRef)ref);
+ if ((int)tileIndex >= m_maxTiles)
+ return 0;
+ const dtMeshTile* tile = &m_tiles[tileIndex];
+ if (tile->salt != tileSalt)
+ return 0;
+ return tile;
+}
+
+int dtNavMesh::getMaxTiles() const
+{
+ return m_maxTiles;
+}
+
+dtMeshTile* dtNavMesh::getTile(int i)
+{
+ return &m_tiles[i];
+}
+
+const dtMeshTile* dtNavMesh::getTile(int i) const
+{
+ return &m_tiles[i];
+}
+
+void dtNavMesh::calcTileLoc(const float* pos, int* tx, int* ty) const
+{
+ *tx = (int)floorf((pos[0]-m_orig[0]) / m_tileWidth);
+ *ty = (int)floorf((pos[2]-m_orig[2]) / m_tileHeight);
+}
+
+dtStatus dtNavMesh::getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ if (ip >= (unsigned int)m_tiles[it].header->polyCount) return DT_FAILURE;
+ *tile = &m_tiles[it];
+ *poly = &m_tiles[it].polys[ip];
+ return DT_SUCCESS;
+}
+
+void dtNavMesh::getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ *tile = &m_tiles[it];
+ *poly = &m_tiles[it].polys[ip];
+}
+
+bool dtNavMesh::isValidPolyRef(dtPolyRef ref) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return false;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return false;
+ if (ip >= (unsigned int)m_tiles[it].header->polyCount) return false;
+ return true;
+}
+
+dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSize)
+{
+ if (!ref)
+ return DT_FAILURE;
+ unsigned int tileIndex = decodePolyIdTile((dtPolyRef)ref);
+ unsigned int tileSalt = decodePolyIdSalt((dtPolyRef)ref);
+ if ((int)tileIndex >= m_maxTiles)
+ return DT_FAILURE;
+ dtMeshTile* tile = &m_tiles[tileIndex];
+ if (tile->salt != tileSalt)
+ return DT_FAILURE;
+
+ // Remove tile from hash lookup.
+ int h = computeTileHash(tile->header->x,tile->header->y,m_tileLutMask);
+ dtMeshTile* prev = 0;
+ dtMeshTile* cur = m_posLookup[h];
+ while (cur)
+ {
+ if (cur == tile)
+ {
+ if (prev)
+ prev->next = cur->next;
+ else
+ m_posLookup[h] = cur->next;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ // Remove connections to neighbour tiles.
+ for (int i = 0; i < 8; ++i)
+ {
+ dtMeshTile* nei = getNeighbourTileAt(tile->header->x,tile->header->y,i);
+ if (!nei) continue;
+ unconnectExtLinks(nei, dtOppositeTile(i));
+ }
+
+
+ // Reset tile.
+ if (tile->flags & DT_TILE_FREE_DATA)
+ {
+ // Owns data
+ dtFree(tile->data);
+ tile->data = 0;
+ tile->dataSize = 0;
+ if (data) *data = 0;
+ if (dataSize) *dataSize = 0;
+ }
+ else
+ {
+ if (data) *data = tile->data;
+ if (dataSize) *dataSize = tile->dataSize;
+ }
+
+ tile->header = 0;
+ tile->flags = 0;
+ tile->linksFreeList = 0;
+ tile->polys = 0;
+ tile->verts = 0;
+ tile->links = 0;
+ tile->detailMeshes = 0;
+ tile->detailVerts = 0;
+ tile->detailTris = 0;
+ tile->bvTree = 0;
+ tile->offMeshCons = 0;
+
+ // Update salt, salt should never be zero.
+ tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1);
+ if (tile->salt == 0)
+ tile->salt++;
+
+ // Add to free list.
+ tile->next = m_nextFree;
+ m_nextFree = tile;
+
+ return DT_SUCCESS;
+}
+
+dtTileRef dtNavMesh::getTileRef(const dtMeshTile* tile) const
+{
+ if (!tile) return 0;
+ const unsigned int it = tile - m_tiles;
+ return (dtTileRef)encodePolyId(tile->salt, it, 0);
+}
+
+dtPolyRef dtNavMesh::getPolyRefBase(const dtMeshTile* tile) const
+{
+ if (!tile) return 0;
+ const unsigned int it = tile - m_tiles;
+ return encodePolyId(tile->salt, it, 0);
+}
+
+struct dtTileState
+{
+ int magic; // Magic number, used to identify the data.
+ int version; // Data version number.
+ dtTileRef ref; // Tile ref at the time of storing the data.
+};
+
+struct dtPolyState
+{
+ unsigned short flags; // Flags (see dtPolyFlags).
+ unsigned char area; // Area ID of the polygon.
+};
+
+int dtNavMesh::getTileStateSize(const dtMeshTile* tile) const
+{
+ if (!tile) return 0;
+ const int headerSize = dtAlign4(sizeof(dtTileState));
+ const int polyStateSize = dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+ return headerSize + polyStateSize;
+}
+
+dtStatus dtNavMesh::storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const
+{
+ // Make sure there is enough space to store the state.
+ const int sizeReq = getTileStateSize(tile);
+ if (maxDataSize < sizeReq)
+ return DT_FAILURE;
+
+ dtTileState* tileState = (dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
+ dtPolyState* polyStates = (dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+
+ // Store tile state.
+ tileState->magic = DT_NAVMESH_STATE_MAGIC;
+ tileState->version = DT_NAVMESH_STATE_VERSION;
+ tileState->ref = getTileRef(tile);
+
+ // Store per poly state.
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ const dtPoly* p = &tile->polys[i];
+ dtPolyState* s = &polyStates[i];
+ s->flags = p->flags;
+ s->area = p->getArea();
+ }
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize)
+{
+ // Make sure there is enough space to store the state.
+ const int sizeReq = getTileStateSize(tile);
+ if (maxDataSize < sizeReq)
+ return DT_FAILURE;
+
+ const dtTileState* tileState = (const dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
+ const dtPolyState* polyStates = (const dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+
+ // Check that the restore is possible.
+ if (tileState->magic != DT_NAVMESH_STATE_MAGIC)
+ return DT_FAILURE_DATA_MAGIC;
+ if (tileState->version != DT_NAVMESH_STATE_VERSION)
+ return DT_FAILURE_DATA_VERSION;
+ if (tileState->ref != getTileRef(tile))
+ return DT_FAILURE;
+
+ // Restore per poly state.
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* p = &tile->polys[i];
+ const dtPolyState* s = &polyStates[i];
+ p->flags = s->flags;
+ p->setArea(s->area);
+ }
+
+ return DT_SUCCESS;
+}
+
+// Returns start and end location of an off-mesh link polygon.
+dtStatus dtNavMesh::getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const
+{
+ unsigned int salt, it, ip;
+
+ // Get current polygon
+ decodePolyId(polyRef, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ const dtPoly* poly = &tile->polys[ip];
+
+ // Make sure that the current poly is indeed off-mesh link.
+ if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION)
+ return DT_FAILURE;
+
+ // Figure out which way to hand out the vertices.
+ int idx0 = 0, idx1 = 1;
+
+ // Find link that points to first vertex.
+ for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
+ {
+ if (tile->links[i].edge == 0)
+ {
+ if (tile->links[i].ref != prevRef)
+ {
+ idx0 = 1;
+ idx1 = 0;
+ }
+ break;
+ }
+ }
+
+ dtVcopy(startPos, &tile->verts[poly->verts[idx0]*3]);
+ dtVcopy(endPos, &tile->verts[poly->verts[idx1]*3]);
+
+ return DT_SUCCESS;
+}
+
+
+const dtOffMeshConnection* dtNavMesh::getOffMeshConnectionByRef(dtPolyRef ref) const
+{
+ unsigned int salt, it, ip;
+
+ // Get current polygon
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return 0;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return 0;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return 0;
+ const dtPoly* poly = &tile->polys[ip];
+
+ // Make sure that the current poly is indeed off-mesh link.
+ if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION)
+ return 0;
+
+ const unsigned int idx = ip - tile->header->offMeshBase;
+ dtAssert(idx < (unsigned int)tile->header->offMeshConCount);
+ return &tile->offMeshCons[idx];
+}
+
+
+dtStatus dtNavMesh::setPolyFlags(dtPolyRef ref, unsigned short flags)
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ dtPoly* poly = &tile->polys[ip];
+
+ // Change flags.
+ poly->flags = flags;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ const dtPoly* poly = &tile->polys[ip];
+
+ *resultFlags = poly->flags;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::setPolyArea(dtPolyRef ref, unsigned char area)
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ dtPoly* poly = &tile->polys[ip];
+
+ poly->setArea(area);
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::getPolyArea(dtPolyRef ref, unsigned char* resultArea) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ const dtPoly* poly = &tile->polys[ip];
+
+ *resultArea = poly->getArea();
+
+ return DT_SUCCESS;
+}
+
diff --git a/dep/recastnavigation/Detour/DetourNavMesh.h b/dep/recastnavigation/Detour/DetourNavMesh.h
new file mode 100644
index 0000000..52d2c50
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMesh.h
@@ -0,0 +1,428 @@
+//
+// 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 DETOURNAVMESH_H
+#define DETOURNAVMESH_H
+
+#include "DetourAlloc.h"
+
+#ifdef WIN32
+ typedef unsigned __int64 uint64;
+#else
+#include <stdint.h>
+#ifndef uint64_t
+#ifdef __linux__
+#include <linux/types.h>
+#endif
+#endif
+ typedef uint64_t uint64;
+#endif
+
+// Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef.
+// It is also recommended to change dtHashRef() to proper 64-bit hash too.
+
+// Reference to navigation polygon.
+typedef uint64 dtPolyRef;
+
+// Reference to navigation mesh tile.
+typedef uint64 dtTileRef;
+
+// Maximum number of vertices per navigation polygon.
+static const int DT_VERTS_PER_POLYGON = 6;
+
+static const int DT_NAVMESH_MAGIC = 'D'<<24 | 'N'<<16 | 'A'<<8 | 'V'; //'DNAV';
+static const int DT_NAVMESH_VERSION = 6;
+
+static const int DT_NAVMESH_STATE_MAGIC = 'D'<<24 | 'N'<<16 | 'M'<<8 | 'S'; //'DNMS';
+static const int DT_NAVMESH_STATE_VERSION = 1;
+
+static const unsigned short DT_EXT_LINK = 0x8000;
+static const unsigned int DT_NULL_LINK = 0xffffffff;
+static const unsigned int DT_OFFMESH_CON_BIDIR = 1;
+
+static const int DT_MAX_AREAS = 64;
+
+static const int STATIC_SALT_BITS = 12;
+static const int STATIC_TILE_BITS = 21;
+static const int STATIC_POLY_BITS = 31;
+// we cannot have over 31 bits for either tile nor poly
+// without changing polyCount to use 64bits too.
+
+// Flags for addTile
+enum dtTileFlags
+{
+ DT_TILE_FREE_DATA = 0x01, // Navmesh owns the tile memory and should free it.
+};
+
+// Flags returned by findStraightPath().
+enum dtStraightPathFlags
+{
+ DT_STRAIGHTPATH_START = 0x01, // The vertex is the start position.
+ DT_STRAIGHTPATH_END = 0x02, // The vertex is the end position.
+ DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04, // The vertex is start of an off-mesh link.
+};
+
+// Flags describing polygon properties.
+enum dtPolyTypes
+{
+ DT_POLYTYPE_GROUND = 0, // Regular ground polygons.
+ DT_POLYTYPE_OFFMESH_CONNECTION = 1, // Off-mesh connections.
+};
+
+enum dtStatus
+{
+ DT_FAILURE = 0, // Operation failed.
+ DT_FAILURE_DATA_MAGIC,
+ DT_FAILURE_DATA_VERSION,
+ DT_FAILURE_OUT_OF_MEMORY,
+ DT_SUCCESS, // Operation succeed.
+ DT_IN_PROGRESS, // Operation still in progress.
+};
+
+
+// Structure describing the navigation polygon data.
+struct dtPoly
+{
+ unsigned int firstLink; // Index to first link in linked list.
+ unsigned short verts[DT_VERTS_PER_POLYGON]; // Indices to vertices of the poly.
+ unsigned short neis[DT_VERTS_PER_POLYGON]; // Refs to neighbours of the poly.
+ unsigned short flags; // Flags (see dtPolyFlags).
+ unsigned char vertCount; // Number of vertices.
+ unsigned char areaAndtype; // Bit packed: Area ID of the polygon, and Polygon type, see dtPolyTypes..
+ inline void setArea(unsigned char a) { areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); }
+ inline void setType(unsigned char t) { areaAndtype = (areaAndtype & 0x3f) | (t << 6); }
+ inline unsigned char getArea() const { return areaAndtype & 0x3f; }
+ inline unsigned char getType() const { return areaAndtype >> 6; }
+};
+
+// Stucture describing polygon detail triangles.
+struct dtPolyDetail
+{
+ unsigned int vertBase; // Offset to detail vertex array.
+ unsigned int triBase; // Offset to detail triangle array.
+ unsigned char vertCount; // Number of vertices in the detail mesh.
+ unsigned char triCount; // Number of triangles.
+};
+
+// Stucture describing a link to another polygon.
+struct dtLink
+{
+ dtPolyRef ref; // Neighbour reference.
+ unsigned int next; // Index to next link.
+ unsigned char edge; // Index to polygon edge which owns this link.
+ unsigned char side; // If boundary link, defines on which side the link is.
+ unsigned char bmin, bmax; // If boundary link, defines the sub edge area.
+};
+
+struct dtBVNode
+{
+ unsigned short bmin[3], bmax[3]; // BVnode bounds
+ int i; // Index to item or if negative, escape index.
+};
+
+struct dtOffMeshConnection
+{
+ float pos[6]; // Both end point locations.
+ float rad; // Link connection radius.
+ unsigned short poly; // Poly Id
+ unsigned char flags; // Link flags
+ unsigned char side; // End point side.
+ unsigned int userId; // User ID to identify this connection.
+};
+
+struct dtMeshHeader
+{
+ int magic; // Magic number, used to identify the data.
+ int version; // Data version number.
+ int x, y; // Location of the time on the grid.
+ unsigned int userId; // User ID of the tile.
+ int polyCount; // Number of polygons in the tile.
+ int vertCount; // Number of vertices in the tile.
+ int maxLinkCount; // Number of allocated links.
+ int detailMeshCount; // Number of detail meshes.
+ int detailVertCount; // Number of detail vertices.
+ int detailTriCount; // Number of detail triangles.
+ int bvNodeCount; // Number of BVtree nodes.
+ int offMeshConCount; // Number of Off-Mesh links.
+ int offMeshBase; // Index to first polygon which is Off-Mesh link.
+ float walkableHeight; // Height of the agent.
+ float walkableRadius; // Radius of the agent
+ float walkableClimb; // Max climb height of the agent.
+ float bmin[3], bmax[3]; // Bounding box of the tile.
+ float bvQuantFactor; // BVtree quantization factor (world to bvnode coords)
+};
+
+struct dtMeshTile
+{
+ unsigned int salt; // Counter describing modifications to the tile.
+
+ unsigned int linksFreeList; // Index to next free link.
+ dtMeshHeader* header; // Pointer to tile header.
+ dtPoly* polys; // Pointer to the polygons (will be updated when tile is added).
+ float* verts; // Pointer to the vertices (will be updated when tile added).
+ dtLink* links; // Pointer to the links (will be updated when tile added).
+ dtPolyDetail* detailMeshes; // Pointer to detail meshes (will be updated when tile added).
+ float* detailVerts; // Pointer to detail vertices (will be updated when tile added).
+ unsigned char* detailTris; // Pointer to detail triangles (will be updated when tile added).
+ dtBVNode* bvTree; // Pointer to BVtree nodes (will be updated when tile added).
+ dtOffMeshConnection* offMeshCons; // Pointer to Off-Mesh links. (will be updated when tile added).
+
+ unsigned char* data; // Pointer to tile data.
+ int dataSize; // Size of the tile data.
+ int flags; // Tile flags, see dtTileFlags.
+ dtMeshTile* next; // Next free tile or, next tile in spatial grid.
+};
+
+struct dtNavMeshParams
+{
+ float orig[3]; // Origin of the nav mesh tile space.
+ float tileWidth, tileHeight; // Width and height of each tile.
+ int maxTiles; // Maximum number of tiles the navmesh can contain.
+ int maxPolys; // Maximum number of polygons each tile can contain.
+};
+
+
+class dtNavMesh
+{
+public:
+ dtNavMesh();
+ ~dtNavMesh();
+
+ // Initializes the nav mesh for tiled use.
+ // Params:
+ // params - (in) navmesh initialization params, see dtNavMeshParams.
+ // Returns: True if succeed, else false.
+ dtStatus init(const dtNavMeshParams* params);
+
+ // Initializes the nav mesh for single tile use.
+ // Params:
+ // data - (in) Data of the new tile mesh.
+ // dataSize - (in) Data size of the new tile mesh.
+ // flags - (in) Tile flags, see dtTileFlags.
+ // Returns: True if succeed, else false.
+ dtStatus init(unsigned char* data, const int dataSize, const int flags);
+
+ // Returns pointer to navmesh initialization params.
+ const dtNavMeshParams* getParams() const;
+
+ // Adds new tile into the navmesh.
+ // The add will fail if the data is in wrong format,
+ // there is not enough tiles left, or if there is a tile already at the location.
+ // Params:
+ // data - (in) Data of the new tile mesh.
+ // dataSize - (in) Data size of the new tile mesh.
+ // flags - (in) Tile flags, see dtTileFlags.
+ // lastRef - (in,optional) Last tile ref, the tile will be restored so that
+ // the reference (as well as poly references) will be the same. Default: 0.
+ // result - (out,optional) tile ref if the tile was succesfully added.
+ dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result);
+
+ // Removes specified tile.
+ // Params:
+ // ref - (in) Reference to the tile to remove.
+ // data - (out) Data associated with deleted tile.
+ // dataSize - (out) Size of the data associated with deleted tile.
+ dtStatus removeTile(dtTileRef ref, unsigned char** data, int* dataSize);
+
+ // Calculates tile location based in input world position.
+ // Params:
+ // pos - (in) world position of the query.
+ // tx - (out) tile x location.
+ // ty - (out) tile y location.
+ void calcTileLoc(const float* pos, int* tx, int* ty) const;
+
+ // Returns pointer to tile at specified location.
+ // Params:
+ // x,y - (in) Location of the tile to get.
+ // Returns: pointer to tile if tile exists or 0 tile does not exists.
+ const dtMeshTile* getTileAt(int x, int y) const;
+
+ // Returns reference to tile at specified location.
+ // Params:
+ // x,y - (in) Location of the tile to get.
+ // Returns: reference to tile if tile exists or 0 tile does not exists.
+ dtTileRef getTileRefAt(int x, int y) const;
+
+ // Returns tile references of a tile based on tile pointer.
+ dtTileRef getTileRef(const dtMeshTile* tile) const;
+
+ // Returns tile based on references.
+ const dtMeshTile* getTileByRef(dtTileRef ref) const;
+
+ // Returns max number of tiles.
+ int getMaxTiles() const;
+
+ // Returns pointer to tile in the tile array.
+ // Params:
+ // i - (in) Index to the tile to retrieve, max index is getMaxTiles()-1.
+ // Returns: Pointer to specified tile.
+ const dtMeshTile* getTile(int i) const;
+
+ // Returns pointer to tile and polygon pointed by the polygon reference.
+ // Params:
+ // ref - (in) reference to a polygon.
+ // tile - (out) pointer to the tile containing the polygon.
+ // poly - (out) pointer to the polygon.
+ dtStatus getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
+
+ // Returns pointer to tile and polygon pointed by the polygon reference.
+ // Note: this function does not check if 'ref' s valid, and is thus faster. Use only with valid refs!
+ // Params:
+ // ref - (in) reference to a polygon.
+ // tile - (out) pointer to the tile containing the polygon.
+ // poly - (out) pointer to the polygon.
+ void getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
+
+ // Returns true if polygon reference points to valid data.
+ bool isValidPolyRef(dtPolyRef ref) const;
+
+ // Returns base poly id for specified tile, polygon refs can be deducted from this.
+ dtPolyRef getPolyRefBase(const dtMeshTile* tile) const;
+
+ // Returns start and end location of an off-mesh link polygon.
+ // Params:
+ // prevRef - (in) ref to the polygon before the link (used to select direction).
+ // polyRef - (in) ref to the off-mesh link polygon.
+ // startPos[3] - (out) start point of the link.
+ // endPos[3] - (out) end point of the link.
+ // Returns: true if link is found.
+ dtStatus getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const;
+
+ // Returns pointer to off-mesh connection based on polyref, or null if ref not valid.
+ const dtOffMeshConnection* getOffMeshConnectionByRef(dtPolyRef ref) const;
+
+ // Sets polygon flags.
+ dtStatus setPolyFlags(dtPolyRef ref, unsigned short flags);
+
+ // Return polygon flags.
+ dtStatus getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const;
+
+ // Set polygon type.
+ dtStatus setPolyArea(dtPolyRef ref, unsigned char area);
+
+ // Return polygon area type.
+ dtStatus getPolyArea(dtPolyRef ref, unsigned char* resultArea) const;
+
+
+ // Returns number of bytes required to store tile state.
+ int getTileStateSize(const dtMeshTile* tile) const;
+
+ // Stores tile state to buffer.
+ dtStatus storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const;
+
+ // Restores tile state.
+ dtStatus restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize);
+
+
+ // Encodes a tile id.
+ inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const
+ {
+ return ((dtPolyRef)salt << (m_polyBits+m_tileBits)) | ((dtPolyRef)it << m_polyBits) | (dtPolyRef)ip;
+ }
+
+ // Decodes a tile id.
+ inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const
+ {
+ const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
+ const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
+ const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
+ salt = (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
+ it = (unsigned int)((ref >> m_polyBits) & tileMask);
+ ip = (unsigned int)(ref & polyMask);
+ }
+
+ // Decodes a tile salt.
+ inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
+ {
+ const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
+ return (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
+ }
+
+ // Decodes a tile id.
+ inline unsigned int decodePolyIdTile(dtPolyRef ref) const
+ {
+ const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
+ return (unsigned int)((ref >> m_polyBits) & tileMask);
+ }
+
+ // Decodes a poly id.
+ inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
+ {
+ const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
+ return (unsigned int)(ref & polyMask);
+ }
+
+private:
+
+ // Returns pointer to tile in the tile array.
+ dtMeshTile* getTile(int i);
+
+ // Returns neighbour tile based on side.
+ dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;
+ // Returns all polygons in neighbour tile based on portal defined by the segment.
+ int findConnectingPolys(const float* va, const float* vb,
+ const dtMeshTile* tile, int side,
+ dtPolyRef* con, float* conarea, int maxcon) const;
+
+ // Builds internal polygons links for a tile.
+ void connectIntLinks(dtMeshTile* tile);
+ // Builds internal polygons links for a tile.
+ void connectIntOffMeshLinks(dtMeshTile* tile);
+
+ // Builds external polygon links for a tile.
+ void connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side);
+ // Builds external polygon links for a tile.
+ void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
+
+ // Removes external links at specified side.
+ void unconnectExtLinks(dtMeshTile* tile, int side);
+
+
+ // TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding.
+
+ // Queries polygons within a tile.
+ int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+ dtPolyRef* polys, const int maxPolys) const;
+ // Find nearest polygon within a tile.
+ dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
+ const float* extents, float* nearestPt) const;
+ // Returns closest point on polygon.
+ dtStatus closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip,
+ const float* pos, float* closest) const;
+
+ dtNavMeshParams m_params; // Current initialization params. TODO: do not store this info twice.
+ float m_orig[3]; // Origin of the tile (0,0)
+ float m_tileWidth, m_tileHeight; // Dimensions of each tile.
+ int m_maxTiles; // Max number of tiles.
+ int m_tileLutSize; // Tile hash lookup size (must be pot).
+ int m_tileLutMask; // Tile hash lookup mask.
+
+ dtMeshTile** m_posLookup; // Tile hash lookup.
+ dtMeshTile* m_nextFree; // Freelist of tiles.
+ dtMeshTile* m_tiles; // List of tiles.
+
+ unsigned int m_saltBits; // Number of salt bits in the tile ID.
+ unsigned int m_tileBits; // Number of tile bits in the tile ID.
+ unsigned int m_polyBits; // Number of poly bits in the tile ID.
+};
+
+// Helper function to allocate navmesh class using Detour allocator.
+dtNavMesh* dtAllocNavMesh();
+void dtFreeNavMesh(dtNavMesh* navmesh);
+
+#endif // DETOURNAVMESH_H
diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp b/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp
new file mode 100644
index 0000000..f648571
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp
@@ -0,0 +1,717 @@
+//
+// 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 <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "DetourNavMesh.h"
+#include "DetourCommon.h"
+#include "DetourNavMeshBuilder.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+
+static unsigned short MESH_NULL_IDX = 0xffff;
+
+
+struct BVItem
+{
+ unsigned short bmin[3];
+ unsigned short bmax[3];
+ int i;
+};
+
+static int compareItemX(const void* va, const void* vb)
+{
+ const BVItem* a = (const BVItem*)va;
+ const BVItem* b = (const BVItem*)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 BVItem* a = (const BVItem*)va;
+ const BVItem* b = (const BVItem*)vb;
+ if (a->bmin[1] < b->bmin[1])
+ return -1;
+ if (a->bmin[1] > b->bmin[1])
+ return 1;
+ return 0;
+}
+
+static int compareItemZ(const void* va, const void* vb)
+{
+ const BVItem* a = (const BVItem*)va;
+ const BVItem* b = (const BVItem*)vb;
+ if (a->bmin[2] < b->bmin[2])
+ return -1;
+ if (a->bmin[2] > b->bmin[2])
+ return 1;
+ return 0;
+}
+
+static void calcExtends(BVItem* items, const int /*nitems*/, const int imin, const int imax,
+ unsigned short* bmin, unsigned short* bmax)
+{
+ bmin[0] = items[imin].bmin[0];
+ bmin[1] = items[imin].bmin[1];
+ bmin[2] = items[imin].bmin[2];
+
+ bmax[0] = items[imin].bmax[0];
+ bmax[1] = items[imin].bmax[1];
+ bmax[2] = items[imin].bmax[2];
+
+ for (int i = imin+1; i < imax; ++i)
+ {
+ const BVItem& 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.bmin[2] < bmin[2]) bmin[2] = it.bmin[2];
+
+ if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0];
+ if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1];
+ if (it.bmax[2] > bmax[2]) bmax[2] = it.bmax[2];
+ }
+}
+
+inline int longestAxis(unsigned short x, unsigned short y, unsigned short z)
+{
+ int axis = 0;
+ unsigned short maxVal = x;
+ if (y > maxVal)
+ {
+ axis = 1;
+ maxVal = y;
+ }
+ if (z > maxVal)
+ {
+ axis = 2;
+ maxVal = z;
+ }
+ return axis;
+}
+
+static void subdivide(BVItem* items, int nitems, int imin, int imax, int& curNode, dtBVNode* nodes)
+{
+ int inum = imax - imin;
+ int icur = curNode;
+
+ dtBVNode& node = nodes[curNode++];
+
+ if (inum == 1)
+ {
+ // Leaf
+ node.bmin[0] = items[imin].bmin[0];
+ node.bmin[1] = items[imin].bmin[1];
+ node.bmin[2] = items[imin].bmin[2];
+
+ node.bmax[0] = items[imin].bmax[0];
+ node.bmax[1] = items[imin].bmax[1];
+ node.bmax[2] = items[imin].bmax[2];
+
+ node.i = items[imin].i;
+ }
+ 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],
+ node.bmax[2] - node.bmin[2]);
+
+ if (axis == 0)
+ {
+ // Sort along x-axis
+ qsort(items+imin, inum, sizeof(BVItem), compareItemX);
+ }
+ else if (axis == 1)
+ {
+ // Sort along y-axis
+ qsort(items+imin, inum, sizeof(BVItem), compareItemY);
+ }
+ else
+ {
+ // Sort along z-axis
+ qsort(items+imin, inum, sizeof(BVItem), compareItemZ);
+ }
+
+ int isplit = imin+inum/2;
+
+ // Left
+ subdivide(items, nitems, imin, isplit, curNode, nodes);
+ // Right
+ subdivide(items, nitems, isplit, imax, curNode, nodes);
+
+ int iescape = curNode - icur;
+ // Negative index means escape.
+ node.i = -iescape;
+ }
+}
+
+static int createBVTree(const unsigned short* verts, const int /*nverts*/,
+ const unsigned short* polys, const int npolys, const int nvp,
+ const float cs, const float ch,
+ const int /*nnodes*/, dtBVNode* nodes)
+{
+ // Build tree
+ BVItem* items = (BVItem*)dtAlloc(sizeof(BVItem)*npolys, DT_ALLOC_TEMP);
+ for (int i = 0; i < npolys; i++)
+ {
+ BVItem& it = items[i];
+ it.i = i;
+ // Calc polygon bounds.
+ const unsigned short* p = &polys[i*nvp*2];
+ it.bmin[0] = it.bmax[0] = verts[p[0]*3+0];
+ it.bmin[1] = it.bmax[1] = verts[p[0]*3+1];
+ it.bmin[2] = it.bmax[2] = verts[p[0]*3+2];
+
+ for (int j = 1; j < nvp; ++j)
+ {
+ if (p[j] == MESH_NULL_IDX) break;
+ unsigned short x = verts[p[j]*3+0];
+ unsigned short y = verts[p[j]*3+1];
+ unsigned short z = verts[p[j]*3+2];
+
+ if (x < it.bmin[0]) it.bmin[0] = x;
+ if (y < it.bmin[1]) it.bmin[1] = y;
+ if (z < it.bmin[2]) it.bmin[2] = z;
+
+ if (x > it.bmax[0]) it.bmax[0] = x;
+ if (y > it.bmax[1]) it.bmax[1] = y;
+ if (z > it.bmax[2]) it.bmax[2] = z;
+ }
+ // Remap y
+ it.bmin[1] = (unsigned short)floorf((float)it.bmin[1]*ch/cs);
+ it.bmax[1] = (unsigned short)ceilf((float)it.bmax[1]*ch/cs);
+ }
+
+ int curNode = 0;
+ subdivide(items, npolys, 0, npolys, curNode, nodes);
+
+ dtFree(items);
+
+ return curNode;
+}
+
+static unsigned char classifyOffMeshPoint(const float* pt, const float* bmin, const float* bmax)
+{
+ static const unsigned char XP = 1<<0;
+ static const unsigned char ZP = 1<<1;
+ static const unsigned char XM = 1<<2;
+ static const unsigned char ZM = 1<<3;
+
+ unsigned char outcode = 0;
+ outcode |= (pt[0] >= bmax[0]) ? XP : 0;
+ outcode |= (pt[2] >= bmax[2]) ? ZP : 0;
+ outcode |= (pt[0] < bmin[0]) ? XM : 0;
+ outcode |= (pt[2] < bmin[2]) ? ZM : 0;
+
+ switch (outcode)
+ {
+ case XP: return 0;
+ case XP|ZP: return 1;
+ case ZP: return 2;
+ case XM|ZP: return 3;
+ case XM: return 4;
+ case XM|ZM: return 5;
+ case ZM: return 6;
+ case XP|ZM: return 7;
+ };
+ return 0xff;
+}
+
+// TODO: Better error handling.
+
+bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize)
+{
+ if (params->nvp > DT_VERTS_PER_POLYGON)
+ return false;
+ if (params->vertCount >= 0xffff)
+ return false;
+ if (!params->vertCount || !params->verts)
+ return false;
+ if (!params->polyCount || !params->polys)
+ return false;
+ if (!params->detailMeshes || !params->detailVerts || !params->detailTris)
+ return false;
+
+ const int nvp = params->nvp;
+
+ // Classify off-mesh connection points. We store only the connections
+ // whose start point is inside the tile.
+ unsigned char* offMeshConClass = 0;
+ int storedOffMeshConCount = 0;
+ int offMeshConLinkCount = 0;
+
+ if (params->offMeshConCount > 0)
+ {
+ offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP);
+ if (!offMeshConClass)
+ return false;
+
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ offMeshConClass[i*2+0] = classifyOffMeshPoint(&params->offMeshConVerts[(i*2+0)*3], params->bmin, params->bmax);
+ offMeshConClass[i*2+1] = classifyOffMeshPoint(&params->offMeshConVerts[(i*2+1)*3], params->bmin, params->bmax);
+
+ // Cound how many links should be allocated for off-mesh connections.
+ if (offMeshConClass[i*2+0] == 0xff)
+ offMeshConLinkCount++;
+ if (offMeshConClass[i*2+1] == 0xff)
+ offMeshConLinkCount++;
+
+ if (offMeshConClass[i*2+0] == 0xff)
+ storedOffMeshConCount++;
+ }
+ }
+
+ // Off-mesh connectionss are stored as polygons, adjust values.
+ const int totPolyCount = params->polyCount + storedOffMeshConCount;
+ const int totVertCount = params->vertCount + storedOffMeshConCount*2;
+
+ // Find portal edges which are at tile borders.
+ int edgeCount = 0;
+ int portalCount = 0;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ const unsigned short* p = &params->polys[i*2*nvp];
+ for (int j = 0; j < nvp; ++j)
+ {
+ if (p[j] == MESH_NULL_IDX) break;
+ int nj = j+1;
+ if (nj >= nvp || p[nj] == MESH_NULL_IDX) nj = 0;
+ const unsigned short* va = &params->verts[p[j]*3];
+ const unsigned short* vb = &params->verts[p[nj]*3];
+
+ edgeCount++;
+
+ if (params->tileSize > 0)
+ {
+ if (va[0] == params->tileSize && vb[0] == params->tileSize)
+ portalCount++; // x+
+ else if (va[2] == params->tileSize && vb[2] == params->tileSize)
+ portalCount++; // z+
+ else if (va[0] == 0 && vb[0] == 0)
+ portalCount++; // x-
+ else if (va[2] == 0 && vb[2] == 0)
+ portalCount++; // z-
+ }
+ }
+ }
+
+ const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2;
+
+ // Find unique detail vertices.
+ int uniqueDetailVertCount = 0;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ const unsigned short* p = &params->polys[i*nvp*2];
+ int ndv = params->detailMeshes[i*4+1];
+ int nv = 0;
+ for (int j = 0; j < nvp; ++j)
+ {
+ if (p[j] == MESH_NULL_IDX) break;
+ nv++;
+ }
+ ndv -= nv;
+ uniqueDetailVertCount += ndv;
+ }
+
+ // Calculate data size
+ const int headerSize = dtAlign4(sizeof(dtMeshHeader));
+ const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount);
+ const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount);
+ const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount);
+ const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount);
+ const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount);
+ const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*params->detailTriCount);
+ const int bvTreeSize = dtAlign4(sizeof(dtBVNode)*params->polyCount*2);
+ const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount);
+
+ const int dataSize = headerSize + vertsSize + polysSize + linksSize +
+ detailMeshesSize + detailVertsSize + detailTrisSize +
+ bvTreeSize + offMeshConsSize;
+
+ unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM);
+ if (!data)
+ {
+ dtFree(offMeshConClass);
+ return false;
+ }
+ memset(data, 0, dataSize);
+
+ unsigned char* d = data;
+ dtMeshHeader* header = (dtMeshHeader*)d; d += headerSize;
+ float* navVerts = (float*)d; d += vertsSize;
+ dtPoly* navPolys = (dtPoly*)d; d += polysSize;
+ d += linksSize;
+ dtPolyDetail* navDMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
+ float* navDVerts = (float*)d; d += detailVertsSize;
+ unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize;
+ dtBVNode* navBvtree = (dtBVNode*)d; d += bvTreeSize;
+ dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshConsSize;
+
+
+ // Store header
+ header->magic = DT_NAVMESH_MAGIC;
+ header->version = DT_NAVMESH_VERSION;
+ header->x = params->tileX;
+ header->y = params->tileY;
+ header->userId = params->userId;
+ header->polyCount = totPolyCount;
+ header->vertCount = totVertCount;
+ header->maxLinkCount = maxLinkCount;
+ dtVcopy(header->bmin, params->bmin);
+ dtVcopy(header->bmax, params->bmax);
+ header->detailMeshCount = params->polyCount;
+ header->detailVertCount = uniqueDetailVertCount;
+ header->detailTriCount = params->detailTriCount;
+ header->bvQuantFactor = 1.0f / params->cs;
+ header->offMeshBase = params->polyCount;
+ header->walkableHeight = params->walkableHeight;
+ header->walkableRadius = params->walkableRadius;
+ header->walkableClimb = params->walkableClimb;
+ header->offMeshConCount = storedOffMeshConCount;
+ header->bvNodeCount = params->polyCount*2;
+
+ const int offMeshVertsBase = params->vertCount;
+ const int offMeshPolyBase = params->polyCount;
+
+ // Store vertices
+ // Mesh vertices
+ for (int i = 0; i < params->vertCount; ++i)
+ {
+ const unsigned short* iv = &params->verts[i*3];
+ float* v = &navVerts[i*3];
+ v[0] = params->bmin[0] + iv[0] * params->cs;
+ v[1] = params->bmin[1] + iv[1] * params->ch;
+ v[2] = params->bmin[2] + iv[2] * params->cs;
+ }
+ // Off-mesh link vertices.
+ int n = 0;
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ // Only store connections which start from this tile.
+ if (offMeshConClass[i*2+0] == 0xff)
+ {
+ const float* linkv = &params->offMeshConVerts[i*2*3];
+ float* v = &navVerts[(offMeshVertsBase + n*2)*3];
+ dtVcopy(&v[0], &linkv[0]);
+ dtVcopy(&v[3], &linkv[3]);
+ n++;
+ }
+ }
+
+ // Store polygons
+ // Mesh polys
+ const unsigned short* src = params->polys;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ dtPoly* p = &navPolys[i];
+ p->vertCount = 0;
+ p->flags = params->polyFlags[i];
+ p->setArea(params->polyAreas[i]);
+ p->setType(DT_POLYTYPE_GROUND);
+ for (int j = 0; j < nvp; ++j)
+ {
+ if (src[j] == MESH_NULL_IDX) break;
+ p->verts[j] = src[j];
+ p->neis[j] = (src[nvp+j]+1) & 0xffff;
+ p->vertCount++;
+ }
+ src += nvp*2;
+ }
+ // Off-mesh connection vertices.
+ n = 0;
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ // Only store connections which start from this tile.
+ if (offMeshConClass[i*2+0] == 0xff)
+ {
+ dtPoly* p = &navPolys[offMeshPolyBase+n];
+ p->vertCount = 2;
+ p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0);
+ p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1);
+ p->flags = params->offMeshConFlags[i];
+ p->setArea(params->offMeshConAreas[i]);
+ p->setType(DT_POLYTYPE_OFFMESH_CONNECTION);
+ n++;
+ }
+ }
+
+ // Store portal edges.
+ if (params->tileSize > 0)
+ {
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ dtPoly* poly = &navPolys[i];
+ for (int j = 0; j < poly->vertCount; ++j)
+ {
+ int nj = j+1;
+ if (nj >= poly->vertCount) nj = 0;
+
+ const unsigned short* va = &params->verts[poly->verts[j]*3];
+ const unsigned short* vb = &params->verts[poly->verts[nj]*3];
+
+ if (va[0] == params->tileSize && vb[0] == params->tileSize) // x+
+ poly->neis[j] = DT_EXT_LINK | 0;
+ else if (va[2] == params->tileSize && vb[2] == params->tileSize) // z+
+ poly->neis[j] = DT_EXT_LINK | 2;
+ else if (va[0] == 0 && vb[0] == 0) // x-
+ poly->neis[j] = DT_EXT_LINK | 4;
+ else if (va[2] == 0 && vb[2] == 0) // z-
+ poly->neis[j] = DT_EXT_LINK | 6;
+ }
+ }
+ }
+
+ // Store detail meshes and vertices.
+ // The nav polygon vertices are stored as the first vertices on each mesh.
+ // We compress the mesh data by skipping them and using the navmesh coordinates.
+ unsigned short vbase = 0;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ dtPolyDetail& dtl = navDMeshes[i];
+ const int vb = (int)params->detailMeshes[i*4+0];
+ const int ndv = (int)params->detailMeshes[i*4+1];
+ const int nv = navPolys[i].vertCount;
+ dtl.vertBase = (unsigned int)vbase;
+ dtl.vertCount = (unsigned char)(ndv-nv);
+ dtl.triBase = (unsigned int)params->detailMeshes[i*4+2];
+ dtl.triCount = (unsigned char)params->detailMeshes[i*4+3];
+ // Copy vertices except the first 'nv' verts which are equal to nav poly verts.
+ if (ndv-nv)
+ {
+ memcpy(&navDVerts[vbase*3], &params->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv));
+ vbase += (unsigned short)(ndv-nv);
+ }
+ }
+ // Store triangles.
+ memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount);
+
+ // Store and create BVtree.
+ // TODO: take detail mesh into account! use byte per bbox extent?
+ createBVTree(params->verts, params->vertCount, params->polys, params->polyCount,
+ nvp, params->cs, params->ch, params->polyCount*2, navBvtree);
+
+ // Store Off-Mesh connections.
+ n = 0;
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ // Only store connections which start from this tile.
+ if (offMeshConClass[i*2+0] == 0xff)
+ {
+ dtOffMeshConnection* con = &offMeshCons[n];
+ con->poly = (unsigned short)(offMeshPolyBase + n);
+ // Copy connection end-points.
+ const float* endPts = &params->offMeshConVerts[i*2*3];
+ dtVcopy(&con->pos[0], &endPts[0]);
+ dtVcopy(&con->pos[3], &endPts[3]);
+ con->rad = params->offMeshConRad[i];
+ con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0;
+ con->side = offMeshConClass[i*2+1];
+ if (params->offMeshConUserID)
+ con->userId = params->offMeshConUserID[i];
+ n++;
+ }
+ }
+
+ dtFree(offMeshConClass);
+
+ *outData = data;
+ *outDataSize = dataSize;
+
+ return true;
+}
+
+inline void swapByte(unsigned char* a, unsigned char* b)
+{
+ unsigned char tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+inline void swapEndian(unsigned short* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+1);
+}
+
+inline void swapEndian(short* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+1);
+}
+
+inline void swapEndian(unsigned int* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+3); swapByte(x+1, x+2);
+}
+
+inline void swapEndian(int* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+3); swapByte(x+1, x+2);
+}
+
+inline void swapEndian(float* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+3); swapByte(x+1, x+2);
+}
+
+bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/)
+{
+ dtMeshHeader* header = (dtMeshHeader*)data;
+
+ int swappedMagic = DT_NAVMESH_MAGIC;
+ int swappedVersion = DT_NAVMESH_VERSION;
+ swapEndian(&swappedMagic);
+ swapEndian(&swappedVersion);
+
+ if ((header->magic != DT_NAVMESH_MAGIC || header->version != DT_NAVMESH_VERSION) &&
+ (header->magic != swappedMagic || header->version != swappedVersion))
+ {
+ return false;
+ }
+
+ swapEndian(&header->magic);
+ swapEndian(&header->version);
+ swapEndian(&header->x);
+ swapEndian(&header->y);
+ swapEndian(&header->userId);
+ swapEndian(&header->polyCount);
+ swapEndian(&header->vertCount);
+ swapEndian(&header->maxLinkCount);
+ swapEndian(&header->detailMeshCount);
+ swapEndian(&header->detailVertCount);
+ swapEndian(&header->detailTriCount);
+ swapEndian(&header->bvNodeCount);
+ swapEndian(&header->offMeshConCount);
+ swapEndian(&header->offMeshBase);
+ swapEndian(&header->walkableHeight);
+ swapEndian(&header->walkableRadius);
+ swapEndian(&header->walkableClimb);
+ swapEndian(&header->bmin[0]);
+ swapEndian(&header->bmin[1]);
+ swapEndian(&header->bmin[2]);
+ swapEndian(&header->bmax[0]);
+ swapEndian(&header->bmax[1]);
+ swapEndian(&header->bmax[2]);
+ swapEndian(&header->bvQuantFactor);
+
+ // Freelist index and pointers are updated when tile is added, no need to swap.
+
+ return true;
+}
+
+bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
+{
+ // Make sure the data is in right format.
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ if (header->magic != DT_NAVMESH_MAGIC)
+ return false;
+ if (header->version != DT_NAVMESH_VERSION)
+ return false;
+
+ // Patch header pointers.
+ const int headerSize = dtAlign4(sizeof(dtMeshHeader));
+ const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount);
+ const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount);
+ const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount));
+ const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
+ const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount);
+ const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
+ const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
+ const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
+
+ unsigned char* d = data + headerSize;
+ float* verts = (float*)d; d += vertsSize;
+ dtPoly* polys = (dtPoly*)d; d += polysSize;
+ /*dtLink* links = (dtLink*)d;*/ d += linksSize;
+ dtPolyDetail* detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
+ float* detailVerts = (float*)d; d += detailVertsSize;
+ /*unsigned char* detailTris = (unsigned char*)d;*/ d += detailTrisSize;
+ dtBVNode* bvTree = (dtBVNode*)d; d += bvtreeSize;
+ dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
+
+ // Vertices
+ for (int i = 0; i < header->vertCount*3; ++i)
+ {
+ swapEndian(&verts[i]);
+ }
+
+ // Polys
+ for (int i = 0; i < header->polyCount; ++i)
+ {
+ dtPoly* p = &polys[i];
+ // poly->firstLink is update when tile is added, no need to swap.
+ for (int j = 0; j < DT_VERTS_PER_POLYGON; ++j)
+ {
+ swapEndian(&p->verts[j]);
+ swapEndian(&p->neis[j]);
+ }
+ swapEndian(&p->flags);
+ }
+
+ // Links are rebuild when tile is added, no need to swap.
+
+ // Detail meshes
+ for (int i = 0; i < header->detailMeshCount; ++i)
+ {
+ dtPolyDetail* pd = &detailMeshes[i];
+ swapEndian(&pd->vertBase);
+ swapEndian(&pd->triBase);
+ }
+
+ // Detail verts
+ for (int i = 0; i < header->detailVertCount*3; ++i)
+ {
+ swapEndian(&detailVerts[i]);
+ }
+
+ // BV-tree
+ for (int i = 0; i < header->bvNodeCount; ++i)
+ {
+ dtBVNode* node = &bvTree[i];
+ for (int j = 0; j < 3; ++j)
+ {
+ swapEndian(&node->bmin[j]);
+ swapEndian(&node->bmax[j]);
+ }
+ swapEndian(&node->i);
+ }
+
+ // Off-mesh Connections.
+ for (int i = 0; i < header->offMeshConCount; ++i)
+ {
+ dtOffMeshConnection* con = &offMeshCons[i];
+ for (int j = 0; j < 6; ++j)
+ swapEndian(&con->pos[j]);
+ swapEndian(&con->rad);
+ swapEndian(&con->poly);
+ }
+
+ return true;
+}
diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.h b/dep/recastnavigation/Detour/DetourNavMeshBuilder.h
new file mode 100644
index 0000000..8d8ef2e
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshBuilder.h
@@ -0,0 +1,77 @@
+//
+// 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 DETOURNAVMESHBUILDER_H
+#define DETOURNAVMESHBUILDER_H
+
+#include "DetourAlloc.h"
+
+
+// The units of the parameters are specified in parenthesis as follows:
+// (vx) voxels, (wu) world units
+struct dtNavMeshCreateParams
+{
+ // Navmesh vertices.
+ const unsigned short* verts; // Array of vertices, each vertex has 3 components. (vx).
+ int vertCount; // Vertex count
+ // Navmesh polygons
+ const unsigned short* polys; // Array of polygons, uses same format as rcPolyMesh.
+ const unsigned short* polyFlags; // Array of flags per polygon.
+ const unsigned char* polyAreas; // Array of area ids per polygon.
+ int polyCount; // Number of polygons
+ int nvp; // Number of verts per polygon.
+ // Navmesh Detail
+ const unsigned int* detailMeshes; // Detail meshes, uses same format as rcPolyMeshDetail.
+ const float* detailVerts; // Detail mesh vertices, uses same format as rcPolyMeshDetail (wu).
+ int detailVertsCount; // Total number of detail vertices
+ const unsigned char* detailTris; // Array of detail tris per detail mesh.
+ int detailTriCount; // Total number of detail triangles.
+ // Off-Mesh Connections.
+ const float* offMeshConVerts; // Off-mesh connection vertices (wu).
+ const float* offMeshConRad; // Off-mesh connection radii (wu).
+ const unsigned short* offMeshConFlags; // Off-mesh connection flags.
+ const unsigned char* offMeshConAreas; // Off-mesh connection area ids.
+ const unsigned char* offMeshConDir; // Off-mesh connection direction flags (1 = bidir, 0 = oneway).
+ const unsigned int* offMeshConUserID; // Off-mesh connection user id (optional).
+ int offMeshConCount; // Number of off-mesh connections
+ // Tile location
+ unsigned int userId; // User ID bound to the tile.
+ int tileX, tileY; // Tile location (tile coords).
+ float bmin[3], bmax[3]; // Tile bounds (wu).
+ // Settings
+ float walkableHeight; // Agent height (wu).
+ float walkableRadius; // Agent radius (wu).
+ float walkableClimb; // Agent max climb (wu).
+ float cs; // Cell size (xz) (wu).
+ float ch; // Cell height (y) (wu).
+ int tileSize; // Tile size (width & height) (vx).
+};
+
+// Build navmesh data from given input data.
+bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize);
+
+// Swaps endianess of navmesh header.
+bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int dataSize);
+
+// Swaps endianess of the navmesh data. This function assumes that the header is in correct
+// endianess already. Call dtNavMeshHeaderSwapEndian() first on the data if the data is
+// assumed to be in wrong endianess to start with. If converting from native endianess to foreign,
+// call dtNavMeshHeaderSwapEndian() after the data has been swapped.
+bool dtNavMeshDataSwapEndian(unsigned char* data, const int dataSize);
+
+#endif // DETOURNAVMESHBUILDER_H
diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp b/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp
new file mode 100644
index 0000000..6a6eb94
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp
@@ -0,0 +1,2564 @@
+//
+// 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 <math.h>
+#include <float.h>
+#include <string.h>
+#include "DetourNavMeshQuery.h"
+#include "DetourNavMesh.h"
+#include "DetourNode.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <new>
+
+
+dtQueryFilter::dtQueryFilter() :
+ m_includeFlags(0xffff),
+ m_excludeFlags(0)
+{
+ for (int i = 0; i < DT_MAX_AREAS; ++i)
+ m_areaCost[i] = 1.0f;
+}
+
+#ifdef DT_VIRTUAL_QUERYFILTER
+bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
+ const dtMeshTile* /*tile*/,
+ const dtPoly* poly) const
+{
+ return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
+}
+
+float dtQueryFilter::getCost(const float* pa, const float* pb,
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
+{
+ return dtVdist(pa, pb) * m_areaCost[curPoly->area];
+}
+#else
+inline bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
+ const dtMeshTile* /*tile*/,
+ const dtPoly* poly) const
+{
+ return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
+}
+
+inline float dtQueryFilter::getCost(const float* pa, const float* pb,
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
+{
+ return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()];
+}
+#endif
+
+static const float H_SCALE = 2.0f; // Search heuristic scale.
+
+
+dtNavMeshQuery* dtAllocNavMeshQuery()
+{
+ void* mem = dtAlloc(sizeof(dtNavMeshQuery), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtNavMeshQuery;
+}
+
+void dtFreeNavMeshQuery(dtNavMeshQuery* navmesh)
+{
+ if (!navmesh) return;
+ navmesh->~dtNavMeshQuery();
+ dtFree(navmesh);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNavMeshQuery::dtNavMeshQuery() :
+ m_tinyNodePool(0),
+ m_nodePool(0),
+ m_openList(0)
+{
+ memset(&m_query, 0, sizeof(dtQueryData));
+}
+
+dtNavMeshQuery::~dtNavMeshQuery()
+{
+ if (m_tinyNodePool)
+ m_tinyNodePool->~dtNodePool();
+ if (m_nodePool)
+ m_nodePool->~dtNodePool();
+ if (m_openList)
+ m_openList->~dtNodeQueue();
+ dtFree(m_tinyNodePool);
+ dtFree(m_nodePool);
+ dtFree(m_openList);
+}
+
+dtStatus dtNavMeshQuery::init(const dtNavMesh* nav, const int maxNodes)
+{
+ m_nav = nav;
+
+ if (!m_nodePool || m_nodePool->getMaxNodes() < maxNodes)
+ {
+ if (m_nodePool)
+ {
+ m_nodePool->~dtNodePool();
+ dtFree(m_nodePool);
+ m_nodePool = 0;
+ }
+ m_nodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(maxNodes, dtNextPow2(maxNodes/4));
+ if (!m_nodePool)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ }
+ else
+ {
+ m_nodePool->clear();
+ }
+
+ if (!m_tinyNodePool)
+ {
+ m_tinyNodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(64, 32);
+ if (!m_tinyNodePool)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ }
+ else
+ {
+ m_tinyNodePool->clear();
+ }
+
+ // TODO: check the open list size too.
+ if (!m_openList || m_openList->getCapacity() < maxNodes)
+ {
+ if (m_openList)
+ {
+ m_openList->~dtNodeQueue();
+ dtFree(m_openList);
+ m_openList = 0;
+ }
+ m_openList = new (dtAlloc(sizeof(dtNodeQueue), DT_ALLOC_PERM)) dtNodeQueue(maxNodes);
+ if (!m_openList)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ }
+ else
+ {
+ m_openList->clear();
+ }
+
+ return DT_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const
+{
+ dtAssert(m_nav);
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+ if (!tile) return DT_FAILURE;
+
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ return DT_FAILURE;
+
+ if (closestPointOnPolyInTile(tile, poly, pos, closest) != DT_SUCCESS)
+ return DT_FAILURE;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly,
+ const float* pos, float* closest) const
+{
+ const unsigned int ip = (unsigned int)(poly - tile->polys);
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+
+ // TODO: The commented out version finds 'cylinder distance' instead of 'sphere distance' to the navmesh.
+ // Test and enable.
+/*
+ // Clamp point to be inside the polygon.
+ float verts[DT_VERTS_PER_POLYGON*3];
+ float edged[DT_VERTS_PER_POLYGON];
+ float edget[DT_VERTS_PER_POLYGON];
+ const int nv = poly->vertCount;
+ for (int i = 0; i < nv; ++i)
+ dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]);
+
+ dtVcopy(closest, pos);
+ if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
+ {
+ // Point is outside the polygon, dtClamp to nearest edge.
+ float dmin = FLT_MAX;
+ int imin = -1;
+ for (int i = 0; i < nv; ++i)
+ {
+ if (edged[i] < dmin)
+ {
+ dmin = edged[i];
+ imin = i;
+ }
+ }
+ const float* va = &verts[imin*3];
+ const float* vb = &verts[((imin+1)%nv)*3];
+ dtVlerp(closest, va, vb, edget[imin]);
+ }
+
+ // Find height at the location.
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float h;
+ if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
+ {
+ closest[1] = h;
+ break;
+ }
+ }
+*/
+ float closestDistSqr = FLT_MAX;
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+
+ float pt[3];
+ dtClosestPtPointTriangle(pt, pos, v[0], v[1], v[2]);
+ float d = dtVdistSqr(pos, pt);
+
+ if (d < closestDistSqr)
+ {
+ dtVcopy(closest, pt);
+ closestDistSqr = d;
+ }
+ }
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const
+{
+ dtAssert(m_nav);
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ // Collect vertices.
+ float verts[DT_VERTS_PER_POLYGON*3];
+ float edged[DT_VERTS_PER_POLYGON];
+ float edget[DT_VERTS_PER_POLYGON];
+ int nv = 0;
+ for (int i = 0; i < (int)poly->vertCount; ++i)
+ {
+ dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
+ nv++;
+ }
+
+ bool inside = dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget);
+ if (inside)
+ {
+ // Point is inside the polygon, return the point.
+ dtVcopy(closest, pos);
+ }
+ else
+ {
+ // Point is outside the polygon, dtClamp to nearest edge.
+ float dmin = FLT_MAX;
+ int imin = -1;
+ for (int i = 0; i < nv; ++i)
+ {
+ if (edged[i] < dmin)
+ {
+ dmin = edged[i];
+ imin = i;
+ }
+ }
+ const float* va = &verts[imin*3];
+ const float* vb = &verts[((imin+1)%nv)*3];
+ dtVlerp(closest, va, vb, edget[imin]);
+ }
+
+ return DT_SUCCESS;
+}
+
+
+dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* height) const
+{
+ dtAssert(m_nav);
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ {
+ const float* v0 = &tile->verts[poly->verts[0]*3];
+ const float* v1 = &tile->verts[poly->verts[1]*3];
+ const float d0 = dtVdist(pos, v0);
+ const float d1 = dtVdist(pos, v1);
+ const float u = d0 / (d0+d1);
+ if (height)
+ *height = v0[1] + (v1[1] - v0[1]) * u;
+ return DT_SUCCESS;
+ }
+ else
+ {
+ const unsigned int ip = (unsigned int)(poly - tile->polys);
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float h;
+ if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
+ {
+ if (height)
+ *height = h;
+ return DT_SUCCESS;
+ }
+ }
+ }
+
+ return DT_FAILURE;
+}
+
+dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* nearestRef, float* nearestPt) const
+{
+ dtAssert(m_nav);
+
+ *nearestRef = 0;
+
+ // Get nearby polygons from proximity grid.
+ dtPolyRef polys[128];
+ int polyCount = 0;
+ if (queryPolygons(center, extents, filter, polys, &polyCount, 128) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ // Find nearest polygon amongst the nearby polygons.
+ dtPolyRef nearest = 0;
+ float nearestDistanceSqr = FLT_MAX;
+ for (int i = 0; i < polyCount; ++i)
+ {
+ dtPolyRef ref = polys[i];
+ float closestPtPoly[3];
+ if (closestPointOnPoly(ref, center, closestPtPoly) != DT_SUCCESS)
+ continue;
+ float d = dtVdistSqr(center, closestPtPoly);
+ if (d < nearestDistanceSqr)
+ {
+ if (nearestPt)
+ dtVcopy(nearestPt, closestPtPoly);
+ nearestDistanceSqr = d;
+ nearest = ref;
+ }
+ }
+
+ if (nearestRef)
+ *nearestRef = nearest;
+
+ return DT_SUCCESS;
+}
+
+dtPolyRef dtNavMeshQuery::findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents,
+ const dtQueryFilter* filter, float* nearestPt) const
+{
+ dtAssert(m_nav);
+
+ float bmin[3], bmax[3];
+ dtVsub(bmin, center, extents);
+ dtVadd(bmax, center, extents);
+
+ // Get nearby polygons from proximity grid.
+ dtPolyRef polys[128];
+ int polyCount = queryPolygonsInTile(tile, bmin, bmax, filter, polys, 128);
+
+ // Find nearest polygon amongst the nearby polygons.
+ dtPolyRef nearest = 0;
+ float nearestDistanceSqr = FLT_MAX;
+ for (int i = 0; i < polyCount; ++i)
+ {
+ dtPolyRef ref = polys[i];
+ const dtPoly* poly = &tile->polys[m_nav->decodePolyIdPoly(ref)];
+ float closestPtPoly[3];
+ if (closestPointOnPolyInTile(tile, poly, center, closestPtPoly) != DT_SUCCESS)
+ continue;
+
+ float d = dtVdistSqr(center, closestPtPoly);
+ if (d < nearestDistanceSqr)
+ {
+ if (nearestPt)
+ dtVcopy(nearestPt, closestPtPoly);
+ nearestDistanceSqr = d;
+ nearest = ref;
+ }
+ }
+
+ return nearest;
+}
+
+int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+ const dtQueryFilter* filter,
+ dtPolyRef* polys, const int maxPolys) const
+{
+ dtAssert(m_nav);
+
+ if (tile->bvTree)
+ {
+ const dtBVNode* node = &tile->bvTree[0];
+ const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount];
+ const float* tbmin = tile->header->bmin;
+ const float* tbmax = tile->header->bmax;
+ const float qfac = tile->header->bvQuantFactor;
+
+ // Calculate quantized box
+ unsigned short bmin[3], bmax[3];
+ // dtClamp query box to world box.
+ float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
+ float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
+ // Quantize
+ bmin[0] = (unsigned short)(qfac * minx) & 0xfffe;
+ bmin[1] = (unsigned short)(qfac * miny) & 0xfffe;
+ bmin[2] = (unsigned short)(qfac * minz) & 0xfffe;
+ bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
+ bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
+ bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;
+
+ // Traverse tree
+ const dtPolyRef base = m_nav->getPolyRefBase(tile);
+ int n = 0;
+ while (node < end)
+ {
+ const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);
+ const bool isLeafNode = node->i >= 0;
+
+ if (isLeafNode && overlap)
+ {
+ dtPolyRef ref = base | (dtPolyRef)node->i;
+ if (filter->passFilter(ref, tile, &tile->polys[node->i]))
+ {
+ if (n < maxPolys)
+ polys[n++] = ref;
+ }
+ }
+
+ if (overlap || isLeafNode)
+ node++;
+ else
+ {
+ const int escapeIndex = -node->i;
+ node += escapeIndex;
+ }
+ }
+
+ return n;
+ }
+ else
+ {
+ float bmin[3], bmax[3];
+ int n = 0;
+ const dtPolyRef base = m_nav->getPolyRefBase(tile);
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ // Calc polygon bounds.
+ dtPoly* p = &tile->polys[i];
+ const float* v = &tile->verts[p->verts[0]*3];
+ dtVcopy(bmin, v);
+ dtVcopy(bmax, v);
+ for (int j = 1; j < p->vertCount; ++j)
+ {
+ v = &tile->verts[p->verts[j]*3];
+ dtVmin(bmin, v);
+ dtVmax(bmax, v);
+ }
+ if (dtOverlapBounds(qmin,qmax, bmin,bmax))
+ {
+ const dtPolyRef ref = base | (dtPolyRef)i;
+ if (filter->passFilter(ref, tile, p))
+ {
+ if (n < maxPolys)
+ polys[n++] = ref;
+ }
+ }
+ }
+ return n;
+ }
+}
+
+dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* polys, int* polyCount, const int maxPolys) const
+{
+ dtAssert(m_nav);
+
+ float bmin[3], bmax[3];
+ dtVsub(bmin, center, extents);
+ dtVadd(bmax, center, extents);
+
+ // Find tiles the query touches.
+ int minx, miny, maxx, maxy;
+ m_nav->calcTileLoc(bmin, &minx, &miny);
+ m_nav->calcTileLoc(bmax, &maxx, &maxy);
+
+ int n = 0;
+ for (int y = miny; y <= maxy; ++y)
+ {
+ for (int x = minx; x <= maxx; ++x)
+ {
+ const dtMeshTile* tile = m_nav->getTileAt(x,y);
+ if (!tile) continue;
+ n += queryPolygonsInTile(tile, bmin, bmax, filter, polys+n, maxPolys-n);
+ if (n >= maxPolys)
+ {
+ *polyCount = n;
+ return DT_SUCCESS;
+ }
+ }
+ }
+ *polyCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ dtPolyRef* path, int* pathCount, const int maxPath) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ *pathCount = 0;
+
+ if (!startRef || !endRef)
+ return DT_FAILURE;
+
+ if (!maxPath)
+ return DT_FAILURE;
+
+ // Validate input
+ if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
+ return DT_FAILURE;
+
+ if (startRef == endRef)
+ {
+ path[0] = startRef;
+ *pathCount = 1;
+ return DT_SUCCESS;
+ }
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, startPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = dtVdist(startPos, endPos) * H_SCALE;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ dtNode* lastBestNode = startNode;
+ float lastBestNodeCost = startNode->total;
+
+ while (!m_openList->empty())
+ {
+ // Remove node from open list and put it in closed list.
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Reached the goal, stop searching.
+ if (bestNode->id == endRef)
+ {
+ lastBestNode = bestNode;
+ break;
+ }
+
+ // Get current poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ dtPolyRef neighbourRef = bestTile->links[i].ref;
+
+ // Skip invalid ids and do not expand back to where we came from.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Get neighbour poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ // If the node is visited the first time, calculate node position.
+ if (neighbourNode->flags == 0)
+ {
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
+ neighbourRef, neighbourPoly, neighbourTile,
+ neighbourNode->pos);
+ }
+
+ // Calculate cost and heuristic.
+ float cost = 0;
+ float heuristic = 0;
+
+ // Special case for last node.
+ if (neighbourRef == endRef)
+ {
+ // Cost
+ const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ const float endCost = filter->getCost(neighbourNode->pos, endPos,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly,
+ 0, 0, 0);
+
+ cost = bestNode->cost + curCost + endCost;
+ heuristic = 0;
+ }
+ else
+ {
+ // Cost
+ const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ cost = bestNode->cost + curCost;
+ heuristic = dtVdist(neighbourNode->pos, endPos)*H_SCALE;
+ }
+
+ const float total = cost + heuristic;
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+ // The node is already visited and process, and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
+ continue;
+
+ // Add or update the node.
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->cost = cost;
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ // Already in open, update node location.
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ // Put the node in open list.
+ neighbourNode->flags |= DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+
+ // Update nearest node to target so far.
+ if (heuristic < lastBestNodeCost)
+ {
+ lastBestNodeCost = heuristic;
+ lastBestNode = neighbourNode;
+ }
+ }
+ }
+
+ // Reverse the path.
+ dtNode* prev = 0;
+ dtNode* node = lastBestNode;
+ do
+ {
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_nodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store path
+ node = prev;
+ int n = 0;
+ do
+ {
+ path[n++] = node->id;
+ node = m_nodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxPath);
+
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter)
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ // Init path state.
+ memset(&m_query, 0, sizeof(dtQueryData));
+ m_query.status = DT_FAILURE;
+ m_query.startRef = startRef;
+ m_query.endRef = endRef;
+ dtVcopy(m_query.startPos, startPos);
+ dtVcopy(m_query.endPos, endPos);
+ m_query.filter = filter;
+
+ if (!startRef || !endRef)
+ return DT_FAILURE;
+
+ // Validate input
+ if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
+ return DT_FAILURE;
+
+ if (startRef == endRef)
+ {
+ m_query.status = DT_SUCCESS;
+ return DT_SUCCESS;
+ }
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, startPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = dtVdist(startPos, endPos) * H_SCALE;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ m_query.status = DT_IN_PROGRESS;
+ m_query.lastBestNode = startNode;
+ m_query.lastBestNodeCost = startNode->total;
+
+ return m_query.status;
+}
+
+dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter)
+{
+ if (m_query.status!= DT_IN_PROGRESS)
+ return m_query.status;
+
+ // Make sure the request is still valid.
+ if (!m_nav->isValidPolyRef(m_query.startRef) || !m_nav->isValidPolyRef(m_query.endRef))
+ {
+ m_query.status = DT_FAILURE;
+ return DT_FAILURE;
+ }
+
+ int iter = 0;
+ while (iter < maxIter && !m_openList->empty())
+ {
+ iter++;
+
+ // Remove node from open list and put it in closed list.
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Reached the goal, stop searching.
+ if (bestNode->id == m_query.endRef)
+ {
+ m_query.lastBestNode = bestNode;
+ m_query.status = DT_SUCCESS;
+ return m_query.status;
+ }
+
+ // Get current poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ if (m_nav->getTileAndPolyByRef(bestRef, &bestTile, &bestPoly) != DT_SUCCESS)
+ {
+ // The polygon has disappeared during the sliced query, fail.
+ m_query.status = DT_FAILURE;
+ return m_query.status;
+ }
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ {
+ if (m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly) != DT_SUCCESS)
+ {
+ // The polygon has disappeared during the sliced query, fail.
+ m_query.status = DT_FAILURE;
+ return m_query.status;
+ }
+ }
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ dtPolyRef neighbourRef = bestTile->links[i].ref;
+
+ // Skip invalid ids and do not expand back to where we came from.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Get neighbour poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ // If the node is visited the first time, calculate node position.
+ if (neighbourNode->flags == 0)
+ {
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
+ neighbourRef, neighbourPoly, neighbourTile,
+ neighbourNode->pos);
+ }
+
+ // Calculate cost and heuristic.
+ float cost = 0;
+ float heuristic = 0;
+
+ // Special case for last node.
+ if (neighbourRef == m_query.endRef)
+ {
+ // Cost
+ const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ const float endCost = m_query.filter->getCost(neighbourNode->pos, m_query.endPos,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly,
+ 0, 0, 0);
+
+ cost = bestNode->cost + curCost + endCost;
+ heuristic = 0;
+ }
+ else
+ {
+ // Cost
+ const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ cost = bestNode->cost + curCost;
+ heuristic = dtVdist(neighbourNode->pos, m_query.endPos)*H_SCALE;
+ }
+
+ const float total = cost + heuristic;
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+ // The node is already visited and process, and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
+ continue;
+
+ // Add or update the node.
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->cost = cost;
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ // Already in open, update node location.
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ // Put the node in open list.
+ neighbourNode->flags |= DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+
+ // Update nearest node to target so far.
+ if (heuristic < m_query.lastBestNodeCost)
+ {
+ m_query.lastBestNodeCost = heuristic;
+ m_query.lastBestNode = neighbourNode;
+ }
+ }
+ }
+
+ // Exhausted all nodes, but could not find path.
+ if (m_openList->empty())
+ m_query.status = DT_SUCCESS;
+
+ return m_query.status;
+}
+
+dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath)
+{
+ *pathCount = 0;
+
+ if (m_query.status != DT_SUCCESS)
+ {
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+ return DT_FAILURE;
+ }
+
+ int n = 0;
+
+ if (m_query.startRef == m_query.endRef)
+ {
+ // Special case: the search starts and ends at same poly.
+ path[n++] = m_query.startRef;
+ }
+ else
+ {
+ // Reverse the path.
+ dtAssert(m_query.lastBestNode);
+ dtNode* prev = 0;
+ dtNode* node = m_query.lastBestNode;
+ do
+ {
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_nodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store path
+ node = prev;
+ do
+ {
+ path[n++] = node->id;
+ node = m_nodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxPath);
+ }
+
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
+ dtPolyRef* path, int* pathCount, const int maxPath)
+{
+ *pathCount = 0;
+
+ if (existingSize == 0)
+ {
+ return DT_FAILURE;
+ }
+
+ if (m_query.status != DT_SUCCESS && m_query.status != DT_IN_PROGRESS)
+ {
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+ return DT_FAILURE;
+ }
+
+ int n = 0;
+
+ if (m_query.startRef == m_query.endRef)
+ {
+ // Special case: the search starts and ends at same poly.
+ path[n++] = m_query.startRef;
+ }
+ else
+ {
+ // Find furthest existing node that was visited.
+ dtNode* prev = 0;
+ dtNode* node = 0;
+ for (int i = existingSize-1; i >= 0; --i)
+ {
+ node = m_nodePool->findNode(existing[i]);
+ if (node)
+ break;
+ }
+
+ if (!node)
+ {
+ return DT_FAILURE;
+ }
+
+ // Reverse the path.
+ do
+ {
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_nodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store path
+ node = prev;
+ do
+ {
+ path[n++] = node->id;
+ node = m_nodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxPath);
+ }
+
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+
+dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* endPos,
+ const dtPolyRef* path, const int pathSize,
+ float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
+ int* straightPathCount, const int maxStraightPath) const
+{
+ dtAssert(m_nav);
+
+ *straightPathCount = 0;
+
+ if (!maxStraightPath)
+ return DT_FAILURE;
+
+ if (!path[0])
+ return DT_FAILURE;
+
+ int n = 0;
+
+ // TODO: Should this be callers responsibility?
+ float closestStartPos[3];
+ if (closestPointOnPolyBoundary(path[0], startPos, closestStartPos) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ // Add start point.
+ dtVcopy(&straightPath[n*3], closestStartPos);
+ if (straightPathFlags)
+ straightPathFlags[n] = DT_STRAIGHTPATH_START;
+ if (straightPathRefs)
+ straightPathRefs[n] = path[0];
+ n++;
+ if (n >= maxStraightPath)
+ {
+ *straightPathCount = n;
+ return DT_SUCCESS;
+ }
+
+ float closestEndPos[3];
+ if (closestPointOnPolyBoundary(path[pathSize-1], endPos, closestEndPos) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ if (pathSize > 1)
+ {
+ float portalApex[3], portalLeft[3], portalRight[3];
+ dtVcopy(portalApex, closestStartPos);
+ dtVcopy(portalLeft, portalApex);
+ dtVcopy(portalRight, portalApex);
+ int apexIndex = 0;
+ int leftIndex = 0;
+ int rightIndex = 0;
+
+ unsigned char leftPolyType = 0;
+ unsigned char rightPolyType = 0;
+
+ dtPolyRef leftPolyRef = path[0];
+ dtPolyRef rightPolyRef = path[0];
+
+ for (int i = 0; i < pathSize; ++i)
+ {
+ float left[3], right[3];
+ unsigned char fromType, toType;
+
+ if (i+1 < pathSize)
+ {
+ // Next portal.
+ if (getPortalPoints(path[i], path[i+1], left, right, fromType, toType) != DT_SUCCESS)
+ {
+ if (closestPointOnPolyBoundary(path[i], endPos, closestEndPos) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ dtVcopy(&straightPath[n*3], closestEndPos);
+ if (straightPathFlags)
+ straightPathFlags[n] = 0;
+ if (straightPathRefs)
+ straightPathRefs[n] = path[i];
+ n++;
+
+ return DT_SUCCESS;
+ }
+
+ // If starting really close the portal, advance.
+ if (i == 0)
+ {
+ float t;
+ if (dtDistancePtSegSqr2D(portalApex, left, right, t) < dtSqr(0.001f))
+ continue;
+ }
+ }
+ else
+ {
+ // End of the path.
+ dtVcopy(left, closestEndPos);
+ dtVcopy(right, closestEndPos);
+
+ fromType = toType = DT_POLYTYPE_GROUND;
+ }
+
+ // Right vertex.
+ if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
+ {
+ if (dtVequal(portalApex, portalRight) || dtTriArea2D(portalApex, portalLeft, right) > 0.0f)
+ {
+ dtVcopy(portalRight, right);
+ rightPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
+ rightPolyType = toType;
+ rightIndex = i;
+ }
+ else
+ {
+ dtVcopy(portalApex, portalLeft);
+ apexIndex = leftIndex;
+
+ unsigned char flags = 0;
+ if (!leftPolyRef)
+ flags = DT_STRAIGHTPATH_END;
+ else if (leftPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
+ flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
+ dtPolyRef ref = leftPolyRef;
+
+ if (!dtVequal(&straightPath[(n-1)*3], portalApex))
+ {
+ // Append new vertex.
+ dtVcopy(&straightPath[n*3], portalApex);
+ if (straightPathFlags)
+ straightPathFlags[n] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n] = ref;
+ n++;
+ // If reached end of path or there is no space to append more vertices, return.
+ if (flags == DT_STRAIGHTPATH_END || n >= maxStraightPath)
+ {
+ *straightPathCount = n;
+ return DT_SUCCESS;
+ }
+ }
+ else
+ {
+ // The vertices are equal, update flags and poly.
+ if (straightPathFlags)
+ straightPathFlags[n-1] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n-1] = ref;
+ }
+
+ dtVcopy(portalLeft, portalApex);
+ dtVcopy(portalRight, portalApex);
+ leftIndex = apexIndex;
+ rightIndex = apexIndex;
+
+ // Restart
+ i = apexIndex;
+
+ continue;
+ }
+ }
+
+ // Left vertex.
+ if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
+ {
+ if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f)
+ {
+ dtVcopy(portalLeft, left);
+ leftPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
+ leftPolyType = toType;
+ leftIndex = i;
+ }
+ else
+ {
+ dtVcopy(portalApex, portalRight);
+ apexIndex = rightIndex;
+
+ unsigned char flags = 0;
+ if (!rightPolyRef)
+ flags = DT_STRAIGHTPATH_END;
+ else if (rightPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
+ flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
+ dtPolyRef ref = rightPolyRef;
+
+ if (!dtVequal(&straightPath[(n-1)*3], portalApex))
+ {
+ // Append new vertex.
+ dtVcopy(&straightPath[n*3], portalApex);
+ if (straightPathFlags)
+ straightPathFlags[n] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n] = ref;
+ n++;
+ // If reached end of path or there is no space to append more vertices, return.
+ if (flags == DT_STRAIGHTPATH_END || n >= maxStraightPath)
+ {
+ *straightPathCount = n;
+ return DT_SUCCESS;
+ }
+ }
+ else
+ {
+ // The vertices are equal, update flags and poly.
+ if (straightPathFlags)
+ straightPathFlags[n-1] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n-1] = ref;
+ }
+
+ dtVcopy(portalLeft, portalApex);
+ dtVcopy(portalRight, portalApex);
+ leftIndex = apexIndex;
+ rightIndex = apexIndex;
+
+ // Restart
+ i = apexIndex;
+
+ continue;
+ }
+ }
+ }
+ }
+
+ // If the point already exists, remove it and add reappend the actual end location.
+ if (n > 0 && dtVequal(&straightPath[(n-1)*3], closestEndPos))
+ n--;
+
+ // Add end point.
+ if (n < maxStraightPath)
+ {
+ dtVcopy(&straightPath[n*3], closestEndPos);
+ if (straightPathFlags)
+ straightPathFlags[n] = DT_STRAIGHTPATH_END;
+ if (straightPathRefs)
+ straightPathRefs[n] = 0;
+ n++;
+ }
+
+ *straightPathCount = n;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_tinyNodePool);
+
+ *visitedCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ static const int MAX_STACK = 48;
+ dtNode* stack[MAX_STACK];
+ int nstack = 0;
+
+ m_tinyNodePool->clear();
+
+ dtNode* startNode = m_tinyNodePool->getNode(startRef);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_CLOSED;
+ stack[nstack++] = startNode;
+
+ float bestPos[3];
+ float bestDist = FLT_MAX;
+ dtNode* bestNode = 0;
+ dtVcopy(bestPos, startPos);
+
+ // Search constraints
+ float searchPos[3], searchRadSqr;
+ dtVlerp(searchPos, startPos, endPos, 0.5f);
+ searchRadSqr = dtSqr(dtVdist(startPos, endPos)/2.0f + 0.001f);
+
+ float verts[DT_VERTS_PER_POLYGON*3];
+
+ while (nstack)
+ {
+ // Pop front.
+ dtNode* curNode = stack[0];
+ for (int i = 0; i < nstack-1; ++i)
+ stack[i] = stack[i+1];
+ nstack--;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef curRef = curNode->id;
+ const dtMeshTile* curTile = 0;
+ const dtPoly* curPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);
+
+ // Collect vertices.
+ const int nverts = curPoly->vertCount;
+ for (int i = 0; i < nverts; ++i)
+ dtVcopy(&verts[i*3], &curTile->verts[curPoly->verts[i]*3]);
+
+ // If target is inside the poly, stop search.
+ if (dtPointInPolygon(endPos, verts, nverts))
+ {
+ bestNode = curNode;
+ dtVcopy(bestPos, endPos);
+ break;
+ }
+
+ // Find wall edges and find nearest point inside the walls.
+ for (int i = 0, j = (int)curPoly->vertCount-1; i < (int)curPoly->vertCount; j = i++)
+ {
+ // Find links to neighbours.
+ static const int MAX_NEIS = 8;
+ int nneis = 0;
+ dtPolyRef neis[MAX_NEIS];
+
+ if (curPoly->neis[j] & DT_EXT_LINK)
+ {
+ // Tile border.
+ for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
+ {
+ const dtLink* link = &curTile->links[k];
+ if (link->edge == j)
+ {
+ if (link->ref != 0)
+ {
+ const dtMeshTile* neiTile = 0;
+ const dtPoly* neiPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
+ {
+ if (nneis < MAX_NEIS)
+ neis[nneis++] = link->ref;
+ }
+ }
+ }
+ }
+ }
+ else if (curPoly->neis[j])
+ {
+ const unsigned int idx = (unsigned int)(curPoly->neis[j]-1);
+ const dtPolyRef ref = m_nav->getPolyRefBase(curTile) | idx;
+ if (filter->passFilter(ref, curTile, &curTile->polys[idx]))
+ {
+ // Internal edge, encode id.
+ neis[nneis++] = ref;
+ }
+ }
+
+ if (!nneis)
+ {
+ // Wall edge, calc distance.
+ const float* vj = &verts[j*3];
+ const float* vi = &verts[i*3];
+ float tseg;
+ const float distSqr = dtDistancePtSegSqr2D(endPos, vj, vi, tseg);
+ if (distSqr < bestDist)
+ {
+ // Update nearest distance.
+ dtVlerp(bestPos, vj,vi, tseg);
+ bestDist = distSqr;
+ bestNode = curNode;
+ }
+ }
+ else
+ {
+ for (int k = 0; k < nneis; ++k)
+ {
+ // Skip if no node can be allocated.
+ dtNode* neighbourNode = m_tinyNodePool->getNode(neis[k]);
+ if (!neighbourNode)
+ continue;
+ // Skip if already visited.
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Skip the link if it is too far from search constraint.
+ // TODO: Maybe should use getPortalPoints(), but this one is way faster.
+ const float* vj = &verts[j*3];
+ const float* vi = &verts[i*3];
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(searchPos, vj, vi, tseg);
+ if (distSqr > searchRadSqr)
+ continue;
+
+ // Mark as the node as visited and push to queue.
+ if (nstack < MAX_STACK)
+ {
+ neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
+ neighbourNode->flags |= DT_NODE_CLOSED;
+ stack[nstack++] = neighbourNode;
+ }
+ }
+ }
+ }
+ }
+
+ int n = 0;
+ if (bestNode)
+ {
+ // Reverse the path.
+ dtNode* prev = 0;
+ dtNode* node = bestNode;
+ do
+ {
+ dtNode* next = m_tinyNodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_tinyNodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store result
+ node = prev;
+ do
+ {
+ visited[n++] = node->id;
+ node = m_tinyNodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxVisitedSize);
+ }
+
+ dtVcopy(resultPos, bestPos);
+
+ *visitedCount = n;
+
+ return DT_SUCCESS;
+}
+
+
+dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
+ unsigned char& fromType, unsigned char& toType) const
+{
+ dtAssert(m_nav);
+
+ const dtMeshTile* fromTile = 0;
+ const dtPoly* fromPoly = 0;
+ if (m_nav->getTileAndPolyByRef(from, &fromTile, &fromPoly) != DT_SUCCESS)
+ return DT_FAILURE;
+ fromType = fromPoly->getType();
+
+ const dtMeshTile* toTile = 0;
+ const dtPoly* toPoly = 0;
+ if (m_nav->getTileAndPolyByRef(to, &toTile, &toPoly) != DT_SUCCESS)
+ return DT_FAILURE;
+ toType = toPoly->getType();
+
+ return getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right);
+}
+
+// Returns portal points between two polygons.
+dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* left, float* right) const
+{
+ // Find the link that points to the 'to' polygon.
+ const dtLink* link = 0;
+ for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
+ {
+ if (fromTile->links[i].ref == to)
+ {
+ link = &fromTile->links[i];
+ break;
+ }
+ }
+ if (!link)
+ return DT_FAILURE;
+
+ // Handle off-mesh connections.
+ if (fromPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ {
+ // Find link that points to first vertex.
+ for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
+ {
+ if (fromTile->links[i].ref == to)
+ {
+ const int v = fromTile->links[i].edge;
+ dtVcopy(left, &fromTile->verts[fromPoly->verts[v]*3]);
+ dtVcopy(right, &fromTile->verts[fromPoly->verts[v]*3]);
+ return DT_SUCCESS;
+ }
+ }
+ return DT_FAILURE;
+ }
+
+ if (toPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ {
+ for (unsigned int i = toPoly->firstLink; i != DT_NULL_LINK; i = toTile->links[i].next)
+ {
+ if (toTile->links[i].ref == from)
+ {
+ const int v = toTile->links[i].edge;
+ dtVcopy(left, &toTile->verts[toPoly->verts[v]*3]);
+ dtVcopy(right, &toTile->verts[toPoly->verts[v]*3]);
+ return DT_SUCCESS;
+ }
+ }
+ return DT_FAILURE;
+ }
+
+ // Find portal vertices.
+ const int v0 = fromPoly->verts[link->edge];
+ const int v1 = fromPoly->verts[(link->edge+1) % (int)fromPoly->vertCount];
+ dtVcopy(left, &fromTile->verts[v0*3]);
+ dtVcopy(right, &fromTile->verts[v1*3]);
+
+ // If the link is at tile boundary, dtClamp the vertices to
+ // the link width.
+ if (link->side != 0xff)
+ {
+ // Unpack portal limits.
+ if (link->bmin != 0 || link->bmax != 255)
+ {
+ const float s = 1.0f/255.0f;
+ const float tmin = link->bmin*s;
+ const float tmax = link->bmax*s;
+ dtVlerp(left, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmin);
+ dtVlerp(right, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmax);
+ }
+ }
+
+ return DT_SUCCESS;
+}
+
+// Returns edge mid point between two polygons.
+dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const
+{
+ float left[3], right[3];
+ unsigned char fromType, toType;
+ if (!getPortalPoints(from, to, left,right, fromType, toType)) return DT_FAILURE;
+ mid[0] = (left[0]+right[0])*0.5f;
+ mid[1] = (left[1]+right[1])*0.5f;
+ mid[2] = (left[2]+right[2])*0.5f;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* mid) const
+{
+ float left[3], right[3];
+ if (getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right) != DT_SUCCESS)
+ return DT_FAILURE;
+ mid[0] = (left[0]+right[0])*0.5f;
+ mid[1] = (left[1]+right[1])*0.5f;
+ mid[2] = (left[2]+right[2])*0.5f;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const
+{
+ dtAssert(m_nav);
+
+ *t = 0;
+ if (pathCount)
+ *pathCount = 0;
+
+ // Validate input
+ if (!startRef || !m_nav->isValidPolyRef(startRef))
+ return DT_FAILURE;
+
+ dtPolyRef curRef = startRef;
+ float verts[DT_VERTS_PER_POLYGON*3];
+ int n = 0;
+
+ hitNormal[0] = 0;
+ hitNormal[1] = 0;
+ hitNormal[2] = 0;
+
+ while (curRef)
+ {
+ // Cast ray against current polygon.
+
+ // The API input has been cheked already, skip checking internal data.
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly);
+
+ // Collect vertices.
+ int nv = 0;
+ for (int i = 0; i < (int)poly->vertCount; ++i)
+ {
+ dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
+ nv++;
+ }
+
+ float tmin, tmax;
+ int segMin, segMax;
+ if (!dtIntersectSegmentPoly2D(startPos, endPos, verts, nv, tmin, tmax, segMin, segMax))
+ {
+ // Could not hit the polygon, keep the old t and report hit.
+ if (pathCount)
+ *pathCount = n;
+ return DT_SUCCESS;
+ }
+ // Keep track of furthest t so far.
+ if (tmax > *t)
+ *t = tmax;
+
+ // Store visited polygons.
+ if (n < maxPath)
+ path[n++] = curRef;
+
+ // Ray end is completely inside the polygon.
+ if (segMax == -1)
+ {
+ *t = FLT_MAX;
+ if (pathCount)
+ *pathCount = n;
+ return DT_SUCCESS;
+ }
+
+ // Follow neighbours.
+ dtPolyRef nextRef = 0;
+
+ for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
+ {
+ const dtLink* link = &tile->links[i];
+
+ // Find link which contains this edge.
+ if ((int)link->edge != segMax)
+ continue;
+
+ // Get pointer to the next polygon.
+ const dtMeshTile* nextTile = 0;
+ const dtPoly* nextPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly);
+
+ // Skip off-mesh connections.
+ if (nextPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Skip links based on filter.
+ if (!filter->passFilter(link->ref, nextTile, nextPoly))
+ continue;
+
+ // If the link is internal, just return the ref.
+ if (link->side == 0xff)
+ {
+ nextRef = link->ref;
+ break;
+ }
+
+ // If the link is at tile boundary,
+
+ // Check if the link spans the whole edge, and accept.
+ if (link->bmin == 0 && link->bmax == 255)
+ {
+ nextRef = link->ref;
+ break;
+ }
+
+ // Check for partial edge links.
+ const int v0 = poly->verts[link->edge];
+ const int v1 = poly->verts[(link->edge+1) % poly->vertCount];
+ const float* left = &tile->verts[v0*3];
+ const float* right = &tile->verts[v1*3];
+
+ // Check that the intersection lies inside the link portal.
+ if (link->side == 0 || link->side == 4)
+ {
+ // Calculate link size.
+ const float s = 1.0f/255.0f;
+ float lmin = left[2] + (right[2] - left[2])*(link->bmin*s);
+ float lmax = left[2] + (right[2] - left[2])*(link->bmax*s);
+ if (lmin > lmax) dtSwap(lmin, lmax);
+
+ // Find Z intersection.
+ float z = startPos[2] + (endPos[2]-startPos[2])*tmax;
+ if (z >= lmin && z <= lmax)
+ {
+ nextRef = link->ref;
+ break;
+ }
+ }
+ else if (link->side == 2 || link->side == 6)
+ {
+ // Calculate link size.
+ const float s = 1.0f/255.0f;
+ float lmin = left[0] + (right[0] - left[0])*(link->bmin*s);
+ float lmax = left[0] + (right[0] - left[0])*(link->bmax*s);
+ if (lmin > lmax) dtSwap(lmin, lmax);
+
+ // Find X intersection.
+ float x = startPos[0] + (endPos[0]-startPos[0])*tmax;
+ if (x >= lmin && x <= lmax)
+ {
+ nextRef = link->ref;
+ break;
+ }
+ }
+ }
+
+ if (!nextRef)
+ {
+ // No neighbour, we hit a wall.
+
+ // Calculate hit normal.
+ const int a = segMax;
+ const int b = segMax+1 < nv ? segMax+1 : 0;
+ const float* va = &verts[a*3];
+ const float* vb = &verts[b*3];
+ const float dx = vb[0] - va[0];
+ const float dz = vb[2] - va[2];
+ hitNormal[0] = dz;
+ hitNormal[1] = 0;
+ hitNormal[2] = -dx;
+ dtVnormalize(hitNormal);
+
+ if (pathCount)
+ *pathCount = n;
+ return DT_SUCCESS;
+ }
+
+ // No hit, advance to neighbour polygon.
+ curRef = nextRef;
+ }
+
+ if (pathCount)
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ *resultCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, centerPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ int n = 0;
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = startNode->id;
+ if (resultParent)
+ resultParent[n] = 0;
+ if (resultCost)
+ resultCost[n] = 0;
+ ++n;
+ }
+
+ const float radiusSqr = dtSqr(radius);
+
+ while (!m_openList->empty())
+ {
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ const dtLink* link = &bestTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours and do not follow back to parent.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Expand to neighbour
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Do not advance if the polygon is excluded by the filter.
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ // Find edge and calc distance to the edge.
+ float va[3], vb[3];
+ if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
+ continue;
+
+ // If the circle is not touching the next polygon, skip it.
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
+ if (distSqr > radiusSqr)
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Cost
+ if (neighbourNode->flags == 0)
+ dtVlerp(neighbourNode->pos, va, vb, 0.5f);
+
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = neighbourNode->id;
+ if (resultParent)
+ resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
+ if (resultCost)
+ resultCost[n] = neighbourNode->total;
+ ++n;
+ }
+ neighbourNode->flags = DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+ }
+ }
+
+ *resultCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ *resultCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ float centerPos[3] = {0,0,0};
+ for (int i = 0; i < nverts; ++i)
+ dtVadd(centerPos,centerPos,&verts[i*3]);
+ dtVscale(centerPos,centerPos,1.0f/nverts);
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, centerPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ int n = 0;
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = startNode->id;
+ if (resultParent)
+ resultParent[n] = 0;
+ if (resultCost)
+ resultCost[n] = 0;
+ ++n;
+ }
+
+ while (!m_openList->empty())
+ {
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ const dtLink* link = &bestTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours and do not follow back to parent.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Expand to neighbour
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Do not advance if the polygon is excluded by the filter.
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ // Find edge and calc distance to the edge.
+ float va[3], vb[3];
+ if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
+ continue;
+
+ // If the poly is not touching the edge to the next polygon, skip the connection it.
+ float tmin, tmax;
+ int segMin, segMax;
+ if (!dtIntersectSegmentPoly2D(va, vb, verts, nverts, tmin, tmax, segMin, segMax))
+ continue;
+ if (tmin > 1.0f || tmax < 0.0f)
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Cost
+ if (neighbourNode->flags == 0)
+ dtVlerp(neighbourNode->pos, va, vb, 0.5f);
+
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = neighbourNode->id;
+ if (resultParent)
+ resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
+ if (resultCost)
+ resultCost[n] = neighbourNode->total;
+ ++n;
+ }
+ neighbourNode->flags = DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+ }
+ }
+
+ *resultCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent,
+ int* resultCount, const int maxResult) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_tinyNodePool);
+
+ *resultCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ static const int MAX_STACK = 48;
+ dtNode* stack[MAX_STACK];
+ int nstack = 0;
+
+ m_tinyNodePool->clear();
+
+ dtNode* startNode = m_tinyNodePool->getNode(startRef);
+ startNode->pidx = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_CLOSED;
+ stack[nstack++] = startNode;
+
+ const float radiusSqr = dtSqr(radius);
+
+ float pa[DT_VERTS_PER_POLYGON*3];
+ float pb[DT_VERTS_PER_POLYGON*3];
+
+ int n = 0;
+ if (n < maxResult)
+ {
+ resultRef[n] = startNode->id;
+ if (resultParent)
+ resultParent[n] = 0;
+ ++n;
+ }
+
+ while (nstack)
+ {
+ // Pop front.
+ dtNode* curNode = stack[0];
+ for (int i = 0; i < nstack-1; ++i)
+ stack[i] = stack[i+1];
+ nstack--;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef curRef = curNode->id;
+ const dtMeshTile* curTile = 0;
+ const dtPoly* curPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);
+
+ for (unsigned int i = curPoly->firstLink; i != DT_NULL_LINK; i = curTile->links[i].next)
+ {
+ const dtLink* link = &curTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours.
+ if (!neighbourRef)
+ continue;
+
+ // Skip if cannot alloca more nodes.
+ dtNode* neighbourNode = m_tinyNodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+ // Skip visited.
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Expand to neighbour
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Skip off-mesh connections.
+ if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Do not advance if the polygon is excluded by the filter.
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ // Find edge and calc distance to the edge.
+ float va[3], vb[3];
+ if (!getPortalPoints(curRef, curPoly, curTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
+ continue;
+
+ // If the circle is not touching the next polygon, skip it.
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
+ if (distSqr > radiusSqr)
+ continue;
+
+ // Mark node visited, this is done before the overlap test so that
+ // we will not visit the poly again if the test fails.
+ neighbourNode->flags |= DT_NODE_CLOSED;
+ neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
+
+ // Check that the polygon does not collide with existing polygons.
+
+ // Collect vertices of the neighbour poly.
+ const int npa = neighbourPoly->vertCount;
+ for (int k = 0; k < npa; ++k)
+ dtVcopy(&pa[k*3], &neighbourTile->verts[neighbourPoly->verts[k]*3]);
+
+ bool overlap = false;
+ for (int j = 0; j < n; ++j)
+ {
+ dtPolyRef pastRef = resultRef[j];
+
+ // Connected polys do not overlap.
+ bool connected = false;
+ for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
+ {
+ if (curTile->links[k].ref == pastRef)
+ {
+ connected = true;
+ break;
+ }
+ }
+ if (connected)
+ continue;
+
+ // Potentially overlapping.
+ const dtMeshTile* pastTile = 0;
+ const dtPoly* pastPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(pastRef, &pastTile, &pastPoly);
+
+ // Get vertices and test overlap
+ const int npb = pastPoly->vertCount;
+ for (int k = 0; k < npb; ++k)
+ dtVcopy(&pb[k*3], &pastTile->verts[pastPoly->verts[k]*3]);
+
+ if (dtOverlapPolyPoly2D(pa,npa, pb,npb))
+ {
+ overlap = true;
+ break;
+ }
+ }
+ if (overlap)
+ continue;
+
+ // This poly is fine, store and advance to the poly.
+ if (n < maxResult)
+ {
+ resultRef[n] = neighbourRef;
+ if (resultParent)
+ resultParent[n] = curRef;
+ ++n;
+ }
+
+ if (nstack < MAX_STACK)
+ {
+ stack[nstack++] = neighbourNode;
+ }
+ }
+ }
+
+ *resultCount = n;
+
+ return DT_SUCCESS;
+}
+
+
+struct dtSegInterval
+{
+ short tmin, tmax;
+};
+
+static void insertInterval(dtSegInterval* ints, int& nints, const int maxInts,
+ const short tmin, const short tmax)
+{
+ if (nints+1 > maxInts) return;
+ // Find insertion point.
+ int idx = 0;
+ while (idx < nints)
+ {
+ if (tmax <= ints[idx].tmin)
+ break;
+ idx++;
+ }
+ // Move current results.
+ if (nints-idx)
+ memmove(ints+idx+1, ints+idx, sizeof(dtSegInterval)*(nints-idx));
+ // Store
+ ints[idx].tmin = tmin;
+ ints[idx].tmax = tmax;
+ nints++;
+}
+
+dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
+ float* segments, int* segmentCount, const int maxSegments) const
+{
+ dtAssert(m_nav);
+
+ *segmentCount = 0;
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ int n = 0;
+ static const int MAX_INTERVAL = 16;
+ dtSegInterval ints[MAX_INTERVAL];
+ int nints;
+
+ for (int i = 0, j = (int)poly->vertCount-1; i < (int)poly->vertCount; j = i++)
+ {
+ // Skip non-solid edges.
+ nints = 0;
+ if (poly->neis[j] & DT_EXT_LINK)
+ {
+ // Tile border.
+ for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
+ {
+ const dtLink* link = &tile->links[k];
+ if (link->edge == j)
+ {
+ if (link->ref != 0)
+ {
+ const dtMeshTile* neiTile = 0;
+ const dtPoly* neiPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
+ {
+ insertInterval(ints, nints, MAX_INTERVAL, link->bmin, link->bmax);
+ }
+ }
+ }
+ }
+ }
+ else if (poly->neis[j])
+ {
+ // Internal edge
+ const unsigned int idx = (unsigned int)(poly->neis[j]-1);
+ const dtPolyRef ref = m_nav->getPolyRefBase(tile) | idx;
+ if (filter->passFilter(ref, tile, &tile->polys[idx]))
+ continue;
+ }
+
+ // Add sentinels
+ insertInterval(ints, nints, MAX_INTERVAL, -1, 0);
+ insertInterval(ints, nints, MAX_INTERVAL, 255, 256);
+
+ // Store segment.
+ const float* vj = &tile->verts[poly->verts[j]*3];
+ const float* vi = &tile->verts[poly->verts[i]*3];
+ for (int k = 1; k < nints; ++k)
+ {
+ // Find the space inbetween the opening areas.
+ const int imin = ints[k-1].tmax;
+ const int imax = ints[k].tmin;
+ if (imin == imax) continue;
+ if (imin == 0 && imax == 255)
+ {
+ if (n < maxSegments)
+ {
+ float* seg = &segments[n*6];
+ n++;
+ dtVcopy(seg+0, vj);
+ dtVcopy(seg+3, vi);
+ }
+ }
+ else
+ {
+ const float tmin = imin/255.0f;
+ const float tmax = imax/255.0f;
+ if (n < maxSegments)
+ {
+ float* seg = &segments[n*6];
+ n++;
+ dtVlerp(seg+0, vj,vi, tmin);
+ dtVlerp(seg+3, vj,vi, tmax);
+ }
+ }
+ }
+ }
+
+ *segmentCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
+ const dtQueryFilter* filter,
+ float* hitDist, float* hitPos, float* hitNormal) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, centerPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ float radiusSqr = dtSqr(maxRadius);
+
+ while (!m_openList->empty())
+ {
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ // Hit test walls.
+ for (int i = 0, j = (int)bestPoly->vertCount-1; i < (int)bestPoly->vertCount; j = i++)
+ {
+ // Skip non-solid edges.
+ if (bestPoly->neis[j] & DT_EXT_LINK)
+ {
+ // Tile border.
+ bool solid = true;
+ for (unsigned int k = bestPoly->firstLink; k != DT_NULL_LINK; k = bestTile->links[k].next)
+ {
+ const dtLink* link = &bestTile->links[k];
+ if (link->edge == j)
+ {
+ if (link->ref != 0)
+ {
+ const dtMeshTile* neiTile = 0;
+ const dtPoly* neiPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
+ solid = false;
+ }
+ break;
+ }
+ }
+ if (!solid) continue;
+ }
+ else if (bestPoly->neis[j])
+ {
+ // Internal edge
+ const unsigned int idx = (unsigned int)(bestPoly->neis[j]-1);
+ const dtPolyRef ref = m_nav->getPolyRefBase(bestTile) | idx;
+ if (filter->passFilter(ref, bestTile, &bestTile->polys[idx]))
+ continue;
+ }
+
+ // Calc distance to the edge.
+ const float* vj = &bestTile->verts[bestPoly->verts[j]*3];
+ const float* vi = &bestTile->verts[bestPoly->verts[i]*3];
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, vj, vi, tseg);
+
+ // Edge is too far, skip.
+ if (distSqr > radiusSqr)
+ continue;
+
+ // Hit wall, update radius.
+ radiusSqr = distSqr;
+ // Calculate hit pos.
+ hitPos[0] = vj[0] + (vi[0] - vj[0])*tseg;
+ hitPos[1] = vj[1] + (vi[1] - vj[1])*tseg;
+ hitPos[2] = vj[2] + (vi[2] - vj[2])*tseg;
+ }
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ const dtLink* link = &bestTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours and do not follow back to parent.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Expand to neighbour.
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Skip off-mesh connections.
+ if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Calc distance to the edge.
+ const float* va = &bestTile->verts[bestPoly->verts[link->edge]*3];
+ const float* vb = &bestTile->verts[bestPoly->verts[(link->edge+1) % bestPoly->vertCount]*3];
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
+
+ // If the circle is not touching the next polygon, skip it.
+ if (distSqr > radiusSqr)
+ continue;
+
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Cost
+ if (neighbourNode->flags == 0)
+ {
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
+ neighbourRef, neighbourPoly, neighbourTile, neighbourNode->pos);
+ }
+
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ neighbourNode->flags |= DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+ }
+ }
+
+ // Calc hit normal.
+ dtVsub(hitNormal, centerPos, hitPos);
+ dtVnormalize(hitNormal);
+
+ *hitDist = sqrtf(radiusSqr);
+
+ return DT_SUCCESS;
+}
+
+bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const
+{
+ if (!m_nodePool) return false;
+ const dtNode* node = m_nodePool->findNode(ref);
+ return node && node->flags & DT_NODE_CLOSED;
+}
diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.h b/dep/recastnavigation/Detour/DetourNavMeshQuery.h
new file mode 100644
index 0000000..f5046d8
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshQuery.h
@@ -0,0 +1,407 @@
+//
+// 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 DETOURNAVMESHQUERY_H
+#define DETOURNAVMESHQUERY_H
+
+#include "DetourNavMesh.h"
+
+
+// Define DT_VIRTUAL_QUERYFILTER if you wish to derive a custom filter from dtQueryFilter.
+// On certain platforms indirect or virtual function call is expensive. The default
+// setting is to use non-virtual functions, the actualy implementations of the functions
+// are declared as inline for maximum speed.
+
+//#define DT_VIRTUAL_QUERYFILTER 1
+
+// Class for polygon filtering and cost calculation during query operations.
+// - It is possible to derive a custom query filter from dtQueryFilter by overriding
+// the virtual functions passFilter() and getCost().
+// - Both functions should be as fast as possible. Use cached local copy of data
+// instead of accessing your own objects where possible.
+// - You do not need to adhere to the flags and cost logic provided by the default
+// implementation.
+// - In order for the A* to work properly, the cost should be proportional to
+// the travel distance. Using cost modifier less than 1.0 is likely to lead
+// to problems during pathfinding.
+class dtQueryFilter
+{
+ float m_areaCost[DT_MAX_AREAS]; // Array storing cost per area type, used by default implementation.
+ unsigned short m_includeFlags; // Include poly flags, used by default implementation.
+ unsigned short m_excludeFlags; // Exclude poly flags, used by default implementation.
+
+public:
+ dtQueryFilter();
+
+ // Returns true if the polygon is can visited.
+ // Params:
+ // ref - (in) reference to the polygon test.
+ // tile - (in) pointer to the tile of the polygon test.
+ // poly - (in) pointer to the polygon test.
+#ifdef DT_VIRTUAL_QUERYFILTER
+ virtual bool passFilter(const dtPolyRef ref,
+ const dtMeshTile* tile,
+ const dtPoly* poly) const;
+#else
+ bool passFilter(const dtPolyRef ref,
+ const dtMeshTile* tile,
+ const dtPoly* poly) const;
+#endif
+
+ // Returns cost to travel from 'pa' to 'pb'.'
+ // The segment is fully contained inside 'cur'.
+ // 'pa' lies on the edge between 'prev' and 'cur',
+ // 'pb' lies on the edge between 'cur' and 'next'.
+ // Params:
+ // pa - (in) segment start position.
+ // pb - (in) segment end position.
+ // prevRef, prevTile, prevPoly - (in) data describing the previous polygon, can be null.
+ // curRef, curTile, curPoly - (in) data describing the current polygon.
+ // nextRef, nextTile, nextPoly - (in) data describing the next polygon, can be null.
+#ifdef DT_VIRTUAL_QUERYFILTER
+ virtual float getCost(const float* pa, const float* pb,
+ const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
+ const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
+ const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
+#else
+ float getCost(const float* pa, const float* pb,
+ const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
+ const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
+ const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
+#endif
+
+ // Getters and setters for the default implementation data.
+ inline float getAreaCost(const int i) const { return m_areaCost[i]; }
+ inline void setAreaCost(const int i, const float cost) { m_areaCost[i] = cost; }
+
+ inline unsigned short getIncludeFlags() const { return m_includeFlags; }
+ inline void setIncludeFlags(const unsigned short flags) { m_includeFlags = flags; }
+
+ inline unsigned short getExcludeFlags() const { return m_excludeFlags; }
+ inline void setExcludeFlags(const unsigned short flags) { m_excludeFlags = flags; }
+};
+
+class dtNavMeshQuery
+{
+public:
+ dtNavMeshQuery();
+ ~dtNavMeshQuery();
+
+ // Initializes the nav mesh query.
+ // Params:
+ // nav - (in) pointer to navigation mesh data.
+ // maxNodes - (in) Maximum number of search nodes to use (max 65536).
+ // Returns: True if succeed, else false.
+ dtStatus init(const dtNavMesh* nav, const int maxNodes);
+
+ // Finds the nearest navigation polygon around the center location.
+ // Params:
+ // center[3] - (in) The center of the search box.
+ // extents[3] - (in) The extents of the search box.
+ // filter - (in) path polygon filter.
+ // nearestRef - (out) Reference to the nearest polygon.
+ // nearestPt[3] - (out, opt) The nearest point on found polygon, null if not needed.
+ // Returns: Reference identifier for the polygon, or 0 if no polygons found.
+ dtStatus findNearestPoly(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* nearestRef, float* nearestPt) const;
+
+ // Returns polygons which overlap the query box.
+ // Params:
+ // center[3] - (in) the center of the search box.
+ // extents[3] - (in) the extents of the search box.
+ // filter - (in) path polygon filter.
+ // polys - (out) array holding the search result.
+ // polyCount - (out) Number of polygons in search result array.
+ // maxPolys - (in) The max number of polygons the polys array can hold.
+ dtStatus queryPolygons(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* polys, int* polyCount, const int maxPolys) const;
+
+ // Finds path from start polygon to end polygon.
+ // If target polygon canno be reached through the navigation graph,
+ // the last node on the array is nearest node to the end polygon.
+ // Start end end positions are needed to calculate more accurate
+ // traversal cost at start end end polygons.
+ // Params:
+ // startRef - (in) ref to path start polygon.
+ // endRef - (in) ref to path end polygon.
+ // startPos[3] - (in) Path start location.
+ // endPos[3] - (in) Path end location.
+ // filter - (in) path polygon filter.
+ // path - (out) array holding the search result.
+ // pathCount - (out) Number of polygons in search result array.
+ // maxPath - (in) The max number of polygons the path array can hold. Must be at least 1.
+ dtStatus findPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ dtPolyRef* path, int* pathCount, const int maxPath) const;
+
+ // Intializes sliced path find query.
+ // Note 1: calling any other dtNavMeshQuery method before calling findPathEnd()
+ // may results in corrupted data!
+ // Note 2: The pointer to filter is store, and used in subsequent
+ // calls to updateSlicedFindPath().
+ // Params:
+ // startRef - (in) ref to path start polygon.
+ // endRef - (in) ref to path end polygon.
+ // startPos[3] - (in) Path start location.
+ // endPos[3] - (in) Path end location.
+ // filter - (in) path polygon filter.
+ dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter);
+
+ // Updates sliced path find query.
+ // Params:
+ // maxIter - (in) max number of iterations to update.
+ // Returns: Path query state.
+ dtStatus updateSlicedFindPath(const int maxIter);
+
+ // Finalizes sliced path find query and returns found path.
+ // path - (out) array holding the search result.
+ // pathCount - (out) Number of polygons in search result array.
+ // maxPath - (in) The max number of polygons the path array can hold.
+ dtStatus finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath);
+
+ // Finalizes partial sliced path find query and returns path to the furthest
+ // polygon on the existing path that was visited during the search.
+ // existing - (out) Array of polygons in the existing path.
+ // existingSize - (out) Number of polygons in existing path array.
+ // path - (out) array holding the search result.
+ // pathCount - (out) Number of polygons in search result array.
+ // maxPath - (in) The max number of polygons the path array can hold.
+ dtStatus finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
+ dtPolyRef* path, int* pathCount, const int maxPath);
+
+ // Finds a straight path from start to end locations within the corridor
+ // described by the path polygons.
+ // Start and end locations will be clamped on the corridor.
+ // The returned polygon references are point to polygon which was entered when
+ // a path point was added. For the end point, zero will be returned. This allows
+ // to match for example off-mesh link points to their representative polygons.
+ // Params:
+ // startPos[3] - (in) Path start location.
+ // endPo[3] - (in) Path end location.
+ // path - (in) Array of connected polygons describing the corridor.
+ // pathSize - (in) Number of polygons in path array.
+ // straightPath - (out) Points describing the straight path.
+ // straightPathFlags - (out, opt) Flags describing each point type, see dtStraightPathFlags.
+ // straightPathRefs - (out, opt) References to polygons at point locations.
+ // straightPathCount - (out) Number of points in the path.
+ // maxStraightPath - (in) The max number of points the straight path array can hold. Must be at least 1.
+ dtStatus findStraightPath(const float* startPos, const float* endPos,
+ const dtPolyRef* path, const int pathSize,
+ float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
+ int* straightPathCount, const int maxStraightPath) const;
+
+ // Moves from startPos to endPos constrained to the navmesh.
+ // If the endPos is reachable, the resultPos will be endPos,
+ // or else the resultPos will be the nearest point in navmesh.
+ // Note: The resulting point is not projected to the ground, use getPolyHeight() to get height.
+ // Note: The algorithm is optimized for small delta movement and small number of polygons.
+ // Params:
+ // startRef - (in) ref to the polygon where startPos lies.
+ // startPos[3] - (in) start position of the mover.
+ // endPos[3] - (in) desired end position of the mover.
+ // filter - (in) path polygon filter.
+ // resultPos[3] - (out) new position of the mover.
+ // visited - (out) array of visited polygons.
+ // visitedCount - (out) Number of entries in the visited array.
+ // maxVisitedSize - (in) max number of polygons in the visited array.
+ dtStatus moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const;
+
+ // Casts 'walkability' ray along the navmesh surface from startPos towards the endPos.
+ // Params:
+ // startRef - (in) ref to the polygon where the start lies.
+ // startPos[3] - (in) start position of the query.
+ // endPos[3] - (in) end position of the query.
+ // t - (out) hit parameter along the segment, FLT_MAX if no hit.
+ // hitNormal[3] - (out) normal of the nearest hit.
+ // filter - (in) path polygon filter.
+ // path - (out,opt) visited path polygons.
+ // pathCount - (out,opt) Number of polygons visited.
+ // maxPath - (in) max number of polygons in the path array.
+ dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
+
+ // Returns distance to nearest wall from the specified location.
+ // Params:
+ // startRef - (in) ref to the polygon where the center lies.
+ // centerPos[3] - (in) center if the query circle.
+ // maxRadius - (in) max search radius.
+ // filter - (in) path polygon filter.
+ // hitDist - (out) distance to nearest wall from the test location.
+ // hitPos[3] - (out) location of the nearest hit.
+ // hitNormal[3] - (out) normal of the nearest hit.
+ dtStatus findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
+ const dtQueryFilter* filter,
+ float* hitDist, float* hitPos, float* hitNormal) const;
+
+ // Finds polygons found along the navigation graph which touch the specified circle.
+ // Params:
+ // startRef - (in) ref to the polygon where the search starts.
+ // centerPos[3] - (in) center if the query circle.
+ // radius - (in) radius of the query circle.
+ // filter - (in) path polygon filter.
+ // resultRef - (out, opt) refs to the polygons touched by the circle.
+ // resultParent - (out, opt) parent of each result polygon.
+ // resultCost - (out, opt) search cost at each result polygon.
+ // resultCount - (out, opt) Number of results.
+ // maxResult - (int) maximum capacity of search results.
+ dtStatus findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const;
+
+ // Finds polygons found along the navigation graph which touch the convex polygon shape.
+ // Params:
+ // startRef - (in) ref to the polygon where the search starts.
+ // verts[3*n] - (in) vertices describing convex polygon shape (CCW).
+ // nverts - (in) number of vertices in the polygon.
+ // filter - (in) path polygon filter.
+ // resultRef - (out, opt) refs to the polygons touched by the circle.
+ // resultParent - (out, opt) parent of each result polygon.
+ // resultCost - (out, opt) search cost at each result polygon.
+ // resultCount - (out) number of results.
+ // maxResult - (int) maximum capacity of search results.
+ dtStatus findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const;
+
+ // Finds non-overlapping local neighbourhood around center location.
+ // Note: The algorithm is optimized for small query radius and small number of polygons.
+ // Params:
+ // startRef - (in) ref to the polygon where the search starts.
+ // centerPos[3] - (in) center if the query circle.
+ // radius - (in) radius of the query circle.
+ // filter - (in) path polygon filter.
+ // resultRef - (out) refs to the polygons touched by the circle.
+ // resultParent - (out, opt) parent of each result polygon.
+ // resultCount - (out) number of results.
+ // maxResult - (int) maximum capacity of search results.
+ dtStatus findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent,
+ int* resultCount, const int maxResult) const;
+
+ // Returns wall segments of specified polygon.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // filter - (in) path polygon filter.
+ // segments[6*maxSegments] - (out) wall segments (2 endpoints per segment).
+ // segmentCount - (out) number of wall segments.
+ // maxSegments - (in) max number of segments that can be stored in 'segments'.
+ dtStatus getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
+ float* segments, int* segmentCount, const int maxSegments) const;
+
+ // Returns closest point on navigation polygon.
+ // Uses detail polygons to find the closest point to the navigation polygon surface.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // pos[3] - (in) the point to check.
+ // closest[3] - (out) closest point.
+ // Returns: true if closest point found.
+ dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const;
+
+ // Returns closest point on navigation polygon boundary.
+ // Uses the navigation polygon boundary to snap the point to poly boundary
+ // if it is outside the polygon. Much faster than closestPointToPoly. Does not affect height.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // pos[3] - (in) the point to check.
+ // closest[3] - (out) closest point.
+ // Returns: true if closest point found.
+ dtStatus closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const;
+
+ // Returns start and end location of an off-mesh link polygon.
+ // Params:
+ // prevRef - (in) ref to the polygon before the link (used to select direction).
+ // polyRef - (in) ref to the off-mesh link polygon.
+ // startPos[3] - (out) start point of the link.
+ // endPos[3] - (out) end point of the link.
+ // Returns: true if link is found.
+ dtStatus getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const;
+
+ // Returns height of the polygon at specified location.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // pos[3] - (in) the point where to locate the height.
+ // height - (out) height at the location.
+ // Returns: true if over polygon.
+ dtStatus getPolyHeight(dtPolyRef ref, const float* pos, float* height) const;
+
+ // Returns true if poly reference ins in closed list.
+ bool isInClosedList(dtPolyRef ref) const;
+
+ class dtNodePool* getNodePool() const { return m_nodePool; }
+
+private:
+
+ // Returns neighbour tile based on side.
+ dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;
+
+ // Queries polygons within a tile.
+ int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter,
+ dtPolyRef* polys, const int maxPolys) const;
+ // Find nearest polygon within a tile.
+ dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents,
+ const dtQueryFilter* filter, float* nearestPt) const;
+ // Returns closest point on polygon.
+ dtStatus closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest) const;
+
+ // Returns portal points between two polygons.
+ dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
+ unsigned char& fromType, unsigned char& toType) const;
+ dtStatus getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* left, float* right) const;
+
+ // Returns edge mid point between two polygons.
+ dtStatus getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const;
+ dtStatus getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* mid) const;
+
+ const dtNavMesh* m_nav; // Pointer to navmesh data.
+
+ struct dtQueryData
+ {
+ dtStatus status;
+ struct dtNode* lastBestNode;
+ float lastBestNodeCost;
+ dtPolyRef startRef, endRef;
+ float startPos[3], endPos[3];
+ const dtQueryFilter* filter;
+ };
+ dtQueryData m_query; // Sliced query state.
+
+ class dtNodePool* m_tinyNodePool; // Pointer to small node pool.
+ class dtNodePool* m_nodePool; // Pointer to node pool.
+ class dtNodeQueue* m_openList; // Pointer to open list queue.
+};
+
+// Helper function to allocate navmesh query class using Detour allocator.
+dtNavMeshQuery* dtAllocNavMeshQuery();
+void dtFreeNavMeshQuery(dtNavMeshQuery* query);
+
+#endif // DETOURNAVMESHQUERY_H
diff --git a/dep/recastnavigation/Detour/DetourNode.cpp b/dep/recastnavigation/Detour/DetourNode.cpp
new file mode 100644
index 0000000..0d1af83
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNode.cpp
@@ -0,0 +1,164 @@
+//
+// 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 "DetourNode.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include "DetourCommon.h"
+#include <string.h>
+
+inline unsigned int dtHashRef(dtPolyRef a)
+{
+ a = (~a) + (a << 18);
+ a = a ^ (a >> 31);
+ a = a * 21;
+ a = a ^ (a >> 11);
+ a = a + (a << 6);
+ a = a ^ (a >> 22);
+ return (unsigned int)a;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNodePool::dtNodePool(int maxNodes, int hashSize) :
+ m_nodes(0),
+ m_first(0),
+ m_next(0),
+ m_maxNodes(maxNodes),
+ m_hashSize(hashSize),
+ m_nodeCount(0)
+{
+ dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize);
+ dtAssert(m_maxNodes > 0);
+
+ m_nodes = (dtNode*)dtAlloc(sizeof(dtNode)*m_maxNodes, DT_ALLOC_PERM);
+ m_next = (unsigned short*)dtAlloc(sizeof(unsigned short)*m_maxNodes, DT_ALLOC_PERM);
+ m_first = (unsigned short*)dtAlloc(sizeof(unsigned short)*hashSize, DT_ALLOC_PERM);
+
+ dtAssert(m_nodes);
+ dtAssert(m_next);
+ dtAssert(m_first);
+
+ memset(m_first, 0xff, sizeof(unsigned short)*m_hashSize);
+ memset(m_next, 0xff, sizeof(unsigned short)*m_maxNodes);
+}
+
+dtNodePool::~dtNodePool()
+{
+ dtFree(m_nodes);
+ dtFree(m_next);
+ dtFree(m_first);
+}
+
+void dtNodePool::clear()
+{
+ memset(m_first, 0xff, sizeof(unsigned short)*m_hashSize);
+ m_nodeCount = 0;
+}
+
+dtNode* dtNodePool::findNode(dtPolyRef id)
+{
+ unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
+ unsigned short i = m_first[bucket];
+ while (i != DT_NULL_IDX)
+ {
+ if (m_nodes[i].id == id)
+ return &m_nodes[i];
+ i = m_next[i];
+ }
+ return 0;
+}
+
+dtNode* dtNodePool::getNode(dtPolyRef id)
+{
+ unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
+ unsigned short i = m_first[bucket];
+ dtNode* node = 0;
+ while (i != DT_NULL_IDX)
+ {
+ if (m_nodes[i].id == id)
+ return &m_nodes[i];
+ i = m_next[i];
+ }
+
+ if (m_nodeCount >= m_maxNodes)
+ return 0;
+
+ i = (unsigned short)m_nodeCount;
+ m_nodeCount++;
+
+ // Init node
+ node = &m_nodes[i];
+ node->pidx = 0;
+ node->cost = 0;
+ node->total = 0;
+ node->id = id;
+ node->flags = 0;
+
+ m_next[i] = m_first[bucket];
+ m_first[bucket] = i;
+
+ return node;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNodeQueue::dtNodeQueue(int n) :
+ m_heap(0),
+ m_capacity(n),
+ m_size(0)
+{
+ dtAssert(m_capacity > 0);
+
+ m_heap = (dtNode**)dtAlloc(sizeof(dtNode*)*(m_capacity+1), DT_ALLOC_PERM);
+ dtAssert(m_heap);
+}
+
+dtNodeQueue::~dtNodeQueue()
+{
+ dtFree(m_heap);
+}
+
+void dtNodeQueue::bubbleUp(int i, dtNode* node)
+{
+ int parent = (i-1)/2;
+ // note: (index > 0) means there is a parent
+ while ((i > 0) && (m_heap[parent]->total > node->total))
+ {
+ m_heap[i] = m_heap[parent];
+ i = parent;
+ parent = (i-1)/2;
+ }
+ m_heap[i] = node;
+}
+
+void dtNodeQueue::trickleDown(int i, dtNode* node)
+{
+ int child = (i*2)+1;
+ while (child < m_size)
+ {
+ if (((child+1) < m_size) &&
+ (m_heap[child]->total > m_heap[child+1]->total))
+ {
+ child++;
+ }
+ m_heap[i] = m_heap[child];
+ i = child;
+ child = (i*2)+1;
+ }
+ bubbleUp(i, node);
+}
diff --git a/dep/recastnavigation/Detour/DetourNode.h b/dep/recastnavigation/Detour/DetourNode.h
new file mode 100644
index 0000000..e893f78
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNode.h
@@ -0,0 +1,157 @@
+//
+// 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 DETOURNODE_H
+#define DETOURNODE_H
+
+#include "DetourNavMesh.h"
+
+enum dtNodeFlags
+{
+ DT_NODE_OPEN = 0x01,
+ DT_NODE_CLOSED = 0x02,
+};
+
+static const unsigned short DT_NULL_IDX = 0xffff;
+
+struct dtNode
+{
+ float pos[3]; // Position of the node.
+ float cost; // Cost from previous node to current node.
+ float total; // Cost up to the node.
+ unsigned int pidx : 30; // Index to parent node.
+ unsigned int flags : 2; // Node flags 0/open/closed.
+ dtPolyRef id; // Polygon ref the node corresponds to.
+};
+
+class dtNodePool
+{
+public:
+ dtNodePool(int maxNodes, int hashSize);
+ ~dtNodePool();
+ inline void operator=(const dtNodePool&) {}
+ void clear();
+ dtNode* getNode(dtPolyRef id);
+ dtNode* findNode(dtPolyRef id);
+
+ inline unsigned int getNodeIdx(const dtNode* node) const
+ {
+ if (!node) return 0;
+ return (unsigned int)(node - m_nodes)+1;
+ }
+
+ inline dtNode* getNodeAtIdx(unsigned int idx)
+ {
+ if (!idx) return 0;
+ return &m_nodes[idx-1];
+ }
+
+ inline const dtNode* getNodeAtIdx(unsigned int idx) const
+ {
+ if (!idx) return 0;
+ return &m_nodes[idx-1];
+ }
+
+ inline int getMemUsed() const
+ {
+ return sizeof(*this) +
+ sizeof(dtNode)*m_maxNodes +
+ sizeof(unsigned short)*m_maxNodes +
+ sizeof(unsigned short)*m_hashSize;
+ }
+
+ inline int getMaxNodes() const { return m_maxNodes; }
+
+ inline int getHashSize() const { return m_hashSize; }
+ inline unsigned short getFirst(int bucket) const { return m_first[bucket]; }
+ inline unsigned short getNext(int i) const { return m_next[i]; }
+
+private:
+
+ dtNode* m_nodes;
+ unsigned short* m_first;
+ unsigned short* m_next;
+ const int m_maxNodes;
+ const int m_hashSize;
+ int m_nodeCount;
+};
+
+class dtNodeQueue
+{
+public:
+ dtNodeQueue(int n);
+ ~dtNodeQueue();
+ inline void operator=(dtNodeQueue&) {}
+
+ inline void clear()
+ {
+ m_size = 0;
+ }
+
+ inline dtNode* top()
+ {
+ return m_heap[0];
+ }
+
+ inline dtNode* pop()
+ {
+ dtNode* result = m_heap[0];
+ m_size--;
+ trickleDown(0, m_heap[m_size]);
+ return result;
+ }
+
+ inline void push(dtNode* node)
+ {
+ m_size++;
+ bubbleUp(m_size-1, node);
+ }
+
+ inline void modify(dtNode* node)
+ {
+ for (int i = 0; i < m_size; ++i)
+ {
+ if (m_heap[i] == node)
+ {
+ bubbleUp(i, node);
+ return;
+ }
+ }
+ }
+
+ inline bool empty() const { return m_size == 0; }
+
+ inline int getMemUsed() const
+ {
+ return sizeof(*this) +
+ sizeof(dtNode*)*(m_capacity+1);
+ }
+
+ inline int getCapacity() const { return m_capacity; }
+
+private:
+ void bubbleUp(int i, dtNode* node);
+ void trickleDown(int i, dtNode* node);
+
+ dtNode** m_heap;
+ const int m_capacity;
+ int m_size;
+};
+
+
+#endif // DETOURNODE_H
\ No newline at end of file
diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp
new file mode 100644
index 0000000..a255c9b
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp
@@ -0,0 +1,532 @@
+//
+// 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 "DetourObstacleAvoidance.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <string.h>
+#include <math.h>
+#include <float.h>
+#include <new>
+
+
+static int sweepCircleCircle(const float* c0, const float r0, const float* v,
+ const float* c1, const float r1,
+ float& tmin, float& tmax)
+{
+ static const float EPS = 0.0001f;
+ float s[3];
+ dtVsub(s,c1,c0);
+ float r = r0+r1;
+ float c = dtVdot2D(s,s) - r*r;
+ float a = dtVdot2D(v,v);
+ if (a < EPS) return 0; // not moving
+
+ // Overlap, calc time to exit.
+ float b = dtVdot2D(v,s);
+ float d = b*b - a*c;
+ if (d < 0.0f) return 0; // no intersection.
+ a = 1.0f / a;
+ const float rd = dtSqrt(d);
+ tmin = (b - rd) * a;
+ tmax = (b + rd) * a;
+ return 1;
+}
+
+static int isectRaySeg(const float* ap, const float* u,
+ const float* bp, const float* bq,
+ float& t)
+{
+ float v[3], w[3];
+ dtVsub(v,bq,bp);
+ dtVsub(w,ap,bp);
+ float d = dtVperp2D(u,v);
+ if (fabsf(d) < 1e-6f) return 0;
+ d = 1.0f/d;
+ t = dtVperp2D(v,w) * d;
+ if (t < 0 || t > 1) return 0;
+ float s = dtVperp2D(u,w) * d;
+ if (s < 0 || s > 1) return 0;
+ return 1;
+}
+
+
+
+dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData()
+{
+ void* mem = dtAlloc(sizeof(dtObstacleAvoidanceDebugData), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtObstacleAvoidanceDebugData;
+}
+
+void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr)
+{
+ if (!ptr) return;
+ ptr->~dtObstacleAvoidanceDebugData();
+ dtFree(ptr);
+}
+
+
+dtObstacleAvoidanceDebugData::dtObstacleAvoidanceDebugData() :
+ m_nsamples(0),
+ m_maxSamples(0),
+ m_vel(0),
+ m_ssize(0),
+ m_pen(0),
+ m_vpen(0),
+ m_vcpen(0),
+ m_spen(0),
+ m_tpen(0)
+{
+}
+
+dtObstacleAvoidanceDebugData::~dtObstacleAvoidanceDebugData()
+{
+ dtFree(m_vel);
+ dtFree(m_ssize);
+ dtFree(m_pen);
+ dtFree(m_vpen);
+ dtFree(m_vcpen);
+ dtFree(m_spen);
+ dtFree(m_tpen);
+}
+
+bool dtObstacleAvoidanceDebugData::init(const int maxSamples)
+{
+ dtAssert(maxSamples);
+ m_maxSamples = maxSamples;
+
+ m_vel = (float*)dtAlloc(sizeof(float)*3*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_vel)
+ return false;
+ m_pen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_pen)
+ return false;
+ m_ssize = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_ssize)
+ return false;
+ m_vpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_vpen)
+ return false;
+ m_vcpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_vcpen)
+ return false;
+ m_spen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_spen)
+ return false;
+ m_tpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_tpen)
+ return false;
+
+ return true;
+}
+
+void dtObstacleAvoidanceDebugData::reset()
+{
+ m_nsamples = 0;
+}
+
+void dtObstacleAvoidanceDebugData::addSample(const float* vel, const float ssize, const float pen,
+ const float vpen, const float vcpen, const float spen, const float tpen)
+{
+ if (m_nsamples >= m_maxSamples)
+ return;
+ dtAssert(m_vel);
+ dtAssert(m_ssize);
+ dtAssert(m_pen);
+ dtAssert(m_vpen);
+ dtAssert(m_vcpen);
+ dtAssert(m_spen);
+ dtAssert(m_tpen);
+ dtVcopy(&m_vel[m_nsamples*3], vel);
+ m_ssize[m_nsamples] = ssize;
+ m_pen[m_nsamples] = pen;
+ m_vpen[m_nsamples] = vpen;
+ m_vcpen[m_nsamples] = vcpen;
+ m_spen[m_nsamples] = spen;
+ m_tpen[m_nsamples] = tpen;
+ m_nsamples++;
+}
+
+static void normalizeArray(float* arr, const int n)
+{
+ // Normalize penaly range.
+ float minPen = FLT_MAX;
+ float maxPen = -FLT_MAX;
+ for (int i = 0; i < n; ++i)
+ {
+ minPen = dtMin(minPen, arr[i]);
+ maxPen = dtMax(maxPen, arr[i]);
+ }
+ const float penRange = maxPen-minPen;
+ const float s = penRange > 0.001f ? (1.0f / penRange) : 1;
+ for (int i = 0; i < n; ++i)
+ arr[i] = dtClamp((arr[i]-minPen)*s, 0.0f, 1.0f);
+}
+
+void dtObstacleAvoidanceDebugData::normalizeSamples()
+{
+ normalizeArray(m_pen, m_nsamples);
+ normalizeArray(m_vpen, m_nsamples);
+ normalizeArray(m_vcpen, m_nsamples);
+ normalizeArray(m_spen, m_nsamples);
+ normalizeArray(m_tpen, m_nsamples);
+}
+
+
+dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery()
+{
+ void* mem = dtAlloc(sizeof(dtObstacleAvoidanceQuery), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtObstacleAvoidanceQuery;
+}
+
+void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr)
+{
+ if (!ptr) return;
+ ptr->~dtObstacleAvoidanceQuery();
+ dtFree(ptr);
+}
+
+
+dtObstacleAvoidanceQuery::dtObstacleAvoidanceQuery() :
+ m_velBias(0.0f),
+ m_weightDesVel(0.0f),
+ m_weightCurVel(0.0f),
+ m_weightSide(0.0f),
+ m_weightToi(0.0f),
+ m_horizTime(0.0f),
+ m_maxCircles(0),
+ m_circles(0),
+ m_ncircles(0),
+ m_maxSegments(0),
+ m_segments(0),
+ m_nsegments(0)
+{
+}
+
+dtObstacleAvoidanceQuery::~dtObstacleAvoidanceQuery()
+{
+ dtFree(m_circles);
+ dtFree(m_segments);
+}
+
+bool dtObstacleAvoidanceQuery::init(const int maxCircles, const int maxSegments)
+{
+ m_maxCircles = maxCircles;
+ m_ncircles = 0;
+ m_circles = (dtObstacleCircle*)dtAlloc(sizeof(dtObstacleCircle)*m_maxCircles, DT_ALLOC_PERM);
+ if (!m_circles)
+ return false;
+ memset(m_circles, 0, sizeof(dtObstacleCircle)*m_maxCircles);
+
+ m_maxSegments = maxSegments;
+ m_nsegments = 0;
+ m_segments = (dtObstacleSegment*)dtAlloc(sizeof(dtObstacleSegment)*m_maxSegments, DT_ALLOC_PERM);
+ if (!m_segments)
+ return false;
+ memset(m_segments, 0, sizeof(dtObstacleSegment)*m_maxSegments);
+
+ return true;
+}
+
+void dtObstacleAvoidanceQuery::reset()
+{
+ m_ncircles = 0;
+ m_nsegments = 0;
+}
+
+void dtObstacleAvoidanceQuery::addCircle(const float* pos, const float rad,
+ const float* vel, const float* dvel)
+{
+ if (m_ncircles >= m_maxCircles)
+ return;
+
+ dtObstacleCircle* cir = &m_circles[m_ncircles++];
+ dtVcopy(cir->p, pos);
+ cir->rad = rad;
+ dtVcopy(cir->vel, vel);
+ dtVcopy(cir->dvel, dvel);
+}
+
+void dtObstacleAvoidanceQuery::addSegment(const float* p, const float* q)
+{
+ if (m_nsegments > m_maxSegments)
+ return;
+
+ dtObstacleSegment* seg = &m_segments[m_nsegments++];
+ dtVcopy(seg->p, p);
+ dtVcopy(seg->q, q);
+}
+
+void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel)
+{
+ // Prepare obstacles
+ for (int i = 0; i < m_ncircles; ++i)
+ {
+ dtObstacleCircle* cir = &m_circles[i];
+
+ // Side
+ const float* pa = pos;
+ const float* pb = cir->p;
+
+ const float orig[3] = {0,0};
+ float dv[3];
+ dtVsub(cir->dp,pb,pa);
+ dtVnormalize(cir->dp);
+ dtVsub(dv, cir->dvel, dvel);
+
+ const float a = dtTriArea2D(orig, cir->dp,dv);
+ if (a < 0.01f)
+ {
+ cir->np[0] = -cir->dp[2];
+ cir->np[2] = cir->dp[0];
+ }
+ else
+ {
+ cir->np[0] = cir->dp[2];
+ cir->np[2] = -cir->dp[0];
+ }
+ }
+
+ for (int i = 0; i < m_nsegments; ++i)
+ {
+ dtObstacleSegment* seg = &m_segments[i];
+
+ // Precalc if the agent is really close to the segment.
+ const float r = 0.01f;
+ float t;
+ seg->touch = dtDistancePtSegSqr2D(pos, seg->p, seg->q, t) < dtSqr(r);
+ }
+}
+
+float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs,
+ const float* pos, const float rad,
+ const float vmax, const float* vel, const float* dvel,
+ dtObstacleAvoidanceDebugData* debug)
+{
+ // Find min time of impact and exit amongst all obstacles.
+ float tmin = m_horizTime;
+ float side = 0;
+ int nside = 0;
+
+ for (int i = 0; i < m_ncircles; ++i)
+ {
+ const dtObstacleCircle* cir = &m_circles[i];
+
+ // RVO
+ float vab[3];
+ dtVscale(vab, vcand, 2);
+ dtVsub(vab, vab, vel);
+ dtVsub(vab, vab, cir->vel);
+
+ // Side
+ side += dtClamp(dtMin(dtVdot2D(cir->dp,vab)*0.5f+0.5f, dtVdot2D(cir->np,vab)*2), 0.0f, 1.0f);
+ nside++;
+
+ float htmin = 0, htmax = 0;
+ if (!sweepCircleCircle(pos,rad, vab, cir->p,cir->rad, htmin, htmax))
+ continue;
+
+ // Handle overlapping obstacles.
+ if (htmin < 0.0f && htmax > 0.0f)
+ {
+ // Avoid more when overlapped.
+ htmin = -htmin * 0.5f;
+ }
+
+ if (htmin >= 0.0f)
+ {
+ // The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
+ if (htmin < tmin)
+ tmin = htmin;
+ }
+ }
+
+ for (int i = 0; i < m_nsegments; ++i)
+ {
+ const dtObstacleSegment* seg = &m_segments[i];
+ float htmin = 0;
+
+ if (seg->touch)
+ {
+ // Special case when the agent is very close to the segment.
+ float sdir[3], snorm[3];
+ dtVsub(sdir, seg->q, seg->p);
+ snorm[0] = -sdir[2];
+ snorm[2] = sdir[0];
+ // If the velocity is pointing towards the segment, no collision.
+ if (dtVdot2D(snorm, vcand) < 0.0f)
+ continue;
+ // Else immediate collision.
+ htmin = 0.0f;
+ }
+ else
+ {
+ if (!isectRaySeg(pos, vcand, seg->p, seg->q, htmin))
+ continue;
+ }
+
+ // Avoid less when facing walls.
+ htmin *= 2.0f;
+
+ // The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
+ if (htmin < tmin)
+ tmin = htmin;
+ }
+
+ // Normalize side bias, to prevent it dominating too much.
+ if (nside)
+ side /= nside;
+
+ const float ivmax = 1.0f / vmax;
+ const float vpen = m_weightDesVel * (dtVdist2D(vcand, dvel) * ivmax);
+ const float vcpen = m_weightCurVel * (dtVdist2D(vcand, vel) * ivmax);
+ const float spen = m_weightSide * side;
+ const float tpen = m_weightToi * (1.0f/(0.1f+tmin / m_horizTime));
+
+ const float penalty = vpen + vcpen + spen + tpen;
+
+ // Store different penalties for debug viewing
+ if (debug)
+ debug->addSample(vcand, cs, penalty, vpen, vcpen, spen, tpen);
+
+ return penalty;
+}
+
+void dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel,
+ float* nvel, const int gsize,
+ dtObstacleAvoidanceDebugData* debug)
+{
+ prepare(pos, dvel);
+
+ dtVset(nvel, 0,0,0);
+
+ if (debug)
+ debug->reset();
+
+ const float cvx = dvel[0] * m_velBias;
+ const float cvz = dvel[2] * m_velBias;
+ const float cs = vmax * 2 * (1 - m_velBias) / (float)(gsize-1);
+ const float half = (gsize-1)*cs*0.5f;
+
+ float minPenalty = FLT_MAX;
+
+ for (int y = 0; y < gsize; ++y)
+ {
+ for (int x = 0; x < gsize; ++x)
+ {
+ float vcand[3];
+ vcand[0] = cvx + x*cs - half;
+ vcand[1] = 0;
+ vcand[2] = cvz + y*cs - half;
+
+ if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+cs/2)) continue;
+
+ const float penalty = processSample(vcand, cs, pos,rad,vmax,vel,dvel, debug);
+ if (penalty < minPenalty)
+ {
+ minPenalty = penalty;
+ dtVcopy(nvel, vcand);
+ }
+ }
+ }
+}
+
+
+static const float DT_PI = 3.14159265f;
+
+void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel, float* nvel,
+ const int ndivs, const int nrings, const int depth,
+ dtObstacleAvoidanceDebugData* debug)
+{
+ prepare(pos, dvel);
+
+ dtVset(nvel, 0,0,0);
+
+ if (debug)
+ debug->reset();
+
+ // Build sampling pattern aligned to desired velocity.
+ static const int MAX_PATTERN_DIVS = 32;
+ static const int MAX_PATTERN_RINGS = 4;
+ float pat[(MAX_PATTERN_DIVS*MAX_PATTERN_RINGS+1)*2];
+ int npat = 0;
+
+ const int nd = dtClamp(ndivs, 1, MAX_PATTERN_DIVS);
+ const int nr = dtClamp(nrings, 1, MAX_PATTERN_RINGS);
+ const float da = (1.0f/nd) * DT_PI*2;
+ const float dang = atan2f(dvel[2], dvel[0]);
+
+ // Always add sample at zero
+ pat[npat*2+0] = 0;
+ pat[npat*2+1] = 0;
+ npat++;
+
+ for (int j = 0; j < nr; ++j)
+ {
+ const float rad = (float)(nr-j)/(float)nr;
+ float a = dang + (j&1)*0.5f*da;
+ for (int i = 0; i < nd; ++i)
+ {
+ pat[npat*2+0] = cosf(a)*rad;
+ pat[npat*2+1] = sinf(a)*rad;
+ npat++;
+ a += da;
+ }
+ }
+
+ // Start sampling.
+ float cr = vmax * (1.0f-m_velBias);
+ float res[3];
+ dtVset(res, dvel[0] * m_velBias, 0, dvel[2] * m_velBias);
+
+ for (int k = 0; k < depth; ++k)
+ {
+ float minPenalty = FLT_MAX;
+ float bvel[3];
+ dtVset(bvel, 0,0,0);
+
+ for (int i = 0; i < npat; ++i)
+ {
+ float vcand[3];
+ vcand[0] = res[0] + pat[i*2+0]*cr;
+ vcand[1] = 0;
+ vcand[2] = res[2] + pat[i*2+1]*cr;
+
+ if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+0.001f)) continue;
+
+ const float penalty = processSample(vcand,cr/10, pos,rad,vmax,vel,dvel, debug);
+ if (penalty < minPenalty)
+ {
+ minPenalty = penalty;
+ dtVcopy(bvel, vcand);
+ }
+ }
+
+ dtVcopy(res, bvel);
+
+ cr *= 0.5f;
+ }
+
+ dtVcopy(nvel, res);
+}
+
diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.h b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h
new file mode 100644
index 0000000..4a7187a
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h
@@ -0,0 +1,148 @@
+//
+// 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 DETOUROBSTACLEAVOIDANCE_H
+#define DETOUROBSTACLEAVOIDANCE_H
+
+struct dtObstacleCircle
+{
+ float p[3]; // Position of the obstacle
+ float vel[3]; // Velocity of the obstacle
+ float dvel[3]; // Velocity of the obstacle
+ float rad; // Radius of the obstacle
+ float dp[3], np[3]; // Use for side selection during sampling.
+};
+
+struct dtObstacleSegment
+{
+ float p[3], q[3]; // End points of the obstacle segment
+ bool touch;
+};
+
+static const int RVO_SAMPLE_RAD = 15;
+static const int MAX_RVO_SAMPLES = (RVO_SAMPLE_RAD*2+1)*(RVO_SAMPLE_RAD*2+1) + 100;
+
+class dtObstacleAvoidanceDebugData
+{
+public:
+ dtObstacleAvoidanceDebugData();
+ ~dtObstacleAvoidanceDebugData();
+
+ bool init(const int maxSamples);
+ void reset();
+ void addSample(const float* vel, const float ssize, const float pen,
+ const float vpen, const float vcpen, const float spen, const float tpen);
+
+ void normalizeSamples();
+
+ inline int getSampleCount() const { return m_nsamples; }
+ inline const float* getSampleVelocity(const int i) const { return &m_vel[i*3]; }
+ inline float getSampleSize(const int i) const { return m_ssize[i]; }
+ inline float getSamplePenalty(const int i) const { return m_pen[i]; }
+ inline float getSampleDesiredVelocityPenalty(const int i) const { return m_vpen[i]; }
+ inline float getSampleCurrentVelocityPenalty(const int i) const { return m_vcpen[i]; }
+ inline float getSamplePreferredSidePenalty(const int i) const { return m_spen[i]; }
+ inline float getSampleCollisionTimePenalty(const int i) const { return m_tpen[i]; }
+
+private:
+ int m_nsamples;
+ int m_maxSamples;
+ float* m_vel;
+ float* m_ssize;
+ float* m_pen;
+ float* m_vpen;
+ float* m_vcpen;
+ float* m_spen;
+ float* m_tpen;
+};
+
+dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData();
+void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr);
+
+
+class dtObstacleAvoidanceQuery
+{
+public:
+ dtObstacleAvoidanceQuery();
+ ~dtObstacleAvoidanceQuery();
+
+ bool init(const int maxCircles, const int maxSegments);
+
+ void reset();
+
+ void addCircle(const float* pos, const float rad,
+ const float* vel, const float* dvel);
+
+ void addSegment(const float* p, const float* q);
+
+ inline void setVelocitySelectionBias(float v) { m_velBias = v; }
+ inline void setDesiredVelocityWeight(float w) { m_weightDesVel = w; }
+ inline void setCurrentVelocityWeight(float w) { m_weightCurVel = w; }
+ inline void setPreferredSideWeight(float w) { m_weightSide = w; }
+ inline void setCollisionTimeWeight(float w) { m_weightToi = w; }
+ inline void setTimeHorizon(float t) { m_horizTime = t; }
+
+ void sampleVelocityGrid(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel, float* nvel,
+ const int gsize,
+ dtObstacleAvoidanceDebugData* debug = 0);
+
+ void sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel, float* nvel,
+ const int ndivs, const int nrings, const int depth,
+ dtObstacleAvoidanceDebugData* debug = 0);
+
+ inline int getObstacleCircleCount() const { return m_ncircles; }
+ const dtObstacleCircle* getObstacleCircle(const int i) { return &m_circles[i]; }
+
+ inline int getObstacleSegmentCount() const { return m_nsegments; }
+ const dtObstacleSegment* getObstacleSegment(const int i) { return &m_segments[i]; }
+
+private:
+
+ void prepare(const float* pos, const float* dvel);
+
+ float processSample(const float* vcand, const float cs,
+ const float* pos, const float rad,
+ const float vmax, const float* vel, const float* dvel,
+ dtObstacleAvoidanceDebugData* debug);
+
+ dtObstacleCircle* insertCircle(const float dist);
+ dtObstacleSegment* insertSegment(const float dist);
+
+ float m_velBias;
+ float m_weightDesVel;
+ float m_weightCurVel;
+ float m_weightSide;
+ float m_weightToi;
+ float m_horizTime;
+
+ int m_maxCircles;
+ dtObstacleCircle* m_circles;
+ int m_ncircles;
+
+ int m_maxSegments;
+ dtObstacleSegment* m_segments;
+ int m_nsegments;
+};
+
+dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery();
+void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr);
+
+
+#endif // DETOUROBSTACLEAVOIDANCE_H
\ No newline at end of file
diff --git a/dep/recastnavigation/License.txt b/dep/recastnavigation/License.txt
new file mode 100644
index 0000000..95f4bfc
--- /dev/null
+++ b/dep/recastnavigation/License.txt
@@ -0,0 +1,18 @@
+Copyright (c) 2009 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.
+
diff --git a/dep/recastnavigation/Readme.txt b/dep/recastnavigation/Readme.txt
new file mode 100644
index 0000000..0c2f7b1
--- /dev/null
+++ b/dep/recastnavigation/Readme.txt
@@ -0,0 +1,120 @@
+
+Recast & Detour Version 1.4
+
+
+Recast
+
+Recast is state of the art navigation mesh construction toolset for games.
+
+ * It is automatic, which means that you can throw any level geometry
+ at it and you will get robust mesh out
+ * It is fast which means swift turnaround times for level designers
+ * It is open source so it comes with full source and you can
+ customize it to your hearts content.
+
+The Recast process starts with constructing a voxel mold from a level geometry
+and then casting a navigation mesh over it. The process consists of three steps,
+building the voxel mold, partitioning the mold into simple regions, peeling off
+the regions as simple polygons.
+
+ 1. The voxel mold is build from the input triangle mesh by rasterizing
+ the triangles into a multi-layer heightfield. Some simple filters are
+ then applied to the mold to prune out locations where the character
+ would not be able to move.
+ 2. The walkable areas described by the mold are divided into simple
+ overlayed 2D regions. The resulting regions have only one non-overlapping
+ contour, which simplifies the final step of the process tremendously.
+ 3. The navigation polygons are peeled off from the regions by first tracing
+ the boundaries and then simplifying them. The resulting polygons are
+ finally converted to convex polygons which makes them perfect for
+ pathfinding and spatial reasoning about the level.
+
+The toolset code is located in the Recast folder and demo application using the Recast
+toolset is located in the RecastDemo folder.
+
+The project files with this distribution can be compiled with Microsoft Visual C++ 2008
+(you can download it for free) and XCode 3.1.
+
+
+Detour
+
+Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
+
+Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes.
+
+
+Latest code available at http://code.google.com/p/recastnavigation/
+
+
+--
+
+Release Notes
+
+----------------
+* Recast 1.4
+ Released August 24th, 2009
+
+- Added detail height mesh generation (RecastDetailMesh.cpp) for single,
+ tiled statmeshes as well as tilemesh.
+- Added feature to contour tracing which detects extra vertices along
+ tile edges which should be removed later.
+- Changed the tiled stat mesh preprocess, so that it first generated
+ polymeshes per tile and finally combines them.
+- Fixed bug in the GUI code where invisible buttons could be pressed.
+
+----------------
+* Recast 1.31
+ Released July 24th, 2009
+
+- Better cost and heuristic functions.
+- Fixed tile navmesh raycast on tile borders.
+
+----------------
+* Recast 1.3
+ Released July 14th, 2009
+
+- Added dtTileNavMesh which allows to dynamically add and remove navmesh pieces at runtime.
+- Renamed stat navmesh types to dtStat* (i.e. dtPoly is now dtStatPoly).
+- Moved common code used by tile and stat navmesh to DetourNode.h/cpp and DetourCommon.h/cpp.
+- Refactores the demo code.
+
+----------------
+* Recast 1.2
+ Released June 17th, 2009
+
+- Added tiled mesh generation. The tiled generation allows to generate navigation for
+ much larger worlds, it removes some of the artifacts that comes from distance fields
+ in open areas, and allows later streaming and dynamic runtime generation
+- Improved and added some debug draw modes
+- API change: The helper function rcBuildNavMesh does not exists anymore,
+ had to change few internal things to cope with the tiled processing,
+ similar API functionality will be added later once the tiled process matures
+- The demo is getting way too complicated, need to split demos
+- Fixed several filtering functions so that the mesh is tighter to the geometry,
+ sometimes there could be up error up to tow voxel units close to walls,
+ now it should be just one.
+
+----------------
+* Recast 1.1
+ Released April 11th, 2009
+
+This is the first release of Detour.
+
+----------------
+* Recast 1.0
+ Released March 29th, 2009
+
+This is the first release of Recast.
+
+The process is not always as robust as I would wish. The watershed phase sometimes swallows tiny islands
+which are close to edges. These droppings are handled in rcBuildContours, but the code is not
+particularly robust either.
+
+Another non-robust case is when portal contours (contours shared between two regions) are always
+assumed to be straight. That can lead to overlapping contours specially when the level has
+large open areas.
+
+
+
+Mikko Mononen
+memon@inside.org
diff --git a/dep/recastnavigation/Recast/CMakeLists.txt b/dep/recastnavigation/Recast/CMakeLists.txt
new file mode 100644
index 0000000..7ced79c
--- /dev/null
+++ b/dep/recastnavigation/Recast/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+set(Recast_STAT_SRCS
+ Recast.cpp
+ RecastAlloc.cpp
+ RecastArea.cpp
+ RecastContour.cpp
+ RecastFilter.cpp
+ RecastMesh.cpp
+ RecastMeshDetail.cpp
+ RecastRasterization.cpp
+ RecastRegion.cpp
+)
+
+if(WIN32)
+ include_directories(
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ )
+endif()
+
+add_library(Recast STATIC ${Recast_STAT_SRCS})
+
+target_link_libraries(Recast ${ZLIB_LIBRARIES})
\ No newline at end of file
diff --git a/dep/recastnavigation/Recast/Recast.cpp b/dep/recastnavigation/Recast/Recast.cpp
new file mode 100644
index 0000000..d051418
--- /dev/null
+++ b/dep/recastnavigation/Recast/Recast.cpp
@@ -0,0 +1,423 @@
+//
+// 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 <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+float rcSqrt(float x)
+{
+ return sqrtf(x);
+}
+
+
+void rcContext::log(const rcLogCategory category, const char* format, ...)
+{
+ if (!m_logEnabled)
+ return;
+ static const int MSG_SIZE = 512;
+ char msg[MSG_SIZE];
+ va_list ap;
+ va_start(ap, format);
+ int len = vsnprintf(msg, MSG_SIZE, format, ap);
+ if (len >= MSG_SIZE)
+ {
+ len = MSG_SIZE-1;
+ msg[MSG_SIZE-1] = '\0';
+ }
+ va_end(ap);
+ doLog(category, msg, len);
+}
+
+rcHeightfield* rcAllocHeightfield()
+{
+ rcHeightfield* hf = (rcHeightfield*)rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM);
+ memset(hf, 0, sizeof(rcHeightfield));
+ return hf;
+}
+
+void rcFreeHeightField(rcHeightfield* hf)
+{
+ if (!hf) return;
+ // Delete span array.
+ rcFree(hf->spans);
+ // Delete span pools.
+ while (hf->pools)
+ {
+ rcSpanPool* next = hf->pools->next;
+ rcFree(hf->pools);
+ hf->pools = next;
+ }
+ rcFree(hf);
+}
+
+rcCompactHeightfield* rcAllocCompactHeightfield()
+{
+ rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM);
+ memset(chf, 0, sizeof(rcCompactHeightfield));
+ return chf;
+}
+
+void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
+{
+ if (!chf) return;
+ rcFree(chf->cells);
+ rcFree(chf->spans);
+ rcFree(chf->dist);
+ rcFree(chf->areas);
+ rcFree(chf);
+}
+
+rcContourSet* rcAllocContourSet()
+{
+ rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM);
+ memset(cset, 0, sizeof(rcContourSet));
+ return cset;
+}
+
+void rcFreeContourSet(rcContourSet* cset)
+{
+ if (!cset) return;
+ for (int i = 0; i < cset->nconts; ++i)
+ {
+ rcFree(cset->conts[i].verts);
+ rcFree(cset->conts[i].rverts);
+ }
+ rcFree(cset->conts);
+ rcFree(cset);
+}
+
+rcPolyMesh* rcAllocPolyMesh()
+{
+ rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM);
+ memset(pmesh, 0, sizeof(rcPolyMesh));
+ return pmesh;
+}
+
+void rcFreePolyMesh(rcPolyMesh* pmesh)
+{
+ if (!pmesh) return;
+ rcFree(pmesh->verts);
+ rcFree(pmesh->polys);
+ rcFree(pmesh->regs);
+ rcFree(pmesh->flags);
+ rcFree(pmesh->areas);
+ rcFree(pmesh);
+}
+
+rcPolyMeshDetail* rcAllocPolyMeshDetail()
+{
+ rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM);
+ memset(dmesh, 0, sizeof(rcPolyMeshDetail));
+ return dmesh;
+}
+
+void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh)
+{
+ if (!dmesh) return;
+ rcFree(dmesh->meshes);
+ rcFree(dmesh->verts);
+ rcFree(dmesh->tris);
+ rcFree(dmesh);
+}
+
+
+void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
+{
+ // Calculate bounding box.
+ rcVcopy(bmin, verts);
+ rcVcopy(bmax, verts);
+ for (int i = 1; i < nv; ++i)
+ {
+ const float* v = &verts[i*3];
+ rcVmin(bmin, v);
+ rcVmax(bmax, v);
+ }
+}
+
+void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h)
+{
+ *w = (int)((bmax[0] - bmin[0])/cs+0.5f);
+ *h = (int)((bmax[2] - bmin[2])/cs+0.5f);
+}
+
+bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height,
+ const float* bmin, const float* bmax,
+ float cs, float ch)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ hf.width = width;
+ hf.height = height;
+ rcVcopy(hf.bmin, bmin);
+ rcVcopy(hf.bmax, bmax);
+ hf.cs = cs;
+ hf.ch = ch;
+ hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM);
+ if (!hf.spans)
+ return false;
+ memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height);
+ return true;
+}
+
+static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm)
+{
+ float e0[3], e1[3];
+ rcVsub(e0, v1, v0);
+ rcVsub(e1, v2, v0);
+ rcVcross(norm, e0, e1);
+ rcVnormalize(norm);
+}
+
+void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
+ const float* verts, int /*nv*/,
+ const int* tris, int nt,
+ unsigned char* areas)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
+
+ float norm[3];
+
+ for (int i = 0; i < nt; ++i)
+ {
+ const int* tri = &tris[i*3];
+ calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
+ // Check if the face is walkable.
+ if (norm[1] > walkableThr)
+ areas[i] = RC_WALKABLE_AREA;
+ }
+}
+
+void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
+ const float* verts, int /*nv*/,
+ const int* tris, int nt,
+ unsigned char* areas)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
+
+ float norm[3];
+
+ for (int i = 0; i < nt; ++i)
+ {
+ const int* tri = &tris[i*3];
+ calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
+ // Check if the face is walkable.
+ if (norm[1] <= walkableThr)
+ areas[i] = RC_NULL_AREA;
+ }
+}
+
+int rcGetHeightFieldSpanCount(rcContext* /*ctx*/, rcHeightfield& hf)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ const int w = hf.width;
+ const int h = hf.height;
+ int spanCount = 0;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next)
+ {
+ if (s->area != RC_NULL_AREA)
+ spanCount++;
+ }
+ }
+ }
+ return spanCount;
+}
+
+bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
+ rcHeightfield& hf, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
+
+ const int w = hf.width;
+ const int h = hf.height;
+ const int spanCount = rcGetHeightFieldSpanCount(ctx, hf);
+
+ // Fill in header.
+ chf.width = w;
+ chf.height = h;
+ chf.spanCount = spanCount;
+ chf.walkableHeight = walkableHeight;
+ chf.walkableClimb = walkableClimb;
+ chf.maxRegions = 0;
+ rcVcopy(chf.bmin, hf.bmin);
+ rcVcopy(chf.bmax, hf.bmax);
+ chf.bmax[1] += walkableHeight*hf.ch;
+ chf.cs = hf.cs;
+ chf.ch = hf.ch;
+ chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM);
+ if (!chf.cells)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
+ return false;
+ }
+ memset(chf.cells, 0, sizeof(rcCompactCell)*w*h);
+ chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM);
+ if (!chf.spans)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
+ return false;
+ }
+ memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount);
+ chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM);
+ if (!chf.areas)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
+ return false;
+ }
+ memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount);
+
+ const int MAX_HEIGHT = 0xffff;
+
+ // Fill in cells and spans.
+ int idx = 0;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcSpan* s = hf.spans[x + y*w];
+ // If there are no spans at this cell, just leave the data to index=0, count=0.
+ if (!s) continue;
+ rcCompactCell& c = chf.cells[x+y*w];
+ c.index = idx;
+ c.count = 0;
+ while (s)
+ {
+ if (s->area != RC_NULL_AREA)
+ {
+ const int bot = (int)s->smax;
+ const int top = s->next ? (int)s->next->smin : MAX_HEIGHT;
+ chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff);
+ chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
+ chf.areas[idx] = s->area;
+ idx++;
+ c.count++;
+ }
+ s = s->next;
+ }
+ }
+ }
+
+ // Find neighbour connections.
+ const int MAX_LAYERS = RC_NOT_CONNECTED-1;
+ int tooHighNeighbour = 0;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ rcCompactSpan& s = chf.spans[i];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ rcSetCon(s, dir, RC_NOT_CONNECTED);
+ const int nx = x + rcGetDirOffsetX(dir);
+ const int ny = y + rcGetDirOffsetY(dir);
+ // First check that the neighbour cell is in bounds.
+ if (nx < 0 || ny < 0 || nx >= w || ny >= h)
+ continue;
+
+ // Iterate over all neighbour spans and check if any of the is
+ // accessible from current cell.
+ const rcCompactCell& nc = chf.cells[nx+ny*w];
+ for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k)
+ {
+ const rcCompactSpan& ns = chf.spans[k];
+ const int bot = rcMax(s.y, ns.y);
+ const int top = rcMin(s.y+s.h, ns.y+ns.h);
+
+ // Check that the gap between the spans is walkable,
+ // and that the climb height between the gaps is not too high.
+ if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb)
+ {
+ // Mark direction as walkable.
+ const int idx = k - (int)nc.index;
+ if (idx < 0 || idx > MAX_LAYERS)
+ {
+ tooHighNeighbour = rcMax(tooHighNeighbour, idx);
+ continue;
+ }
+ rcSetCon(s, dir, idx);
+ break;
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ if (tooHighNeighbour > MAX_LAYERS)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
+ tooHighNeighbour, MAX_LAYERS);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
+
+ return true;
+}
+
+/*
+static int getHeightfieldMemoryUsage(const rcHeightfield& hf)
+{
+ int size = 0;
+ size += sizeof(hf);
+ size += hf.width * hf.height * sizeof(rcSpan*);
+
+ rcSpanPool* pool = hf.pools;
+ while (pool)
+ {
+ size += (sizeof(rcSpanPool) - sizeof(rcSpan)) + sizeof(rcSpan)*RC_SPANS_PER_POOL;
+ pool = pool->next;
+ }
+ return size;
+}
+
+static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
+{
+ int size = 0;
+ size += sizeof(rcCompactHeightfield);
+ size += sizeof(rcCompactSpan) * chf.spanCount;
+ size += sizeof(rcCompactCell) * chf.width * chf.height;
+ return size;
+}
+*/
\ No newline at end of file
diff --git a/dep/recastnavigation/Recast/Recast.h b/dep/recastnavigation/Recast/Recast.h
new file mode 100644
index 0000000..0e5f074
--- /dev/null
+++ b/dep/recastnavigation/Recast/Recast.h
@@ -0,0 +1,688 @@
+//
+// 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 RECAST_H
+#define RECAST_H
+
+// Some math headers don't have PI defined.
+static const float RC_PI = 3.14159265f;
+
+enum rcLogCategory
+{
+ RC_LOG_PROGRESS = 1,
+ RC_LOG_WARNING,
+ RC_LOG_ERROR,
+};
+
+enum rcTimerLabel
+{
+ RC_TIMER_TOTAL,
+ RC_TIMER_TEMP,
+ RC_TIMER_RASTERIZE_TRIANGLES,
+ RC_TIMER_BUILD_COMPACTHEIGHTFIELD,
+ RC_TIMER_BUILD_CONTOURS,
+ RC_TIMER_BUILD_CONTOURS_TRACE,
+ RC_TIMER_BUILD_CONTOURS_SIMPLIFY,
+ RC_TIMER_FILTER_BORDER,
+ RC_TIMER_FILTER_WALKABLE,
+ RC_TIMER_MEDIAN_AREA,
+ RC_TIMER_FILTER_LOW_OBSTACLES,
+ RC_TIMER_BUILD_POLYMESH,
+ RC_TIMER_MERGE_POLYMESH,
+ RC_TIMER_ERODE_AREA,
+ RC_TIMER_MARK_BOX_AREA,
+ RC_TIMER_MARK_CONVEXPOLY_AREA,
+ RC_TIMER_BUILD_DISTANCEFIELD,
+ RC_TIMER_BUILD_DISTANCEFIELD_DIST,
+ RC_TIMER_BUILD_DISTANCEFIELD_BLUR,
+ RC_TIMER_BUILD_REGIONS,
+ RC_TIMER_BUILD_REGIONS_WATERSHED,
+ RC_TIMER_BUILD_REGIONS_EXPAND,
+ RC_TIMER_BUILD_REGIONS_FLOOD,
+ RC_TIMER_BUILD_REGIONS_FILTER,
+ RC_TIMER_BUILD_POLYMESHDETAIL,
+ RC_TIMER_MERGE_POLYMESHDETAIL,
+ RC_MAX_TIMERS
+};
+
+// Build context provides several optional utilities needed for the build process,
+// such as timing, logging, and build time collecting.
+class rcContext
+{
+public:
+ inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {}
+ virtual ~rcContext() {}
+
+ // Enables or disables logging.
+ inline void enableLog(bool state) { m_logEnabled = state; }
+ // Resets log.
+ inline void resetLog() { if (m_logEnabled) doResetLog(); }
+ // Logs a message.
+ void log(const rcLogCategory category, const char* format, ...);
+
+ // Enables or disables timer.
+ inline void enableTimer(bool state) { m_timerEnabled = state; }
+ // Resets all timers.
+ inline void resetTimers() { if (m_timerEnabled) doResetTimers(); }
+ // Starts timer, used for performance timing.
+ inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); }
+ // Stops timer, used for performance timing.
+ inline void stopTimer(const rcTimerLabel label) { if (m_timerEnabled) doStopTimer(label); }
+ // Returns time accumulated between timer start/stop.
+ inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; }
+
+protected:
+ // Virtual functions to override for custom implementations.
+ virtual void doResetLog() {}
+ virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {}
+ virtual void doResetTimers() {}
+ virtual void doStartTimer(const rcTimerLabel /*label*/) {}
+ virtual void doStopTimer(const rcTimerLabel /*label*/) {}
+ virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; }
+
+ bool m_logEnabled;
+ bool m_timerEnabled;
+};
+
+
+// The units of the parameters are specified in parenthesis as follows:
+// (vx) voxels, (wu) world units
+struct rcConfig
+{
+ int width, height; // Dimensions of the rasterized heightfield (vx)
+ int tileSize; // Width and Height of a tile (vx)
+ int borderSize; // Non-navigable Border around the heightfield (vx)
+ float cs, ch; // Grid cell size and height (wu)
+ float bmin[3], bmax[3]; // Grid bounds (wu)
+ float walkableSlopeAngle; // Maximum walkable slope angle in degrees.
+ int walkableHeight; // Minimum height where the agent can still walk (vx)
+ int walkableClimb; // Maximum height between grid cells the agent can climb (vx)
+ int walkableRadius; // Radius of the agent in cells (vx)
+ int maxEdgeLen; // Maximum contour edge length (vx)
+ float maxSimplificationError; // Maximum distance error from contour to cells (vx)
+ int minRegionArea; // Regions whose area is smaller than this threshold will be removed. (vx)
+ int mergeRegionArea; // Regions whose area is smaller than this threshold will be merged (vx)
+ int maxVertsPerPoly; // Max number of vertices per polygon
+ float detailSampleDist; // Detail mesh sample spacing.
+ float detailSampleMaxError; // Detail mesh simplification max sample error.
+};
+
+// Define number of bits in the above structure for smin/smax.
+// The max height is used for clamping rasterized values.
+static const int RC_SPAN_HEIGHT_BITS = 16;
+static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1;
+
+// Heightfield span.
+struct rcSpan
+{
+ unsigned int smin : 16; // Span min height.
+ unsigned int smax : 16; // Span max height.
+ unsigned char area; // Span area type.
+ rcSpan* next; // Next span in column.
+};
+
+// Number of spans allocated per pool.
+static const int RC_SPANS_PER_POOL = 2048;
+
+// Memory pool used for quick span allocation.
+struct rcSpanPool
+{
+ rcSpanPool* next; // Pointer to next pool.
+ rcSpan items[RC_SPANS_PER_POOL]; // Array of spans.
+};
+
+// Dynamic span-heightfield.
+struct rcHeightfield
+{
+ int width, height; // Dimension of the heightfield.
+ float bmin[3], bmax[3]; // Bounding box of the heightfield
+ float cs, ch; // Cell size and height.
+ rcSpan** spans; // Heightfield of spans (width*height).
+ rcSpanPool* pools; // Linked list of span pools.
+ rcSpan* freelist; // Pointer to next free span.
+};
+
+rcHeightfield* rcAllocHeightfield();
+void rcFreeHeightField(rcHeightfield* hf);
+
+
+struct rcCompactCell
+{
+ unsigned int index : 24; // Index to first span in column.
+ unsigned int count : 8; // Number of spans in this column.
+};
+
+struct rcCompactSpan
+{
+ unsigned short y; // Bottom coordinate of the span.
+ unsigned short reg;
+ unsigned int con : 24; // Connections to neighbour cells.
+ unsigned int h : 8; // Height of the span.
+};
+
+// Compact static heightfield.
+struct rcCompactHeightfield
+{
+ int width, height; // Width and height of the heightfield.
+ int spanCount; // Number of spans in the heightfield.
+ int walkableHeight, walkableClimb; // Agent properties.
+ unsigned short maxDistance; // Maximum distance value stored in heightfield.
+ unsigned short maxRegions; // Maximum Region Id stored in heightfield.
+ float bmin[3], bmax[3]; // Bounding box of the heightfield.
+ float cs, ch; // Cell size and height.
+ rcCompactCell* cells; // Pointer to width*height cells.
+ rcCompactSpan* spans; // Pointer to spans.
+ unsigned short* dist; // Pointer to per span distance to border.
+ unsigned char* areas; // Pointer to per span area ID.
+};
+
+rcCompactHeightfield* rcAllocCompactHeightfield();
+void rcFreeCompactHeightfield(rcCompactHeightfield* chf);
+
+
+struct rcContour
+{
+ int* verts; // Vertex coordinates, each vertex contains 4 components.
+ int nverts; // Number of vertices.
+ int* rverts; // Raw vertex coordinates, each vertex contains 4 components.
+ int nrverts; // Number of raw vertices.
+ unsigned short reg; // Region ID of the contour.
+ unsigned char area; // Area ID of the contour.
+};
+
+struct rcContourSet
+{
+ rcContour* conts; // Pointer to all contours.
+ int nconts; // Number of contours.
+ float bmin[3], bmax[3]; // Bounding box of the heightfield.
+ float cs, ch; // Cell size and height.
+};
+
+rcContourSet* rcAllocContourSet();
+void rcFreeContourSet(rcContourSet* cset);
+
+
+// Polymesh store a connected mesh of polygons.
+// The polygons are store in an array where each polygons takes
+// 'nvp*2' elements. The first 'nvp' elements are indices to vertices
+// and the second 'nvp' elements are indices to neighbour polygons.
+// If a polygon has less than 'bvp' vertices, the remaining indices
+// are set to RC_MESH_NULL_IDX. If an polygon edge does not have a neighbour
+// the neighbour index is set to RC_MESH_NULL_IDX.
+// Vertices can be transformed into world space as follows:
+// x = bmin[0] + verts[i*3+0]*cs;
+// y = bmin[1] + verts[i*3+1]*ch;
+// z = bmin[2] + verts[i*3+2]*cs;
+struct rcPolyMesh
+{
+ unsigned short* verts; // Vertices of the mesh, 3 elements per vertex.
+ unsigned short* polys; // Polygons of the mesh, nvp*2 elements per polygon.
+ unsigned short* regs; // Region ID of the polygons.
+ unsigned short* flags; // Per polygon flags.
+ unsigned char* areas; // Area ID of polygons.
+ int nverts; // Number of vertices.
+ int npolys; // Number of polygons.
+ int maxpolys; // Number of allocated polygons.
+ int nvp; // Max number of vertices per polygon.
+ float bmin[3], bmax[3]; // Bounding box of the mesh.
+ float cs, ch; // Cell size and height.
+};
+
+rcPolyMesh* rcAllocPolyMesh();
+void rcFreePolyMesh(rcPolyMesh* pmesh);
+
+
+// Detail mesh generated from a rcPolyMesh.
+// Each submesh represents a polygon in the polymesh and they are stored in
+// exactly same order. Each submesh is described as 4 values:
+// base vertex, vertex count, base triangle, triangle count. That is,
+// const unsigned char* t = &dmesh.tris[(tbase+i)*3]; and
+// const float* v = &dmesh.verts[(vbase+t[j])*3];
+// If the input polygon has 'n' vertices, those vertices are first in the
+// submesh vertex list. This allows to compres the mesh by not storing the
+// first vertices and using the polymesh vertices instead.
+// Max number of vertices per submesh is 127 and
+// max number of triangles per submesh is 255.
+
+struct rcPolyMeshDetail
+{
+ unsigned int* meshes; // Pointer to all mesh data.
+ float* verts; // Pointer to all vertex data.
+ unsigned char* tris; // Pointer to all triangle data.
+ int nmeshes; // Number of meshes.
+ int nverts; // Number of total vertices.
+ int ntris; // Number of triangles.
+};
+
+rcPolyMeshDetail* rcAllocPolyMeshDetail();
+void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh);
+
+
+// If heightfield region ID has the following bit set, the region is on border area
+// and excluded from many calculations.
+static const unsigned short RC_BORDER_REG = 0x8000;
+
+// If contour region ID has the following bit set, the vertex will be later
+// removed in order to match the segments and vertices at tile boundaries.
+static const int RC_BORDER_VERTEX = 0x10000;
+
+static const int RC_AREA_BORDER = 0x20000;
+
+enum rcBuildContoursFlags
+{
+ RC_CONTOUR_TESS_WALL_EDGES = 0x01, // Tessellate wall edges
+ RC_CONTOUR_TESS_AREA_EDGES = 0x02, // Tessellate edges between areas.
+};
+
+// Mask used with contours to extract region id.
+static const int RC_CONTOUR_REG_MASK = 0xffff;
+
+// Null index which is used with meshes to mark unset or invalid indices.
+static const unsigned short RC_MESH_NULL_IDX = 0xffff;
+
+// Area ID that is considered empty.
+static const unsigned char RC_NULL_AREA = 0;
+
+// Area ID that is considered generally walkable.
+static const unsigned char RC_WALKABLE_AREA = 63;
+
+// Value returned by rcGetCon() if the direction is not connected.
+static const int RC_NOT_CONNECTED = 0x3f;
+
+// Compact span neighbour helpers.
+inline void rcSetCon(rcCompactSpan& s, int dir, int i)
+{
+ const unsigned int shift = (unsigned int)dir*6;
+ unsigned int con = s.con;
+ s.con = (con & ~(0x3f << shift)) | (((unsigned int)i & 0x3f) << shift);
+}
+
+inline int rcGetCon(const rcCompactSpan& s, int dir)
+{
+ const unsigned int shift = (unsigned int)dir*6;
+ return (s.con >> shift) & 0x3f;
+}
+
+inline int rcGetDirOffsetX(int dir)
+{
+ const int offset[4] = { -1, 0, 1, 0, };
+ return offset[dir&0x03];
+}
+
+inline int rcGetDirOffsetY(int dir)
+{
+ const int offset[4] = { 0, 1, 0, -1 };
+ return offset[dir&0x03];
+}
+
+// Common helper functions
+template<class T> inline void rcSwap(T& a, T& b) { T t = a; a = b; b = t; }
+template<class T> inline T rcMin(T a, T b) { return a < b ? a : b; }
+template<class T> inline T rcMax(T a, T b) { return a > b ? a : b; }
+template<class T> inline T rcAbs(T a) { return a < 0 ? -a : a; }
+template<class T> inline T rcSqr(T a) { return a*a; }
+template<class T> inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
+float rcSqrt(float x);
+
+// Common vector helper functions.
+inline void rcVcross(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+inline float rcVdot(const float* v1, const float* v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+inline void rcVmad(float* dest, const float* v1, const float* v2, const float s)
+{
+ dest[0] = v1[0]+v2[0]*s;
+ dest[1] = v1[1]+v2[1]*s;
+ dest[2] = v1[2]+v2[2]*s;
+}
+
+inline void rcVadd(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]+v2[0];
+ dest[1] = v1[1]+v2[1];
+ dest[2] = v1[2]+v2[2];
+}
+
+inline void rcVsub(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]-v2[0];
+ dest[1] = v1[1]-v2[1];
+ dest[2] = v1[2]-v2[2];
+}
+
+inline void rcVmin(float* mn, const float* v)
+{
+ mn[0] = rcMin(mn[0], v[0]);
+ mn[1] = rcMin(mn[1], v[1]);
+ mn[2] = rcMin(mn[2], v[2]);
+}
+
+inline void rcVmax(float* mx, const float* v)
+{
+ mx[0] = rcMax(mx[0], v[0]);
+ mx[1] = rcMax(mx[1], v[1]);
+ mx[2] = rcMax(mx[2], v[2]);
+}
+
+inline void rcVcopy(float* dest, const float* v)
+{
+ dest[0] = v[0];
+ dest[1] = v[1];
+ dest[2] = v[2];
+}
+
+inline float rcVdist(const float* v1, const float* v2)
+{
+ float dx = v2[0] - v1[0];
+ float dy = v2[1] - v1[1];
+ float dz = v2[2] - v1[2];
+ return rcSqrt(dx*dx + dy*dy + dz*dz);
+}
+
+inline float rcVdistSqr(const float* v1, const float* v2)
+{
+ float dx = v2[0] - v1[0];
+ float dy = v2[1] - v1[1];
+ float dz = v2[2] - v1[2];
+ return dx*dx + dy*dy + dz*dz;
+}
+
+inline void rcVnormalize(float* v)
+{
+ float d = 1.0f / rcSqrt(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2]));
+ v[0] *= d;
+ v[1] *= d;
+ v[2] *= d;
+}
+
+inline bool rcVequal(const float* p0, const float* p1)
+{
+ static const float thr = rcSqr(1.0f/16384.0f);
+ const float d = rcVdistSqr(p0, p1);
+ return d < thr;
+}
+
+// Calculated bounding box of array of vertices.
+// Params:
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// bmin, bmax - (out) bounding box
+void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax);
+
+// Calculates grid size based on bounding box and grid cell size.
+// Params:
+// bmin, bmax - (in) bounding box
+// cs - (in) grid cell size
+// w - (out) grid width
+// h - (out) grid height
+void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h);
+
+// Creates and initializes new heightfield.
+// Params:
+// hf - (in/out) heightfield to initialize.
+// width - (in) width of the heightfield.
+// height - (in) height of the heightfield.
+// bmin, bmax - (in) bounding box of the heightfield
+// cs - (in) grid cell size
+// ch - (in) grid cell height
+bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
+ const float* bmin, const float* bmax,
+ float cs, float ch);
+
+// Sets the RC_WALKABLE_AREA for every triangle whose slope is below
+// the maximum walkable slope angle.
+// Params:
+// walkableSlopeAngle - (in) maximum slope angle in degrees.
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// nt - (in) triangle count
+// areas - (out) array of triangle area types
+void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
+ const int* tris, int nt, unsigned char* areas);
+
+// Sets the RC_NULL_AREA for every triangle whose slope is steeper than
+// the maximum walkable slope angle.
+// Params:
+// walkableSlopeAngle - (in) maximum slope angle in degrees.
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// nt - (in) triangle count
+// areas - (out) array of triangle are types
+void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
+ const int* tris, int nt, unsigned char* areas);
+
+// Adds span to heightfield.
+// The span addition can set to favor flags. If the span is merged to
+// another span and the new smax is within 'flagMergeThr' units away
+// from the existing span the span flags are merged and stored.
+// Params:
+// solid - (in) heightfield where the spans is added to
+// x,y - (in) location on the heightfield where the span is added
+// smin,smax - (in) spans min/max height
+// flags - (in) span flags (zero or WALKABLE)
+// flagMergeThr - (in) merge threshold.
+void rcAddSpan(rcContext* ctx, rcHeightfield& solid, const int x, const int y,
+ const unsigned short smin, const unsigned short smax,
+ const unsigned short area, const int flagMergeThr);
+
+// Rasterizes a triangle into heightfield spans.
+// Params:
+// v0,v1,v2 - (in) the vertices of the triangle.
+// area - (in) area type of the triangle.
+// solid - (in) heightfield where the triangle is rasterized
+// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
+void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
+ const unsigned char area, rcHeightfield& solid,
+ const int flagMergeThr = 1);
+
+// Rasterizes indexed triangle mesh into heightfield spans.
+// Params:
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// area - (in) array of triangle area types.
+// nt - (in) triangle count
+// solid - (in) heightfield where the triangles are rasterized
+// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
+ const int* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr = 1);
+
+// Rasterizes indexed triangle mesh into heightfield spans.
+// Params:
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// area - (in) array of triangle area types.
+// nt - (in) triangle count
+// solid - (in) heightfield where the triangles are rasterized
+// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
+ const unsigned short* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr = 1);
+
+// Rasterizes the triangles into heightfield spans.
+// Params:
+// verts - (in) array of vertices
+// area - (in) array of triangle area types.
+// nt - (in) triangle count
+// solid - (in) heightfield where the triangles are rasterized
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr = 1);
+
+// Marks non-walkable low obstacles as walkable if they are closer than walkableClimb
+// from a walkable surface. Applying this filter allows to step over low hanging
+// low obstacles.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// solid - (in/out) heightfield describing the solid space
+// TODO: Missuses ledge flag, must be called before rcFilterLedgeSpans!
+void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid);
+
+// Removes WALKABLE flag from all spans that are at ledges. This filtering
+// removes possible overestimation of the conservative voxelization so that
+// the resulting mesh will not have regions hanging in air over ledges.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// walkableClimb - (in) maximum height between grid cells the agent can climb
+// solid - (in/out) heightfield describing the solid space
+void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight,
+ const int walkableClimb, rcHeightfield& solid);
+
+// Removes WALKABLE flag from all spans which have smaller than
+// 'walkableHeight' clearance above them.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// solid - (in/out) heightfield describing the solid space
+void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid);
+
+// Returns number of spans contained in a heightfield.
+// Params:
+// hf - (in) heightfield to be compacted
+// Returns number of spans.
+int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf);
+
+// Builds compact representation of the heightfield.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// walkableClimb - (in) maximum height between grid cells the agent can climb
+// flags - (in) require flags for a cell to be included in the compact heightfield.
+// hf - (in) heightfield to be compacted
+// chf - (out) compact heightfield representing the open space.
+// Returns false if operation ran out of memory.
+bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
+ rcHeightfield& hf, rcCompactHeightfield& chf);
+
+// Erodes walkable area.
+// Params:
+// radius - (in) radius of erosion (max 255).
+// chf - (in/out) compact heightfield to erode.
+// Returns false if operation ran out of memory.
+bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf);
+
+// Applies median filter to walkable area types, removing noise.
+// Params:
+// chf - (in/out) compact heightfield to erode.
+// Returns false if operation ran out of memory.
+bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf);
+
+// Marks the area of the convex polygon into the area type of the compact heightfield.
+// Params:
+// bmin/bmax - (in) bounds of the axis aligned box.
+// areaId - (in) area ID to mark.
+// chf - (in/out) compact heightfield to mark.
+void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
+ rcCompactHeightfield& chf);
+
+// Marks the area of the convex polygon into the area type of the compact heightfield.
+// Params:
+// verts - (in) vertices of the convex polygon.
+// nverts - (in) number of vertices in the polygon.
+// hmin/hmax - (in) min and max height of the polygon.
+// areaId - (in) area ID to mark.
+// chf - (in/out) compact heightfield to mark.
+void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
+ const float hmin, const float hmax, unsigned char areaId,
+ rcCompactHeightfield& chf);
+
+// Builds distance field and stores it into the combat heightfield.
+// Params:
+// chf - (in/out) compact heightfield representing the open space.
+// Returns false if operation ran out of memory.
+bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
+
+// Divides the walkable heighfied into simple regions using watershed partitioning.
+// Each region has only one contour and no overlaps.
+// The regions are stored in the compact heightfield 'reg' field.
+// The process sometimes creates small regions. If the area of a regions is
+// smaller than 'mergeRegionArea' then the region will be merged with a neighbour
+// region if possible. If multiple regions form an area which is smaller than
+// 'minRegionArea' all the regions belonging to that area will be removed.
+// Here area means the count of spans in an area.
+// Params:
+// chf - (in/out) compact heightfield representing the open space.
+// minRegionArea - (in) the smallest allowed region area.
+// maxMergeRegionArea - (in) the largest allowed region area which can be merged.
+// Returns false if operation ran out of memory.
+bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea);
+
+// Divides the walkable heighfied into simple regions using simple monotone partitioning.
+// Each region has only one contour and no overlaps.
+// The regions are stored in the compact heightfield 'reg' field.
+// The process sometimes creates small regions. If the area of a regions is
+// smaller than 'mergeRegionArea' then the region will be merged with a neighbour
+// region if possible. If multiple regions form an area which is smaller than
+// 'minRegionArea' all the regions belonging to that area will be removed.
+// Here area means the count of spans in an area.
+// Params:
+// chf - (in/out) compact heightfield representing the open space.
+// minRegionArea - (in) the smallest allowed regions size.
+// maxMergeRegionArea - (in) the largest allowed regions size which can be merged.
+// Returns false if operation ran out of memory.
+bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea);
+
+// Builds simplified contours from the regions outlines.
+// Params:
+// chf - (in) compact heightfield which has regions set.
+// maxError - (in) maximum allowed distance between simplified contour and cells.
+// maxEdgeLen - (in) maximum allowed contour edge length in cells.
+// cset - (out) Resulting contour set.
+// flags - (in) build flags, see rcBuildContoursFlags.
+// Returns false if operation ran out of memory.
+bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
+ const float maxError, const int maxEdgeLen,
+ rcContourSet& cset, const int flags = RC_CONTOUR_TESS_WALL_EDGES);
+
+// Builds connected convex polygon mesh from contour polygons.
+// Params:
+// cset - (in) contour set.
+// nvp - (in) maximum number of vertices per polygon.
+// mesh - (out) poly mesh.
+// Returns false if operation ran out of memory.
+bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh);
+
+bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh);
+
+// Builds detail triangle mesh for each polygon in the poly mesh.
+// Params:
+// mesh - (in) poly mesh to detail.
+// chf - (in) compact height field, used to query height for new vertices.
+// sampleDist - (in) spacing between height samples used to generate more detail into mesh.
+// sampleMaxError - (in) maximum allowed distance between simplified detail mesh and height sample.
+// pmdtl - (out) detail mesh.
+// Returns false if operation ran out of memory.
+bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
+ const float sampleDist, const float sampleMaxError,
+ rcPolyMeshDetail& dmesh);
+
+bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh);
+
+
+#endif // RECAST_H
diff --git a/dep/recastnavigation/Recast/RecastAlloc.cpp b/dep/recastnavigation/Recast/RecastAlloc.cpp
new file mode 100644
index 0000000..2c7396a
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastAlloc.cpp
@@ -0,0 +1,67 @@
+//
+// 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 <stdlib.h>
+#include <string.h>
+#include "RecastAlloc.h"
+
+static void *rcAllocDefault(int size, rcAllocHint)
+{
+ return malloc(size);
+}
+
+static void rcFreeDefault(void *ptr)
+{
+ free(ptr);
+}
+
+static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
+static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
+
+void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
+{
+ sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
+ sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
+}
+
+void* rcAlloc(int size, rcAllocHint hint)
+{
+ return sRecastAllocFunc(size, hint);
+}
+
+void rcFree(void* ptr)
+{
+ if (ptr)
+ sRecastFreeFunc(ptr);
+}
+
+
+void rcIntArray::resize(int n)
+{
+ if (n > m_cap)
+ {
+ if (!m_cap) m_cap = n;
+ while (m_cap < n) m_cap *= 2;
+ int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
+ if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
+ rcFree(m_data);
+ m_data = newData;
+ }
+ m_size = n;
+}
+
diff --git a/dep/recastnavigation/Recast/RecastAlloc.h b/dep/recastnavigation/Recast/RecastAlloc.h
new file mode 100644
index 0000000..9a31637
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastAlloc.h
@@ -0,0 +1,69 @@
+//
+// 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 RECASTALLOC_H
+#define RECASTALLOC_H
+
+enum rcAllocHint
+{
+ RC_ALLOC_PERM, // Memory persist after a function call.
+ RC_ALLOC_TEMP // Memory used temporarily within a function.
+};
+
+typedef void* (rcAllocFunc)(int size, rcAllocHint hint);
+typedef void (rcFreeFunc)(void* ptr);
+
+void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
+
+void* rcAlloc(int size, rcAllocHint hint);
+void rcFree(void* ptr);
+
+
+
+// Simple dynamic array ints.
+class rcIntArray
+{
+ int* m_data;
+ int m_size, m_cap;
+ inline rcIntArray(const rcIntArray&);
+ inline rcIntArray& operator=(const rcIntArray&);
+public:
+ inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
+ inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
+ inline ~rcIntArray() { rcFree(m_data); }
+ void resize(int n);
+ inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
+ inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
+ inline const int& operator[](int i) const { return m_data[i]; }
+ inline int& operator[](int i) { return m_data[i]; }
+ inline int size() const { return m_size; }
+};
+
+// Simple internal helper class to delete array in scope
+template<class T> class rcScopedDelete
+{
+ T* ptr;
+ inline T* operator=(T* p);
+public:
+ inline rcScopedDelete() : ptr(0) {}
+ inline rcScopedDelete(T* p) : ptr(p) {}
+ inline ~rcScopedDelete() { rcFree(ptr); }
+ inline operator T*() { return ptr; }
+};
+
+#endif
diff --git a/dep/recastnavigation/Recast/RecastArea.cpp b/dep/recastnavigation/Recast/RecastArea.cpp
new file mode 100644
index 0000000..e89caee
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastArea.cpp
@@ -0,0 +1,413 @@
+//
+// 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 <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+
+bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ ctx->startTimer(RC_TIMER_ERODE_AREA);
+
+ unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!dist)
+ {
+ ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount);
+ return false;
+ }
+
+ // Init distance.
+ memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount);
+
+ // Mark boundary cells.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (chf.areas[i] != RC_NULL_AREA)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ int nc = 0;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ nc++;
+ }
+ // At least one missing neighbour.
+ if (nc != 4)
+ dist[i] = 0;
+ }
+ }
+ }
+ }
+
+ unsigned char nd;
+
+ // Pass 1
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ // (-1,0)
+ const int ax = x + rcGetDirOffsetX(0);
+ const int ay = y + rcGetDirOffsetY(0);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (-1,-1)
+ if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(3);
+ const int aay = ay + rcGetDirOffsetY(3);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
+ {
+ // (0,-1)
+ const int ax = x + rcGetDirOffsetX(3);
+ const int ay = y + rcGetDirOffsetY(3);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (1,-1)
+ if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(2);
+ const int aay = ay + rcGetDirOffsetY(2);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ }
+ }
+ }
+
+ // Pass 2
+ for (int y = h-1; y >= 0; --y)
+ {
+ for (int x = w-1; x >= 0; --x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
+ {
+ // (1,0)
+ const int ax = x + rcGetDirOffsetX(2);
+ const int ay = y + rcGetDirOffsetY(2);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (1,1)
+ if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(1);
+ const int aay = ay + rcGetDirOffsetY(1);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
+ {
+ // (0,1)
+ const int ax = x + rcGetDirOffsetX(1);
+ const int ay = y + rcGetDirOffsetY(1);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (-1,1)
+ if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(0);
+ const int aay = ay + rcGetDirOffsetY(0);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ }
+ }
+ }
+
+ const unsigned char thr = (unsigned char)(radius*2);
+ for (int i = 0; i < chf.spanCount; ++i)
+ if (dist[i] < thr)
+ chf.areas[i] = RC_NULL_AREA;
+
+ rcFree(dist);
+
+ ctx->stopTimer(RC_TIMER_ERODE_AREA);
+
+ return true;
+}
+
+static void insertSort(unsigned char* a, const int n)
+{
+ int i, j;
+ for (i = 1; i < n; i++)
+ {
+ const unsigned char value = a[i];
+ for (j = i - 1; j >= 0 && a[j] > value; j--)
+ a[j+1] = a[j];
+ a[j+1] = value;
+ }
+}
+
+
+bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ ctx->startTimer(RC_TIMER_MEDIAN_AREA);
+
+ unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!areas)
+ {
+ ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount);
+ return false;
+ }
+
+ // Init distance.
+ memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount);
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ if (chf.areas[i] == RC_NULL_AREA)
+ {
+ areas[i] = chf.areas[i];
+ continue;
+ }
+
+ unsigned char nei[9];
+ for (int j = 0; j < 9; ++j)
+ nei[j] = chf.areas[i];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ if (chf.areas[ai] != RC_NULL_AREA)
+ nei[dir*2+0] = chf.areas[ai];
+
+ const rcCompactSpan& as = chf.spans[ai];
+ const int dir2 = (dir+1) & 0x3;
+ if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir2);
+ const int ay2 = ay + rcGetDirOffsetY(dir2);
+ const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
+ if (chf.areas[ai2] != RC_NULL_AREA)
+ nei[dir*2+1] = chf.areas[ai2];
+ }
+ }
+ }
+ insertSort(nei, 9);
+ areas[i] = nei[4];
+ }
+ }
+ }
+
+ memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount);
+
+ rcFree(areas);
+
+ ctx->stopTimer(RC_TIMER_MEDIAN_AREA);
+
+ return true;
+}
+
+void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
+ rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_MARK_BOX_AREA);
+
+ int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
+ int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
+ int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
+ int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
+ int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
+ int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
+
+ if (maxx < 0) return;
+ if (minx >= chf.width) return;
+ if (maxz < 0) return;
+ if (minz >= chf.height) return;
+
+ if (minx < 0) minx = 0;
+ if (maxx >= chf.width) maxx = chf.width-1;
+ if (minz < 0) minz = 0;
+ if (maxz >= chf.height) maxz = chf.height-1;
+
+ for (int z = minz; z <= maxz; ++z)
+ {
+ for (int x = minx; x <= maxx; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+z*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ rcCompactSpan& s = chf.spans[i];
+ if ((int)s.y >= miny && (int)s.y <= maxy)
+ {
+ chf.areas[i] = areaId;
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_MARK_BOX_AREA);
+
+}
+
+
+static int pointInPoly(int nvert, const float* verts, const float* p)
+{
+ int i, j, c = 0;
+ for (i = 0, j = nvert-1; i < nvert; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
+ (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ }
+ return c;
+}
+
+void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
+ const float hmin, const float hmax, unsigned char areaId,
+ rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
+
+ float bmin[3], bmax[3];
+ rcVcopy(bmin, verts);
+ rcVcopy(bmax, verts);
+ for (int i = 1; i < nverts; ++i)
+ {
+ rcVmin(bmin, &verts[i*3]);
+ rcVmax(bmax, &verts[i*3]);
+ }
+ bmin[1] = hmin;
+ bmax[1] = hmax;
+
+ int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
+ int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
+ int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
+ int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
+ int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
+ int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
+
+ if (maxx < 0) return;
+ if (minx >= chf.width) return;
+ if (maxz < 0) return;
+ if (minz >= chf.height) return;
+
+ if (minx < 0) minx = 0;
+ if (maxx >= chf.width) maxx = chf.width-1;
+ if (minz < 0) minz = 0;
+ if (maxz >= chf.height) maxz = chf.height-1;
+
+
+ // TODO: Optimize.
+ for (int z = minz; z <= maxz; ++z)
+ {
+ for (int x = minx; x <= maxx; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+z*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ rcCompactSpan& s = chf.spans[i];
+ if ((int)s.y >= miny && (int)s.y <= maxy)
+ {
+ float p[3];
+ p[0] = chf.bmin[0] + (x+0.5f)*chf.cs;
+ p[1] = 0;
+ p[2] = chf.bmin[2] + (z+0.5f)*chf.cs;
+
+ if (pointInPoly(nverts, verts, p))
+ {
+ chf.areas[i] = areaId;
+ }
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
+}
diff --git a/dep/recastnavigation/Recast/RecastAssert.h b/dep/recastnavigation/Recast/RecastAssert.h
new file mode 100644
index 0000000..b58b8fc
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastAssert.h
@@ -0,0 +1,33 @@
+//
+// 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 RECASTASSERT_H
+#define RECASTASSERT_H
+
+// Note: This header file's only purpose is to include define assert.
+// Feel free to change the file and include your own implementation instead.
+
+#ifdef NDEBUG
+// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
+# define rcAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false)
+#else
+# include <assert.h>
+# define rcAssert assert
+#endif
+
+#endif // RECASTASSERT_H
diff --git a/dep/recastnavigation/Recast/RecastContour.cpp b/dep/recastnavigation/Recast/RecastContour.cpp
new file mode 100644
index 0000000..1906b6e
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastContour.cpp
@@ -0,0 +1,804 @@
+//
+// 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.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+
+static int getCornerHeight(int x, int y, int i, int dir,
+ const rcCompactHeightfield& chf,
+ bool& isBorderVertex)
+{
+ const rcCompactSpan& s = chf.spans[i];
+ int ch = (int)s.y;
+ int dirp = (dir+1) & 0x3;
+
+ unsigned int regs[4] = {0,0,0,0};
+
+ // Combine region and area codes in order to prevent
+ // border vertices which are in between two areas to be removed.
+ regs[0] = chf.spans[i].reg | (chf.areas[i] << 16);
+
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
+ const rcCompactSpan& as = chf.spans[ai];
+ ch = rcMax(ch, (int)as.y);
+ regs[1] = chf.spans[ai].reg | (chf.areas[ai] << 16);
+ if (rcGetCon(as, dirp) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dirp);
+ const int ay2 = ay + rcGetDirOffsetY(dirp);
+ const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dirp);
+ const rcCompactSpan& as2 = chf.spans[ai2];
+ ch = rcMax(ch, (int)as2.y);
+ regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16);
+ }
+ }
+ if (rcGetCon(s, dirp) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dirp);
+ const int ay = y + rcGetDirOffsetY(dirp);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp);
+ const rcCompactSpan& as = chf.spans[ai];
+ ch = rcMax(ch, (int)as.y);
+ regs[3] = chf.spans[ai].reg | (chf.areas[ai] << 16);
+ if (rcGetCon(as, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir);
+ const int ay2 = ay + rcGetDirOffsetY(dir);
+ const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dir);
+ const rcCompactSpan& as2 = chf.spans[ai2];
+ ch = rcMax(ch, (int)as2.y);
+ regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16);
+ }
+ }
+
+ // Check if the vertex is special edge vertex, these vertices will be removed later.
+ for (int j = 0; j < 4; ++j)
+ {
+ const int a = j;
+ const int b = (j+1) & 0x3;
+ const int c = (j+2) & 0x3;
+ const int d = (j+3) & 0x3;
+
+ // The vertex is a border vertex there are two same exterior cells in a row,
+ // followed by two interior cells and none of the regions are out of bounds.
+ const bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b];
+ const bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0;
+ const bool intsSameArea = (regs[c]>>16) == (regs[d]>>16);
+ const bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0;
+ if (twoSameExts && twoInts && intsSameArea && noZeros)
+ {
+ isBorderVertex = true;
+ break;
+ }
+ }
+
+ return ch;
+}
+
+static void walkContour(int x, int y, int i,
+ rcCompactHeightfield& chf,
+ unsigned char* flags, rcIntArray& points)
+{
+ // Choose the first non-connected edge
+ unsigned char dir = 0;
+ while ((flags[i] & (1 << dir)) == 0)
+ dir++;
+
+ unsigned char startDir = dir;
+ int starti = i;
+
+ const unsigned char area = chf.areas[i];
+
+ int iter = 0;
+ while (++iter < 40000)
+ {
+ if (flags[i] & (1 << dir))
+ {
+ // Choose the edge corner
+ bool isBorderVertex = false;
+ bool isAreaBorder = false;
+ int px = x;
+ int py = getCornerHeight(x, y, i, dir, chf, isBorderVertex);
+ int pz = y;
+ switch(dir)
+ {
+ case 0: pz++; break;
+ case 1: px++; pz++; break;
+ case 2: px++; break;
+ }
+ int r = 0;
+ const rcCompactSpan& s = chf.spans[i];
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
+ r = (int)chf.spans[ai].reg;
+ if (area != chf.areas[ai])
+ isAreaBorder = true;
+ }
+ if (isBorderVertex)
+ r |= RC_BORDER_VERTEX;
+ if (isAreaBorder)
+ r |= RC_AREA_BORDER;
+ points.push(px);
+ points.push(py);
+ points.push(pz);
+ points.push(r);
+
+ flags[i] &= ~(1 << dir); // Remove visited edges
+ dir = (dir+1) & 0x3; // Rotate CW
+ }
+ else
+ {
+ int ni = -1;
+ const int nx = x + rcGetDirOffsetX(dir);
+ const int ny = y + rcGetDirOffsetY(dir);
+ const rcCompactSpan& s = chf.spans[i];
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
+ ni = (int)nc.index + rcGetCon(s, dir);
+ }
+ if (ni == -1)
+ {
+ // Should not happen.
+ return;
+ }
+ x = nx;
+ y = ny;
+ i = ni;
+ dir = (dir+3) & 0x3; // Rotate CCW
+ }
+
+ if (starti == i && startDir == dir)
+ {
+ break;
+ }
+ }
+}
+
+static float distancePtSeg(const int x, const int z,
+ const int px, const int pz,
+ const int qx, const int qz)
+{
+/* float pqx = (float)(qx - px);
+ float pqy = (float)(qy - py);
+ float pqz = (float)(qz - pz);
+ float dx = (float)(x - px);
+ float dy = (float)(y - py);
+ float dz = (float)(z - pz);
+ float d = pqx*pqx + pqy*pqy + pqz*pqz;
+ float t = pqx*dx + pqy*dy + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = px + t*pqx - x;
+ dy = py + t*pqy - y;
+ dz = pz + t*pqz - z;
+
+ return dx*dx + dy*dy + dz*dz;*/
+
+ float pqx = (float)(qx - px);
+ float pqz = (float)(qz - pz);
+ float dx = (float)(x - px);
+ float dz = (float)(z - pz);
+ float d = pqx*pqx + pqz*pqz;
+ float t = pqx*dx + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = px + t*pqx - x;
+ dz = pz + t*pqz - z;
+
+ return dx*dx + dz*dz;
+}
+
+static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
+ const float maxError, const int maxEdgeLen, const int buildFlags)
+{
+ // Add initial points.
+ bool hasConnections = false;
+ for (int i = 0; i < points.size(); i += 4)
+ {
+ if ((points[i+3] & RC_CONTOUR_REG_MASK) != 0)
+ {
+ hasConnections = true;
+ break;
+ }
+ }
+
+ if (hasConnections)
+ {
+ // The contour has some portals to other regions.
+ // Add a new point to every location where the region changes.
+ for (int i = 0, ni = points.size()/4; i < ni; ++i)
+ {
+ int ii = (i+1) % ni;
+ const bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK);
+ const bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER);
+ if (differentRegs || areaBorders)
+ {
+ simplified.push(points[i*4+0]);
+ simplified.push(points[i*4+1]);
+ simplified.push(points[i*4+2]);
+ simplified.push(i);
+ }
+ }
+ }
+
+ if (simplified.size() == 0)
+ {
+ // If there is no connections at all,
+ // create some initial points for the simplification process.
+ // Find lower-left and upper-right vertices of the contour.
+ int llx = points[0];
+ int lly = points[1];
+ int llz = points[2];
+ int lli = 0;
+ int urx = points[0];
+ int ury = points[1];
+ int urz = points[2];
+ int uri = 0;
+ for (int i = 0; i < points.size(); i += 4)
+ {
+ int x = points[i+0];
+ int y = points[i+1];
+ int z = points[i+2];
+ if (x < llx || (x == llx && z < llz))
+ {
+ llx = x;
+ lly = y;
+ llz = z;
+ lli = i/4;
+ }
+ if (x > urx || (x == urx && z > urz))
+ {
+ urx = x;
+ ury = y;
+ urz = z;
+ uri = i/4;
+ }
+ }
+ simplified.push(llx);
+ simplified.push(lly);
+ simplified.push(llz);
+ simplified.push(lli);
+
+ simplified.push(urx);
+ simplified.push(ury);
+ simplified.push(urz);
+ simplified.push(uri);
+ }
+
+ // Add points until all raw points are within
+ // error tolerance to the simplified shape.
+ const int pn = points.size()/4;
+ for (int i = 0; i < simplified.size()/4; )
+ {
+ int ii = (i+1) % (simplified.size()/4);
+
+ const int ax = simplified[i*4+0];
+ const int az = simplified[i*4+2];
+ const int ai = simplified[i*4+3];
+
+ const int bx = simplified[ii*4+0];
+ const int bz = simplified[ii*4+2];
+ const int bi = simplified[ii*4+3];
+
+ // Find maximum deviation from the segment.
+ float maxd = 0;
+ int maxi = -1;
+ int ci, cinc, endi;
+
+ // Traverse the segment in lexilogical order so that the
+ // max deviation is calculated similarly when traversing
+ // opposite segments.
+ if (bx > ax || (bx == ax && bz > az))
+ {
+ cinc = 1;
+ ci = (ai+cinc) % pn;
+ endi = bi;
+ }
+ else
+ {
+ cinc = pn-1;
+ ci = (bi+cinc) % pn;
+ endi = ai;
+ }
+
+ // Tessellate only outer edges oredges between areas.
+ if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 ||
+ (points[ci*4+3] & RC_AREA_BORDER))
+ {
+ while (ci != endi)
+ {
+ float d = distancePtSeg(points[ci*4+0], points[ci*4+2], ax, az, bx, bz);
+ if (d > maxd)
+ {
+ maxd = d;
+ maxi = ci;
+ }
+ ci = (ci+cinc) % pn;
+ }
+ }
+
+
+ // If the max deviation is larger than accepted error,
+ // add new point, else continue to next segment.
+ if (maxi != -1 && maxd > (maxError*maxError))
+ {
+ // Add space for the new point.
+ simplified.resize(simplified.size()+4);
+ const int n = simplified.size()/4;
+ for (int j = n-1; j > i; --j)
+ {
+ simplified[j*4+0] = simplified[(j-1)*4+0];
+ simplified[j*4+1] = simplified[(j-1)*4+1];
+ simplified[j*4+2] = simplified[(j-1)*4+2];
+ simplified[j*4+3] = simplified[(j-1)*4+3];
+ }
+ // Add the point.
+ simplified[(i+1)*4+0] = points[maxi*4+0];
+ simplified[(i+1)*4+1] = points[maxi*4+1];
+ simplified[(i+1)*4+2] = points[maxi*4+2];
+ simplified[(i+1)*4+3] = maxi;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ // Split too long edges.
+ if (maxEdgeLen > 0 && (buildFlags & (RC_CONTOUR_TESS_WALL_EDGES|RC_CONTOUR_TESS_AREA_EDGES)) != 0)
+ {
+ for (int i = 0; i < simplified.size()/4; )
+ {
+ const int ii = (i+1) % (simplified.size()/4);
+
+ const int ax = simplified[i*4+0];
+ const int az = simplified[i*4+2];
+ const int ai = simplified[i*4+3];
+
+ const int bx = simplified[ii*4+0];
+ const int bz = simplified[ii*4+2];
+ const int bi = simplified[ii*4+3];
+
+ // Find maximum deviation from the segment.
+ int maxi = -1;
+ int ci = (ai+1) % pn;
+
+ // Tessellate only outer edges or edges between areas.
+ bool tess = false;
+ // Wall edges.
+ if ((buildFlags & RC_CONTOUR_TESS_WALL_EDGES) && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0)
+ tess = true;
+ // Edges between areas.
+ if ((buildFlags & RC_CONTOUR_TESS_AREA_EDGES) && (points[ci*4+3] & RC_AREA_BORDER))
+ tess = true;
+
+ if (tess)
+ {
+ int dx = bx - ax;
+ int dz = bz - az;
+ if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen)
+ {
+ // Round based on the segments in lexilogical order so that the
+ // max tesselation is consistent regardles in which direction
+ // segments are traversed.
+ if (bx > ax || (bx == ax && bz > az))
+ {
+ const int n = bi < ai ? (bi+pn - ai) : (bi - ai);
+ maxi = (ai + n/2) % pn;
+ }
+ else
+ {
+ const int n = bi < ai ? (bi+pn - ai) : (bi - ai);
+ maxi = (ai + (n+1)/2) % pn;
+ }
+ }
+ }
+
+ // If the max deviation is larger than accepted error,
+ // add new point, else continue to next segment.
+ if (maxi != -1)
+ {
+ // Add space for the new point.
+ simplified.resize(simplified.size()+4);
+ const int n = simplified.size()/4;
+ for (int j = n-1; j > i; --j)
+ {
+ simplified[j*4+0] = simplified[(j-1)*4+0];
+ simplified[j*4+1] = simplified[(j-1)*4+1];
+ simplified[j*4+2] = simplified[(j-1)*4+2];
+ simplified[j*4+3] = simplified[(j-1)*4+3];
+ }
+ // Add the point.
+ simplified[(i+1)*4+0] = points[maxi*4+0];
+ simplified[(i+1)*4+1] = points[maxi*4+1];
+ simplified[(i+1)*4+2] = points[maxi*4+2];
+ simplified[(i+1)*4+3] = maxi;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+ }
+
+ for (int i = 0; i < simplified.size()/4; ++i)
+ {
+ // The edge vertex flag is take from the current raw point,
+ // and the neighbour region is take from the next raw point.
+ const int ai = (simplified[i*4+3]+1) % pn;
+ const int bi = simplified[i*4+3];
+ simplified[i*4+3] = (points[ai*4+3] & RC_CONTOUR_REG_MASK) | (points[bi*4+3] & RC_BORDER_VERTEX);
+ }
+
+}
+
+static void removeDegenerateSegments(rcIntArray& simplified)
+{
+ // Remove adjacent vertices which are equal on xz-plane,
+ // or else the triangulator will get confused.
+ for (int i = 0; i < simplified.size()/4; ++i)
+ {
+ int ni = i+1;
+ if (ni >= (simplified.size()/4))
+ ni = 0;
+
+ if (simplified[i*4+0] == simplified[ni*4+0] &&
+ simplified[i*4+2] == simplified[ni*4+2])
+ {
+ // Degenerate segment, remove.
+ for (int j = i; j < simplified.size()/4-1; ++j)
+ {
+ simplified[j*4+0] = simplified[(j+1)*4+0];
+ simplified[j*4+1] = simplified[(j+1)*4+1];
+ simplified[j*4+2] = simplified[(j+1)*4+2];
+ simplified[j*4+3] = simplified[(j+1)*4+3];
+ }
+ simplified.resize(simplified.size()-4);
+ }
+ }
+}
+
+static int calcAreaOfPolygon2D(const int* verts, const int nverts)
+{
+ int area = 0;
+ for (int i = 0, j = nverts-1; i < nverts; j=i++)
+ {
+ const int* vi = &verts[i*4];
+ const int* vj = &verts[j*4];
+ area += vi[0] * vj[2] - vj[0] * vi[2];
+ }
+ return (area+1) / 2;
+}
+
+inline bool ileft(const int* a, const int* b, const int* c)
+{
+ return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0;
+}
+
+static void getClosestIndices(const int* vertsa, const int nvertsa,
+ const int* vertsb, const int nvertsb,
+ int& ia, int& ib)
+{
+ int closestDist = 0xfffffff;
+ ia = -1, ib = -1;
+ for (int i = 0; i < nvertsa; ++i)
+ {
+ const int in = (i+1) % nvertsa;
+ const int ip = (i+nvertsa-1) % nvertsa;
+ const int* va = &vertsa[i*4];
+ const int* van = &vertsa[in*4];
+ const int* vap = &vertsa[ip*4];
+
+ for (int j = 0; j < nvertsb; ++j)
+ {
+ const int* vb = &vertsb[j*4];
+ // vb must be "infront" of va.
+ if (ileft(vap,va,vb) && ileft(va,van,vb))
+ {
+ const int dx = vb[0] - va[0];
+ const int dz = vb[2] - va[2];
+ const int d = dx*dx + dz*dz;
+ if (d < closestDist)
+ {
+ ia = i;
+ ib = j;
+ closestDist = d;
+ }
+ }
+ }
+ }
+}
+
+static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
+{
+ const int maxVerts = ca.nverts + cb.nverts + 2;
+ int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
+ if (!verts)
+ return false;
+
+ int nv = 0;
+
+ // Copy contour A.
+ for (int i = 0; i <= ca.nverts; ++i)
+ {
+ int* dst = &verts[nv*4];
+ const int* src = &ca.verts[((ia+i)%ca.nverts)*4];
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ nv++;
+ }
+
+ // Copy contour B
+ for (int i = 0; i <= cb.nverts; ++i)
+ {
+ int* dst = &verts[nv*4];
+ const int* src = &cb.verts[((ib+i)%cb.nverts)*4];
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ nv++;
+ }
+
+ rcFree(ca.verts);
+ ca.verts = verts;
+ ca.nverts = nv;
+
+ rcFree(cb.verts);
+ cb.verts = 0;
+ cb.nverts = 0;
+
+ return true;
+}
+
+bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
+ const float maxError, const int maxEdgeLen,
+ rcContourSet& cset, const int buildFlags)
+{
+ rcAssert(ctx);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ ctx->startTimer(RC_TIMER_BUILD_CONTOURS);
+
+ rcVcopy(cset.bmin, chf.bmin);
+ rcVcopy(cset.bmax, chf.bmax);
+ cset.cs = chf.cs;
+ cset.ch = chf.ch;
+
+ int maxContours = rcMax((int)chf.maxRegions, 8);
+ cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
+ if (!cset.conts)
+ return false;
+ cset.nconts = 0;
+
+ rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!flags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount);
+ return false;
+ }
+
+ ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
+
+ // Mark boundaries.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ unsigned char res = 0;
+ const rcCompactSpan& s = chf.spans[i];
+ if (!chf.spans[i].reg || (chf.spans[i].reg & RC_BORDER_REG))
+ {
+ flags[i] = 0;
+ continue;
+ }
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ unsigned short r = 0;
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ r = chf.spans[ai].reg;
+ }
+ if (r == chf.spans[i].reg)
+ res |= (1 << dir);
+ }
+ flags[i] = res ^ 0xf; // Inverse, mark non connected edges.
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
+
+ ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
+
+ rcIntArray verts(256);
+ rcIntArray simplified(64);
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (flags[i] == 0 || flags[i] == 0xf)
+ {
+ flags[i] = 0;
+ continue;
+ }
+ const unsigned short reg = chf.spans[i].reg;
+ if (!reg || (reg & RC_BORDER_REG))
+ continue;
+ const unsigned char area = chf.areas[i];
+
+ verts.resize(0);
+ simplified.resize(0);
+ walkContour(x, y, i, chf, flags, verts);
+ simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
+ removeDegenerateSegments(simplified);
+
+ // Store region->contour remap info.
+ // Create contour.
+ if (simplified.size()/4 >= 3)
+ {
+ if (cset.nconts >= maxContours)
+ {
+ // Allocate more contours.
+ // This can happen when there are tiny holes in the heightfield.
+ const int oldMax = maxContours;
+ maxContours *= 2;
+ rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
+ for (int j = 0; j < cset.nconts; ++j)
+ {
+ newConts[j] = cset.conts[j];
+ // Reset source pointers to prevent data deletion.
+ cset.conts[j].verts = 0;
+ cset.conts[j].rverts = 0;
+ }
+ rcFree(cset.conts);
+ cset.conts = newConts;
+
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours);
+ }
+
+ rcContour* cont = &cset.conts[cset.nconts++];
+
+ cont->nverts = simplified.size()/4;
+ cont->verts = (int*)rcAlloc(sizeof(int)*cont->nverts*4, RC_ALLOC_PERM);
+ if (!cont->verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' (%d).", cont->nverts);
+ return false;
+ }
+ memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4);
+
+ cont->nrverts = verts.size()/4;
+ cont->rverts = (int*)rcAlloc(sizeof(int)*cont->nrverts*4, RC_ALLOC_PERM);
+ if (!cont->rverts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' (%d).", cont->nrverts);
+ return false;
+ }
+ memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4);
+
+/* cont->cx = cont->cy = cont->cz = 0;
+ for (int i = 0; i < cont->nverts; ++i)
+ {
+ cont->cx += cont->verts[i*4+0];
+ cont->cy += cont->verts[i*4+1];
+ cont->cz += cont->verts[i*4+2];
+ }
+ cont->cx /= cont->nverts;
+ cont->cy /= cont->nverts;
+ cont->cz /= cont->nverts;*/
+
+ cont->reg = reg;
+ cont->area = area;
+ }
+ }
+ }
+ }
+
+ // Check and merge droppings.
+ // Sometimes the previous algorithms can fail and create several contours
+ // per area. This pass will try to merge the holes into the main region.
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ rcContour& cont = cset.conts[i];
+ // Check if the contour is would backwards.
+ if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
+ {
+ // Find another contour which has the same region ID.
+ int mergeIdx = -1;
+ for (int j = 0; j < cset.nconts; ++j)
+ {
+ if (i == j) continue;
+ if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg)
+ {
+ // Make sure the polygon is correctly oriented.
+ if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts))
+ {
+ mergeIdx = j;
+ break;
+ }
+ }
+ }
+ if (mergeIdx == -1)
+ {
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i);
+ }
+ else
+ {
+ rcContour& mcont = cset.conts[mergeIdx];
+ // Merge by closest points.
+ int ia = 0, ib = 0;
+ getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib);
+ if (ia == -1 || ib == -1)
+ {
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx);
+ continue;
+ }
+ if (!mergeContours(mcont, cont, ia, ib))
+ {
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
+ continue;
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
+
+ ctx->stopTimer(RC_TIMER_BUILD_CONTOURS);
+
+ return true;
+}
diff --git a/dep/recastnavigation/Recast/RecastFilter.cpp b/dep/recastnavigation/Recast/RecastFilter.cpp
new file mode 100644
index 0000000..d01808a
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastFilter.cpp
@@ -0,0 +1,179 @@
+//
+// 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.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAssert.h"
+
+
+void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
+
+ const int w = solid.width;
+ const int h = solid.height;
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ rcSpan* ps = 0;
+ bool previousWalkable = false;
+
+ for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next)
+ {
+ const bool walkable = s->area != RC_NULL_AREA;
+ // If current span is not walkable, but there is walkable
+ // span just below it, mark the span above it walkable too.
+ if (!walkable && previousWalkable)
+ {
+ if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb)
+ s->area = RC_NULL_AREA;
+ }
+ // Copy walkable flag so that it cannot propagate
+ // past multiple non-walkable objects.
+ previousWalkable = walkable;
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
+}
+
+void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb,
+ rcHeightfield& solid)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_FILTER_BORDER);
+
+ const int w = solid.width;
+ const int h = solid.height;
+ const int MAX_HEIGHT = 0xffff;
+
+ // Mark border spans.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
+ {
+ // Skip non walkable spans.
+ if (s->area == RC_NULL_AREA)
+ continue;
+
+ const int bot = (int)(s->smax);
+ const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
+
+ // Find neighbours minimum height.
+ int minh = MAX_HEIGHT;
+
+ // Min and max height of accessible neighbours.
+ int asmin = s->smax;
+ int asmax = s->smax;
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ int dx = x + rcGetDirOffsetX(dir);
+ int dy = y + rcGetDirOffsetY(dir);
+ // Skip neighbours which are out of bounds.
+ if (dx < 0 || dy < 0 || dx >= w || dy >= h)
+ {
+ minh = rcMin(minh, -walkableClimb - bot);
+ continue;
+ }
+
+ // From minus infinity to the first span.
+ rcSpan* ns = solid.spans[dx + dy*w];
+ int nbot = -walkableClimb;
+ int ntop = ns ? (int)ns->smin : MAX_HEIGHT;
+ // Skip neightbour if the gap between the spans is too small.
+ if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
+ minh = rcMin(minh, nbot - bot);
+
+ // Rest of the spans.
+ for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next)
+ {
+ nbot = (int)ns->smax;
+ ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT;
+ // Skip neightbour if the gap between the spans is too small.
+ if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
+ {
+ minh = rcMin(minh, nbot - bot);
+
+ // Find min/max accessible neighbour height.
+ if (rcAbs(nbot - bot) <= walkableClimb)
+ {
+ if (nbot < asmin) asmin = nbot;
+ if (nbot > asmax) asmax = nbot;
+ }
+
+ }
+ }
+ }
+
+ // The current span is close to a ledge if the drop to any
+ // neighbour span is less than the walkableClimb.
+ if (minh < -walkableClimb)
+ s->area = RC_NULL_AREA;
+
+ // If the difference between all neighbours is too large,
+ // we are at steep slope, mark the span as ledge.
+ if ((asmax - asmin) > walkableClimb)
+ {
+ s->area = RC_NULL_AREA;
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_FILTER_BORDER);
+}
+
+void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_FILTER_WALKABLE);
+
+ const int w = solid.width;
+ const int h = solid.height;
+ const int MAX_HEIGHT = 0xffff;
+
+ // Remove walkable flag from spans which do not have enough
+ // space above them for the agent to stand there.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
+ {
+ const int bot = (int)(s->smax);
+ const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
+ if ((top - bot) <= walkableHeight)
+ s->area = RC_NULL_AREA;
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_FILTER_WALKABLE);
+}
diff --git a/dep/recastnavigation/Recast/RecastMesh.cpp b/dep/recastnavigation/Recast/RecastMesh.cpp
new file mode 100644
index 0000000..4b33c10
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastMesh.cpp
@@ -0,0 +1,1322 @@
+//
+// 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.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+struct rcEdge
+{
+ unsigned short vert[2];
+ unsigned short polyEdge[2];
+ unsigned short poly[2];
+};
+
+static bool buildMeshAdjacency(unsigned short* polys, const int npolys,
+ const int nverts, const int vertsPerPoly)
+{
+ // Based on code by Eric Lengyel from:
+ // http://www.terathon.com/code/edges.php
+
+ int maxEdgeCount = npolys*vertsPerPoly;
+ unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP);
+ if (!firstEdge)
+ return false;
+ unsigned short* nextEdge = firstEdge + nverts;
+ int edgeCount = 0;
+
+ rcEdge* edges = (rcEdge*)rcAlloc(sizeof(rcEdge)*maxEdgeCount, RC_ALLOC_TEMP);
+ if (!edges)
+ {
+ rcFree(firstEdge);
+ return false;
+ }
+
+ for (int i = 0; i < nverts; i++)
+ firstEdge[i] = RC_MESH_NULL_IDX;
+
+ for (int i = 0; i < npolys; ++i)
+ {
+ unsigned short* t = &polys[i*vertsPerPoly*2];
+ for (int j = 0; j < vertsPerPoly; ++j)
+ {
+ unsigned short v0 = t[j];
+ unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1];
+ if (v0 < v1)
+ {
+ rcEdge& edge = edges[edgeCount];
+ edge.vert[0] = v0;
+ edge.vert[1] = v1;
+ edge.poly[0] = (unsigned short)i;
+ edge.polyEdge[0] = (unsigned short)j;
+ edge.poly[1] = (unsigned short)i;
+ edge.polyEdge[1] = 0;
+ // Insert edge
+ nextEdge[edgeCount] = firstEdge[v0];
+ firstEdge[v0] = (unsigned short)edgeCount;
+ edgeCount++;
+ }
+ }
+ }
+
+ for (int i = 0; i < npolys; ++i)
+ {
+ unsigned short* t = &polys[i*vertsPerPoly*2];
+ for (int j = 0; j < vertsPerPoly; ++j)
+ {
+ unsigned short v0 = t[j];
+ unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1];
+ if (v0 > v1)
+ {
+ for (unsigned short e = firstEdge[v1]; e != RC_MESH_NULL_IDX; e = nextEdge[e])
+ {
+ rcEdge& edge = edges[e];
+ if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1])
+ {
+ edge.poly[1] = (unsigned short)i;
+ edge.polyEdge[1] = (unsigned short)j;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Store adjacency
+ for (int i = 0; i < edgeCount; ++i)
+ {
+ const rcEdge& e = edges[i];
+ if (e.poly[0] != e.poly[1])
+ {
+ unsigned short* p0 = &polys[e.poly[0]*vertsPerPoly*2];
+ unsigned short* p1 = &polys[e.poly[1]*vertsPerPoly*2];
+ p0[vertsPerPoly + e.polyEdge[0]] = e.poly[1];
+ p1[vertsPerPoly + e.polyEdge[1]] = e.poly[0];
+ }
+ }
+
+ rcFree(firstEdge);
+ rcFree(edges);
+
+ return true;
+}
+
+
+static const int VERTEX_BUCKET_COUNT = (1<<12);
+
+inline int computeVertexHash(int x, int y, int z)
+{
+ const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
+ const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
+ const unsigned int h3 = 0xcb1ab31f;
+ unsigned int n = h1 * x + h2 * y + h3 * z;
+ return (int)(n & (VERTEX_BUCKET_COUNT-1));
+}
+
+static unsigned short addVertex(unsigned short x, unsigned short y, unsigned short z,
+ unsigned short* verts, int* firstVert, int* nextVert, int& nv)
+{
+ int bucket = computeVertexHash(x, 0, z);
+ int i = firstVert[bucket];
+
+ while (i != -1)
+ {
+ const unsigned short* v = &verts[i*3];
+ if (v[0] == x && (rcAbs(v[1] - y) <= 2) && v[2] == z)
+ return (unsigned short)i;
+ i = nextVert[i]; // next
+ }
+
+ // Could not find, create new.
+ i = nv; nv++;
+ unsigned short* v = &verts[i*3];
+ v[0] = x;
+ v[1] = y;
+ v[2] = z;
+ nextVert[i] = firstVert[bucket];
+ firstVert[bucket] = i;
+
+ return (unsigned short)i;
+}
+
+inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
+inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
+
+inline int area2(const int* a, const int* b, const int* c)
+{
+ return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]);
+}
+
+// Exclusive or: true iff exactly one argument is true.
+// The arguments are negated to ensure that they are 0/1
+// values. Then the bitwise Xor operator may apply.
+// (This idea is due to Michael Baldwin.)
+inline bool xorb(bool x, bool y)
+{
+ return !x ^ !y;
+}
+
+// Returns true iff c is strictly to the left of the directed
+// line through a to b.
+inline bool left(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) < 0;
+}
+
+inline bool leftOn(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) <= 0;
+}
+
+inline bool collinear(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) == 0;
+}
+
+// Returns true iff ab properly intersects cd: they share
+// a point interior to both segments. The properness of the
+// intersection is ensured by using strict leftness.
+bool intersectProp(const int* a, const int* b, const int* c, const int* d)
+{
+ // Eliminate improper cases.
+ if (collinear(a,b,c) || collinear(a,b,d) ||
+ collinear(c,d,a) || collinear(c,d,b))
+ return false;
+
+ return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b));
+}
+
+// Returns T iff (a,b,c) are collinear and point c lies
+// on the closed segement ab.
+static bool between(const int* a, const int* b, const int* c)
+{
+ if (!collinear(a, b, c))
+ return false;
+ // If ab not vertical, check betweenness on x; else on y.
+ if (a[0] != b[0])
+ return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0]));
+ else
+ return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2]));
+}
+
+// Returns true iff segments ab and cd intersect, properly or improperly.
+static bool intersect(const int* a, const int* b, const int* c, const int* d)
+{
+ if (intersectProp(a, b, c, d))
+ return true;
+ else if (between(a, b, c) || between(a, b, d) ||
+ between(c, d, a) || between(c, d, b))
+ return true;
+ else
+ return false;
+}
+
+static bool vequal(const int* a, const int* b)
+{
+ return a[0] == b[0] && a[2] == b[2];
+}
+
+// Returns T iff (v_i, v_j) is a proper internal *or* external
+// diagonal of P, *ignoring edges incident to v_i and v_j*.
+static bool diagonalie(int i, int j, int n, const int* verts, int* indices)
+{
+ const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
+
+ // For each edge (k,k+1) of P
+ for (int k = 0; k < n; k++)
+ {
+ int k1 = next(k, n);
+ // Skip edges incident to i or j
+ if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
+ {
+ const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4];
+ const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4];
+
+ if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
+ continue;
+
+ if (intersect(d0, d1, p0, p1))
+ return false;
+ }
+ }
+ return true;
+}
+
+// Returns true iff the diagonal (i,j) is strictly internal to the
+// polygon P in the neighborhood of the i endpoint.
+static bool inCone(int i, int j, int n, const int* verts, int* indices)
+{
+ const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
+ const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4];
+ const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4];
+
+ // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
+ if (leftOn(pin1, pi, pi1))
+ return left(pi, pj, pin1) && left(pj, pi, pi1);
+ // Assume (i-1,i,i+1) not collinear.
+ // else P[i] is reflex.
+ return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
+}
+
+// Returns T iff (v_i, v_j) is a proper internal
+// diagonal of P.
+static bool diagonal(int i, int j, int n, const int* verts, int* indices)
+{
+ return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices);
+}
+
+static int triangulate(int n, const int* verts, int* indices, int* tris)
+{
+ int ntris = 0;
+ int* dst = tris;
+
+ // The last bit of the index is used to indicate if the vertex can be removed.
+ for (int i = 0; i < n; i++)
+ {
+ int i1 = next(i, n);
+ int i2 = next(i1, n);
+ if (diagonal(i, i2, n, verts, indices))
+ indices[i1] |= 0x80000000;
+ }
+
+ while (n > 3)
+ {
+ int minLen = -1;
+ int mini = -1;
+ for (int i = 0; i < n; i++)
+ {
+ int i1 = next(i, n);
+ if (indices[i1] & 0x80000000)
+ {
+ const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* p2 = &verts[(indices[next(i1, n)] & 0x0fffffff) * 4];
+
+ int dx = p2[0] - p0[0];
+ int dy = p2[2] - p0[2];
+ int len = dx*dx + dy*dy;
+
+ if (minLen < 0 || len < minLen)
+ {
+ minLen = len;
+ mini = i;
+ }
+ }
+ }
+
+ if (mini == -1)
+ {
+ // Should not happen.
+/* printf("mini == -1 ntris=%d n=%d\n", ntris, n);
+ for (int i = 0; i < n; i++)
+ {
+ printf("%d ", indices[i] & 0x0fffffff);
+ }
+ printf("\n");*/
+ return -ntris;
+ }
+
+ int i = mini;
+ int i1 = next(i, n);
+ int i2 = next(i1, n);
+
+ *dst++ = indices[i] & 0x0fffffff;
+ *dst++ = indices[i1] & 0x0fffffff;
+ *dst++ = indices[i2] & 0x0fffffff;
+ ntris++;
+
+ // Removes P[i1] by copying P[i+1]...P[n-1] left one index.
+ n--;
+ for (int k = i1; k < n; k++)
+ indices[k] = indices[k+1];
+
+ if (i1 >= n) i1 = 0;
+ i = prev(i1,n);
+ // Update diagonal flags.
+ if (diagonal(prev(i, n), i1, n, verts, indices))
+ indices[i] |= 0x80000000;
+ else
+ indices[i] &= 0x0fffffff;
+
+ if (diagonal(i, next(i1, n), n, verts, indices))
+ indices[i1] |= 0x80000000;
+ else
+ indices[i1] &= 0x0fffffff;
+ }
+
+ // Append the remaining triangle.
+ *dst++ = indices[0] & 0x0fffffff;
+ *dst++ = indices[1] & 0x0fffffff;
+ *dst++ = indices[2] & 0x0fffffff;
+ ntris++;
+
+ return ntris;
+}
+
+static int countPolyVerts(const unsigned short* p, const int nvp)
+{
+ for (int i = 0; i < nvp; ++i)
+ if (p[i] == RC_MESH_NULL_IDX)
+ return i;
+ return nvp;
+}
+
+inline bool uleft(const unsigned short* a, const unsigned short* b, const unsigned short* c)
+{
+ return ((int)b[0] - (int)a[0]) * ((int)c[2] - (int)a[2]) -
+ ((int)c[0] - (int)a[0]) * ((int)b[2] - (int)a[2]) < 0;
+}
+
+static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
+ const unsigned short* verts, int& ea, int& eb,
+ const int nvp)
+{
+ const int na = countPolyVerts(pa, nvp);
+ const int nb = countPolyVerts(pb, nvp);
+
+ // If the merged polygon would be too big, do not merge.
+ if (na+nb-2 > nvp)
+ return -1;
+
+ // Check if the polygons share an edge.
+ ea = -1;
+ eb = -1;
+
+ for (int i = 0; i < na; ++i)
+ {
+ unsigned short va0 = pa[i];
+ unsigned short va1 = pa[(i+1) % na];
+ if (va0 > va1)
+ rcSwap(va0, va1);
+ for (int j = 0; j < nb; ++j)
+ {
+ unsigned short vb0 = pb[j];
+ unsigned short vb1 = pb[(j+1) % nb];
+ if (vb0 > vb1)
+ rcSwap(vb0, vb1);
+ if (va0 == vb0 && va1 == vb1)
+ {
+ ea = i;
+ eb = j;
+ break;
+ }
+ }
+ }
+
+ // No common edge, cannot merge.
+ if (ea == -1 || eb == -1)
+ return -1;
+
+ // Check to see if the merged polygon would be convex.
+ unsigned short va, vb, vc;
+
+ va = pa[(ea+na-1) % na];
+ vb = pa[ea];
+ vc = pb[(eb+2) % nb];
+ if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3]))
+ return -1;
+
+ va = pb[(eb+nb-1) % nb];
+ vb = pb[eb];
+ vc = pa[(ea+2) % na];
+ if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3]))
+ return -1;
+
+ va = pa[ea];
+ vb = pa[(ea+1)%na];
+
+ int dx = (int)verts[va*3+0] - (int)verts[vb*3+0];
+ int dy = (int)verts[va*3+2] - (int)verts[vb*3+2];
+
+ return dx*dx + dy*dy;
+}
+
+static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb,
+ unsigned short* tmp, const int nvp)
+{
+ const int na = countPolyVerts(pa, nvp);
+ const int nb = countPolyVerts(pb, nvp);
+
+ // Merge polygons.
+ memset(tmp, 0xff, sizeof(unsigned short)*nvp);
+ int n = 0;
+ // Add pa
+ for (int i = 0; i < na-1; ++i)
+ tmp[n++] = pa[(ea+1+i) % na];
+ // Add pb
+ for (int i = 0; i < nb-1; ++i)
+ tmp[n++] = pb[(eb+1+i) % nb];
+
+ memcpy(pa, tmp, sizeof(unsigned short)*nvp);
+}
+
+static void pushFront(int v, int* arr, int& an)
+{
+ an++;
+ for (int i = an-1; i > 0; --i) arr[i] = arr[i-1];
+ arr[0] = v;
+}
+
+static void pushBack(int v, int* arr, int& an)
+{
+ arr[an] = v;
+ an++;
+}
+
+static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem)
+{
+ const int nvp = mesh.nvp;
+
+ // Count number of polygons to remove.
+ int numRemovedVerts = 0;
+ int numTouchedVerts = 0;
+ int numRemainingEdges = 0;
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ int numRemoved = 0;
+ int numVerts = 0;
+ for (int j = 0; j < nv; ++j)
+ {
+ if (p[j] == rem)
+ {
+ numTouchedVerts++;
+ numRemoved++;
+ }
+ numVerts++;
+ }
+ if (numRemoved)
+ {
+ numRemovedVerts += numRemoved;
+ numRemainingEdges += numVerts-(numRemoved+1);
+ }
+ }
+
+ // There would be too few edges remaining to create a polygon.
+ // This can happen for example when a tip of a triangle is marked
+ // as deletion, but there are no other polys that share the vertex.
+ // In this case, the vertex should not be removed.
+ if (numRemainingEdges <= 2)
+ return false;
+
+ // Find edges which share the removed vertex.
+ const int maxEdges = numTouchedVerts*2;
+ int nedges = 0;
+ rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP);
+ if (!edges)
+ {
+ ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3);
+ return false;
+ }
+
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+
+ // Collect edges which touches the removed vertex.
+ for (int j = 0, k = nv-1; j < nv; k = j++)
+ {
+ if (p[j] == rem || p[k] == rem)
+ {
+ // Arrange edge so that a=rem.
+ int a = p[j], b = p[k];
+ if (b == rem)
+ rcSwap(a,b);
+
+ // Check if the edge exists
+ bool exists = false;
+ for (int k = 0; k < nedges; ++k)
+ {
+ int* e = &edges[k*3];
+ if (e[1] == b)
+ {
+ // Exists, increment vertex share count.
+ e[2]++;
+ exists = true;
+ }
+ }
+ // Add new edge.
+ if (!exists)
+ {
+ int* e = &edges[nedges*3];
+ e[0] = a;
+ e[1] = b;
+ e[2] = 1;
+ nedges++;
+ }
+ }
+ }
+ }
+
+ // There should be no more than 2 open edges.
+ // This catches the case that two non-adjacent polygons
+ // share the removed vertex. In that case, do not remove the vertex.
+ int numOpenEdges = 0;
+ for (int i = 0; i < nedges; ++i)
+ {
+ if (edges[i*3+2] < 2)
+ numOpenEdges++;
+ }
+ if (numOpenEdges > 2)
+ return false;
+
+ return true;
+}
+
+static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem, const int maxTris)
+{
+ const int nvp = mesh.nvp;
+
+ // Count number of polygons to remove.
+ int numRemovedVerts = 0;
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ for (int j = 0; j < nv; ++j)
+ {
+ if (p[j] == rem)
+ numRemovedVerts++;
+ }
+ }
+
+ int nedges = 0;
+ rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP);
+ if (!edges)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4);
+ return false;
+ }
+
+ int nhole = 0;
+ rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+ if (!hole)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp);
+ return false;
+ }
+
+ int nhreg = 0;
+ rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+ if (!hreg)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp);
+ return false;
+ }
+
+ int nharea = 0;
+ rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+ if (!harea)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp);
+ return false;
+ }
+
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ bool hasRem = false;
+ for (int j = 0; j < nv; ++j)
+ if (p[j] == rem) hasRem = true;
+ if (hasRem)
+ {
+ // Collect edges which does not touch the removed vertex.
+ for (int j = 0, k = nv-1; j < nv; k = j++)
+ {
+ if (p[j] != rem && p[k] != rem)
+ {
+ int* e = &edges[nedges*4];
+ e[0] = p[k];
+ e[1] = p[j];
+ e[2] = mesh.regs[i];
+ e[3] = mesh.areas[i];
+ nedges++;
+ }
+ }
+ // Remove the polygon.
+ unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2];
+ memcpy(p,p2,sizeof(unsigned short)*nvp);
+ memset(p+nvp,0xff,sizeof(unsigned short)*nvp);
+ mesh.regs[i] = mesh.regs[mesh.npolys-1];
+ mesh.areas[i] = mesh.areas[mesh.npolys-1];
+ mesh.npolys--;
+ --i;
+ }
+ }
+
+ // Remove vertex.
+ for (int i = (int)rem; i < mesh.nverts; ++i)
+ {
+ mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0];
+ mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1];
+ mesh.verts[i*3+2] = mesh.verts[(i+1)*3+2];
+ }
+ mesh.nverts--;
+
+ // Adjust indices to match the removed vertex layout.
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ for (int j = 0; j < nv; ++j)
+ if (p[j] > rem) p[j]--;
+ }
+ for (int i = 0; i < nedges; ++i)
+ {
+ if (edges[i*4+0] > rem) edges[i*4+0]--;
+ if (edges[i*4+1] > rem) edges[i*4+1]--;
+ }
+
+ if (nedges == 0)
+ return true;
+
+ // Start with one vertex, keep appending connected
+ // segments to the start and end of the hole.
+ pushBack(edges[0], hole, nhole);
+ pushBack(edges[2], hreg, nhreg);
+ pushBack(edges[3], harea, nharea);
+
+ while (nedges)
+ {
+ bool match = false;
+
+ for (int i = 0; i < nedges; ++i)
+ {
+ const int ea = edges[i*4+0];
+ const int eb = edges[i*4+1];
+ const int r = edges[i*4+2];
+ const int a = edges[i*4+3];
+ bool add = false;
+ if (hole[0] == eb)
+ {
+ // The segment matches the beginning of the hole boundary.
+ pushFront(ea, hole, nhole);
+ pushFront(r, hreg, nhreg);
+ pushFront(a, harea, nharea);
+ add = true;
+ }
+ else if (hole[nhole-1] == ea)
+ {
+ // The segment matches the end of the hole boundary.
+ pushBack(eb, hole, nhole);
+ pushBack(r, hreg, nhreg);
+ pushBack(a, harea, nharea);
+ add = true;
+ }
+ if (add)
+ {
+ // The edge segment was added, remove it.
+ edges[i*4+0] = edges[(nedges-1)*4+0];
+ edges[i*4+1] = edges[(nedges-1)*4+1];
+ edges[i*4+2] = edges[(nedges-1)*4+2];
+ edges[i*4+3] = edges[(nedges-1)*4+3];
+ --nedges;
+ match = true;
+ --i;
+ }
+ }
+
+ if (!match)
+ break;
+ }
+
+ rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP);
+ if (!tris)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3);
+ return false;
+ }
+
+ rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP);
+ if (!tverts)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4);
+ return false;
+ }
+
+ rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP);
+ if (!tverts)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole);
+ return false;
+ }
+
+ // Generate temp vertex array for triangulation.
+ for (int i = 0; i < nhole; ++i)
+ {
+ const int pi = hole[i];
+ tverts[i*4+0] = mesh.verts[pi*3+0];
+ tverts[i*4+1] = mesh.verts[pi*3+1];
+ tverts[i*4+2] = mesh.verts[pi*3+2];
+ tverts[i*4+3] = 0;
+ thole[i] = i;
+ }
+
+ // Triangulate the hole.
+ int ntris = triangulate(nhole, &tverts[0], &thole[0], tris);
+ if (ntris < 0)
+ {
+ ntris = -ntris;
+ ctx->log(RC_LOG_WARNING, "removeVertex: triangulate() returned bad results.");
+ }
+
+ // Merge the hole triangles back to polygons.
+ rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP);
+ if (!polys)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp);
+ return false;
+ }
+ rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP);
+ if (!pregs)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris);
+ return false;
+ }
+ rcScopedDelete<unsigned char> pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP);
+ if (!pregs)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris);
+ return false;
+ }
+
+ unsigned short* tmpPoly = &polys[ntris*nvp];
+
+ // Build initial polygons.
+ int npolys = 0;
+ memset(polys, 0xff, ntris*nvp*sizeof(unsigned short));
+ for (int j = 0; j < ntris; ++j)
+ {
+ int* t = &tris[j*3];
+ if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
+ {
+ polys[npolys*nvp+0] = (unsigned short)hole[t[0]];
+ polys[npolys*nvp+1] = (unsigned short)hole[t[1]];
+ polys[npolys*nvp+2] = (unsigned short)hole[t[2]];
+ pregs[npolys] = (unsigned short)hreg[t[0]];
+ pareas[npolys] = (unsigned char)harea[t[0]];
+ npolys++;
+ }
+ }
+ if (!npolys)
+ return true;
+
+ // Merge polygons.
+ if (nvp > 3)
+ {
+ for (;;)
+ {
+ // Find best polygons to merge.
+ int bestMergeVal = 0;
+ int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
+
+ for (int j = 0; j < npolys-1; ++j)
+ {
+ unsigned short* pj = &polys[j*nvp];
+ for (int k = j+1; k < npolys; ++k)
+ {
+ unsigned short* pk = &polys[k*nvp];
+ int ea, eb;
+ int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp);
+ if (v > bestMergeVal)
+ {
+ bestMergeVal = v;
+ bestPa = j;
+ bestPb = k;
+ bestEa = ea;
+ bestEb = eb;
+ }
+ }
+ }
+
+ if (bestMergeVal > 0)
+ {
+ // Found best, merge.
+ unsigned short* pa = &polys[bestPa*nvp];
+ unsigned short* pb = &polys[bestPb*nvp];
+ mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
+ memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
+ pregs[bestPb] = pregs[npolys-1];
+ pareas[bestPb] = pareas[npolys-1];
+ npolys--;
+ }
+ else
+ {
+ // Could not merge any polygons, stop.
+ break;
+ }
+ }
+ }
+
+ // Store polygons.
+ for (int i = 0; i < npolys; ++i)
+ {
+ if (mesh.npolys >= maxTris) break;
+ unsigned short* p = &mesh.polys[mesh.npolys*nvp*2];
+ memset(p,0xff,sizeof(unsigned short)*nvp*2);
+ for (int j = 0; j < nvp; ++j)
+ p[j] = polys[i*nvp+j];
+ mesh.regs[mesh.npolys] = pregs[i];
+ mesh.areas[mesh.npolys] = pareas[i];
+ mesh.npolys++;
+ if (mesh.npolys > maxTris)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Too many polygons %d (max:%d).", mesh.npolys, maxTris);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_POLYMESH);
+
+ rcVcopy(mesh.bmin, cset.bmin);
+ rcVcopy(mesh.bmax, cset.bmax);
+ mesh.cs = cset.cs;
+ mesh.ch = cset.ch;
+
+ int maxVertices = 0;
+ int maxTris = 0;
+ int maxVertsPerCont = 0;
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ // Skip null contours.
+ if (cset.conts[i].nverts < 3) continue;
+ maxVertices += cset.conts[i].nverts;
+ maxTris += cset.conts[i].nverts - 2;
+ maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts);
+ }
+
+ if (maxVertices >= 0xfffe)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices);
+ return false;
+ }
+
+ rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP);
+ if (!vflags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
+ return false;
+ }
+ memset(vflags, 0, maxVertices);
+
+ mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertices*3, RC_ALLOC_PERM);
+ if (!mesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
+ return false;
+ }
+ mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2*2, RC_ALLOC_PERM);
+ if (!mesh.polys)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2);
+ return false;
+ }
+ mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris, RC_ALLOC_PERM);
+ if (!mesh.regs)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris);
+ return false;
+ }
+ mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris, RC_ALLOC_PERM);
+ if (!mesh.areas)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' (%d).", maxTris);
+ return false;
+ }
+
+ mesh.nverts = 0;
+ mesh.npolys = 0;
+ mesh.nvp = nvp;
+ mesh.maxpolys = maxTris;
+
+ memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3);
+ memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2);
+ memset(mesh.regs, 0, sizeof(unsigned short)*maxTris);
+ memset(mesh.areas, 0, sizeof(unsigned char)*maxTris);
+
+ rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP);
+ if (!nextVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices);
+ return false;
+ }
+ memset(nextVert, 0, sizeof(int)*maxVertices);
+
+ rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
+ if (!firstVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
+ return false;
+ }
+ for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
+ firstVert[i] = -1;
+
+ rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP);
+ if (!indices)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
+ return false;
+ }
+ rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP);
+ if (!tris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
+ return false;
+ }
+ rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP);
+ if (!polys)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp);
+ return false;
+ }
+ unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp];
+
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ rcContour& cont = cset.conts[i];
+
+ // Skip null contours.
+ if (cont.nverts < 3)
+ continue;
+
+ // Triangulate contour
+ for (int j = 0; j < cont.nverts; ++j)
+ indices[j] = j;
+
+ int ntris = triangulate(cont.nverts, cont.verts, &indices[0], &tris[0]);
+ if (ntris <= 0)
+ {
+ // Bad triangulation, should not happen.
+/* printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]);
+ printf("\tconst float cs = %ff;\n", cset.cs);
+ printf("\tconst float ch = %ff;\n", cset.ch);
+ printf("\tconst int verts[] = {\n");
+ for (int k = 0; k < cont.nverts; ++k)
+ {
+ const int* v = &cont.verts[k*4];
+ printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]);
+ }
+ printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/
+ ctx->log(RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour %d.", i);
+ ntris = -ntris;
+ }
+
+ // Add and merge vertices.
+ for (int j = 0; j < cont.nverts; ++j)
+ {
+ const int* v = &cont.verts[j*4];
+ indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2],
+ mesh.verts, firstVert, nextVert, mesh.nverts);
+ if (v[3] & RC_BORDER_VERTEX)
+ {
+ // This vertex should be removed.
+ vflags[indices[j]] = 1;
+ }
+ }
+
+ // Build initial polygons.
+ int npolys = 0;
+ memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(unsigned short));
+ for (int j = 0; j < ntris; ++j)
+ {
+ int* t = &tris[j*3];
+ if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
+ {
+ polys[npolys*nvp+0] = (unsigned short)indices[t[0]];
+ polys[npolys*nvp+1] = (unsigned short)indices[t[1]];
+ polys[npolys*nvp+2] = (unsigned short)indices[t[2]];
+ npolys++;
+ }
+ }
+ if (!npolys)
+ continue;
+
+ // Merge polygons.
+ if (nvp > 3)
+ {
+ for(;;)
+ {
+ // Find best polygons to merge.
+ int bestMergeVal = 0;
+ int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
+
+ for (int j = 0; j < npolys-1; ++j)
+ {
+ unsigned short* pj = &polys[j*nvp];
+ for (int k = j+1; k < npolys; ++k)
+ {
+ unsigned short* pk = &polys[k*nvp];
+ int ea, eb;
+ int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp);
+ if (v > bestMergeVal)
+ {
+ bestMergeVal = v;
+ bestPa = j;
+ bestPb = k;
+ bestEa = ea;
+ bestEb = eb;
+ }
+ }
+ }
+
+ if (bestMergeVal > 0)
+ {
+ // Found best, merge.
+ unsigned short* pa = &polys[bestPa*nvp];
+ unsigned short* pb = &polys[bestPb*nvp];
+ mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
+ memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
+ npolys--;
+ }
+ else
+ {
+ // Could not merge any polygons, stop.
+ break;
+ }
+ }
+ }
+
+ // Store polygons.
+ for (int j = 0; j < npolys; ++j)
+ {
+ unsigned short* p = &mesh.polys[mesh.npolys*nvp*2];
+ unsigned short* q = &polys[j*nvp];
+ for (int k = 0; k < nvp; ++k)
+ p[k] = q[k];
+ mesh.regs[mesh.npolys] = cont.reg;
+ mesh.areas[mesh.npolys] = cont.area;
+ mesh.npolys++;
+ if (mesh.npolys > maxTris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons %d (max:%d).", mesh.npolys, maxTris);
+ return false;
+ }
+ }
+ }
+
+
+ // Remove edge vertices.
+ for (int i = 0; i < mesh.nverts; ++i)
+ {
+ if (vflags[i])
+ {
+ if (!canRemoveVertex(ctx, mesh, (unsigned short)i))
+ continue;
+ if (!removeVertex(ctx, mesh, (unsigned short)i, maxTris))
+ {
+ // Failed to remove vertex
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex %d.", i);
+ return false;
+ }
+ // Remove vertex
+ // Note: mesh.nverts is already decremented inside removeVertex()!
+ for (int j = i; j < mesh.nverts; ++j)
+ vflags[j] = vflags[j+1];
+ --i;
+ }
+ }
+
+ // Calculate adjacency.
+ if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp))
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed.");
+ return false;
+ }
+
+ // Just allocate the mesh flags array. The user is resposible to fill it.
+ mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*mesh.npolys, RC_ALLOC_PERM);
+ if (!mesh.flags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' (%d).", mesh.npolys);
+ return false;
+ }
+ memset(mesh.flags, 0, sizeof(unsigned short) * mesh.npolys);
+
+ if (mesh.nverts > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
+ }
+ if (mesh.npolys > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_POLYMESH);
+
+ return true;
+}
+
+bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
+{
+ rcAssert(ctx);
+
+ if (!nmeshes || !meshes)
+ return true;
+
+ ctx->startTimer(RC_TIMER_MERGE_POLYMESH);
+
+ mesh.nvp = meshes[0]->nvp;
+ mesh.cs = meshes[0]->cs;
+ mesh.ch = meshes[0]->ch;
+ rcVcopy(mesh.bmin, meshes[0]->bmin);
+ rcVcopy(mesh.bmax, meshes[0]->bmax);
+
+ int maxVerts = 0;
+ int maxPolys = 0;
+ int maxVertsPerMesh = 0;
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ rcVmin(mesh.bmin, meshes[i]->bmin);
+ rcVmax(mesh.bmax, meshes[i]->bmax);
+ maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts);
+ maxVerts += meshes[i]->nverts;
+ maxPolys += meshes[i]->npolys;
+ }
+
+ mesh.nverts = 0;
+ mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVerts*3, RC_ALLOC_PERM);
+ if (!mesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3);
+ return false;
+ }
+
+ mesh.npolys = 0;
+ mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM);
+ if (!mesh.polys)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp);
+ return false;
+ }
+ memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp);
+
+ mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM);
+ if (!mesh.regs)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys);
+ return false;
+ }
+ memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys);
+
+ mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxPolys, RC_ALLOC_PERM);
+ if (!mesh.areas)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' (%d).", maxPolys);
+ return false;
+ }
+ memset(mesh.areas, 0, sizeof(unsigned char)*maxPolys);
+
+ mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM);
+ if (!mesh.flags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' (%d).", maxPolys);
+ return false;
+ }
+ memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys);
+
+ rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP);
+ if (!nextVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts);
+ return false;
+ }
+ memset(nextVert, 0, sizeof(int)*maxVerts);
+
+ rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
+ if (!firstVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
+ return false;
+ }
+ for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
+ firstVert[i] = -1;
+
+ rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM);
+ if (!vremap)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh);
+ return false;
+ }
+ memset(nextVert, 0, sizeof(int)*maxVerts);
+
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ const rcPolyMesh* pmesh = meshes[i];
+
+ const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f);
+ const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f);
+
+ for (int j = 0; j < pmesh->nverts; ++j)
+ {
+ unsigned short* v = &pmesh->verts[j*3];
+ vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz,
+ mesh.verts, firstVert, nextVert, mesh.nverts);
+ }
+
+ for (int j = 0; j < pmesh->npolys; ++j)
+ {
+ unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp];
+ unsigned short* src = &pmesh->polys[j*2*mesh.nvp];
+ mesh.regs[mesh.npolys] = pmesh->regs[j];
+ mesh.areas[mesh.npolys] = pmesh->areas[j];
+ mesh.flags[mesh.npolys] = pmesh->flags[j];
+ mesh.npolys++;
+ for (int k = 0; k < mesh.nvp; ++k)
+ {
+ if (src[k] == RC_MESH_NULL_IDX) break;
+ tgt[k] = vremap[src[k]];
+ }
+ }
+ }
+
+ // Calculate adjacency.
+ if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp))
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed.");
+ return false;
+ }
+
+ if (mesh.nverts > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
+ }
+ if (mesh.npolys > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
+ }
+
+ ctx->stopTimer(RC_TIMER_MERGE_POLYMESH);
+
+ return true;
+}
diff --git a/dep/recastnavigation/Recast/RecastMeshDetail.cpp b/dep/recastnavigation/Recast/RecastMeshDetail.cpp
new file mode 100644
index 0000000..ffb4b58
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastMeshDetail.cpp
@@ -0,0 +1,1237 @@
+//
+// 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 <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+
+static const unsigned RC_UNSET_HEIGHT = 0xffff;
+
+struct rcHeightPatch
+{
+ inline rcHeightPatch() : data(0), xmin(0), ymin(0), width(0), height(0) {}
+ inline ~rcHeightPatch() { rcFree(data); }
+ unsigned short* data;
+ int xmin, ymin, width, height;
+};
+
+
+inline float vdot2(const float* a, const float* b)
+{
+ return a[0]*b[0] + a[2]*b[2];
+}
+
+inline float vdistSq2(const float* p, const float* q)
+{
+ const float dx = q[0] - p[0];
+ const float dy = q[2] - p[2];
+ return dx*dx + dy*dy;
+}
+
+inline float vdist2(const float* p, const float* q)
+{
+ return sqrtf(vdistSq2(p,q));
+}
+
+inline float vcross2(const float* p1, const float* p2, const float* p3)
+{
+ const float u1 = p2[0] - p1[0];
+ const float v1 = p2[2] - p1[2];
+ const float u2 = p3[0] - p1[0];
+ const float v2 = p3[2] - p1[2];
+ return u1 * v2 - v1 * u2;
+}
+
+static bool circumCircle(const float* p1, const float* p2, const float* p3,
+ float* c, float& r)
+{
+ static const float EPS = 1e-6f;
+
+ const float cp = vcross2(p1, p2, p3);
+ if (fabsf(cp) > EPS)
+ {
+ const float p1Sq = vdot2(p1,p1);
+ const float p2Sq = vdot2(p2,p2);
+ const float p3Sq = vdot2(p3,p3);
+ c[0] = (p1Sq*(p2[2]-p3[2]) + p2Sq*(p3[2]-p1[2]) + p3Sq*(p1[2]-p2[2])) / (2*cp);
+ c[2] = (p1Sq*(p3[0]-p2[0]) + p2Sq*(p1[0]-p3[0]) + p3Sq*(p2[0]-p1[0])) / (2*cp);
+ r = vdist2(c, p1);
+ return true;
+ }
+
+ c[0] = p1[0];
+ c[2] = p1[2];
+ r = 0;
+ return false;
+}
+
+static float distPtTri(const float* p, const float* a, const float* b, const float* c)
+{
+ float v0[3], v1[3], v2[3];
+ rcVsub(v0, c,a);
+ rcVsub(v1, b,a);
+ rcVsub(v2, p,a);
+
+ const float dot00 = vdot2(v0, v0);
+ const float dot01 = vdot2(v0, v1);
+ const float dot02 = vdot2(v0, v2);
+ const float dot11 = vdot2(v1, v1);
+ const float dot12 = vdot2(v1, v2);
+
+ // Compute barycentric coordinates
+ const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
+ const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // If point lies inside the triangle, return interpolated y-coord.
+ static const float EPS = 1e-4f;
+ if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
+ {
+ const float y = a[1] + v0[1]*u + v1[1]*v;
+ return fabsf(y-p[1]);
+ }
+ return FLT_MAX;
+}
+
+static float distancePtSeg(const float* pt, const float* p, const float* q)
+{
+ float pqx = q[0] - p[0];
+ float pqy = q[1] - p[1];
+ float pqz = q[2] - p[2];
+ float dx = pt[0] - p[0];
+ float dy = pt[1] - p[1];
+ float dz = pt[2] - p[2];
+ float d = pqx*pqx + pqy*pqy + pqz*pqz;
+ float t = pqx*dx + pqy*dy + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = p[0] + t*pqx - pt[0];
+ dy = p[1] + t*pqy - pt[1];
+ dz = p[2] + t*pqz - pt[2];
+
+ return dx*dx + dy*dy + dz*dz;
+}
+
+static float distancePtSeg2d(const float* pt, const float* p, const float* q)
+{
+ float pqx = q[0] - p[0];
+ float pqz = q[2] - p[2];
+ float dx = pt[0] - p[0];
+ float dz = pt[2] - p[2];
+ float d = pqx*pqx + pqz*pqz;
+ float t = pqx*dx + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = p[0] + t*pqx - pt[0];
+ dz = p[2] + t*pqz - pt[2];
+
+ return dx*dx + dz*dz;
+}
+
+static float distToTriMesh(const float* p, const float* verts, const int /*nverts*/, const int* tris, const int ntris)
+{
+ float dmin = FLT_MAX;
+ for (int i = 0; i < ntris; ++i)
+ {
+ const float* va = &verts[tris[i*4+0]*3];
+ const float* vb = &verts[tris[i*4+1]*3];
+ const float* vc = &verts[tris[i*4+2]*3];
+ float d = distPtTri(p, va,vb,vc);
+ if (d < dmin)
+ dmin = d;
+ }
+ if (dmin == FLT_MAX) return -1;
+ return dmin;
+}
+
+static float distToPoly(int nvert, const float* verts, const float* p)
+{
+
+ float dmin = FLT_MAX;
+ int i, j, c = 0;
+ for (i = 0, j = nvert-1; i < nvert; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
+ (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ dmin = rcMin(dmin, distancePtSeg2d(p, vj, vi));
+ }
+ return c ? -dmin : dmin;
+}
+
+
+static unsigned short getHeight(const float fx, const float fy, const float fz,
+ const float /*cs*/, const float ics, const float ch,
+ const rcHeightPatch& hp)
+{
+ int ix = (int)floorf(fx*ics + 0.01f);
+ int iz = (int)floorf(fz*ics + 0.01f);
+ ix = rcClamp(ix-hp.xmin, 0, hp.width);
+ iz = rcClamp(iz-hp.ymin, 0, hp.height);
+ unsigned short h = hp.data[ix+iz*hp.width];
+ if (h == RC_UNSET_HEIGHT)
+ {
+ // Special case when data might be bad.
+ // Find nearest neighbour pixel which has valid height.
+ const int off[8*2] = { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1};
+ float dmin = FLT_MAX;
+ for (int i = 0; i < 8; ++i)
+ {
+ const int nx = ix+off[i*2+0];
+ const int nz = iz+off[i*2+1];
+ if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue;
+ const unsigned short nh = hp.data[nx+nz*hp.width];
+ if (nh == RC_UNSET_HEIGHT) continue;
+
+ const float d = fabsf(nh*ch - fy);
+ if (d < dmin)
+ {
+ h = nh;
+ dmin = d;
+ }
+
+/* const float dx = (nx+0.5f)*cs - fx;
+ const float dz = (nz+0.5f)*cs - fz;
+ const float d = dx*dx+dz*dz;
+ if (d < dmin)
+ {
+ h = nh;
+ dmin = d;
+ } */
+ }
+ }
+ return h;
+}
+
+
+enum EdgeValues
+{
+ UNDEF = -1,
+ HULL = -2,
+};
+
+static int findEdge(const int* edges, int nedges, int s, int t)
+{
+ for (int i = 0; i < nedges; i++)
+ {
+ const int* e = &edges[i*4];
+ if ((e[0] == s && e[1] == t) || (e[0] == t && e[1] == s))
+ return i;
+ }
+ return UNDEF;
+}
+
+static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges, int s, int t, int l, int r)
+{
+ if (nedges >= maxEdges)
+ {
+ ctx->log(RC_LOG_ERROR, "addEdge: Too many edges (%d/%d).", nedges, maxEdges);
+ return UNDEF;
+ }
+
+ // Add edge if not already in the triangulation.
+ int e = findEdge(edges, nedges, s, t);
+ if (e == UNDEF)
+ {
+ int* e = &edges[nedges*4];
+ e[0] = s;
+ e[1] = t;
+ e[2] = l;
+ e[3] = r;
+ return nedges++;
+ }
+ else
+ {
+ return UNDEF;
+ }
+}
+
+static void updateLeftFace(int* e, int s, int t, int f)
+{
+ if (e[0] == s && e[1] == t && e[2] == UNDEF)
+ e[2] = f;
+ else if (e[1] == s && e[0] == t && e[3] == UNDEF)
+ e[3] = f;
+}
+
+static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d)
+{
+ const float a1 = vcross2(a, b, d);
+ const float a2 = vcross2(a, b, c);
+ if (a1*a2 < 0.0f)
+ {
+ float a3 = vcross2(c, d, a);
+ float a4 = a3 + a2 - a1;
+ if (a3 * a4 < 0.0f)
+ return 1;
+ }
+ return 0;
+}
+
+static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1, int t1)
+{
+ for (int i = 0; i < nedges; ++i)
+ {
+ const int s0 = edges[i*4+0];
+ const int t0 = edges[i*4+1];
+ // Same or connected edges do not overlap.
+ if (s0 == s1 || s0 == t1 || t0 == s1 || t0 == t1)
+ continue;
+ if (overlapSegSeg2d(&pts[s0*3],&pts[t0*3], &pts[s1*3],&pts[t1*3]))
+ return true;
+ }
+ return false;
+}
+
+static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e)
+{
+ static const float EPS = 1e-5f;
+
+ int* edge = &edges[e*4];
+
+ // Cache s and t.
+ int s,t;
+ if (edge[2] == UNDEF)
+ {
+ s = edge[0];
+ t = edge[1];
+ }
+ else if (edge[3] == UNDEF)
+ {
+ s = edge[1];
+ t = edge[0];
+ }
+ else
+ {
+ // Edge already completed.
+ return;
+ }
+
+ // Find best point on left of edge.
+ int pt = npts;
+ float c[3] = {0,0,0};
+ float r = -1;
+ for (int u = 0; u < npts; ++u)
+ {
+ if (u == s || u == t) continue;
+ if (vcross2(&pts[s*3], &pts[t*3], &pts[u*3]) > EPS)
+ {
+ if (r < 0)
+ {
+ // The circle is not updated yet, do it now.
+ pt = u;
+ circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
+ continue;
+ }
+ const float d = vdist2(c, &pts[u*3]);
+ const float tol = 0.001f;
+ if (d > r*(1+tol))
+ {
+ // Outside current circumcircle, skip.
+ continue;
+ }
+ else if (d < r*(1-tol))
+ {
+ // Inside safe circumcircle, update circle.
+ pt = u;
+ circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
+ }
+ else
+ {
+ // Inside epsilon circum circle, do extra tests to make sure the edge is valid.
+ // s-u and t-u cannot overlap with s-pt nor t-pt if they exists.
+ if (overlapEdges(pts, edges, nedges, s,u))
+ continue;
+ if (overlapEdges(pts, edges, nedges, t,u))
+ continue;
+ // Edge is valid.
+ pt = u;
+ circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
+ }
+ }
+ }
+
+ // Add new triangle or update edge info if s-t is on hull.
+ if (pt < npts)
+ {
+ // Update face information of edge being completed.
+ updateLeftFace(&edges[e*4], s, t, nfaces);
+
+ // Add new edge or update face info of old edge.
+ e = findEdge(edges, nedges, pt, s);
+ if (e == UNDEF)
+ addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, UNDEF);
+ else
+ updateLeftFace(&edges[e*4], pt, s, nfaces);
+
+ // Add new edge or update face info of old edge.
+ e = findEdge(edges, nedges, t, pt);
+ if (e == UNDEF)
+ addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF);
+ else
+ updateLeftFace(&edges[e*4], t, pt, nfaces);
+
+ nfaces++;
+ }
+ else
+ {
+ updateLeftFace(&edges[e*4], s, t, HULL);
+ }
+}
+
+static void delaunayHull(rcContext* ctx, const int npts, const float* pts,
+ const int nhull, const int* hull,
+ rcIntArray& tris, rcIntArray& edges)
+{
+ int nfaces = 0;
+ int nedges = 0;
+ const int maxEdges = npts*10;
+ edges.resize(maxEdges*4);
+
+ for (int i = 0, j = nhull-1; i < nhull; j=i++)
+ addEdge(ctx, &edges[0], nedges, maxEdges, hull[j],hull[i], HULL, UNDEF);
+
+ int currentEdge = 0;
+ while (currentEdge < nedges)
+ {
+ if (edges[currentEdge*4+2] == UNDEF)
+ completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge);
+ if (edges[currentEdge*4+3] == UNDEF)
+ completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge);
+ currentEdge++;
+ }
+
+ // Create tris
+ tris.resize(nfaces*4);
+ for (int i = 0; i < nfaces*4; ++i)
+ tris[i] = -1;
+
+ for (int i = 0; i < nedges; ++i)
+ {
+ const int* e = &edges[i*4];
+ if (e[3] >= 0)
+ {
+ // Left face
+ int* t = &tris[e[3]*4];
+ if (t[0] == -1)
+ {
+ t[0] = e[0];
+ t[1] = e[1];
+ }
+ else if (t[0] == e[1])
+ t[2] = e[0];
+ else if (t[1] == e[0])
+ t[2] = e[1];
+ }
+ if (e[2] >= 0)
+ {
+ // Right
+ int* t = &tris[e[2]*4];
+ if (t[0] == -1)
+ {
+ t[0] = e[1];
+ t[1] = e[0];
+ }
+ else if (t[0] == e[0])
+ t[2] = e[1];
+ else if (t[1] == e[1])
+ t[2] = e[0];
+ }
+ }
+
+ for (int i = 0; i < tris.size()/4; ++i)
+ {
+ int* t = &tris[i*4];
+ if (t[0] == -1 || t[1] == -1 || t[2] == -1)
+ {
+ ctx->log(RC_LOG_WARNING, "delaunayHull: Removing dangling face %d [%d,%d,%d].", i, t[0],t[1],t[2]);
+ t[0] = tris[tris.size()-4];
+ t[1] = tris[tris.size()-3];
+ t[2] = tris[tris.size()-2];
+ t[3] = tris[tris.size()-1];
+ tris.resize(tris.size()-4);
+ --i;
+ }
+ }
+}
+
+
+inline float getJitterX(const int i)
+{
+ return (((i * 0x8da6b343) & 0xffff) / 65535.0f * 2.0f) - 1.0f;
+}
+
+inline float getJitterY(const int i)
+{
+ return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f;
+}
+
+static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
+ const float sampleDist, const float sampleMaxError,
+ const rcCompactHeightfield& chf, const rcHeightPatch& hp,
+ float* verts, int& nverts, rcIntArray& tris,
+ rcIntArray& edges, rcIntArray& samples)
+{
+ static const int MAX_VERTS = 127;
+ static const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts).
+ static const int MAX_VERTS_PER_EDGE = 32;
+ float edge[(MAX_VERTS_PER_EDGE+1)*3];
+ int hull[MAX_VERTS];
+ int nhull = 0;
+
+ nverts = 0;
+
+ for (int i = 0; i < nin; ++i)
+ rcVcopy(&verts[i*3], &in[i*3]);
+ nverts = nin;
+
+ const float cs = chf.cs;
+ const float ics = 1.0f/cs;
+
+ // Tessellate outlines.
+ // This is done in separate pass in order to ensure
+ // seamless height values across the ply boundaries.
+ if (sampleDist > 0)
+ {
+ for (int i = 0, j = nin-1; i < nin; j=i++)
+ {
+ const float* vj = &in[j*3];
+ const float* vi = &in[i*3];
+ bool swapped = false;
+ // Make sure the segments are always handled in same order
+ // using lexological sort or else there will be seams.
+ if (fabsf(vj[0]-vi[0]) < 1e-6f)
+ {
+ if (vj[2] > vi[2])
+ {
+ rcSwap(vj,vi);
+ swapped = true;
+ }
+ }
+ else
+ {
+ if (vj[0] > vi[0])
+ {
+ rcSwap(vj,vi);
+ swapped = true;
+ }
+ }
+ // Create samples along the edge.
+ float dx = vi[0] - vj[0];
+ float dy = vi[1] - vj[1];
+ float dz = vi[2] - vj[2];
+ float d = sqrtf(dx*dx + dz*dz);
+ int nn = 1 + (int)floorf(d/sampleDist);
+ if (nn >= MAX_VERTS_PER_EDGE) nn = MAX_VERTS_PER_EDGE-1;
+ if (nverts+nn >= MAX_VERTS)
+ nn = MAX_VERTS-1-nverts;
+
+ for (int k = 0; k <= nn; ++k)
+ {
+ float u = (float)k/(float)nn;
+ float* pos = &edge[k*3];
+ pos[0] = vj[0] + dx*u;
+ pos[1] = vj[1] + dy*u;
+ pos[2] = vj[2] + dz*u;
+ pos[1] = getHeight(pos[0],pos[1],pos[2], cs, ics, chf.ch, hp)*chf.ch;
+ }
+ // Simplify samples.
+ int idx[MAX_VERTS_PER_EDGE] = {0,nn};
+ int nidx = 2;
+ for (int k = 0; k < nidx-1; )
+ {
+ const int a = idx[k];
+ const int b = idx[k+1];
+ const float* va = &edge[a*3];
+ const float* vb = &edge[b*3];
+ // Find maximum deviation along the segment.
+ float maxd = 0;
+ int maxi = -1;
+ for (int m = a+1; m < b; ++m)
+ {
+ float d = distancePtSeg(&edge[m*3],va,vb);
+ if (d > maxd)
+ {
+ maxd = d;
+ maxi = m;
+ }
+ }
+ // If the max deviation is larger than accepted error,
+ // add new point, else continue to next segment.
+ if (maxi != -1 && maxd > rcSqr(sampleMaxError))
+ {
+ for (int m = nidx; m > k; --m)
+ idx[m] = idx[m-1];
+ idx[k+1] = maxi;
+ nidx++;
+ }
+ else
+ {
+ ++k;
+ }
+ }
+
+ hull[nhull++] = j;
+ // Add new vertices.
+ if (swapped)
+ {
+ for (int k = nidx-2; k > 0; --k)
+ {
+ rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
+ hull[nhull++] = nverts;
+ nverts++;
+ }
+ }
+ else
+ {
+ for (int k = 1; k < nidx-1; ++k)
+ {
+ rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
+ hull[nhull++] = nverts;
+ nverts++;
+ }
+ }
+ }
+ }
+
+
+ // Tessellate the base mesh.
+ edges.resize(0);
+ tris.resize(0);
+
+ delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
+
+ if (tris.size() == 0)
+ {
+ // Could not triangulate the poly, make sure there is some valid data there.
+ ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data.");
+ for (int i = 2; i < nverts; ++i)
+ {
+ tris.push(0);
+ tris.push(i-1);
+ tris.push(i);
+ tris.push(0);
+ }
+ return true;
+ }
+
+ if (sampleDist > 0)
+ {
+ // Create sample locations in a grid.
+ float bmin[3], bmax[3];
+ rcVcopy(bmin, in);
+ rcVcopy(bmax, in);
+ for (int i = 1; i < nin; ++i)
+ {
+ rcVmin(bmin, &in[i*3]);
+ rcVmax(bmax, &in[i*3]);
+ }
+ int x0 = (int)floorf(bmin[0]/sampleDist);
+ int x1 = (int)ceilf(bmax[0]/sampleDist);
+ int z0 = (int)floorf(bmin[2]/sampleDist);
+ int z1 = (int)ceilf(bmax[2]/sampleDist);
+ samples.resize(0);
+ for (int z = z0; z < z1; ++z)
+ {
+ for (int x = x0; x < x1; ++x)
+ {
+ float pt[3];
+ pt[0] = x*sampleDist;
+ pt[1] = (bmax[1]+bmin[1])*0.5f;
+ pt[2] = z*sampleDist;
+ // Make sure the samples are not too close to the edges.
+ if (distToPoly(nin,in,pt) > -sampleDist/2) continue;
+ samples.push(x);
+ samples.push(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, hp));
+ samples.push(z);
+ samples.push(0); // Not added
+ }
+ }
+
+ // Add the samples starting from the one that has the most
+ // error. The procedure stops when all samples are added
+ // or when the max error is within treshold.
+ const int nsamples = samples.size()/4;
+ for (int iter = 0; iter < nsamples; ++iter)
+ {
+ if (nverts >= MAX_VERTS)
+ break;
+
+ // Find sample with most error.
+ float bestpt[3] = {0,0,0};
+ float bestd = 0;
+ int besti = -1;
+ for (int i = 0; i < nsamples; ++i)
+ {
+ const int* s = &samples[i*4];
+ if (s[3]) continue; // skip added.
+ float pt[3];
+ // The sample location is jittered to get rid of some bad triangulations
+ // which are cause by symmetrical data from the grid structure.
+ pt[0] = s[0]*sampleDist + getJitterX(i)*cs*0.1f;
+ pt[1] = s[1]*chf.ch;
+ pt[2] = s[2]*sampleDist + getJitterY(i)*cs*0.1f;
+ float d = distToTriMesh(pt, verts, nverts, &tris[0], tris.size()/4);
+ if (d < 0) continue; // did not hit the mesh.
+ if (d > bestd)
+ {
+ bestd = d;
+ besti = i;
+ rcVcopy(bestpt,pt);
+ }
+ }
+ // If the max error is within accepted threshold, stop tesselating.
+ if (bestd <= sampleMaxError || besti == -1)
+ break;
+ // Mark sample as added.
+ samples[besti*4+3] = 1;
+ // Add the new sample point.
+ rcVcopy(&verts[nverts*3],bestpt);
+ nverts++;
+
+ // Create new triangulation.
+ // TODO: Incremental add instead of full rebuild.
+ edges.resize(0);
+ tris.resize(0);
+ delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
+ }
+ }
+
+ const int ntris = tris.size()/4;
+ if (ntris > MAX_TRIS)
+ {
+ tris.resize(MAX_TRIS*4);
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS);
+ }
+
+ return true;
+}
+
+static void getHeightData(const rcCompactHeightfield& chf,
+ const unsigned short* poly, const int npoly,
+ const unsigned short* verts,
+ rcHeightPatch& hp, rcIntArray& stack)
+{
+ // Floodfill the heightfield to get 2D height data,
+ // starting at vertex locations as seeds.
+
+ memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
+
+ stack.resize(0);
+
+ static const int offset[9*2] =
+ {
+ 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0,
+ };
+
+ // Use poly vertices as seed points for the flood fill.
+ for (int j = 0; j < npoly; ++j)
+ {
+ int cx = 0, cz = 0, ci =-1;
+ int dmin = RC_UNSET_HEIGHT;
+ for (int k = 0; k < 9; ++k)
+ {
+ const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0];
+ const int ay = (int)verts[poly[j]*3+1];
+ const int az = (int)verts[poly[j]*3+2] + offset[k*2+1];
+ if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
+ az < hp.ymin || az >= hp.ymin+hp.height)
+ continue;
+
+ const rcCompactCell& c = chf.cells[ax+az*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ int d = rcAbs(ay - (int)s.y);
+ if (d < dmin)
+ {
+ cx = ax;
+ cz = az;
+ ci = i;
+ dmin = d;
+ }
+ }
+ }
+ if (ci != -1)
+ {
+ stack.push(cx);
+ stack.push(cz);
+ stack.push(ci);
+ }
+ }
+
+ // Find center of the polygon using flood fill.
+ int pcx = 0, pcz = 0;
+ for (int j = 0; j < npoly; ++j)
+ {
+ pcx += (int)verts[poly[j]*3+0];
+ pcz += (int)verts[poly[j]*3+2];
+ }
+ pcx /= npoly;
+ pcz /= npoly;
+
+ for (int i = 0; i < stack.size(); i += 3)
+ {
+ int cx = stack[i+0];
+ int cy = stack[i+1];
+ int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+ }
+
+ while (stack.size() > 0)
+ {
+ int ci = stack.pop();
+ int cy = stack.pop();
+ int cx = stack.pop();
+
+ // Check if close to center of the polygon.
+ if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1)
+ {
+ stack.resize(0);
+ stack.push(cx);
+ stack.push(cy);
+ stack.push(ci);
+ break;
+ }
+
+ const rcCompactSpan& cs = chf.spans[ci];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
+
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+
+ if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
+ ay < hp.ymin || ay >= (hp.ymin+hp.height))
+ continue;
+
+ if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0)
+ continue;
+
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir);
+
+ int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+
+ memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
+
+ // Mark start locations.
+ for (int i = 0; i < stack.size(); i += 3)
+ {
+ int cx = stack[i+0];
+ int cy = stack[i+1];
+ int ci = stack[i+2];
+ int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
+ const rcCompactSpan& cs = chf.spans[ci];
+ hp.data[idx] = cs.y;
+ }
+
+ static const int RETRACT_SIZE = 256;
+ int head = 0;
+
+ while (head*3 < stack.size())
+ {
+ int cx = stack[head*3+0];
+ int cy = stack[head*3+1];
+ int ci = stack[head*3+2];
+ head++;
+ if (head >= RETRACT_SIZE)
+ {
+ head = 0;
+ if (stack.size() > RETRACT_SIZE*3)
+ memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3));
+ stack.resize(stack.size()-RETRACT_SIZE*3);
+ }
+
+ const rcCompactSpan& cs = chf.spans[ci];
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
+
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+
+ if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
+ ay < hp.ymin || ay >= (hp.ymin+hp.height))
+ continue;
+
+ if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != RC_UNSET_HEIGHT)
+ continue;
+
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir);
+
+ const rcCompactSpan& as = chf.spans[ai];
+ int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
+ hp.data[idx] = as.y;
+
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+
+}
+
+static unsigned char getEdgeFlags(const float* va, const float* vb,
+ const float* vpoly, const int npoly)
+{
+ // Return true if edge (va,vb) is part of the polygon.
+ static const float thrSqr = rcSqr(0.001f);
+ for (int i = 0, j = npoly-1; i < npoly; j=i++)
+ {
+ if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr &&
+ distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr)
+ return 1;
+ }
+ return 0;
+}
+
+static unsigned char getTriFlags(const float* va, const float* vb, const float* vc,
+ const float* vpoly, const int npoly)
+{
+ unsigned char flags = 0;
+ flags |= getEdgeFlags(va,vb,vpoly,npoly) << 0;
+ flags |= getEdgeFlags(vb,vc,vpoly,npoly) << 2;
+ flags |= getEdgeFlags(vc,va,vpoly,npoly) << 4;
+ return flags;
+}
+
+
+
+bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
+ const float sampleDist, const float sampleMaxError,
+ rcPolyMeshDetail& dmesh)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
+
+ if (mesh.nverts == 0 || mesh.npolys == 0)
+ return true;
+
+ const int nvp = mesh.nvp;
+ const float cs = mesh.cs;
+ const float ch = mesh.ch;
+ const float* orig = mesh.bmin;
+
+ rcIntArray edges(64);
+ rcIntArray tris(512);
+ rcIntArray stack(512);
+ rcIntArray samples(512);
+ float verts[256*3];
+ rcHeightPatch hp;
+ int nPolyVerts = 0;
+ int maxhw = 0, maxhh = 0;
+
+ rcScopedDelete<int> bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP);
+ if (!bounds)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4);
+ return false;
+ }
+ rcScopedDelete<float> poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP);
+ if (!poly)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3);
+ return false;
+ }
+
+ // Find max size for a polygon area.
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ const unsigned short* p = &mesh.polys[i*nvp*2];
+ int& xmin = bounds[i*4+0];
+ int& xmax = bounds[i*4+1];
+ int& ymin = bounds[i*4+2];
+ int& ymax = bounds[i*4+3];
+ xmin = chf.width;
+ xmax = 0;
+ ymin = chf.height;
+ ymax = 0;
+ for (int j = 0; j < nvp; ++j)
+ {
+ if(p[j] == RC_MESH_NULL_IDX) break;
+ const unsigned short* v = &mesh.verts[p[j]*3];
+ xmin = rcMin(xmin, (int)v[0]);
+ xmax = rcMax(xmax, (int)v[0]);
+ ymin = rcMin(ymin, (int)v[2]);
+ ymax = rcMax(ymax, (int)v[2]);
+ nPolyVerts++;
+ }
+ xmin = rcMax(0,xmin-1);
+ xmax = rcMin(chf.width,xmax+1);
+ ymin = rcMax(0,ymin-1);
+ ymax = rcMin(chf.height,ymax+1);
+ if (xmin >= xmax || ymin >= ymax) continue;
+ maxhw = rcMax(maxhw, xmax-xmin);
+ maxhh = rcMax(maxhh, ymax-ymin);
+ }
+
+ hp.data = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxhw*maxhh, RC_ALLOC_TEMP);
+ if (!hp.data)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh);
+ return false;
+ }
+
+ dmesh.nmeshes = mesh.npolys;
+ dmesh.nverts = 0;
+ dmesh.ntris = 0;
+ dmesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*dmesh.nmeshes*4, RC_ALLOC_PERM);
+ if (!dmesh.meshes)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4);
+ return false;
+ }
+
+ int vcap = nPolyVerts+nPolyVerts/2;
+ int tcap = vcap*2;
+
+ dmesh.nverts = 0;
+ dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
+ if (!dmesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3);
+ return false;
+ }
+ dmesh.ntris = 0;
+ dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char*)*tcap*4, RC_ALLOC_PERM);
+ if (!dmesh.tris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4);
+ return false;
+ }
+
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ const unsigned short* p = &mesh.polys[i*nvp*2];
+
+ // Store polygon vertices for processing.
+ int npoly = 0;
+ for (int j = 0; j < nvp; ++j)
+ {
+ if(p[j] == RC_MESH_NULL_IDX) break;
+ const unsigned short* v = &mesh.verts[p[j]*3];
+ poly[j*3+0] = v[0]*cs;
+ poly[j*3+1] = v[1]*ch;
+ poly[j*3+2] = v[2]*cs;
+ npoly++;
+ }
+
+ // Get the height data from the area of the polygon.
+ hp.xmin = bounds[i*4+0];
+ hp.ymin = bounds[i*4+2];
+ hp.width = bounds[i*4+1]-bounds[i*4+0];
+ hp.height = bounds[i*4+3]-bounds[i*4+2];
+ getHeightData(chf, p, npoly, mesh.verts, hp, stack);
+
+ // Build detail mesh.
+ int nverts = 0;
+ if (!buildPolyDetail(ctx, poly, npoly,
+ sampleDist, sampleMaxError,
+ chf, hp, verts, nverts, tris,
+ edges, samples))
+ {
+ return false;
+ }
+
+ // Move detail verts to world space.
+ for (int j = 0; j < nverts; ++j)
+ {
+ verts[j*3+0] += orig[0];
+ verts[j*3+1] += orig[1] + chf.ch; // Is this offset necessary?
+ verts[j*3+2] += orig[2];
+ }
+ // Offset poly too, will be used to flag checking.
+ for (int j = 0; j < npoly; ++j)
+ {
+ poly[j*3+0] += orig[0];
+ poly[j*3+1] += orig[1];
+ poly[j*3+2] += orig[2];
+ }
+
+ // Store detail submesh.
+ const int ntris = tris.size()/4;
+
+ dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts;
+ dmesh.meshes[i*4+1] = (unsigned int)nverts;
+ dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris;
+ dmesh.meshes[i*4+3] = (unsigned int)ntris;
+
+ // Store vertices, allocate more memory if necessary.
+ if (dmesh.nverts+nverts > vcap)
+ {
+ while (dmesh.nverts+nverts > vcap)
+ vcap += 256;
+
+ float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
+ if (!newv)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3);
+ return false;
+ }
+ if (dmesh.nverts)
+ memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts);
+ rcFree(dmesh.verts);
+ dmesh.verts = newv;
+ }
+ for (int j = 0; j < nverts; ++j)
+ {
+ dmesh.verts[dmesh.nverts*3+0] = verts[j*3+0];
+ dmesh.verts[dmesh.nverts*3+1] = verts[j*3+1];
+ dmesh.verts[dmesh.nverts*3+2] = verts[j*3+2];
+ dmesh.nverts++;
+ }
+
+ // Store triangles, allocate more memory if necessary.
+ if (dmesh.ntris+ntris > tcap)
+ {
+ while (dmesh.ntris+ntris > tcap)
+ tcap += 256;
+ unsigned char* newt = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM);
+ if (!newt)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4);
+ return false;
+ }
+ if (dmesh.ntris)
+ memcpy(newt, dmesh.tris, sizeof(unsigned char)*4*dmesh.ntris);
+ rcFree(dmesh.tris);
+ dmesh.tris = newt;
+ }
+ for (int j = 0; j < ntris; ++j)
+ {
+ const int* t = &tris[j*4];
+ dmesh.tris[dmesh.ntris*4+0] = (unsigned char)t[0];
+ dmesh.tris[dmesh.ntris*4+1] = (unsigned char)t[1];
+ dmesh.tris[dmesh.ntris*4+2] = (unsigned char)t[2];
+ dmesh.tris[dmesh.ntris*4+3] = getTriFlags(&verts[t[0]*3], &verts[t[1]*3], &verts[t[2]*3], poly, npoly);
+ dmesh.ntris++;
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
+
+ return true;
+}
+
+bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
+
+ int maxVerts = 0;
+ int maxTris = 0;
+ int maxMeshes = 0;
+
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ if (!meshes[i]) continue;
+ maxVerts += meshes[i]->nverts;
+ maxTris += meshes[i]->ntris;
+ maxMeshes += meshes[i]->nmeshes;
+ }
+
+ mesh.nmeshes = 0;
+ mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM);
+ if (!mesh.meshes)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4);
+ return false;
+ }
+
+ mesh.ntris = 0;
+ mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM);
+ if (!mesh.tris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4);
+ return false;
+ }
+
+ mesh.nverts = 0;
+ mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM);
+ if (!mesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3);
+ return false;
+ }
+
+ // Merge datas.
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ rcPolyMeshDetail* dm = meshes[i];
+ if (!dm) continue;
+ for (int j = 0; j < dm->nmeshes; ++j)
+ {
+ unsigned int* dst = &mesh.meshes[mesh.nmeshes*4];
+ unsigned int* src = &dm->meshes[j*4];
+ dst[0] = (unsigned int)mesh.nverts+src[0];
+ dst[1] = src[1];
+ dst[2] = (unsigned int)mesh.ntris+src[2];
+ dst[3] = src[3];
+ mesh.nmeshes++;
+ }
+
+ for (int k = 0; k < dm->nverts; ++k)
+ {
+ rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]);
+ mesh.nverts++;
+ }
+ for (int k = 0; k < dm->ntris; ++k)
+ {
+ mesh.tris[mesh.ntris*4+0] = dm->tris[k*4+0];
+ mesh.tris[mesh.ntris*4+1] = dm->tris[k*4+1];
+ mesh.tris[mesh.ntris*4+2] = dm->tris[k*4+2];
+ mesh.tris[mesh.ntris*4+3] = dm->tris[k*4+3];
+ mesh.ntris++;
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
+
+ return true;
+}
+
diff --git a/dep/recastnavigation/Recast/RecastRasterization.cpp b/dep/recastnavigation/Recast/RecastRasterization.cpp
new file mode 100644
index 0000000..71adfb6
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastRasterization.cpp
@@ -0,0 +1,360 @@
+//
+// 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.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax)
+{
+ 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;
+ overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
+ return overlap;
+}
+
+inline bool overlapInterval(unsigned short amin, unsigned short amax,
+ unsigned short bmin, unsigned short bmax)
+{
+ if (amax < bmin) return false;
+ if (amin > bmax) return false;
+ return true;
+}
+
+
+static rcSpan* allocSpan(rcHeightfield& hf)
+{
+ // If running out of memory, allocate new page and update the freelist.
+ if (!hf.freelist || !hf.freelist->next)
+ {
+ // Create new page.
+ // Allocate memory for the new pool.
+ rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
+ if (!pool) return 0;
+ pool->next = 0;
+ // Add the pool into the list of pools.
+ pool->next = hf.pools;
+ hf.pools = pool;
+ // Add new items to the free list.
+ rcSpan* freelist = hf.freelist;
+ rcSpan* head = &pool->items[0];
+ rcSpan* it = &pool->items[RC_SPANS_PER_POOL];
+ do
+ {
+ --it;
+ it->next = freelist;
+ freelist = it;
+ }
+ while (it != head);
+ hf.freelist = it;
+ }
+
+ // Pop item from in front of the free list.
+ rcSpan* it = hf.freelist;
+ hf.freelist = hf.freelist->next;
+ return it;
+}
+
+static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
+{
+ if (!ptr) return;
+ // Add the node in front of the free list.
+ ptr->next = hf.freelist;
+ hf.freelist = ptr;
+}
+
+static void addSpan(rcHeightfield& hf, const int x, const int y,
+ const unsigned short smin, const unsigned short smax,
+ const unsigned char area, const int flagMergeThr)
+{
+
+ int idx = x + y*hf.width;
+
+ rcSpan* s = allocSpan(hf);
+ s->smin = smin;
+ s->smax = smax;
+ s->area = area;
+ s->next = 0;
+
+ // Empty cell, add he first span.
+ if (!hf.spans[idx])
+ {
+ hf.spans[idx] = s;
+ return;
+ }
+ rcSpan* prev = 0;
+ rcSpan* cur = hf.spans[idx];
+
+ // Insert and merge spans.
+ while (cur)
+ {
+ if (cur->smin > s->smax)
+ {
+ // Current span is further than the new span, break.
+ break;
+ }
+ else if (cur->smax < s->smin)
+ {
+ // Current span is before the new span advance.
+ prev = cur;
+ cur = cur->next;
+ }
+ else
+ {
+ // Merge spans.
+ if (cur->smin < s->smin)
+ s->smin = cur->smin;
+ if (cur->smax > s->smax)
+ s->smax = cur->smax;
+
+ // Merge flags.
+ if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr)
+ s->area = rcMax(s->area, cur->area);
+
+ // Remove current span.
+ rcSpan* next = cur->next;
+ freeSpan(hf, cur);
+ if (prev)
+ prev->next = next;
+ else
+ hf.spans[idx] = next;
+ cur = next;
+ }
+ }
+
+ // Insert new span.
+ if (prev)
+ {
+ s->next = prev->next;
+ prev->next = s;
+ }
+ else
+ {
+ s->next = hf.spans[idx];
+ hf.spans[idx] = s;
+ }
+}
+
+void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y,
+ const unsigned short smin, const unsigned short smax,
+ const unsigned char area, const int flagMergeThr)
+{
+// rcAssert(ctx);
+ addSpan(hf, x,y, smin, smax, area, flagMergeThr);
+}
+
+static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd)
+{
+ float d[12];
+ for (int i = 0; i < n; ++i)
+ d[i] = pnx*in[i*3+0] + pnz*in[i*3+2] + pd;
+
+ int m = 0;
+ for (int i = 0, j = n-1; i < n; j=i, ++i)
+ {
+ bool ina = d[j] >= 0;
+ bool inb = d[i] >= 0;
+ if (ina != inb)
+ {
+ float s = d[j] / (d[j] - d[i]);
+ out[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s;
+ out[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s;
+ out[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s;
+ m++;
+ }
+ if (inb)
+ {
+ out[m*3+0] = in[i*3+0];
+ out[m*3+1] = in[i*3+1];
+ out[m*3+2] = in[i*3+2];
+ m++;
+ }
+ }
+ return m;
+}
+
+static void rasterizeTri(const float* v0, const float* v1, const float* v2,
+ const unsigned char area, rcHeightfield& hf,
+ const float* bmin, const float* bmax,
+ const float cs, const float ics, const float ich,
+ const int flagMergeThr)
+{
+ const int w = hf.width;
+ const int h = hf.height;
+ float tmin[3], tmax[3];
+ const float by = bmax[1] - bmin[1];
+
+ // Calculate the bounding box of the triangle.
+ rcVcopy(tmin, v0);
+ rcVcopy(tmax, v0);
+ rcVmin(tmin, v1);
+ rcVmin(tmin, v2);
+ rcVmax(tmax, v1);
+ rcVmax(tmax, v2);
+
+ // If the triangle does not touch the bbox of the heightfield, skip the triagle.
+ if (!overlapBounds(bmin, bmax, tmin, tmax))
+ return;
+
+ // Calculate the footpring of the triangle on the grid.
+ int x0 = (int)((tmin[0] - bmin[0])*ics);
+ int y0 = (int)((tmin[2] - bmin[2])*ics);
+ int x1 = (int)((tmax[0] - bmin[0])*ics);
+ int y1 = (int)((tmax[2] - bmin[2])*ics);
+ x0 = rcClamp(x0, 0, w-1);
+ y0 = rcClamp(y0, 0, h-1);
+ x1 = rcClamp(x1, 0, w-1);
+ y1 = rcClamp(y1, 0, h-1);
+
+ // Clip the triangle into all grid cells it touches.
+ float in[7*3], out[7*3], inrow[7*3];
+
+ for (int y = y0; y <= y1; ++y)
+ {
+ // Clip polygon to row.
+ rcVcopy(&in[0], v0);
+ rcVcopy(&in[1*3], v1);
+ rcVcopy(&in[2*3], v2);
+ int nvrow = 3;
+ const float cz = bmin[2] + y*cs;
+ nvrow = clipPoly(in, nvrow, out, 0, 1, -cz);
+ if (nvrow < 3) continue;
+ nvrow = clipPoly(out, nvrow, inrow, 0, -1, cz+cs);
+ if (nvrow < 3) continue;
+
+ for (int x = x0; x <= x1; ++x)
+ {
+ // Clip polygon to column.
+ int nv = nvrow;
+ const float cx = bmin[0] + x*cs;
+ nv = clipPoly(inrow, nv, out, 1, 0, -cx);
+ if (nv < 3) continue;
+ nv = clipPoly(out, nv, in, -1, 0, cx+cs);
+ if (nv < 3) continue;
+
+ // Calculate min and max of the span.
+ float smin = in[1], smax = in[1];
+ for (int i = 1; i < nv; ++i)
+ {
+ smin = rcMin(smin, in[i*3+1]);
+ smax = rcMax(smax, in[i*3+1]);
+ }
+ smin -= bmin[1];
+ smax -= bmin[1];
+ // Skip the span if it is outside the heightfield bbox
+ if (smax < 0.0f) continue;
+ if (smin > by) continue;
+ // Clamp the span to the heightfield bbox.
+ if (smin < 0.0f) smin = 0;
+ if (smax > by) smax = by;
+
+ // Snap the span to the heightfield height grid.
+ unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT);
+ unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT);
+
+ addSpan(hf, x, y, ismin, ismax, area, flagMergeThr);
+ }
+ }
+}
+
+void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
+ const unsigned char area, rcHeightfield& solid,
+ const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
+
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
+ const int* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ // Rasterize triangles.
+ for (int i = 0; i < nt; ++i)
+ {
+ const float* v0 = &verts[tris[i*3+0]*3];
+ const float* v1 = &verts[tris[i*3+1]*3];
+ const float* v2 = &verts[tris[i*3+2]*3];
+ // Rasterize.
+ rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+ }
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
+
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
+ const unsigned short* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ // Rasterize triangles.
+ for (int i = 0; i < nt; ++i)
+ {
+ const float* v0 = &verts[tris[i*3+0]*3];
+ const float* v1 = &verts[tris[i*3+1]*3];
+ const float* v2 = &verts[tris[i*3+2]*3];
+ // Rasterize.
+ rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+ }
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
+
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ // Rasterize triangles.
+ for (int i = 0; i < nt; ++i)
+ {
+ const float* v0 = &verts[(i*3+0)*3];
+ const float* v1 = &verts[(i*3+1)*3];
+ const float* v2 = &verts[(i*3+2)*3];
+ // Rasterize.
+ rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+ }
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
diff --git a/dep/recastnavigation/Recast/RecastRegion.cpp b/dep/recastnavigation/Recast/RecastRegion.cpp
new file mode 100644
index 0000000..6ad9fa5
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastRegion.cpp
@@ -0,0 +1,1283 @@
+//
+// 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 <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+#include <new>
+
+
+static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ // Init distance and points.
+ for (int i = 0; i < chf.spanCount; ++i)
+ src[i] = 0xffff;
+
+ // Mark boundary cells.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ const unsigned char area = chf.areas[i];
+
+ int nc = 0;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ if (area == chf.areas[ai])
+ nc++;
+ }
+ }
+ if (nc != 4)
+ src[i] = 0;
+ }
+ }
+ }
+
+
+ // Pass 1
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ // (-1,0)
+ const int ax = x + rcGetDirOffsetX(0);
+ const int ay = y + rcGetDirOffsetY(0);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (-1,-1)
+ if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(3);
+ const int aay = ay + rcGetDirOffsetY(3);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
+ {
+ // (0,-1)
+ const int ax = x + rcGetDirOffsetX(3);
+ const int ay = y + rcGetDirOffsetY(3);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (1,-1)
+ if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(2);
+ const int aay = ay + rcGetDirOffsetY(2);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ }
+ }
+ }
+
+ // Pass 2
+ for (int y = h-1; y >= 0; --y)
+ {
+ for (int x = w-1; x >= 0; --x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
+ {
+ // (1,0)
+ const int ax = x + rcGetDirOffsetX(2);
+ const int ay = y + rcGetDirOffsetY(2);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (1,1)
+ if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(1);
+ const int aay = ay + rcGetDirOffsetY(1);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
+ {
+ // (0,1)
+ const int ax = x + rcGetDirOffsetX(1);
+ const int ay = y + rcGetDirOffsetY(1);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (-1,1)
+ if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(0);
+ const int aay = ay + rcGetDirOffsetY(0);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ }
+ }
+ }
+
+ maxDist = 0;
+ for (int i = 0; i < chf.spanCount; ++i)
+ maxDist = rcMax(src[i], maxDist);
+
+}
+
+static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr,
+ unsigned short* src, unsigned short* dst)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ thr *= 2;
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ const unsigned short cd = src[i];
+ if (cd <= thr)
+ {
+ dst[i] = cd;
+ continue;
+ }
+
+ int d = (int)cd;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ d += (int)src[ai];
+
+ const rcCompactSpan& as = chf.spans[ai];
+ const int dir2 = (dir+1) & 0x3;
+ if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir2);
+ const int ay2 = ay + rcGetDirOffsetY(dir2);
+ const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
+ d += (int)src[ai2];
+ }
+ else
+ {
+ d += cd;
+ }
+ }
+ else
+ {
+ d += cd*2;
+ }
+ }
+ dst[i] = (unsigned short)((d+5)/9);
+ }
+ }
+ }
+ return dst;
+}
+
+
+static bool floodRegion(int x, int y, int i,
+ unsigned short level, unsigned short r,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, unsigned short* srcDist,
+ rcIntArray& stack)
+{
+ const int w = chf.width;
+
+ const unsigned char area = chf.areas[i];
+
+ // Flood fill mark region.
+ stack.resize(0);
+ stack.push((int)x);
+ stack.push((int)y);
+ stack.push((int)i);
+ srcReg[i] = r;
+ srcDist[i] = 0;
+
+ unsigned short lev = level >= 2 ? level-2 : 0;
+ int count = 0;
+
+ while (stack.size() > 0)
+ {
+ int ci = stack.pop();
+ int cy = stack.pop();
+ int cx = stack.pop();
+
+ const rcCompactSpan& cs = chf.spans[ci];
+
+ // Check if any of the neighbours already have a valid region set.
+ unsigned short ar = 0;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ // 8 connected
+ if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
+ if (chf.areas[ai] != area)
+ continue;
+ unsigned short nr = srcReg[ai];
+ if (nr != 0 && nr != r)
+ ar = nr;
+
+ const rcCompactSpan& as = chf.spans[ai];
+
+ const int dir2 = (dir+1) & 0x3;
+ if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir2);
+ const int ay2 = ay + rcGetDirOffsetY(dir2);
+ const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
+ if (chf.areas[ai2] != area)
+ continue;
+ unsigned short nr = srcReg[ai2];
+ if (nr != 0 && nr != r)
+ ar = nr;
+ }
+ }
+ }
+ if (ar != 0)
+ {
+ srcReg[ci] = 0;
+ continue;
+ }
+ count++;
+
+ // Expand neighbours.
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
+ if (chf.areas[ai] != area)
+ continue;
+ if (chf.dist[ai] >= lev)
+ {
+ if (srcReg[ai] == 0)
+ {
+ srcReg[ai] = r;
+ srcDist[ai] = 0;
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+ }
+ }
+ }
+
+ return count > 0;
+}
+
+static unsigned short* expandRegions(int maxIter, unsigned short level,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, unsigned short* srcDist,
+ unsigned short* dstReg, unsigned short* dstDist,
+ rcIntArray& stack)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ // Find cells revealed by the raised level.
+ stack.resize(0);
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
+ {
+ stack.push(x);
+ stack.push(y);
+ stack.push(i);
+ }
+ }
+ }
+ }
+
+ int iter = 0;
+ while (stack.size() > 0)
+ {
+ int failed = 0;
+
+ memcpy(dstReg, srcReg, sizeof(unsigned short)*chf.spanCount);
+ memcpy(dstDist, srcDist, sizeof(unsigned short)*chf.spanCount);
+
+ for (int j = 0; j < stack.size(); j += 3)
+ {
+ int x = stack[j+0];
+ int y = stack[j+1];
+ int i = stack[j+2];
+ if (i < 0)
+ {
+ failed++;
+ continue;
+ }
+
+ unsigned short r = srcReg[i];
+ unsigned short d2 = 0xffff;
+ const unsigned char area = chf.areas[i];
+ const rcCompactSpan& s = chf.spans[i];
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) == RC_NOT_CONNECTED) continue;
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ if (chf.areas[ai] != area) continue;
+ if (srcReg[ai] > 0 && (srcReg[ai] & RC_BORDER_REG) == 0)
+ {
+ if ((int)srcDist[ai]+2 < (int)d2)
+ {
+ r = srcReg[ai];
+ d2 = srcDist[ai]+2;
+ }
+ }
+ }
+ if (r)
+ {
+ stack[j+2] = -1; // mark as used
+ dstReg[i] = r;
+ dstDist[i] = d2;
+ }
+ else
+ {
+ failed++;
+ }
+ }
+
+ // rcSwap source and dest.
+ rcSwap(srcReg, dstReg);
+ rcSwap(srcDist, dstDist);
+
+ if (failed*3 == stack.size())
+ break;
+
+ if (level > 0)
+ {
+ ++iter;
+ if (iter >= maxIter)
+ break;
+ }
+ }
+
+ return srcReg;
+}
+
+
+struct rcRegion
+{
+ inline rcRegion(unsigned short i) :
+ spanCount(0),
+ id(i),
+ areaType(0),
+ remap(false),
+ visited(false)
+ {}
+
+ int spanCount; // Number of spans belonging to this region
+ unsigned short id; // ID of the region
+ unsigned char areaType; // Are type.
+ bool remap;
+ bool visited;
+ rcIntArray connections;
+ rcIntArray floors;
+};
+
+static void removeAdjacentNeighbours(rcRegion& reg)
+{
+ // Remove adjacent duplicates.
+ for (int i = 0; i < reg.connections.size() && reg.connections.size() > 1; )
+ {
+ int ni = (i+1) % reg.connections.size();
+ if (reg.connections[i] == reg.connections[ni])
+ {
+ // Remove duplicate
+ for (int j = i; j < reg.connections.size()-1; ++j)
+ reg.connections[j] = reg.connections[j+1];
+ reg.connections.pop();
+ }
+ else
+ ++i;
+ }
+}
+
+static void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short newId)
+{
+ bool neiChanged = false;
+ for (int i = 0; i < reg.connections.size(); ++i)
+ {
+ if (reg.connections[i] == oldId)
+ {
+ reg.connections[i] = newId;
+ neiChanged = true;
+ }
+ }
+ for (int i = 0; i < reg.floors.size(); ++i)
+ {
+ if (reg.floors[i] == oldId)
+ reg.floors[i] = newId;
+ }
+ if (neiChanged)
+ removeAdjacentNeighbours(reg);
+}
+
+static bool canMergeWithRegion(const rcRegion& rega, const rcRegion& regb)
+{
+ if (rega.areaType != regb.areaType)
+ return false;
+ int n = 0;
+ for (int i = 0; i < rega.connections.size(); ++i)
+ {
+ if (rega.connections[i] == regb.id)
+ n++;
+ }
+ if (n > 1)
+ return false;
+ for (int i = 0; i < rega.floors.size(); ++i)
+ {
+ if (rega.floors[i] == regb.id)
+ return false;
+ }
+ return true;
+}
+
+static void addUniqueFloorRegion(rcRegion& reg, int n)
+{
+ for (int i = 0; i < reg.floors.size(); ++i)
+ if (reg.floors[i] == n)
+ return;
+ reg.floors.push(n);
+}
+
+static bool mergeRegions(rcRegion& rega, rcRegion& regb)
+{
+ unsigned short aid = rega.id;
+ unsigned short bid = regb.id;
+
+ // Duplicate current neighbourhood.
+ rcIntArray acon;
+ acon.resize(rega.connections.size());
+ for (int i = 0; i < rega.connections.size(); ++i)
+ acon[i] = rega.connections[i];
+ rcIntArray& bcon = regb.connections;
+
+ // Find insertion point on A.
+ int insa = -1;
+ for (int i = 0; i < acon.size(); ++i)
+ {
+ if (acon[i] == bid)
+ {
+ insa = i;
+ break;
+ }
+ }
+ if (insa == -1)
+ return false;
+
+ // Find insertion point on B.
+ int insb = -1;
+ for (int i = 0; i < bcon.size(); ++i)
+ {
+ if (bcon[i] == aid)
+ {
+ insb = i;
+ break;
+ }
+ }
+ if (insb == -1)
+ return false;
+
+ // Merge neighbours.
+ rega.connections.resize(0);
+ for (int i = 0, ni = acon.size(); i < ni-1; ++i)
+ rega.connections.push(acon[(insa+1+i) % ni]);
+
+ for (int i = 0, ni = bcon.size(); i < ni-1; ++i)
+ rega.connections.push(bcon[(insb+1+i) % ni]);
+
+ removeAdjacentNeighbours(rega);
+
+ for (int j = 0; j < regb.floors.size(); ++j)
+ addUniqueFloorRegion(rega, regb.floors[j]);
+ rega.spanCount += regb.spanCount;
+ regb.spanCount = 0;
+ regb.connections.resize(0);
+
+ return true;
+}
+
+static bool isRegionConnectedToBorder(const rcRegion& reg)
+{
+ // Region is connected to border if
+ // one of the neighbours is null id.
+ for (int i = 0; i < reg.connections.size(); ++i)
+ {
+ if (reg.connections[i] == 0)
+ return true;
+ }
+ return false;
+}
+
+static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg,
+ int x, int y, int i, int dir)
+{
+ const rcCompactSpan& s = chf.spans[i];
+ unsigned short r = 0;
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
+ r = srcReg[ai];
+ }
+ if (r == srcReg[i])
+ return false;
+ return true;
+}
+
+static void walkContour(int x, int y, int i, int dir,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg,
+ rcIntArray& cont)
+{
+ int startDir = dir;
+ int starti = i;
+
+ const rcCompactSpan& ss = chf.spans[i];
+ unsigned short curReg = 0;
+ if (rcGetCon(ss, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir);
+ curReg = srcReg[ai];
+ }
+ cont.push(curReg);
+
+ int iter = 0;
+ while (++iter < 40000)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (isSolidEdge(chf, srcReg, x, y, i, dir))
+ {
+ // Choose the edge corner
+ unsigned short r = 0;
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
+ r = srcReg[ai];
+ }
+ if (r != curReg)
+ {
+ curReg = r;
+ cont.push(curReg);
+ }
+
+ dir = (dir+1) & 0x3; // Rotate CW
+ }
+ else
+ {
+ int ni = -1;
+ const int nx = x + rcGetDirOffsetX(dir);
+ const int ny = y + rcGetDirOffsetY(dir);
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
+ ni = (int)nc.index + rcGetCon(s, dir);
+ }
+ if (ni == -1)
+ {
+ // Should not happen.
+ return;
+ }
+ x = nx;
+ y = ny;
+ i = ni;
+ dir = (dir+3) & 0x3; // Rotate CCW
+ }
+
+ if (starti == i && startDir == dir)
+ {
+ break;
+ }
+ }
+
+ // Remove adjacent duplicates.
+ if (cont.size() > 1)
+ {
+ for (int i = 0; i < cont.size(); )
+ {
+ int ni = (i+1) % cont.size();
+ if (cont[i] == cont[ni])
+ {
+ for (int j = i; j < cont.size()-1; ++j)
+ cont[j] = cont[j+1];
+ cont.pop();
+ }
+ else
+ ++i;
+ }
+ }
+}
+
+static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
+ unsigned short& maxRegionId,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ const int nreg = maxRegionId+1;
+ rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
+ if (!regions)
+ {
+ ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg);
+ return false;
+ }
+
+ // Construct regions
+ for (int i = 0; i < nreg; ++i)
+ new(&regions[i]) rcRegion((unsigned short)i);
+
+ // Find edge of a region and find connections around the contour.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ unsigned short r = srcReg[i];
+ if (r == 0 || r >= nreg)
+ continue;
+
+ rcRegion& reg = regions[r];
+ reg.spanCount++;
+
+
+ // Update floors.
+ for (int j = (int)c.index; j < ni; ++j)
+ {
+ if (i == j) continue;
+ unsigned short floorId = srcReg[j];
+ if (floorId == 0 || floorId >= nreg)
+ continue;
+ addUniqueFloorRegion(reg, floorId);
+ }
+
+ // Have found contour
+ if (reg.connections.size() > 0)
+ continue;
+
+ reg.areaType = chf.areas[i];
+
+ // Check if this cell is next to a border.
+ int ndir = -1;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (isSolidEdge(chf, srcReg, x, y, i, dir))
+ {
+ ndir = dir;
+ break;
+ }
+ }
+
+ if (ndir != -1)
+ {
+ // The cell is at border.
+ // Walk around the contour to find all the neighbours.
+ walkContour(x, y, i, ndir, chf, srcReg, reg.connections);
+ }
+ }
+ }
+ }
+
+ // Remove too small regions.
+ rcIntArray stack(32);
+ rcIntArray trace(32);
+ for (int i = 0; i < nreg; ++i)
+ {
+ rcRegion& reg = regions[i];
+ if (reg.id == 0 || (reg.id & RC_BORDER_REG))
+ continue;
+ if (reg.spanCount == 0)
+ continue;
+ if (reg.visited)
+ continue;
+
+ // Count the total size of all the connected regions.
+ // Also keep track of the regions connects to a tile border.
+ bool connectsToBorder = false;
+ int spanCount = 0;
+ stack.resize(0);
+ trace.resize(0);
+
+ reg.visited = true;
+ stack.push(i);
+
+ while (stack.size())
+ {
+ // Pop
+ int ri = stack.pop();
+
+ rcRegion& creg = regions[ri];
+
+ spanCount += creg.spanCount;
+ trace.push(ri);
+
+ for (int j = 0; j < creg.connections.size(); ++j)
+ {
+ if (creg.connections[j] & RC_BORDER_REG)
+ {
+ connectsToBorder = true;
+ continue;
+ }
+ rcRegion& nreg = regions[creg.connections[j]];
+ if (nreg.visited)
+ continue;
+ if (nreg.id == 0 || (nreg.id & RC_BORDER_REG))
+ continue;
+ // Visit
+ stack.push(nreg.id);
+ nreg.visited = true;
+ }
+ }
+
+ // If the accumulated regions size is too small, remove it.
+ // Do not remove areas which connect to tile borders
+ // as their size cannot be estimated correctly and removing them
+ // can potentially remove necessary areas.
+ if (spanCount < minRegionArea && !connectsToBorder)
+ {
+ // Kill all visited regions.
+ for (int j = 0; j < trace.size(); ++j)
+ {
+ regions[trace[j]].spanCount = 0;
+ regions[trace[j]].id = 0;
+ }
+ }
+ }
+
+ // Merge too small regions to neighbour regions.
+ int mergeCount = 0 ;
+ do
+ {
+ mergeCount = 0;
+ for (int i = 0; i < nreg; ++i)
+ {
+ rcRegion& reg = regions[i];
+ if (reg.id == 0 || (reg.id & RC_BORDER_REG))
+ continue;
+ if (reg.spanCount == 0)
+ continue;
+
+ // Check to see if the region should be merged.
+ if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg))
+ continue;
+
+ // Small region with more than 1 connection.
+ // Or region which is not connected to a border at all.
+ // Find smallest neighbour region that connects to this one.
+ int smallest = 0xfffffff;
+ unsigned short mergeId = reg.id;
+ for (int j = 0; j < reg.connections.size(); ++j)
+ {
+ if (reg.connections[j] & RC_BORDER_REG) continue;
+ rcRegion& mreg = regions[reg.connections[j]];
+ if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue;
+ if (mreg.spanCount < smallest &&
+ canMergeWithRegion(reg, mreg) &&
+ canMergeWithRegion(mreg, reg))
+ {
+ smallest = mreg.spanCount;
+ mergeId = mreg.id;
+ }
+ }
+ // Found new id.
+ if (mergeId != reg.id)
+ {
+ unsigned short oldId = reg.id;
+ rcRegion& target = regions[mergeId];
+
+ // Merge neighbours.
+ if (mergeRegions(target, reg))
+ {
+ // Fixup regions pointing to current region.
+ for (int j = 0; j < nreg; ++j)
+ {
+ if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG)) continue;
+ // If another region was already merged into current region
+ // change the nid of the previous region too.
+ if (regions[j].id == oldId)
+ regions[j].id = mergeId;
+ // Replace the current region with the new one if the
+ // current regions is neighbour.
+ replaceNeighbour(regions[j], oldId, mergeId);
+ }
+ mergeCount++;
+ }
+ }
+ }
+ }
+ while (mergeCount > 0);
+
+ // Compress region Ids.
+ for (int i = 0; i < nreg; ++i)
+ {
+ regions[i].remap = false;
+ if (regions[i].id == 0) continue; // Skip nil regions.
+ if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions.
+ regions[i].remap = true;
+ }
+
+ unsigned short regIdGen = 0;
+ for (int i = 0; i < nreg; ++i)
+ {
+ if (!regions[i].remap)
+ continue;
+ unsigned short oldId = regions[i].id;
+ unsigned short newId = ++regIdGen;
+ for (int j = i; j < nreg; ++j)
+ {
+ if (regions[j].id == oldId)
+ {
+ regions[j].id = newId;
+ regions[j].remap = false;
+ }
+ }
+ }
+ maxRegionId = regIdGen;
+
+ // Remap regions.
+ for (int i = 0; i < chf.spanCount; ++i)
+ {
+ if ((srcReg[i] & RC_BORDER_REG) == 0)
+ srcReg[i] = regions[srcReg[i]].id;
+ }
+
+ for (int i = 0; i < nreg; ++i)
+ regions[i].~rcRegion();
+ rcFree(regions);
+
+ return true;
+}
+
+
+bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD);
+
+ if (chf.dist)
+ {
+ rcFree(chf.dist);
+ chf.dist = 0;
+ }
+
+ unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!src)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' (%d).", chf.spanCount);
+ return false;
+ }
+ unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!dst)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' (%d).", chf.spanCount);
+ rcFree(src);
+ return false;
+ }
+
+ unsigned short maxDist = 0;
+
+ ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
+
+ calculateDistanceField(chf, src, maxDist);
+ chf.maxDistance = maxDist;
+
+ ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
+
+ ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
+
+ // Blur
+ if (boxBlur(chf, 1, src, dst) != src)
+ rcSwap(src, dst);
+
+ // Store distance.
+ chf.dist = src;
+
+ ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
+
+ ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD);
+
+ rcFree(dst);
+
+ return true;
+}
+
+static void paintRectRegion(int minx, int maxx, int miny, int maxy, unsigned short regId,
+ rcCompactHeightfield& chf, unsigned short* srcReg)
+{
+ const int w = chf.width;
+ for (int y = miny; y < maxy; ++y)
+ {
+ for (int x = minx; x < maxx; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (chf.areas[i] != RC_NULL_AREA)
+ srcReg[i] = regId;
+ }
+ }
+ }
+}
+
+
+static const unsigned short RC_NULL_NEI = 0xffff;
+
+struct rcSweepSpan
+{
+ unsigned short rid; // row id
+ unsigned short id; // region id
+ unsigned short ns; // number samples
+ unsigned short nei; // neighbour id
+};
+
+bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+
+ const int w = chf.width;
+ const int h = chf.height;
+ unsigned short id = 1;
+
+ rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!srcReg)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount);
+ return false;
+ }
+ memset(srcReg,0,sizeof(unsigned short)*chf.spanCount);
+
+ const int nsweeps = rcMax(chf.width,chf.height);
+ rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP);
+ if (!sweeps)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps);
+ return false;
+ }
+
+
+ // Mark border regions.
+ if (borderSize > 0)
+ {
+ // Make sure border will not overflow.
+ const int bw = rcMin(w, borderSize);
+ const int bh = rcMin(h, borderSize);
+ // Paint regions
+ paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ }
+
+ rcIntArray prev(256);
+
+ // Sweep one line at a time.
+ for (int y = borderSize; y < h-borderSize; ++y)
+ {
+ // Collect spans from this row.
+ prev.resize(id+1);
+ memset(&prev[0],0,sizeof(int)*id);
+ unsigned short rid = 1;
+
+ for (int x = borderSize; x < w-borderSize; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ if (chf.areas[i] == RC_NULL_AREA) continue;
+
+ // -x
+ unsigned short previd = 0;
+ if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(0);
+ const int ay = y + rcGetDirOffsetY(0);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
+ if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
+ previd = srcReg[ai];
+ }
+
+ if (!previd)
+ {
+ previd = rid++;
+ sweeps[previd].rid = previd;
+ sweeps[previd].ns = 0;
+ sweeps[previd].nei = 0;
+ }
+
+ // -y
+ if (rcGetCon(s,3) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(3);
+ const int ay = y + rcGetDirOffsetY(3);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
+ if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
+ {
+ unsigned short nr = srcReg[ai];
+ if (!sweeps[previd].nei || sweeps[previd].nei == nr)
+ {
+ sweeps[previd].nei = nr;
+ sweeps[previd].ns++;
+ prev[nr]++;
+ }
+ else
+ {
+ sweeps[previd].nei = RC_NULL_NEI;
+ }
+ }
+ }
+
+ srcReg[i] = previd;
+ }
+ }
+
+ // Create unique ID.
+ for (int i = 1; i < rid; ++i)
+ {
+ if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 &&
+ prev[sweeps[i].nei] == (int)sweeps[i].ns)
+ {
+ sweeps[i].id = sweeps[i].nei;
+ }
+ else
+ {
+ sweeps[i].id = id++;
+ }
+ }
+
+ // Remap IDs
+ for (int x = borderSize; x < w-borderSize; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (srcReg[i] > 0 && srcReg[i] < rid)
+ srcReg[i] = sweeps[srcReg[i]].id;
+ }
+ }
+ }
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Filter out small regions.
+ chf.maxRegions = id;
+ if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ return false;
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Store the result out.
+ for (int i = 0; i < chf.spanCount; ++i)
+ chf.spans[i].reg = srcReg[i];
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
+
+ return true;
+}
+
+bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP);
+ if (!buf)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4);
+ return false;
+ }
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
+
+ rcIntArray stack(1024);
+ rcIntArray visited(1024);
+
+ unsigned short* srcReg = buf;
+ unsigned short* srcDist = buf+chf.spanCount;
+ unsigned short* dstReg = buf+chf.spanCount*2;
+ unsigned short* dstDist = buf+chf.spanCount*3;
+
+ memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount);
+ memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount);
+
+ unsigned short regionId = 1;
+ unsigned short level = (chf.maxDistance+1) & ~1;
+
+ // TODO: Figure better formula, expandIters defines how much the
+ // watershed "overflows" and simplifies the regions. Tying it to
+ // agent radius was usually good indication how greedy it could be.
+// const int expandIters = 4 + walkableRadius * 2;
+ const int expandIters = 8;
+
+ // Mark border regions.
+ paintRectRegion(0, borderSize, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+ paintRectRegion(w-borderSize, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+ paintRectRegion(0, w, 0, borderSize, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+ paintRectRegion(0, w, h-borderSize, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+
+ while (level > 0)
+ {
+ level = level >= 2 ? level-2 : 0;
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
+
+ // Expand current regions until no empty connected cells found.
+ if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
+ {
+ rcSwap(srcReg, dstReg);
+ rcSwap(srcDist, dstDist);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
+
+ // Mark new regions with IDs.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA)
+ continue;
+
+ if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
+ regionId++;
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
+
+ }
+
+ // Expand current regions until no empty connected cells found.
+ if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
+ {
+ rcSwap(srcReg, dstReg);
+ rcSwap(srcDist, dstDist);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Filter out small regions.
+ chf.maxRegions = regionId;
+ if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ return false;
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Write the result out.
+ for (int i = 0; i < chf.spanCount; ++i)
+ chf.spans[i].reg = srcReg[i];
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
+
+ return true;
+}
+
+
diff --git a/dep/recastnavigation/TODO.txt b/dep/recastnavigation/TODO.txt
new file mode 100644
index 0000000..b911c0e
--- /dev/null
+++ b/dep/recastnavigation/TODO.txt
@@ -0,0 +1,20 @@
+TODO/Roadmap
+
+Summer/Autumn 2009
+
+- Off mesh links (jump links)
+- Area annotations
+- Embed extra data per polygon
+- Height conforming navmesh
+
+
+Autumn/Winter 2009/2010
+
+- Detour path following
+- More dynamic example with tile navmesh
+- Faster small tile process
+
+
+More info at http://digestingduck.blogspot.com/2009/07/recast-and-detour-roadmap.html
+
+-
diff --git a/src/server/collision/CMakeLists.txt b/src/server/collision/CMakeLists.txt
index 9fc696a..d54d5f9 100644
--- a/src/server/collision/CMakeLists.txt
+++ b/src/server/collision/CMakeLists.txt
@@ -34,7 +34,9 @@ set(collision_STAT_SRCS
include_directories(
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Database
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
diff --git a/src/server/collision/Management/MMapFactory.cpp b/src/server/collision/Management/MMapFactory.cpp
new file mode 100644
index 0000000..768f8c9
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "MMapFactory.h"
+#include "World.h"
+#include "Config.h"
+#include "DisableMgr.h"
+#include <set>
+
+namespace MMAP
+{
+ // ######################## MMapFactory ########################
+ // our global singleton copy
+ MMapManager* g_MMapManager = NULL;
+
+ MMapManager* MMapFactory::createOrGetMMapManager()
+ {
+ if (g_MMapManager == NULL)
+ g_MMapManager = new MMapManager();
+
+ return g_MMapManager;
+ }
+
+ bool MMapFactory::IsPathfindingEnabled(uint32 mapId)
+ {
+ if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MMAP, mapId, NULL))
+ return false;
+
+ return sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS);
+ }
+
+ void MMapFactory::clear()
+ {
+ if (g_MMapManager)
+ {
+ delete g_MMapManager;
+ g_MMapManager = NULL;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/server/collision/Management/MMapFactory.h b/src/server/collision/Management/MMapFactory.h
new file mode 100644
index 0000000..a006aed
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_FACTORY_H
+#define _MMAP_FACTORY_H
+
+#include "MMapManager.h"
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+namespace MMAP
+{
+ enum MMAP_LOAD_RESULT
+ {
+ MMAP_LOAD_RESULT_ERROR,
+ MMAP_LOAD_RESULT_OK,
+ MMAP_LOAD_RESULT_IGNORED,
+ };
+
+ // static class
+ // holds all mmap global data
+ // access point to MMapManager singleton
+ class MMapFactory
+ {
+ public:
+ static MMapManager* createOrGetMMapManager();
+ static void clear();
+ static bool IsPathfindingEnabled(uint32 mapId);
+ };
+}
+
+#endif
+
diff --git a/src/server/collision/Management/MMapManager.cpp b/src/server/collision/Management/MMapManager.cpp
new file mode 100644
index 0000000..6ab5152
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "MMapManager.h"
+#include "Log.h"
+#include "World.h"
+
+namespace MMAP
+{
+ // ######################## MMapManager ########################
+ MMapManager::~MMapManager()
+ {
+ for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i)
+ delete i->second;
+
+ // by now we should not have maps loaded
+ // if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
+ }
+
+ bool MMapManager::loadMapData(uint32 mapId)
+ {
+ // we already have this map loaded?
+ if (loadedMMaps.find(mapId) != loadedMMaps.end())
+ return true;
+
+ // load and init dtNavMesh - read parameters from file
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i.mmap")+1;
+ char *fileName = new char[pathLen];
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i.mmap").c_str(), mapId);
+
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ dtNavMeshParams params;
+ fread(&params, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+
+ dtNavMesh* mesh = dtAllocNavMesh();
+ ASSERT(mesh);
+ if (DT_SUCCESS != mesh->init(&params))
+ {
+ dtFreeNavMesh(mesh);
+ sLog->outError("MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ delete [] fileName;
+
+ sLog->outDetail("MMAP:loadMapData: Loaded %03i.mmap", mapId);
+
+ // store inside our map list
+ MMapData* mmap_data = new MMapData(mesh);
+ mmap_data->mmapLoadedTiles.clear();
+
+ loadedMMaps.insert(std::pair<uint32, MMapData*>(mapId, mmap_data));
+ return true;
+ }
+
+ uint32 MMapManager::packTileID(int32 x, int32 y)
+ {
+ return uint32(x << 16 | y);
+ }
+
+ bool MMapManager::loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y)
+ {
+ // make sure the mmap is loaded and ready to load tiles
+ if (!loadMapData(mapId))
+ return false;
+
+ // get this mmap data
+ MMapData* mmap = loadedMMaps[mapId];
+ ASSERT(mmap->navMesh);
+
+ // check if we already have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) != mmap->mmapLoadedTiles.end())
+ return false;
+
+ // load this tile :: mmaps/MMMXXYY.mmtile
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i%02i%02i.mmtile")+1;
+ char *fileName = new char[pathLen];
+ // this change of y and x is needed because of mapbuilder.cpp (x and y are swapped there) so change it here so we dont need to re-extract. All swappes are done in other files. DONT CHANGE THIS!
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i%02i%02i.mmtile").c_str(), mapId, y, x);
+
+ FILE *file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMap: Could not open mmtile file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+ delete [] fileName;
+
+ // read header
+ MmapTileHeader fileHeader;
+ fread(&fileHeader, sizeof(MmapTileHeader), 1, file);
+
+ if (fileHeader.mmapMagic != MMAP_MAGIC)
+ {
+ sLog->outError("MMAP:loadMap: Bad header in mmap %03u%02i%02i.mmtile", mapId, y, x);
+ return false;
+ }
+
+ if (fileHeader.mmapVersion != MMAP_VERSION)
+ {
+ sLog->outError("MMAP:loadMap: %03u%02i%02i.mmtile was built with generator v%i, expected v%i",
+ mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
+ return false;
+ }
+
+ unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
+ ASSERT(data);
+
+ size_t result = fread(data, fileHeader.size, 1, file);
+ if (!result)
+ {
+ sLog->outError("MMAP:loadMap: Bad header or data in mmap %03u%02i%02i.mmtile", mapId, y, x);
+ fclose(file);
+ return false;
+ }
+
+ fclose(file);
+
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ dtTileRef tileRef = 0;
+
+ dtStatus stat;
+
+ {
+ TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, i_tileLock);
+ stat = mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef);
+ }
+ // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
+ if (stat == DT_SUCCESS)
+ {
+ mmap->mmapLoadedTiles.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
+ ++loadedTiles;
+ sLog->outDetail("MMAP:loadMap: Loaded mmtile %03i[%02i,%02i] into %03i[%02i,%02i]", mapId, y, x, mapId, header->x, header->y);
+ return true;
+ }
+ else
+ {
+ sLog->outError("MMAP:loadMap: Could not load %03u%02i%02i.mmtile into navmesh", mapId, y, x);
+ dtFree(data);
+ return false;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId, int32 x, int32 y)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map. %03u%02i%02i.mmtile", mapId, y, x);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+
+ // check if we have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) == mmap->mmapLoadedTiles.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh tile. %03u%02i%02i.mmtile", mapId, y, x);
+ return false;
+ }
+
+ dtTileRef tileRef = mmap->mmapLoadedTiles[packedGridPos];
+
+ dtStatus status;
+ // unload, and mark as non loaded
+ {
+ TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, i_tileLock);
+ status = mmap->navMesh->removeTile(tileRef, NULL, NULL);
+ }
+
+ if (status != DT_SUCCESS)
+ {
+ // this is technically a memory leak
+ // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
+ // we cannot recover from this error - assert out
+ sLog->outError("MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, y, x);
+ ASSERT(false);
+ }
+ else
+ {
+ mmap->mmapLoadedTiles.erase(packedGridPos);
+ --loadedTiles;
+ sLog->outDetail("MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, y, x, mapId);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ // unload all tiles from given map
+ MMapData* mmap = loadedMMaps[mapId];
+ for (MMapTileSet::iterator i = mmap->mmapLoadedTiles.begin(); i != mmap->mmapLoadedTiles.end(); ++i)
+ {
+ uint32 x = (i->first >> 16);
+ uint32 y = (i->first & 0x0000FFFF);
+ if (DT_SUCCESS != mmap->navMesh->removeTile(i->second, NULL, NULL))
+ sLog->outError("MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, y, x);
+ else
+ {
+ --loadedTiles;
+ sLog->outDetail("MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, y, x, mapId);
+ }
+ }
+
+ delete mmap;
+ loadedMMaps.erase(mapId);
+ sLog->outDetail("MMAP:unloadMap: Unloaded %03i.mmap", mapId);
+
+ return true;
+ }
+
+ bool MMapManager::unloadMapInstance(uint32 mapId, uint32 instanceId)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId %03u instanceId %u", mapId, instanceId);
+ return false;
+ }
+
+ dtNavMeshQuery* query = mmap->navMeshQueries[instanceId];
+
+ dtFreeNavMeshQuery(query);
+ mmap->navMeshQueries.erase(instanceId);
+ sLog->outDetail("MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId);
+
+ return true;
+ }
+
+ dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ return loadedMMaps[mapId]->navMesh;
+ }
+
+ dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ // allocate mesh query
+ dtNavMeshQuery* query = dtAllocNavMeshQuery();
+ ASSERT(query);
+ if (DT_SUCCESS != query->init(mmap->navMesh, 1024))
+ {
+ dtFreeNavMeshQuery(query);
+ sLog->outError("MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ return NULL;
+ }
+
+ sLog->outDetail("MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query));
+ }
+
+ return mmap->navMeshQueries[instanceId];
+ }
+}
\ No newline at end of file
diff --git a/src/server/collision/Management/MMapManager.h b/src/server/collision/Management/MMapManager.h
new file mode 100644
index 0000000..d469a44
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_MANAGER_H
+#define _MMAP_MANAGER_H
+
+#include <ace/RW_Thread_Mutex.h>
+#include <ace/Thread_Mutex.h>
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+// move map related classes
+namespace MMAP
+{
+ typedef UNORDERED_MAP<uint32, dtTileRef> MMapTileSet;
+ typedef UNORDERED_MAP<uint32, dtNavMeshQuery*> NavMeshQuerySet;
+
+ // dummy struct to hold map's mmap data
+ struct MMapData
+ {
+ MMapData(dtNavMesh* mesh) : navMesh(mesh) {}
+ ~MMapData()
+ {
+ for (NavMeshQuerySet::iterator i = navMeshQueries.begin(); i != navMeshQueries.end(); ++i)
+ dtFreeNavMeshQuery(i->second);
+
+ if (navMesh)
+ dtFreeNavMesh(navMesh);
+ }
+
+ dtNavMesh* navMesh;
+
+ // we have to use single dtNavMeshQuery for every instance, since those are not thread safe
+ NavMeshQuerySet navMeshQueries; // instanceId to query
+ MMapTileSet mmapLoadedTiles; // maps [map grid coords] to [dtTile]
+ };
+
+
+ typedef UNORDERED_MAP<uint32, MMapData*> MMapDataSet;
+
+ // singleton class
+ // holds all all access to mmap loading unloading and meshes
+ class MMapManager
+ {
+ public:
+ MMapManager() : loadedTiles(0) {}
+ ~MMapManager();
+
+ bool loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId);
+ bool unloadMapInstance(uint32 mapId, uint32 instanceId);
+
+ // the returned [dtNavMeshQuery const*] is NOT threadsafe
+ dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId);
+ dtNavMesh const* GetNavMesh(uint32 mapId);
+
+ uint32 getLoadedTilesCount() const { return loadedTiles; }
+ uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
+ private:
+ bool loadMapData(uint32 mapId);
+ uint32 packTileID(int32 x, int32 y);
+
+ MMapDataSet loadedMMaps;
+ uint32 loadedTiles;
+ ACE_RW_Thread_Mutex i_tileLock;
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/server/collision/Management/VMapManager2.h b/src/server/collision/Management/VMapManager2.h
index 1fba108..c87c42f 100755
--- a/src/server/collision/Management/VMapManager2.h
+++ b/src/server/collision/Management/VMapManager2.h
@@ -112,6 +112,8 @@ namespace VMAP
return getMapFileName(mapId);
}
virtual bool existsMap(const char* basePath, unsigned int mapId, int x, int y);
+ public:
+ void getInstanceMapTree(InstanceTreeMap &instanceMapTree);
};
}
diff --git a/src/server/collision/Maps/MapTree.h b/src/server/collision/Maps/MapTree.h
index 1732209..f0df713 100755
--- a/src/server/collision/Maps/MapTree.h
+++ b/src/server/collision/Maps/MapTree.h
@@ -80,6 +80,7 @@ namespace VMAP
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm);
bool isTiled() const { return iIsTiled; }
uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
+ void getModelInstances(ModelInstance* &models, uint32 &count);
};
struct AreaInfo
diff --git a/src/server/collision/Models/ModelInstance.h b/src/server/collision/Models/ModelInstance.h
index 1118b65..3c46967 100755
--- a/src/server/collision/Models/ModelInstance.h
+++ b/src/server/collision/Models/ModelInstance.h
@@ -74,6 +74,8 @@ namespace VMAP
G3D::Matrix3 iInvRot;
float iInvScale;
WorldModel* iModel;
+ public:
+ WorldModel* const getWorldModel();
};
} // namespace VMAP
diff --git a/src/server/collision/Models/WorldModel.h b/src/server/collision/Models/WorldModel.h
index ebf828e..98be349 100755
--- a/src/server/collision/Models/WorldModel.h
+++ b/src/server/collision/Models/WorldModel.h
@@ -66,6 +66,8 @@ namespace VMAP
uint32 iType; //!< liquid type
float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
uint8 *iFlags; //!< info if liquid tile is used
+ public:
+ void getPosInfo(uint32 &tilesX, uint32 &tilesY, Vector3 &corner) const;
};
/*! holding additional info for WMO group files */
@@ -98,6 +100,8 @@ namespace VMAP
std::vector<MeshTriangle> triangles;
BIH meshTree;
WmoLiquid* iLiquid;
+ public:
+ void getMeshData(std::vector<Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid);
};
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
class WorldModel
@@ -117,6 +121,8 @@ namespace VMAP
uint32 RootWMOID;
std::vector<GroupModel> groupModels;
BIH groupTree;
+ public:
+ void getGroupModels(std::vector<GroupModel> &groupModels);
};
} // namespace VMAP
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index a83e77e..099e2f7 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -106,6 +106,8 @@ set(game_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp
index 0c43c9e..e43bd8a 100755
--- a/src/server/game/Combat/ThreatManager.cpp
+++ b/src/server/game/Combat/ThreatManager.cpp
@@ -293,7 +293,15 @@ HostileReference* ThreatContainer::addThreat(Unit* victim, float threat)
void ThreatContainer::modifyThreatPercent(Unit* victim, int32 percent)
{
if (HostileReference* ref = getReferenceByTarget(victim))
- ref->addThreatPercent(percent);
+ {
+ if (percent < -100)
+ {
+ ref->removeReference();
+ delete ref;
+ }
+ else
+ ref->addThreatPercent(percent);
+ }
}
//============================================================
diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp
index d7a769d..d949d98 100755
--- a/src/server/game/Conditions/DisableMgr.cpp
+++ b/src/server/game/Conditions/DisableMgr.cpp
@@ -40,7 +40,7 @@ namespace
DisableMap m_DisableMap;
- uint8 MAX_DISABLE_TYPES = 7;
+ uint8 MAX_DISABLE_TYPES = 8;
}
void LoadDisables()
@@ -219,6 +219,20 @@ void LoadDisables()
}
break;
}
+ case DISABLE_TYPE_MMAP:
+ {
+ MapEntry const* mapEntry = sMapStore.LookupEntry(entry);
+ if (!mapEntry)
+ {
+ sLog->outErrorDb("Pathfinding for map entry %u from `disables` doesn't exist in dbc, skipped.", entry);
+ continue;
+ }
+ if (flags)
+ sLog->outErrorDb("Disable flags specified for pathfinding in map %u, useless data.", entry);
+
+ sLog->outString("Pathfinding disabled for map %u.", entry);
+ break;
+ }
default:
break;
}
@@ -348,6 +362,7 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags
case DISABLE_TYPE_BATTLEGROUND:
case DISABLE_TYPE_OUTDOORPVP:
case DISABLE_TYPE_ACHIEVEMENT_CRITERIA:
+ case DISABLE_TYPE_MMAP:
return true;
case DISABLE_TYPE_VMAP:
return flags & itr->second.flags;
diff --git a/src/server/game/Conditions/DisableMgr.h b/src/server/game/Conditions/DisableMgr.h
index 0f9310a..b0b328b 100755
--- a/src/server/game/Conditions/DisableMgr.h
+++ b/src/server/game/Conditions/DisableMgr.h
@@ -32,6 +32,7 @@ enum DisableType
DISABLE_TYPE_ACHIEVEMENT_CRITERIA = 4,
DISABLE_TYPE_OUTDOORPVP = 5,
DISABLE_TYPE_VMAP = 6,
+ DISABLE_TYPE_MMAP = 7,
};
enum SpellDisableTypes
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index fdc09bb..903a387 100755
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1650,6 +1650,13 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
{
case TYPEID_UNIT:
{
+ Unit* victim = ToCreature()->getVictim();
+ if (victim)
+ {
+ // anyway creature move to victim for thinly Z distance (shun some VMAP wrong ground calculating)
+ if (fabs(GetPositionZ() - victim->GetPositionZ()) < 5.0f)
+ return;
+ }
// non fly unit don't must be in air
// non swim unit must be at ground (mostly speedup, because it don't must be in water and water level check less fast
if (!ToCreature()->CanFly())
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 6158e08..bbdf403 100755
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -368,10 +368,10 @@ bool Unit::haveOffhandWeapon() const
return m_canDualWield;
}
-void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed)
+void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath, bool forceDestination)
{
Movement::MoveSplineInit init(*this);
- init.MoveTo(x,y,z);
+ init.MoveTo(x, y, z, generatePath, forceDestination);
init.SetVelocity(speed);
init.Launch();
}
@@ -12892,6 +12892,20 @@ void Unit::DeleteThreatList()
//======================================================================
+void Unit::DeleteFromThreatList(Unit* victim)
+{
+ if (CanHaveThreatList() && !m_ThreatManager.isThreatListEmpty())
+ {
+ // remove unreachable target from our threat list
+ // next tick we will select next possible target
+ m_HostileRefManager.deleteReference(victim);
+ m_ThreatManager.modifyThreatPercent(victim, -101);
+ // _removeAttacker(victim);
+ }
+}
+
+//======================================================================
+
void Unit::TauntApply(Unit* taunter)
{
ASSERT(GetTypeId() == TYPEID_UNIT);
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 46dbf48..6573a55 100755
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -498,6 +498,7 @@ enum UnitState
UNIT_STATE_FLEEING_MOVE = 0x02000000,
UNIT_STATE_CHASE_MOVE = 0x04000000,
UNIT_STATE_FOLLOW_MOVE = 0x08000000,
+ UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
UNIT_STATE_UNATTACKABLE = (UNIT_STATE_IN_FLIGHT | UNIT_STATE_ONVEHICLE),
// for real move using movegen check and stop (except unstoppable flight)
UNIT_STATE_MOVING = UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE ,
@@ -1596,7 +1597,7 @@ class Unit : public WorldObject
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject* obj, float speedZ);
- void MonsterMoveWithSpeed(float x, float y, float z, float speed);
+ void MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath = false, bool forceDestination = false);
//void SetFacing(float ori, WorldObject* obj = NULL);
//void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL);
void SendMovementFlagUpdate();
@@ -1951,6 +1952,7 @@ class Unit : public WorldObject
void AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = NULL);
float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
void DeleteThreatList();
+ void DeleteFromThreatList(Unit* victim);
void TauntApply(Unit* victim);
void TauntFadeOut(Unit* taunter);
ThreatManager& getThreatManager() { return m_ThreatManager; }
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 270c4c7..fe10787 100755
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -20,6 +20,7 @@
#include "GridStates.h"
#include "ScriptMgr.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "MapInstanced.h"
#include "CellImpl.h"
#include "GridNotifiers.h"
@@ -69,6 +70,8 @@ Map::~Map()
if (!m_scriptSchedule.empty())
sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
+
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), GetInstanceId());
}
bool Map::ExistMap(uint32 mapid, int gx, int gy)
@@ -120,7 +123,7 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy)
void Map::LoadVMap(int gx, int gy)
{
// x and y are swapped !!
- int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld->GetDataPath()+ "vmaps").c_str(), GetId(), gx, gy);
+ int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld->GetDataPath() + "vmaps").c_str(), GetId(), gx, gy);
switch (vmapLoadResult)
{
case VMAP::VMAP_LOAD_RESULT_OK:
@@ -135,6 +138,24 @@ void Map::LoadVMap(int gx, int gy)
}
}
+void Map::LoadMMap(int gx, int gy)
+{
+ // DONT CHANGE "gy" and "gx" - Its necessary !
+ int mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap((sWorld->GetDataPath() + "mmaps").c_str(), GetId(), gy, gx);
+ switch (mmapLoadResult)
+ {
+ case MMAP::MMAP_LOAD_RESULT_OK:
+ sLog->outDetail("MMAP loaded name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+ break;
+ case MMAP::MMAP_LOAD_RESULT_ERROR:
+ sLog->outDetail("Could not load MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+ break;
+ case MMAP::MMAP_LOAD_RESULT_IGNORED:
+ sLog->outStaticDebug("Ignored MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+ break;
+ }
+}
+
void Map::LoadMap(int gx, int gy, bool reload)
{
if (i_InstanceId != 0)
@@ -173,9 +194,8 @@ void Map::LoadMap(int gx, int gy, bool reload)
// loading data
GridMaps[gx][gy] = new GridMap();
if (!GridMaps[gx][gy]->loadData(tmp))
- {
sLog->outError("Error loading map file: \n %s\n", tmp);
- }
+
delete [] tmp;
sScriptMgr->OnLoadGridMap(this, GridMaps[gx][gy], gx, gy);
@@ -185,7 +205,10 @@ void Map::LoadMapAndVMap(int gx, int gy)
{
LoadMap(gx, gy);
if (i_InstanceId == 0)
+ {
LoadVMap(gx, gy); // Only load the data for the base map
+ LoadMMap(gx, gy);
+ }
}
void Map::InitStateMachine()
@@ -996,6 +1019,7 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll)
}
// x and y are swapped
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gx, gy);
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId(), gx, gy);
}
else
((MapInstanced*)m_parentMap)->RemoveGridMapReference(GridCoord(gx, gy));
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 6d526f2..8cdfc54 100755
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -76,6 +76,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -479,6 +481,7 @@ class Map : public GridRefManager<NGridType>
void LoadMapAndVMap(int gx, int gy);
void LoadVMap(int gx, int gy);
void LoadMap(int gx, int gy, bool reload = false);
+ void LoadMMap(int gx, int gy);
GridMap* GetGrid(float x, float y);
void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; }
diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp
index 2c1bdb0..341db80 100755
--- a/src/server/game/Maps/MapInstanced.cpp
+++ b/src/server/game/Maps/MapInstanced.cpp
@@ -21,6 +21,7 @@
#include "MapManager.h"
#include "Battleground.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "InstanceSaveMgr.h"
#include "World.h"
#include "Group.h"
@@ -260,6 +261,7 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
if (m_InstancedMaps.size() <= 1 && sWorld->getBoolConfig(CONFIG_GRID_UNLOAD))
{
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId());
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(itr->second->GetId());
// in that case, unload grids of the base map, too
// so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded
Map::UnloadAll();
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index f74d6c4..5014082 100755
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -19,6 +19,7 @@
#ifndef TRINITY_SHAREDDEFINES_H
#define TRINITY_SHAREDDEFINES_H
+#include "DetourNavMesh.h"
#include "Define.h"
#include <cassert>
@@ -3522,4 +3523,33 @@ enum CalendarError
// Calendar - end
+#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids : 1;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+enum NavTerrain
+{
+ NAV_EMPTY = 0x00,
+ NAV_GROUND = 0x01,
+ NAV_MAGMA = 0x02,
+ NAV_SLIME = 0x04,
+ NAV_WATER = 0x08,
+ NAV_UNUSED1 = 0x10,
+ NAV_UNUSED2 = 0x20,
+ NAV_UNUSED3 = 0x40,
+ NAV_UNUSED4 = 0x80
+ // we only have 8 bits
+};
+
#endif
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index cde1764..aad1bed 100755
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -123,9 +123,10 @@ void MotionMaster::DirectClean(bool reset)
{
while (size() > 1)
{
- MovementGenerator *curr = top();
+ MovementGenerator* curr = top();
pop();
- if (curr) DirectDelete(curr);
+ if (curr)
+ DirectDelete(curr);
}
if (needInitTop())
@@ -209,8 +210,11 @@ void MotionMaster::MoveTargetedHome()
Unit *target = ((Creature*)_owner)->GetCharmerOrOwner();
if (target)
{
- sLog->outStaticDebug("Following %s (GUID: %u)", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Creature>(*target,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
+ if (!_owner->m_movedPlayer)
+ {
+ sLog->outStaticDebug("Following %s (GUID: %u)", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow());
+ Mutate(new FollowMovementGenerator<Creature>(*target,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
+ }
}
}
else
@@ -283,18 +287,18 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
}
}
-void MotionMaster::MovePoint(uint32 id, float x, float y, float z)
+void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath)
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outStaticDebug("Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
else
{
sLog->outStaticDebug("Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
}
@@ -337,15 +341,39 @@ void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, floa
float dist = 2 * moveTimeHalf * speedXY;
float max_height = -Movement::computeFallElevation(moveTimeHalf,false,-speedZ);
- _owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist, _owner->GetAngle(srcX, srcY) + M_PI);
+ for (uint8 i = 0; i < 5; ++i)
+ {
+ switch (i)
+ {
+ case 0:
+ _owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist, _owner->GetAngle(srcX, srcY) + M_PI);
+ break;
+ case 1:
+ _owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist*0.8, _owner->GetAngle(srcX, srcY) + M_PI);
+ break;
+ case 2:
+ _owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist*0.5, _owner->GetAngle(srcX, srcY) + M_PI);
+ break;
+ case 3:
+ _owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist*0.25, _owner->GetAngle(srcX, srcY) + M_PI);
+ break;
+ case 4:
+ _owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist*0.1, _owner->GetAngle(srcX, srcY) + M_PI);
+ break;
+ }
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(x,y,z);
- init.SetParabolic(max_height,0);
- init.SetOrientationFixed(true);
- init.SetVelocity(speedXY);
- init.Launch();
- Mutate(new EffectMovementGenerator(0), MOTION_SLOT_CONTROLLED);
+ if (_owner->IsWithinLOS(x, y, z))
+ {
+ Movement::MoveSplineInit init(*_owner);
+ init.MoveTo(x,y,z);
+ init.SetParabolic(max_height,0);
+ init.SetOrientationFixed(true);
+ init.SetVelocity(speedXY);
+ init.Launch();
+ Mutate(new EffectMovementGenerator(0), MOTION_SLOT_CONTROLLED);
+ break;
+ }
+ }
}
void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ)
@@ -381,6 +409,13 @@ void MotionMaster::MoveFall(uint32 id/*=0*/)
{
// use larger distance for vmap height search than in most other cases
float tz = _owner->GetMap()->GetHeight(_owner->GetPhaseMask(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ(), true, MAX_FALL_DISTANCE);
+
+ if (tz <= INVALID_HEIGHT)
+ {
+ // try fall down to ground
+ tz = _owner->GetMap()->GetHeight(_owner->GetPhaseMask(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ(), false, MAX_FALL_DISTANCE);
+ }
+
if (tz <= INVALID_HEIGHT)
{
sLog->outStaticDebug("MotionMaster::MoveFall: unable retrive a proper height at map %u (x: %f, y: %f, z: %f).",
@@ -405,7 +440,7 @@ void MotionMaster::MoveFall(uint32 id/*=0*/)
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
}
-void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
+void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, bool generatePath)
{
if (Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
return;
@@ -413,13 +448,13 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outStaticDebug("Player (GUID: %u) charge point (X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
else
{
sLog->outStaticDebug("Creature (Entry: %u GUID: %u) charge point (X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
}
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index 727f626..918896a 100755
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -154,13 +154,13 @@ class MotionMaster //: private std::stack<MovementGenerator *>
void MoveFleeing(Unit* enemy, uint32 time = 0);
void MovePoint(uint32 id, const Position &pos)
{ MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ); }
- void MovePoint(uint32 id, float x, float y, float z);
+ void MovePoint(uint32 id, float x, float y, float z, bool generatePath = true);
// These two movement types should only be used with creatures having landing/takeoff animations
void MoveLand(uint32 id, Position const& pos);
void MoveTakeoff(uint32 id, Position const& pos);
- void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
+ void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE, bool generatePath = true);
void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ);
void MoveJumpTo(float angle, float speedXY, float speedZ);
void MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id = 0);
diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h
index 0a2ebcf..85e1fec 100755
--- a/src/server/game/Movement/MovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerator.h
@@ -43,6 +43,9 @@ class MovementGenerator
virtual MovementGeneratorType GetMovementGeneratorType() = 0;
virtual void unitSpeedChanged() { }
+
+ // used by Evade code for select point to evade with expected restart default movement
+ virtual bool GetResetPosition(Unit &, float& /*x*/, float& /*y*/, float& /*z*/) { return false; }
};
template<class T, class D>
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
index 54a68f9..789dc7d 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
@@ -19,6 +19,7 @@
#include "Creature.h"
#include "MapManager.h"
#include "ConfusedMovementGenerator.h"
+#include "PathFinderMovementGenerator.h"
#include "VMapFactory.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -32,79 +33,18 @@
template<class T>
void ConfusedMovementGenerator<T>::Initialize(T &unit)
{
- float const wander_distance = 4;
- float x = unit.GetPositionX();
- float y = unit.GetPositionY();
- float z = unit.GetPositionZ();
-
- Map const* map = unit.GetBaseMap();
-
- i_nextMove = 1;
-
- bool is_water_ok, is_land_ok;
- _InitSpecific(unit, is_water_ok, is_land_ok);
-
- for (uint8 idx = 0; idx < MAX_CONF_WAYPOINTS + 1; ++idx)
- {
- float wanderX = x + (wander_distance * (float)rand_norm() - wander_distance/2);
- float wanderY = y + (wander_distance * (float)rand_norm() - wander_distance/2);
-
- // prevent invalid coordinates generation
- Trinity::NormalizeMapCoord(wanderX);
- Trinity::NormalizeMapCoord(wanderY);
-
- if (unit.IsWithinLOS(wanderX, wanderY, z))
- {
- bool is_water = map->IsInWater(wanderX, wanderY, z);
-
- if ((is_water && !is_water_ok) || (!is_water && !is_land_ok))
- {
- //! Cannot use coordinates outside our InhabitType. Use the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
- }
- else
- {
- //! Trying to access path outside line of sight. Skip this by using the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
-
- unit.UpdateAllowedPositionZ(wanderX, wanderY, z);
-
- //! Positions are fine - apply them to this waypoint
- i_waypoints[idx][0] = wanderX;
- i_waypoints[idx][1] = wanderY;
- i_waypoints[idx][2] = z;
- }
-
+ unit.GetPosition(i_x, i_y, i_z);
unit.StopMoving();
unit.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
}
-template<>
-void ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = creature.canSwim();
- is_land_ok = creature.canWalk();
-}
-
-template<>
-void ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = true;
- is_land_ok = true;
-}
-
template<class T>
void ConfusedMovementGenerator<T>::Reset(T &unit)
{
- i_nextMove = 1;
i_nextMoveTime.Reset(0);
- unit.StopMoving();
unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit.StopMoving();
}
template<class T>
@@ -119,10 +59,7 @@ bool ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
if (unit.movespline->Finalized())
- {
- i_nextMove = urand(1, MAX_CONF_WAYPOINTS);
- i_nextMoveTime.Reset(urand(500, 1200)); // Guessed
- }
+ i_nextMoveTime.Reset(urand(800, 1500));
}
else
{
@@ -133,12 +70,30 @@ bool ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
// start moving
unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
- ASSERT(i_nextMove <= MAX_CONF_WAYPOINTS);
- float x = i_waypoints[i_nextMove][0];
- float y = i_waypoints[i_nextMove][1];
- float z = i_waypoints[i_nextMove][2];
+ float x = i_x + 10.0f*((float)rand_norm() - 0.5f);
+ float y = i_y + 10.0f*((float)rand_norm() - 0.5f);
+ float z = i_z + 0.5f;
+
+ Trinity::NormalizeMapCoord(x);
+ Trinity::NormalizeMapCoord(y);
+
+ unit.UpdateAllowedPositionZ(x, y, z);
+
+ if (z <= INVALID_HEIGHT)
+ i_z = unit.GetBaseMap()->GetHeight(unit.GetPhaseMask(), x, y, MAX_HEIGHT) + 2.0f;
+
+ PathFinderMovementGenerator path(&unit);
+ path.setPathLengthLimit(30.0f);
+ path.setUseStrightPath(false);
+
+ if (!unit.IsWithinLOS(x, y, z) || !path.calculate(x, y, z) || path.getPathType() & PATHFIND_NOPATH)
+ {
+ i_nextMoveTime.Reset(urand(200, 500));
+ return true;
+ }
+
Movement::MoveSplineInit init(unit);
- init.MoveTo(x, y, z);
+ init.MovebyPath(path.getPath());
init.SetWalk(true);
init.Launch();
}
@@ -152,6 +107,7 @@ void ConfusedMovementGenerator<Player>::Finalize(Player &unit)
{
unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
unit.ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit.StopMoving();
}
template<>
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
index 7f2226e..72ffd7b 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
@@ -22,8 +22,6 @@
#include "MovementGenerator.h"
#include "Timer.h"
-#define MAX_CONF_WAYPOINTS 24 //! Allows a twelve second confusion if i_nextMove always is the absolute minimum timer.
-
template<class T>
class ConfusedMovementGenerator : public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
{
@@ -37,10 +35,8 @@ class ConfusedMovementGenerator : public MovementGeneratorMedium< T, ConfusedMov
MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
private:
- void _InitSpecific(T &, bool &, bool &);
TimeTracker i_nextMoveTime;
- float i_waypoints[MAX_CONF_WAYPOINTS+1][3];
- uint32 i_nextMove;
+ float i_x, i_y, i_z;
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
index 08e27ab..0f54f63 100755
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
@@ -21,6 +21,7 @@
#include "MapManager.h"
#include "FleeingMovementGenerator.h"
#include "ObjectAccessor.h"
+#include "PathFinderMovementGenerator.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -45,10 +46,21 @@ void FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
owner.AddUnitState(UNIT_STATE_FLEEING_MOVE);
+ PathFinderMovementGenerator path(&owner);
+ path.setPathLengthLimit(MAX_QUIET_DISTANCE * 2);
+ path.setUseStrightPath(false);
+
+ if (!path.calculate(x, y, z) || path.getPathType() & PATHFIND_NOPATH)
+ {
+ i_nextCheckTime.Reset(urand(500, 1500));
+ return;
+ }
+
Movement::MoveSplineInit init(owner);
- init.MoveTo(x,y,z);
+ init.MovebyPath(path.getPath());
init.SetWalk(false);
- init.Launch();
+ int32 traveltime = init.Launch();
+ i_nextCheckTime.Reset(traveltime + urand(100, 1500));
}
template<class T>
@@ -64,12 +76,12 @@ bool FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float
float temp_x, temp_y, angle;
const Map* _map = owner.GetBaseMap();
// primitive path-finding
- for (uint8 i = 0; i < 18; ++i)
+ for (uint8 i = 0; i < 25; ++i)
{
- if (i_only_forward && i > 2)
+ if (i_only_forward && i > 3)
break;
- float distance = 5.0f;
+ float distance = MAX_QUIET_DISTANCE;
switch (i)
{
@@ -85,58 +97,92 @@ bool FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float
distance /= 4;
break;
case 3:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
+ angle = i_cur_angle;
+ distance /= 8;
break;
case 4:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
+ angle = i_cur_angle + static_cast<float>(M_PI/4);
break;
case 5:
+ angle = i_cur_angle - static_cast<float>(M_PI/4);
+ break;
+ case 6:
angle = i_cur_angle + static_cast<float>(M_PI/4);
distance /= 2;
break;
- case 6:
+ case 7:
angle = i_cur_angle - static_cast<float>(M_PI/4);
distance /= 2;
break;
- case 7:
+ case 8:
+ angle = i_cur_angle + static_cast<float>(3*M_PI/4);
+ distance /= 8;
+ break;
+ case 9:
+ angle = i_cur_angle - static_cast<float>(3*M_PI/4);
+ distance /= 8;
+ break;
+ case 10:
+ angle = i_cur_angle + static_cast<float>(M_PI/4);
+ distance /= 8;
+ break;
+ case 11:
+ angle = i_cur_angle - static_cast<float>(M_PI/4);
+ distance /= 8;
+ break;
+ case 12:
angle = i_cur_angle + static_cast<float>(M_PI/2);
break;
- case 8:
+ case 13:
angle = i_cur_angle - static_cast<float>(M_PI/2);
break;
- case 9:
+ case 14:
angle = i_cur_angle + static_cast<float>(M_PI/2);
distance /= 2;
break;
- case 10:
+ case 15:
angle = i_cur_angle - static_cast<float>(M_PI/2);
distance /= 2;
break;
- case 11:
+ case 16:
angle = i_cur_angle + static_cast<float>(M_PI/4);
distance /= 4;
break;
- case 12:
+ case 17:
angle = i_cur_angle - static_cast<float>(M_PI/4);
distance /= 4;
break;
- case 13:
+ case 18:
angle = i_cur_angle + static_cast<float>(M_PI/2);
- distance /= 4;
+ distance /= 8;
break;
- case 14:
+ case 19:
angle = i_cur_angle - static_cast<float>(M_PI/2);
- distance /= 4;
+ distance /= 8;
break;
- case 15:
+ case 20:
+ angle = i_cur_angle + static_cast<float>(3*M_PI/4);
+ break;
+ case 21:
+ angle = i_cur_angle - static_cast<float>(3*M_PI/4);
+ break;
+ case 22:
angle = i_cur_angle + static_cast<float>(3*M_PI/4);
distance /= 2;
break;
- case 16:
+ case 23:
angle = i_cur_angle - static_cast<float>(3*M_PI/4);
distance /= 2;
break;
- case 17:
+ case 24:
+ angle = i_cur_angle + static_cast<float>(3*M_PI/4);
+ distance /= 4;
+ break;
+ case 25:
+ angle = i_cur_angle - static_cast<float>(3*M_PI/4);
+ distance /= 4;
+ break;
+ case 26:
angle = i_cur_angle + static_cast<float>(M_PI);
distance /= 2;
break;
@@ -330,6 +376,7 @@ void FleeingMovementGenerator<Player>::Finalize(Player &owner)
{
owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ owner.StopMoving();
}
template<>
@@ -339,6 +386,7 @@ void FleeingMovementGenerator<Creature>::Finalize(Creature &owner)
owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
if (owner.getVictim())
owner.SetTarget(owner.getVictim()->GetGUID());
+ owner.StopMoving();
}
template<class T>
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
index a8bdb69..814352f 100755
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
@@ -40,12 +40,12 @@ void HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner)
Movement::MoveSplineInit init(owner);
float x, y, z, o;
// at apply we can select more nice return points base at current movegen
- //if (owner.GetMotionMaster()->empty() || !owner.GetMotionMaster()->top()->GetResetPosition(owner,x,y,z))
- //{
- owner.GetHomePosition(x, y, z, o);
- init.SetFacing(o);
- //}
- init.MoveTo(x,y,z);
+ if (owner.GetMotionMaster()->empty() || !owner.GetMotionMaster()->top()->GetResetPosition(owner,x,y,z))
+ {
+ owner.GetHomePosition(x, y, z, o);
+ init.SetFacing(o);
+ }
+ init.MoveTo(x, y, z, true);
init.SetWalk(false);
init.Launch();
@@ -61,9 +61,9 @@ bool HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32 /*tim
void HomeMovementGenerator<Creature>::Finalize(Creature& owner)
{
+ owner.ClearUnitState(UNIT_STATE_EVADE);
if (arrived)
{
- owner.ClearUnitState(UNIT_STATE_EVADE);
owner.SetWalk(true);
owner.LoadCreaturesAddon(true);
owner.AI()->JustReachedHome();
diff --git a/src/server/game/Movement/MovementGenerators/PathFinderMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PathFinderMovementGenerator.cpp
new file mode 100644
index 0000000..11da46f
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/PathFinderMovementGenerator.cpp
@@ -0,0 +1,781 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathFinderMovementGenerator.h"
+#include "Map.h"
+#include "Creature.h"
+#include "MMapFactory.h"
+#include "MMapManager.h"
+#include "Log.h"
+
+#include "DetourCommon.h"
+#include "DetourNavMeshQuery.h"
+
+////////////////// PathFinderMovementGenerator //////////////////
+PathFinderMovementGenerator::PathFinderMovementGenerator(const Unit* owner) :
+ m_polyLength(0), m_type(PATHFIND_BLANK),
+ m_useStraightPath(false), m_forceDestination(false), m_pointPathLimit(MAX_POINT_PATH_LENGTH),
+ m_sourceUnit(owner), m_navMesh(NULL), m_navMeshQuery(NULL)
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathFinderMovementGenerator::PathFinderMovementGenerator for %u \n", m_sourceUnit->GetGUIDLow());
+
+ uint32 mapId = m_sourceUnit->GetMapId();
+ if (MMAP::MMapFactory::IsPathfindingEnabled(mapId))
+ {
+ MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager();
+ m_navMesh = mmap->GetNavMesh(mapId);
+ m_navMeshQuery = mmap->GetNavMeshQuery(mapId, m_sourceUnit->GetInstanceId());
+ }
+
+ createFilter();
+}
+
+PathFinderMovementGenerator::~PathFinderMovementGenerator()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathFinderMovementGenerator::~PathFinderMovementGenerator() for %u \n", m_sourceUnit->GetGUIDLow());
+}
+
+bool PathFinderMovementGenerator::calculate(float destX, float destY, float destZ, bool forceDest)
+{
+ if (!Trinity::IsValidMapCoord(destX, destY, destZ) ||
+ !Trinity::IsValidMapCoord(m_sourceUnit->GetPositionX(), m_sourceUnit->GetPositionY(), m_sourceUnit->GetPositionZ()))
+ return false;
+
+ Vector3 oldDest = getEndPosition();
+ Vector3 dest(destX, destY, destZ);
+ setEndPosition(dest);
+
+ float x, y, z;
+ m_sourceUnit->GetPosition(x, y, z);
+ Vector3 start(x, y, z);
+ setStartPosition(start);
+
+ m_forceDestination = forceDest;
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathFinderMovementGenerator::calculate() for %u \n", m_sourceUnit->GetGUIDLow());
+
+ // make sure navMesh works - we can run on map w/o mmap
+ // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
+ if (!m_navMesh || !m_navMeshQuery || m_sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) ||
+ !HaveTile(start) || !HaveTile(dest))
+ {
+ BuildShortcut();
+ m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return true;
+ }
+
+ updateFilter();
+
+ // check if destination moved - if not we can optimize something here
+ // we are following old, precalculated path?
+ float dist = m_sourceUnit->GetObjectSize();
+ if (inRange(oldDest, dest, dist, dist) && m_pathPoints.size() > 2)
+ {
+ // our target is not moving - we just coming closer
+ // we are moving on precalculated path - enjoy the ride
+ sLog->outStaticDebug("++ PathFinderMovementGenerator::calculate:: precalculated path\n");
+
+ m_pathPoints.erase(m_pathPoints.begin());
+ return false;
+ }
+ else
+ {
+ // target moved, so we need to update the poly path
+ TRINITY_GUARD(ACE_Thread_Mutex, Lock);
+ BuildPolyPath(start, dest);
+ return true;
+ }
+}
+
+dtPolyRef PathFinderMovementGenerator::getPathPolyByPosition(const dtPolyRef *polyPath, uint32 polyPathSize, const float* point, float *distance) const
+{
+ if (!polyPath || !polyPathSize)
+ return INVALID_POLYREF;
+
+ dtPolyRef nearestPoly = INVALID_POLYREF;
+ float minDist2d = FLT_MAX;
+ float minDist3d = 0.0f;
+
+ for (uint32 i = 0; i < polyPathSize; ++i)
+ {
+ float closestPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint))
+ continue;
+
+ float d = dtVdist2DSqr(point, closestPoint);
+ if (d < minDist2d)
+ {
+ minDist2d = d;
+ nearestPoly = polyPath[i];
+ minDist3d = dtVdistSqr(point, closestPoint);
+ }
+
+ if(minDist2d < 1.0f) // shortcut out - close enough for us
+ break;
+ }
+
+ if (distance)
+ *distance = dtSqrt(minDist3d);
+
+ return (minDist2d < 3.0f) ? nearestPoly : INVALID_POLYREF;
+}
+
+dtPolyRef PathFinderMovementGenerator::getPolyByLocation(const float* point, float *distance) const
+{
+ // first we check the current path
+ // if the current path doesn't contain the current poly,
+ // we need to use the expensive navMesh.findNearestPoly
+ dtPolyRef polyRef = getPathPolyByPosition(m_pathPolyRefs, m_polyLength, point, distance);
+ if (polyRef != INVALID_POLYREF)
+ return polyRef;
+
+ // we don't have it in our old path
+ // try to get it by findNearestPoly()
+ // first try with low search box
+ float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; // bounds of poly search area
+ float closestPoint[VERTEX_SIZE] = {0.0f, 0.0f, 0.0f};
+ dtStatus result = m_navMeshQuery->findNearestPoly(point, extents, &m_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ // still nothing ..
+ // try with bigger search box
+ extents[1] = 200.0f;
+ result = m_navMeshQuery->findNearestPoly(point, extents, &m_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ return INVALID_POLYREF;
+}
+
+void PathFinderMovementGenerator::BuildPolyPath(const Vector3 &startPos, const Vector3 &endPos)
+{
+ // *** getting start/end poly logic ***
+
+ float distToStartPoly, distToEndPoly;
+ float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x};
+ float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x};
+
+ dtPolyRef startPoly = getPolyByLocation(startPoint, &distToStartPoly);
+ dtPolyRef endPoly = getPolyByLocation(endPoint, &distToEndPoly);
+
+ // we have a hole in our mesh
+ // make shortcut path and mark it as NOPATH ( with flying exception )
+ // its up to caller how he will use this info
+ if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n");
+ BuildShortcut();
+ m_type = (m_sourceUnit->GetTypeId() == TYPEID_UNIT && ((Creature*)m_sourceUnit)->CanFly())
+ ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH;
+ return;
+ }
+
+ // we may need a better number here
+ bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f);
+ if (farFromPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly);
+
+ bool buildShotrcut = false;
+ if (m_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* owner = (Creature*)m_sourceUnit;
+
+ Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos;
+ if (m_sourceUnit->GetBaseMap()->IsUnderWater(p.x, p.y, p.z))
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: underWater case\n");
+ if (owner->canSwim())
+ buildShotrcut = true;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: flying case\n");
+ if (owner->CanFly())
+ buildShotrcut = true;
+ }
+ }
+
+ if (buildShotrcut)
+ {
+ BuildShortcut();
+ m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return;
+ }
+ else
+ {
+ float closestPoint[VERTEX_SIZE];
+ // we may want to use closestPointOnPolyBoundary instead
+ if (DT_SUCCESS == m_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint))
+ {
+ dtVcopy(endPoint, closestPoint);
+ setActualEndPosition(Vector3(endPoint[2],endPoint[0],endPoint[1]));
+ }
+
+ m_type = PATHFIND_INCOMPLETE;
+ }
+ }
+
+ // *** poly path generating logic ***
+
+ // start and end are on same polygon
+ // just need to move in straight line
+ if (startPoly == endPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == endPoly)\n");
+
+ BuildShortcut();
+
+ m_pathPolyRefs[0] = startPoly;
+ m_polyLength = 1;
+
+ m_type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL;
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: path type %d\n", m_type);
+ return;
+ }
+
+ // look for startPoly/endPoly in current path
+ // TODO: we can merge it with getPathPolyByPosition() loop
+ bool startPolyFound = false;
+ bool endPolyFound = false;
+ uint32 pathStartIndex, pathEndIndex;
+
+ if (m_polyLength)
+ {
+ for (pathStartIndex = 0; pathStartIndex < m_polyLength; ++pathStartIndex)
+ {
+ // here to carch few bugs
+ ASSERT(m_pathPolyRefs[pathStartIndex] != INVALID_POLYREF);
+
+ if (m_pathPolyRefs[pathStartIndex] == startPoly)
+ {
+ startPolyFound = true;
+ break;
+ }
+ }
+
+ for (pathEndIndex = m_polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex)
+ if (m_pathPolyRefs[pathEndIndex] == endPoly)
+ {
+ endPolyFound = true;
+ break;
+ }
+ }
+
+ if (startPolyFound && endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n");
+
+ // we moved along the path and the target did not move out of our old poly-path
+ // our path is a simple subpath case, we have all the data we need
+ // just "cut" it out
+
+ m_polyLength = pathEndIndex - pathStartIndex + 1;
+ memmove(m_pathPolyRefs, m_pathPolyRefs+pathStartIndex, m_polyLength*sizeof(dtPolyRef));
+ }
+ else if (startPolyFound && !endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n");
+
+ // we are moving on the old path but target moved out
+ // so we have atleast part of poly-path ready
+
+ m_polyLength -= pathStartIndex;
+
+ // try to adjust the suffix of the path instead of recalculating entire length
+ // at given interval the target cannot get too far from its last location
+ // thus we have less poly to cover
+ // sub-path of optimal path is optimal
+
+ // take ~80% of the original length
+ // TODO : play with the values here
+ uint32 prefixPolyLength = uint32(m_polyLength*0.8f + 0.5f);
+ memmove(m_pathPolyRefs, m_pathPolyRefs+pathStartIndex, prefixPolyLength*sizeof(dtPolyRef));
+
+ dtPolyRef suffixStartPoly = m_pathPolyRefs[prefixPolyLength-1];
+
+ // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
+ float suffixEndPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
+ // try to recover by using prev polyref
+ --prefixPolyLength;
+ suffixStartPoly = m_pathPolyRefs[prefixPolyLength-1];
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // suffixStartPoly is still invalid, error state
+ BuildShortcut();
+ m_type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // generate suffix
+ uint32 suffixPolyLength = 0;
+ dtStatus dtResult = m_navMeshQuery->findPath(
+ suffixStartPoly, // start polygon
+ endPoly, // end polygon
+ suffixEndPoint, // start position
+ endPoint, // end position
+ &m_filter, // polygon search filter
+ m_pathPolyRefs + prefixPolyLength - 1, // [out] path
+ (int*)&suffixPolyLength,
+ MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path
+
+ if (!suffixPolyLength || dtResult != DT_SUCCESS)
+ {
+ // this is probably an error state, but we'll leave it
+ // and hopefully recover on the next Update
+ // we still need to copy our preffix
+ sLog->outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUIDLow());
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n",m_polyLength, prefixPolyLength, suffixPolyLength);
+
+ // new path = prefix + suffix - overlap
+ m_polyLength = prefixPolyLength + suffixPolyLength - 1;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n");
+
+ // either we have no path at all -> first run
+ // or something went really wrong -> we aren't moving along the path to the target
+ // just generate new path
+
+ // free and invalidate old path data
+ clear();
+
+ dtStatus dtResult = m_navMeshQuery->findPath(
+ startPoly, // start polygon
+ endPoly, // end polygon
+ startPoint, // start position
+ endPoint, // end position
+ &m_filter, // polygon search filter
+ m_pathPolyRefs, // [out] path
+ (int*)&m_polyLength,
+ MAX_PATH_LENGTH); // max number of polygons in output path
+
+ if (!m_polyLength || dtResult != DT_SUCCESS)
+ {
+ // only happens if we passed bad data to findPath(), or navmesh is messed up
+ sLog->outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUIDLow());
+ BuildShortcut();
+ m_type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // by now we know what type of path we can get
+ if (m_pathPolyRefs[m_polyLength - 1] == endPoly && !(m_type & PATHFIND_INCOMPLETE))
+ m_type = PATHFIND_NORMAL;
+ else
+ m_type = PATHFIND_INCOMPLETE;
+
+ // generate the point-path out of our up-to-date poly-path
+ BuildPointPath(startPoint, endPoint);
+}
+
+void PathFinderMovementGenerator::BuildPointPath(const float *startPoint, const float *endPoint)
+{
+ float pathPoints[MAX_POINT_PATH_LENGTH*VERTEX_SIZE];
+ uint32 pointCount = 0;
+ dtStatus dtResult = DT_FAILURE;
+ if (m_useStraightPath)
+ {
+ dtResult = m_navMeshQuery->findStraightPath(
+ startPoint, // start position
+ endPoint, // end position
+ m_pathPolyRefs, // current path
+ m_polyLength, // lenth of current path
+ pathPoints, // [out] path corner points
+ NULL, // [out] flags
+ NULL, // [out] shortened path
+ (int*)&pointCount,
+ m_pointPathLimit); // maximum number of points/polygons to use
+ }
+ else
+ {
+ dtResult = findSmoothPath(
+ startPoint, // start position
+ endPoint, // end position
+ m_pathPolyRefs, // current path
+ m_polyLength, // length of current path
+ pathPoints, // [out] path corner points
+ (int*)&pointCount,
+ m_pointPathLimit); // maximum number of points
+ }
+
+ if (pointCount < 2 || dtResult != DT_SUCCESS)
+ {
+ // only happens if pass bad data to findStraightPath or navmesh is broken
+ // single point paths can be generated here
+ // TODO : check the exact cases
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathFinderMovementGenerator::BuildPointPath FAILED! path sized %d returned\n", pointCount);
+ BuildShortcut();
+ m_type = PATHFIND_NOPATH;
+ return;
+ }
+
+ m_pathPoints.resize(pointCount);
+ for (uint32 i = 0; i < pointCount; ++i)
+ m_pathPoints[i] = Vector3(pathPoints[i*VERTEX_SIZE+2], pathPoints[i*VERTEX_SIZE], pathPoints[i*VERTEX_SIZE+1]);
+
+ // first point is always our current location - we need the next one
+ setActualEndPosition(m_pathPoints[pointCount-1]);
+
+ // force the given destination, if needed
+ if(m_forceDestination &&
+ (!(m_type & PATHFIND_NORMAL) || !inRange(getEndPosition(), getActualEndPosition(), 1.0f, 1.0f)))
+ {
+ // we may want to keep partial subpath
+ if(dist3DSqr(getActualEndPosition(), getEndPosition()) <
+ 0.3f * dist3DSqr(getStartPosition(), getEndPosition()))
+ {
+ setActualEndPosition(getEndPosition());
+ m_pathPoints[m_pathPoints.size()-1] = getEndPosition();
+ }
+ else
+ {
+ setActualEndPosition(getEndPosition());
+ BuildShortcut();
+ }
+
+ m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathFinderMovementGenerator::BuildPointPath path type %d size %d poly-size %d\n", m_type, pointCount, m_polyLength);
+}
+
+void PathFinderMovementGenerator::BuildShortcut()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildShortcut :: making shortcut\n");
+
+ clear();
+
+ // make two point path, our curr pos is the start, and dest is the end
+ m_pathPoints.resize(2);
+
+ // set start and a default next position
+ m_pathPoints[0] = getStartPosition();
+ m_pathPoints[1] = getActualEndPosition();
+
+ m_type = PATHFIND_SHORTCUT;
+}
+
+void PathFinderMovementGenerator::createFilter()
+{
+ uint16 includeFlags = 0;
+ uint16 excludeFlags = 0;
+
+ if (m_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* creature = (Creature*)m_sourceUnit;
+ if (creature->canWalk())
+ includeFlags |= NAV_GROUND; // walk
+
+ // creatures don't take environmental damage
+ if (creature->canSwim())
+ includeFlags |= (NAV_WATER | NAV_MAGMA | NAV_SLIME); // swim
+ }
+ else if (m_sourceUnit->GetTypeId() == TYPEID_PLAYER)
+ {
+ // perfect support not possible, just stay 'safe'
+ includeFlags |= (NAV_GROUND | NAV_WATER);
+ }
+
+ m_filter.setIncludeFlags(includeFlags);
+ m_filter.setExcludeFlags(excludeFlags);
+
+ updateFilter();
+}
+
+void PathFinderMovementGenerator::updateFilter()
+{
+ // allow creatures to cheat and use different movement types if they are moved
+ // forcefully into terrain they can't normally move in
+ if (m_sourceUnit->IsInWater() || m_sourceUnit->IsUnderWater())
+ {
+ uint16 includedFlags = m_filter.getIncludeFlags();
+ includedFlags |= getNavTerrain(m_sourceUnit->GetPositionX(),
+ m_sourceUnit->GetPositionY(),
+ m_sourceUnit->GetPositionZ());
+
+ m_filter.setIncludeFlags(includedFlags);
+ }
+}
+
+NavTerrain PathFinderMovementGenerator::getNavTerrain(float x, float y, float z)
+{
+ LiquidData data;
+ m_sourceUnit->GetBaseMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data);
+
+ switch (data.type_flags)
+ {
+ case MAP_LIQUID_TYPE_WATER:
+ case MAP_LIQUID_TYPE_OCEAN:
+ return NAV_WATER;
+ case MAP_LIQUID_TYPE_MAGMA:
+ return NAV_MAGMA;
+ case MAP_LIQUID_TYPE_SLIME:
+ return NAV_SLIME;
+ default:
+ return NAV_GROUND;
+ }
+}
+
+bool PathFinderMovementGenerator::HaveTile(const Vector3 &p) const
+{
+ int tx, ty;
+ float point[VERTEX_SIZE] = {p.y, p.z, p.x};
+
+ m_navMesh->calcTileLoc(point, &tx, &ty);
+ return (m_navMesh->getTileAt(tx, ty) != NULL);
+}
+
+uint32 PathFinderMovementGenerator::fixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath,
+ const dtPolyRef* visited, uint32 nvisited)
+{
+ int32 furthestPath = -1;
+ int32 furthestVisited = -1;
+
+ // Find furthest common polygon.
+ for (int32 i = npath-1; i >= 0; --i)
+ {
+ bool found = false;
+ for (int32 j = nvisited-1; j >= 0; --j)
+ {
+ if (path[i] == visited[j])
+ {
+ furthestPath = i;
+ furthestVisited = j;
+ found = true;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ // If no intersection found just return current path.
+ if (furthestPath == -1 || furthestVisited == -1)
+ return npath;
+
+ // Concatenate paths.
+
+ // Adjust beginning of the buffer to include the visited.
+ uint32 req = nvisited - furthestVisited;
+ uint32 orig = uint32(furthestPath+1) < npath ? furthestPath+1 : npath;
+ uint32 size = npath-orig > 0 ? npath-orig : 0;
+ if (req+size > maxPath)
+ size = maxPath-req;
+
+ if (size)
+ memmove(path+req, path+orig, size*sizeof(dtPolyRef));
+
+ // Store visited
+ for (uint32 i = 0; i < req; ++i)
+ path[i] = visited[(nvisited-1)-i];
+
+ return req+size;
+}
+
+bool PathFinderMovementGenerator::getSteerTarget(const float* startPos, const float* endPos,
+ float minTargetDist, const dtPolyRef* path, uint32 pathSize,
+ float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef)
+{
+ // Find steer target.
+ static const uint32 MAX_STEER_POINTS = 3;
+ float steerPath[MAX_STEER_POINTS*VERTEX_SIZE];
+ unsigned char steerPathFlags[MAX_STEER_POINTS];
+ dtPolyRef steerPathPolys[MAX_STEER_POINTS];
+ uint32 nsteerPath = 0;
+ dtStatus dtResult = m_navMeshQuery->findStraightPath(startPos, endPos, path, pathSize,
+ steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS);
+ if (!nsteerPath || DT_SUCCESS != dtResult)
+ return false;
+
+ // Find vertex far enough to steer to.
+ uint32 ns = 0;
+ while (ns < nsteerPath)
+ {
+ // Stop at Off-Mesh link or when point is further than slop away.
+ if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
+ !inRangeYZX(&steerPath[ns*VERTEX_SIZE], startPos, minTargetDist, 1000.0f))
+ break;
+ ns++;
+ }
+ // Failed to find good point to steer to.
+ if (ns >= nsteerPath)
+ return false;
+
+ dtVcopy(steerPos, &steerPath[ns*VERTEX_SIZE]);
+ steerPos[1] = startPos[1]; // keep Z value
+ steerPosFlag = steerPathFlags[ns];
+ steerPosRef = steerPathPolys[ns];
+
+ return true;
+}
+
+dtStatus PathFinderMovementGenerator::findSmoothPath(const float* startPos, const float* endPos,
+ const dtPolyRef* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 maxSmoothPathSize)
+{
+ *smoothPathSize = 0;
+ uint32 nsmoothPath = 0;
+
+ dtPolyRef polys[MAX_PATH_LENGTH];
+ memcpy(polys, polyPath, sizeof(dtPolyRef)*polyPathSize);
+ uint32 npolys = polyPathSize;
+
+ float iterPos[VERTEX_SIZE], targetPos[VERTEX_SIZE];
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos))
+ return DT_FAILURE;
+
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPolyBoundary(polys[npolys-1], endPos, targetPos))
+ return DT_FAILURE;
+
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+
+ // Move towards target a small advancement at a time until target reached or
+ // when ran out of memory to store the path.
+ while (npolys && nsmoothPath < maxSmoothPathSize)
+ {
+ // Find location to steer towards.
+ float steerPos[VERTEX_SIZE];
+ unsigned char steerPosFlag;
+ dtPolyRef steerPosRef = INVALID_POLYREF;
+
+ if (!getSteerTarget(iterPos, targetPos, SMOOTH_PATH_SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef))
+ break;
+
+ bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END);
+ bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION);
+
+ // Find movement delta.
+ float delta[VERTEX_SIZE];
+ dtVsub(delta, steerPos, iterPos);
+ float len = dtSqrt(dtVdot(delta,delta));
+ // If the steer target is end of path or off-mesh link, do not move past the location.
+ if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE)
+ len = 1.0f;
+ else
+ len = SMOOTH_PATH_STEP_SIZE / len;
+
+ float moveTgt[VERTEX_SIZE];
+ dtVmad(moveTgt, iterPos, delta, len);
+
+ // Move
+ float result[VERTEX_SIZE];
+ const static uint32 MAX_VISIT_POLY = 16;
+ dtPolyRef visited[MAX_VISIT_POLY];
+
+ uint32 nvisited = 0;
+ m_navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &m_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY);
+ npolys = fixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited);
+
+ m_navMeshQuery->getPolyHeight(polys[0], result, &result[1]);
+ result[1] += 0.5f;
+ dtVcopy(iterPos, result);
+
+ // Handle end of path and off-mesh links when close enough.
+ if (endOfPath && inRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Reached end of path.
+ dtVcopy(iterPos, targetPos);
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ break;
+ }
+ else if (offMeshConnection && inRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Advance the path up to and over the off-mesh connection.
+ dtPolyRef prevRef = INVALID_POLYREF;
+ dtPolyRef polyRef = polys[0];
+ uint32 npos = 0;
+ while (npos < npolys && polyRef != steerPosRef)
+ {
+ prevRef = polyRef;
+ polyRef = polys[npos];
+ npos++;
+ }
+
+ for (uint32 i = npos; i < npolys; ++i)
+ polys[i-npos] = polys[i];
+
+ npolys -= npos;
+
+ // Handle the connection.
+ float startPos[VERTEX_SIZE], endPos[VERTEX_SIZE];
+ if (DT_SUCCESS == m_navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos))
+ {
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], startPos);
+ nsmoothPath++;
+ }
+ // Move position at the other side of the off-mesh link.
+ dtVcopy(iterPos, endPos);
+ m_navMeshQuery->getPolyHeight(polys[0], iterPos, &iterPos[1]);
+ iterPos[1] += 0.5f;
+ }
+ }
+
+ // Store results.
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ }
+
+ *smoothPathSize = nsmoothPath;
+
+ // this is most likely a loop
+ return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE;
+}
+
+bool PathFinderMovementGenerator::inRangeYZX(const float* v1, const float* v2, float r, float h) const
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1]; // elevation
+ const float dz = v2[2] - v1[2];
+ return (dx*dx + dz*dz) < r*r && fabsf(dy) < h;
+}
+
+bool PathFinderMovementGenerator::inRange(const Vector3 &p1, const Vector3 &p2, float r, float h) const
+{
+ Vector3 d = p1-p2;
+ return (d.x*d.x + d.y*d.y) < r*r && fabsf(d.z) < h;
+}
+
+float PathFinderMovementGenerator::dist3DSqr(const Vector3 &p1, const Vector3 &p2) const
+{
+ return (p1-p2).squaredLength();
+}
diff --git a/src/server/game/Movement/MovementGenerators/PathFinderMovementGenerator.h b/src/server/game/Movement/MovementGenerators/PathFinderMovementGenerator.h
new file mode 100644
index 0000000..ca2112c
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/PathFinderMovementGenerator.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PATH_INFO_H
+#define _PATH_INFO_H
+
+#include <ace/RW_Thread_Mutex.h>
+#include <ace/Thread_Mutex.h>
+#include "SharedDefines.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+#include "MoveSplineInitArgs.h"
+
+using Movement::Vector3;
+using Movement::PointsArray;
+
+class Unit;
+
+// 74*4.0f=296y number_of_points*interval = max_path_len
+// this is way more than actual evade range
+// I think we can safely cut those down even more
+#define MAX_PATH_LENGTH 74
+#define MAX_POINT_PATH_LENGTH 74
+
+#define SMOOTH_PATH_STEP_SIZE 4.0f
+#define SMOOTH_PATH_SLOP 0.3f
+
+#define VERTEX_SIZE 3
+#define INVALID_POLYREF 0
+
+enum PathType
+{
+ PATHFIND_BLANK = 0x0000, // path not built yet
+ PATHFIND_NORMAL = 0x0001, // normal path
+ PATHFIND_SHORTCUT = 0x0002, // travel through obstacles, terrain, air, etc (old behavior)
+ PATHFIND_INCOMPLETE = 0x0004, // we have partial path to follow - getting closer to target
+ PATHFIND_NOPATH = 0x0008, // no valid path at all or error in generating one
+ PATHFIND_NOT_USING_PATH = 0x0010 // used when we are either flying/swiming or on map w/o mmaps
+};
+
+class PathFinderMovementGenerator
+{
+ public:
+ PathFinderMovementGenerator(Unit const* owner);
+ ~PathFinderMovementGenerator();
+
+ // Calculate the path from owner to given destination
+ // return: true if new path was calculated, false otherwise (no change needed)
+ bool calculate(float destX, float destY, float destZ, bool forceDest = false);
+
+ // option setters - use optional
+ void setUseStrightPath(bool useStraightPath) { m_useStraightPath = useStraightPath; };
+ void setPathLengthLimit(float distance) { m_pointPathLimit = std::min<uint32>(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); };
+
+ // result getters
+ Vector3 getStartPosition() const { return m_startPosition; }
+ Vector3 getEndPosition() const { return m_endPosition; }
+ Vector3 getActualEndPosition() const { return m_actualEndPosition; }
+
+ PointsArray& getPath() { return m_pathPoints; }
+ PathType getPathType() const { return m_type; }
+
+ private:
+
+ dtPolyRef m_pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
+ uint32 m_polyLength; // number of polygons in the path
+
+ PointsArray m_pathPoints; // our actual (x,y,z) path to the target
+ PathType m_type; // tells what kind of path this is
+
+ bool m_useStraightPath; // type of path will be generated
+ bool m_forceDestination; // when set, we will always arrive at given point
+ uint32 m_pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
+
+ Vector3 m_startPosition; // {x, y, z} of current location
+ Vector3 m_endPosition; // {x, y, z} of the destination
+ Vector3 m_actualEndPosition;// {x, y, z} of the closest possible point to given destination
+
+ const Unit* const m_sourceUnit; // the unit that is moving
+ const dtNavMesh* m_navMesh; // the nav mesh
+ const dtNavMeshQuery* m_navMeshQuery; // the nav mesh query used to find the path
+
+ dtQueryFilter m_filter; // use single filter for all movements, update it when needed
+
+ void setStartPosition(Vector3 point) { m_startPosition = point; }
+ void setEndPosition(Vector3 point) { m_actualEndPosition = point; m_endPosition = point; }
+ void setActualEndPosition(Vector3 point) { m_actualEndPosition = point; }
+
+ void clear()
+ {
+ m_polyLength = 0;
+ m_pathPoints.clear();
+ }
+
+ bool inRange(const Vector3 &p1, const Vector3 &p2, float r, float h) const;
+ float dist3DSqr(const Vector3 &p1, const Vector3 &p2) const;
+ bool inRangeYZX(const float* v1, const float* v2, float r, float h) const;
+
+ dtPolyRef getPathPolyByPosition(const dtPolyRef *polyPath, uint32 polyPathSize, const float* point, float *distance = NULL) const;
+ dtPolyRef getPolyByLocation(const float* point, float *distance) const;
+ bool HaveTile(const Vector3 &p) const;
+
+ void BuildPolyPath(const Vector3 &startPos, const Vector3 &endPos);
+ void BuildPointPath(const float *startPoint, const float *endPoint);
+ void BuildShortcut();
+
+ NavTerrain getNavTerrain(float x, float y, float z);
+ void createFilter();
+ void updateFilter();
+
+ // smooth path aux functions
+ uint32 fixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath,
+ const dtPolyRef* visited, uint32 nvisited);
+ bool getSteerTarget(const float* startPos, const float* endPos, float minTargetDist,
+ const dtPolyRef* path, uint32 pathSize, float* steerPos,
+ unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
+ dtStatus findSmoothPath(const float* startPos, const float* endPos,
+ const dtPolyRef* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
+ ACE_Thread_Mutex Lock;
+};
+
+#endif
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
index 07a5761..6dec628 100755
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
@@ -32,10 +32,10 @@ void PointMovementGenerator<T>::Initialize(T &unit)
if (!unit.IsStopped())
unit.StopMoving();
- unit.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ unit.AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
- init.MoveTo(i_x, i_y, i_z);
+ init.MoveTo(i_x, i_y, i_z, m_generatePath);
if (speed > 0.0f)
init.SetVelocity(speed);
init.Launch();
@@ -59,7 +59,7 @@ bool PointMovementGenerator<T>::Update(T &unit, const uint32 & /*diff*/)
{
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
- init.MoveTo(i_x, i_y, i_z);
+ init.MoveTo(i_x, i_y, i_z, m_generatePath);
if (speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit
init.SetVelocity(speed);
init.Launch();
@@ -71,7 +71,7 @@ bool PointMovementGenerator<T>::Update(T &unit, const uint32 & /*diff*/)
template<class T>
void PointMovementGenerator<T>::Finalize(T &unit)
{
- unit.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ unit.ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
if (unit.movespline->Finalized())
MovementInform(unit);
@@ -126,12 +126,4 @@ void EffectMovementGenerator::Finalize(Unit &unit)
if (((Creature&)unit).AI())
((Creature&)unit).AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
- // Need restore previous movement since we have no proper states system
- //if (unit.isAlive() && !unit.HasUnitState(UNIT_STATE_CONFUSED|UNIT_STATE_FLEEING))
- //{
- // if (Unit * victim = unit.getVictim())
- // unit.GetMotionMaster()->MoveChase(victim);
- // else
- // unit.GetMotionMaster()->Initialize();
- //}
}
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
index d2833a5..8c5ea8e 100755
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
@@ -26,8 +26,8 @@ template<class T>
class PointMovementGenerator : public MovementGeneratorMedium< T, PointMovementGenerator<T> >
{
public:
- PointMovementGenerator(uint32 _id, float _x, float _y, float _z, float _speed = 0.0f) : id(_id),
- i_x(_x), i_y(_y), i_z(_z), speed(_speed) {}
+ PointMovementGenerator(uint32 _id, float _x, float _y, float _z, bool _generatePath, float _speed = 0.0f) : id(_id),
+ i_x(_x), i_y(_y), i_z(_z), m_generatePath(_generatePath), speed(_speed) {}
void Initialize(T &);
void Finalize(T &);
@@ -46,13 +46,14 @@ class PointMovementGenerator : public MovementGeneratorMedium< T, PointMovementG
float i_x, i_y, i_z;
float speed;
bool i_recalculateSpeed;
+ bool m_generatePath;
};
class AssistanceMovementGenerator : public PointMovementGenerator<Creature>
{
public:
AssistanceMovementGenerator(float _x, float _y, float _z) :
- PointMovementGenerator<Creature>(0, _x, _y, _z) {}
+ PointMovementGenerator<Creature>(0, _x, _y, _z, true) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
void Finalize(Unit &);
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
index 84cd9e8..aa70f9c 100755
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
@@ -26,7 +26,7 @@
#include "MoveSplineInit.h"
#include "MoveSpline.h"
-#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV"
+#define RUNNING_CHANCE_RANDOMMV 5 //will be "1 / RUNNING_CHANCE_RANDOMMV"
#ifdef MAP_BASED_RAND_GEN
#define rand_norm() creature.rand_norm()
@@ -40,12 +40,10 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
Map const* map = creature.GetBaseMap();
// For 2D/3D system selection
- //bool is_land_ok = creature.CanWalk(); // not used?
- //bool is_water_ok = creature.CanSwim(); // not used?
bool is_air_ok = creature.CanFly();
const float angle = float(rand_norm()) * static_cast<float>(M_PI*2.0f);
- const float range = float(rand_norm()) * wander_distance;
+ const float range = float(rand_norm()) * wander_distance * (is_air_ok ? 4.0f : 2.0f);
const float distanceX = range * cos(angle);
const float distanceY = range * sin(angle);
@@ -96,17 +94,25 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
}
}
- if (is_air_ok)
- i_nextMoveTime.Reset(0);
- else
- i_nextMoveTime.Reset(urand(500, 10000));
+ PathFinderMovementGenerator path(&creature);
- creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ if (!path.calculate(destX, destY, destZ) || path.getPathType() & PATHFIND_NOPATH)
+ {
+ i_nextMoveTime.Reset(urand(500, 1500));
+ return;
+ }
+
+ creature.AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
Movement::MoveSplineInit init(creature);
- init.MoveTo(destX, destY, destZ);
- init.SetWalk(true);
- init.Launch();
+ init.MovebyPath(path.getPath());
+ init.SetWalk((irand(0, RUNNING_CHANCE_RANDOMMV) > 0) ? true : false);
+ int32 traveltime = init.Launch();
+
+ if (is_air_ok)
+ i_nextMoveTime.Reset(0);
+ else
+ i_nextMoveTime.Reset(traveltime + urand(500, 10000));
//Call for creature group update
if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
@@ -122,8 +128,8 @@ void RandomMovementGenerator<Creature>::Initialize(Creature &creature)
if (!wander_distance)
wander_distance = creature.GetRespawnRadius();
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- _setRandomLocation(creature);
+ creature.AddUnitState(UNIT_STATE_ROAMING);
+ i_nextMoveTime.Reset(urand(1000, 5000));
}
template<>
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index fdff5a9..a9a70a6 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -26,7 +26,8 @@
#include "MoveSpline.h"
#include "Player.h"
-#include <cmath>
+#define RECHECK_DISTANCE_TIMER 100
+#define TARGET_NOT_ACCESSIBLE_MAX_TIMER 5000
template<class T, typename D>
void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T &owner)
@@ -37,58 +38,46 @@ void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T &owner)
if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
return;
+ if (owner.GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(((Creature*)&owner)))
+ return;
+
float x, y, z;
- //! Following block of code deleted by MrSmite in issue 4891
- //! Code kept for learning and diagnostical purposes
-//
-// if (i_offset && i_target->IsWithinDistInMap(&owner,2*i_offset))
-// {
-// if (!owner.movespline->Finalized())
-// return;
-//
-// owner.GetPosition(x, y, z);
-// }
-// else
+ bool targetIsVictim = owner.getVictim() && owner.getVictim()->GetGUID() == i_target->GetGUID();
+
if (!i_offset)
{
- if (i_target->IsWithinMeleeRange(&owner))
- return;
+ // to nearest contact position
+ float dist = 0.0f;
+ if (targetIsVictim)
+ dist = owner.GetFloatValue(UNIT_FIELD_COMBATREACH) + i_target->GetFloatValue(UNIT_FIELD_COMBATREACH) - i_target->GetObjectSize() - owner.GetObjectSize() - 1.0f;
+
+ if (dist < 0.5f)
+ dist = 0.5f;
- // to nearest random contact position
- i_target->GetRandomContactPoint(&owner, x, y, z, 0, MELEE_RANGE - 0.5f);
+ i_target->GetContactPoint(&owner, x, y, z, dist);
}
else
{
- if (i_target->IsWithinDistInMap(&owner, i_offset + 1.0f))
- return;
// to at i_offset distance from target and i_angle from target facing
i_target->GetClosePoint(x, y, z, owner.GetObjectSize(), i_offset, i_angle);
}
- /*
- We MUST not check the distance difference and avoid setting the new location for smaller distances.
- By that we risk having far too many GetContactPoint() calls freezing the whole system.
- In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
- some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
- If the distance to the target it too large to ignore,
- but the distance to the new contact point is short enough to be ignored,
- we will calculate a new contact point each update loop, but will never move to it.
- The system will freeze.
- ralf
-
- //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
- float bothObjectSize = i_target->GetObjectBoundingRadius() + owner.GetObjectBoundingRadius() + CONTACT_DISTANCE;
- if ( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize )
- return;
- */
+ if (!i_path)
+ i_path = new PathFinderMovementGenerator(&owner);
+ // allow pets following their master to cheat while generating paths
+ bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->isPet()
+ && owner.HasUnitState(UNIT_STATE_FOLLOW));
+ i_path->calculate(x, y, z, forceDest);
+ if (i_path->getPathType() & PATHFIND_NOPATH)
+ return;
D::_addUnitStateMove(owner);
i_targetReached = false;
i_recalculateTravel = false;
Movement::MoveSplineInit init(owner);
- init.MoveTo(x,y,z);
+ init.MovebyPath(i_path->getPath());
init.SetWalk(((D*)this)->EnableWalking());
init.Launch();
}
@@ -123,10 +112,18 @@ template<class T, typename D>
bool TargetedMovementGeneratorMedium<T,D>::Update(T &owner, const uint32 & time_diff)
{
if (!i_target.isValid() || !i_target->IsInWorld())
- return false;
+ {
+ if (i_targetSearchingTimer >= TARGET_NOT_ACCESSIBLE_MAX_TIMER)
+ return false;
+ else
+ {
+ i_targetSearchingTimer += time_diff;
+ return true;
+ }
+ }
if (!owner.isAlive())
- return true;
+ return false;
if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
{
@@ -138,7 +135,19 @@ bool TargetedMovementGeneratorMedium<T,D>::Update(T &owner, const uint32 & time_
if (owner.HasUnitState(UNIT_STATE_CASTING))
{
if (!owner.IsStopped())
- owner.StopMoving();
+ {
+ // some spells should be able to be cast while moving
+ // maybe some attribute? here, check the entry of creatures useing these spells
+ switch(owner.GetEntry())
+ {
+ case 36633: // Ice Sphere (Lich King)
+ case 37562: // Volatile Ooze and Gas Cloud (Putricide)
+ case 37697:
+ break;
+ default:
+ owner.StopMoving();
+ }
+ }
return true;
}
@@ -146,17 +155,63 @@ bool TargetedMovementGeneratorMedium<T,D>::Update(T &owner, const uint32 & time_
if (static_cast<D*>(this)->_lostTarget(owner))
{
D::_clearUnitStateMove(owner);
- return true;
+ if (i_targetSearchingTimer >= TARGET_NOT_ACCESSIBLE_MAX_TIMER)
+ return false;
+ else
+ {
+ i_targetSearchingTimer += time_diff;
+ return true;
+ }
}
i_recheckDistance.Update(time_diff);
if (i_recheckDistance.Passed())
{
- i_recheckDistance.Reset(50);
- //More distance let have better performance, less distance let have more sensitive reaction at target move.
- float allowed_dist = i_target->GetObjectSize() + owner.GetObjectSize() + MELEE_RANGE - 0.5f;
- float dist = (owner.movespline->FinalDestination() - G3D::Vector3(i_target->GetPositionX(),i_target->GetPositionY(),i_target->GetPositionZ())).squaredLength();
- if (dist >= allowed_dist * allowed_dist)
+ i_recheckDistance.Reset(RECHECK_DISTANCE_TIMER);
+
+ G3D::Vector3 dest = owner.movespline->FinalDestination();
+ float allowed_dist = 0.0f;
+ bool targetIsVictim = owner.getVictim() && owner.getVictim()->GetGUID() == i_target->GetGUID();
+ if (targetIsVictim)
+ allowed_dist = owner.GetMeleeReach() + owner.getVictim()->GetMeleeReach()/* + owner.getVictim()->GetObjectSize()*/;
+ else
+ allowed_dist = i_target->GetObjectSize() + owner.GetObjectSize() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+
+ if (allowed_dist < owner.GetObjectSize())
+ allowed_dist = owner.GetObjectSize();
+
+ bool targetMoved = false;
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->IsFlying())
+ targetMoved = !i_target->IsWithinDist3d(dest.x, dest.y, dest.z, allowed_dist);
+ else
+ targetMoved = !i_target->IsWithinDist2d(dest.x, dest.y, allowed_dist);
+
+ if (targetIsVictim && owner.GetTypeId() == TYPEID_UNIT && !((Creature*)&owner)->isPet())
+ {
+ if ((!owner.getVictim() || !owner.getVictim()->isAlive()) && owner.movespline->Finalized())
+ return false;
+
+ if (!i_offset && owner.movespline->Finalized() && !owner.IsWithinMeleeRange(owner.getVictim())
+ && !i_target->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_PENDING_STOP))
+ {
+ if (i_targetSearchingTimer >= TARGET_NOT_ACCESSIBLE_MAX_TIMER)
+ {
+ owner.DeleteFromThreatList(owner.getVictim());
+ return false;
+ }
+ else
+ {
+ i_targetSearchingTimer += time_diff;
+ targetMoved = true;
+ }
+ }
+ else
+ i_targetSearchingTimer = 0;
+ }
+ else
+ i_targetSearchingTimer = 0;
+
+ if (targetMoved)
_setTargetLocation(owner);
}
@@ -185,13 +240,13 @@ template<class T>
void ChaseMovementGenerator<T>::_reachTarget(T &owner)
{
if (owner.IsWithinMeleeRange(this->i_target.getTarget()))
- owner.Attack(this->i_target.getTarget(),true);
+ owner.Attack(this->i_target.getTarget(), true);
}
template<>
void ChaseMovementGenerator<Player>::Initialize(Player &owner)
{
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
+ owner.AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
_setTargetLocation(owner);
}
@@ -199,14 +254,23 @@ template<>
void ChaseMovementGenerator<Creature>::Initialize(Creature &owner)
{
owner.SetWalk(false);
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
+ owner.AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
_setTargetLocation(owner);
}
template<class T>
void ChaseMovementGenerator<T>::Finalize(T &owner)
{
- owner.ClearUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
+ owner.ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
+ if (owner.GetTypeId() == TYPEID_UNIT && !((Creature*)&owner)->isPet() && owner.isAlive())
+ {
+ if (!owner.isInCombat() || ( this->i_target.getTarget() && !this->i_target.getTarget()->isInAccessiblePlaceFor(((Creature*)&owner))))
+ {
+ if (owner.isInCombat())
+ owner.CombatStop(true);
+ owner.GetMotionMaster()->MoveTargetedHome();
+ }
+ }
}
template<class T>
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
index 29fd736..1a3ec4a 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
@@ -23,6 +23,7 @@
#include "FollowerReference.h"
#include "Timer.h"
#include "Unit.h"
+#include "PathFinderMovementGenerator.h"
class TargetedMovementGeneratorBase
{
@@ -38,12 +39,12 @@ class TargetedMovementGeneratorMedium : public MovementGeneratorMedium< T, D >,
{
protected:
TargetedMovementGeneratorMedium(Unit &target, float offset, float angle) :
- TargetedMovementGeneratorBase(target), i_recheckDistance(0),
- i_offset(offset), i_angle(angle),
+ TargetedMovementGeneratorBase(target), i_recheckDistance(0), i_path(NULL),
+ i_offset(offset), i_angle(angle), i_targetSearchingTimer(0),
i_recalculateTravel(false), i_targetReached(false)
{
}
- ~TargetedMovementGeneratorMedium() {}
+ ~TargetedMovementGeneratorMedium() { delete i_path; }
public:
bool Update(T &, const uint32 &);
@@ -51,15 +52,17 @@ class TargetedMovementGeneratorMedium : public MovementGeneratorMedium< T, D >,
void unitSpeedChanged() { i_recalculateTravel=true; }
void UpdateFinalDistance(float fDistance);
-
+ bool IsReachable() const { return (i_path) ? (i_path->getPathType() & PATHFIND_NORMAL) : true; }
protected:
void _setTargetLocation(T &);
TimeTrackerSmall i_recheckDistance;
+ uint32 i_targetSearchingTimer;
float i_offset;
float i_angle;
bool i_recalculateTravel : 1;
bool i_targetReached : 1;
+ PathFinderMovementGenerator* i_path;
};
template<class T>
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
index 25730f9..a7c8cd4 100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -320,331 +320,3 @@ void FlightPathMovementGenerator::PreloadEndGrid()
else
sLog->outDetail("Unable to determine map to preload flightmaster grid");
}
-
-
-//
-// Unique1's ASTAR Pathfinding Code... For future use & reference...
-//
-
-#ifdef __PATHFINDING__
-
-int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
-
-int ShortenASTARRoute(short int *pathlist, int number)
-{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
- short int temppathlist[MAX_PATHLIST_NODES];
- int count = 0;
- // int count2 = 0;
- int temp, temp2;
- int link;
- int upto = 0;
-
- for (temp = number; temp >= 0; temp--)
- {
- qboolean shortened = qfalse;
-
- for (temp2 = 0; temp2 < temp; temp2++)
- {
- for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
- {
- if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
- continue;
-
- //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
- // continue;
-
- //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
- // continue;
-
- if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
- { // Found a shorter route...
- //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
- {
- temppathlist[count] = pathlist[temp2];
- temp = temp2;
- ++count;
- shortened = qtrue;
- }
- }
- }
- }
-
- if (!shortened)
- {
- temppathlist[count] = pathlist[temp];
- ++count;
- }
- }
-
- upto = count;
-
- for (temp = 0; temp < count; temp++)
- {
- pathlist[temp] = temppathlist[upto];
- --upto;
- }
-
- G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
- return count;
-}
-
-/*
-===========================================================================
-CreatePathAStar
-This function uses the A* pathfinding algorithm to determine the
-shortest path between any two nodes.
-It's fairly complex, so I'm not really going to explain it much.
-Look up A* and binary heaps for more info.
-pathlist stores the ideal path between the nodes, in reverse order,
-and the return value is the number of nodes in that path
-===========================================================================
-*/
-int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
-{
- //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
- //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
- short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index
- float gcost[MAX_NODES];
- int fcost[MAX_NODES];
- char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
- short int parent[MAX_NODES];
-
- short int numOpen = 0;
- short int atNode, temp, newnode=-1;
- qboolean found = qfalse;
- int count = -1;
- float gc;
- int i, u, v, m;
- vec3_t vec;
-
- //clear out all the arrays
- memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
- memset(fcost, 0, sizeof(int)*MAX_NODES);
- memset(list, 0, sizeof(char)*MAX_NODES);
- memset(parent, 0, sizeof(short int)*MAX_NODES);
- memset(gcost, -1, sizeof(float)*MAX_NODES);
-
- //make sure we have valid data before calculating everything
- if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
- return -1;
-
- openlist[1] = from; //add the starting node to the open list
- ++numOpen;
- gcost[from] = 0; //its f and g costs are obviously 0
- fcost[from] = 0;
-
- while (1)
- {
- if (numOpen != 0) //if there are still items in the open list
- {
- //pop the top item off of the list
- atNode = openlist[1];
- list[atNode] = 2; //put the node on the closed list so we don't check it again
- --numOpen;
-
- openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position
- v = 1;
-
- //this while loop reorders the list so that the new lowest fcost is at the top again
- while (1)
- {
- u = v;
- if ((2*u+1) < numOpen) //if both children exist
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
- v = 2*u+1;
- }
- else
- {
- if ((2*u) < numOpen) //if only one child exists
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- }
- }
-
- if (u != v) //if they're out of order, swap this item with its parent
- {
- temp = openlist[u];
- openlist[u] = openlist[v];
- openlist[v] = temp;
- }
- else
- break;
- }
-
- for (i = 0; i < nodes[atNode].enodenum; ++i) //loop through all the links for this node
- {
- newnode = nodes[atNode].links[i].targetNode;
-
- //if this path is blocked, skip it
- if (nodes[atNode].links[i].flags & PATH_BLOCKED)
- continue;
- //if this path is blocked, skip it
- if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
- continue;
- //skip any unreachable nodes
- if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
- continue;
- if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
- continue;
-
- if (list[newnode] == 2) //if this node is on the closed list, skip it
- continue;
-
- if (list[newnode] != 1) //if this node is not already on the open list
- {
- openlist[++numOpen] = newnode; //add the new node to the open list
- list[newnode] = 1;
- parent[newnode] = atNode; //record the node's parent
-
- if (newnode == to) //if we've found the goal, don't keep computing paths!
- break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
-
- //store it's f cost value
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //this loop re-orders the heap so that the lowest fcost is at the top
- m = numOpen;
- while (m != 1) //while this item isn't at the top of the heap already
- {
- //if it has a lower fcost than its parent
- if (fcost[openlist[m]] <= fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- }
- else //if this node is already on the open list
- {
- gc = gcost[atNode];
- VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
- gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path
-
- if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before)
- {
- parent[newnode] = atNode; //set the new parent for this node
- gcost[newnode] = gc; //and the new g cost
-
- for (i = 1; i < numOpen; ++i) //loop through all the items on the open list
- {
- if (openlist[i] == newnode) //find this node in the list
- {
- //calculate the new fcost and store it
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //reorder the list again, with the lowest fcost item on top
- m = i;
- while (m != 1)
- {
- //if the item has a lower fcost than it's parent
- if (fcost[openlist[m]] < fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- break; //exit the 'for' loop because we already changed this node
- } //if
- } //for
- } //if (gc < gcost[newnode])
- } //if (list[newnode] != 1) --> else
- } //for (loop through links)
- } //if (numOpen != 0)
- else
- {
- found = qfalse; //there is no path between these nodes
- break;
- }
-
- if (list[to] == 1) //if the destination node is on the open list, we're done
- {
- found = qtrue;
- break;
- }
- } //while (1)
-
- if (found == qtrue) //if we found a path
- {
- //G_Printf("%s - path found!n", bot->client->pers.netname);
- count = 0;
-
- temp = to; //start at the end point
- while (temp != from) //travel along the path (backwards) until we reach the starting point
- {
- pathlist[count++] = temp; //add the node to the pathlist and increment the count
- temp = parent[temp]; //move to the parent of this node to continue the path
- }
-
- pathlist[count++] = from; //add the beginning node to the end of the pathlist
-
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- }
- else
- {
- //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
- count = CreateDumbRoute(from, to, pathlist);
-
- if (count > 0)
- {
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- return count;
- }
- }
-
- return count; //return the number of nodes in the path, -1 if not found
-}
-
-/*
-===========================================================================
-GetFCost
-Utility function used by A* pathfinding to calculate the
-cost to move between nodes towards a goal. Using the A*
-algorithm F = G + H, G here is the distance along the node
-paths the bot must travel, and H is the straight-line distance
-to the goal node.
-Returned as an int because more precision is unnecessary and it
-will slightly speed up heap access
-===========================================================================
-*/
-int GetFCost(int to, int num, int parentNum, float *gcost)
-{
- float gc = 0;
- float hc = 0;
- vec3_t v;
-
- if (gcost[num] == -1)
- {
- if (parentNum != -1)
- {
- gc = gcost[parentNum];
- VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
- gc += VectorLength(v);
- }
- gcost[num] = gc;
- }
- else
- gc = gcost[num];
-
- VectorSubtract(nodes[to].origin, nodes[num].origin, v);
- hc = VectorLength(v);
-
- return (int)(gc + hc);
-}
-#endif //__PATHFINDING__
-
diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h
index d4b19b2..4ec8d2c 100644
--- a/src/server/game/Movement/Spline/MoveSpline.h
+++ b/src/server/game/Movement/Spline/MoveSpline.h
@@ -77,7 +77,6 @@ namespace Movement
UpdateResult _updateState(int32& ms_time_diff);
int32 next_timestamp() const { return spline.length(point_Idx+1);}
int32 segment_time_elapsed() const { return next_timestamp()-time_passed;}
- int32 Duration() const { return spline.length();}
int32 timeElapsed() const { return Duration() - time_passed;}
int32 timePassed() const { return time_passed;}
@@ -88,7 +87,7 @@ namespace Movement
void _Interrupt() { splineflags.done = true;}
public:
-
+ int32 Duration() const { return spline.length();}
void Initialize(const MoveSplineInitArgs&);
bool Initialized() const { return !spline.empty();}
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp
index c539dd3..1d0feda 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.cpp
+++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp
@@ -56,7 +56,7 @@ namespace Movement
return MOVE_RUN;
}
- void MoveSplineInit::Launch()
+ int32 MoveSplineInit::Launch()
{
MoveSpline& move_spline = *unit.movespline;
@@ -79,7 +79,7 @@ namespace Movement
// should i do the things that user should do? - no.
if (args.path.empty())
- return;
+ return 0;
// corrent first vertex
args.path[0] = real_position;
@@ -97,7 +97,7 @@ namespace Movement
args.velocity = unit.GetSpeed(SelectSpeedType(moveFlags));
if (!args.Validate())
- return;
+ return 0;
if (moveFlags & MOVEMENTFLAG_ROOT)
moveFlags &= ~MOVEMENTFLAG_MASK_MOVING;
@@ -115,6 +115,8 @@ namespace Movement
PacketBuilder::WriteMonsterMove(move_spline, data);
unit.SendMessageToSet(&data,true);
+
+ return move_spline.Duration();
}
MoveSplineInit::MoveSplineInit(Unit& m) : unit(m)
@@ -146,12 +148,21 @@ namespace Movement
args.flags.EnableFacingAngle();
}
- void MoveSplineInit::MoveTo(Vector3 const& dest)
+ void MoveSplineInit::MoveTo(Vector3 const& dest, bool generatePath, bool forceDestination)
{
- args.path_Idx_offset = 0;
- args.path.resize(2);
- TransportPathTransform transform(unit, args.TransformForTransport);
- args.path[1] = transform(dest);
+ if (generatePath)
+ {
+ PathFinderMovementGenerator path(&unit);
+ path.calculate(dest.x, dest.y, dest.z, forceDestination);
+ MovebyPath(path.getPath());
+ }
+ else
+ {
+ args.path_Idx_offset = 0;
+ args.path.resize(2);
+ TransportPathTransform transform(unit, args.TransformForTransport);
+ args.path[1] = transform(dest);
+ }
}
Vector3 TransportPathTransform::operator()(Vector3 input)
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.h b/src/server/game/Movement/Spline/MoveSplineInit.h
index ef84780..02ad1f6 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.h
+++ b/src/server/game/Movement/Spline/MoveSplineInit.h
@@ -20,6 +20,7 @@
#define TRINITYSERVER_MOVESPLINEINIT_H
#include "MoveSplineInitArgs.h"
+#include "PathFinderMovementGenerator.h"
class Unit;
@@ -56,7 +57,7 @@ namespace Movement
/* Final pass of initialization that launches spline movement.
*/
- void Launch();
+ int32 Launch();
/* Adds movement by parabolic trajectory
* @param amplitude - the maximum height of parabola, value could be negative and positive
@@ -85,8 +86,8 @@ namespace Movement
/* Initializes simple A to B mition, A is current unit's position, B is destination
*/
- void MoveTo(const Vector3& destination);
- void MoveTo(float x, float y, float z);
+ void MoveTo(const Vector3& destination, bool generatePath = false, bool forceDestination = false);
+ void MoveTo(float x, float y, float z, bool generatePath = false, bool forceDestination = false);
/* Sets Id of fisrt point of the path. When N-th path point will be done ILisener will notify that pointId + N done
* Needed for waypoint movement where path splitten into parts
@@ -158,10 +159,10 @@ namespace Movement
std::transform(controls.begin(), controls.end(), args.path.begin(), TransportPathTransform(unit, args.TransformForTransport));
}
- inline void MoveSplineInit::MoveTo(float x, float y, float z)
+ inline void MoveSplineInit::MoveTo(float x, float y, float z, bool generatePath, bool forceDestination)
{
- Vector3 v(x, y, z);
- MoveTo(v);
+ Vector3 v(x , y, z);
+ MoveTo(v, generatePath, forceDestination);
}
inline void MoveSplineInit::SetParabolic(float amplitude, float time_shift)
diff --git a/src/server/game/Movement/Waypoints/Path.h b/src/server/game/Movement/Waypoints/Path.h
index b6ddaa9..0389585 100755
--- a/src/server/game/Movement/Waypoints/Path.h
+++ b/src/server/game/Movement/Waypoints/Path.h
@@ -20,10 +20,12 @@
#define TRINITYCORE_PATH_H
#include "Common.h"
-#include <vector>
+#include <deque>
-struct SimplePathNode
+struct PathNode
{
+ PathNode(): x(0.0f), y(0.0f), z(0.0f) { }
+ PathNode(float _x, float _y, float _z): x(_x), y(_y), z(_z) { }
float x, y, z;
};
template<typename PathElem, typename PathNode = PathElem>
@@ -36,6 +38,20 @@ class Path
void resize(unsigned int sz) { i_nodes.resize(sz); }
void clear() { i_nodes.clear(); }
void erase(uint32 idx) { i_nodes.erase(i_nodes.begin()+idx); }
+ void crop(unsigned int start, unsigned int end)
+ {
+ while(start && !i_nodes.empty())
+ {
+ i_nodes.pop_front();
+ --start;
+ }
+
+ while(end && !i_nodes.empty())
+ {
+ i_nodes.pop_back();
+ --end;
+ }
+ }
float GetTotalLength(uint32 start, uint32 end) const
{
@@ -76,10 +92,10 @@ class Path
void set(size_t idx, PathElem elem) { i_nodes[idx] = elem; }
protected:
- std::vector<PathElem> i_nodes;
+ std::deque<PathElem> i_nodes;
};
-typedef Path<SimplePathNode> SimplePath;
+typedef Path<PathNode> SimplePath;
#endif
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 94e42f2..afc0b59 100755
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -21,6 +21,7 @@
*/
#include "Common.h"
+#include "Memory.h"
#include "DatabaseEnv.h"
#include "Config.h"
#include "SystemConfig.h"
@@ -54,6 +55,7 @@
#include "TemporarySummon.h"
#include "WaypointMovementGenerator.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "GameEventMgr.h"
#include "PoolMgr.h"
#include "GridNotifiersImpl.h"
@@ -134,6 +136,7 @@ World::~World()
delete command;
VMAP::VMapFactory::clear();
+ MMAP::MMapFactory::clear();
//TODO free addSessQueue
}
@@ -1121,6 +1124,10 @@ void World::LoadConfigSettings(bool reload)
sLog->outString("Using DataDir %s", m_dataPath.c_str());
}
+ // MMap related
+ m_bool_configs[CONFIG_ENABLE_MMAPS] = ConfigMgr::GetBoolDefault("mmap.enablePathFinding", true);
+ sLog->outString("WORLD: MMap data directory is: %smmaps", m_dataPath.c_str());
+
m_bool_configs[CONFIG_VMAP_INDOOR_CHECK] = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", 0);
bool enableIndoor = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", true);
bool enableLOS = ConfigMgr::GetBoolDefault("vmap.enableLOS", true);
@@ -1217,6 +1224,9 @@ void World::SetInitialWorldSettings()
///- Initialize the random number generator
srand((unsigned int)time(NULL));
+ ///- Initialize detour memory management
+ dtAllocSetCustom(dtCustomAlloc, dtCustomFree);
+
///- Initialize config settings
LoadConfigSettings();
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index f0dbc3c..9f8b646 100755
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -164,6 +164,7 @@ enum WorldBoolConfigs
CONFIG_QUEST_IGNORE_AUTO_ACCEPT,
CONFIG_QUEST_IGNORE_AUTO_COMPLETE,
CONFIG_WARDEN_ENABLED,
+ CONFIG_ENABLE_MMAPS,
BOOL_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index 792fdeb..3d21085 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -51,6 +51,8 @@ message("")
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/scripts/Commands/cs_disable.cpp b/src/server/scripts/Commands/cs_disable.cpp
index c537f9a..160903f 100644
--- a/src/server/scripts/Commands/cs_disable.cpp
+++ b/src/server/scripts/Commands/cs_disable.cpp
@@ -43,6 +43,7 @@ public:
{ "achievement_criteria", SEC_GAMEMASTER, false, &HandleDisableAchievementCriteriaCommand, "", NULL },
{ "outdoorpvp", SEC_GAMEMASTER, false, &HandleDisableOutdoorPvPCommand, "", NULL },
{ "vmap", SEC_GAMEMASTER, false, &HandleDisableVmapCommand, "", NULL },
+ { "mmap, SEC_GAMEMASTER, false, &HandleDisableMMapCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
@@ -83,8 +84,6 @@ public:
uint32 entry = (uint32)atoi(cEntry);
uint8 flags = atoi(cFlags);
-
-
std::string disableTypeStr = "";
switch (disableType)
@@ -107,6 +106,9 @@ public:
case DISABLE_TYPE_VMAP:
disableTypeStr = "vmap";
break;
+ case DISABLE_TYPE_MMAP:
+ disableTypeStr = "mmap";
+ break;
}
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_DISABLES);
@@ -186,6 +188,15 @@ public:
HandleDisables(handler, args, DISABLE_TYPE_VMAP);
return true;
}
+
+ static bool HandleDisableMMapCommand(ChatHandler* handler, char const* args)
+ {
+ if (!*args)
+ return false;
+
+ HandleDisables(handler, args, DISABLE_TYPE_MMAP);
+ return true;
+ }
};
diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt
index de99844..e016ad2 100644
--- a/src/server/shared/CMakeLists.txt
+++ b/src/server/shared/CMakeLists.txt
@@ -52,6 +52,7 @@ set(shared_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/utf8cpp
diff --git a/src/server/shared/Memory.h b/src/server/shared/Memory.h
new file mode 100644
index 0000000..ac697f7
--- /dev/null
+++ b/src/server/shared/Memory.h
@@ -0,0 +1,17 @@
+
+
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+// memory management
+inline void* dtCustomAlloc(int size, dtAllocHint /*hint*/)
+{
+ return (void*)new unsigned char[size];
+}
+
+inline void dtCustomFree(void* ptr)
+{
+ delete [] (unsigned char*)ptr;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index eb5bb04..4544212 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -45,6 +45,7 @@ endif()
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/gsoap
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/SFMT
@@ -166,6 +167,7 @@ target_link_libraries(worldserver
collision
g3dlib
gsoap
+ Detour
${JEMALLOC_LIBRARY}
${READLINE_LIBRARY}
${TERMCAP_LIBRARY}
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index cc4c4a5..8673c1c 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -267,6 +267,14 @@ PlayerSave.Stats.MinLevel = 0
PlayerSave.Stats.SaveOnlyOnLogout = 1
#
+# mmap.enablePathFinding
+# Description: Enable/Disable pathfinding using mmaps
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+mmap.enablePathFinding = 1
+
+#
# vmap.enableLOS
# vmap.enableHeight
# Description: VMmap support for line of sight and height calculation.
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index a956655..2d37896 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -11,3 +11,4 @@
add_subdirectory(map_extractor)
add_subdirectory(vmap4_assembler)
add_subdirectory(vmap4_extractor)
+add_subdirectory(mmaps_generator)
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index d64276c..e34bc42 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -292,6 +292,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -826,6 +828,28 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
map.liquidMapSize += sizeof(float)*liquidHeader.width*liquidHeader.height;
}
+ // map hole info
+ uint16 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID];
+
+ if (map.liquidMapOffset)
+ map.holesOffset = map.liquidMapOffset + map.liquidMapSize;
+ else
+ map.holesOffset = map.heightMapOffset + map.heightMapSize;
+
+ map.holesSize = sizeof(holes);
+ memset(holes, 0, map.holesSize);
+
+ for (int i = 0; i < ADT_CELLS_PER_GRID; ++i)
+ {
+ for (int j = 0; j < ADT_CELLS_PER_GRID; ++j)
+ {
+ adt_MCNK * cell = cells->getMCNK(i,j);
+ if (!cell)
+ continue;
+ holes[i][j] = cell->holes;
+ }
+ }
+
// Ok all data prepared - store it
FILE *output=fopen(filename2, "wb");
if(!output)
@@ -875,6 +899,9 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
fwrite(&liquid_height[y+liquidHeader.offsetY][liquidHeader.offsetX], sizeof(float), liquidHeader.width, output);
}
}
+ // store hole data
+ fwrite(holes, map.holesSize, 1, output);
+
fclose(output);
return true;
diff --git a/src/tools/mmaps_generator/CMakeLists.txt b/src/tools/mmaps_generator/CMakeLists.txt
new file mode 100644
index 0000000..46bf9d0
--- /dev/null
+++ b/src/tools/mmaps_generator/CMakeLists.txt
@@ -0,0 +1,68 @@
+# Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+file(GLOB_RECURSE sources *.cpp *.h)
+
+# definitions
+add_definitions(-DNO_CORE_FUNCS)
+add_definitions(-DDEBUG)
+add_definitions(-DNO_vsnprintf)
+
+include_directories(
+ ${CMAKE_BINARY_DIR}
+ ${ACE_INCLUDE_DIR}
+ ${MYSQL_INCLUDE_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ ${CMAKE_SOURCE_DIR}/dep/bzip2
+ ${CMAKE_SOURCE_DIR}/dep/acelite
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database/Implementation
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Threading
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Logging
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic/LinkedReference
+ ${CMAKE_SOURCE_DIR}/src/server/game/Maps
+ ${CMAKE_SOURCE_DIR}/src/server/game/DataStores
+ ${CMAKE_SOURCE_DIR}/src/server/game/Movement/Waypoints
+ ${CMAKE_SOURCE_DIR}/src/server/game/Grids
+ ${CMAKE_SOURCE_DIR}/src/server/game/Grids/Cells
+ ${CMAKE_SOURCE_DIR}/src/server/game/Miscellaneous
+ ${CMAKE_SOURCE_DIR}/src/server/game/Conditions
+ ${CMAKE_SOURCE_DIR}/src/server/collision
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Management
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Maps
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Models
+)
+
+add_executable(mmaps_generator ${sources})
+
+target_link_libraries(mmaps_generator
+ ${MYSQL_LIBRARY}
+ ${ACE_LIBRARY}
+ ${BZIP2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ Recast
+ Detour
+ collision
+ g3dlib
+ shared
+)
+
+if( UNIX )
+ install(TARGETS mmaps_generator DESTINATION bin)
+elseif( WIN32 )
+ install(TARGETS mmaps_generator DESTINATION "${CMAKE_INSTALL_PREFIX}")
+endif()
diff --git a/src/tools/mmaps_generator/Info/readme.txt b/src/tools/mmaps_generator/Info/readme.txt
new file mode 100644
index 0000000..8d7c4f9
--- /dev/null
+++ b/src/tools/mmaps_generator/Info/readme.txt
@@ -0,0 +1,66 @@
+Generator command line args
+
+--offMeshInput [file.*] Path to file containing off mesh connections data.
+ Format must be: (see offmesh_example.txt)
+ "map_id tile_x,tile_y (start_x start_y start_z) (end_x end_y end_z) size //optional comments"
+ Single mesh connection per line.
+
+--silent Make us script friendly. Do not wait for user input
+ on error or completion.
+
+--bigBaseUnit [true|false] Generate tile/map using bigger basic unit.
+ Use this option only if you have unexpected gaps.
+
+ false: use normal metrics (default)
+
+--maxAngle [#] Max walkable inclination angle
+
+ float between 45 and 90 degrees (default 60)
+
+--skipLiquid liquid data for maps
+
+ false: include liquid data (default)
+
+--skipContinents [true|false] continents are maps 0 (Eastern Kingdoms),
+ 1 (Kalimdor), 530 (Outlands), 571 (Northrend)
+
+ false: build continents (default)
+
+--skipJunkMaps [true|false] junk maps include some unused
+ maps, transport maps, and some other
+
+ true: skip junk maps (default)
+
+--skipBattlegrounds [true|false] does not include PVP arenas
+
+ false: skip battlegrounds (default)
+
+--debugOutput [true|false] create debugging files for use with RecastDemo
+ if you are only creating mmaps for use with MaNGOS,
+ you don't want debugging files
+
+ false: don't create debugging files (default)
+
+--tile [#,#] Build the specified tile
+ seperate number with a comma ','
+ must specify a map number (see below)
+ if this option is not used, all tiles are built
+
+ [#] Build only the map specified by #
+ this command will build the map regardless of --skip* option settings
+ if you do not specify a map number, builds all maps that pass the filters specified by --skip* options
+
+
+examples:
+
+movement_extractor
+builds maps using the default settings (see above for defaults)
+
+movement_extractor --skipContinents true
+builds the default maps, except continents
+
+movement_extractor 0
+builds all tiles of map 0
+
+movement_extractor 0 --tile 34,46
+builds only tile 34,46 of map 0 (this is the southern face of blackrock mountain)
\ No newline at end of file
diff --git a/src/tools/mmaps_generator/IntermediateValues.cpp b/src/tools/mmaps_generator/IntermediateValues.cpp
new file mode 100644
index 0000000..b473d64
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "IntermediateValues.h"
+
+namespace MMAP
+{
+ IntermediateValues::~IntermediateValues()
+ {
+ rcFreeCompactHeightfield(compactHeightfield);
+ rcFreeHeightField(heightfield);
+ rcFreeContourSet(contours);
+ rcFreePolyMesh(polyMesh);
+ rcFreePolyMeshDetail(polyMeshDetail);
+ }
+
+ void IntermediateValues::writeIV(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileX, tileY);
+
+ printf("%sWriting debug output... \r", tileString);
+
+ string name("meshes/%03u%02i%02i.");
+
+#define DEBUG_WRITE(fileExtension,data) \
+ do { \
+ sprintf(fileName, (name + fileExtension).c_str(), mapID, tileY, tileX); \
+ FILE* file = fopen(fileName, "wb"); \
+ if (!file) \
+ { \
+ char message[1024]; \
+ sprintf(message, "%sFailed to open %s for writing!\n", tileString, fileName); \
+ perror(message); \
+ } \
+ else \
+ debugWrite(file, data); \
+ if (file) fclose(file); \
+ printf("%sWriting debug output... \r", tileString); \
+ } while (false)
+
+ if (heightfield)
+ DEBUG_WRITE("hf", heightfield);
+ if (compactHeightfield)
+ DEBUG_WRITE("chf", compactHeightfield);
+ if (contours)
+ DEBUG_WRITE("cs", contours);
+ if (polyMesh)
+ DEBUG_WRITE("pmesh", polyMesh);
+ if (polyMeshDetail)
+ DEBUG_WRITE("dmesh", polyMeshDetail);
+
+#undef DEBUG_WRITE
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcHeightfield* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->width), sizeof(int), 1, file);
+ fwrite(&(mesh->height), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+
+ for (int y = 0; y < mesh->height; ++y)
+ for (int x = 0; x < mesh->width; ++x)
+ {
+ rcSpan* span = mesh->spans[x+y*mesh->width];
+
+ // first, count the number of spans
+ int spanCount = 0;
+ while (span)
+ {
+ spanCount++;
+ span = span->next;
+ }
+
+ // write the span count
+ fwrite(&spanCount, sizeof(int), 1, file);
+
+ // write the spans
+ span = mesh->spans[x+y*mesh->width];
+ while (span)
+ {
+ fwrite(span, sizeof(rcSpan), 1, file);
+ span = span->next;
+ }
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcCompactHeightfield* chf)
+ {
+ if (!file | !chf)
+ return;
+
+ fwrite(&(chf->width), sizeof(chf->width), 1, file);
+ fwrite(&(chf->height), sizeof(chf->height), 1, file);
+ fwrite(&(chf->spanCount), sizeof(chf->spanCount), 1, file);
+
+ fwrite(&(chf->walkableHeight), sizeof(chf->walkableHeight), 1, file);
+ fwrite(&(chf->walkableClimb), sizeof(chf->walkableClimb), 1, file);
+
+ fwrite(&(chf->maxDistance), sizeof(chf->maxDistance), 1, file);
+ fwrite(&(chf->maxRegions), sizeof(chf->maxRegions), 1, file);
+
+ fwrite(chf->bmin, sizeof(chf->bmin), 1, file);
+ fwrite(chf->bmax, sizeof(chf->bmax), 1, file);
+
+ fwrite(&(chf->cs), sizeof(chf->cs), 1, file);
+ fwrite(&(chf->ch), sizeof(chf->ch), 1, file);
+
+ int tmp = 0;
+ if (chf->cells) tmp |= 1;
+ if (chf->spans) tmp |= 2;
+ if (chf->dist) tmp |= 4;
+ if (chf->areas) tmp |= 8;
+
+ fwrite(&tmp, sizeof(tmp), 1, file);
+
+ if (chf->cells)
+ fwrite(chf->cells, sizeof(rcCompactCell), chf->width*chf->height, file);
+ if (chf->spans)
+ fwrite(chf->spans, sizeof(rcCompactSpan), chf->spanCount, file);
+ if (chf->dist)
+ fwrite(chf->dist, sizeof(unsigned short), chf->spanCount, file);
+ if (chf->areas)
+ fwrite(chf->areas, sizeof(unsigned char), chf->spanCount, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcContourSet* cs)
+ {
+ if (!file || !cs)
+ return;
+
+ fwrite(&(cs->cs), sizeof(float), 1, file);
+ fwrite(&(cs->ch), sizeof(float), 1, file);
+ fwrite(cs->bmin, sizeof(float), 3, file);
+ fwrite(cs->bmax, sizeof(float), 3, file);
+ fwrite(&(cs->nconts), sizeof(int), 1, file);
+ for (int i = 0; i < cs->nconts; ++i)
+ {
+ fwrite(&cs->conts[i].area, sizeof(unsigned char), 1, file);
+ fwrite(&cs->conts[i].reg, sizeof(unsigned short), 1, file);
+ fwrite(&cs->conts[i].nverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].verts, sizeof(int), cs->conts[i].nverts*4, file);
+ fwrite(&cs->conts[i].nrverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].rverts, sizeof(int), cs->conts[i].nrverts*4, file);
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMesh* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->nvp), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(unsigned short), mesh->nverts*3, file);
+ fwrite(&(mesh->npolys), sizeof(int), 1, file);
+ fwrite(mesh->polys, sizeof(unsigned short), mesh->npolys*mesh->nvp*2, file);
+ fwrite(mesh->flags, sizeof(unsigned short), mesh->npolys, file);
+ fwrite(mesh->areas, sizeof(unsigned char), mesh->npolys, file);
+ fwrite(mesh->regs, sizeof(unsigned short), mesh->npolys, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMeshDetail* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(float), mesh->nverts*3, file);
+ fwrite(&(mesh->ntris), sizeof(int), 1, file);
+ fwrite(mesh->tris, sizeof(char), mesh->ntris*4, file);
+ fwrite(&(mesh->nmeshes), sizeof(int), 1, file);
+ fwrite(mesh->meshes, sizeof(int), mesh->nmeshes*4, file);
+ }
+
+ void IntermediateValues::generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ char objFileName[255];
+ sprintf(objFileName, "meshes/map%03u%02u%02u.obj", mapID, tileY, tileX);
+
+ FILE* objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ G3D::Array<float> allVerts;
+ G3D::Array<int> allTris;
+
+ allTris.append(meshData.liquidTris);
+ allVerts.append(meshData.liquidVerts);
+ TerrainBuilder::copyIndices(meshData.solidTris, allTris, allVerts.size() / 3);
+ allVerts.append(meshData.solidVerts);
+
+ float* verts = allVerts.getCArray();
+ int vertCount = allVerts.size() / 3;
+ int* tris = allTris.getCArray();
+ int triCount = allTris.size() / 3;
+
+ for (int i = 0; i < allVerts.size() / 3; i++)
+ fprintf(objFile, "v %f %f %f\n", verts[i*3], verts[i*3 + 1], verts[i*3 + 2]);
+
+ for (int i = 0; i < allTris.size() / 3; i++)
+ fprintf(objFile, "f %i %i %i\n", tris[i*3] + 1, tris[i*3 + 1] + 1, tris[i*3 + 2] + 1);
+
+ fclose(objFile);
+
+
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileY, tileX);
+ printf("%sWriting debug output... \r", tileString);
+
+ sprintf(objFileName, "meshes/%03u.map", mapID);
+
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ char b = '\0';
+ fwrite(&b, sizeof(char), 1, objFile);
+ fclose(objFile);
+
+ sprintf(objFileName, "meshes/%03u%02u%02u.mesh", mapID, tileY, tileX);
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ fwrite(&vertCount, sizeof(int), 1, objFile);
+ fwrite(verts, sizeof(float), vertCount*3, objFile);
+ fflush(objFile);
+
+ fwrite(&triCount, sizeof(int), 1, objFile);
+ fwrite(tris, sizeof(int), triCount*3, objFile);
+ fflush(objFile);
+
+ fclose(objFile);
+ }
+}
diff --git a/src/tools/mmaps_generator/IntermediateValues.h b/src/tools/mmaps_generator/IntermediateValues.h
new file mode 100644
index 0000000..a267a0f
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _INTERMEDIATE_VALUES_H
+#define _INTERMEDIATE_VALUES_H
+
+#include "PathCommon.h"
+#include "TerrainBuilder.h"
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+namespace MMAP
+{
+ // this class gathers all debug info holding and output
+ struct IntermediateValues
+ {
+ rcHeightfield* heightfield;
+ rcCompactHeightfield* compactHeightfield;
+ rcContourSet* contours;
+ rcPolyMesh* polyMesh;
+ rcPolyMeshDetail* polyMeshDetail;
+
+ IntermediateValues() : compactHeightfield(NULL), heightfield(NULL),
+ contours(NULL), polyMesh(NULL), polyMeshDetail(NULL) {}
+ ~IntermediateValues();
+
+ void writeIV(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ void debugWrite(FILE* file, const rcHeightfield* mesh);
+ void debugWrite(FILE* file, const rcCompactHeightfield* chf);
+ void debugWrite(FILE* file, const rcContourSet* cs);
+ void debugWrite(FILE* file, const rcPolyMesh* mesh);
+ void debugWrite(FILE* file, const rcPolyMeshDetail* mesh);
+
+ void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ };
+}
+#endif
diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp
new file mode 100644
index 0000000..2987e65
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.cpp
@@ -0,0 +1,899 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "MapTree.h"
+#include "ModelInstance.h"
+#include "LoginDatabase.h"
+
+#include "DetourNavMeshBuilder.h"
+#include "DetourCommon.h"
+
+// These make the linker happy.
+LoginDatabaseWorkerPool LoginDatabase;
+uint32 GetLiquidFlags(uint32 liquidType)
+{
+ return 0;
+}
+
+#include "DisableMgr.h"
+namespace DisableMgr
+{
+ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags)
+ {
+ return 0;
+ }
+}
+
+using namespace VMAP;
+
+namespace MMAP
+{
+ MapBuilder::MapBuilder(float maxWalkableAngle, bool skipLiquid,
+ bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds,
+ bool debugOutput, bool bigBaseUnit, const char* offMeshFilePath) :
+ m_terrainBuilder(NULL),
+ m_debugOutput (debugOutput),
+ m_skipContinents (skipContinents),
+ m_skipJunkMaps (skipJunkMaps),
+ m_skipBattlegrounds (skipBattlegrounds),
+ m_maxWalkableAngle (maxWalkableAngle),
+ m_bigBaseUnit (bigBaseUnit),
+ m_rcContext (NULL),
+ m_offMeshFilePath (offMeshFilePath)
+ {
+ m_terrainBuilder = new TerrainBuilder(skipLiquid);
+
+ m_rcContext = new rcContext(false);
+
+ discoverTiles();
+ }
+
+ /**************************************************************************/
+ MapBuilder::~MapBuilder()
+ {
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ (*it).second->clear();
+ delete (*it).second;
+ }
+
+ delete m_terrainBuilder;
+ delete m_rcContext;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::discoverTiles()
+ {
+ vector<string> files;
+ uint32 mapID, tileX, tileY, tileID, count = 0;
+ char filter[12];
+
+ printf("Discovering maps... ");
+ getDirContents(files, "maps");
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ mapID = uint32(atoi(files[i].substr(0,3).c_str()));
+ if (m_tiles.find(mapID) == m_tiles.end())
+ {
+ m_tiles.insert(pair<uint32,set<uint32>*>(mapID, new set<uint32>));
+ count++;
+ }
+ }
+
+ files.clear();
+ getDirContents(files, "vmaps", "*.vmtree");
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ mapID = uint32(atoi(files[i].substr(0,3).c_str()));
+ m_tiles.insert(pair<uint32,set<uint32>*>(mapID, new set<uint32>));
+ count++;
+ }
+ printf("found %u.\n", count);
+
+ count = 0;
+ printf("Discovering tiles... ");
+ for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr)
+ {
+ set<uint32>* tiles = (*itr).second;
+ mapID = (*itr).first;
+
+ sprintf(filter, "%03u*.vmtile", mapID);
+ files.clear();
+ getDirContents(files, "vmaps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileX = uint32(atoi(files[i].substr(7,2).c_str()));
+ tileY = uint32(atoi(files[i].substr(4,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileY, tileX);
+
+ tiles->insert(tileID);
+ count++;
+ }
+
+ sprintf(filter, "%03u*", mapID);
+ files.clear();
+ getDirContents(files, "maps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileY = uint32(atoi(files[i].substr(3,2).c_str()));
+ tileX = uint32(atoi(files[i].substr(5,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileX, tileY);
+
+ if (tiles->insert(tileID).second)
+ count++;
+ }
+ }
+ printf("found %u.\n\n", count);
+ }
+
+ /**************************************************************************/
+ set<uint32>* MapBuilder::getTileList(uint32 mapID)
+ {
+ TileList::iterator itr = m_tiles.find(mapID);
+ if (itr != m_tiles.end())
+ return (*itr).second;
+
+ set<uint32>* tiles = new set<uint32>();
+ m_tiles.insert(pair<uint32, set<uint32>*>(mapID, tiles));
+ return tiles;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildAllMaps()
+ {
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ uint32 mapID = (*it).first;
+ if (!shouldSkipMap(mapID))
+ buildMap(mapID);
+ }
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY)
+ {
+ maxX = INT_MAX;
+ maxY = INT_MAX;
+ minX = INT_MIN;
+ minY = INT_MIN;
+
+ float bmin[3], bmax[3], lmin[3], lmax[3];
+ MeshData meshData;
+
+ // make sure we process maps which don't have tiles
+ // initialize the static tree, which loads WDT models
+ if (!m_terrainBuilder->loadVMap(mapID, 64, 64, meshData))
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() + meshData.liquidVerts.size() == 0)
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() && meshData.liquidVerts.size())
+ {
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+ rcVmin(bmin, lmin);
+ rcVmax(bmax, lmax);
+ }
+ else if (meshData.solidVerts.size())
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ else
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+
+ // convert coord bounds to grid bounds
+ maxX = 32 - bmin[0] / GRID_SIZE;
+ maxY = 32 - bmin[2] / GRID_SIZE;
+ minX = 32 - bmax[0] / GRID_SIZE;
+ minY = 32 - bmax[2] / GRID_SIZE;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ return;
+ }
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ dtFreeNavMesh(navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMap(uint32 mapID)
+ {
+ printf("Building map %03u:\n", mapID);
+
+ set<uint32>* tiles = getTileList(mapID);
+
+ // make sure we process maps which don't have tiles
+ if (!tiles->size())
+ {
+ // convert coord bounds to grid bounds
+ uint32 minX, minY, maxX, maxY;
+ getGridBounds(mapID, minX, minY, maxX, maxY);
+
+ // add all tiles within bounds to tile list.
+ for (uint32 i = minX; i <= maxX; ++i)
+ for (uint32 j = minY; j <= maxY; ++j)
+ tiles->insert(StaticMapTree::packTileID(i, j));
+ }
+
+ if (!tiles->size())
+ return;
+
+ // build navMesh
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ return;
+ }
+
+ // now start building mmtiles for each tile
+ printf("We have %u tiles. \n", (unsigned int)tiles->size());
+ for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ uint32 tileX, tileY;
+
+ // unpack tile coords
+ StaticMapTree::unpackTileID((*it), tileX, tileY);
+
+ if (shouldSkipTile(mapID, tileX, tileY))
+ continue;
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ }
+
+ dtFreeNavMesh(navMesh);
+
+ printf("Complete! \n\n");
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh)
+ {
+ printf("Building map %03u, tile [%02u,%02u]\n", mapID, tileX, tileY);
+
+ MeshData meshData;
+
+ // get heightmap data
+ m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData);
+
+ // get model data
+ m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData);
+
+ // if there is no data, give up now
+ if (!meshData.solidVerts.size() && !meshData.liquidVerts.size())
+ return;
+
+ // remove unused vertices
+ TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris);
+ TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris);
+
+ // gather all mesh data for final data check, and bounds calculation
+ G3D::Array<float> allVerts;
+ allVerts.append(meshData.liquidVerts);
+ allVerts.append(meshData.solidVerts);
+
+ if (!allVerts.size())
+ return;
+
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax);
+
+ m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath);
+
+ // build navmesh tile
+ buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildNavMesh(uint32 mapID, dtNavMesh* &navMesh)
+ {
+ set<uint32>* tiles = getTileList(mapID);
+
+ // old code for non-statically assigned bitmask sizes:
+ ///*** calculate number of bits needed to store tiles & polys ***/
+ //int tileBits = dtIlog2(dtNextPow2(tiles->size()));
+ //if (tileBits < 1) tileBits = 1; // need at least one bit!
+ //int polyBits = sizeof(dtPolyRef)*8 - SALT_MIN_BITS - tileBits;
+
+ int tileBits = STATIC_TILE_BITS;
+ int polyBits = STATIC_POLY_BITS;
+
+ int maxTiles = tiles->size();
+ int maxPolysPerTile = 1 << polyBits;
+
+ /*** calculate bounds of map ***/
+
+ uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY;
+ for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ StaticMapTree::unpackTileID((*it), tileX, tileY);
+
+ if (tileX > tileXMax)
+ tileXMax = tileX;
+ else if (tileX < tileXMin)
+ tileXMin = tileX;
+
+ if (tileY > tileYMax)
+ tileYMax = tileY;
+ else if (tileY < tileYMin)
+ tileYMin = tileY;
+ }
+
+ // use Max because '32 - tileX' is negative for values over 32
+ float bmin[3], bmax[3];
+ getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
+
+ /*** now create the navmesh ***/
+
+ // navmesh creation params
+ dtNavMeshParams navMeshParams;
+ memset(&navMeshParams, 0, sizeof(dtNavMeshParams));
+ navMeshParams.tileWidth = GRID_SIZE;
+ navMeshParams.tileHeight = GRID_SIZE;
+ rcVcopy(navMeshParams.orig, bmin);
+ navMeshParams.maxTiles = maxTiles;
+ navMeshParams.maxPolys = maxPolysPerTile;
+
+ navMesh = dtAllocNavMesh();
+ printf("Creating navMesh... \r");
+ if (!navMesh->init(&navMeshParams))
+ {
+ printf("Failed creating navmesh! \n");
+ return;
+ }
+
+ char fileName[25];
+ sprintf(fileName, "mmaps/%03u.mmap", mapID);
+
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ dtFreeNavMesh(navMesh);
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", fileName);
+ perror(message);
+ return;
+ }
+
+ // now that we know navMesh params are valid, we can write them to file
+ fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY,
+ MeshData &meshData, float bmin[3], float bmax[3],
+ dtNavMesh* navMesh)
+ {
+ // console output
+ char tileString[10];
+ sprintf(tileString, "[%02i,%02i]: ", tileX, tileY);
+ printf("%s Building movemap tiles... \r", tileString);
+
+ IntermediateValues iv;
+
+ float* tVerts = meshData.solidVerts.getCArray();
+ int tVertCount = meshData.solidVerts.size() / 3;
+ int* tTris = meshData.solidTris.getCArray();
+ int tTriCount = meshData.solidTris.size() / 3;
+
+ float* lVerts = meshData.liquidVerts.getCArray();
+ int lVertCount = meshData.liquidVerts.size() / 3;
+ int* lTris = meshData.liquidTris.getCArray();
+ int lTriCount = meshData.liquidTris.size() / 3;
+ uint8* lTriFlags = meshData.liquidType.getCArray();
+
+ // these are WORLD UNIT based metrics
+ // this are basic unit dimentions
+ // value have to divide GRID_SIZE(533.33333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
+ const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.533333f : 0.266666f;
+
+ // All are in UNIT metrics!
+ const static int VERTEX_PER_MAP = int(GRID_SIZE/BASE_UNIT_DIM + 0.5f);
+ const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
+ const static int TILES_PER_MAP = VERTEX_PER_MAP/VERTEX_PER_TILE;
+
+ rcConfig config;
+ memset(&config, 0, sizeof(rcConfig));
+
+ rcVcopy(config.bmin, bmin);
+ rcVcopy(config.bmax, bmax);
+
+ config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
+ config.cs = BASE_UNIT_DIM;
+ config.ch = BASE_UNIT_DIM;
+ config.walkableSlopeAngle = m_maxWalkableAngle;
+ config.tileSize = VERTEX_PER_TILE;
+ config.walkableRadius = m_bigBaseUnit ? 1 : 2;
+ config.borderSize = config.walkableRadius + 3;
+ config.maxEdgeLen = VERTEX_PER_TILE + 1; //anything bigger than tileSize
+ config.walkableHeight = m_bigBaseUnit ? 3 : 6;
+ config.walkableClimb = m_bigBaseUnit ? 2 : 4; // keep less than walkableHeight
+ config.minRegionArea = rcSqr(60);
+ config.mergeRegionArea = rcSqr(50);
+ config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
+ config.detailSampleDist = config.cs * 64;
+ config.detailSampleMaxError = config.ch * 2;
+
+ // this sets the dimensions of the heightfield - should maybe happen before border padding
+ rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
+
+ // allocate subregions : tiles
+ Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP];
+
+ // Initialize per tile config.
+ rcConfig tileCfg;
+ memcpy(&tileCfg, &config, sizeof(rcConfig));
+ tileCfg.width = config.tileSize + config.borderSize*2;
+ tileCfg.height = config.tileSize + config.borderSize*2;
+
+ // build all tiles
+ for (int y = 0; y < TILES_PER_MAP; ++y)
+ {
+ for (int x = 0; x < TILES_PER_MAP; ++x)
+ {
+ Tile& tile = tiles[x + y*TILES_PER_MAP];
+
+ // Calculate the per tile bounding box.
+ tileCfg.bmin[0] = config.bmin[0] + (x*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmin[2] = config.bmin[2] + (y*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmax[0] = config.bmin[0] + ((x+1)*config.tileSize + config.borderSize)*config.cs;
+ tileCfg.bmax[2] = config.bmin[2] + ((y+1)*config.tileSize + config.borderSize)*config.cs;
+
+ float tbmin[2], tbmax[2];
+ tbmin[0] = tileCfg.bmin[0];
+ tbmin[1] = tileCfg.bmin[2];
+ tbmax[0] = tileCfg.bmax[0];
+ tbmax[1] = tileCfg.bmax[2];
+
+ // build heightfield
+ tile.solid = rcAllocHeightfield();
+ if (!tile.solid || !rcCreateHeightfield(m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
+ {
+ printf("%sFailed building heightfield! \n", tileString);
+ continue;
+ }
+
+ // mark all walkable tiles, both liquids and solids
+ unsigned char* triFlags = new unsigned char[tTriCount];
+ memset(triFlags, NAV_GROUND, tTriCount*sizeof(unsigned char));
+ rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags);
+ rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb);
+ delete [] triFlags;
+
+ rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid);
+ rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
+ rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid);
+
+ rcRasterizeTriangles(m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
+
+ // compact heightfield spans
+ tile.chf = rcAllocCompactHeightfield();
+ if (!tile.chf || !rcBuildCompactHeightfield(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
+ {
+ printf("%sFailed compacting heightfield! \n", tileString);
+ continue;
+ }
+
+ // build polymesh intermediates
+ if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf))
+ {
+ printf("%sFailed eroding area! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildDistanceField(m_rcContext, *tile.chf))
+ {
+ printf("%sFailed building distance field! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
+ {
+ printf("%sFailed building regions! \n", tileString);
+ continue;
+ }
+
+ tile.cset = rcAllocContourSet();
+ if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
+ {
+ printf("%sFailed building contours! \n", tileString);
+ continue;
+ }
+
+ // build polymesh
+ tile.pmesh = rcAllocPolyMesh();
+ if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
+ {
+ printf("%sFailed building polymesh! \n", tileString);
+ continue;
+ }
+
+ tile.dmesh = rcAllocPolyMeshDetail();
+ if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg .detailSampleMaxError, *tile.dmesh))
+ {
+ printf("%sFailed building polymesh detail! \n", tileString);
+ continue;
+ }
+
+ // free those up
+ // we may want to keep them in the future for debug
+ // but right now, we don't have the code to merge them
+ rcFreeHeightField(tile.solid);
+ tile.solid = NULL;
+ rcFreeCompactHeightfield(tile.chf);
+ tile.chf = NULL;
+ rcFreeContourSet(tile.cset);
+ tile.cset = NULL;
+ }
+ }
+
+ // merge per tile poly and detail meshes
+ rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!pmmerge)
+ {
+ printf("%s alloc pmmerge FIALED! \r", tileString);
+ return;
+ }
+
+ rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!dmmerge)
+ {
+ printf("%s alloc dmmerge FIALED! \r", tileString);
+ return;
+ }
+
+ int nmerge = 0;
+ for (int y = 0; y < TILES_PER_MAP; ++y)
+ {
+ for (int x = 0; x < TILES_PER_MAP; ++x)
+ {
+ Tile& tile = tiles[x + y*TILES_PER_MAP];
+ if (tile.pmesh)
+ {
+ pmmerge[nmerge] = tile.pmesh;
+ dmmerge[nmerge] = tile.dmesh;
+ nmerge++;
+ }
+ }
+ }
+
+ iv.polyMesh = rcAllocPolyMesh();
+ if (!iv.polyMesh)
+ {
+ printf("%s alloc iv.polyMesh FIALED! \r", tileString);
+ return;
+ }
+ rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh);
+
+ iv.polyMeshDetail = rcAllocPolyMeshDetail();
+ if (!iv.polyMeshDetail)
+ {
+ printf("%s alloc m_dmesh FIALED! \r", tileString);
+ return;
+ }
+ rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail);
+
+ // free things up
+ delete [] pmmerge;
+ delete [] dmmerge;
+
+ delete [] tiles;
+
+ // remove padding for extraction
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] -= (unsigned short)config.borderSize;
+ v[2] -= (unsigned short)config.borderSize;
+ }
+
+ // set polygons as walkable
+ // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
+ for (int i = 0; i < iv.polyMesh->npolys; ++i)
+ if (iv.polyMesh->areas[i] & RC_WALKABLE_AREA)
+ iv.polyMesh->flags[i] = iv.polyMesh->areas[i];
+
+ // setup mesh parameters
+ dtNavMeshCreateParams params;
+ memset(&params, 0, sizeof(params));
+ params.verts = iv.polyMesh->verts;
+ params.vertCount = iv.polyMesh->nverts;
+ params.polys = iv.polyMesh->polys;
+ params.polyAreas = iv.polyMesh->areas;
+ params.polyFlags = iv.polyMesh->flags;
+ params.polyCount = iv.polyMesh->npolys;
+ params.nvp = iv.polyMesh->nvp;
+ params.detailMeshes = iv.polyMeshDetail->meshes;
+ params.detailVerts = iv.polyMeshDetail->verts;
+ params.detailVertsCount = iv.polyMeshDetail->nverts;
+ params.detailTris = iv.polyMeshDetail->tris;
+ params.detailTriCount = iv.polyMeshDetail->ntris;
+
+ params.offMeshConVerts = meshData.offMeshConnections.getCArray();
+ params.offMeshConCount = meshData.offMeshConnections.size()/6;
+ params.offMeshConRad = meshData.offMeshConnectionRads.getCArray();
+ params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray();
+ params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray();
+ params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray();
+
+ params.walkableHeight = BASE_UNIT_DIM*config.walkableHeight; // agent height
+ params.walkableRadius = BASE_UNIT_DIM*config.walkableRadius; // agent radius
+ params.walkableClimb = BASE_UNIT_DIM*config.walkableClimb; // keep less that walkableHeight (aka agent height)!
+ params.tileX = (((bmin[0] + bmax[0]) / 2) - navMesh->getParams()->orig[0]) / GRID_SIZE;
+ params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE;
+ rcVcopy(params.bmin, bmin);
+ rcVcopy(params.bmax, bmax);
+ params.cs = config.cs;
+ params.ch = config.ch;
+ params.tileSize = VERTEX_PER_MAP;
+
+ // will hold final navmesh
+ unsigned char* navData = NULL;
+ int navDataSize = 0;
+
+ do
+ {
+ // these values are checked within dtCreateNavMeshData - handle them here
+ // so we have a clear error message
+ if (params.nvp > DT_VERTS_PER_POLYGON)
+ {
+ printf("%s Invalid verts-per-polygon value! \n", tileString);
+ continue;
+ }
+ if (params.vertCount >= 0xffff)
+ {
+ printf("%s Too many vertices! \n", tileString);
+ continue;
+ }
+ if (!params.vertCount || !params.verts)
+ {
+ // occurs mostly when adjacent tiles have models
+ // loaded but those models don't span into this tile
+
+ // message is an annoyance
+ //printf("%sNo vertices to build tile! \n", tileString);
+ continue;
+ }
+ if (!params.polyCount || !params.polys ||
+ TILES_PER_MAP*TILES_PER_MAP == params.polyCount)
+ {
+ // we have flat tiles with no actual geometry - don't build those, its useless
+ // keep in mind that we do output those into debug info
+ // drop tiles with only exact count - some tiles may have geometry while having less tiles
+ printf("%s No polygons to build on tile! \n", tileString);
+ continue;
+ }
+ if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
+ {
+ printf("%s No detail mesh to build tile! \n", tileString);
+ continue;
+ }
+
+ printf("%s Building navmesh tile... \r", tileString);
+ if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
+ {
+ printf("%s Failed building navmesh tile! \n", tileString);
+ continue;
+ }
+
+ dtTileRef tileRef = 0;
+ printf("%s Adding tile to navmesh... \r", tileString);
+ // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
+ // is removed via removeTile()
+ dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
+ if (!tileRef || dtResult != DT_SUCCESS)
+ {
+ printf("%s Failed adding tile to navmesh! \n", tileString);
+ continue;
+ }
+
+ // file output
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", fileName);
+ perror(message);
+ navMesh->removeTile(tileRef, NULL, NULL);
+ continue;
+ }
+
+ printf("%s Writing to file... \r", tileString);
+
+ // write header
+ MmapTileHeader header;
+ header.usesLiquids = m_terrainBuilder->usesLiquids();
+ header.size = uint32(navDataSize);
+ fwrite(&header, sizeof(MmapTileHeader), 1, file);
+
+ // write data
+ fwrite(navData, sizeof(unsigned char), navDataSize, file);
+ fclose(file);
+
+ // now that tile is written to disk, we can unload it
+ navMesh->removeTile(tileRef, NULL, NULL);
+ }
+ while (0);
+
+ if (m_debugOutput)
+ {
+ // restore padding so that the debug visualization is correct
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] += (unsigned short)config.borderSize;
+ v[2] += (unsigned short)config.borderSize;
+ }
+
+ iv.generateObjFile(mapID, tileX, tileY, meshData);
+ iv.writeIV(mapID, tileX, tileY);
+ }
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
+ {
+ // this is for elevation
+ if (verts && vertCount)
+ rcCalcBounds(verts, vertCount, bmin, bmax);
+ else
+ {
+ bmin[1] = FLT_MIN;
+ bmax[1] = FLT_MAX;
+ }
+
+ // this is for width and depth
+ bmax[0] = (32 - int(tileX)) * GRID_SIZE;
+ bmax[2] = (32 - int(tileY)) * GRID_SIZE;
+ bmin[0] = bmax[0] - GRID_SIZE;
+ bmin[2] = bmax[2] - GRID_SIZE;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipMap(uint32 mapID)
+ {
+ if (m_skipContinents)
+ switch (mapID)
+ {
+ case 0:
+ case 1:
+ case 530:
+ case 571:
+ return true;
+ default:
+ break;
+ }
+
+ if (m_skipJunkMaps)
+ switch (mapID)
+ {
+ case 13: // test.wdt
+ case 25: // ScottTest.wdt
+ case 29: // Test.wdt
+ case 42: // Colin.wdt
+ case 169: // EmeraldDream.wdt (unused, and very large)
+ case 451: // development.wdt
+ case 573: // ExteriorTest.wdt
+ case 597: // CraigTest.wdt
+ case 605: // development_nonweighted.wdt
+ case 606: // QA_DVD.wdt
+ return true;
+ default:
+ if (isTransportMap(mapID))
+ return true;
+ break;
+ }
+
+ if (m_skipBattlegrounds)
+ switch (mapID)
+ {
+ case 30: // AV
+ case 37: // ?
+ case 489: // WSG
+ case 529: // AB
+ case 566: // EotS
+ case 607: // SotA
+ case 628: // IoC
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::isTransportMap(uint32 mapID)
+ {
+ switch (mapID)
+ {
+ // transport maps
+ case 582:
+ case 584:
+ case 586:
+ case 587:
+ case 588:
+ case 589:
+ case 590:
+ case 591:
+ case 592:
+ case 593:
+ case 594:
+ case 596:
+ case 610:
+ case 612:
+ case 613:
+ case 614:
+ case 620:
+ case 621:
+ case 622:
+ case 623:
+ case 641:
+ case 642:
+ case 647:
+ case 672:
+ case 673:
+ case 712:
+ case 713:
+ case 718:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ return false;
+
+ MmapTileHeader header;
+ fread(&header, sizeof(MmapTileHeader), 1, file);
+ fclose(file);
+
+ if (header.mmapMagic != MMAP_MAGIC || header.dtVersion != DT_NAVMESH_VERSION)
+ return false;
+
+ if (header.mmapVersion != MMAP_VERSION)
+ return false;
+
+ return true;
+ }
+
+}
diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h
new file mode 100644
index 0000000..d0f33ce
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MAP_BUILDER_H
+#define _MAP_BUILDER_H
+
+#include <vector>
+#include <set>
+#include <map>
+
+#include "TerrainBuilder.h"
+#include "IntermediateValues.h"
+
+#include "IVMapManager.h"
+#include "WorldModel.h"
+
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+using namespace std;
+using namespace VMAP;
+
+// G3D namespace typedefs conflicts with ACE typedefs
+
+namespace MMAP
+{
+ typedef map<uint32,set<uint32>*> TileList;
+ struct Tile
+ {
+ Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {}
+ ~Tile()
+ {
+ rcFreeCompactHeightfield(chf);
+ rcFreeContourSet(cset);
+ rcFreeHeightField(solid);
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ }
+ rcCompactHeightfield* chf;
+ rcHeightfield* solid;
+ rcContourSet* cset;
+ rcPolyMesh* pmesh;
+ rcPolyMeshDetail* dmesh;
+ };
+
+ class MapBuilder
+ {
+ public:
+ MapBuilder(float maxWalkableAngle = 60.f,
+ bool skipLiquid = false,
+ bool skipContinents = false,
+ bool skipJunkMaps = true,
+ bool skipBattlegrounds = false,
+ bool debugOutput = false,
+ bool bigBaseUnit = false,
+ const char* offMeshFilePath = NULL);
+
+ ~MapBuilder();
+
+ // builds all mmap tiles for the specified map id (ignores skip settings)
+ void buildMap(uint32 mapID);
+
+ // builds an mmap tile for the specified map and its mesh
+ void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ // builds list of maps, then builds all of mmap tiles (based on the skip settings)
+ void buildAllMaps();
+
+ private:
+ // detect maps and tiles
+ void discoverTiles();
+ set<uint32>* getTileList(uint32 mapID);
+
+ void buildNavMesh(uint32 mapID, dtNavMesh* &navMesh);
+
+ void buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh);
+
+ // move map building
+ void buildMoveMapTile(uint32 mapID,
+ uint32 tileX,
+ uint32 tileY,
+ MeshData &meshData,
+ float bmin[3],
+ float bmax[3],
+ dtNavMesh* navMesh);
+
+ void getTileBounds(uint32 tileX, uint32 tileY,
+ float* verts, int vertCount,
+ float* bmin, float* bmax);
+ void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY);
+
+ bool shouldSkipMap(uint32 mapID);
+ bool isTransportMap(uint32 mapID);
+ bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ TerrainBuilder* m_terrainBuilder;
+ TileList m_tiles;
+
+ bool m_debugOutput;
+
+ const char* m_offMeshFilePath;
+ bool m_skipContinents;
+ bool m_skipJunkMaps;
+ bool m_skipBattlegrounds;
+
+ float m_maxWalkableAngle;
+ bool m_bigBaseUnit;
+
+ // build performance - not really used for now
+ rcContext* m_rcContext;
+ };
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h
new file mode 100644
index 0000000..fd02ec0
--- /dev/null
+++ b/src/tools/mmaps_generator/PathCommon.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_COMMON_H
+#define _MMAP_COMMON_H
+
+#include <string>
+#include <vector>
+
+#include "Define.h"
+
+#ifndef _WIN32
+ #include <stddef.h>
+ #include <dirent.h>
+#endif
+
+#ifdef __linux__
+ #include <errno.h>
+#endif
+
+using namespace std;
+
+namespace MMAP
+{
+ inline bool matchWildcardFilter(const char* filter, const char* str)
+ {
+ if (!filter || !str)
+ return false;
+
+ // end on null character
+ while (*filter && *str)
+ {
+ if (*filter == '*')
+ {
+ if (*++filter == '\0') // wildcard at end of filter means all remaing chars match
+ return true;
+
+ while (true)
+ {
+ if (*filter == *str)
+ break;
+ if (*str == '\0')
+ return false; // reached end of string without matching next filter character
+ str++;
+ }
+ }
+ else if (*filter != *str)
+ return false; // mismatch
+
+ filter++;
+ str++;
+ }
+
+ return ((*filter == '\0' || (*filter == '*' && *++filter == '\0')) && *str == '\0');
+ }
+
+ enum ListFilesResult
+ {
+ LISTFILE_DIRECTORY_NOT_FOUND = 0,
+ LISTFILE_OK = 1
+ };
+
+ inline ListFilesResult getDirContents(vector<string> &fileList, string dirpath = ".", string filter = "*", bool includeSubDirs = false)
+ {
+ #ifdef WIN32
+ HANDLE hFind;
+ WIN32_FIND_DATA findFileInfo;
+ string directory;
+
+ directory = dirpath + "/" + filter;
+
+ hFind = FindFirstFile(directory.c_str(), &findFileInfo);
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ do
+ {
+ if (includeSubDirs || (findFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ fileList.push_back(string(findFileInfo.cFileName));
+ }
+ while (FindNextFile(hFind, &findFileInfo));
+
+ FindClose(hFind);
+
+ #else
+ const char *p = dirpath.c_str();
+ DIR * dirp = opendir(p);
+ struct dirent * dp;
+ dirp = opendir(p);
+
+ while (dirp)
+ {
+ errno = 0;
+ if ((dp = readdir(dirp)) != NULL)
+ {
+ if (matchWildcardFilter(filter.c_str(), dp->d_name))
+ fileList.push_back(string(dp->d_name));
+ }
+ else
+ break;
+ }
+
+ if (dirp)
+ closedir(dirp);
+ else
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ #endif
+
+ return LISTFILE_OK;
+ }
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp
new file mode 100644
index 0000000..2eb2c65
--- /dev/null
+++ b/src/tools/mmaps_generator/PathGenerator.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+using namespace MMAP;
+
+bool checkDirectories(bool debugOutput)
+{
+ vector<string> dirFiles;
+
+ if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || !dirFiles.size())
+ {
+ printf("'maps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "vmaps", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || !dirFiles.size())
+ {
+ printf("'vmaps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "mmaps") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'mmaps' directory does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (debugOutput)
+ {
+ if (getDirContents(dirFiles, "meshes") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'meshes' directory does not exist (no place to put debugOutput files)\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool handleArgs(int argc, char** argv,
+ int &mapnum,
+ int &tileX,
+ int &tileY,
+ float &maxAngle,
+ bool &skipLiquid,
+ bool &skipContinents,
+ bool &skipJunkMaps,
+ bool &skipBattlegrounds,
+ bool &debugOutput,
+ bool &silent,
+ bool &bigBaseUnit,
+ char* &offMeshInputPath)
+{
+ char* param = NULL;
+ for (int i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "--maxAngle") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ float maxangle = atof(param);
+ if (maxangle <= 90.f && maxangle >= 45.f)
+ maxAngle = maxangle;
+ else
+ printf("invalid option for '--maxAngle', using default\n");
+ }
+ else if (strcmp(argv[i], "--tile") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ char* stileX = strtok(param, ",");
+ char* stileY = strtok(NULL, ",");
+ int tilex = atoi(stileX);
+ int tiley = atoi(stileY);
+
+ if ((tilex > 0 && tilex < 64) || (tilex == 0 && strcmp(stileX, "0") == 0))
+ tileX = tilex;
+ if ((tiley > 0 && tiley < 64) || (tiley == 0 && strcmp(stileY, "0") == 0))
+ tileY = tiley;
+
+ if (tileX < 0 || tileY < 0)
+ {
+ printf("invalid tile coords.\n");
+ return false;
+ }
+ }
+ else if (strcmp(argv[i], "--skipLiquid") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipLiquid = true;
+ else if (strcmp(param, "false") == 0)
+ skipLiquid = false;
+ else
+ printf("invalid option for '--skipLiquid', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipContinents") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipContinents = true;
+ else if (strcmp(param, "false") == 0)
+ skipContinents = false;
+ else
+ printf("invalid option for '--skipContinents', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipJunkMaps") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipJunkMaps = true;
+ else if (strcmp(param, "false") == 0)
+ skipJunkMaps = false;
+ else
+ printf("invalid option for '--skipJunkMaps', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipBattlegrounds") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipBattlegrounds = true;
+ else if (strcmp(param, "false") == 0)
+ skipBattlegrounds = false;
+ else
+ printf("invalid option for '--skipBattlegrounds', using default\n");
+ }
+ else if (strcmp(argv[i], "--debugOutput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ debugOutput = true;
+ else if (strcmp(param, "false") == 0)
+ debugOutput = false;
+ else
+ printf("invalid option for '--debugOutput', using default true\n");
+ }
+ else if (strcmp(argv[i], "--silent") == 0)
+ {
+ silent = true;
+ }
+ else if (strcmp(argv[i], "--bigBaseUnit") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ bigBaseUnit = true;
+ else if (strcmp(param, "false") == 0)
+ bigBaseUnit = false;
+ else
+ printf("invalid option for '--bigBaseUnit', using default false\n");
+ }
+ else if (strcmp(argv[i], "--offMeshInput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ offMeshInputPath = param;
+ }
+ else
+ {
+ int map = atoi(argv[i]);
+ if (map > 0 || (map == 0 && (strcmp(argv[i], "0") == 0)))
+ mapnum = map;
+ else
+ {
+ printf("invalid map id\n");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+int finish(const char* message, int returnValue)
+{
+ printf("%s", message);
+ getchar();
+ return returnValue;
+}
+
+int main(int argc, char** argv)
+{
+ int mapnum = -1;
+ float maxAngle = 60.0f;
+ int tileX = -1, tileY = -1;
+ bool skipLiquid = false,
+ skipContinents = false,
+ skipJunkMaps = true,
+ skipBattlegrounds = false,
+ debugOutput = false,
+ silent = false,
+ bigBaseUnit = false;
+ char* offMeshInputPath = NULL;
+
+ bool validParam = handleArgs(argc, argv, mapnum,
+ tileX, tileY, maxAngle,
+ skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds,
+ debugOutput, silent, bigBaseUnit, offMeshInputPath);
+
+ if (!validParam)
+ return silent ? -1 : finish("You have specified invalid parameters", -1);
+
+ if (mapnum == -1 && debugOutput)
+ {
+ if (silent)
+ return -2;
+
+ printf("You have specifed debug output, but didn't specify a map to generate.\n");
+ printf("This will generate debug output for ALL maps.\n");
+ printf("Are you sure you want to continue? (y/n) ");
+ if (getchar() != 'y')
+ return 0;
+ }
+
+ if (!checkDirectories(debugOutput))
+ return silent ? -3 : finish("Press any key to close...", -3);
+
+ MapBuilder builder(maxAngle, skipLiquid, skipContinents, skipJunkMaps,
+ skipBattlegrounds, debugOutput, bigBaseUnit, offMeshInputPath);
+
+ if (tileX > -1 && tileY > -1 && mapnum >= 0)
+ builder.buildSingleTile(mapnum, tileX, tileY);
+ else if (mapnum >= 0)
+ builder.buildMap(uint32(mapnum));
+ else
+ builder.buildAllMaps();
+
+ return silent ? 1 : finish("Movemap build is complete!", 1);
+}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp
new file mode 100644
index 0000000..c696f60
--- /dev/null
+++ b/src/tools/mmaps_generator/TerrainBuilder.cpp
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TerrainBuilder.h"
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "VMapManager2.h"
+#include "MapTree.h"
+#include "ModelInstance.h"
+
+namespace MMAP
+{
+ TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ }
+ TerrainBuilder::~TerrainBuilder() { }
+
+ /**************************************************************************/
+ void TerrainBuilder::getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc)
+ {
+ switch (portion)
+ {
+ case ENTIRE:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ case TOP:
+ loopStart = 0;
+ loopEnd = V8_SIZE;
+ loopInc = 1;
+ break;
+ case LEFT:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
+ loopInc = V8_SIZE;
+ break;
+ case RIGHT:
+ loopStart = V8_SIZE - 1;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = V8_SIZE;
+ break;
+ case BOTTOM:
+ loopStart = V8_SIZE_SQ - V8_SIZE;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ if (loadMap(mapID, tileX, tileY, meshData, ENTIRE))
+ {
+ loadMap(mapID, tileX+1, tileY, meshData, LEFT);
+ loadMap(mapID, tileX-1, tileY, meshData, RIGHT);
+ loadMap(mapID, tileX, tileY+1, meshData, TOP);
+ loadMap(mapID, tileX, tileY-1, meshData, BOTTOM);
+ }
+ }
+
+ /**************************************************************************/
+ bool TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion)
+ {
+ char mapFileName[255];
+ sprintf(mapFileName, "maps/%03u%02u%02u.map", mapID, tileY, tileX);
+
+ FILE* mapFile = fopen(mapFileName, "rb");
+ if (!mapFile)
+ return false;
+
+ map_fileheader fheader;
+ fread(&fheader, sizeof(map_fileheader), 1, mapFile);
+
+ if (fheader.versionMagic != *((uint32 const*)(MAP_VERSION_MAGIC)))
+ {
+ fclose(mapFile);
+ printf("%s is the wrong version, please extract new .map files\n", mapFileName);
+ return false;
+ }
+
+ map_heightHeader hheader;
+ fseek(mapFile, fheader.heightMapOffset, SEEK_SET);
+ fread(&hheader, sizeof(map_heightHeader), 1, mapFile);
+
+ bool haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT);
+ bool haveLiquid = fheader.liquidMapOffset && !m_skipLiquid;
+
+ // no data in this map file
+ if (!haveTerrain && !haveLiquid)
+ {
+ fclose(mapFile);
+ return false;
+ }
+
+ // data used later
+ uint16 holes[16][16];
+ memset(holes, 0, sizeof(holes));
+ uint8 liquid_type[16][16];
+ memset(liquid_type, 0, sizeof(liquid_type));
+ G3D::Array<int> ltriangles;
+ G3D::Array<int> ttriangles;
+
+ // terrain data
+ if (haveTerrain)
+ {
+ int i;
+ float heightMultiplier;
+ float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ];
+
+ if (hheader.flags & MAP_HEIGHT_AS_INT8)
+ {
+ uint8 v9[V9_SIZE_SQ];
+ uint8 v8[V8_SIZE_SQ];
+ fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile);
+ fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile);
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;
+
+ for (i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else if (hheader.flags & MAP_HEIGHT_AS_INT16)
+ {
+ uint16 v9[V9_SIZE_SQ];
+ uint16 v8[V8_SIZE_SQ];
+ fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile);
+ fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile);
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;
+
+ for (i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else
+ {
+ fread (V9, sizeof(float), V9_SIZE_SQ, mapFile);
+ fread(V8, sizeof(float), V8_SIZE_SQ, mapFile);
+ }
+
+ // hole data
+ memset(holes, 0, fheader.holesSize);
+ fseek(mapFile, fheader.holesOffset, SEEK_SET);
+ fread(holes, fheader.holesSize, 1, mapFile);
+
+ int count = meshData.solidVerts.size() / 3;
+ float xoffset = (float(tileX)-32)*GRID_SIZE;
+ float yoffset = (float(tileY)-32)*GRID_SIZE;
+
+ float coord[3];
+
+ for (i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ for (i = 0; i < V8_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ int j, indices[3], loopStart, loopEnd, loopInc;
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ for (i = loopStart; i < loopEnd; i+=loopInc)
+ for (j = TOP; j <= BOTTOM; j+=1)
+ {
+ getHeightTriangle(i, Spot(j), indices);
+ ttriangles.append(indices[2] + count);
+ ttriangles.append(indices[1] + count);
+ ttriangles.append(indices[0] + count);
+ }
+ }
+
+ // liquid data
+ if (haveLiquid)
+ {
+ map_liquidHeader lheader;
+ fseek(mapFile, fheader.liquidMapOffset, SEEK_SET);
+ fread(&lheader, sizeof(map_liquidHeader), 1, mapFile);
+
+ float* liquid_map = NULL;
+
+ if (!(lheader.flags & MAP_LIQUID_NO_TYPE))
+ fread(liquid_type, sizeof(liquid_type), 1, mapFile);
+
+ if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
+ {
+ liquid_map = new float [lheader.width*lheader.height];
+ fread(liquid_map, sizeof(float), lheader.width*lheader.height, mapFile);
+ }
+
+ if (liquid_type && liquid_map)
+ {
+ int count = meshData.liquidVerts.size() / 3;
+ float xoffset = (float(tileX)-32)*GRID_SIZE;
+ float yoffset = (float(tileY)-32)*GRID_SIZE;
+
+ float coord[3];
+ int row, col;
+
+ // generate coordinates
+ if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
+ {
+ int j = 0;
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ row = i / V9_SIZE;
+ col = i % V9_SIZE;
+
+ if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
+ col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
+ {
+ // dummy vert using invalid height
+ meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, INVALID_MAP_LIQ_HEIGHT, (yoffset+row*GRID_PART_SIZE)*-1);
+ continue;
+ }
+
+ getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map);
+ meshData.liquidVerts.append(coord[0]);
+ meshData.liquidVerts.append(coord[2]);
+ meshData.liquidVerts.append(coord[1]);
+ j++;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ row = i / V9_SIZE;
+ col = i % V9_SIZE;
+ meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1);
+ }
+ }
+
+ delete [] liquid_map;
+
+ int indices[3], loopStart, loopEnd, loopInc, triInc;
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ triInc = BOTTOM-TOP;
+
+ // generate triangles
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ for (int j = TOP; j <= BOTTOM; j+= triInc)
+ {
+ getHeightTriangle(i, Spot(j), indices, true);
+ ltriangles.append(indices[2] + count);
+ ltriangles.append(indices[1] + count);
+ ltriangles.append(indices[0] + count);
+ }
+ }
+ }
+
+ fclose(mapFile);
+
+ // now that we have gathered the data, we can figure out which parts to keep:
+ // liquid above ground, ground above liquid
+ int loopStart, loopEnd, loopInc, tTriCount = 4;
+ bool useTerrain, useLiquid;
+
+ float* lverts = meshData.liquidVerts.getCArray();
+ int* ltris = ltriangles.getCArray();
+
+ float* tverts = meshData.solidVerts.getCArray();
+ int* ttris = ttriangles.getCArray();
+
+ if (ltriangles.size() + ttriangles.size() == 0)
+ return false;
+
+ // make a copy of liquid vertices
+ // used to pad right-bottom frame due to lost vertex data at extraction
+ float* lverts_copy = NULL;
+ if (meshData.liquidVerts.size())
+ {
+ lverts_copy = new float[meshData.liquidVerts.size()];
+ memcpy(lverts_copy, lverts, sizeof(float)*meshData.liquidVerts.size());
+ }
+
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ {
+ for (int j = 0; j < 2; ++j)
+ {
+ // default is true, will change to false if needed
+ useTerrain = true;
+ useLiquid = true;
+ uint8 liquidType = MAP_LIQUID_TYPE_NO_WATER;
+
+ // if there is no liquid, don't use liquid
+ if (!liquid_type || !meshData.liquidVerts.size() || !ltriangles.size())
+ useLiquid = false;
+ else
+ {
+ liquidType = getLiquidType(i, liquid_type);
+ switch (liquidType)
+ {
+ default:
+ useLiquid = false;
+ break;
+ case MAP_LIQUID_TYPE_WATER:
+ case MAP_LIQUID_TYPE_OCEAN:
+ // merge different types of water
+ liquidType = NAV_WATER;
+ break;
+ case MAP_LIQUID_TYPE_MAGMA:
+ liquidType = NAV_MAGMA;
+ break;
+ case MAP_LIQUID_TYPE_SLIME:
+ liquidType = NAV_SLIME;
+ break;
+ case MAP_LIQUID_TYPE_DARK_WATER:
+ // players should not be here, so logically neither should creatures
+ useTerrain = false;
+ useLiquid = false;
+ break;
+ }
+ }
+
+ // if there is no terrain, don't use terrain
+ if (!ttriangles.size())
+ useTerrain = false;
+
+ // while extracting ADT data we are losing right-bottom vertices
+ // this code adds fair approximation of lost data
+ if (useLiquid)
+ {
+ float quadHeight = 0;
+ uint32 validCount = 0;
+ for(uint32 idx = 0; idx < 3; idx++)
+ {
+ float h = lverts_copy[ltris[idx]*3 + 1];
+ if (h != INVALID_MAP_LIQ_HEIGHT && h < INVALID_MAP_LIQ_HEIGHT_MAX)
+ {
+ quadHeight += h;
+ validCount++;
+ }
+ }
+
+ // update vertex height data
+ if (validCount > 0 && validCount < 3)
+ {
+ quadHeight /= validCount;
+ for(uint32 idx = 0; idx < 3; idx++)
+ {
+ float h = lverts[ltris[idx]*3 + 1];
+ if (h == INVALID_MAP_LIQ_HEIGHT || h > INVALID_MAP_LIQ_HEIGHT_MAX)
+ lverts[ltris[idx]*3 + 1] = quadHeight;
+ }
+ }
+
+ // no valid vertexes - don't use this poly at all
+ if (validCount == 0)
+ useLiquid = false;
+ }
+
+ // if there is a hole here, don't use the terrain
+ if (useTerrain)
+ useTerrain = !isHole(i, holes);
+
+ // we use only one terrain kind per quad - pick higher one
+ if (useTerrain && useLiquid)
+ {
+ float minLLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
+ float maxLLevel = INVALID_MAP_LIQ_HEIGHT;
+ for(uint32 x = 0; x < 3; x++)
+ {
+ float h = lverts[ltris[x]*3 + 1];
+ if (minLLevel > h)
+ minLLevel = h;
+
+ if (maxLLevel < h)
+ maxLLevel = h;
+ }
+
+ float maxTLevel = INVALID_MAP_LIQ_HEIGHT;
+ float minTLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
+ for(uint32 x = 0; x < 6; x++)
+ {
+ float h = tverts[ttris[x]*3 + 1];
+ if (maxTLevel < h)
+ maxTLevel = h;
+
+ if (minTLevel > h)
+ minTLevel = h;
+ }
+
+ // terrain under the liquid?
+ if (minLLevel > maxTLevel)
+ useTerrain = false;
+
+ //liquid under the terrain?
+ if (minTLevel > maxLLevel)
+ useLiquid = false;
+ }
+
+ // store the result
+ if (useLiquid)
+ {
+ meshData.liquidType.append(liquidType);
+ for (int k = 0; k < 3; ++k)
+ meshData.liquidTris.append(ltris[k]);
+ }
+
+ if (useTerrain)
+ for (int k = 0; k < 3*tTriCount/2; ++k)
+ meshData.solidTris.append(ttris[k]);
+
+ // advance to next set of triangles
+ ltris += 3;
+ ttris += 3*tTriCount/2;
+ }
+ }
+
+ if (lverts_copy)
+ delete [] lverts_copy;
+
+ return meshData.solidTris.size() || meshData.liquidTris.size();
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v)
+ {
+ // wow coords: x, y, height
+ // coord is mirroed about the horizontal axes
+ switch (grid)
+ {
+ case GRID_V9:
+ coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
+ coord[2] = v[index];
+ break;
+ case GRID_V8:
+ coord[0] = (xOffset + index%(V8_SIZE)*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V8_SIZE))*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
+ coord[2] = v[index];
+ break;
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getHeightTriangle(int square, Spot triangle, int* indices, bool liquid/* = false*/)
+ {
+ int rowOffset = square/V8_SIZE;
+ if (!liquid)
+ switch (triangle)
+ {
+ case TOP:
+ indices[0] = square+rowOffset; // 0-----1 .... 128
+ indices[1] = square+1+rowOffset; // |\ T /|
+ indices[2] = (V9_SIZE_SQ)+square; // | \ / |
+ break; // |L 0 R| .. 127
+ case LEFT: // | / \ |
+ indices[0] = square+rowOffset; // |/ B \|
+ indices[1] = (V9_SIZE_SQ)+square; // 129---130 ... 386
+ indices[2] = square+V9_SIZE+rowOffset; // |\ /|
+ break; // | \ / |
+ case RIGHT: // | 128 | .. 255
+ indices[0] = square+1+rowOffset; // | / \ |
+ indices[1] = square+V9_SIZE+1+rowOffset; // |/ \|
+ indices[2] = (V9_SIZE_SQ)+square; // 258---259 ... 515
+ break;
+ case BOTTOM:
+ indices[0] = (V9_SIZE_SQ)+square;
+ indices[1] = square+V9_SIZE+1+rowOffset;
+ indices[2] = square+V9_SIZE+rowOffset;
+ break;
+ default: break;
+ }
+ else
+ switch (triangle)
+ { // 0-----1 .... 128
+ case TOP: // |\ |
+ indices[0] = square+rowOffset; // | \ T |
+ indices[1] = square+1+rowOffset; // | \ |
+ indices[2] = square+V9_SIZE+1+rowOffset; // | B \ |
+ break; // | \|
+ case BOTTOM: // 129---130 ... 386
+ indices[0] = square+rowOffset; // |\ |
+ indices[1] = square+V9_SIZE+1+rowOffset; // | \ |
+ indices[2] = square+V9_SIZE+rowOffset; // | \ |
+ break; // | \ |
+ default: break; // | \|
+ } // 258---259 ... 515
+
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v)
+ {
+ // wow coords: x, y, height
+ // coord is mirroed about the horizontal axes
+ coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
+ coord[2] = v[index2];
+ }
+
+ static uint16 holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888};
+ static uint16 holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000};
+
+ /**************************************************************************/
+ bool TerrainBuilder::isHole(int square, const uint16 holes[16][16])
+ {
+ int row = square / 128;
+ int col = square % 128;
+ int cellRow = row / 8; // 8 squares per cell
+ int cellCol = col / 8;
+ int holeRow = row % 8 / 2;
+ int holeCol = (square - (row * 128 + cellCol * 8)) / 2;
+
+ uint16 hole = holes[cellRow][cellCol];
+
+ return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0;
+ }
+
+ /**************************************************************************/
+ uint8 TerrainBuilder::getLiquidType(int square, const uint8 liquid_type[16][16])
+ {
+ int row = square / 128;
+ int col = square % 128;
+ int cellRow = row / 8; // 8 squares per cell
+ int cellCol = col / 8;
+
+ return liquid_type[cellRow][cellCol];
+ }
+
+ /**************************************************************************/
+ bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ IVMapManager* vmapManager = new VMapManager2();
+ int result = vmapManager->loadMap("vmaps", mapID, tileX, tileY);
+ bool retval = false;
+
+ do
+ {
+ if (result == VMAP_LOAD_RESULT_ERROR)
+ break;
+
+ InstanceTreeMap instanceTrees;
+ ((VMapManager2*)vmapManager)->getInstanceMapTree(instanceTrees);
+
+ if (!instanceTrees[mapID])
+ break;
+
+ ModelInstance* models = NULL;
+ uint32 count = 0;
+ instanceTrees[mapID]->getModelInstances(models, count);
+
+ if (!models)
+ break;
+
+ for (uint32 i = 0; i < count; ++i)
+ {
+ ModelInstance instance = models[i];
+
+ // model instances exist in tree even though there are instances of that model in this tile
+ WorldModel* worldModel = instance.getWorldModel();
+ if (!worldModel)
+ continue;
+
+ // now we have a model to add to the meshdata
+ retval = true;
+
+ vector<GroupModel> groupModels;
+ worldModel->getGroupModels(groupModels);
+
+ // all M2s need to have triangle indices reversed
+ bool isM2 = instance.name.find(".m2") != instance.name.npos || instance.name.find(".M2") != instance.name.npos;
+
+ // transform data
+ float scale = instance.iScale;
+ G3D::Matrix3 rotation = G3D::Matrix3::fromEulerAnglesXYZ(G3D::pi()*instance.iRot.z/-180.f, G3D::pi()*instance.iRot.x/-180.f, G3D::pi()*instance.iRot.y/-180.f);
+ Vector3 position = instance.iPos;
+ position.x -= 32*GRID_SIZE;
+ position.y -= 32*GRID_SIZE;
+
+ for (vector<GroupModel>::iterator it = groupModels.begin(); it != groupModels.end(); ++it)
+ {
+ vector<Vector3> tempVertices;
+ vector<Vector3> transformedVertices;
+ vector<MeshTriangle> tempTriangles;
+ WmoLiquid* liquid = NULL;
+
+ (*it).getMeshData(tempVertices, tempTriangles, liquid);
+
+ // first handle collision mesh
+ transform(tempVertices, transformedVertices, scale, rotation, position);
+
+ int offset = meshData.solidVerts.size() / 3;
+
+ copyVertices(transformedVertices, meshData.solidVerts);
+ copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
+
+ // now handle liquid data
+ if (liquid)
+ {
+ vector<Vector3> liqVerts;
+ vector<int> liqTris;
+ uint32 tilesX, tilesY, vertsX, vertsY;
+ Vector3 corner;
+ liquid->getPosInfo(tilesX, tilesY, corner);
+ vertsX = tilesX + 1;
+ vertsY = tilesY + 1;
+ uint8* flags = liquid->GetFlagsStorage();
+ float* data = liquid->GetHeightStorage();
+ uint8 type = NAV_EMPTY;
+
+ // convert liquid type to NavTerrain
+ switch (liquid->GetType())
+ {
+ case 0:
+ case 1:
+ type = NAV_WATER;
+ break;
+ case 2:
+ type = NAV_MAGMA;
+ break;
+ case 3:
+ type = NAV_SLIME;
+ break;
+ }
+
+ // indexing is weird...
+ // after a lot of trial and error, this is what works:
+ // vertex = y*vertsX+x
+ // tile = x*tilesY+y
+ // flag = y*tilesY+x
+
+ Vector3 vert;
+ for (uint32 x = 0; x < vertsX; ++x)
+ for (uint32 y = 0; y < vertsY; ++y)
+ {
+ vert = Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y*vertsX + x]);
+ vert = vert * rotation * scale + position;
+ vert.x *= -1.f;
+ vert.y *= -1.f;
+ liqVerts.push_back(vert);
+ }
+
+ int idx1, idx2, idx3, idx4;
+ uint32 square;
+ for (uint32 x = 0; x < tilesX; ++x)
+ for (uint32 y = 0; y < tilesY; ++y)
+ if ((flags[x+y*tilesX] & 0x0f) != 0x0f)
+ {
+ square = x * tilesY + y;
+ idx1 = square+x;
+ idx2 = square+1+x;
+ idx3 = square+tilesY+1+1+x;
+ idx4 = square+tilesY+1+x;
+
+ // top triangle
+ liqTris.push_back(idx3);
+ liqTris.push_back(idx2);
+ liqTris.push_back(idx1);
+ // bottom triangle
+ liqTris.push_back(idx4);
+ liqTris.push_back(idx3);
+ liqTris.push_back(idx1);
+ }
+
+ uint32 liqOffset = meshData.liquidVerts.size() / 3;
+ for (uint32 i = 0; i < liqVerts.size(); ++i)
+ meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x);
+
+ for (uint32 i = 0; i < liqTris.size() / 3; ++i)
+ {
+ meshData.liquidTris.append(liqTris[i*3+1] + liqOffset, liqTris[i*3+2] + liqOffset, liqTris[i*3] + liqOffset);
+ meshData.liquidType.append(type);
+ }
+ }
+ }
+ }
+ }
+ while (false);
+
+ vmapManager->unloadMap(mapID, tileX, tileY);
+ delete vmapManager;
+
+ return retval;
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::transform(vector<Vector3> &source, vector<Vector3> &transformedVertices, float scale, G3D::Matrix3 &rotation, Vector3 &position)
+ {
+ for (vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ // apply tranform, then mirror along the horizontal axes
+ Vector3 v((*it) * rotation * scale + position);
+ v.x *= -1.f;
+ v.y *= -1.f;
+ transformedVertices.push_back(v);
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyVertices(vector<Vector3> &source, G3D::Array<float> &dest)
+ {
+ for (vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).y);
+ dest.push_back((*it).z);
+ dest.push_back((*it).x);
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyIndices(vector<MeshTriangle> &source, G3D::Array<int> &dest, int offset, bool flip)
+ {
+ if (flip)
+ {
+ for (vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).idx2+offset);
+ dest.push_back((*it).idx1+offset);
+ dest.push_back((*it).idx0+offset);
+ }
+ }
+ else
+ {
+ for (vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).idx0+offset);
+ dest.push_back((*it).idx1+offset);
+ dest.push_back((*it).idx2+offset);
+ }
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyIndices(G3D::Array<int> &source, G3D::Array<int> &dest, int offset)
+ {
+ int* src = source.getCArray();
+ for (int32 i = 0; i < source.size(); ++i)
+ dest.append(src[i] + offset);
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris)
+ {
+ map<int, int> vertMap;
+
+ int* t = tris.getCArray();
+ float* v = verts.getCArray();
+
+ // collect all the vertex indices from triangle
+ for (int i = 0; i < tris.size(); ++i)
+ {
+ if (vertMap.find(t[i]) != vertMap.end())
+ continue;
+
+ vertMap.insert(std::pair<int, int>(t[i], 0));
+ }
+
+ // collect the vertices
+ G3D::Array<float> cleanVerts;
+ int index, count = 0;
+ for (map<int, int>::iterator it = vertMap.begin(); it != vertMap.end(); ++it)
+ {
+ index = (*it).first;
+ (*it).second = count;
+ cleanVerts.append(v[index*3], v[index*3+1], v[index*3+2]);
+ count++;
+ }
+ verts.fastClear();
+ verts.append(cleanVerts);
+ cleanVerts.clear();
+
+ // update triangles to use new indices
+ for (int i = 0; i < tris.size(); ++i)
+ {
+ map<int, int>::iterator it;
+ if ((it = vertMap.find(t[i])) == vertMap.end())
+ continue;
+
+ t[i] = (*it).second;
+ }
+
+ vertMap.clear();
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath)
+ {
+ // no meshfile input given?
+ if (offMeshFilePath == NULL)
+ return;
+
+ FILE* fp = fopen(offMeshFilePath, "rb");
+ if (!fp)
+ {
+ printf(" loadOffMeshConnections:: input file %s not found!\n", offMeshFilePath);
+ return;
+ }
+
+ // pretty silly thing, as we parse entire file and load only the tile we need
+ // but we don't expect this file to be too large
+ char* buf = new char[512];
+ while(fgets(buf, 512, fp))
+ {
+ float p0[3], p1[3];
+ int mid, tx, ty;
+ float size;
+ if (10 != sscanf(buf, "%d %d,%d (%f %f %f) (%f %f %f) %f", &mid, &tx, &ty,
+ &p0[0], &p0[1], &p0[2], &p1[0], &p1[1], &p1[2], &size))
+ continue;
+
+ if (mapID == mid, tileX == tx, tileY == ty)
+ {
+ meshData.offMeshConnections.append(p0[1]);
+ meshData.offMeshConnections.append(p0[2]);
+ meshData.offMeshConnections.append(p0[0]);
+
+ meshData.offMeshConnections.append(p1[1]);
+ meshData.offMeshConnections.append(p1[2]);
+ meshData.offMeshConnections.append(p1[0]);
+
+ meshData.offMeshConnectionDirs.append(1); // 1 - both direction, 0 - one sided
+ meshData.offMeshConnectionRads.append(size); // agent size equivalent
+ // can be used same way as polygon flags
+ meshData.offMeshConnectionsAreas.append((unsigned char)0xFF);
+ meshData.offMeshConnectionsFlags.append((unsigned short)0xFF); // all movement masks can make this path
+ }
+
+ }
+
+ delete [] buf;
+ fclose(fp);
+ }
+}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.h b/src/tools/mmaps_generator/TerrainBuilder.h
new file mode 100644
index 0000000..a7f2188
--- /dev/null
+++ b/src/tools/mmaps_generator/TerrainBuilder.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_TERRAIN_BUILDER_H
+#define _MMAP_TERRAIN_BUILDER_H
+
+#include "PathCommon.h"
+#include "Map.h"
+#include "SharedDefines.h"
+
+#include "WorldModel.h"
+
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/Matrix3.h"
+
+using namespace Trinity;
+
+namespace MMAP
+{
+ enum Spot
+ {
+ TOP = 1,
+ RIGHT = 2,
+ LEFT = 3,
+ BOTTOM = 4,
+ ENTIRE = 5
+ };
+
+ enum Grid
+ {
+ GRID_V8,
+ GRID_V9
+ };
+
+ static const int V9_SIZE = 129;
+ static const int V9_SIZE_SQ = V9_SIZE*V9_SIZE;
+ static const int V8_SIZE = 128;
+ static const int V8_SIZE_SQ = V8_SIZE*V8_SIZE;
+ static const float GRID_SIZE = 533.33333f;
+ static const float GRID_PART_SIZE = GRID_SIZE/V8_SIZE;
+
+ // see contrib/extractor/system.cpp, CONF_use_minHeight
+ static const float INVALID_MAP_LIQ_HEIGHT = -500.f;
+ static const float INVALID_MAP_LIQ_HEIGHT_MAX = 5000.0f;
+
+ // see following files:
+ // contrib/extractor/system.cpp
+ // src/game/Map.cpp
+ static char const* MAP_VERSION_MAGIC = "v1.2";
+
+ struct MeshData
+ {
+ G3D::Array<float> solidVerts;
+ G3D::Array<int> solidTris;
+
+ G3D::Array<float> liquidVerts;
+ G3D::Array<int> liquidTris;
+ G3D::Array<uint8> liquidType;
+
+ // offmesh connection data
+ G3D::Array<float> offMeshConnections; // [p0y,p0z,p0x,p1y,p1z,p1x] - per connection
+ G3D::Array<float> offMeshConnectionRads;
+ G3D::Array<unsigned char> offMeshConnectionDirs;
+ G3D::Array<unsigned char> offMeshConnectionsAreas;
+ G3D::Array<unsigned short> offMeshConnectionsFlags;
+ };
+
+ class TerrainBuilder
+ {
+ public:
+ TerrainBuilder(bool skipLiquid);
+ ~TerrainBuilder();
+
+ void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath);
+
+ bool usesLiquids() { return !m_skipLiquid; }
+
+ // vert and triangle methods
+ static void transform(vector<G3D::Vector3> &original, vector<G3D::Vector3> &transformed,
+ float scale, G3D::Matrix3 &rotation, G3D::Vector3 &position);
+ static void copyVertices(vector<G3D::Vector3> &source, G3D::Array<float> &dest);
+ static void copyIndices(vector<VMAP::MeshTriangle> &source, G3D::Array<int> &dest, int offest, bool flip);
+ static void copyIndices(G3D::Array<int> &src, G3D::Array<int> &dest, int offset);
+ static void cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris);
+ private:
+ /// Loads a portion of a map's terrain
+ bool loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion);
+
+ /// Sets loop variables for selecting only certain parts of a map's terrain
+ void getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc);
+
+ /// Controls whether liquids are loaded
+ bool m_skipLiquid;
+
+ /// Load the map terrain from file
+ bool loadHeightMap(uint32 mapID, uint32 tileX, uint32 tileY, G3D::Array<float> &vertices, G3D::Array<int> &triangles, Spot portion);
+
+ /// Get the vector coordinate for a specific position
+ void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v);
+
+ /// Get the triangle's vector indices for a specific position
+ void getHeightTriangle(int square, Spot triangle, int* indices, bool liquid = false);
+
+ /// Determines if the specific position's triangles should be rendered
+ bool isHole(int square, const uint16 holes[16][16]);
+
+ /// Get the liquid vector coordinate for a specific position
+ void getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v);
+
+ /// Get the liquid type for a specific position
+ uint8 getLiquidType(int square, const uint8 liquid_type[16][16]);
+
+ // hide parameterless and copy constructor
+ TerrainBuilder();
+ TerrainBuilder(const TerrainBuilder &tb);
+ };
+}
+
+#endif
+
diff --git a/src/tools/mmaps_generator/VMapExtensions.cpp b/src/tools/mmaps_generator/VMapExtensions.cpp
new file mode 100644
index 0000000..4f203e1
--- /dev/null
+++ b/src/tools/mmaps_generator/VMapExtensions.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <vector>
+#include "MapTree.h"
+#include "VMapManager2.h"
+#include "WorldModel.h"
+#include "ModelInstance.h"
+
+using namespace std;
+
+namespace VMAP
+{
+ // Need direct access to encapsulated VMAP data, so we add functions for MMAP generator
+ // maybe add MapBuilder as friend to all of the below classes would be better?
+
+ // declared in src/shared/vmap/MapTree.h
+ void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)
+ {
+ models = iTreeValues;
+ count = iNTreeValues;
+ }
+
+ // declared in src/shared/vmap/VMapManager2.h
+ void VMapManager2::getInstanceMapTree(InstanceTreeMap &instanceMapTree)
+ {
+ instanceMapTree = iInstanceMapTrees;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void WorldModel::getGroupModels(vector<GroupModel> &groupModels)
+ {
+ groupModels = this->groupModels;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void GroupModel::getMeshData(vector<Vector3> &vertices, vector<MeshTriangle> &triangles, WmoLiquid* &liquid)
+ {
+ vertices = this->vertices;
+ triangles = this->triangles;
+ liquid = iLiquid;
+ }
+
+ // declared in src/shared/vmap/ModelInstance.h
+ WorldModel* const ModelInstance::getWorldModel()
+ {
+ return iModel;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void WmoLiquid::getPosInfo(uint32 &tilesX, uint32 &tilesY, Vector3 &corner) const
+ {
+ tilesX = iTilesX;
+ tilesY = iTilesY;
+ corner = iCorner;
+ }
+}
@jjbreis
Copy link

jjbreis commented May 25, 2013

Is this for 3.3.5a? What does it fix? Could you specify :o ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment