/**
* Copyright Glidias ( http://wonderfl.net/user/Glidias )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/p8LL
*/
package {
import flash.display.Sprite;
public class FlashTest extends Sprite {
public function FlashTest() {
// You know perlin noise works across an infinite number of terrain tiles and can scroll infinitely.
// Ie. yoo can sample any seeded region and it'll have the
// deterministic pattern be generated accordingly for that tile region. But can the same be done for the below algorithm?
// Basically, I want to see a visual example of a heightmap generated through terrainIterateFault(), that scrolls infinitely!
// http://www.lighthouse3d.com/opengl/terrain/index.php3?fault
//TerrainProcesses.terrainIterateFault()
// Can the above algorithm be made to be able to run across an infinite number of tiles?
// Here are 3 situations/challenges/rule to choose from.
// 1) Faultlines are straight throughout and run infinitely. (HARD! Impossible??)
// Giving a bit of chance... here are 2 other alternatives...
// 2) Faultlines can bend in any direction, but still run infinitely. (HARD! Possibly impossible? Maybe seeding the edges,
// but everything still needs to join up without stopping abruptly!)
// 3) Faultlines have limited range radius (easier, should be possible using brute force conditional checks against neighboring tiles)
// Basically, i want a pattern of straight criss-cross lines, that scrolls infinitely given any arbituary coordinate...
// Is this possible or a paradox? I think it might not be impossible given the fact that if infinity starts at (0,0)
// and expands outward in any direction, there is no map edge. Since this algorithm requires a map edge to start with, there's no
// way to determine the genesis of a line under case 1, from which means no way of determining anything.
// ANyway, enjoy the library below. The msa library is actually Noise functions that you can use from some github library.
// Just search for AS3 Noise functions. The TerrainProcesses is a port of the C++ code based on the link above, but I can't guarantee
// everything is working because I didn't test all of 'em.
// BTW, if Circles algorithm http://www.lighthouse3d.com/opengl/terrain/index.php?circles is capped at a maximum radius,
// it can scroll infinitely too using method described in 3).
}
}
}
//package alternterrainxtras.util
//{
//import alternterrainxtras.msa.Perlin;
//import alternterrainxtras.msa.Smoothing;
import flash.display.BitmapData;
/**
*
* Useful for modifying terrain heights, adding additional detail, noise, smoothing, etc. Based on code found in terrain.cpp.
* @author Glenn Ko
*/
public class TerrainProcesses //implements ITerrainProcess
{
public var terrainHeights:Vector.<int>;
public var terrainGridLength:int;
public var terrainGridWidth:int;
public var iterationsDone :int= 0;
public var maxDisp:Number = 1.0;
public var minDisp:Number = 0.1;
public var disp:Number;
public var itMinDisp:int=100;
public var terrainWaveSize:Number= 3.0;
public var terrainFunction:int = MPD;
public var terrainParticleMode:int = ROLL;
public var terrainCircleSize:Number = 100.0;
public var terrainRandomSeed:int = 0;
public var roughness:Number = 1.0;
public var steps:int = 1;
public function setupSquareHeights(heights:Vector.<int>):void {
terrainHeights = heights;
var val:int = Math.sqrt(heights.length);
terrainGridLength = val;
terrainGridWidth = val;
iterationsDone = 0;
}
public function setupHeights(heights:Vector.<int>, width:int, length:int):void {
terrainHeights = heights;
terrainGridLength = length;
terrainGridWidth = width;
iterationsDone = 0;
}
public function setupEmptyTerrain(x:int, y:int):void {
//var i:int;
terrainGridWidth = x;
terrainGridLength = y;
terrainHeights = new Vector.<int>(x * y, true);
//for (i=0;i<terrainGridWidth*terrainGridLength; i++)
// terrainHeights[i] = 0.0;
iterationsDone = 0;
}
public static const STEP:int = 1;
public static const SIN:int = 2;
public static const COS :int = 3;
public static const CIRCLE:int = 4;
public static const MPD:int = 5;
public static const RandomDirection:int = 6;
public static const ROLL:int = 0;
public static const STICK:int = 1;
public function TerrainProcesses()
{
}
private function rand():int {
return int(Math.random() * 32767);
}
public function terrainGrayscaleHeightMap(heightmap:BitmapData):void {
var range:Number = maxDisp - minDisp;
var multiplier:Number = 1 / 255;
for (var y:int = 0; y < terrainGridLength; y++) {
for (var x:int = 0; x < terrainGridWidth; x++) {
terrainHeights[y*terrainGridWidth + x] += minDisp + (heightmap.getPixel(x, y) & 0xFF)*multiplier * range;
}
}
}
/*
public function terrainApplyNoise(scale:Number, octaves:int=4, lacunarity:Number=128, H:Number = 128 ):void {
Perlin.setParams( { octaves:octaves, H:H, lacunarity:lacunarity } );
var phase:int = terrainRandomSeed;
var range:Number = maxDisp - minDisp;
var mx:int = terrainGridWidth * .5;
var my:int = terrainGridLength * .5;
var xMult:Number = 1 / scale;
var yMult:Number = 1 / scale;
for (var y:int = 0; y < terrainGridLength; y++) {
for (var x:int = 0; x < terrainGridWidth; x++) {
terrainHeights[y * terrainGridWidth + x] += minDisp + Perlin.fractalNoise((x - mx)*xMult, (y - my)*yMult , phase) * range; //Perlin.fractalNoise((x - mx), (y - my) , phase)
}
}
}
*/
public function terrainIterateCircles(numIterations:int, xOff:int = 0, yOff:int = 0, w:int = 0, h:int = 0 ):void {
var dispAux:Number;
var i:int,j:int,k:int,dispSign:int;
var x:Number,z:Number,r:Number,pd:Number;
w = w != 0 ? w : terrainGridWidth;
h = h != 0 ? h : terrainGridLength;
iterationsDone = 0;
for (k = 0; k < numIterations;k++) {
z = xOff + Math.random() * w;
x = yOff + Math.random() * h;
iterationsDone++;
if (iterationsDone < itMinDisp)
disp = maxDisp + (iterationsDone/(itMinDisp+0.0))* (minDisp - maxDisp);
else
disp = minDisp;
r = Math.random();
if (r > 0.5)
dispSign = 1;
else
dispSign = -1;
for (i = 0;i < terrainGridLength; i++)
for(j = 0; j < terrainGridWidth; j++) {
pd = Math.sqrt(((i-x)*(i-x) + (j-z)*(j-z)) / terrainCircleSize)*2;
if (pd > 1) dispAux = 0.0;
else if (pd < -1) dispAux = 0.0;
else
dispAux = disp/2*dispSign + Math.cos(pd*3.14)*disp/2 * dispSign;
terrainHeights[i*terrainGridWidth + j] += dispAux;
}
}
}
private function deposit(x:int, z:int):void {
var j:int,k:int,kk:int,jj:int,flag:int;
flag = 0;
for (k=-1;k<2;k++)
for(j=-1;j<2;j++)
if (k!=0 && j!=0 && x+k>-1 && x+k<terrainGridWidth && z+j>-1 && z+j<terrainGridLength)
if (terrainHeights[(x+k) * terrainGridLength + (z+j)] < terrainHeights[x * terrainGridLength + z]) {
flag = 1;
kk = k;
jj = j;
}
if (!flag)
terrainHeights[x * terrainGridLength + z] += maxDisp;
else
deposit(x+kk,z+jj);
}
public function terrainIterateParticleDeposition( numIt:int):void {
var x:int,z:int,i:int,dir:int;
x = rand() % terrainGridWidth;
z = rand() % terrainGridLength;
for (i=0; i < numIt; i++) {
iterationsDone++;
dir = rand() % 4;
if (dir == 2) {
x++;
if (x >= terrainGridWidth)
x = 0;
}
else if (dir == 3){
x--;
if (x == -1)
x = terrainGridWidth-1;
}
else if (dir == 1) {
z++;
if (z >= terrainGridLength)
z = 0;
}
else if (dir == 0){
z--;
if (z == -1)
z = terrainGridLength - 1;
}
if (terrainParticleMode == ROLL)
deposit(x,z);
else
terrainHeights[x * terrainGridLength + z] += maxDisp;
}
}
public function BoxFilterHeightMap(
smoothEdges:Boolean = true, xStart:int=0, zStart:int=0, w:int = 0,h:int=0 ):void
{
// width: Width of the height map in bytes
// height: Height of the height map in bytes
// heightMap: Pointer to your height map data
// Temporary values for traversing single dimensional arrays
var x:int = xStart;
var z:int = zStart;
var width:int = terrainGridWidth;
var height:int = terrainGridLength;
w = w != 0 ? w : width;
h = h != 0 ? h : height;
var widthClamp:int = (smoothEdges) ? w : w - 1;
var heightClamp:int = (smoothEdges) ? h : h - 1;
// [Optimization] Calculate bounds ahead of time
var bounds:int = width * height;
// Validate requirements
// Allocate the result
var heightMap:Vector.<int> = terrainHeights;
var result:Vector.<int> = heightMap.concat(); // this should be a float
// Make sure memory was allocated
if (!result)
return;
for (z = (smoothEdges) ? 0 : 1; z < heightClamp; ++z)
{
for (x = (smoothEdges) ? 0 : 1; x < widthClamp; ++x)
{
// Sample a 3x3 filtering grid based on surrounding neighbors
var value:Number = 0.0;
var cellAverage:Number = 1.0;
// Sample top row
if (((x - 1) + (z - 1) * width) >= 0 &&
((x - 1) + (z - 1) * width) < bounds)
{
value += heightMap[(x - 1) + (z - 1) * width];
++cellAverage;
}
if (((x - 0) + (z - 1) * width) >= 0 &&
((x - 0) + (z - 1) * width) < bounds)
{
value += heightMap[(x ) + (z - 1) * width];
++cellAverage;
}
if (((x + 1) + (z - 1) * width) >= 0 &&
((x + 1) + (z - 1) * width) < bounds)
{
value += heightMap[(x + 1) + (z - 1) * width];
++cellAverage;
}
// Sample middle row
if (((x - 1) + (z - 0) * width) >= 0 &&
((x - 1) + (z - 0) * width) < bounds)
{
value += heightMap[(x - 1) + (z ) * width];
++cellAverage;
}
// Sample center point (will always be in bounds)
value += heightMap[x + z * width];
if (((x + 1) + (z - 0) * width) >= 0 &&
((x + 1) + (z - 0) * width) < bounds)
{
value += heightMap[(x + 1) + (z ) * width];
++cellAverage;
}
// Sample bottom row
if (((x - 1) + (z + 1) * width) >= 0 &&
((x - 1) + (z + 1) * width) < bounds)
{
value += heightMap[(x - 1) + (z + 1) * width];
++cellAverage;
}
if (((x - 0) + (z + 1) * width) >= 0 &&
((x - 0) + (z + 1) * width) < bounds)
{
value += heightMap[(x ) + (z + 1) * width];
++cellAverage;
}
if (((x + 1) + (z + 1) * width) >= 0 &&
((x + 1) + (z + 1) * width) < bounds)
{
value += heightMap[(x + 1) + (z + 1) * width];
++cellAverage;
}
// Store the result
result[x + z * width] = value / cellAverage;
}
}
// Store the new one
for (z = (smoothEdges) ? 0 : 1; z < heightClamp; z++)
{
for (x = (smoothEdges) ? 0 : 1; x < widthClamp; x++) {
heightMap[x+z*width] = result[x + z * width];
}
}
}
private function terrainHeight( x:int,z:int):int {
if (x > terrainGridWidth-1)
x -= (terrainGridWidth-1);
else if (x < 0)
x += terrainGridWidth-1;
if (z > terrainGridLength-1)
z -= (terrainGridLength-1);
else if (z < 0)
z += terrainGridLength-1;
assert(x>=0 && x < terrainGridWidth);
assert(z>=0 && z < terrainGridLength);
return(terrainHeights[x * terrainGridWidth + z]);
}
private function assert(cond:Boolean):void
{
if (!cond) throw new Error("Assertion failed!");
}
private function terrainRandom( dispH:Number):Number {
var r:Number;
r = ( Math.random() ) * dispH - (dispH * 0.5);
return(r);
}
private function terrainMPDDiamondStep( i:int,j:int,step:int,dispH:Number):void {
terrainHeights[(i+step/2)*terrainGridWidth + j+step/2] =
(terrainHeight(i,j) +
terrainHeight(i+step,j) +
terrainHeight(i+step,j+step) +
terrainHeight(i,j+step)) / 4;
terrainHeights[(i+step/2)*terrainGridWidth + j+step/2] += terrainRandom(dispH);
}
private function terrainMPDSquareStep( x1:int, z1:int, step:int, dispH:Number):void {
var i:int,j:int;
var x:int,z:int;
x = x1 + step/2;
z = z1 + step/2;
i = x + step/2;
j = z;
if (i == terrainGridLength-1)
terrainHeights[i*terrainGridWidth + j] =
(terrainHeight(i,j+step/2) +
terrainHeight(i,j-step/2) +
terrainHeight(i-step/2,j)) / 3;
else
terrainHeights[i*terrainGridWidth + j] =
(terrainHeight(i,j+step/2) +
terrainHeight(i,j-step/2) +
terrainHeight(i-step/2,j) +
terrainHeight(i+step/2,j)) / 4;
terrainHeights[i*terrainGridWidth + j] += terrainRandom(dispH);
j = z + step/2;
i = x;
if (j == terrainGridWidth-1)
terrainHeights[i*terrainGridWidth + j] =
(terrainHeight(i,j-step/2) +
terrainHeight(i-step/2,j) +
terrainHeight(i+step/2,j)) / 3;
else
terrainHeights[i*terrainGridWidth + j] =
(terrainHeight(i,j+step/2) +
terrainHeight(i,j-step/2) +
terrainHeight(i-step/2,j) +
terrainHeight(i+step/2,j)) / 4;
terrainHeights[i*terrainGridWidth + j] += terrainRandom(dispH);
i = x - step/2;
j = z;
if (i == 0){
terrainHeights[i*terrainGridWidth + j] =
(terrainHeight(i,j+step/2) +
terrainHeight(i,j-step/2) +
terrainHeight(i+step/2,j)) / 3;
terrainHeights[i*terrainGridWidth + j] += terrainRandom(dispH);
}
j = z - step/2;
i = x;
if (j == 0){
terrainHeights[i*terrainGridWidth + j] =
(terrainHeight(i,j+step/2) +
terrainHeight(i-step/2,j) +
terrainHeight(i+step/2,j)) / 3;
terrainHeights[i*terrainGridWidth + j] += terrainRandom(dispH);
}
}
public function terrainIterateMidPointDisplacement(steps:int,maxDispH:Number,r:Number):void {
var i:int,j:int,step:int;
var m:Number = maxDispH;
terrainGridWidth = int(Math.pow(2,steps)) + 1;
terrainGridLength = terrainGridWidth;
for (i=0;i<terrainGridWidth*terrainGridLength; i++)
terrainHeights[i] = 0.0;
iterationsDone = 0;
for (step = terrainGridWidth-1; step > 1; step /= 2 ) {
for (i = 0;i<terrainGridLength-2;i+=step)
for(j=0;j<terrainGridWidth-2;j+=step) {
terrainMPDDiamondStep(i,j,step,m);
}
for (i = 0;i<terrainGridLength-2;i+=step)
for(j=0;j<terrainGridWidth-2;j+=step) {
terrainMPDSquareStep(i,j,step,m);
}
m *= Math.pow(2,-r);
}
}
public function terrainIterateFault( numIterations:int):void {
var dispAux:Number;
var pd:Number;
var i:int,j:int,k:int,halfX:int,halfZ:int;
var a:Number,b:Number,c:Number,w:Number,d:Number;
halfX = terrainGridWidth / 2;
halfZ = terrainGridLength / 2;
for (k = 0; k < numIterations;k++) {
d = Math.sqrt(halfX * halfX + halfZ * halfZ);
w = rand();
a = Math.cos(w);
b = Math.sin(w);
c = Math.random() * 2*d - d;
iterationsDone++;
if (iterationsDone < itMinDisp)
disp = maxDisp + (iterationsDone/(itMinDisp+0.0))* (minDisp - maxDisp);
else
disp = minDisp;
for (i = 0;i < terrainGridLength; i++)
for(j = 0; j < terrainGridWidth; j++) {
switch(terrainFunction){
case STEP:
if ((i-halfZ) * a + (j-halfX) * b + c > 0)
dispAux = disp;
else
dispAux = -disp;
break;
case SIN:
pd = ((i-halfZ) * a + (j-halfX) * b + c)/terrainWaveSize;
if (pd > 1.57) pd = 1.57;
else if (pd < 0) pd = 0;
dispAux = -disp/2 + Math.sin(pd)*disp;
break;
case COS:
pd = ((i-halfZ) * a + (j-halfX) * b + c)/terrainWaveSize;
if (pd > 3.14) pd = 3.14;
else if (pd < -3.14) pd = -3.14;
dispAux = disp-(terrainWaveSize/(terrainGridWidth+0.0)) + Math.cos(pd)*disp;
break;
}
terrainHeights[i*terrainGridWidth + j] += dispAux;
}
}
}
/**
*
* @param k Smoothness ratio betwene 0-1. A value of 1 would flatten terrain. Smaller numbers indiciate smoothing amount.
*/
public function terrainSmooth(k:Number, x:int = 0, y:int = 0, w:int = 0, h:int=0 ):void {
var i:int;
var j:int;
w = w != 0 ? w : terrainGridWidth;
h = h != 0 ? h : terrainGridLength;
for(i=0+y;i<h+y;i++)
for(j=1+x;j<w+x;j++)
terrainHeights[i*terrainGridWidth + j] =
terrainHeights[i*terrainGridWidth + j] * (1-k) +
terrainHeights[i*terrainGridWidth + j-1] * k;
for(i=1+y;i<h+y;i++)
for(j=0+x;j<w+x;j++)
terrainHeights[i*terrainGridWidth + j] =
terrainHeights[i*terrainGridWidth + j] * (1-k) +
terrainHeights[(i-1)*terrainGridWidth + j] * k;
for(i=0+y; i<h+y; i++)
for(j=w-2+x;j>-1+x;j--)
terrainHeights[i*terrainGridWidth + j] =
terrainHeights[i*terrainGridWidth + j] * (1-k) +
terrainHeights[i * terrainGridWidth + j + 1] * k;
for(i=h-2 + y;i<-1+y;i--)
for(j=0+x;j<w+x;j++)
terrainHeights[i*terrainGridWidth + j] =
terrainHeights[i*terrainGridWidth + j] * (1-k) +
terrainHeights[(i+1)*terrainGridWidth + j] * k;
//if (terrainNormals != NULL)
//terrainComputeNormals();
}
public function adjustHeights(amount:int, x:int = 0, y:int = 0, w:int = 0, h:int=0 ):void
{
w = w != 0 ? w + x : x + terrainGridWidth;
h = h != 0 ? h + y : y + terrainGridLength;
var xStart:int = x;
var yStart:int = y;
for (y = yStart; y < h; y++) {
for (x =xStart; x < w; x++) {
terrainHeights[y * terrainGridWidth + x] += amount;
}
}
}
}
//}