Vector subscript out of range

Started by
1 comment, last by Garra 12 years, 8 months ago
I'm working with a FPS game using terrain from one of Frank D. Luna's books. The problem is I'm having a slight issue with the getHeight function of the terrain. It works perfect when I use it to set the initial y position of the enemies/targets, and when I'm updating the height of the camera to walk over the terrain. However, I call it again to loop through the enemies and create a simple evade based on the enemies health. The code for this is below. Without the call to getHeight in this section of the code everything works fine (other than the targets will no longer walk on the terrain since it doesn't have the terrains y pos). However with the getHeight call in the game it runs and everything but after shooting a few targets i get a (vector subscript out of range) error. Any help would be appreciated.

T is an array of structures representing targets.



//loops through the 10 targets
for (int i = 0; i < 10; i++)
{
//if the targets health is less than 50
if (T.health < 50)
{
//calculate a direction and do a simple evade
T.direction = currentPos - T.pos;
D3DXVec3Normalize(&T.normPos, &T.direction);
T.pos.x += T.normPos.x * -4.0f;
T.pos.z += T.normPos.z * -4.0f;
T.pos.y = TheTerrain->getHeight(T.pos.x, T.pos.z) + 20;

}
}
}


terrain.h

#ifndef __terrainH__
#define __terrainH__

#include "base.h"
#include <string>
#include <vector>

class Terrain
{
public:
Terrain(
IDirect3DDevice9* device,
std::string heightmapFileName,
int numVertsPerRow,
int numVertsPerCol,
int cellSpacing, // space between cells
float heightScale);

~Terrain();

int getHeightmapEntry(int row, int col);
void setHeightmapEntry(int row, int col, int value);

float getHeight(float x, float z);

bool loadTexture(std::string fileName);
bool genTexture(D3DXVECTOR3* directionToLight);
bool draw(D3DXMATRIX* world, bool drawTris);

private:
IDirect3DDevice9* _device;
IDirect3DTexture9* _tex;
IDirect3DVertexBuffer9* _vb;
IDirect3DIndexBuffer9* _ib;

int _numVertsPerRow;
int _numVertsPerCol;
int _cellSpacing;

int _numCellsPerRow;
int _numCellsPerCol;
int _width;
int _depth;
int _numVertices;
int _numTriangles;

float _heightScale;

std::vector<int> _heightmap;

// helper methods
bool readRawFile(std::string fileName);
bool computeVertices();
bool computeIndices();
bool lightTerrain(D3DXVECTOR3* directionToLight);
float computeShade(int cellRow, int cellCol, D3DXVECTOR3* directionToLight);

struct TerrainVertex
{
TerrainVertex(){}
TerrainVertex(float x, float y, float z, float u, float v)
{
_x = x; _y = y; _z = z; _u = u; _v = v;
}
float _x, _y, _z;
float _u, _v;

static const DWORD FVF;
};
};

#endif // __terrainH__




terrain.cpp

#include "terrain.h"
#include <fstream>
#include <cmath>

const DWORD Terrain::TerrainVertex::FVF = D3DFVF_XYZ | D3DFVF_TEX1;

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;

// load heightmap
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
if( !computeVertices() )
{
::MessageBox(0, "computeVertices - FAILED", 0, 0);
::PostQuitMessage(0);
}

// compute the indices
if( !computeIndices() )
{
::MessageBox(0, "computeIndices - FAILED", 0, 0);
::PostQuitMessage(0);
}
}

Terrain::~Terrain()
{
basewin::Release<IDirect3DVertexBuffer9*>(_vb);
basewin::Release<IDirect3DIndexBuffer9*>(_ib);
basewin::Release<IDirect3DTexture9*>(_tex);
}

int Terrain::getHeightmapEntry(int row, int col)
{
return _heightmap[row * _numVertsPerRow + col];
}

void Terrain::setHeightmapEntry(int row, int col, int value)
{
_heightmap[row * _numVertsPerRow + col] = value;
}

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 = basewin::BEACH_SAND;
else if( (height) < 85.0f ) c = basewin::LIGHT_YELLOW_GREEN;
else if( (height) < 127.5f ) c = basewin::PUREGREEN;
else if( (height) < 170.0f ) c = basewin::DARK_YELLOW_GREEN;
else if( (height) < 212.5f ) c = basewin::DARKBROWN;
else c = basewin::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(int i = 0; i < textureDesc.Height; i++)
{
for(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.

// 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 + basewin::Lerp(0.0f, uy, dx) + basewin::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 + basewin::Lerp(0.0f, uy, 1.0f - dx) + basewin::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;
}


Advertisement


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




I think it's this bit. Your height map is probably trying to access a row/column out of range. More than likely on the row+1s and col+1s.
ty for the response, i believe i figured this out. The issue was a stupid mistake on my part, but your post did help me figure it out. Basically it was out of range because when i shoot a target and it dies, it will stop drawing. However it's position will still be moving. So when it goes off the terrain it will be out of range, and therefore create the error. Fixed it quickly once i realized what I left out.

This topic is closed to new replies.

Advertisement