Commit 336ab979 authored by christopher.spray's avatar christopher.spray

First Commit

parents
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(TerrainController))]
public class EditorScript : Editor
{
public override void OnInspectorGUI()
{
TerrainController TC = (TerrainController)target;
DrawDefaultInspector();
if (GUILayout.Button("Generate"))
{
TC.GenerateTerrain();
}
if (GUILayout.Button("Destroy Trees"))
{
TC.DestroyTrees();
}
}
}
fileFormatVersion: 2
guid: f017cbd767bf7f84595e01f197da1dfa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeshGeneration
{
public int[] triangles;
public Vector3[] vertexArr;
public Vector2[] uvArr;
public int vertexAmount;
public int simplificationLevel;
public Mesh GenMesh(float[,] noiseMap, float heightMultiplier, int simplificationLevel)
{
int width = noiseMap.GetLength(0); //get x length of array
int height = noiseMap.GetLength(1); //get y length of array
if (simplificationLevel == 1)
{
simplificationLevel = 1;
}
else if (simplificationLevel == 2)
{
simplificationLevel = 2;
}
else if (simplificationLevel == 3)
{
simplificationLevel = 4;
}
else if (simplificationLevel == 4)
{
simplificationLevel = 8;
}
vertexAmount = (width - 1) / simplificationLevel + 1;
triangles = new int[(vertexAmount - 1) * (vertexAmount - 1) * 6]; //fill triangle array size
vertexArr = new Vector3[vertexAmount * vertexAmount]; //fill vertex array size
uvArr = new Vector2[vertexAmount * vertexAmount]; //fill uv array size
MakeGrid(width, height, noiseMap, heightMultiplier, simplificationLevel); //Create the grid separetly to the triangles
MakeTriangles(vertexAmount, vertexAmount); //Make triangles AFTER so I can mess with simplification
//ADD EVERYTHING TO THE MESH OBJECT
Mesh mesh = new Mesh();
mesh.vertices = vertexArr; //assign it the variables I created
mesh.triangles = triangles;
mesh.uv = uvArr;
mesh.RecalculateNormals(); //do the lighting again
return mesh; //GIMMIE. DAT. MESH.
}
void MakeGrid(int width, int height, float[,] noiseMap, float heightMultiplier, int simplificationLevel)
{
int currentVert = 0; //keep track of what vertex I am on
for (int x = 0; x < width; x+= simplificationLevel)
{
for (int y = 0; y < height; y+= simplificationLevel) //loop through the grid points
{
vertexArr[currentVert] = new Vector3(x, noiseMap[x, y] * heightMultiplier, y); //make a point on the grid at the height of the noiseMap
//Debug.Log("Vertex Arr:" + vertexArr[currentVert].y);
uvArr[currentVert] = new Vector2(x / (float)width, y / (float)height); //divided as the result needs to be between 0 and 1 due to how UV's work
currentVert++;
}
}
}
void MakeTriangles(int width, int height)
{
int currentVert = 0; //keep track of what vertex I am on
int currentTrianglePoint = 0; //keep track of which triangle point I am on
for(int y=0; y<height-1; y++) //-1 so I don't make triangles outside of the array (Don't need last column)
{
for (int x=0; x<width-1; x++) //loop through the mesh points
{
//create triangles to make a square per vertex
//Triangle 1
triangles[currentTrianglePoint] = currentVert; //top left
triangles[currentTrianglePoint+1] = currentVert + vertexAmount + 1; //bottom right
triangles[currentTrianglePoint+2] = currentVert + vertexAmount; //bottom left
//Triangle 2
triangles[currentTrianglePoint+3] = currentVert + 1 + vertexAmount; //top left
triangles[currentTrianglePoint+4] = currentVert; //top right
triangles[currentTrianglePoint+5] = currentVert + 1; //bottom right
currentTrianglePoint += 6; //move 6 points across since we used 6
currentVert++; //move to the next vertex
}
currentVert++; //at the end of every row, move up one so there isn't a LONG triangle between rows
}
}
}
fileFormatVersion: 2
guid: 1ebaac5558e16a947abda74aff52eaea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PerlinNoise : MonoBehaviour
{
public static float[,] genNoise(int width, int height, int seed, int octaves, float scale, float persistence, float lacunarity, float curve)
{
float[,] noiseMap = new float[width, height]; //create empty noisemap array
Random.InitState(seed); //plant seed into prng
int randX = Random.Range(-5000, 5000); //generate random X coord
int randY = Random.Range(-5000, 5000); //generate random Y coord
float maxHeight = 0;
float minHeight = 0;
for (int x=0; x < width; x++)
{
for (int y=0; y < width; y++)
{
float amp = 1;
float freq = 1;
float noise = 0;
for (int i=0; i<octaves; i++)
{
float perlinX = x / scale * freq + randX;
float perlinY = y / scale * freq + randY;
noise += Mathf.PerlinNoise(perlinX, perlinY) * amp;
//decrease the amp, increase the freq
amp *= persistence;
freq *= lacunarity;
}
//smooth out the land
noise = Mathf.Pow(noise, curve);
//catch the lowest and highest values for normalising later
if (noise > maxHeight)
{
maxHeight = noise;
}
else if (noise < minHeight)
{
minHeight = noise;
}
noiseMap[x, y] = noise;
}
}
//loop through every point
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
noiseMap[x, y] = Mathf.InverseLerp(minHeight, maxHeight, noiseMap[x, y]); //normalise the number for colouring later
}
}
return noiseMap;
}
}
fileFormatVersion: 2
guid: d9ef399a9de6b0d43a81bad0dd2e0d9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoissonNoise : MonoBehaviour
{
public static List<Vector2> GenPoissonNoise(int width, int height, float radius)
{
width -= 1;
height -= 1;
float[,] grid = new float[width, height];
float cellWidth = radius / Mathf.Sqrt(2);
List<Vector2> activePoints = new List<Vector2>();
List<Vector2> poissonNoise = new List<Vector2>();
const int k = 30; //amount of times to choose new location before giving up
//initialise grid with all -1;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
grid[x, y] = -1;
}
}
//generate a random starting point
int randX = Random.Range(0, width);
int randY = Random.Range(0, height);
Vector2 StartPoint = new Vector2(randX, randY);
//add it to the active points list and change it's grid val to 1
grid[randX, randY] = 1;
activePoints.Add(StartPoint);
//whilst there is a point that is active
while (activePoints.Count > 0)
{
int randIndex = Random.Range(0, activePoints.Count);//choose random index
Vector2 current = activePoints[randIndex]; //get its value
int failedAttempts = 0;
for (int j = 0; j <= k; j++) //generate up to k points
{
float angle = Random.Range(0, 360); //create random angle
float radian = angle * Mathf.PI / 180; //convert angle to radians
float distance = Random.Range(radius, radius * 2); //random distance between r and 2r
float endingX = current.x + distance * Mathf.Cos(radian); //xposition to place
float endingY = current.y + distance * Mathf.Sin(radian); //yposition to place
if(endingX < 0)
{
endingX = 0;
}
if (endingX > width)
{
endingX = width;
}
if (endingY < 0)
{
endingY = 0;
}
if (endingY > width)
{
endingY = width;
}
Vector2 toPlace = new Vector2(endingX, endingY); //save previously generated points to a vector2
//generate it's position in the grid
int gridX = Mathf.FloorToInt(toPlace.x);
int gridY = Mathf.FloorToInt(toPlace.y);
//Checking a square of the 9 cells around it(8 cells AND the current cell point)
bool canBePlaced = true;
for (int x=-1; x <=1; x++)
{
for(int y=-1; y <=1; y++)
{
int gridRow = gridX + x; //check the grid x pos
int gridCol = gridY + y; //check the grid y pos
//clamp the values (if point is being palced on the edge, cannot check outside the array)
if (gridRow > 127)
{
gridRow = 127;
}
if (gridRow < 0)
{
gridRow = 0;
}
if (gridCol > 127)
{
gridCol = 127;
}
if (gridCol < 0)
{
gridCol = 0;
}
//if the grid point IS OCCUPIED
if (grid[gridRow,gridCol] == 1)
{
//Debug.Log("NO");
canBePlaced = false;
failedAttempts++; //increment failure counter by 1
}
}
}
if (gridX > 127)
{
gridX = 127;
}
if (gridX < 0)
{
gridX = 0;
}
if (gridY > 127)
{
gridY = 127;
}
if (gridY < 0)
{
gridY = 0;
}
//Processing what to do with points based on if they can be placed or not
if (canBePlaced) //if it can be placed
{
grid[gridX, gridY] = 1;
activePoints.Add(toPlace); //add it to the active points list
}
if(failedAttempts == k) //if failedAttempts = max num of tries (every point couldn't be placed), remove the point
{
poissonNoise.Add(current);
activePoints.RemoveAt(randIndex); //remove it from the list
}
}
}
return poissonNoise;
}
}
fileFormatVersion: 2
guid: e041c27045c2cce4db0fc3602dba2a54
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TerrainController : MonoBehaviour
{
//Variable Initialization
public MeshFilter meshFilter;
public MeshRenderer meshRenderer;
public Renderer textureRenderer;
public GameObject treeParent;
public GameObject tree;
public MeshGeneration meshGen = new MeshGeneration();
const int chunkSize = 129; //129
[Range (20f, 70f)]public float scale;
[Range (1,10)]public int octaves;
[Range (0.3f, 0.7f)]public float persistance;
[Range(1.5f, 3f)] public float lacunarity;
[Min(0)] public int seed;
[Range(6, 30)] public int heightMultiplier;
[Range(1, 4)] public int meshSimplificationLevel;
public Gradient gradient;
public gradientFilter ColourType;
[Range(2f,5f)] public float curve;
string enumResult;
Color[] gradientColour = new Color[chunkSize * chunkSize]; //make new colour array the size of the map
public bool spawnTrees;
public int forestStartKey;
public int forestEndKey;
[Range(10f, 300f)] public float forestDensity;
[System.Serializable]
public enum gradientFilter
{
Blend,
Fixed
}
public void GenerateTerrain()
{
float[,] noiseMap = PerlinNoise.genNoise(chunkSize, chunkSize, seed, octaves, scale, persistance, lacunarity, curve); //Make the noise map
ColourMap(noiseMap); //colour the noise map
Mesh mesh = meshGen.GenMesh(noiseMap, heightMultiplier, meshSimplificationLevel); //make a mesh with the noisemap
meshFilter.sharedMesh = mesh; //assign the mesh to filtered mesh so it can be shown in editor
//spawn trees if chosen to
if (spawnTrees) {
PlaceTrees(chunkSize - 1, chunkSize - 1, forestStartKey, forestEndKey, tree, noiseMap);
}
else if (!spawnTrees)
{
DestroyTrees();
}
}
public void ColourMap(float[,] noiseMap)
{
int width = noiseMap.GetLength(0);
int height = noiseMap.GetLength(1);
Texture2D texture = new Texture2D(width, height); //create a new empty texture
//Colouring the map
enumResult = ColourType.ToString(); //get the result from the enum choice
//change the gradientMode based on the result, as well as the filterMode that suits it best
if (enumResult == "Blend")
{
gradient.mode = GradientMode.Blend; //change gradient mode to blend
texture.filterMode = FilterMode.Bilinear; //change gradient filter to Bilinear
}
else if (enumResult == "Fixed")
{
gradient.mode = GradientMode.Fixed; //change gradient to fixed
texture.filterMode = FilterMode.Point; //change filter to point
}
//apply the colour to the map based on the height
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
float noiseHeight = noiseMap[x, y]; //store the height
gradientColour[y * width + x] = gradient.Evaluate(noiseHeight); //put the right colour in
}
}
//set the colour to the texture
texture.SetPixels(gradientColour);
texture.Apply();
textureRenderer.sharedMaterial.mainTexture = texture;
textureRenderer.transform.localScale = new Vector3(width, 1, height);
}
public void OnValidate()
{
scale = Mathf.RoundToInt(scale);
}
//destroy trees
public void DestroyTrees()
{
var trees = GameObject.FindGameObjectsWithTag("tree");
foreach (var newTree in trees)
{
DestroyImmediate(newTree);
}
}
void PlaceTrees(int width, int height, int forestStart, int forestEnd, GameObject tree, float[,] noiseMap)
{
//destroy the trees
var trees = GameObject.FindGameObjectsWithTag("tree");
foreach (var newTree in trees)
{
DestroyImmediate(newTree);
}
//create new variables
Vector3[] treeLocations = new Vector3[width*height];
forestStart -= 1;
forestEnd -= 1;
int counter = 0;
float maxHeight = 10;
float minHeight = 10;
float[] normalizedHeight = new float[width * height];
//create an array for gradient keys
GradientColorKey[] gradKeys;
gradKeys = gradient.colorKeys;
//ensure that the forest at the correct positions
if(forestEnd < forestStart)
{
forestEnd = forestStart + 1;
}
List<Vector2> poissonList = PoissonNoise.GenPoissonNoise(chunkSize, chunkSize, forestDensity); //create list of tree positions from poisson noise
int treeCounter = 0;
for (int x = 0; x < width; x+= meshSimplificationLevel)
{
//catch max and minimum height
for (int y = 0; y < height; y+= meshSimplificationLevel)
{
if (noiseMap[x, y] * heightMultiplier > maxHeight)
{
maxHeight = noiseMap[x, y] * heightMultiplier;
}
else if (noiseMap[x, y] * heightMultiplier < minHeight)
{
minHeight = noiseMap[x, y] * heightMultiplier;
}
//create new array for each tree location based on poisson list
if (treeCounter < poissonList.Count)
{
treeLocations[counter] = new Vector3(poissonList[treeCounter].x, noiseMap[x, y] * heightMultiplier, poissonList[treeCounter].y);
treeCounter++;
}
//normalizedHeight[counter] = (((treeLocations[counter].y) - minHeight) / (maxHeight - minHeight)); //Poisson Noise
treeLocations[counter] = new Vector3(x, noiseMap[x, y] * heightMultiplier, y); // GRID SPAWNING
normalizedHeight[counter] = (((noiseMap[x, y] * heightMultiplier) - minHeight) / (maxHeight - minHeight));
//if it's in the right height zone, spawn the trees
if (normalizedHeight[counter] > gradKeys[forestStart].time && normalizedHeight[counter] < gradKeys[forestEnd].time)
{
Instantiate(tree, treeLocations[counter], Quaternion.Euler(270, Random.Range(0f, 360f), 0));
counter ++;
}
}
}
// set trees to random size
var spawnedTrees = GameObject.FindGameObjectsWithTag("tree");
foreach (var newTree in spawnedTrees)
{
float size = Random.Range(7f, 15f);
newTree.gameObject.transform.localScale = new Vector3(size, size, size);
newTree.transform.parent = treeParent.transform;
}
}
}
fileFormatVersion: 2
guid: f78d24c29cd98bd48973e3bf780d31f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment