Added better perlin terrain generation

Added function to scatter models onto the terrain
This commit is contained in:
iDunnoDev
2022-05-11 21:03:19 +01:00
committed by iDunnoDev
parent f6bba67897
commit b856b43209
26 changed files with 248 additions and 126 deletions

View File

@ -15,7 +15,7 @@ void Graphics2::CreateSceneGraph()
GetCamera()->SetCameraPosition(0.0f, 550.0f, -80.0f);
SceneGraphPointer sceneGraph = GetSceneGraph();
// This is where you add nodes to the scene graph
//shared_ptr<TexturedCubeNode> cube = make_shared<TexturedCubeNode>(L"Body", L"Textures\\woodbox.bmp");
//cube->SetWorldTransform(XMMatrixScaling(5.0f, 8.0f, 2.5f) * XMMatrixTranslation(0, 23.0f, 0));
@ -28,7 +28,7 @@ void Graphics2::CreateSceneGraph()
sceneGraph->Add(skyDome);
//shared_ptr<HeightMapTerrainNode> terrainNode = make_shared<HeightMapTerrainNode>(L"MainTerrain", L"Textures\\Example_HeightMap.raw", L"RandomWords");
shared_ptr<PerlinTerrainNode> terrainNode = make_shared<PerlinTerrainNode>(L"MainTerrain", L"LovelyCat", 10.0f, 20.0f, 1.0f, 1);
shared_ptr<PerlinTerrainNode> terrainNode = make_shared<PerlinTerrainNode>(L"MainTerrain", L"LovelyCat", 10.0f);
terrainNode->SetAmbientLight(DirectXFramework::GetDXFramework()->GetGlobalLighting()->GetAmbientLight());
terrainNode->SetDirectionalLight(DirectXFramework::GetDXFramework()->GetGlobalLighting()->GetDirectionalLightDirection(), DirectXFramework::GetDXFramework()->GetGlobalLighting()->GetDirectionalLightColor());
terrainNode->SetWaterColor(XMFLOAT4(SharedMethods::RGBValueToIntensity(0x84), SharedMethods::RGBValueToIntensity(0xC1), SharedMethods::RGBValueToIntensity(0xF9), 1.0f));
@ -36,26 +36,6 @@ void Graphics2::CreateSceneGraph()
//terrainNode->SetWaterColor(XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f));
sceneGraph->Add(terrainNode);
shared_ptr<SceneGraph> treeGroupA = make_shared<SceneGraph>(L"TreeGroupA");
shared_ptr<MeshNode> treeNode1 = make_shared<MeshNode>(L"Tree1", L"Models\\Tree\\Tree1.3ds");
treeNode1->SetWorldTransform(XMMatrixScaling(0.005f, 0.01f, 0.005f) * XMMatrixRotationAxis(XMVectorSet(1.0f, 0.0f, 1.0f, 0.0f), 0.1f * XM_PI) * XMMatrixTranslation(0.0f, 320.0f, 250.0f));
treeGroupA->Add(treeNode1);
shared_ptr<MeshNode> treeNode2 = make_shared<MeshNode>(L"Tree2", L"Models\\Tree\\Tree1.3ds");
treeNode2->SetWorldTransform(XMMatrixScaling(0.002f, 0.002f, 0.002f) * XMMatrixTranslation(-150.0f, 380.0f, 150.0f));
treeGroupA->Add(treeNode2);
shared_ptr<MeshNode> treeNode3 = make_shared<MeshNode>(L"Tree3", L"Models\\Tree\\Tree1.3ds");
treeNode3->SetWorldTransform(XMMatrixScaling(0.005f, 0.005f, 0.005f) * XMMatrixRotationAxis(XMVectorSet(1.0f, 0.0f, 1.0f, 0.0f), -0.1f * XM_PI) * XMMatrixTranslation(100.0f, 290.0f, 350.0f));
treeGroupA->Add(treeNode3);
shared_ptr<MeshNode> treeNode4 = make_shared<MeshNode>(L"Tree4", L"Models\\Tree\\Tree1.3ds");
treeNode4->SetWorldTransform(XMMatrixScaling(0.005f, 0.01f, 0.005f) * XMMatrixRotationAxis(XMVectorSet(1.0f, 0.0f, 1.0f, 0.0f), 0.1f * XM_PI) * XMMatrixTranslation(0.0f, 320.0f, 250.0f));
treeGroupA->Add(treeNode4);
sceneGraph->Add(treeGroupA);
shared_ptr<ControlledSplitMeshNode> plane1Node = make_shared<ControlledSplitMeshNode>(L"Plane1", L"Models\\Plane\\Bonanza.3DS");
plane1Node->SetStartOrientation(XMMatrixRotationAxis(XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), XM_PI) * XMMatrixRotationAxis(XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), 0.5f * XM_PI));
plane1Node->SetNodePosition(0.0f, 550.0f, -50.0f);
@ -74,9 +54,30 @@ void Graphics2::CreateSceneGraph()
void Graphics2::UpdateSceneGraph()
{
GetCurrentControlInputs();
SceneGraphPointer sceneGraph = GetSceneGraph();
shared_ptr<TerrainNode> mainTerrain = dynamic_pointer_cast<TerrainNode>(sceneGraph->Find(L"MainTerrain"));
if (!_initDone && mainTerrain != nullptr)
{
vector<TerrainPopNode> rngSpawnList;
TerrainPopNode treeModelA = TerrainPopNode();
treeModelA.modelName = L"Models\\Tree\\Tree2.fbx";
treeModelA.nodeBaseName = L"treeA";
treeModelA.scaleFactor = 1.0f;
rngSpawnList.push_back(treeModelA);
shared_ptr<SceneGraph> treeGroupA = make_shared<SceneGraph>(L"TreeGroupA");
mainTerrain->PopulateTerrain(treeGroupA, rngSpawnList, 10, 10, 400.0f, 550.0f, 0.9f, 1.0f);
treeGroupA->Initialise();
sceneGraph->Add(treeGroupA);
_initDone = true;
}
GetCurrentControlInputs();
XMVECTOR startCameraPos = GetCamera()->GetCameraPosition();
if (_currentPlayerObject != nullptr)
@ -189,8 +190,7 @@ void Graphics2::UpdateSceneGraph()
}
// Check the camera and our terrain boundaries
XMVECTOR currentCameraPos = GetCamera()->GetCameraPosition();
shared_ptr<TerrainNode> mainTerrain = dynamic_pointer_cast<TerrainNode>(sceneGraph->Find(L"MainTerrain"));
XMVECTOR currentCameraPos = GetCamera()->GetCameraPosition();
GetCamera()->Update();
if (mainTerrain != nullptr)
{

View File

@ -19,6 +19,8 @@ public:
void UpdateSceneGraph();
private:
bool _initDone = false;
bool _boosting = false;
float _boostMultiplier = 1;
float _boostMin = 1;

View File

@ -0,0 +1,27 @@
newmtl mat_0-default-grey.jpg
Ka 0.000000 0.000000 0.000000
Kd 0.698039 0.698039 0.698039
Ks 0.000000 0.000000 0.000000
d 1.000000
illum 2
Ns 9.84916
map_Kd default-grey.jpg
newmtl mat_1-boat_fishing02.jpg
Ka 0.000000 0.000000 0.000000
Kd 0.698039 0.698039 0.698039
Ks 0.000000 0.000000 0.000000
d 1.000000
illum 2
Ns 9.84916
map_Kd boat_fishing02.jpg
newmtl mat_2-default-grey.jpg
Ka 0.000000 0.000000 0.000000
Kd 0.698039 0.698039 0.698039
Ks 0.000000 0.000000 0.000000
d 1.000000
illum 2
Ns 9.84916
map_Kd default-grey.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,11 +1,8 @@
#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)
PerlinTerrainNode::PerlinTerrainNode(wstring name, wstring seed, float chunkSize, 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())
@ -14,44 +11,64 @@ PerlinTerrainNode::PerlinTerrainNode(wstring name, wstring seed, float offsetX,
}
}
void PerlinTerrainNode::GeneratePerlinValues()
{
std::seed_seq seedGenerator(_seedString.begin(), _seedString.end());
std::mt19937 generator;
generator.seed(seedGenerator);
std::uniform_int_distribution<> dist(0, 511);
char bufferChar;
int newPosition;
_p.clear();
copy(_staticP.begin(), _staticP.end(), back_inserter(_p));
for (int i = 0; i < 512; i++)
{
newPosition = dist(generator);
bufferChar = _p[i];
_p[i] = _p[newPosition];
_p[newPosition] = bufferChar;
}
}
// 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;
float mapZ = ((float)z / (float)_gridRows);
for (int x = 0; x < _gridCols; x++)
{
float mapX = ((float)x / (float)_gridCols) + 0.5f;
float mapX = ((float)x / (float)_gridCols);
float currentHeightValue = GetPerlinValueAt(_offsetX + mapX, _offsetY + mapZ, 8, 0.85f) - 0.25f;
//float currentHeightValue = GetPerlinValueAt(mapX, mapZ, 8, 0.85f) - 0.25f;
float currentHeightValue = 0.0f;
int octaves = 12;
float currentOctaveValue = 1.0f;
float stepOff = 1.0f;
float stepTotal = 0.0f;
// n covers the persistance, frequency and
for (int n = 1; n <= octaves; n++)
{
currentHeightValue += stepOff * GetNoiseValueAt(((float)x * currentOctaveValue) / ((float)_gridCols / _chunkSize), ((float)z * currentOctaveValue) / ((float)_gridRows / _chunkSize));
currentOctaveValue *= 2;
stepTotal += stepOff;
stepOff *= 0.5f;
}
//currentHeightValue = currentHeightValue / stepTotal; // *(currentHeightValue * 2.0f);
//currentHeightValue = currentHeightValue - floor(currentHeightValue);
_heightValues.push_back(currentHeightValue);
_heightValues.push_back((currentHeightValue + 1.0f) * 0.5f);
}
}
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);
@ -59,77 +76,56 @@ float PerlinTerrainNode::Fade(float t)
float PerlinTerrainNode::Grad(int hash, float x, float y)
{
// Fast Grad switch check converted from a java implimentation of perlin noise
switch (hash & 0xF)
int h = hash & 7;
float u = y;
float v = x;
if (h < 4)
{
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
u = x;
v = y;
}
}
// 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++)
float outA = u;
if ((h & 1))
{
total += (GetNoiseValueAt(x * frequency, y * frequency) * amplitude);
maxValue += amplitude;
amplitude *= persistance;
frequency *= 2;
outA = -u;
}
return total / maxValue;
float outB = 2.0f * v;
if ((h & 2))
{
outB = -2.0f * v;
}
return outA + outB;
}
// 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 ix0 = (int)floor(x);
int iy0 = (int)floor(y);
int x1 = x0 + 1;
int y1 = y0 + 1;
float fx0 = x - (float)ix0;
float fy0 = y - (float)iy0;
float fx1 = fx0 - 1.0f;
float fy1 = fy0 - 1.0f;
int ix1 = (ix0 + 1) & 0xff;
int iy1 = (iy0 + 1) & 0xff;
ix0 = ix0 & 0xff;
iy0 = iy0 & 0xff;
float dx = x - (float)x0;
float dy = y - (float)y0;
float t = Fade(fy0);
float s = Fade(fx0);
float u = Fade(dx);
float v = Fade(dy);
float nx0 = Grad(_p[ix0 + _p[iy0]], fx0, fy0);
float nx1 = Grad(_p[ix0 + _p[iy1]], fx0, fy1);
float n0 = SharedMethods::Lerp<float>(nx0, nx1, t);
int aa = p[p[x0] + y0];
int ab = p[p[x0] + y1];
int ba = p[p[x1] + y0];
int bb = p[p[x1] + y1];
nx0 = Grad(_p[ix1 + _p[iy0]], fx1, fy0);
nx1 = Grad(_p[ix1 + _p[iy1]], fx1, fy1);
float n1 = SharedMethods::Lerp<float>(nx0, nx1, t);
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;
return 0.507f * SharedMethods::Lerp(n0, n1, s);
}

