dev.nlited.com

>>

SimpleGrid Reorg

<<<< prev
next >>>>

2017-11-30 00:35:02 chip Page 2074 📢 PUBLIC

Nov 29 2017

The SimpleGrid Game.cpp code was becoming bloated with code for various components. I took some time to extract the major pieces into dedicated classes and interfaces.

Terrain

The Terrain class generates the surface, renders the polygons, and provides information about the terrain.


Terrain.cpp: Game.h: typedef struct RectF_s { float left,top,right,bottom; } RECTF; #define TERRAIN_OBJ_SIZE 72 class Terrain { public: Terrain(void); ~Terrain(void); HRESULT SetGrid(const RECTF &rGrid, int cTile, float maxElevation); HRESULT CreateShader(ID3D11Device *pDevice, ID3D11DeviceContext *pDC, BasicEffect *pEffect); HRESULT CreateSurface(); void Reset(void); void getVertex(float x0, float z0, VertexPositionNormalColor &vtxDst); virtual void Render(ID3D11DeviceContext *pDC, CommonStates *pStates, const XMMATRIX &view, const XMMATRIX &proj); private: class nTerrain *pObj; BYTE Obj[TERRAIN_OBJ_SIZE]; };
Terrain.cpp: /*************************************************************************/ /** Terrain.cpp: Builds and manages the 3D terrain model. **/ /** (C)2017 nlited systems inc, chip doran **/ /*************************************************************************/ #include "pch.h" #include <new> #include <stdio.h> #include <windows.h> #include <d2d1_2.h> #include <d2d1_1helper.h> #include <dwrite_1.h> #include <wrl/client.h> #include "Game.h" #pragma message(__FILE__": Optimizer disabled.") #pragma optimize("",off) using namespace DirectX; #define Zero(O) memset(&O,0,sizeof(O)) #define CHECK_OBJ_SIZE(name,size) \ template<int N> struct name##_CheckSizeT { short operator()() { return((N+0x7FFF)-size); } }; \ static void name##_CheckSize(void) { name##_CheckSizeT<sizeof(name)>()(); } #define SafeRelease(pObj) { if(pObj) pObj->Release(); pObj= 0; } class nTerrain { public: nTerrain(void); ~nTerrain(void); HRESULT SetGrid(const RECTF &rGrid, int cTile, float maxElevation); HRESULT CreateShader(ID3D11Device *pDevice, ID3D11DeviceContext *pDC, BasicEffect *pEffect); HRESULT CreateSurface(void); void Reset(void); VertexPositionNormalColor &getVertex(int x0, int z0) { return(vecVertex.at(z0*cTile+x0)); }; void getVertex(float x0, float z0, VertexPositionNormalColor &vtxDst); void Render(ID3D11DeviceContext *pDC, CommonStates *pStates, const XMMATRIX &view, const XMMATRIX &proj); private: //Data RECTF rGrid; //Grid boundaries float szTile; //Dimensions of grid tiles float maxElevation; //Maximum terrain elevation int cTile; //Number of tiles per side. bool doWireFrame; //Render as wireframe? std::unique_ptr<PrimitiveBatch<VertexPositionNormalColor>> batchVertex; std::vector<VertexPositionNormalColor> vecVertex; ComPtr<ID3D11InputLayout> d3InputLayout; }; // If this generates a compiler warning, DBTEXT_OBJ_SIZE needs to be updated. CHECK_OBJ_SIZE(nTerrain,TERRAIN_OBJ_SIZE); /*************************************************************************/ /** Public interface **/ /*************************************************************************/ Terrain::Terrain(void) { Zero(Obj); pObj= new(Obj) nTerrain(); } Terrain::~Terrain(void) { if(pObj) pObj->~nTerrain(); pObj= 0; } HRESULT Terrain::SetGrid(const RECTF &rGrid, int cTile, float maxElevation) { return(pObj ? pObj->SetGrid(rGrid,cTile,maxElevation):ERROR_INVALID_HANDLE); } HRESULT Terrain::CreateShader(ID3D11Device *pDevice,ID3D11DeviceContext *pDC,BasicEffect *pEffect) { return(pObj ? pObj->CreateShader(pDevice,pDC,pEffect):ERROR_INVALID_HANDLE); } HRESULT Terrain::CreateSurface(void) { return(pObj ? pObj->CreateSurface():ERROR_INVALID_HANDLE); } void Terrain::Reset(void) { if(pObj) pObj->Reset(); } void Terrain::getVertex(float x0, float z0, VertexPositionNormalColor &vtxDst) { return(pObj->getVertex(x0,z0,vtxDst)); } void Terrain::Render(ID3D11DeviceContext *pDC, CommonStates *pStates, const XMMATRIX &view, const XMMATRIX &proj) { return(pObj->Render(pDC,pStates,view,proj)); } /*************************************************************************/ /** nTerrain **/ /*************************************************************************/ nTerrain::nTerrain(void) { } nTerrain::~nTerrain(void) { } HRESULT nTerrain::SetGrid(const RECTF &_rGrid, int _cTile, float _maxElevation) { HRESULT Err= 0; rGrid= _rGrid; cTile= _cTile; szTile= (rGrid.right - rGrid.left)/cTile; maxElevation= _maxElevation; return(Err); } HRESULT nTerrain::CreateShader(ID3D11Device *pDevice, ID3D11DeviceContext *pDC, BasicEffect *pEffect) { HRESULT Err= 0; void const *shaderByteCode; size_t byteCodeLength; pEffect->GetVertexShaderBytecode(&shaderByteCode,&byteCodeLength); DX::ThrowIfFailed(pDevice->CreateInputLayout( VertexPositionNormalColor::InputElements, VertexPositionNormalColor::InputElementCount, shaderByteCode,byteCodeLength, d3InputLayout.ReleaseAndGetAddressOf() )); batchVertex= std::make_unique<PrimitiveBatch<VertexPositionNormalColor>>(pDC); return(Err); } void nTerrain::Reset(void) { //TODO: Release resources. } HRESULT nTerrain::CreateSurface(void) { HRESULT Err= 0; // Create the 3D surface. vecVertex.resize(cTile*cTile); // Put the grid in the XZ plane with elevation along the Y axis. // First pass calculates the position and color. // (x0,z0) is an integer index into the X/Z plane of the grid. for(int z0=0;z0<cTile;z0++) { for(int x0=0;x0<cTile;x0++) { //(x1,y1,z1) is a scaled value from 0.0 to 1.0 across the grid maximums. float x1= (float)x0/(float)cTile; float z1= (float)z0/(float)cTile; float y0= -cosf(x1*4.0f*XM_PI)*sinf(z1*2.0f*XM_PI); // z0 is the elevation function of the vertex, -PI <= z0 <= +PI float y1= (y0+XM_PI)/(XM_PI*2.0f); y1= (y1>1.0f) ? 1.0f : (y1<0.0f) ? 0.0f : y1; //(x2,y2,z2) is the actual 3D position of the vertex. float x2= x0*szTile; float y2= y1*maxElevation; float z2= z0*szTile; Vector3 pos(x2,y2,z2); Vector3 normal= -Vector3::UnitY; // Avoid absolute black, which cannot be illuminated. Vector3 color(0.5f,std::min(1.0f,std::max(0.0f,y1+0.10f)),0.5f); getVertex(x0,z0)= VertexPositionNormalColor(pos,normal,color); } } // Second pass calculates the normals (for lighting effects) //Vector3 vMax= getVertex(cTile-1,cTile-1).position; float dx= szTile; float dz= szTile; for(int z0=1;z0+1<cTile;z0++) { for(int x0=1;x0+1<cTile;x0++) { // Vertex normal is the sum of the adjacent vertices normals. // See https://stackoverflow.com/questions/6656358/calculating-normals-in-a-triangle-mesh/ Vector3 vP= getVertex(x0,z0).position; Vector3 vU= getVertex(x0,z0+1).position; Vector3 vR= getVertex(x0+1,z0).position; Vector3 vD= getVertex(x0,z0-1).position; Vector3 vL= getVertex(x0-1,z0).position; Vector3 sum; sum.x= (vL.y - vR.y)/dx; sum.z= (vD.y - vU.y)/dz; sum.y= 2; sum.Normalize(); getVertex(x0,z0).normal= sum; } } return(Err); } //Returns nearest vertex void nTerrain::getVertex(float x0, float z0, VertexPositionNormalColor &vtxDst) { int nX= (int)(cTile*((x0-rGrid.left)/(rGrid.right - rGrid.left))); nX= (nX<0) ? 0 : (nX>=cTile) ? cTile-1 : nX; int nZ= (int)(cTile*((z0-rGrid.top)/(rGrid.bottom - rGrid.top))); nZ= (nZ<0) ? 0 : (nZ>=cTile) ? cTile-1 : nZ; vtxDst= getVertex(nX,nZ); if(nX+1 < cTile && nZ+1 < cTile) { // (x0,z0) will always be greater than pt(nX,nZ) // Interpolate D(nX,nZ)+U(nX+1,nZ)+R(nX+1+nZ+1)+(nX,nZ+1) Vector3 vT(x0,vtxDst.position.y,z0); VertexPositionNormalColor v0(vtxDst); VertexPositionNormalColor v1(getVertex(nX+1,nZ));; VertexPositionNormalColor v2(getVertex(nX+1,nZ+1)); VertexPositionNormalColor v3(getVertex(nX,nZ+1)); // Determine the distance from vT to each known vertex. float d0= SimpleMath::Vector3::Distance(vT,v0.position); float d1= SimpleMath::Vector3::Distance(vT,v1.position); float d2= SimpleMath::Vector3::Distance(vT,v2.position); float d3= SimpleMath::Vector3::Distance(vT,v3.position); float dMax= std::max(d0,std::max(d1,std::max(d2,d3))); // Bias each vertex so that the closer vertex has more influence. Vector3 dv0= (1.0f-(d0/dMax))*v0.position; Vector3 dn0= (1.0f-(d0/dMax))*v0.normal; Vector3 dv1= (1.0f-(d1/dMax))*v1.position; Vector3 dn1= (1.0f-(d1/dMax))*v1.normal; Vector3 dv2= (1.0f-(d2/dMax))*v2.position; Vector3 dn2= (1.0f-(d2/dMax))*v2.normal; Vector3 dv3= (1.0f-(d3/dMax))*v3.position; Vector3 dn3= (1.0f-(d3/dMax))*v3.normal; float dSum= 4.0f - (d0/dMax + d1/dMax + d2/dMax + d3/dMax); Vector3 pos= (dv0+dv1+dv2+dv3)/dSum; Vector3 nml= (dn0+dn1+dn2+dn3)/dSum; vtxDst.position= pos; vtxDst.normal= nml; } } void nTerrain::Render(ID3D11DeviceContext *pDC, CommonStates *pStates, const XMMATRIX &view, const XMMATRIX &proj) { pDC->OMSetBlendState(pStates->Opaque(),nullptr,0xFFFFFFFF); pDC->OMSetDepthStencilState(pStates->DepthDefault(),0); pDC->RSSetState(pStates->CullCounterClockwise()); pDC->IASetInputLayout(d3InputLayout.Get()); batchVertex->Begin(); VertexPositionNormalColor v0,v1,v2,v3; for(int x0=0;x0+1<cTile;x0++) { for(int z0=0;z0+1<cTile;z0++) { v0= getVertex(x0,z0); v1= getVertex(x0+1,z0); v2= getVertex(x0+1,z0+1); v3= getVertex(x0,z0+1); if(doWireFrame) { batchVertex->DrawLine(v0,v1); batchVertex->DrawLine(v1,v2); } else { batchVertex->DrawQuad(v0,v1,v2,v3); } } } batchVertex->End(); } //EOF: TERRAIN.CPP

