Jump to content
  • Advertisement
Sign in to follow this  
Prog101

getting the tank to rotate to the terrain

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

If you intended to correct an error in the post then please contact us.

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;

	// load height map
	if( !readRawFile(heightmapFileName) )
	{
		::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;

			// next quad
			baseIndex += 6;
		}
	}

	_ib->Unlock();

	return true;
}

bool Terrain::loadTexture(std::string fileName)
{
	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] );

			// shade current quad
			c *= computeShade(i, j, directionToLight);;

			// save shaded color
			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
	// vectors on the quad.
	D3DXVECTOR3 n;
	D3DXVec3Cross(&n, &u, &v);
	D3DXVec3Normalize(&n, &n);

	float cosine = D3DXVec3Dot(&n, directionToLight);

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

	return cosine;
}

bool Terrain::readRawFile(std::string fileName)
{
	// 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;

	inFile.read(
		(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 this post


Link to post
Share on other sites
Advertisement
Guest Anonymous Poster
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Guest Anonymous Poster
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!