#include "PerlinTerrainNode.h" PerlinTerrainNode::PerlinTerrainNode(wstring name, wstring seed, float offsetX, float offsetY, float chunkSize, int layers, int widthX, int widthZ, float waterHeight, int cellSizeX, int cellSizeZ) : TerrainNode(name, seed, waterHeight, widthX, widthZ, cellSizeX, cellSizeZ) { _offsetX = offsetX; _offsetY = offsetY; _chunkSize = chunkSize; _layers = layers; GeneratePerlinValues(); if (!GeneratePerlinHeights()) { _initError = true; } } // Method for filling the height values array for the terrain generation bool PerlinTerrainNode::GeneratePerlinHeights() { for (int z = 0; z < _gridRows; z++) { float mapZ = ((float)z / (float)_gridRows) + 0.5f; for (int x = 0; x < _gridCols; x++) { float mapX = ((float)x / (float)_gridCols) + 0.5f; float currentHeightValue = GetPerlinValueAt(_offsetX + mapX, _offsetY + mapZ, 8, 0.85f) - 0.25f; //currentHeightValue = currentHeightValue - floor(currentHeightValue); _heightValues.push_back(currentHeightValue); } } return true; } // Method for generating the permutation array needed for perlin noise void PerlinTerrainNode::GeneratePerlinValues() { // Set the permutation array length p.resize(256); // Fill the permutation array with 0 to 255 iota(p.begin(), p.end(), 0); default_random_engine engine(_seedHash); // Shuffle the array values using the seed value given shuffle(p.begin(), p.end(), engine); // Duplicate the permutation results so our array is 512 p.insert(p.end(), p.begin(), p.end()); } float PerlinTerrainNode::Fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } float PerlinTerrainNode::Grad(int hash, float x, float y) { // Fast Grad switch check converted from a java implimentation of perlin noise switch (hash & 0xF) { case 0x0: return x; case 0x1: return x + y; case 0x2: return y; case 0x3: return -x + y; case 0x4: return -x; case 0x5: return -x - y; case 0x6: return -y; case 0x7: return x - y; case 0x8: return x; case 0x9: return x + y; case 0xA: return y; case 0xB: return -x + y; case 0xC: return -x; case 0xD: return -x - y; case 0xE: return -y; case 0xF: return x - y; default: return 0; // Never occurs } } // Method to calculate the perlin value at x/y with the given octaves and persistance values, also normalised float PerlinTerrainNode::GetPerlinValueAt(float x, float y, int octaves, float persistance) { float total = 0.0f; float frequency = 1.0f; float amplitude = 1.0f; float maxValue = 0.0f; for (int i = 0; i < octaves; i++) { total += (GetNoiseValueAt(x * frequency, y * frequency) * amplitude); maxValue += amplitude; amplitude *= persistance; frequency *= 2; } return total / maxValue; } // Method to get the perlin noise value at x/y float PerlinTerrainNode::GetNoiseValueAt(float x, float y) { int x0 = (int)x & 255; int y0 = (int)y & 255; int x1 = x0 + 1; int y1 = y0 + 1; float dx = x - (float)x0; float dy = y - (float)y0; float u = Fade(dx); float v = Fade(dy); int aa = p[p[x0] + y0]; int ab = p[p[x0] + y1]; int ba = p[p[x1] + y0]; int bb = p[p[x1] + y1]; float na0 = Grad(aa, dx, dy); float na1 = Grad(ba, dx - 1.0f, dy); float nal = SharedMethods::Lerp(na0, na1, u); float nb0 = Grad(ab, dx, dy - 1.0f); float nb1 = Grad(bb, dx - 1.0f, dy - 1.0f); float nbl = SharedMethods::Lerp(nb0, nb1, u); return (SharedMethods::Lerp(nal, nbl, v) + 1.0f) / 2.0f; }