forked from 0ad/0ad
Spahbod
bc805bd357
Also takes all the duplicated parts of random maps including the new player start entities and random biome system into rmgen libraries. Tweaks nile river and snowflake random maps. This was SVN commit r11152.
196 lines
5.0 KiB
JavaScript
196 lines
5.0 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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 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);
|
|
|
|
// 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 (g_Map.validT(x, z) && constraint.allows(x, z))
|
|
{
|
|
retVec.push(new PointXZ(x, z));
|
|
}
|
|
}
|
|
}
|
|
|
|
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 retVec;
|
|
}
|