| @@ -0,0 +1,393 @@ | ||
| // | ||
| // 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; | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// All points are projected onto the xz-plane, so the y-values are ignored. | ||
| 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; | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// All vertices are projected onto the xz-plane, so the y-values are ignored. | ||
| 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; | ||
| } | ||
|
|
||
| // Returns a random point in a convex polygon. | ||
| // Adapted from Graphics Gems article. | ||
| void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas, | ||
| const float s, const float t, float* out) | ||
| { | ||
| // Calc triangle araes | ||
| float areasum = 0.0f; | ||
| for (int i = 2; i < npts; i++) { | ||
| areas[i] = dtTriArea2D(&pts[0], &pts[(i-1)*3], &pts[i*3]); | ||
| areasum += dtMax(0.001f, areas[i]); | ||
| } | ||
| // Find sub triangle weighted by area. | ||
| const float thr = s*areasum; | ||
| float acc = 0.0f; | ||
| float u = 0.0f; | ||
| int tri = 0; | ||
| for (int i = 2; i < npts; i++) { | ||
| const float dacc = areas[i]; | ||
| if (thr >= acc && thr < (acc+dacc)) | ||
| { | ||
| u = (thr - acc) / dacc; | ||
| tri = i; | ||
| break; | ||
| } | ||
| acc += dacc; | ||
| } | ||
|
|
||
| float v = dtSqrt(t); | ||
|
|
||
| const float a = 1 - v; | ||
| const float b = (1 - u) * v; | ||
| const float c = u * v; | ||
| const float* pa = &pts[0]; | ||
| const float* pb = &pts[(tri-1)*3]; | ||
| const float* pc = &pts[tri*3]; | ||
|
|
||
| out[0] = a*pa[0] + b*pb[0] + c*pc[0]; | ||
| out[1] = a*pa[1] + b*pb[1] + c*pc[1]; | ||
| out[2] = a*pa[2] + b*pb[2] + c*pc[2]; | ||
| } | ||
|
|
||
| inline float vperpXZ(const float* a, const float* b) { return a[0]*b[2] - a[2]*b[0]; } | ||
|
|
||
| bool dtIntersectSegSeg2D(const float* ap, const float* aq, | ||
| const float* bp, const float* bq, | ||
| float& s, float& t) | ||
| { | ||
| float u[3], v[3], w[3]; | ||
| dtVsub(u,aq,ap); | ||
| dtVsub(v,bq,bp); | ||
| dtVsub(w,ap,bp); | ||
| float d = vperpXZ(u,v); | ||
| if (fabsf(d) < 1e-6f) return false; | ||
| s = vperpXZ(v,w) / d; | ||
| t = vperpXZ(u,w) / d; | ||
| return true; | ||
| } | ||
|
|
| @@ -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 DETOURNAVMESHBUILDER_H | ||
| #define DETOURNAVMESHBUILDER_H | ||
|
|
||
| #include "DetourAlloc.h" | ||
|
|
||
| /// Represents the source data used to build an navigation mesh tile. | ||
| /// @ingroup detour | ||
| struct dtNavMeshCreateParams | ||
| { | ||
|
|
||
| /// @name Polygon Mesh Attributes | ||
| /// Used to create the base navigation graph. | ||
| /// See #rcPolyMesh for details related to these attributes. | ||
| /// @{ | ||
|
|
||
| const unsigned short* verts; ///< The polygon mesh vertices. [(x, y, z) * #vertCount] [Unit: vx] | ||
| int vertCount; ///< The number vertices in the polygon mesh. [Limit: >= 3] | ||
| const unsigned short* polys; ///< The polygon data. [Size: #polyCount * 2 * #nvp] | ||
| const unsigned short* polyFlags; ///< The user defined flags assigned to each polygon. [Size: #polyCount] | ||
| const unsigned char* polyAreas; ///< The user defined area ids assigned to each polygon. [Size: #polyCount] | ||
| int polyCount; ///< Number of polygons in the mesh. [Limit: >= 1] | ||
| int nvp; ///< Number maximum number of vertices per polygon. [Limit: >= 3] | ||
|
|
||
| /// @} | ||
| /// @name Height Detail Attributes (Optional) | ||
| /// See #rcPolyMeshDetail for details related to these attributes. | ||
| /// @{ | ||
|
|
||
| const unsigned int* detailMeshes; ///< The height detail sub-mesh data. [Size: 4 * #polyCount] | ||
| const float* detailVerts; ///< The detail mesh vertices. [Size: 3 * #detailVertsCount] [Unit: wu] | ||
| int detailVertsCount; ///< The number of vertices in the detail mesh. | ||
| const unsigned char* detailTris; ///< The detail mesh triangles. [Size: 4 * #detailTriCount] | ||
| int detailTriCount; ///< The number of triangles in the detail mesh. | ||
|
|
||
| /// @} | ||
| /// @name Off-Mesh Connections Attributes (Optional) | ||
| /// Used to define a custom point-to-point edge within the navigation graph, an | ||
| /// off-mesh connection is a user defined traversable connection made up to two vertices, | ||
| /// at least one of which resides within a navigation mesh polygon. | ||
| /// @{ | ||
|
|
||
| /// Off-mesh connection vertices. [(ax, ay, az, bx, by, bz) * #offMeshConCount] [Unit: wu] | ||
| const float* offMeshConVerts; | ||
| /// Off-mesh connection radii. [Size: #offMeshConCount] [Unit: wu] | ||
| const float* offMeshConRad; | ||
| /// User defined flags assigned to the off-mesh connections. [Size: #offMeshConCount] | ||
| const unsigned short* offMeshConFlags; | ||
| /// User defined area ids assigned to the off-mesh connections. [Size: #offMeshConCount] | ||
| const unsigned char* offMeshConAreas; | ||
| /// The permitted travel direction of the off-mesh connections. [Size: #offMeshConCount] | ||
| /// | ||
| /// 0 = Travel only from endpoint A to endpoint B.<br/> | ||
| /// #DT_OFFMESH_CON_BIDIR = Bidirectional travel. | ||
| const unsigned char* offMeshConDir; | ||
| /// The user defined ids of the off-mesh connection. [Size: #offMeshConCount] | ||
| const unsigned int* offMeshConUserID; | ||
| /// The number of off-mesh connections. [Limit: >= 0] | ||
| int offMeshConCount; | ||
|
|
||
| /// @} | ||
| /// @name Tile Attributes | ||
| /// @note The tile grid/layer data can be left at zero if the destination is a single tile mesh. | ||
| /// @{ | ||
|
|
||
| unsigned int userId; ///< The user defined id of the tile. | ||
| int tileX; ///< The tile's x-grid location within the multi-tile destination mesh. (Along the x-axis.) | ||
| int tileY; ///< The tile's y-grid location within the multi-tile desitation mesh. (Along the z-axis.) | ||
| int tileLayer; ///< The tile's layer within the layered destination mesh. [Limit: >= 0] (Along the y-axis.) | ||
| float bmin[3]; ///< The minimum bounds of the tile. [(x, y, z)] [Unit: wu] | ||
| float bmax[3]; ///< The maximum bounds of the tile. [(x, y, z)] [Unit: wu] | ||
|
|
||
| /// @} | ||
| /// @name General Configuration Attributes | ||
| /// @{ | ||
|
|
||
| float walkableHeight; ///< The agent height. [Unit: wu] | ||
| float walkableRadius; ///< The agent radius. [Unit: wu] | ||
| float walkableClimb; ///< The agent maximum traversable ledge. (Up/Down) [Unit: wu] | ||
| float cs; ///< The xz-plane cell size of the polygon mesh. [Limit: > 0] [Unit: wu] | ||
| float ch; ///< The y-axis cell height of the polygon mesh. [Limit: > 0] [Unit: wu] | ||
|
|
||
| /// True if a bounding volume tree should be built for the tile. | ||
| /// @note The BVTree is not normally needed for layered navigation meshes. | ||
| bool buildBvTree; | ||
|
|
||
| /// @} | ||
| }; | ||
|
|
||
| /// Builds navigation mesh tile data from the provided tile creation data. | ||
| /// @ingroup detour | ||
| /// @param[in] params Tile creation data. | ||
| /// @param[out] outData The resulting tile data. | ||
| /// @param[out] outDataSize The size of the tile data array. | ||
| /// @return True if the tile data was successfully created. | ||
| bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize); | ||
|
|
||
| /// Swaps the endianess of the tile data's header (#dtMeshHeader). | ||
| /// @param[in,out] data The tile data array. | ||
| /// @param[in] dataSize The size of the data array. | ||
| bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int dataSize); | ||
|
|
||
| /// Swaps endianess of the tile data. | ||
| /// @param[in,out] data The tile data array. | ||
| /// @param[in] dataSize The size of the data array. | ||
| bool dtNavMeshDataSwapEndian(unsigned char* data, const int dataSize); | ||
|
|
||
| #endif // DETOURNAVMESHBUILDER_H | ||
|
|
||
| // This section contains detailed documentation for members that don't have | ||
| // a source file. It reduces clutter in the main section of the header. | ||
|
|
||
| /** | ||
| @struct dtNavMeshCreateParams | ||
| @par | ||
| This structure is used to marshal data between the Recast mesh generation pipeline and Detour navigation components. | ||
| See the rcPolyMesh and rcPolyMeshDetail documentation for detailed information related to mesh structure. | ||
| Units are usually in voxels (vx) or world units (wu). The units for voxels, grid size, and cell size | ||
| are all based on the values of #cs and #ch. | ||
| The standard navigation mesh build process is to create tile data using dtCreateNavMeshData, then add the tile | ||
| to a navigation mesh using either the dtNavMesh single tile <tt>init()</tt> function or the dtNavMesh::addTile() | ||
| function. | ||
| @see dtCreateNavMeshData | ||
| */ |
| @@ -0,0 +1,165 @@ | ||
| // | ||
| // 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) | ||
| { | ||
| // Edited by TC | ||
| 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 = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*m_maxNodes, DT_ALLOC_PERM); | ||
| m_first = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*hashSize, DT_ALLOC_PERM); | ||
|
|
||
| dtAssert(m_nodes); | ||
| dtAssert(m_next); | ||
| dtAssert(m_first); | ||
|
|
||
| memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize); | ||
| memset(m_next, 0xff, sizeof(dtNodeIndex)*m_maxNodes); | ||
| } | ||
|
|
||
| dtNodePool::~dtNodePool() | ||
| { | ||
| dtFree(m_nodes); | ||
| dtFree(m_next); | ||
| dtFree(m_first); | ||
| } | ||
|
|
||
| void dtNodePool::clear() | ||
| { | ||
| memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize); | ||
| m_nodeCount = 0; | ||
| } | ||
|
|
||
| dtNode* dtNodePool::findNode(dtPolyRef id) | ||
| { | ||
| unsigned int bucket = dtHashRef(id) & (m_hashSize-1); | ||
| dtNodeIndex 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); | ||
| dtNodeIndex 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 = (dtNodeIndex)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); | ||
| } |
| @@ -0,0 +1,159 @@ | ||
| // | ||
| // 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, | ||
| }; | ||
|
|
||
| typedef unsigned short dtNodeIndex; | ||
| static const dtNodeIndex DT_NULL_IDX = (dtNodeIndex)~0; | ||
|
|
||
| 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(dtNodeIndex)*m_maxNodes + | ||
| sizeof(dtNodeIndex)*m_hashSize; | ||
| } | ||
|
|
||
| inline int getMaxNodes() const { return m_maxNodes; } | ||
|
|
||
| inline int getHashSize() const { return m_hashSize; } | ||
| inline dtNodeIndex getFirst(int bucket) const { return m_first[bucket]; } | ||
| inline dtNodeIndex getNext(int i) const { return m_next[i]; } | ||
|
|
||
| private: | ||
|
|
||
| dtNode* m_nodes; | ||
| dtNodeIndex* m_first; | ||
| dtNodeIndex* 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 |
| @@ -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 |
| @@ -0,0 +1,64 @@ | ||
| // | ||
| // 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 DETOURSTATUS_H | ||
| #define DETOURSTATUS_H | ||
|
|
||
| typedef unsigned int dtStatus; | ||
|
|
||
| // High level status. | ||
| static const unsigned int DT_FAILURE = 1u << 31; // Operation failed. | ||
| static const unsigned int DT_SUCCESS = 1u << 30; // Operation succeed. | ||
| static const unsigned int DT_IN_PROGRESS = 1u << 29; // Operation still in progress. | ||
|
|
||
| // Detail information for status. | ||
| static const unsigned int DT_STATUS_DETAIL_MASK = 0x0ffffff; | ||
| static const unsigned int DT_WRONG_MAGIC = 1 << 0; // Input data is not recognized. | ||
| static const unsigned int DT_WRONG_VERSION = 1 << 1; // Input data is in wrong version. | ||
| static const unsigned int DT_OUT_OF_MEMORY = 1 << 2; // Operation ran out of memory. | ||
| static const unsigned int DT_INVALID_PARAM = 1 << 3; // An input parameter was invalid. | ||
| static const unsigned int DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results. | ||
| static const unsigned int DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search. | ||
| static const unsigned int DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess. | ||
|
|
||
|
|
||
| // Returns true of status is success. | ||
| inline bool dtStatusSucceed(dtStatus status) | ||
| { | ||
| return (status & DT_SUCCESS) != 0; | ||
| } | ||
|
|
||
| // Returns true of status is failure. | ||
| inline bool dtStatusFailed(dtStatus status) | ||
| { | ||
| return (status & DT_FAILURE) != 0; | ||
| } | ||
|
|
||
| // Returns true of status is in progress. | ||
| inline bool dtStatusInProgress(dtStatus status) | ||
| { | ||
| return (status & DT_IN_PROGRESS) != 0; | ||
| } | ||
|
|
||
| // Returns true if specific detail is set. | ||
| inline bool dtStatusDetail(dtStatus status, unsigned int detail) | ||
| { | ||
| return (status & detail) != 0; | ||
| } | ||
|
|
||
| #endif // DETOURSTATUS_H |
| @@ -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. | ||
|
|
| @@ -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 |
| @@ -0,0 +1,33 @@ | ||
| # Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/> | ||
| # Copyright (C) 2008-2016 TrinityCore <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 | ||
| RecastLayers.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}) |
| @@ -0,0 +1,88 @@ | ||
| // | ||
| // 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; | ||
|
|
||
| /// @see rcAlloc, rcFree | ||
| void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc) | ||
| { | ||
| sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault; | ||
| sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault; | ||
| } | ||
|
|
||
| /// @see rcAllocSetCustom | ||
| void* rcAlloc(int size, rcAllocHint hint) | ||
| { | ||
| return sRecastAllocFunc(size, hint); | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// @warning This function leaves the value of @p ptr unchanged. So it still | ||
| /// points to the same (now invalid) location, and not to null. | ||
| /// | ||
| /// @see rcAllocSetCustom | ||
| void rcFree(void* ptr) | ||
| { | ||
| if (ptr) | ||
| sRecastFreeFunc(ptr); | ||
| } | ||
|
|
||
| /// @class rcIntArray | ||
| /// | ||
| /// While it is possible to pre-allocate a specific array size during | ||
| /// construction or by using the #resize method, certain methods will | ||
| /// automatically resize the array as needed. | ||
| /// | ||
| /// @warning The array memory is not initialized to zero when the size is | ||
| /// manually set during construction or when using #resize. | ||
|
|
||
| /// @par | ||
| /// | ||
| /// Using this method ensures the array is at least large enough to hold | ||
| /// the specified number of elements. This can improve performance by | ||
| /// avoiding auto-resizing during use. | ||
| 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; | ||
| } | ||
|
|
| @@ -0,0 +1,124 @@ | ||
| // | ||
| // 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 | ||
|
|
||
| /// Provides hint values to the memory allocator on how long the | ||
| /// memory is expected to be used. | ||
| enum rcAllocHint | ||
| { | ||
| RC_ALLOC_PERM, ///< Memory will persist after a function call. | ||
| RC_ALLOC_TEMP ///< Memory used temporarily within a function. | ||
| }; | ||
|
|
||
| /// A memory allocation function. | ||
| // @param[in] size The size, in bytes of memory, to allocate. | ||
| // @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use. | ||
| // @return A pointer to the beginning of the allocated memory block, or null if the allocation failed. | ||
| /// @see rcAllocSetCustom | ||
| typedef void* (rcAllocFunc)(int size, rcAllocHint hint); | ||
|
|
||
| /// A memory deallocation function. | ||
| /// @param[in] ptr A pointer to a memory block previously allocated using #rcAllocFunc. | ||
| /// @see rcAllocSetCustom | ||
| typedef void (rcFreeFunc)(void* ptr); | ||
|
|
||
| /// Sets the base custom allocation functions to be used by Recast. | ||
| /// @param[in] allocFunc The memory allocation function to be used by #rcAlloc | ||
| /// @param[in] freeFunc The memory de-allocation function to be used by #rcFree | ||
| void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc); | ||
|
|
||
| /// Allocates a memory block. | ||
| /// @param[in] size The size, in bytes of memory, to allocate. | ||
| /// @param[in] hint A hint to the allocator on how long the memory is expected to be in use. | ||
| /// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed. | ||
| /// @see rcFree | ||
| void* rcAlloc(int size, rcAllocHint hint); | ||
|
|
||
| /// Deallocates a memory block. | ||
| /// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc. | ||
| /// @see rcAlloc | ||
| void rcFree(void* ptr); | ||
|
|
||
|
|
||
| /// A simple dynamic array of integers. | ||
| class rcIntArray | ||
| { | ||
| int* m_data; | ||
| int m_size, m_cap; | ||
| inline rcIntArray(const rcIntArray&); | ||
| inline rcIntArray& operator=(const rcIntArray&); | ||
| public: | ||
|
|
||
| /// Constructs an instance with an initial array size of zero. | ||
| inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {} | ||
|
|
||
| /// Constructs an instance initialized to the specified size. | ||
| /// @param[in] n The initial size of the integer array. | ||
| inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); } | ||
| inline ~rcIntArray() { rcFree(m_data); } | ||
|
|
||
| /// Specifies the new size of the integer array. | ||
| /// @param[in] n The new size of the integer array. | ||
| void resize(int n); | ||
|
|
||
| /// Push the specified integer onto the end of the array and increases the size by one. | ||
| /// @param[in] item The new value. | ||
| inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; } | ||
|
|
||
| /// Returns the value at the end of the array and reduces the size by one. | ||
| /// @return The value at the end of the array. | ||
| inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; } | ||
|
|
||
| /// The value at the specified array index. | ||
| /// @warning Does not provide overflow protection. | ||
| /// @param[in] i The index of the value. | ||
| inline const int& operator[](int i) const { return m_data[i]; } | ||
|
|
||
| /// The value at the specified array index. | ||
| /// @warning Does not provide overflow protection. | ||
| /// @param[in] i The index of the value. | ||
| inline int& operator[](int i) { return m_data[i]; } | ||
|
|
||
| /// The current size of the integer array. | ||
| inline int size() const { return m_size; } | ||
| }; | ||
|
|
||
| /// A simple helper class used to delete an array when it goes out of scope. | ||
| /// @note This class is rarely if ever used by the end user. | ||
| template<class T> class rcScopedDelete | ||
| { | ||
| T* ptr; | ||
| inline T* operator=(T* p); | ||
| public: | ||
|
|
||
| /// Constructs an instance with a null pointer. | ||
| inline rcScopedDelete() : ptr(0) {} | ||
|
|
||
| /// Constructs an instance with the specified pointer. | ||
| /// @param[in] p An pointer to an allocated array. | ||
| inline rcScopedDelete(T* p) : ptr(p) {} | ||
| inline ~rcScopedDelete() { rcFree(ptr); } | ||
|
|
||
| /// The root array pointer. | ||
| /// @return The root array pointer. | ||
| inline operator T*() { return ptr; } | ||
| }; | ||
|
|
||
| #endif |
| @@ -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((void)(__LINE__==-1),false) | ||
| #else | ||
| # include <assert.h> | ||
| # define rcAssert assert | ||
| #endif | ||
|
|
||
| #endif // RECASTASSERT_H |
| @@ -0,0 +1,207 @@ | ||
| // | ||
| // 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" | ||
|
|
||
| /// @par | ||
| /// | ||
| /// Allows the formation of walkable regions that will flow over low lying | ||
| /// objects such as curbs, and up structures such as stairways. | ||
| /// | ||
| /// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt> | ||
| /// | ||
| /// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call | ||
| /// #rcFilterLedgeSpans after calling this filter. | ||
| /// | ||
| /// @see rcHeightfield, rcConfig | ||
| 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; | ||
| unsigned char previousArea = RC_NULL_AREA; | ||
|
|
||
| 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 = previousArea; | ||
| } | ||
| // Copy walkable flag so that it cannot propagate | ||
| // past multiple non-walkable objects. | ||
| previousWalkable = walkable; | ||
| previousArea = s->area; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES); | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb | ||
| /// from the current span's maximum. | ||
| /// This method removes the impact of the overestimation of conservative voxelization | ||
| /// so the resulting mesh will not have regions hanging in the air over ledges. | ||
| /// | ||
| /// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt> | ||
| /// | ||
| /// @see rcHeightfield, rcConfig | ||
| 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); | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// For this filter, the clearance above the span is the distance from the span's | ||
| /// maximum to the next higher span's minimum. (Same grid column.) | ||
| /// | ||
| /// @see rcHeightfield, rcConfig | ||
| 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); | ||
| } |
| @@ -0,0 +1,387 @@ | ||
| // | ||
| // 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; | ||
| } | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// The span addition can be set to favor flags. If the span is merged to | ||
| /// another span and the new @p smax is within @p flagMergeThr units | ||
| /// from the existing span, the span flags are merged. | ||
| /// | ||
| /// @see rcHeightfield, rcSpan. | ||
| 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); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// No spans will be added if the triangle does not overlap the heightfield grid. | ||
| /// | ||
| /// @see rcHeightfield | ||
| 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); | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// Spans will only be added for triangles that overlap the heightfield grid. | ||
| /// | ||
| /// @see rcHeightfield | ||
| 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); | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// Spans will only be added for triangles that overlap the heightfield grid. | ||
| /// | ||
| /// @see rcHeightfield | ||
| 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); | ||
| } | ||
|
|
||
| /// @par | ||
| /// | ||
| /// Spans will only be added for triangles that overlap the heightfield grid. | ||
| /// | ||
| /// @see rcHeightfield | ||
| 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); | ||
| } |
| @@ -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 | ||
|
|
||
| - |
| @@ -0,0 +1,55 @@ | ||
| /* | ||
| * Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/> | ||
| * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> | ||
| * Copyright (C) 2005-2016 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 3 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 _TRINITY_AUTO_PTR_H | ||
| #define _TRINITY_AUTO_PTR_H | ||
|
|
||
| #include <ace/Bound_Ptr.h> | ||
|
|
||
| namespace Trinity | ||
| { | ||
|
|
||
| template <class Pointer, class Lock> | ||
| class AutoPtr : public ACE_Strong_Bound_Ptr<Pointer, Lock> | ||
| { | ||
| typedef ACE_Strong_Bound_Ptr<Pointer, Lock> Base; | ||
|
|
||
| public: | ||
| AutoPtr() | ||
| : Base() | ||
| { } | ||
|
|
||
| AutoPtr(Pointer* x) | ||
| : Base(x) | ||
| { } | ||
|
|
||
| operator bool() const | ||
| { | ||
| return !Base::null(); | ||
| } | ||
|
|
||
| bool operator !() const | ||
| { | ||
| return Base::null(); | ||
| } | ||
| }; | ||
|
|
||
| } // namespace Trinity | ||
|
|
||
| #endif |
| @@ -0,0 +1,74 @@ | ||
| /* | ||
| * Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/> | ||
| * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> | ||
| * Copyright (C) 2005-2016 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 3 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 TRINITY_CONTAINERS_H | ||
| #define TRINITY_CONTAINERS_H | ||
|
|
||
| #include "Define.h" | ||
| #include <list> | ||
|
|
||
| //! Because circular includes are bad | ||
| extern uint32 urand(uint32 min, uint32 max); | ||
|
|
||
| namespace Trinity | ||
| { | ||
| namespace Containers | ||
| { | ||
| template<class T> | ||
| void RandomResizeList(std::list<T> &list, uint32 size) | ||
| { | ||
| size_t list_size = list.size(); | ||
|
|
||
| while (list_size > size) | ||
| { | ||
| typename std::list<T>::iterator itr = list.begin(); | ||
| std::advance(itr, urand(0, list_size - 1)); | ||
| list.erase(itr); | ||
| --list_size; | ||
| } | ||
| } | ||
|
|
||
| template<class T, class Predicate> | ||
| void RandomResizeList(std::list<T> &list, Predicate& predicate, uint32 size) | ||
| { | ||
| //! First use predicate filter | ||
| std::list<T> listCopy; | ||
| for (typename std::list<T>::iterator itr = list.begin(); itr != list.end(); ++itr) | ||
| if (predicate(*itr)) | ||
| listCopy.push_back(*itr); | ||
|
|
||
| if (size) | ||
| RandomResizeList(listCopy, size); | ||
|
|
||
| list = listCopy; | ||
| } | ||
|
|
||
| /* Select a random element from a container. Note: make sure you explicitly empty check the container */ | ||
| template <class C> typename C::value_type const& SelectRandomContainerElement(C const& container) | ||
| { | ||
| typename C::const_iterator it = container.begin(); | ||
| std::advance(it, urand(0, container.size() - 1)); | ||
| return *it; | ||
| } | ||
| } | ||
| //! namespace Containers | ||
| } | ||
| //! namespace Trinity | ||
|
|
||
| #endif //! #ifdef TRINITY_CONTAINERS_H |