Removed not used files

Added missing sql updates
This commit is contained in:
hondacrx
2017-12-13 14:35:01 -05:00
parent 4b2675944b
commit bc7dee3c8d
14 changed files with 73 additions and 8930 deletions
File diff suppressed because it is too large Load Diff
@@ -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);
}
}
@@ -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<int> 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<int> points, List<int> 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<int> 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<byte> 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<int> verts(256);
List<int> verts = new List<int>();
verts.Capacity = 256;
//List<int> simplified(64);
List<int> simplified = new List<int>();
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;
}
}
@@ -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: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
///
/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call
/// #rcFilterLedgeSpans after calling this filter.
///
/// @see rcHeightfield, rcConfig
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: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
///
/// @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);
}
}
@@ -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<byte> 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<chf.spanCount;++i){
srcReg[i] = 0xff;
}
int nsweeps = chf.width;
//rcScopedDelete<rcLayerSweepSpan> 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<rcLayerRegion> 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<gridSize;++j){
layer.heights[j] = 0xFF;
}
layer.areas = new byte[gridSize];// (byte*)rcAlloc(gridSize, RC_ALLOC_PERM);
if (layer.areas == null)
{
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'areas' " + gridSize);
return false;
}
//memset(layer.areas, 0, gridSize);
layer.cons =new byte[gridSize];// (byte*)rcAlloc(gridSize, RC_ALLOC_PERM);
if (layer.cons == null)
{
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'cons' " + gridSize);
return false;
}
//memset(layer.cons, 0, gridSize);
// Find layer height bounds.
int hmin = 0, hmax = 0;
for (int j = 0; j < nregs; ++j)
{
if (regs[j].baseFlag != 0 && regs[j].layerId == curId)
{
hmin = (int)regs[j].ymin;
hmax = (int)regs[j].ymax;
}
}
layer.width = lw;
layer.height = lh;
layer.cs = chf.cs;
layer.ch = chf.ch;
// Adjust the bbox to fit the heighfield.
rcVcopy(layer.bmin, bmin);
rcVcopy(layer.bmax, bmax);
layer.bmin[1] = bmin[1] + hmin*chf.ch;
layer.bmax[1] = bmin[1] + hmax*chf.ch;
layer.hmin = hmin;
layer.hmax = hmax;
// Update usable data region.
layer.minx = layer.width;
layer.maxx = 0;
layer.miny = layer.height;
layer.maxy = 0;
// Copy height and area from compact heighfield.
for (int y = 0; y < lh; ++y)
{
for (int x = 0; x < lw; ++x)
{
int cx = borderSize+x;
int cy = borderSize+y;
rcCompactCell c = chf.cells[cx+cy*w];
for (int j = (int)c.index, nj = (int)(c.index+c.count); j < nj; ++j)
{
rcCompactSpan s = chf.spans[j];
// Skip unassigned regions.
if (srcReg[j] == 0xff){
continue;
}
// Skip of does nto belong to current layer.
byte lid = regs[srcReg[j]].layerId;
if (lid != curId)
continue;
// Update data bounds.
layer.minx = Math.Min(layer.minx, x);
layer.maxx = Math.Max(layer.maxx, x);
layer.miny = Math.Min(layer.miny, y);
layer.maxy = Math.Max(layer.maxy, y);
// Store height and area type.
int idx = x+y*lw;
layer.heights[idx] = (byte)(s.y - hmin);
layer.areas[idx] = chf.areas[j];
// Check connection.
byte portal = 0;
byte con = 0;
for (int dir = 0; dir < 4; ++dir)
{
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
{
int ax = cx + rcGetDirOffsetX(dir);
int ay = cy + rcGetDirOffsetY(dir);
int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
byte alid = srcReg[ai] != (byte)0xff ? regs[srcReg[ai]].layerId : (byte)0xff;
// Portal mask
if (chf.areas[ai] != RC_NULL_AREA && lid != alid)
{
portal |= (byte)(1<<dir);
// Update height so that it matches on both sides of the portal.
rcCompactSpan aSpan = chf.spans[ai];
if (aSpan.y > 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<<dir);
}
}
}
layer.cons[idx] = (byte)( (portal << 4) | con );
}
}
}
if (layer.minx > 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;
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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<nvrow; ++i)
{
if (minX > 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);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,2 @@
ALTER TABLE `version`
CHANGE `core_version` `core_version` VARCHAR(255) DEFAULT '' NOT NULL COMMENT 'Core revision dumped at startup.';
@@ -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');
@@ -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');
@@ -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);
@@ -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;