• 11
• 14
• 19
• 27
• 9

# getting the tank to rotate to the terrain

This topic is 4051 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

My tank follows the hight of the terrain by using the getHeight() and applying the result to the pos.y value of the tank, but what i need to do is rotate the tank so it follows the terrain more real. How do i do this , here is my code for the terrain terrain.cpp
//****************************
// Private consts
//****************************
const DWORD Terrain::TerrainVertex::FVF = D3DFVF_XYZ | D3DFVF_TEX1;
//****************************
// Constructor
//****************************
Terrain::Terrain(IDirect3DDevice9* device,
std::string heightmapFileName,
int numVertsPerRow,
int numVertsPerCol,
int cellSpacing,
float heightScale)
{
_device         = device;
_numVertsPerRow = numVertsPerRow;
_numVertsPerCol = numVertsPerCol;
_cellSpacing    = cellSpacing;

_numCellsPerRow = _numVertsPerRow - 1;
_numCellsPerCol = _numVertsPerCol - 1;

_width = _numCellsPerRow * _cellSpacing;
_depth = _numCellsPerCol * _cellSpacing;

_numVertices  = _numVertsPerRow * _numVertsPerCol;
_numTriangles = _numCellsPerRow * _numCellsPerCol * 2;

_heightScale = heightScale;

fCellSpacing = cellSpacing;

{
::MessageBox(0, "readRawFile - FAILED", 0, 0);
::PostQuitMessage(0);
}

// scale heights
for(int i = 0; i < _heightmap.size(); i++)
_heightmap *= heightScale;

// compute the vertices's
if( !computeVertices() )
{
::MessageBox(0, "computeVertices - FAILED", 0, 0);
::PostQuitMessage(0);
}

// compute the indices
if( !computeIndices() )
{
::MessageBox(0, "computeIndices - FAILED", 0, 0);
::PostQuitMessage(0);
}
}
//****************************
// Destructor
//****************************
Terrain::~Terrain()
{
d3d::Release<IDirect3DVertexBuffer9*>(_vb);
d3d::Release<IDirect3DIndexBuffer9*>(_ib);
d3d::Release<IDirect3DTexture9*>(_tex);
}
//****************************
// Return the height at a given point
//****************************
int Terrain::getHeightmapEntry(int row, int col)
{
return _heightmap[row * _numVertsPerRow + col];
}
//****************************
// Set the height at a given point
//****************************
void Terrain::setHeightmapEntry(int row, int col, int value)
{
_heightmap[row * _numVertsPerRow + col] = value;
}
//****************************
// Initialize the map parameters
//****************************
bool Terrain::computeVertices()
{
HRESULT hr = 0;

hr = _device->CreateVertexBuffer(
_numVertices * sizeof(TerrainVertex),
D3DUSAGE_WRITEONLY,
TerrainVertex::FVF,
D3DPOOL_MANAGED,
&_vb,
0);

if(FAILED(hr))
return false;

// coordinates to start generating vertices at
int startX = -_width / 2;
int startZ =  _depth / 2;

// coordinates to end generating vertices at
int endX =  _width / 2;
int endZ = -_depth / 2;

// compute the increment size of the texture coordinates
// from one vertex to the next.
float uCoordIncrementSize = 1.0f / (float)_numCellsPerRow;
float vCoordIncrementSize = 1.0f / (float)_numCellsPerCol;

TerrainVertex* v = 0;
_vb->Lock(0, 0, (void**)&v, 0);

int i = 0;
for(int z = startZ; z >= endZ; z -= _cellSpacing)
{
int j = 0;
for(int x = startX; x <= endX; x += _cellSpacing)
{
// compute the correct index into the vertex buffer and heightmap
// based on where we are in the nested loop.
int index = i * _numVertsPerRow + j;

v[index] = TerrainVertex(
(float)x,
(float)_heightmap[index],
(float)z,
(float)j * uCoordIncrementSize,
(float)i * vCoordIncrementSize);

j++; // next column
}
i++; // next row
}

_vb->Unlock();

return true;
}