Game Objects

The GameObj class is both the interface and base class for all the various game objects. This includes two object types:

GameObj: Game.h: class GameObj { public: static HRESULT Create(GameObj *&pObj, const char *Type, Game *pGame, ID3D11DeviceContext *pDC); static void Destroy(GameObj *&pObj); virtual ~GameObj(void); virtual void Reset(void); virtual void SetPosition(const Vector3 &pos); virtual void Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse); virtual void Render(ID3D11DeviceContext *pDC, BasicEffect *pEffect, const XMMATRIX &view, const XMMATRIX &proj); protected: GameObj(Game *pGame); Game *pGame; //Data Vector3 pos; //Current position Vector3 vel; //Current velocity Vector3 dir; //Face: x=roll y=yaw z=pitch Matrix xform; //Transform matrix ComPtr<ID3D11InputLayout> d3InputLayout; private: };
GameObj.cpp: /*************************************************************************/ /** GameObj.cpp: A simple game object. **/ /** (C)2017 nlited systems, chip doran **/ /*************************************************************************/ #include "pch.h" #include <new> #include <stdio.h> #include <windows.h> #include <d2d1_2.h> #include <d2d1_1helper.h> #include <dwrite_1.h> #include <wrl/client.h> #include "Game.h" #pragma message(__FILE__": Optimizer disabled.") #pragma optimize("",off) using namespace DirectX; #define Zero(O) memset(&O,0,sizeof(O)) #define SafeRelease(pObj) { if(pObj) pObj->Release(); pObj= 0; } class ObjPawn: public GameObj { public: static HRESULT Create(GameObj *&pObj, Game *pGame, ID3D11DeviceContext *pDC); ~ObjPawn(void); void Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse); void Render(ID3D11DeviceContext *pDC, BasicEffect *pEffect, const XMMATRIX &view, const XMMATRIX &proj); protected: private: ObjPawn(Game *pGame); HRESULT Create2(ID3D11DeviceContext *pDC); //Data float Hgt; std::unique_ptr<PrimitiveBatch<VertexPositionNormalColor>> pBatch; std::unique_ptr<GeometricPrimitive> pShape; }; class ObjSun: public GameObj { public: static HRESULT Create(GameObj *&pObj, Game *pGame, ID3D11DeviceContext *pDC); ~ObjSun(void); void Reset(void); void Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse); //void GetLights(BasicEffect *pEffect); void Render(ID3D11DeviceContext *pDC, BasicEffect *pEffect, const XMMATRIX &view, const XMMATRIX &proj); protected: private: ObjSun(Game *pGame); HRESULT Create2(ID3D11DeviceContext *pDC); //Data float Radius; //Radius of the sun Vector3 ptOrbit; //Center of the orbit float OrbitRadius; //Radius of the orbit float OrbitPeriod; //Orbital period float OrbitClock; //Orbital clock (position in seconds) std::unique_ptr<GeometricPrimitive> pShape; }; /*************************************************************************/ /** Base GameObj **/ /*************************************************************************/ GameObj::GameObj(Game *_pGame) { pGame= _pGame; xform= Matrix::Identity; } GameObj::~GameObj(void) { } HRESULT GameObj::Create(GameObj *&pObj, const char *Type, Game *pGame, ID3D11DeviceContext *pDC) { HRESULT Err= 0; if(stricmp("PAWN",Type)==0) { Err= ObjPawn::Create(pObj,pGame,pDC); } else if(stricmp("SUN",Type)==0) { Err= ObjSun::Create(pObj,pGame,pDC); } else { Err= ERROR_NOT_FOUND; } return(Err); } void GameObj::Reset(void) { } void GameObj::Destroy(GameObj *&pObj) { if(pObj) delete pObj; pObj= 0; } void GameObj::SetPosition(const Vector3 &_pos) { pos= _pos; xform= Matrix::Identity; xform.Translation(pos); } void GameObj::Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse) { } void GameObj::Render(ID3D11DeviceContext *pDC, BasicEffect *pEffect, const XMMATRIX &view, const XMMATRIX &proj) { // Reset the DC effects before rendering each object. pEffect->Apply(pDC); } /*************************************************************************/ /** Pawn **/ /*************************************************************************/ ObjPawn::ObjPawn(Game *_pGame): GameObj(_pGame) { Hgt= 0.25f; } ObjPawn::~ObjPawn(void) { } HRESULT ObjPawn::Create(GameObj *&_pObj, Game *_pGame, ID3D11DeviceContext *pDC) { HRESULT Err; ObjPawn *pObj; if(!(pObj= new ObjPawn(_pGame))) { Err= Warn(ERROR_NO_MEM,"ObjPawn:Create: NoMem(%d)",sizeof(*pObj)); } else if(FAILED(Err= pObj->Create2(pDC))) { delete pObj; } else { _pObj= pObj; } return(Err); } HRESULT ObjPawn::Create2(ID3D11DeviceContext *pDC) { HRESULT Err= 0; pBatch= std::make_unique<PrimitiveBatch<VertexPositionNormalColor>>(pDC); pShape= GeometricPrimitive::CreateCylinder(pDC,Hgt,Hgt/10.0f); pos= Vector3(0,0,0); dir= vel= Vector3(0.1f,0,0.1f); return(Err); } void ObjPawn::Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse) { RECTF rArena; rArena= pGame->GetArena(); xform= Matrix::Identity; pos+= vel*secElapsed; if(pos.x < rArena.left) { pos.x= vel.x= (0.010f*(float)rand())/RAND_MAX + 0.10f; } else if(pos.x >= rArena.right) { vel.x= -((0.010f*(float)rand())/RAND_MAX + 0.10f); pos.x= rArena.right + vel.x; } if(pos.z < rArena.top) { pos.z= vel.z= (0.010f*(float)rand())/RAND_MAX + 0.10f; } else if(pos.z >= rArena.bottom) { vel.z= -((0.010f*(float)rand())/RAND_MAX + 0.10f); pos.z= rArena.bottom + vel.z; } VertexPositionNormalColor vtxPawn; //TODO: Interpolate vtxPawn with neighboring vertices. pGame->getVertex(pos.x,pos.z,vtxPawn); pos.y= vtxPawn.position.y; // + 0.125f; Vector3 ptCenter= (Hgt/2.0f)*vtxPawn.normal; ptCenter+= pos; xform.Translation(ptCenter); xform.Up(vtxPawn.normal); } void ObjPawn::Render(ID3D11DeviceContext *pDC, BasicEffect *pEffect, const XMMATRIX &view, const XMMATRIX &proj) { GameObj::Render(pDC,pEffect,view,proj); // Draw vertical reference line through pos. VertexPositionNormalColor v0(Vector3(pos.x,0,pos.z),Vector3::UnitX,Colors::Brown); VertexPositionNormalColor v1(Vector3(pos.x,10.0f,pos.z),Vector3::UnitX,Colors::White); pBatch->Begin(); pBatch->DrawLine(v0,v1); pBatch->End(); pShape->Draw(xform,view,proj); //TODO: Figure out how to draw the pawn's shadow. } /*************************************************************************/ /** ObjSun: Sphere with a light source. **/ /*************************************************************************/ ObjSun::ObjSun(Game *_pGame): GameObj(_pGame) { Radius= 2.0f; } ObjSun::~ObjSun(void) { } HRESULT ObjSun::Create(GameObj *&_pObj, Game *_pGame, ID3D11DeviceContext *pDC) { HRESULT Err; ObjSun *pObj; if(!(pObj= new ObjSun(_pGame))) { Err= Warn(ERROR_NO_MEM,"ObjSun:Create: NoMem(%d)",sizeof(*pObj)); } else if(FAILED(Err= pObj->Create2(pDC))) { delete pObj; } else { _pObj= pObj; } return(Err); } HRESULT ObjSun::Create2(ID3D11DeviceContext *pDC) { HRESULT Err= 0; pShape= GeometricPrimitive::CreateSphere(pDC,Radius); dir= vel= Vector3(0,0,0); RECTF rArena= pGame->GetArena(); ptOrbit= Vector3((rArena.left+rArena.right)/2,10.0f,(rArena.top+rArena.bottom)/2); OrbitRadius= (rArena.right - rArena.left)*2; OrbitPeriod= 30.0f; OrbitClock= 0; float angle= XM_PI*2*OrbitClock/OrbitPeriod; pos= Vector3(ptOrbit.x+OrbitRadius*cosf(angle),ptOrbit.y,ptOrbit.z+OrbitRadius*sinf(angle)); return(Err); } void ObjSun::Reset(void) { //TODO: Release resources. } void ObjSun::Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse) { OrbitClock+= secElapsed; float angle= XM_PI*2*OrbitClock/OrbitPeriod; pos= Vector3(ptOrbit.x+OrbitRadius*cosf(angle),ptOrbit.y,ptOrbit.z+OrbitRadius*sinf(angle)); xform= Matrix::Identity; xform.Translation(pos); } void ObjSun::Render(ID3D11DeviceContext *pDC, BasicEffect *pEffect, const XMMATRIX &view, const XMMATRIX &proj) { //Cancel lights on this object, since it is the light source. pEffect->SetLightEnabled(0,false); GameObj::Render(pDC,pEffect,view,proj); pShape->Draw(xform,view,proj); //Set lights for all remaining objects. Vector3 light= Vector3(ptOrbit.x,0,ptOrbit.z) - pos; light.Normalize(); pEffect->SetLightEnabled(0,true); pEffect->SetLightDirection(0,light); pEffect->Apply(pDC); }

