From bc7dee3c8de0f587aff592b70e43d53d08efe4d3 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Wed, 13 Dec 2017 14:35:01 -0500 Subject: [PATCH] Removed not used files Added missing sql updates --- .../Framework/RecastDetour/Recast/Recast.cs | 1692 ----------------- .../RecastDetour/Recast/RecastArea.cs | 526 ----- .../RecastDetour/Recast/RecastContour.cs | 860 --------- .../RecastDetour/Recast/RecastFilter.cs | 192 -- .../RecastDetour/Recast/RecastLayers.cs | 641 ------- .../RecastDetour/Recast/RecastMesh.cs | 1513 --------------- .../RecastDetour/Recast/RecastMeshDetail.cs | 1650 ---------------- .../Recast/RecastRasterization.cs | 430 ----- .../RecastDetour/Recast/RecastRegion.cs | 1426 -------------- .../world/master/2017_12_09_00_world.sql | 2 + .../world/master/2017_12_10_00_world.sql | 37 + ...12_10_01_world_2016_11_03_01_world_335.sql | 12 + ...017_12_10_02_world_2016_11_04_01_world.sql | 14 + ...017_12_10_03_world_2016_11_04_02_world.sql | 8 + 14 files changed, 73 insertions(+), 8930 deletions(-) delete mode 100644 Source/Framework/RecastDetour/Recast/Recast.cs delete mode 100644 Source/Framework/RecastDetour/Recast/RecastArea.cs delete mode 100644 Source/Framework/RecastDetour/Recast/RecastContour.cs delete mode 100644 Source/Framework/RecastDetour/Recast/RecastFilter.cs delete mode 100644 Source/Framework/RecastDetour/Recast/RecastLayers.cs delete mode 100644 Source/Framework/RecastDetour/Recast/RecastMesh.cs delete mode 100644 Source/Framework/RecastDetour/Recast/RecastMeshDetail.cs delete mode 100644 Source/Framework/RecastDetour/Recast/RecastRasterization.cs delete mode 100644 Source/Framework/RecastDetour/Recast/RecastRegion.cs create mode 100644 sql/updates/world/master/2017_12_09_00_world.sql create mode 100644 sql/updates/world/master/2017_12_10_00_world.sql create mode 100644 sql/updates/world/master/2017_12_10_01_world_2016_11_03_01_world_335.sql create mode 100644 sql/updates/world/master/2017_12_10_02_world_2016_11_04_01_world.sql create mode 100644 sql/updates/world/master/2017_12_10_03_world_2016_11_04_02_world.sql diff --git a/Source/Framework/RecastDetour/Recast/Recast.cs b/Source/Framework/RecastDetour/Recast/Recast.cs deleted file mode 100644 index b4fcdcc49..000000000 --- a/Source/Framework/RecastDetour/Recast/Recast.cs +++ /dev/null @@ -1,1692 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Text; - -public static partial class Recast { - - /// The value of PI used by Recast. - public const float RC_PI = 3.14159265f; - - /// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax. - public const int RC_SPAN_HEIGHT_BITS = 13; - /// Defines the maximum value for rcSpan::smin and rcSpan::smax. - public const int RC_SPAN_MAX_HEIGHT = (1 << RC_SPAN_HEIGHT_BITS) - 1; - - /// Recast log categories. - /// @see rcContext - public enum rcLogCategory { - RC_LOG_PROGRESS = 1, //< A progress log entry. - RC_LOG_WARNING, //< A warning log entry. - RC_LOG_ERROR, //< An error log entry. - }; - - /// Recast performance timer categories. - /// @see rcContext - public enum rcTimerLabel { - /// The user defined total time of the build. - RC_TIMER_TOTAL, - /// A user defined build time. - RC_TIMER_TEMP, - /// The time to rasterize the triangles. (See: #rcRasterizeTriangle) - RC_TIMER_RASTERIZE_TRIANGLES, - /// The time to build the compact heightfield. (See: #rcBuildCompactHeightfield) - RC_TIMER_BUILD_COMPACTHEIGHTFIELD, - /// The total time to build the contours. (See: #rcBuildContours) - RC_TIMER_BUILD_CONTOURS, - /// The time to trace the boundaries of the contours. (See: #rcBuildContours) - RC_TIMER_BUILD_CONTOURS_TRACE, - /// The time to simplify the contours. (See: #rcBuildContours) - RC_TIMER_BUILD_CONTOURS_SIMPLIFY, - /// The time to filter ledge spans. (See: #rcFilterLedgeSpans) - RC_TIMER_FILTER_BORDER, - /// The time to filter low height spans. (See: #rcFilterWalkableLowHeightSpans) - RC_TIMER_FILTER_WALKABLE, - /// The time to apply the median filter. (See: #rcMedianFilterWalkableArea) - RC_TIMER_MEDIAN_AREA, - /// The time to filter low obstacles. (See: #rcFilterLowHangingWalkableObstacles) - RC_TIMER_FILTER_LOW_OBSTACLES, - /// The time to build the polygon mesh. (See: #rcBuildPolyMesh) - RC_TIMER_BUILD_POLYMESH, - /// The time to merge polygon meshes. (See: #rcMergePolyMeshes) - RC_TIMER_MERGE_POLYMESH, - /// The time to erode the walkable area. (See: #rcErodeWalkableArea) - RC_TIMER_ERODE_AREA, - /// The time to mark a box area. (See: #rcMarkBoxArea) - RC_TIMER_MARK_BOX_AREA, - /// The time to mark a cylinder area. (See: #rcMarkCylinderArea) - RC_TIMER_MARK_CYLINDER_AREA, - /// The time to mark a convex polygon area. (See: #rcMarkConvexPolyArea) - RC_TIMER_MARK_CONVEXPOLY_AREA, - /// The total time to build the distance field. (See: #rcBuildDistanceField) - RC_TIMER_BUILD_DISTANCEFIELD, - /// The time to build the distances of the distance field. (See: #rcBuildDistanceField) - RC_TIMER_BUILD_DISTANCEFIELD_DIST, - /// The time to blur the distance field. (See: #rcBuildDistanceField) - RC_TIMER_BUILD_DISTANCEFIELD_BLUR, - /// The total time to build the regions. (See: #rcBuildRegions, #rcBuildRegionsMonotone) - RC_TIMER_BUILD_REGIONS, - /// The total time to apply the watershed algorithm. (See: #rcBuildRegions) - RC_TIMER_BUILD_REGIONS_WATERSHED, - /// The time to expand regions while applying the watershed algorithm. (See: #rcBuildRegions) - RC_TIMER_BUILD_REGIONS_EXPAND, - /// The time to flood regions while applying the watershed algorithm. (See: #rcBuildRegions) - RC_TIMER_BUILD_REGIONS_FLOOD, - /// The time to filter out small regions. (See: #rcBuildRegions, #rcBuildRegionsMonotone) - RC_TIMER_BUILD_REGIONS_FILTER, - /// The time to build heightfield layers. (See: #rcBuildHeightfieldLayers) - RC_TIMER_BUILD_LAYERS, - /// The time to build the polygon mesh detail. (See: #rcBuildPolyMeshDetail) - RC_TIMER_BUILD_POLYMESHDETAIL, - /// The time to merge polygon mesh details. (See: #rcMergePolyMeshDetails) - RC_TIMER_MERGE_POLYMESHDETAIL, - /// The maximum number of timers. (Used for iterating timers.) - RC_MAX_TIMERS - }; - - - public static void rcCalcBounds(float[] verts, int nv, float[] bmin, float[] bmax) { - // Calculate bounding box. - rcVcopy(bmin, verts); - rcVcopy(bmax, verts); - for (int i = 1; i < nv; ++i) { - int vStart = i * 3; - rcVmin(bmin, 0, verts, vStart); - rcVmax(bmax, 0, verts, vStart); - } - } - - public static void rcCalcGridSize(float[] bmin, float[] bmax, float cs, out int w, out int h) { - w = (int)((bmax[0] - bmin[0]) / cs + 0.5f); - h = (int)((bmax[2] - bmin[2]) / cs + 0.5f); - } - - - /// @par - /// - /// See the #rcConfig documentation for more information on the configuration parameters. - /// - /// @see rcAllocHeightfield, rcHeightfield - public static bool rcCreateHeightfield(rcContext ctx, rcHeightfield hf, int width, int height, - float[] bmin, float[] bmax, - float cs, float ch) { - rcIgnoreUnused(ctx); - - hf.width = width; - hf.height = height; - rcVcopy(hf.bmin, bmin); - rcVcopy(hf.bmax, bmax); - hf.cs = cs; - hf.ch = ch; - hf.spans = new rcSpan[hf.width * hf.height];//(rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM); - if (hf.spans == null) - return false; - //memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height); - return true; - } - - public static void calcTriNormal(float[] v0, float[] v1, float[] v2, float[] norm) { - float[] e0 = new float[3]; - float[] e1 = new float[3]; - rcVsub(e0, v1, v0); - rcVsub(e1, v2, v0); - rcVcross(norm, e0, e1); - rcVnormalize(norm); - } - - public static void calcTriNormal(float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start, float[] norm) { - float[] e0 = new float[3]; - float[] e1 = new float[3]; - rcVsub(e0, 0, v1, v1Start, v0, v0Start); - rcVsub(e1, 0, v2, v2Start, v0, v0Start); - rcVcross(norm, 0, e0, 0, e1, 0); - rcVnormalize(norm); - } - - /// @par - /// - /// Only sets the aread id's for the walkable triangles. Does not alter the - /// area id's for unwalkable triangles. - /// - /// See the #rcConfig documentation for more information on the configuration parameters. - /// - /// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles - public static void rcMarkWalkableTriangles(rcContext ctx, float walkableSlopeAngle, - float[] verts, int nv, - int[] tris, int nt, - byte[] areas) { - rcIgnoreUnused(ctx); - - float walkableThr = (float)Math.Cos(walkableSlopeAngle / 180.0f * RC_PI); - - float[] norm = new float[3]; - - for (int i = 0; i < nt; ++i) { - int triStart = i * 3; - calcTriNormal(verts, tris[triStart + 0] * 3, verts, tris[triStart + 1] * 3, verts, tris[triStart + 2] * 3, norm); - // Check if the face is walkable. - if (norm[1] > walkableThr) - areas[i] = RC_WALKABLE_AREA; - } - } - - /// @par - /// - /// Only sets the aread id's for the unwalkable triangles. Does not alter the - /// area id's for walkable triangles. - /// - /// See the #rcConfig documentation for more information on the configuration parameters. - /// - /// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles - public static void rcClearUnwalkableTriangles(rcContext ctx, float walkableSlopeAngle, - float[] verts, int nv, - int[] tris, int nt, - byte[] areas) { - rcIgnoreUnused(ctx); - - float walkableThr = (float)Math.Cos(walkableSlopeAngle / 180.0f * RC_PI); - - float[] norm = new float[3]; - - for (int i = 0; i < nt; ++i) { - int triStart = i * 3; - calcTriNormal(verts, tris[triStart + 0] * 3, verts, tris[triStart + 1] * 3, verts, tris[triStart + 2] * 3, norm); - // Check if the face is walkable. - if (norm[1] <= walkableThr) - areas[i] = RC_NULL_AREA; - } - } - - public static int rcGetHeightFieldSpanCount(rcContext ctx, rcHeightfield hf) { - rcIgnoreUnused(ctx); - - int w = hf.width; - 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 != null; s = s.next) { - if (s.area != RC_NULL_AREA) - spanCount++; - } - } - } - return spanCount; - } - - public static void rccsArrayItemsCreate(T[] array) where T : class, new() { - for (int i = 0; i < array.Length; ++i) { - array[i] = new T(); - } - } - - /// @par - /// - /// This is just the beginning of the process of fully building a compact heightfield. - /// Various filters may be applied applied, then the distance field and regions built. - /// E.g: #rcBuildDistanceField and #rcBuildRegions - /// - /// See the #rcConfig documentation for more information on the configuration parameters. - /// - /// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig - public static bool rcBuildCompactHeightfield(rcContext ctx, int walkableHeight, int walkableClimb, - rcHeightfield hf, rcCompactHeightfield chf) { - Debug.Assert(ctx != null, "rcBuildCompactHeightfield Assert(ctx != null)"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_COMPACTHEIGHTFIELD); - - int w = hf.width; - int h = hf.height; - 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 = new rcCompactCell[w * h]; - //chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM); - - if (chf.cells == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (" + (w * h) + ")"); - return false; - } - chf.spans = new rcCompactSpan[spanCount];// (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM); - if (chf.spans == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (" + spanCount + ")"); - return false; - } - chf.areas = new byte[spanCount]; //(byte*)rcAlloc(sizeof(byte)*spanCount, RC_ALLOC_PERM); - if (chf.areas == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (" + spanCount + ")"); ; - return false; - } - - 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) { - 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 == null) { - continue; - } - rcCompactCell c;// = chf.cells[x + y * w]; - c.index = (uint)idx; - c.count = 0; - - while (s != null) { - if (s.area != RC_NULL_AREA) { - int bot = (int)s.smax; - int top = s.next != null ? (int)s.next.smin : MAX_HEIGHT; - chf.spans[idx].y = (ushort)rcClamp(bot, 0, 0xffff); - chf.spans[idx].h = (byte)rcClamp(top - bot, 0, 0xff); - chf.areas[idx] = s.area; - idx++; - c.count++; - } - s = s.next; - } - chf.cells[x + y * w] = c; - } - } - - // Find neighbour connections. - int MAX_LAYERS = RC_NOT_CONNECTED - 1; - int tooHighNeighbour = 0; - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - 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(ref s, dir, RC_NOT_CONNECTED); - int nx = x + rcGetDirOffsetX(dir); - 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. - rcCompactCell nc = chf.cells[nx + ny * w]; - for (int k = (int)nc.index, nk = (int)(nc.index + nc.count); k < nk; ++k) { - rcCompactSpan ns = chf.spans[k]; - int bot = Math.Max(s.y, ns.y); - int top = Math.Min(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 && Math.Abs((int)ns.y - (int)s.y) <= walkableClimb) { - // Mark direction as walkable. - int lidx = k - (int)nc.index; - if (lidx < 0 || lidx > MAX_LAYERS) { - tooHighNeighbour = Math.Max(tooHighNeighbour, lidx); - continue; - } - rcSetCon(ref s, dir, lidx); - break; - } - } - - } - - chf.spans[i] = s; - } - } - } - - if (tooHighNeighbour > MAX_LAYERS) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers " + tooHighNeighbour + " (max: " + MAX_LAYERS + ")"); - } - - ctx.stopTimer(rcTimerLabel.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; - } - */ - - /// @class rcContext - /// @par - /// - /// This class does not provide logging or timer functionality on its - /// own. Both must be provided by a concrete implementation - /// by overriding the protected member functions. Also, this class does not - /// provide an interface for extracting log messages. (Only adding them.) - /// So concrete implementations must provide one. - /// - /// If no logging or timers are required, just pass an instance of this - /// class through the Recast build process. - /// - - /// @par - /// - /// Example: - /// @code - /// // Where ctx is an instance of rcContext and filepath is a char array. - /// ctx.log(rcLogCategory.RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath); - /// @endcode - /// Provides an interface for optional logging and performance tracking of the Recast - /// build process. - /// @ingroup recast - public class rcContext { - - /// True if logging is enabled. - bool m_logEnabled = true; - - /// True if the performance timers are enabled. - bool m_timerEnabled = true; - - /// Contructor. - /// @param[in] state TRUE if the logging and performance timers should be enabled. [Default: true] - public rcContext(bool state = true) { - m_logEnabled = state; - m_timerEnabled = state; - } - - /// Enables or disables logging. - /// @param[in] state TRUE if logging should be enabled. - public void enableLog(bool state) { - m_logEnabled = state; - } - - /// Clears all log entries. - public void resetLog() { - if (m_logEnabled) { - doResetLog(); - } - } - - /// Logs a message. - /// @param[in] category The category of the message. - /// @param[in] message The message. - //public void log(rcLogCategory category, string message); - public void log(rcLogCategory category, string message) { - if (!m_logEnabled) { - return; - } - - doLog(category, message); - } - - /// Enables or disables the performance timers. - /// @param[in] state TRUE if timers should be enabled. - public void enableTimer(bool state) { - m_timerEnabled = state; - } - - /// Clears all peformance timers. (Resets all to unused.) - public void resetTimers() { - if (m_timerEnabled) { - doResetTimers(); - } - } - - /// Starts the specified performance timer. - /// @param label The category of timer. - public void startTimer(rcTimerLabel label) { - if (m_timerEnabled) { - doStartTimer(label); - } - } - - /// Stops the specified performance timer. - /// @param label The category of the timer. - public void stopTimer(rcTimerLabel label) { - if (m_timerEnabled) { - doStopTimer(label); - } - } - - /// Returns the total accumulated time of the specified performance timer. - /// @param label The category of the timer. - /// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started. - public long getAccumulatedTime(rcTimerLabel label) { - return m_timerEnabled ? doGetAccumulatedTime(label) : -1; - } - public double getAccumulatedTimeHiResolution(rcTimerLabel label) { - return m_timerEnabled ? doGetAccumulatedTimeHiResolution(label) : -1.0; - } - - - /// Clears all log entries. - protected virtual void doResetLog() { } - - /// Logs a message. - /// @param[in] category The category of the message. - /// @param[in] msg The formatted message. - protected virtual void doLog(rcLogCategory category, string msg) { //int len unnecessary because c# string - } - - /// Clears all timers. (Resets all to unused.) - protected virtual void doResetTimers() { - } - - /// Starts the specified performance timer. - /// @param[in] label The category of timer. - protected virtual void doStartTimer(rcTimerLabel label) { - } - - /// Stops the specified performance timer. - /// @param[in] label The category of the timer. - protected virtual void doStopTimer(rcTimerLabel label) { - } - - /// Returns the total accumulated time of the specified performance timer. - /// @param[in] label The category of the timer. - /// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started. - protected virtual long doGetAccumulatedTime(rcTimerLabel label) { - return -1; - } - - //C# port: alternate return type to use high precision timer on platforms where it's available - /// Returns the total accumulated time of the specified performance timer. - /// @param[in] label The category of the timer. - /// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started. - protected virtual double doGetAccumulatedTimeHiResolution(rcTimerLabel label) { - return -1.0; - } - - }; - - /// Specifies a configuration to use when performing Recast builds. - /// @ingroup recast - public class rcConfig { - /// The width of the field along the x-axis. [Limit: >= 0] [Units: vx] - public int width; - - /// The height of the field along the z-axis. [Limit: >= 0] [Units: vx] - public int height; - - /// The width/height size of tile's on the xz-plane. [Limit: >= 0] [Units: vx] - public int tileSize; - - /// The size of the non-navigable border around the heightfield. [Limit: >=0] [Units: vx] - public int borderSize; - - /// The xz-plane cell size to use for fields. [Limit: > 0] [Units: wu] - public float cs; - - /// The y-axis cell size to use for fields. [Limit: > 0] [Units: wu] - public float ch; - - /// The minimum bounds of the field's AABB. [(x, y, z)] [Units: wu] - public float[] bmin = new float[3]; - - /// The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu] - public float[] bmax = new float[3]; - - /// The maximum slope that is considered walkable. [Limits: 0 <= value < 90] [Units: Degrees] - public float walkableSlopeAngle; - - /// Minimum floor to 'ceiling' height that will still allow the floor area to - /// be considered walkable. [Limit: >= 3] [Units: vx] - public int walkableHeight; - - /// Maximum ledge height that is considered to still be traversable. [Limit: >=0] [Units: vx] - public int walkableClimb; - - /// The distance to erode/shrink the walkable area of the heightfield away from - /// obstructions. [Limit: >=0] [Units: vx] - public int walkableRadius; - - /// The maximum allowed length for contour edges along the border of the mesh. [Limit: >=0] [Units: vx] - public int maxEdgeLen; - - /// The maximum distance a simplfied contour's border edges should deviate - /// the original raw contour. [Limit: >=0] [Units: vx] - public float maxSimplificationError; - - /// The minimum number of cells allowed to form isolated island areas. [Limit: >=0] [Units: vx] - public int minRegionArea; - - /// Any regions with a span count smaller than this value will, if possible, - /// be merged with larger regions. [Limit: >=0] [Units: vx] - public int mergeRegionArea; - - /// The maximum number of vertices allowed for polygons generated during the - /// contour to polygon conversion process. [Limit: >= 3] - public int maxVertsPerPoly; - - /// Sets the sampling distance to use when generating the detail mesh. - /// (For height detail only.) [Limits: 0 or >= 0.9] [Units: wu] - public float detailSampleDist; - - /// The maximum distance the detail mesh surface should deviate from heightfield - /// data. (For height detail only.) [Limit: >=0] [Units: wu] - public float detailSampleMaxError; - }; - - - - /// Represents a span in a heightfield. - /// @see rcHeightfield - public class rcSpan { - public ushort smin;// : 13; //< The lower limit of the span. [Limit: < #smax] - public ushort smax;// : 13; //< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] - public byte area;// : 6; //< The area id assigned to the span. - public rcSpan next = null; //< The next span higher up in column. - } - // A memory pool used for quick allocation of spans within a heightfield. - // @see rcHeightfield - - public class rcSpanPool { - public rcSpanPool next = null; //< The next span pool. - public rcSpan[] items = new rcSpan[RC_SPANS_PER_POOL]; //< Array of spans in the pool. - /// - public rcSpanPool() { - for (int i = 0; i < items.Length; ++i) { - items[i] = new rcSpan(); - } - } - }; - - /// A dynamic heightfield representing obstructed space. - /// @ingroup recast - public class rcHeightfield { - public int width; //< The width of the heightfield. (Along the x-axis in cell units.) - public int height; //< The height of the heightfield. (Along the z-axis in cell units.) - public float[] bmin = new float[3]; //< The minimum bounds in world space. [(x, y, z)] - public float[] bmax = new float[3]; //< The maximum bounds in world space. [(x, y, z)] - public float cs; //< The size of each cell. (On the xz-plane.) - public float ch; //< The height of each cell. (The minimum increment along the y-axis.) - public rcSpan[] spans = null;//** //< Heightfield of spans (width*height). - public rcSpanPool pools = null; //< Linked list of span pools. - public rcSpan freelist = null; //< The next free span. - }; - - /// Provides information on the content of a cell column in a compact heightfield. - public struct rcCompactCell { - public uint index;// : 24; //< Index to the first span in the column. - public ushort count;// : 8; //< Number of spans in the column. - }; - - /// Represents a span of unobstructed space within a compact heightfield. - public struct rcCompactSpan { - public ushort y; //< The lower extent of the span. (Measured from the heightfield's base.) - public ushort reg; //< The id of the region the span belongs to. (Or zero if not in a region.) - public uint con;// : 24; //< Packed neighbor connection data. - public ushort h;// : 8; //< The height of the span. (Measured from #y.) - }; - - /// A compact, static heightfield representing unobstructed space. - /// @ingroup recast - public class rcCompactHeightfield { - public int width; //< The width of the heightfield. (Along the x-axis in cell units.) - public int height; //< The height of the heightfield. (Along the z-axis in cell units.) - public int spanCount; //< The number of spans in the heightfield. - public int walkableHeight; //< The walkable height used during the build of the field. (See: rcConfig::walkableHeight) - public int walkableClimb; //< The walkable climb used during the build of the field. (See: rcConfig::walkableClimb) - public int borderSize; //< The AABB border size used during the build of the field. (See: rcConfig::borderSize) - public ushort maxDistance; //< The maximum distance value of any span within the field. - public ushort maxRegions; //< The maximum region id of any span within the field. - public float[] bmin = new float[3]; //< The minimum bounds in world space. [(x, y, z)] - public float[] bmax = new float[3]; //< The maximum bounds in world space. [(x, y, z)] - public float cs; //< The size of each cell. (On the xz-plane.) - public float ch; //< The height of each cell. (The minimum increment along the y-axis.) - public rcCompactCell[] cells = null; //< Array of cells. [Size: #width*#height] - public rcCompactSpan[] spans = null; //< Array of spans. [Size: #spanCount] - public ushort[] dist = null; //< Array containing border distance data. [Size: #spanCount] - public byte[] areas = null; //< Array containing area id data. [Size: #spanCount] - }; - - /// Represents a heightfield layer within a layer set. - /// @see rcHeightfieldLayerSet - public class rcHeightfieldLayer { - public float[] bmin = new float[3]; //< The minimum bounds in world space. [(x, y, z)] - public float[] bmax = new float[3]; //< The maximum bounds in world space. [(x, y, z)] - public float cs; //< The size of each cell. (On the xz-plane.) - public float ch; //< The height of each cell. (The minimum increment along the y-axis.) - public int width; //< The width of the heightfield. (Along the x-axis in cell units.) - public int height; //< The height of the heightfield. (Along the z-axis in cell units.) - public int minx; //< The minimum x-bounds of usable data. - public int maxx; //< The maximum x-bounds of usable data. - public int miny; //< The minimum y-bounds of usable data. (Along the z-axis.) - public int maxy; //< The maximum y-bounds of usable data. (Along the z-axis.) - public int hmin; //< The minimum height bounds of usable data. (Along the y-axis.) - public int hmax; //< The maximum height bounds of usable data. (Along the y-axis.) - public byte[] heights; //< The heightfield. [Size: (width - borderSize*2) * (h - borderSize*2)] - public byte[] areas; //< Area ids. [Size: Same as #heights] - public byte[] cons; //< Packed neighbor connection information. [Size: Same as #heights] - }; - - /// Represents a set of heightfield layers. - /// @ingroup recast - /// @see rcAllocHeightfieldLayerSet, rcFreeHeightfieldLayerSet - public class rcHeightfieldLayerSet { - public rcHeightfieldLayer[] layers = null; //< The layers in the set. [Size: #nlayers] - public int nlayers; //< The number of layers in the set. - }; - - /// Represents a simple, non-overlapping contour in field space. - public struct rcContour { - public int[] verts; //< Simplified contour vertex and connection data. [Size: 4 * #nverts] - public int nverts; //< The number of vertices in the simplified contour. - public int[] rverts; //< Raw contour vertex and connection data. [Size: 4 * #nrverts] - public int nrverts; //< The number of vertices in the raw contour. - public ushort reg; //< The region id of the contour. - public byte area; //< The area id of the contour. - /// - public void dumpToTxt(StreamWriter stream) { - stream.WriteLine("\treg: " + reg); - stream.WriteLine("\tarea: " + area); - stream.WriteLine("\tnverts: " + nverts); - for (int i = 0; i < nverts; ++i) { - int vIndex = i * 4; - stream.WriteLine("\t\tverts[" + i + "]: x:" + verts[vIndex] + " y:" + verts[vIndex + 1] + " z:" + verts[vIndex + 2] + " ?:" + verts[vIndex + 3]); - } - stream.WriteLine("\tnrverts: " + nrverts); - for (int i = 0; i < nrverts; ++i) { - int vIndex = i * 4; - stream.WriteLine("\t\trverts[" + i + "]: x:" + rverts[vIndex] + " y:" + rverts[vIndex + 1] + " z:" + rverts[vIndex + 2] + " ?:" + rverts[vIndex + 3]); - } - } - public string dumpToString() { - StringBuilder sb = new StringBuilder(); - sb.AppendLine("\treg: " + reg); - sb.AppendLine("\tarea: " + area); - sb.AppendLine("\tnverts: " + nverts); - for (int i = 0; i < nverts; ++i) { - int vIndex = i * 4; - sb.AppendLine("\t\tverts[" + i + "]: x:" + verts[vIndex] + " y:" + verts[vIndex + 1] + " z:" + verts[vIndex + 2] + " ?:" + verts[vIndex + 3]); - } - sb.AppendLine("\tnrverts: " + nrverts); - for (int i = 0; i < nrverts; ++i) { - int vIndex = i * 4; - sb.AppendLine("\t\trverts[" + i + "]: x:" + rverts[vIndex] + " y:" + rverts[vIndex + 1] + " z:" + rverts[vIndex + 2] + " ?:" + rverts[vIndex + 3]); - } - return sb.ToString(); - } - }; - - /// Represents a group of related contours. - /// @ingroup recast - public class rcContourSet { - public rcContour[] conts = null; //< An array of the contours in the set. [Size: #nconts] - public int nconts; //< The number of contours in the set. - public float[] bmin = new float[3]; //< The minimum bounds in world space. [(x, y, z)] - public float[] bmax = new float[3]; //< The maximum bounds in world space. [(x, y, z)] - public float cs; //< The size of each cell. (On the xz-plane.) - public float ch; //< The height of each cell. (The minimum increment along the y-axis.) - public int width; //< The width of the set. (Along the x-axis in cell units.) - public int height; //< The height of the set. (Along the z-axis in cell units.) - public int borderSize; //< The AABB border size used to generate the source data from which the contours were derived. - /// - public override string ToString() { - StringBuilder sb = new StringBuilder(); - - sb.AppendLine("nconts: " + nconts); - sb.AppendLine("bmin: " + bmin[0] + " " + bmin[1] + " " + bmin[2]); - sb.AppendLine("bmax: " + bmax[0] + " " + bmax[1] + " " + bmax[2]); - sb.AppendLine("cs: " + cs); - sb.AppendLine("ch: " + ch); - sb.AppendLine("width: " + width); - sb.AppendLine("height: " + height); - sb.AppendLine("bordersize: " + borderSize); - - for (int i = 0; i < nconts; ++i) { - sb.Append("contour[" + i + "]: "); - sb.AppendLine(conts[i].ToString()); - } - - return sb.ToString(); - } - }; - - /// Represents a polygon mesh suitable for use in building a navigation mesh. - /// @ingroup recast - public class rcPolyMesh { - public ushort[] verts = null; //< The mesh vertices. [Form: (x, y, z) * #nverts] - public ushort[] polys = null; //< Polygon and neighbor data. [Length: #maxpolys * 2 * #nvp] - public ushort[] regs = null; //< The region id assigned to each polygon. [Length: #maxpolys] - public ushort[] flags = null; //< The user defined flags for each polygon. [Length: #maxpolys] - public byte[] areas = null; //< The area id assigned to each polygon. [Length: #maxpolys] - public int nverts; //< The number of vertices. - public int npolys; //< The number of polygons. - public int maxpolys; //< The number of allocated polygons. - public int nvp; //< The maximum number of vertices per polygon. - public float[] bmin = new float[3]; //< The minimum bounds in world space. [(x, y, z)] - public float[] bmax = new float[3]; //< The maximum bounds in world space. [(x, y, z)] - public float cs; //< The size of each cell. (On the xz-plane.) - public float ch; //< The height of each cell. (The minimum increment along the y-axis.) - public int borderSize; //< The AABB border size used to generate the source data from which the mesh was derived. - /// - public override string ToString() { - StringBuilder sb = new StringBuilder(); - - sb.AppendLine("bmin: " + bmin[0] + " " + bmin[1] + " " + bmin[2]); - sb.AppendLine("bmax: " + bmax[0] + " " + bmax[1] + " " + bmax[2]); - sb.AppendLine("cs: " + cs); - sb.AppendLine("ch: " + ch); - sb.AppendLine("bordersize: " + borderSize); - - sb.AppendLine("nverts: " + nverts); - for (int i = 0; i < nverts; ++i) { - int vIndex = i * 3; - sb.AppendLine("\tverts[" + i + "]: x:" + verts[vIndex] + " y:" + verts[vIndex + 1] + " z:" + verts[vIndex + 2]); - } - sb.AppendLine("\tmaxpolys: " + maxpolys); - sb.AppendLine("\tnvp: " + nvp); - sb.AppendLine("\tnpolys: " + npolys); - for (int i = 0; i < maxpolys; ++i) { - int vIndex = i * nvp; - sb.Append("\t\tpolys[" + i + "]: "); - for (int j = 0; j < nvp; ++j) { - sb.Append(" " + j + ":" + polys[vIndex + j]); - } - - vIndex += nvp; - sb.AppendLine(); - sb.Append("\t\tneighbor[" + i + "]: "); - for (int j = 0; j < nvp; ++j) { - sb.Append(" " + j + ":" + polys[vIndex + j]); - } - sb.AppendLine(); - } - - for (int i = 0; i < maxpolys; ++i) { - sb.AppendLine("regs[" + i + "]: " + regs[i]); - } - sb.AppendLine(); - for (int i = 0; i < flags.Length; ++i) { - sb.AppendLine("flags[" + i + "]: " + flags[i]); - } - - return sb.ToString(); - } - - public string ToObj(){ - StringBuilder sb = new StringBuilder(); - - sb.AppendLine("# Recast Navmesh"); - sb.AppendLine("o NavMesh"); - - sb.AppendLine(); - - for (int i = 0; i < nverts; ++i) { - //ushort* v = &pmesh.verts[i*3]; - int vIndex = i * 3; - float x = bmin[0] + verts[vIndex + 0] * cs; - float y = bmin[1] + (verts[vIndex + 1] + 1) * ch + 0.1f; - float z = bmin[2] + verts[vIndex + 2] * cs; - //ioprintf(io, "v %f %f %f\n", x,y,z); - sb.AppendLine("v " + x + " " + y + " " + z); - } - - sb.AppendLine(); - - for (int i = 0; i < npolys; ++i) { - //const unsigned short* p = &pmesh.polys[i*nvp*2]; - int pIndex = i * nvp * 2; - for (int j = 2; j < nvp; ++j) { - if (polys[pIndex + j] == RC_MESH_NULL_IDX) - break; - //ioprintf(io, "f %d %d %d\n", p[0]+1, p[j-1]+1, p[j]+1); - int a = polys[pIndex] + 1; - int b = polys[pIndex + j - 1] + 1; - int c = polys[pIndex + j] + 1; - sb.AppendLine("f " + a + " " + b + " " + c); - } - } - return sb.ToString (); - } - }; - - /// Contains triangle meshes that represent detailed height data associated - /// with the polygons in its associated polygon mesh object. - /// @ingroup recast - public class rcPolyMeshDetail { - public uint[] meshes = null; //< The sub-mesh data. [Size: 4*#nmeshes] - public float[] verts = null; //< The mesh vertices. [Size: 3*#nverts] - public byte[] tris = null; //< The mesh triangles. [Size: 4*#ntris] - public int nmeshes; //< The number of sub-meshes defined by #meshes. - public int nverts; //< The number of vertices in #verts. - public int ntris; //< The number of triangles in #tris. - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - - sb.AppendLine("nmeshes: " + nmeshes); - for (int i = 0; i < nmeshes; ++i) { - int vIndex = i * 4; - sb.AppendLine("\tmeshes[" + i + "]: a:" + meshes[vIndex] + " b:" + meshes[vIndex + 1] + " c:" + meshes[vIndex + 2] + " d:" + meshes[vIndex + 3]); - } - sb.AppendLine("nverts: " + nverts); - for (int i = 0; i < nverts; ++i) { - int vIndex = i * 3; - sb.AppendLine("\tverts[" + i + "]: x:" + verts[vIndex] + " y:" + verts[vIndex + 1] + " z:" + verts[vIndex + 2]); - } - sb.AppendLine("ntris: " + ntris); - for (int i = 0; i < ntris; ++i) { - int vIndex = i * 4; - sb.AppendLine("\ttris[" + i + "]: a:" + tris[vIndex] + " b:" + tris[vIndex + 1] + " c:" + tris[vIndex + 2] + " d:" + tris[vIndex + 3]); - } - - return sb.ToString(); - } - - public string ToObj() - { - StringBuilder sb = new StringBuilder(); - - sb.AppendLine("# Recast C# Navmesh\n"); - sb.AppendLine("o NavMesh\n"); - - sb.AppendLine("\n"); - - for (int i = 0; i < nverts; ++i) { - int vIndex = i * 3; - sb.AppendLine("v " + verts[vIndex + 0] + " " + verts[vIndex + 1] + " " + verts[vIndex + 2]); - } - - sb.AppendLine(); - - for (int i = 0; i < nmeshes; ++i) { - //uint* m = &dmesh.meshes[i*4]; - int mIndex = i * 4; - uint bverts = meshes[mIndex + 0]; - uint btris = meshes[mIndex + 2]; - uint _ntris = meshes[mIndex + 3]; - uint trisIndex = btris * 4; - for (uint j = 0; j < _ntris; ++j) { - sb.AppendLine("f " - + ((int)(bverts + tris[trisIndex + j * 4 + 0]) + 1) + " " - + ((int)(bverts + tris[trisIndex + j * 4 + 1]) + 1) + " " - + ((int)(bverts + tris[trisIndex + j * 4 + 2]) + 1) + " "); - } - } - - return sb.ToString(); - } - }; - - /// @name Allocation Functions - /// Functions used to allocate and de-allocate Recast objects. - /// @see rcAllocSetCustom - /// @{ - - /// Allocates a heightfield object using the Recast allocator. - /// @return A heightfield that is ready for initialization, or null on failure. - /// @ingroup recast - /// @see rcCreateHeightfield, rcFreeHeightField - //rcHeightfield* rcAllocHeightfield(); - - /// Frees the specified heightfield object using the Recast allocator. - /// @param[in] hf A heightfield allocated using #rcAllocHeightfield - /// @ingroup recast - /// @see rcAllocHeightfield - //void rcFreeHeightField(rcHeightfield* hf); - - /// Allocates a compact heightfield object using the Recast allocator. - /// @return A compact heightfield that is ready for initialization, or null on failure. - /// @ingroup recast - /// @see rcBuildCompactHeightfield, rcFreeCompactHeightfield - //rcCompactHeightfield* rcAllocCompactHeightfield(); - - /// Frees the specified compact heightfield object using the Recast allocator. - /// @param[in] chf A compact heightfield allocated using #rcAllocCompactHeightfield - /// @ingroup recast - /// @see rcAllocCompactHeightfield - //void rcFreeCompactHeightfield(rcCompactHeightfield* chf); - - /// Allocates a heightfield layer set using the Recast allocator. - /// @return A heightfield layer set that is ready for initialization, or null on failure. - /// @ingroup recast - /// @see rcBuildHeightfieldLayers, rcFreeHeightfieldLayerSet - //rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet(); - - /// Frees the specified heightfield layer set using the Recast allocator. - /// @param[in] lset A heightfield layer set allocated using #rcAllocHeightfieldLayerSet - /// @ingroup recast - /// @see rcAllocHeightfieldLayerSet - //void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset); - - /// Allocates a contour set object using the Recast allocator. - /// @return A contour set that is ready for initialization, or null on failure. - /// @ingroup recast - /// @see rcBuildContours, rcFreeContourSet - //rcContourSet* rcAllocContourSet(); - - /// Frees the specified contour set using the Recast allocator. - /// @param[in] cset A contour set allocated using #rcAllocContourSet - /// @ingroup recast - /// @see rcAllocContourSet - //void rcFreeContourSet(rcContourSet* cset); - - /// Allocates a polygon mesh object using the Recast allocator. - /// @return A polygon mesh that is ready for initialization, or null on failure. - /// @ingroup recast - /// @see rcBuildPolyMesh, rcFreePolyMesh - //rcPolyMesh* rcAllocPolyMesh(); - - /// Frees the specified polygon mesh using the Recast allocator. - /// @param[in] pmesh A polygon mesh allocated using #rcAllocPolyMesh - /// @ingroup recast - /// @see rcAllocPolyMesh - //void rcFreePolyMesh(rcPolyMesh* pmesh); - - /// Allocates a detail mesh object using the Recast allocator. - /// @return A detail mesh that is ready for initialization, or null on failure. - /// @ingroup recast - /// @see rcBuildPolyMeshDetail, rcFreePolyMeshDetail - //rcPolyMeshDetail* rcAllocPolyMeshDetail(); - - /// Frees the specified detail mesh using the Recast allocator. - /// @param[in] dmesh A detail mesh allocated using #rcAllocPolyMeshDetail - /// @ingroup recast - /// @see rcAllocPolyMeshDetail - //void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh); - - /// The number of spans allocated per span spool. - /// @see rcSpanPool - public const int RC_SPANS_PER_POOL = 2048; - - /// Heighfield border flag. - /// If a heightfield region ID has this bit set, then the region is a border - /// region and its spans are considered unwalkable. - /// (Used during the region and contour build process.) - /// @see rcCompactSpan::reg - - public const ushort RC_BORDER_REG = 0x8000; - - /// Border vertex flag. - /// If a region ID has this bit set, then the associated element lies on - /// a tile border. If a contour vertex's region ID has this bit set, the - /// vertex will later be removed in order to match the segments and vertices - /// at tile boundaries. - /// (Used during the build process.) - /// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts - public const int RC_BORDER_VERTEX = 0x10000; - - /// Area border flag. - /// If a region ID has this bit set, then the associated element lies on - /// the border of an area. - /// (Used during the region and contour build process.) - /// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts - public const int RC_AREA_BORDER = 0x20000; - - /// Contour build flags. - /// @see rcBuildContours - public enum rcBuildContoursFlags { - RC_CONTOUR_TESS_WALL_EDGES = 0x01, //< Tessellate solid (impassable) edges during contour simplification. - RC_CONTOUR_TESS_AREA_EDGES = 0x02, //< Tessellate edges between areas during contour simplification. - }; - - /// Applied to the region id field of contour vertices in order to extract the region id. - /// The region id field of a vertex may have several flags applied to it. So the - /// fields value can't be used directly. - /// @see rcContour::verts, rcContour::rverts - public const int RC_CONTOUR_REG_MASK = 0xffff; - - /// An value which indicates an invalid index within a mesh. - /// @note This does not necessarily indicate an error. - /// @see rcPolyMesh::polys - public const ushort RC_MESH_NULL_IDX = 0xffff; - - /// Represents the null area. - /// When a data element is given this value it is considered to no longer be - /// assigned to a usable area. (E.g. It is unwalkable.) - public const byte RC_NULL_AREA = 0; - - /// The default area id used to indicate a walkable polygon. - /// This is also the maximum allowed area id, and the only non-null area id - /// recognized by some steps in the build process. - public const byte RC_WALKABLE_AREA = 63; - - /// The value returned by #rcGetCon if the specified direction is not connected - /// to another span. (Has no neighbor.) - public const int RC_NOT_CONNECTED = 0x3f; - - /// @name General helper functions - /// @{ - - /// Used to ignore a function parameter. VS complains about unused parameters - /// and this silences the warning. - /// @param [in] _ Unused parameter - public static void rcIgnoreUnused(T t) { } - - //Use C# for this kind of things - /// Swaps the values of the two parameters. - /// @param[in,out] a Value A - /// @param[in,out] b Value B - //public void rcSwap(T a, T b) { T t = a; a = b; b = t; } - static void rcSwap(ref T lhs, ref T rhs) { - T temp = lhs; - lhs = rhs; - rhs = temp; - } - - /// Returns the minimum of two values. - /// @param[in] a Value A - /// @param[in] b Value B - /// @return The minimum of the two values. - //public T Math.Min(T a, T b) { - // return a < b ? a : b; - //} - - /// Returns the maximum of two values. - /// @param[in] a Value A - /// @param[in] b Value B - /// @return The maximum of the two values. - //template inline T Math.Max(T a, T b) { return a > b ? a : b; } - - /// Returns the absolute value. - /// @param[in] a The value. - /// @return The absolute value of the specified value. - //template inline T rcAbs(T a) { return a < 0 ? -a : a; } - - /// Returns the square of the value. - /// @param[in] a The value. - /// @return The square of the value. - //template inline T rcSqr(T a) { return a*a; } - - /// Clamps the value to the specified range. - /// @param[in] v The value to clamp. - /// @param[in] mn The minimum permitted return value. - /// @param[in] mx The maximum permitted return value. - /// @return The value, clamped to the specified range. - //template inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); } - public static int rcClamp(int v, int mn, int mx) { - return v < mn ? mn : (v > mx ? mx : v); - } - - /// Returns the square root of the value. - /// @param[in] x The value. - /// @return The square root of the vlaue. - //float rcSqrt(float x); - - /// @} - /// @name Vector helper functions. - /// @{ - - /// Derives the cross product of two vectors. (@p v1 x @p v2) - /// @param[out] dest The cross product. [(x, y, z)] - /// @param[in] v1 A Vector [(x, y, z)] - /// @param[in] v2 A vector [(x, y, z)] - - public static void rcVcross(float[] dest, float[] v1, 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]; - } - public static void rcVcross(float[] dest, int destStart, float[] v1, int v1Start, float[] v2, int v2Start) { - dest[destStart + 0] = v1[v1Start + 1] * v2[v2Start + 2] - v1[v1Start + 2] * v2[v2Start + 1]; - dest[destStart + 1] = v1[v1Start + 2] * v2[v2Start + 0] - v1[v1Start + 0] * v2[v2Start + 2]; - dest[destStart + 2] = v1[v1Start + 0] * v2[v2Start + 1] - v1[v1Start + 1] * v2[v2Start + 0]; - } - - /// Derives the dot product of two vectors. (@p v1 . @p v2) - /// @param[in] v1 A Vector [(x, y, z)] - /// @param[in] v2 A vector [(x, y, z)] - /// @return The dot product. - public static float rcVdot(float[] v1, float[] v2) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; - } - - /// Performs a scaled vector addition. (@p v1 + (@p v2 * @p s)) - /// @param[out] dest The result vector. [(x, y, z)] - /// @param[in] v1 The base vector. [(x, y, z)] - /// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)] - /// @param[in] s The amount to scale @p v2 by before adding to @p v1. - public static void rcVmad(float[] dest, float[] v1, float[] v2, float s) { - dest[0] = v1[0] + v2[0] * s; - dest[1] = v1[1] + v2[1] * s; - dest[2] = v1[2] + v2[2] * s; - } - - /// Performs a vector addition. (@p v1 + @p v2) - /// @param[out] dest The result vector. [(x, y, z)] - /// @param[in] v1 The base vector. [(x, y, z)] - /// @param[in] v2 The vector to add to @p v1. [(x, y, z)] - public static void rcVadd(float[] dest, float[] v1, float[] v2) { - dest[0] = v1[0] + v2[0]; - dest[1] = v1[1] + v2[1]; - dest[2] = v1[2] + v2[2]; - } - - /// Performs a vector subtraction. (@p v1 - @p v2) - /// @param[out] dest The result vector. [(x, y, z)] - /// @param[in] v1 The base vector. [(x, y, z)] - /// @param[in] v2 The vector to subtract from @p v1. [(x, y, z)] - public static void rcVsub(float[] dest, float[] v1, float[] v2) { - dest[0] = v1[0] - v2[0]; - dest[1] = v1[1] - v2[1]; - dest[2] = v1[2] - v2[2]; - } - - public static void rcVsub(float[] dest, int destStart, float[] v1, int v1Start, float[] v2, int v2Start) { - dest[destStart + 0] = v1[v1Start + 0] - v2[v2Start + 0]; - dest[destStart + 1] = v1[v1Start + 1] - v2[v2Start + 1]; - dest[destStart + 2] = v1[v1Start + 2] - v2[v2Start + 2]; - } - - /// Selects the minimum value of each element from the specified vectors. - /// @param[in,out] mn A vector. (Will be updated with the result.) [(x, y, z)] - /// @param[in] v A vector. [(x, y, z)] - public static void rcVmin(float[] mn, float[] v) { - mn[0] = Math.Min(mn[0], v[0]); - mn[1] = Math.Min(mn[1], v[1]); - mn[2] = Math.Min(mn[2], v[2]); - } - - public static void rcVmin(float[] mn, int mnStart, float[] v, int vStart) { - mn[0 + mnStart] = Math.Min(mn[0 + mnStart], v[0 + vStart]); - mn[1 + mnStart] = Math.Min(mn[1 + mnStart], v[1 + vStart]); - mn[2 + mnStart] = Math.Min(mn[2 + mnStart], v[2 + vStart]); - } - - - /// Selects the maximum value of each element from the specified vectors. - /// @param[in,out] mx A vector. (Will be updated with the result.) [(x, y, z)] - /// @param[in] v A vector. [(x, y, z)] - public static void rcVmax(float[] mx, float[] v) { - mx[0] = Math.Max(mx[0], v[0]); - mx[1] = Math.Max(mx[1], v[1]); - mx[2] = Math.Max(mx[2], v[2]); - } - - public static void rcVmax(float[] mx, int mxStart, float[] v, int vStart) { - mx[0 + mxStart] = Math.Max(mx[0 + mxStart], v[0 + vStart]); - mx[1 + mxStart] = Math.Max(mx[1 + mxStart], v[1 + vStart]); - mx[2 + mxStart] = Math.Max(mx[2 + mxStart], v[2 + vStart]); - } - - /// Performs a vector copy. - /// @param[out] dest The result. [(x, y, z)] - /// @param[in] v The vector to copy. [(x, y, z)] - public static void rcVcopy(float[] dest, float[] v) { - dest[0] = v[0]; - dest[1] = v[1]; - dest[2] = v[2]; - } - public static void rcVcopy(float[] dest, int destStart, float[] v, int vStart) { - dest[destStart + 0] = v[vStart + 0]; - dest[destStart + 1] = v[vStart + 1]; - dest[destStart + 2] = v[vStart + 2]; - } - - /// Returns the distance between two points. - /// @param[in] v1 A point. [(x, y, z)] - /// @param[in] v2 A point. [(x, y, z)] - /// @return The distance between the two points. - public static float rcVdist(float[] v1, float[] v2) { - float dx = v2[0] - v1[0]; - float dy = v2[1] - v1[1]; - float dz = v2[2] - v1[2]; - return (float)Math.Sqrt(dx * dx + dy * dy + dz * dz); - } - - /// Returns the square of the distance between two points. - /// @param[in] v1 A point. [(x, y, z)] - /// @param[in] v2 A point. [(x, y, z)] - /// @return The square of the distance between the two points. - public static float rcVdistSqr(float[] v1, 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; - } - - /// Normalizes the vector. - /// @param[in,out] v The vector to normalize. [(x, y, z)] - public static void rcVnormalize(float[] v) { - float d = 1.0f / (float)Math.Sqrt((v[0] * v[0]) + (v[1] * v[1]) + (v[2] * v[2])); - v[0] *= d; - v[1] *= d; - v[2] *= d; - } - - /// @} - /// @name Heightfield Functions - /// @see rcHeightfield - /// @{ - - /// Calculates the bounding box of an array of vertices. - /// @ingroup recast - /// @param[in] verts An array of vertices. [(x, y, z) * @p nv] - /// @param[in] nv The number of vertices in the @p verts array. - /// @param[out] bmin The minimum bounds of the AABB. [(x, y, z)] [Units: wu] - /// @param[out] bmax The maximum bounds of the AABB. [(x, y, z)] [Units: wu] - //void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax); - - /// Calculates the grid size based on the bounding box and grid cell size. - /// @ingroup recast - /// @param[in] bmin The minimum bounds of the AABB. [(x, y, z)] [Units: wu] - /// @param[in] bmax The maximum bounds of the AABB. [(x, y, z)] [Units: wu] - /// @param[in] cs The xz-plane cell size. [Limit: > 0] [Units: wu] - /// @param[out] w The width along the x-axis. [Limit: >= 0] [Units: vx] - /// @param[out] h The height along the z-axis. [Limit: >= 0] [Units: vx] - //void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h); - - /// Initializes a new heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in,out] hf The allocated heightfield to initialize. - /// @param[in] width The width of the field along the x-axis. [Limit: >= 0] [Units: vx] - /// @param[in] height The height of the field along the z-axis. [Limit: >= 0] [Units: vx] - /// @param[in] bmin The minimum bounds of the field's AABB. [(x, y, z)] [Units: wu] - /// @param[in] bmax The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu] - /// @param[in] cs The xz-plane cell size to use for the field. [Limit: > 0] [Units: wu] - /// @param[in] ch The y-axis cell size to use for field. [Limit: > 0] [Units: wu] - //bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, - // const float* bmin, const float* bmax, - // float cs, float ch); - - /// Sets the area id of all triangles with a slope below the specified value - /// to #RC_WALKABLE_AREA. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. - /// [Limits: 0 <= value < 90] [Units: Degrees] - /// @param[in] verts The vertices. [(x, y, z) * @p nv] - /// @param[in] nv The number of vertices. - /// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] - /// @param[in] nt The number of triangles. - /// @param[out] areas The triangle area ids. [Length: >= @p nt] - //void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, - // const int* tris, int nt, byte* areas); - - /// Sets the area id of all triangles with a slope greater than or equal to the specified value to #RC_NULL_AREA. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. - /// [Limits: 0 <= value < 90] [Units: Degrees] - /// @param[in] verts The vertices. [(x, y, z) * @p nv] - /// @param[in] nv The number of vertices. - /// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] - /// @param[in] nt The number of triangles. - /// @param[out] areas The triangle area ids. [Length: >= @p nt] - //void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, - //const int* tris, int nt, byte* areas); - - /// Adds a span to the specified heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in,out] hf An initialized heightfield. - /// @param[in] x The width index where the span is to be added. - /// [Limits: 0 <= value < rcHeightfield::width] - /// @param[in] y The height index where the span is to be added. - /// [Limits: 0 <= value < rcHeightfield::height] - /// @param[in] smin The minimum height of the span. [Limit: < @p smax] [Units: vx] - /// @param[in] smax The maximum height of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] [Units: vx] - /// @param[in] area The area id of the span. [Limit: <= #RC_WALKABLE_AREA) - /// @param[in] flagMergeThr The merge theshold. [Limit: >= 0] [Units: vx] - //void rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y, - // const ushort smin, const ushort smax, - // const byte area, const int flagMergeThr); - - /// Rasterizes a triangle into the specified heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] v0 Triangle vertex 0 [(x, y, z)] - /// @param[in] v1 Triangle vertex 1 [(x, y, z)] - /// @param[in] v2 Triangle vertex 2 [(x, y, z)] - /// @param[in] area The area id of the triangle. [Limit: <= #RC_WALKABLE_AREA] - /// @param[in,out] solid An initialized heightfield. - /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. - /// [Limit: >= 0] [Units: vx] - //void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, - // const byte area, rcHeightfield& solid, - // const int flagMergeThr = 1); - - /// Rasterizes an indexed triangle mesh into the specified heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] verts The vertices. [(x, y, z) * @p nv] - /// @param[in] nv The number of vertices. - /// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] - /// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] - /// @param[in] nt The number of triangles. - /// @param[in,out] solid An initialized heightfield. - /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. - /// [Limit: >= 0] [Units: vx] - //void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, - // const int* tris, const byte* areas, const int nt, - // rcHeightfield& solid, const int flagMergeThr = 1); - - /// Rasterizes an indexed triangle mesh into the specified heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] verts The vertices. [(x, y, z) * @p nv] - /// @param[in] nv The number of vertices. - /// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] - /// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] - /// @param[in] nt The number of triangles. - /// @param[in,out] solid An initialized heightfield. - /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. - /// [Limit: >= 0] [Units: vx] - //void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, - // const ushort* tris, const byte* areas, const int nt, - // rcHeightfield& solid, const int flagMergeThr = 1); - - /// Rasterizes triangles into the specified heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] verts The triangle vertices. [(ax, ay, az, bx, by, bz, cx, by, cx) * @p nt] - /// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] - /// @param[in] nt The number of triangles. - /// @param[in,out] solid An initialized heightfield. - /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. - /// [Limit: >= 0] [Units: vx] - //void rcRasterizeTriangles(rcContext* ctx, const float* verts, const byte* areas, const int nt, - // rcHeightfield& solid, const int flagMergeThr = 1); - - /// Marks non-walkable spans as walkable if their maximum is within @p walkableClimp of a walkable neihbor. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. - /// [Limit: >=0] [Units: vx] - /// @param[in,out] solid A fully built heightfield. (All spans have been added.) - //void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid); - - /// Marks spans that are ledges as not-walkable. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to - /// be considered walkable. [Limit: >= 3] [Units: vx] - /// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. - /// [Limit: >=0] [Units: vx] - /// @param[in,out] solid A fully built heightfield. (All spans have been added.) - //void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, - // const int walkableClimb, rcHeightfield& solid); - - /// Marks walkable spans as not walkable if the clearence above the span is less than the specified height. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to - /// be considered walkable. [Limit: >= 3] [Units: vx] - /// @param[in,out] solid A fully built heightfield. (All spans have been added.) - //void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid); - - /// Returns the number of spans contained in the specified heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] hf An initialized heightfield. - /// @returns The number of spans in the heightfield. - //int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf); - - /// @} - /// @name Compact Heightfield Functions - /// @see rcCompactHeightfield - /// @{ - - /// Builds a compact heightfield representing open space, from a heightfield representing solid space. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area - /// to be considered walkable. [Limit: >= 3] [Units: vx] - /// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. - /// [Limit: >=0] [Units: vx] - /// @param[in] hf The heightfield to be compacted. - /// @param[out] chf The resulting compact heightfield. (Must be pre-allocated.) - /// @returns True if the operation completed successfully. - //bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb, - // rcHeightfield& hf, rcCompactHeightfield& chf); - - - /// Erodes the walkable area within the heightfield by the specified radius. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] radius The radius of erosion. [Limits: 0 < value < 255] [Units: vx] - /// @param[in,out] chf The populated compact heightfield to erode. - /// @returns True if the operation completed successfully. - //bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf); - - /// Applies a median filter to walkable area types (based on area id), removing noise. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in,out] chf A populated compact heightfield. - /// @returns True if the operation completed successfully. - //bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf); - - /// Applies an area id to all spans within the specified bounding box. (AABB) - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] bmin The minimum of the bounding box. [(x, y, z)] - /// @param[in] bmax The maximum of the bounding box. [(x, y, z)] - /// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] - /// @param[in,out] chf A populated compact heightfield. - //void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, byte areaId, - // rcCompactHeightfield& chf); - - /// Applies the area id to the all spans within the specified convex polygon. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] verts The vertices of the polygon [Fomr: (x, y, z) * @p nverts] - /// @param[in] nverts The number of vertices in the polygon. - /// @param[in] hmin The height of the base of the polygon. - /// @param[in] hmax The height of the top of the polygon. - /// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] - /// @param[in,out] chf A populated compact heightfield. - //void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts, - // const float hmin, const float hmax, byte areaId, - // rcCompactHeightfield& chf); - - /// Helper function to offset voncex polygons for rcMarkConvexPolyArea. - /// @ingroup recast - /// @param[in] verts The vertices of the polygon [Form: (x, y, z) * @p nverts] - /// @param[in] nverts The number of vertices in the polygon. - /// @param[out] outVerts The offset vertices (should hold up to 2 * @p nverts) [Form: (x, y, z) * return value] - /// @param[in] maxOutVerts The max number of vertices that can be stored to @p outVerts. - /// @returns Number of vertices in the offset polygon or 0 if too few vertices in @p outVerts. - //int rcOffsetPoly(const float* verts, const int nverts, const float offset, - // float* outVerts, const int maxOutVerts); - - /// Applies the area id to all spans within the specified cylinder. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] pos The center of the base of the cylinder. [Form: (x, y, z)] - /// @param[in] r The radius of the cylinder. - /// @param[in] h The height of the cylinder. - /// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] - /// @param[in,out] chf A populated compact heightfield. - //void rcMarkCylinderArea(rcContext* ctx, const float* pos, - // const float r, const float h, byte areaId, - // rcCompactHeightfield& chf); - - /// Builds the distance field for the specified compact heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in,out] chf A populated compact heightfield. - /// @returns True if the operation completed successfully. - //bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf); - - /// Builds region data for the heightfield using watershed partitioning. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in,out] chf A populated compact heightfield. - /// @param[in] borderSize The size of the non-navigable border around the heightfield. - /// [Limit: >=0] [Units: vx] - /// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. - /// [Limit: >=0] [Units: vx]. - /// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible, - /// be merged with larger regions. [Limit: >=0] [Units: vx] - /// @returns True if the operation completed successfully. - //bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, - // const int borderSize, const int minRegionArea, const int mergeRegionArea); - - /// Builds region data for the heightfield using simple monotone partitioning. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in,out] chf A populated compact heightfield. - /// @param[in] borderSize The size of the non-navigable border around the heightfield. - /// [Limit: >=0] [Units: vx] - /// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. - /// [Limit: >=0] [Units: vx]. - /// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible, - /// be merged with larger regions. [Limit: >=0] [Units: vx] - /// @returns True if the operation completed successfully. - //bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, - // const int borderSize, const int minRegionArea, const int mergeRegionArea); - - - /// Sets the neighbor connection data for the specified direction. - /// @param[in] s The span to update. - /// @param[in] dir The direction to set. [Limits: 0 <= value < 4] - /// @param[in] i The index of the neighbor span. - public static void rcSetCon(ref rcCompactSpan s, int dir, int i) { - uint udir = (uint)dir; - int shift = (int)(udir * 6); - uint con = s.con; - s.con = (uint)(con & ~(0x3f << shift)) | (((uint)i & 0x3f) << shift); - } - - /// Gets neighbor connection data for the specified direction. - /// @param[in] s The span to check. - /// @param[in] dir The direction to check. [Limits: 0 <= value < 4] - /// @return The neighbor connection data for the specified direction, - /// or #RC_NOT_CONNECTED if there is no connection. - public static int rcGetCon(rcCompactSpan s, int dir) { - uint udir = (uint)dir; - int shift = (int)(udir * 6); - return (int)((s.con >> shift) & 0x3f); - } - - /// Gets the standard width (x-axis) offset for the specified direction. - /// @param[in] dir The direction. [Limits: 0 <= value < 4] - /// @return The width offset to apply to the current cell position to move - /// in the direction. - public static int rcGetDirOffsetX(int dir) { - int[] offset = new int[] { -1, 0, 1, 0, }; - return offset[dir & 0x03]; - } - - /// Gets the standard height (z-axis) offset for the specified direction. - /// @param[in] dir The direction. [Limits: 0 <= value < 4] - /// @return The height offset to apply to the current cell position to move - /// in the direction. - public static int rcGetDirOffsetY(int dir) { - int[] offset = new int[] { 0, 1, 0, -1 }; - return offset[dir & 0x03]; - } - - /// @} - /// @name Layer, Contour, Polymesh, and Detail Mesh Functions - /// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail - /// @{ - - /// Builds a layer set from the specified compact heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] chf A fully built compact heightfield. - /// @param[in] borderSize The size of the non-navigable border around the heightfield. [Limit: >=0] - /// [Units: vx] - /// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area - /// to be considered walkable. [Limit: >= 3] [Units: vx] - /// @param[out] lset The resulting layer set. (Must be pre-allocated.) - /// @returns True if the operation completed successfully. - //bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, - // const int borderSize, const int walkableHeight, - // rcHeightfieldLayerSet& lset); - - /// Builds a contour set from the region outlines in the provided compact heightfield. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] chf A fully built compact heightfield. - /// @param[in] maxError The maximum distance a simplfied contour's border edges should deviate - /// the original raw contour. [Limit: >=0] [Units: wu] - /// @param[in] maxEdgeLen The maximum allowed length for contour edges along the border of the mesh. - /// [Limit: >=0] [Units: vx] - /// @param[out] cset The resulting contour set. (Must be pre-allocated.) - /// @param[in] buildFlags The build flags. (See: #rcBuildContoursFlags) - /// @returns True if the operation completed successfully. - //bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, - // const float maxError, const int maxEdgeLen, - // rcContourSet& cset, const int flags = RC_CONTOUR_TESS_WALL_EDGES); - - /// Builds a polygon mesh from the provided contours. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] cset A fully built contour set. - /// @param[in] nvp The maximum number of vertices allowed for polygons generated during the - /// contour to polygon conversion process. [Limit: >= 3] - /// @param[out] mesh The resulting polygon mesh. (Must be re-allocated.) - /// @returns True if the operation completed successfully. - //bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh); - - /// Merges multiple polygon meshes into a single mesh. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] meshes An array of polygon meshes to merge. [Size: @p nmeshes] - /// @param[in] nmeshes The number of polygon meshes in the meshes array. - /// @param[in] mesh The resulting polygon mesh. (Must be pre-allocated.) - /// @returns True if the operation completed successfully. - //bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh); - - /// Builds a detail mesh from the provided polygon mesh. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] mesh A fully built polygon mesh. - /// @param[in] chf The compact heightfield used to build the polygon mesh. - /// @param[in] sampleDist Sets the distance to use when samping the heightfield. [Limit: >=0] [Units: wu] - /// @param[in] sampleMaxError The maximum distance the detail mesh surface should deviate from - /// heightfield data. [Limit: >=0] [Units: wu] - /// @param[out] dmesh The resulting detail mesh. (Must be pre-allocated.) - /// @returns True if the operation completed successfully. - //bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf, - // const float sampleDist, const float sampleMaxError, - // rcPolyMeshDetail& dmesh); - - /// Copies the poly mesh data from src to dst. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] src The source mesh to copy from. - /// @param[out] dst The resulting detail mesh. (Must be pre-allocated, must be empty mesh.) - /// @returns True if the operation completed successfully. - //bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst); - - /// Merges multiple detail meshes into a single detail mesh. - /// @ingroup recast - /// @param[in,out] ctx The build context to use during the operation. - /// @param[in] meshes An array of detail meshes to merge. [Size: @p nmeshes] - /// @param[in] nmeshes The number of detail meshes in the meshes array. - /// @param[out] mesh The resulting detail mesh. (Must be pre-allocated.) - /// @returns True if the operation completed successfully. - //bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh); - - /// @} - -} - -/////////////////////////////////////////////////////////////////////////// - -// Due to the large amount of detail documentation for this file, -// the content normally located at the end of the header file has been separated -// out to a file in /Docs/Extern. diff --git a/Source/Framework/RecastDetour/Recast/RecastArea.cs b/Source/Framework/RecastDetour/Recast/RecastArea.cs deleted file mode 100644 index 84776e018..000000000 --- a/Source/Framework/RecastDetour/Recast/RecastArea.cs +++ /dev/null @@ -1,526 +0,0 @@ -using System; -using System.Diagnostics; - -public static partial class Recast { - /// @par - /// - /// Basically, any spans that are closer to a boundary or obstruction than the specified radius - /// are marked as unwalkable. - /// - /// This method is usually called immediately after the heightfield has been built. - /// - /// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius - public static bool rcErodeWalkableArea(rcContext ctx, int radius, rcCompactHeightfield chf) { - Debug.Assert(ctx != null, "rcContext is null"); - - int w = chf.width; - int h = chf.height; - - ctx.startTimer(rcTimerLabel.RC_TIMER_ERODE_AREA); - - byte[] dist = new byte[chf.spanCount];//(byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP); - if (dist == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' " + chf.spanCount); - return false; - } - - // Init distance. - for (int i=0; i < chf.spanCount; ++i) { - dist[i] = 0xff; - } - // memset(dist, 0xff, sizeof(byte)*chf.spanCount); - - // Mark boundary cells. - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - 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) { - dist[i] = 0; - } else { - rcCompactSpan s = chf.spans[i]; - int nc = 0; - for (int dir = 0; dir < 4; ++dir) { - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { - int nx = x + rcGetDirOffsetX(dir); - int ny = y + rcGetDirOffsetY(dir); - int nidx = (int)chf.cells[nx + ny * w].index + rcGetCon(s, dir); - if (chf.areas[nidx] != RC_NULL_AREA) { - nc++; - } - } - } - // At least one missing neighbour. - if (nc != 4) - dist[i] = 0; - } - } - } - } - - byte nd = 0; - - // Pass 1 - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - 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]; - - if (rcGetCon(s, 0) != RC_NOT_CONNECTED) { - // (-1,0) - int ax = x + rcGetDirOffsetX(0); - int ay = y + rcGetDirOffsetY(0); - int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 0); - rcCompactSpan aSpan = chf.spans[ai]; - nd = (byte)Math.Min((int)dist[ai] + 2, 255); - if (nd < dist[i]) - dist[i] = nd; - - // (-1,-1) - if (rcGetCon(aSpan, 3) != RC_NOT_CONNECTED) { - int aax = ax + rcGetDirOffsetX(3); - int aay = ay + rcGetDirOffsetY(3); - int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 3); - nd = (byte)Math.Min((int)dist[aai] + 3, 255); - if (nd < dist[i]) - dist[i] = nd; - } - } - if (rcGetCon(s, 3) != RC_NOT_CONNECTED) { - // (0,-1) - int ax = x + rcGetDirOffsetX(3); - int ay = y + rcGetDirOffsetY(3); - int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 3); - rcCompactSpan aSpan = chf.spans[ai]; - nd = (byte)Math.Min((int)dist[ai] + 2, 255); - if (nd < dist[i]) - dist[i] = nd; - - // (1,-1) - if (rcGetCon(aSpan, 2) != RC_NOT_CONNECTED) { - int aax = ax + rcGetDirOffsetX(2); - int aay = ay + rcGetDirOffsetY(2); - int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 2); - nd = (byte)Math.Min((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) { - 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]; - - if (rcGetCon(s, 2) != RC_NOT_CONNECTED) { - // (1,0) - int ax = x + rcGetDirOffsetX(2); - int ay = y + rcGetDirOffsetY(2); - int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 2); - rcCompactSpan aSpan = chf.spans[ai]; - nd = (byte)Math.Min((int)dist[ai] + 2, 255); - if (nd < dist[i]) - dist[i] = nd; - - // (1,1) - if (rcGetCon(aSpan, 1) != RC_NOT_CONNECTED) { - int aax = ax + rcGetDirOffsetX(1); - int aay = ay + rcGetDirOffsetY(1); - int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 1); - nd = (byte)Math.Min((int)dist[aai] + 3, 255); - if (nd < dist[i]) - dist[i] = nd; - } - } - if (rcGetCon(s, 1) != RC_NOT_CONNECTED) { - // (0,1) - int ax = x + rcGetDirOffsetX(1); - int ay = y + rcGetDirOffsetY(1); - int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 1); - rcCompactSpan aSpan = chf.spans[ai]; - nd = (byte)Math.Min((int)dist[ai] + 2, 255); - if (nd < dist[i]) - dist[i] = nd; - - // (-1,1) - if (rcGetCon(aSpan, 0) != RC_NOT_CONNECTED) { - int aax = ax + rcGetDirOffsetX(0); - int aay = ay + rcGetDirOffsetY(0); - int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 0); - nd = (byte)Math.Min((int)dist[aai] + 3, 255); - if (nd < dist[i]) - dist[i] = nd; - } - } - } - } - } - - byte thr = (byte)(radius * 2); - for (int i = 0; i < chf.spanCount; ++i) - if (dist[i] < thr) - chf.areas[i] = RC_NULL_AREA; - - ctx.stopTimer(rcTimerLabel.RC_TIMER_ERODE_AREA); - - return true; - } - - static void insertSort(byte[] a, int n) { - int i, j; - for (i = 1; i < n; i++) { - byte value = a[i]; - for (j = i - 1; j >= 0 && a[j] > value; j--) - a[j + 1] = a[j]; - a[j + 1] = value; - } - } - - /// @par - /// - /// This filter is usually applied after applying area id's using functions - /// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea. - /// - /// @see rcCompactHeightfield - public static bool rcMedianFilterWalkableArea(rcContext ctx, rcCompactHeightfield chf) { - Debug.Assert(ctx != null, "rcContext is null"); - - int w = chf.width; - int h = chf.height; - - ctx.startTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA); - - byte[] areas = new byte[chf.spanCount];//(byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP); - if (areas == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' " + chf.spanCount); - return false; - } - - // Init distance. - for (int i = 0; i < chf.spanCount; ++i) { - areas[i] = 0xff; - } - //memset(areas, 0xff, sizeof(byte)*chf.spanCount); - - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - 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]; - if (chf.areas[i] == RC_NULL_AREA) { - areas[i] = chf.areas[i]; - continue; - } - - byte[] nei = new byte[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) { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - 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]; - - rcCompactSpan aSpan = chf.spans[ai]; - int dir2 = (dir + 1) & 0x3; - if (rcGetCon(aSpan, dir2) != RC_NOT_CONNECTED) { - int ax2 = ax + rcGetDirOffsetX(dir2); - int ay2 = ay + rcGetDirOffsetY(dir2); - int ai2 = (int)chf.cells[ax2 + ay2 * w].index + rcGetCon(aSpan, dir2); - if (chf.areas[ai2] != RC_NULL_AREA) - nei[dir * 2 + 1] = chf.areas[ai2]; - } - } - } - insertSort(nei, 9); - areas[i] = nei[4]; - } - } - } - - chf.areas = areas; - //memcpy(chf.areas, areas, sizeof(byte)*chf.spanCount); - - //rcFree(areas); - - ctx.stopTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA); - - return true; - } - - /// @par - /// - /// The value of spacial parameters are in world units. - /// - /// @see rcCompactHeightfield, rcMedianFilterWalkableArea - public static void rcMarkBoxArea(rcContext ctx, float[] bmin, float[] bmax, byte areaId, - rcCompactHeightfield chf) { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.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) { - 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) { - if (chf.areas[i] != RC_NULL_AREA) - chf.areas[i] = areaId; - } - } - } - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_BOX_AREA); - - } - - - public static bool pointInPoly(int nvert, float[] verts, float[] p) { - bool c = false; - int i = 0; - int j = 0; - for (i = 0, j = nvert - 1; i < nvert; j = i++) { - int viStart = i * 3; - int vjStart = j * 3; - if (((verts[viStart + 2] > p[2]) != (verts[vjStart + 2] > p[2])) && - (p[0] < (verts[vjStart + 0] - verts[viStart + 0]) * (p[2] - verts[viStart + 2]) / (verts[vjStart + 2] - verts[viStart + 2]) + verts[viStart + 0])) { - c = !c; - } - } - return c; - } - - /// @par - /// - /// The value of spacial parameters are in world units. - /// - /// The y-values of the polygon vertices are ignored. So the polygon is effectively - /// projected onto the xz-plane at @p hmin, then extruded to @p hmax. - /// - /// @see rcCompactHeightfield, rcMedianFilterWalkableArea - public static void rcMarkConvexPolyArea(rcContext ctx, float[] verts, int nverts, - float hmin, float hmax, byte areaId, - rcCompactHeightfield chf) { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA); - - float[] bmin = new float[3]; - float[] bmax = new float[3]; - rcVcopy(bmin, verts); - rcVcopy(bmax, verts); - for (int i = 1; i < nverts; ++i) { - int vStart = i * 3; - rcVmin(bmin, 0, verts, vStart); - rcVmax(bmax, 0, verts, vStart); - } - 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) { - 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 (chf.areas[i] == RC_NULL_AREA) - continue; - if ((int)s.y >= miny && (int)s.y <= maxy) { - float[] p = new float[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(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA); - } - - static int rcOffsetPoly(float[] verts, int nverts, float offset, - float[] outVerts, int maxOutVerts) { - const float MITER_LIMIT = 1.20f; - - int n = 0; - - for (int i = 0; i < nverts; i++) { - int a = (i + nverts - 1) % nverts; - int b = i; - int c = (i + 1) % nverts; - int vaStart = a * 3; - int vbStart = b * 3; - int vcStart = c * 3; - float dx0 = verts[vbStart + 0] - verts[vaStart + 0]; - float dy0 = verts[vbStart + 2] - verts[vaStart + 2]; - float d0 = dx0 * dx0 + dy0 * dy0; - if (d0 > 1e-6f) { - d0 = 1.0f / (float)Math.Sqrt(d0); - dx0 *= d0; - dy0 *= d0; - } - float dx1 = verts[vcStart + 0] - verts[vbStart + 0]; - float dy1 = verts[vcStart + 2] - verts[vbStart + 2]; - float d1 = dx1 * dx1 + dy1 * dy1; - if (d1 > 1e-6f) { - d1 = 1.0f / (float)Math.Sqrt(d1); - dx1 *= d1; - dy1 *= d1; - } - float dlx0 = -dy0; - float dly0 = dx0; - float dlx1 = -dy1; - float dly1 = dx1; - float cross = dx1 * dy0 - dx0 * dy1; - float dmx = (dlx0 + dlx1) * 0.5f; - float dmy = (dly0 + dly1) * 0.5f; - float dmr2 = dmx * dmx + dmy * dmy; - bool bevel = dmr2 * MITER_LIMIT * MITER_LIMIT < 1.0f; - if (dmr2 > 1e-6f) { - float scale = 1.0f / dmr2; - dmx *= scale; - dmy *= scale; - } - - if (bevel && cross < 0.0f) { - if (n + 2 >= maxOutVerts) - return 0; - float d = (1.0f - (dx0 * dx1 + dy0 * dy1)) * 0.5f; - outVerts[n * 3 + 0] = verts[vbStart + 0] + (-dlx0 + dx0 * d) * offset; - outVerts[n * 3 + 1] = verts[vbStart + 1]; - outVerts[n * 3 + 2] = verts[vbStart + 2] + (-dly0 + dy0 * d) * offset; - n++; - outVerts[n * 3 + 0] = verts[vbStart + 0] + (-dlx1 - dx1 * d) * offset; - outVerts[n * 3 + 1] = verts[vbStart + 1]; - outVerts[n * 3 + 2] = verts[vbStart + 2] + (-dly1 - dy1 * d) * offset; - n++; - } else { - if (n + 1 >= maxOutVerts) - return 0; - outVerts[n * 3 + 0] = verts[vbStart + 0] - dmx * offset; - outVerts[n * 3 + 1] = verts[vbStart + 1]; - outVerts[n * 3 + 2] = verts[vbStart + 2] - dmy * offset; - n++; - } - } - - return n; - } - - - /// @par - /// - /// The value of spacial parameters are in world units. - /// - /// @see rcCompactHeightfield, rcMedianFilterWalkableArea - static public void rcMarkCylinderArea(rcContext ctx, float[] pos, - float r, float h, byte areaId, - rcCompactHeightfield chf) { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA); - - float[] bmin = new float[3]; - float[] bmax = new float[3]; - bmin[0] = pos[0] - r; - bmin[1] = pos[1]; - bmin[2] = pos[2] - r; - bmax[0] = pos[0] + r; - bmax[1] = pos[1] + h; - bmax[2] = pos[2] + r; - float r2 = r * r; - - 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) { - 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 (chf.areas[i] == RC_NULL_AREA) - continue; - - if ((int)s.y >= miny && (int)s.y <= maxy) { - float sx = chf.bmin[0] + (x + 0.5f) * chf.cs; - float sz = chf.bmin[2] + (z + 0.5f) * chf.cs; - float dx = sx - pos[0]; - float dz = sz - pos[2]; - - if (dx * dx + dz * dz < r2) { - chf.areas[i] = areaId; - } - } - } - } - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA); - } -} diff --git a/Source/Framework/RecastDetour/Recast/RecastContour.cs b/Source/Framework/RecastDetour/Recast/RecastContour.cs deleted file mode 100644 index 290c87106..000000000 --- a/Source/Framework/RecastDetour/Recast/RecastContour.cs +++ /dev/null @@ -1,860 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; - -public static partial class Recast{ -static int getCornerHeight(int x, int y, int i, int dir, - rcCompactHeightfield chf, - ref bool isBorderVertex) -{ - rcCompactSpan s = chf.spans[i]; - int ch = (int)s.y; - int dirp = (dir+1) & 0x3; - - uint[] regs = new uint[] {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] = (uint)( chf.spans[i].reg | (chf.areas[i] << 16) ); - - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - rcCompactSpan aSpan = chf.spans[ai]; - ch = Math.Max(ch, (int)aSpan.y); - regs[1] = (uint)( chf.spans[ai].reg | (chf.areas[ai] << 16) ); - if (rcGetCon(aSpan, dirp) != RC_NOT_CONNECTED) - { - int ax2 = ax + rcGetDirOffsetX(dirp); - int ay2 = ay + rcGetDirOffsetY(dirp); - int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(aSpan, dirp); - rcCompactSpan as2 = chf.spans[ai2]; - ch = Math.Max(ch, (int)as2.y); - regs[2] = (uint)(chf.spans[ai2].reg | (chf.areas[ai2] << 16)); - } - } - if (rcGetCon(s, dirp) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(dirp); - int ay = y + rcGetDirOffsetY(dirp); - int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp); - rcCompactSpan aSpan = chf.spans[ai]; - ch = Math.Max(ch, (int)aSpan.y); - regs[3] = (uint)(chf.spans[ai].reg | (chf.areas[ai] << 16)); - if (rcGetCon(aSpan, dir) != RC_NOT_CONNECTED) - { - int ax2 = ax + rcGetDirOffsetX(dir); - int ay2 = ay + rcGetDirOffsetY(dir); - int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(aSpan, dir); - rcCompactSpan as2 = chf.spans[ai2]; - ch = Math.Max(ch, (int)as2.y); - regs[2] = (uint)(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) - { - int a = j; - int b = (j+1) & 0x3; - int c = (j+2) & 0x3; - 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. - bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b]; - bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0; - bool intsSameArea = (regs[c]>>16) == (regs[d]>>16); - bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0; - if (twoSameExts && twoInts && intsSameArea && noZeros) - { - isBorderVertex = true; - break; - } - } - - return ch; -} - -public static void walkContour(int x, int y, int i, - rcCompactHeightfield chf, - byte[] flags, List points) -{ - // Choose the first non-connected edge - byte dir = 0; - while ((flags[i] & (1 << dir)) == 0) - dir++; - - byte startDir = dir; - int starti = i; - - byte area = chf.areas[i]; - - int iter = 0; - while (++iter < 40000) - { - if ((flags[i] & (1 << dir)) != 0) - { - // Choose the edge corner - bool isBorderVertex = false; - bool isAreaBorder = false; - int px = x; - int py = getCornerHeight(x, y, i, dir, chf,ref isBorderVertex); - int pz = y; - switch(dir) - { - case 0: pz++; break; - case 1: px++; pz++; break; - case 2: px++; break; - } - int r = 0; - rcCompactSpan s = chf.spans[i]; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - 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.Add(px); - points.Add(py); - points.Add(pz); - points.Add(r); - - flags[i] &= (byte)( ~(1 << dir) ); // Remove visited edges - dir = (byte)( (dir+1) & 0x3); // Rotate CW - } - else - { - int ni = -1; - int nx = x + rcGetDirOffsetX(dir); - int ny = y + rcGetDirOffsetY(dir); - rcCompactSpan s = chf.spans[i]; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - 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 = (byte)((dir+3) & 0x3); // Rotate CCW - } - - if (starti == i && startDir == dir) - { - break; - } - } -} - -public static float distancePtSeg(int x, int z, - int px, int pz, - int qx, 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; -} - -public static void simplifyContour(List points, List simplified, - float maxError, int maxEdgeLen, int buildFlags) -{ - // Add initial points. - bool hasConnections = false; - for (int i = 0; i < points.Count; 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.Count /4; i < ni; ++i) - { - int ii = (i+1) % ni; - bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK); - bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER); - if (differentRegs || areaBorders) - { - simplified.Add(points[i*4+0]); - simplified.Add(points[i*4+1]); - simplified.Add(points[i*4+2]); - simplified.Add(i); - } - } - } - - if (simplified.Count == 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.Count; 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.Add(llx); - simplified.Add(lly); - simplified.Add(llz); - simplified.Add(lli); - - simplified.Add(urx); - simplified.Add(ury); - simplified.Add(urz); - simplified.Add(uri); - } - - // Add points until all raw points are within - // error tolerance to the simplified shape. - int pn = points.Count/4; - for (int i = 0; i < simplified.Count/4; ) - { - int ii = (i+1) % (simplified.Count/4); - - int ax = simplified[i*4+0]; - int az = simplified[i*4+2]; - int ai = simplified[i*4+3]; - - int bx = simplified[ii*4+0]; - int bz = simplified[ii*4+2]; - 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 or edges between areas. - if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 || - (points[ci*4+3] & RC_AREA_BORDER) != 0) - { - 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.Count+4); - rccsResizeList(simplified, simplified.Count + 4); - int n = simplified.Count/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 & (int)(rcBuildContoursFlags.RC_CONTOUR_TESS_WALL_EDGES|rcBuildContoursFlags.RC_CONTOUR_TESS_AREA_EDGES)) != 0) - { - for (int i = 0; i < simplified.Count/4; ) - { - int ii = (i+1) % (simplified.Count/4); - - int ax = simplified[i*4+0]; - int az = simplified[i*4+2]; - int ai = simplified[i*4+3]; - - int bx = simplified[ii*4+0]; - int bz = simplified[ii*4+2]; - 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 & (int)rcBuildContoursFlags.RC_CONTOUR_TESS_WALL_EDGES) != 0 && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0) - tess = true; - // Edges between areas. - if ((buildFlags & (int)rcBuildContoursFlags.RC_CONTOUR_TESS_AREA_EDGES) != 0 && (points[ci*4+3] & RC_AREA_BORDER) != 0) - 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. - int n = bi < ai ? (bi+pn - ai) : (bi - ai); - if (n > 1) - { - if (bx > ax || (bx == ax && bz > az)) - maxi = (ai + n/2) % pn; - else - 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. - rccsResizeList(simplified, simplified.Count + 4); - int n = simplified.Count/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.Count/4; ++i) - { - // The edge vertex flag is take from the current raw point, - // and the neighbour region is take from the next raw point. - int ai = (simplified[i*4+3]+1) % pn; - int bi = simplified[i*4+3]; - simplified[i*4+3] = (points[ai*4+3] & (RC_CONTOUR_REG_MASK|RC_AREA_BORDER)) | (points[bi*4+3] & RC_BORDER_VERTEX); - } - -} - -public static void removeDegenerateSegments(List simplified) -{ - // Remove adjacent vertices which are equal on xz-plane, - // or else the triangulator will get confused. - for (int i = 0; i < simplified.Count/4; ++i) - { - int ni = i+1; - if (ni >= (simplified.Count/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.Count/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.Capacity = (simplified.Count-4); - rccsResizeList(simplified, simplified.Count - 4); - } - } -} - -public static int calcAreaOfPolygon2D(int[] verts, int nverts) -{ - int area = 0; - for (int i = 0, j = nverts-1; i < nverts; j=i++) - { - int viStart = i * 4; - int vjStart = j * 4; - area += verts[viStart + 0] * verts[vjStart + 2] - verts[vjStart + 0] * verts[viStart + 2]; - } - return (area+1) / 2; -} - -public static bool ileft(int[] a, int[] b, int[] c) -{ - return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0; -} - - -public static bool ileft(int[] a,int aStart, int[] b, int bStart, int[] c, int cStart) { - return (b[bStart + 0] - a[aStart + 0]) * (c[cStart + 2] - a[aStart + 2]) - (c[cStart + 0] - a[aStart + 0]) * (b[bStart + 2] - a[aStart + 2]) <= 0; -} - -public static void getClosestIndices(int[] vertsa, int nvertsa, - int[] vertsb, int nvertsb, - ref int ia, ref int ib) -{ - int closestDist = 0xfffffff; - ia = -1; - ib = -1; - for (int i = 0; i < nvertsa; ++i) - { - int i_n = (i+1) % nvertsa; - int ip = (i+nvertsa-1) % nvertsa; - int vaStart = i * 4; - int vanStart = i_n * 4; - int vapStart = ip * 4; - - for (int j = 0; j < nvertsb; ++j) - { - int vbStart = j * 4; - // vb must be "infront" of va. - if (ileft(vertsa,vapStart,vertsa,vaStart,vertsb,vbStart) && ileft(vertsa,vaStart,vertsa,vanStart,vertsb,vbStart)) - { - int dx = vertsb[vbStart+0] - vertsa[vaStart + 0]; - int dz = vertsb[vbStart+2] - vertsa[vaStart+2]; - int d = dx*dx + dz*dz; - if (d < closestDist) - { - ia = i; - ib = j; - closestDist = d; - } - } - } - } -} - -public static bool mergeContours(ref rcContour ca, ref rcContour cb, int ia, int ib) -{ - int maxVerts = ca.nverts + cb.nverts + 2; - int[] verts = new int[maxVerts * 4];//(int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM); - if (verts == null) - return false; - - int nv = 0; - - // Copy contour A. - for (int i = 0; i <= ca.nverts; ++i) - { - //int* dst = &verts[nv*4]; - int dstIndex = nv*4; - int srcIndex = ((ia+i)%ca.nverts)*4; - for (int j=0;i<4;++i){ - verts[dstIndex + j] = ca.verts[srcIndex + j]; - } - nv++; - } - - // Copy contour B - for (int i = 0; i <= cb.nverts; ++i) - { - int dstIndex = nv*4; - int srcIndex = ((ib+i)%cb.nverts)*4; - //int* dst = &verts[nv*4]; - //const int* src = &cb.verts[((ib+i)%cb.nverts)*4]; - for (int j=0;j<4;++j){ - verts[dstIndex + j] = cb.verts[srcIndex + j]; - } - nv++; - } - - ca.verts = verts; - ca.nverts = nv; - - cb.verts = null; - cb.nverts = 0; - - return true; -} - -/// @par -/// -/// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen -/// parameters control how closely the simplified contours will match the raw contours. -/// -/// Simplified contours are generated such that the vertices for portals between areas match up. -/// (They are considered mandatory vertices.) -/// -/// Setting @p maxEdgeLength to zero will disabled the edge length feature. -/// -/// See the #rcConfig documentation for more information on the configuration parameters. -/// -/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig -public static bool rcBuildContours(rcContext ctx, rcCompactHeightfield chf, - float maxError, int maxEdgeLen, - rcContourSet cset, int buildFlags) -{ - Debug.Assert(ctx != null, "rcContext is null"); - - int w = chf.width; - int h = chf.height; - int borderSize = chf.borderSize; - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS); - - rcVcopy(cset.bmin, chf.bmin); - rcVcopy(cset.bmax, chf.bmax); - if (borderSize > 0) - { - // If the heightfield was build with bordersize, remove the offset. - float pad = borderSize*chf.cs; - cset.bmin[0] += pad; - cset.bmin[2] += pad; - cset.bmax[0] -= pad; - cset.bmax[2] -= pad; - } - cset.cs = chf.cs; - cset.ch = chf.ch; - cset.width = chf.width - chf.borderSize*2; - cset.height = chf.height - chf.borderSize*2; - cset.borderSize = chf.borderSize; - - int maxContours = Math.Max((int)chf.maxRegions, 8); - //cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); - cset.conts = new rcContour[maxContours]; - //if (cset.conts == null) -// return false; - cset.nconts = 0; - - //rcScopedDelete flags = (byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP); - byte[] flags = new byte[chf.spanCount]; - if (flags == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' " + chf.spanCount); - return false; - } - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE); - - // Mark boundaries. - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - rcCompactCell c = chf.cells[x+y*w]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - byte res = 0; - rcCompactSpan s = chf.spans[i]; - if (chf.spans[i].reg == 0 || (chf.spans[i].reg & RC_BORDER_REG) != 0) - { - flags[i] = 0; - continue; - } - for (int dir = 0; dir < 4; ++dir) - { - ushort r = 0; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - r = chf.spans[ai].reg; - } - if (r == chf.spans[i].reg) - res |= (byte)(1 << dir); - } - flags[i] = (byte)(res ^ 0xf); // Inverse, mark non connected edges. - } - } - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE); - - //List verts(256); - List verts = new List(); - verts.Capacity = 256; - //List simplified(64); - List simplified = new List(); - simplified.Capacity = 64; - - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - 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; - } - ushort reg = chf.spans[i].reg; - if (reg == 0 || (reg & RC_BORDER_REG) != 0) { - continue; - } - byte area = chf.areas[i]; - - //verts.resize(0); - //simplified.resize(0); - verts.Clear(); - simplified.Clear(); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE); - walkContour(x, y, i, chf, flags, verts); - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY); - simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags); - removeDegenerateSegments(simplified); - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY); - - - // Store region.contour remap info. - // Create contour. - if (simplified.Count/4 >= 3) - { - if (cset.nconts >= maxContours) - { - // Allocate more contours. - // This can happen when there are tiny holes in the heightfield. - int oldMax = maxContours; - maxContours *= 2; - rcContour[] newConts = new rcContour[maxContours];// (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 = null; - cset.conts[j].rverts = null; - } - //rcFree(cset.conts); - cset.conts = newConts; - - ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Expanding max contours from " + oldMax + " to "+ maxContours); - } - - int contId = cset.nconts; - cset.nconts++; - rcContour cont = cset.conts[contId]; - - cont.nverts = simplified.Count/4; - cont.verts = new int[cont.nverts * 4]; //(int*)rcAlloc(sizeof(int)*cont.nverts*4, RC_ALLOC_PERM); - if (cont.verts == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' " + cont.nverts); - return false; - } - //memcpy(cont.verts, &simplified[0], sizeof(int)*cont.nverts*4); - for (int j = 0; j < cont.nverts * 4; ++j) { - cont.verts[j] = simplified[j]; - } - if (borderSize > 0) - { - // If the heightfield was build with bordersize, remove the offset. - for (int j = 0; j < cont.nverts; ++j) - { - //int* v = &cont.verts[j*4]; - cont.verts[j * 4] -= borderSize; - cont.verts[j*4 + 2] -= borderSize; - //v[0] -= borderSize; - //v[2] -= borderSize; - } - } - - cont.nrverts = verts.Count/4; - cont.rverts = new int[cont.nrverts * 4];//(int*)rcAlloc(sizeof(int)*cont.nrverts*4, RC_ALLOC_PERM); - if (cont.rverts == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' " + cont.nrverts); - return false; - } - //memcpy(cont.rverts, &verts[0], sizeof(int)*cont.nrverts*4); - for (int j = 0; j < cont.nrverts * 4; ++j) { - cont.rverts[j] = verts[j]; - } - if (borderSize > 0) - { - // If the heightfield was build with bordersize, remove the offset. - for (int j = 0; j < cont.nrverts; ++j) - { - //int* v = &cont.rverts[j*4]; - cont.rverts[j * 4] -= borderSize; - cont.rverts[j * 4 + 2] -= borderSize; - } - } - -/* 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; - - cset.conts[contId] = cont; - } - } - } - } - - // 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 != 0 && cset.conts[j].reg == cont.reg) - { - // Make sure the polygon is correctly oriented. - if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts) != 0) - { - mergeIdx = j; - break; - } - } - } - if (mergeIdx == -1) - { - ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour " + 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, ref ia, ref ib); - if (ia == -1 || ib == -1) - { - ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for " + i + " and " + mergeIdx); - continue; - } - if (!mergeContours(ref mcont,ref cont, ia, ib)) - { - ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to merge contours " + i + " and " + mergeIdx); - continue; - } - cset.conts[mergeIdx] = mcont; - cset.conts[i] = cont; - } - } - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS); - - return true; -} -} \ No newline at end of file diff --git a/Source/Framework/RecastDetour/Recast/RecastFilter.cs b/Source/Framework/RecastDetour/Recast/RecastFilter.cs deleted file mode 100644 index 30c4bb339..000000000 --- a/Source/Framework/RecastDetour/Recast/RecastFilter.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Diagnostics; - -public static partial class Recast{ - /// @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: rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb - /// - /// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call - /// #rcFilterLedgeSpans after calling this filter. - /// - /// @see rcHeightfield, rcConfig - public static void rcFilterLowHangingWalkableObstacles(rcContext ctx, int walkableClimb, rcHeightfield solid) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES); - - int w = solid.width; - int h = solid.height; - - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - rcSpan ps = null; - bool previousWalkable = false; - byte previousArea = RC_NULL_AREA; - - for (rcSpan s = solid.spans[x + y*w]; s != null; ps = s, s = s.next) - { - 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 (Math.Abs((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(rcTimerLabel.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: rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb - /// - /// @see rcHeightfield, rcConfig - public static void rcFilterLedgeSpans(rcContext ctx, int walkableHeight, int walkableClimb, - rcHeightfield solid) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER); - - int w = solid.width; - int h = solid.height; - 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 != null; s = s.next) - { - // Skip non walkable spans. - if (s.area == RC_NULL_AREA){ - continue; - } - - int bot = (int)(s.smax); - int top = s.next != null ? (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 = Math.Min(minh, -walkableClimb - bot); - continue; - } - - // From minus infinity to the first span. - rcSpan ns = solid.spans[dx + dy*w]; - int nbot = -walkableClimb; - int ntop = ns != null ? (int)ns.smin : MAX_HEIGHT; - // Skip neightbour if the gap between the spans is too small. - if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight) - minh = Math.Min(minh, nbot - bot); - - // Rest of the spans. - for (ns = solid.spans[dx + dy*w]; ns != null; ns = ns.next) - { - nbot = (int)ns.smax; - ntop = ns.next != null ? (int)ns.next.smin : MAX_HEIGHT; - // Skip neightbour if the gap between the spans is too small. - if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight) - { - minh = Math.Min(minh, nbot - bot); - - // Find min/max accessible neighbour height. - if (Math.Abs(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(rcTimerLabel.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 - public static void rcFilterWalkableLowHeightSpans(rcContext ctx, int walkableHeight, rcHeightfield solid) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE); - - int w = solid.width; - int h = solid.height; - 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 != null; s = s.next) - { - int bot = (int)(s.smax); - int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT; - if ((top - bot) <= walkableHeight) { - s.area = RC_NULL_AREA; - } - } - } - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE); - } -} \ No newline at end of file diff --git a/Source/Framework/RecastDetour/Recast/RecastLayers.cs b/Source/Framework/RecastDetour/Recast/RecastLayers.cs deleted file mode 100644 index f92067c74..000000000 --- a/Source/Framework/RecastDetour/Recast/RecastLayers.cs +++ /dev/null @@ -1,641 +0,0 @@ -using System; -using System.Diagnostics; - -public static partial class Recast{ - - const int RC_MAX_LAYERS = RC_NOT_CONNECTED; - const int RC_MAX_NEIS = 16; - - public class rcLayerRegion - { - public byte[] layers = new byte[RC_MAX_LAYERS]; - public byte[] neis = new byte[RC_MAX_NEIS]; - public ushort ymin; - public ushort ymax; - public byte layerId; // Layer ID - public byte nlayers; // Layer count - public byte nneis; // Neighbour count - public byte baseFlag; // Flag indicating if the region is hte base of merged regions. - }; - - - public static void addUnique(byte[] a,ref byte an, byte v) - { - int n = (int)an; - for (int i = 0; i < n; ++i){ - if (a[i] == v){ - return; - } - } - a[an] = v; - an++; - } - - public static bool contains(byte[] a, byte an, byte v) - { - int n = (int)an; - for (int i = 0; i < n; ++i){ - if (a[i] == v){ - return true; - } - } - return false; - } - - public static bool overlapRange( ushort amin, ushort amax, - ushort bmin, ushort bmax) - { - return (amin > bmax || amax < bmin) ? false : true; - } - - - - public class rcLayerSweepSpan - { - public ushort ns; // number samples - public byte id; // region id - public byte nei; // neighbour id - }; - - /// @par - /// - /// See the #rcConfig documentation for more information on the configuration parameters. - /// - /// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig - public static bool rcBuildHeightfieldLayers(rcContext ctx, rcCompactHeightfield chf, - int borderSize, int walkableHeight, - rcHeightfieldLayerSet lset) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS); - - int w = chf.width; - int h = chf.height; - - //rcScopedDelete srcReg = (byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP); - byte[] srcReg = new byte[chf.spanCount]; - if (srcReg == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' " + chf.spanCount); - return false; - } - //memset(srcReg,0xff,sizeof(byte)*chf.spanCount); - for (int i=0;i sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP); - rcLayerSweepSpan[] sweeps = new rcLayerSweepSpan[nsweeps]; - if (sweeps == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' " + nsweeps); - return false; - } - - - // Partition walkable area into monotone regions. - int[] prevCount = new int[256]; - byte regId = 0; - - for (int y = borderSize; y < h-borderSize; ++y) - { - //memset to 0 is done by C# alloc - //memset(prevCount,0,sizeof(int)*regId); - - byte sweepId = 0; - - for (int x = borderSize; x < w-borderSize; ++x) - { - 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]; - if (chf.areas[i] == RC_NULL_AREA) continue; - - byte sid = 0xff; - - // -x - if (rcGetCon(s, 0) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(0); - int ay = y + rcGetDirOffsetY(0); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); - if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff) - sid = srcReg[ai]; - } - - if (sid == 0xff) - { - sid = sweepId++; - sweeps[sid].nei = (byte)0xff; - sweeps[sid].ns = 0; - } - - // -y - if (rcGetCon(s,3) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(3); - int ay = y + rcGetDirOffsetY(3); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); - byte nr = srcReg[ai]; - if (nr != 0xff) - { - // Set neighbour when first valid neighbour is encoutered. - if (sweeps[sid].ns == 0) - sweeps[sid].nei = nr; - - if (sweeps[sid].nei == nr) - { - // Update existing neighbour - sweeps[sid].ns++; - prevCount[nr]++; - } - else - { - // This is hit if there is nore than one neighbour. - // Invalidate the neighbour. - sweeps[sid].nei = 0xff; - } - } - } - - srcReg[i] = sid; - } - } - - // Create unique ID. - for (int i = 0; i < sweepId; ++i) - { - // If the neighbour is set and there is only one continuous connection to it, - // the sweep will be merged with the previous one, else new region is created. - if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns) - { - sweeps[i].id = sweeps[i].nei; - } - else - { - if (regId == 255) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow."); - return false; - } - sweeps[i].id = regId++; - } - } - - // Remap local sweep ids to region ids. - for (int x = borderSize; x < w-borderSize; ++x) - { - 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] != 0xff) - srcReg[i] = sweeps[srcReg[i]].id; - } - } - } - - // Allocate and init layer regions. - int nregs = (int)regId; - //rcScopedDelete regs = (rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP); - rcLayerRegion[] regs = new rcLayerRegion[nregs]; - if (regs == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' " + nregs); - return false; - } - //memset(regs, 0, sizeof(rcLayerRegion)*nregs); - for (int i = 0; i < nregs; ++i) - { - regs[i].layerId = 0xff; - regs[i].ymin = 0xffff; - regs[i].ymax = 0; - } - - // Find region neighbours and overlapping regions. - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - rcCompactCell c = chf.cells[x+y*w]; - - byte[] lregs = new byte[RC_MAX_LAYERS]; - int nlregs = 0; - - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - rcCompactSpan s = chf.spans[i]; - byte ri = srcReg[i]; - if (ri == 0xff){ - continue; - } - - regs[ri].ymin = Math.Min(regs[ri].ymin, s.y); - regs[ri].ymax = Math.Max(regs[ri].ymax, s.y); - - // Collect all region layers. - if (nlregs < RC_MAX_LAYERS) - lregs[nlregs++] = ri; - - // Update neighbours - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - byte rai = srcReg[ai]; - if (rai != 0xff && rai != ri){ - addUnique(regs[ri].neis,ref regs[ri].nneis, rai); - } - } - } - - } - - // Update overlapping regions. - for (int i = 0; i < nlregs-1; ++i) - { - for (int j = i+1; j < nlregs; ++j) - { - if (lregs[i] != lregs[j]) - { - rcLayerRegion ri = regs[lregs[i]]; - rcLayerRegion rj = regs[lregs[j]]; - addUnique(ri.layers,ref ri.nlayers, lregs[j]); - addUnique(rj.layers,ref rj.nlayers, lregs[i]); - } - } - } - - } - } - - // Create 2D layers from regions. - byte layerId = 0; - - const int MAX_STACK = 64; - byte[] stack = new byte[MAX_STACK]; - int nstack = 0; - - for (int i = 0; i < nregs; ++i) - { - rcLayerRegion root = regs[i]; - // Skip alreadu visited. - if (root.layerId != 0xff){ - continue; - } - - // Start search. - root.layerId = layerId; - root.baseFlag = 1; - - nstack = 0; - stack[nstack++] = (byte)i; - - while (nstack != 0) - { - // Pop front - rcLayerRegion reg = regs[stack[0]]; - nstack--; - for (int j = 0; j < nstack; ++j){ - stack[j] = stack[j+1]; - } - - int nneis = (int)reg.nneis; - for (int j = 0; j < nneis; ++j) - { - byte nei = reg.neis[j]; - rcLayerRegion regn = regs[nei]; - // Skip already visited. - if (regn.layerId != 0xff){ - continue; - } - // Skip if the neighbour is overlapping root region. - if (contains(root.layers, root.nlayers, nei)){ - continue; - } - // Skip if the height range would become too large. - int ymin = Math.Min(root.ymin, regn.ymin); - int ymax = Math.Max(root.ymax, regn.ymax); - if ((ymax - ymin) >= 255){ - continue; - } - - if (nstack < MAX_STACK) - { - // Deepen - stack[nstack++] = (byte)nei; - - // Mark layer id - regn.layerId = layerId; - // Merge current layers to root. - for (int k = 0; k < regn.nlayers; ++k){ - addUnique(root.layers,ref root.nlayers, regn.layers[k]); - } - root.ymin = Math.Min(root.ymin, regn.ymin); - root.ymax = Math.Max(root.ymax, regn.ymax); - } - } - } - - layerId++; - } - - // Merge non-overlapping regions that are close in height. - ushort mergeHeight = (ushort)(walkableHeight * 4); - - for (int i = 0; i < nregs; ++i) - { - rcLayerRegion ri = regs[i]; - if (ri.baseFlag == 0){ - continue; - } - - byte newId = ri.layerId; - - for (;;) - { - byte oldId = 0xff; - - for (int j = 0; j < nregs; ++j) - { - if (i == j){ - continue; - } - rcLayerRegion rj = regs[j]; - if (rj.baseFlag == 0){ - continue; - } - - // Skip if teh regions are not close to each other. - if (!overlapRange(ri.ymin, - (ushort)(ri.ymax + mergeHeight), - rj.ymin, - (ushort)(rj.ymax + mergeHeight))){ - continue; - } - // Skip if the height range would become too large. - int ymin = Math.Min(ri.ymin, rj.ymin); - int ymax = Math.Max(ri.ymax, rj.ymax); - if ((ymax - ymin) >= 255){ - continue; - } - - // Make sure that there is no overlap when mergin 'ri' and 'rj'. - bool overlap = false; - // Iterate over all regions which have the same layerId as 'rj' - for (int k = 0; k < nregs; ++k) - { - if (regs[k].layerId != rj.layerId) - continue; - // Check if region 'k' is overlapping region 'ri' - // Index to 'regs' is the same as region id. - if (contains(ri.layers,ri.nlayers, (byte)k)) - { - overlap = true; - break; - } - } - // Cannot merge of regions overlap. - if (overlap) - continue; - - // Can merge i and j. - oldId = rj.layerId; - break; - } - - // Could not find anything to merge with, stop. - if (oldId == 0xff) - break; - - // Merge - for (int j = 0; j < nregs; ++j) - { - rcLayerRegion rj = regs[j]; - if (rj.layerId == oldId) - { - rj.baseFlag = 0; - // Remap layerIds. - rj.layerId = newId; - // Add overlaid layers from 'rj' to 'ri'. - for (int k = 0; k < rj.nlayers; ++k){ - addUnique(ri.layers,ref ri.nlayers, rj.layers[k]); - } - // Update heigh bounds. - ri.ymin = Math.Min(ri.ymin, rj.ymin); - ri.ymax = Math.Max(ri.ymax, rj.ymax); - } - } - } - } - - // Compact layerIds - byte[] remap = new byte[256]; - //memset(remap, 0, 256); - - // Find number of unique layers. - layerId = 0; - for (int i = 0; i < nregs; ++i){ - remap[regs[i].layerId] = 1; - } - for (int i = 0; i < 256; ++i) - { - if (remap[i] != 0){ - remap[i] = layerId++; - } - else{ - remap[i] = 0xff; - } - } - // Remap ids. - for (int i = 0; i < nregs; ++i){ - regs[i].layerId = remap[regs[i].layerId]; - } - - // No layers, return empty. - if (layerId == 0) - { - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS); - return true; - } - - // Create layers. - Debug.Assert(lset.layers == null,"Assert lset.layers == 0"); - - int lw = w - borderSize*2; - int lh = h - borderSize*2; - - // Build contracted bbox for layers. - float[] bmin = new float[3]; - float[] bmax = new float[3]; - rcVcopy(bmin, chf.bmin); - rcVcopy(bmax, chf.bmax); - bmin[0] += borderSize*chf.cs; - bmin[2] += borderSize*chf.cs; - bmax[0] -= borderSize*chf.cs; - bmax[2] -= borderSize*chf.cs; - - lset.nlayers = (int)layerId; - - //lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM); - lset.layers = new rcHeightfieldLayer[lset.nlayers]; - if (lset.layers == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' " + lset.nlayers); - return false; - } - //memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers); - - - // Store layers. - for (int i = 0; i < lset.nlayers; ++i) - { - byte curId = (byte)i; - - // Allocate memory for the current layer. - rcHeightfieldLayer layer = lset.layers[i]; - //memset(layer, 0, sizeof(rcHeightfieldLayer)); - - int gridSize = sizeof(byte)*lw*lh; - - layer.heights = new byte[gridSize];//(byte*)rcAlloc(gridSize, RC_ALLOC_PERM); - if (layer.heights == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' " + gridSize); - return false; - } - //memset(layer.heights, 0xff, gridSize); - for (int j=0;j hmin) - layer.heights[idx] = Math.Max(layer.heights[idx], (byte)(aSpan.y - hmin)); - } - // Valid connection mask - if (chf.areas[ai] != RC_NULL_AREA && lid == alid) - { - int nx = ax - borderSize; - int ny = ay - borderSize; - if (nx >= 0 && ny >= 0 && nx < lw && ny < lh) - con |= (byte)(1< layer.maxx) - layer.minx = layer.maxx = 0; - if (layer.miny > layer.maxy) - layer.miny = layer.maxy = 0; - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS); - - return true; - } -} \ No newline at end of file diff --git a/Source/Framework/RecastDetour/Recast/RecastMesh.cs b/Source/Framework/RecastDetour/Recast/RecastMesh.cs deleted file mode 100644 index 054f60b88..000000000 --- a/Source/Framework/RecastDetour/Recast/RecastMesh.cs +++ /dev/null @@ -1,1513 +0,0 @@ -using System; -using System.Diagnostics; - -public static partial class Recast { - - public class rcEdge { - public ushort[] vert = new ushort[2]; - public ushort[] polyEdge = new ushort[2]; - public ushort[] poly = new ushort[2]; - }; - - public static bool buildMeshAdjacency(ushort[] polys, int npolys, - int nverts, int vertsPerPoly) { - // Based on code by Eric Lengyel from: - // http://www.terathon.com/code/edges.php - - int maxEdgeCount = npolys * vertsPerPoly; - ushort[] firstEdge = new ushort[nverts + maxEdgeCount];//(ushort*)rcAlloc(sizeof(ushort)*(nverts + maxEdgeCount), RC_ALLOC_TEMP); - if (firstEdge == null) - return false; - //ushort* nextEdge = firstEdge + nverts; - int nextEdgeIndex = nverts; - int edgeCount = 0; - - //rcEdge* edges = (rcEdge*)rcAlloc(sizeof(rcEdge)*maxEdgeCount, RC_ALLOC_TEMP); - rcEdge[] edges = new rcEdge[maxEdgeCount]; - rccsArrayItemsCreate(edges); - if (edges == null) { - //rcFree(firstEdge); - firstEdge = null; - return false; - } - - for (int i = 0; i < nverts; i++) { - firstEdge[i] = RC_MESH_NULL_IDX; - } - - for (int i = 0; i < npolys; ++i) { - int tIndex = i * vertsPerPoly * 2; - //ushort* t = &polys[i*vertsPerPoly*2]; - for (int j = 0; j < vertsPerPoly; ++j) { - if (polys[tIndex + j] == RC_MESH_NULL_IDX) break; - ushort v0 = polys[tIndex + j]; - ushort v1 = (j + 1 >= vertsPerPoly || polys[tIndex + j + 1] == RC_MESH_NULL_IDX) ? polys[tIndex + 0] : polys[tIndex + j + 1]; - if (v0 < v1) { - rcEdge edge = edges[edgeCount]; - edge.vert[0] = v0; - edge.vert[1] = v1; - edge.poly[0] = (ushort)i; - edge.polyEdge[0] = (ushort)j; - edge.poly[1] = (ushort)i; - edge.polyEdge[1] = 0; - // Insert edge - firstEdge[nextEdgeIndex + edgeCount] = firstEdge[v0]; - firstEdge[v0] = (ushort)edgeCount; - edgeCount++; - } - } - } - - for (int i = 0; i < npolys; ++i) { - //ushort* t = &polys[i*vertsPerPoly*2]; - int tIndex = i * vertsPerPoly * 2; - for (int j = 0; j < vertsPerPoly; ++j) { - if (polys[tIndex + j] == RC_MESH_NULL_IDX) break; - ushort v0 = polys[tIndex + j]; - ushort v1 = (j + 1 >= vertsPerPoly || polys[tIndex + j + 1] == RC_MESH_NULL_IDX) ? polys[tIndex + 0] : polys[tIndex + j + 1]; - if (v0 > v1) { - for (ushort e = firstEdge[v1]; e != RC_MESH_NULL_IDX; e = firstEdge[nextEdgeIndex + e]) { - rcEdge edge = edges[e]; - if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1]) { - edge.poly[1] = (ushort)i; - edge.polyEdge[1] = (ushort)j; - break; - } - } - } - } - } - - // Store adjacency - for (int i = 0; i < edgeCount; ++i) { - rcEdge e = edges[i]; - if (e.poly[0] != e.poly[1]) { - //ushort* p0 = &polys[e.poly[0]*vertsPerPoly*2]; - //ushort* p1 = &polys[e.poly[1]*vertsPerPoly*2]; - //p0[vertsPerPoly + e.polyEdge[0]] = e.poly[1]; - //p1[vertsPerPoly + e.polyEdge[1]] = e.poly[0]; - polys[e.poly[0] * vertsPerPoly * 2 + vertsPerPoly + e.polyEdge[0]] = e.poly[1]; - polys[e.poly[1] * vertsPerPoly * 2 + vertsPerPoly + e.polyEdge[1]] = e.poly[0]; - } - } - - //rcFree(firstEdge); - //rcFree(edges); - - return true; - } - - - const int VERTEX_BUCKET_COUNT = (1 << 12); - - public static int computeVertexHash(int x, int y, int z) { - uint h1 = 0x8da6b343; // Large multiplicative constants; - uint h2 = 0xd8163841; // here arbitrarily chosen primes - uint h3 = 0xcb1ab31f; - uint n = (uint)(h1 * x + h2 * y + h3 * z); - return (int)(n & (VERTEX_BUCKET_COUNT - 1)); - } - - public static ushort addVertex(ushort x, ushort y, ushort z, - ushort[] verts, int[] firstVert, int[] nextVert, ref int nv) { - int bucket = computeVertexHash(x, 0, z); - int i = firstVert[bucket]; - - while (i != -1) { - //const ushort* v = &verts[i*3]; - int vIndex = i * 3; - if (verts[vIndex] == x && (Math.Abs(verts[vIndex + 1] - y) <= 2) && verts[vIndex + 2] == z) { - return (ushort)i; - } - i = nextVert[i]; // next - } - - // Could not find, create new. - i = nv; nv++; - //ushort[] v = &verts[i*3]; - int vInd = i * 3; - verts[vInd] = x; - verts[vInd + 1] = y; - verts[vInd + 2] = z; - nextVert[i] = firstVert[bucket]; - firstVert[bucket] = i; - - return (ushort)i; - } - - public static int prev(int i, int n) { - return i - 1 >= 0 ? i - 1 : n - 1; - } - public static int next(int i, int n) { - return i + 1 < n ? i + 1 : 0; - } - - public static int area2(int[] a, int[] b, int[] c) { - return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]); - } - public static int area2(int[] a, int aStart, int[] b, int bStart, int[] c, int cStart) { - return (b[bStart + 0] - a[aStart + 0]) * (c[cStart + 2] - a[aStart + 2]) - (c[cStart + 0] - a[aStart + 0]) * (b[bStart + 2] - a[aStart + 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.) - public static 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. - public static bool left(int[] a, int[] b, int[] c) { - return area2(a, b, c) < 0; - } - public static bool left(int[] a, int aStart, int[] b, int bStart, int[] c, int cStart) { - return area2(a, aStart, b, bStart, c, cStart) < 0; - } - - public static bool leftOn(int[] a, int[] b, int[] c) { - return area2(a, b, c) <= 0; - } - public static bool leftOn(int[] a, int aStart, int[] b, int bStart, int[] c, int cStart) { - return area2(a, aStart, b, bStart, c, cStart) <= 0; - } - - public static bool collinear(int[] a, int[] b, int[] c) { - return area2(a, b, c) == 0; - } - public static bool collinear(int[] a, int aStart, int[] b, int bStart, int[] c, int cStart) { - return area2(a, aStart, b, bStart, c, cStart) == 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. - public static bool intersectProp(int[] a, int[] b, int[] c, 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)); - } - public static bool intersectProp(int[] a, int aStart, int[] b, int bStart, int[] c, int cStart, int[] d, int dStart) { - // Eliminate improper cases. - if (collinear(a, aStart, b, bStart, c, cStart) || collinear(a, aStart, b, bStart, d, dStart) || - collinear(c, cStart, d, dStart, a, aStart) || collinear(c, cStart, d, dStart, b, bStart)) - return false; - - return xorb(left(a, aStart, b, bStart, c, cStart), left(a, aStart, b, bStart, d, dStart)) && xorb(left(c, cStart, d, dStart, a, aStart), left(c, cStart, d, dStart, b, bStart)); - } - - // Returns T iff (a,b,c) are collinear and point c lies - // on the closed segement ab. - public static bool between(int[] a, int[] b, 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])); - } - public static bool between(int[] a, int aStart, int[] b, int bStart, int[] c, int cStart) { - if (!collinear(a, aStart, b, bStart, c, cStart)) - return false; - // If ab not vertical, check betweenness on x; else on y. - if (a[aStart+0] != b[bStart+0]) - return ((a[aStart+0] <= c[cStart+0]) && (c[cStart+0] <= b[bStart+0])) || ((a[aStart+0] >= c[cStart+0]) && (c[cStart+0] >= b[bStart+0])); - else - return ((a[aStart+2] <= c[cStart+2]) && (c[cStart+2] <= b[bStart+2])) || ((a[aStart+2] >= c[cStart+2]) && (c[cStart+2] >= b[bStart+2])); - } - - - // Returns true iff segments ab and cd intersect, properly or improperly. - public static bool intersect(int[] a, int[] b, int[] c, 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; - } - public static bool intersect(int[] a, int aStart, int[] b, int bStart, int[] c, int cStart, int[] d, int dStart) { - if (intersectProp(a, aStart, b, bStart, c, cStart, d, dStart)) - return true; - else if (between(a, aStart, b, bStart, c, cStart) || between(a, aStart, b, bStart, d, dStart) || - between(c, cStart, d, dStart, a, aStart) || between(c, cStart, d, dStart, b, bStart)) - return true; - else - return false; - } - - public static bool vequal(int[] a, int[] b) { - return a[0] == b[0] && a[2] == b[2]; - } - public static bool vequal(int[] a, int aStart, int[] b, int bStart) { - return a[aStart + 0] == b[bStart + 0] && a[aStart + 2] == b[bStart + 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*. - public static bool diagonalie(int i, int j, int n, int[] verts, int[] indices) { - //int* d0 = &verts[(indices[i] & 0x0fffffff) * 4]; - //int* d1 = &verts[(indices[j] & 0x0fffffff) * 4]; - int d0Start = (indices[i] & 0x0fffffff) * 4; - int d1Start = (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))) { - int p0Start = (indices[k] & 0x0fffffff) * 4; - int p1Start = (indices[k1] & 0x0fffffff) * 4; - - if (vequal(verts, d0Start, verts, p0Start) || vequal(verts,d1Start, verts, p0Start) || vequal(verts, d0Start, verts, p1Start) || vequal(verts, d1Start, verts, p1Start)) - continue; - - if (intersect(verts, d0Start,verts, d1Start,verts, p0Start, verts, p1Start)) - 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. - public static bool inCone(int i, int j, int n, int[] verts, int[] indices) { - int piStart = (indices[i] & 0x0fffffff) * 4; - int pjStart = (indices[j] & 0x0fffffff) * 4; - int pi1Start = (indices[next(i, n)] & 0x0fffffff) * 4; - int pin1Start = (indices[prev(i, n)] & 0x0fffffff) * 4; - - // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ]. - if (leftOn(verts, pin1Start,verts, piStart,verts, pi1Start)) - return left(verts,piStart,verts, pjStart,verts, pin1Start) && left(verts,pjStart,verts, piStart,verts, pi1Start); - // Assume (i-1,i,i+1) not collinear. - // else P[i] is reflex. - return !(leftOn(verts,piStart,verts, pjStart,verts, pi1Start) && leftOn(verts, pjStart,verts, piStart, verts, pin1Start)); - } - - // Returns T iff (v_i, v_j) is a proper internal - // diagonal of P. - public static bool diagonal(int i, int j, int n, int[] verts, int[] indices) { - return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices); - } - - public static int triangulate(int n, int[] verts, int[] indices, int[] tris) { - int ntris = 0; - //int* dst = tris; - //int[] dst = tris; - int dstIndex = 0; - - int removeVertexFlag = 0; - unchecked { - removeVertexFlag = (int)0x80000000; - } - - // 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)) { - unchecked { - indices[_i1] |= removeVertexFlag; - } - } - } - - while (n > 3) { - int minLen = -1; - int mini = -1; - for (int i = 0; i < n; i++) { - int _i1 = next(i, n); - if ((indices[_i1] & removeVertexFlag) != 0) { - int p0Start = (indices[i] & 0x0fffffff) * 4; - int p2Start = (indices[next(_i1, n)] & 0x0fffffff) * 4; - int dx = verts[p2Start+0] - verts[p0Start+0]; - int dy = verts[p2Start+2] - verts[p0Start+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 i0 = mini; - int i1 = next(i0, n); - int i2 = next(i1, n); - - tris[dstIndex] = indices[i0] & 0x0fffffff; - ++dstIndex; - tris[dstIndex] = indices[i1] & 0x0fffffff; - ++dstIndex; - tris[dstIndex] = indices[i2] & 0x0fffffff; - ++dstIndex; - - 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; - i0 = prev(i1, n); - // Update diagonal flags. - if (diagonal(prev(i0, n), i1, n, verts, indices)) - indices[i0] |= removeVertexFlag; - else - indices[i0] &= 0x0fffffff; - - if (diagonal(i0, next(i1, n), n, verts, indices)) - indices[i1] |= removeVertexFlag; - else - indices[i1] &= 0x0fffffff; - } - - // Append the remaining triangle. - tris[dstIndex] = indices[0] & 0x0fffffff; - ++dstIndex; - tris[dstIndex] = indices[1] & 0x0fffffff; - ++dstIndex; - tris[dstIndex] = indices[2] & 0x0fffffff; - ++dstIndex; - ntris++; - - return ntris; - } - - public static int countPolyVerts(ushort[] p, int pStart, int nvp) { - for (int i = 0; i < nvp; ++i) { - if (p[pStart + i] == RC_MESH_NULL_IDX) { - return i; - } - } - return nvp; - } - - public static bool uleft(ushort[] a, ushort[] b, ushort[] 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; - } - - public static bool uleft(ushort[] a, int aStart, ushort[] b, int bStart, ushort[] c, int cStart) { - return ((int)b[bStart + 0] - (int)a[aStart + 0]) * ((int)c[cStart + 2] - (int)a[aStart + 2]) - - ((int)c[cStart + 0] - (int)a[aStart + 0]) * ((int)b[bStart + 2] - (int)a[aStart + 2]) < 0; - } - - public static int getPolyMergeValue(ushort[] pa, int paStart, ushort[] pb, int pbStart, - ushort[] verts, ref int ea, ref int eb, - int nvp) { - int na = countPolyVerts(pa, paStart, nvp); - int nb = countPolyVerts(pb, pbStart, 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) { - ushort va0 = pa[paStart + i]; - ushort va1 = pa[paStart + ((i + 1) % na)]; - if (va0 > va1) { - rcSwap(ref va0, ref va1); - } - for (int j = 0; j < nb; ++j) { - ushort vb0 = pb[pbStart + j]; - ushort vb1 = pb[pbStart + ((j + 1) % nb)]; - if (vb0 > vb1) - rcSwap(ref vb0, ref 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. - ushort va, vb, vc; - - va = pa[paStart + ((ea + na - 1) % na)]; - vb = pa[paStart + ea]; - vc = pb[pbStart + ((eb + 2) % nb)]; - if (!uleft(verts, va * 3, verts, vb * 3, verts, vc * 3)) - return -1; - - va = pb[pbStart + ((eb + nb - 1) % nb)]; - vb = pb[pbStart + eb]; - vc = pa[paStart + ((ea + 2) % na)]; - if (!uleft(verts, va * 3, verts, vb * 3, verts, vc * 3)) - return -1; - - va = pa[paStart + ea]; - vb = pa[paStart + ((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; - } - - public static void mergePolys(ushort[] pa, int paStart, ushort[] pb, int pbStart, int ea, int eb, - ushort[] tmp, int tmpStart, int nvp) { - int na = countPolyVerts(pa, paStart, nvp); - int nb = countPolyVerts(pb, pbStart, nvp); - - // Merge polygons. - //memset(tmp, 0xff, sizeof(ushort)*nvp); - for (int i = 0; i < nvp; ++i) { - tmp[tmpStart + i] = 0xffff; - } - int n = 0; - // Add pa - for (int i = 0; i < na - 1; ++i) - tmp[tmpStart + n++] = pa[paStart + ((ea + 1 + i) % na)]; - // Add pb - for (int i = 0; i < nb - 1; ++i) - tmp[tmpStart + n++] = pb[pbStart + ((eb + 1 + i) % nb)]; - - //memcpy(pa, tmp, sizeof(ushort)*nvp); - for (int i = 0; i < nvp; ++i) { - pa[paStart + i] = tmp[tmpStart + i]; - } - } - - - public static void pushFront(int v, int[] arr, ref int an) { - an++; - for (int i = an - 1; i > 0; --i) { - arr[i] = arr[i - 1]; - } - arr[0] = v; - } - - public static void pushBack(int v, int[] arr, ref int an) { - arr[an] = v; - an++; - } - - public static bool canRemoveVertex(rcContext ctx, rcPolyMesh mesh, ushort rem) { - 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) { - //ushort* p = &mesh.polys[i*nvp*2]; - int pIndex = i * nvp * 2; - int nv = countPolyVerts(mesh.polys, i * nvp * 2, nvp); - int numRemoved = 0; - int numVerts = 0; - for (int j = 0; j < nv; ++j) { - if (mesh.polys[pIndex + j] == rem) { - numTouchedVerts++; - numRemoved++; - } - numVerts++; - } - if (numRemoved != 0) { - 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. - int maxEdges = numTouchedVerts * 2; - int nedges = 0; - //rcScopedDelete edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP); - int[] edges = new int[maxEdges * 3]; - if (edges == null) { - ctx.log(rcLogCategory.RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' " + maxEdges * 3); - return false; - } - - for (int i = 0; i < mesh.npolys; ++i) { - //ushort* p = &mesh.polys[i*nvp*2]; - int pIndex = i * nvp * 2; - int nv = countPolyVerts(mesh.polys, pIndex, nvp); - - // Collect edges which touches the removed vertex. - for (int j = 0, k = nv - 1; j < nv; k = j++) { - if (mesh.polys[pIndex + j] == rem || mesh.polys[pIndex + k] == rem) { - // Arrange edge so that a=rem. - int a = mesh.polys[pIndex + j], b = mesh.polys[pIndex + k]; - if (b == rem) { - rcSwap(ref a, ref b); - } - - // Check if the edge exists - bool exists = false; - for (int m = 0; m < nedges; ++m) { - //int* e = &edges[m*3]; - int eIndex = m * 3; - if (edges[eIndex + 1] == b) { - // Exists, increment vertex share count. - edges[eIndex + 2]++; - exists = true; - } - } - // Add new edge. - if (!exists) { - //int* e = &edges[nedges*3]; - int eIndex = nedges * 3; - edges[eIndex + 0] = a; - edges[eIndex + 1] = b; - edges[eIndex + 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; - } - - public static bool removeVertex(rcContext ctx, rcPolyMesh mesh, ushort rem, int maxTris) { - int nvp = mesh.nvp; - - // Count number of polygons to remove. - int numRemovedVerts = 0; - for (int i = 0; i < mesh.npolys; ++i) { - //ushort* p = &mesh.polys[i*nvp*2]; - int pIndex = i * nvp * 2; - int nv = countPolyVerts(mesh.polys, pIndex, nvp); - for (int j = 0; j < nv; ++j) { - if (mesh.polys[pIndex + j] == rem) - numRemovedVerts++; - } - } - - int nedges = 0; - //rcScopedDelete edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP); - int[] edges = new int[numRemovedVerts * nvp * 4]; - if (edges == null) { - ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'edges' " + numRemovedVerts * nvp * 4); - return false; - } - - int nhole = 0; - //rcScopedDelete hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); - int[] hole = new int[numRemovedVerts * nvp]; - if (hole == null) { - ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'hole' " + numRemovedVerts * nvp); - return false; - } - - int nhreg = 0; - //rcScopedDelete hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); - int[] hreg = new int[numRemovedVerts * nvp]; - if (hreg == null) { - ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' " + numRemovedVerts * nvp); - return false; - } - - int nharea = 0; - //rcScopedDelete harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); - int[] harea = new int[numRemovedVerts * nvp]; - if (harea == null) { - ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'harea' " + numRemovedVerts * nvp); - return false; - } - - for (int i = 0; i < mesh.npolys; ++i) { - //ushort* p = &mesh.polys[i*nvp*2]; - int pIndex = i * nvp * 2; - int nv = countPolyVerts(mesh.polys, pIndex, nvp); - bool hasRem = false; - for (int j = 0; j < nv; ++j) - if (mesh.polys[pIndex + 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 (mesh.polys[pIndex + j] != rem && mesh.polys[pIndex + k] != rem) { - //int[] e = &edges[nedges*4]; - int eIndex = nedges * 4; - edges[eIndex + 0] = mesh.polys[pIndex + k]; - edges[eIndex + 1] = mesh.polys[pIndex + j]; - edges[eIndex + 2] = mesh.regs[i]; - edges[eIndex + 3] = mesh.areas[i]; - nedges++; - } - } - // Remove the polygon. - //ushort* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2]; - int p2Index = (mesh.npolys - 1) * nvp * 2; - if (mesh.polys[pIndex] != mesh.polys[p2Index]) { - //memcpy(p,p2,sizeof(ushort)*nvp); - for (int j = 0; j < nvp; ++j) { - mesh.polys[pIndex + j] = mesh.polys[p2Index + j]; - } - } - //memset(p+nvp,0xff,sizeof(ushort)*nvp); - for (int j = 0; j < nvp; ++j) { - mesh.polys[pIndex + nvp + j] = 0xffff; - } - 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) { - //ushort* p = &mesh.polys[i*nvp*2]; - int pIndex = i * nvp * 2; - int nv = countPolyVerts(mesh.polys, i * nvp * 2, nvp); - for (int j = 0; j < nv; ++j) { - if (mesh.polys[pIndex + j] > rem) { - mesh.polys[pIndex + 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, ref nhole); - pushBack(edges[2], hreg, ref nhreg); - pushBack(edges[3], harea, ref nharea); - - while (nedges != 0) { - bool match = false; - - for (int i = 0; i < nedges; ++i) { - int ea = edges[i * 4 + 0]; - int eb = edges[i * 4 + 1]; - int r = edges[i * 4 + 2]; - 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, ref nhole); - pushFront(r, hreg, ref nhreg); - pushFront(a, harea, ref nharea); - add = true; - } else if (hole[nhole - 1] == ea) { - // The segment matches the end of the hole boundary. - pushBack(eb, hole, ref nhole); - pushBack(r, hreg, ref nhreg); - pushBack(a, harea, ref 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 tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP); - int[] tris = new int[nhole * 3]; - if (tris == null) { - ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'tris' " + nhole * 3); - return false; - } - - //rcScopedDelete tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP); - int[] tverts = new int[nhole * 4]; - if (tverts == null) { - ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' " + nhole * 4); - return false; - } - - //rcScopedDelete thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP); - int[] thole = new int[nhole]; - if (tverts == null) { - ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'thole' " + nhole); - return false; - } - - // Generate temp vertex array for triangulation. - for (int i = 0; i < nhole; ++i) { - 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, thole, tris); - if (ntris < 0) { - ntris = -ntris; - ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: triangulate() returned bad results."); - } - - // Merge the hole triangles back to polygons. - //rcScopedDelete polys = (ushort*)rcAlloc(sizeof(ushort)*(ntris+1)*nvp, RC_ALLOC_TEMP); - ushort[] polys = new ushort[(ntris + 1) * nvp]; - if (polys == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "removeVertex: Out of memory 'polys' " + (ntris + 1) * nvp); - return false; - } - //rcScopedDelete pregs = (ushort*)rcAlloc(sizeof(ushort)*ntris, RC_ALLOC_TEMP); - ushort[] pregs = new ushort[ntris]; - if (pregs == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' " + ntris); - return false; - } - //rcScopedDelete pareas = (byte*)rcAlloc(sizeof(byte)*ntris, RC_ALLOC_TEMP); - byte[] pareas = new byte[ntris]; - if (pregs == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' " + ntris); - return false; - } - - int tmpPolyIndex = ntris * nvp; - //ushort* tmpPoly = &polys[ntris*nvp]; - - // Build initial polygons. - int npolys = 0; - //memset(polys, 0xff, ntris*nvp*sizeof(ushort)); - for (int i = 0; i < ntris * nvp; ++i) { - polys[i] = 0xffff; - } - for (int j = 0; j < ntris; ++j) { - //int* t = &tris[j*3]; - int tIndex = j * 3; - if (tris[tIndex + 0] != tris[tIndex + 1] && tris[tIndex + 0] != tris[tIndex + 2] && tris[tIndex + 1] != tris[tIndex + 2]) { - polys[npolys * nvp + 0] = (ushort)hole[tris[tIndex + 0]]; - polys[npolys * nvp + 1] = (ushort)hole[tris[tIndex + 1]]; - polys[npolys * nvp + 2] = (ushort)hole[tris[tIndex + 2]]; - pregs[npolys] = (ushort)hreg[tris[tIndex + 0]]; - pareas[npolys] = (byte)harea[tris[tIndex + 0]]; - npolys++; - } - } - if (npolys == 0) { - 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) { - int pjIndex = j * nvp; - //ushort* pj = &polys[j*nvp]; - for (int k = j + 1; k < npolys; ++k) { - int pkIndex = k * nvp; - //ushort* pk = &polys[k*nvp]; - int ea = 0; - int eb = 0; - int v = getPolyMergeValue(polys, pjIndex, polys, pkIndex, mesh.verts, ref ea, ref eb, nvp); - if (v > bestMergeVal) { - bestMergeVal = v; - bestPa = j; - bestPb = k; - bestEa = ea; - bestEb = eb; - } - } - } - - if (bestMergeVal > 0) { - // Found best, merge. - - //ushort* pa = &polys[bestPa*nvp]; - //ushort* pb = &polys[bestPb*nvp]; - int paIndex = bestPa * nvp; - int pbIndex = bestPb * nvp; - mergePolys(polys, paIndex, polys, pbIndex, bestEa, bestEb, polys, tmpPolyIndex, nvp); - //ushort* last = &polys[(npolys-1)*nvp]; - int lastIndex = (npolys - 1) * nvp; - if (polys[pbIndex] != polys[lastIndex]) { - //memcpy(pb, last, sizeof(ushort)*nvp); - for (int j = 0; j < nvp; ++j) { - polys[pbIndex + j] = polys[lastIndex + j]; - } - } - 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; - //ushort* p = &mesh.polys[mesh.npolys*nvp*2]; - int pIndex = mesh.npolys * nvp * 2; - for (int j = 0; j < nvp * 2; ++j) { - polys[pIndex + j] = 0xffff; - } - //memset(p,0xff,sizeof(ushort)*nvp*2); - for (int j = 0; j < nvp; ++j) { - polys[pIndex + 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(rcLogCategory.RC_LOG_ERROR, "removeVertex: Too many polygons " + mesh.npolys + " (max:" + maxTris + ")"); - return false; - } - } - - return true; - } - - /// @par - /// - /// @note If the mesh data is to be used to construct a Detour navigation mesh, then the upper - /// limit must be retricted to <= #DT_VERTS_PER_POLYGON. - /// - /// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig - public static bool rcBuildPolyMesh(rcContext ctx, rcContourSet cset, int nvp, rcPolyMesh mesh) { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_POLYMESH); - - rcVcopy(mesh.bmin, cset.bmin); - rcVcopy(mesh.bmax, cset.bmax); - mesh.cs = cset.cs; - mesh.ch = cset.ch; - mesh.borderSize = cset.borderSize; - - 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 = Math.Max(maxVertsPerCont, cset.conts[i].nverts); - } - - if (maxVertices >= 0xfffe) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices " + maxVertices); - return false; - } - - //rcScopedDelete vflags = (byte*)rcAlloc(sizeof(byte)*maxVertices, RC_ALLOC_TEMP); - byte[] vflags = new byte[maxVertices]; - if (vflags == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'vflags' " + maxVertices); - return false; - } - //memset(vflags, 0, maxVertices); - - //mesh.verts = (ushort*)rcAlloc(sizeof(ushort)*maxVertices*3, RC_ALLOC_PERM); - mesh.verts = new ushort[maxVertices * 3]; - if (mesh.verts == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' " + maxVertices); - return false; - } - //mesh.polys = (ushort*)rcAlloc(sizeof(ushort)*maxTris*nvp*2, RC_ALLOC_PERM); - mesh.polys = new ushort[maxTris * nvp * 2]; - if (mesh.polys == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' " + maxTris * nvp * 2); - return false; - } - //mesh.regs = (ushort*)rcAlloc(sizeof(ushort)*maxTris, RC_ALLOC_PERM); - mesh.regs = new ushort[maxTris]; - if (mesh.regs == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' " + maxTris); - return false; - } - //mesh.areas = (byte*)rcAlloc(sizeof(byte)*maxTris, RC_ALLOC_PERM); - mesh.areas = new byte[maxTris]; - if (mesh.areas == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' " + maxTris); - return false; - } - - mesh.nverts = 0; - mesh.npolys = 0; - mesh.nvp = nvp; - mesh.maxpolys = maxTris; - - //memset(mesh.verts, 0, sizeof(ushort)*maxVertices*3); - //memset(mesh.polys, 0xff, sizeof(ushort)*maxTris*nvp*2); - for (int i = 0; i < maxTris * nvp * 2; ++i) { - mesh.polys[i] = 0xffff; - } - //memset(mesh.regs, 0, sizeof(ushort)*maxTris); - //memset(mesh.areas, 0, sizeof(byte)*maxTris); - - //rcScopedDelete nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP); - int[] nextVert = new int[maxVertices]; - if (nextVert == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' " + maxVertices); - return false; - } - //memset(nextVert, 0, sizeof(int)*maxVertices); - - //rcScopedDelete firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); - int[] firstVert = new int[VERTEX_BUCKET_COUNT]; - if (firstVert == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' " + VERTEX_BUCKET_COUNT); - return false; - } - for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) - firstVert[i] = -1; - - //rcScopedDelete indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP); - int[] indices = new int[maxVertsPerCont]; - if (indices == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' " + maxVertsPerCont); - return false; - } - //rcScopedDelete tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP); - int[] tris = new int[maxVertsPerCont * 3]; - if (tris == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' " + maxVertsPerCont * 3); - return false; - } - //rcScopedDelete polys = (ushort*)rcAlloc(sizeof(ushort)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP); - ushort[] polys = new ushort[(maxVertsPerCont + 1) * nvp]; - if (polys == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' " + (maxVertsPerCont + 1) * nvp); - return false; - } - int tmpPolyIndex = maxVertsPerCont * nvp; - //ushort[] 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, tris); - 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(rcLogCategory.RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour " + i); - ntris = -ntris; - } - - // Add and merge vertices. - for (int j = 0; j < cont.nverts; ++j) { - int vIndex = j * 4; - //const int* v = &cont.verts[j*4]; - indices[j] = addVertex((ushort)cont.verts[vIndex + 0], (ushort)cont.verts[vIndex + 1], (ushort)cont.verts[vIndex + 2], - mesh.verts, firstVert, nextVert, ref mesh.nverts); - if ((cont.verts[vIndex + 3] & RC_BORDER_VERTEX) != 0) { - // This vertex should be removed. - vflags[indices[j]] = 1; - } - } - - // Build initial polygons. - int npolys = 0; - //memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(ushort)); - for (int j = 0; j < nvp * maxVertsPerCont; ++j) { - polys[j] = 0xffff; - } - for (int j = 0; j < ntris; ++j) { - int tIndex = j * 3; - //int* t = &tris[j*3]; - if (tris[tIndex + 0] != tris[tIndex + 1] && tris[tIndex + 0] != tris[tIndex + 2] && tris[tIndex + 1] != tris[tIndex + 2]) { - polys[npolys * nvp + 0] = (ushort)indices[tris[tIndex + 0]]; - polys[npolys * nvp + 1] = (ushort)indices[tris[tIndex + 1]]; - polys[npolys * nvp + 2] = (ushort)indices[tris[tIndex + 2]]; - npolys++; - } - } - if (npolys == 0) { - 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) { - int pjIndex = j * nvp; - //ushort* pj = &polys[j*nvp]; - for (int k = j + 1; k < npolys; ++k) { - //ushort* pk = &polys[k*nvp]; - int pkIndex = k * nvp; - int ea = 0, eb = 0; - int v = getPolyMergeValue(polys, pjIndex, polys, pkIndex, mesh.verts, ref ea, ref eb, nvp); - if (v > bestMergeVal) { - bestMergeVal = v; - bestPa = j; - bestPb = k; - bestEa = ea; - bestEb = eb; - } - } - } - - if (bestMergeVal > 0) { - // Found best, merge. - //ushort* pa = &polys[bestPa*nvp]; - //ushort* pb = &polys[bestPb*nvp]; - int paIndex = bestPa * nvp; - int pbIndex = bestPb * nvp; - mergePolys(polys, paIndex, polys, pbIndex, bestEa, bestEb, polys, tmpPolyIndex, nvp); - //ushort* lastPoly = &polys[(npolys-1)*nvp]; - int lastPolyIndex = (npolys - 1) * nvp; - if (pbIndex != lastPolyIndex) { - //memcpy(pb, lastPoly, sizeof(ushort)*nvp); - for (int j = 0; j < nvp; ++j) { - polys[pbIndex + j] = polys[lastPolyIndex + j]; - } - } - npolys--; - } else { - // Could not merge any polygons, stop. - break; - } - } - } - - // Store polygons. - for (int j = 0; j < npolys; ++j) { - //ushort* p = &mesh.polys[mesh.npolys*nvp*2]; - //ushort* q = &polys[j*nvp]; - int pIndex = mesh.npolys * nvp * 2; - int qIndex = j * nvp; - for (int k = 0; k < nvp; ++k) { - mesh.polys[pIndex + k] = polys[qIndex + k]; - } - mesh.regs[mesh.npolys] = cont.reg; - mesh.areas[mesh.npolys] = cont.area; - mesh.npolys++; - if (mesh.npolys > maxTris) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons " + mesh.npolys + " max " + maxTris); - return false; - } - } - } - - - // Remove edge vertices. - for (int i = 0; i < mesh.nverts; ++i) { - if (vflags[i] != 0) { - if (!canRemoveVertex(ctx, mesh, (ushort)i)) { - continue; - } - if (!removeVertex(ctx, mesh, (ushort)i, maxTris)) { - // Failed to remove vertex - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex " + i); - return false; - } - // Remove vertex - // Note: mesh.nverts is already decremented inside removeVertex()! - // Fixup vertex flags - 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(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed."); - return false; - } - - // Find portal edges - if (mesh.borderSize > 0) { - int w = cset.width; - int h = cset.height; - for (int i = 0; i < mesh.npolys; ++i) { - int pIndex = i * 2 * nvp; - //ushort* p = &mesh.polys[i*2*nvp]; - for (int j = 0; j < nvp; ++j) { - if (mesh.polys[pIndex + j] == RC_MESH_NULL_IDX) { - break; - } - // Skip connected edges. - if (mesh.polys[pIndex + nvp + j] != RC_MESH_NULL_IDX) { - continue; - } - int nj = j + 1; - if (nj >= nvp || mesh.polys[pIndex + nj] == RC_MESH_NULL_IDX) nj = 0; - //ushort* va = &mesh.verts[mesh.polys[pIndex + j]*3]; - //ushort* vb = &mesh.verts[mesh.polys[pIndex + nj]*3]; - int vaIndex = mesh.polys[pIndex + j] * 3; - int vbIndex = mesh.polys[pIndex + nj] * 3; - - if ((int)mesh.verts[vaIndex + 0] == 0 && (int)mesh.verts[vbIndex + 0] == 0) - mesh.polys[pIndex + nvp + j] = 0x8000 | 0; - else if ((int)mesh.verts[vaIndex + 2] == h && (int)mesh.verts[vbIndex + 2] == h) - mesh.polys[pIndex + nvp + j] = 0x8000 | 1; - else if ((int)mesh.verts[vaIndex + 0] == w && (int)mesh.verts[vbIndex + 0] == w) - mesh.polys[pIndex + nvp + j] = 0x8000 | 2; - else if ((int)mesh.verts[vaIndex + 2] == 0 && (int)mesh.verts[vbIndex + 2] == 0) - mesh.polys[pIndex + nvp + j] = 0x8000 | 3; - } - } - } - - // Just allocate the mesh flags array. The user is resposible to fill it. - //mesh.flags = (ushort*)rcAlloc(sizeof(ushort)*mesh.npolys, RC_ALLOC_PERM); - mesh.flags = new ushort[mesh.npolys]; - if (mesh.flags == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' " + mesh.npolys); - return false; - } - //memset(mesh.flags, 0, sizeof(ushort) * mesh.npolys); - - if (mesh.nverts > 0xffff) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many vertices " + mesh.nverts + "(max " + 0xffff + ") Data can be corrupted."); - } - if (mesh.npolys > 0xffff) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many polygons " + mesh.npolys + " (max " + 0xffff + "). Data can be corrupted."); - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_POLYMESH); - - return true; - } - - /// @see rcAllocPolyMesh, rcPolyMesh - public static bool rcMergePolyMeshes(rcContext ctx, ref rcPolyMesh[] meshes, int nmeshes, rcPolyMesh mesh) { - Debug.Assert(ctx != null, "rcContext is null"); - - if (nmeshes == 0 || meshes == null) - return true; - - ctx.startTimer(rcTimerLabel.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 = Math.Max(maxVertsPerMesh, meshes[i].nverts); - maxVerts += meshes[i].nverts; - maxPolys += meshes[i].npolys; - } - - mesh.nverts = 0; - //mesh.verts = (ushort*)rcAlloc(sizeof(ushort)*maxVerts*3, RC_ALLOC_PERM); - mesh.verts = new ushort[maxVerts * 3]; - if (mesh.verts == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' " + maxVerts * 3); - return false; - } - - mesh.npolys = 0; - //mesh.polys = (ushort*)rcAlloc(sizeof(ushort)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM); - mesh.polys = new ushort[maxPolys * 2 * mesh.nvp]; - if (mesh.polys == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' " + maxPolys * 2 * mesh.nvp); - return false; - } - //memset(mesh.polys, 0xff, sizeof(ushort)*maxPolys*2*mesh.nvp); - for (int i = 0; i < maxPolys * 2 * mesh.nvp; ++i) { - mesh.polys[i] = 0xffff; - } - - //mesh.regs = (ushort*)rcAlloc(sizeof(ushort)*maxPolys, RC_ALLOC_PERM); - mesh.regs = new ushort[maxPolys]; - if (mesh.regs == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' " + maxPolys); - return false; - } - //memset(mesh.regs, 0, sizeof(ushort)*maxPolys); - - //mesh.areas = (byte*)rcAlloc(sizeof(byte)*maxPolys, RC_ALLOC_PERM); - mesh.areas = new byte[maxPolys]; - if (mesh.areas == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' " + maxPolys); - return false; - } - //memset(mesh.areas, 0, sizeof(byte)*maxPolys); - - //mesh.flags = (ushort*)rcAlloc(sizeof(ushort)*maxPolys, RC_ALLOC_PERM); - mesh.flags = new ushort[maxPolys]; - if (mesh.flags == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' " + maxPolys); - return false; - } - //memset(mesh.flags, 0, sizeof(ushort)*maxPolys); - - //rcScopedDelete nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP); - int[] nextVert = new int[maxVerts]; - if (nextVert == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' " + maxVerts); - return false; - } - //memset(nextVert, 0, sizeof(int)*maxVerts); - - //rcScopedDelete firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); - int[] firstVert = new int[VERTEX_BUCKET_COUNT]; - if (firstVert == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' " + VERTEX_BUCKET_COUNT); - return false; - } - for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) { - firstVert[i] = -1; - } - - //rcScopedDelete vremap = (ushort*)rcAlloc(sizeof(ushort)*maxVertsPerMesh, RC_ALLOC_PERM); - ushort[] vremap = new ushort[maxVertsPerMesh]; - if (vremap == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' " + maxVertsPerMesh); - return false; - } - //memset(vremap, 0, sizeof(ushort)*maxVertsPerMesh); - - for (int i = 0; i < nmeshes; ++i) { - rcPolyMesh pmesh = meshes[i]; - - ushort ox = (ushort)Math.Floor((pmesh.bmin[0] - mesh.bmin[0]) / mesh.cs + 0.5f); - ushort oz = (ushort)Math.Floor((pmesh.bmin[2] - mesh.bmin[2]) / mesh.cs + 0.5f); - - bool isMinX = (ox == 0); - bool isMinZ = (oz == 0); - bool isMaxX = ((ushort)Math.Floor((mesh.bmax[0] - pmesh.bmax[0]) / mesh.cs + 0.5f)) == 0; - bool isMaxZ = ((ushort)Math.Floor((mesh.bmax[2] - pmesh.bmax[2]) / mesh.cs + 0.5f)) == 0; - bool isOnBorder = (isMinX || isMinZ || isMaxX || isMaxZ); - - for (int j = 0; j < pmesh.nverts; ++j) { - //ushort* v = &pmesh.verts[j*3]; - int vIndex = j * 3; - vremap[j] = addVertex((ushort)(pmesh.verts[vIndex + 0] + ox), pmesh.verts[vIndex + 1], (ushort)(pmesh.verts[vIndex + 2] + oz), - mesh.verts, firstVert, nextVert, ref mesh.nverts); - } - - for (int j = 0; j < pmesh.npolys; ++j) { - //ushort* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp]; - //ushort* src = &pmesh.polys[j*2*mesh.nvp]; - int tgtIndex = mesh.npolys * 2 * mesh.nvp; - int srcIndex = 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 (pmesh.polys[srcIndex + k] == RC_MESH_NULL_IDX) { - break; - } - mesh.polys[tgtIndex + k] = vremap[pmesh.polys[srcIndex + k]]; - } - - if (isOnBorder) { - for (int k = mesh.nvp; k < mesh.nvp * 2; ++k) { - if ((pmesh.polys[srcIndex + k] & 0x8000) != 0 && (pmesh.polys[srcIndex + k] != 0xffff)) { - ushort dir = (ushort)(pmesh.polys[srcIndex + k] & 0xf); - switch (dir) { - case 0: // Portal x- - if (isMinX) - mesh.polys[tgtIndex + k] = pmesh.polys[srcIndex + k]; - break; - case 1: // Portal z+ - if (isMaxZ) - mesh.polys[tgtIndex + k] = pmesh.polys[srcIndex + k]; - break; - case 2: // Portal x+ - if (isMaxX) - mesh.polys[tgtIndex + k] = pmesh.polys[srcIndex + k]; - break; - case 3: // Portal z- - if (isMinZ) - mesh.polys[tgtIndex + k] = pmesh.polys[srcIndex + k]; - break; - } - } - } - } - } - } - - // Calculate adjacency. - if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp)) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed."); - return false; - } - - if (mesh.nverts > 0xffff) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices " + mesh.nverts + " (max " + 0xffff + "). Data can be corrupted."); - } - if (mesh.npolys > 0xffff) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons " + mesh.npolys + " (max " + 0xffff + "). Data can be corrupted."); - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_MERGE_POLYMESH); - - return true; - } - - public static bool rcCopyPolyMesh(rcContext ctx, rcPolyMesh src, rcPolyMesh dst) { - Debug.Assert(ctx != null, "rcContext is null"); - - // Destination must be empty. - Debug.Assert(dst.verts == null); - Debug.Assert(dst.polys == null); - Debug.Assert(dst.regs == null); - Debug.Assert(dst.areas == null); - Debug.Assert(dst.flags == null); - - dst.nverts = src.nverts; - dst.npolys = src.npolys; - dst.maxpolys = src.npolys; - dst.nvp = src.nvp; - rcVcopy(dst.bmin, src.bmin); - rcVcopy(dst.bmax, src.bmax); - dst.cs = src.cs; - dst.ch = src.ch; - dst.borderSize = src.borderSize; - - //dst.verts = (ushort*)rcAlloc(sizeof(ushort)*src.nverts*3, RC_ALLOC_PERM); - dst.verts = new ushort[src.nverts * 3]; - if (dst.verts == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.verts' (" + src.nverts * 3 + ")."); - return false; - } - //memcpy(dst.verts, src.verts, sizeof(ushort)*src.nverts*3); - for (int i = 0; i < src.nverts * 3; ++i) { - dst.verts[i] = src.verts[i]; - } - - //dst.polys = (ushort*)rcAlloc(sizeof(ushort)*src.npolys*2*src.nvp, RC_ALLOC_PERM); - dst.polys = new ushort[src.npolys * 2 * src.nvp]; - if (dst.polys == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.polys' (" + src.npolys * 2 * src.nvp + ")."); - return false; - } - //memcpy(dst.polys, src.polys, sizeof(ushort)*src.npolys*2*src.nvp); - for (int i = 0; i < src.npolys * 2 * src.nvp; ++i) { - dst.polys[i] = src.polys[i]; - } - - //dst.regs = (ushort*)rcAlloc(sizeof(ushort)*src.npolys, RC_ALLOC_PERM); - dst.regs = new ushort[src.npolys]; - if (dst.regs == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.regs' (" + src.npolys + ")."); - return false; - } - //memcpy(dst.regs, src.regs, sizeof(ushort)*src.npolys); - for (int i = 0; i < src.npolys; ++i) { - dst.regs[i] = src.regs[i]; - } - - //dst.areas = (byte*)rcAlloc(sizeof(byte)*src.npolys, RC_ALLOC_PERM); - dst.areas = new byte[src.npolys]; - if (dst.areas == null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.areas' (" + src.npolys + ")."); - return false; - } - //memcpy(dst.areas, src.areas, sizeof(byte)*src.npolys); - for (int i = 0; i < src.npolys; ++i) { - dst.areas[i] = src.areas[i]; - } - - //dst.flags = (ushort*)rcAlloc(sizeof(ushort)*src.npolys, RC_ALLOC_PERM); - dst.flags = new ushort[src.npolys]; - if (dst.flags != null) { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.flags' (" + src.npolys + ")."); - return false; - } - //memcpy(dst.flags, src.flags, sizeof(byte)*src.npolys); - for (int i = 0; i < src.npolys; ++i) { - dst.flags[i] = src.flags[i]; - } - - return true; - } -} \ No newline at end of file diff --git a/Source/Framework/RecastDetour/Recast/RecastMeshDetail.cs b/Source/Framework/RecastDetour/Recast/RecastMeshDetail.cs deleted file mode 100644 index 9c375dce9..000000000 --- a/Source/Framework/RecastDetour/Recast/RecastMeshDetail.cs +++ /dev/null @@ -1,1650 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; - -public static partial class Recast{ - const ushort RC_UNSET_HEIGHT = 0xffff; - - public class rcHeightPatch - { - public rcHeightPatch(){ - } - - public ushort[] data = null; - public int xmin = 0; - public int ymin = 0; - public int width = 0; - public int height = 0; - }; - - - public static float vdot2(float[] a, float[] b) - { - return a[0]*b[0] + a[2]*b[2]; - } - - public static float vdot2(float[] a, int aStart, float[] b, int bStart) - { - return a[aStart]*b[bStart] + a[aStart +2]*b[bStart + 2]; - } - - public static float vdistSq2(float[] p, float[] q) - { - float dx = q[0] - p[0]; - float dy = q[2] - p[2]; - return dx*dx + dy*dy; - } - - public static float vdistSq2(float[] p, int pStart, float[] q, int qStart) - { - float dx = q[qStart + 0] - p[pStart + 0]; - float dy = q[qStart + 2] - p[pStart + 2]; - return dx*dx + dy*dy; - } - - - public static float vdist2(float[] p, float[] q) - { - return (float)Math.Sqrt(vdistSq2(p,q)); - } - - public static float vdist2(float[] p, int pStart, float[] q, int qStart) - { - return (float)Math.Sqrt(vdistSq2(p,pStart,q,qStart)); - } - - public static float vcross2(float[] p1, float[] p2, float[]p3) - { - float u1 = p2[0] - p1[0]; - float v1 = p2[2] - p1[2]; - float u2 = p3[0] - p1[0]; - float v2 = p3[2] - p1[2]; - return u1 * v2 - v1 * u2; - } - - - public static float vcross2(float[] p1, int p1Start, float[] p2, int p2Start, float[]p3, int p3Start) - { - float u1 = p2[0 + p2Start] - p1[0 + p1Start]; - float v1 = p2[2 + p2Start] - p1[2 + p1Start]; - float u2 = p3[0 + p3Start] - p1[0 + p1Start]; - float v2 = p3[2 + p3Start] - p1[2 + p1Start]; - return u1 * v2 - v1 * u2; - } - - public static bool circumCircle(float[] p1, float[] p2, float[]p3, - float[] c,ref float r) - { - const float EPS = 1e-6f; - - float cp = vcross2(p1, p2, p3); - if (Math.Abs(cp) > EPS) - { - float p1Sq = vdot2(p1,p1); - float p2Sq = vdot2(p2,p2); - 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; - } - - public static bool circumCircle(float[] p1, int p1Start, float[] p2, int p2Start, float[]p3, int p3Start, - float[] c, int cStart ,ref float r) - { - const float EPS = 1e-6f; - - float cp = vcross2(p1, p1Start, p2, p2Start, p3, p3Start); - if (Math.Abs(cp) > EPS) - { - float p1Sq = vdot2(p1,p1Start,p1, p1Start); - float p2Sq = vdot2(p2, p2Start,p2, p2Start); - float p3Sq = vdot2(p3,p3Start,p3,p3Start); - c[cStart+ 0] = (p1Sq * (p2[p2Start + 2] - p3[p3Start + 2]) + p2Sq * (p3[p3Start + 2] - p1[p1Start + 2]) + p3Sq * (p1[p1Start + 2] - p2[p2Start + 2])) / (2 * cp); - c[cStart+ 2] = (p1Sq * (p3[p3Start + 0] - p2[p2Start + 0]) + p2Sq * (p1[p1Start + 0] - p3[p3Start + 0]) + p3Sq * (p2[p2Start + 0] - p1[p1Start + 0])) / (2 * cp); - r = vdist2(c, cStart, p1, p1Start); - return true; - } - - c[cStart + 0] = p1[p1Start + 0]; - c[cStart + 2] = p1[p1Start + 2]; - r = 0; - return false; - } - - static float distPtTri(float[] p, float[]a, float[] b, float[]c) - { - float[] v0 = new float[3]; - float[] v1 = new float[3]; - float[] v2 = new float[3]; - rcVsub(v0, c,a); - rcVsub(v1, b,a); - rcVsub(v2, p,a); - - float dot00 = vdot2(v0, v0); - float dot01 = vdot2(v0, v1); - float dot02 = vdot2(v0, v2); - float dot11 = vdot2(v1, v1); - float dot12 = vdot2(v1, v2); - - // Compute barycentric coordinates - float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); - float u = (dot11 * dot02 - dot01 * dot12) * invDenom; - float v = (dot00 * dot12 - dot01 * dot02) * invDenom; - - // If point lies inside the triangle, return interpolated y-coord. - const float EPS = 1e-4f; - if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS) - { - float y = a[1] + v0[1]*u + v1[1]*v; - return Math.Abs(y-p[1]); - } - return float.MaxValue; - } - static float distPtTri(float[] p, int pStart, float[] a, int aStart, float[] b, int bStart, float[] c, int cStart) { - float[] v0 = new float[3]; - float[] v1 = new float[3]; - float[] v2 = new float[3]; - rcVsub(v0,0, c,cStart, a,aStart); - rcVsub(v1,0, b,bStart, a,aStart); - rcVsub(v2,0, p,pStart, a,aStart); - - float dot00 = vdot2(v0, v0); - float dot01 = vdot2(v0, v1); - float dot02 = vdot2(v0, v2); - float dot11 = vdot2(v1, v1); - float dot12 = vdot2(v1, v2); - - // Compute barycentric coordinates - float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); - float u = (dot11 * dot02 - dot01 * dot12) * invDenom; - float v = (dot00 * dot12 - dot01 * dot02) * invDenom; - - // If point lies inside the triangle, return interpolated y-coord. - const float EPS = 1e-4f; - if (u >= -EPS && v >= -EPS && (u + v) <= 1 + EPS) { - float y = a[aStart+1] + v0[1] * u + v1[1] * v; - return Math.Abs(y - p[pStart+1]); - } - return float.MaxValue; - } - - - static float distancePtSeg(float[] pt, float[] p, 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 distancePtSeg(float[] pt, int ptStart, float[] p, int pStart, float[] q, int qStart) { - float pqx = q[qStart + 0] - p[pStart + 0]; - float pqy = q[qStart + 1] - p[pStart + 1]; - float pqz = q[qStart + 2] - p[pStart + 2]; - float dx = pt[ptStart + 0] - p[pStart + 0]; - float dy = pt[ptStart + 1] - p[pStart + 1]; - float dz = pt[ptStart + 2] - p[pStart + 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[pStart + 0] + t * pqx - pt[ptStart + 0]; - dy = p[pStart + 1] + t * pqy - pt[ptStart + 1]; - dz = p[pStart + 2] + t * pqz - pt[ptStart + 2]; - - return dx * dx + dy * dy + dz * dz; - } - - static float distancePtSeg2d(float[] pt, float[] p, 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 distancePtSeg2d(float[] pt, int ptStart, float[] p, int pStart, float[] q, int qStart) - { - float pqx = q[qStart + 0] - p[0 + pStart]; - float pqz = q[qStart + 2] - p[2 + pStart]; - float dx = pt[ptStart + 0] - p[0 + pStart]; - float dz = pt[ptStart + 2] - p[2 + pStart]; - 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 + pStart] + t*pqx - pt[ptStart + 0]; - dz = p[2 + pStart] + t*pqz - pt[ptStart + 2]; - - return dx*dx + dz*dz; - } - - static float distToTriMesh(float[] p, float[] verts, int nverts, List tris, int ntris) - { - float dmin = float.MaxValue; - for (int i = 0; i < ntris; ++i) - { - int vaStart = tris[i * 4 + 0] * 3; - int vbStart = tris[i * 4 + 1] * 3; - int vcStart = tris[i * 4 + 2] * 3; - float d = distPtTri(p,0, verts,vaStart,verts,vbStart,verts,vcStart); - if (d < dmin) - dmin = d; - } - if (dmin == float.MaxValue) return -1; - return dmin; - } - - static float distToPoly(int nvert, float[] verts, float[] p) - { - - float dmin = float.MaxValue; - int i, j; - bool c = false; - for (i = 0, j = nvert-1; i < nvert; j = i++) - { - int viStart = i * 3; - int vjStart = j * 3; - if (((verts[viStart+2] > p[2]) != (verts[vjStart+2] > p[2])) && - (p[0] < (verts[vjStart + 0] - verts[viStart + 0]) * (p[2] - verts[viStart + 2]) / (verts[vjStart + 2] - verts[viStart + 2]) + verts[viStart + 0])) - c = !c; - dmin = Math.Min(dmin, distancePtSeg2d(p, 0, verts, vjStart, verts, viStart)); - } - return c ? -dmin : dmin; - } - - - static ushort getHeight(float fx, float fy, float fz, - float cs, float ics, float ch, - rcHeightPatch hp) - { - int ix = (int)Math.Floor(fx*ics + 0.01f); - int iz = (int)Math.Floor(fz*ics + 0.01f); - ix = rcClamp(ix-hp.xmin, 0, hp.width - 1); - iz = rcClamp(iz-hp.ymin, 0, hp.height - 1); - ushort 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. - int[] off/*[8*2]*/ = new int[] { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1}; - float dmin = float.MaxValue; - for (int i = 0; i < 8; ++i) - { - int nx = ix+off[i*2+0]; - int nz = iz+off[i*2+1]; - if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) { - continue; - } - ushort nh = hp.data[nx+nz*hp.width]; - if (nh == RC_UNSET_HEIGHT){ - continue; - } - - float d = Math.Abs(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; - } - - - public static class EdgeValues - { - public const int UNDEF = -1; - public const int HULL = -2; - }; - - static int findEdge(List edges, int nedges, int s, int t) - { - for (int i = 0; i < nedges; i++) - { - //int[] e = &edges[i*4]; - int eIndex = i*4; - if ((edges[eIndex + 0] == s && edges[eIndex + 1] == t) || (edges[eIndex + 0] == t && edges[eIndex + 1] == s)){ - return i; - } - } - return EdgeValues.UNDEF; - } - - static int findEdge(int[] edges, int nedges, int s, int t) - { - for (int i = 0; i < nedges; i++) - { - //int[] e = &edges[i*4]; - int eIndex = i*4; - if ((edges[eIndex + 0] == s && edges[eIndex + 1] == t) || (edges[eIndex + 0] == t && edges[eIndex + 1] == s)){ - return i; - } - } - return EdgeValues.UNDEF; - } - - static int addEdge(rcContext ctx, List edges, ref int nedges, int maxEdges, int s, int t, int l, int r) - { - if (nedges >= maxEdges) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "addEdge (list version): Too many edges ("+nedges+"/"+maxEdges+")."); - return EdgeValues.UNDEF; - } - - // Add edge if not already in the triangulation. - int e = findEdge(edges, nedges, s, t); - if (e == EdgeValues.UNDEF) - { - //int* edge = &edges[nedges*4]; - //int edgeIndex = nedges*4; - /*edges[edgeIndex + 0] = s; - edges[edgeIndex + 1] = t; - edges[edgeIndex + 2] = l; - edges[edgeIndex + 3] = r;*/ - edges.Add(s); - edges.Add(t); - edges.Add(l); - edges.Add(r); - return nedges++; - } - else - { - return EdgeValues.UNDEF; - } - } - - static int addEdge(rcContext ctx, int[] edges, ref int nedges, int maxEdges, int s, int t, int l, int r) - { - if (nedges >= maxEdges) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "addEdge: Too many edges ("+nedges+"/"+maxEdges+")."); - return EdgeValues.UNDEF; - } - - // Add edge if not already in the triangulation. - int e = findEdge(edges, nedges, s, t); - if (e == EdgeValues.UNDEF) - { - //int* edge = &edges[nedges*4]; - int edgeIndex = nedges*4; - edges[edgeIndex + 0] = s; - edges[edgeIndex + 1] = t; - edges[edgeIndex + 2] = l; - edges[edgeIndex + 3] = r; - return nedges++; - } - else - { - return EdgeValues.UNDEF; - } - } - static void updateLeftFace(List e, int eStart, int s, int t, int f) - { - if (e[eStart + 0] == s && e[eStart + 1] == t && e[eStart + 2] == EdgeValues.UNDEF){ - e[eStart + 2] = f; - }else if (e[eStart + 1] == s && e[eStart + 0] == t && e[eStart + 3] == EdgeValues.UNDEF){ - e[eStart + 3] = f; - } - } - - static void updateLeftFace(List e, int s, int t, int f) - { - if (e[0] == s && e[1] == t && e[2] == EdgeValues.UNDEF){ - e[2] = f; - }else if (e[1] == s && e[0] == t && e[3] == EdgeValues.UNDEF){ - e[3] = f; - } - } - - static int overlapSegSeg2d(float[] a, float[] b, float[] c, float[] d) - { - float a1 = vcross2(a, b, d); - 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 int overlapSegSeg2d(float[] a, int aStart, float[] b, int bStart, float[] c, int cStart, float[] d, int dStart) - { - float a1 = vcross2(a, aStart, b, bStart, d, dStart); - float a2 = vcross2(a, aStart, b, bStart, c, cStart); - if (a1*a2 < 0.0f) - { - float a3 = vcross2(c, cStart, d, dStart, a, aStart); - float a4 = a3 + a2 - a1; - if (a3 * a4 < 0.0f){ - return 1; - } - } - return 0; - } - - - static bool overlapEdges(float[] pts, List edges, int nedges, int s1, int t1) - { - for (int i = 0; i < nedges; ++i) - { - int s0 = edges[i*4+0]; - 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) != 0){ - return true; - } - } - return false; - } - - static void completeFacet(rcContext ctx, float[] pts, int npts, List edges, ref int nedges, int maxEdges, ref int nfaces, int e) - { - const float EPS = 1e-5f; - - //int[] edge = &edges[e*4]; - int edgeIndex = e*4; - - // Cache s and t. - int s,t; - if (edges[edgeIndex + 2] == EdgeValues.UNDEF) - { - s = edges[edgeIndex + 0]; - t = edges[edgeIndex + 1]; - } - else if (edges[edgeIndex + 3] == EdgeValues.UNDEF) - { - s = edges[edgeIndex + 1]; - t = edges[edgeIndex + 0]; - } - else - { - // Edge already completed. - return; - } - - // Find best point on left of edge. - int pt = npts; - float[] c = new float[] {0,0,0}; - float r = -1.0f; - 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, 0,ref r); - continue; - } - float d = vdist2(c, 0 , pts, u*3); - 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, 0,ref 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, 0, ref 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 == EdgeValues.UNDEF) - addEdge(ctx, edges, ref nedges, maxEdges, pt, s, nfaces, EdgeValues.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 == EdgeValues.UNDEF) - addEdge(ctx, edges, ref nedges, maxEdges, t, pt, nfaces, EdgeValues.UNDEF); - else - updateLeftFace(edges,e*4, t, pt, nfaces); - - nfaces++; - } - else - { - updateLeftFace(edges,e*4, s, t, EdgeValues.HULL); - } - } - - static void delaunayHull(rcContext ctx, int npts, float[] pts, - int nhull, int[] hull, - List tris, List edges) - { - int nfaces = 0; - int nedges = 0; - int maxEdges = npts*10; - //edges.resize(maxEdges*4); - edges.Capacity = maxEdges * 4; - - for (int i = 0, j = nhull-1; i < nhull; j=i++){ - addEdge(ctx, edges, ref nedges, maxEdges, hull[j],hull[i], EdgeValues.HULL, EdgeValues.UNDEF); - } - - int currentEdge = 0; - while (currentEdge < nedges) - { - if (edges[currentEdge*4+2] == EdgeValues.UNDEF){ - completeFacet(ctx, pts, npts, edges,ref nedges, maxEdges, ref nfaces, currentEdge); - } - if (edges[currentEdge*4+3] == EdgeValues.UNDEF){ - completeFacet(ctx, pts, npts, edges, ref nedges, maxEdges, ref nfaces, currentEdge); - } - currentEdge++; - } - - // Create tris - //tris.resize(nfaces*4); - tris.Capacity = nfaces*4; - tris.Clear(); - for (int i = 0; i < nfaces*4; ++i){ - //tris[i] = -1; - tris.Add(-1); - } - - for (int i = 0; i < nedges; ++i) - { - //const int* e = &edges[i*4]; - int edgeIndex = i*4; - if (edges[edgeIndex + 3] >= 0) - { - // Left face - //int* t = &tris[e[3]*4]; - int tIndex = edges[edgeIndex +3]*4; - if (tris[tIndex + 0] == -1) - { - tris[tIndex + 0] = edges[edgeIndex +0]; - tris[tIndex + 1] = edges[edgeIndex +1]; - } - else if (tris[tIndex + 0] == edges[edgeIndex +1]) - tris[tIndex + 2] = edges[edgeIndex +0]; - else if (tris[tIndex + 1] == edges[edgeIndex +0]) - tris[tIndex + 2] = edges[edgeIndex +1]; - } - if (edges[edgeIndex +2] >= 0) - { - // Right - //int* t = &tris[e[2]*4]; - int tIndex = edges[edgeIndex + 2]*4; - if (tris[tIndex + 0] == -1) - { - tris[tIndex + 0] = edges[edgeIndex + 1]; - tris[tIndex + 1] = edges[edgeIndex + 0]; - } - else if (tris[tIndex + 0] == edges[edgeIndex + 0]) - tris[tIndex + 2] = edges[edgeIndex + 1]; - else if (tris[tIndex + 1] == edges[edgeIndex + 1]) - tris[tIndex + 2] = edges[edgeIndex + 0]; - } - } - - for (int i = 0; i < tris.Count/4; ++i) - { - //int* t = &tris[i*4]; - int tIndex = i*4; - if (tris[tIndex + 0] == -1 || tris[tIndex + 1] == -1 || tris[tIndex + 2] == -1) - { - ctx.log(rcLogCategory.RC_LOG_WARNING, "delaunayHull: Removing dangling face "+i+" [" + tris[tIndex + 0] + "," + tris[tIndex + 1] + "," + tris[tIndex + 2] + "]."); - tris[tIndex + 0] = tris[tris.Count-4]; - tris[tIndex + 1] = tris[tris.Count-3]; - tris[tIndex + 2] = tris[tris.Count-2]; - tris[tIndex + 3] = tris[tris.Count-1]; - //tris.resize(tris.Count-4); - //tris.Capacity = tris.Count - 4; - tris.RemoveRange(tris.Count - 4, 4); - --i; - } - } - } - - - public static float getJitterX(int i) - { - return (((i * 0x8da6b343) & 0xffff) / 65535.0f * 2.0f) - 1.0f; - } - - public static float getJitterY(int i) - { - return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f; - } - - static bool buildPolyDetail(rcContext ctx, float[] _in, int nin, - float sampleDist, float sampleMaxError, - rcCompactHeightfield chf,rcHeightPatch hp, - float[] verts, ref int nverts, List tris, - List edges, List samples) - { - const int MAX_VERTS = 127; - const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts). - const int MAX_VERTS_PER_EDGE = 32; - float[] edge = new float[(MAX_VERTS_PER_EDGE+1)*3]; - int[] hull = new int[MAX_VERTS]; - int nhull = 0; - - nverts = 0; - - for (int i = 0; i < nin; ++i){ - rcVcopy(verts,i*3, _in,i*3); - } - nverts = nin; - - float cs = chf.cs; - 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]; - int vjStart = j*3; - int viStart = 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 (Math.Abs(_in[vjStart]-_in[viStart]) < 1e-6f) - { - if (_in[vjStart + 2] > _in[viStart + 2]) - { - rcSwap(ref vjStart,ref viStart); - swapped = true; - } - } - else - { - if (_in[vjStart] > _in[viStart]) - { - rcSwap(ref vjStart,ref viStart); - swapped = true; - } - } - // Create samples along the edge. - float dx = _in[viStart] - _in[vjStart];//vi[0] - vj[0]; - float dy = _in[viStart+1] - _in[vjStart+1];//vi[1] - vj[1]; - float dz = _in[viStart+2] - _in[vjStart+2];//vi[2] - vj[2]; - float d = (float)Math.Sqrt(dx*dx + dz*dz); - int nn = 1 + (int)Math.Floor(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]; - int posStart = k*3; - edge[posStart + 0] = _in[vjStart + 0] + dx*u; - edge[posStart + 1] = _in[vjStart + 1] + dy*u; - edge[posStart + 2] = _in[vjStart + 2] + dz*u; - edge[posStart + 1] = getHeight(edge[posStart + 0],edge[posStart + 1],edge[posStart + 2], cs, ics, chf.ch, hp)*chf.ch; - } - // Simplify samples. - int[] idx = new int[MAX_VERTS_PER_EDGE];// {0,nn}; - idx[1] = nn; - int nidx = 2; - for (int k = 0; k < nidx-1; ) - { - int a = idx[k]; - int b = idx[k+1]; - //float* va = &edge[a*3]; - //float* vb = &edge[b*3]; - int vaStart = a*3; - int vbStart = b*3; - // Find maximum deviation along the segment. - float maxd = 0; - int maxi = -1; - for (int m = a+1; m < b; ++m) - { - int ptStart = m * 3; - float dev = distancePtSeg(edge, ptStart, edge, vaStart,edge, vbStart); - if (dev > maxd) - { - maxd = dev; - maxi = m; - } - } - // If the max deviation is larger than accepted error, - // add new point, else continue to next segment. - if (maxi != -1 && maxd > sampleMaxError * 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]); - 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]); - rcVcopy(verts,nverts*3,edge,idx[k]*3); - hull[nhull++] = nverts; - nverts++; - } - } - } - } - - - // Tessellate the base mesh. - //edges.resize(0); - //tris.resize(0); - edges.Clear(); - tris.Clear(); - - delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); - - if (tris.Count == 0) - { - // Could not triangulate the poly, make sure there is some valid data there. - ctx.log(rcLogCategory.RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data."); - for (int i = 2; i < nverts; ++i) - { - tris.Add(0); - tris.Add(i-1); - tris.Add(i); - tris.Add(0); - } - return true; - } - - if (sampleDist > 0) - { - // Create sample locations in a grid. - float[] bmin = new float[3]; - float[] bmax = new float[3]; - rcVcopy(bmin, _in); - rcVcopy(bmax, _in); - for (int i = 1; i < nin; ++i) - { - rcVmin(bmin, 0, _in,i*3); - rcVmax(bmax, 0, _in,i*3); - } - int x0 = (int)Math.Floor(bmin[0]/sampleDist); - int x1 = (int)Math.Ceiling(bmax[0]/sampleDist); - int z0 = (int)Math.Floor(bmin[2]/sampleDist); - int z1 = (int)Math.Ceiling(bmax[2]/sampleDist); - //samples.resize(0); - samples.Clear(); - for (int z = z0; z < z1; ++z) - { - for (int x = x0; x < x1; ++x) - { - float[] pt = new float[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.Add(x); - samples.Add(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, hp)); - samples.Add(z); - samples.Add(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. - int nsamples = samples.Count/4; - for (int iter = 0; iter < nsamples; ++iter) - { - if (nverts >= MAX_VERTS){ - break; - } - - // Find sample with most error. - float[] bestpt = new float[] {0.0f,0.0f,0.0f}; - float bestd = 0; - int besti = -1; - for (int i = 0; i < nsamples; ++i) - { - // int* s = &samples[i*4]; - int sStart = i*4; - if (samples[sStart + 3] != 0) - continue; // skip added. - float[] pt = new float[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] = samples[sStart + 0]*sampleDist + getJitterX(i)*cs*0.1f; - pt[1] = samples[sStart + 1]*chf.ch; - pt[2] = samples[sStart + 2]*sampleDist + getJitterY(i)*cs*0.1f; - float d = distToTriMesh(pt, verts, nverts, tris, tris.Count/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,0); - nverts++; - - // Create new triangulation. - // TODO: Incremental add instead of full rebuild. - //edges.resize(0); - //tris.resize(0); - edges.Clear(); - tris.Clear(); - delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); - } - } - - int ntris = tris.Count/4; - if (ntris > MAX_TRIS) - { - //tris.resize(MAX_TRIS*4); - tris.RemoveRange(MAX_TRIS*4, tris.Count - MAX_TRIS*4); - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from "+ntris+" to max "+MAX_TRIS+"."); - } - - return true; - } - - - static void getHeightDataSeedsFromVertices(rcCompactHeightfield chf, - ushort[] poly, int polyStart, int npoly, - ushort[] verts, int bs, - rcHeightPatch hp, List stack) - { - // Floodfill the heightfield to get 2D height data, - // starting at vertex locations as seeds. - - // Note: Reads to the compact heightfield are offset by border size (bs) - // since border size offset is already removed from the polymesh vertices. - - //memset(hp.data, 0, sizeof(ushort)*hp.width*hp.height); - for (int i=0;i= hp.xmin+hp.width || - az < hp.ymin || az >= hp.ymin+hp.height) - continue; - - rcCompactCell c = chf.cells[(ax+bs)+(az+bs)*chf.width]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - rcCompactSpan s = chf.spans[i]; - int d = Math.Abs(ay - (int)s.y); - if (d < dmin) - { - cx = ax; - cz = az; - ci = i; - dmin = d; - } - } - } - if (ci != -1) - { - stack.Add(cx); - stack.Add(cz); - stack.Add(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[polyStart + j]*3+0]; - pcz += (int)verts[poly[polyStart + j]*3+2]; - } - pcx /= npoly; - pcz /= npoly; - - for (int i = 0; i < stack.Count; 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.Count > 0) - { - int ci = stack[stack.Count - 1]; - stack.RemoveAt(stack.Count - 1); - int cy = stack[stack.Count - 1]; - stack.RemoveAt(stack.Count - 1); - int cx = stack[stack.Count - 1]; - stack.RemoveAt(stack.Count - 1); - - // Check if close to center of the polygon. - if (Math.Abs(cx-pcx) <= 1 && Math.Abs(cy-pcz) <= 1) - { - //stack.resize(0); - stack.Clear(); - stack.Add(cx); - stack.Add(cy); - stack.Add(ci); - break; - } - - rcCompactSpan cs = chf.spans[ci]; - - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) - continue; - - int ax = cx + rcGetDirOffsetX(dir); - 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; - - int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); - - int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; - hp.data[idx] = 1; - - stack.Add(ax); - stack.Add(ay); - stack.Add(ai); - } - } - - //memset(hp.data, 0xff, sizeof(ushort)*hp.width*hp.height); - for (int i=0;i stack, - int region) - { - // Note: Reads to the compact heightfield are offset by border size (bs) - // since border size offset is already removed from the polymesh vertices. - - //stack.resize(0); - //memset(hp.data, 0xff, sizeof(ushort)*hp.width*hp.height); - stack.Clear(); - for (int i=0;i= RETRACT_SIZE) - { - head = 0; - if (stack.Count > RETRACT_SIZE*3){ - //memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.Count-RETRACT_SIZE*3)); - for (int i=0;i 0,"Resizing under zero"); - stack.RemoveRange(newSize, stack.Count - newSize); - } - - rcCompactSpan cs = chf.spans[ci]; - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) - continue; - - int ax = cx + rcGetDirOffsetX(dir); - int ay = cy + rcGetDirOffsetY(dir); - int hx = ax - hp.xmin - bs; - int hy = ay - hp.ymin - bs; - - if (hx < 0 || hx >= hp.width || hy < 0 || hy >= hp.height) - continue; - - if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT) - continue; - - int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(cs, dir); - rcCompactSpan aSpan = chf.spans[ai]; - - hp.data[hx + hy*hp.width] = aSpan.y; - - stack.Add(ax); - stack.Add(ay); - stack.Add(ai); - } - } - } - - static byte getEdgeFlags(float[] va,float[] vb, - float[] vpoly, int npoly) - { - // Return true if edge (va,vb) is part of the polygon. - float thrSqr = 0.001f * 0.001f; - for (int i = 0, j = npoly-1; i < npoly; j=i++) - { - if (distancePtSeg2d(va, 0, vpoly, j*3, vpoly, i*3) < thrSqr && - distancePtSeg2d(vb, 0, vpoly, j*3, vpoly, i*3) < thrSqr) - return 1; - } - return 0; - } - static byte getEdgeFlags(float[] va, int vaStart, float[] vb, int vbStart, - float[] vpoly, int vpolyStart, int npoly) { - // Return true if edge (va,vb) is part of the polygon. - float thrSqr = 0.001f * 0.001f; - for (int i = 0, j = npoly - 1; i < npoly; j = i++) { - if (distancePtSeg2d(va, vaStart, vpoly, vpolyStart + j * 3, vpoly, vpolyStart + i * 3) < thrSqr && - distancePtSeg2d(vb, vbStart, vpoly, vpolyStart + j * 3, vpoly, vpolyStart + i * 3) < thrSqr) - return 1; - } - return 0; - } - - static byte getTriFlags(float[] va, float[] vb, float[] vc, - float[] vpoly, int npoly) - { - byte flags = 0; - flags |= (byte)( getEdgeFlags(va,vb,vpoly,npoly) << 0); - flags |= (byte)( getEdgeFlags(vb,vc,vpoly,npoly) << 2); - flags |= (byte)( getEdgeFlags(vc,va,vpoly,npoly) << 4); - return flags; - } - static byte getTriFlags(float[] va, int vaStart, float[] vb, int vbStart, float[] vc, int vcStart, - float[] vpoly, int vpolyStart, int npoly) { - byte flags = 0; - flags |= (byte)(getEdgeFlags(va, vaStart, vb, vbStart, vpoly, vpolyStart, npoly) << 0); - flags |= (byte)(getEdgeFlags(vb, vbStart, vc, vcStart, vpoly, vpolyStart, npoly) << 2); - flags |= (byte)(getEdgeFlags(vc, vcStart, va, vaStart, vpoly, vpolyStart, npoly) << 4); - return flags; - } - - public static int rccsPop(List list) { - //Let it crash if empty, so that we know there s a pb - int ret = list[list.Count - 1]; - list.RemoveAt(list.Count - 1); - return ret; - } - - public static void rccsResizeList(List list, int length) { - if (length > list.Count){ - for (int i = list.Count; i < length; ++i) { - list.Add(0); - } - } - else if (length < list.Count){ - list.RemoveRange(length, list.Count - length); - } - } - /// @par - /// - /// See the #rcConfig documentation for more information on the configuration parameters. - /// - /// @see rcAllocPolyMeshDetail, rcPolyMesh, rcCompactHeightfield, rcPolyMeshDetail, rcConfig - public static bool rcBuildPolyMeshDetail(rcContext ctx, rcPolyMesh mesh, rcCompactHeightfield chf, - float sampleDist, float sampleMaxError, - rcPolyMeshDetail dmesh) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_POLYMESHDETAIL); - - if (mesh.nverts == 0 || mesh.npolys == 0) - return true; - - int nvp = mesh.nvp; - float cs = mesh.cs; - float ch = mesh.ch; - float[] orig = mesh.bmin; - int borderSize = mesh.borderSize; - - List edges = new List(); - List tris = new List(); - List stack = new List(); - List samples = new List(); - edges.Capacity = 64; - tris.Capacity = 512; - stack.Capacity = 512; - samples.Capacity = 512; - float[] verts = new float[256*3]; - rcHeightPatch hp = new rcHeightPatch(); - int nPolyVerts = 0; - int maxhw = 0, maxhh = 0; - - //rcScopedDelete bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP); - int[] bounds = new int[mesh.npolys*4]; - if (bounds == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' ("+ mesh.npolys*4+")."); - return false; - } - //rcScopedDelete poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP); - float[] poly = new float[nvp*3]; - if (poly == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' ("+nvp*3+")."); - return false; - } - - // Find max size for a polygon area. - for (int i = 0; i < mesh.npolys; ++i) - { - //ushort* p = &mesh.polys[i*nvp*2]; - int pStart = 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]; - int xmin = i*4+0; - int xmax = i*4+1; - int ymin = i*4+2; - int ymax = i*4+3; - bounds[xmin] = chf.width; - bounds[xmax] = 0; - bounds[ymin] = chf.height; - bounds[ymax] = 0; - for (int j = 0; j < nvp; ++j) - { - if(mesh.polys[pStart + j] == RC_MESH_NULL_IDX) - break; - //t ushort* v = &mesh.verts[p[j]*3]; - int vIndex = mesh.polys[pStart + j] * 3; - bounds[xmin] = Math.Min(bounds[xmin], (int)mesh.verts[vIndex + 0]); - bounds[xmax] = Math.Max(bounds[xmax], (int)mesh.verts[vIndex + 0]); - bounds[ymin] = Math.Min(bounds[ymin], (int)mesh.verts[vIndex + 2]); - bounds[ymax] = Math.Max(bounds[ymax], (int)mesh.verts[vIndex + 2]); - nPolyVerts++; - } - bounds[xmin] = Math.Max(0,bounds[xmin]-1); - bounds[xmax] = Math.Min(chf.width,bounds[xmax]+1); - bounds[ymin] = Math.Max(0,bounds[ymin]-1); - bounds[ymax] = Math.Min(chf.height,bounds[ymax]+1); - if (bounds[xmin] >= bounds[xmax] || bounds[ymin] >= bounds[ymax]) continue; - maxhw = Math.Max(maxhw, bounds[xmax]-bounds[xmin]); - maxhh = Math.Max(maxhh, bounds[ymax]-bounds[ymin]); - } - - //hp.data = (ushort*)rcAlloc(sizeof(ushort)*maxhw*maxhh, RC_ALLOC_TEMP); - hp.data = new ushort[maxhh*maxhw]; - if (hp.data == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' ("+maxhw*maxhh+")."); - return false; - } - - dmesh.nmeshes = mesh.npolys; - dmesh.nverts = 0; - dmesh.ntris = 0; - //dmesh.meshes = (uint*)rcAlloc(sizeof(uint)*dmesh.nmeshes*4, RC_ALLOC_PERM); - dmesh.meshes = new uint[dmesh.nmeshes*4]; - if (dmesh.meshes == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' ("+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); - dmesh.verts = new float[vcap*3]; - if (dmesh.verts == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' ("+vcap*3+")."); - return false; - } - dmesh.ntris = 0; - //dmesh.tris = (byte*)rcAlloc(sizeof(byte*)*tcap*4, RC_ALLOC_PERM); - dmesh.tris = new byte[tcap*4]; - if (dmesh.tris == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' ("+tcap*4+")."); - return false; - } - - for (int i = 0; i < mesh.npolys; ++i) - { - //const ushort* p = &mesh.polys[i*nvp*2]; - int pIndex = i*nvp*2; - - // Store polygon vertices for processing. - int npoly = 0; - for (int j = 0; j < nvp; ++j) - { - if(mesh.polys[pIndex + j] == RC_MESH_NULL_IDX) - break; - //const ushort* v = &mesh.verts[p[j]*3]; - int vIndex = mesh.polys[pIndex + j] * 3; - poly[j*3+0] = mesh.verts[vIndex + 0]*cs; - poly[j*3+1] = mesh.verts[vIndex + 1]*ch; - poly[j*3+2] = mesh.verts[vIndex + 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, mesh.polys, pIndex, npoly, mesh.verts, borderSize, hp, stack, mesh.regs[i]); - - // Build detail mesh. - int nverts = 0; - if (!buildPolyDetail(ctx, poly, npoly, - sampleDist, sampleMaxError, - chf, hp, verts, ref 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. - int ntris = tris.Count/4; - - dmesh.meshes[i*4+0] = (uint)dmesh.nverts; - dmesh.meshes[i*4+1] = (uint)nverts; - dmesh.meshes[i*4+2] = (uint)dmesh.ntris; - dmesh.meshes[i*4+3] = (uint)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); - float[] newv = new float[vcap*3]; - if (newv == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' ("+vcap*3+")."); - return false; - } - if (dmesh.nverts != 0){ - //memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts); - for (int j=0;j<3*dmesh.nverts;++j){ - newv[j] = dmesh.verts[j]; - } - } - //rcFree(dmesh.verts); - //dmesh.verts = null; - 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; - } - //byte* newt = (byte*)rcAlloc(sizeof(byte)*tcap*4, RC_ALLOC_PERM); - byte[] newt = new byte[tcap*4]; - if (newt == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' ("+tcap*4+")."); - return false; - } - if (dmesh.ntris != 0){ - //memcpy(newt, dmesh.tris, sizeof(byte)*4*dmesh.ntris); - for (int j = 0;j<4*dmesh.ntris;++j){ - newt[j] = dmesh.tris[j]; - } - } - //rcFree(dmesh.tris); - dmesh.tris = newt; - } - for (int j = 0; j < ntris; ++j) - { - //const int* t = &tris[j*4]; - int tIndex = j*4; - dmesh.tris[dmesh.ntris*4+0] = (byte)tris[tIndex + 0]; - dmesh.tris[dmesh.ntris*4+1] = (byte)tris[tIndex + 1]; - dmesh.tris[dmesh.ntris*4+2] = (byte)tris[tIndex + 2]; - dmesh.tris[dmesh.ntris*4+3] = getTriFlags(verts, tris[tIndex + 0]*3, verts, tris[tIndex + 1]*3, verts, tris[tIndex + 2]*3, poly, 0, npoly); - dmesh.ntris++; - } - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_POLYMESHDETAIL); - - return true; - } - - /// @see rcAllocPolyMeshDetail, rcPolyMeshDetail - static bool rcMergePolyMeshDetails(rcContext ctx, rcPolyMeshDetail[] meshes, int nmeshes, ref rcPolyMeshDetail mesh) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_MERGE_POLYMESHDETAIL); - - int maxVerts = 0; - int maxTris = 0; - int maxMeshes = 0; - - for (int i = 0; i < nmeshes; ++i) - { - if (meshes[i] == null) { - continue; - } - maxVerts += meshes[i].nverts; - maxTris += meshes[i].ntris; - maxMeshes += meshes[i].nmeshes; - } - - mesh.nmeshes = 0; - //mesh.meshes = (uint*)rcAlloc(sizeof(uint)*maxMeshes*4, RC_ALLOC_PERM); - mesh.meshes = new uint[maxMeshes*4]; - if (mesh.meshes == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' ("+maxMeshes*4+")."); - return false; - } - - mesh.ntris = 0; - //mesh.tris = (byte*)rcAlloc(sizeof(byte)*maxTris*4, RC_ALLOC_PERM); - mesh.tris = new byte[maxTris*4]; - if (mesh.tris == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (" + maxTris*4 + ")."); - return false; - } - - mesh.nverts = 0; - //mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM); - mesh.verts = new float[maxVerts*3]; - if (mesh.verts == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' ("+maxVerts*3+")."); - return false; - } - - // Merge datas. - for (int i = 0; i < nmeshes; ++i) - { - rcPolyMeshDetail dm = meshes[i]; - if (dm == null) { - continue; - } - for (int j = 0; j < dm.nmeshes; ++j) - { - //uint* dst = &mesh.meshes[mesh.nmeshes*4]; - //uint* src = &dm.meshes[j*4]; - int dstIndex = mesh.nmeshes*4; - int srcIndex = j*4; - mesh.meshes[dstIndex + 0] = (uint)mesh.nverts + dm.meshes[srcIndex + 0]; - mesh.meshes[dstIndex + 1] = dm.meshes[srcIndex + 1]; - mesh.meshes[dstIndex + 2] = (uint)mesh.ntris + dm.meshes[srcIndex + 2]; - mesh.meshes[dstIndex + 3] = dm.meshes[srcIndex + 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(rcTimerLabel.RC_TIMER_MERGE_POLYMESHDETAIL); - - return true; - } -} - diff --git a/Source/Framework/RecastDetour/Recast/RecastRasterization.cs b/Source/Framework/RecastDetour/Recast/RecastRasterization.cs deleted file mode 100644 index 115dacc55..000000000 --- a/Source/Framework/RecastDetour/Recast/RecastRasterization.cs +++ /dev/null @@ -1,430 +0,0 @@ -using System; -using System.Diagnostics; - -public static partial class Recast{ - static bool overlapBounds(float[] amin, float[] amax, float[] bmin, 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; - } - - static bool overlapInterval(ushort amin, ushort amax, - ushort bmin, ushort 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 == null || hf.freelist.next == null) - { - // Create new page. - // Allocate memory for the new pool. - //rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM); - rcSpanPool pool = new rcSpanPool(); - if (pool == null) - return null; - pool.next = null; - // 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]; - int itIndex = RC_SPANS_PER_POOL; - do - { - --itIndex; - pool.items[itIndex].next = freelist; - freelist = pool.items[itIndex]; - } - while (itIndex != 0); - hf.freelist = pool.items[itIndex]; - } - - // 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 == null) { - return; - } - // Add the node in front of the free list. - ptr.next = hf.freelist; - hf.freelist = ptr; - } - - static void addSpan(rcHeightfield hf, int x, int y, - ushort smin, ushort smax, - byte area, int flagMergeThr) - { - - int idx = x + y*hf.width; - - rcSpan s = allocSpan(hf); - s.smin = smin; - s.smax = smax; - s.area = area; - s.next = null; - - // Empty cell, add the first span. - if (hf.spans[idx] == null) - { - hf.spans[idx] = s; - return; - } - rcSpan prev = null; - rcSpan cur = hf.spans[idx]; - - // Insert and merge spans. - while (cur != null) - { - 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 (Math.Abs((int)s.smax - (int)cur.smax) <= flagMergeThr){ - s.area = Math.Max(s.area, cur.area); - } - - // Remove current span. - rcSpan next = cur.next; - freeSpan(hf, cur); - if (prev != null) - prev.next = next; - else - hf.spans[idx] = next; - cur = next; - } - } - - // Insert new span. - if (prev != null) - { - 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. - static void rcAddSpan(rcContext ctx, rcHeightfield hf, int x, int y, - ushort smin, ushort smax, - byte area, int flagMergeThr) - { - // Debug.Assert(ctx != null, "rcContext is null"); - addSpan(hf, x,y, smin, smax, area, flagMergeThr); - } - - // divides a convex polygons into two convex polygons on both sides of a line - static void dividePoly(float[] _in, int nin, - float[] out1, ref int nout1, - float[] out2, ref int nout2, - float x, int axis) - { - float[] d = new float[12]; - for (int i = 0; i < nin; ++i){ - d[i] = x - _in[i*3+axis]; - } - - int m = 0, n = 0; - for (int i = 0, j = nin-1; i < nin; j=i, ++i) - { - bool ina = d[j] >= 0; - bool inb = d[i] >= 0; - if (ina != inb) - { - float s = d[j] / (d[j] - d[i]); - out1[m*3+0] = _in[j*3+0] + (_in[i*3+0] - _in[j*3+0])*s; - out1[m*3+1] = _in[j*3+1] + (_in[i*3+1] - _in[j*3+1])*s; - out1[m*3+2] = _in[j*3+2] + (_in[i*3+2] - _in[j*3+2])*s; - rcVcopy(out2, n*3, out1, m*3); - m++; - n++; - // add the i'th point to the right polygon. Do NOT add points that are on the dividing line - // since these were already added above - if (d[i] > 0) - { - rcVcopy(out1,m*3, _in, i*3); - m++; - } - else if (d[i] < 0) - { - rcVcopy(out2,n*3, _in, i*3); - n++; - } - } - else // same side - { - // add the i'th point to the right polygon. Addition is done even for points on the dividing line - if (d[i] >= 0) - { - rcVcopy(out1, m*3, _in, i*3); - m++; - if (d[i] != 0) - continue; - } - rcVcopy(out2, n*3, _in, i*3); - n++; - } - } - - nout1 = m; - nout2 = n; - } - - - - static void rasterizeTri(float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start, - byte area, rcHeightfield hf, - float[] bmin, float[] bmax, - float cs, float ics, float ich, - int flagMergeThr) - { - int w = hf.width; - int h = hf.height; - float[] tmin = new float[3]; - float[] tmax = new float[3]; - float by = bmax[1] - bmin[1]; - - // Calculate the bounding box of the triangle. - rcVcopy(tmin, 0, v0, v0Start); - rcVcopy(tmax, 0, v0, v0Start); - rcVmin(tmin, 0, v1, v1Start); - rcVmin(tmin, 0, v2, v2Start); - rcVmax(tmax, 0, v1, v1Start); - rcVmax(tmax, 0, v2, v2Start); - - // If the triangle does not touch the bbox of the heightfield, skip the triagle. - if (!overlapBounds(bmin, bmax, tmin, tmax)) - return; - - // Calculate the footprint of the triangle on the grid's y-axis - int y0 = (int)((tmin[2] - bmin[2])*ics); - int y1 = (int)((tmax[2] - bmin[2])*ics); - y0 = rcClamp(y0, 0, h-1); - y1 = rcClamp(y1, 0, h-1); - - // Clip the triangle into all grid cells it touches. - //float[] buf = new float[7*3*4]; - - float[] _in = new float[7*3]; - float[] inrow = new float[7*3]; - float[] p1 = new float[7*3]; - float[] p2 = new float[7*3]; - - rcVcopy(_in,0 , v0, v0Start); - rcVcopy(_in,1*3, v1, v1Start); - rcVcopy(_in,2*3, v2, v2Start); - - int nvrow = 0; - int nvIn = 3; - - for (int y = y0; y <= y1; ++y) - { - // Clip polygon to row. Store the remaining polygon as well - float cz = bmin[2] + y*cs; - dividePoly(_in, nvIn, inrow, ref nvrow, p1, ref nvIn, cz+cs, 2); - //rcSwap(_in, p1); - float[] tmp = _in; - _in = p1; - p1 = tmp; - - if (nvrow < 3) - continue; - - // find the horizontal bounds in the row - float minX = inrow[0], maxX = inrow[0]; - for (int i=1; i inrow[i*3]) minX = inrow[i*3]; - if (maxX < inrow[i*3]) maxX = inrow[i*3]; - } - int x0 = (int)((minX - bmin[0])*ics); - int x1 = (int)((maxX - bmin[0])*ics); - x0 = rcClamp(x0, 0, w-1); - x1 = rcClamp(x1, 0, w-1); - - int nv = 0; - int nv2 = nvrow; - - for (int x = x0; x <= x1; ++x) - { - // Clip polygon to column. store the remaining polygon as well - float cx = bmin[0] + x*cs; - dividePoly(inrow, nv2, p1, ref nv, p2, ref nv2, cx+cs, 0); - //rcSwap(inrow, p2); - tmp = inrow; - inrow = p2; - p2 = tmp; - if (nv < 3) { - continue; - } - - // Calculate min and max of the span. - float smin = p1[1], smax = p1[1]; - for (int i = 1; i < nv; ++i) - { - smin = Math.Min(smin, p1[i*3+1]); - smax = Math.Max(smax, p1[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. - ushort ismin = (ushort)rcClamp((int)Math.Floor(smin * ich), 0, RC_SPAN_MAX_HEIGHT); - ushort ismax = (ushort)rcClamp((int)Math.Ceiling(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 - public static void rcRasterizeTriangle(rcContext ctx, float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start, - byte area, rcHeightfield solid, - int flagMergeThr) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); - - float ics = 1.0f/solid.cs; - float ich = 1.0f/solid.ch; - rasterizeTri(v0, v0Start, v1, v1Start, v2, v2Start, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); - - ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); - } - - /// @par - /// - /// Spans will only be added for triangles that overlap the heightfield grid. - /// - /// @see rcHeightfield - public static void rcRasterizeTriangles(rcContext ctx, float[] verts, int nv, - int[] tris, byte[] areas, int nt, - rcHeightfield solid, int flagMergeThr) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); - - float ics = 1.0f/solid.cs; - float ich = 1.0f/solid.ch; - // Rasterize triangles. - for (int i = 0; i < nt; ++i) - { - int v0Start = tris[i*3+0]*3; - int v1Start = tris[i*3+1]*3; - int v2Start = tris[i*3+2]*3; - // Rasterize. - rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); - } - - /// @par - /// - /// Spans will only be added for triangles that overlap the heightfield grid. - /// - /// @see rcHeightfield - public static void rcRasterizeTriangles(rcContext ctx, float[] verts, int nv, - ushort[] tris, byte[] areas, int nt, - rcHeightfield solid, int flagMergeThr) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); - - float ics = 1.0f/solid.cs; - float ich = 1.0f/solid.ch; - // Rasterize triangles. - for (int i = 0; i < nt; ++i) - { - int v0Start = tris[i*3+0]*3; - int v1Start = tris[i*3+1]*3; - int v2Start = tris[i*3+2]*3; - - // Rasterize. - rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); - } - - /// @par - /// - /// Spans will only be added for triangles that overlap the heightfield grid. - /// - /// @see rcHeightfield - public static void rcRasterizeTriangles(rcContext ctx, float[] verts, byte[] areas, int nt, - rcHeightfield solid, int flagMergeThr) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); - - float ics = 1.0f/solid.cs; - float ich = 1.0f/solid.ch; - // Rasterize triangles. - for (int i = 0; i < nt; ++i) - { - int v0Start = (i*3+0)*3; - int v1Start = (i*3+1)*3; - int v2Start = (i*3+2)*3; - // Rasterize. - rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); - } -} \ No newline at end of file diff --git a/Source/Framework/RecastDetour/Recast/RecastRegion.cs b/Source/Framework/RecastDetour/Recast/RecastRegion.cs deleted file mode 100644 index 469488290..000000000 --- a/Source/Framework/RecastDetour/Recast/RecastRegion.cs +++ /dev/null @@ -1,1426 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; - -public static partial class Recast{ - static void calculateDistanceField( rcContext ctx, rcCompactHeightfield chf, ushort[] src, ref ushort maxDist) - { - int w = chf.width; - 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) - { - 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]; - byte area = chf.areas[i]; - - int nc = 0; - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - 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) - { - 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]; - - if (rcGetCon(s, 0) != RC_NOT_CONNECTED) - { - // (-1,0) - int ax = x + rcGetDirOffsetX(0); - int ay = y + rcGetDirOffsetY(0); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); - rcCompactSpan aSpan = chf.spans[ai]; - if (src[ai]+2 < src[i]){ - src[i] = (ushort)(src[ai]+2); - } - - // (-1,-1) - if (rcGetCon(aSpan, 3) != RC_NOT_CONNECTED) - { - int aax = ax + rcGetDirOffsetX(3); - int aay = ay + rcGetDirOffsetY(3); - int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(aSpan, 3); - if (src[aai]+3 < src[i]){ - src[i] = (ushort)(src[aai]+3); - } - } - } - if (rcGetCon(s, 3) != RC_NOT_CONNECTED) - { - // (0,-1) - int ax = x + rcGetDirOffsetX(3); - int ay = y + rcGetDirOffsetY(3); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); - rcCompactSpan aSpan = chf.spans[ai]; - if (src[ai]+2 < src[i]){ - src[i] = (ushort)(src[ai]+2); - } - - // (1,-1) - if (rcGetCon(aSpan, 2) != RC_NOT_CONNECTED) - { - int aax = ax + rcGetDirOffsetX(2); - int aay = ay + rcGetDirOffsetY(2); - int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(aSpan, 2); - if (src[aai]+3 < src[i]){ - src[i] = (ushort)(src[aai]+3); - } - } - } - } - } - } - // Pass 2 - for (int y = h-1; y >= 0; --y) - { - for (int x = w-1; x >= 0; --x) - { - 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]; - - if (rcGetCon(s, 2) != RC_NOT_CONNECTED) - { - // (1,0) - int ax = x + rcGetDirOffsetX(2); - int ay = y + rcGetDirOffsetY(2); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2); - rcCompactSpan aSpan = chf.spans[ai]; - if (src[ai]+2 < src[i]){ - src[i] = (ushort)(src[ai]+2); - } - - // (1,1) - if (rcGetCon(aSpan, 1) != RC_NOT_CONNECTED) - { - int aax = ax + rcGetDirOffsetX(1); - int aay = ay + rcGetDirOffsetY(1); - int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(aSpan, 1); - if (src[aai]+3 < src[i]){ - src[i] = (ushort)(src[aai]+3); - } - } - } - if (rcGetCon(s, 1) != RC_NOT_CONNECTED) - { - // (0,1) - int ax = x + rcGetDirOffsetX(1); - int ay = y + rcGetDirOffsetY(1); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1); - rcCompactSpan aSpan = chf.spans[ai]; - if (src[ai]+2 < src[i]){ - src[i] = (ushort)(src[ai]+2); - } - - // (-1,1) - if (rcGetCon(aSpan, 0) != RC_NOT_CONNECTED) - { - int aax = ax + rcGetDirOffsetX(0); - int aay = ay + rcGetDirOffsetY(0); - int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(aSpan, 0); - if (src[aai]+3 < src[i]){ - src[i] = (ushort)(src[aai]+3); - } - } - } - } - } - } - maxDist = 0; - for (int i = 0; i < chf.spanCount; ++i){ - maxDist = Math.Max(src[i], maxDist); - } - - } - - static ushort[] boxBlur(rcCompactHeightfield chf, int thr, - ushort[] src, ushort[] dst) - { - int w = chf.width; - int h = chf.height; - - thr *= 2; - - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - 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]; - ushort 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) - { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - d += (int)src[ai]; - - rcCompactSpan aSpan = chf.spans[ai]; - int dir2 = (dir+1) & 0x3; - if (rcGetCon(aSpan, dir2) != RC_NOT_CONNECTED) - { - int ax2 = ax + rcGetDirOffsetX(dir2); - int ay2 = ay + rcGetDirOffsetY(dir2); - int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(aSpan, dir2); - d += (int)src[ai2]; - } - else - { - d += cd; - } - } - else - { - d += cd*2; - } - } - dst[i] = (ushort)((d+5)/9); - } - } - } - return dst; - } - - - static bool floodRegion(int x, int y, int i, - ushort level, ushort r, - rcCompactHeightfield chf, - ushort[] srcReg, ushort[] srcDist, - List stack) - { - int w = chf.width; - - byte area = chf.areas[i]; - - // Flood fill mark region. - //stack.resize(0); - stack.Clear(); - stack.Add((int)x); - stack.Add((int)y); - stack.Add((int)i); - srcReg[i] = r; - srcDist[i] = 0; - - ushort lev = (ushort)(level >= 2 ? level-2 : 0); - int count = 0; - - while (stack.Count > 0) - { - int ci = rccsPop(stack); - int cy = rccsPop(stack); - int cx = rccsPop(stack); - - rcCompactSpan cs = chf.spans[ci]; - - // Check if any of the neighbours already have a valid region set. - ushort ar = 0; - for (int dir = 0; dir < 4; ++dir) - { - // 8 connected - if (rcGetCon(cs, dir) != RC_NOT_CONNECTED) - { - int ax = cx + rcGetDirOffsetX(dir); - int ay = cy + rcGetDirOffsetY(dir); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir); - if (chf.areas[ai] != area) - continue; - ushort nr = srcReg[ai]; - if ((nr & RC_BORDER_REG) != 0) // Do not take borders into account. - continue; - if (nr != 0 && nr != r) - { - ar = nr; - break; - } - - rcCompactSpan aSpan = chf.spans[ai]; - - int dir2 = (dir+1) & 0x3; - if (rcGetCon(aSpan, dir2) != RC_NOT_CONNECTED) - { - int ax2 = ax + rcGetDirOffsetX(dir2); - int ay2 = ay + rcGetDirOffsetY(dir2); - int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(aSpan, dir2); - if (chf.areas[ai2] != area) - continue; - ushort nr2 = srcReg[ai2]; - if (nr2 != 0 && nr2 != r) - { - ar = nr2; - break; - } - } - } - } - if (ar != 0) - { - srcReg[ci] = 0; - continue; - } - count++; - - // Expand neighbours. - for (int dir = 0; dir < 4; ++dir) - { - if (rcGetCon(cs, dir) != RC_NOT_CONNECTED) - { - int ax = cx + rcGetDirOffsetX(dir); - int ay = cy + rcGetDirOffsetY(dir); - int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir); - if (chf.areas[ai] != area) - continue; - if (chf.dist[ai] >= lev && srcReg[ai] == 0) - { - srcReg[ai] = r; - srcDist[ai] = 0; - stack.Add(ax); - stack.Add(ay); - stack.Add(ai); - } - } - } - } - - return count > 0; - } - - static ushort[] expandRegions(int maxIter, ushort level, - rcCompactHeightfield chf, - ushort[] srcReg, ushort[] srcDist, - ushort[] dstReg, ushort[] dstDist, - List stack, - bool fillStack) - { - int w = chf.width; - int h = chf.height; - - if (fillStack) - { - // Find cells revealed by the raised level. - stack.Clear(); - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - 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.Add(x); - stack.Add(y); - stack.Add(i); - } - } - } - } - } - else // use cells in the input stack - { - // mark all cells which already have a region - for (int j=0; j 0) - { - int failed = 0; - - //memcpy(dstReg, srcReg, sizeof(ushort)*chf.spanCount); - for (int i=0;i 0 && (srcReg[ai] & RC_BORDER_REG) == 0) - { - if ((int)srcDist[ai]+2 < (int)d2) - { - r = srcReg[ai]; - d2 = (ushort)(srcDist[ai]+2); - } - } - } - if (r != 0) - { - stack[j+2] = -1; // mark as used - dstReg[i] = r; - dstDist[i] = d2; - } - else - { - failed++; - } - } - - // rcSwap source and dest. - rcSwap(ref srcReg, ref dstReg); - rcSwap(ref srcDist, ref dstDist); - - if (failed*3 == stack.Count) - break; - - if (level > 0) - { - ++iter; - if (iter >= maxIter) - break; - } - } - - return srcReg; - } - - - - static void sortCellsByLevel(ushort startLevel, - rcCompactHeightfield chf, - ushort[] srcReg, - uint nbStacks, List[] stacks, - ushort loglevelsPerStack) // the levels per stack (2 in our case) as a bit shift - { - int w = chf.width; - int h = chf.height; - startLevel = (ushort)(startLevel >> loglevelsPerStack); - - for (uint j=0; j> loglevelsPerStack; - int sId = startLevel - level; - if (sId >= (int)nbStacks) - continue; - if (sId < 0) - sId = 0; - - stacks[sId].Add(x); - stacks[sId].Add(y); - stacks[sId].Add(i); - } - } - } - } - - - static void appendStacks(List srcStack, List dstStack, - ushort[] srcReg) - { - for (int j=0; j connections = new List(); - public List floors = new List(); - }; - - static void removeAdjacentNeighbours(rcRegion reg) - { - // Remove adjacent duplicates. - for (int i = 0; i < reg.connections.Count && reg.connections.Count > 1; ) - { - int ni = (i+1) % reg.connections.Count; - if (reg.connections[i] == reg.connections[ni]) - { - // Remove duplicate - for (int j = i; j < reg.connections.Count-1; ++j){ - reg.connections[j] = reg.connections[j+1]; - } - rccsPop(reg.connections); - } - else - ++i; - } - } - - static void replaceNeighbour(rcRegion reg, ushort oldId, ushort newId) - { - bool neiChanged = false; - for (int i = 0; i < reg.connections.Count; ++i) - { - if (reg.connections[i] == oldId) - { - reg.connections[i] = newId; - neiChanged = true; - } - } - for (int i = 0; i < reg.floors.Count; ++i) - { - if (reg.floors[i] == oldId) - reg.floors[i] = newId; - } - if (neiChanged) - removeAdjacentNeighbours(reg); - } - - static bool canMergeWithRegion(rcRegion rega, rcRegion regb) - { - if (rega.areaType != regb.areaType) - return false; - int n = 0; - for (int i = 0; i < rega.connections.Count; ++i) - { - if (rega.connections[i] == regb.id) - n++; - } - if (n > 1) - return false; - for (int i = 0; i < rega.floors.Count; ++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.Count; ++i) - if (reg.floors[i] == n) - return; - reg.floors.Add(n); - } - - static bool mergeRegions(rcRegion rega, rcRegion regb) - { - ushort aid = rega.id; - ushort bid = regb.id; - - // Duplicate current neighbourhood. - List acon = new List(); - - for (int i = 0; i < rega.connections.Count; ++i) - acon.Add( rega.connections[i] ); - List bcon = regb.connections; - - // Find insertion point on A. - int insa = -1; - for (int i = 0; i < acon.Count; ++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.Count; ++i) - { - if (bcon[i] == aid) - { - insb = i; - break; - } - } - if (insb == -1) - return false; - - // Merge neighbours. - rega.connections.Clear(); - for (int i = 0, ni = acon.Count; i < ni-1; ++i) - rega.connections.Add(acon[(insa+1+i) % ni]); - - for (int i = 0, ni = bcon.Count; i < ni-1; ++i) - rega.connections.Add(bcon[(insb+1+i) % ni]); - - removeAdjacentNeighbours(rega); - - for (int j = 0; j < regb.floors.Count; ++j) - addUniqueFloorRegion(rega, regb.floors[j]); - rega.spanCount += regb.spanCount; - regb.spanCount = 0; - regb.connections.Clear(); - - return true; - } - - static bool isRegionConnectedToBorder(rcRegion reg) - { - // Region is connected to border if - // one of the neighbours is null id. - for (int i = 0; i < reg.connections.Count; ++i) - { - if (reg.connections[i] == 0) - return true; - } - return false; - } - - static bool isSolidEdge(rcCompactHeightfield chf, ushort[] srcReg, - int x, int y, int i, int dir) - { - rcCompactSpan s = chf.spans[i]; - ushort r = 0; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - 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, - ushort[] srcReg, - List cont) - { - int startDir = dir; - int starti = i; - - rcCompactSpan ss = chf.spans[i]; - ushort curReg = 0; - if (rcGetCon(ss, dir) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir); - curReg = srcReg[ai]; - } - cont.Add(curReg); - - int iter = 0; - while (++iter < 40000) - { - rcCompactSpan s = chf.spans[i]; - - if (isSolidEdge(chf, srcReg, x, y, i, dir)) - { - // Choose the edge corner - ushort r = 0; - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - int ax = x + rcGetDirOffsetX(dir); - int ay = y + rcGetDirOffsetY(dir); - int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - r = srcReg[ai]; - } - if (r != curReg) - { - curReg = r; - cont.Add(curReg); - } - - dir = (dir+1) & 0x3; // Rotate CW - } - else - { - int ni = -1; - int nx = x + rcGetDirOffsetX(dir); - int ny = y + rcGetDirOffsetY(dir); - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) - { - 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.Count > 1) - { - for (int j = 0; j < cont.Count; ) - { - int nj = (j+1) % cont.Count; - if (cont[j] == cont[nj]) - { - for (int k = j; k < cont.Count-1; ++k) - cont[k] = cont[k+1]; - rccsPop(cont); - } - else - ++j; - } - } - } - - static bool filterSmallRegions(rcContext ctx, int minRegionArea, int mergeRegionSize, - ref ushort maxRegionId, - rcCompactHeightfield chf, - ushort[] srcReg) - { - int w = chf.width; - int h = chf.height; - - int nreg = maxRegionId+1; - - rcRegion[] regions = new rcRegion[nreg]; - if (regions == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (" +nreg+ ")."); - return false; - } - - // Construct regions - for (int i = 0; i < nreg; ++i){ - regions[i] = new rcRegion((ushort) 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) - { - rcCompactCell c = chf.cells[x+y*w]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - ushort 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; - ushort floorId = srcReg[j]; - if (floorId == 0 || floorId >= nreg) - continue; - addUniqueFloorRegion(reg, floorId); - } - - // Have found contour - if (reg.connections.Count > 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. - List stack = new List();//(32); - List trace= new List();//(32); - stack.Capacity = 32; - trace.Capacity = 32; - for (int i = 0; i < nreg; ++i) - { - rcRegion reg = regions[i]; - if (reg.id == 0 || (reg.id & RC_BORDER_REG) != 0) - 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.Clear(); - trace.Clear(); - - reg.visited = true; - stack.Add(i); - - while (stack.Count != 0) - { - // Pop - int ri = rccsPop(stack); - - rcRegion creg = regions[ri]; - - spanCount += creg.spanCount; - trace.Add(ri); - - for (int j = 0; j < creg.connections.Count; ++j) - { - if ((creg.connections[j] & RC_BORDER_REG) != 0) - { - connectsToBorder = true; - continue; - } - rcRegion neireg = regions[creg.connections[j]]; - if (neireg.visited) - continue; - if (neireg.id == 0 || (neireg.id & RC_BORDER_REG) != 0) - continue; - // Visit - stack.Add(neireg.id); - neireg.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.Count; ++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) != 0) - 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; - ushort mergeId = reg.id; - for (int j = 0; j < reg.connections.Count; ++j) - { - if ((reg.connections[j] & RC_BORDER_REG) != 0) - continue; - rcRegion mreg = regions[reg.connections[j]]; - if (mreg.id == 0 || (mreg.id & RC_BORDER_REG) != 0) - continue; - if (mreg.spanCount < smallest && - canMergeWithRegion(reg, mreg) && - canMergeWithRegion(mreg, reg)) - { - smallest = mreg.spanCount; - mergeId = mreg.id; - } - } - // Found new id. - if (mergeId != reg.id) - { - ushort 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) != 0) - 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) != 0) - continue; // Skip external regions. - regions[i].remap = true; - } - - ushort regIdGen = 0; - for (int i = 0; i < nreg; ++i) - { - if (!regions[i].remap) - continue; - ushort oldId = regions[i].id; - ushort 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; - } - - return true; - } - - /// @par - /// - /// This is usually the second to the last step in creating a fully built - /// compact heightfield. This step is required before regions are built - /// using #rcBuildRegions or #rcBuildRegionsMonotone. - /// - /// After this step, the distance data is available via the rcCompactHeightfield::maxDistance - /// and rcCompactHeightfield::dist fields. - /// - /// @see rcCompactHeightfield, rcBuildRegions, rcBuildRegionsMonotone - public static bool rcBuildDistanceField(rcContext ctx, rcCompactHeightfield chf) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD); - - chf.dist = null; - - //ushort* src = (ushort*)rcAlloc(sizeof(ushort)*chf.spanCount, RC_ALLOC_TEMP); - ushort[] src = new ushort[chf.spanCount]; - if (src == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' ("+chf.spanCount+")."); - return false; - } - //ushort* dst = (ushort*)rcAlloc(sizeof(ushort)*chf.spanCount, RC_ALLOC_TEMP); - ushort[] dst = new ushort[chf.spanCount]; - if (dst == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' ("+chf.spanCount+")."); - //rcFree(src); - return false; - } - - ushort maxDist = 0; - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_DIST); - - calculateDistanceField(ctx, chf, src, ref maxDist); - chf.maxDistance = maxDist; - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_DIST); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_BLUR); - - // Blur - if (boxBlur(chf, 1, src, dst) != src){ - rcSwap(ref src,ref dst); - } - - // Store distance. - chf.dist = src; - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_BLUR); - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD); - - //rcFree(dst); - dst = null; - - return true; - } - - public static void paintRectRegion(int minx, int maxx, int miny, int maxy, ushort regId, - rcCompactHeightfield chf, ushort[] srcReg) - { - int w = chf.width; - for (int y = miny; y < maxy; ++y) - { - for (int x = minx; x < maxx; ++x) - { - 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; - } - } - } - } - - - const ushort RC_NULL_NEI = 0xffff; - - public class rcSweepSpan - { - public ushort rid = 0; // row id - public ushort id = 0; // region id - public ushort ns = 0; // number samples - public ushort nei = 0; // neighbour id - }; - - /// @par - /// - /// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour. - /// Contours will form simple polygons. - /// - /// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be - /// re-assigned to the zero (null) region. - /// - /// Partitioning can result in smaller than necessary regions. @p mergeRegionArea helps - /// reduce unecessarily small regions. - /// - /// See the #rcConfig documentation for more information on the configuration parameters. - /// - /// The region data will be available via the rcCompactHeightfield::maxRegions - /// and rcCompactSpan::reg fields. - /// - /// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions. - /// - /// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig - public static bool rcBuildRegionsMonotone(rcContext ctx, rcCompactHeightfield chf, - int borderSize, int minRegionArea, int mergeRegionArea) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS); - - int w = chf.width; - int h = chf.height; - ushort id = 1; - - ushort[] srcReg = new ushort[chf.spanCount]; - if (srcReg == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' ("+chf.spanCount+")."); - return false; - } - - int nsweeps = Math.Max(chf.width,chf.height); - rcSweepSpan[] sweeps = new rcSweepSpan[nsweeps]; - rccsArrayItemsCreate(sweeps); - if (sweeps == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' ("+nsweeps+")."); - return false; - } - - // Mark border regions. - if (borderSize > 0) - { - // Make sure border will not overflow. - int bw = Math.Min(w, borderSize); - int bh = Math.Min(h, borderSize); - // Paint regions - paintRectRegion(0, bw, 0, h, (ushort)(id|RC_BORDER_REG), chf, srcReg); id++; - paintRectRegion(w-bw, w, 0, h, (ushort)(id|RC_BORDER_REG), chf, srcReg); id++; - paintRectRegion(0, w, 0, bh, (ushort)(id|RC_BORDER_REG), chf, srcReg); id++; - paintRectRegion(0, w, h-bh, h, (ushort)(id|RC_BORDER_REG), chf, srcReg); id++; - - chf.borderSize = borderSize; - } - - List prev = new List();//256 - prev.Capacity = 256; - // Sweep one line at a time. - for (int y = borderSize; y < h-borderSize; ++y) - { - // Collect spans from this row. - rccsResizeList(prev, id+1); - for (int i=0;i 0 && srcReg[i] < rid) - srcReg[i] = sweeps[srcReg[i]].id; - } - } - } - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FILTER); - - // Filter out small regions. - chf.maxRegions = id; - if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, ref chf.maxRegions, chf, srcReg)) - return false; - - ctx.stopTimer(rcTimerLabel.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(rcTimerLabel.RC_TIMER_BUILD_REGIONS); - - return true; - } - - /// @par - /// - /// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour. - /// Contours will form simple polygons. - /// - /// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be - /// re-assigned to the zero (null) region. - /// - /// Watershed partitioning can result in smaller than necessary regions, especially in diagonal corridors. - /// @p mergeRegionArea helps reduce unecessarily small regions. - /// - /// See the #rcConfig documentation for more information on the configuration parameters. - /// - /// The region data will be available via the rcCompactHeightfield::maxRegions - /// and rcCompactSpan::reg fields. - /// - /// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions. - /// - /// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig - public static bool rcBuildRegions(rcContext ctx, rcCompactHeightfield chf, - int borderSize, int minRegionArea, int mergeRegionArea) - { - Debug.Assert(ctx != null, "rcContext is null"); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS); - - int w = chf.width; - int h = chf.height; - - //rcScopedDelete buf = (ushort*)rcAlloc(sizeof(ushort)*chf.spanCount*4, RC_ALLOC_TEMP); - /* - ushort[] buf = new ushort[chf.spanCount*4]; - if (buf == null) - { - ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' ("+chf.spanCount*4+")."); - return false; - } - */ - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_WATERSHED); - - const int LOG_NB_STACKS = 3; - const int NB_STACKS = 1 << LOG_NB_STACKS; - List[] lvlStacks = new List[NB_STACKS]; - for (int i = 0; i < NB_STACKS; ++i) { - lvlStacks[i] = new List(); - //rccsResizeList(lvlStacks[i], 1024); - lvlStacks[i].Capacity = 1024; - } - - List stack = new List();//(1024); - List visited = new List();//(1024); - stack.Capacity = 1024; - visited.Capacity = 1024; - //rccResizeList(stack, 1024); - //rccResizeList(visited, 1024); - - ushort[] srcReg = new ushort[chf.spanCount]; - ushort[] srcDist = new ushort[chf.spanCount];//buf+chf.spanCount; - ushort[] dstReg = new ushort[chf.spanCount];// buf+chf.spanCount*2; - ushort[] dstDist = new ushort[chf.spanCount];//buf+chf.spanCount*3; - - //memset(srcReg, 0, sizeof(ushort)*chf.spanCount); - //memset(srcDist, 0, sizeof(ushort)*chf.spanCount); - - ushort regionId = 1; - ushort level = (ushort)((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; - - if (borderSize > 0) - { - // Make sure border will not overflow. - int bw = Math.Min(w, borderSize); - int bh = Math.Min(h, borderSize); - // Paint regions - paintRectRegion(0, bw, 0, h,(ushort)( regionId|RC_BORDER_REG ), chf, srcReg); regionId++; - paintRectRegion(w - bw, w, 0, h, (ushort)(regionId | RC_BORDER_REG), chf, srcReg); regionId++; - paintRectRegion(0, w, 0, bh, (ushort)(regionId | RC_BORDER_REG), chf, srcReg); regionId++; - paintRectRegion(0, w, h - bh, h, (ushort)(regionId | RC_BORDER_REG), chf, srcReg); regionId++; - - chf.borderSize = borderSize; - } - - int sId = -1; - while (level > 0) - { - level = (ushort)(level >= 2 ? level-2 : 0); - sId = (sId+1) & (NB_STACKS-1); - - // ctx.startTimer(rcTimerLabel.RC_TIMER_DIVIDE_TO_LEVELS); - - if (sId == 0) - sortCellsByLevel(level, chf, srcReg, NB_STACKS, lvlStacks, 1); - else - appendStacks(lvlStacks[sId-1], lvlStacks[sId], srcReg); // copy left overs from last level - - // ctx.stopTimer(rcTimerLabel.RC_TIMER_DIVIDE_TO_LEVELS); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_EXPAND); - - // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg) - { - rcSwap(ref srcReg,ref dstReg); - rcSwap(ref srcDist,ref dstDist); - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_EXPAND); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FLOOD); - - // Mark new regions with IDs. - for (int j=0; j= 0 && srcReg[i] == 0) - { - if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) - regionId++; - } - } - - ctx.stopTimer(rcTimerLabel.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, true) != srcReg) - { - rcSwap(ref srcReg,ref dstReg); - rcSwap(ref srcDist,ref dstDist); - } - - ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_WATERSHED); - - ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FILTER); - - // Filter out small regions. - chf.maxRegions = regionId; - if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, ref chf.maxRegions, chf, srcReg)) - return false; - - ctx.stopTimer(rcTimerLabel.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(rcTimerLabel.RC_TIMER_BUILD_REGIONS); - - return true; - } -} \ No newline at end of file diff --git a/sql/updates/world/master/2017_12_09_00_world.sql b/sql/updates/world/master/2017_12_09_00_world.sql new file mode 100644 index 000000000..4dde1fe19 --- /dev/null +++ b/sql/updates/world/master/2017_12_09_00_world.sql @@ -0,0 +1,2 @@ +ALTER TABLE `version` + CHANGE `core_version` `core_version` VARCHAR(255) DEFAULT '' NOT NULL COMMENT 'Core revision dumped at startup.'; diff --git a/sql/updates/world/master/2017_12_10_00_world.sql b/sql/updates/world/master/2017_12_10_00_world.sql new file mode 100644 index 000000000..47ead1190 --- /dev/null +++ b/sql/updates/world/master/2017_12_10_00_world.sql @@ -0,0 +1,37 @@ +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=17 AND `SourceEntry` IN ( +8593, +13982, +29528, +31225, +31333, +34367, +39246, +42788, +44422, +44550, +44610, +46488, +47374, +47469, +47634, +47911, +48363, +49367, +55161, +56765, +57908, +65200, +65258, +65265, +70586, +77585); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` IN (29528,44610,46488,57908,70586); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13,1,29528,0,0,31,0,3,16518,0,0,0,0,'',NULL), +(13,1,44610,0,0,31,0,3,24824,0,0,0,0,'','Allow spell Collect Data only on Iron Dwarf Relics'), +(13,1,46488,0,0,31,0,3,26817,0,0,0,0,'',NULL), +(13,1,46488,0,0,36,0,0, 0,0,1,0,0,'',NULL), +(13,1,57908,0,0,31,0,3,26631,0,0,0,0,'',NULL), +(13,1,57908,0,0,36,0,0, 0,0,1,0,0,'',NULL), +(13,1,70586,0,0,31,0,3,37852,0,0,0,0,'','Throw Quel''delar on Quel''delar skull'); diff --git a/sql/updates/world/master/2017_12_10_01_world_2016_11_03_01_world_335.sql b/sql/updates/world/master/2017_12_10_01_world_2016_11_03_01_world_335.sql new file mode 100644 index 000000000..058c38879 --- /dev/null +++ b/sql/updates/world/master/2017_12_10_01_world_2016_11_03_01_world_335.sql @@ -0,0 +1,12 @@ +-- Archmage Arugal, NPC entry 4275 +SET @Arugal := 4275; + +UPDATE `creature_template` SET `ScriptName` = 'boss_archmage_arugal' WHERE `entry`= @Arugal; + +UPDATE `creature_text` SET `comment`= 'Archmage Arugal - Fenrus the Devourer dies' WHERE `CreatureID`= @Arugal AND `GroupID`= 0; + +DELETE FROM `creature_text` WHERE `CreatureID` = @Arugal AND `GroupID` IN (1,2,3); +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`, `comment`) VALUES +(@Arugal,1,0,'You, too, shall serve!', 14,0,100,0,0,0,6115,0,'Archmage Arugal - Aggro'), +(@Arugal,2,0,'Release your rage!', 14,0,100,0,0,0,6535,0,'Archmage Arugal - Transforms player into a Worgen'), +(@Arugal,3,0,'Another falls!', 14,0,100,0,0,0,6116,0,'Archmage Arugal - Killing a player'); diff --git a/sql/updates/world/master/2017_12_10_02_world_2016_11_04_01_world.sql b/sql/updates/world/master/2017_12_10_02_world_2016_11_04_01_world.sql new file mode 100644 index 000000000..0b23027ff --- /dev/null +++ b/sql/updates/world/master/2017_12_10_02_world_2016_11_04_01_world.sql @@ -0,0 +1,14 @@ +-- +DELETE FROM `creature` WHERE `guid` IN (1070, 1115, 1158,121595,1162); +INSERT INTO `creature` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `PhaseID`, `modelid`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `VerifiedBuild`) VALUES +(1070, 31787, 571, 0, 0, 1, 0, 0, 0, 6016.53, 2052.48, 636.058, 5.23599, 300, 0, 0, 12175, 3893, 0, 0, 0, 0, 0), +(1115, 31787, 571, 0, 0, 1, 0, 0, 0, 6110.05, 2330.27, 636.336, 0.575959, 300, 0, 0, 12175, 3893, 0, 0, 0, 0, 0), +(1158, 31787, 571, 0, 0, 1, 0, 0, 0, 6121.4, 2309.11, 636.225, 0.575959, 300, 0, 0, 12175, 3893, 0, 0, 0, 0, 0), +(121595, 31787, 571, 0, 0, 1, 0, 0, 0, 5952.47, 2197.624, 636.041, 5.23587, 300, 0, 0, 12175, 3893, 0, 0, 0, 0, 0), +(1162, 31787, 571, 0, 0, 1, 0, 0, 0, 5990.604, 2116.24, 636.041, 1.876254, 300, 0, 0, 12175, 3893, 0, 0, 0, 0, 0); + +UPDATE `creature_template` SET `InhabitType`=4 WHERE `entry` IN (31787); +DELETE FROM `creature_addon` WHERE `guid` IN (SELECT `guid` FROM `creature` WHERE `id`=31787); + +DELETE FROM `creature_template_addon` WHERE `entry` = 31787; +INSERT INTO `creature_template_addon` (`entry`,`bytes1`,`bytes2`,`emote`) VALUES (31787,50331648,1,429); diff --git a/sql/updates/world/master/2017_12_10_03_world_2016_11_04_02_world.sql b/sql/updates/world/master/2017_12_10_03_world_2016_11_04_02_world.sql new file mode 100644 index 000000000..8dd6cf4e1 --- /dev/null +++ b/sql/updates/world/master/2017_12_10_03_world_2016_11_04_02_world.sql @@ -0,0 +1,8 @@ +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=29857, `action_param2`=2, `action_param3`=0, `comment`='Arcane Protector - In Combat - Cast Summon Astral Spark' WHERE `entryorguid`=16504 AND `source_type`=0 AND `id`=1 AND `link`=0; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=34064, `action_param2`=2, `action_param3`=0, `comment`='Voidwraith - On Just Died - Cast Soul Split' WHERE `entryorguid`=18659 AND `source_type`=0 AND `id`=0 AND `link`=0; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=36595, `action_param2`=2, `action_param3`=0, `comment`='Apex - Between 0-75% Health - Cast Summon Apex Crumbler (Phase 1) (No Repeat)' WHERE `entryorguid`=19940 AND `source_type`=0 AND `id`=1 AND `link`=0; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=36596, `action_param2`=2, `action_param3`=0, `comment`='Apex - Between 0-50% Health - Cast Summon Apex Crumbler (Phase 1) (No Repeat)' WHERE `entryorguid`=19940 AND `source_type`=0 AND `id`=2 AND `link`=0; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=36597, `action_param2`=2, `action_param3`=0, `comment`='Apex - Between 0-25% Health - Cast Summon Apex Crumbler (Phase 1) (No Repeat)' WHERE `entryorguid`=19940 AND `source_type`=0 AND `id`=3 AND `link`=0; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=31995, `action_param2`=2, `action_param3`=0, `comment`='Shattered Rumbler - On Script - Cast Shattered Rumbler' WHERE `entryorguid`=1715700 AND `source_type`=9 AND `id`IN(0,1,2) AND `link`=0; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=38854, `action_param2`=2, `action_param3`=0, `comment`='Cursed egg - Action list - Cast Hatch Arakkoa' WHERE `entryorguid`=18521100 AND `source_type`=9 AND `id`=0 AND `link`=0; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=38865, `action_param2`=2, `action_param3`=0, `comment`='Cursed egg - Action list - Cast Hatch Bad Arakkoa' WHERE `entryorguid`=18521101 AND `source_type`=9 AND `id`=0 AND `link`=0;