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.
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.
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 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.
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.
Interpolation:
//Returns nearest vertex
void Game::getVertex(float x0, float z0, VertexPositionNormalColor &vtxDst) {
int nX= (int)(tileCt*((x0-rGrid.left)/(rGrid.right - rGrid.left)));
nX= (nX<0) ? 0 : (nX>=tileCt) ? tileCt-1 : nX;
int nZ= (int)(tileCt*((z0-rGrid.top)/(rGrid.bottom - rGrid.top)));
nZ= (nZ<0) ? 0 : (nZ>=tileCt) ? tileCt-1 : nZ;
vtxDst= getVertex(nX,nZ);
if(nX+1 < tileCt && nZ+1 < tileCt) {
// (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;
}
}
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.
Game.cpp:
void Game::CreateDevice2(void) {
...
GameObj::Create(pPawn,"PAWN",this,d3dContext.Get());
}
void Game::Update2(float totalTime, float elapsedTime) {
if(!doPause) {
...
pPawn->Update(totalTime,elapsedTime,keyboard.get(),mouse.get());
}
}
void Game::RenderObjects(XMMATRIX &view) {
// Reset the DC effects before rendering each object.
dxEffect->Apply(d3dContext.Get());
objSun->Draw(mtrxSun,view,mtrxProj);
dxEffect->Apply(d3dContext.Get());
pPawn->Render(view,mtrxProj);
}
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 122.449ms | 3.15.237.229