413 lines
8.5 KiB
JavaScript
413 lines
8.5 KiB
JavaScript
// Constants for using SmoothElevationPainter
|
|
const ELEVATION_SET = 0;
|
|
const ELEVATION_MODIFY = 1;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ElevationPainter
|
|
//
|
|
// Class for painting elevation over an area
|
|
//
|
|
// elevation: Target elevation/height to be painted
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
function ElevationPainter(elevation)
|
|
{
|
|
this.elevation = elevation;
|
|
this.DX = [0, 1, 1, 0];
|
|
this.DZ = [0, 0, 1, 1];
|
|
}
|
|
|
|
ElevationPainter.prototype.paint = function(area)
|
|
{
|
|
var length = area.points.length;
|
|
var elevation = this.elevation;
|
|
|
|
for (var i=0; i < length; i++)
|
|
{
|
|
var pt = area.points[i];
|
|
|
|
for (var j=0; j < 4; j++)
|
|
{
|
|
g_Map.height[pt.x + this.DX[j]][pt.z + this.DZ[j]] = elevation;
|
|
}
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// LayeredPainter
|
|
//
|
|
// Class for painting multiple layered terrains over an area
|
|
//
|
|
// terrainArray: Array of terrain painter objects
|
|
// widths: Array of widths for each layer
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
function LayeredPainter(terrainArray, widths)
|
|
{
|
|
if (!(terrainArray instanceof Array))
|
|
{
|
|
throw("LayeredPainter: terrains must be an array!");
|
|
}
|
|
|
|
this.terrains = [];
|
|
for (var i = 0; i < terrainArray.length; ++i)
|
|
{
|
|
this.terrains.push(createTerrain(terrainArray[i]));
|
|
}
|
|
|
|
this.widths = widths;
|
|
}
|
|
|
|
LayeredPainter.prototype.paint = function(area)
|
|
{
|
|
var size = getMapSize();
|
|
var saw = new Array(size);
|
|
var dist = new Array(size);
|
|
|
|
// init typed arrays
|
|
for (var i = 0; i < size; ++i)
|
|
{
|
|
saw[i] = new Uint8Array(size); // bool / uint8
|
|
dist[i] = new Uint16Array(size); // uint16
|
|
}
|
|
|
|
// Point queue (implemented with array)
|
|
var pointQ = [];
|
|
|
|
// push edge points
|
|
var pts = area.points;
|
|
var length = pts.length;
|
|
var areaID = area.getID();
|
|
|
|
for (var i=0; i < length; i++)
|
|
{
|
|
var x = pts[i].x;
|
|
var z = pts[i].z;
|
|
|
|
for (var dx=-1; dx <= 1; dx++)
|
|
{
|
|
var nx = x+dx;
|
|
for (var dz=-1; dz <= 1; dz++)
|
|
{
|
|
var nz = z+dz;
|
|
|
|
if (g_Map.validT(nx, nz) && g_Map.area[nx][nz] != areaID && !saw[nx][nz])
|
|
{
|
|
saw[nx][nz] = 1;
|
|
dist[nx][nz] = 0;
|
|
pointQ.push(new PointXZ(nx, nz));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// do BFS inwards to find distances to edge
|
|
while (pointQ.length)
|
|
{
|
|
var pt = pointQ.shift(); // Pop queue
|
|
var px = pt.x;
|
|
var pz = pt.z;
|
|
var d = dist[px][pz];
|
|
|
|
// paint if in area
|
|
if (g_Map.area[px][pz] == areaID)
|
|
{
|
|
var w=0;
|
|
var i=0;
|
|
|
|
for (; i < this.widths.length; i++)
|
|
{
|
|
w += this.widths[i];
|
|
if (w >= d)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
this.terrains[i].place(px, pz);
|
|
}
|
|
|
|
// enqueue neighbours
|
|
for (var dx=-1; dx<=1; dx++)
|
|
{
|
|
var nx = px+dx;
|
|
for (var dz=-1; dz<=1; dz++)
|
|
{
|
|
var nz = pz+dz;
|
|
|
|
if (g_Map.validT(nx, nz) && g_Map.area[nx][nz] == areaID && !saw[nx][nz])
|
|
{
|
|
saw[nx][nz] = 1;
|
|
dist[nx][nz] = d+1;
|
|
pointQ.push(new PointXZ(nx, nz));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// MultiPainter
|
|
//
|
|
// Class for applying multiple painters over an area
|
|
//
|
|
// painters: Array of painter objects
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
function MultiPainter(painters)
|
|
{
|
|
this.painters = painters;
|
|
}
|
|
|
|
MultiPainter.prototype.paint = function(area)
|
|
{
|
|
for (var i=0; i < this.painters.length; i++)
|
|
{
|
|
this.painters[i].paint(area);
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// SmoothElevationPainter
|
|
//
|
|
// Class for painting elevation smoothly over an area
|
|
//
|
|
// type: Type of elevation modification
|
|
// ELEVATION_MODIFY = relative
|
|
// ELEVATION_SET = absolute
|
|
// elevation: Target elevation/height of area
|
|
// blendRadius: How steep the elevation change is
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
function SmoothElevationPainter(type, elevation, blendRadius)
|
|
{
|
|
this.type = type;
|
|
this.elevation = elevation;
|
|
this.blendRadius = blendRadius;
|
|
|
|
if (type != ELEVATION_SET && type != ELEVATION_MODIFY)
|
|
{
|
|
throw("SmoothElevationPainter: invalid type '"+type+"'");
|
|
}
|
|
}
|
|
|
|
SmoothElevationPainter.prototype.checkInArea = function(areaID, x, z)
|
|
{
|
|
// Check given tile and its neighbors
|
|
return (
|
|
(g_Map.validT(x, z) && g_Map.area[x][z] == areaID)
|
|
|| (g_Map.validT(x-1, z) && g_Map.area[x-1][z] == areaID)
|
|
|| (g_Map.validT(x, z-1) && g_Map.area[x][z-1] == areaID)
|
|
|| (g_Map.validT(x-1, z-1) && g_Map.area[x-1][z-1] == areaID)
|
|
);
|
|
};
|
|
|
|
SmoothElevationPainter.prototype.paint = function(area)
|
|
{
|
|
var pointQ = [];
|
|
var pts = area.points;
|
|
var heightPts = [];
|
|
|
|
var mapSize = getMapSize()+1;
|
|
|
|
var saw = new Array(mapSize);
|
|
var dist = new Array(mapSize);
|
|
var gotHeightPt = new Array(mapSize);
|
|
var newHeight = new Array(mapSize);
|
|
|
|
// init typed arrays
|
|
for (var i = 0; i < mapSize; ++i)
|
|
{
|
|
saw[i] = new Uint8Array(mapSize); // bool / uint8
|
|
dist[i] = new Uint16Array(mapSize); // uint16
|
|
gotHeightPt[i] = new Uint8Array(mapSize); // bool / uint8
|
|
newHeight[i] = new Float32Array(mapSize); // float32
|
|
}
|
|
|
|
var length = pts.length;
|
|
var areaID = area.getID();
|
|
|
|
// get a list of all points
|
|
for (var i=0; i < length; i++)
|
|
{
|
|
var x = pts[i].x;
|
|
var z = pts[i].z;
|
|
|
|
for (var dx=-1; dx <= 2; dx++)
|
|
{
|
|
var nx = x+dx;
|
|
for (var dz=-1; dz <= 2; dz++)
|
|
{
|
|
var nz = z+dz;
|
|
|
|
if (g_Map.validH(nx, nz) && !gotHeightPt[nx][nz])
|
|
{
|
|
gotHeightPt[nx][nz] = 1;
|
|
heightPts.push(new PointXZ(nx, nz));
|
|
newHeight[nx][nz] = g_Map.height[nx][nz];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// push edge points
|
|
for (var i=0; i < length; i++)
|
|
{
|
|
var x = pts[i].x;
|
|
var z = pts[i].z;
|
|
for (var dx=-1; dx <= 2; dx++)
|
|
{
|
|
var nx = x+dx;
|
|
for (var dz=-1; dz <= 2; dz++)
|
|
{
|
|
var nz = z+dz;
|
|
|
|
if (g_Map.validH(nx, nz) && !this.checkInArea(areaID, nx, nz) && !saw[nx][nz])
|
|
{
|
|
saw[nx][nz]= 1;
|
|
dist[nx][nz] = 0;
|
|
pointQ.push(new PointXZ(nx, nz));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// do BFS inwards to find distances to edge
|
|
while(pointQ.length)
|
|
{
|
|
var pt = pointQ.shift();
|
|
var px = pt.x;
|
|
var pz = pt.z;
|
|
var d = dist[px][pz];
|
|
|
|
// paint if in area
|
|
if (g_Map.validH(px, pz) && this.checkInArea(areaID, px, pz))
|
|
{
|
|
if (d <= this.blendRadius)
|
|
{
|
|
var a = (d-1) / this.blendRadius;
|
|
if (this.type == ELEVATION_SET)
|
|
{
|
|
newHeight[px][pz] = a*this.elevation + (1-a)*g_Map.height[px][pz];
|
|
}
|
|
else
|
|
{ // type == MODIFY
|
|
newHeight[px][pz] += a*this.elevation;
|
|
}
|
|
}
|
|
else
|
|
{ // also happens when blendRadius == 0
|
|
if (this.type == ELEVATION_SET)
|
|
{
|
|
newHeight[px][pz] = this.elevation;
|
|
}
|
|
else
|
|
{ // type == MODIFY
|
|
newHeight[px][pz] += this.elevation;
|
|
}
|
|
}
|
|
}
|
|
|
|
// enqueue neighbours
|
|
for (var dx=-1; dx <= 1; dx++)
|
|
{
|
|
var nx = px+dx;
|
|
for (var dz=-1; dz <= 1; dz++)
|
|
{
|
|
var nz = pz+dz;
|
|
|
|
if (g_Map.validH(nx, nz) && this.checkInArea(areaID, nx, nz) && !saw[nx][nz])
|
|
{
|
|
saw[nx][nz] = 1;
|
|
dist[nx][nz] = d+1;
|
|
pointQ.push(new PointXZ(nx, nz));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
length = heightPts.length;
|
|
|
|
// smooth everything out
|
|
for (var i = 0; i < length; ++i)
|
|
{
|
|
var pt = heightPts[i];
|
|
var px = pt.x;
|
|
var pz = pt.z;
|
|
|
|
if (this.checkInArea(areaID, px, pz))
|
|
{
|
|
var sum = 8 * newHeight[px][pz];
|
|
var count = 8;
|
|
|
|
for (var dx=-1; dx <= 1; dx++)
|
|
{
|
|
var nx = px+dx;
|
|
for (var dz=-1; dz <= 1; dz++)
|
|
{
|
|
var nz = pz+dz;
|
|
|
|
if (g_Map.validH(nx, nz))
|
|
{
|
|
sum += newHeight[nx][nz];
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_Map.height[px][pz] = sum/count;
|
|
}
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// TerrainPainter
|
|
//
|
|
// Class for painting a terrain over an area
|
|
//
|
|
// terrain: Terrain placer object
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
function TerrainPainter(terrain)
|
|
{
|
|
this.terrain = createTerrain(terrain);
|
|
}
|
|
|
|
TerrainPainter.prototype.paint = function(area)
|
|
{
|
|
var length = area.points.length;
|
|
for (var i=0; i < length; i++)
|
|
{
|
|
var pt = area.points[i];
|
|
this.terrain.place(pt.x, pt.z);
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// TileClassPainter
|
|
//
|
|
// Class for painting tileClasses over an area
|
|
//
|
|
// tileClass: TileClass object
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
function TileClassPainter(tileClass)
|
|
{
|
|
this.tileClass = tileClass;
|
|
}
|
|
|
|
TileClassPainter.prototype.paint = function(area)
|
|
{
|
|
var length = area.points.length;
|
|
for (var i=0; i < length; i++)
|
|
{
|
|
var pt = area.points[i];
|
|
this.tileClass.add(pt.x, pt.z);
|
|
}
|
|
};
|