2017-11-27 05:07:35 chip
Page 2072
📢 PUBLIC
Nov 26 2017
I want to place an object on the surface of the grid and move it
around, following the contour of the terrain.
Create a "pawn" object.
Update its position.
Render the object.
Steps 1 and 3 are easy, just copy how the sun object is created and
use a cylinder instead.
TEXT Create Pawn object :
1 class Game {
2 private:
3 std::unique_ptr<GeometricPrimitive> objPawn;
4 Vector3 pawnPt;
5 Matrix mtrxPawn;
6 };
7
8 void Game::CreateDevice2(void) {
9 objPawn= GeometricPrimitive::CreateCylinder(d3dContext.Get(),0.25f,gridSz/100.0f);
10 pawnPt= Vector3(0,0,0);
11 }
12
13 void Game::Update2(float totalTime,float elapsedTime) {
14 mtrxPawn= Matrix::Identity;
15 mtrxPawn.Translation(pawnPt);
16 }
17
18 void Game::RenderObjects(XMMATRIX &view) {
19 objPawn->Draw(mtrxPawn,view,mtrxProj);
20 }
Step 2 is the tricky part. I need a new function to find the vertex
that is nearest a given (x,z) position on the grid. The elevation is
taken directly from vertex.position.y and applied to the shape
position.
TEXT Position the Pawn :
1 class Game {
2 private:
3 VertexPositionNormalColor &getVertex(float x0, float y0);
4 };
5
6 //Returns nearest vertex
7 VertexPositionNormalColor &Game::getVertex(float x0, float y0) {
8 int _x0= (int)(tileCt*(x0/gridSz));
9 _x0= (_x0<0) ? 0 : (_x0>=tileCt) ? tileCt-1 : _x0;
10 int _y0= (int)(tileCt*(y0/gridSz));
11 _y0= (_y0<0) ? 0 : (_y0>=tileCt) ? tileCt-1 : _y0;
12 return(getVertex(_x0,_y0));
13 }
14
15 void Game::Update2(float totalTime,float elapsedTime) {
16 VertexPositionNormalColor vtxPawn= getVertex(pawnPt.x,pawnPt.z);
17 pawnPt.y= vtxPawn.position.y + 0.125f;
18 mtrxPawn.Translation(pawnPt);
19 }
I want the object to be flat against the surface, instead of always
pointing straight up. The vertex normal vector is used to orient the
shape flat against the surface by using it for the "UP" transform.
TEXT Pawn orientation :
1 void Game::Update2(float totalTime, float elapsedTime) {
2 VertexPositionNormalColor vtxPawn= getVertex(pawnPt.x,pawnPt.z);
3 pawnPt.y= vtxPawn.position.y + 0.125f;
4 mtrxPawn.Translation(pawnPt);
5 mtrxPawn.Up(vtxPawn.normal);
6 }
I use a simple "pong" motion to move the object across the grid,
rebounding off the edges.
TEXT Pawn motion :
1 class Game {
2 private:
3 std::unique_ptr<GeometricPrimitive> objPawn;
4 Vector3 pawnPt;
5 Vector3 pawnMv;
6 Matrix mtrxPawn;
7 };
8
9 void Game::CreateDevice2(void) {
10 objPawn= GeometricPrimitive::CreateCylinder(d3dContext.Get(),0.25f,gridSz/100.0f);
11 pawnPt= Vector3(0,0,0);
12 pawnMv= Vector3(0.1f,0,0.1f);
13 }
14
15 void Game::Update2(float totalTime, float elapsedTime) {
16 // Move the pawn along the surface of the grid.
17 mtrxPawn= Matrix::Identity;
18 pawnPt+= pawnMv*elapsedTime;
19 if(pawnPt.x < 0.0f) {
20 pawnPt.x= pawnMv.x= (float)rand()/RAND_MAX + 0.10f;
21 } else if(pawnPt.x >= gridSz) {
22 pawnMv.x= -((float)rand()/RAND_MAX + 0.10f);
23 pawnPt.x= gridSz + pawnMv.x;
24 }
25 if(pawnPt.z < 0.0f) {
26 pawnPt.z= pawnMv.z= (float)rand()/RAND_MAX + 0.10f;
27 } else if(pawnPt.z >= gridSz) {
28 pawnMv.z= -((float)rand()/RAND_MAX + 0.10f);
29 pawnPt.z= gridSz + pawnMv.z;
30 }
31 VertexPositionNormalColor vtxPawn= getVertex(pawnPt.x,pawnPt.z);
32 pawnPt.y= vtxPawn.position.y + 0.125f;
33 mtrxPawn.Translation(pawnPt);
34 mtrxPawn.Up(vtxPawn.normal);
35 }
This is close but there are a couple problems. There is a severe
jump in elevation as pawn jumps from one vertex to another. The
solution is to interpolate the elevation between the 4 nearest
vertices. This interpolation should also be applied to the normal
vector.
The object is distorted by the UP transformation. I think this
is because using the UP transformation is not the right answer; it
is transforming the view instead of rotating the object.
This is my first attempt at interpolation:
TEXT getVertex with interpolation :
1 //Returns nearest vertex
2 void Game::getVertex(float x0, float z0, VertexPositionNormalColor &vtxDst) {
3 int nX= (int)(tileCt*(x0/gridSz));
4 nX= (nX<0) ? 0 : (nX>=tileCt) ? tileCt-1 : nX;
5 int nZ= (int)(tileCt*(z0/gridSz));
6 nZ= (nZ<0) ? 0 : (nZ>=tileCt) ? tileCt-1 : nZ;
7 vtxDst= getVertex(nX,nZ);
8 if(nX+1 < tileCt && nZ+1 < tileCt) {
9 // (x0,z0) will always be greater than pt(nX,nZ)
10 // Interpolate D(nX,nZ)+U(nX+1,nZ)+R(nX+1+nZ+1)+(nX,nZ+1)
11 Vector3 vT(x0,vtxDst.position.y,z0);
12 VertexPositionNormalColor v0(vtxDst);
13 VertexPositionNormalColor v1(getVertex(nX+1,nZ));;
14 VertexPositionNormalColor v2(getVertex(nX+1,nZ+1));
15 VertexPositionNormalColor v3(getVertex(nX,nZ+1));
16 float d0= SimpleMath::Vector3::Distance(vT,v0.position);
17 float d1= SimpleMath::Vector3::Distance(vT,v1.position);
18 float d2= SimpleMath::Vector3::Distance(vT,v2.position);
19 float d3= SimpleMath::Vector3::Distance(vT,v3.position);
20 float dSum= d0+d1+d2+d3;
21 // TODO: Convert from Vector3 to XMFLOAT3
22 Vector3 pos= ((v0.position*d0+v1.position*d1+v2.position*d2+v3.position*d3)/dSum);
23 Vector3 nml= ((v0.normal*d0+v1.normal*d1+v2.normal*d2+v3.normal*d3)/dSum);
24 vtxDst.position= pos;
25 vtxDst.normal= nml;
26 }
27 }
This seems to be only marginally better, the movement is still very
jerky. Part of the problem is that the center of the object is not the
base. My simplistic initial approach was to elevate the tracking point
vertically (+y) by half the height of the cylinder. This is wrong for
(at least) two reasons. First, the distance from the surface up to the
center is not the distance from the center down through the surface
normal, resulting in the base either floating above or extend down
through the surface. Second, I am using the surface normal from
directly under the center instead of the surface normal under the base
which is visually wrong.
I should be projecting the tracking point up through the surface
normal to determine the center point.
I created a vector from the base to the center of the shape
(0,0.125,0), multiplied by the surface normal, and applied it as a
translation to the matrix.
TEXT Pawn update :
1 VertexPositionNormalColor vtxPawn;
2 getVertex(pawnPt.x,pawnPt.z,vtxPawn);
3 //pawnPt.y= vtxPawn.position.y + 0.125f;
4 mtrxPawn.Translation(pawnPt);
5 Vector3 ptCenter(0,0.125f,0);
6 ptCenter*= vtxPawn.normal;
7 mtrxPawn.Translation(-ptCenter);
8 mtrxPawn.Up(vtxPawn.normal);
The results were not at all what I expected and left me
perplexed...
My weighted average code is wrong, by multiplying the distance I
am biasing in favor of distance instead of against. I need to
determine the maximum distance and use the difference. My second
attempt is smoother (although still jerky in some areas) and the
base is much closer to the surface.
TEXT Interpolation :
1 //Returns nearest vertex
2 void Game::getVertex(float x0, float z0, VertexPositionNormalColor &vtxDst) {
3 int nX= (int)(tileCt*((x0-rGrid.left)/(rGrid.right - rGrid.left)));
4 nX= (nX<0) ? 0 : (nX>=tileCt) ? tileCt-1 : nX;
5 int nZ= (int)(tileCt*((z0-rGrid.top)/(rGrid.bottom - rGrid.top)));
6 nZ= (nZ<0) ? 0 : (nZ>=tileCt) ? tileCt-1 : nZ;
7 vtxDst= getVertex(nX,nZ);
8 if(nX+1 < tileCt && nZ+1 < tileCt) {
9 // (x0,z0) will always be greater than pt(nX,nZ)
10 // Interpolate D(nX,nZ)+U(nX+1,nZ)+R(nX+1+nZ+1)+(nX,nZ+1)
11 Vector3 vT(x0,vtxDst.position.y,z0);
12 VertexPositionNormalColor v0(vtxDst);
13 VertexPositionNormalColor v1(getVertex(nX+1,nZ));;
14 VertexPositionNormalColor v2(getVertex(nX+1,nZ+1));
15 VertexPositionNormalColor v3(getVertex(nX,nZ+1));
16 // Determine the distance from vT to each known vertex.
17 float d0= SimpleMath::Vector3::Distance(vT,v0.position);
18 float d1= SimpleMath::Vector3::Distance(vT,v1.position);
19 float d2= SimpleMath::Vector3::Distance(vT,v2.position);
20 float d3= SimpleMath::Vector3::Distance(vT,v3.position);
21 float dMax= std::max(d0,std::max(d1,std::max(d2,d3)));
22 // Bias each vertex so that the closer vertex has more influence.
23 Vector3 dv0= (1.0f-(d0/dMax))*v0.position;
24 Vector3 dn0= (1.0f-(d0/dMax))*v0.normal;
25 Vector3 dv1= (1.0f-(d1/dMax))*v1.position;
26 Vector3 dn1= (1.0f-(d1/dMax))*v1.normal;
27 Vector3 dv2= (1.0f-(d2/dMax))*v2.position;
28 Vector3 dn2= (1.0f-(d2/dMax))*v2.normal;
29 Vector3 dv3= (1.0f-(d3/dMax))*v3.position;
30 Vector3 dn3= (1.0f-(d3/dMax))*v3.normal;
31 float dSum= 4.0f - (d0/dMax + d1/dMax + d2/dMax + d3/dMax);
32 Vector3 pos= (dv0+dv1+dv2+dv3)/dSum;
33 Vector3 nml= (dn0+dn1+dn2+dn3)/dSum;
34 vtxDst.position= pos;
35 vtxDst.normal= nml;
36 }
37 }
I still don't know how to properly apply the normal the object's
transformation matrix. My math skillz need some serious help.
Math Books
I rented
3D Math Primer for Graphics and Game Development, 2nd Edition
for $20... I am waiting for this massive book to download...
I need some visual aids to help me understand what is going on. I
want to draw some spheres and lines between the various points, which
means creating a thin shape class to make it easier.
The new GameObj class makes it easier to separate the code for
drawing specific things from the overall game framework.
The declarations:
TEXT Game.h :
1 typedef struct RectF_s { float left,top,right,bottom; } RECTF;
2
3 class Game;
4
5 class GameObj {
6 public:
7 static HRESULT Create(GameObj *&pObj, const char *Type, Game *pGame, ID3D11DeviceContext *pDC);
8 static void Destroy(GameObj *&pObj);
9 virtual ~GameObj(void);
10 virtual void Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse);
11 virtual void Render(const XMMATRIX &view, const XMMATRIX &proj);
12 const Vector3 &GetPt(void);
13 protected:
14 GameObj(Game *pGame);
15 Game *pGame;
16 //Data
17 Vector3 pos; //Current position
18 Vector3 vel; //Current velocity
19 Vector3 dir; //Face: x=roll y=yaw z=pitch
20 Matrix xform; //Transform matrix
21 private:
22 };
23
24 class Game {
25 public:
26 const RECTF &GetArena(void) { return(rGrid); };
27 VertexPositionNormalColor &getVertex(int x0, int y0) { return(vecVertex.at(y0*tileCt+x0)); };
28 void getVertex(float x0, float y0, VertexPositionNormalColor &vtxDst);
29 private:
30 RECTF rGrid; //Grid dimensions
31 ...
32 GameObj *pPawn; //Glides across the surface.
33 };
The GameObj code:
TEXT GameObj.cpp :
1 /*************************************************************************/
2 /** GameObj.cpp: A simple game object. **/
3 /** (C)2017 nlited systems, chip doran **/
4 /*************************************************************************/
5 #include "pch.h"
6 #include <new>
7 #include <stdio.h>
8 #include <windows.h>
9 #include <d2d1_2.h>
10 #include <d2d1_1helper.h>
11 #include <dwrite_1.h>
12 #include <wrl/client.h>
13 #include "Game.h"
14
15 #pragma message(__FILE__": Optimizer disabled.")
16 #pragma optimize("",off)
17
18 using namespace DirectX;
19
20 #define Zero(O) memset(&O,0,sizeof(O))
21
22 #define SafeRelease(pObj) { if(pObj) pObj->Release(); pObj= 0; }
23
24 class ObjPawn: public GameObj {
25 public:
26 static HRESULT Create(GameObj *&pObj, Game *pGame, ID3D11DeviceContext *pDC);
27 ~ObjPawn(void);
28 virtual void Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse);
29 virtual void Render(const XMMATRIX &view, const XMMATRIX &proj);
30 protected:
31 private:
32 ObjPawn(Game *pGame);
33 HRESULT Create2(ID3D11DeviceContext *pDC);
34 //Data
35 float Hgt;
36 std::unique_ptr<PrimitiveBatch<VertexPositionNormalColor>> pBatch;
37 std::unique_ptr<GeometricPrimitive> pShape;
38 //Matrix mtrxPawn;
39 //Matrix mtrxShadow;
40 };
41
42 /*************************************************************************/
43 /** Base GameObj **/
44 /*************************************************************************/
45 GameObj::GameObj(Game *_pGame) {
46 pGame= _pGame;
47 }
48
49 GameObj::~GameObj(void) {
50 }
51
52 HRESULT GameObj::Create(GameObj *&pObj, const char *Type, Game *pGame, ID3D11DeviceContext *pDC) {
53 HRESULT Err= 0;
54 if(stricmp("PAWN",Type)==0) {
55 Err= ObjPawn::Create(pObj,pGame,pDC);
56 } else {
57 Err= ERROR_NOT_FOUND;
58 }
59 return(Err);
60 }
61
62 void GameObj::Destroy(GameObj *&pObj) {
63 if(pObj)
64 delete pObj;
65 pObj= 0;
66 }
67
68 void GameObj::Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse) {
69 }
70
71 void GameObj::Render(const XMMATRIX &view, const XMMATRIX &proj) {
72 }
73
74 /*************************************************************************/
75 /** Pawn **/
76 /*************************************************************************/
77 ObjPawn::ObjPawn(Game *_pGame):
78 GameObj(_pGame)
79 {
80 Hgt= 0.25f;
81 }
82
83 ObjPawn::~ObjPawn(void) {
84 }
85
86 HRESULT ObjPawn::Create(GameObj *&_pObj, Game *_pGame, ID3D11DeviceContext *pDC) {
87 HRESULT Err;
88 ObjPawn *pObj;
89 if(!(pObj= new ObjPawn(_pGame))) {
90 Err= Warn(ERROR_NO_MEM,"ObjPawn:Create: NoMem(%d)",sizeof(*pObj));
91 } else if(FAILED(Err= pObj->Create2(pDC))) {
92 delete pObj;
93 } else {
94 _pObj= pObj;
95 }
96 return(Err);
97 }
98
99 HRESULT ObjPawn::Create2(ID3D11DeviceContext *pDC) {
100 HRESULT Err= 0;
101 pBatch= std::make_unique<PrimitiveBatch<VertexPositionNormalColor>>(pDC);
102 pShape= GeometricPrimitive::CreateCylinder(pDC,Hgt,Hgt/10.0f);
103 pos= Vector3(0,0,0);
104 dir= vel= Vector3(0.1f,0,0.1f);
105 return(Err);
106 }
107
108 void ObjPawn::Update(float secTotal, float secElapsed, Keyboard *pKB, Mouse *pMouse) {
109 RECTF rArena;
110 rArena= pGame->GetArena();
111 xform= Matrix::Identity;
112 pos+= vel*secElapsed;
113 if(pos.x < rArena.left) {
114 pos.x= vel.x= (0.010f*(float)rand())/RAND_MAX + 0.10f;
115 } else if(pos.x >= rArena.right) {
116 vel.x= -((0.010f*(float)rand())/RAND_MAX + 0.10f);
117 pos.x= rArena.right + vel.x;
118 }
119 if(pos.z < rArena.top) {
120 pos.z= vel.z= (0.010f*(float)rand())/RAND_MAX + 0.10f;
121 } else if(pos.z >= rArena.bottom) {
122 vel.z= -((0.010f*(float)rand())/RAND_MAX + 0.10f);
123 pos.z= rArena.bottom + vel.z;
124 }
125 VertexPositionNormalColor vtxPawn;
126 //TODO: Interpolate vtxPawn with neighboring vertices.
127 pGame->getVertex(pos.x,pos.z,vtxPawn);
128 pos.y= vtxPawn.position.y; // + 0.125f;
129 Vector3 ptCenter= (Hgt/2.0f)*vtxPawn.normal;
130 ptCenter+= pos;
131 xform.Translation(ptCenter);
132 xform.Up(vtxPawn.normal);
133 //Plane plane(pos,vtxPawn.normal);
134 //mtrxShadow= mtrxPawn.CreateShadow(light,plane);
135 }
136
137 void ObjPawn::Render(const XMMATRIX &view, const XMMATRIX &proj) {
138 // Draw reference line through pos.
139 VertexPositionNormalColor v0(Vector3(pos.x,0,pos.z),Vector3::UnitX,Colors::Brown);
140 VertexPositionNormalColor v1(Vector3(pos.x,10.0f,pos.z),Vector3::UnitX,Colors::White);
141 pBatch->Begin();
142 pBatch->DrawLine(v0,v1);
143 pBatch->End();
144 pShape->Draw(xform,view,proj);
145 //TODO: Figure out how to draw the pawn's shadow.
146 //objPawn->Draw(mtrxShadow,view,mtrxProj);
147 }
148
149 //EOF: GAMEOBJ.CPP
The game code:
TEXT Game.cpp :
1 void Game::CreateDevice2(void) {
2 ...
3 GameObj::Create(pPawn,"PAWN",this,d3dContext.Get());
4 }
5
6 void Game::Update2(float totalTime, float elapsedTime) {
7 if(!doPause) {
8 ...
9 pPawn->Update(totalTime,elapsedTime,keyboard.get(),mouse.get());
10 }
11 }
12
13 void Game::RenderObjects(XMMATRIX &view) {
14 // Reset the DC effects before rendering each object.
15 dxEffect->Apply(d3dContext.Get());
16 objSun->Draw(mtrxSun,view,mtrxProj);
17 dxEffect->Apply(d3dContext.Get());
18 pPawn->Render(view,mtrxProj);
19 }
Adding the reference line took longer than expected. It seemed all
that was required was creating a new vertex batch processor, setting
two vertices, and drawing the line. But nothing appeared. It wasn't
until I happened to look up and saw the line originating from the sun
that I realized what was happening. There is a note in the DirectXTK
docs: "When Draw is called, it will set the states needed to render
with the effect. Existing state is not save or restored. For
efficiency, it simply sets the state it requires to render and assumes
that any subsequent rendering will overwrite state that it needs."
I need to reset the view transform, which is part of the Effects,
by re-applying the effect to the D3D device context before rendering
each game object.
It is too hard to understand exactly what is happening while the
pawn is zooming around. I added a single-step mode to make it easier
to stop the action when something strange happens. I press SCROLL to
pause, then + or - to advance or rewind one frame.
The single-stepping was very helpful. Now I know the problem is in
the Y interpolation, the normal vector looks fine. The pawns moves
across the tile at roughly the same altitude until it crosses the next
vertex then drops down.
WebV7 (C)2018 nlited | Rendered by tikope in 3293.485ms | 216.73.216.77