Game.cpp

Game.cpp: #include "pch.h" #include "Game.h" #pragma message(__FILE__": Optimizer disabled.") #pragma optimize("",off) extern void ExitGame(); #define Zero(O) memset(&O,0,sizeof(O)) #define DBG_RENDER 0x00000100 void Debug(DWORD Mask, const char *Fmt, ...) { va_list ArgList; char text[200]; int ChrCt= 0; va_start(ArgList,Fmt); ChrCt+= _vsnprintf(text+ChrCt,sizeof(text)-ChrCt,Fmt,ArgList); ChrCt+= _snprintf(text+ChrCt,sizeof(text)-ChrCt,"\r\n"); OutputDebugStringA(text); va_end(ArgList); } HRESULT Warn(HRESULT Err, const char *Fmt, ...) { va_list ArgList; char text[200]; int ChrCt= 0; va_start(ArgList,Fmt); ChrCt+= _snprintf(text+ChrCt,sizeof(text)-ChrCt,"WRN[%X]: ",Err); ChrCt+= _vsnprintf(text+ChrCt,sizeof(text)-ChrCt,Fmt,ArgList); ChrCt+= _snprintf(text+ChrCt,sizeof(text)-ChrCt,"\r\n"); OutputDebugStringA(text); va_end(ArgList); return(Err); } HRESULT Error(HRESULT Err, const char *Fmt, ...) { va_list ArgList; char text[200]; int ChrCt= 0; va_start(ArgList,Fmt); ChrCt+= _snprintf(text+ChrCt,sizeof(text)-ChrCt,"ERR[%X]: ",Err); ChrCt+= _vsnprintf(text+ChrCt,sizeof(text)-ChrCt,Fmt,ArgList); ChrCt+= _snprintf(text+ChrCt,sizeof(text)-ChrCt,"\r\n"); OutputDebugStringA(text); va_end(ArgList); return(Err); } /*************************************************************************/ /** DirectXTK framework **/ /** This code should be left mostly alone. **/ /*************************************************************************/ Game::Game(): hWnd(nullptr), WndWid(800), WndHgt(600), d3FeatureLevel(D3D_FEATURE_LEVEL_9_1) { frameCt= 0; Initialize2(); } void Game::Initialize(HWND window, int width, int height) { hWnd = window; WndWid = std::max(width,1); WndHgt = std::max(height,1); QueryPerformanceFrequency(&clkFreq); CreateDevice(); CreateResources(); Initialize3(window); } void Game::Tick(void) { timerGame.Tick([&]() { Update(timerGame); }); Render(); } void Game::Update(DX::StepTimer const& timer) { float elapsedTime = float(timer.GetElapsedSeconds()); float totalTime= float(timer.GetTotalSeconds()); Update2(totalTime,elapsedTime); } HRESULT gQueryErr= 0; void Game::Render(void) { if(timerGame.GetFrameCount() == 0) return; frameCt++; RenderTime(); Clear(); Render2(); Present(); } void Game::Clear(void) { d3dContext->ClearRenderTargetView(viewRT.Get(),Colors::CornflowerBlue); d3dContext->ClearDepthStencilView(viewStencil.Get(),D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL,1.0f,0); d3dContext->OMSetRenderTargets(1,viewRT.GetAddressOf(),viewStencil.Get()); CD3D11_VIEWPORT viewport(0.0f,0.0f,static_cast<float>(WndWid),static_cast<float>(WndHgt)); d3dContext->RSSetViewports(1,&viewport); } void Game::Present(void) { HRESULT hr = swapChain->Present(1,0); if(hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { OnDeviceLost(); } else { DX::ThrowIfFailed(hr); } } void Game::OnActivated(void) { } void Game::OnDeactivated(void) { } void Game::OnSuspending(void) { } void Game::OnResuming(void) { timerGame.ResetElapsedTime(); } void Game::OnWindowSizeChanged(int width, int height) { WndWid = std::max(width,1); WndHgt = std::max(height,1); CreateResources(); } // Properties void Game::GetDefaultSize(int& width, int& height) const { width = 800; height = 600; } void Game::CreateDevice(void) { UINT creationFlags= D3D11_CREATE_DEVICE_BGRA_SUPPORT; //For Direct2D interop #ifdef _DEBUG creationFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif static const D3D_FEATURE_LEVEL featureLevels[] ={ D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1, }; ComPtr<ID3D11Device> device; ComPtr<ID3D11DeviceContext> context; DX::ThrowIfFailed(D3D11CreateDevice( nullptr, // specify nullptr to use the default adapter D3D_DRIVER_TYPE_HARDWARE, nullptr, creationFlags, featureLevels, _countof(featureLevels), D3D11_SDK_VERSION, device.ReleaseAndGetAddressOf(), // returns the Direct3D device created &d3FeatureLevel, // returns feature level of device created context.ReleaseAndGetAddressOf() // returns the device immediate context )); #ifndef NDEBUG ComPtr<ID3D11Debug> d3dDebug; if(SUCCEEDED(device.As(&d3dDebug))) { ComPtr<ID3D11InfoQueue> d3dInfoQueue; if(SUCCEEDED(d3dDebug.As(&d3dInfoQueue))) { #ifdef _DEBUG d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION,true); d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR,true); #endif D3D11_MESSAGE_ID hide[] = { D3D11_MESSAGE_ID_SETPRIVATEDATA_CHANGINGPARAMS, // TODO: Add more message IDs here as needed. }; D3D11_INFO_QUEUE_FILTER filter ={ }; filter.DenyList.NumIDs = _countof(hide); filter.DenyList.pIDList = hide; d3dInfoQueue->AddStorageFilterEntries(&filter); } } #endif DX::ThrowIfFailed(device.As(&d3dDevice)); DX::ThrowIfFailed(context.As(&d3dContext)); CreateDevice2(); } void Game::CreateResources(void) { // Clear the previous window size specific context. ID3D11RenderTargetView* nullViews[] ={ nullptr }; d3dContext->OMSetRenderTargets(_countof(nullViews),nullViews,nullptr); viewRT.Reset(); viewStencil.Reset(); d3dContext->Flush(); UINT backBufferWidth = static_cast<UINT>(WndWid); UINT backBufferHeight = static_cast<UINT>(WndHgt); DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM; DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; UINT backBufferCount = 2; // If the swap chain already exists, resize it, otherwise create one. if(swapChain) { HRESULT hr = swapChain->ResizeBuffers(backBufferCount,backBufferWidth,backBufferHeight,backBufferFormat,0); if(hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { // If the device was removed for any reason, a new device and swap chain will need to be created. OnDeviceLost(); // Everything is set up now. Do not continue execution of this method. OnDeviceLost will reenter this method // and correctly set up the new device. return; } else { DX::ThrowIfFailed(hr); } } else { // First, retrieve the underlying DXGI Device from the D3D Device. ComPtr<IDXGIDevice1> dxgiDevice; DX::ThrowIfFailed(d3dDevice.As(&dxgiDevice)); // Identify the physical adapter (GPU or card) this device is running on. ComPtr<IDXGIAdapter> dxgiAdapter; DX::ThrowIfFailed(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf())); // And obtain the factory object that created it. ComPtr<IDXGIFactory2> dxgiFactory; DX::ThrowIfFailed(dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.GetAddressOf()))); // Create a descriptor for the swap chain. DXGI_SWAP_CHAIN_DESC1 swapChainDesc ={ }; swapChainDesc.Width = backBufferWidth; swapChainDesc.Height = backBufferHeight; swapChainDesc.Format = backBufferFormat; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = backBufferCount; DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsSwapChainDesc ={ }; fsSwapChainDesc.Windowed = TRUE; // Create a SwapChain from a Win32 window. DX::ThrowIfFailed(dxgiFactory->CreateSwapChainForHwnd( d3dDevice.Get(), hWnd, &swapChainDesc, &fsSwapChainDesc, nullptr, swapChain.ReleaseAndGetAddressOf() )); // This template does not support exclusive fullscreen mode and prevents DXGI from responding to the ALT+ENTER shortcut. DX::ThrowIfFailed(dxgiFactory->MakeWindowAssociation(hWnd,DXGI_MWA_NO_ALT_ENTER)); } // Obtain the backbuffer for this window which will be the final 3D rendertarget. ComPtr<ID3D11Texture2D> backBuffer; DX::ThrowIfFailed(swapChain->GetBuffer(0,IID_PPV_ARGS(backBuffer.GetAddressOf()))); // Create a view interface on the rendertarget to use on bind. DX::ThrowIfFailed(d3dDevice->CreateRenderTargetView(backBuffer.Get(),nullptr,viewRT.ReleaseAndGetAddressOf())); // Allocate a 2-D surface as the depth/stencil buffer and // create a DepthStencil view on this surface to use on bind. CD3D11_TEXTURE2D_DESC depthStencilDesc(depthBufferFormat,backBufferWidth,backBufferHeight,1,1,D3D11_BIND_DEPTH_STENCIL); ComPtr<ID3D11Texture2D> depthStencil; DX::ThrowIfFailed(d3dDevice->CreateTexture2D(&depthStencilDesc,nullptr,depthStencil.GetAddressOf())); CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); DX::ThrowIfFailed(d3dDevice->CreateDepthStencilView(depthStencil.Get(),&depthStencilViewDesc,viewStencil.ReleaseAndGetAddressOf())); CreateResources2(); } void Game::OnDeviceLost(void) { viewStencil.Reset(); viewRT.Reset(); swapChain.Reset(); d3dContext.Reset(); d3dDevice.Reset(); OnDeviceLost2(); //Recreate everything CreateDevice(); CreateResources(); } /*************************************************************************/ /** TODO additions **/ /*************************************************************************/ // Game object has been created. void Game::Initialize2(void) { rGrid= { 0.0f, 0.0f, 5.0f, 5.0f }; tileCt= 100; tileSz= (rGrid.right - rGrid.left)/(float)tileCt; maxElevation= 4.0f; } void Game::SetVertexCt(UINT vertexCt) { tileCt= (UINT)sqrt(vertexCt); tileSz= (rGrid.right - rGrid.left)/(float)tileCt; } // Window has been created. void Game::Initialize3(HWND window) { keyboard= std::make_unique<Keyboard>(); mouse= std::make_unique<Mouse>(); mouse->SetWindow(window); mouse->SetMode(Mouse::MODE_RELATIVE); kbTab= false; kbScrollLock= false; doPause= false; auto mouseState= mouse->GetState(); mousewheel= mouseState.scrollWheelValue; } // D3DDevice has been created. void Game::CreateDevice2(void) { HRESULT Err= 0; dxStates= std::make_unique<CommonStates>(d3dDevice.Get()); dxEffect= std::make_unique<BasicEffect>(d3dDevice.Get()); dxEffect->SetVertexColorEnabled(true); dxEffect->SetLightingEnabled(true); dxEffect->SetLightEnabled(0,true); dxEffect->SetLightDiffuseColor(0,Colors::White); Vector3 light(1.0f,maxElevation*2.0f,1.0f); light.Normalize(); dxEffect->SetLightDirection(0,light); if(FAILED(Err= terrain.CreateShader(d3dDevice.Get(),d3dContext.Get(),dxEffect.get()))) Warn(Err,"Game:CreateDevice2: Terrain.Create() failed."); if(FAILED(Err= GameObj::Create(pSun,"SUN",this,d3dContext.Get()))) Warn(Err,"Game:CreateDevice2: GameObj:Create(SUN) failed."); GameObj::Create(pPawn,"PAWN",this,d3dContext.Get()); } // 3D resources and game objects have been created. void Game::CreateResources2(void) { HRESULT Err= 0; text.SetFont(L"Bitstream Vera Sans Mono",16.0f); text.Create(d3dDevice.Get(),d3dContext.Get()); // Create the perspective. doWireFrame= false; viewPt= Vector3(tileCt*tileSz,maxElevation*1.50f,tileCt*tileSz); viewVec.y= XMConvertToRadians(-170.0f); viewVec.z= XMConvertToRadians(-2.0f); fovAngle= XM_PI/4.0f; fovNear= 0.1f; fovFar= 20.0f; mtrxProj= Matrix::CreatePerspectiveFieldOfView(fovAngle,float(WndWid)/float(WndHgt),fovNear,fovFar); dxEffect->SetProjection(mtrxProj); if(FAILED(Err= terrain.SetGrid(rGrid,tileCt,maxElevation))) Err= Warn(Err,"Game:CreateResources2: Terrain:SetGrid failed."); if(FAILED(Err= terrain.CreateSurface())) Err= Warn(Err,"Game:CreateResources2: Terrain:CreateSurface failed."); } void Game::OnDeviceLost2(void) { dxStates.reset(); dxEffect.reset(); pSun->Reset(); pPawn->Reset(); terrain.Reset(); text.Reset(); } // Update game objects over time. // Read keyboard and mouse inputs. void Game::Update2(float totalTime, float elapsedTime) { auto mouseState= mouse->GetState(); auto kb= keyboard->GetState(); // time input if(doPause) { elapsedTime= 0; if(kb.OemPlus) elapsedTime= 1/60.0f; if(kb.OemMinus) elapsedTime= -1/60.0f; } if(elapsedTime!=0) { // Update game objects: Sun and Pawn. pSun->Update(totalTime,elapsedTime,keyboard.get(),mouse.get()); pPawn->Update(totalTime,elapsedTime,keyboard.get(),mouse.get()); // Update light sources from sun. //pSun->GetLights(dxEffect.get()); } // Mouse input: yaw, pitch, and elevation. Vector3 move= Vector3::Zero; Vector3 delta= Vector3(float(mouseState.x),float(mouseState.y),0.0f); viewPt.y+= float(mouseState.scrollWheelValue - mousewheel)*0.0005f; mousewheel= mouseState.scrollWheelValue; viewVec.y-= delta.x*0.0010f; viewVec.z-= delta.y*0.0001f; // "Gimbal lock" is when roll calculation becomes difficult as // pitch approaches vertical. I avoid this by enforcing minimum // and maximum limits on the pitch. float limit = XM_PI/2.0f - 0.01f; viewVec.z = std::max(-limit,viewVec.z); viewVec.z = std::min(+limit,viewVec.z); // Avoid spinning up yaw beyond +-2pi. if(viewVec.y > XM_PI) { viewVec.y -= XM_PI * 2.0f; } else if(viewVec.y < -XM_PI) { viewVec.y += XM_PI * 2.0f; } // Keyboard: forward, back, left, right, misc. if(kb.Escape) PostQuitMessage(0); if(kb.Tab) { if(!kbTab) { doWireFrame= !doWireFrame; kbTab= true; } } else { kbTab= false; } if(kb.Scroll) { if(!kbScrollLock) doPause= !doPause; kbScrollLock= true; } else { kbScrollLock= false; } // Navigation is easier if only yaw is used. // Elevation (pitch) changes are controlled with the scroll wheel. // This allows me to move around the world while looking straight down. Quaternion moveQ= Quaternion::CreateFromYawPitchRoll(viewVec.y,0.0f,0.0f); if(kb.Left || kb.A) move.x= +1.0f; if(kb.Right || kb.D) move.x= -1.0f; if(kb.Up || kb.W) move.z= +1.0f; if(kb.Down || kb.S) move.z= -1.0f; if(kb.PageUp) move.y= +1.0f; if(kb.PageDown) move.y= -1.0f; move= Vector3::Transform(move,moveQ)*0.010f; viewPt+= move; if(viewPt.y < 0.005f) viewPt.y= 0.005f; // Field of View adjustments if(kb.OemComma) { //Decrease if(kb.LeftShift) { //Decrease near plane fovNear= fovNear*0.90f; } else if(kb.RightShift) { //Decrease far plane fovFar= fovFar*0.90f; } else if(kb.LeftControl || kb.RightControl) { //Decrease angle fovAngle-= 0.01f; } mtrxProj= Matrix::CreatePerspectiveFieldOfView(fovAngle,float(WndWid)/float(WndHgt),fovNear,fovFar); } if(kb.OemPeriod) { if(kb.LeftShift) { fovNear= fovNear*1.10f; } else if(kb.RightShift) { fovFar= fovFar*1.10f; } else if(kb.LeftControl || kb.RightControl) { fovAngle+= 0.01f; } mtrxProj= Matrix::CreatePerspectiveFieldOfView(fovAngle,float(WndWid)/float(WndHgt),fovNear,fovFar); } } // Record the instantaneous frame rate for the last 10 seconds. // I want the actual instantaneous frame rate, not an average. // vecFrameTS is a FIFO of the high-resolution timestamps for each frame. // Drop all timestamps that have expired (older than 10 seconds) // The instantaneous framerate is the number of timestamps in the FIFO. // The framerate history for the last 1000 frames is stored in vecFrameRate. void Game::RenderTime(void) { LARGE_INTEGER clkTick; QueryPerformanceCounter(&clkTick); vecFrameTS.push_front(clkTick); INT64 clkExpire= clkTick.QuadPart - clkFreq.QuadPart*10; while(vecFrameTS.size()>0 && vecFrameTS.back().QuadPart < clkExpire) vecFrameTS.pop_back(); frameRate= (UINT16)(vecFrameTS.size()/10); while(vecFrameRate.size() > 200) vecFrameRate.pop_back(); vecFrameRate.push_front(frameRate); } // Draw the frame. void Game::Render2(void) { float r= 0.1f; XMVECTOR lookAt= viewPt + Vector3(r*sinf(viewVec.y),sinf(viewVec.z),r*cosf(viewVec.y)); XMMATRIX view= XMMatrixLookAtRH(viewPt,lookAt,Vector3::Up); dxEffect->SetView(view); dxEffect->SetProjection(mtrxProj); d3dContext->OMSetBlendState(dxStates->Opaque(),nullptr,0xFFFFFFFF); d3dContext->OMSetDepthStencilState(dxStates->DepthDefault(),0); d3dContext->RSSetState(dxStates->CullCounterClockwise()); dxEffect->Apply(d3dContext.Get()); // Render Sun first since it will set the lighting effects. pSun->Render(d3dContext.Get(),dxEffect.get(),view,mtrxProj); // Render terrain before game objects. terrain.Render(d3dContext.Get(),dxStates.get(),view,mtrxProj); // Render game objects. RenderObjects(view); RenderText(); } // Draw the game objects. void Game::RenderObjects(XMMATRIX &view) { pPawn->Render(d3dContext.Get(),dxEffect.get(),view,mtrxProj); } // Draw 2D status text. void Game::RenderText(void) { //D3Text text(d3dDevice.Get(),d3dContext.Get(),swapChain.Get()); text.Begin(swapChain.Get()); text.SetColor(D2D1::ColorF(D2D1::ColorF::White,0.25f)); text.FillRect(D2D1::Point2F(0,0),D2D1::Point2F(600,64)); text.Write(L"Frame %06llu %ufps V%u\r\n",frameCt,frameRate,tileCt*tileCt); text.Write(L"(%6.2f,%6.2f,%6.2f)",viewPt.x,viewPt.y,viewPt.z); text.Write(L" %3.1f %3.1f\r\n",XMConvertToDegrees(viewVec.y),XMConvertToDegrees(viewVec.z)); text.Write(L"FOV %5.2f %5.2f/%5.2f\r\n",fovAngle,fovNear,fovFar); text.DrawRectangle(D2D1::Point2F(10.0f,100.0f),D2D1::Point2F(210.0f,120.0f)); D2D1_POINT_2F pt0,pt1; pt0= { 10.0f,120.0f-std::min((INT16)120,vecFrameRate[0])*(20.0f/120.0f) }; text.SetColor(D2D1::ColorF(D2D1::ColorF::Yellow)); for(UINT n1=1;n1<vecFrameRate.size();n1++) { pt1= { 10.0f+n1,120.0f-std::min((INT16)120,vecFrameRate[n1])*(20.0f/120.0f) }; text.DrawLine(pt0,pt1); pt0= pt1; } text.SetColor(D2D1::ColorF(D2D1::ColorF::Black)); text.End(); } //EOF: SIMPLEGRID.CPP

This is mostly working, except that the sun is now being rendered as a wireframe instead of a solid sphere. I suspect this is because it is now being rendered before the terrain since the pawn, rendered after the terrain, is appearing as a solid. Possibly the shader model is not being set correctly? I had assumed the DirectXTK GeometricShape class would handle everything.

Direct3D simple grid



WebV7 (C)2018 nlited | Rendered by tikope in 39.577ms | 18.191.200.47