public void SmoothTerrain(int iterations, float blend) { generatorTypeInt = 3; generatorType = GeneratorType.Smooth; smoothIterations = iterations; smoothBlend = blend; GeneratorProgressDelegate generatorProgressDelegate = new GeneratorProgressDelegate(dummyGeneratorProgress); generateTerrain(generatorProgressDelegate); }
public void NormaliseTerrain(float minHeight, float maxHeight, float blend) { generatorTypeInt = 4; generatorType = GeneratorType.Normalise; normaliseMin = minHeight; normaliseMax = maxHeight; normaliseBlend = blend; GeneratorProgressDelegate generatorProgressDelegate = new GeneratorProgressDelegate(dummyGeneratorProgress); generateTerrain(generatorProgressDelegate); }
public void PerlinGenerator(int frequency, float amplitude, int octaves, float blend) { generatorTypeInt = 2; generatorType = GeneratorType.Perlin; perlinFrequency = frequency; perlinAmplitude = amplitude; perlinOctaves = octaves; perlinBlend = blend; GeneratorProgressDelegate generatorProgressDelegate = new GeneratorProgressDelegate(dummyGeneratorProgress); generateTerrain(generatorProgressDelegate); }
public void FractalGenerator(float fractalDelta, float blend) { generatorTypeInt = 1; generatorType = GeneratorType.DiamondSquare; diamondSquareDelta = fractalDelta; diamondSquareBlend = blend; GeneratorProgressDelegate generatorProgressDelegate = new GeneratorProgressDelegate(dummyGeneratorProgress); generateTerrain(generatorProgressDelegate); }
// -------------------------------------------------------------------------------------------------------- GENERATOR FILTERS public void generateTerrain(GeneratorProgressDelegate generatorProgressDelegate) { // Check enum vars... convertIntVarsToEnums(); // Error checking... Terrain ter = (Terrain) GetComponent(typeof(Terrain)); if (ter == null) { return; } TerrainData terData = ter.terrainData; int Tw = terData.heightmapWidth; int Th = terData.heightmapHeight; float[,] heightMap = terData.GetHeights(0, 0, Tw, Th); float[,] generatedHeightMap = (float[,]) heightMap.Clone(); // Set the number of iterations and pass the height array to the appropriate generator script... switch (generatorType) { case GeneratorType.Voronoi: generatedHeightMap = generateVoronoi(generatedHeightMap, new Vector2(Tw, Th), generatorProgressDelegate); break; case GeneratorType.DiamondSquare: generatedHeightMap = generateDiamondSquare(generatedHeightMap, new Vector2(Tw, Th), generatorProgressDelegate); break; case GeneratorType.Perlin: generatedHeightMap = generatePerlin(generatedHeightMap, new Vector2(Tw, Th), generatorProgressDelegate); break; case GeneratorType.Smooth: generatedHeightMap = smooth(generatedHeightMap, new Vector2(Tw, Th), generatorProgressDelegate); break; case GeneratorType.Normalise: generatedHeightMap = normalise(generatedHeightMap, new Vector2(Tw, Th), generatorProgressDelegate); break; default: return; } // Apply it to the terrain object... for (int Ty = 0; Ty < Th; Ty++) { for (int Tx = 0; Tx < Tw; Tx++) { float oldHeightAtPoint = heightMap[Tx, Ty]; float newHeightAtPoint = generatedHeightMap[Tx, Ty]; float blendedHeightAtPoint = 0.0f; switch (generatorType) { case GeneratorType.Voronoi: blendedHeightAtPoint = (newHeightAtPoint * voronoiBlend) + (oldHeightAtPoint * (1.0f - voronoiBlend)); break; case GeneratorType.DiamondSquare: blendedHeightAtPoint = (newHeightAtPoint * diamondSquareBlend) + (oldHeightAtPoint * (1.0f - diamondSquareBlend)); break; case GeneratorType.Perlin: blendedHeightAtPoint = (newHeightAtPoint * perlinBlend) + (oldHeightAtPoint * (1.0f - perlinBlend)); break; case GeneratorType.Smooth: blendedHeightAtPoint = (newHeightAtPoint * smoothBlend) + (oldHeightAtPoint * (1.0f - smoothBlend)); break; case GeneratorType.Normalise: blendedHeightAtPoint = (newHeightAtPoint * normaliseBlend) + (oldHeightAtPoint * (1.0f - normaliseBlend)); break; } heightMap[Tx, Ty] = blendedHeightAtPoint; } } terData.SetHeights(0, 0, heightMap); }
private float[,] smooth(float[,] heightMap, Vector2 arraySize, GeneratorProgressDelegate generatorProgressDelegate) { int Tw = (int) arraySize.x; int Th = (int) arraySize.y; int xNeighbours; int yNeighbours; int xShift; int yShift; int xIndex; int yIndex; int Tx; int Ty; // Start iterations... for (int iter = 0; iter < smoothIterations; iter++) { for (Ty = 0; Ty < Th; Ty++) { // y... if (Ty == 0) { yNeighbours = 2; yShift = 0; yIndex = 0; } else if (Ty == Th - 1) { yNeighbours = 2; yShift = -1; yIndex = 1; } else { yNeighbours = 3; yShift = -1; yIndex = 1; } for (Tx = 0; Tx < Tw; Tx++) { // x... if (Tx == 0) { xNeighbours = 2; xShift = 0; xIndex = 0; } else if (Tx == Tw - 1) { xNeighbours = 2; xShift = -1; xIndex = 1; } else { xNeighbours = 3; xShift = -1; xIndex = 1; } int Ny; int Nx; float hCumulative = 0.0f; int nNeighbours = 0; for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { float heightAtPoint = heightMap[Tx + Nx + xShift, Ty + Ny + yShift]; // Get height at point hCumulative += heightAtPoint; nNeighbours++; } } } float hAverage = hCumulative / nNeighbours; heightMap[Tx + xIndex + xShift, Ty + yIndex + yShift] = hAverage; } } // Show progress... float percentComplete = (iter + 1) / smoothIterations; generatorProgressDelegate("Smoothing Filter", "Smoothing height map. Please wait.", percentComplete); } return heightMap; }
// -------------------------------------------------------------------------------------------------------- WIND EROSION private float[,] windErosion(float[,] heightMap, Vector2 arraySize, int iterations, ErosionProgressDelegate erosionProgressDelegate) { Terrain ter = (Terrain) GetComponent(typeof(Terrain)); TerrainData terData = ter.terrainData; Quaternion windQuaternion = Quaternion.Euler(0, (windDirection + 180.0f), 0); Vector3 windVector = windQuaternion * Vector3.forward; int Tw = (int) arraySize.x; int Th = (int) arraySize.y; float[,] stressMap = new float[Tw, Th]; float[,] flowMap = new float[Tw, Th]; float[,] velocityMap = new float[Tw, Th]; float[,] velocityDiffMap = new float[Tw, Th]; float[,] suspensionMap = new float[Tw, Th]; float[,] suspensionDiffMap = new float[Tw, Th]; float[,] heightDiffMap = new float[Tw, Th]; int xNeighbours; int yNeighbours; int xShift; int yShift; int xIndex; int yIndex; int Mx; int My; float materialAtIndex; float velocityAtCell; // Zero maps... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { stressMap[Mx, My] = 0.0f; flowMap[Mx, My] = 0.0f; velocityMap[Mx, My] = 0.0f; velocityDiffMap[Mx, My] = 0.0f; suspensionMap[Mx, My] = 0.0f; suspensionDiffMap[Mx, My] = 0.0f; heightDiffMap[Mx, My] = 0.0f; } } // Start iterations... for (int iter = 0; iter < iterations; iter++) { // Drop material... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { velocityAtCell = velocityMap[Mx, My]; float heightAtCell = heightMap[Mx, My]; float materialAtCell = suspensionMap[Mx, My]; float droppedMaterial = materialAtCell * windGravity; // * 1.0f - (velocityAtCell) suspensionMap[Mx, My] = materialAtCell - droppedMaterial; heightMap[Mx, My] = heightAtCell + droppedMaterial; } } // Calculate stress and flow... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { float heightAtIndex = heightMap[Mx, My]; // ALTITUDE Vector3 pNormal = terData.GetInterpolatedNormal((float) Mx / Tw, (float) My / Th); // NORMAL float stress = (Vector3.Angle(pNormal, windVector) - 90) / 90; if (stress < 0.0f) { stress = 0.0f; } stressMap[Mx, My] = stress * heightAtIndex; float flow = 1.0f - Mathf.Abs(Vector3.Angle(pNormal, windVector) - 90) / 90; flowMap[Mx, My] = flow * heightAtIndex; // ^2 float inFlow = flow * heightAtIndex * windForce; // ^2 float velocityAtPoint = velocityMap[Mx, My]; float newVelocityAtPoint = velocityAtPoint + inFlow; // * UnityEngine.Random.value; velocityMap[Mx, My] = newVelocityAtPoint; // Lift material... materialAtIndex = suspensionMap[Mx, My]; float liftedMaterial = windLift * newVelocityAtPoint; // * UnityEngine.Random.value; if (materialAtIndex + liftedMaterial > windCapacity) { liftedMaterial = windCapacity - materialAtIndex; } suspensionMap[Mx, My] = materialAtIndex + liftedMaterial; heightMap[Mx, My] = heightAtIndex - liftedMaterial; } } // Calculate flow propagation... for (My = 0; My < Th; My++) { // y... if (My == 0) { yNeighbours = 2; yShift = 0; yIndex = 0; } else if (My == Th - 1) { yNeighbours = 2; yShift = -1; yIndex = 1; } else { yNeighbours = 3; yShift = -1; yIndex = 1; } for (Mx = 0; Mx < Tw; Mx++) { // x... if (Mx == 0) { xNeighbours = 2; xShift = 0; xIndex = 0; } else if (Mx == Tw - 1) { xNeighbours = 2; xShift = -1; xIndex = 1; } else { xNeighbours = 3; xShift = -1; xIndex = 1; } int Ny; int Nx; float flowAtIndex = flowMap[Mx, My]; float stressAtIndex = stressMap[Mx, My]; materialAtIndex = suspensionMap[Mx, My]; for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (Nx != xIndex || Ny != yIndex) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { Vector3 vectorToNeighbour = new Vector3(Nx + xShift, 0, -1 * (Ny + yShift)); float pWeighting = (90 - Vector3.Angle(vectorToNeighbour, windVector)) / 90; if (pWeighting < 0.0f) { pWeighting = 0.0f; } // Propagate velocity... float propagatedVelocitySoFar = velocityDiffMap[Mx + Nx + xShift, My + Ny + yShift]; float propagatedVelocity = pWeighting * (flowAtIndex - stressAtIndex) * 0.1f; if (propagatedVelocity < 0.0f) { propagatedVelocity = 0.0f; } float propagatedVelocityAfter = propagatedVelocitySoFar + propagatedVelocity; // Pass to difference map... velocityDiffMap[Mx + Nx + xShift, My + Ny + yShift] = propagatedVelocityAfter; // Lose velocity... float lostVelocitySoFar = velocityDiffMap[Mx, My]; float lostVelocityAfter = lostVelocitySoFar - propagatedVelocity; // Pass to difference map... velocityDiffMap[Mx, My] = lostVelocityAfter; // Propagate material... float propagatedMaterialSoFar = suspensionDiffMap[Mx + Nx + xShift, My + Ny + yShift]; float propagatedMaterial = materialAtIndex * propagatedVelocity; float propagatedMaterialAfter = propagatedMaterialSoFar + propagatedMaterial; // Pass to difference map... suspensionDiffMap[Mx + Nx + xShift, My + Ny + yShift] = propagatedMaterialAfter; // Lose material... float lostMaterialSoFar = suspensionDiffMap[Mx, My]; float lostMaterialAfter = lostMaterialSoFar - propagatedMaterial; // Pass to difference map... suspensionDiffMap[Mx, My] = lostMaterialAfter; } } } } } } // Apply suspension difference map to suspension map... float suspensionAtCell; for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { suspensionAtCell = suspensionMap[Mx, My] + suspensionDiffMap[Mx, My]; if (suspensionAtCell > 1.0f) { suspensionAtCell = 1.0f; } else if (suspensionAtCell < 0.0f) { suspensionAtCell = 0.0f; } suspensionMap[Mx, My] = suspensionAtCell; suspensionDiffMap[Mx, My] = 0.0f; } } // Apply velocity difference map to velocity map... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { velocityAtCell = velocityMap[Mx, My] + velocityDiffMap[Mx, My]; // Calculate entropy... velocityAtCell *= 1.0f - windEntropy; if (velocityAtCell > 1.0f) { velocityAtCell = 1.0f; } else if (velocityAtCell < 0.0f) { velocityAtCell = 0.0f; } velocityMap[Mx, My] = velocityAtCell; velocityDiffMap[Mx, My] = 0.0f; } } // Smooth... smoothIterations = 1; smoothBlend = 0.25f; float[,] smoothedHeightMap = (float[,]) heightMap.Clone(); GeneratorProgressDelegate generatorProgressDelegate = new GeneratorProgressDelegate(dummyGeneratorProgress); smoothedHeightMap = smooth(smoothedHeightMap, arraySize, generatorProgressDelegate); // Combine... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { float oldHeightAtPoint = heightMap[Mx, My]; float newHeightAtPoint = smoothedHeightMap[Mx, My]; float blendAmount = stressMap[Mx, My] * windSmoothing; float blendedHeightAtPoint = (newHeightAtPoint * blendAmount) + (oldHeightAtPoint * (1.0f - blendAmount)); heightMap[Mx, My] = blendedHeightAtPoint; } } // Update progress... float percentComplete = (float) iter / (float) iterations; erosionProgressDelegate("Applying Wind Erosion", "Applying wind erosion.", iter, iterations, percentComplete); } return heightMap; }
private float[,] normalise(float[,] heightMap, Vector2 arraySize, GeneratorProgressDelegate generatorProgressDelegate) { int Tx = (int) arraySize.x; int Ty = (int) arraySize.y; int Mx; int My; float highestPoint = 0.0f; float lowestPoint = 1.0f; generatorProgressDelegate("Normalise Filter", "Normalising height map. Please wait.", 0.0f); // Find highest and lowest points... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { float heightAtPoint = heightMap[Mx, My]; if (heightAtPoint < lowestPoint) { lowestPoint = heightAtPoint; } else if (heightAtPoint > highestPoint) { highestPoint = heightAtPoint; } } } generatorProgressDelegate("Normalise Filter", "Normalising height map. Please wait.", 0.5f); // Normalise... float heightRange = highestPoint - lowestPoint; float normalisedHeightRange = normaliseMax - normaliseMin; for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { float normalisedHeight = ((heightMap[Mx, My] - lowestPoint) / heightRange) * normalisedHeightRange; heightMap[Mx, My] = normaliseMin + normalisedHeight; } } generatorProgressDelegate("Normalise Filter", "Normalising height map. Please wait.", 1.0f); return heightMap; }
private float[,] generateVoronoi(float[,] heightMap, Vector2 arraySize, GeneratorProgressDelegate generatorProgressDelegate) { int Tx = (int) arraySize.x; int Ty = (int) arraySize.y; // Create Voronoi set... ArrayList voronoiSet = new ArrayList(); int i; for (i = 0; i < voronoiCells; i++) { Peak newPeak = new Peak(); int xCoord = (int) Mathf.Floor(UnityEngine.Random.value * Tx); int yCoord = (int) Mathf.Floor(UnityEngine.Random.value * Ty); float pointHeight = UnityEngine.Random.value; if (UnityEngine.Random.value > voronoiFeatures) { pointHeight = 0.0f; } newPeak.peakPoint = new Vector2(xCoord, yCoord); newPeak.peakHeight = pointHeight; voronoiSet.Add(newPeak); } int Mx; int My; float highestScore = 0.0f; for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { ArrayList peakDistances = new ArrayList(); for (i = 0; i < voronoiCells; i++) { Peak peakI = (Peak) voronoiSet[i]; Vector2 peakPoint = peakI.peakPoint; float distanceToPeak = Vector2.Distance(peakPoint, new Vector2(Mx, My)); PeakDistance newPeakDistance = new PeakDistance(); newPeakDistance.id = i; newPeakDistance.dist = distanceToPeak; peakDistances.Add(newPeakDistance); } peakDistances.Sort(); PeakDistance peakDistOne = (PeakDistance) peakDistances[0]; PeakDistance peakDistTwo = (PeakDistance) peakDistances[1]; int p1 = peakDistOne.id; float d1 = peakDistOne.dist; float d2 = peakDistTwo.dist; float scale = Mathf.Abs(d1 - d2) / ((Tx + Ty) / Mathf.Sqrt(voronoiCells)); Peak peakOne = (Peak) voronoiSet[p1]; float h1 = (float) peakOne.peakHeight; float hScore = h1 - Mathf.Abs(d1 / d2) * h1; float asRadians; switch (voronoiType) { case VoronoiType.Linear: // Nothing... break; case VoronoiType.Sine: asRadians = hScore * Mathf.PI - Mathf.PI / 2; hScore = 0.5f + Mathf.Sin(asRadians) / 2; break; case VoronoiType.Tangent: asRadians = hScore * Mathf.PI / 2; hScore = 0.5f + Mathf.Tan(asRadians) / 2; break; } hScore = (hScore * scale * voronoiScale) + (hScore * (1.0f - voronoiScale)); if (hScore < 0.0f) { hScore = 0.0f; } else if (hScore > 1.0f) { hScore = 1.0f; } heightMap[Mx, My] = hScore; if (hScore > highestScore) { highestScore = hScore; } } // Show progress... float completePoints = My * Ty; float totalPoints = Tx * Ty; float percentComplete = completePoints / totalPoints; generatorProgressDelegate("Voronoi Generator", "Generating height map. Please wait.", percentComplete); } // Normalise... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { float normalisedHeight = heightMap[Mx, My] * (1.0f / highestScore); heightMap[Mx, My] = normalisedHeight; } } return heightMap; }
private float[,] generatePerlin(float[,] heightMap, Vector2 arraySize, GeneratorProgressDelegate generatorProgressDelegate) { int Tw = (int) arraySize.x; int Th = (int) arraySize.y; // Zero... for (int My = 0; My < Th; My++) { for (int Mx = 0; Mx < Tw; Mx++) { heightMap[Mx, My] = 0.0f; } } PerlinNoise2D[] noiseFunctions = new PerlinNoise2D[perlinOctaves]; int freq = perlinFrequency; float amp = 1.0f; int i; for (i = 0; i < perlinOctaves; i++) { noiseFunctions[i] = new PerlinNoise2D(freq, amp); freq *= 2; amp /= 2; } for (i = 0; i < perlinOctaves; i++) { double xStep = (float) Tw / (float) noiseFunctions[i].Frequency; double yStep = (float) Th / (float) noiseFunctions[i].Frequency; for (int Px = 0; Px < Tw; Px++) { for (int Py = 0; Py < Th; Py++) { int Pa = (int) (Px / xStep); int Pb = Pa + 1; int Pc = (int) (Py / yStep); int Pd = Pc + 1; double interpValue = noiseFunctions[i].getInterpolatedPoint(Pa, Pb, Pc, Pd, (Px / xStep) - Pa, (Py / yStep) - Pc); heightMap[Px, Py] += (float) (interpValue * noiseFunctions[i].Amplitude); } } // Show progress... float percentComplete = (i + 1) / perlinOctaves; generatorProgressDelegate("Perlin Generator", "Generating height map. Please wait.", percentComplete); } GeneratorProgressDelegate normaliseProgressDelegate = new GeneratorProgressDelegate(dummyGeneratorProgress); float oldNormaliseMin = normaliseMin; float oldNormaliseMax = normaliseMax; float oldNormaliseBlend = normaliseBlend; normaliseMin = 0.0f; normaliseMax = 1.0f; normaliseBlend = 1.0f; heightMap = normalise(heightMap, arraySize, normaliseProgressDelegate); normaliseMin = oldNormaliseMin; normaliseMax = oldNormaliseMax; normaliseBlend = oldNormaliseBlend; for (int Px = 0; Px < Tw; Px++) { for (int Py = 0; Py < Th; Py++) { heightMap[Px, Py] = heightMap[Px, Py] * perlinAmplitude; } } for (i = 0; i < perlinOctaves; i++) { noiseFunctions[i] = null; } noiseFunctions = null; return heightMap; }
private float[,] generateDiamondSquare(float[,] heightMap, Vector2 arraySize, GeneratorProgressDelegate generatorProgressDelegate) { int Tw = (int) arraySize.x; int Th = (int) arraySize.y; float heightRange = 1.0f; int step = Tw - 1; heightMap[0, 0] = 0.5f; heightMap[Tw - 1, 0] = 0.5f; heightMap[0, Th - 1] = 0.5f; heightMap[Tw - 1, Th - 1] = 0.5f; generatorProgressDelegate("Fractal Generator", "Generating height map. Please wait.", 0.0f); while (step > 1) { // diamond for (int Tx = 0; Tx < Tw - 1; Tx += step){ for (int Ty = 0; Ty < Th - 1; Ty += step){ int sx = Tx + (step >> 1); int sy = Ty + (step >> 1); Vector2[] points = new Vector2[4]; points[0] = new Vector2(Tx, Ty); points[1] = new Vector2(Tx + step, Ty); points[2] = new Vector2(Tx, Ty + step); points[3] = new Vector2(Tx + step, Ty + step); dsCalculateHeight(heightMap, arraySize, sx, sy, points, heightRange); } } // square for (int Tx = 0; Tx < Tw - 1; Tx += step) { for (int Ty = 0; Ty < Th - 1; Ty += step) { int halfstep = step >> 1; int x1 = Tx + halfstep; int y1 = Ty; int x2 = Tx; int y2 = Ty + halfstep; Vector2[] points1 = new Vector2[4]; points1[0] = new Vector2(x1 - halfstep, y1); points1[1] = new Vector2(x1, y1 - halfstep); points1[2] = new Vector2(x1 + halfstep, y1); points1[3] = new Vector2(x1, y1 + halfstep); Vector2[] points2 = new Vector2[4]; points2[0] = new Vector2(x2 - halfstep, y2); points2[1] = new Vector2(x2, y2 - halfstep); points2[2] = new Vector2(x2 + halfstep, y2); points2[3] = new Vector2(x2, y2 + halfstep); dsCalculateHeight(heightMap, arraySize, x1, y1, points1, heightRange); dsCalculateHeight(heightMap, arraySize, x2, y2, points2, heightRange); } } heightRange *= diamondSquareDelta; step >>= 1; } generatorProgressDelegate("Fractal Generator", "Generating height map. Please wait.", 1.0f); return heightMap; }
public void VoronoiGenerator(FeatureType featureType, int cells, float features, float scale, float blend) { generatorTypeInt = 0; generatorType = GeneratorType.Voronoi; switch (featureType) { case FeatureType.Mountains: voronoiTypeInt = 0; voronoiType = VoronoiType.Linear; break; case FeatureType.Hills: voronoiTypeInt = 1; voronoiType = VoronoiType.Sine; break; case FeatureType.Plateaus: voronoiTypeInt = 2; voronoiType = VoronoiType.Tangent; break; } voronoiCells = cells; voronoiFeatures = features; voronoiScale = scale; voronoiBlend = blend; GeneratorProgressDelegate generatorProgressDelegate = new GeneratorProgressDelegate(dummyGeneratorProgress); generateTerrain(generatorProgressDelegate); }