bool Terrain::computeIndices()
{
HRESULT hr = 0;

hr = _device->CreateIndexBuffer(
_numTriangles * 3 * sizeof(WORD), // 3 indices per triangle
D3DUSAGE_WRITEONLY,
D3DFMT_INDEX16,
D3DPOOL_MANAGED,
&_ib,
0);

if(FAILED(hr))
return false;

WORD* indices = 0;
_ib->Lock(0, 0, (void**)&indices, 0);

// index to start of a group of 6 indices that describe the
// two triangles that make up a quad
int baseIndex = 0;

// loop through and compute the triangles of each quad
for(int i = 0; i < _numCellsPerCol; i++)
{
for(int j = 0; j < _numCellsPerRow; j++)
{
indices[baseIndex]     =   i   * _numVertsPerRow + j;
indices[baseIndex + 1] =   i   * _numVertsPerRow + j + 1;
indices[baseIndex + 2] = (i+1) * _numVertsPerRow + j;

indices[baseIndex + 3] = (i+1) * _numVertsPerRow + j;
indices[baseIndex + 4] =   i   * _numVertsPerRow + j + 1;
indices[baseIndex + 5] = (i+1) * _numVertsPerRow + j + 1;

baseIndex += 6;
}
}

_ib->Unlock();

return true;
}

{
HRESULT hr = 0;

hr = D3DXCreateTextureFromFile(
_device,
fileName.c_str(),
&_tex);

if(FAILED(hr))
return false;

return true;
}

bool Terrain::genTexture(D3DXVECTOR3* directionToLight)
{
// Method fills the top surface of a texture procedurally.  Then
// lights the top surface.  Finally, it fills the other mipmap
// surfaces based on the top surface data using D3DXFilterTexture.

HRESULT hr = 0;

// texel for each quad cell
int texWidth  = _numCellsPerRow;
int texHeight = _numCellsPerCol;

// create an empty texture
hr = D3DXCreateTexture(
_device,
texWidth, texHeight,
0, // create a complete mipmap chain
0, // usage
D3DFMT_X8R8G8B8,// 32 bit XRGB format
D3DPOOL_MANAGED, &_tex);

if(FAILED(hr))
return false;

D3DSURFACE_DESC textureDesc;
_tex->GetLevelDesc(0 /*level*/, &textureDesc);

// make sure we got the requested format because our code
// that fills the texture is hard coded to a 32 bit pixel depth.
if( textureDesc.Format != D3DFMT_X8R8G8B8 )
return false;

D3DLOCKED_RECT lockedRect;
_tex->LockRect(0/*lock top surface*/, &lockedRect,
0 /* lock entire tex*/, 0/*flags*/);

DWORD* imageData = (DWORD*)lockedRect.pBits;
for(int i = 0; i < texHeight; i++)
{
for(int j = 0; j < texWidth; j++)
{
D3DXCOLOR c;

// get height of upper left vertex of quad.
float height = (float)getHeightmapEntry(i, j) / _heightScale;

if( (height) < 42.5f ) 		 c = d3d::BEACH_SAND;
else if( (height) < 85.0f )	 c = d3d::LIGHT_YELLOW_GREEN;
else if( (height) < 127.5f ) c = d3d::PUREGREEN;
else if( (height) < 170.0f ) c = d3d::DARK_YELLOW_GREEN;
else if( (height) < 212.5f ) c = d3d::DARKBROWN;
else	                     c = d3d::WHITE;

// fill locked data, note we divide the pitch by four because the
// pitch is given in bytes and there are 4 bytes per DWORD.
imageData[i * lockedRect.Pitch / 4 + j] = (D3DCOLOR)c;
}
}

_tex->UnlockRect(0);

if(!lightTerrain(directionToLight))
{
::MessageBox(0, "lightTerrain() - FAILED", 0, 0);
return false;
}

hr = D3DXFilterTexture(
_tex,
0, // default palette
0, // use top level as source level
D3DX_DEFAULT); // default filter

if(FAILED(hr))
{
::MessageBox(0, "D3DXFilterTexture() - FAILED", 0, 0);
return false;
}

return true;
}

bool Terrain::lightTerrain(D3DXVECTOR3* directionToLight)
{
HRESULT hr = 0;

D3DSURFACE_DESC textureDesc;
_tex->GetLevelDesc(0 /*level*/, &textureDesc);

// make sure we got the requested format because our code that fills the
// texture is hard coded to a 32 bit pixel depth.
if( textureDesc.Format != D3DFMT_X8R8G8B8 )
return false;

D3DLOCKED_RECT lockedRect;
_tex->LockRect(
0,          // lock top surface level in mipmap chain
&lockedRect,// pointer to receive locked data
0,          // lock entire texture image
0);         // no lock flags specified

DWORD* imageData = (DWORD*)lockedRect.pBits;
for(unsigned int i = 0; i < textureDesc.Height; i++)
{
for(unsigned int j = 0; j < textureDesc.Width; j++)
{
// index into texture, note we use the pitch and divide by
// four since the pitch is given in bytes and there are
// 4 bytes per DWORD.
int index = i * lockedRect.Pitch / 4 + j;

// get current color of quad
D3DXCOLOR c( imageData[index] );

imageData[index] = (D3DCOLOR)c;
}
}

_tex->UnlockRect(0);

return true;
}

