Initial commit
This commit is contained in:
BIN
libgdx-ProceduralTerrain/core/assets/badlogic.jpg
Normal file
BIN
libgdx-ProceduralTerrain/core/assets/badlogic.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
11
libgdx-ProceduralTerrain/core/build.gradle
Normal file
11
libgdx-ProceduralTerrain/core/build.gradle
Normal file
@ -0,0 +1,11 @@
|
||||
apply plugin: "java"
|
||||
|
||||
sourceCompatibility = 1.6
|
||||
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
|
||||
|
||||
sourceSets.main.java.srcDirs = [ "src/" ]
|
||||
|
||||
|
||||
eclipse.project {
|
||||
name = appName + "-core"
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
package tibi.ProceduralTerrain.Business;
|
||||
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import tibi.ProceduralTerrain.Model.*;
|
||||
import tibi.ProceduralTerrain.Model.Map;
|
||||
import tibi.ProceduralTerrain.Noise.NoiseGenerator;
|
||||
import tibi.ProceduralTerrain.Noise.SimplexNoiseGenerator;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/20/15.
|
||||
*/
|
||||
public class MapGenerator
|
||||
{
|
||||
final float elevationNoiseScale = 0.004f;
|
||||
final float waterNoiseScale = 0.009f;
|
||||
|
||||
NoiseGenerator noiseGenerator = new SimplexNoiseGenerator();
|
||||
int w, h;
|
||||
|
||||
private class WaterQueueItem
|
||||
{
|
||||
Corner c;
|
||||
int x, y;
|
||||
|
||||
WaterQueueItem(int x, int y, Corner c)
|
||||
{
|
||||
this.c = c;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
private static Biome generateBiome()
|
||||
{
|
||||
int a = 4;//MathUtils.random(4);
|
||||
|
||||
switch (a)
|
||||
{
|
||||
case 0:
|
||||
System.out.println("Generated biome: Mountain");
|
||||
return Biome.MOUNTAIN;
|
||||
|
||||
case 1:
|
||||
System.out.println("Generated biome: Snow");
|
||||
return Biome.SNOW;
|
||||
|
||||
case 2:
|
||||
System.out.println("Generated biome: Grassland");
|
||||
return Biome.GRASSLAND;
|
||||
|
||||
default:
|
||||
System.out.println("Generated biome: All");
|
||||
return Biome.ALL;
|
||||
}
|
||||
}
|
||||
|
||||
private static void displaceCorners(Map map)
|
||||
{
|
||||
int w = map.width();
|
||||
int h = map.height();
|
||||
|
||||
for (int x = 1; x < w; ++x)
|
||||
for (int y = 1; y < h; ++y)
|
||||
{
|
||||
// Restrict corner movement to the 4 centers
|
||||
Vector2 min = map.get(x - 1, y - 1).center;
|
||||
Vector2 max = map.get(x, y).center;
|
||||
|
||||
// Move corner
|
||||
map.corner(x, y).pos = new Vector2(
|
||||
MathUtils.random(min.x, max.x),
|
||||
MathUtils.random(min.y, max.y));
|
||||
}
|
||||
}
|
||||
|
||||
public Map generate(int w, int h)
|
||||
{
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
|
||||
// Create map
|
||||
Map map = new Map(w, h);
|
||||
map.biome = generateBiome();
|
||||
//map.drawToFile("/home/tibi/Pictures/tmp/0_InitialMap");
|
||||
|
||||
// Displace corners
|
||||
displaceCorners(map);
|
||||
//map.drawToFile("/home/tibi/Pictures/tmp/1_DistortedMap");
|
||||
|
||||
// Generate water
|
||||
generateWater(map);
|
||||
//map.drawToFile("/home/tibi/Pictures/tmp/2_WithWater");
|
||||
|
||||
// Elevation
|
||||
generateElevation(map);
|
||||
//map.drawToFile("/home/tibi/Pictures/tmp/3_Elevation");
|
||||
map.drawToFile("/home/tibi/Pictures/tmp/3_ElevationFancy", Map.DRAW_ELEVATION);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private void generateWater(Map map)
|
||||
{
|
||||
Queue<WaterQueueItem> waterCorners = new LinkedList<WaterQueueItem>();
|
||||
|
||||
float waterAmount = MathUtils.random(map.biome.minMoisture, map.biome.maxMoisture);
|
||||
for (int x = 0; x < w; ++x)
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
if (noiseGenerator.generate(x, y, 0, 1, waterNoiseScale, 4, .5f) <= waterAmount)
|
||||
{
|
||||
map.get(x, y).isWater = true;
|
||||
|
||||
// Set distance to water to corners
|
||||
map.corner(x, y).distanceToWater = 0;
|
||||
map.corner(x + 1, y).distanceToWater = 0;
|
||||
map.corner(x, y + 1).distanceToWater = 0;
|
||||
map.corner(x + 1, y + 1).distanceToWater = 0;
|
||||
|
||||
waterCorners.add(new WaterQueueItem(x, y, map.corner(x, y)));
|
||||
waterCorners.add(new WaterQueueItem(x + 1, y, map.corner(x + 1, y)));
|
||||
waterCorners.add(new WaterQueueItem(x, y + 1, map.corner(x, y + 1)));
|
||||
waterCorners.add(new WaterQueueItem(x + 1, y + 1, map.corner(x + 1, y + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate distances to water
|
||||
calculateDistanceToWater(map, waterCorners);
|
||||
}
|
||||
|
||||
private void calculateDistanceToWater(Map map, Queue<WaterQueueItem> queue)
|
||||
{
|
||||
// Directions: N E S W
|
||||
final int[] dx = {0, 1, 0, -1};
|
||||
final int[] dy = {-1, 0, 1, 0};
|
||||
|
||||
while (!queue.isEmpty())
|
||||
{
|
||||
WaterQueueItem current = queue.remove();
|
||||
|
||||
for (int k = 0; k < 4; k++)
|
||||
{
|
||||
int newX = current.x + dx[k];
|
||||
int newY = current.y + dy[k];
|
||||
|
||||
// Ignore if out of bounds
|
||||
if (newX < 0 || newY < 0 || newX >= w + 1 || newY >= h + 1)
|
||||
continue;
|
||||
|
||||
// Get corner to visit
|
||||
Corner newC = map.corner(newX, newY);
|
||||
|
||||
// Visit if not visited, or if distance is smaller
|
||||
if (newC.distanceToWater == Corner.INF_DIST || newC.distanceToWater > current.c.distanceToWater + 1)
|
||||
{
|
||||
newC.distanceToWater = current.c.distanceToWater + 1;
|
||||
queue.add(new WaterQueueItem(newX, newY, newC));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateElevation(Map map)
|
||||
{
|
||||
noiseGenerator.setOctaves(9);
|
||||
noiseGenerator.setNonLinearPower(3f);
|
||||
noiseGenerator.setScale(elevationNoiseScale);
|
||||
|
||||
final float waterImpact = 1f;
|
||||
final float noiseImpact = 3f;
|
||||
|
||||
for (int x = 0; x < w + 1; ++x)
|
||||
for (int y = 0; y < h + 1; ++y)
|
||||
{
|
||||
// No water body on map
|
||||
if (map.corner(x, y).distanceToWater == Corner.INF_DIST)
|
||||
{
|
||||
map.corner(x, y).elevation = noiseGenerator.generate(x, y, map.biome.minHeight, map.biome.maxHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
float noiseVal = noiseGenerator.generate(x, y, 0, 1);
|
||||
|
||||
float distFromWater = (float)map.corner(x, y).distanceToWater / (float)Math.max(w, h);
|
||||
distFromWater = (float)Math.pow(distFromWater, .3); // non-linear scale
|
||||
|
||||
float elevation = (noiseVal * noiseImpact + distFromWater * waterImpact) / (waterImpact + noiseImpact);
|
||||
|
||||
// Rescale value
|
||||
map.corner(x, y).elevation = elevation * (map.biome.maxHeight - map.biome.minHeight) + map.biome.minHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package tibi.ProceduralTerrain.Business;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.VertexAttributes;
|
||||
import com.badlogic.gdx.graphics.g3d.Material;
|
||||
import com.badlogic.gdx.graphics.g3d.Model;
|
||||
import com.badlogic.gdx.graphics.g3d.ModelInstance;
|
||||
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
|
||||
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import tibi.ProceduralTerrain.Model.Cell;
|
||||
import tibi.ProceduralTerrain.Model.Corner;
|
||||
import tibi.ProceduralTerrain.Model.Map;
|
||||
import tibi.ProceduralTerrain.Noise.NoiseGenerator;
|
||||
import tibi.ProceduralTerrain.Noise.SimplexNoiseGenerator;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/27/15.
|
||||
*/
|
||||
public class MapMeshGenerator
|
||||
{
|
||||
final int chunkSize = 20;
|
||||
final int detailLevels = 1;
|
||||
final float strength = 1;
|
||||
|
||||
final Material material = new Material();
|
||||
|
||||
NoiseGenerator noiseGenerator = new SimplexNoiseGenerator();
|
||||
|
||||
Map map;
|
||||
Model[][][] chunks;
|
||||
ModelInstance[][][] chunksInstances;
|
||||
|
||||
int chunksW;
|
||||
int chunksH;
|
||||
|
||||
public MapMeshGenerator(Map map)
|
||||
{
|
||||
this.map = map;
|
||||
|
||||
chunksW = map.width() / chunkSize;
|
||||
chunksH = map.height() / chunkSize;
|
||||
|
||||
chunks = new Model[detailLevels][chunksW][chunksH];
|
||||
chunksInstances = new ModelInstance[detailLevels][chunksW][chunksH];
|
||||
}
|
||||
|
||||
public ModelInstance get(int x, int y, int detailLevel)
|
||||
{
|
||||
if (chunksInstances[detailLevel][x][y] == null)
|
||||
createInstance(x, y, detailLevel);
|
||||
|
||||
return chunksInstances[detailLevel][x][y];
|
||||
}
|
||||
|
||||
private void createInstance(int x, int y, int detailLevel)
|
||||
{
|
||||
if (chunks[detailLevel][x][y] == null)
|
||||
generateChunk(x, y, detailLevel);
|
||||
|
||||
chunksInstances[detailLevel][x][y] = new ModelInstance(chunks[detailLevel][x][y]);
|
||||
}
|
||||
|
||||
private void generateChunk(int chunkX, int chunkY, int detailLevel)
|
||||
{
|
||||
ModelBuilder modelBuilder = new ModelBuilder();
|
||||
modelBuilder.begin();
|
||||
|
||||
MeshPartBuilder builder = modelBuilder.part(
|
||||
"chunk",
|
||||
GL20.GL_TRIANGLES,
|
||||
VertexAttributes.Usage.Position | VertexAttributes.Usage.ColorPacked, //| VertexAttributes.Usage.Normal | VertexAttributes.Usage.ColorPacked,
|
||||
material);
|
||||
|
||||
for (int x = chunkX * chunkSize; x < (chunkX + 1) * chunkSize; x++)
|
||||
for (int y = chunkY * chunkSize; y < (chunkY + 1) * chunkSize; y++)
|
||||
{
|
||||
Cell cell = map.get(x, y);
|
||||
|
||||
Vector3 pos00 = new Vector3(
|
||||
cell.corner00.pos.x,
|
||||
strength * cell.corner00.elevation,
|
||||
cell.corner00.pos.y);
|
||||
|
||||
Vector3 pos01 = new Vector3(
|
||||
cell.corner01.pos.x,
|
||||
strength * cell.corner01.elevation,
|
||||
cell.corner01.pos.y);
|
||||
|
||||
Vector3 pos10 = new Vector3(
|
||||
cell.corner10.pos.x,
|
||||
strength * cell.corner10.elevation,
|
||||
cell.corner10.pos.y);
|
||||
|
||||
Vector3 pos11 = new Vector3(
|
||||
cell.corner11.pos.x,
|
||||
strength * cell.corner11.elevation,
|
||||
cell.corner11.pos.y);
|
||||
|
||||
Color col00 = new Color();
|
||||
Color col01 = new Color();
|
||||
Color col10 = new Color();
|
||||
Color col11 = new Color();
|
||||
|
||||
Color.argb8888ToColor(col00, Map.getColorOfTerrain(cell.corner00.elevation).getRGB());
|
||||
Color.argb8888ToColor(col01, Map.getColorOfTerrain(cell.corner01.elevation).getRGB());
|
||||
Color.argb8888ToColor(col10, Map.getColorOfTerrain(cell.corner10.elevation).getRGB());
|
||||
Color.argb8888ToColor(col11, Map.getColorOfTerrain(cell.corner11.elevation).getRGB());
|
||||
|
||||
if (cell.isWater)
|
||||
col00 = col01 = col10 = col11 = Color.BLUE;
|
||||
|
||||
// Calculate normals
|
||||
Vector3 normal1 = getNormal(pos10, pos01, pos00);
|
||||
Vector3 normal2 = getNormal(pos11, pos01, pos10);
|
||||
|
||||
// Create vertices
|
||||
MeshPartBuilder.VertexInfo v1 = new MeshPartBuilder.VertexInfo().setPos(pos10).setNor(normal1).setCol(col10);
|
||||
MeshPartBuilder.VertexInfo v2 = new MeshPartBuilder.VertexInfo().setPos(pos01).setNor(normal1).setCol(col01);
|
||||
MeshPartBuilder.VertexInfo v3 = new MeshPartBuilder.VertexInfo().setPos(pos00).setNor(normal1).setCol(col00);
|
||||
|
||||
MeshPartBuilder.VertexInfo v4 = new MeshPartBuilder.VertexInfo().setPos(pos11).setNor(normal2).setCol(col11);
|
||||
MeshPartBuilder.VertexInfo v5 = new MeshPartBuilder.VertexInfo().setPos(pos01).setNor(normal2).setCol(col01);
|
||||
MeshPartBuilder.VertexInfo v6 = new MeshPartBuilder.VertexInfo().setPos(pos10).setNor(normal2).setCol(col10);
|
||||
|
||||
// Create triangles
|
||||
// builder.triangle(v1, v2, v3);
|
||||
// builder.triangle(v4, v5, v6);
|
||||
builder.triangle(v6, v5, v4);
|
||||
builder.triangle(v3, v2, v1);
|
||||
}
|
||||
|
||||
chunks[detailLevel][chunkX][chunkY] = modelBuilder.end();
|
||||
}
|
||||
|
||||
private Vector3 getNormal(Vector3 a, Vector3 b, Vector3 c)
|
||||
{
|
||||
Vector3 u = new Vector3(b).sub(a);
|
||||
Vector3 v = new Vector3(c).sub(b);
|
||||
return u.crs(v).nor();
|
||||
}
|
||||
|
||||
public int getChunksW()
|
||||
{
|
||||
return chunksW;
|
||||
}
|
||||
|
||||
public int getChunksH()
|
||||
{
|
||||
return chunksH;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package tibi.ProceduralTerrain.Business;
|
||||
|
||||
import tibi.ProceduralTerrain.Model.Terrain;
|
||||
import tibi.ProceduralTerrain.Noise.NoiseGenerator;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/19/15.
|
||||
*/
|
||||
public class TerrainGenerator
|
||||
{
|
||||
public Terrain generate(int w, int h, NoiseGenerator noiseGenerator)
|
||||
{
|
||||
Terrain terrain = new Terrain(w, h);
|
||||
|
||||
for (int x = 0; x < w; ++x)
|
||||
for (int y = 0; y < h; ++y)
|
||||
terrain.set(x, y, noiseGenerator.generate(x, y, 0, 1));
|
||||
|
||||
return terrain;
|
||||
}
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
package tibi.ProceduralTerrain.Business;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.VertexAttributes;
|
||||
import com.badlogic.gdx.graphics.g3d.Material;
|
||||
import com.badlogic.gdx.graphics.g3d.Model;
|
||||
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
|
||||
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import tibi.ProceduralTerrain.Model.Terrain;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/19/15.
|
||||
*/
|
||||
public class TerrainMeshGenerator
|
||||
{
|
||||
float strength = 6f;
|
||||
float gridDensity = .1f;
|
||||
|
||||
int chunkSize = 32;
|
||||
|
||||
public float getStrength()
|
||||
{
|
||||
return strength;
|
||||
}
|
||||
|
||||
public void setStrength(float strength)
|
||||
{
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
public float getGridDensity()
|
||||
{
|
||||
return gridDensity;
|
||||
}
|
||||
|
||||
public void setGridDensity(float gridDensity)
|
||||
{
|
||||
this.gridDensity = gridDensity;
|
||||
}
|
||||
|
||||
public int getChunkSize()
|
||||
{
|
||||
return chunkSize;
|
||||
}
|
||||
|
||||
public void setChunkSize(int chunkSize)
|
||||
{
|
||||
this.chunkSize = chunkSize;
|
||||
}
|
||||
|
||||
public Model generate(Terrain terrain)
|
||||
{
|
||||
// Make terrain centered
|
||||
float offsetX = -terrain.width() * gridDensity * .5f;
|
||||
float offsetY = -terrain.height() * gridDensity * .5f;
|
||||
|
||||
ModelBuilder modelBuilder = new ModelBuilder();
|
||||
modelBuilder.begin();
|
||||
|
||||
int chunksX = terrain.width() / chunkSize + ((terrain.width() % chunkSize > 0) ? 1 : 0);
|
||||
int chunksY = terrain.height() / chunkSize + ((terrain.height() % chunkSize > 0) ? 1 : 0);
|
||||
|
||||
for (int chunkX = 0; chunkX < chunksX; chunkX++)
|
||||
for (int chunkY = 0; chunkY < chunksY; chunkY++)
|
||||
{
|
||||
MeshPartBuilder builder = modelBuilder.part(
|
||||
String.format("chunk_%d_%d", chunkX, chunkY),
|
||||
GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.ColorPacked,
|
||||
new Material());
|
||||
|
||||
for(int x = chunkX * chunkSize; x < (chunkX + 1) * chunkSize && x < terrain.width() - 1; x++)
|
||||
for (int y = chunkY * chunkSize; y < (chunkY + 1) * chunkSize && y < terrain.height() - 1; y++)
|
||||
{
|
||||
// Calculate corners
|
||||
Vector3 pos00 = new Vector3(offsetX + x * gridDensity,
|
||||
strength * terrain.get(x, y),
|
||||
offsetY + y * gridDensity);
|
||||
|
||||
Vector3 pos01 = new Vector3(
|
||||
offsetX + (x + 1) * gridDensity,
|
||||
strength * terrain.get(x + 1, y),
|
||||
offsetY + y * gridDensity);
|
||||
|
||||
Vector3 pos10 = new Vector3(
|
||||
offsetX + x * gridDensity,
|
||||
strength * terrain.get(x, y + 1),
|
||||
offsetY + (y + 1) * gridDensity);
|
||||
|
||||
Vector3 pos11 = new Vector3(
|
||||
offsetX + (x + 1) * gridDensity,
|
||||
strength * terrain.get(x + 1, y + 1),
|
||||
offsetY + (y + 1) * gridDensity);
|
||||
|
||||
// Calculate colors
|
||||
Color col00 = getColor(terrain.get(x, y));
|
||||
Color col01 = getColor(terrain.get(x + 1, y));
|
||||
Color col10 = getColor(terrain.get(x, y + 1));
|
||||
Color col11 = getColor(terrain.get(x + 1, y + 1));
|
||||
|
||||
// Calculate normals
|
||||
Vector3 normal1 = getNormal(pos10, pos01, pos00);
|
||||
Vector3 normal2 = getNormal(pos11, pos01, pos10);
|
||||
|
||||
// Create vertices
|
||||
MeshPartBuilder.VertexInfo v1 = new MeshPartBuilder.VertexInfo().setPos(pos10).setNor(normal1).setCol(col10);
|
||||
MeshPartBuilder.VertexInfo v2 = new MeshPartBuilder.VertexInfo().setPos(pos01).setNor(normal1).setCol(col01);
|
||||
MeshPartBuilder.VertexInfo v3 = new MeshPartBuilder.VertexInfo().setPos(pos00).setNor(normal1).setCol(col00);
|
||||
|
||||
MeshPartBuilder.VertexInfo v4 = new MeshPartBuilder.VertexInfo().setPos(pos11).setNor(normal2).setCol(col11);
|
||||
MeshPartBuilder.VertexInfo v5 = new MeshPartBuilder.VertexInfo().setPos(pos01).setNor(normal2).setCol(col01);
|
||||
MeshPartBuilder.VertexInfo v6 = new MeshPartBuilder.VertexInfo().setPos(pos10).setNor(normal2).setCol(col10);
|
||||
|
||||
// Create triangles
|
||||
builder.triangle(v1, v2, v3);
|
||||
builder.triangle(v4, v5, v6);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish mesh
|
||||
return modelBuilder.end();
|
||||
}
|
||||
|
||||
private Color lerp(Color a, Color b, float alpha)
|
||||
{
|
||||
return new Color(
|
||||
MathUtils.lerp(a.r, b.r, alpha),
|
||||
MathUtils.lerp(a.g, b.g, alpha),
|
||||
MathUtils.lerp(a.b, b.b, alpha),
|
||||
MathUtils.lerp(a.a, b.a, alpha));
|
||||
}
|
||||
|
||||
private Color lerp2(Color a, Color b, float alpha)
|
||||
{
|
||||
return lerp(a, b, alpha * alpha);
|
||||
}
|
||||
|
||||
private Color getColor(float height)
|
||||
{
|
||||
// OUT OF RANGE
|
||||
if (height < 0 || height > 1)
|
||||
return Color.RED;
|
||||
|
||||
final Color water = new Color(.3f, .5f, .8f, 0);
|
||||
final Color beach = new Color(.75f, .78f, .05f, 0);
|
||||
final Color grass = new Color(.1f, .6f, .3f, 0);
|
||||
final Color dirt = new Color(.46f, .46f, 0, 0);
|
||||
final Color stone = Color.GRAY;
|
||||
final Color snow = Color.WHITE;
|
||||
|
||||
// Water
|
||||
if (height < .3f)
|
||||
return water;
|
||||
|
||||
// Blend between beach and grass
|
||||
if (height < .4f)
|
||||
return lerp2(beach, grass, (height - .3f) / .1f); // [0.3,0.4] to [0,1]
|
||||
|
||||
// Blend between grass and dirt
|
||||
if (height < .6f)
|
||||
return lerp2(grass, dirt, (height - .4f) / .2f); // [0.4,0.6] to [0,1]
|
||||
|
||||
// Blend between dirt and stone
|
||||
if (height < .65f)
|
||||
return lerp2(dirt, stone, (height - .6f) / .05f); // [0.6,0.65] to [0,1]
|
||||
|
||||
// Blend between stone and snow
|
||||
if (height < .85f)
|
||||
return lerp2(stone, snow, (height - .75f) / .2f); // [0.75,0.85] to [0,1]
|
||||
|
||||
// Snow
|
||||
return Color.WHITE;
|
||||
|
||||
//return new Color(height, height, height, 1.0f);
|
||||
}
|
||||
|
||||
private Vector3 getNormal(Vector3 a, Vector3 b, Vector3 c)
|
||||
{
|
||||
Vector3 u = new Vector3(b).sub(a);
|
||||
Vector3 v = new Vector3(c).sub(b);
|
||||
return u.crs(v).nor();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package tibi.ProceduralTerrain.Model;
|
||||
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/21/15.
|
||||
*/
|
||||
public class Biome
|
||||
{
|
||||
public float minMoisture; // Probability of rivers,
|
||||
public float maxMoisture; // water bodies
|
||||
|
||||
public float minHeight; // Height range to generate
|
||||
public float maxHeight; // in hundreds of meters
|
||||
|
||||
public float slope; // Maximum steepness of hills - in degrees
|
||||
|
||||
public float mountains; // Probability to generate steep mountains
|
||||
|
||||
|
||||
public static Biome MOUNTAIN;
|
||||
public static Biome SNOW;
|
||||
public static Biome GRASSLAND;
|
||||
public static Biome ALL;
|
||||
|
||||
static
|
||||
{
|
||||
MOUNTAIN = new Biome();
|
||||
MOUNTAIN.minMoisture = .1f;
|
||||
MOUNTAIN.maxMoisture = .3f;
|
||||
MOUNTAIN.minHeight = 15f;
|
||||
MOUNTAIN.maxHeight = 50f;
|
||||
MOUNTAIN.slope = 45;
|
||||
MOUNTAIN.mountains = 1f;
|
||||
|
||||
SNOW = new Biome();
|
||||
SNOW.minMoisture = 0f;
|
||||
SNOW.maxMoisture = .1f;
|
||||
SNOW.minHeight = 0f;
|
||||
SNOW.maxHeight = 10f;
|
||||
SNOW.slope = 15;
|
||||
SNOW.mountains = .1f;
|
||||
|
||||
GRASSLAND = new Biome();
|
||||
GRASSLAND.minMoisture = .2f;
|
||||
GRASSLAND.maxMoisture = .5f;
|
||||
GRASSLAND.minHeight = .01f;
|
||||
GRASSLAND.maxHeight = 7f;
|
||||
GRASSLAND.slope = 12;
|
||||
GRASSLAND.mountains = .05f;
|
||||
|
||||
ALL = new Biome();
|
||||
ALL.minMoisture = .1f;
|
||||
ALL.maxMoisture = .5f;
|
||||
ALL.minHeight = 0f;
|
||||
ALL.maxHeight = 50f;
|
||||
ALL.slope = 45;
|
||||
ALL.mountains = 1f;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package tibi.ProceduralTerrain.Model;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/20/15.
|
||||
*/
|
||||
public class Cell
|
||||
{
|
||||
public Vector2 center;
|
||||
public Corner corner00, corner01, corner10, corner11;
|
||||
public boolean isWater;
|
||||
|
||||
public Cell()
|
||||
{
|
||||
center = new Vector2();
|
||||
corner00 = new Corner();
|
||||
corner01 = new Corner();
|
||||
corner10 = new Corner();
|
||||
corner11 = new Corner();
|
||||
isWater = false;
|
||||
}
|
||||
|
||||
public float elevation()
|
||||
{
|
||||
return .25f * corner00.elevation
|
||||
+ .25f * corner01.elevation
|
||||
+ .25f * corner10.elevation
|
||||
+ .25f * corner11.elevation;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package tibi.ProceduralTerrain.Model;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/20/15.
|
||||
*/
|
||||
public class Corner
|
||||
{
|
||||
public final static int INF_DIST = -1;
|
||||
|
||||
public Vector2 pos;
|
||||
public float elevation;
|
||||
public int distanceToWater;
|
||||
|
||||
public Corner()
|
||||
{
|
||||
this.pos = new Vector2();
|
||||
this.elevation = 0;
|
||||
this.distanceToWater = INF_DIST;
|
||||
}
|
||||
|
||||
public Corner(Vector2 pos)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.elevation = 0;
|
||||
this.distanceToWater = INF_DIST;
|
||||
}
|
||||
|
||||
public Corner(float x, float y)
|
||||
{
|
||||
this.pos = new Vector2(x, y);
|
||||
this.elevation = 0;
|
||||
this.distanceToWater = INF_DIST;
|
||||
}
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
package tibi.ProceduralTerrain.Model;
|
||||
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/20/15.
|
||||
*/
|
||||
public class Map
|
||||
{
|
||||
private Cell[][] grid;
|
||||
private Corner[][] corners;
|
||||
public Biome biome;
|
||||
|
||||
public Map(int w, int h)
|
||||
{
|
||||
// Generate grid
|
||||
grid = new Cell[w][h];
|
||||
corners = new Corner[w+1][h+1];
|
||||
|
||||
for (int x = 0; x < w + 1; ++x)
|
||||
for (int y = 0; y < h + 1; ++y)
|
||||
corners[x][y] = new Corner(x, y);
|
||||
|
||||
// Generate cells
|
||||
for (int x = 0; x < w; ++x)
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
grid[x][y] = new Cell();
|
||||
grid[x][y].center = new Vector2(x + .5f, y + .5f);
|
||||
|
||||
grid[x][y].corner00 = corner(x, y);
|
||||
grid[x][y].corner01 = corner(x, y + 1);
|
||||
grid[x][y].corner10 = corner(x + 1, y);
|
||||
grid[x][y].corner11 = corner(x + 1, y + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public Cell get(int x, int y)
|
||||
{
|
||||
return grid[x][y];
|
||||
}
|
||||
|
||||
public Corner corner(int x, int y)
|
||||
{
|
||||
return corners[x][y];
|
||||
}
|
||||
|
||||
public int width()
|
||||
{
|
||||
return grid.length;
|
||||
}
|
||||
|
||||
public int height()
|
||||
{
|
||||
return grid[0].length;
|
||||
}
|
||||
|
||||
public final static int DRAW_ELEVATION = 0;
|
||||
public final static int DRAW_GRID = 1;
|
||||
public final static int DRAW_CENTERS = 2;
|
||||
|
||||
public void drawToFile(String filename)
|
||||
{
|
||||
drawToFile(filename, DRAW_ELEVATION | DRAW_GRID | DRAW_CENTERS);
|
||||
}
|
||||
|
||||
public void drawToFile(String filename, int draw_options)
|
||||
{
|
||||
final int scale = 10;
|
||||
|
||||
// Draw map
|
||||
BufferedImage img = new BufferedImage(scale * width(), scale * height(), BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = img.createGraphics();
|
||||
|
||||
// Draw cells
|
||||
for (int x = 0; x < width(); ++x)
|
||||
{
|
||||
for (int y = 0; y < height(); ++y)
|
||||
{
|
||||
Cell c = get(x, y);
|
||||
Vector2 c00 = new Vector2(c.corner00.pos).scl(scale);
|
||||
Vector2 c01 = new Vector2(c.corner01.pos).scl(scale);
|
||||
Vector2 c10 = new Vector2(c.corner10.pos).scl(scale);
|
||||
Vector2 c11 = new Vector2(c.corner11.pos).scl(scale);
|
||||
|
||||
// Fill cell
|
||||
Polygon p = new Polygon();
|
||||
p.addPoint((int)c00.x, (int)c00.y);
|
||||
p.addPoint((int)c01.x, (int)c01.y);
|
||||
p.addPoint((int)c11.x, (int)c11.y);
|
||||
p.addPoint((int) c10.x, (int)c10.y);
|
||||
|
||||
if (c.isWater)
|
||||
{
|
||||
g.setColor(Color.BLUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
g.setColor(getColorOfTerrain(c.elevation()));
|
||||
}
|
||||
|
||||
g.fillPolygon(p);
|
||||
|
||||
if ((draw_options & DRAW_CENTERS) > 0)
|
||||
{
|
||||
// Draw center
|
||||
Vector2 center = new Vector2(c.center).scl(scale);
|
||||
|
||||
g.setColor(Color.RED);
|
||||
g.fillOval((int)center.x - 2, (int)center.y - 2, 5, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((draw_options & DRAW_GRID) > 0)
|
||||
{
|
||||
// Draw corners
|
||||
for (int x = 0; x < width() + 1; ++x)
|
||||
for (int y = 0; y < height() + 1; ++y)
|
||||
{
|
||||
Corner c = corner(x, y);
|
||||
Vector2 cp = new Vector2(c.pos).scl(scale);
|
||||
|
||||
// Draw edges
|
||||
g.setColor(Color.LIGHT_GRAY);
|
||||
if (x > 0)
|
||||
{
|
||||
Vector2 cp1 = new Vector2(corner(x - 1, y).pos).scl(scale);
|
||||
g.drawLine((int) cp.x, (int) cp.y, (int) cp1.x, (int) cp1.y);
|
||||
}
|
||||
if (y > 0)
|
||||
{
|
||||
Vector2 cp1 = new Vector2(corner(x, y - 1).pos).scl(scale);
|
||||
g.drawLine((int) cp.x, (int) cp.y, (int) cp1.x, (int) cp1.y);
|
||||
}
|
||||
|
||||
// Draw corner
|
||||
g.setColor(Color.WHITE);
|
||||
g.fillOval((int) cp.x - 2, (int) cp.y - 2, 5, 5);
|
||||
|
||||
// For debugging - draw text
|
||||
// g.setColor(Color.YELLOW);
|
||||
// g.drawString(String.format("%d", c.distanceToWater), cp.x, cp.y);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File outputFile = new File(filename + ".png");
|
||||
ImageIO.write(img, "png", outputFile);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static Color getColorOfTerrain(float height)
|
||||
{
|
||||
Color color1, color2;
|
||||
float alpha;
|
||||
|
||||
if (height < 10f)
|
||||
{
|
||||
color1 = new Color(0x00ffa2);
|
||||
color2 = new Color(0xFCD628);
|
||||
alpha = height / 10f;
|
||||
}
|
||||
|
||||
else if (height < 25f)
|
||||
{
|
||||
color1 = new Color(0xFCD628);
|
||||
color2 = new Color(0x9C6713);
|
||||
alpha = (height - 10f) / 15f;
|
||||
}
|
||||
|
||||
else if (height < 50f)
|
||||
{
|
||||
color1 = new Color(0xaaaaaa);
|
||||
color2 = new Color(0xffffff);
|
||||
alpha = (height - 25f) / 25f;
|
||||
}
|
||||
else
|
||||
{
|
||||
color1 = color2 = Color.WHITE;
|
||||
alpha = 1;
|
||||
}
|
||||
|
||||
return new Color(
|
||||
(int)MathUtils.lerp(color1.getRed(), color2.getRed(), alpha),
|
||||
(int)MathUtils.lerp(color1.getGreen(), color2.getGreen(), alpha),
|
||||
(int)MathUtils.lerp(color1.getBlue(), color2.getBlue(), alpha));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package tibi.ProceduralTerrain.Model;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/19/15.
|
||||
*/
|
||||
public class Terrain
|
||||
{
|
||||
float[][] heightMap;
|
||||
|
||||
public Terrain(int w, int h)
|
||||
{
|
||||
heightMap = new float[w][h];
|
||||
}
|
||||
|
||||
public float get(int x, int y)
|
||||
{
|
||||
if (heightMap[x][y] < .29f)
|
||||
return .29f;
|
||||
|
||||
return heightMap[x][y];
|
||||
}
|
||||
|
||||
public void set(int x, int y, float value)
|
||||
{
|
||||
heightMap[x][y] = value;
|
||||
}
|
||||
|
||||
public int height()
|
||||
{
|
||||
return heightMap[0].length;
|
||||
}
|
||||
|
||||
public int width()
|
||||
{
|
||||
return heightMap.length;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package tibi.ProceduralTerrain.Noise;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/20/15.
|
||||
*/
|
||||
public class NeighbourNoiseGenerator extends NoiseGenerator
|
||||
{
|
||||
NoiseGenerator whiteNoiseGenerator;
|
||||
|
||||
public NeighbourNoiseGenerator()
|
||||
{
|
||||
whiteNoiseGenerator = new WhiteNoiseGenerator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates noise in [-1,1] interval
|
||||
*
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @return Floating point value in [-1,1] interval
|
||||
*/
|
||||
@Override
|
||||
protected float generateNoise(float x, float y)
|
||||
{
|
||||
final int neighbourCount = 5;
|
||||
float total = 0;
|
||||
|
||||
for (float i = x - neighbourCount; i <= x + neighbourCount; ++i)
|
||||
for (float j = y - neighbourCount; j <= y + neighbourCount; ++j)
|
||||
total += whiteNoiseGenerator.generate(i, j);
|
||||
|
||||
return total / (float)Math.pow(neighbourCount * 2 + 1, 2);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
package tibi.ProceduralTerrain.Noise;
|
||||
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/19/15.
|
||||
*/
|
||||
public abstract class NoiseGenerator
|
||||
{
|
||||
float scale = 1f;
|
||||
float low = -1f, high = 1f;
|
||||
float nonLinearPower = 1f;
|
||||
int octaves = 1;
|
||||
float persistence = .5f;
|
||||
|
||||
private float seedX, seedY;
|
||||
|
||||
public NoiseGenerator()
|
||||
{
|
||||
seedX = MathUtils.random(-1024f, 1024f);
|
||||
seedY = MathUtils.random(-1024f, 1024f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates noise in [-1,1] interval
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @return Floating point value in [-1,1] interval
|
||||
*/
|
||||
protected abstract float generateNoise(float x, float y);
|
||||
|
||||
/**
|
||||
* Generates noise
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param low Minimum value
|
||||
* @param high Maximum value
|
||||
* @param scale Scale
|
||||
* @param octaves Number of octaves (layers)
|
||||
* @param persistence Persistence (impact of each layer)
|
||||
* @param nonLinearPower Non-linearity
|
||||
* @return Noise value
|
||||
*/
|
||||
public float generate(float x, float y, float low, float high, float scale, int octaves, float persistence, float nonLinearPower)
|
||||
{
|
||||
float value = 0;
|
||||
int freq = 1;
|
||||
float amp = 1;
|
||||
float maxAmp = 0;
|
||||
|
||||
for (int i = 0; i < octaves; ++i)
|
||||
{
|
||||
value += generateNoise(seedX + freq * scale * x, seedY + freq * scale * y) * amp;
|
||||
maxAmp += amp;
|
||||
amp *= persistence;
|
||||
freq *= 2;
|
||||
}
|
||||
|
||||
// Bring to [0,1]
|
||||
value = (value / maxAmp) / 2f + .5f;
|
||||
|
||||
// Raise to non-linear power
|
||||
value = (float)Math.pow(value, nonLinearPower);
|
||||
|
||||
// Bring to required interval
|
||||
value = value * (high - low) + low;
|
||||
|
||||
// Done
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates noise
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param low Minimum value
|
||||
* @param high Maximum value
|
||||
* @param scale Scale
|
||||
* @param octaves Number of octaves (layers)
|
||||
* @param persistence Persistence (impact of each layer)
|
||||
* @return Noise value
|
||||
*/
|
||||
public float generate(float x, float y, float low, float high, float scale, int octaves, float persistence)
|
||||
{
|
||||
return generate(x, y, low, high, scale, octaves, persistence, nonLinearPower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates noise
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param low Minimum value
|
||||
* @param high Maximum value
|
||||
* @param scale Scale
|
||||
* @return Noise value
|
||||
*/
|
||||
public float generate(float x, float y, float low, float high, float scale)
|
||||
{
|
||||
return generate(x, y, low, high, scale, octaves, persistence, nonLinearPower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates noise
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param low Minimum value
|
||||
* @param high Maximum value
|
||||
* @return Noise value
|
||||
*/
|
||||
public float generate(float x, float y, float low, float high)
|
||||
{
|
||||
return generate(x, y, low, high, scale, octaves, persistence, nonLinearPower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates noise
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @return Noise value
|
||||
*/
|
||||
public float generate(float x, float y)
|
||||
{
|
||||
return generate(x, y, low, high, scale, octaves, persistence, nonLinearPower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets scale
|
||||
* @return scale
|
||||
*/
|
||||
public float getScale()
|
||||
{
|
||||
return scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets scale
|
||||
* @param scale Scale value
|
||||
*/
|
||||
public void setScale(float scale)
|
||||
{
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public float getLow()
|
||||
{
|
||||
return low;
|
||||
}
|
||||
|
||||
public void setLow(float low)
|
||||
{
|
||||
this.low = low;
|
||||
}
|
||||
|
||||
public float getHigh()
|
||||
{
|
||||
return high;
|
||||
}
|
||||
|
||||
public void setHigh(float high)
|
||||
{
|
||||
this.high = high;
|
||||
}
|
||||
|
||||
public float getNonLinearPower()
|
||||
{
|
||||
return nonLinearPower;
|
||||
}
|
||||
|
||||
public void setNonLinearPower(float nonLinearPower)
|
||||
{
|
||||
this.nonLinearPower = nonLinearPower;
|
||||
}
|
||||
|
||||
public int getOctaves()
|
||||
{
|
||||
return octaves;
|
||||
}
|
||||
|
||||
public void setOctaves(int octaves)
|
||||
{
|
||||
this.octaves = octaves;
|
||||
}
|
||||
|
||||
public float getPersistence()
|
||||
{
|
||||
return persistence;
|
||||
}
|
||||
|
||||
public void setPersistence(float persistence)
|
||||
{
|
||||
this.persistence = persistence;
|
||||
}
|
||||
}
|
@ -0,0 +1,461 @@
|
||||
/*
|
||||
* A speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.
|
||||
*
|
||||
* Based on example code by Stefan Gustavson (stegu@itn.liu.se).
|
||||
* Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
|
||||
* Better rank ordering method by Stefan Gustavson in 2012.
|
||||
*
|
||||
* This could be speeded up even further, but it's useful as it is.
|
||||
*
|
||||
* Version 2012-03-09
|
||||
*
|
||||
* This code was placed in the public domain by its original author,
|
||||
* Stefan Gustavson. You may use it as you see fit, but
|
||||
* attribution is appreciated.
|
||||
*
|
||||
*/
|
||||
|
||||
package tibi.ProceduralTerrain.Noise;
|
||||
|
||||
public class SimplexNoiseGenerator extends NoiseGenerator
|
||||
{
|
||||
// Simplex noise in 2D, 3D and 4D
|
||||
private static Grad grad3[] = {new Grad(1, 1, 0), new Grad(-1, 1, 0), new Grad(1, -1, 0), new Grad(-1, -1, 0),
|
||||
new Grad(1, 0, 1), new Grad(-1, 0, 1), new Grad(1, 0, -1), new Grad(-1, 0, -1),
|
||||
new Grad(0, 1, 1), new Grad(0, -1, 1), new Grad(0, 1, -1), new Grad(0, -1, -1)};
|
||||
|
||||
private static Grad grad4[] = {new Grad(0, 1, 1, 1), new Grad(0, 1, 1, -1), new Grad(0, 1, -1, 1), new Grad(0, 1,
|
||||
-1, -1),
|
||||
new Grad(0, -1, 1, 1), new Grad(0, -1, 1, -1), new Grad(0, -1, -1, 1), new Grad(0, -1, -1, -1),
|
||||
new Grad(1, 0, 1, 1), new Grad(1, 0, 1, -1), new Grad(1, 0, -1, 1), new Grad(1, 0, -1, -1),
|
||||
new Grad(-1, 0, 1, 1), new Grad(-1, 0, 1, -1), new Grad(-1, 0, -1, 1), new Grad(-1, 0, -1, -1),
|
||||
new Grad(1, 1, 0, 1), new Grad(1, 1, 0, -1), new Grad(1, -1, 0, 1), new Grad(1, -1, 0, -1),
|
||||
new Grad(-1, 1, 0, 1), new Grad(-1, 1, 0, -1), new Grad(-1, -1, 0, 1), new Grad(-1, -1, 0, -1),
|
||||
new Grad(1, 1, 1, 0), new Grad(1, 1, -1, 0), new Grad(1, -1, 1, 0), new Grad(1, -1, -1, 0),
|
||||
new Grad(-1, 1, 1, 0), new Grad(-1, 1, -1, 0), new Grad(-1, -1, 1, 0), new Grad(-1, -1, -1, 0)};
|
||||
|
||||
private static short p[] = {151, 160, 137, 91, 90, 15,
|
||||
131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
|
||||
190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
|
||||
88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166,
|
||||
77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244,
|
||||
102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196,
|
||||
135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123,
|
||||
5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
|
||||
223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
|
||||
129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228,
|
||||
251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107,
|
||||
49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
|
||||
138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180};
|
||||
// To remove the need for index wrapping, double the permutation table length
|
||||
private static short perm[] = new short[512];
|
||||
private static short permMod12[] = new short[512];
|
||||
|
||||
static
|
||||
{
|
||||
for (int i = 0; i < 512; i++)
|
||||
{
|
||||
perm[i] = p[i & 255];
|
||||
permMod12[i] = (short) (perm[i] % 12);
|
||||
}
|
||||
}
|
||||
|
||||
// Skewing and unskewing factors for 2, 3, and 4 dimensions
|
||||
private static final double F2 = 0.5 * (Math.sqrt(3.0) - 1.0);
|
||||
private static final double G2 = (3.0 - Math.sqrt(3.0)) / 6.0;
|
||||
private static final double F3 = 1.0 / 3.0;
|
||||
private static final double G3 = 1.0 / 6.0;
|
||||
private static final double F4 = (Math.sqrt(5.0) - 1.0) / 4.0;
|
||||
private static final double G4 = (5.0 - Math.sqrt(5.0)) / 20.0;
|
||||
|
||||
// This method is a *lot* faster than using (int)Math.floor(x)
|
||||
private static int fastfloor(double x)
|
||||
{
|
||||
int xi = (int) x;
|
||||
return x < xi ? xi - 1 : xi;
|
||||
}
|
||||
|
||||
private static double dot(Grad g, double x, double y)
|
||||
{
|
||||
return g.x * x + g.y * y;
|
||||
}
|
||||
|
||||
private static double dot(Grad g, double x, double y, double z)
|
||||
{
|
||||
return g.x * x + g.y * y + g.z * z;
|
||||
}
|
||||
|
||||
private static double dot(Grad g, double x, double y, double z, double w)
|
||||
{
|
||||
return g.x * x + g.y * y + g.z * z + g.w * w;
|
||||
}
|
||||
|
||||
|
||||
// 2D simplex noise
|
||||
public static double noise(double xin, double yin)
|
||||
{
|
||||
double n0, n1, n2; // Noise contributions from the three corners
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
double s = (xin + yin) * F2; // Hairy factor for 2D
|
||||
int i = fastfloor(xin + s);
|
||||
int j = fastfloor(yin + s);
|
||||
double t = (i + j) * G2;
|
||||
double X0 = i - t; // Unskew the cell origin back to (x,y) space
|
||||
double Y0 = j - t;
|
||||
double x0 = xin - X0; // The x,y distances from the cell origin
|
||||
double y0 = yin - Y0;
|
||||
// For the 2D case, the simplex shape is an equilateral triangle.
|
||||
// Determine which simplex we are in.
|
||||
int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
|
||||
if (x0 > y0)
|
||||
{
|
||||
i1 = 1;
|
||||
j1 = 0;
|
||||
} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
|
||||
else
|
||||
{
|
||||
i1 = 0;
|
||||
j1 = 1;
|
||||
} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
|
||||
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
|
||||
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
|
||||
// c = (3-sqrt(3))/6
|
||||
double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
|
||||
double y1 = y0 - j1 + G2;
|
||||
double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
|
||||
double y2 = y0 - 1.0 + 2.0 * G2;
|
||||
// Work out the hashed gradient indices of the three simplex corners
|
||||
int ii = i & 255;
|
||||
int jj = j & 255;
|
||||
int gi0 = permMod12[ii + perm[jj]];
|
||||
int gi1 = permMod12[ii + i1 + perm[jj + j1]];
|
||||
int gi2 = permMod12[ii + 1 + perm[jj + 1]];
|
||||
// Calculate the contribution from the three corners
|
||||
double t0 = 0.5 - x0 * x0 - y0 * y0;
|
||||
if (t0 < 0) n0 = 0.0;
|
||||
else
|
||||
{
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient
|
||||
}
|
||||
double t1 = 0.5 - x1 * x1 - y1 * y1;
|
||||
if (t1 < 0) n1 = 0.0;
|
||||
else
|
||||
{
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
|
||||
}
|
||||
double t2 = 0.5 - x2 * x2 - y2 * y2;
|
||||
if (t2 < 0) n2 = 0.0;
|
||||
else
|
||||
{
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * dot(grad3[gi2], x2, y2);
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to return values in the interval [-1,1].
|
||||
return 70.0 * (n0 + n1 + n2);
|
||||
}
|
||||
|
||||
|
||||
// 3D simplex noise
|
||||
public static double noise(double xin, double yin, double zin)
|
||||
{
|
||||
double n0, n1, n2, n3; // Noise contributions from the four corners
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
double s = (xin + yin + zin) * F3; // Very nice and simple skew factor for 3D
|
||||
int i = fastfloor(xin + s);
|
||||
int j = fastfloor(yin + s);
|
||||
int k = fastfloor(zin + s);
|
||||
double t = (i + j + k) * G3;
|
||||
double X0 = i - t; // Unskew the cell origin back to (x,y,z) space
|
||||
double Y0 = j - t;
|
||||
double Z0 = k - t;
|
||||
double x0 = xin - X0; // The x,y,z distances from the cell origin
|
||||
double y0 = yin - Y0;
|
||||
double z0 = zin - Z0;
|
||||
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
|
||||
// Determine which simplex we are in.
|
||||
int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
|
||||
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
|
||||
if (x0 >= y0)
|
||||
{
|
||||
if (y0 >= z0)
|
||||
{
|
||||
i1 = 1;
|
||||
j1 = 0;
|
||||
k1 = 0;
|
||||
i2 = 1;
|
||||
j2 = 1;
|
||||
k2 = 0;
|
||||
} // X Y Z order
|
||||
else if (x0 >= z0)
|
||||
{
|
||||
i1 = 1;
|
||||
j1 = 0;
|
||||
k1 = 0;
|
||||
i2 = 1;
|
||||
j2 = 0;
|
||||
k2 = 1;
|
||||
} // X Z Y order
|
||||
else
|
||||
{
|
||||
i1 = 0;
|
||||
j1 = 0;
|
||||
k1 = 1;
|
||||
i2 = 1;
|
||||
j2 = 0;
|
||||
k2 = 1;
|
||||
} // Z X Y order
|
||||
}
|
||||
else
|
||||
{ // x0<y0
|
||||
if (y0 < z0)
|
||||
{
|
||||
i1 = 0;
|
||||
j1 = 0;
|
||||
k1 = 1;
|
||||
i2 = 0;
|
||||
j2 = 1;
|
||||
k2 = 1;
|
||||
} // Z Y X order
|
||||
else if (x0 < z0)
|
||||
{
|
||||
i1 = 0;
|
||||
j1 = 1;
|
||||
k1 = 0;
|
||||
i2 = 0;
|
||||
j2 = 1;
|
||||
k2 = 1;
|
||||
} // Y Z X order
|
||||
else
|
||||
{
|
||||
i1 = 0;
|
||||
j1 = 1;
|
||||
k1 = 0;
|
||||
i2 = 1;
|
||||
j2 = 1;
|
||||
k2 = 0;
|
||||
} // Y X Z order
|
||||
}
|
||||
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
|
||||
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
|
||||
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
|
||||
// c = 1/6.
|
||||
double x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
|
||||
double y1 = y0 - j1 + G3;
|
||||
double z1 = z0 - k1 + G3;
|
||||
double x2 = x0 - i2 + 2.0 * G3; // Offsets for third corner in (x,y,z) coords
|
||||
double y2 = y0 - j2 + 2.0 * G3;
|
||||
double z2 = z0 - k2 + 2.0 * G3;
|
||||
double x3 = x0 - 1.0 + 3.0 * G3; // Offsets for last corner in (x,y,z) coords
|
||||
double y3 = y0 - 1.0 + 3.0 * G3;
|
||||
double z3 = z0 - 1.0 + 3.0 * G3;
|
||||
// Work out the hashed gradient indices of the four simplex corners
|
||||
int ii = i & 255;
|
||||
int jj = j & 255;
|
||||
int kk = k & 255;
|
||||
int gi0 = permMod12[ii + perm[jj + perm[kk]]];
|
||||
int gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]];
|
||||
int gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]];
|
||||
int gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]];
|
||||
// Calculate the contribution from the four corners
|
||||
double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
|
||||
if (t0 < 0) n0 = 0.0;
|
||||
else
|
||||
{
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0);
|
||||
}
|
||||
double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
|
||||
if (t1 < 0) n1 = 0.0;
|
||||
else
|
||||
{
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1);
|
||||
}
|
||||
double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
|
||||
if (t2 < 0) n2 = 0.0;
|
||||
else
|
||||
{
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2);
|
||||
}
|
||||
double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
|
||||
if (t3 < 0) n3 = 0.0;
|
||||
else
|
||||
{
|
||||
t3 *= t3;
|
||||
n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3);
|
||||
}
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to stay just inside [-1,1]
|
||||
return 32.0 * (n0 + n1 + n2 + n3);
|
||||
}
|
||||
|
||||
|
||||
// 4D simplex noise, better simplex rank ordering method 2012-03-09
|
||||
public static double noise(double x, double y, double z, double w)
|
||||
{
|
||||
|
||||
double n0, n1, n2, n3, n4; // Noise contributions from the five corners
|
||||
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
|
||||
double s = (x + y + z + w) * F4; // Factor for 4D skewing
|
||||
int i = fastfloor(x + s);
|
||||
int j = fastfloor(y + s);
|
||||
int k = fastfloor(z + s);
|
||||
int l = fastfloor(w + s);
|
||||
double t = (i + j + k + l) * G4; // Factor for 4D unskewing
|
||||
double X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
|
||||
double Y0 = j - t;
|
||||
double Z0 = k - t;
|
||||
double W0 = l - t;
|
||||
double x0 = x - X0; // The x,y,z,w distances from the cell origin
|
||||
double y0 = y - Y0;
|
||||
double z0 = z - Z0;
|
||||
double w0 = w - W0;
|
||||
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
|
||||
// To find out which of the 24 possible simplices we're in, we need to
|
||||
// determine the magnitude ordering of x0, y0, z0 and w0.
|
||||
// Six pair-wise comparisons are performed between each possible pair
|
||||
// of the four coordinates, and the results are used to rank the numbers.
|
||||
int rankx = 0;
|
||||
int ranky = 0;
|
||||
int rankz = 0;
|
||||
int rankw = 0;
|
||||
if (x0 > y0) rankx++;
|
||||
else ranky++;
|
||||
if (x0 > z0) rankx++;
|
||||
else rankz++;
|
||||
if (x0 > w0) rankx++;
|
||||
else rankw++;
|
||||
if (y0 > z0) ranky++;
|
||||
else rankz++;
|
||||
if (y0 > w0) ranky++;
|
||||
else rankw++;
|
||||
if (z0 > w0) rankz++;
|
||||
else rankw++;
|
||||
int i1, j1, k1, l1; // The integer offsets for the second simplex corner
|
||||
int i2, j2, k2, l2; // The integer offsets for the third simplex corner
|
||||
int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
|
||||
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
|
||||
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
|
||||
// impossible. Only the 24 indices which have non-zero entries make any sense.
|
||||
// We use a thresholding to set the coordinates in turn from the largest magnitude.
|
||||
// Rank 3 denotes the largest coordinate.
|
||||
i1 = rankx >= 3 ? 1 : 0;
|
||||
j1 = ranky >= 3 ? 1 : 0;
|
||||
k1 = rankz >= 3 ? 1 : 0;
|
||||
l1 = rankw >= 3 ? 1 : 0;
|
||||
// Rank 2 denotes the second largest coordinate.
|
||||
i2 = rankx >= 2 ? 1 : 0;
|
||||
j2 = ranky >= 2 ? 1 : 0;
|
||||
k2 = rankz >= 2 ? 1 : 0;
|
||||
l2 = rankw >= 2 ? 1 : 0;
|
||||
// Rank 1 denotes the second smallest coordinate.
|
||||
i3 = rankx >= 1 ? 1 : 0;
|
||||
j3 = ranky >= 1 ? 1 : 0;
|
||||
k3 = rankz >= 1 ? 1 : 0;
|
||||
l3 = rankw >= 1 ? 1 : 0;
|
||||
// The fifth corner has all coordinate offsets = 1, so no need to compute that.
|
||||
double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
|
||||
double y1 = y0 - j1 + G4;
|
||||
double z1 = z0 - k1 + G4;
|
||||
double w1 = w0 - l1 + G4;
|
||||
double x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords
|
||||
double y2 = y0 - j2 + 2.0 * G4;
|
||||
double z2 = z0 - k2 + 2.0 * G4;
|
||||
double w2 = w0 - l2 + 2.0 * G4;
|
||||
double x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords
|
||||
double y3 = y0 - j3 + 3.0 * G4;
|
||||
double z3 = z0 - k3 + 3.0 * G4;
|
||||
double w3 = w0 - l3 + 3.0 * G4;
|
||||
double x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords
|
||||
double y4 = y0 - 1.0 + 4.0 * G4;
|
||||
double z4 = z0 - 1.0 + 4.0 * G4;
|
||||
double w4 = w0 - 1.0 + 4.0 * G4;
|
||||
// Work out the hashed gradient indices of the five simplex corners
|
||||
int ii = i & 255;
|
||||
int jj = j & 255;
|
||||
int kk = k & 255;
|
||||
int ll = l & 255;
|
||||
int gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32;
|
||||
int gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32;
|
||||
int gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32;
|
||||
int gi3 = perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32;
|
||||
int gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32;
|
||||
// Calculate the contribution from the five corners
|
||||
double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
|
||||
if (t0 < 0) n0 = 0.0;
|
||||
else
|
||||
{
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0);
|
||||
}
|
||||
double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
|
||||
if (t1 < 0) n1 = 0.0;
|
||||
else
|
||||
{
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1);
|
||||
}
|
||||
double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
|
||||
if (t2 < 0) n2 = 0.0;
|
||||
else
|
||||
{
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2);
|
||||
}
|
||||
double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
|
||||
if (t3 < 0) n3 = 0.0;
|
||||
else
|
||||
{
|
||||
t3 *= t3;
|
||||
n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3);
|
||||
}
|
||||
double t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
|
||||
if (t4 < 0) n4 = 0.0;
|
||||
else
|
||||
{
|
||||
t4 *= t4;
|
||||
n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4);
|
||||
}
|
||||
// Sum up and scale the result to cover the range [-1,1]
|
||||
return 27.0 * (n0 + n1 + n2 + n3 + n4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates noise in [-1,1] interval
|
||||
*
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @return Floating point value in [-1,1] interval
|
||||
*/
|
||||
@Override
|
||||
protected float generateNoise(float x, float y)
|
||||
{
|
||||
return (float)noise(x, y);
|
||||
}
|
||||
|
||||
// Inner class to speed upp gradient computations
|
||||
// (array access is a lot slower than member access)
|
||||
private static class Grad
|
||||
{
|
||||
double x, y, z, w;
|
||||
|
||||
Grad(double x, double y, double z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
Grad(double x, double y, double z, double w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package tibi.ProceduralTerrain.Noise;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by tibi on 1/19/15.
|
||||
*/
|
||||
public class WhiteNoiseGenerator extends NoiseGenerator
|
||||
{
|
||||
Random random;
|
||||
long seed;
|
||||
|
||||
public WhiteNoiseGenerator()
|
||||
{
|
||||
random = new Random();
|
||||
seed = random.nextLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates noise in [-1,1] interval
|
||||
*
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @return Floating point value in [-1,1] interval
|
||||
*/
|
||||
@Override
|
||||
protected float generateNoise(float x, float y)
|
||||
{
|
||||
// Generate seed
|
||||
long seed = this.seed;
|
||||
|
||||
random.setSeed(this.seed);
|
||||
seed += random.nextLong() * Float.floatToRawIntBits(x);
|
||||
seed += random.nextLong() * Float.floatToRawIntBits(y);
|
||||
|
||||
random.setSeed(seed);
|
||||
|
||||
// Scale to [-1,1]
|
||||
float value = (random.nextFloat() * 2f) - 1f;
|
||||
|
||||
// Done
|
||||
return value;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package tibi.ProceduralTerrain;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.assets.AssetManager;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.PerspectiveCamera;
|
||||
import com.badlogic.gdx.graphics.g3d.Environment;
|
||||
import com.badlogic.gdx.graphics.g3d.Model;
|
||||
import com.badlogic.gdx.graphics.g3d.ModelBatch;
|
||||
import com.badlogic.gdx.graphics.g3d.ModelInstance;
|
||||
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
|
||||
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
|
||||
import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
|
||||
import tibi.ProceduralTerrain.Business.*;
|
||||
import tibi.ProceduralTerrain.Model.Map;
|
||||
import tibi.ProceduralTerrain.Model.Terrain;
|
||||
import tibi.ProceduralTerrain.Noise.*;
|
||||
|
||||
public class ProceduralTerrainGenerator extends ApplicationAdapter
|
||||
{
|
||||
AssetManager assetManager;
|
||||
ModelBatch modelBatch;
|
||||
Environment environment;
|
||||
|
||||
PerspectiveCamera camera;
|
||||
CameraInputController cameraInputController;
|
||||
|
||||
MapMeshGenerator mapMesh;
|
||||
|
||||
@Override
|
||||
public void create()
|
||||
{
|
||||
assetManager = new AssetManager();
|
||||
|
||||
modelBatch = new ModelBatch();
|
||||
|
||||
environment = new Environment();
|
||||
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 1f, 1f, 1f, 0f));
|
||||
environment.add(new DirectionalLight().set(.4f, .8f, .8f, -1f, -.8f, -.2f));
|
||||
|
||||
camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
camera.position.set(0f, 10f, 0f);
|
||||
camera.lookAt(5f, 1f, 5f);
|
||||
camera.near = .4f;
|
||||
camera.far = 10000;
|
||||
camera.update();
|
||||
|
||||
cameraInputController = new CameraInputController(camera);
|
||||
Gdx.input.setInputProcessor(cameraInputController);
|
||||
|
||||
System.out.println("Generating map...");
|
||||
|
||||
// Generate map
|
||||
MapGenerator mapGenerator = new MapGenerator();
|
||||
Map map = mapGenerator.generate(256, 256);
|
||||
|
||||
System.out.println("Generating mesh...");
|
||||
mapMesh = new MapMeshGenerator(map);
|
||||
|
||||
// Get once, to generate the mesh
|
||||
for (int i = 0; i < mapMesh.getChunksW(); ++i)
|
||||
for (int j = 0; j < mapMesh.getChunksH(); ++j)
|
||||
mapMesh.get(i, j, 0);
|
||||
|
||||
System.out.println("Finished.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render()
|
||||
{
|
||||
cameraInputController.update();
|
||||
|
||||
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
modelBatch.begin(camera);
|
||||
|
||||
for (int i = 0; i < mapMesh.getChunksW(); ++i)
|
||||
for (int j = 0; j < mapMesh.getChunksH(); ++j)
|
||||
modelBatch.render(mapMesh.get(i, j, 0), environment);
|
||||
|
||||
modelBatch.end();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user