Either moved from terrain directory, or an inital revision, depending on the file. Whole bunch of changes related to props and animation.

This was SVN commit r306.
This commit is contained in:
notpete 2004-05-29 20:56:24 +00:00
parent 4d826bb5f2
commit 7fb944a1e1
46 changed files with 4865 additions and 0 deletions

146
source/graphics/Camera.cpp Executable file
View File

@ -0,0 +1,146 @@
//***********************************************************
//
// Name: Camera.Cpp
// Last Update: 24/2/02
// Author: Poya Manouchehri
//
// Description: CCamera holds a view and a projection matrix.
// It also has a frustum which can be used to
// cull objects for rendering.
//
//***********************************************************
#include "Camera.h"
CCamera::CCamera ()
{
// set viewport to something anything should handle, but should be initialised
// to window size before use
m_ViewPort.m_X = 0;
m_ViewPort.m_Y = 0;
m_ViewPort.m_Width = 800;
m_ViewPort.m_Height = 600;
}
CCamera::~CCamera ()
{
}
void CCamera::SetProjection (float nearp, float farp, float fov)
{
float h, w, Q;
m_NearPlane = nearp;
m_FarPlane = farp;
m_FOV = fov;
float Aspect = (float)m_ViewPort.m_Width/(float)m_ViewPort.m_Height;
w = 1/tanf (fov*0.5f*Aspect);
h = 1/tanf (fov*0.5f);
Q = m_FarPlane / (m_FarPlane - m_NearPlane);
m_ProjMat.SetZero ();
m_ProjMat._11 = w;
m_ProjMat._22 = h;
m_ProjMat._33 = (m_FarPlane+m_NearPlane)/(m_FarPlane-m_NearPlane);;
m_ProjMat._34 = -2*m_FarPlane*m_NearPlane/(m_FarPlane-m_NearPlane);
m_ProjMat._43 = 1.0f;
}
//Updates the frustum planes. Should be called
//everytime the view or projection matrices are
//altered.
void CCamera::UpdateFrustum ()
{
CMatrix3D MatFinal;
CMatrix3D MatView;
m_Orientation.GetInverse(MatView);
MatFinal = m_ProjMat * MatView;
//get the RIGHT plane
m_ViewFrustum.SetNumPlanes (6);
m_ViewFrustum.m_aPlanes[0].m_Norm.X = MatFinal._41-MatFinal._11;
m_ViewFrustum.m_aPlanes[0].m_Norm.Y = MatFinal._42-MatFinal._12;
m_ViewFrustum.m_aPlanes[0].m_Norm.Z = MatFinal._43-MatFinal._13;
m_ViewFrustum.m_aPlanes[0].m_Dist = MatFinal._44-MatFinal._14;
//get the LEFT plane
m_ViewFrustum.m_aPlanes[1].m_Norm.X = MatFinal._41+MatFinal._11;
m_ViewFrustum.m_aPlanes[1].m_Norm.Y = MatFinal._42+MatFinal._12;
m_ViewFrustum.m_aPlanes[1].m_Norm.Z = MatFinal._43+MatFinal._13;
m_ViewFrustum.m_aPlanes[1].m_Dist = MatFinal._44+MatFinal._14;
//get the BOTTOM plane
m_ViewFrustum.m_aPlanes[2].m_Norm.X = MatFinal._41+MatFinal._21;
m_ViewFrustum.m_aPlanes[2].m_Norm.Y = MatFinal._42+MatFinal._22;
m_ViewFrustum.m_aPlanes[2].m_Norm.Z = MatFinal._43+MatFinal._23;
m_ViewFrustum.m_aPlanes[2].m_Dist = MatFinal._44+MatFinal._24;
//get the TOP plane
m_ViewFrustum.m_aPlanes[3].m_Norm.X = MatFinal._41-MatFinal._21;
m_ViewFrustum.m_aPlanes[3].m_Norm.Y = MatFinal._42-MatFinal._22;
m_ViewFrustum.m_aPlanes[3].m_Norm.Z = MatFinal._43-MatFinal._23;
m_ViewFrustum.m_aPlanes[3].m_Dist = MatFinal._44-MatFinal._24;
//get the FAR plane
m_ViewFrustum.m_aPlanes[4].m_Norm.X = MatFinal._41-MatFinal._31;
m_ViewFrustum.m_aPlanes[4].m_Norm.Y = MatFinal._42-MatFinal._32;
m_ViewFrustum.m_aPlanes[4].m_Norm.Z = MatFinal._43-MatFinal._33;
m_ViewFrustum.m_aPlanes[4].m_Dist = MatFinal._44-MatFinal._34;
//get the NEAR plane
m_ViewFrustum.m_aPlanes[5].m_Norm.X = MatFinal._41+MatFinal._31;
m_ViewFrustum.m_aPlanes[5].m_Norm.Y = MatFinal._42+MatFinal._32;
m_ViewFrustum.m_aPlanes[5].m_Norm.Z = MatFinal._43+MatFinal._33;
m_ViewFrustum.m_aPlanes[5].m_Dist = MatFinal._44+MatFinal._34;
}
void CCamera::SetViewPort (SViewPort *viewport)
{
m_ViewPort.m_X = viewport->m_X;
m_ViewPort.m_Y = viewport->m_Y;
m_ViewPort.m_Width = viewport->m_Width;
m_ViewPort.m_Height = viewport->m_Height;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GetCameraPlanePoints: return four points in camera space at given distance from camera
void CCamera::GetCameraPlanePoints(float dist,CVector3D pts[4]) const
{
float aspect=float(m_ViewPort.m_Width)/float(m_ViewPort.m_Height);
float x=dist*float(tan(GetFOV()*aspect*0.5));
float y=dist*float(tan(GetFOV()*0.5));
pts[0].X=-x;
pts[0].Y=-y;
pts[0].Z=dist;
pts[1].X=x;
pts[1].Y=-y;
pts[1].Z=dist;
pts[2].X=x;
pts[2].Y=y;
pts[2].Z=dist;
pts[3].X=-x;
pts[3].Y=y;
pts[3].Z=dist;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GetFrustumPoints: calculate and return the position of the 8 points of the frustum in world space
void CCamera::GetFrustumPoints(CVector3D pts[8]) const
{
// get camera space points for near and far planes
CVector3D cpts[8];
GetCameraPlanePoints(m_NearPlane,pts);
GetCameraPlanePoints(m_FarPlane,pts+4);
// transform to world space
for (int i=0;i<8;i++) {
m_Orientation.Transform(cpts[i],pts[i]);
}
}

78
source/graphics/Camera.h Executable file
View File

@ -0,0 +1,78 @@
//***********************************************************
//
// Name: Camera.H
// Last Update: 24/2/02
// Author: Poya Manouchehri
//
// Description: CCamera holds a view and a projection matrix.
// It also has a frustum which can be used to
// cull objects for rendering.
//
//***********************************************************
#ifndef CAMERA_H
#define CAMERA_H
#include "Frustum.h"
#include "Matrix3D.h"
//view port
struct SViewPort
{
unsigned int m_X;
unsigned int m_Y;
unsigned int m_Width;
unsigned int m_Height;
};
class CCamera
{
public:
CCamera ();
~CCamera ();
//Methods for projection
void SetProjection (CMatrix3D *proj) { m_ProjMat = *proj; }
void SetProjection (float nearp, float farp, float fov);
CMatrix3D GetProjection () { return m_ProjMat; }
//Updates the frustum planes. Should be called
//everytime the view or projection matrices are
//altered.
void UpdateFrustum ();
CFrustum GetFustum () { return m_ViewFrustum; }
void SetViewPort (SViewPort *viewport);
SViewPort GetViewPort () { return m_ViewPort; }
//getters
float GetNearPlane() const { return m_NearPlane; }
float GetFarPlane() const { return m_FarPlane; }
float GetFOV() const { return m_FOV; }
// calculate and return the position of the 8 points of the frustum in world space
void GetFrustumPoints(CVector3D pts[8]) const;
// return four points in camera space at given distance from camera
void GetCameraPlanePoints(float dist,CVector3D pts[4]) const;
public:
//This is the orientation matrix. The inverse of this
//is the view matrix
CMatrix3D m_Orientation;
private:
//keep the projection matrix private
//so we can't fiddle with it.
CMatrix3D m_ProjMat;
float m_NearPlane;
float m_FarPlane;
float m_FOV;
SViewPort m_ViewPort;
CFrustum m_ViewFrustum;
};
#endif

30
source/graphics/Color.h Executable file
View File

@ -0,0 +1,30 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: Color.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _COLOR_H
#define _COLOR_H
#include "Vector3D.h"
#include "Vector4D.h"
// simple defines for 3 and 4 component floating point colors - just map to
// corresponding vector types
typedef CVector3D RGBColor;
typedef CVector4D RGBAColor;
// SColor4ub: structure for packed RGBA colors
struct SColor4ub
{
u8 R;
u8 G;
u8 B;
u8 A;
};
#endif

144
source/graphics/Frustum.cpp Executable file
View File

@ -0,0 +1,144 @@
//***********************************************************
//
// Name: Frustum.Cpp
// Last Update: 24/2/02
// Author: Poya Manouchehri
//
// Description: CFrustum is a collection of planes which define
// a viewing space. Usually associated with the
// camera, there are 6 planes which define the
// view pyramid. But we allow more planes per
// frustum which maybe used for portal rendering,
// where a portal may have 3 or more edges.
//
//***********************************************************
#include "Frustum.h"
CFrustum::CFrustum ()
{
m_NumPlanes = 0;
}
CFrustum::~CFrustum ()
{
}
void CFrustum::SetNumPlanes (int num)
{
m_NumPlanes = num;
//clip it
if (m_NumPlanes >= MAX_NUM_FRUSTUM_PLANES)
m_NumPlanes = MAX_NUM_FRUSTUM_PLANES-1;
else if (m_NumPlanes < 0)
m_NumPlanes = 0;
}
bool CFrustum::IsPointVisible (const CVector3D &point) const
{
PLANESIDE Side;
for (int i=0; i<m_NumPlanes; i++)
{
Side = m_aPlanes[i].ClassifyPoint (point);
if (Side == PS_BACK)
return false;
}
return true;
}
bool CFrustum::IsSphereVisible (const CVector3D &center, float radius) const
{
for (int i=0; i<m_NumPlanes; i++)
{
float Dist = m_aPlanes[i].DistanceToPlane (center);
//is it behind the plane
if (Dist < 0)
{
//if non of it falls in front its outside the
//frustum
if (-Dist > radius)
return false;
}
}
return true;
}
bool CFrustum::IsBoxVisible (const CVector3D &position,const CBound &bounds) const
{
//basically for every plane we calculate the furthust point
//in the box to that plane. If that point is beyond the plane
//then the box is not visible
CVector3D FarPoint;
PLANESIDE Side;
CVector3D Min = position+bounds[0];
CVector3D Max = position+bounds[1];
for (int i=0; i<m_NumPlanes; i++)
{
if (m_aPlanes[i].m_Norm.X > 0.0f)
{
if (m_aPlanes[i].m_Norm.Y > 0.0f)
{
if (m_aPlanes[i].m_Norm.Z > 0.0f)
{
FarPoint.X = Max.X; FarPoint.Y = Max.Y; FarPoint.Z = Max.Z;
}
else
{
FarPoint.X = Max.X; FarPoint.Y = Max.Y; FarPoint.Z = Min.Z;
}
}
else
{
if (m_aPlanes[i].m_Norm.Z > 0.0f)
{
FarPoint.X = Max.X; FarPoint.Y = Min.Y; FarPoint.Z = Max.Z;
}
else
{
FarPoint.X = Max.X; FarPoint.Y = Min.Y; FarPoint.Z = Min.Z;
}
}
}
else
{
if (m_aPlanes[i].m_Norm.Y > 0.0f)
{
if (m_aPlanes[i].m_Norm.Z > 0.0f)
{
FarPoint.X = Min.X; FarPoint.Y = Max.Y; FarPoint.Z = Max.Z;
}
else
{
FarPoint.X = Min.X; FarPoint.Y = Max.Y; FarPoint.Z = Min.Z;
}
}
else
{
if (m_aPlanes[i].m_Norm.Z > 0.0f)
{
FarPoint.X = Min.X; FarPoint.Y = Min.Y; FarPoint.Z = Max.Z;
}
else
{
FarPoint.X = Min.X; FarPoint.Y = Min.Y; FarPoint.Z = Min.Z;
}
}
}
Side = m_aPlanes[i].ClassifyPoint (FarPoint);
if (Side == PS_BACK)
return false;
}
return true;
}

51
source/graphics/Frustum.h Executable file
View File

@ -0,0 +1,51 @@
//***********************************************************
//
// Name: Frustum.H
// Last Update: 24/2/02
// Author: Poya Manouchehri
//
// Description: CFrustum is a collection of planes which define
// a viewing space. Usually associated with the
// camera, there are 6 planes which define the
// view pyramid. But we allow more planes per
// frustum which maybe used for portal rendering,
// where a portal may have 3 or more edges.
//
//***********************************************************
#ifndef FRUSTUM_H
#define FRUSTUM_H
#include "Plane.h"
#include "Bound.h"
//10 planes should be enough
#define MAX_NUM_FRUSTUM_PLANES (10)
class CFrustum
{
public:
CFrustum ();
~CFrustum ();
//Set the number of planes to use for
//calculations. This is clipped to
//[0,MAX_NUM_FRUSTUM_PLANES]
void SetNumPlanes (int num);
//The following methods return true if the shape is
//partially or completely in front of the frustum planes
bool IsPointVisible (const CVector3D &point) const;
bool IsSphereVisible (const CVector3D &center, float radius) const;
bool IsBoxVisible (const CVector3D &position,const CBound &bounds) const;
public:
//make the planes public for ease of use
CPlane m_aPlanes[MAX_NUM_FRUSTUM_PLANES];
private:
int m_NumPlanes;
};
#endif

177
source/graphics/HFTracer.cpp Executable file
View File

@ -0,0 +1,177 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: HFTracer.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "HFTracer.h"
#include "Terrain.h"
#include "Bound.h"
#include "Vector3D.h"
extern CTerrain g_Terrain;
///////////////////////////////////////////////////////////////////////////////
// CHFTracer constructor
CHFTracer::CHFTracer(const u16* hf,u32 mapsize,float cellsize,float heightscale)
: m_Heightfield(hf), m_MapSize(mapsize), m_CellSize(cellsize),
m_HeightScale(heightscale)
{
}
///////////////////////////////////////////////////////////////////////////////
// RayTriIntersect: intersect a ray with triangle defined by vertices
// v0,v1,v2; return true if ray hits triangle at distance less than dist,
// or false otherwise
bool CHFTracer::RayTriIntersect(const CVector3D& v0,const CVector3D& v1,const CVector3D& v2,
const CVector3D& origin,const CVector3D& dir,float& dist) const
{
const float EPSILON=0.00001f;
// calculate edge vectors
CVector3D edge0=v1-v0;
CVector3D edge1=v2-v0;
// begin calculating determinant - also used to calculate U parameter
CVector3D pvec=dir.Cross(edge1);
// if determinant is near zero, ray lies in plane of triangle
float det = edge0.Dot(pvec);
if (fabs(det)<EPSILON)
return false;
float inv_det = 1.0f/det;
// calculate vector from vert0 to ray origin
CVector3D tvec=origin-v0;
// calculate U parameter, test bounds
float u=tvec.Dot(pvec)*inv_det;
if (u<-0.01f || u>1.01f)
return false;
// prepare to test V parameter
CVector3D qvec=tvec.Cross(edge0);
// calculate V parameter and test bounds
float v=dir.Dot(qvec)*inv_det;
if (v<0.0f || u+v>1.0f)
return false;
// calculate distance to intersection point from ray origin
float d=edge1.Dot(qvec)*inv_det;
if (d>=0 && d<dist) {
dist=d;
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// CellIntersect: test if ray intersects either of the triangles in the given
// cell - return hit result, and distance to hit, if hit occurred
bool CHFTracer::CellIntersect(int cx,int cz,CVector3D& origin,CVector3D& dir,float& dist) const
{
bool res=false;
// get vertices for this cell
CVector3D vpos[4];
g_Terrain.CalcPosition(cx,cz,vpos[0]);
g_Terrain.CalcPosition(cx+1,cz,vpos[1]);
g_Terrain.CalcPosition(cx+1,cz+1,vpos[2]);
g_Terrain.CalcPosition(cx,cz+1,vpos[3]);
dist=1.0e30f;
if (RayTriIntersect(vpos[0],vpos[1],vpos[2],origin,dir,dist)) {
res=true;
}
if (RayTriIntersect(vpos[0],vpos[2],vpos[3],origin,dir,dist)) {
res=true;
}
return res;
}
///////////////////////////////////////////////////////////////////////////////
// RayIntersect: intersect ray with this heightfield; return true if
// intersection occurs (and fill in grid coordinates of intersection), or false
// otherwise
bool CHFTracer::RayIntersect(CVector3D& origin,CVector3D& dir,int& x,int& z,CVector3D& ipt) const
{
// intersect first against bounding box
CBound bound;
bound[0]=CVector3D(0,0,0);
bound[1]=CVector3D(m_MapSize*m_CellSize,65535*m_HeightScale,m_MapSize*m_CellSize);
float tmin,tmax;
if (!bound.RayIntersect(origin,dir,tmin,tmax)) {
// ray missed world bounds; no intersection
return false;
}
// project origin onto grid, if necessary, to get starting point for traversal
CVector3D traversalPt;
if (tmin>0) {
traversalPt=origin+dir*tmin;
} else {
traversalPt=origin;
}
// setup traversal variables
int sx=dir.X<0 ? -1 : 1;
int sz=dir.Z<0 ? -1 : 1;
float invCellSize=1.0f/float(m_CellSize);
float fcx=traversalPt.X*invCellSize;
int cx=int(fcx);
float fcz=traversalPt.Z*invCellSize;
int cz=int(fcz);
float invdx=float(1.0/fabs(dir.X));
float invdz=float(1.0/fabs(dir.Z));
float dist;
do {
// test current cell
if (cx>=0 && cx<int(m_MapSize-1) && cz>=0 && cz<int(m_MapSize-1)) {
if (CellIntersect(cx,cz,origin,dir,dist)) {
x=cx;
z=cz;
ipt=origin+dir*dist;
return true;
}
}
// get coords of current cell
fcx=traversalPt.X*invCellSize;
fcz=traversalPt.Z*invCellSize;
// get distance to next cell in x,z
float dx=(sx==-1) ? fcx-float(cx) : 1-(fcx-float(cx));
dx*=invdx;
float dz=(sz==-1) ? fcz-float(cz) : 1-(fcz-float(cz));
dz*=invdz;
// advance ..
float dist;
if (dx<dz) {
cx+=sx;
dist=dx;
} else {
cz+=sz;
dist=dz;
}
traversalPt+=dir*dist;
} while (traversalPt.Y>=0);
// fell off end of heightmap with no intersection; return a miss
return false;
}

48
source/graphics/HFTracer.h Executable file
View File

@ -0,0 +1,48 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: HFTracer.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _HFTRACER_H
#define _HFTRACER_H
class CVector3D;
#include "res/res.h"
///////////////////////////////////////////////////////////////////////////////
// CHFTracer: a class for determining ray intersections with a heightfield
class CHFTracer
{
public:
// constructor; setup data
CHFTracer(const u16* hf,u32 mapsize,float cellsize,float heightscale);
// intersect ray with this heightfield; return true if intersection
// occurs (and fill in grid coordinates and point of intersection), or false otherwise
bool RayIntersect(CVector3D& origin,CVector3D& dir,int& x,int& z,CVector3D& ipt) const;
private:
// intersect a ray with triangle defined by vertices
// v0,v1,v2; return true if ray hits triangle at distance less than dist,
// or false otherwise
bool RayTriIntersect(const CVector3D& v0,const CVector3D& v1,const CVector3D& v2,
const CVector3D& origin,const CVector3D& dir,float& dist) const;
// test if ray intersects either of the triangles in the given
bool CellIntersect(int cx,int cz,CVector3D& origin,CVector3D& dir,float& dist) const;
// the heightfield were tracing
const u16* m_Heightfield;
// size of the heightfield
u32 m_MapSize;
// cell size - size of each cell in x and z
float m_CellSize;
// vertical scale - size of each cell in y
float m_HeightScale;
};
#endif

47
source/graphics/LightEnv.h Executable file
View File

@ -0,0 +1,47 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: LightEnv.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
// Description: class describing current lighting environment -
// at the minute, this is only sunlight and ambient light
// parameters; will be extended to handle dynamic lights at some
// later date
//
///////////////////////////////////////////////////////////////////////////////
#ifndef __LIGHTENV_H
#define __LIGHTENV_H
#include "Color.h"
#include "Vector3D.h"
///////////////////////////////////////////////////////////////////////////////
// CLightEnv: description of a lighting environment - contains all the
// necessary parameters for representation of the lighting within a scenario
class CLightEnv
{
public:
RGBColor m_SunColor;
float m_Elevation;
float m_Rotation;
RGBColor m_TerrainAmbientColor;
RGBColor m_UnitsAmbientColor;
// get sun direction from a rotation and elevation; defined such that:
// 0 rotation = (0,0,1)
// PI/2 rotation = (-1,0,0)
// 0 elevation = (0,0,0)
// PI/2 elevation = (0,-1,0)
void GetSunDirection(CVector3D& lightdir) const {
lightdir.Y=-float(sin(m_Elevation));
float scale=1+lightdir.Y;
lightdir.X=scale*float(sin(m_Rotation));
lightdir.Z=scale*float(cos(m_Rotation));
lightdir.Normalize();
}
};
#endif

35
source/graphics/MapIO.h Executable file
View File

@ -0,0 +1,35 @@
#ifndef _MAPIO_H
#define _MAPIO_H
class CMapIO
{
public:
// current file version given to saved maps
enum { FILE_VERSION = 2 };
// supported file read version - file with version less than this will be reject
enum { FILE_READ_VERSION = 1 };
#pragma pack(push, 1)
// description of a tile for I/O purposes
struct STileDesc {
// index into the texture array of first texture on tile
u16 m_Tex1Index;
// index into the texture array of second texture; (0xffff) if none
u16 m_Tex2Index;
// priority
u32 m_Priority;
};
// description of an object for I/O purposes
struct SObjectDesc {
// index into the object array
u16 m_ObjectIndex;
// transformation matrix
float m_Transform[16];
};
#pragma pack(pop)
};
#endif

186
source/graphics/MapReader.cpp Executable file
View File

@ -0,0 +1,186 @@
// switch off warnings before including stl files
#pragma warning(disable : 4786) // identifier truncated to 255 chars
#include "Types.h"
#include "MapReader.h"
#include "UnitManager.h"
#include "ObjectManager.h"
#include "BaseEntity.h"
#include "BaseEntityCollection.h"
#include "EntityManager.h"
#include "Model.h"
#include "Terrain.h"
#include "TextureManager.h"
extern CTerrain g_Terrain;
extern CLightEnv g_LightEnv;
#include <set>
#include <stdio.h>
// CMapReader constructor: nothing to do at the minute
CMapReader::CMapReader()
{
}
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void CMapReader::LoadMap(const char* filename)
{
CFileUnpacker unpacker;
unpacker.Read(filename,"PSMP");
// check version
if (unpacker.GetVersion()<FILE_READ_VERSION) {
throw CFileUnpacker::CFileVersionError();
}
// unpack the data
UnpackMap(unpacker);
// finally, apply data to the world
ApplyData(unpacker);
}
// UnpackMap: unpack the given data from the raw data stream into local variables
void CMapReader::UnpackMap(CFileUnpacker& unpacker)
{
// now unpack everything into local data
UnpackTerrain(unpacker);
UnpackObjects(unpacker);
if (unpacker.GetVersion()>=2) {
UnpackLightEnv(unpacker);
}
}
// UnpackLightEnv: unpack lighting parameters from input stream
void CMapReader::UnpackLightEnv(CFileUnpacker& unpacker)
{
unpacker.UnpackRaw(&m_LightEnv.m_SunColor,sizeof(m_LightEnv.m_SunColor));
unpacker.UnpackRaw(&m_LightEnv.m_Elevation,sizeof(m_LightEnv.m_Elevation));
unpacker.UnpackRaw(&m_LightEnv.m_Rotation,sizeof(m_LightEnv.m_Rotation));
unpacker.UnpackRaw(&m_LightEnv.m_TerrainAmbientColor,sizeof(m_LightEnv.m_TerrainAmbientColor));
unpacker.UnpackRaw(&m_LightEnv.m_UnitsAmbientColor,sizeof(m_LightEnv.m_UnitsAmbientColor));
}
// UnpackObjects: unpack world objects from input stream
void CMapReader::UnpackObjects(CFileUnpacker& unpacker)
{
// unpack object types
u32 numObjTypes;
unpacker.UnpackRaw(&numObjTypes,sizeof(numObjTypes));
m_ObjectTypes.resize(numObjTypes);
for (uint i=0;i<numObjTypes;i++) {
CStr objname;
unpacker.UnpackString(objname);
CObjectEntry* object=g_ObjMan.FindObject((const char*) objname);
m_ObjectTypes[i]=object;
}
// unpack object data
u32 numObjects;
unpacker.UnpackRaw(&numObjects,sizeof(numObjects));
m_Objects.resize(numObjects);
unpacker.UnpackRaw(&m_Objects[0],sizeof(SObjectDesc)*numObjects);
}
// UnpackTerrain: unpack the terrain from the end of the input data stream
// - data: map size, heightmap, list of textures used by map, texture tile assignments
void CMapReader::UnpackTerrain(CFileUnpacker& unpacker)
{
// unpack map size
unpacker.UnpackRaw(&m_MapSize,sizeof(m_MapSize));
// unpack heightmap
u32 verticesPerSide=m_MapSize*PATCH_SIZE+1;
m_Heightmap.resize(SQR(verticesPerSide));
unpacker.UnpackRaw(&m_Heightmap[0],SQR(verticesPerSide)*sizeof(u16));
// unpack texture names; find handle for each texture
u32 numTextures;
unpacker.UnpackRaw(&numTextures,sizeof(numTextures));
m_TerrainTextures.reserve(numTextures);
for (uint i=0;i<numTextures;i++) {
CStr texturename;
unpacker.UnpackString(texturename);
Handle handle;
CTextureEntry* texentry=g_TexMan.FindTexture(texturename);
if (!texentry) {
// ack; mismatch between texture datasets?
handle=0;
} else {
handle=texentry->m_Handle;
}
m_TerrainTextures.push_back(handle);
}
// unpack tile data
u32 tilesPerSide=m_MapSize*PATCH_SIZE;
m_Tiles.resize(SQR(tilesPerSide));
unpacker.UnpackRaw(&m_Tiles[0],sizeof(STileDesc)*m_Tiles.size());
}
// ApplyData: take all the input data, and rebuild the scene from it
void CMapReader::ApplyData(CFileUnpacker& unpacker)
{
// initialise the terrain
g_Terrain.Initialize(m_MapSize,&m_Heightmap[0]);
// setup the textures on the minipatches
STileDesc* tileptr=&m_Tiles[0];
for (u32 j=0;j<m_MapSize;j++) {
for (u32 i=0;i<m_MapSize;i++) {
for (u32 m=0;m<PATCH_SIZE;m++) {
for (u32 k=0;k<PATCH_SIZE;k++) {
CMiniPatch& mp=g_Terrain.GetPatch(i,j)->m_MiniPatches[m][k];
mp.Tex1=m_TerrainTextures[tileptr->m_Tex1Index];
mp.Tex1Priority=tileptr->m_Priority;
tileptr++;
}
}
}
}
// empty out existing units
g_UnitMan.DeleteAll();
// add new objects
for (u32 i=0;i<m_Objects.size();i++) {
CObjectEntry* objentry=m_ObjectTypes[m_Objects[i].m_ObjectIndex];
if (objentry && objentry->m_Model) {
// Hijack the standard actor instantiation for actors that correspond to entities.
// Not an ideal solution; we'll have to figure out a map format that can define entities seperately or somesuch.
CBaseEntity* templateObject = g_EntityTemplateCollection.getTemplateByActor( objentry );
if( templateObject )
{
CVector3D orient = ((CMatrix3D*)m_Objects[i].m_Transform)->GetIn();
CVector3D position = ((CMatrix3D*)m_Objects[i].m_Transform)->GetTranslation();
g_EntityManager.create( templateObject, position, atan2( orient.X, orient.Z ) );
}
else
{
CUnit* unit=new CUnit(objentry,objentry->m_Model->Clone());
CMatrix3D transform;
memcpy(&transform._11,m_Objects[i].m_Transform,sizeof(float)*16);
unit->GetModel()->SetTransform(transform);
// add this unit to list of units stored in unit manager
g_UnitMan.AddUnit(unit);
}
}
}
if (unpacker.GetVersion()>=2) {
// copy over the lighting parameters
g_LightEnv=m_LightEnv;
}
}

48
source/graphics/MapReader.h Executable file
View File

@ -0,0 +1,48 @@
#ifndef _MAPREADER_H
#define _MAPREADER_H
#include "MapIO.h"
#include "CStr.h"
#include "LightEnv.h"
#include "FileUnpacker.h"
class CObjectEntry;
class CMapReader : public CMapIO
{
public:
// constructor
CMapReader();
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void LoadMap(const char* filename);
private:
// UnpackMap: unpack the given data from the raw data stream into local variables
void UnpackMap(CFileUnpacker& unpacker);
// UnpackTerrain: unpack the terrain from the input stream
void UnpackTerrain(CFileUnpacker& unpacker);
// UnpackObjects: unpack world objects from the input stream
void UnpackObjects(CFileUnpacker& unpacker);
// UnpackObjects: unpack lighting parameters from the input stream
void UnpackLightEnv(CFileUnpacker& unpacker);
// ApplyData: take all the input data, and rebuild the scene from it
void ApplyData(CFileUnpacker& unpacker);
// size of map
u32 m_MapSize;
// heightmap for map
std::vector<u16> m_Heightmap;
// list of terrain textures used by map
std::vector<Handle> m_TerrainTextures;
// tile descriptions for each tile
std::vector<STileDesc> m_Tiles;
// list of object types used by map
std::vector<CObjectEntry*> m_ObjectTypes;
// descriptions for each objects
std::vector<SObjectDesc> m_Objects;
// lightenv stored in file
CLightEnv m_LightEnv;
};
#endif

226
source/graphics/MapWriter.cpp Executable file
View File

@ -0,0 +1,226 @@
// switch off warnings before including stl files
#pragma warning(disable : 4786) // identifier truncated to 255 chars
#include "Types.h"
#include "MapWriter.h"
#include "UnitManager.h"
#include "ObjectManager.h"
#include "Model.h"
#include "Terrain.h"
#include "LightEnv.h"
#include "TextureManager.h"
extern CTerrain g_Terrain;
extern CLightEnv g_LightEnv;
#include <set>
#include <stdio.h>
///////////////////////////////////////////////////////////////////////////////////////////////////
// CMapWriter constructor: nothing to do at the minute
CMapWriter::CMapWriter()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// SaveMap: try to save the current map to the given file
void CMapWriter::SaveMap(const char* filename)
{
CFilePacker packer;
// build necessary data
PackMap(packer);
// write it out
packer.Write(filename,FILE_VERSION,"PSMP");
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// GetHandleIndex: return the index of the given handle in the given list; or 0xffff if
// handle isn't in list
static u16 GetHandleIndex(const Handle handle,const std::vector<Handle>& handles)
{
for (uint i=0;i<handles.size();i++) {
if (handles[i]==handle) {
return i;
}
}
return 0xffff;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// GetObjectIndex: return the index of the given object in the given list; or 0xffff if
// object isn't in list
static u16 GetObjectIndex(const CObjectEntry* object,const std::vector<CObjectEntry*>& objects)
{
for (uint i=0;i<objects.size();i++) {
if (objects[i]==object) {
return i;
}
}
return 0xffff;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// EnumTerrainTextures: build lists of textures used by map, and tile descriptions for
// each tile on the terrain
void CMapWriter::EnumTerrainTextures(std::vector<CStr>& textures,
std::vector<STileDesc>& tiles)
{
// the list of all handles in use
std::vector<Handle> handles;
// resize tile array to required size
tiles.resize(SQR(g_Terrain.GetVerticesPerSide()-1));
STileDesc* tileptr=&tiles[0];
// now iterate through all the tiles
u32 mapsize=g_Terrain.GetPatchesPerSide();
for (u32 j=0;j<mapsize;j++) {
for (u32 i=0;i<mapsize;i++) {
for (u32 m=0;m<PATCH_SIZE;m++) {
for (u32 k=0;k<PATCH_SIZE;k++) {
CMiniPatch& mp=g_Terrain.GetPatch(i,j)->m_MiniPatches[m][k];
u16 index=u16(GetHandleIndex(mp.Tex1,handles));
if (index==0xffff) {
index=handles.size();
handles.push_back(mp.Tex1);
}
tileptr->m_Tex1Index=index;
tileptr->m_Tex2Index=0xffff;
tileptr->m_Priority=mp.Tex1Priority;
tileptr++;
}
}
}
}
// now find the texture names for each handle
for (uint i=0;i<handles.size();i++) {
CStr texturename;
CTextureEntry* texentry=g_TexMan.FindTexture(handles[i]);
if (!texentry) {
// uh-oh, this shouldn't happen; set texturename to empty string
texturename="";
} else {
texturename=texentry->m_Name;
}
textures.push_back(texturename);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// EnumObjects: build lists of object types used by map, and object descriptions for
// each object in the world
void CMapWriter::EnumObjects(std::vector<CStr>& objectTypes,std::vector<SObjectDesc>& objects)
{
// the list of all object entries in use
std::vector<CObjectEntry*> objectsInUse;
// resize object array to required size
const std::vector<CUnit*>& units=g_UnitMan.GetUnits();
objects.resize(units.size());
SObjectDesc* objptr=&objects[0];
// now iterate through all the units
for (u32 j=0;j<units.size();j++) {
CUnit* unit=units[j];
u16 index=u16(GetObjectIndex(unit->GetObject(),objectsInUse));
if (index==0xffff) {
index=objectsInUse.size();
objectsInUse.push_back(unit->GetObject());
}
objptr->m_ObjectIndex=index;
memcpy(objptr->m_Transform,&unit->GetModel()->GetTransform()._11,sizeof(float)*16);
objptr++;
}
// now build outgoing objectTypes array
for (uint i=0;i<objectsInUse.size();i++) {
objectTypes.push_back(objectsInUse[i]->m_Name);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// PackMap: pack the current world into a raw data stream
void CMapWriter::PackMap(CFilePacker& packer)
{
// now pack everything up
PackTerrain(packer);
PackObjects(packer);
PackLightEnv(packer);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// PackLightEnv: pack lighting parameters onto the end of the output data stream
void CMapWriter::PackLightEnv(CFilePacker& packer)
{
packer.PackRaw(&g_LightEnv.m_SunColor,sizeof(g_LightEnv.m_SunColor));
packer.PackRaw(&g_LightEnv.m_Elevation,sizeof(g_LightEnv.m_Elevation));
packer.PackRaw(&g_LightEnv.m_Rotation,sizeof(g_LightEnv.m_Rotation));
packer.PackRaw(&g_LightEnv.m_TerrainAmbientColor,sizeof(g_LightEnv.m_TerrainAmbientColor));
packer.PackRaw(&g_LightEnv.m_UnitsAmbientColor,sizeof(g_LightEnv.m_UnitsAmbientColor));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// PackObjects: pack world objects onto the end of the output data stream
// - data: list of objects types used by map, list of object descriptions
void CMapWriter::PackObjects(CFilePacker& packer)
{
// the list of object types used by map
std::vector<CStr> objectTypes;
// descriptions of each object
std::vector<SObjectDesc> objects;
// build lists by scanning through the world
EnumObjects(objectTypes,objects);
// pack object types
u32 numObjTypes=objectTypes.size();
packer.PackRaw(&numObjTypes,sizeof(numObjTypes));
for (uint i=0;i<numObjTypes;i++) {
packer.PackString(objectTypes[i]);
}
// pack object data
u32 numObjects=objects.size();
packer.PackRaw(&numObjects,sizeof(numObjects));
packer.PackRaw(&objects[0],sizeof(SObjectDesc)*numObjects);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// PackTerrain: pack the terrain onto the end of the output data stream
// - data: map size, heightmap, list of textures used by map, texture tile assignments
void CMapWriter::PackTerrain(CFilePacker& packer)
{
// pack map size
u32 mapsize=g_Terrain.GetPatchesPerSide();
packer.PackRaw(&mapsize,sizeof(mapsize));
// pack heightmap
packer.PackRaw(g_Terrain.GetHeightMap(),sizeof(u16)*SQR(g_Terrain.GetVerticesPerSide()));
// the list of textures used by map
std::vector<CStr> terrainTextures;
// descriptions of each tile
std::vector<STileDesc> tiles;
// build lists by scanning through the terrain
EnumTerrainTextures(terrainTextures,tiles);
// pack texture names
u32 numTextures=terrainTextures.size();
packer.PackRaw(&numTextures,sizeof(numTextures));
for (uint i=0;i<numTextures;i++) {
packer.PackString(terrainTextures[i]);
}
// pack tile data
packer.PackRaw(&tiles[0],sizeof(STileDesc)*tiles.size());
}

36
source/graphics/MapWriter.h Executable file
View File

@ -0,0 +1,36 @@
#ifndef _MAPWRITER_H
#define _MAPWRITER_H
#include <vector>
#include "MapIO.h"
#include "CStr.h"
#include "FilePacker.h"
class CMapWriter : public CMapIO
{
public:
// constructor
CMapWriter();
// SaveMap: try to save the current map to the given file
void SaveMap(const char* filename);
private:
// PackMap: pack the current world into a raw data stream
void PackMap(CFilePacker& packer);
// PackTerrain: pack the terrain onto the end of the data stream
void PackTerrain(CFilePacker& packer);
// PackObjects: pack world objects onto the end of the output data stream
void PackObjects(CFilePacker& packer);
// PackLightEnv: pack lighting parameters onto the end of the output data stream
void PackLightEnv(CFilePacker& packer);
// EnumTerrainTextures: build lists of textures used by map, and indices into this list
// for each tile on the terrain
void EnumTerrainTextures(std::vector<CStr>& textures,std::vector<STileDesc>& tileIndices);
// EnumObjects: build lists of object types used by map, and object descriptions for
// each object in the world
void EnumObjects(std::vector<CStr>& objectTypes,std::vector<SObjectDesc>& objects);
};
#endif

34
source/graphics/MiniPatch.cpp Executable file
View File

@ -0,0 +1,34 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: MiniPatch.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "MiniPatch.h"
#include "Patch.h"
///////////////////////////////////////////////////////////////////////////////
// Constructor
CMiniPatch::CMiniPatch() : Tex1(0), Tex1Priority(0), m_Parent(0)
{
}
///////////////////////////////////////////////////////////////////////////////
// Destructor
CMiniPatch::~CMiniPatch()
{
}
///////////////////////////////////////////////////////////////////////////////
// GetTileIndex: get the index of this tile in the root terrain object;
// on return, parameters x,y contain index in [0,MapSize)
void CMiniPatch::GetTileIndex(u32& x,u32& z)
{
u32 tindex=this-&m_Parent->m_MiniPatches[0][0];
x=(m_Parent->m_X*16)+tindex%16;
z=(m_Parent->m_Z*16)+tindex/16;
}

39
source/graphics/MiniPatch.h Executable file
View File

@ -0,0 +1,39 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: MiniPatch.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _MINIPATCH_H
#define _MINIPATCH_H
#include "res/res.h"
class CPatch;
///////////////////////////////////////////////////////////////////////////////
// CMiniPatch: definition of a single terrain tile
class CMiniPatch
{
public:
// constructor
CMiniPatch();
// destructor
~CMiniPatch();
// get the index of this tile in the root terrain object; x,y in [0,MapSize)
void GetTileIndex(u32& x,u32& z);
public:
// texture applied to tile
Handle Tex1;
// 'priority' of the texture - determines drawing order of terrain textures
int Tex1Priority;
// parent patch
CPatch* m_Parent;
};
#endif

336
source/graphics/Model.cpp Executable file
View File

@ -0,0 +1,336 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: Model.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "Model.h"
#include "Quaternion.h"
#include "Bound.h"
#include "SkeletonAnim.h"
#include "SkeletonAnimDef.h"
#include "SkeletonAnimManager.h"
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor
CModel::CModel()
: m_pModelDef(0), m_Anim(0), m_AnimTime(0),
m_BoneMatrices(0), m_InvBoneMatrices(0), m_BoneMatricesValid(false)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Destructor
CModel::~CModel()
{
ReleaseData();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ReleaseData: delete anything allocated by the model
void CModel::ReleaseData()
{
delete[] m_BoneMatrices;
delete[] m_InvBoneMatrices;
for (size_t i=0;i<m_Props.size();i++) {
delete m_Props[i].m_Model;
}
m_Props.clear();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// InitModel: setup model from given geometry
bool CModel::InitModel(CModelDef* modeldef)
{
// clean up any existing data first
ReleaseData();
m_pModelDef = modeldef;
u32 numBones=modeldef->GetNumBones();
if (numBones>0) {
// allocate matrices for bone transformations
m_BoneMatrices=new CMatrix3D[numBones];
m_InvBoneMatrices=new CMatrix3D[numBones];
// store default pose until animation assigned
CBoneState* defpose=modeldef->GetBones();
for (uint i=0;i<numBones;i++) {
CMatrix3D& m=m_BoneMatrices[i];
m.SetIdentity();
m.Rotate(defpose[i].m_Rotation);
m.Translate(defpose[i].m_Translation);
m.GetInverse(m_InvBoneMatrices[i]);
}
m_BoneMatricesValid=true;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SkinPoint: skin the given point using the given blend and bonestate data
static CVector3D SkinPoint(const CVector3D& pos,const SVertexBlend& blend,
const CBoneState* bonestates)
{
CVector3D result(0,0,0);
for (int i=0;i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff;i++) {
CMatrix3D m;
m.SetIdentity();
m.Rotate(bonestates[blend.m_Bone[i]].m_Rotation);
m.Translate(bonestates[blend.m_Bone[i]].m_Translation);
CVector3D tmp=m.Transform(pos);
result+=tmp*blend.m_Weight[i];
}
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SkinPoint: skin the given point using the given blend and matrix data
static CVector3D SkinPoint(const CVector3D& pos,const SVertexBlend& blend,
const CMatrix3D* bonestates)
{
CVector3D result(0,0,0);
for (int i=0;i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff;i++) {
const CMatrix3D& m=bonestates[blend.m_Bone[i]];
CVector3D tmp=m.Transform(pos);
result+=tmp*blend.m_Weight[i];
}
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CalcBound: calculate the world space bounds of this model
void CModel::CalcBounds()
{
m_ObjectBounds.Transform(GetTransform(),m_Bounds);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CalcObjectBounds: calculate object space bounds of this model, based solely on vertex positions
void CModel::CalcObjectBounds()
{
m_ObjectBounds.SetEmpty();
int numverts=m_pModelDef->GetNumVertices();
SModelVertex* verts=m_pModelDef->GetVertices();
for (int i=0;i<numverts;i++) {
m_ObjectBounds+=verts[i].m_Coords;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CalcAnimatedObjectBound: calculate bounds encompassing all vertex positions for given animation
void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
{
result.SetEmpty();
CSkeletonAnim dummyanim;
dummyanim.m_AnimDef=anim;
if (!SetAnimation(&dummyanim)) return;
int numverts=m_pModelDef->GetNumVertices();
SModelVertex* verts=m_pModelDef->GetVertices();
// iterate through every frame of the animation
for (uint j=0;j<anim->GetNumFrames();j++) {
// extend bounds by vertex positions at the frame
for (int i=0;i<numverts;i++) {
CVector3D tmp=SkinPoint(verts[i].m_Coords,verts[i].m_Blend,GetBoneMatrices());
result+=tmp;
}
// advance to next frame
m_AnimTime+=anim->GetFrameTime();
m_BoneMatricesValid=false;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BuildAnimation: load raw animation frame animation from given file, and build a
// animation specific to this model
CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed)
{
CSkeletonAnimDef* def=g_SkelAnimMan.GetAnimation(filename);
if (!def) return 0;
CSkeletonAnim* anim=new CSkeletonAnim;
anim->m_AnimDef=def;
anim->m_Speed=speed;
CalcAnimatedObjectBound(def,anim->m_ObjectBounds);
return anim;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Update: update this model by the given time, in seconds
void CModel::Update(float time)
{
if (m_Anim && m_BoneMatrices) {
// convert to ms and adjust for animation speed
float animtime=time*1000*m_Anim->m_Speed;
// update animation time, but don't calculate bone matrices - do that (lazily) when
// something requests them; that saves some calculation work for offscreen models,
// and also assures the world space, inverted bone matrices (required for normal
// skinning) are up to date with respect to m_Transform
m_AnimTime+=animtime;
float duration=m_Anim->m_AnimDef->GetDuration();
if (m_AnimTime>duration) {
m_AnimTime=(float) fmod(m_AnimTime,duration);
}
// mark vertices as dirty
SetDirty(RENDERDATA_UPDATE_VERTICES);
// mark matrices as dirty
m_BoneMatricesValid=false;
}
// update props
for (uint i=0;i<m_Props.size();i++) {
m_Props[i].m_Model->Update(time);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GenerateBoneMatrices: calculate necessary bone transformation matrices for skinning
void CModel::GenerateBoneMatrices()
{
if (!m_Anim || !m_BoneMatrices) return;
m_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime,m_BoneMatrices);
const CMatrix3D& transform=GetTransform();
for (int i=0;i<m_pModelDef->GetNumBones();i++) {
CMatrix3D m=m_BoneMatrices[i];
m.Concatenate(transform);
m.GetInverse(m_InvBoneMatrices[i]);
}
// update transform of boned props
// TODO, RC - ugh, we'll be doing this twice (for boned props, at least) - once here,
// and once again in SetTransform; better to just do it in Update?
for (size_t i=0;i<m_Props.size();i++) {
const Prop& prop=m_Props[i];
if (prop.m_Point->m_BoneIndex!=0xff) {
CMatrix3D proptransform=prop.m_Point->m_Transform;;
if (prop.m_Point->m_BoneIndex!=0xff) {
proptransform.Concatenate(m_BoneMatrices[prop.m_Point->m_BoneIndex]);
}
proptransform.Concatenate(transform);
prop.m_Model->SetTransform(proptransform);
}
}
m_BoneMatricesValid=true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SetAnimation: set the given animation as the current animation on this model;
// return false on error, else true
bool CModel::SetAnimation(CSkeletonAnim* anim)
{
m_Anim=anim;
if (m_Anim) {
if (!m_BoneMatrices) {
// not boned, can't animate
return false;
}
if (anim->m_AnimDef->GetNumKeys()!=m_pModelDef->GetNumBones()) {
// mismatch between models skeleton and animations skeleton
return false;
}
// update object bounds to the bounds when given animation applied
m_ObjectBounds=m_Anim->m_ObjectBounds;
// start anim from beginning
m_AnimTime=0;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AddProp: add a prop to the model on the given point
void CModel::AddProp(SPropPoint* point,CModel* model)
{
// position model according to prop point position
model->SetTransform(point->m_Transform);
// check if we're already using this point, and replace
// model on it if so
uint i;
for (i=0;i<m_Props.size();i++) {
if (m_Props[i].m_Point==point) {
m_Props[i].m_Model=model;
return;
}
}
// not using point; add new prop
Prop prop;
prop.m_Point=point;
prop.m_Model=model;
m_Props.push_back(prop);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// RemoveProp: remove a prop from the given point
void CModel::RemoveProp(SPropPoint* point)
{
typedef std::vector<Prop>::iterator Iter;
for (Iter iter=m_Props.begin();iter!=m_Props.end();++iter) {
const Prop& prop=*iter;
if (prop.m_Point==point) {
m_Props.erase(iter);
return;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Clone: return a clone of this model
CModel* CModel::Clone() const
{
CModel* clone=new CModel;
clone->m_ObjectBounds=m_ObjectBounds;
clone->InitModel(m_pModelDef);
clone->SetTexture(m_Texture);
clone->SetAnimation(m_Anim);
for (uint i=0;i<m_Props.size();i++) {
// eek! TODO, RC - need to investigate shallow clone here
clone->AddProp(m_Props[i].m_Point,m_Props[i].m_Model->Clone());
}
return clone;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SetTransform: set the transform on this object, and reorientate props accordingly
void CModel::SetTransform(const CMatrix3D& transform)
{
// call base class to set transform on this object
CRenderableObject::SetTransform(transform);
// now set transforms on props
const CMatrix3D* bonematrices=GetBoneMatrices();
for (size_t i=0;i<m_Props.size();i++) {
const Prop& prop=m_Props[i];
CMatrix3D proptransform=prop.m_Point->m_Transform;;
if (prop.m_Point->m_BoneIndex!=0xff) {
proptransform.Concatenate(m_BoneMatrices[prop.m_Point->m_BoneIndex]);
}
proptransform.Concatenate(transform);
prop.m_Model->SetTransform(proptransform);
}
}

117
source/graphics/Model.h Executable file
View File

@ -0,0 +1,117 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: Model.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _MODEL_H
#define _MODEL_H
#include "Texture.h"
#include "ModelDef.h"
#include "RenderableObject.h"
#include "SkeletonAnim.h"
///////////////////////////////////////////////////////////////////////////////
// CModel: basically, a mesh object - holds the texturing and skinning
// information for a model in game
class CModel : public CRenderableObject
{
public:
struct Prop {
SPropPoint* m_Point;
CModel* m_Model;
};
public:
// constructor
CModel();
// destructor
~CModel();
// setup model from given geometry
bool InitModel(CModelDef *modeldef);
// calculate the world space bounds of this model
void CalcBounds();
// update this model's state; 'time' is the time since the last update, in MS
void Update(float time);
// get the model's geometry data
CModelDef *GetModelDef() { return m_pModelDef; }
// set the model's texture
void SetTexture(const CTexture& tex) { m_Texture=tex; }
// get the model's texture
CTexture* GetTexture() { return &m_Texture; }
// set the given animation as the current animation on this model
bool SetAnimation(CSkeletonAnim* anim);
// get the currently playing animation, if any
CSkeletonAnim* GetAnimation() { return m_Anim; }
// calculate object space bounds of this model, based solely on vertex positions
void CalcObjectBounds();
// calculate bounds encompassing all vertex positions for given animation
void CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result);
// return object space bounds
const CBound& GetObjectBounds() const { return m_ObjectBounds; }
// set transform of this object, and recurse down into props to update their world space transform
void SetTransform(const CMatrix3D& transform);
// return the models bone matrices
const CMatrix3D* GetBoneMatrices() {
if (!m_BoneMatricesValid) GenerateBoneMatrices();
return m_BoneMatrices;
}
// return the models inverted bone matrices
const CMatrix3D* GetInvBoneMatrices() {
if (!m_BoneMatricesValid) GenerateBoneMatrices();
return m_InvBoneMatrices;
}
// load raw animation frame animation from given file, and build a
// animation specific to this model
CSkeletonAnim* BuildAnimation(const char* filename,float speed);
// add a prop to the model on the given point
void AddProp(SPropPoint* point,CModel* model);
// remove a prop from the given point
void RemoveProp(SPropPoint* point);
// return prop list
const std::vector<Prop>& GetProps() { return m_Props; }
// return a clone of this model
CModel* Clone() const;
private:
// delete anything allocated by the model
void ReleaseData();
// calculate necessary bone transformation matrices for skinning
void GenerateBoneMatrices();
// texture used by model
CTexture m_Texture;
// pointer to the model's raw 3d data
CModelDef* m_pModelDef;
// object space bounds of model - accounts for bounds of all possible animations
// that can play on a model
CBound m_ObjectBounds;
// animation currently playing on this model, if any
CSkeletonAnim* m_Anim;
// time (in MS) into the current animation
float m_AnimTime;
// flag stating whether bone matrices are currently valid
bool m_BoneMatricesValid;
// current state of all bones on this model; null if associated modeldef isn't skeletal
CMatrix3D* m_BoneMatrices;
// inverse of the above world space transform of the above matrices
CMatrix3D* m_InvBoneMatrices;
// list of current props on model
std::vector<Prop> m_Props;
};
#endif

134
source/graphics/ModelDef.cpp Executable file
View File

@ -0,0 +1,134 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: ModelDef.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "ModelDef.h"
#include "FilePacker.h"
#include "FileUnpacker.h"
///////////////////////////////////////////////////////////////////////////////
// CModelDef Constructor
CModelDef::CModelDef()
: m_pVertices(0), m_NumVertices(0), m_pFaces(0), m_NumFaces(0), m_Bones(0), m_NumBones(0),
m_NumPropPoints(0), m_PropPoints(0)
{
}
///////////////////////////////////////////////////////////////////////////////
// CModelDef Destructor
CModelDef::~CModelDef()
{
delete[] m_pVertices;
delete[] m_pFaces;
delete[] m_Bones;
delete[] m_PropPoints;
}
///////////////////////////////////////////////////////////////////////////////
// FindPropPoint: find and return pointer to prop point matching given name;
// return null if no match (case insensitive search)
SPropPoint* CModelDef::FindPropPoint(const char* name) const
{
for (uint i=0;i<m_NumPropPoints;i++) {
if (stricmp(name,m_PropPoints[i].m_Name)==0) {
return &m_PropPoints[i];
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// Load: read and return a new CModelDef initialised with data from given file
CModelDef* CModelDef::Load(const char* filename)
{
CFileUnpacker unpacker;
// read everything in from file
unpacker.Read(filename,"PSMD");
// check version
if (unpacker.GetVersion()<FILE_READ_VERSION) {
throw CFileUnpacker::CFileVersionError();
}
CModelDef* mdef=new CModelDef;
try {
// now unpack everything
unpacker.UnpackRaw(&mdef->m_NumVertices,sizeof(mdef->m_NumVertices));
mdef->m_pVertices=new SModelVertex[mdef->m_NumVertices];
unpacker.UnpackRaw(mdef->m_pVertices,sizeof(SModelVertex)*mdef->m_NumVertices);
unpacker.UnpackRaw(&mdef->m_NumFaces,sizeof(mdef->m_NumFaces));
mdef->m_pFaces=new SModelFace[mdef->m_NumFaces];
unpacker.UnpackRaw(mdef->m_pFaces,sizeof(SModelFace)*mdef->m_NumFaces);
unpacker.UnpackRaw(&mdef->m_NumBones,sizeof(mdef->m_NumBones));
if (mdef->m_NumBones) {
mdef->m_Bones=new CBoneState[mdef->m_NumBones];
unpacker.UnpackRaw(mdef->m_Bones,mdef->m_NumBones*sizeof(CBoneState));
}
if (unpacker.GetVersion()>=2) {
// versions >=2 also have prop point data
unpacker.UnpackRaw(&mdef->m_NumPropPoints,sizeof(mdef->m_NumPropPoints));
if (mdef->m_NumPropPoints) {
mdef->m_PropPoints=new SPropPoint[mdef->m_NumPropPoints];
for (u32 i=0;i<mdef->m_NumPropPoints;i++) {
unpacker.UnpackString(mdef->m_PropPoints[i].m_Name);
unpacker.UnpackRaw(&mdef->m_PropPoints[i].m_Position.X,sizeof(mdef->m_PropPoints[i].m_Position));
unpacker.UnpackRaw(&mdef->m_PropPoints[i].m_Rotation.m_V.X,sizeof(mdef->m_PropPoints[i].m_Rotation));
unpacker.UnpackRaw(&mdef->m_PropPoints[i].m_BoneIndex,sizeof(mdef->m_PropPoints[i].m_BoneIndex));
// build prop point transform
mdef->m_PropPoints[i].m_Transform.SetIdentity();
mdef->m_PropPoints[i].m_Transform.Rotate(mdef->m_PropPoints[i].m_Rotation);
mdef->m_PropPoints[i].m_Transform.Translate(mdef->m_PropPoints[i].m_Position);
}
}
}
} catch (...) {
delete mdef;
throw CFileUnpacker::CFileEOFError();
}
return mdef;
}
///////////////////////////////////////////////////////////////////////////////
// Save: write the given CModelDef to the given file
void CModelDef::Save(const char* filename,const CModelDef* mdef)
{
CFilePacker packer;
// pack everything up
u32 numVertices=mdef->GetNumVertices();
packer.PackRaw(&numVertices,sizeof(numVertices));
packer.PackRaw(mdef->GetVertices(),sizeof(SModelVertex)*numVertices);
u32 numFaces=mdef->GetNumFaces();
packer.PackRaw(&numFaces,sizeof(numFaces));
packer.PackRaw(mdef->GetFaces(),sizeof(SModelFace)*numFaces);
packer.PackRaw(&mdef->m_NumBones,sizeof(mdef->m_NumBones));
if (mdef->m_NumBones) {
packer.PackRaw(mdef->m_Bones,sizeof(CBoneState)*mdef->m_NumBones);
}
packer.PackRaw(&mdef->m_NumPropPoints,sizeof(mdef->m_NumPropPoints));
for (u32 i=0;i<mdef->m_NumPropPoints;i++) {
packer.PackString(mdef->m_PropPoints[i].m_Name);
packer.PackRaw(&mdef->m_PropPoints[i].m_Position.X,sizeof(mdef->m_PropPoints[i].m_Position));
packer.PackRaw(&mdef->m_PropPoints[i].m_Rotation.m_V.X,sizeof(mdef->m_PropPoints[i].m_Rotation));
packer.PackRaw(&mdef->m_PropPoints[i].m_BoneIndex,sizeof(mdef->m_PropPoints[i].m_BoneIndex));
}
// flush everything out to file
packer.Write(filename,FILE_VERSION,"PSMD");
}

123
source/graphics/ModelDef.h Executable file
View File

@ -0,0 +1,123 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: ModelDef.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _MODELDEF_H
#define _MODELDEF_H
#include "res/res.h"
#include "CStr.h"
#include "Vector3D.h"
#include "SkeletonAnimDef.h"
///////////////////////////////////////////////////////////////////////////////
// SPropPoint: structure describing a prop point
struct SPropPoint
{
// name of the prop point
CStr m_Name;
// position of the point
CVector3D m_Position;
// rotation of the point
CQuaternion m_Rotation;
// object to parent space transformation
CMatrix3D m_Transform;
// index of parent bone; 0xff if unboned
u8 m_BoneIndex;
};
///////////////////////////////////////////////////////////////////////////////
// SVertexBlend: structure containing the necessary data for blending vertices
// with multiple bones
struct SVertexBlend
{
enum { SIZE = 4 };
// index of the influencing bone, or 0xff if none
u8 m_Bone[SIZE];
// weight of the influence; all weights sum to 1
float m_Weight[SIZE];
};
///////////////////////////////////////////////////////////////////////////////
// SModelVertex: structure containing per-vertex data
struct SModelVertex
{
// vertex position
CVector3D m_Coords;
// vertex normal
CVector3D m_Norm;
// vertex UVs
float m_U, m_V;
// vertex blend data
SVertexBlend m_Blend;
};
///////////////////////////////////////////////////////////////////////////////
// SModelFace: structure containing per-face data
struct SModelFace
{
// indices of the 3 vertices on this face
u16 m_Verts[3];
};
////////////////////////////////////////////////////////////////////////////////////////
// CModelDef: a raw 3D model; describes the vertices, faces, skinning and skeletal
// information of a model
class CModelDef
{
public:
// current file version given to saved animations
enum { FILE_VERSION = 2 };
// supported file read version - files with a version less than this will be rejected
enum { FILE_READ_VERSION = 1 };
public:
// constructor
CModelDef();
// destructor
virtual ~CModelDef();
// model I/O functions
static CModelDef* Load(const char* filename);
static void Save(const char* filename,const CModelDef* mdef);
public:
// accessor: get vertex data
int GetNumVertices() const { return m_NumVertices; }
SModelVertex *GetVertices() const { return m_pVertices; }
// accessor: get face data
int GetNumFaces() const { return m_NumFaces; }
SModelFace *GetFaces() const { return m_pFaces; }
// accessor: get bone data
int GetNumBones() const { return m_NumBones; }
CBoneState *GetBones() const { return m_Bones; }
// find and return pointer to prop point matching given name; return
// null if no match (case insensitive search)
SPropPoint* FindPropPoint(const char* name) const;
public:
// vertex data
u32 m_NumVertices;
SModelVertex* m_pVertices;
// face data
u32 m_NumFaces;
SModelFace* m_pFaces;
// bone data - default model pose
u32 m_NumBones;
CBoneState* m_Bones;
// prop point data
u32 m_NumPropPoints;
SPropPoint* m_PropPoints;
};
#endif

321
source/graphics/ObjectEntry.cpp Executable file
View File

@ -0,0 +1,321 @@
#include "ObjectEntry.h"
#include "ObjectManager.h"
#include "Model.h"
#include "ModelDef.h"
#include "UnitManager.h"
// xerces XML stuff
#include <xercesc/dom/DOM.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
// Gee's custom error handler
#include <ps/XercesErrorHandler.h>
#ifdef _MSC_VER
#pragma comment(lib, "xerces-c_2.lib")
#endif
// automatically use namespace ..
XERCES_CPP_NAMESPACE_USE
CObjectEntry::CObjectEntry(int type) : m_Model(0), m_Type(type)
{
m_IdleAnim=0;
m_WalkAnim=0;
m_DeathAnim=0;
m_MeleeAnim=0;
m_RangedAnim=0;
}
CObjectEntry::~CObjectEntry()
{
for (size_t i=0;i<m_Animations.size();i++) {
CSkeletonAnim* anim=m_Animations[i].m_AnimData;
delete anim;
}
delete m_Model;
}
bool CObjectEntry::BuildModel()
{
debug_out("Building model %s\n",(const char*) m_Name);
// check we've enough data to consider building the object
if (m_ModelName.Length()==0 || m_TextureName.Length()==0) {
return false;
}
// get the root directory of this object
CStr dirname=g_ObjMan.m_ObjectTypes[m_Type].m_Name;
// remember the old model so we can replace any models using it later on
CModelDef* oldmodel=m_Model ? m_Model->GetModelDef() : 0;
// build filename
CStr modelfilename("mods\\official\\");
modelfilename+=m_ModelName;
// try and create a model
CModelDef* modeldef;
try {
modeldef=CModelDef::Load((const char*) modelfilename);
} catch (...) {
return false;
}
// create new Model
m_Model=new CModel;
m_Model->SetTexture((const char*) m_TextureName);
m_Model->InitModel(modeldef);
// calculate initial object space bounds, based on vertex positions
m_Model->CalcObjectBounds();
// load animations
for( uint t = 0; t < m_Animations.size(); t++ )
{
if( m_Animations[t].m_FileName.Length() > 0 )
{
CStr animfilename( "mods\\official\\" );
animfilename += m_Animations[t].m_FileName;
m_Animations[t].m_AnimData = m_Model->BuildAnimation((const char*) animfilename,m_Animations[t].m_Speed);
if( m_Animations[t].m_AnimName.LowerCase() == CStr( "idle" ) )
m_IdleAnim = m_Animations[t].m_AnimData;
if( m_Animations[t].m_AnimName.LowerCase() == CStr( "walk" ) )
m_WalkAnim = m_Animations[t].m_AnimData;
}
else
{
// FIXME, RC - don't store invalid animations
m_Animations[t].m_AnimData=0;
}
}
// start up idling
m_Model->SetAnimation( m_IdleAnim );
// build props - TODO, RC - need to fix up bounds here
for (uint p=0;p<m_Props.size();p++) {
const Prop& prop=m_Props[p];
SPropPoint* proppoint=modeldef->FindPropPoint((const char*) prop.m_PropPointName);
if (proppoint) {
CObjectEntry* oe=g_ObjMan.FindObject(prop.m_ModelName);
if (oe) {
// try and build model if we haven't already got it
if (!oe->m_Model) oe->BuildModel();
if (oe->m_Model) {
CModel* propmodel=oe->m_Model->Clone();
m_Model->AddProp(proppoint,propmodel);
if (oe->m_WalkAnim) propmodel->SetAnimation(oe->m_WalkAnim);
}
}
}
}
// build world space bounds
m_Model->CalcBounds();
// replace any units using old model to now use new model; also reprop models, if necessary
const std::vector<CUnit*>& units=g_UnitMan.GetUnits();
for (uint i=0;i<units.size();++i) {
CModel* unitmodel=units[i]->GetModel();
if (unitmodel->GetModelDef()==oldmodel) {
unitmodel->InitModel(m_Model->GetModelDef());
const std::vector<CModel::Prop>& newprops=m_Model->GetProps();
for (uint j=0;j<newprops.size();j++) {
unitmodel->AddProp(newprops[j].m_Point,newprops[j].m_Model->Clone());
}
}
}
// and were done with the old model ..
delete oldmodel;
return true;
}
CSkeletonAnim* CObjectEntry::GetNamedAnimation( CStr animationName )
{
for( uint t = 0; t < m_Animations.size(); t++ )
{
if( m_Animations[t].m_AnimName == animationName )
return( m_Animations[t].m_AnimData );
}
return( NULL );
}
CStr Transcode(const XMLCh* xmltext)
{
char* str=XMLString::transcode(xmltext);
CStr result(str);
XMLString::release(&str);
return result;
}
bool CObjectEntry::Load(const char* filename)
{
bool parseOK = false;
// Initialize XML library
XMLPlatformUtils::Initialize();
{
XMLCh* attachpointtext=XMLString::transcode("attachpoint");
XMLCh* modeltext=XMLString::transcode("model");
XMLCh* nametext=XMLString::transcode("name");
XMLCh* filetext=XMLString::transcode("file");
XMLCh* speedtext=XMLString::transcode("speed");
// Create parser instance
XercesDOMParser *parser = new XercesDOMParser();
// Setup parser
parser->setValidationScheme(XercesDOMParser::Val_Auto);
parser->setDoNamespaces(false);
parser->setDoSchema(false);
parser->setCreateEntityReferenceNodes(false);
// Set customized error handler
CXercesErrorHandler *errorHandler = new CXercesErrorHandler();
parser->setErrorHandler(errorHandler);
// Push the CLogger to mark it's reading this file.
// Get main node
XMLCh* xfilename=XMLString::transcode(filename);
LocalFileInputSource source(xfilename);
XMLString::release(&xfilename);
// Parse file
parser->parse(source);
// Check how many errors
parseOK = parser->getErrorCount() == 0;
if (parseOK) {
// parsed successfully - grab our data
DOMDocument *doc = parser->getDocument();
DOMElement *element = doc->getDocumentElement();
// root_name should be Object
CStr root_name = Transcode( element->getNodeName() );
// should have at least 3 children - Name, ModelName and TextureName
DOMNodeList *children = element->getChildNodes();
int numChildren=children->getLength();
for (int i=0; i<numChildren; ++i) {
// Get node
DOMNode *child = children->item(i);
// A child element
if (child->getNodeType() == DOMNode::ELEMENT_NODE)
{
// First get element and not node
DOMElement *child_element = (DOMElement*)child;
CStr element_name = Transcode( child_element->getNodeName() );
DOMNode *value_node= child_element->getChildNodes()->item(0);
CStr element_value=value_node ? Transcode(value_node->getNodeValue()) : "";
if (element_name==CStr("Name")) {
m_Name=element_value;
} else if (element_name==CStr("ModelName")) {
m_ModelName=element_value;
} else if (element_name==CStr("TextureName")) {
m_TextureName=element_value;
} else if (element_name==CStr("Animations")) {
DOMNodeList* animations=(DOMNodeList*) child_element->getChildNodes();
for (uint j=0; j<animations->getLength(); ++j) {
DOMElement *anim_element = (DOMElement*) animations->item(j);
CStr element_name = Transcode( anim_element->getNodeName() );
DOMNamedNodeMap* attributes=anim_element->getAttributes();
if (attributes) {
Anim anim;
DOMNode *nameattr=attributes->getNamedItem(nametext);
anim.m_AnimName=Transcode(nameattr->getChildNodes()->item(0)->getNodeValue());
DOMNode *fileattr=attributes->getNamedItem(filetext);
anim.m_FileName=Transcode(fileattr->getChildNodes()->item(0)->getNodeValue());
DOMNode *speedattr=attributes->getNamedItem(speedtext);
CStr speedstr=Transcode(speedattr->getChildNodes()->item(0)->getNodeValue());
anim.m_Speed=float(atoi((const char*) speedstr))/100.0f;
if (anim.m_Speed<=0) anim.m_Speed=1.0f;
m_Animations.push_back(anim);
}
}
} else if (element_name==CStr("Props")) {
DOMNodeList* props=(DOMNodeList*) child_element->getChildNodes();
for (uint j=0; j<props->getLength(); ++j) {
DOMElement *prop_element = (DOMElement*) props->item(j);
CStr element_name = Transcode( prop_element->getNodeName() );
DOMNamedNodeMap* attributes=prop_element->getAttributes();
if (attributes) {
Prop prop;
DOMNode *nameattr=attributes->getNamedItem(attachpointtext);
prop.m_PropPointName=Transcode(nameattr->getChildNodes()->item(0)->getNodeValue());
DOMNode *modelattr=attributes->getNamedItem(modeltext);
prop.m_ModelName=XMLString::transcode(modelattr->getChildNodes()->item(0)->getNodeValue());
m_Props.push_back(prop);
}
}
}
}
}
}
XMLString::release(&attachpointtext);
XMLString::release(&modeltext);
XMLString::release(&nametext);
XMLString::release(&filetext);
XMLString::release(&speedtext);
delete parser;
delete errorHandler;
}
XMLPlatformUtils::Terminate();
return parseOK;
}
bool CObjectEntry::Save(const char* filename)
{
FILE* fp=fopen(filename,"w");
if (!fp) return false;
// write XML header
fprintf(fp,"<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>\n\n");
fprintf(fp,"<!DOCTYPE Object SYSTEM \"..\\object.dtd\">\n\n");
// write the object itself
fprintf(fp,"<!-- File automatically generated by ScEd -->\n");
fprintf(fp,"<Object>\n");
fprintf(fp,"\t<Name>%s</Name>\n",(const char*) m_Name);
fprintf(fp,"\t<ModelName>%s</ModelName>\n",(const char*) m_ModelName);
fprintf(fp,"\t<TextureName>%s</TextureName>\n",(const char*) m_TextureName);
if (m_Animations.size()>0) {
fprintf(fp,"\t<Animations>\n");
for (uint i=0;i<m_Animations.size();i++) {
fprintf(fp,"\t\t<Animation name=\"%s\" file=\"%s\"> </Animation>\n",(const char*) m_Animations[i].m_AnimName,(const char*) m_Animations[i].m_FileName);
}
fprintf(fp,"\t</Animations>\n");
}
fprintf(fp,"</Object>\n");
fclose(fp);
return true;
}

65
source/graphics/ObjectEntry.h Executable file
View File

@ -0,0 +1,65 @@
#ifndef _OBJECTENTRY_H
#define _OBJECTENTRY_H
class CModel;
class CSkeletonAnim;
#include <vector>
#include "CStr.h"
#include "Bound.h"
#include "ModelDef.h"
class CObjectEntry
{
public:
struct Anim {
// name of the animation - "Idle", "Run", etc
CStr m_AnimName;
// filename of the animation - manidle.psa, manrun.psa, etc
CStr m_FileName;
// animation speed, as specified in XML actor file
float m_Speed;
// the animation data, specific to the this model
CSkeletonAnim* m_AnimData;
};
struct Prop {
// name of the prop point to attach to - "Prop01", "Prop02", "Head", "LeftHand", etc ..
CStr m_PropPointName;
// name of the model file - art/actors/props/sword.xml or whatever
CStr m_ModelName;
};
public:
CObjectEntry(int type);
~CObjectEntry();
bool BuildModel();
bool Load(const char* filename);
bool Save(const char* filename);
// object name
CStr m_Name;
// texture name
CStr m_TextureName;
// model name
CStr m_ModelName;
// list of valid animations for this object
std::vector<Anim> m_Animations;
CSkeletonAnim* m_IdleAnim;
CSkeletonAnim* m_WalkAnim;
CSkeletonAnim* m_DeathAnim;
CSkeletonAnim* m_MeleeAnim;
CSkeletonAnim* m_RangedAnim;
CSkeletonAnim* GetNamedAnimation( CStr animationName );
// list of props attached to object
std::vector<Prop> m_Props;
// corresponding model
CModel* m_Model;
// type of object; index into object managers types array
int m_Type;
};
#endif

151
source/graphics/ObjectManager.cpp Executable file
View File

@ -0,0 +1,151 @@
#include "ObjectManager.h"
#include <io.h>
#include <algorithm>
CObjectManager::CObjectManager() : m_SelectedObject(0)
{
m_ObjectTypes.reserve(32);
}
CObjectManager::~CObjectManager()
{
m_SelectedObject=0;
for (size_t i=0;i<m_ObjectTypes.size();i++) {
for (size_t j=0;j<m_ObjectTypes[i].m_Objects.size();j++) {
delete m_ObjectTypes[i].m_Objects[j];
}
}
}
CObjectEntry* CObjectManager::FindObject(const char* objectname)
{
for (uint k=0;k<m_ObjectTypes.size();k++) {
std::vector<CObjectEntry*>& objects=m_ObjectTypes[k].m_Objects;
for (uint i=0;i<objects.size();i++) {
if (strcmp(objectname,(const char*) objects[i]->m_Name)==0) {
return objects[i];
}
}
}
return 0;
}
void CObjectManager::AddObjectType(const char* name)
{
m_ObjectTypes.resize(m_ObjectTypes.size()+1);
SObjectType& type=m_ObjectTypes.back();
type.m_Name=name;
type.m_Index=m_ObjectTypes.size()-1;
}
void CObjectManager::AddObject(CObjectEntry* object,int type)
{
assert((uint)type<m_ObjectTypes.size());
m_ObjectTypes[type].m_Objects.push_back(object);
}
void CObjectManager::DeleteObject(CObjectEntry* entry)
{
std::vector<CObjectEntry*>& objects=m_ObjectTypes[entry->m_Type].m_Objects;
typedef std::vector<CObjectEntry*>::iterator Iter;
Iter i=std::find(objects.begin(),objects.end(),entry);
if (i!=objects.end()) {
objects.erase(i);
}
delete entry;
}
void CObjectManager::LoadObjects()
{
// find all the object types by directory name
BuildObjectTypes();
// now iterate through terrain types loading all textures of that type
uint i;
for (i=0;i<m_ObjectTypes.size();i++) {
LoadObjects(i);
}
// now build all the models
for (i=0;i<m_ObjectTypes.size();i++) {
std::vector<CObjectEntry*>& objects=m_ObjectTypes[i].m_Objects;
for (uint j=0;j<objects.size();j++) {
// object might have already been built (eg if it's a prop);
// and only build if we haven't a model
if (!objects[j]->m_Model) {
if (!objects[j]->BuildModel()) {
DeleteObject(objects[j]);
}
}
}
}
}
void CObjectManager::BuildObjectTypes()
{
struct _finddata_t file;
long handle;
// Find first matching directory in terrain\textures
if ((handle=_findfirst("mods\\official\\art\\actors\\*",&file))!=-1) {
if ((file.attrib & _A_SUBDIR) && file.name[0]!='.') {
AddObjectType(file.name);
}
// Find the rest of the matching files
while( _findnext(handle,&file)==0) {
if ((file.attrib & _A_SUBDIR) && file.name[0]!='.') {
AddObjectType(file.name);
}
}
_findclose(handle);
}
}
void CObjectManager::LoadObjects(int type)
{
struct _finddata_t file;
long handle;
// build pathname
CStr pathname("mods\\official\\art\\actors\\");
pathname+=m_ObjectTypes[type].m_Name;
pathname+="\\";
CStr findname(pathname);
findname+="*.xml";
// Find first matching file in directory for this terrain type
if ((handle=_findfirst((const char*) findname,&file))!=-1) {
CObjectEntry* object=new CObjectEntry(type);
CStr filename(pathname);
filename+=file.name;
if (!object->Load((const char*) filename)) {
delete object;
} else {
AddObject(object,type);
}
// Find the rest of the matching files
while( _findnext(handle,&file)==0) {
CObjectEntry* object=new CObjectEntry(type);
CStr filename(pathname);
filename+=file.name;
if (!object->Load((const char*) filename)) {
delete object;
} else {
AddObject(object,type);
}
}
_findclose(handle);
}
}

52
source/graphics/ObjectManager.h Executable file
View File

@ -0,0 +1,52 @@
#ifndef _OBJECTMANAGER_H
#define _OBJECTMANAGER_H
#include <vector>
#include "Singleton.h"
#include "ObjectEntry.h"
// access to sole CObjectManager object
#define g_ObjMan CObjectManager::GetSingleton()
///////////////////////////////////////////////////////////////////////////////////////////
// CObjectManager: manager class for all possible actor types
class CObjectManager : public Singleton<CObjectManager>
{
public:
struct SObjectType
{
// name of this object type (derived from directory name)
CStr m_Name;
// index in parent array
int m_Index;
// list of objects of this type (found from the objects directory)
std::vector<CObjectEntry*> m_Objects;
};
public:
// constructor, destructor
CObjectManager();
~CObjectManager();
void LoadObjects();
void AddObjectType(const char* name);
CObjectEntry* FindObject(const char* objname);
void AddObject(CObjectEntry* entry,int type);
void DeleteObject(CObjectEntry* entry);
CObjectEntry* GetSelectedObject() const { return m_SelectedObject; }
void SetSelectedObject(CObjectEntry* obj) { m_SelectedObject=obj; }
std::vector<SObjectType> m_ObjectTypes;
private:
void BuildObjectTypes();
void LoadObjects(int type);
CObjectEntry* m_SelectedObject;
};
#endif

86
source/graphics/Particle.cpp Executable file
View File

@ -0,0 +1,86 @@
/*==================================================================
|
| Name: Particle.cpp
|
|===================================================================
|
| Author: Ben Vinegar
| Contact: benvinegar () hotmail ! com
|
|
| Last Modified: 03/08/04
|
| Overview: A single particle, currently only utilized by
| CParticleEmitter. Public variables are for performance
| reasons.
|
|
| Usage: Instantiate a particle, set public variables, then call
| Frame() every frame.
|
| To do: TBA
|
| More Information: TBA
|
==================================================================*/
#include "Particle.h"
#include "timer.h"
#include "ogl.h"
#include <assert.h>
CParticle::CParticle() :
m_duration(0.0f),
m_timeElapsedTotal(0.0f),
m_position(0.0f, 0.0f, 0.0f),
m_velocity(0.0f, 0.0f, 0.0f),
m_gravity(0.0f, 0.0f, 0.0f)
{
m_timeOfLastFrame = get_time();
// default white colour
m_colour[0] = m_colour[1] = m_colour[2] = m_colour[3] = 1.0f;
}
CParticle::~CParticle()
{
}
void CParticle::Init()
{
// calculate colour increment per second in order to fade to black
m_colourInc[0] = - (m_colour[0] / m_duration);
m_colourInc[1] = - (m_colour[1] / m_duration);
m_colourInc[2] = - (m_colour[2] / m_duration);
}
void CParticle::Frame()
{
Update();
Render();
}
void CParticle::Render()
{
assert(m_sprite);
m_sprite->SetColour(m_colour);
m_sprite->SetTranslation(m_position);
m_sprite->Render();
}
void CParticle::Update()
{
float timeElapsed = float(get_time() - m_timeOfLastFrame);
m_velocity += m_gravity * timeElapsed;
m_position += m_velocity * timeElapsed;
// fade colour
m_colour[0] += m_colourInc[0] * timeElapsed;
m_colour[1] += m_colourInc[1] * timeElapsed;
m_colour[2] += m_colourInc[2] * timeElapsed;
m_timeOfLastFrame = get_time();
m_timeElapsedTotal += timeElapsed;
}

71
source/graphics/Particle.h Executable file
View File

@ -0,0 +1,71 @@
/*==================================================================
|
| Name: Particle.h
|
|===================================================================
|
| Author: Ben Vinegar
| Contact: benvinegar () hotmail ! com
|
|
| Last Modified: 03/08/04
|
| Overview: A single particle, currently only utilized by
| CParticleEmitter. Public variables are for performance
| reasons.
|
|
| Usage: Instantiate a particle, set public variables, then call
| Frame() every frame.
|
| To do: TBA
|
| More Information: TBA
|
==================================================================*/
#ifndef PARTICLE_H
#define PARTICLE_H
//--------------------------------------------------------
// Includes / Compiler directives
//--------------------------------------------------------
#include "Vector3D.h"
#include "Sprite.h"
//--------------------------------------------------------
// Declarations
//--------------------------------------------------------
class CParticle
{
public:
CParticle();
~CParticle();
// necessary pre-processing immediately before first update call
void Init();
void Frame();
void Update();
void Render();
void SetColour(float r, float g, float b, float a);
CSprite * m_sprite;
float m_duration;
double m_timeOfLastFrame;
double m_timeElapsedTotal;
CVector3D m_position;
CVector3D m_velocity;
CVector3D m_gravity;
float m_colour[4];
float m_colourInc[3];
};
#endif // PARTICLE_H

View File

@ -0,0 +1,277 @@
/*==================================================================
|
| Name: ParticleEmitter.cpp
|
|===================================================================
|
| Author: Ben Vinegar
| Contact: benvinegar () hotmail ! com
|
|
| Last Modified: 03/08/04
|
| Overview: Particle emitter class that emits particles from
| an origin (or area) with a variety of set colours,
| durations, forces and a single common sprite.
|
|
| Usage: Instantiate one emitter per desired effect. Set the
| various fields (preferably all, the defaults are rather
| boring) and then call Frame() - you guessed it - every
| frame.
|
| To do: TBA
|
| More Information: TBA
|
==================================================================*/
#include "ParticleEmitter.h"
#include "timer.h"
#include "ogl.h"
#include <stdlib.h>
CParticleEmitter::CParticleEmitter() :
m_particles(NULL),
m_origin(0.0f, 0.0f, 0.0f),
m_originSpread(0.0f, 0.0f, 0.0f),
m_velocity(0.0f, 0.0f, 0.0f),
m_velocitySpread(0.0f, 0.0f, 0.0f),
m_gravity(0.0f, 0.0f, 0.0f),
m_maxParticles(0),
m_minParticles(0),
m_numParticles(0),
m_maxLifetime(0),
m_minLifetime(0),
m_timeOfLastFrame(0.0f),
m_timeSinceLastEmit(0.0f)
{
m_particles.clear();
}
CParticleEmitter::~CParticleEmitter()
{
}
void CParticleEmitter::Frame()
{
Update();
Render();
}
void CParticleEmitter::Render()
{
glEnable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glAlphaFunc(GL_GREATER, 0.0f);
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
vector<CParticle *>::iterator itor = m_particles.begin();
while (itor != m_particles.end())
{
CParticle * curParticle = (*itor);
curParticle->Frame();
++itor;
}
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
void CParticleEmitter::Update()
{
double timeElapsed = get_time() - m_timeOfLastFrame;
// update existing particles
vector<CParticle *>::iterator itor = m_particles.begin();
while (itor != m_particles.end())
{
CParticle * curParticle = (*itor);
curParticle->Update();
// destroy particle if it has lived beyond its duration
if (curParticle->m_timeElapsedTotal >= curParticle->m_duration)
{
m_particles.erase(itor);
delete curParticle;
--m_numParticles;
}
++itor;
}
double secondsPerEmit = 1 / (m_minParticles / m_minLifetime);
if (m_timeSinceLastEmit > secondsPerEmit)
{
float duration;
CVector3D position, velocity;
float colour[4];
bool moreParticlesToEmit = true;
while (moreParticlesToEmit) {
CParticle * newParticle = new CParticle();
// calculate particle duration
duration = (float)m_minLifetime;
duration += (rand() % (int)((m_maxLifetime - m_minLifetime) * 1000.0f + 1)) / 1000.0f;
newParticle->m_duration = duration;
// calculate particle start position from spread
position = m_origin;
position.X += (rand() % (int)(m_originSpread.X * 2000.0f + 1)) / 1000.0f - m_originSpread.X;
position.Y += (rand() % (int)(m_originSpread.Y * 2000.0f + 1)) / 1000.0f - m_originSpread.Y;
position.Z += (rand() % (int)(m_originSpread.Z * 2000.0f + 1)) / 1000.0f - m_originSpread.Z;
newParticle->m_position = position;
// calculate particle velocity from spread
velocity = m_velocity;
velocity.X += (rand() % (int)(m_velocitySpread.X * 2000.0f + 1)) / 1000.0f - m_velocitySpread.X;
velocity.Y += (rand() % (int)(m_velocitySpread.Y * 2000.0f + 1)) / 1000.0f - m_velocitySpread.Y;
velocity.Z += (rand() % (int)(m_velocitySpread.Z * 2000.0f + 1)) / 1000.0f - m_velocitySpread.Z;
newParticle->m_velocity = velocity;
newParticle->m_gravity = m_gravity;
// calculate and assign colour
memcpy(colour, m_startColour, sizeof(float) * 4);
colour[0] += (rand() % (int)((m_endColour[0] - m_startColour[0]) * 1000.0f + 1)) / 1000.0f;
colour[1] += (rand() % (int)((m_endColour[1] - m_startColour[1]) * 1000.0f + 1)) / 1000.0f;
colour[2] += (rand() % (int)((m_endColour[2] - m_startColour[2]) * 1000.0f + 1)) / 1000.0f;
colour[3] += (rand() % (int)((m_endColour[3] - m_startColour[3]) * 1000.0f + 1)) / 1000.0f;
memcpy(newParticle->m_colour, colour, sizeof(float) * 4);
// assign sprite
newParticle->m_sprite = m_sprite;
// final pre-processing init call
newParticle->Init();
// add to vector of particles
m_particles.push_back(newParticle);
timeElapsed -= secondsPerEmit;
if (timeElapsed < secondsPerEmit)
{
moreParticlesToEmit = false;
}
++m_numParticles;
}
m_timeSinceLastEmit = 0.0f;
}
else
m_timeSinceLastEmit += (float)timeElapsed;
m_timeOfLastFrame = get_time();
}
void CParticleEmitter::SetSprite(CSprite * sprite)
{
m_sprite = sprite;
}
void CParticleEmitter::SetOrigin(CVector3D origin)
{
m_origin = origin;
}
void CParticleEmitter::SetOrigin(float x, float y, float z)
{
m_origin.X = x;
m_origin.Y = y;
m_origin.Z = z;
}
void CParticleEmitter::SetOriginSpread(CVector3D spread)
{
m_originSpread = spread;
}
void CParticleEmitter::SetOriginSpread(float x, float y, float z)
{
m_originSpread.X = x;
m_originSpread.Y = y;
m_originSpread.Z = z;
}
void CParticleEmitter::SetGravity(CVector3D gravity)
{
m_gravity = gravity;
}
void CParticleEmitter::SetGravity(float x, float y, float z)
{
m_gravity.X = x;
m_gravity.Y = y;
m_gravity.Z = z;
}
void CParticleEmitter::SetVelocity(CVector3D velocity)
{
m_velocity = velocity;
}
void CParticleEmitter::SetVelocity(float x, float y, float z)
{
m_velocity.X = x;
m_velocity.Y = y;
m_velocity.Z = z;
}
void CParticleEmitter::SetVelocitySpread(CVector3D spread)
{
m_velocitySpread = spread;
}
void CParticleEmitter::SetVelocitySpread(float x, float y, float z)
{
m_velocitySpread.X = x;
m_velocitySpread.Y = y;
m_velocitySpread.Z = z;
}
void CParticleEmitter::SetStartColour(float r, float g, float b, float a)
{
m_startColour[0] = r;
m_startColour[1] = g;
m_startColour[2] = b;
m_startColour[3] = a;
}
void CParticleEmitter::SetEndColour(float r, float g, float b, float a)
{
m_endColour[0] = r;
m_endColour[1] = g;
m_endColour[2] = b;
m_endColour[3] = a;
}
void CParticleEmitter::SetMaxLifetime(double maxLife)
{
m_maxLifetime = maxLife;
}
void CParticleEmitter::SetMinLifetime(double minLife)
{
m_minLifetime = minLife;
}
void CParticleEmitter::SetMaxParticles(int maxParticles)
{
m_maxParticles = maxParticles;
}
void CParticleEmitter::SetMinParticles(int minParticles)
{
m_minParticles = minParticles;
}

119
source/graphics/ParticleEmitter.h Executable file
View File

@ -0,0 +1,119 @@
/*==================================================================
|
| Name: ParticleEmitter.h
|
|===================================================================
|
| Author: Ben Vinegar
| Contact: benvinegar () hotmail ! com
|
|
| Last Modified: 03/08/04
|
| Overview: Particle emitter class that emits particles from
| an origin (or area) with a variety of set colours,
| durations, forces and a single common sprite.
|
|
| Usage: Instantiate one emitter per desired effect. Set the
| various fields (preferably all, the defaults are rather
| boring) and then call Frame() - you guessed it - every
| frame.
|
| To do: TBA
|
| More Information: TBA
|
==================================================================*/
#ifndef PARTICLE_EMITTER_H
#define PARTICLE_EMITTER_H
//--------------------------------------------------------
// Includes / Compiler directives
//--------------------------------------------------------
#include "Particle.h"
#include "Sprite.h"
#include "Vector3D.h"
#include <vector>
//--------------------------------------------------------
// Declarations
//--------------------------------------------------------
class CParticleEmitter
{
public:
CParticleEmitter();
~CParticleEmitter();
// must be performed before first frame/render/update call
bool Init();
// renders and updates particles
void Frame();
// renders without updating particles
void Render();
void Update();
void SetSprite(CSprite * sprite);
void SetOrigin(CVector3D origin);
void SetOrigin(float x, float y, float z);
void SetOriginSpread(CVector3D spread);
void SetOriginSpread(float x, float y, float z);
void SetGravity(CVector3D gravity);
void SetGravity(float x, float y, float z);
void SetVelocity(CVector3D direction);
void SetVelocity(float x, float y, float z);
void SetVelocitySpread(CVector3D spread);
void SetVelocitySpread(float x, float y, float z);
void SetStartColour(float r, float g, float b, float a);
void SetEndColour(float r, float g, float b, float a);
// in milliseconds
void SetMaxLifetime(double maxLife);
// in milliseconds
void SetMinLifetime(double minLife);
void SetMaxParticles(int maxParticles);
void SetMinParticles(int minParticles);
private:
CSprite * m_sprite;
std::vector<CParticle *> m_particles;
CVector3D m_origin;
CVector3D m_originSpread;
CVector3D m_velocity;
CVector3D m_velocitySpread;
CVector3D m_gravity;
float m_startColour[4];
float m_endColour[4];
int m_maxParticles;
int m_minParticles;
int m_numParticles;
double m_maxLifetime;
double m_minLifetime;
double m_timeOfLastFrame;
float m_timeSinceLastEmit;
};
#endif // PARTICLE_EMITTER_H

60
source/graphics/Patch.cpp Executable file
View File

@ -0,0 +1,60 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: ModelDef.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "Patch.h"
#include "Terrain.h"
///////////////////////////////////////////////////////////////////////////////
// CPatch constructor
CPatch::CPatch() : m_Parent(0)
{
}
///////////////////////////////////////////////////////////////////////////////
// CPatch destructor
CPatch::~CPatch()
{
}
///////////////////////////////////////////////////////////////////////////////
// Initialize: setup patch data
void CPatch::Initialize(CTerrain* parent,u32 x,u32 z)
{
delete m_RenderData;
m_RenderData=0;
m_Parent=parent;
m_X=x;
m_Z=z;
// set parent of each patch
for (int j=0;j<16;j++) {
for (int i=0;i<16;i++) {
m_MiniPatches[j][i].m_Parent=this;
}
}
CalcBounds();
}
///////////////////////////////////////////////////////////////////////////////
// CalcBounds: calculating the bounds of this patch
void CPatch::CalcBounds()
{
m_Bounds.SetEmpty();
for (int j=0;j<PATCH_SIZE+1;j++) {
for (int i=0;i<PATCH_SIZE+1;i++) {
CVector3D pos;
m_Parent->CalcPosition(m_X*PATCH_SIZE+i,m_Z*PATCH_SIZE+j,pos);
m_Bounds+=pos;
}
}
}

42
source/graphics/Patch.h Executable file
View File

@ -0,0 +1,42 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: Patch.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _PATCH_H
#define _PATCH_H
#include "MiniPatch.h"
#include "RenderableObject.h"
class CTerrain;
///////////////////////////////////////////////////////////////////////////////
// CPatch: a single terrain patch, 16 tiles square
class CPatch : public CRenderableObject
{
public:
// constructor
CPatch();
// destructor
~CPatch();
// initialize the patch
void Initialize(CTerrain* parent,u32 x,u32 z);
// calculate and store bounds of this patch
void CalcBounds();
public:
// minipatches (tiles) making up the patch
CMiniPatch m_MiniPatches[16][16];
// position of patch in parent terrain grid
u32 m_X,m_Z;
// parent terrain
CTerrain* m_Parent;
};
#endif

View File

@ -0,0 +1,104 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: RenderableObject.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _RENDERABLEOBJECT_H
#define _RENDERABLEOBJECT_H
#include <assert.h>
#include "res/res.h"
#include "Bound.h"
#include "Matrix3D.h"
// dirty flags - used as notification to the renderer that some bit of data
// need updating
#define RENDERDATA_UPDATE_VERTICES (1<<1)
#define RENDERDATA_UPDATE_INDICES (1<<2)
#define RENDERDATA_UPDATE_TRANSFORM (1<<3)
///////////////////////////////////////////////////////////////////////////////
// CRenderData: base class of all the renderer's renderdata classes - the
// derived class stores necessary information for rendering an object of a
// particular type
class CRenderData
{
public:
CRenderData() : m_UpdateFlags(0) {}
virtual ~CRenderData() {}
u32 m_UpdateFlags;
};
///////////////////////////////////////////////////////////////////////////////
// CRenderableObject: base class of all renderable objects - patches, models,
// sprites, etc; stores position and bound information, and a pointer to
// some renderdata necessary for the renderer to actually render it
class CRenderableObject
{
public:
// constructor
CRenderableObject() : m_RenderData(0) {
m_Transform.SetIdentity();
}
// destructor
virtual ~CRenderableObject() { delete m_RenderData; }
// set object transform
virtual void SetTransform(const CMatrix3D& transform) {
// store transform, calculate inverse
m_Transform=transform;
m_Transform.GetInverse(m_InvTransform);
// normal recalculation likely required on transform change; flag it
SetDirty(RENDERDATA_UPDATE_VERTICES);
// rebuild world space bounds
CalcBounds();
}
// get object to world space transform
const CMatrix3D& GetTransform() const { return m_Transform; }
// get world to object space transform
const CMatrix3D& GetInvTransform() const { return m_InvTransform; }
// mark some part of the renderdata as dirty, and requiring
// an update on next render
void SetDirty(u32 dirtyflags) {
if (m_RenderData) m_RenderData->m_UpdateFlags|=dirtyflags;
}
// calculate (and store in m_Bounds) the world space bounds of this object
// - must be implemented by all concrete subclasses
virtual void CalcBounds() = 0;
// return world space bounds of this object
const CBound& GetBounds() const { return m_Bounds; }
// set the object renderdata
// TODO,RC 10/04/04 - need to delete existing renderdata here, or can we
// assume the renderer won't set renderdata when an object already has it?
// - just assert we've no renderdata at the minute
void SetRenderData(CRenderData* renderdata) {
assert(m_RenderData==0);
m_RenderData=renderdata;
}
// return object renderdata - can be null if renderer hasn't yet
// created the renderdata
CRenderData* GetRenderData() { return m_RenderData; }
protected:
// object bounds
CBound m_Bounds;
// local->world space transform
CMatrix3D m_Transform;
// world->local space transform
CMatrix3D m_InvTransform;
// object renderdata
CRenderData* m_RenderData;
};
#endif

30
source/graphics/SkeletonAnim.h Executable file
View File

@ -0,0 +1,30 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: SkeletonAnim.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _SKELETONANIM_H
#define _SKELETONANIM_H
#include "Bound.h"
class CSkeletonAnimDef;
////////////////////////////////////////////////////////////////////////////////////////
// CSkeletonAnim: an instance of a CSkeletonAnimDef, for application onto a model
class CSkeletonAnim
{
public:
// the raw animation frame data
CSkeletonAnimDef* m_AnimDef;
// speed at which this animation runs
float m_Speed;
// object space bounds of the model when this animation is applied to it
CBound m_ObjectBounds;
};
#endif

View File

@ -0,0 +1,101 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: SkeletonAnimDef.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "SkeletonAnimDef.h"
#include "FilePacker.h"
#include "FileUnpacker.h"
///////////////////////////////////////////////////////////////////////////////////////////
// CSkeletonAnimDef constructor
CSkeletonAnimDef::CSkeletonAnimDef() : m_Keys(0), m_NumKeys(0), m_NumFrames(0), m_FrameTime(0)
{
}
///////////////////////////////////////////////////////////////////////////////////////////
// CSkeletonAnimDef destructor
CSkeletonAnimDef::~CSkeletonAnimDef()
{
delete[] m_Keys;
}
///////////////////////////////////////////////////////////////////////////////////////////
// BuildBoneMatrices: build matrices for all bones at the given time (in MS) in this
// animation
void CSkeletonAnimDef::BuildBoneMatrices(float time,CMatrix3D* matrices) const
{
float fstartframe=time/m_FrameTime;
u32 startframe=u32(time/m_FrameTime);
float deltatime=fstartframe-startframe;
startframe%=m_NumFrames;
u32 endframe=startframe+1;
endframe%=m_NumFrames;
u32 i;
for (i=0;i<m_NumKeys;i++) {
const Key& startkey=GetKey(startframe,i);
const Key& endkey=GetKey(endframe,i);
CVector3D trans=startkey.m_Translation*(1-deltatime)+endkey.m_Translation*deltatime;
CQuaternion rot;
rot.Slerp(startkey.m_Rotation,endkey.m_Rotation,deltatime);
matrices[i].SetIdentity();
matrices[i].Rotate(rot);
matrices[i].Translate(trans);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Load: try to load the anim from given file; return a new anim if successful
CSkeletonAnimDef* CSkeletonAnimDef::Load(const char* filename)
{
CFileUnpacker unpacker;
unpacker.Read(filename,"PSSA");
// check version
if (unpacker.GetVersion()<FILE_READ_VERSION) {
throw CFileUnpacker::CFileVersionError();
}
// unpack the data
CSkeletonAnimDef* anim=new CSkeletonAnimDef;
try {
unpacker.UnpackString(anim->m_Name);
unpacker.UnpackRaw(&anim->m_FrameTime,sizeof(anim->m_FrameTime));
unpacker.UnpackRaw(&anim->m_NumKeys,sizeof(anim->m_NumKeys));
unpacker.UnpackRaw(&anim->m_NumFrames,sizeof(anim->m_NumFrames));
anim->m_Keys=new Key[anim->m_NumKeys*anim->m_NumFrames];
unpacker.UnpackRaw(anim->m_Keys,anim->m_NumKeys*anim->m_NumFrames*sizeof(Key));
} catch (...) {
delete anim;
throw CFileUnpacker::CFileEOFError();
}
return anim;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Save: try to save anim to file
void CSkeletonAnimDef::Save(const char* filename,const CSkeletonAnimDef* anim)
{
CFilePacker packer;
// pack up all the data
packer.PackString(CStr(anim->m_Name));
packer.PackRaw(&anim->m_FrameTime,sizeof(anim->m_FrameTime));
packer.PackRaw(&anim->m_NumKeys,sizeof(anim->m_NumKeys));
packer.PackRaw(&anim->m_NumFrames,sizeof(anim->m_NumFrames));
packer.PackRaw(anim->m_Keys,anim->m_NumKeys*anim->m_NumFrames*sizeof(Key));
// now write it
packer.Write(filename,FILE_VERSION,"PSSA");
}

View File

@ -0,0 +1,85 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: SkeletonAnimDef.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _SKELETONANIMDEF_H
#define _SKELETONANIMDEF_H
#include "res/res.h"
#include "CStr.h"
#include "Vector3D.h"
#include "Quaternion.h"
////////////////////////////////////////////////////////////////////////////////////////
// CBoneState: structure describing state of a bone at some point
class CBoneState
{
public:
// translation of bone relative to root
CVector3D m_Translation;
// rotation of bone relative to root
CQuaternion m_Rotation;
};
////////////////////////////////////////////////////////////////////////////////////////
// CSkeletonAnimDef: raw description - eg bonestates - of an animation that plays upon
// a skeleton
class CSkeletonAnimDef
{
public:
// current file version given to saved animations
enum { FILE_VERSION = 1 };
// supported file read version - files with a version less than this will be rejected
enum { FILE_READ_VERSION = 1 };
public:
// Key: description of a single key in a skeleton animation
typedef CBoneState Key;
public:
// CSkeletonAnimDef constructor + destructor
CSkeletonAnimDef();
~CSkeletonAnimDef();
// return the number of keys in this animation
u32 GetNumKeys() const { return m_NumKeys; }
// accessors: get a key for given bone at given time
Key& GetKey(u32 frame,u32 bone) { return m_Keys[frame*m_NumKeys+bone]; }
const Key& GetKey(u32 frame,u32 bone) const { return m_Keys[frame*m_NumKeys+bone]; }
// get duration of this anim, in ms
float GetDuration() const { return m_NumFrames*m_FrameTime; }
// return length of each frame, in ms
float GetFrameTime() const { return m_FrameTime; }
// return number of frames in animation
u32 GetNumFrames() const { return m_NumFrames; }
// build matrices for all bones at the given time (in MS) in this animation
void BuildBoneMatrices(float time,CMatrix3D* matrices) const;
// anim I/O functions
static CSkeletonAnimDef* Load(const char* filename);
static void Save(const char* filename,const CSkeletonAnimDef* anim);
public:
// name of the animation
CStr m_Name;
// frame time - time between successive frames, in ms
float m_FrameTime;
// number of keys in each frame - should match number of bones in the skeleton
u32 m_NumKeys;
// number of frames in the animation
u32 m_NumFrames;
// animation data - m_NumKeys*m_NumFrames total keys
Key* m_Keys;
};
#endif

View File

@ -0,0 +1,67 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: SkeletonAnimManager.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "res/res.h"
#include "Model.h"
#include "SkeletonAnimManager.h"
#include <algorithm>
///////////////////////////////////////////////////////////////////////////////
// CSkeletonAnimManager constructor
CSkeletonAnimManager::CSkeletonAnimManager()
{
}
///////////////////////////////////////////////////////////////////////////////
// CSkeletonAnimManager destructor
CSkeletonAnimManager::~CSkeletonAnimManager()
{
typedef std::map<CStr,CSkeletonAnimDef*>::iterator Iter;
for (Iter i=m_Animations.begin();i!=m_Animations.end();++i) {
delete i->second;
}
}
///////////////////////////////////////////////////////////////////////////////
// GetAnimation: return a given animation by filename; return null if filename
// doesn't refer to valid animation file
CSkeletonAnimDef* CSkeletonAnimManager::GetAnimation(const char* filename)
{
// already loaded?
CStr fname(filename);
std::map<CStr,CSkeletonAnimDef*>::iterator iter=m_Animations.find(fname);
if (iter!=m_Animations.end()) {
// yes - return it
return iter->second;
}
// already failed to load?
std::set<CStr>::iterator setiter=m_BadAnimationFiles.find(fname);
if (setiter!=m_BadAnimationFiles.end()) {
// yes - return null
return 0;
}
// try and load it now
CSkeletonAnimDef* def;
try {
def=CSkeletonAnimDef::Load(filename);
} catch (...) {
def=0;
}
if (!def) {
// add this file as bad
m_BadAnimationFiles.insert(fname);
return 0;
} else {
// add mapping for this file
m_Animations[fname]=def;
return def;
}
}

View File

@ -0,0 +1,44 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: SkeletonAnimManager.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _SKELETONANIMMANAGER_H
#define _SKELETONANIMMANAGER_H
#include <map>
#include <set>
#include "SkeletonAnimDef.h"
#include "Singleton.h"
// access to sole CSkeletonAnimManager object
#define g_SkelAnimMan CSkeletonAnimManager::GetSingleton()
///////////////////////////////////////////////////////////////////////////////
// CSkeletonAnimManager : owner class of all skeleton anims - manages creation,
// loading and destruction of animation data
class CSkeletonAnimManager : public Singleton<CSkeletonAnimManager>
{
public:
// constructor, destructor
CSkeletonAnimManager();
~CSkeletonAnimManager();
// return a given animation by filename; return null if filename doesn't
// refer to valid animation file
CSkeletonAnimDef* GetAnimation(const char* filename);
private:
CSkeletonAnimDef* LoadAnimation(const char* filename);
// map of all known animations
std::map<CStr,CSkeletonAnimDef*> m_Animations;
// set of bad animation names - prevents multiple reloads of bad files
std::set<CStr> m_BadAnimationFiles;
};
#endif

215
source/graphics/Sprite.cpp Executable file
View File

@ -0,0 +1,215 @@
/*==================================================================
|
| Name: Sprite.cpp
|
|===================================================================
|
| Author: Ben Vinegar
| Contact: benvinegar () hotmail ! com
|
|
| Last Modified: 03/08/04
|
| Overview: Billboarding sprite class - always faces the camera. It
| does this by getting the current model view matrix state.
|
|
| Usage: The functions speak for themselves. Instantiate, then be
| sure to pass a loaded (using tex_load()) texture before
| calling Render().
|
| To do: TBA
|
| More Information: TBA
|
==================================================================*/
#include "Sprite.h"
#include "Renderer.h"
#include "ogl.h"
#include "res/tex.h"
CSprite::CSprite() :
m_texture(NULL)
{
// default scale 1:1
m_scale.X = m_scale.Y = m_scale.Z = 1.0f;
// default position (0.0f, 0.0f, 0.0f)
m_translation.X = m_translation.Y = m_translation.Z = 0.0f;
// default size 1.0 x 1.0
SetSize(1.0f, 1.0f);
// default colour, white
m_colour[0] = m_colour[1] = m_colour[2] = m_colour[3] = 1.0f;
}
CSprite::~CSprite()
{
}
void CSprite::Render()
{
BeginBillboard();
glDisable(GL_CULL_FACE);
glTranslatef(m_translation.X, m_translation.Y, m_translation.Z);
glScalef(m_scale.X, m_scale.Y, m_scale.Z);
g_Renderer.BindTexture(0,tex_id(m_texture->GetHandle()));
glColor4fv(m_colour);
glBegin(GL_TRIANGLE_STRIP);
// bottom left
glTexCoord2f(0.0f, 0.0f);
glVertex3fv((GLfloat *) &m_coords[0]);
// top left
glTexCoord2f(0.0f, 1.0f);
glVertex3fv((GLfloat *) &m_coords[1]);
// bottom right
glTexCoord2f(1.0f, 0.0f);
glVertex3fv((GLfloat *) &m_coords[2]);
// top left
glTexCoord2f(1.0f, 1.0f);
glVertex3fv((GLfloat *) &m_coords[3]);
glEnd();
glEnable(GL_CULL_FACE);
EndBillboard();
}
int CSprite::SetTexture(CTexture *texture)
{
if (texture == NULL) return -1;
m_texture = texture;
return 0;
}
void CSprite::SetSize(float width, float height)
{
m_width = width;
m_height = height;
float xOffset = m_width / 2;
float yOffset = m_height / 2;
// bottom left
m_coords[0].X = - (xOffset);
m_coords[0].Y = - (yOffset);
m_coords[0].Z = 0.0f;
// top left
m_coords[1].X = - (xOffset);
m_coords[1].Y = yOffset;
m_coords[1].Z = 0.0f;
// bottom right
m_coords[2].X = xOffset;
m_coords[2].Y = - (yOffset);
m_coords[2].Z = 0.0f;
// top right
m_coords[3].X = xOffset;
m_coords[3].Y = yOffset;
m_coords[3].Z = 0.0f;
}
float CSprite::GetWidth()
{
return m_width;
}
void CSprite::SetWidth(float width)
{
SetSize(width, m_height);
}
float CSprite::GetHeight()
{
return m_height;
}
void CSprite::SetHeight(float height)
{
SetSize(m_width, height);
}
CVector3D CSprite::GetTranslation()
{
return m_translation;
}
void CSprite::SetTranslation(CVector3D trans)
{
m_translation = trans;
}
void CSprite::SetTranslation(float x, float y, float z)
{
m_translation.X = x;
m_translation.Y = y;
m_translation.Z = z;
}
CVector3D CSprite::GetScale()
{
return m_scale;
}
void CSprite::SetScale(CVector3D scale)
{
m_scale = scale;
}
void CSprite::SetScale(float x, float y, float z)
{
m_scale.X = x;
m_scale.Y = y;
m_scale.Z = z;
}
void CSprite::SetColour(float * colour)
{
m_colour[0] = colour[0];
m_colour[1] = colour[1];
m_colour[2] = colour[2];
m_colour[3] = colour[3];
}
// should be called before any other gl calls
void CSprite::BeginBillboard()
{
float newMatrix[16] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
float currentMatrix[16];
glGetFloatv(GL_MODELVIEW_MATRIX, currentMatrix);
newMatrix[0] = currentMatrix[0];
newMatrix[1] = currentMatrix[4];
newMatrix[2] = currentMatrix[8];
newMatrix[4] = currentMatrix[1];
newMatrix[5] = currentMatrix[5];
newMatrix[6] = currentMatrix[9];
newMatrix[8] = currentMatrix[2];
newMatrix[9] = currentMatrix[6];
newMatrix[10] = currentMatrix[10];
glPushMatrix();
glMultMatrixf(newMatrix);
}
void CSprite::EndBillboard()
{
glPopMatrix();
}

86
source/graphics/Sprite.h Executable file
View File

@ -0,0 +1,86 @@
/*==================================================================
|
| Name: Sprite.h
|
|===================================================================
|
| Author: Ben Vinegar
| Contact: benvinegar () hotmail ! com
|
|
| Last Modified: 03/08/04
|
| Overview: Billboarding sprite class - always faces the camera. It
| does this by getting the current model view matrix state.
|
|
| Usage: The functions speak for themselves. Instantiate, then be
| sure to pass a loaded (using tex_load()) texture before
| calling Render().
|
| To do: TBA
|
| More Information: TBA
|
==================================================================*/
#ifndef SPRITE_H
#define SPRITE_H
//--------------------------------------------------------
// Includes / Compiler directives
//--------------------------------------------------------
#include "Vector3D.h"
#include "Texture.h"
//--------------------------------------------------------
// Declarations
//--------------------------------------------------------
class CSprite
{
public:
CSprite();
~CSprite();
void Render();
int SetTexture(CTexture *texture);
void SetSize(float width, float height);
float GetWidth();
void SetWidth(float width);
float GetHeight();
void SetHeight(float height);
CVector3D GetTranslation();
void SetTranslation(CVector3D pos);
void SetTranslation(float x, float y, float z);
CVector3D GetScale();
void SetScale(CVector3D scale);
void SetScale(float x, float y, float z);
void SetColour(float * colour);
void SetColour(float r, float g, float b, float a = 1.0f);
private:
void BeginBillboard();
void EndBillboard();
CTexture *m_texture;
CVector3D m_coords[4];
float m_width;
float m_height;
CVector3D m_translation;
CVector3D m_scale;
float m_colour[4];
};
#endif // SPRITE_H

292
source/graphics/Terrain.cpp Executable file
View File

@ -0,0 +1,292 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: Terrain.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "res/tex.h"
#include "res/mem.h"
#include <string.h>
#include "Terrain.h"
///////////////////////////////////////////////////////////////////////////////
// CTerrain constructor
CTerrain::CTerrain() : m_Heightmap(0), m_Patches(0), m_MapSize(0), m_MapSizePatches(0)
{
}
///////////////////////////////////////////////////////////////////////////////
// CTerrain constructor
CTerrain::~CTerrain()
{
ReleaseData();
}
///////////////////////////////////////////////////////////////////////////////
// ReleaseData: delete any data allocated by this terrain
void CTerrain::ReleaseData()
{
delete[] m_Heightmap;
delete[] m_Patches;
}
///////////////////////////////////////////////////////////////////////////////
// Initialise: initialise this terrain to the given size (in patches per side);
// using given heightmap to setup elevation data
bool CTerrain::Initialize(u32 size,const u16* data)
{
// clean up any previous terrain
ReleaseData();
// store terrain size
m_MapSize=(size*PATCH_SIZE)+1;
m_MapSizePatches=size;
// allocate data for new terrain
m_Heightmap=new u16[m_MapSize*m_MapSize];
m_Patches=new CPatch[m_MapSizePatches*m_MapSizePatches];
// given a heightmap?
if (data) {
// yes; keep a copy of it
memcpy(m_Heightmap,data,m_MapSize*m_MapSize*sizeof(u16));
} else {
// build a flat terrain
memset(m_Heightmap,0,m_MapSize*m_MapSize*sizeof(u16));
}
// setup patch parents, indices etc
InitialisePatches();
return true;
}
///////////////////////////////////////////////////////////////////////////////
// CalcPosition: calculate the world space position of the vertex at (i,j)
void CTerrain::CalcPosition(u32 i,u32 j,CVector3D& pos)
{
u16 height=m_Heightmap[j*m_MapSize + i];
pos.X = float(i)*CELL_SIZE;
pos.Y = float(height)*HEIGHT_SCALE;
pos.Z = float(j)*CELL_SIZE;
}
///////////////////////////////////////////////////////////////////////////////
// CalcNormal: calculate the world space normal of the vertex at (i,j)
void CTerrain::CalcNormal(u32 i,u32 j,CVector3D& normal)
{
CVector3D left, right, up, down;
left.Clear();
right.Clear();
up.Clear();
down.Clear();
// get position of vertex where normal is being evaluated
CVector3D basepos;
CalcPosition(i,j,basepos);
CVector3D tmp;
if (i>0) {
CalcPosition(i-1,j,tmp);
left=tmp-basepos;
}
if (i<m_MapSize-1) {
CalcPosition(i+1,j,tmp);
right=tmp-basepos;
}
if (j>0) {
CalcPosition(i,j-1,tmp);
up=tmp-basepos;
}
if (j<m_MapSize-1) {
CalcPosition(i,j+1,tmp);
down=tmp-basepos;
}
CVector3D n0 = up.Cross(left);
CVector3D n1 = left.Cross(down);
CVector3D n2 = down.Cross(right);
CVector3D n3 = right.Cross(up);
normal = n0 + n1 + n2 + n3;
float nlen=normal.GetLength();
if (nlen>0.00001f) normal*=1.0f/nlen;
}
///////////////////////////////////////////////////////////////////////////////
// GetPatch: return the patch at (x,z) in patch space, or null if the patch is
// out of bounds
CPatch* CTerrain::GetPatch(int32 x,int32 z)
{
if (x<0 || x>=int32(m_MapSizePatches)) return 0;
if (z<0 || z>=int32(m_MapSizePatches)) return 0;
return &m_Patches[(z*m_MapSizePatches)+x];
}
///////////////////////////////////////////////////////////////////////////////
// GetPatch: return the tile at (x,z) in tile space, or null if the tile is out
// of bounds
CMiniPatch* CTerrain::GetTile(int32 x,int32 z)
{
if (x<0 || x>=int32(m_MapSize)-1) return 0;
if (z<0 || z>=int32(m_MapSize)-1) return 0;
CPatch* patch=GetPatch(x/16,z/16);
return &patch->m_MiniPatches[z%16][x%16];
}
///////////////////////////////////////////////////////////////////////////////
// Resize: resize this terrain to the given size (in patches per side)
void CTerrain::Resize(u32 size)
{
if (size==m_MapSizePatches) {
// inexplicable request to resize terrain to the same size .. ignore it
return;
}
if (!m_Heightmap) {
// not yet created a terrain; build a default terrain of the given size now
Initialize(size,0);
return;
}
// allocate data for new terrain
u32 newMapSize=(size*PATCH_SIZE)+1;
u16* newHeightmap=new u16[newMapSize*newMapSize];
CPatch* newPatches=new CPatch[size*size];
if (size>m_MapSizePatches) {
// new map is bigger than old one - zero the heightmap so we don't get uninitialised
// height data along the expanded edges
memset(newHeightmap,0,newMapSize*newMapSize);
}
// now copy over rows of data
u32 j;
u16* src=m_Heightmap;
u16* dst=newHeightmap;
u32 copysize=newMapSize>m_MapSize ? m_MapSize : newMapSize;
for (j=0;j<copysize;j++) {
memcpy(dst,src,copysize*sizeof(u16));
dst+=copysize;
src+=m_MapSize;
if (newMapSize>m_MapSize) {
// entend the last height to the end of the row
for (u32 i=0;i<newMapSize-m_MapSize;i++) {
*dst++=*(src-1);
}
}
}
if (newMapSize>m_MapSize) {
// copy over heights of the last row to any remaining rows
src=newHeightmap+((m_MapSize-1)*newMapSize);
dst=src+newMapSize;
for (u32 i=0;i<newMapSize-m_MapSize;i++) {
memcpy(dst,src,newMapSize*sizeof(u16));
dst+=newMapSize;
}
}
// now build new patches
for (j=0;j<size;j++) {
for (u32 i=0;i<size;i++) {
// copy over texture data from existing tiles, if possible
if (i<m_MapSizePatches && j<m_MapSizePatches) {
memcpy(newPatches[j*size+i].m_MiniPatches,m_Patches[j*m_MapSizePatches+i].m_MiniPatches,sizeof(CMiniPatch)*16*16);
}
}
if (j<m_MapSizePatches && size>m_MapSizePatches) {
// copy over the last tile from each column
for (u32 n=0;n<size-m_MapSizePatches;n++) {
for (int m=0;m<16;m++) {
CMiniPatch& src=m_Patches[j*m_MapSizePatches+m_MapSizePatches-1].m_MiniPatches[m][15];
for (int k=0;k<16;k++) {
CMiniPatch& dst=newPatches[j*size+m_MapSizePatches+n].m_MiniPatches[m][k];
dst.Tex1=src.Tex1;
dst.Tex1Priority=src.Tex1Priority;
}
}
}
}
}
if (size>m_MapSizePatches) {
// copy over the last tile from each column
CPatch* srcpatch=&newPatches[(m_MapSizePatches-1)*size];
CPatch* dstpatch=srcpatch+size;
for (u32 p=0;p<size-m_MapSizePatches;p++) {
for (u32 n=0;n<size;n++) {
for (int m=0;m<16;m++) {
for (int k=0;k<16;k++) {
CMiniPatch& src=srcpatch->m_MiniPatches[15][k];
CMiniPatch& dst=dstpatch->m_MiniPatches[m][k];
dst.Tex1=src.Tex1;
dst.Tex1Priority=src.Tex1Priority;
}
}
srcpatch++;
dstpatch++;
}
}
}
// release all the original data
ReleaseData();
// store new data
m_Heightmap=newHeightmap;
m_Patches=newPatches;
m_MapSize=newMapSize;
m_MapSizePatches=size;
// initialise all the new patches
InitialisePatches();
}
///////////////////////////////////////////////////////////////////////////////
// InitialisePatches: initialise patch data
void CTerrain::InitialisePatches()
{
for (u32 j=0;j<m_MapSizePatches;j++) {
for (u32 i=0;i<m_MapSizePatches;i++) {
CPatch* patch=GetPatch(i,j);
patch->Initialize(this,i,j);
}
}
}
///////////////////////////////////////////////////////////////////////////////
// SetHeightMap: set up a new heightmap from 16-bit source data;
// assumes heightmap matches current terrain size
void CTerrain::SetHeightMap(u16* heightmap)
{
// keep a copy of the given heightmap
memcpy(m_Heightmap,heightmap,m_MapSize*m_MapSize*sizeof(u16));
// recalculate patch bounds, invalidate vertices
for (u32 j=0;j<m_MapSizePatches;j++) {
for (u32 i=0;i<m_MapSizePatches;i++) {
CPatch* patch=GetPatch(i,j);
patch->CalcBounds();
patch->SetDirty(RENDERDATA_UPDATE_VERTICES);
}
}
}

82
source/graphics/Terrain.h Executable file
View File

@ -0,0 +1,82 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: Terrain.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _TERRAIN_H
#define _TERRAIN_H
#include "Patch.h"
#include "Vector3D.h"
///////////////////////////////////////////////////////////////////////////////
// Terrain Constants:
//
// PATCH_SIZE: number of tiles in each patch
const int PATCH_SIZE = 16;
// CELL_SIZE: size of each tile in x and z
const int CELL_SIZE = 4;
// HEIGHT_SCALE: vertical scale of terrain - terrain has a coordinate range of
// 0 to 65536*HEIGHT_SCALE
const float HEIGHT_SCALE = 0.35f/256.0f;
///////////////////////////////////////////////////////////////////////////////
// CTerrain: main terrain class; contains the heightmap describing elevation
// data, and the smaller subpatches that form the terrain
class CTerrain
{
public:
CTerrain();
~CTerrain();
bool Initialize(u32 size,const u16* ptr);
// return number of vertices along edge of the terrain
u32 GetVerticesPerSide() { return m_MapSize; }
// return number of patches along edge of the terrain
u32 GetPatchesPerSide() { return m_MapSizePatches; }
// resize this terrain such that each side has given number of patches
void Resize(u32 size);
// set up a new heightmap from 16 bit data; assumes heightmap matches current terrain size
void SetHeightMap(u16* heightmap);
// return a pointer to the heightmap
u16* GetHeightMap() const { return m_Heightmap; }
// get patch at given coordinates, expressed in patch-space; return 0 if
// coordinates represent patch off the edge of the map
CPatch* GetPatch(int32 x,int32 z);
// get tile at given coordinates, expressed in tile-space; return 0 if
// coordinates represent tile off the edge of the map
CMiniPatch* GetTile(int32 x,int32 z);
// calculate the position of a given vertex
void CalcPosition(u32 i,u32 j,CVector3D& pos);
// calculate the normal at a given vertex
void CalcNormal(u32 i,u32 j,CVector3D& normal);
private:
// delete any data allocated by this terrain
void ReleaseData();
// setup patch pointers etc
void InitialisePatches();
// size of this map in each direction, in vertices; ie. total tiles = sqr(m_MapSize-1)
u32 m_MapSize;
// size of this map in each direction, in patches; total patches = sqr(m_MapSizePatches)
u32 m_MapSizePatches;
// the patches comprising this terrain
CPatch* m_Patches;
// 16-bit heightmap data
u16* m_Heightmap;
};
extern CTerrain g_Terrain;
#endif

35
source/graphics/Texture.h Executable file
View File

@ -0,0 +1,35 @@
//-----------------------------------------------------------
//
// Name: Texture.h
// Last Update: 25/11/03
// Author: Rich Cross
// Contact: rich@0ad.wildfiregames.com
//
// Description: Basic texture class
//
//-----------------------------------------------------------
#ifndef _TEXTURE_H
#define _TEXTURE_H
#include "res/res.h"
#include "CStr.h"
class CTexture
{
public:
CTexture() : m_Handle(0) {}
CTexture(const char* name) : m_Name(name), m_Handle(0) {}
void SetName(const char* name) { m_Name=name; }
const char* GetName() const { return (const char*) m_Name; }
Handle GetHandle() const { return m_Handle; }
void SetHandle(Handle handle) { m_Handle=handle; }
private:
CStr m_Name;
Handle m_Handle;
};
#endif

24
source/graphics/TextureEntry.h Executable file
View File

@ -0,0 +1,24 @@
#ifndef _TEXTUREENTRY_H
#define _TEXTUREENTRY_H
#include "res/res.h"
#include "CStr.h"
class CTextureEntry
{
public:
CTextureEntry() : m_Bitmap(0), m_Handle(0), m_BaseColor(0), m_Type(0) {}
// filename
CStr m_Name;
// UI bitmap object
void* m_Bitmap;
// handle to GL texture data
Handle m_Handle;
// BGRA color of topmost mipmap level, for coloring minimap
unsigned int m_BaseColor;
// "type" of texture - index into TextureManager texturetypes array
int m_Type;
};
#endif

View File

@ -0,0 +1,208 @@
#include "TextureManager.h"
#include "lib.h"
#include "ogl.h"
#include "res/tex.h"
#ifdef _WIN32
#include <io.h>
#endif
#include <algorithm>
const char* SupportedTextureFormats[] = { "png", "dds", "tga", "bmp" };
CTextureManager::CTextureManager()
{
m_TerrainTextures.reserve(32);
}
CTextureManager::~CTextureManager()
{
for (size_t i=0;i<m_TerrainTextures.size();i++) {
for (size_t j=0;j<m_TerrainTextures[i].m_Textures.size();j++) {
delete m_TerrainTextures[i].m_Textures[j];
}
}
}
void CTextureManager::AddTextureType(const char* name)
{
m_TerrainTextures.resize(m_TerrainTextures.size()+1);
STextureType& ttype=m_TerrainTextures.back();
ttype.m_Name=name;
ttype.m_Index=m_TerrainTextures.size()-1;
}
CTextureEntry* CTextureManager::FindTexture(const char* filename)
{
// check if file already loaded
for (uint k=0;k<m_TerrainTextures.size();k++) {
STextureType& ttype=m_TerrainTextures[k];
for (uint i=0;i<ttype.m_Textures.size();i++) {
if (strcmp((const char*) ttype.m_Textures[i]->m_Name,filename)==0) {
return ttype.m_Textures[i];
}
}
}
return 0;
}
CTextureEntry* CTextureManager::FindTexture(Handle handle)
{
for (uint k=0;k<m_TerrainTextures.size();k++) {
STextureType& ttype=m_TerrainTextures[k];
for (uint i=0;i<ttype.m_Textures.size();i++) {
if (handle==ttype.m_Textures[i]->m_Handle) {
return ttype.m_Textures[i];
}
}
}
return 0;
}
CTextureEntry* CTextureManager::AddTexture(const char* filename,int type)
{
assert((uint)type<m_TerrainTextures.size());
CStr pathname("art/textures/terrain/types/");
pathname+=m_TerrainTextures[type].m_Name;
pathname+='/';
pathname+=filename;
Handle h=tex_load((const char*) pathname);
if (!h) {
return 0;
} else {
int tw;
int th;
tex_info(h, &tw, &th, NULL, NULL, NULL);
tw &= (tw-1);
th &= (th-1);
if (tw || th) {
return 0;
}
}
// create new texture entry
CTextureEntry* texentry=new CTextureEntry;
texentry->m_Name=filename;
texentry->m_Handle=h;
texentry->m_Type=type;
// upload texture for future GL use
tex_upload(h,GL_LINEAR_MIPMAP_LINEAR);
// setup texture to repeat
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// get root color for coloring minimap by querying root level of the texture
// (this should decompress any compressed textures for us),
// then scaling it down to a 1x1 size
// - an alternative approach of just grabbing the top level of the mipmap tree fails
// (or gives an incorrect colour) in some cases:
// - suspect bug on Radeon cards when SGIS_generate_mipmap is used
// - any textures without mipmaps
// we'll just take the basic approach here:
int width,height;
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
unsigned char* buf=new unsigned char[width*height*4];
glGetTexImage(GL_TEXTURE_2D,0,GL_BGRA_EXT,GL_UNSIGNED_BYTE,buf);
gluScaleImage(GL_BGRA_EXT,width,height,GL_UNSIGNED_BYTE,buf,
1,1,GL_UNSIGNED_BYTE,&texentry->m_BaseColor);
delete[] buf;
// add entry to list ..
m_TerrainTextures[type].m_Textures.push_back(texentry);
// .. and return it
return texentry;
}
void CTextureManager::DeleteTexture(CTextureEntry* entry)
{
// find entry in list
std::vector<CTextureEntry*>& textures=m_TerrainTextures[entry->m_Type].m_Textures;
typedef std::vector<CTextureEntry*>::iterator Iter;
Iter i=std::find(textures.begin(),textures.end(),entry);
if (i!=textures.end()) {
textures.erase(i);
}
delete entry;
}
void CTextureManager::LoadTerrainTextures(int terraintype,const char* fileext)
{
#ifdef _WIN32
struct _finddata_t file;
long handle;
// build pathname
CStr pathname("mods\\official\\art\\textures\\terrain\\types\\");
pathname+=m_TerrainTextures[terraintype].m_Name;
pathname+="\\";
CStr findname(pathname);
findname+="*.";
findname+=fileext;
// Find first matching file in directory for this terrain type
if ((handle=_findfirst((const char*) findname,&file))!=-1) {
AddTexture(file.name,terraintype);
// Find the rest of the matching files
while( _findnext(handle,&file)==0) {
AddTexture((const char*) file.name,terraintype);
}
_findclose(handle);
}
#endif
}
void CTextureManager::BuildTerrainTypes()
{
#ifdef _WIN32
struct _finddata_t file;
long handle;
// Find first matching directory in terrain\textures
if ((handle=_findfirst("mods\\official\\art\\textures\\terrain\\types\\*",&file))!=-1) {
if ((file.attrib & _A_SUBDIR) && file.name[0]!='.') {
AddTextureType(file.name);
}
// Find the rest of the matching files
while( _findnext(handle,&file)==0) {
if ((file.attrib & _A_SUBDIR) && file.name[0]!='.') {
AddTextureType(file.name);
}
}
_findclose(handle);
}
#endif
}
void CTextureManager::LoadTerrainTextures()
{
// find all the terrain types by directory name
BuildTerrainTypes();
// now iterate through terrain types loading all textures of that type
for (uint i=0;i<m_TerrainTextures.size();i++) {
for (uint j=0;j<sizeof(SupportedTextureFormats)/sizeof(const char*);j++) {
LoadTerrainTextures(i,SupportedTextureFormats[j]);
}
}
}

View File

@ -0,0 +1,51 @@
#ifndef _TEXTUREMANAGER_H
#define _TEXTUREMANAGER_H
#include <vector>
#include "CStr.h"
#include "Singleton.h"
#include "TextureEntry.h"
// access to sole CTextureManager object
#define g_TexMan CTextureManager ::GetSingleton()
///////////////////////////////////////////////////////////////////////////////////////////
// CTextureManager : manager class for all terrain texture objects
class CTextureManager : public Singleton<CTextureManager>
{
public:
struct STextureType
{
// name of this texture type (derived from directory name)
CStr m_Name;
// index in parent array
int m_Index;
// list of textures of this type (found from the texture directory)
std::vector<CTextureEntry*> m_Textures;
};
public:
// constructor, destructor
CTextureManager();
~CTextureManager();
void LoadTerrainTextures();
void AddTextureType(const char* name);
CTextureEntry* FindTexture(const char* filename);
CTextureEntry* FindTexture(Handle handle);
CTextureEntry* AddTexture(const char* filename,int type);
void DeleteTexture(CTextureEntry* entry);
std::vector<STextureType> m_TerrainTextures;
private:
void LoadTerrainTextures(int terraintype,const char* fileext);
void BuildTerrainTypes();
};
#endif

36
source/graphics/Unit.h Executable file
View File

@ -0,0 +1,36 @@
#ifndef _UNIT_H
#define _UNIT_H
#include <assert.h>
#include "Model.h"
class CObjectEntry;
/////////////////////////////////////////////////////////////////////////////////////////////
// CUnit: simple "actor" definition - defines a sole object within the world
class CUnit
{
public:
// sole constructor - unit invalid without a model and object
CUnit(CObjectEntry* object,CModel* model) : m_Object(object), m_Model(model) {
assert(object && model);
}
// destructor
~CUnit() {
delete m_Model;
}
// get unit's template object
CObjectEntry* GetObject() { return m_Object; }
// get unit's model data
CModel* GetModel() { return m_Model; }
private:
// object from which unit was created
CObjectEntry* m_Object;
// object model representation
CModel* m_Model;
};
#endif

77
source/graphics/UnitManager.cpp Executable file
View File

@ -0,0 +1,77 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: UnitManager.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "res/res.h"
#include "Model.h"
#include "UnitManager.h"
#include <algorithm>
///////////////////////////////////////////////////////////////////////////////
// CUnitManager constructor
CUnitManager::CUnitManager()
{
}
///////////////////////////////////////////////////////////////////////////////
// CUnitManager destructor
CUnitManager::~CUnitManager()
{
DeleteAll();
}
///////////////////////////////////////////////////////////////////////////////
// AddUnit: add given unit to world
void CUnitManager::AddUnit(CUnit* unit)
{
m_Units.push_back(unit);
}
///////////////////////////////////////////////////////////////////////////////
// RemoveUnit: remove given unit from world, but don't delete it
void CUnitManager::RemoveUnit(CUnit* unit)
{
// find entry in list
typedef std::vector<CUnit*>::iterator Iter;
Iter i=std::find(m_Units.begin(),m_Units.end(),unit);
if (i!=m_Units.end()) {
m_Units.erase(i);
}
}
///////////////////////////////////////////////////////////////////////////////
// DeleteAll: remove and delete all units
void CUnitManager::DeleteAll()
{
for (uint i=0;i<m_Units.size();i++) {
delete m_Units[i];
}
m_Units.clear();
}
///////////////////////////////////////////////////////////////////////////////
// PickUnit: iterate through units testing given ray against bounds of each
// unit; return the closest unit, or null if everything missed
CUnit* CUnitManager::PickUnit(const CVector3D& origin,const CVector3D& dir) const
{
// closest object found so far
CUnit* hit=0;
// distance to closest object found so far
float dist=1.0e30f;
for (uint i=0;i<m_Units.size();i++) {
CUnit* unit=m_Units[i];
float tmin,tmax;
if (unit->GetModel()->GetBounds().RayIntersect(origin,dir,tmin,tmax)) {
if (!hit || tmin<dist) {
hit=unit;
dist=tmin;
}
}
}
return hit;
}

49
source/graphics/UnitManager.h Executable file
View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: UnitManager.h
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _UNITMANAGER_H
#define _UNITMANAGER_H
#include <vector>
#include "Unit.h"
#include "Singleton.h"
class CVector3D;
// access to sole CUnitManager object
#define g_UnitMan CUnitManager::GetSingleton()
///////////////////////////////////////////////////////////////////////////////
// CUnitManager: simple container class holding all units within the world
class CUnitManager : public Singleton<CUnitManager>
{
public:
// constructor, destructor
CUnitManager();
~CUnitManager();
// add given unit to world
void AddUnit(CUnit* unit);
// remove given unit from world, but don't delete it
void RemoveUnit(CUnit* unit);
// remove and delete all units
void DeleteAll();
// return the units
const std::vector<CUnit*>& GetUnits() const { return m_Units; }
// iterate through units testing given ray against bounds of each unit;
// return the closest unit, or null if everything missed
CUnit* PickUnit(const CVector3D& origin,const CVector3D& dir) const;
private:
// list of all known units
std::vector<CUnit*> m_Units;
};
#endif