float Terrain::computeShade(int cellRow, int cellCol, D3DXVECTOR3* directionToLight)
{
// get heights of three vertices on the quad
float heightA = getHeightmapEntry(cellRow,   cellCol);
float heightB = getHeightmapEntry(cellRow,   cellCol+1);
float heightC = getHeightmapEntry(cellRow+1, cellCol);

// build two vectors on the quad
D3DXVECTOR3 u(_cellSpacing, heightB - heightA, 0.0f);
D3DXVECTOR3 v(0.0f,         heightC - heightA, -_cellSpacing);

// find the normal by taking the cross product of two
D3DXVECTOR3 n;
D3DXVec3Cross(&n, &u, &v);
D3DXVec3Normalize(&n, &n);

float cosine = D3DXVec3Dot(&n, directionToLight);

if(cosine < 0.0f)
cosine = 0.0f;

return cosine;
}

{
// Restriction: RAW file dimensions must be >= to the
// dimensions of the terrain.  That is a 128x128 RAW file
// can only be used with a terrain constructed with at most
// 128x128 vertices's.

// A height for each vertex
std::vector<BYTE> in( _numVertices );

std::ifstream inFile(fileName.c_str(), std::ios_base::binary);

if( inFile == 0 )
return false;

(char*)&in[0], // buffer
in.size());// number of bytes to read into buffer

inFile.close();

// copy BYTE vector to int vector
_heightmap.resize( _numVertices );

for(int i = 0; i < in.size(); i++)
_heightmap = in;

return true;
}

float Terrain::getHeight(float x, float z)
{
// Translate on xz-plane by the transformation that takes
// the terrain START point to the origin.
x = ((float)_width / 2.0f) + x;
z = ((float)_depth / 2.0f) - z;

// Scale down by the transformation that makes the
// cellspacing equal to one.  This is given by
// 1 / cellspacing since; cellspacing * 1 / cellspacing = 1.
x /= (float)_cellSpacing;
z /= (float)_cellSpacing;

// From now on, we will interpret our positive z-axis as
// going in the 'down' direction, rather than the 'up' direction.
// This allows to extract the row and column simply by 'flooring'
// x and z:

float col = ::floorf(x);
float row = ::floorf(z);

// get the heights of the quad we're in:
//
//  A   B
//  *---*
//  | / |
//  *---*
//  C   D

float A = getHeightmapEntry(row,   col);
float B = getHeightmapEntry(row,   col+1);
float C = getHeightmapEntry(row+1, col);
float D = getHeightmapEntry(row+1, col+1);

//
// Find the triangle we are in:
//

// Translate by the transformation that takes the upper-left
// corner of the cell we are in to the origin.  Recall that our
// cellspacing was nomalized to 1.  Thus we have a unit square
// at the origin of our +x -> 'right' and +z -> 'down' system.
float dx = x - col;
float dz = z - row;

// Note the below compuations of u and v are unneccessary, we really
// only need the height, but we compute the entire vector to emphasis
// the books discussion.
float height = 0.0f;
if(dz < 1.0f - dx)  // upper triangle ABC
{
float uy = B - A; // A->B
float vy = C - A; // A->C

// Linearly interpolate on each vector.  The height is the vertex
// height the vectors u and v originate from {A}, plus the heights
// found by interpolating on each vector u and v.
height = A + d3d::Lerp(0.0f, uy, dx) + d3d::Lerp(0.0f, vy, dz);
}
else // lower triangle DCB
{
float uy = C - D; // D->C
float vy = B - D; // D->B

// Linearly interpolate on each vector.  The height is the vertex
// height the vectors u and v originate from {D}, plus the heights
// found by interpolating on each vector u and v.
height = D + d3d::Lerp(0.0f, uy, 1.0f - dx) + d3d::Lerp(0.0f, vy, 1.0f - dz);
}

return height;
}

