forked from 0ad/0ad
Spahbod
9c34b1ffed
Fixes the chicken problem There are no more unreachable trees around the map (hopefully) Fixed the bug that caused edges of the map ruin the layout of painters (One of the best examples to look is archipelago, before and after this revision). Fixed those out of memory errors caused by pathplacer (Mostly noticeable in alpine valley and corinthian isthmus) And some other minor tweaks. Fixes #1577 This was SVN commit r13004.
211 lines
5.5 KiB
JavaScript
211 lines
5.5 KiB
JavaScript
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
// PathPlacer
|
|
//
|
|
// Class for creating a winding path between two points
|
|
//
|
|
// x1,z1: Starting point of path
|
|
// x2,z2: Ending point of path
|
|
// width: Width of the path
|
|
// a: Waviness - how wavy the path will be (higher is wavier, 0.0 is straight line)
|
|
// b: Smoothness - how smooth the path will be (higher is smoother)
|
|
// c: Offset - max amplitude of waves along the path (0.0 is straight line)
|
|
// taper: Tapering - how much the width of the path changes from start to end
|
|
// if positive, the width will decrease by that factor, if negative the width
|
|
// will increase by that factor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function PathPlacer(x1, z1, x2, z2, width, a, b, c, taper, failfraction)
|
|
{
|
|
this.x1 = x1;
|
|
this.z1 = z1;
|
|
this.x2 = x2;
|
|
this.z2 = z2;
|
|
this.width = width;
|
|
this.a = a;
|
|
this.b = b;
|
|
this.c = c;
|
|
this.taper = taper;
|
|
this.failfraction = (failfraction !== undefined ? failfraction : 5);
|
|
}
|
|
|
|
PathPlacer.prototype.place = function(constraint)
|
|
{
|
|
/*/ Preliminary bounds check
|
|
if (!g_Map.validT(this.x1, this.z1) || !constraint.allows(this.x1, this.z1) ||
|
|
!g_Map.validT(this.x2, this.z2) || !constraint.allows(this.x2, this.z2))
|
|
{
|
|
return undefined;
|
|
}*/
|
|
|
|
var failed = 0;
|
|
var dx = (this.x2 - this.x1);
|
|
var dz = (this.z2 - this.z1);
|
|
var dist = Math.sqrt(dx*dx + dz*dz);
|
|
dx /= dist;
|
|
dz /= dist;
|
|
|
|
var numSteps = 1 + Math.floor(dist/4 * this.a);
|
|
var numISteps = 1 + Math.floor(dist/4 * this.b);
|
|
var totalSteps = numSteps*numISteps;
|
|
var offset = 1 + Math.floor(dist/4 * this.c);
|
|
|
|
var size = getMapSize();
|
|
var gotRet = new Array(size);
|
|
for (var i = 0; i < size; ++i)
|
|
{
|
|
gotRet[i] = new Uint8Array(size); // bool / uint8
|
|
}
|
|
|
|
// Generate random offsets
|
|
var ctrlVals = new Float32Array(numSteps); //float32
|
|
for (var j = 1; j < (numSteps-1); ++j)
|
|
{
|
|
ctrlVals[j] = randFloat(-offset, offset);
|
|
}
|
|
|
|
// Interpolate for smoothed 1D noise
|
|
var noise = new Float32Array(totalSteps+1); //float32
|
|
for (var j = 0; j < numSteps; ++j)
|
|
{
|
|
// Cubic interpolation
|
|
var v0 = ctrlVals[(j+numSteps-1)%numSteps];
|
|
var v1 = ctrlVals[j];
|
|
var v2 = ctrlVals[(j+1)%numSteps];
|
|
var v3 = ctrlVals[(j+2)%numSteps];
|
|
var P = (v3 - v2) - (v0 - v1);
|
|
var Q = (v0 - v1) - P;
|
|
var R = v2 - v0;
|
|
var S = v1;
|
|
for (var k = 0; k < numISteps; ++k)
|
|
{
|
|
var t = k/numISteps;
|
|
noise[j*numISteps + k] = P*t*t*t + Q*t*t + R*t + S;
|
|
}
|
|
}
|
|
|
|
var halfWidth = 0.5 * this.width;
|
|
|
|
// Add smoothed noise to straight path
|
|
var segments1 = [];
|
|
var segments2 = [];
|
|
|
|
for (var j = 0; j < totalSteps; ++j)
|
|
{
|
|
// Interpolated points along straight path
|
|
var t = j/totalSteps;
|
|
var tx = this.x1 * (1.0 - t) + this.x2 * t;
|
|
var tz = this.z1 * (1.0 - t) + this.z2 * t;
|
|
var t2 = (j+1)/totalSteps;
|
|
var tx2 = this.x1 * (1.0 - t2) + this.x2 * t2;
|
|
var tz2 = this.z1 * (1.0 - t2) + this.z2 * t2;
|
|
|
|
// Find noise offset points
|
|
var nx = (tx - dz * noise[j]);
|
|
var nz = (tz + dx * noise[j]);
|
|
var nx2 = (tx2 - dz * noise[j+1]);
|
|
var nz2 = (tz2 + dx * noise[j+1]);
|
|
|
|
// Find slope of offset points
|
|
var ndx = (nx2 - nx);
|
|
var ndz = (nz2 - nz);
|
|
var dist = Math.sqrt(ndx*ndx + ndz*ndz);
|
|
ndx /= dist;
|
|
ndz /= dist;
|
|
|
|
var taperedWidth = (1.0 - t*this.taper) * halfWidth;
|
|
|
|
// Find slope of offset path
|
|
var px = Math.round(nx - ndz * -taperedWidth);
|
|
var pz = Math.round(nz + ndx * -taperedWidth);
|
|
segments1.push(new PointXZ(px, pz));
|
|
var px2 = Math.round(nx2 - ndz * taperedWidth);
|
|
var pz2 = Math.round(nz2 + ndx * taperedWidth);
|
|
segments2.push(new PointXZ(px2, pz2));
|
|
|
|
|
|
}
|
|
|
|
var retVec = [];
|
|
// Draw path segments
|
|
var num = segments1.length - 1;
|
|
for (var j = 0; j < num; ++j)
|
|
{
|
|
// Fill quad formed by these 4 points
|
|
// Note the code assumes these points have been rounded to integer values
|
|
var pt11 = segments1[j];
|
|
var pt12 = segments1[j+1];
|
|
var pt21 = segments2[j];
|
|
var pt22 = segments2[j+1];
|
|
|
|
var tris = [[pt12, pt11, pt21], [pt12, pt21, pt22]];
|
|
|
|
for (var t = 0; t < 2; ++t)
|
|
{
|
|
// Sort vertices by min z
|
|
var tri = tris[t].sort(
|
|
function(a, b)
|
|
{
|
|
return a.z - b.z;
|
|
}
|
|
);
|
|
|
|
// Fills in a line from (z, x1) to (z,x2)
|
|
var fillLine = function(z, x1, x2)
|
|
{
|
|
var left = Math.round(Math.min(x1, x2));
|
|
var right = Math.round(Math.max(x1, x2));
|
|
for (var x = left; x <= right; x++)
|
|
{
|
|
if (constraint.allows(x, z))
|
|
{
|
|
if (g_Map.inMapBounds(x, z) && !gotRet[x][z])
|
|
{
|
|
retVec.push(new PointXZ(x, z));
|
|
gotRet[x][z] = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
failed++;
|
|
}
|
|
}
|
|
}
|
|
|
|
var A = tri[0];
|
|
var B = tri[1];
|
|
var C = tri[2];
|
|
|
|
var dx1 = (B.z != A.z) ? ((B.x - A.x) / (B.z - A.z)) : 0;
|
|
var dx2 = (C.z != A.z) ? ((C.x - A.x) / (C.z - A.z)) : 0;
|
|
var dx3 = (C.z != B.z) ? ((C.x - B.x) / (C.z - B.z)) : 0;
|
|
|
|
if (A.z == B.z)
|
|
{
|
|
fillLine(A.z, A.x, B.x);
|
|
}
|
|
else
|
|
{
|
|
for (var z = A.z; z <= B.z; z++)
|
|
{
|
|
fillLine(z, A.x + dx1*(z - A.z), A.x + dx2*(z - A.z));
|
|
}
|
|
}
|
|
if (B.z == C.z)
|
|
{
|
|
fillLine(B.z, B.x, C.x);
|
|
}
|
|
else
|
|
{
|
|
for (var z = B.z + 1; z < C.z; z++)
|
|
{
|
|
fillLine(z, B.x + dx3*(z - B.z), A.x + dx2*(z - A.z));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ((failed > this.width*this.failfraction*dist) ? undefined : retVec);
|
|
}
|