#include "TerrainNode.h" #define WORLD_HEIGHT 512 TerrainNode::TerrainNode(wstring name, wstring heightMap, int widthX, int widthZ, int cellSizeX, int cellSizeZ) : SceneNode(name) { _heightMap = heightMap; _widthX = widthX; _widthZ = widthZ; _gridCols = _widthX + 1; _gridRows = _widthZ + 1; _cellSizeX = cellSizeX; _cellSizeZ = cellSizeZ; _polygonsCount = (_gridCols * _gridRows) * 2; _vertexCount = _polygonsCount * 2; _indiciesCount = _polygonsCount * 3; } bool TerrainNode::Initialise() { _device = DirectXFramework::GetDXFramework()->GetDevice(); _deviceContext = DirectXFramework::GetDXFramework()->GetDeviceContext(); if (_heightMap.length() > 0) { if (LoadHeightMap(_heightMap)) { _usedHeightMap = true; } else { _usedHeightMap = false; } } else { _usedHeightMap = false; } GenerateTerrainData(); GenerateBuffers(); BuildShaders(); BuildVertexLayout(); BuildConstantBuffer(); BuildBlendState(); BuildRendererStates(); return true; } void TerrainNode::GenerateTerrainData() { float totalWidth = (float)(_gridCols * _cellSizeX); float totalDepth = (float)(_gridRows * _cellSizeZ); float centerWidth = totalWidth * -0.5f; float centerDepth = totalDepth * 0.5f; int currentVertexCount = 0; for (int z = 0; z < _gridRows; z++) { for (int x = 0; x < _gridCols; x++) { int currentIndex = (z * _gridCols) + x; int offsetIndexX = 1; if (x == _widthX) { offsetIndexX = 0; } int offsetIndexZ = _gridCols; if (z == _widthZ) { offsetIndexZ = 0; } Vector3D polygonNormal = Vector3D(0.0f, 0.0f, 0.0f); VERTEX vertex; vertex.Position = XMFLOAT3(x * _cellSizeX + centerWidth, GetHeightMapValueAt(currentIndex), (-z + 1) * _cellSizeZ + centerDepth); vertex.Normal = XMFLOAT3(0.0f, 0.0f, 0.0f); vertex.TexCoord = XMFLOAT2(0.0f, 1.0f); _terrainVerts.push_back(vertex); _terrainNormals.push_back(polygonNormal); vertex = VERTEX(); vertex.Position = XMFLOAT3((x + 1) * _cellSizeX + centerWidth, GetHeightMapValueAt(currentIndex + offsetIndexX), (-z + 1) * _cellSizeZ + centerDepth); vertex.Normal = XMFLOAT3(0.0f, 0.0f, 0.0f); vertex.TexCoord = XMFLOAT2(1.0f, 1.0f); _terrainVerts.push_back(vertex); _terrainNormals.push_back(polygonNormal); vertex = VERTEX(); vertex.Position = XMFLOAT3(x * _cellSizeX + centerWidth, GetHeightMapValueAt(currentIndex + offsetIndexZ), -z * _cellSizeZ + centerDepth); vertex.Normal = XMFLOAT3(0.0f, 0.0f, 0.0f); vertex.TexCoord = XMFLOAT2(0.0f, 0.0f); _terrainVerts.push_back(vertex); _terrainNormals.push_back(polygonNormal); vertex = VERTEX(); vertex.Position = XMFLOAT3((x + 1) * _cellSizeX + centerWidth, GetHeightMapValueAt(currentIndex + offsetIndexZ + offsetIndexX), -z * _cellSizeZ + centerDepth); vertex.Normal = XMFLOAT3(0.0f, 0.0f, 0.0f); vertex.TexCoord = XMFLOAT2(1.0f, 0.0f); _terrainVerts.push_back(vertex); _terrainNormals.push_back(polygonNormal); _indices.push_back(currentVertexCount); _indices.push_back(currentVertexCount + 1); _indices.push_back(currentVertexCount + 2); _indices.push_back(currentVertexCount + 2); _indices.push_back(currentVertexCount + 1); _indices.push_back(currentVertexCount + 3); currentVertexCount += 4; } } int currentNormalIndex = 0; for (int z = 0; z < _gridRows; z++) { for (int x = 0; x < _gridCols; x++) { Vector3D vectorA = Vector3D(_terrainVerts[currentNormalIndex + 1].Position, _terrainVerts[currentNormalIndex].Position); Vector3D vectorB = Vector3D(_terrainVerts[currentNormalIndex + 2].Position, _terrainVerts[currentNormalIndex].Position); Vector3D polygonNormal = Vector3D::CrossProduct(vectorA, vectorB); _terrainNormals[currentNormalIndex] += polygonNormal; _terrainNormals[currentNormalIndex + 1] += polygonNormal; _terrainNormals[currentNormalIndex + 2] += polygonNormal; _terrainNormals[currentNormalIndex + 3] += polygonNormal; AddNormalToVertex(z - 1, x - 1, 3, polygonNormal); AddNormalToVertex(z - 1, x, 2, polygonNormal); AddNormalToVertex(z - 1, x, 3, polygonNormal); AddNormalToVertex(z - 1, x + 1, 2, polygonNormal); AddNormalToVertex(z, x - 1, 1, polygonNormal); AddNormalToVertex(z, x - 1, 3, polygonNormal); AddNormalToVertex(z, x + 1, 0, polygonNormal); AddNormalToVertex(z, x + 1, 2, polygonNormal); AddNormalToVertex(z + 1, x - 1, 1, polygonNormal); AddNormalToVertex(z + 1, x, 0, polygonNormal); AddNormalToVertex(z + 1, x, 1, polygonNormal); AddNormalToVertex(z + 1, x + 1, 0, polygonNormal); currentNormalIndex += 4; } } for (int i = 0; i < _terrainNormals.size(); i++) { _terrainNormals[i].Normalize(); _terrainVerts[i].Normal = _terrainNormals[i].Get(); } _terrainNormals.clear(); } void TerrainNode::AddNormalToVertex(int row, int col, int vertexIndex, Vector3D normal) { int rowIndexOffset = row * _gridCols; int colIndexOffset = col; int finalIndex = vertexIndex + ((rowIndexOffset + colIndexOffset) * 4); if (row >= 0 && row < _gridRows && col >= 0 && col < _gridCols) { _terrainNormals[finalIndex] += normal; } if (row == _gridRows) { int test = 1; } } void TerrainNode::GenerateBuffers() { // Setup the structure that specifies how big the vertex // buffer should be D3D11_BUFFER_DESC vertexBufferDescriptor; vertexBufferDescriptor.Usage = D3D11_USAGE_IMMUTABLE; vertexBufferDescriptor.ByteWidth = sizeof(VERTEX) * _vertexCount; vertexBufferDescriptor.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDescriptor.CPUAccessFlags = 0; vertexBufferDescriptor.MiscFlags = 0; vertexBufferDescriptor.StructureByteStride = 0; // Now set up a structure that tells DirectX where to get the // data for the vertices from D3D11_SUBRESOURCE_DATA vertexInitialisationData; vertexInitialisationData.pSysMem = &_terrainVerts[0]; // and create the vertex buffer ThrowIfFailed(_device->CreateBuffer(&vertexBufferDescriptor, &vertexInitialisationData, _vertexBuffer.GetAddressOf())); // Setup the structure that specifies how big the index // buffer should be D3D11_BUFFER_DESC indexBufferDescriptor; indexBufferDescriptor.Usage = D3D11_USAGE_IMMUTABLE; indexBufferDescriptor.ByteWidth = sizeof(UINT) * _indiciesCount; indexBufferDescriptor.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDescriptor.CPUAccessFlags = 0; indexBufferDescriptor.MiscFlags = 0; indexBufferDescriptor.StructureByteStride = 0; // Now set up a structure that tells DirectX where to get the // data for the indices from D3D11_SUBRESOURCE_DATA indexInitialisationData; indexInitialisationData.pSysMem = &_indices[0]; // and create the index buffer ThrowIfFailed(_device->CreateBuffer(&indexBufferDescriptor, &indexInitialisationData, _indexBuffer.GetAddressOf())); } void TerrainNode::Render() { XMMATRIX projectionTransformation = DirectXFramework::GetDXFramework()->GetProjectionTransformation(); XMMATRIX viewTransformation = DirectXFramework::GetDXFramework()->GetCamera()->GetViewMatrix(); XMMATRIX completeTransformation = XMLoadFloat4x4(&_worldTransformation) * viewTransformation * projectionTransformation; // Draw the first cube CBUFFER cBuffer; cBuffer.completeTransformation = completeTransformation; cBuffer.worldTransformation = XMLoadFloat4x4(&_worldTransformation); cBuffer.ambientColor = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f); //cBuffer.AmbientColor = XMFLOAT4(SharedMethods::RGBValueToIntensity(0xfd), 0.0f, 1.0f, 1.0f); //XMFLOAT4(1, 1, 1, 1); //_ambientLight; cBuffer.lightVector = XMVector4Normalize(XMLoadFloat4(&_directionalLightVector)); cBuffer.lightColor = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f); //cBuffer.LightColor = XMFLOAT4(SharedMethods::RGBValueToIntensity(0x89), 0.0f, 5.0f, 5.0f); //cBuffer.LightColor = _directionalLightColor; cBuffer.cameraPosition = DirectXFramework::GetDXFramework()->GetCamera()->GetRawCameraPosition(); _deviceContext->VSSetShader(_vertexShader.Get(), 0, 0); _deviceContext->PSSetShader(_pixelShader.Get(), 0, 0); _deviceContext->IASetInputLayout(_layout.Get()); // Update the constant buffer _deviceContext->VSSetConstantBuffers(0, 1, _constantBuffer.GetAddressOf()); _deviceContext->UpdateSubresource(_constantBuffer.Get(), 0, 0, &cBuffer, 0, 0); // Set the texture to be used by the pixel shader _deviceContext->PSSetShaderResources(0, 1, _texture.GetAddressOf()); UINT stride = sizeof(VERTEX); UINT offset = 0; _deviceContext->IASetVertexBuffers(0, 1, _vertexBuffer.GetAddressOf(), &stride, &offset); _deviceContext->IASetIndexBuffer(_indexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0); cBuffer.diffuseCoefficient = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); cBuffer.specularCoefficient = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); cBuffer.shininess = 1.0f; cBuffer.opacity = 1.0f; // Update the constant buffer _deviceContext->VSSetConstantBuffers(0, 1, _constantBuffer.GetAddressOf()); _deviceContext->UpdateSubresource(_constantBuffer.Get(), 0, 0, &cBuffer, 0, 0); _deviceContext->PSSetShaderResources(0, 1, _texture.GetAddressOf()); _deviceContext->PSSetConstantBuffers(0, 1, _constantBuffer.GetAddressOf()); _deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); //_deviceContext->RSSetState(_wireframeRasteriserState.Get()); _deviceContext->DrawIndexed(_indiciesCount, 0, 0); // Turn back face culling back on in case another renderer // relies on it _deviceContext->RSSetState(_defaultRasteriserState.Get()); } void TerrainNode::Shutdown(void) { } void TerrainNode::BuildShaders() { DWORD shaderCompileFlags = 0; #if defined( _DEBUG ) shaderCompileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; #endif ComPtr compilationMessages = nullptr; //Compile vertex shader HRESULT hr = D3DCompileFromFile(L"TerrainShadersNoBlend.hlsl", nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, "VShader", "vs_5_0", shaderCompileFlags, 0, _vertexShaderByteCode.GetAddressOf(), compilationMessages.GetAddressOf()); if (compilationMessages.Get() != nullptr) { // If there were any compilation messages, display them MessageBoxA(0, (char*)compilationMessages->GetBufferPointer(), 0, 0); } // Even if there are no compiler messages, check to make sure there were no other errors. ThrowIfFailed(hr); ThrowIfFailed(_device->CreateVertexShader(_vertexShaderByteCode->GetBufferPointer(), _vertexShaderByteCode->GetBufferSize(), NULL, _vertexShader.GetAddressOf())); // Compile pixel shader hr = D3DCompileFromFile(L"TerrainShadersNoBlend.hlsl", nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, "PShader", "ps_5_0", shaderCompileFlags, 0, _pixelShaderByteCode.GetAddressOf(), compilationMessages.GetAddressOf()); if (compilationMessages.Get() != nullptr) { // If there were any compilation messages, display them MessageBoxA(0, (char*)compilationMessages->GetBufferPointer(), 0, 0); } ThrowIfFailed(hr); ThrowIfFailed(_device->CreatePixelShader(_pixelShaderByteCode->GetBufferPointer(), _pixelShaderByteCode->GetBufferSize(), NULL, _pixelShader.GetAddressOf())); } void TerrainNode::BuildVertexLayout() { // Create the vertex input layout. This tells DirectX the format // of each of the vertices we are sending to it. D3D11_INPUT_ELEMENT_DESC vertexDesc[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT , D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT , D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT , D3D11_INPUT_PER_VERTEX_DATA, 0 } }; ThrowIfFailed(_device->CreateInputLayout(vertexDesc, ARRAYSIZE(vertexDesc), _vertexShaderByteCode->GetBufferPointer(), _vertexShaderByteCode->GetBufferSize(), _layout.GetAddressOf())); } void TerrainNode::BuildConstantBuffer() { D3D11_BUFFER_DESC bufferDesc; ZeroMemory(&bufferDesc, sizeof(bufferDesc)); bufferDesc.Usage = D3D11_USAGE_DEFAULT; bufferDesc.ByteWidth = sizeof(CBUFFER); bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; ThrowIfFailed(_device->CreateBuffer(&bufferDesc, NULL, _constantBuffer.GetAddressOf())); } void TerrainNode::BuildBlendState() { D3D11_BLEND_DESC transparentDesc = { 0 }; transparentDesc.AlphaToCoverageEnable = false; transparentDesc.IndependentBlendEnable = false; transparentDesc.RenderTarget[0].BlendEnable = true; transparentDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; transparentDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; transparentDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; transparentDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO; transparentDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; transparentDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; transparentDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; ThrowIfFailed(_device->CreateBlendState(&transparentDesc, _transparentBlendState.GetAddressOf())); } void TerrainNode::BuildRendererStates() { // Set default and wireframe rasteriser states D3D11_RASTERIZER_DESC rasteriserDesc; rasteriserDesc.FillMode = D3D11_FILL_SOLID; rasteriserDesc.CullMode = D3D11_CULL_BACK; rasteriserDesc.FrontCounterClockwise = false; rasteriserDesc.DepthBias = 0; rasteriserDesc.SlopeScaledDepthBias = 0.0f; rasteriserDesc.DepthBiasClamp = 0.0f; rasteriserDesc.DepthClipEnable = true; rasteriserDesc.ScissorEnable = false; rasteriserDesc.MultisampleEnable = false; rasteriserDesc.AntialiasedLineEnable = false; ThrowIfFailed(_device->CreateRasterizerState(&rasteriserDesc, _defaultRasteriserState.GetAddressOf())); rasteriserDesc.FillMode = D3D11_FILL_WIREFRAME; ThrowIfFailed(_device->CreateRasterizerState(&rasteriserDesc, _wireframeRasteriserState.GetAddressOf())); } bool TerrainNode::LoadHeightMap(wstring heightMapFilename) { unsigned int mapSize = _gridCols * _gridRows; USHORT* rawFileValues = new USHORT[mapSize]; std::ifstream inputHeightMap; inputHeightMap.open(heightMapFilename.c_str(), std::ios_base::binary); if (!inputHeightMap) { return false; } inputHeightMap.read((char*)rawFileValues, mapSize * 2); inputHeightMap.close(); // Normalise BYTE values to the range 0.0f - 1.0f; for (unsigned int i = 0; i < mapSize; i++) { _heightValues.push_back((float)rawFileValues[i] / 65536); } delete[] rawFileValues; return true; } float TerrainNode::GetHeightMapValueAt(int index) { float result = 0; if (_usedHeightMap) { result = _heightValues[index] * WORLD_HEIGHT; // +(rand() % 10); } return result; }