Added Comments
Added ability to hold shift and skip the terrain generation when loading Added ability for the perlin terrain to save a raw image of the terrain to use as a cache
This commit is contained in:
@ -185,7 +185,9 @@ void Camera::Update(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Trying some rotation so that the camera actually follows the model instead of staying in place
|
||||
XMVECTOR offset = XMVectorSet(_followOffset.x, _followOffset.y, _followOffset.z, 1.0f);
|
||||
// The pitch and roll rotations really mess it up and i dont have time to look into it, the simple turn follow is good enough for now
|
||||
XMMATRIX offsetRotationMatrix = XMMatrixRotationAxis(defaultUp, _cameraYaw); // *XMMatrixRotationAxis(defaultForward, _cameraPitch);
|
||||
|
||||
cameraPosition = _nodeFollowed->GetNodePosition() + XMVector3TransformCoord(offset, offsetRotationMatrix);
|
||||
|
@ -27,6 +27,8 @@ public:
|
||||
float GetRoll() const;
|
||||
void SetLeftRight(float leftRight);
|
||||
void SetForwardBack(float forwardBack);
|
||||
|
||||
// Method for setting which object node is being followed
|
||||
void SetFollowNode(shared_ptr<ObjectNode> nodeFollowed, XMFLOAT3 followOffset, bool positionOnly);
|
||||
|
||||
private:
|
||||
@ -38,6 +40,7 @@ private:
|
||||
float _moveForwardBack;
|
||||
|
||||
float _cameraYaw;
|
||||
// Camera yaw previous to help with the rotation, remove later
|
||||
float _cameraYawPrev;
|
||||
float _cameraPitch;
|
||||
float _cameraRoll;
|
||||
|
@ -6,6 +6,7 @@ ControlledMeshNode::ControlledMeshNode(wstring name, wstring modelName) : MeshNo
|
||||
|
||||
void ControlledMeshNode::Update(FXMMATRIX& currentWorldTransformation)
|
||||
{
|
||||
// Run the update for both parent node classes
|
||||
MeshNode::Update(currentWorldTransformation);
|
||||
ObjectNode::Update(_worldTransformation);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "ObjectNode.h"
|
||||
#include "MeshNode.h"
|
||||
|
||||
// Class for handing the moveable mesh nodes, inherits from both the mesh and object nodes, the object node contains what is needed for pitch, yaw and forward back movement
|
||||
class ControlledMeshNode : public MeshNode, public ObjectNode
|
||||
{
|
||||
public:
|
||||
|
@ -6,6 +6,7 @@ ControlledSplitMeshNode::ControlledSplitMeshNode(wstring name, wstring modelName
|
||||
|
||||
void ControlledSplitMeshNode::Update(FXMMATRIX& currentWorldTransformation)
|
||||
{
|
||||
// Run the update for both parent node classes
|
||||
ObjectNode::Update(_worldTransformation);
|
||||
SceneGraph::Update(currentWorldTransformation);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "ObjectNode.h"
|
||||
#include "SubMeshNode.h"
|
||||
|
||||
// Class for handing the moveable split mesh nodes, inherits from both the mesh and object nodes, the object node contains what is needed for pitch, yaw and forward back movement
|
||||
class ControlledSplitMeshNode : public SplitMeshNode, public ObjectNode
|
||||
{
|
||||
public:
|
||||
|
@ -11,6 +11,7 @@ GamePadController::~GamePadController(void)
|
||||
{
|
||||
}
|
||||
|
||||
// Method for processing the current inputs, i added params for the current inputs vector and a bool to set the boosting varaible
|
||||
void GamePadController::ProcessGameController(set<ControlInputs>& currentInputs, bool &boostHit)
|
||||
{
|
||||
DWORD magnitudeSquared;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <XInput.h>
|
||||
#pragma comment(lib, "XInput.lib")
|
||||
|
||||
// Emum class for helping with the select of which controller inputs to use
|
||||
enum class ControlInputs { Forward, Back, TurnLeft, TurnRight, StrafeLeft, StrafeRight, Up, Down, Fire1, Fire2 };
|
||||
|
||||
class GamePadController
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
GlobalLighting::GlobalLighting()
|
||||
{
|
||||
// Set the default lighting to 0
|
||||
_ambientLight = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
_directionalLightVector = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
_directionalLightColor = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "DirectXCore.h"
|
||||
|
||||
// Class for holding the global lighting settings, this saves having to go through every node to change these values
|
||||
class GlobalLighting
|
||||
{
|
||||
public:
|
||||
|
@ -4,6 +4,7 @@ Graphics2 app;
|
||||
|
||||
void Graphics2::CreateSceneGraph()
|
||||
{
|
||||
// Reset our variables for movement
|
||||
_boostMultiplier = _boostMin;
|
||||
_flySpeed = 1;
|
||||
_turnSpeed = 1;
|
||||
@ -13,7 +14,16 @@ void Graphics2::CreateSceneGraph()
|
||||
|
||||
_currentPlayerObject = nullptr;
|
||||
|
||||
// Check if the shift and ctrl keys are held to disable the clutter
|
||||
if (GetAsyncKeyState(VK_SHIFT) && GetAsyncKeyState(VK_CONTROL))
|
||||
{
|
||||
_noClutter = true;
|
||||
}
|
||||
|
||||
// Set the default camera position
|
||||
GetCamera()->SetCameraPosition(0.0f, 550.0f, -80.0f);
|
||||
|
||||
// Get the main scene graph
|
||||
SceneGraphPointer sceneGraph = GetSceneGraph();
|
||||
|
||||
// This is where you add nodes to the scene graph
|
||||
@ -21,29 +31,40 @@ void Graphics2::CreateSceneGraph()
|
||||
//cube->SetWorldTransform(XMMatrixScaling(5.0f, 8.0f, 2.5f) * XMMatrixTranslation(0, 23.0f, 0)); XMMatrixScaling(50.0f, 800.0f, 50.0f) * XMMatrixTranslation(-65.0f, 350.0f, -65.0f)
|
||||
//sceneGraph->Add(cube);
|
||||
|
||||
// Set some global lighting variables for the ambient and directional lights, if i get time look into other lighting?
|
||||
DirectXFramework::GetDXFramework()->GetGlobalLighting()->SetAmbientLight(XMFLOAT4(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
DirectXFramework::GetDXFramework()->GetGlobalLighting()->SetDirectionalLight(XMVectorSet(0.5f, -1.0f, -1.0f, 0.0f), XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f));
|
||||
|
||||
shared_ptr<SkyNode> skyDome = make_shared<SkyNode>(L"SkyDome", L"Textures\\SkyWater.dds", 180.0f);
|
||||
// Add the skybox node
|
||||
shared_ptr<SkyNode> skyDome = make_shared<SkyNode>(L"SkyDome", L"Textures\\SkyWater.dds", 360.0f);
|
||||
sceneGraph->Add(skyDome);
|
||||
|
||||
// Set the terrain node, we can use the heightmap or perlin noise nodes
|
||||
//shared_ptr<HeightMapTerrainNode> terrainNode = make_shared<HeightMapTerrainNode>(L"MainTerrain", L"Textures\\Example_HeightMap.raw", L"RandomWords");
|
||||
|
||||
//My height maps do not load with the given function, i have no idea what format its in then...
|
||||
//shared_ptr<HeightMapTerrainNode> terrainNode = make_shared<HeightMapTerrainNode>(L"MainTerrain", L"TerrainCache\\cache_LovelyCat_10_000000.raw", L"RandomWords");
|
||||
|
||||
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());
|
||||
// Set the water color for tinting here
|
||||
terrainNode->SetWaterColor(XMFLOAT4(SharedMethods::RGBValueToIntensity(0x84), SharedMethods::RGBValueToIntensity(0xC1), SharedMethods::RGBValueToIntensity(0xF9), 1.0f));
|
||||
//terrainNode->SetWaterColor(XMFLOAT4(1.0f, 0.0f, 0.8f, 1.0f));
|
||||
//terrainNode->SetWaterColor(XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f));
|
||||
// Rivers of blood mode
|
||||
//terrainNode->SetWaterColor(XMFLOAT4(SharedMethods::RGBValueToIntensity(0x66), 0.0f, 0.0f, 1.0f));
|
||||
sceneGraph->Add(terrainNode);
|
||||
|
||||
// Add the controlled plane model
|
||||
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);
|
||||
sceneGraph->Add(plane1Node);
|
||||
|
||||
// Add the boat scene graph so i can scale and position the model
|
||||
shared_ptr<SceneGraph> boatGraph = make_shared<SceneGraph>(L"boatGraph");
|
||||
boatGraph->SetWorldTransform(XMMatrixScaling(0.1f, 0.1f, 0.1f) * XMMatrixTranslation(-1880.0f, 290.0f, 610.0f));
|
||||
|
||||
// Added the moveable boat model
|
||||
shared_ptr<ControlledMeshNode> boat1Node = make_shared<ControlledMeshNode>(L"Boat1", L"Models\\Boat\\Boat.FBX");
|
||||
boat1Node->SetStartOrientation(XMMatrixRotationAxis(XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), (90.0f * XM_PI) / 180.0f) * XMMatrixRotationAxis(XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), (90.0f * XM_PI) / 180.0f));
|
||||
boat1Node->SetNodePosition(0.0f, 0.0f, 0.0f);
|
||||
@ -51,9 +72,11 @@ void Graphics2::CreateSceneGraph()
|
||||
|
||||
sceneGraph->Add(boatGraph);
|
||||
|
||||
// Set the current player object to the plane and turn on follow mode
|
||||
_currentPlayerObject = plane1Node;
|
||||
GetCamera()->SetFollowNode(plane1Node, XMFLOAT3(0.0f, 40.0f, -100.0f), false);
|
||||
|
||||
// Set the background color although i assume this will be deprecated when the skybox is added
|
||||
SetBackgroundColour(XMFLOAT4(0.29f, 0.38f, 0.72f, 1.0f));
|
||||
//SetBackgroundColour(XMFLOAT4(SharedMethods::RGBValueToIntensity(0x89), 0, 1, 1));
|
||||
|
||||
@ -62,18 +85,23 @@ void Graphics2::CreateSceneGraph()
|
||||
_currentPropRotation = 0;
|
||||
}
|
||||
|
||||
// Method for generating the random clutter on the terrain
|
||||
void Graphics2::GenerateClutter()
|
||||
{
|
||||
SceneGraphPointer sceneGraph = GetSceneGraph();
|
||||
// Get the current terrain node so that we can access its methods
|
||||
shared_ptr<TerrainNode> mainTerrain = dynamic_pointer_cast<TerrainNode>(sceneGraph->Find(L"MainTerrain"));
|
||||
|
||||
// Check if the terrain is actually loaded before trying to access it.
|
||||
if (!_initDone && mainTerrain != nullptr)
|
||||
{
|
||||
// Create the spawn object lists
|
||||
vector<TerrainPopNode> rngSpawnList;
|
||||
vector<TerrainPopNode> rngBushSpawnList;
|
||||
vector<TerrainPopNode> rngSnowSpawnList;
|
||||
vector<TerrainPopNode> rngDirtSpawnList;
|
||||
|
||||
// Trees
|
||||
TerrainPopNode treeModelA = TerrainPopNode();
|
||||
treeModelA.modelName = L"Models\\Tree\\Tree1.fbx";
|
||||
treeModelA.nodeBaseName = L"treeA";
|
||||
@ -89,10 +117,13 @@ void Graphics2::GenerateClutter()
|
||||
|
||||
shared_ptr<SceneGraph> treeGroupA = make_shared<SceneGraph>(L"TreeGroupA");
|
||||
|
||||
// Populate the terrain and add the child scene graph to the main scenegraph
|
||||
mainTerrain->PopulateTerrain(treeGroupA, rngSpawnList, 20, 20, 500.0f, 650.0f, 0.9f, 1.0f);
|
||||
// Initialise the newly created models
|
||||
treeGroupA->Initialise();
|
||||
sceneGraph->Add(treeGroupA);
|
||||
|
||||
// Bushes
|
||||
TerrainPopNode bushModelA = TerrainPopNode();
|
||||
bushModelA.modelName = L"Models\\Tree\\Bush1.fbx";
|
||||
bushModelA.nodeBaseName = L"bushA";
|
||||
@ -130,6 +161,7 @@ void Graphics2::GenerateClutter()
|
||||
bushGroupA->Initialise();
|
||||
sceneGraph->Add(bushGroupA);
|
||||
|
||||
// The Snow trees
|
||||
TerrainPopNode treeModelSA = TerrainPopNode();
|
||||
treeModelSA.modelName = L"Models\\Tree\\Tree3.fbx";
|
||||
treeModelSA.nodeBaseName = L"treeSA";
|
||||
@ -143,6 +175,7 @@ void Graphics2::GenerateClutter()
|
||||
treeGroupSA->Initialise();
|
||||
sceneGraph->Add(treeGroupSA);
|
||||
|
||||
// The Rocks and desert tree
|
||||
TerrainPopNode treeModelDA = TerrainPopNode();
|
||||
treeModelDA.modelName = L"Models\\Tree\\Tree4.fbx";
|
||||
treeModelDA.nodeBaseName = L"treeDA";
|
||||
@ -181,16 +214,21 @@ void Graphics2::GenerateClutter()
|
||||
void Graphics2::UpdateSceneGraph()
|
||||
{
|
||||
SceneGraphPointer sceneGraph = GetSceneGraph();
|
||||
|
||||
shared_ptr<TerrainNode> mainTerrain = dynamic_pointer_cast<TerrainNode>(sceneGraph->Find(L"MainTerrain"));
|
||||
|
||||
if (!_initDone && mainTerrain != nullptr)
|
||||
// Check if the main terrain exists and runs the generate clutter method, we have to run it in the update because
|
||||
// the terrain hasnt actually been generated til now, and we need the heights and normals so we can check if something can fit in a place
|
||||
if (!_initDone && mainTerrain != nullptr && !_noClutter)
|
||||
{
|
||||
//GenerateClutter();
|
||||
GenerateClutter();
|
||||
}
|
||||
|
||||
// This block gets the current inputs from either the keyboard of the gamepad, gamepad is a little iffy since the pitch is kind of wanky
|
||||
GetCurrentControlInputs();
|
||||
XMVECTOR startCameraPos = GetCamera()->GetCameraPosition();
|
||||
|
||||
// If the current player object is set, then always move forward
|
||||
if (_currentPlayerObject != nullptr)
|
||||
{
|
||||
_currentPlayerObject->SetForwardBack(_flySpeed * _boostMultiplier);
|
||||
@ -278,9 +316,11 @@ void Graphics2::UpdateSceneGraph()
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the plane and boat nodes so that we can move them
|
||||
shared_ptr<ControlledSplitMeshNode> plane1 = dynamic_pointer_cast<ControlledSplitMeshNode>(sceneGraph->Find(L"Plane1"));
|
||||
shared_ptr<ControlledMeshNode> boat1 = dynamic_pointer_cast<ControlledMeshNode>(sceneGraph->Find(L"Boat1"));
|
||||
|
||||
// Move the plane
|
||||
if (plane1 != nullptr)
|
||||
{
|
||||
//plane1->SetWorldTransform(SharedMethods::RotateFromPoint(-60.0f, 0, 0, XMMatrixRotationAxis(XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), _currentRotation * XM_PI / 180.0f)));
|
||||
@ -288,15 +328,19 @@ void Graphics2::UpdateSceneGraph()
|
||||
//sceneGraph->Find(L"Plane1")->GetFirstChild()->GetFirstChild()->SetWorldTransform(XMMatrixRotationAxis(XMVectorSet(0.0f, -1.0f, -1.0f, 0.0f), _currentSideRotation * XM_PI / 180.0f));
|
||||
|
||||
//sceneGraph->Find(L"Plane1")->Update((SharedMethods::RotateFromPoint(30.0f, -20.0f, 0, XMMatrixRotationAxis(XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), _currentSideRotation * XM_PI / 180.0f))) * XMMatrixRotationAxis(XMVectorSet(0.0f, -1.0f, 0.0f, 0.0f), _currentRotation * XM_PI / 180.0f));
|
||||
|
||||
// Spin the planes propeller, it goes faster if you are boosting
|
||||
plane1->Find(L"airscrew")->SetWorldTransform(SharedMethods::RotateFromPoint(0.0f, 15.471f, 14.5f, XMMatrixRotationAxis(XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), _currentPropRotation * XM_PI / 180.0f)));
|
||||
}
|
||||
|
||||
// Move the boat
|
||||
if (boat1 != nullptr)
|
||||
{
|
||||
boat1->SetForwardBack(-3.0f);
|
||||
boat1->SetYaw(-0.2f);
|
||||
}
|
||||
|
||||
// Reset the rotation numbers when theyve done a full rotation so they just just increase forever
|
||||
if (_currentRotation == 360)
|
||||
{
|
||||
_currentRotation = 0;
|
||||
@ -320,6 +364,7 @@ void Graphics2::UpdateSceneGraph()
|
||||
GetCamera()->Update();
|
||||
if (mainTerrain != nullptr)
|
||||
{
|
||||
// If a boundary is hit then move the object back
|
||||
bool boundaryHit = false;
|
||||
XMFLOAT4 cameraOffset = XMFLOAT4(XMVectorGetX(startCameraPos), XMVectorGetY(startCameraPos), XMVectorGetZ(startCameraPos), 0.0f);
|
||||
|
||||
@ -372,6 +417,7 @@ void Graphics2::UpdateSceneGraph()
|
||||
ResetCurrentControlInputs();
|
||||
}
|
||||
|
||||
// Method for getting the current frames inputs
|
||||
void Graphics2::GetCurrentControlInputs()
|
||||
{
|
||||
// Check if the window has focus before accepting any keypresses
|
||||
@ -393,25 +439,25 @@ void Graphics2::GetCurrentControlInputs()
|
||||
}
|
||||
|
||||
// Forward
|
||||
if (GetAsyncKeyState(0x57))
|
||||
if (GetAsyncKeyState(0x57) || GetAsyncKeyState(VK_UP))
|
||||
{
|
||||
_currentInputs.insert(ControlInputs::Forward);
|
||||
}
|
||||
|
||||
// Back
|
||||
if (GetAsyncKeyState(0x53))
|
||||
if (GetAsyncKeyState(0x53) || GetAsyncKeyState(VK_DOWN))
|
||||
{
|
||||
_currentInputs.insert(ControlInputs::Back);
|
||||
}
|
||||
|
||||
// Turn Left
|
||||
if (GetAsyncKeyState(0x41))
|
||||
if (GetAsyncKeyState(0x41) || GetAsyncKeyState(VK_LEFT))
|
||||
{
|
||||
_currentInputs.insert(ControlInputs::TurnLeft);
|
||||
}
|
||||
|
||||
// Turn Right
|
||||
if (GetAsyncKeyState(0x44))
|
||||
if (GetAsyncKeyState(0x44) || GetAsyncKeyState(VK_RIGHT))
|
||||
{
|
||||
_currentInputs.insert(ControlInputs::TurnRight);
|
||||
}
|
||||
@ -442,6 +488,7 @@ void Graphics2::GetCurrentControlInputs()
|
||||
}
|
||||
}
|
||||
|
||||
// Method for clearing the current inputs ready for the next frame
|
||||
void Graphics2::ResetCurrentControlInputs()
|
||||
{
|
||||
_currentInputs.clear();
|
||||
|
@ -28,9 +28,9 @@ private:
|
||||
float _boostStep = 0.5f;
|
||||
float _boostMax = 5.0f;
|
||||
|
||||
float _flySpeed;
|
||||
float _turnSpeed;
|
||||
int _invertPitch;
|
||||
float _flySpeed = 0.0f;
|
||||
float _turnSpeed = 0.0f;
|
||||
int _invertPitch = 0;
|
||||
|
||||
float _currentRotation = 0.0f;
|
||||
float _currentSideRotation = 0.0f;
|
||||
@ -43,6 +43,8 @@ private:
|
||||
|
||||
shared_ptr<ObjectNode> _currentPlayerObject;
|
||||
|
||||
bool _noClutter = false;
|
||||
|
||||
void GetCurrentControlInputs();
|
||||
void ResetCurrentControlInputs();
|
||||
void GenerateClutter();
|
||||
|
@ -5,6 +5,7 @@
|
||||
HeightMapTerrainNode::HeightMapTerrainNode(wstring name, wstring heightMap, wstring seed, float waterHeight, int widthX, int widthZ, int cellSizeX, int cellSizeZ) : TerrainNode(name, seed, waterHeight, widthX, widthZ, cellSizeX, cellSizeZ)
|
||||
{
|
||||
_heightMap = heightMap;
|
||||
// Try to load the height map otherwise fail and set the error bool
|
||||
if (!LoadHeightMap(_heightMap))
|
||||
{
|
||||
_initError = true;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "SharedMethods.h"
|
||||
#include "TerrainNode.h"
|
||||
|
||||
// Height map terrain node, contains the methods needed to generate a terrain node with the given weird height map
|
||||
class HeightMapTerrainNode : public TerrainNode
|
||||
{
|
||||
public:
|
||||
|
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
#include "DirectXCore.h"
|
||||
|
||||
// The object node contains all of the methods and variables needed to move a node using pitch, yaw, roll, and the directions
|
||||
// It should be inherited by another node so that it doesnt interupt with the normal rendering functions and saves us having to
|
||||
// duplicate code for every time, it also allows the child classes to be accessed by the ObjectNode type when needed
|
||||
class ObjectNode
|
||||
{
|
||||
public:
|
||||
|
@ -2,17 +2,29 @@
|
||||
|
||||
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)
|
||||
{
|
||||
// Set the chink size
|
||||
_chunkSize = chunkSize;
|
||||
|
||||
GeneratePerlinValues();
|
||||
if (!GeneratePerlinHeights())
|
||||
// Check if a cache image exists and load it if so
|
||||
if (!ReadCache())
|
||||
{
|
||||
_initError = true;
|
||||
// No Cache image existed so generate the perlin noise map
|
||||
GeneratePerlinValues();
|
||||
if (!GeneratePerlinHeights())
|
||||
{
|
||||
_initError = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The generation was successful, save it to the cache
|
||||
CacheOutput();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PerlinTerrainNode::GeneratePerlinValues()
|
||||
{
|
||||
// Setup the RNG
|
||||
std::seed_seq seedGenerator(_seedString.begin(), _seedString.end());
|
||||
std::mt19937 generator;
|
||||
generator.seed(seedGenerator);
|
||||
@ -20,9 +32,12 @@ void PerlinTerrainNode::GeneratePerlinValues()
|
||||
char bufferChar;
|
||||
int newPosition;
|
||||
|
||||
// Clear the permutation array so we can reinsert a new copy
|
||||
_p.clear();
|
||||
copy(_staticP.begin(), _staticP.end(), back_inserter(_p));
|
||||
|
||||
// Loop through and alternate the numbers so they are in a random position, this will not create any new numbers so we should always have what we started with
|
||||
// just in a different order
|
||||
for (int i = 0; i < 512; i++)
|
||||
{
|
||||
newPosition = dist(generator);
|
||||
@ -32,20 +47,83 @@ void PerlinTerrainNode::GeneratePerlinValues()
|
||||
}
|
||||
}
|
||||
|
||||
bool PerlinTerrainNode::ReadCache()
|
||||
{
|
||||
ifstream cachedImage;
|
||||
string stringSeed = string(_seedString.begin(), _seedString.end());
|
||||
string chunkSizeString = to_string(_chunkSize);
|
||||
replace(chunkSizeString.begin(), chunkSizeString.end(), '.', '_');
|
||||
cachedImage.open("TerrainCache\\cache_" + stringSeed + "_" + chunkSizeString + ".raw", std::ios_base::binary);
|
||||
|
||||
// Couldnt open the cache, either doesnt exist or some issue opening the file, a new one should be generated
|
||||
if (!cachedImage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the files bytes into a vector we can foreach loop
|
||||
vector<char> fileBytes((istreambuf_iterator<char>(cachedImage)), (istreambuf_iterator<char>()));
|
||||
|
||||
for (BYTE currentByte : fileBytes)
|
||||
{
|
||||
// Change the number back into a float value, some precision is lost because of this but i cant notice it really
|
||||
float heightValue = currentByte / 255.0f;
|
||||
_heightValues.push_back(heightValue);
|
||||
}
|
||||
|
||||
cachedImage.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PerlinTerrainNode::CacheOutput()
|
||||
{
|
||||
// Create a unique string name for the seed and cluster size given
|
||||
string stringSeed = string(_seedString.begin(), _seedString.end());
|
||||
string chunkSizeString = to_string(_chunkSize);
|
||||
replace(chunkSizeString.begin(), chunkSizeString.end(), '.', '_');
|
||||
ofstream output("TerrainCache\\cache_" + stringSeed + "_" + chunkSizeString + ".raw", std::ios_base::binary);
|
||||
|
||||
// Loop through the height values array
|
||||
int current = 0;
|
||||
for (float value : _heightValues)
|
||||
{
|
||||
// Force the value to be within 255, we lose some precision from this conversion but to generate an image it has to be this way
|
||||
BYTE currentByte = (int)(255 * value) & 0xff;
|
||||
|
||||
// Write the value to the file buffer
|
||||
output << currentByte;
|
||||
|
||||
// Constantly flush the buffer so that we dont have it waiting in memory for too long
|
||||
if (current >= 100)
|
||||
{
|
||||
output.flush();
|
||||
current = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
// Close the file
|
||||
output.close();
|
||||
}
|
||||
|
||||
// Method for filling the height values array for the terrain generation
|
||||
bool PerlinTerrainNode::GeneratePerlinHeights()
|
||||
{
|
||||
for (int z = 0; z < _gridRows; z++)
|
||||
for (int z = 0; z < (int)_gridRows; z++)
|
||||
{
|
||||
float mapZ = ((float)z / (float)_gridRows);
|
||||
|
||||
for (int x = 0; x < _gridCols; x++)
|
||||
for (int x = 0; x < (int)_gridCols; x++)
|
||||
{
|
||||
float mapX = ((float)x / (float)_gridCols);
|
||||
|
||||
//float currentHeightValue = GetPerlinValueAt(mapX, mapZ, 8, 0.85f) - 0.25f;
|
||||
float currentHeightValue = 0.0f;
|
||||
|
||||
// Octaves adds "noise" to the final outcome, 12 is really the max before it tanks the system for little effect
|
||||
int octaves = 12;
|
||||
float currentOctaveValue = 1.0f;
|
||||
float stepOff = 1.0f;
|
||||
@ -53,6 +131,7 @@ bool PerlinTerrainNode::GeneratePerlinHeights()
|
||||
// n covers the persistance, frequency and
|
||||
for (int n = 1; n <= octaves; n++)
|
||||
{
|
||||
// Generate a noise at the given position
|
||||
currentHeightValue += stepOff * GetNoiseValueAt(((float)x * currentOctaveValue) / ((float)_gridCols / _chunkSize), ((float)z * currentOctaveValue) / ((float)_gridRows / _chunkSize));
|
||||
currentOctaveValue *= 2;
|
||||
stepTotal += stepOff;
|
||||
@ -62,6 +141,7 @@ bool PerlinTerrainNode::GeneratePerlinHeights()
|
||||
|
||||
//currentHeightValue = currentHeightValue / stepTotal; // *(currentHeightValue * 2.0f);
|
||||
//currentHeightValue = currentHeightValue - floor(currentHeightValue);
|
||||
// Normalise the result to a number between 0 and 1, since most like to generate numbers between -1 and 1, we just have to add 1 and half the value
|
||||
_heightValues.push_back((currentHeightValue + 1.0f) * 0.5f);
|
||||
}
|
||||
}
|
||||
@ -76,6 +156,7 @@ float PerlinTerrainNode::Fade(float t)
|
||||
|
||||
float PerlinTerrainNode::Grad(int hash, float x, float y)
|
||||
{
|
||||
// To be honest this is something i dont really understand, it shifts bits around to see which direction we need to multiply in
|
||||
int h = hash & 7;
|
||||
float u = y;
|
||||
float v = x;
|
||||
|
@ -3,9 +3,11 @@
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <fstream>
|
||||
#include "SharedMethods.h"
|
||||
#include "TerrainNode.h"
|
||||
|
||||
// Class for generating a terrain using a perlin noise algorithm, this now caches the result too so it only has to run on the initial time its loaded.
|
||||
class PerlinTerrainNode : public TerrainNode
|
||||
{
|
||||
public:
|
||||
@ -41,11 +43,20 @@ private:
|
||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||
};
|
||||
|
||||
// Vector to hold the actual randomised perlin noise permutation array
|
||||
vector<unsigned char> _p;
|
||||
|
||||
// The chunk size lets us control how large a noise spot should be, lower numbers will make things flatter, larger numbers will make things spikey
|
||||
float _chunkSize;
|
||||
|
||||
// Methods for Caching the output to a raw image format, and reading it back in the next time its loaded
|
||||
bool ReadCache();
|
||||
void CacheOutput();
|
||||
|
||||
// Methods for fading a number, used for the noise calculation
|
||||
float Fade(float t);
|
||||
|
||||
// Method for generating a perlin gradient.
|
||||
float Grad(int hash, float x, float y);
|
||||
|
||||
void GeneratePerlinValues();
|
||||
|
@ -71,6 +71,7 @@ SceneNodePointer SceneGraph::Find(wstring name)
|
||||
for (SceneNodePointer& currentSceneGraphPtr : _children)
|
||||
{
|
||||
foundValue = currentSceneGraphPtr->Find(name);
|
||||
// Keep going til we find a value, then break the loop all the way down
|
||||
if (foundValue != nullptr)
|
||||
{
|
||||
break;
|
||||
|
@ -17,10 +17,15 @@ public:
|
||||
void Add(SceneNodePointer node);
|
||||
void Remove(SceneNodePointer node);
|
||||
SceneNodePointer Find(wstring name);
|
||||
|
||||
// Placeholder methods for possibly going up the graph to find the Root and Parent nodes
|
||||
SceneNodePointer GetRootNode();
|
||||
SceneNodePointer GetParent();
|
||||
|
||||
// Returns a scenegraphs first child element, useful if you dont know the names but know the structure
|
||||
SceneNodePointer GetFirstChild();
|
||||
|
||||
// Method for returning a pointer to the child list, needed for the split mesh renderer mainly
|
||||
list<SceneNodePointer>& GetChildren();
|
||||
|
||||
protected:
|
||||
|
@ -6,13 +6,13 @@ XMMATRIX SharedMethods::RotateFromPoint(const float x, const float y, const floa
|
||||
return XMMatrixTranslation(x, y, z) * rotationMatrix * XMMatrixTranslation(-x, -y, -z);
|
||||
}
|
||||
|
||||
// Method so i can use normal RGB values and hopefully hex values too since im use to those
|
||||
// Function so i can use normal RGB values and hopefully hex values too since im use to those
|
||||
float SharedMethods::RGBValueToIntensity(const int value)
|
||||
{
|
||||
return (float)value / 255.0f;
|
||||
}
|
||||
|
||||
// Method to generate a random intensity rating between 2 floats
|
||||
// Function to generate a random intensity rating between 2 floats
|
||||
float SharedMethods::GenerateRandomIntensity(const float min, const float max)
|
||||
{
|
||||
float randNo = (float)rand() / (float)RAND_MAX;
|
||||
@ -22,7 +22,7 @@ float SharedMethods::GenerateRandomIntensity(const float min, const float max)
|
||||
return (float)roundedNo * 0.01f;
|
||||
}
|
||||
|
||||
// Method for genering random UV's, maybe get rid of this in future
|
||||
// Function for genering random UV's, maybe get rid of this in future
|
||||
float SharedMethods::GenerateRandomUV(const float min, const float max)
|
||||
{
|
||||
return SharedMethods::Clamp<float>((float)(rand() % (int)(max * 100.0f)) * 0.01f, (float)min, (float)max);
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "DirectXCore.h"
|
||||
#include <string>
|
||||
|
||||
// The generic CBuffer used in most of the nodes
|
||||
struct CBUFFER
|
||||
{
|
||||
XMMATRIX completeTransformation;
|
||||
@ -18,6 +19,7 @@ struct CBUFFER
|
||||
float padding[1];
|
||||
};
|
||||
|
||||
// Struct to hold the terrain populate methods variables
|
||||
struct TerrainPopNode
|
||||
{
|
||||
std::wstring nodeBaseName;
|
||||
@ -25,12 +27,16 @@ struct TerrainPopNode
|
||||
float scaleFactor;
|
||||
};
|
||||
|
||||
// The Shared methods namespace should be used for functions that are used in many places to stop duplicate code and keep things consolidated
|
||||
namespace SharedMethods
|
||||
{
|
||||
// Function for creating a rotation matrix that should be around a specific point
|
||||
XMMATRIX RotateFromPoint(const float x, const float y, const float z, const XMMATRIX rotationMatrix);
|
||||
|
||||
// Function for converting a 255 style RGB value to its normalised intensity value
|
||||
float RGBValueToIntensity(const int value);
|
||||
|
||||
// Template function for clamping a number between 2 values, the template allows this to be used for any numeric type
|
||||
template <typename T = float> T Clamp(const T value, const T min, const T max)
|
||||
{
|
||||
if (value < min)
|
||||
@ -47,12 +53,13 @@ namespace SharedMethods
|
||||
}
|
||||
}
|
||||
|
||||
// Methods for lerping values
|
||||
// Template function for lerping values
|
||||
template <typename T = float> float Lerp(const T a, const T b, const float p)
|
||||
{
|
||||
return (float)a + p * ((float)b - (float)a);
|
||||
}
|
||||
|
||||
// Template function for doing a cubic interpolate, i think this is unused currently but i did play around with the idea
|
||||
template <typename T = float> float CubicInterpolate(const T n0, const T n1, const T n2, const T n3, const float a)
|
||||
{
|
||||
float p = ((float)n3 - (float)n2) - ((float)n0 - (float)n1);
|
||||
@ -63,7 +70,10 @@ namespace SharedMethods
|
||||
return p * a * a * a + q * a * a + r * a + s;
|
||||
}
|
||||
|
||||
// Function for generating a random float value
|
||||
float GenerateRandomIntensity(const float min, const float max);
|
||||
|
||||
// Function for generating random UV values
|
||||
float GenerateRandomUV(const float min, const float max);
|
||||
};
|
||||
|
||||
|
@ -13,6 +13,7 @@ bool SplitMeshNode::Initialise()
|
||||
}
|
||||
else
|
||||
{
|
||||
// Loop the meshes sub meshes to generate the scene graph tree
|
||||
Add(AddMeshNode(_mesh->GetRootNode()));
|
||||
}
|
||||
|
||||
@ -40,6 +41,7 @@ void SplitMeshNode::Render(void)
|
||||
|
||||
void SplitMeshNode::Shutdown(void)
|
||||
{
|
||||
// Release the mesh from the resource manager
|
||||
_resourceManager->ReleaseMesh(_modelName);
|
||||
SceneGraph::Shutdown();
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "SubMeshRenderer.h"
|
||||
#include "SubMeshNode.h"
|
||||
|
||||
// Class for creating a scenegraph centric split mesh node, that allows for transformation of any of the sub meshes
|
||||
class SplitMeshNode : public SceneGraph
|
||||
{
|
||||
public:
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "DirectXFramework.h"
|
||||
#include "SceneNode.h"
|
||||
|
||||
// Class for holding the details of the sub meshes of a split node class
|
||||
class SubMeshNode : public SceneNode
|
||||
{
|
||||
public:
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
void SubMeshRenderer::SetRootChildren(list<SceneNodePointer>& rootChildren)
|
||||
{
|
||||
// Sets the root children since the actual sub mesh root could have multiple children...
|
||||
_rootGraph = &rootChildren;
|
||||
}
|
||||
|
||||
@ -35,8 +36,10 @@ bool SubMeshRenderer::Initialise()
|
||||
|
||||
void SubMeshRenderer::RenderChild(SceneNodePointer node, bool renderTransparent)
|
||||
{
|
||||
// Check if the current node is a sub mesh
|
||||
shared_ptr<SubMeshNode> currentMeshNode = dynamic_pointer_cast<SubMeshNode>(node);
|
||||
|
||||
// If the current node is a submesh, render it
|
||||
if (currentMeshNode != nullptr)
|
||||
{
|
||||
XMMATRIX projectionTransformation = DirectXFramework::GetDXFramework()->GetProjectionTransformation();
|
||||
@ -82,6 +85,7 @@ void SubMeshRenderer::RenderChild(SceneNodePointer node, bool renderTransparent)
|
||||
}
|
||||
}
|
||||
|
||||
// If the current node is not a sub mesh its most likely a scene graph, if so loop the children of it
|
||||
shared_ptr<SceneGraph> currentGraphNode = dynamic_pointer_cast<SceneGraph>(node);
|
||||
|
||||
if (currentGraphNode != nullptr)
|
||||
@ -118,6 +122,9 @@ void SubMeshRenderer::Render()
|
||||
float blendFactors[] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
_deviceContext->OMSetBlendState(_transparentBlendState.Get(), blendFactors, 0xffffffff);
|
||||
|
||||
// IT REALLY NEEDS TO DO BOTH TO RENDER SOMEWHAT CORRECTLY
|
||||
// This should solve that now
|
||||
|
||||
// We do two passes through the nodes. The first time we render nodes
|
||||
// that are not transparent (i.e. their opacity == 1.0f).
|
||||
for (SceneNodePointer& child : *_rootGraph)
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Renderer.h"
|
||||
#include "Mesh.h"
|
||||
|
||||
// Class for rendering a sub mesh since its different to a normal mesh render
|
||||
class SubMeshRenderer : public Renderer
|
||||
{
|
||||
public:
|
||||
|
2
Graphics2/TerrainCache/cache_ApplePie_10_000000.raw
Normal file
2
Graphics2/TerrainCache/cache_ApplePie_10_000000.raw
Normal file
File diff suppressed because one or more lines are too long
5
Graphics2/TerrainCache/cache_ApplePie_15_000000.raw
Normal file
5
Graphics2/TerrainCache/cache_ApplePie_15_000000.raw
Normal file
File diff suppressed because one or more lines are too long
1
Graphics2/TerrainCache/cache_ApplePie_5_000000.raw
Normal file
1
Graphics2/TerrainCache/cache_ApplePie_5_000000.raw
Normal file
File diff suppressed because one or more lines are too long
1
Graphics2/TerrainCache/cache_CheeseCake_10_000000.raw
Normal file
1
Graphics2/TerrainCache/cache_CheeseCake_10_000000.raw
Normal file
File diff suppressed because one or more lines are too long
16
Graphics2/TerrainCache/cache_LovelyCat_10_000000.raw
Normal file
16
Graphics2/TerrainCache/cache_LovelyCat_10_000000.raw
Normal file
File diff suppressed because one or more lines are too long
63
Graphics2/TerrainCache/cache_SkyWorld_10_000000.raw
Normal file
63
Graphics2/TerrainCache/cache_SkyWorld_10_000000.raw
Normal file
File diff suppressed because one or more lines are too long
48
Graphics2/TerrainCache/cache_SkyWorld_7_000000.raw
Normal file
48
Graphics2/TerrainCache/cache_SkyWorld_7_000000.raw
Normal file
File diff suppressed because one or more lines are too long
37
Graphics2/TerrainCache/cache_SkyWorld_8_000000.raw
Normal file
37
Graphics2/TerrainCache/cache_SkyWorld_8_000000.raw
Normal file
File diff suppressed because one or more lines are too long
18
Graphics2/TerrainCache/cache_WaterWorld_10_000000.raw
Normal file
18
Graphics2/TerrainCache/cache_WaterWorld_10_000000.raw
Normal file
File diff suppressed because one or more lines are too long
@ -4,9 +4,11 @@
|
||||
|
||||
TerrainNode::TerrainNode(wstring name, wstring seed, float waterHeight, int widthX, int widthZ, int cellSizeX, int cellSizeZ) : SceneNode(name)
|
||||
{
|
||||
// Get the seed string and hash it
|
||||
_seedString = seed;
|
||||
_seedHash = std::hash<wstring>{}(_seedString);
|
||||
_seedHash = (unsigned int)std::hash<wstring>{}(_seedString);
|
||||
|
||||
// Set the random seed number
|
||||
srand(_seedHash);
|
||||
|
||||
_widthX = widthX;
|
||||
@ -83,9 +85,9 @@ void TerrainNode::GenerateTerrainData()
|
||||
float bv = 1.0f / (float)(_gridRows - 1u);
|
||||
|
||||
int currentVertexCount = 0;
|
||||
for (int z = 0; z < _gridRows; z++)
|
||||
for (int z = 0; z < (int)_gridRows; z++)
|
||||
{
|
||||
for (int x = 0; x < _gridCols; x++)
|
||||
for (int x = 0; x < (int)_gridCols; x++)
|
||||
{
|
||||
int currentIndex = (z * _gridCols) + x;
|
||||
int offsetIndexX = 1;
|
||||
@ -165,9 +167,9 @@ void TerrainNode::GenerateTerrainData()
|
||||
void TerrainNode::GenerateTerrainNormals()
|
||||
{
|
||||
int currentNormalIndex = 0;
|
||||
for (int z = 0; z < _gridRows; z++)
|
||||
for (int z = 0; z < (int)_gridRows; z++)
|
||||
{
|
||||
for (int x = 0; x < _gridCols; x++)
|
||||
for (int x = 0; x < (int)_gridCols; x++)
|
||||
{
|
||||
XMVECTOR v1 = XMVectorSet(_terrainVerts[currentNormalIndex + 1].Position.x - _terrainVerts[currentNormalIndex].Position.x,
|
||||
_terrainVerts[currentNormalIndex + 1].Position.y - _terrainVerts[currentNormalIndex].Position.y,
|
||||
@ -185,6 +187,7 @@ void TerrainNode::GenerateTerrainNormals()
|
||||
XMStoreFloat3(&_terrainVerts[currentNormalIndex + 2].Normal, XMVectorAdd(XMLoadFloat3(&_terrainVerts[currentNormalIndex + 2].Normal), polygonNormal));
|
||||
XMStoreFloat3(&_terrainVerts[currentNormalIndex + 3].Normal, XMVectorAdd(XMLoadFloat3(&_terrainVerts[currentNormalIndex + 3].Normal), polygonNormal));
|
||||
|
||||
// Process all the connected normals
|
||||
AddNormalToVertex(z - 1, x - 1, 3, polygonNormal);
|
||||
AddNormalToVertex(z - 1, x, 2, polygonNormal);
|
||||
AddNormalToVertex(z - 1, x, 3, polygonNormal);
|
||||
@ -218,6 +221,7 @@ void TerrainNode::AddNormalToVertex(int row, int col, int vertexIndex, XMVECTOR
|
||||
|
||||
int finalIndex = vertexIndex + ((rowIndexOffset + colIndexOffset) * 4);
|
||||
|
||||
// Check if the index is not around the edges
|
||||
if (row >= 0 && row < (int)_gridRows && col >= 0 && col < (int)_gridCols)
|
||||
{
|
||||
XMStoreFloat3(&_terrainVerts[finalIndex].Normal, XMVectorAdd(XMLoadFloat3(&_terrainVerts[finalIndex].Normal), normal));
|
||||
@ -498,13 +502,15 @@ void TerrainNode::LoadTerrainTextures()
|
||||
));
|
||||
}
|
||||
|
||||
// Method for populating the terrain with random clutter described in the structs
|
||||
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)
|
||||
// Step through the z and x with the given spacing
|
||||
for (int z = 0; z < (int)_gridRows; z += zStep)
|
||||
{
|
||||
for (int x = 0; x < _gridCols; x += xStep)
|
||||
for (int x = 0; x < (int)_gridCols; x += xStep)
|
||||
{
|
||||
int currentIndex = ((z * _gridCols) + x) * 4;
|
||||
int currentIndex = ((z * (int)_gridCols) + x) * 4;
|
||||
|
||||
float totalHeights = 0.0f;
|
||||
float totalSlope = 0.0f;
|
||||
@ -516,12 +522,15 @@ void TerrainNode::PopulateTerrain(SceneGraphPointer currentSceneGraph, vector<Te
|
||||
|
||||
float heightValue = totalHeights / 4.0f;
|
||||
|
||||
// If the height value is within the given range
|
||||
if (heightValue >= heightLower && heightValue <= heightUpper)
|
||||
{
|
||||
float avgSlope = totalSlope / 4.0f;
|
||||
|
||||
// If the slope value is within the given range
|
||||
if (avgSlope >= slopeLower && avgSlope <= slopeUpper)
|
||||
{
|
||||
// The Height level and slope values are in range, create a mesh node with the provided details
|
||||
int nodeIndex = 0;
|
||||
if (nodesForPop.size() > 1)
|
||||
{
|
||||
@ -529,12 +538,13 @@ void TerrainNode::PopulateTerrain(SceneGraphPointer currentSceneGraph, vector<Te
|
||||
}
|
||||
|
||||
float scale = (rand() % 100 * 0.01f) * nodesForPop[nodeIndex].scaleFactor;
|
||||
float yRotation = rand() % 360;
|
||||
float yRotation = (float)(rand() % 360);
|
||||
|
||||
float xPos = x * _cellSizeX + _terrainStartX;
|
||||
float yPos = heightValue;
|
||||
float zPos = (-z + 1) * _cellSizeZ + _terrainStartZ;
|
||||
|
||||
// Made a unique name to stop conflicts
|
||||
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(XMMatrixRotationAxis(XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), yRotation * XM_PI / 180.0f) * XMMatrixScaling(scale, scale, scale) * XMMatrixTranslation(xPos, yPos, zPos));
|
||||
@ -547,11 +557,13 @@ void TerrainNode::PopulateTerrain(SceneGraphPointer currentSceneGraph, vector<Te
|
||||
|
||||
void TerrainNode::GenerateBlendMap()
|
||||
{
|
||||
// Set up the color gradients for each type of texture
|
||||
RGBA snowTops = RGBA{ 0u, 0u, 0u, 255u };
|
||||
RGBA grassLand = RGBA{ 0u, 0u, 0u, 0u };
|
||||
RGBA lightDirt = RGBA{ 50u, 0u, 255u, 0u };
|
||||
RGBA darkDirt = RGBA{ 255u, 50u, 50u, 0u };
|
||||
|
||||
// Create a vector of these gradients, multiple grass because i want it to cover more area, i need to come up with a better way if i have time
|
||||
vector<RGBA> colorSteps = {darkDirt, lightDirt, grassLand, grassLand, grassLand, snowTops};
|
||||
|
||||
float waterOffset = _waterHeight - 50.0f;
|
||||
@ -560,6 +572,7 @@ void TerrainNode::GenerateBlendMap()
|
||||
waterOffset = 0.0f;
|
||||
}
|
||||
|
||||
// Set up the gradient
|
||||
ColorGradient terrainBlendGradient = ColorGradient(waterOffset, 820.0f, colorSteps);
|
||||
|
||||
// Note that _numberOfRows and _numberOfColumns need to be setup
|
||||
@ -593,6 +606,7 @@ void TerrainNode::GenerateBlendMap()
|
||||
float totalHeights = 0.0f;
|
||||
float totalSlope = 0.0f;
|
||||
|
||||
// Calculate the average height and slope values
|
||||
for (int si = 0; si < 4; si++)
|
||||
{
|
||||
totalHeights += _terrainVerts[currentIndex + si].Position.y;
|
||||
@ -602,6 +616,7 @@ void TerrainNode::GenerateBlendMap()
|
||||
float avgHeight = totalHeights / 4.0f;
|
||||
float avgSlope = totalSlope / 4.0f;
|
||||
|
||||
// Get the RGB value from the gradient at the given height value
|
||||
RGBA currentBlend = terrainBlendGradient.GetRGBAValue(avgHeight);
|
||||
r = (BYTE)currentBlend.red;
|
||||
g = (BYTE)currentBlend.green;
|
||||
@ -612,7 +627,7 @@ void TerrainNode::GenerateBlendMap()
|
||||
{
|
||||
if (avgSlope < 0.6f)
|
||||
{
|
||||
g = (BYTE)SharedMethods::Clamp<int>(avgHeight - 450.0f, 0, 150);
|
||||
g = (BYTE)SharedMethods::Clamp<int>((int)(avgHeight - 450.0f), 0, 150);
|
||||
}
|
||||
}
|
||||
|
||||
@ -667,6 +682,7 @@ void TerrainNode::BuildExtraMaps()
|
||||
));
|
||||
}
|
||||
|
||||
// Method for getting the height value at a given point, can also check if we are colliding with water or not
|
||||
float TerrainNode::GetHeightAtPoint(float x, float z, bool waterCollide)
|
||||
{
|
||||
int cellX = (int)((x - _terrainStartX) / _cellSizeX);
|
||||
@ -695,6 +711,7 @@ float TerrainNode::GetHeightAtPoint(float x, float z, bool waterCollide)
|
||||
XMFLOAT4 normal;
|
||||
XMStoreFloat4(&normal, XMVector3Normalize(XMVector3Cross(v1, v2)));
|
||||
|
||||
// Get the normal value for the vert we landed on
|
||||
float result = _terrainVerts[currentIndex].Position.y + (normal.x * dx + normal.z * dz) / -normal.y;
|
||||
if (waterCollide)
|
||||
{
|
||||
@ -707,6 +724,7 @@ float TerrainNode::GetHeightAtPoint(float x, float z, bool waterCollide)
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method for checking if we are within the X Boundary
|
||||
bool TerrainNode::CheckXBoundary(float x)
|
||||
{
|
||||
if (!(x > _terrainStartX && x < _terrainEndX))
|
||||
@ -717,6 +735,7 @@ bool TerrainNode::CheckXBoundary(float x)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method for checking if we are withing the Z Boundary
|
||||
bool TerrainNode::CheckZBoundary(float z)
|
||||
{
|
||||
if (!(z < _terrainStartZ && z > _terrainEndZ))
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "MeshNode.h"
|
||||
#include "SplitMeshNode.h"
|
||||
|
||||
// Struct for holding the terrain vertex data
|
||||
typedef struct TerrainVertex
|
||||
{
|
||||
XMFLOAT3 Position;
|
||||
@ -20,6 +21,8 @@ typedef struct TerrainVertex
|
||||
XMFLOAT2 BlendMapTexCoord;
|
||||
} TerrainVertex;
|
||||
|
||||
// Custom CBuffer for the terrain that can hold the water values, THE BYTE TOTAL FOR THIS MUST HAVE A FACTOR OF 16!!
|
||||
// a float is 4 bytes, so we techically dont need to pad with the array at the bottom but i left it in to remind myself
|
||||
struct TCBUFFER
|
||||
{
|
||||
XMMATRIX completeTransformation;
|
||||
@ -38,6 +41,7 @@ struct TCBUFFER
|
||||
float padding[4];
|
||||
};
|
||||
|
||||
// Class for processing the data of a terrain, one of the child classes should be passing the values to here to process as this class does not generate any actual values.
|
||||
class TerrainNode : public SceneNode
|
||||
{
|
||||
public:
|
||||
@ -51,13 +55,16 @@ public:
|
||||
void Render(void);
|
||||
void virtual Shutdown(void);
|
||||
|
||||
// Method for randomly placing models onto the terrain with the given commands
|
||||
void PopulateTerrain(SceneGraphPointer currentSceneGraph, vector<TerrainPopNode>& nodesForPop, int xStep, int zStep, float heightLower, float heightUpper, float slopeLower, float slopeUpper);
|
||||
|
||||
// Methods for checking if you are in the bounds
|
||||
float GetHeightAtPoint(float x, float z, bool waterCollide = false);
|
||||
bool CheckXBoundary(float x);
|
||||
bool CheckZBoundary(float z);
|
||||
|
||||
protected:
|
||||
// Bool so we can set this if the children have an error processing the data and we wont run
|
||||
bool _initError = false;
|
||||
|
||||
int _widthX;
|
||||
@ -80,6 +87,7 @@ protected:
|
||||
float _terrainEndX;
|
||||
float _terrainEndZ;
|
||||
|
||||
// Vars for dealing with the random number seed
|
||||
wstring _seedString;
|
||||
unsigned int _seedHash;
|
||||
|
||||
@ -111,7 +119,9 @@ protected:
|
||||
ComPtr<ID3D11RasterizerState> _defaultRasteriserState;
|
||||
ComPtr<ID3D11RasterizerState> _wireframeRasteriserState;
|
||||
|
||||
// Pointer for the snow texture since i could not load it with the other textures, i think its because of mixed formats
|
||||
ComPtr<ID3D11ShaderResourceView> _snowTest;
|
||||
|
||||
ComPtr<ID3D11ShaderResourceView> _waterNormalMap;
|
||||
XMFLOAT4 _waterColor;
|
||||
|
||||
@ -126,9 +136,13 @@ protected:
|
||||
void LoadTerrainTextures();
|
||||
void GenerateBlendMap();
|
||||
|
||||
// Method to get a height value at the given index
|
||||
float GetHeightValueAt(int index);
|
||||
|
||||
// Method for adding the normals to each vertex, easier to manage this way
|
||||
void AddNormalToVertex(int row, int col, int vertexIndex, XMVECTOR normal);
|
||||
|
||||
// Method for loading the extra image maps we had, but now just for the water normals
|
||||
void BuildExtraMaps();
|
||||
};
|
||||
|
||||
|
@ -22,6 +22,8 @@ cbuffer ConstantBuffer
|
||||
|
||||
Texture2D BlendMap : register(t0);
|
||||
Texture2DArray TexturesArray : register(t1);
|
||||
|
||||
// Textures for the water normals and the snow
|
||||
Texture2D WaterNormalMap : register(t2);
|
||||
Texture2D snowTest : register(t3);
|
||||
|
||||
@ -51,6 +53,7 @@ struct PixelShaderInput
|
||||
float2 BlendMapTexCoord : TEXCOORD1;
|
||||
};
|
||||
|
||||
// Function for rotating a UV around a point
|
||||
float2 UVRotate(float2 uvCoord, float2 pivotPoint, float rotation)
|
||||
{
|
||||
float2x2 rotateMatrix = float2x2(float2(sin(rotation), -cos(rotation)), float2(cos(rotation), sin(rotation)));
|
||||
@ -63,31 +66,35 @@ float2 UVRotate(float2 uvCoord, float2 pivotPoint, float rotation)
|
||||
|
||||
}
|
||||
|
||||
// Typical HLSL hasing function for psuedo random number generation
|
||||
float4 hash4(float2 v)
|
||||
{
|
||||
float4 p = mul(float4x2(127.1f, 311.7f, 269.5f, 183.3f, 113.5f, 271.9f, 246.1f, 124.6f), v);
|
||||
return frac(sin(p) * 43758.5453123);
|
||||
}
|
||||
|
||||
PixelShaderInput VShader(VertexShaderInput vin)
|
||||
{
|
||||
PixelShaderInput output;
|
||||
float3 position = vin.Position;
|
||||
output.PositionOG = output.PositionWS = mul(worldTransformation, float4(position, 1.0f));
|
||||
output.TexCoord = vin.TexCoord;
|
||||
// Check if the y pos is lower than the water point, and set the position to it minus a tiny offset because the textures were clipping and causing a weird effect
|
||||
// Check if the y pos is lower than the water point
|
||||
if (position.y < waterHeight)
|
||||
{
|
||||
position.y = waterHeight;
|
||||
// Change up the flat water height with some psuedo RNG height generation
|
||||
float4 yOffset = hash4(float2(-position.y, position.y));
|
||||
// the offset should return as a value between 0 and 1, i change this to be a value between -1 and 1, then add some height modifier so you can actually see the difference
|
||||
position.y = waterHeight + (((yOffset.y * 2.0f) - 1.0f) * 3.0f);
|
||||
}
|
||||
output.Position = mul(completeTransformation, float4(position, 1.0f));
|
||||
output.PositionWS = mul(worldTransformation, float4(position, 1.0f));
|
||||
//output.PositionWS = mul(worldTransformation, float4(position, 1.0f));
|
||||
output.NormalWS = float4(mul((float3x3)worldTransformation, vin.Normal), 1.0f);
|
||||
|
||||
output.BlendMapTexCoord = vin.BlendMapTexCoord;
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 hash4(float2 v)
|
||||
{
|
||||
float4 p = mul(float4x2(127.1, 311.7, 269.5, 183.3, 113.5, 271.9, 246.1, 124.6), v);
|
||||
return frac(sin(p) * 43758.5453123);
|
||||
}
|
||||
|
||||
float4 PShader(PixelShaderInput input) : SV_TARGET
|
||||
{
|
||||
float4 directionToCamera = normalize(input.PositionWS - cameraPosition);
|
||||
@ -95,14 +102,13 @@ float4 PShader(PixelShaderInput input) : SV_TARGET
|
||||
float surfaceShininess = shininess;
|
||||
float4 adjustedNormal = normalize(input.NormalWS);
|
||||
|
||||
float currentHeightPer = (waterHeight - input.PositionOG.y) / waterHeight;
|
||||
|
||||
if (input.PositionOG.y < waterHeight)
|
||||
{
|
||||
// Sample the normal from the water normal map and calculate it with the worldtransform
|
||||
float3 n0 = 2.0f * (float3)WaterNormalMap.Sample(ss, input.TexCoord) - 1.0f;
|
||||
float4 n2 = float4(mul((float3x3)worldTransformation, n0), 1.0f);
|
||||
adjustedNormal = normalize(n2);
|
||||
float4 n0 = WaterNormalMap.Sample(ss, input.TexCoord);
|
||||
float4 n1 = float4((n0.xyz * 2.0f) - 1.0f, 1.0f);
|
||||
//float4 n2 = float4(mul((float3x3)worldTransformation, n0), 1.0f);
|
||||
adjustedNormal = n1; // normalize(n2);
|
||||
surfaceShininess = waterShininess;
|
||||
}
|
||||
|
||||
@ -123,8 +129,6 @@ float4 PShader(PixelShaderInput input) : SV_TARGET
|
||||
|
||||
// Randomly rotate the UV coordinates using the noise map
|
||||
float2 scaleCenter = float2(0.5f, 0.5f);
|
||||
// The noise map is greyscale so we only need to check 1 channel since they should all be the same value
|
||||
|
||||
float2 scaledUV = frac(UVRotate(input.TexCoord, scaleCenter, input.BlendMapTexCoord.x)); // (input.TexCoord - scaleCenter) * currentScale + scaleCenter;
|
||||
|
||||
float2 xChange = ddx(scaledUV);
|
||||
@ -145,6 +149,7 @@ float4 PShader(PixelShaderInput input) : SV_TARGET
|
||||
float4 c2 = TexturesArray.SampleGrad(ss, float3(scaledUV, 2.0f), xChange, yChange);
|
||||
float4 c3 = TexturesArray.SampleGrad(ss, float3(scaledUV, 3.0f), xChange, yChange);
|
||||
//float4 c4 = TexturesArray.SampleGrad(ss, float3(scaledUV, 4.0f), xChange, yChange);
|
||||
// Using the single snow texture because of the snow dds loading glitch
|
||||
float4 c4 = snowTest.SampleGrad(ss, float2(scaledUV), xChange, yChange);
|
||||
|
||||
// Sample the blend map.
|
||||
@ -157,7 +162,7 @@ float4 PShader(PixelShaderInput input) : SV_TARGET
|
||||
color = lerp(color, c3, t.b);
|
||||
color = lerp(color, c4, t.a);
|
||||
|
||||
// Combine all components
|
||||
// Combine all components, and check if this is water
|
||||
if (input.PositionOG.y < waterHeight)
|
||||
{
|
||||
// Tint the water
|
||||
|
@ -64,6 +64,7 @@ float4 PShader(PixelShaderInput input) : SV_TARGET
|
||||
// Combine all components
|
||||
float4 color;
|
||||
// Check if the texture is valid before trying to sample it, some meshes use materials to color them and dont export an image texture file even though the color value is stored with the vertex
|
||||
// This might be totally my end because ive seen others be able to load the plane with all the correct textures???
|
||||
if (validTexture == 1)
|
||||
{
|
||||
color = saturate((ambientLight + diffuse + specular) * Texture.Sample(ss, input.TexCoord));
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user