bool Terrain::draw(D3DXMATRIX* world, bool drawTris)
{
HRESULT hr = 0;

if( _device )
{
_device->SetTransform(D3DTS_WORLD, world);

_device->SetStreamSource(0, _vb, 0, sizeof(TerrainVertex));
_device->SetFVF(TerrainVertex::FVF);
_device->SetIndices(_ib);

_device->SetTexture(0, _tex);

// turn off lighting since we're lighting it ourselves
_device->SetRenderState(D3DRS_LIGHTING, false);

hr =_device->DrawIndexedPrimitive(
D3DPT_TRIANGLELIST,
0,
0,
_numVertices,
0,
_numTriangles);

_device->SetRenderState(D3DRS_LIGHTING, true);

if( drawTris )
{
_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
hr =_device->DrawIndexedPrimitive(
D3DPT_TRIANGLELIST,
0,
0,
_numVertices,
0,
_numTriangles);

_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
}

if(FAILED(hr))
return false;
}

return true;
}


[Edited by - Prog101 on January 31, 2007 2:44:42 PM]

##### Share on other sites
Im assuming you want to thank to tilt and lean so it stays flat against the ground when the ground is not level.
So 'tilt' is more descriptive than 'rotate'. Right?

Easy:
Find the Normal of the ground at the point the tank is located.
Tilt the tank to match the Normal.

##### Share on other sites
Yep yep, use the normals from the terrain mesh to align the tank to the terrain. If your mesh distance is to far for the tank you may want to average the normals for the 4 corners of the quad...

##### Share on other sites
ok so with the code i have now for the terrain how do i create a function to find the normals , it already has a function to find the height of the vertices of the tanks positon
float height = TheTerrain->getHeight( PlayerPos.x, PlayerPos.z );
??????

[Edited by - Prog101 on February 13, 2007 2:37:45 PM]

##### Share on other sites
Generate the normals at the beginning for each vertex.
Then, you can smooth interpolate them if you are in the middle of a terrain cell.

##### Share on other sites
to clarify, in addition to each point on the heightmap having a height, give it a normal.

use the same kind of bi-linear interpolation math you use to find height, on the normals to find normal for any given position.

of course, you need to generate those normals in the first place
there are a few ways of doing it,

one is to find the normals of the 4 triangles surrounding the point and average them

what I like to do though, is just compare the heightmap positions to the north,south, east and west of the point in question, and use the difference in their heights to get two slopes, which i then combine for the normal

##### Share on other sites
I did something like this I while back. I didn't interpolate but the previous posts should give you the info on how to do it.

triangles have three points: A, B, C

Calculate two edge vectors: E1 = B-A, E2 = C-A

The normal N equals E1 cross E2. Normalize it (make it length 1 using /= ) However the normal might face up or down. You need it to face up. Make a vector pointing straight up called U. If U dot N is positive the normal faces up, otherwise multiply by -1.

Now cross the normal and the up vector to get a rotation axis R. Dot the normal and the up vector to get the sin of the angle between them. Now rotate. Have to go so I can't double check this or add more explanation.

##### Share on other sites
I got a great solution for you but whenever I post it the Apache server times out ffs (it aint a big post tho!) PM me if u want it...

[Edited by - Aph3x on February 2, 2007 8:51:20 AM]

##### Share on other sites
Right below i have the code so that the 4 corners of the Tank are calculated and the wheel at each corner is calculated.

as you can see below the green balls represent the wheels at each corner on the ground.