View File

@ -9,21 +9,48 @@
class PerlinTerrainNode : public TerrainNode
{
public:
PerlinTerrainNode(wstring name, wstring seed, float offsetX, float offsetY, float chunkSize, int layers, int widthX = 1023, int widthZ = 1023, float waterHeight = 150.0f, int cellSizeX = 10, int cellSizeZ = 10);
PerlinTerrainNode(wstring name, wstring seed, float chunkSize, int widthX = 1023, int widthZ = 1023, float waterHeight = 300.0f, int cellSizeX = 10, int cellSizeZ = 10);
private:
vector<int> p;
float _offsetX;
float _offsetY;
// the static perlin permutation array, we could calculate a random one but apparently you can run into sequencing issues and this just seems like the best solution
vector<unsigned char> _staticP = { 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,
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
};
vector<unsigned char> _p;
float _chunkSize;
int _layers;
float Fade(float t);
float Grad(int hash, float x, float y);
void GeneratePerlinValues();
bool GeneratePerlinHeights();
float GetPerlinValueAt(float x, float y, int octaves, float persistance);
float GetNoiseValueAt(float x, float y);
void GeneratePerlinValues();
};

View File

@ -1,5 +1,6 @@
#pragma once
#include "DirectXCore.h"
#include <string>
struct CBUFFER
{
@ -16,6 +17,13 @@ struct CBUFFER
float padding[2];
};
struct TerrainPopNode
{
std::wstring nodeBaseName;
std::wstring modelName;
float scaleFactor;
};
namespace SharedMethods
{
XMMATRIX RotateFromPoint(const float x, const float y, const float z, const XMMATRIX rotationMatrix);

View File

@ -498,6 +498,52 @@ void TerrainNode::LoadTerrainTextures()
));
}
void TerrainNode::PopulateTerrain(SceneGraphPointer currentSceneGraph, vector<TerrainPopNode>& nodesForPop, int xStep, int zStep, float heightLower, float heightUpper, float slopeLower, float slopeUpper)
{
for (int z = 0; z < _gridRows; z += zStep)
{
for (int x = 0; x < _gridCols; x += xStep)
{
int currentIndex = ((z * _gridCols) + x) * 4;
float totalHeights = 0.0f;
float totalSlope = 0.0f;
for (int si = 0; si < 4; si++)
{
totalHeights += _terrainVerts[currentIndex + si].Position.y;
totalSlope += _terrainVerts[currentIndex + si].Normal.y;
}
float heightValue = totalHeights / 4.0f;
if (heightValue >= heightLower && heightValue <= heightUpper)
{
float avgSlope = totalSlope / 4.0f;
if (avgSlope >= slopeLower && avgSlope <= slopeUpper)
{
int nodeIndex = 0;
if (nodesForPop.size() > 1)
{
nodeIndex = rand() % nodesForPop.size();
}
float scale = (rand() % 100 * 0.01f) * nodesForPop[nodeIndex].scaleFactor;
float xPos = x * _cellSizeX + _terrainStartX;
float yPos = heightValue - 1.0f;
float zPos = (-z + 1) * _cellSizeZ + _terrainStartZ;
wstring nodeName = L"_" + to_wstring(z) + L"_" + to_wstring(x);
shared_ptr<MeshNode> newNode = make_shared<MeshNode>(nodesForPop[nodeIndex].nodeBaseName + nodeName, nodesForPop[nodeIndex].modelName);
newNode->SetWorldTransform(XMMatrixScaling(scale, scale, scale) * XMMatrixTranslation(xPos, yPos, zPos));
currentSceneGraph->Add(newNode);
}
}
}
}
}
void TerrainNode::GenerateBlendMap()
{
RGBA snowTops = RGBA{ 0u, 0u, 0u, 255u };
@ -507,7 +553,13 @@ void TerrainNode::GenerateBlendMap()
vector<RGBA> colorSteps = {darkDirt, lightDirt, grassLand, grassLand, grassLand, snowTops};
ColorGradient terrainBlendGradient = ColorGradient(80.0f, 620.0f, colorSteps);
float waterOffset = _waterHeight - 50.0f;
if (waterOffset < 0.0f)
{
waterOffset = 0.0f;
}
ColorGradient terrainBlendGradient = ColorGradient(waterOffset, 820.0f, colorSteps);
// Note that _numberOfRows and _numberOfColumns need to be setup
// to the number of rows and columns in your grid in order for this
@ -538,24 +590,30 @@ void TerrainNode::GenerateBlendMap()
int currentIndex = ((i * _gridCols) + j) * 4;
float totalHeights = 0.0f;
float biggestSlope = 0.0f;
float totalSlope = 0.0f;
for (int si = 0; si < 4; si++)
{
totalHeights += _terrainVerts[currentIndex + si].Position.y;
if (_terrainVerts[currentIndex + si].Normal.y > biggestSlope)
{
biggestSlope = _terrainVerts[currentIndex + si].Normal.y;
}
totalHeights += _terrainVerts[currentIndex + si].Position.y;
totalSlope += _terrainVerts[currentIndex + si].Normal.y;
}
float avgHeight = totalHeights / 4.0f;
float avgSlope = totalSlope / 4.0f;
RGBA currentBlend = terrainBlendGradient.GetRGBAValue(avgHeight);
r = (BYTE)currentBlend.red;
g = (BYTE)currentBlend.green;
b = (BYTE)currentBlend.blue;
a = (BYTE)currentBlend.alpha;
a = (BYTE)currentBlend.alpha;
// Override the G channel if we are cliff height and slope angle
if (avgHeight > 500.0f)
{
if (avgSlope < 0.6f)
{
g = (BYTE)SharedMethods::Clamp<int>(avgHeight - 450.0f, 0, 150);
}
}
DWORD mapValue = (a << 24) + (b << 16) + (g << 8) + r;
*blendMapPtr++ = mapValue;

View File

@ -8,7 +8,9 @@
#include "WICTextureLoader.h"
#include "SharedMethods.h"
#include "SceneNode.h"
#include "SceneGraph.h"
#include "MeshNode.h"
#include "SplitMeshNode.h"
typedef struct TerrainVertex
{
@ -49,6 +51,8 @@ public:
void Render(void);
void virtual Shutdown(void);
void PopulateTerrain(SceneGraphPointer currentSceneGraph, vector<TerrainPopNode>& nodesForPop, int xStep, int zStep, float heightLower, float heightUpper, float slopeLower, float slopeUpper);
float GetHeightAtPoint(float x, float z, bool waterCollide = false);
bool CheckXBoundary(float x);
bool CheckZBoundary(float z);