//Set the offsets for the wheels so be at the corners of the tank	LFWheelOffset = D3DXVECTOR3(-TANK_WIDTH/2,-TANK_HEIGHT/2,TANK_LENGTH/2);	LRWheelOffset = D3DXVECTOR3(-TANK_WIDTH/2,-TANK_HEIGHT/2,-TANK_LENGTH/2);	RFWheelOffset = D3DXVECTOR3(TANK_WIDTH/2,-TANK_HEIGHT/2,TANK_LENGTH/2);	RRWheelOffset = D3DXVECTOR3(TANK_WIDTH/2,-TANK_HEIGHT/2,-TANK_LENGTH/2);	GroundVectorX = D3DXVECTOR3(1,0,0);	GroundVectorY = D3DXVECTOR3(0,1,0);	GroundVectorZ = D3DXVECTOR3(0,0,1);/********************************************************************* Update the wheels of the tank, These are the 4 wheels representing the corners of the tank********************************************************************/void Tank::update_Wheels(){	//========================= Get the world positions of the Wheels on th 4 corners of the tank	//===== Right Front Wheel	D3DXMatrixTranslation(&RFWheelMat,RFWheelOffset.x,RFWheelOffset.y,RFWheelOffset.z);	D3DXMatrixMultiply(&RFWheelMat,&RFWheelMat,&transMatrix);	frontWheel_Right.vPostion.x = RFWheelMat.m[3][0];	frontWheel_Right.vPostion.y = RFWheelMat.m[3][1];	frontWheel_Right.vPostion.z = RFWheelMat.m[3][2];	//===== Left Front Wheel	D3DXMatrixTranslation(&LFWheelMat,LFWheelOffset.x,LFWheelOffset.y,LFWheelOffset.z);	D3DXMatrixMultiply(&LFWheelMat,&LFWheelMat,&transMatrix);	frontWheel_Left.vPostion.x = LFWheelMat.m[3][0];	frontWheel_Left.vPostion.y = LFWheelMat.m[3][1];	frontWheel_Left.vPostion.z = LFWheelMat.m[3][2];	//===== Left Rear Wheel	D3DXMatrixTranslation(&LRWheelMat,LRWheelOffset.x,LRWheelOffset.y,LRWheelOffset.z);	D3DXMatrixMultiply(&LRWheelMat,&LRWheelMat,&transMatrix);	rearWheel_Left.vPostion.x = LRWheelMat.m[3][0];	rearWheel_Left.vPostion.y = LRWheelMat.m[3][1];	rearWheel_Left.vPostion.z = LRWheelMat.m[3][2];	//===== Right Rear Wheel	D3DXMatrixTranslation(&RRWheelMat,RRWheelOffset.x,RRWheelOffset.y,RRWheelOffset.z);	D3DXMatrixMultiply(&RRWheelMat,&RRWheelMat,&transMatrix);	rearWheel_Right.vPostion.x = RRWheelMat.m[3][0];	rearWheel_Right.vPostion.y = RRWheelMat.m[3][1];	rearWheel_Right.vPostion.z = RRWheelMat.m[3][2];		//========================= Set the heights for all the wheels on the terrain.	Height = TheTerrain->getHeight(frontWheel_Right.vPostion.x,frontWheel_Right.vPostion.y);	frontWheel_Right.vPostion.y = Height;	Height = TheTerrain->getHeight(frontWheel_Left.vPostion.x,frontWheel_Left.vPostion.y);	frontWheel_Left.vPostion.y = Height;	Height = TheTerrain->getHeight(rearWheel_Right.vPostion.x,rearWheel_Right.vPostion.y);	rearWheel_Right.vPostion.y = Height;	Height = TheTerrain->getHeight(rearWheel_Left.vPostion.x,rearWheel_Left.vPostion.y);	rearWheel_Left.vPostion.y = Height;	//========= Create our 2 vectors 	D3DXVec3Subtract(&vVec1 ,&frontWheel_Right.vPostion,&rearWheel_Left.vPostion);	D3DXVec3Subtract(&vVec2 ,&frontWheel_Left.vPostion,&frontWheel_Right.vPostion);	//========= Get the cross product that is perpendicular to the 2 vectors	D3DXVec3Cross(&vNormal,&vVec2,&vVec1);	//========= Normalize the Normal to get a unit vector	D3DXVec3Normalize(&vNormal,&vNormal);	//========= Now get the Angle to tilt by getting the inverse cos of the dot product	AngleTiltX = acos(D3DXVec3Dot(&GroundVectorX,&vNormal));	AngleTiltY = acos(D3DXVec3Dot(&GroundVectorY,&vNormal));	AngleTiltZ = acos(D3DXVec3Dot(&GroundVectorZ,&vNormal));}/********************************************************************* render********************************************************************/void Tank::render(LPDIRECT3DDEVICE9 device){	D3DXMATRIX rotMatrix;	// create the translation matrix	D3DXMatrixTranslation(&transMatrix, positionVector.x, positionVector.y, positionVector.z);	// create the rotation matrix for the object	D3DXMatrixRotationYawPitchRoll(&rotMatrix,TurningAngle+AngleTiltY,AngleTiltX,AngleTiltZ);	// Scale the Tank by the size amount	D3DXMatrixScaling(&scaleMatrix, size, size, size);	// Multiply the translation matrix by the rotation matrix	// The resulting matrix is stored in the transMatrix	D3DXMatrixMultiply(&transMatrix, &rotMatrix, &transMatrix);	// Multiply the translation matrix by the scale	D3DXMatrixMultiply(&transMatrix, &scaleMatrix, &transMatrix);	// Transform the object into world space	device->SetTransform(D3DTS_WORLD, &transMatrix);}

[Edited by - Prog101 on February 15, 2007 11:10:03 AM]