2006-12-10

This commit is contained in:
Anonymous Maarten 2015-12-03 01:37:02 +01:00
commit 78c27f03c8
153 changed files with 24678 additions and 0 deletions

1237
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

12
blockdata.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "blockdata.h"
namespace OpenGTA {
float BlockData::slope_raw_data[numBlockTypes][numFaces][4][3] = {
#include "slope1_data.h"
};
float BlockData::slope_tex_data[numBlockTypes][numFaces-1][4][2] = {
#include "slope1_tcoords.h"
};
float BlockData::lid_normal_data[numBlockTypes][3] = {
#include "lid_normal_data.h"
};
}

25
blockdata.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef OPENGTA_BLOCKDATA
#define OPENGTA_BLOCKDATA
#include <cstddef>
#include "Singleton.h"
namespace OpenGTA {
struct BlockData {
static const size_t numBlockTypes = 45;
static const size_t numFaces = 5;
static float slope_raw_data[numBlockTypes][numFaces][4][3];
static float slope_tex_data[numBlockTypes][numFaces-1][4][2];
static float lid_normal_data[numBlockTypes][3];
};
typedef Loki::SingletonHolder<BlockData, Loki::CreateUsingNew,
Loki::DefaultLifetime, Loki::SingleThreaded> BlockDataHolder;
}
#define SLOPE_RAW_DATA BlockDataHolder::Instance().slope_raw_data
#define SLOPE_TEX_DATA BlockDataHolder::Instance().slope_tex_data
#define LID_NORMAL_DATA BlockDataHolder::Instance().lid_normal_data
#endif

281
coldet/box.cpp Normal file
View File

@ -0,0 +1,281 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#include "sysdep.h"
#include "box.h"
#include "mytritri.h"
__CD__BEGIN
////////////////////////////////////////////////////
// code from here is used in detection process
int BoxTreeInnerNode::getTrianglesNumber()
{
return m_Boxes.size();
}
BoxedTriangle* BoxTreeInnerNode::getTriangle(int which)
{
if (which<0 || which>=getTrianglesNumber()) return NULL;
return m_Boxes[which];
}
RotationState::RotationState(const Matrix3D& transform)
: t(transform)
{
N[0]=Vector3D(t._11,t._12,t._13);
N[1]=Vector3D(t._21,t._22,t._23);
N[2]=Vector3D(t._31,t._32,t._33);
}
inline float DotWithCol(const Vector3D& v, const Matrix3& m, int col)
{
return v.x*m(0,col) + v.y*m(1,col) + v.z*m(2,col);
}
bool Box::intersect(const Vector3D& O, float radius)
{
Vector3D mx=m_Pos+m_Size;
float dist=0.0f;
for(int i=0;i<3;i++)
{
if (O[i] < m_Pos[i])
{
float d=O[i]-m_Pos[i];
dist+=d*d;
}
else
if (O[i] > mx[i])
{
float d=O[i]-mx[i];
dist+=d*d;
}
}
return (dist <= (radius*radius));
}
bool Box::intersect(const Vector3D& O, const Vector3D& D,
float segmax)
{
if (segmax>3e30f) return intersect(O,D); // infinite ray
Vector3D abs_segdir, abs_diff, abs_cross;
Vector3D segdir=0.5f*segmax*D;
Vector3D seg_center=O+segdir;
Vector3D diff=seg_center - getCenter();
int i;
for(i=0;i<3;i++)
{
abs_segdir[i]=flabs(segdir[i]);
abs_diff[i]=flabs(diff[i]);
float f=getSize()[i] + abs_segdir[i];
if (abs_diff[i] > f) return false;
}
Vector3D cross=CrossProduct(segdir,diff);
int idx[] = {0,1,2,0,1};
for(i=0;i<3;i++)
{
int i1=idx[i+1];
int i2=idx[i+2];
abs_cross[i] = flabs(cross[i]);
float f = getSize()[i1]*abs_segdir[i2] + getSize()[i2]*abs_segdir[i1];
if ( abs_cross[i] > f ) return false;
}
return true;
}
bool Box::intersect(const Vector3D& O, const Vector3D& D)
{
Vector3D abs_segdir, abs_cross;
float f;
Vector3D diff = O - getCenter();
for(int i=0;i<3;i++)
{
abs_segdir[i] = flabs(D[i]);
if ( flabs(diff[i])>m_Size[i] && diff[i]*D[i]>=0.0f )
return false;
}
Vector3D cross = CrossProduct(D,diff);
abs_cross[0] = flabs(cross[0]);
f = m_Size[1]*abs_segdir[2] + m_Size[2]*abs_segdir[1];
if ( abs_cross[0] > f )
return false;
abs_cross[1] = flabs(cross[1]);
f = m_Size[0]*abs_segdir[2] + m_Size[2]*abs_segdir[0];
if ( abs_cross[1] > f )
return false;
abs_cross[2] = flabs(cross[2]);
f = m_Size[0]*abs_segdir[1] + m_Size[1]*abs_segdir[0];
if ( abs_cross[2] > f )
return false;
return true;
}
bool Box::intersect(const Box& b, RotationState& rs)
{
const Vector3D bCenter=Transform(b.getCenter(),rs.t);
Vector3D EA=0.5f*getSize();
Vector3D EB=0.5f*b.getSize();
Vector3D distance=bCenter-getCenter();
Matrix3 C,abs_C;
float R0,R1,R,R01;
int i;
for(i=0;i<3;i++)
{
C(i,0)=rs.N[0][i];
C(i,1)=rs.N[1][i];
C(i,2)=rs.N[2][i];
abs_C(i,0)=flabs(C(i,0));
abs_C(i,1)=flabs(C(i,1));
abs_C(i,2)=flabs(C(i,2));
R=flabs(distance[i]);
R1=EB*abs_C.baseRow(i);
R01=EA[i]+R1;
if (R>R01) return false;
}
for(i=0;i<3;i++)
{
R=flabs(rs.N[i]*distance);
R0=DotWithCol(EA,abs_C,i);
R01=R0+EB[i];
if (R>R01) return false;
}
R=flabs(distance.z*C(1,0) - distance.y*C(2,0));
R0=EA.y*abs_C(2,0) + EA.z*abs_C(1,0);
R1=EB.y*abs_C(0,2) + EB.z*abs_C(0,1);
R01=R0+R1;
if (R>R01) return false;
R=flabs(distance.z*C(1,1) - distance.y*C(2,1));
R0=EA.y*abs_C(2,1) + EA.z*abs_C(1,1);
R1=EB.x*abs_C(0,2) + EB.z*abs_C(0,0);
R01=R0+R1;
if (R>R01) return false;
R=flabs(distance.z*C(1,2) - distance.y*C(2,2));
R0=EA.y*abs_C(2,2) + EA.z*abs_C(1,2);
R1=EB.x*abs_C(0,1) + EB.y*abs_C(0,0);
R01=R0+R1;
if (R>R01) return false;
R=flabs(distance.x*C(2,0) - distance.z*C(0,0));
R0=EA.x*abs_C(2,0) + EA.z*abs_C(0,0);
R1=EB.y*abs_C(1,2) + EB.z*abs_C(1,1);
R01=R0+R1;
if (R>R01) return false;
R=flabs(distance.x*C(2,1) - distance.z*C(0,1));
R0=EA.x*abs_C(2,1) + EA.z*abs_C(0,1);
R1=EB.x*abs_C(1,2) + EB.z*abs_C(1,0);
R01=R0+R1;
if (R>R01) return false;
R=flabs(distance.x*C(2,2) - distance.z*C(0,2));
R0=EA.x*abs_C(2,2) + EA.z*abs_C(0,2);
R1=EB.x*abs_C(1,1) + EB.y*abs_C(1,0);
R01=R0+R1;
if (R>R01) return false;
R=flabs(distance.y*C(0,0) - distance.x*C(1,0));
R0=EA.x*abs_C(1,0) + EA.y*abs_C(0,0);
R1=EB.y*abs_C(2,2) + EB.z*abs_C(2,1);
R01=R0+R1;
if (R>R01) return false;
R=flabs(distance.y*C(0,1) - distance.x*C(1,1));
R0=EA.x*abs_C(1,1) + EA.y*abs_C(0,1);
R1=EB.x*abs_C(2,2) + EB.z*abs_C(2,0);
R01=R0+R1;
if (R>R01) return false;
R=flabs(distance.y*C(0,2) - distance.x*C(1,2));
R0=EA.x*abs_C(1,2) + EA.y*abs_C(0,2);
R1=EB.x*abs_C(2,1) + EB.y*abs_C(2,0);
R01=R0+R1;
if (R>R01) return false;
return true;
}
extern "C" {
int tri_tri_intersect(float V0[3],float V1[3],float V2[3],
float U0[3],float U1[3],float U2[3]);
};
Triangle::Triangle(const Vector3D& _1, const Vector3D& _2, const Vector3D& _3)
: v1(_1), v2(_2), v3(_3), center((1.0f/3.0f)*(_1+_2+_3))
{}
bool Triangle::intersect(const Vector3D& O, const Vector3D& D, Vector3D& cp,
float& tparm, float segmax)
{
Plane p(v1,v2,v3);
float denom=p.normal*D;
if (IsZero(denom)) return false;
float t=-(p.d+p.normal*O)/denom;
if (t<=0.0f) return false;
if (t>segmax) return false;
TriangleDesc td(*this,p);
cp=O+t*D;
if (td.pointInTri(cp))
{
tparm=t;
return true;
}
return false;
}
bool Triangle::intersect(const Vector3D& O, float radius, Vector3D& cp)
{
Plane p(v1,v2,v3);
float dist=p.Classify(O);
if (flabs(dist) > radius) return false;
Vector3D point=O-dist*p.normal;
TriangleDesc td(*this,p);
if (td.pointInTri(point))
{
cp=point;
return true;
}
return false;
}
bool Triangle::intersect(const Triangle& t) const
{
return (tri_tri_intersect((float*)&v1.x,
(float*)&v2.x,
(float*)&v3.x,
(float*)&t.v1.x,
(float*)&t.v2.x,
(float*)&t.v3.x) != 0);
}
__CD__END

205
coldet/box.h Normal file
View File

@ -0,0 +1,205 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#ifndef H_BOX
#define H_BOX
#include <vector>
#include "math3d.h"
#include "sysdep.h"
__CD__BEGIN
/** Stores rotation vectors used in the intersection tests,
to avoid recalculating them each time. */
class RotationState
{
public:
RotationState(const Matrix3D& transform);
Vector3D N[3];
Matrix3D t;
};
/** AABB class, with support for testing against OBBs. */
class Box
{
public:
/** Default constructor */
Box() {}
/** Construct from scalar corner position and size */
Box(float x, float y, float z, float sx, float sy, float sz)
: m_Pos(x,y,z), m_Size(sx,sy,sz),
m_Center(x+0.5f*sx,y+0.5f*sy,z+0.5f*sz) {}
/** Construct from corner position and size */
Box(const Vector3D& pos, const Vector3D& size)
: m_Pos(pos), m_Size(size), m_Center(pos+0.5f*size) {}
/** Copy constructor */
Box(const Box& b) : m_Pos(b.m_Pos), m_Size(b.m_Size), m_Center(b.m_Center) {}
virtual ~Box() {}
/** Returns the box's position */
const Vector3D& getPosition() const { return m_Pos; }
/** Returns the sizes of the box's edges */
const Vector3D& getSize() const { return m_Size; }
/** Returns the center position of the box */
const Vector3D& getCenter() const { return m_Center; }
/** Returns the volume of the box */
float getVolume() const { return m_Size.x*m_Size.y*m_Size.z; }
/** Ray intersection */
bool intersect(const Vector3D& O, const Vector3D& D);
/** Line segment intersection */
bool intersect(const Vector3D& O, const Vector3D& D, float segmax);
/** Sphere intersection */
bool intersect(const Vector3D& O, float radius);
/** Point in box */
bool intersect(const Vector3D& p) const;
/** Aligned box intersection */
bool intersect(const Box& b);
/** Oriented box intersection. */
bool intersect(const Box& b, RotationState& rs);
/** Position of box corner */
Vector3D m_Pos;
/** Size of box box edges */
Vector3D m_Size;
/** Position of box center. m_Pos+0.5f*m_Size; */
Vector3D m_Center;
};
/** A single triangle in the model */
class Triangle
{
public:
/** Default constructor */
Triangle() {}
/** Constructor to build a triangle from 3 points */
Triangle(const Vector3D& _1, const Vector3D& _2, const Vector3D& _3);
/** Tests for intersection with another triangle. */
bool intersect(const Triangle& t) const;
/** Tests for intersection with a ray (O origin, D direction)
Returns true if collision occured.
Outputs collision point in cp
Outputs the distance from the origin to the collision point in tparm
This distance is relative to the magnitude of D
Allows testing against a finite segment, by specifying
the maximum length of the ray in segmax
This length is also relative to the magnitude of D
*/
bool intersect(const Vector3D& O, const Vector3D& D, Vector3D& cp,
float& tparm, float segmax);
/** Test for intersection with a sphere (O origin)
Returns true if collision occured.
Outputs collision point in cp
*/
bool intersect(const Vector3D& O, float radius, Vector3D& cp);
Vector3D v1,v2,v3;
Vector3D center;
};
class BoxedTriangle;
/** Base class for hierarchy tree nodes. */
class BoxTreeNode : public Box
{
public:
/** Default constructor */
BoxTreeNode() : Box() {}
/** Constructor for a box from position and size */
BoxTreeNode(const Vector3D& pos, const Vector3D& size)
: Box(pos,size) {}
/** Returns true if the node is a leaf node. */
virtual bool isLeaf() const = 0;
/** Returns the number of sons this node has */
virtual int getSonsNumber() = 0;
/** Returns a son node, by index */
virtual BoxTreeNode* getSon(int which) = 0;
/** Returns the number of triangles in this node.
Only non-zero for leaf nodes. */
virtual int getTrianglesNumber() = 0;
/** Returns the boxed triangle contained in this node
by its index
*/
virtual BoxedTriangle* getTriangle(int which) = 0;
};
/** Inner node, containing other nodes. */
class BoxTreeInnerNode : public BoxTreeNode
{
public:
BoxTreeInnerNode(const Vector3D& pos, const Vector3D& size, int logdepth)
: BoxTreeNode(pos,size), m_First(NULL), m_Second(NULL),
m_logdepth(logdepth) {};
virtual bool isLeaf() const { return false; }
/** Create the sons that will divide this box */
int createSons(const Vector3D& center);
/** Recalculate the bounds of this box to fully contain
all of its triangles
*/
void recalcBounds(Vector3D& center);
/** Recursively divide this box */
int divide(int p_depth);
int getSonsNumber()
{
int n=0;
if (m_First!=NULL) n++;
if (m_Second!=NULL) n++;
return n;
}
int getTrianglesNumber();
BoxedTriangle* getTriangle(int which);
BoxTreeNode* getSon(int which)
{
if (which==0) return m_First;
if (which==1) return m_Second;
return NULL;
}
BoxTreeNode* m_First;
BoxTreeNode* m_Second;
int m_logdepth;
std::vector<BoxedTriangle*> m_Boxes;
};
/** Leaf node, containing 1 triangle. */
class BoxedTriangle : public BoxTreeNode, public Triangle
{
public:
BoxedTriangle(const Vector3D& _1, const Vector3D& _2, const Vector3D& _3);
virtual bool isLeaf() const { return true; }
int getSonsNumber() { return 0; }
BoxTreeNode* getSon(int which) { return NULL; }
int getTrianglesNumber() { return 1; }
BoxedTriangle* getTriangle(int which)
{
if (which==0) return this;
return NULL;
}
};
__CD__END
#endif // H_BOX

197
coldet/box_bld.cpp Normal file
View File

@ -0,0 +1,197 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#include "sysdep.h"
#include "box.h"
__CD__BEGIN
// point in box test
bool Box::intersect(const Vector3D& p) const
{
const Vector3D& pos=getPosition();
const Vector3D& s=getSize();
if (p.x<pos.x || p.x>(pos.x+s.x)) return false;
if (p.y<pos.y || p.y>(pos.y+s.y)) return false;
if (p.z<pos.z || p.z>(pos.z+s.z)) return false;
return true;
}
// Non-oriented intersection test
bool Box::intersect(const Box& b)
{
const Vector3D& t1=getPosition();
Vector3D t2=getPosition()+getSize();
const Vector3D& p1=b.getPosition();
Vector3D p2=b.getPosition()+b.getSize();
return (Max(p1.x,t1.x) <= Min(p2.x,t2.x) &&
Max(p1.y,t1.y) <= Min(p2.y,t2.y) &&
Max(p1.z,t1.z) <= Min(p2.z,t2.z));
}
BoxedTriangle::BoxedTriangle(const Vector3D& _1,
const Vector3D& _2,
const Vector3D& _3)
: BoxTreeNode(), Triangle(_1,_2,_3)
{
m_Pos.x=Min(Min(_1.x,_2.x),_3.x);
m_Pos.y=Min(Min(_1.y,_2.y),_3.y);
m_Pos.z=Min(Min(_1.z,_2.z),_3.z);
Vector3D mx;
mx.x=Max(Max(_1.x,_2.x),_3.x);
mx.y=Max(Max(_1.y,_2.y),_3.y);
mx.z=Max(Max(_1.z,_2.z),_3.z);
m_Size=mx-getPosition();
m_Center=getPosition()+0.5f*getSize();
}
int BoxTreeInnerNode::createSons(const Vector3D& center)
{
int longest=0;
Vector3D p=getPosition();
Vector3D s=getSize();
if (1)
{
Vector3D dist(Vector3D::Zero);
for(unsigned i=0;i<m_Boxes.size();i++)
{
BoxedTriangle* bt=m_Boxes[i];
dist.x+=flabs(bt->center.x - center.x);
dist.y+=flabs(bt->center.y - center.y);
dist.z+=flabs(bt->center.z - center.z);
}
if (dist.y>dist.x && dist.y>dist.z) longest=1;
else
if (dist.z>dist.x && dist.z>dist.y) longest=2;
}
else
{
int dist[3];
dist[0]=dist[1]=dist[2]=0;
for(unsigned i=0;i<m_Boxes.size();i++)
{
BoxedTriangle* bt=m_Boxes[i];
for(int j=0;j<3;j++)
if (bt->center[j] > center[j]) dist[j]++;
else dist[j]--;
}
for(int j=0;j<3;j++)
dist[j]=abs(dist[j]);
if (dist[1]<dist[0] && dist[1]<dist[2]) longest=1;
else
if (dist[2]<dist[0] && dist[2]<dist[1]) longest=2;
}
float s1=center[longest]-p[longest];
float s2=s[longest]-s1;
s[longest]=s1;
m_First=new BoxTreeInnerNode(p,s,m_logdepth);
p[longest]+=s1;
s[longest]=s2;
m_Second=new BoxTreeInnerNode(p,s,m_logdepth);
return longest;
}
void BoxTreeInnerNode::recalcBounds(Vector3D& center)
{
if (m_Boxes.empty()) return;
center=Vector3D::Zero;
Vector3D mn(9e9f,9e9f,9e9f),mx(-9e9f,-9e9f,-9e9f);
for(unsigned i=0;i<m_Boxes.size();i++)
{
BoxedTriangle* bt=m_Boxes[i];
center+=bt->center;
mn.x=Min(Min(Min(bt->v1.x,bt->v2.x),bt->v3.x),mn.x);
mn.y=Min(Min(Min(bt->v1.y,bt->v2.y),bt->v3.y),mn.y);
mn.z=Min(Min(Min(bt->v1.z,bt->v2.z),bt->v3.z),mn.z);
mx.x=Max(Max(Max(bt->v1.x,bt->v2.x),bt->v3.x),mx.x);
mx.y=Max(Max(Max(bt->v1.y,bt->v2.y),bt->v3.y),mx.y);
mx.z=Max(Max(Max(bt->v1.z,bt->v2.z),bt->v3.z),mx.z);
}
center/=float(m_Boxes.size());
m_Pos=mn;
m_Size=mx-mn;
if (m_Size.x==0.0f) { m_Size.x=0.002f; m_Pos.x-=0.001f; }
if (m_Size.y==0.0f) { m_Size.y=0.002f; m_Pos.y-=0.001f; }
if (m_Size.z==0.0f) { m_Size.z=0.002f; m_Pos.z-=0.001f; }
m_Center=getPosition()+0.5f*getSize();
}
int BoxTreeInnerNode::divide(int p_depth)
{
if (m_Boxes.empty()) return 0;
Vector3D center;
recalcBounds(center);
int longest=createSons(center);
BoxTreeInnerNode* f=static_cast<BoxTreeInnerNode*>(m_First);
BoxTreeInnerNode* s=static_cast<BoxTreeInnerNode*>(m_Second);
int depth=1;
int bnum=m_Boxes.size();
#ifdef _DEBUG
int fnum=0;
#endif
for(unsigned i=0;i<bnum;i++)
{
BoxedTriangle* bt=m_Boxes[i];
if (bt->center[longest]<center[longest])
{
f->m_Boxes.push_back(bt);
#ifdef _DEBUG
fnum++;
#endif
}
else
{
s->m_Boxes.push_back(bt);
}
}
int b1num=f->m_Boxes.size();
int b2num=s->m_Boxes.size();
if ((b1num==bnum || b2num==bnum))// && p_depth>m_logdepth)
{
delete m_First; m_First=NULL;
delete m_Second; m_Second=NULL;
return depth+1;
}
m_Boxes.clear();
if (f->m_Boxes.empty()) { delete m_First; m_First=NULL; }
else
if (f->m_Boxes.size()==1)
{
BoxedTriangle* bt=f->m_Boxes.back();
delete m_First;
m_First=bt;
} else depth=f->divide(p_depth+1);
if (s->m_Boxes.empty()) { delete m_Second; m_Second=NULL; }
else
if (s->m_Boxes.size()==1)
{
BoxedTriangle* bt=s->m_Boxes.back();
delete m_Second;
m_Second=bt;
} else depth=Max(depth,s->divide(p_depth+1));
return depth+1;
}
__CD__BEGIN

389
coldet/coldet.cpp Normal file
View File

@ -0,0 +1,389 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#include "sysdep.h"
#include "coldetimpl.h"
#include "mytritri.h"
#include <assert.h>
__CD__BEGIN
class Check
{
public:
Check() {}
Check(BoxTreeNode* f, BoxTreeNode* s, int d)
: m_first(f), m_second(s), depth(d) {}
BoxTreeNode* m_first;
BoxTreeNode* m_second;
int depth;
};
bool CollisionModel3DImpl::collision(CollisionModel3D* other,
int AccuracyDepth,
int MaxProcessingTime,
float* other_transform)
{
m_ColType=Models;
CollisionModel3DImpl* o=static_cast<CollisionModel3DImpl*>(other);
if (!m_Final) throw Inconsistency();
if (!o->m_Final) throw Inconsistency();
Matrix3D t=( other_transform==NULL ? o->m_Transform : *((Matrix3D*)other_transform) );
if (m_Static) t *= m_InvTransform;
else t *= m_Transform.Inverse();
RotationState rs(t);
if (AccuracyDepth<0) AccuracyDepth=0xFFFFFF;
if (MaxProcessingTime==0) MaxProcessingTime=0xFFFFFF;
DWORD EndTime,BeginTime = GetTickCount();
int num=Max(m_Triangles.size(),o->m_Triangles.size());
int Allocated=Max(64,(num>>4));
std::vector<Check> checks(Allocated);
int queue_idx=1;
Check& c=checks[0];
c.m_first=&m_Root;
c.depth=0;
c.m_second=&o->m_Root;
while (queue_idx>0)
{
if (queue_idx>(Allocated/2)) // enlarge the queue.
{
Check c;
checks.insert(checks.end(),Allocated,c);
Allocated*=2;
}
EndTime=GetTickCount();
if (EndTime >= (BeginTime+MaxProcessingTime)) throw TimeoutExpired();
// @@@ add depth check
//Check c=checks.back();
Check& c=checks[--queue_idx];
BoxTreeNode* first=c.m_first;
BoxTreeNode* second=c.m_second;
assert(first!=NULL);
assert(second!=NULL);
if (first->intersect(*second,rs))
{
int tnum1=first->getTrianglesNumber();
int tnum2=second->getTrianglesNumber();
if (tnum1>0 && tnum2>0)
{
{
for(int i=0;i<tnum2;i++)
{
BoxedTriangle* bt2=second->getTriangle(i);
Triangle tt(Transform(bt2->v1,rs.t),Transform(bt2->v2,rs.t),Transform(bt2->v3,rs.t));
for(int j=0;j<tnum1;j++)
{
BoxedTriangle* bt1=first->getTriangle(j);
if (tt.intersect(*bt1))
{
m_ColTri1=*bt1;
m_iColTri1=getTriangleIndex(bt1);
m_ColTri2=tt;
m_iColTri2=o->getTriangleIndex(bt2);
return true;
}
}
}
}
}
else
if (first->getSonsNumber()==0)
{
BoxTreeNode* s1=second->getSon(0);
BoxTreeNode* s2=second->getSon(1);
assert(s1!=NULL);
assert(s2!=NULL);
Check& c1=checks[queue_idx++];
c1.m_first=first;
c1.m_second=s1;
Check& c2=checks[queue_idx++];
c2.m_first=first;
c2.m_second=s2;
}
else
if (second->getSonsNumber()==0)
{
BoxTreeNode* f1=first->getSon(0);
BoxTreeNode* f2=first->getSon(1);
assert(f1!=NULL);
assert(f2!=NULL);
Check& c1=checks[queue_idx++];
c1.m_first=f1;
c1.m_second=second;
Check& c2=checks[queue_idx++];
c2.m_first=f2;
c2.m_second=second;
}
else
{
float v1=first->getVolume();
float v2=second->getVolume();
if (v1>v2)
{
BoxTreeNode* f1=first->getSon(0);
BoxTreeNode* f2=first->getSon(1);
assert(f1!=NULL);
assert(f2!=NULL);
Check& c1=checks[queue_idx++];
c1.m_first=f1;
c1.m_second=second;
Check& c2=checks[queue_idx++];
c2.m_first=f2;
c2.m_second=second;
}
else
{
BoxTreeNode* s1=second->getSon(0);
BoxTreeNode* s2=second->getSon(1);
assert(s1!=NULL);
assert(s2!=NULL);
Check& c1=checks[queue_idx++];
c1.m_first=first;
c1.m_second=s1;
Check& c2=checks[queue_idx++];
c2.m_first=first;
c2.m_second=s2;
}
}
}
}
return false;
}
bool CollisionModel3DImpl::rayCollision(float origin[3],
float direction[3],
bool closest,
float segmin,
float segmax)
{
float mintparm=9e9f,tparm;
Vector3D col_point;
m_ColType=Ray;
Vector3D O;
Vector3D D;
if (m_Static)
{
O=Transform(*(Vector3D*)origin,m_InvTransform);
D=rotateVector(*(Vector3D*)direction,m_InvTransform);
}
else
{
Matrix3D inv=m_Transform.Inverse();
O=Transform(*(Vector3D*)origin,inv);
D=rotateVector(*(Vector3D*)direction,inv);
}
if (segmin!=0.0f) // normalize ray
{
O+=segmin*D;
segmax-=segmin;
segmin=0.0f;
}
if (segmax<segmin)
{
D=-D;
segmax=-segmax;
}
std::vector<BoxTreeNode*> checks;
checks.push_back(&m_Root);
while (!checks.empty())
{
BoxTreeNode* b=checks.back();
checks.pop_back();
if (b->intersect(O,D,segmax))
{
int sons=b->getSonsNumber();
if (sons)
while (sons--) checks.push_back(b->getSon(sons));
else
{
int tri=b->getTrianglesNumber();
while (tri--)
{
BoxedTriangle* bt=b->getTriangle(tri);
Triangle* t=static_cast<Triangle*>(bt);
if (t->intersect(O,D,col_point,tparm,segmax))
{
if (closest)
{
if (tparm<mintparm)
{
mintparm=tparm;
m_ColTri1=*bt;
m_iColTri1=getTriangleIndex(bt);
m_ColPoint=col_point;
}
}
else
{
m_ColTri1=*bt;
m_iColTri1=getTriangleIndex(bt);
m_ColPoint=col_point;
return true;
}
}
}
}
}
}
if (closest && mintparm<9e9f) return true;
return false;
}
bool CollisionModel3DImpl::sphereCollision(float origin[3], float radius)
{
m_ColType=Sphere;
Vector3D O;
if (m_Static)
O=Transform(*(Vector3D*)origin,m_InvTransform);
else
{
Matrix3D inv=m_Transform.Inverse();
O=Transform(*(Vector3D*)origin,inv);
}
std::vector<BoxTreeNode*> checks;
checks.push_back(&m_Root);
while (!checks.empty())
{
BoxTreeNode* b=checks.back();
checks.pop_back();
if (b->intersect(O,radius))
{
int sons=b->getSonsNumber();
if (sons)
while (sons--) checks.push_back(b->getSon(sons));
else
{
int tri=b->getTrianglesNumber();
while (tri--)
{
BoxedTriangle* bt=b->getTriangle(tri);
Triangle* t=static_cast<Triangle*>(bt);
if (t->intersect(O,radius,m_ColPoint))
{
m_ColTri1=*bt;
m_iColTri1=getTriangleIndex(bt);
return true;
}
}
}
}
}
return false;
}
bool CollisionModel3DImpl::getCollidingTriangles(float t1[9], float t2[9], bool ModelSpace)
{
if (ModelSpace)
{
if (t1!=NULL)
{
*((Vector3D*)&t1[0]) = m_ColTri1.v1;
*((Vector3D*)&t1[3]) = m_ColTri1.v2;
*((Vector3D*)&t1[6]) = m_ColTri1.v3;
}
if (t2!=NULL)
{
*((Vector3D*)&t2[0]) = m_ColTri2.v1;
*((Vector3D*)&t2[3]) = m_ColTri2.v2;
*((Vector3D*)&t2[6]) = m_ColTri2.v3;
}
}
else
{
if (t1!=NULL)
{
*((Vector3D*)&t1[0]) = Transform(m_ColTri1.v1,m_Transform);
*((Vector3D*)&t1[3]) = Transform(m_ColTri1.v2,m_Transform);
*((Vector3D*)&t1[6]) = Transform(m_ColTri1.v3,m_Transform);
}
if (t2!=NULL)
{
*((Vector3D*)&t2[0]) = Transform(m_ColTri2.v1,m_Transform);
*((Vector3D*)&t2[3]) = Transform(m_ColTri2.v2,m_Transform);
*((Vector3D*)&t2[6]) = Transform(m_ColTri2.v3,m_Transform);
}
}
return true;
}
bool CollisionModel3DImpl::getCollidingTriangles(int& t1, int& t2)
{
t1=m_iColTri1;
t2=m_iColTri2;
return true;
}
bool CollisionModel3DImpl::getCollisionPoint(float p[3], bool ModelSpace)
{
Vector3D& v=*((Vector3D*)p);
switch (m_ColType)
{
case Models: v=my_tri_tri_intersect(m_ColTri1,m_ColTri2); break;
case Sphere:
case Ray: v=m_ColPoint; break;
default: v=Vector3D::Zero;
}
if (!ModelSpace) v=Transform(v,m_Transform);
return true;
}
bool SphereRayCollision(float center[3], float radius,
float origin[3], float direction[3],
float point[3])
{
Vector3D& C=*((Vector3D*)center);
Vector3D& O=*((Vector3D*)origin);
Vector3D D=((Vector3D*)direction)->Normalized();
Vector3D& P=*((Vector3D*)point);
Vector3D EO=C-O;
float v=EO*D;
float disc=radius*radius - (EO*EO - v*v);
if (disc<0.0f) return false;
float d=sqrt(disc);
P=O+(v-d)*D;
return true;
}
bool SphereSphereCollision(float c1[3], float r1,
float c2[3], float r2)
{
Vector3D& C1=*((Vector3D*)c1);
Vector3D& C2=*((Vector3D*)c2);
float dist=(C2-C1).SquareMagnitude();
float sum=r1+r2;
return (dist < sum*sum);
}
__CD__END

157
coldet/coldet.dsp Normal file
View File

@ -0,0 +1,157 @@
# Microsoft Developer Studio Project File - Name="coldet" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=coldet - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "coldet.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "coldet.mak" CFG="coldet - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "coldet - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "coldet - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "coldet - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COLDET_EXPORTS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COLDET_EXPORTS" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
!ELSEIF "$(CFG)" == "coldet - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COLDET_EXPORTS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COLDET_EXPORTS" /FR /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "coldet - Win32 Release"
# Name "coldet - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\box.cpp
# End Source File
# Begin Source File
SOURCE=.\box_bld.cpp
# End Source File
# Begin Source File
SOURCE=.\coldet.cpp
# End Source File
# Begin Source File
SOURCE=.\coldet_bld.cpp
# End Source File
# Begin Source File
SOURCE=.\math3d.cpp
# End Source File
# Begin Source File
SOURCE=.\mytritri.cpp
# End Source File
# Begin Source File
SOURCE=.\sysdep.cpp
# End Source File
# Begin Source File
SOURCE=.\tritri.c
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\box.h
# End Source File
# Begin Source File
SOURCE=.\coldet.h
# End Source File
# Begin Source File
SOURCE=.\coldetimpl.h
# End Source File
# Begin Source File
SOURCE=.\math3d.h
# End Source File
# Begin Source File
SOURCE=.\mytritri.h
# End Source File
# Begin Source File
SOURCE=.\sysdep.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

180
coldet/coldet.h Normal file
View File

@ -0,0 +1,180 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
/** \file coldet.h
3D Collision Detection
Interface for the library.
Isolated from any implementation details.
*/
#ifndef H_COLDET
#define H_COLDET
#ifndef EXPORT
#define EXPORT
#endif
/** Collision Model. Will represent the mesh to be tested for
collisions. It has to be notified of all triangles, via
addTriangle()
After all triangles are added, a call to finalize() will
process the information and prepare for collision tests.
Call collision() to check for a collision
Note: Transformations must not contain scaling.
*/
class CollisionModel3D
{
public:
virtual ~CollisionModel3D() {}
/** Optional: Optimization for construction speed.
If you know the number of triangles. */
virtual void setTriangleNumber(int num) = 0;
/** Use any of the forms of this functions to enter the coordinates
of the model's triangles. */
virtual void addTriangle(float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3) = 0;
virtual void addTriangle(float v1[3], float v2[3], float v3[3]) = 0;
/** All triangles have been added, process model. */
virtual void finalize() = 0;
/** The the current affine matrix for the model.
See transform.txt for format information */
virtual void setTransform(float m[16]) = 0;
/** Check for collision with another model.
Do not mix model types here.
MaxProcessingTime determines the maximum time in milliseconds
to check for collision. If a rejection is not found by that
time, the function will return true.
AccuracyDepth is not yet supported.
other_transform allows overriding the other model's
transform, by supplying an alternative one.
This can be useful when testing a model against itself
with different orientations.
*/
virtual bool collision(CollisionModel3D* other,
int AccuracyDepth=-1,
int MaxProcessingTime=0,
float* other_transform=0) = 0;
/** Returns true if the ray given in world space coordinates
intersects with the object.
getCollidingTriangles() and getCollisionPoint() can be
used to retrieve information about a collision.
If closest if false, the first triangle that collides with
the ray is used. Otherwise the closest one will be used.
Closest triangle searching will slow the test considerably.
The default ray is a standard infinite ray. However, using
segmin and segmax you can define a line segment along the
ray.
*/
virtual bool rayCollision(float origin[3],
float direction[3],
bool closest=false,
float segmin=0.0f,
float segmax=3.4e+38F) = 0;
/** Returns true if the given sphere collides with the model.
getCollidingTriangles() and getCollisionPoint() can be
used to retrieve information about a collision.
*/
virtual bool sphereCollision(float origin[3],
float radius) = 0;
/** Retrieve the pair of triangles that collided.
Only valid after a call to collision() that returned true.
t1 is this model's triangle and t2 is the other one.
In case of ray or sphere collision, only t1 will be valid.
The coordinates will be in _this_ model's coordinate space,
unless ModelSpace is false, in which case, coordinates will
be transformed by the model's current transform to world space.
*/
virtual bool getCollidingTriangles(float t1[9], float t2[9], bool ModelSpace=true) = 0;
/** Retrieve the pair of triangles indices that collided.
Only valid after a call to collision() that returned true.
t1 belongs to _this_ model, while t2 is in the other one.
*/
virtual bool getCollidingTriangles(int& t1, int& t2) = 0;
/** Retrieve the detected collision point.
Only valid after a call to collision()
that returned true.
The coordinates will be in _this_ model's coordinate space,
unless ModelSpace is false, in which case, coordinates will
be transformed by the model's current transform to world space.
*/
virtual bool getCollisionPoint(float p[3], bool ModelSpace=true) = 0;
};
/** Timeout exception class. Exception will be thrown if
the detection algorithm could not complete within
the given time limit. */
class TimeoutExpired {};
/** Inconsistency exception. Exception will be thrown if
the model is inconsistent.
Examples:
Checking for collisions before calling finalize()
Trying to add triangles after calling finalize() */
class Inconsistency {};
/** Create a new collision model object.
Use delete when finished with it.
Setting Static to true indicates that the model does not
move a lot, and certain calculations can be done every time
its transform changes instead of every collision test.
*/
EXPORT CollisionModel3D* newCollisionModel3D(bool Static=false);
//////////////////////////////////////////////
// Utility Functions
//////////////////////////////////////////////
/** Checks for intersection between a ray and a sphere.
center, radius define the sphere
origin, direction define the ray
point will contain point of intersection, if one is found.
*/
bool SphereRayCollision(float center[3], float radius,
float origin[3], float direction[3],
float point[3]);
/** Checks for intersection between 2 spheres. */
bool SphereSphereCollision(float c1[3], float r1,
float c2[3], float r2);
#endif // H_COLDET

74
coldet/coldet_bld.cpp Normal file
View File

@ -0,0 +1,74 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#include "sysdep.h"
#include "coldetimpl.h"
__CD__BEGIN
CollisionModel3D* newCollisionModel3D(bool Static)
{
return new CollisionModel3DImpl(Static);
}
CollisionModel3DImpl::CollisionModel3DImpl(bool Static)
: m_Root(Vector3D::Zero, Vector3D::Zero,0),
m_Transform(Matrix3D::Identity),
m_InvTransform(Matrix3D::Identity),
m_ColTri1(Vector3D::Zero,Vector3D::Zero,Vector3D::Zero),
m_ColTri2(Vector3D::Zero,Vector3D::Zero,Vector3D::Zero),
m_iColTri1(0),
m_iColTri2(0),
m_Final(false),
m_Static(Static)
{}
void CollisionModel3DImpl::addTriangle(const Vector3D& v1, const Vector3D& v2, const Vector3D& v3)
{
if (m_Final) throw Inconsistency();
m_Triangles.push_back(BoxedTriangle(v1,v2,v3));
}
void CollisionModel3DImpl::setTransform(const Matrix3D& m)
{
m_Transform=m;
if (m_Static) m_InvTransform=m_Transform.Inverse();
}
void CollisionModel3DImpl::finalize()
{
if (m_Final) throw Inconsistency();
// Prepare initial triangle list
m_Final=true;
for(unsigned i=0;i<m_Triangles.size();i++)
{
BoxedTriangle& bt=m_Triangles[i];
m_Root.m_Boxes.push_back(&bt);
}
int logdepth=0;
for(int num=m_Triangles.size();num>0;num>>=1,logdepth++);
m_Root.m_logdepth=int(logdepth*1.5f);
m_Root.divide(0);
}
__CD__END

108
coldet/coldetimpl.h Normal file
View File

@ -0,0 +1,108 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#ifndef H_COLDET_IMPL
#define H_COLDET_IMPL
#include "coldet.h"
#include "box.h"
#include "math3d.h"
#include <vector>
__CD__BEGIN
class CollisionModel3DImpl : public CollisionModel3D
{
public:
CollisionModel3DImpl(bool Static);
void setTriangleNumber(int num) { if (!m_Final) m_Triangles.reserve(num); }
void addTriangle(float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3)
{
addTriangle(Vector3D(x1,y1,z1),
Vector3D(x2,y2,z2),
Vector3D(x3,y3,z3));
}
void addTriangle(float v1[3], float v2[3], float v3[3])
{
addTriangle(Vector3D(v1[0],v1[1],v1[2]),
Vector3D(v2[0],v2[1],v2[2]),
Vector3D(v3[0],v3[1],v3[2]));
}
void addTriangle(const Vector3D& v1, const Vector3D& v2, const Vector3D& v3);
void finalize();
void setTransform(float m[16]) { setTransform(*(Matrix3D*)m); }
void setTransform(const Matrix3D& m);
bool collision(CollisionModel3D* other,
int AccuracyDepth,
int MaxProcessingTime,
float* other_transform);
bool rayCollision(float origin[3], float direction[3], bool closest,
float segmin, float segmax);
bool sphereCollision(float origin[3], float radius);
bool getCollidingTriangles(float t1[9], float t2[9], bool ModelSpace);
bool getCollidingTriangles(int& t1, int& t2);
bool getCollisionPoint(float p[3], bool ModelSpace);
int getTriangleIndex(BoxedTriangle* bt)
{
//return int(bt-m_Triangles.begin());
return int(bt-&(*m_Triangles.begin()));
}
/** Stores all the actual triangles. Other objects will use
pointers into this array.
*/
std::vector<BoxedTriangle> m_Triangles;
/** Root of the hierarchy tree */
BoxTreeInnerNode m_Root;
/** The current transform and its inverse */
Matrix3D m_Transform,m_InvTransform;
/** The triangles that last collided */
Triangle m_ColTri1,m_ColTri2;
/** The indices of the triangles that last collided */
int m_iColTri1,m_iColTri2;
/** The collision point of the last test */
Vector3D m_ColPoint;
/** Type of the last collision test */
enum { Models, Ray, Sphere }
m_ColType;
/** Flag for indicating the model is finalized. */
bool m_Final;
/** Static models will maintain the same transform for a while
so the inverse transform is calculated each set instead
of in the collision test. */
bool m_Static;
};
__CD__BEGIN
#endif // H_COLDET_IMPL

50
coldet/makefile.g++ Normal file
View File

@ -0,0 +1,50 @@
PROJECT=coldet
LIB=libcoldet.a
CC=g++
OPT=-O2
CFLAGS=-c $(OPT) -DGCC
OBJS= \
coldet.o \
coldet_bld.o \
box.o \
box_bld.o \
tritri.o \
math3d.o \
sysdep.o \
mytritri.o
all: $(LIB)
$(LIB): $(OBJS)
rm -f $(LIB)
ar cr $(LIB) $(OBJS)
ranlib $(LIB)
coldet.o: coldet.cpp
$(CC) $(CFLAGS) coldet.cpp
coldet_bld.o: coldet_bld.cpp
$(CC) $(CFLAGS) coldet_bld.cpp
box.o: box.cpp
$(CC) $(CFLAGS) box.cpp
box_bld.o: box_bld.cpp
$(CC) $(CFLAGS) box_bld.cpp
tritri.o: tritri.c
gcc $(CFLAGS) tritri.c
mytritri.o: mytritri.cpp
$(CC) $(CFLAGS) mytritri.cpp
math3d.o: math3d.cpp
$(CC) $(CFLAGS) math3d.cpp
sysdep.o: sysdep.cpp
$(CC) $(CFLAGS) sysdep.cpp
clean:
rm -f *.o $(LIB)

93
coldet/math3d.cpp Normal file
View File

@ -0,0 +1,93 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#include "sysdep.h"
#include "math3d.h"
const Vector3D Vector3D::Zero(0.0f,0.0f,0.0f);
const Matrix3D Matrix3D::Identity(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);
inline float
MINOR(const Matrix3D& m, const int r0, const int r1, const int r2, const int c0, const int c1, const int c2)
{
return m(r0,c0) * (m(r1,c1) * m(r2,c2) - m(r2,c1) * m(r1,c2)) -
m(r0,c1) * (m(r1,c0) * m(r2,c2) - m(r2,c0) * m(r1,c2)) +
m(r0,c2) * (m(r1,c0) * m(r2,c1) - m(r2,c0) * m(r1,c1));
}
Matrix3D
Matrix3D::Adjoint() const
{
return Matrix3D( MINOR(*this, 1, 2, 3, 1, 2, 3),
-MINOR(*this, 0, 2, 3, 1, 2, 3),
MINOR(*this, 0, 1, 3, 1, 2, 3),
-MINOR(*this, 0, 1, 2, 1, 2, 3),
-MINOR(*this, 1, 2, 3, 0, 2, 3),
MINOR(*this, 0, 2, 3, 0, 2, 3),
-MINOR(*this, 0, 1, 3, 0, 2, 3),
MINOR(*this, 0, 1, 2, 0, 2, 3),
MINOR(*this, 1, 2, 3, 0, 1, 3),
-MINOR(*this, 0, 2, 3, 0, 1, 3),
MINOR(*this, 0, 1, 3, 0, 1, 3),
-MINOR(*this, 0, 1, 2, 0, 1, 3),
-MINOR(*this, 1, 2, 3, 0, 1, 2),
MINOR(*this, 0, 2, 3, 0, 1, 2),
-MINOR(*this, 0, 1, 3, 0, 1, 2),
MINOR(*this, 0, 1, 2, 0, 1, 2));
}
float
Matrix3D::Determinant() const
{
return m[0][0] * MINOR(*this, 1, 2, 3, 1, 2, 3) -
m[0][1] * MINOR(*this, 1, 2, 3, 0, 2, 3) +
m[0][2] * MINOR(*this, 1, 2, 3, 0, 1, 3) -
m[0][3] * MINOR(*this, 1, 2, 3, 0, 1, 2);
}
Matrix3D
Matrix3D::Inverse() const
{
return (1.0f / Determinant()) * Adjoint();
}
Vector3D Matrix3D::GetTranslate() const
{
return Vector3D(m[3][0], m[3][1], m[3][2]);
}
void Matrix3D::Translate(const Vector3D & v)
{
m[3][0] += v.x * m[0][0] + v.y * m[1][0] + v.z * m[2][0];
m[3][1] += v.x * m[0][1] + v.y * m[1][1] + v.z * m[2][1];
m[3][2] += v.x * m[0][2] + v.y * m[1][2] + v.z * m[2][2];
}

335
coldet/math3d.h Normal file
View File

@ -0,0 +1,335 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#ifndef H_MATH3D
#define H_MATH3D
#include <math.h>
struct Vector3D;
struct Matrix3;
struct Matrix3D;
struct Plane;
inline float flabs(float f) { return (f>=0.0f?f:-f); }
const float epsilon=1e-8f;
inline bool IsZero(float f) { return flabs(f)<epsilon; }
Vector3D operator*(float scalar, const Vector3D& v);
Vector3D operator*(const Vector3D & v, float scalar);
float operator*(const Vector3D& v1, const Vector3D& v2);
Vector3D operator+(const Vector3D& v1, const Vector3D& v2);
Vector3D operator-(const Vector3D& v1, const Vector3D& v2);
Vector3D CrossProduct(const Vector3D& v1, const Vector3D& v2);
Matrix3D operator*(const Matrix3D& m1, const Matrix3D& m2);
Matrix3D operator*(float scalar, const Matrix3D& m);
struct Vector3D
{
float x,y,z;
static const Vector3D Zero;
Vector3D() {}
Vector3D(float X, float Y, float Z) : x(X), y(Y), z(Z) {}
Vector3D(const Vector3D& v) : x(v.x), y(v.y), z(v.z) {}
Vector3D& operator+=(const Vector3D& v) { x+=v.x; y+=v.y; z+=v.z; return *this; }
Vector3D& operator*=(float s) { x*=s; y*=s; z*=s; return *this; }
Vector3D& operator/=(float s) { return *this *= (1.0f/s); }
bool operator==(const Vector3D& v) { return x==v.x && y==v.y && z==v.z; }
Vector3D operator- () const { return Vector3D(-x,-y,-z); }
float SquareMagnitude () const { return x*x+y*y+z*z; }
float Magnitude () const { return (float)sqrt(SquareMagnitude()); }
Vector3D Normalized () const { return (1.0f/Magnitude())*(*this); }
float operator[] (int i) const { return ((float*)&x)[i]; }
float& operator[] (int i) { return ((float*)&x)[i]; }
};
#define _11 sclr.s11
#define _12 sclr.s12
#define _13 sclr.s13
#define _14 sclr.s14
#define _21 sclr.s21
#define _22 sclr.s22
#define _23 sclr.s23
#define _24 sclr.s24
#define _31 sclr.s31
#define _32 sclr.s32
#define _33 sclr.s33
#define _34 sclr.s34
#define _41 sclr.s41
#define _42 sclr.s42
#define _43 sclr.s43
#define _44 sclr.s44
struct Matrix3
{
union {
struct { float s11,s12,s13,
s21,s22,s23,
s31,s32,s33; } sclr;
float m[3][3];
};
static const Matrix3 Identity;
Vector3D& baseRow(int i) { return *((Vector3D*)m[i]); }
float operator() (int i, int j) const { return m[i][j]; }
float& operator() (int i, int j) { return m[i][j]; }
};
struct Matrix3D
{
union {
struct { float s11,s12,s13,s14,
s21,s22,s23,s24,
s31,s32,s33,s34,
s41,s42,s43,s44; } sclr;
float m[4][4];
};
static const Matrix3D Identity;
Matrix3D() {}
Matrix3D(float f11, float f12, float f13, float f14,
float f21, float f22, float f23, float f24,
float f31, float f32, float f33, float f34,
float f41, float f42, float f43, float f44)
{
_11=f11; _12=f12; _13=f13; _14=f14;
_21=f21; _22=f22; _23=f23; _24=f24;
_31=f31; _32=f32; _33=f33; _34=f34;
_41=f41; _42=f42; _43=f43; _44=f44;
}
Matrix3D& operator*= (const Matrix3D& m)
{
return *this = *this * m;
}
friend Matrix3D PitchMatrix3D(const float theta);
friend Matrix3D YawMatrix3D(const float theta);
friend Matrix3D RollMatrix3D(const float theta);
void rotate(const Vector3D& v);
Matrix3D Inverse() const;
Matrix3D Adjoint() const;
float Determinant() const;
Vector3D GetTranslate() const;
void Translate(const Vector3D & v);
float operator() (int i, int j) const { return m[i][j]; }
float& operator() (int i, int j) { return m[i][j]; }
};
struct Plane
{
Vector3D normal;
float d;
Plane(const Vector3D& a, const Vector3D& b, const Vector3D& c)
{
normal = CrossProduct(b - a, c - a).Normalized();
d = -normal * a;
}
float Classify(const Vector3D& v)
{
return v * normal + d;
}
};
inline Vector3D operator* (float scalar, const Vector3D& v)
{
return Vector3D(scalar*v.x,scalar*v.y,scalar*v.z);
}
inline Vector3D operator* (const Vector3D& v, float scalar)
{
return Vector3D(scalar*v.x,scalar*v.y,scalar*v.z);
}
inline Vector3D operator+ (const Vector3D& v1, const Vector3D& v2)
{
return Vector3D(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z);
}
inline Vector3D operator- (const Vector3D& v1, const Vector3D& v2)
{
return Vector3D(v1.x-v2.x,v1.y-v2.y,v1.z-v2.z);
}
inline float operator* (const Vector3D& v1, const Vector3D& v2)
{
return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
}
inline Vector3D CrossProduct(const Vector3D& v1, const Vector3D& v2)
{
return Vector3D(v1.y*v2.z-v2.y*v1.z,
v1.z*v2.x-v2.z*v1.x,
v1.x*v2.y-v2.x*v1.y);
}
inline Vector3D Transform(const Vector3D& v, const Matrix3D& m)
{
return Vector3D(v.x*m._11 + v.y*m._21 + v.z*m._31 + m._41,
v.x*m._12 + v.y*m._22 + v.z*m._32 + m._42,
v.x*m._13 + v.y*m._23 + v.z*m._33 + m._43);
}
inline Vector3D rotateVector(const Vector3D& v, const Matrix3D& m)
{
return Vector3D(v.x*m._11 + v.y*m._21 + v.z*m._31,
v.x*m._12 + v.y*m._22 + v.z*m._32,
v.x*m._13 + v.y*m._23 + v.z*m._33);
}
inline Matrix3D operator*(float scalar, const Matrix3D& m)
{
return Matrix3D(scalar*m(0,0),scalar*m(0,1),scalar*m(0,2),scalar*m(0,3),
scalar*m(1,0),scalar*m(1,1),scalar*m(1,2),scalar*m(1,3),
scalar*m(2,0),scalar*m(2,1),scalar*m(2,2),scalar*m(2,3),
scalar*m(3,0),scalar*m(3,1),scalar*m(3,2),scalar*m(3,3));
}
inline Matrix3D operator*(const Matrix3D& m1, const Matrix3D& m2)
{
return Matrix3D(
m1._11*m2._11 + m1._12*m2._21 + m1._13*m2._31 + m1._14*m2._41,
m1._11*m2._12 + m1._12*m2._22 + m1._13*m2._32 + m1._14*m2._42,
m1._11*m2._13 + m1._12*m2._23 + m1._13*m2._33 + m1._14*m2._43,
m1._11*m2._14 + m1._12*m2._24 + m1._13*m2._34 + m1._14*m2._44,
m1._21*m2._11 + m1._22*m2._21 + m1._23*m2._31 + m1._24*m2._41,
m1._21*m2._12 + m1._22*m2._22 + m1._23*m2._32 + m1._24*m2._42,
m1._21*m2._13 + m1._22*m2._23 + m1._23*m2._33 + m1._24*m2._43,
m1._21*m2._14 + m1._22*m2._24 + m1._23*m2._34 + m1._24*m2._44,
m1._31*m2._11 + m1._32*m2._21 + m1._33*m2._31 + m1._34*m2._41,
m1._31*m2._12 + m1._32*m2._22 + m1._33*m2._32 + m1._34*m2._42,
m1._31*m2._13 + m1._32*m2._23 + m1._33*m2._33 + m1._34*m2._43,
m1._31*m2._14 + m1._32*m2._24 + m1._33*m2._34 + m1._34*m2._44,
m1._41*m2._11 + m1._42*m2._21 + m1._43*m2._31 + m1._44*m2._41,
m1._41*m2._12 + m1._42*m2._22 + m1._43*m2._32 + m1._44*m2._42,
m1._41*m2._13 + m1._42*m2._23 + m1._43*m2._33 + m1._44*m2._43,
m1._41*m2._14 + m1._42*m2._24 + m1._43*m2._34 + m1._44*m2._44);
}
inline void
Matrix3D::rotate(const Vector3D& v)
{
if (v.x!=0.0f) *this = PitchMatrix3D(v.x) * (*this);
if (v.y!=0.0f) *this = YawMatrix3D (v.y) * (*this);
if (v.z!=0.0f) *this = RollMatrix3D (v.z) * (*this);
}
inline Matrix3D
TranslateMatrix3D(const Vector3D& v)
{
return Matrix3D(1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
v.x, v.y, v.z,1.0f);
}
inline Matrix3D
ScaleMatrix3D(const Vector3D& v)
{
return Matrix3D( v.x,0.0f,0.0f,0.0f,
0.0f, v.y,0.0f,0.0f,
0.0f,0.0f, v.z,0.0f,
0.0f,0.0f,0.0f,1.0f);
}
inline Matrix3D
ScaleMatrix3D(const float s)
{
return ScaleMatrix3D(Vector3D(s,s,s));
}
inline Matrix3D
PitchMatrix3D(const float c, const float s)
{
return Matrix3D(1.0f, 0.0f, 0.0f, 0.0f,
0.0f, c, -s, 0.0f,
0.0f, s, c, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
inline Matrix3D
PitchMatrix3D(const float theta)
{
return PitchMatrix3D((float) cos(theta), (float) sin(theta));
}
inline Matrix3D
YawMatrix3D(const float c, const float s)
{
return Matrix3D( c, 0.0f, s, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
-s, 0.0f, c, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
inline Matrix3D
YawMatrix3D(const float theta)
{
return YawMatrix3D((float) cos(theta), (float) sin(theta));
}
inline Matrix3D
RollMatrix3D(const float c, const float s)
{
return Matrix3D(c, -s, 0.0f, 0.0f,
s, c, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
inline Matrix3D
RollMatrix3D(const float theta)
{
return RollMatrix3D((float) cos(theta), (float) sin(theta));
}
template<class T>
inline T Max(T a, T b)
{
return (a>b ? a : b);
}
template<class T>
inline T Min(T a, T b)
{
return (a<b ? a : b);
}
#endif // H_MATH3D

73
coldet/mytritri.cpp Normal file
View File

@ -0,0 +1,73 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#include "sysdep.h"
#include "mytritri.h"
Vector3D my_tri_tri_intersect(const Triangle& t1, const Triangle& t2)
{
Plane p1(t1.v1,t1.v2,t1.v3);
int other_side=0;
{
float f1=p1.Classify(t2.v1);
float f2=p1.Classify(t2.v2);
float f3=p1.Classify(t2.v3);
float f12=f1*f2;
float f23=f2*f3;
if (f12>0.0f && f23>0.0f) return Vector3D::Zero;
other_side=(f12<0.0f?(f23<0.0f?1:0):2);
}
Plane p2(t2.v1,t2.v2,t2.v3);
Vector3D n12(p1.normal+p2.normal);
TriangleDesc td2(t2,p2);
const Vector3D& a2=td2[other_side+1];
const Vector3D& b2=td2[other_side];
const Vector3D& c2=td2[other_side+2];
float t21=-(p1.d+p2.d+a2*n12)/((b2-a2)*n12);
TriangleDesc td1(t1,p1);
Vector3D P21(a2+t21*(b2-a2));
if (td1.pointInTri(P21)) return P21;
float t22=-(p1.d+p2.d+c2*n12)/((b2-c2)*n12);
Vector3D P22(c2+t22*(b2-c2));
if (td1.pointInTri(P22)) return P22;
{
float f1=p2.Classify(t1.v1);
float f2=p2.Classify(t1.v2);
float f3=p2.Classify(t1.v3);
float f12=f1*f2;
float f23=f2*f3;
if (f12>0.0f && f23>0.0f) return Vector3D::Zero;
other_side=(f12<0.0f?(f23<0.0f?1:0):2);
}
const Vector3D& a1=td1[other_side+1];
const Vector3D& b1=td1[other_side];
const Vector3D& c1=td1[other_side+2];
float t11=-(p1.d+p2.d+a1*n12)/((b1-a1)*n12);
Vector3D P11(a1+t11*(b1-a1));
if (td2.pointInTri(P11)) return P11;
float t12=-(p1.d+p2.d+c1*n12)/((b1-c1)*n12);
Vector3D P12(c1+t12*(b1-c1));
if (td2.pointInTri(P12)) return P12;
return Vector3D::Zero;
}

96
coldet/mytritri.h Normal file
View File

@ -0,0 +1,96 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#ifndef H_MYTRITRI
#define H_MYTRITRI
#include "box.h"
/** A slower triangle-triangle intersection test, that returns the
point of intersection. */
Vector3D my_tri_tri_intersect(const Triangle& t1, const Triangle& t2);
/** Triangle description class. It is used to determine if a point
on the triangle's plane is inside the triangle. */
class TriangleDesc : public Triangle
{
public:
TriangleDesc(const Triangle& t, const Plane& p)
: Triangle(t)
{
const Vector3D& n=p.normal;
Vector3D a(flabs(n.x),flabs(n.y),flabs(n.z));
if (a.x>a.y)
{
if (a.x>a.z) { i1=1; i2=2; }
else { i1=0; i2=1; }
}
else
{
if (a.y>a.z) { i1=0; i2=2; }
else { i1=0; i2=1; }
}
}
bool pointInTri(const Vector3D& P)
{
Vector3D u(P[i1]-v1[i1],
v2[i1]-v1[i1],
v3[i1]-v1[i1]);
Vector3D v(P[i2]-v1[i2],
v2[i2]-v1[i2],
v3[i2]-v1[i2]);
float a,b;
if (u.y==0.0f)
{
b=u.x/u.z;
if (b>=0.0f && b<=1.0f) a=(v.x-b*v.z)/v.y;
else return false;
}
else
{
b=(v.x*u.y-u.x*v.y)/(v.z*u.y-u.z*v.y);
if (b>=0.0f && b<=1.0f) a=(u.x-b*u.z)/u.y;
else return false;
}
return (a>=0 && (a+b)<=1);
}
const Vector3D& operator[] (int index)
{
switch (index)
{
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v1;
}
return v2;
}
int i1,i2;
};
#endif // H_MYTRITRI

52
coldet/quickstart.html Normal file
View File

@ -0,0 +1,52 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>ColDet - Quick Start</title>
</head>
<body>
<center>
<h1>
Quickstart:</h1></center>
<h3>
Model Setup:</h3>
For each mesh, create a collision model by using:
<pre>CollisionModel3D* model = newCollisionModel3D();</pre>
(Shared meshes can use one model)
<br>Add all the triangles the mesh has to the model by using:
<pre>model->addTriangle(vertex1,vertex2,vertex3);</pre>
Call:
<pre>model->finalize();</pre>
&nbsp;
<h3>
Collision Test:</h3>
Assuming you have two models (m1,m2), either set both of theirs transformation
<br>matrices (world matrix) by calling:
<pre>m1->setTransform(model1_transformation_matrix);</pre>
<pre>m2->setTransform(model2_transformation_matrix);</pre>
or set only one of them (in case youre testing the model against itself,
with different
<br>transform) Then call:
<pre>m1->collision(m2);</pre>
The function returns a bool indicating if a collision has occurred.&nbsp;&nbsp;
Note that if you test a
<br>model against itself with a different transform, you need to supply
that transform as an
<br>optional parameter.
<br>&nbsp;
<h3>
Collision Test Results:</h3>
Use the getCollidingTriangles function to get which triangles have collided.&nbsp;&nbsp;
Use the
<br>getCollisionPoint function to find the exact collision point.
<br>&nbsp;
<h3>
Other Collision Tests:</h3>
You can use the rayCollision and sphereCollision functions to test the
model against
<br>these primitives.
</body>
</html>

29
coldet/readme.txt Normal file
View File

@ -0,0 +1,29 @@
ColDet - 3D Collision Detection Library
Copyright (C) 2000 Amir Geva
Description:
ColDet is a 3D collision detection library, intended for games.
It supports generic polyhedra, and even polygon soups.
Requirements:
It is written in standard C++ and can be compiled on these systems:
Windows: Visual C++ 6
Windows: Borland C++ Builder 5
Linux: g++ 2.8
Other systems that have g++ will probably compile with no modification.
The code is portable to any system with a standard C++ compliant compiler
(as compliant as they get)
Installation:
Use the supplied Visual C++ project file (coldet.dsp)
or the makefile (for systems with g++)
In other cases, just create a project/makefile and include all of the source files.
Distribution:
It is distributed under the Library GNU Public License (See the file: COPYING)
Any redistribution of the files in this package must include the entire package.
Contact Information:
Web Site: http://photoneffect.com/coldet/
email: photon@photoneffect.com

41
coldet/sysdep.cpp Normal file
View File

@ -0,0 +1,41 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#include "sysdep.h"
#ifdef GCC
#include <sys/time.h>
// Returns a time index in milliseconds
DWORD GetTickCount()
{
static struct timezone tz={0,0};
static const double t1=1000.0;
static const double t2=0.001;
timeval t;
gettimeofday(&t,&tz);
return long((t.tv_sec&0x000FFFFF)*t1 + t.tv_usec*t2);
}
#endif

55
coldet/sysdep.h Normal file
View File

@ -0,0 +1,55 @@
/* ColDet - C++ 3D Collision Detection Library
* Copyright (C) 2000 Amir Geva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Any comments, questions and bug reports send to:
* photon@photoneffect.com
*
* Or visit the home page: http://photoneffect.com/coldet/
*/
#ifndef H_SYSDEP
#define H_SYSDEP
#ifdef GCC
typedef unsigned long DWORD;
DWORD GetTickCount();
#define __CD__BEGIN
#define __CD__END
#elif defined(WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define __CD__BEGIN
#define __CD__END
#ifdef COLDET_EXPORTS
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
#else
#error No system specified (WIN32 GCC)
#endif
#ifndef EXPORT
#define EXPORT
#endif
#endif // H_SYSDEP

21
coldet/transform.txt Normal file
View File

@ -0,0 +1,21 @@
The matrices used to describe model transformations are affine 4x4 matrices
which are D3D style, row major with translations in the 4th row.
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
The translation vector Tx,Ty,Tz is in elements 13,14,15
If you use the transposed scheme, which is:
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
With the translation vector in elements 13,14,15 you can use
this matrix without any conversion.
It is also important to note that the transformations should include
no scaling, as it disrupts the consistency of the collision model.

292
coldet/tritri.c Normal file
View File

@ -0,0 +1,292 @@
/* Triangle/triangle intersection test routine,
* by Tomas Moller, 1997.
* See article "A Fast Triangle-Triangle Intersection Test",
* Journal of Graphics Tools, 2(2), 1997
*
* int tri_tri_intersect(float V0[3],float V1[3],float V2[3],
* float U0[3],float U1[3],float U2[3])
*
* parameters: vertices of triangle 1: V0,V1,V2
* vertices of triangle 2: U0,U1,U2
* result : returns 1 if the triangles intersect, otherwise 0
*
*/
#include <math.h>
/* if USE_EPSILON_TEST is true then we do a check:
if |dv|<EPSILON then dv=0.0;
else no check is done (which is less robust)
*/
#define USE_EPSILON_TEST TRUE
#define EPSILON 0.000001
/* some macros */
#define CROSS(dest,v1,v2) \
dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \
dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \
dest[2]=v1[0]*v2[1]-v1[1]*v2[0];
#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
#define SUB(dest,v1,v2) \
dest[0]=v1[0]-v2[0]; \
dest[1]=v1[1]-v2[1]; \
dest[2]=v1[2]-v2[2];
/* sort so that a<=b */
#define SORT(a,b) \
if(a>b) \
{ \
float c; \
c=a; \
a=b; \
b=c; \
}
#define ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1) \
isect0=VV0+(VV1-VV0)*D0/(D0-D1); \
isect1=VV0+(VV2-VV0)*D0/(D0-D2);
#define COMPUTE_INTERVALS(VV0,VV1,VV2,D0,D1,D2,D0D1,D0D2,isect0,isect1) \
if(D0D1>0.0f) \
{ \
/* here we know that D0D2<=0.0 */ \
/* that is D0, D1 are on the same side, D2 on the other or on the plane */ \
ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \
} \
else if(D0D2>0.0f) \
{ \
/* here we know that d0d1<=0.0 */ \
ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \
} \
else if(D1*D2>0.0f || D0!=0.0f) \
{ \
/* here we know that d0d1<=0.0 or that D0!=0.0 */ \
ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1); \
} \
else if(D1!=0.0f) \
{ \
ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \
} \
else if(D2!=0.0f) \
{ \
ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \
} \
else \
{ \
/* triangles are coplanar */ \
return coplanar_tri_tri(N1,V0,V1,V2,U0,U1,U2); \
}
/* this edge to edge test is based on Franlin Antonio's gem:
"Faster Line Segment Intersection", in Graphics Gems III,
pp. 199-202 */
#define EDGE_EDGE_TEST(V0,U0,U1) \
Bx=U0[i0]-U1[i0]; \
By=U0[i1]-U1[i1]; \
Cx=V0[i0]-U0[i0]; \
Cy=V0[i1]-U0[i1]; \
f=Ay*Bx-Ax*By; \
d=By*Cx-Bx*Cy; \
if((f>0 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) \
{ \
e=Ax*Cy-Ay*Cx; \
if(f>0) \
{ \
if(e>=0 && e<=f) return 1; \
} \
else \
{ \
if(e<=0 && e>=f) return 1; \
} \
}
#define EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2) \
{ \
float Ax,Ay,Bx,By,Cx,Cy,e,d,f; \
Ax=V1[i0]-V0[i0]; \
Ay=V1[i1]-V0[i1]; \
/* test edge U0,U1 against V0,V1 */ \
EDGE_EDGE_TEST(V0,U0,U1); \
/* test edge U1,U2 against V0,V1 */ \
EDGE_EDGE_TEST(V0,U1,U2); \
/* test edge U2,U1 against V0,V1 */ \
EDGE_EDGE_TEST(V0,U2,U0); \
}
#define POINT_IN_TRI(V0,U0,U1,U2) \
{ \
float a,b,c,d0,d1,d2; \
/* is T1 completly inside T2? */ \
/* check if V0 is inside tri(U0,U1,U2) */ \
a=U1[i1]-U0[i1]; \
b=-(U1[i0]-U0[i0]); \
c=-a*U0[i0]-b*U0[i1]; \
d0=a*V0[i0]+b*V0[i1]+c; \
\
a=U2[i1]-U1[i1]; \
b=-(U2[i0]-U1[i0]); \
c=-a*U1[i0]-b*U1[i1]; \
d1=a*V0[i0]+b*V0[i1]+c; \
\
a=U0[i1]-U2[i1]; \
b=-(U0[i0]-U2[i0]); \
c=-a*U2[i0]-b*U2[i1]; \
d2=a*V0[i0]+b*V0[i1]+c; \
if(d0*d1>0.0) \
{ \
if(d0*d2>0.0) return 1; \
} \
}
int coplanar_tri_tri(float N[3],float V0[3],float V1[3],float V2[3],
float U0[3],float U1[3],float U2[3])
{
float A[3];
short i0,i1;
/* first project onto an axis-aligned plane, that maximizes the area */
/* of the triangles, compute indices: i0,i1. */
A[0]=fabs(N[0]);
A[1]=fabs(N[1]);
A[2]=fabs(N[2]);
if(A[0]>A[1])
{
if(A[0]>A[2])
{
i0=1; /* A[0] is greatest */
i1=2;
}
else
{
i0=0; /* A[2] is greatest */
i1=1;
}
}
else /* A[0]<=A[1] */
{
if(A[2]>A[1])
{
i0=0; /* A[2] is greatest */
i1=1;
}
else
{
i0=0; /* A[1] is greatest */
i1=2;
}
}
/* test all edges of triangle 1 against the edges of triangle 2 */
EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2);
EDGE_AGAINST_TRI_EDGES(V1,V2,U0,U1,U2);
EDGE_AGAINST_TRI_EDGES(V2,V0,U0,U1,U2);
/* finally, test if tri1 is totally contained in tri2 or vice versa */
POINT_IN_TRI(V0,U0,U1,U2);
POINT_IN_TRI(U0,V0,V1,V2);
return 0;
}
int tri_tri_intersect(float V0[3],float V1[3],float V2[3],
float U0[3],float U1[3],float U2[3])
{
float E1[3],E2[3];
float N1[3],N2[3],d1,d2;
float du0,du1,du2,dv0,dv1,dv2;
float D[3];
float isect1[2], isect2[2];
float du0du1,du0du2,dv0dv1,dv0dv2;
short index;
float vp0,vp1,vp2;
float up0,up1,up2;
float b,c,max;
/* compute plane equation of triangle(V0,V1,V2) */
SUB(E1,V1,V0);
SUB(E2,V2,V0);
CROSS(N1,E1,E2);
d1=-DOT(N1,V0);
/* plane equation 1: N1.X+d1=0 */
/* put U0,U1,U2 into plane equation 1 to compute signed distances to the plane*/
du0=DOT(N1,U0)+d1;
du1=DOT(N1,U1)+d1;
du2=DOT(N1,U2)+d1;
/* coplanarity robustness check */
#if USE_EPSILON_TEST==TRUE
if(fabs(du0)<EPSILON) du0=0.0;
if(fabs(du1)<EPSILON) du1=0.0;
if(fabs(du2)<EPSILON) du2=0.0;
#endif
du0du1=du0*du1;
du0du2=du0*du2;
if(du0du1>0.0f && du0du2>0.0f) /* same sign on all of them + not equal 0 ? */
return 0; /* no intersection occurs */
/* compute plane of triangle (U0,U1,U2) */
SUB(E1,U1,U0);
SUB(E2,U2,U0);
CROSS(N2,E1,E2);
d2=-DOT(N2,U0);
/* plane equation 2: N2.X+d2=0 */
/* put V0,V1,V2 into plane equation 2 */
dv0=DOT(N2,V0)+d2;
dv1=DOT(N2,V1)+d2;
dv2=DOT(N2,V2)+d2;
#if USE_EPSILON_TEST==TRUE
if(fabs(dv0)<EPSILON) dv0=0.0;
if(fabs(dv1)<EPSILON) dv1=0.0;
if(fabs(dv2)<EPSILON) dv2=0.0;
#endif
dv0dv1=dv0*dv1;
dv0dv2=dv0*dv2;
if(dv0dv1>0.0f && dv0dv2>0.0f) /* same sign on all of them + not equal 0 ? */
return 0; /* no intersection occurs */
/* compute direction of intersection line */
CROSS(D,N1,N2);
/* compute and index to the largest component of D */
max=fabs(D[0]);
index=0;
b=fabs(D[1]);
c=fabs(D[2]);
if(b>max) max=b,index=1;
if(c>max) max=c,index=2;
/* this is the simplified projection onto L*/
vp0=V0[index];
vp1=V1[index];
vp2=V2[index];
up0=U0[index];
up1=U1[index];
up2=U2[index];
/* compute interval for triangle 1 */
COMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,isect1[0],isect1[1]);
/* compute interval for triangle 2 */
COMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,isect2[0],isect2[1]);
SORT(isect1[0],isect1[1]);
SORT(isect2[0],isect2[1]);
if(isect1[1]<isect2[0] || isect2[1]<isect1[0]) return 0;
return 1;
}

153
common_sdl_gl.cpp Normal file
View File

@ -0,0 +1,153 @@
#include <iostream>
#include <SDL.h>
#include <SDL_opengl.h>
extern int global_EC;
#ifndef VIEWGL_FOVY
#define VIEWGL_FOVY 60.0f
#endif
#ifndef VIEWGL_ZNEAR
#define VIEWGL_ZNEAR 0.1f
#endif
#ifndef VIEWGL_ZFAR
#define VIEWGL_ZFAR 250.0f
#endif
extern SDL_Surface* screen;
int videoFlags = 0;
/*
void ERROR(const char* s) {
std::cerr << "Error" << s << std::endl;
std::cerr << "* last SDL error was: " << SDL_GetError() << std::endl;
global_EC = 1;
exit(1);
}*/
int resize(int w, int h) {
GLfloat ratio;
if ( h == 0 )
h = 1;
ratio = ( GLfloat )w / ( GLfloat )h;
glViewport( 0, 0, ( GLint )w, ( GLint )h );
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
//glRotatef(180, 0, 0, 1);
gluPerspective( VIEWGL_FOVY, ratio, VIEWGL_ZNEAR, VIEWGL_ZFAR);
//glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, 0.1f, 300.0f);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );
return(1);
}
void initVideo(int w, int h, int bpp) {
const SDL_VideoInfo *videoInfo;
/*
SDL_Rect **modes;
int i;
videoInfo = SDL_GetVideoInfo( );
modes=SDL_ListModes(videoInfo->vfmt, SDL_FULLSCREEN|SDL_HWSURFACE);
if(modes == (SDL_Rect **)0){
printf("No modes available!\n");
exit(1);
}
if(modes == (SDL_Rect **)-1){
printf("All resolutions available.\n");
}
else{
printf("Available Modes\n");
for(i=0;modes[i];++i)
printf(" %d x %d\n", modes[i]->w, modes[i]->h);
}
*/
if (!videoInfo)
ERROR("VideoInfo query failed");
videoFlags = SDL_OPENGL;
videoFlags |= SDL_GL_DOUBLEBUFFER;
videoFlags |= SDL_HWPALETTE;
//videoFlags |= SDL_RESIZABLE;
//videoFlags |= SDL_FULLSCREEN;
if ( videoInfo->hw_available ) {
std::cerr << "Info: Using HWSURFACE" << std::endl;
videoFlags |= SDL_HWSURFACE;
}
else {
std::cerr << "Info: Using SWSURFACE" << std::endl;
videoFlags |= SDL_SWSURFACE;
}
if ( videoInfo->blit_hw ) {
std::cerr << "Info: Using HWACCEL" << std::endl;
videoFlags |= SDL_HWACCEL;
}
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
screen = SDL_SetVideoMode( w, h, bpp, videoFlags );
if (!screen)
ERROR("SDL failed to generate requested VideoSurface!");
resize(w, h);
}
void initGL() {
GLfloat LightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat LightPosition[] = { 500.0f, 200.0f, 300.0f, 1.0f };
//glEnable( GL_TEXTURE_2D );
glShadeModel( GL_SMOOTH );
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
//glClearDepth( 1.0f );
glEnable( GL_DEPTH_TEST );
glEnable( GL_LIGHTING );
//glDepthFunc( GL_LEQUAL );
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
glLightfv( GL_LIGHT0, GL_AMBIENT, LightAmbient );
glLightfv( GL_LIGHT0, GL_DIFFUSE, LightDiffuse );
glLightfv( GL_LIGHT0, GL_POSITION, LightPosition );
glEnable( GL_LIGHT0 );
glEnable( GL_COLOR_MATERIAL);
glCullFace(GL_BACK);
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_LINE);
}
/*
SDL_Surface* createRGBASurface(int w, int h) {
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define rmask 0xff000000
#define gmask 0x00ff0000
#define bmask 0x0000ff00
#define amask 0x000000ff
#else
#define rmask 0x000000ff
#define gmask 0x0000ff00
#define bmask 0x00ff0000
#define amask 0xff000000
#endif
return SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,
64, 64, 32, rmask, gmask, bmask, amask);
}
SDL_Surface* createRGBSurface(int w, int h) {
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define rmask 0xff000000
#define gmask 0x00ff0000
#define bmask 0x0000ff00
#define amask 0x000000ff
#else
#define rmask 0x000000ff
#define gmask 0x0000ff00
#define bmask 0x00ff0000
#define amask 0xff000000
#endif
return SDL_CreateRGBSurface(SDL_SWSURFACE,
w, h, 24, rmask, gmask, bmask, amask);
}
*/

17
common_sdl_gl.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef SDL_GL_COMMON_FUNCS_H
#define SDL_GL_COMMON_FUNCS_H
#include <SDL.h>
int resize(int w, int h);
void initVideo(int w, int h, int bpp);
void initGL();
//void ERROR(const char* s);
//SDL_Surface* createRGBSurface(int w, int h);
//SDL_Surface* createRGBASurface(int w, int h);
extern int videoFlags;
extern int global_Done;
extern int global_EC;
#endif

100
dataholder.cpp Normal file
View File

@ -0,0 +1,100 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#include "dataholder.h"
#include "log.h"
#include "cistring.h"
namespace OpenGTA {
template<> ActiveStyle::ActiveData() {
m_data = 0;
}
template<> ActiveStyle::~ActiveData() {
unload();
}
template<> GraphicsBase & ActiveStyle::get() {
assert(m_data);
return *m_data;
}
template<class T> void ActiveData<T>::unload() {
if (m_data)
delete m_data;
m_data = 0;
}
template<> void ActiveStyle::load(const std::string & file) {
unload();
Util::ci_string tmpname(file.c_str());
if (tmpname.find(".g24") != std::string::npos) {
m_data = new Graphics24Bit(file);
}
else if (tmpname.find(".gry") != std::string::npos) {
m_data = new Graphics8Bit(file);
}
else {
try {
m_data = new Graphics8Bit(file);
}
catch (const Exception & e) {
INFO << "loading 8 bit failed: " << e.what() << std::endl;
m_data = 0;
try {
m_data = new Graphics24Bit(file);
}
catch (const Exception & e) {
ERROR << "loading 24 bit failed " << e.what() << std::endl;
m_data = 0;
}
}
}
assert(m_data);
}
template<> ActiveMap::ActiveData() {
m_data = 0;
}
template<> ActiveMap::~ActiveData() {
unload();
}
template<> Map & ActiveMap::get() {
assert(m_data);
return *m_data;
}
template<> void ActiveMap::load(const std::string & file) {
unload();
try {
m_data = new Map(file);
}
catch (const Exception & e) {
ERROR << "loading map failed: " << e.what();
m_data = 0;
}
assert(m_data);
}
}

64
dataholder.h Normal file
View File

@ -0,0 +1,64 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef STYLE_HOLDER_H
#define STYLE_HOLDER_H
#include <string>
#include "opengta.h"
#include "m_exceptions.h"
#include "Singleton.h"
namespace OpenGTA {
/*
class ActiveStyle {
public:
ActiveStyle();
~ActiveStyle();
GraphicsBase & getStyle();
void load(const std::string & file);
private:
void unload();
GraphicsBase* m_style;
};
*/
template <class T>
class ActiveData {
public:
ActiveData();
~ActiveData();
T & get();
void load(const std::string & file);
private:
void unload();
T* m_data;
};
typedef ActiveData< GraphicsBase > ActiveStyle;
typedef ActiveData< Map > ActiveMap;
typedef Loki::SingletonHolder< ActiveStyle, Loki::CreateUsingNew, Loki::DefaultLifetime,
Loki::SingleThreaded> StyleHolder;
typedef Loki::SingletonHolder< ActiveMap, Loki::CreateUsingNew, Loki::DefaultLifetime,
Loki::SingleThreaded> MapHolder;
}
#endif

27
doc/doc_links.txt Normal file
View File

@ -0,0 +1,27 @@
Cityscape Data Structure
http://azz.gouranga.com/files/cds.zip
Do not miss the rest of these gems:
http://azz.gouranga.com/index.php?page=8
GTA Mission Template Description (can be found elsewhere)
http://www.fileplanet.com/19571/10000/fileinfo/2/0/section/General
GTA2 Format Docs (somewhat related ;-)
http://files.filefront.com/GTA2+Format+Docs/;10536;;/fileinfo.html
The unofficial Grand Theft Auto Reference Handbook
http://gta.mendelsohn.de/Reference/
Corrections and Notes regarding DMA GTA technical doument [cds.doc v12.10]
http://www.fifengr.com/gtacars/topic.html
--- other links ---
A Stream-based Time Synchronization Technique For Networked Computer Games
http://www.mine-control.com/zack/timesync/timesync.html
Fix Your Timestep!
http://www.gaffer.org/game-physics/fix-your-timestep/
Car Physics for Games - by Marco Monster
http://web.archive.org/web/20051218060818/http://home.planet.nl/~monstrous/tutcar.html

133
doc/gouranga.txt Normal file
View File

@ -0,0 +1,133 @@
i emailed you all this but am posting it here just-in-case:
hi, i declare myself an expert on gta1 and gta2 map format, i can help you with anything you ask, email me at jernejcoder@gmail.com, let's get straight to answers:
* Can anyone tell me more about the height/size of blocks as drawn in-game? Currently I texture a unit-cube, but that doesn't look right (may have other causes).
one gta cube is 1*1*1 unit big
* When looking at the "slope type" bits in "type_map" (CDS.DOC) I am not sure howto interpret something like "1- 2 = up 26 low, high"; is it 26 degrees, pixel or another unit?
use it like this:
* 1- 2 = up 26 low, high
* 3 - 4 = down 26 low, high
* 5 - 6 = left 26 low, high
* 7 - 8 = right 26 low, high
* 9 - 16 = up 7 low - high
* 17 - 24 = down 7 low - high
* 25 - 32 = left 7 low - high
* 33 - 40 = right 7 low - high
* 41 - 44 = 45 up,down,left,right
there are 3 types of slopes in gta1, (90°, 45° and 7°) all change level for just one cube.
take parts 1 and 2, these when used together make a pass from lower to upper level over 2 cubes, cube 1 is lower and cube 2 is upper part for this.
* Concerning the FON files; the GTA hacking handbook (http://gta.mendelsohn.de/Reference/Fil_index.html) seems to define them, but the link is broken; the older (zip) version doesn't contain that file. Is that fileformat documented somewhere?
this is very easy format:
Quote:
type
TFon_file_header = packed record
NumPics:byte;
height:Byte;
end;
type
TFon_image_header = packed record
width:byte;
end;
the file starts with TFon_file_header which tells you num pics and all pictures height
then for NumPics there are repeatitous data for characters starting with TFon_image_header that tells you character width, following that you get charwidth * fontheight of 8 bit bitmap data.
last 768 bytes in a file is palette (RGB format)
i have sound format as well:
Quote:
grand theft auto .sdt format
this file is copyright 2002 Delfi delfi@volja.net
data types are named as used in borland delphi's object pascal language
warning!
this is for GTA1 and GTA:L only
gta2 and gta3 use different format...
sdt files contains info for raw sound data files with same name but raw extension
divide size of sdt file by 12 to get number of sound records
each record contains information on how to use raw file
here is delphi packed record:
Tsoundentry = packed record
rawstart: longword;
rawsize: longword;
samplerate: longword;
end;
you can export sound like this:
make new empty file and write wav file header:
TWave_data = packed record
ChunkID: array[0..3] of char; // 'RIFF' text
ChunkSize: Longword;
Format: array[0..3] of char; // 'WAVE' text
Subchunk1ID: array[0..3] of char; // 'fmt ' text
Subchunk1Size: Longword;
AudioFormat: Word;
NumChannels: Word;
SampleRate: Longword;
ByteRate: Longword;
BlockAlign: Word;
BitsPerSample: Word;
Subchunk2ID: array[0..3] of char; // 'data' text
Subchunk2Size: Longword;
end;
set info in header
Wave_data.ChunkID := 'RIFF';
Wave_data.ChunkSize:= 49188;
Wave_data.Format:= 'WAVE';
Wave_data.Subchunk1ID:= 'fmt ';
Wave_data.Subchunk1Size:= 16;
Wave_data.AudioFormat:= 1;
Wave_data.NumChannels:= 1;
Wave_data.SampleRate:= sample rate of entry
Wave_data.BitsPerSample:=8;
Wave_data.ByteRate:= sample rate of entry
Wave_data.BlockAlign:=1;
Wave_data.Subchunk2ID:= 'data';
Wave_data.Subchunk2Size:= raw data size
now copy raw data and write it after this header
if you did everything correct you will be able to play wav file with any program...
something interesting from GTA Wave gta sound editor readme file:
In GTA, all the sounds are in 8-bit mono format, except for those in
LEVEL000. The sounds in this file are 16-bit, the first three being
stereo.
should this be all for now, i have slope cubes in a 3d format representation if you want, email me at jernejcoder@gmail.com or MSN - stdcall@gmail.com (messenger only)
i'm onto a similar project, a gta1 / gta2 clone project, but gta formats are too limiting for me, and i went onto coding my own gta game clone, you can get screenshoots & demo http://gtatools.com/tdc/, it is written with opengl and delphi 4, i've gotten rendering pretty much perfect, and made my own map and sprite editor, but won't be compatible with gta file formats, but converters can be easily done, here is a picture:
gtatools.com/tdc/tdcware.jpg
best regards, Jernej L.

20
doc/gta1_winex.txt Normal file
View File

@ -0,0 +1,20 @@
GTA Settings runs fine here. Winex 2.2.1.
---
In order to run "GTA Settings.exe" you need a legit Windows install (so you have MFC42.DLL to be specific). The DLL needs to be in your WineX "windows\system32" directory.
---
no need to use the setup tool, drop this into your system.reg:
[Software\\DMA Design\\Grand Theft Auto]
"Language"=dword:00000000
[Software\\DMA Design\\Grand Theft Auto\\Controls]
"Control 0"=dword:000000cb
"Control 1"=dword:000000cd
"Control 2"=dword:000000c8
"Control 3"=dword:000000d0
"Control 4"=dword:00000039
"Control 5"=dword:0000001c
"Control 6"=dword:0000001d
"Control 7"=dword:0000002d
"Control 8"=dword:0000002c
"Control 9"=dword:0000000f

79
doc/hacking.txt Normal file
View File

@ -0,0 +1,79 @@
== OpenGTA project overview ==
- \subpage Fileformats
- \subpage Rendering
\namespace Audio
\brief Making noise.
Very incomplete wrapper around \e SDL_sound and \e SDL_mixer.
\namespace Loki
\brief From: http://loki-lib.sourceforge.net/
Loki is a C++ library of designs, containing flexible implementations
of common design patterns and idioms.
I only use the \e Singleton a lot, but there is some nice code there.
\namespace OpenGL
\brief Drawing stuff
A bunch of quite experimental code; the highlights:
- #OpenGL::TextureCache
The following are used as \e Singletons:
- #OpenGL::Camera --- #OpenGL::CameraHolder
- #OpenGL::Screen --- #OpenGL::ScreenHolder
- #OpenGL::SpriteCache --- #OpenGL::SpriteCacheHolder
\namespace Util
\brief This and that
- #Util::ci_string
- #Util::Log
- A bunch of exceptions derived from std::exception
And a \e Singleton object:
- #Util::BufferCache --- #Util::BufferCacheHolder
= Fileformats of the GTA1 data files =
Fortunately some documentation exists, specifically about
the most important formats.
Reading _(cds.doc) is probably vital; you may want them all.
So in no particular order:
include(`doc/doc_links.txt')dnl ~oh m4, your glorious thing~
Support for the following formats is implemented:
- CMP (compressed map) #OpenGTA::Map
- FNT (font bitmaps) #OpenGTA::Font
- FXT (text strings) #OpenGTA::MessageDB
- GRY (8bit graphics) #OpenGTA::Graphics8Bit
- G24 (24 bit graphics) #OpenGTA::Graphics24Bit
- SDT (RAW sound offsets) #OpenGTA::SoundsDB
= Rendering the scene =
The central part is #OpenGTA::CityView, which renders the #OpenGTA::Map
using the graphics loaded inside a derived instance of
#OpenGTA::GraphicsBase.
CityView maintains a couple of #OpenGL::TextureCache (s) to store the
texture-ids (for the static city blocks).
#OpenGTA::BlockData contains the vertex data and texture coords for
each of the of the possible blocks; this corresponds to
#OpenGTA::Map::BlockInfo::slopeType.
Drawing of objects (sprites) is being moved into #OpenGTA::SpriteManager,
though this is not yet complete.
#OpenGL::DrawableFont can display strings using the bitmaps from
a #OpenGTA::Font.

80
doc/more_hints_delfi.txt Normal file
View File

@ -0,0 +1,80 @@
2006/8/21, tok@openlinux.org.uk <tok@openlinux.org.uk>:
> I have got it; quite a lot in there.
>
> Guess I have to try my luck with dosbox, wine and such; last time I
> tried neither m1win nor junction25 were working very well (if I remember
> correctly).
thats weird, since i can run both topdown j25 and 3D beta j25 just
fine, only M1win doesn't work (on xp) for some reason. i sent you old
M1 for DOS in the pack i sent you the url to.
> Thanks for the other tips; now the main problem are the incorrect
> tex-coords. I think I have a pretty good idea why it breaks and a few
> on how to fix it.
> I found the off-by-one in the tile-indices on my own on the same afternoon.
some of your side tiles are rotated, there is no side rotation, sides
only have a bit flag which means "mirror" - no rotations.
cds says about side tiles that bit 5 and 6 in type_map_ext contain the
mirroring:
bit 5 means that textures on north and south sides should be mirrored,
bit 6 indicates same for east and west side of that cube.
> I have started with the g24 files and hit a small snag; I don't
> understand 'palette_index' (so far) and the actual way tiles
> (lid+side textures; not working with sprites so far) index the clut
> isn't working either. [but don't tell me yet]
the pal_index reindexes virtual palette numbers into actual - physical
palette numbers.
it works like this:
you take tile palette index, you add palette_index
for sprites for example you need to skip clut dedicated to tiles:
pal_index = (g24_header.tileclut_size div 1024)+ activesprite.clut;
for tiles it is just simple, but remember - if it is lid you need to
add side tiles to the number (and side tiles + lid tiles for animation
/ aux tiles):
pal_index[tile number]
please_see_attached_illustration which also shows the palclut visualized :)
> I have put a few new screenshots (after your fixes) up and removed the
> code until I have worked out the license.
> I am soon going on vacation for some time, so there won't be any updates
> for a few weeks. You'll hear from me when I am back.
>
> these already look a lot better
> http://skybound.portland.co.uk/gta/nyc_3d__two_bugs_down_2006-08-21.jpg
In the nyc i can clearly see you are rotating side tiles, where they
shouldn't be, side tiles are never rotated in gta1 (they are in gta2)
in gta1 they just have mirroring flag in type_map_ext.
> http://skybound.portland.co.uk/gta/far_out_2006-08-21.jpg
and i am seeing this right? - the san andreas map on screenshoot -
unless you rotated the camera itself, should be rotated 180° left (i
know how it shouls look, since i drawn entire san andreas map layout
in mspaint with alt + tabbing 7 years ago ;) ).
> http://skybound.portland.co.uk/gta/texture_coords_are_next_2006-08-21.jpg
this looks quite right, but it appears lid rotation is wrong in blocks
where the type_map_ext mirror flags are used.
i have a delphi project written in pascal, which can render all tiles
from g24 files, i can send you whole source code for it, it may help
you understand how g24 files work :)
thats all for now, and enjoy your vacations! :)
> Best regards,
> tok
>

115
doc/more_info.txt Normal file
View File

@ -0,0 +1,115 @@
you should only work with g24 files, since most, if not all mods for gta
only use g24 files, they are much better quality and are not any harder than
gry files (actually there are also GRX files described in cds.doc for use by
editor but nobody has these).
you should take a look at how the slope models work, it is all in very
simple order, you could simplify things a lot, since gta1 maps don't use
more complex things.
my friend steve made some nice code for rendering map slopes some time back,
here is how it works in pascal, basicly it modifies edge heights for the
cube depending on slope number, slope 0 is normal cube and other slope
levels modify the height, the bottom vertices stay where they are, so you
can easily draw quads using this:
// 1---2
// | z |
// 3---4
// calculate slope (lid vertex z values)
case s of
1..8: // 2 blocks
begin
z1:=(((s-1) mod 2)+ord(s in [1,2, 5,6]))/2;
z2:=(((s-1) mod 2)+ord(s in [1,2, 7,8]))/2;
z3:=(((s-1) mod 2)+ord(s in [3,4, 5,6]))/2;
z4:=(((s-1) mod 2)+ord(s in [3,4, 7,8]))/2;
end;
9..40: // 8 blocks
begin
z1:=(((s-9) mod 8)+ord(s in [9..16, 25..32]))/8;
z2:=(((s-9) mod 8)+ord(s in [9..16, 33..40]))/8;
z3:=(((s-9) mod 8)+ord(s in [17..24, 25..32]))/8;
z4:=(((s-9) mod 8)+ord(s in [17..24, 33..40]))/8;
end;
41..44: // 1 block
begin
z1:=ord(s in [41, 43]);
z2:=ord(s in [41, 44]);
z3:=ord(s in [42, 43]);
z4:=ord(s in [42, 44]);
end;
else // no slope
z1:=1; z2:=1; z3:=1; z4:=1;
//z1:=0; z2:=0; z3:=0; z4:=0;
end;
i don't recomment display lists for this, i tried it in my game and got huge
slowdowns, maybe if you compiled bigger parts of maps into it, but you will
need to take into account that the game uses sprites, and sprites + slopes
don't mix too good, in my game i solved this by rendering without any depth
buffer at all, but i have to do some painful sorting of rendering order, if
you want to render map with zbuffer you could use projected textures for
cars or render map in layers bottom to up, switching off zbuffer when
rendering sprites.
if a cube is set to flat, all faces on it use transparency, and if left +
right faces use same texture the right face is folded into left double-sided
tile one to form fences (same applies to up and bottom tiles, they create a
double-sided fence on north tile only)
transparency rule in gta this:
every palette index 0 is transparent if the map tile is set to use it. i
recommend you to convert graphical data, take the 8 bit graphic and palette
and create 32 bit image and upload it to opengl, create alpha transparency
channel using palette index 0.
every tile can have 4 shading levels, in g24 they are stored in palette
clut, i recommend you to ignore the shading data as whole, and use glcolor
commands to apply shading levels, also gtacars g24 editor has a terrible
habbit of generating these shades wrong and they look ugly in some mode.
for sprites, they use one palette, except peds (pedesterian and cop) and
cars,
i suggest your game should render all ped sprites into one bigger texture,
using vertex coordinates to address each of these, you will need to create
around 30 or more different recoloured remaps while loading g24 file (all
tiles would fit into 256*512 texture if you use block sorting algorythm to
optimize room usage), every ped remap would cost 524.288 bytes, 30 remaps
would so end up at 15728640b - 15 mb, too bad that all new hardware comes
without support for palettized textures, they would help a lot here, gta1
for windows for example uses driectdraw and the renderer is custom built.
cars are even more difficult, they come in 16 different color variant, and
each of cars can have delta damage or doors or special police light
animations on them, i talked to dma employee working on gta2 and he said
that each car simply uses its own texture, which is created, modified and
destroyed as needed, don't worrs about speed, i done tests for my own game
and i can say that this is not a problem, the deltas don't change too often,
and when they are they are cached as long as the car is onscreen.
some fonts in gta1 have limited use, such as pager messages or score counter
so they are missing characters or use modified order, a lot of font stuff is
hard-coded, but i found out that some onscreen fonts such as score
multiplier and lifes counter fonts are actually remapped using a palette in
G24 file (!!), i suggest you to just use the font as-is and ignore the
recoloring variants, probably very few people would notice this, but then
you can recolour them with glcolor commands aniway.
the idea of a project website sounds good, don't worry about legal troubles,
take2 won't bother about this little project for a older game that they
released as free few years ago on rockstar classics website (
http://www.rockstargames.com/classics/), they did discreetly interfere when
a member on gtaforums ran a project to bring gta vice city map to unreal
tournament engine, and for hot cofee fiasco they knew it was their fault all
the time, and they even supported us with emails that aren't for the public,
as for the CDS and MTD docs, they were officaly released to public with no
licenses (same for gta2 too) so using them is not legal trouble.
also when you have time check out http://www.dmadesign.org/ it is DMA Design
history website ran by Mike Daily who worked on gta1 graphical engine for
gta1, gta1 prototypes and created lemmings game, it has some history pages
and a forum, altrough it looks inactive it is not, if you want you can ask
him questions in the forum :)

1413
doc/slopes1.txt Normal file

File diff suppressed because it is too large Load Diff

33
font_cache.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "font_cache.h"
namespace OpenGTA {
FontCache::FontCache() {
}
FontCache::~FontCache() {
}
OpenGL::DrawableFont & FontCache::getFont(const std::string & file,
const uint32_t scale) {
FontMap::iterator i = findFont(file, scale);
if (i == loadedFonts.end()) {
OpenGL::DrawableFont* fnt = createFont(file, scale);
assert(fnt);
loadedFonts.insert(std::make_pair<FontIdentifier, OpenGL::DrawableFont*>(
FontIdentifier(file, scale), fnt));
return *fnt;
}
return *i->second;
}
FontCache::FontMap::iterator FontCache::findFont(const std::string & file, const uint32_t & scale) {
return loadedFonts.find(FontIdentifier(file, scale));
}
OpenGL::DrawableFont* FontCache::createFont(const std::string & file, const uint32_t & scale) {
OpenGL::DrawableFont * fnt = new OpenGL::DrawableFont();
fnt->setScale(scale);
fnt->loadFont(file);
return fnt;
}
}

44
font_cache.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef FONT_CACHE_H
#define FONT_CACHE_H
#include <string>
#include <map>
#include "gl_font.h"
#include "Singleton.h"
namespace OpenGTA {
class FontCache {
public:
FontCache();
~FontCache();
OpenGL::DrawableFont & getFont(const std::string & file, const uint32_t scale);
private:
struct FontIdentifier {
FontIdentifier(const std::string & f, const uint32_t s) : filename(f), scale(s) {}
const std::string filename;
const uint32_t scale;
bool operator == (const FontIdentifier & o) const {
if (scale == o.scale && filename == o.filename)
return true;
return false;
}
bool operator < (const FontIdentifier & o) const {
if (scale < o.scale)
return true;
if (scale > o.scale)
return false;
return filename < o.filename;
}
};
typedef std::map<FontIdentifier, OpenGL::DrawableFont*> FontMap;
FontMap loadedFonts;
FontMap::iterator findFont(const std::string & file, const uint32_t & scale);
OpenGL::DrawableFont* createFont(const std::string & file, const uint32_t & scale);
};
typedef Loki::SingletonHolder<FontCache, Loki::CreateUsingNew,
Loki::DefaultLifetime, Loki::SingleThreaded> FontCacheHolder;
}
#endif

60
fx_sdt.h Normal file
View File

@ -0,0 +1,60 @@
/* derived from:
*
* grand theft auto .sdt format
* copyright 2002 Delfi delfi@volja.net
*/
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef OGTA_FX_SDT_H
#define OGTA_FX_SDT_H
#include <physfs.h>
#include <string>
#include <map>
namespace OpenGTA {
class SoundsDB {
public:
SoundsDB();
SoundsDB(const std::string & sdt_file);
~SoundsDB();
void load(const std::string & sdt_file);
struct Entry {
Entry(PHYSFS_uint32, PHYSFS_uint32, PHYSFS_uint32);
PHYSFS_uint32 rawStart;
PHYSFS_uint32 rawSize;
PHYSFS_uint32 sampleRate;
};
typedef PHYSFS_uint16 KeyType;
Entry & getEntry(KeyType key);
unsigned char* getBuffered(KeyType key);
private:
void clear();
typedef std::map<KeyType, Entry> MapType;
MapType knownEntries;
PHYSFS_file *dataFile;
};
}
#endif

247
gfx_extract.cpp Normal file
View File

@ -0,0 +1,247 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#include <iostream>
#include <getopt.h>
#include <stdlib.h>
#include <SDL.h>
#ifdef DUMP_DELTA_DEBUG
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#include "opengta.h"
#include "dataholder.h"
#include "m_exceptions.h"
#include "set.h"
SDL_Surface* image = NULL;
void at_exit() {
if (image)
SDL_FreeSurface(image);
PHYSFS_deinit();
SDL_Quit();
}
void display_image(SDL_Surface* s) {
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, SDL_DOUBLEBUF);
SDL_Event event;
SDL_BlitSurface(s, NULL, screen, NULL);
SDL_Flip(screen);
while (1) {
while (SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_QUIT:
return;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE)
return;
default:
break;
}
}
SDL_Delay(100);
}
}
SDL_Surface* get_image(unsigned char* rp, unsigned int w, unsigned int h) {
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define rmask 0xff000000
#define gmask 0x00ff0000
#define bmask 0x0000ff00
#define amask 0x000000ff
#else
#define rmask 0x000000ff
#define gmask 0x0000ff00
#define bmask 0x00ff0000
#define amask 0xff000000
#endif
SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,
w, h, 32, rmask, gmask, bmask, amask);
SDL_LockSurface(s);
unsigned char* dst = static_cast<unsigned char*>(s->pixels);
for (unsigned int i=0; i<w*h; i++) {
*dst = *rp; ++dst;++rp;
*dst = *rp; ++dst;++rp;
*dst = *rp; ++dst;++rp;
*dst = *rp; ++dst;++rp;
//*dst = 0xff; ++dst;
}
SDL_UnlockSurface(s);
return s;
}
void usage(const char* a0) {
std::cout << "USAGE: " << a0 << " --file $STYLE --extract|--display [--section N] --index N" <<
std::endl;
std::cout << "Where section 0 are 'side blocks', 1 'lid blocks', 2 'aux blocks' and 3 are 'sprites'" << std::endl;
std::cout << "You can apply remaps (--remap N) and deltas (--delta N) but the relative indices are" << std::endl;
std::cout << "not always correct (== experimental)." << std::endl;
return;
}
int main(int argc, char* argv[]) {
atexit(at_exit);
PHYSFS_init(argv[0]);
// add pwd to search path
PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1);
PHYSFS_addToSearchPath("gtadata.zip", 1);
char* file = NULL;
SDL_Init(SDL_INIT_VIDEO);
int c = 0;
int mode = 0;
int remap = -1;
int delta = 0;
Util::Set delta_as_set(32, (unsigned char*)&delta);
bool delta_set = false;
bool rgba = true;
unsigned int idx = 0;
unsigned int section = 0;
while (1) {
int option_index = 0;
static struct option long_options[] = {
{"info", 0, 0, 'i'},
{"display", 0, 0, 'd'},
{"extract", 0, 0, 'e'},
{"section", 1, 0, 's'},
{"index", 1, 0, 'x'},
{"file", 1, 0, 'f'},
{"remap", 1, 0, 'r'},
{"delta", 1, 0, 'a'},
{"delta-set", 1, 0, 'A'},
{0, 0, 0, 0}
};
c = getopt_long (argc, argv, "h",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'i':
mode = 0;
break;
case 'd':
mode |= 1;
break;
case 'e':
mode |= 2;
break;
case 'f':
file = optarg;
break;
case 'x':
idx = atoi(optarg);
break;
case 's':
section = atoi(optarg);
break;
case 'r':
remap = atoi(optarg);
break;
case 'a':
delta = atoi(optarg);
break;
case 'A':
delta_set = true;
delta_as_set.set_item(atoi(optarg), true);
break;
case 'h':
default:
usage(argv[0]);
return(0);
}
}
if (!file) {
std::cerr << "Error: no data file selected" << std::endl;
usage(argv[0]);
return 1;
}
if (!PHYSFS_exists(file)) {
std::cerr << "File does not exist in searchpath: " << file << std::endl;
return 1;
}
try {
// exception handling of the constructor doesn't work here; urgh...
OpenGTA::StyleHolder::Instance().load(file);
OpenGTA::GraphicsBase & graphics = OpenGTA::StyleHolder::Instance().get();
if (delta_set)
graphics.setDeltaHandling(true);
if (!mode)
return 0;
switch(section) {
case 0:
graphics.getSide(idx, remap, rgba);
image = get_image(graphics.getTmpBuffer(rgba), 64,64);
break;
case 1:
graphics.getLid(idx, remap, rgba);
image = get_image(graphics.getTmpBuffer(rgba), 64, 64);
break;
case 2:
graphics.getAux(idx, remap, rgba);
image = get_image(graphics.getTmpBuffer(rgba), 64, 64);
break;
case 3:
OpenGTA::GraphicsBase::SpriteInfo *sprite = graphics.getSprite(idx);
std::cout << "Sprite is " << int(sprite->w) << "x" << int(sprite->h) <<
" with " << int(sprite->deltaCount) << " deltas" << std::endl;
image = get_image(graphics.getSpriteBitmap(idx, remap, delta), sprite->w, sprite->h);
#ifdef DUMP_DELTA_DEBUG
if (delta && !delta_set) {
std::cout << "dumping delta" << std::endl;
OpenGTA::GraphicsBase::DeltaInfo & dinfo = sprite->delta[delta-1];
int dump_fd = open("delta.raw", O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR);
write(dump_fd, dinfo.ptr, dinfo.size);
close(dump_fd);
}
#endif
break;
}
if (!image) {
std::cerr << "Error: image pointer is NULL; aborting" << std::endl;
return 1;
}
if (mode & 1) {
display_image(image);
}
if (mode & 2) {
SDL_SaveBMP(image, "out.bmp");
}
}
catch (Exception & e) {
std::cerr << "Exception occured: " << e.what() << std::endl;
return 1;
}
return 0;
}

85
gl_base.cpp Normal file
View File

@ -0,0 +1,85 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#include "gl_base.h"
#include <SDL_opengl.h>
#include "common_sdl_gl.h"
#include "log.h"
using namespace Util;
namespace OpenGL {
template<class T> void ImmediateRenderer<T>::assertCorrectPrimitive(GLenum newType) {
if (!insideBegin) {
WARN << "Missing glBegin() call" << std::endl;
}
else {
if (currentPrimitiveType == newType)
return;
WARN << "Forcing switch of primitive type" << std::endl;
glEnd();
}
glBegin(newType);
currentPrimitiveType = newType;
insideBegin = true;
}
template<class T> void ImmediateRenderer<T>::begin(GLenum type) {
// FIXME: check !insideBegin
currentPrimitiveType = type;
insideBegin = true;
}
template<class T> void ImmediateRenderer<T>::end() {
insideBegin = false;
}
template<class T> GLenum ImmediateRenderer<T>::currentPrimitiveType = GL_POINTS;
template<> void ImmediateRenderer<Quad2Int>::draw(const Quad2Int & quad) {
//assertCorrectPrimitive(quad.primitiveType);
for (int i = 0; i < 4; i++)
glVertex2i(quad.vertices[i][0], quad.vertices[i][1]);
}
template<> void ImmediateRenderer<ColoredQuad2Int>::draw(const ColoredQuad2Int & quad) {
//assertCorrectPrimitive(quad.primitiveType);
for (int i = 0; i < 4; i++) {
glColor3f(quad.colors[i][0], quad.colors[i][1], quad.colors[i][2]);
glVertex2i(quad.vertices[i][0], quad.vertices[i][1]);
}
}
template<> void ImmediateRenderer<FontQuad>::draw(const FontQuad & quad) {
glBindTexture(GL_TEXTURE_2D, quad.texId);
glBegin(quad.primitiveType);
for (int i = 0; i < 4; i++) {
glTexCoord2f(quad.texCoords[i][0], quad.texCoords[i][1]);
glVertex2i(quad.vertices[i][0], quad.vertices[i][1]);
}
glEnd();
}
/*
template<> void ImmediateRenderer<OpenGTA::Map::BlockInfo>::draw(const OpenGTA::Map::BlockInfo & info) {
for (int i = 0; i < 4; ++i) {
}
}
*/
}

94
gl_base.h Normal file
View File

@ -0,0 +1,94 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef GL_BASE_H
#define GL_BASE_H
#include <SDL_opengl.h>
namespace OpenGL {
/*
template<GLenum target>
struct TexOps
{
static GLuint create();
static void bind(GLuint id);
static void destropy(GLuint id);
};
*/
template<typename vertex_type, int entries_per_vertex>
struct Quad
{
vertex_type vertices[4][entries_per_vertex];
static const GLenum primitiveType = GL_QUADS;
};
template<typename normal_type, int entries_per_vertex>
struct QuadNormals
{
normal_type normals[4][entries_per_vertex];
};
template<typename color_type, int num_colors>
struct QuadColors
{
color_type colors[4][num_colors];
};
template<typename texcoord_type, int num_coords>
struct QuadTexCoords
{
texcoord_type texCoords[4][num_coords];
GLuint texId;
};
template<typename vertex_type, int entries_per_vertex, typename color_type, int num_colors>
struct ColoredQuad : public Quad<vertex_type, entries_per_vertex>,
public QuadColors<color_type, num_colors>
{
};
template<typename vertex_type, int entries_per_vertex, typename texcoord_type, int num_coords>
struct TexturedQuad : public Quad<vertex_type, entries_per_vertex>,
public QuadTexCoords<texcoord_type, num_coords>
{
};
typedef Quad<GLint, 2> Quad2Int;
typedef ColoredQuad<GLint, 2, GLfloat, 3> ColoredQuad2Int;
typedef TexturedQuad<GLint, 2, GLfloat, 2> FontQuad;
template<class T> class ImmediateRenderer
{
public:
static void draw(const T & t);
static void begin(GLenum type);
static void end();
protected:
static GLenum currentPrimitiveType;
static bool insideBegin;
static void assertCorrectPrimitive(GLenum newType);
};
// ugly as hell, but seems to work
#define Renderer ImmediateRenderer
}
#endif

255
gl_camera.cpp Normal file
View File

@ -0,0 +1,255 @@
#include "gl_camera.h"
#include "gl_screen.h"
#include "opengta.h"
#include "dataholder.h"
#include <iostream>
#include "log.h"
#include "blockdata.h"
using namespace OpenGTA;
float slope_height_offset(unsigned char slope_type, float dx, float dz);
namespace OpenGL {
/* implementation mainly taken from:
*
* "Talk to me like I'm a 3 year old!" Programming Lessons
* by DigiBen (digiben@gametutorials.com)
*
*/
Camera::Camera() : eye(), center(), up(), doRotate(false),
camGravity(false), gameCamMode(false), followTarget(&center) {
interpolateStart = 0;
interpolateEnd = 0;
}
void Camera::update_game(Uint32 ticks) {
Vector3D delta(center - *followTarget);
//INFO << delta.x << ", " << delta.y << ", " << delta.z << std::endl;
delta.y = 0;
center += -delta;
eye += -delta;
gluLookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z,
up.x, up.y, up.z);
}
void Camera::setFollowMode(const Vector3D & target) {
followTarget = &target;
//INFO << "following " << target.x << ", " << target.y << ", " << target.z << std::endl;
gameCamMode = 1;
}
void Camera::releaseFollowMode() {
followTarget = &center;
gameCamMode = 0;
}
void Camera::update(Uint32 ticks) {
if (gameCamMode) {
update_game(ticks);
return;
}
moveByMouse();
float x,y,z;
x = floor(eye.x);
y = floor(eye.y);
z = floor(eye.z);
int do_grav = 1;
float delta_y = 0;
if (camGravity && do_grav) {
center.y -= 0.1f * ticks/40.0f;
eye.y -= 0.1f * ticks/40.0f;
}
OpenGTA::Map & map = OpenGTA::MapHolder::Instance().get();
if (y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z)) && y > 0.0f) {
OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z), PHYSFS_uint8(y));
if (block->blockType() > 0 && block->blockType() <= 5) {
float bz = slope_height_offset(block->slopeType(), eye.x - x, eye.z - z);
if (block->slopeType() == 0 && block->blockType() != 5)
bz -= 1;
//INFO << int(block->blockType()) << ", " << eye.y << ", " <<
// eye.y - y << ", " << eye.y - y - bz << ", " << bz << std::endl;
float react_delta = 0.3f;
if (block->blockType() == 5)
react_delta = 0.0f;
if (eye.y - y - bz < react_delta) {
//do_grav = 0;
Vector3D new_eye(eye);
new_eye.y = y + bz + react_delta;
delta_y = new_eye.y - eye.y;
center.y = center.y - eye.y + new_eye.y;
eye.y = new_eye.y;
}
}
#if 0
if (y >= 1.0f) {
block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z), PHYSFS_uint8(y-1));
if (block->blockType() > 0) {
INFO << "below is " << int(block->blockType()) << std::endl;
center.y = center.y - eye.y + y + 0.3f;
eye.y = y + 0.3f;
}
}
#endif
}
y -= 1;
if (y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z)) && y > 0.0f) {
OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z), PHYSFS_uint8(y));
if (block->blockType() == 5) {
float bz = slope_height_offset(block->slopeType(), eye.x - x, eye.z - z);
//INFO << eye.y << ", " << y << " bz " << bz << std::endl;
if (eye.y - y - bz < 0.4f) {
Vector3D new_eye(eye);
new_eye.y = y + bz + 0.4;
delta_y = new_eye.y - eye.y;
//INFO << "setting " << new_eye.y << std::endl;
center.y = center.y - eye.y + new_eye.y;
eye.y = new_eye.y;
do_grav = 0;
}
}
}
/*
if (camGravity && do_grav) {
Vector dn(0, -0.1f*do_grav, 0);
eye += dn;
center += dn;
}*/
/*
if (camGravity && do_grav) {
center.y -= 0.1f * ticks/40.0f;
eye.y -= 0.1f * ticks/40.0f;
}*/
if (interpolateStart) {
float as_one = float(interpolateStart - 1) / interpolateEnd;
Vector3D now = (1 - as_one) * interpolateFrom + as_one * interpolateTo;
center = center - eye + now;
eye = now;
interpolateStart += ticks;
if (interpolateStart > interpolateEnd)
interpolateStart = 0;
}
else {
if (speed > 0.01f || speed < -0.01f) {
Vector3D v = center - eye;
if (camGravity)
v.y = delta_y;
v = v.Normalized();
center += speed * ticks/40.0f * v;
eye += speed * ticks/40.0f * v;
//INFO << v.y << std::endl;
}
if (doRotate)
rotateAround(Vector3D(center.x, 0, center.z), 0, 0.01f, 0);
}
gluLookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z,
up.x, up.y, up.z);
}
void Camera::setRotating(bool demo) {
doRotate = demo;
}
void Camera::setCamGravity(bool demo) {
camGravity = demo;
}
void Camera::setVectors(const Vector3D & e, const Vector3D & c, const Vector3D & u) {
eye = e;
center = c;
up = u;
}
void Camera::setSpeed(float new_speed) {
speed = new_speed;
}
void Camera::translateBy(const Vector3D & t) {
eye += t;
center += t;
}
void Camera::translateTo(const Vector3D & e) {
Vector3D rel = eye - e;
translateBy(rel);
}
void Camera::rotateView(float x, float y, float z) {
Vector3D v = center - eye;
if (x) {
center.z = eye.z + sin(x) * v.y + cos(x) * v.z;
center.y = eye.y + cos(x) * v.y - sin(x) * v.z;
}
if (y) {
center.z = eye.z + sin(y) * v.x + cos(y) * v.z;
center.x = eye.x + cos(y) * v.x - sin(y) * v.z;
}
if (z) {
center.x = eye.x + sin(z) * v.y + cos(z) * v.x;
center.y = eye.y + cos(z) * v.y - sin(z) * v.x;
}
}
void Camera::rotateAround(const Vector3D & lookAt, float x, float y, float z) {
Vector3D v = eye - lookAt;
if (x) {
eye.z = lookAt.z + sin(x) * v.y + cos(x) * v.z;
eye.y = lookAt.y + cos(x) * v.y - sin(x) * v.z;
}
if (y) {
eye.z = lookAt.z + sin(y) * v.x + cos(y) * v.z;
eye.x = lookAt.x + cos(y) * v.x - sin(y) * v.z;
}
if (z) {
eye.x = lookAt.x + sin(z) * v.y + cos(z) * v.x;
eye.y = lookAt.y + cos(z) * v.y - sin(z) * v.x;
}
}
void Camera::moveByMouse() {
Screen & screen = ScreenHolder::Instance();
int w, h;
w = screen.getWidth() / 2;
h = screen.getHeight() / 2;
int mx, my;
SDL_GetMouseState(&mx, &my);
SDL_WarpMouse(w, h);
if ((mx == w) && (my == h))
return;
float rot_x = (float(w) - mx) / 100;
float rot_y = (float(h) - my) / 100;
center.y += rot_y * 8;
if (center.y - eye.y > 15)
center.y = eye.y + 15;
else if (center.y - eye.y < -15)
center.y = eye.y - 15;
rotateView(0, -rot_x, 0);
}
void Camera::interpolate(const Vector3D & to, const Uint32 & start, const Uint32 & end) {
interpolateFrom = eye;
interpolateTo = to;
interpolateStart = start;
interpolateEnd = end;
}
#if 0
void QuaternionCamera::update(Uint32 ticks) {
float cur_ratio = 0;
quat step = exp(sampleLinear(ln_start, ln_end, cur_ratio));
mtx44 m = quatToRotationMatrix(step);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMultMatrixf((float*)m.x);
}
#endif
}

51
gl_camera.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef GL_CAMERA_H
#define GL_CAMERA_H
#include <SDL.h>
#include <SDL_opengl.h>
#include "math3d.h"
#include "Singleton.h"
namespace OpenGL {
class Camera {
public:
Camera();
void setSpeed(float forward_is_positive);
void setRotating(bool demo);
void setCamGravity(bool demo);
void rotateView(float x, float y, float z);
void rotateAround(const Vector3D & c, float x, float y, float z);
void translateBy(const Vector3D & t);
void translateTo(const Vector3D & e);
void setVectors(const Vector3D & e, const Vector3D & c, const Vector3D & u);
void moveByMouse();
void interpolate(const Vector3D & to, const Uint32 & start, const Uint32 & end);
void setFollowMode(const Vector3D & target);
void releaseFollowMode();
void update(Uint32 dt);
Vector3D & getEye() { return eye; }
Vector3D & getCenter() { return center; }
Vector3D & getUp() { return up; }
private:
void update_game(Uint32 dt);
Vector3D eye;
Vector3D center;
Vector3D up;
float speed;
bool doRotate;
bool camGravity;
bool gameCamMode;
Vector3D const * followTarget;
Vector3D interpolateFrom;
Vector3D interpolateTo;
Uint32 interpolateStart;
Uint32 interpolateEnd;
};
using namespace Loki;
typedef SingletonHolder<Camera, CreateUsingNew, DefaultLifetime,
SingleThreaded> CameraHolder;
}
#endif

1113
gl_cityview.cpp Normal file

File diff suppressed because it is too large Load Diff

96
gl_cityview.h Normal file
View File

@ -0,0 +1,96 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef GL_CITYVIEW_H
#define GL_CITYVIEW_H
#include "opengta.h"
#include "navdata.h"
#include "gl_texturecache.h"
#include "gl_frustum.h"
#include "gl_pagedtexture.h"
namespace OpenGTA {
class CityView {
public:
CityView();
~CityView();
void loadMap(const std::string &map, const std::string &style);
void createLevelObject(OpenGTA::Map::ObjectPosition *obj);
void setPosition(const GLfloat & x, const GLfloat & y, const GLfloat & z);
void setTopDownView(const GLfloat & height);
//void setCamVector(const GLfloat & x, const GLfloat & y, const GLfloat & z);
void setZoom(const GLfloat zoom);
void setViewMode(bool topDown);
bool getViewMode() { return topDownView; }
void setDrawHeadingArrows(bool yes) { drawHeadingMarkers = yes; }
void setTexFlipTest(int v) { texFlipTest = v; }
GLfloat* getCamPos() { return (GLfloat*)&camPos; }
void setVisibleRange(int);
int getVisibleRange();
void getTerrainHeight(GLfloat & x, GLfloat & y, GLfloat & z);
void draw(Uint32 ticks);
NavData::Sector* getCurrentSector();
OpenGL::PagedTexture renderMap2Texture();
bool CityView::getDrawTextured();
bool CityView::getDrawLines();
void CityView::setDrawTextured(bool v);
void CityView::setDrawLines(bool v);
void resetTextures();
protected:
void setNull();
void cleanup();
void drawBlock(OpenGTA::Map::BlockInfo* bi);
void drawObject(OpenGTA::Map::ObjectPosition*);
//OpenGL::PagedTexture createSprite(size_t sprNum, GraphicsBase::SpriteInfo* info);
Util::CFrustum frustum;
OpenGL::TextureCache<uint8_t>* sideCache;
OpenGL::TextureCache<uint8_t>* lidCache;
Map* loadedMap;
OpenGTA::GraphicsBase* style;
GLfloat zoomLevel;
GLfloat camPos[3];
GLfloat camVec[3];
int visibleRange;
bool topDownView;
bool drawTextured;
bool drawLines;
int scene_rendered_vertices;
int scene_rendered_blocks;
GLuint scene_display_list;
bool scene_is_dirty;
bool drawHeadingMarkers;
int texFlipTest;
Uint32 lastCacheEmptyTicks;
NavData::Sector *current_sector;
};
}
#endif

175
gl_font.cpp Normal file
View File

@ -0,0 +1,175 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#include <cassert>
#include <sstream>
#include "gl_font.h"
#include "log.h"
#include "buffercache.h"
#include "m_exceptions.h"
namespace OpenGL {
DrawableFont::DrawableFont() {
fontSource = NULL;
texCache = NULL;
scale = 1;
}
DrawableFont::~DrawableFont() {
cleanup();
}
void DrawableFont::setScale(unsigned int newScale) {
scale = newScale;
clearCached();
}
void DrawableFont::clearCached() {
std::map<char, FontQuad*>::const_iterator j = drawables.begin();
while (j != drawables.end()) {
delete j->second;
++j;
}
drawables.clear();
}
void DrawableFont::resetTextures() {
clearCached();
texCache->clearAll();
}
void DrawableFont::loadFont(const std::string & filename) {
cleanup();
fontSource = new OpenGTA::Font(filename);
texCache = new TextureCache<char>(("FontTextures: " + filename).c_str());
srcName.clear();
srcName = filename;
}
void DrawableFont::cleanup() {
clearCached();
if (fontSource != NULL)
delete fontSource;
if (texCache != NULL)
delete texCache;
fontSource = NULL;
texCache = NULL;
}
GLfloat DrawableFont::drawString(const std::string & text) {
assert(texCache != NULL);
assert(fontSource != NULL);
std::string::const_iterator i = text.begin();
std::string::const_iterator e = text.end();
GLfloat move = 0.0f;
while (i != e) {
if (*i != ' ') {
FontQuad* character = NULL;
std::map<char, FontQuad*>::const_iterator j = drawables.find(*i);
if (j == drawables.end()) {
character = createDrawableCharacter(*i);
drawables[*i] = character;
}
else
character = j->second;
Renderer<FontQuad>::draw(*character);
}
GLfloat mm = float(fontSource->getMoveWidth(*i)) * 1.1f * scale;
glTranslatef(mm, 0.0f, 0.0f);
move += mm;
i++;
}
return move;
}
uint16_t DrawableFont::getHeight() {
return scale * fontSource->getCharHeight();
}
FontQuad* DrawableFont::createDrawableCharacter(const char & c) {
GLuint texid;
unsigned int w;
unsigned int h;
unsigned char * src = fontSource->getCharacterBitmap(
fontSource->getIdByChar(c), &w, &h);
if (src == NULL) {
std::ostringstream o;
o << "Failed to load bitmap for: " << c;
throw E_UNKNOWNKEY(o.str());
//throw std::string("Failed to load bitmap for character: " + c);
}
unsigned int glwidth = 1;
unsigned int glheight = 1;
while(glwidth < w)
glwidth <<= 1;
while(glheight < h)
glheight <<= 1;
Util::BufferCache & bc = Util::BufferCacheHolder::Instance();
unsigned char* dst = bc.requestBuffer(glwidth * glheight * 4);
assert(dst != NULL);
unsigned char * t = dst;
unsigned char * r = src;
for (unsigned int i = 0; i < h; i++) {
memcpy(t, r, w * 4);
t += glwidth * 4;
r += w * 4;
}
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth, glheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst);
texCache->addTexture(c, texid);
FontQuad* res = new FontQuad();
res->vertices[0][0] = res->vertices[0][1] = 0;
res->vertices[1][0] = w * scale;
res->vertices[1][1] = 0;
res->vertices[2][0] = w * scale;
res->vertices[2][1] = h * scale;
res->vertices[3][0] = 0;
res->vertices[3][1] = h * scale;
float glw = float(w) / float(glwidth);
float glh = float(h) / float(glheight);
res->texCoords[0][0] = 0.0f;
res->texCoords[0][1] = glh;
res->texCoords[1][0] = glw;
res->texCoords[1][1] = glh;
res->texCoords[2][0] = glw;
res->texCoords[2][1] = 0.0f;
res->texCoords[3][0] = 0.0f;
res->texCoords[3][1] = 0.0f;
res->texId = texid;
return res;
}
}
#if 0
int main() {
OpenGL::DrawableFont *f = new OpenGL::DrawableFont();
f->drawString("hello world");
delete f;
}
#endif

54
gl_font.h Normal file
View File

@ -0,0 +1,54 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef M_OPENGL_FONT_H
#define M_OPENGL_FONT_H
#include <string>
#include <map>
#include "opengta.h"
#include "gl_base.h"
#include "gl_texturecache.h"
namespace OpenGL {
class DrawableFont {
public:
DrawableFont();
~DrawableFont();
void loadFont(const std::string & filename);
GLfloat drawString(const std::string & text) ;
void setScale(unsigned int newScale);
uint16_t getHeight();
void resetTextures();
private:
void cleanup();
void clearCached();
FontQuad* createDrawableCharacter(const char & c);
OpenGTA::Font *fontSource;
std::string srcName;
TextureCache<char> *texCache;
std::map<char, FontQuad* > drawables;
unsigned int scale;
};
}
#endif

302
gl_frustum.cpp Normal file
View File

@ -0,0 +1,302 @@
#include <cmath>
#include <SDL_opengl.h>
#include "gl_frustum.h"
namespace Util {
// We create an enum of the sides so we don't have to call each side 0 or 1.
// This way it makes it more understandable and readable when dealing with frustum sides.
enum FrustumSide
{
RIGHT = 0, // The RIGHT side of the frustum
LEFT = 1, // The LEFT side of the frustum
BOTTOM = 2, // The BOTTOM side of the frustum
TOP = 3, // The TOP side of the frustum
BACK = 4, // The BACK side of the frustum
FRONT = 5 // The FRONT side of the frustum
};
// Like above, instead of saying a number for the ABC and D of the plane, we
// want to be more descriptive.
enum PlaneData
{
A = 0, // The X value of the plane's normal
B = 1, // The Y value of the plane's normal
C = 2, // The Z value of the plane's normal
D = 3 // The distance the plane is from the origin
};
///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This normalizes a plane (A side) from a given frustum.
/////
///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void NormalizePlane(float frustum[6][4], int side)
{
// Here we calculate the magnitude of the normal to the plane (point A B C)
// Remember that (A, B, C) is that same thing as the normal's (X, Y, Z).
// To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2)
float magnitude = (float)sqrt( frustum[side][A] * frustum[side][A] +
frustum[side][B] * frustum[side][B] +
frustum[side][C] * frustum[side][C] );
// Then we divide the plane's values by it's magnitude.
// This makes it easier to work with.
frustum[side][A] /= magnitude;
frustum[side][B] /= magnitude;
frustum[side][C] /= magnitude;
frustum[side][D] /= magnitude;
}
///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This extracts our frustum from the projection and modelview matrix.
/////
///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CFrustum::CalculateFrustum()
{
float proj[16]; // This will hold our projection matrix
float modl[16]; // This will hold our modelview matrix
float clip[16]; // This will hold the clipping planes
// glGetFloatv() is used to extract information about our OpenGL world.
// Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix.
// It then stores the matrix into an array of [16].
glGetFloatv( GL_PROJECTION_MATRIX, proj );
// By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix.
// This also stores it in an array of [16].
glGetFloatv( GL_MODELVIEW_MATRIX, modl );
// Now that we have our modelview and projection matrix, if we combine these 2 matrices,
// it will give us our clipping planes. To combine 2 matrices, we multiply them.
clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12];
clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13];
clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14];
clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15];
clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12];
clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13];
clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14];
clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15];
clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12];
clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13];
clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14];
clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15];
clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12];
clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13];
clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14];
clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15];
// Now we actually want to get the sides of the frustum. To do this we take
// the clipping planes we received above and extract the sides from them.
// This will extract the RIGHT side of the frustum
m_Frustum[RIGHT][A] = clip[ 3] - clip[ 0];
m_Frustum[RIGHT][B] = clip[ 7] - clip[ 4];
m_Frustum[RIGHT][C] = clip[11] - clip[ 8];
m_Frustum[RIGHT][D] = clip[15] - clip[12];
// Now that we have a normal (A,B,C) and a distance (D) to the plane,
// we want to normalize that normal and distance.
// Normalize the RIGHT side
NormalizePlane(m_Frustum, RIGHT);
// This will extract the LEFT side of the frustum
m_Frustum[LEFT][A] = clip[ 3] + clip[ 0];
m_Frustum[LEFT][B] = clip[ 7] + clip[ 4];
m_Frustum[LEFT][C] = clip[11] + clip[ 8];
m_Frustum[LEFT][D] = clip[15] + clip[12];
// Normalize the LEFT side
NormalizePlane(m_Frustum, LEFT);
// This will extract the BOTTOM side of the frustum
m_Frustum[BOTTOM][A] = clip[ 3] + clip[ 1];
m_Frustum[BOTTOM][B] = clip[ 7] + clip[ 5];
m_Frustum[BOTTOM][C] = clip[11] + clip[ 9];
m_Frustum[BOTTOM][D] = clip[15] + clip[13];
// Normalize the BOTTOM side
NormalizePlane(m_Frustum, BOTTOM);
// This will extract the TOP side of the frustum
m_Frustum[TOP][A] = clip[ 3] - clip[ 1];
m_Frustum[TOP][B] = clip[ 7] - clip[ 5];
m_Frustum[TOP][C] = clip[11] - clip[ 9];
m_Frustum[TOP][D] = clip[15] - clip[13];
// Normalize the TOP side
NormalizePlane(m_Frustum, TOP);
// This will extract the BACK side of the frustum
m_Frustum[BACK][A] = clip[ 3] - clip[ 2];
m_Frustum[BACK][B] = clip[ 7] - clip[ 6];
m_Frustum[BACK][C] = clip[11] - clip[10];
m_Frustum[BACK][D] = clip[15] - clip[14];
// Normalize the BACK side
NormalizePlane(m_Frustum, BACK);
// This will extract the FRONT side of the frustum
m_Frustum[FRONT][A] = clip[ 3] + clip[ 2];
m_Frustum[FRONT][B] = clip[ 7] + clip[ 6];
m_Frustum[FRONT][C] = clip[11] + clip[10];
m_Frustum[FRONT][D] = clip[15] + clip[14];
// Normalize the FRONT side
NormalizePlane(m_Frustum, FRONT);
}
// The code below will allow us to make checks within the frustum. For example,
// if we want to see if a point, a sphere, or a cube lies inside of the frustum.
// Because all of our planes point INWARDS (The normals are all pointing inside the frustum)
// we then can assume that if a point is in FRONT of all of the planes, it's inside.
///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This determines if a point is inside of the frustum
/////
///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool CFrustum::PointInFrustum( float x, float y, float z )
{
// If you remember the plane equation (A*x + B*y + C*z + D = 0), then the rest
// of this code should be quite obvious and easy to figure out yourself.
// In case don't know the plane equation, it might be a good idea to look
// at our Plane Collision tutorial at www.GameTutorials.com in OpenGL Tutorials.
// I will briefly go over it here. (A,B,C) is the (X,Y,Z) of the normal to the plane.
// They are the same thing... but just called ABC because you don't want to say:
// (x*x + y*y + z*z + d = 0). That would be wrong, so they substitute them.
// the (x, y, z) in the equation is the point that you are testing. The D is
// The distance the plane is from the origin. The equation ends with "= 0" because
// that is true when the point (x, y, z) is ON the plane. When the point is NOT on
// the plane, it is either a negative number (the point is behind the plane) or a
// positive number (the point is in front of the plane). We want to check if the point
// is in front of the plane, so all we have to do is go through each point and make
// sure the plane equation goes out to a positive number on each side of the frustum.
// The result (be it positive or negative) is the distance the point is front the plane.
// Go through all the sides of the frustum
for(int i = 0; i < 6; i++ )
{
// Calculate the plane equation and check if the point is behind a side of the frustum
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0)
{
// The point was behind a side, so it ISN'T in the frustum
return false;
}
}
// The point was inside of the frustum (In front of ALL the sides of the frustum)
return true;
}
///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This determines if a sphere is inside of our frustum by it's center and radius.
/////
///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool CFrustum::SphereInFrustum( float x, float y, float z, float radius )
{
// Now this function is almost identical to the PointInFrustum(), except we
// now have to deal with a radius around the point. The point is the center of
// the radius. So, the point might be outside of the frustum, but it doesn't
// mean that the rest of the sphere is. It could be half and half. So instead of
// checking if it's less than 0, we need to add on the radius to that. Say the
// equation produced -2, which means the center of the sphere is the distance of
// 2 behind the plane. Well, what if the radius was 5? The sphere is still inside,
// so we would say, if(-2 < -5) then we are outside. In that case it's false,
// so we are inside of the frustum, but a distance of 3. This is reflected below.
// Go through all the sides of the frustum
for(int i = 0; i < 6; i++ )
{
// If the center of the sphere is farther away from the plane than the radius
if( m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius )
{
// The distance was greater than the radius so the sphere is outside of the frustum
return false;
}
}
// The sphere was inside of the frustum!
return true;
}
///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This determines if a cube is in or around our frustum by it's center and 1/2 it's length
/////
///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool CFrustum::CubeInFrustum( float x, float y, float z, float size )
{
// This test is a bit more work, but not too much more complicated.
// Basically, what is going on is, that we are given the center of the cube,
// and half the length. Think of it like a radius. Then we checking each point
// in the cube and seeing if it is inside the frustum. If a point is found in front
// of a side, then we skip to the next side. If we get to a plane that does NOT have
// a point in front of it, then it will return false.
// *Note* - This will sometimes say that a cube is inside the frustum when it isn't.
// This happens when all the corners of the bounding box are not behind any one plane.
// This is rare and shouldn't effect the overall rendering speed.
for(int i = 0; i < 6; i++ )
{
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
// If we get here, it isn't in the frustum
return false;
}
return true;
}
bool CFrustum::BlockInFrustum(float x, float z, float size) {
const float b_height = 6.0f;
for(int i = 0; i < 6; i++ )
{
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * 0.0f + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * 0.0f + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * b_height + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * b_height + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * 0.0f + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * 0.0f + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * b_height + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * b_height + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
// If we get here, it isn't in the frustum
return false;
}
return true;
}
}

47
gl_frustum.h Normal file
View File

@ -0,0 +1,47 @@
//***********************************************************************//
// //
// - "Talk to me like I'm a 3 year old!" Programming Lessons - //
// //
// $Author: DigiBen digiben@gametutorials.com //
// //
// $Program: Frustum Culling //
// //
// $Description: Demonstrates checking if shapes are in view //
// //
// $Date: 8/28/01 //
// //
//***********************************************************************//
#ifndef DIGIBEN_FRUSTUM
#define DIGIBEN_FRUSTUM
namespace Util {
// This will allow us to create an object to keep track of our frustum
class CFrustum {
public:
// Call this every time the camera moves to update the frustum
void CalculateFrustum();
// This takes a 3D point and returns TRUE if it's inside of the frustum
bool PointInFrustum(float x, float y, float z);
// This takes a 3D point and a radius and returns TRUE if the sphere is inside of the frustum
bool SphereInFrustum(float x, float y, float z, float radius);
// This takes the center and half the length of the cube.
bool CubeInFrustum( float x, float y, float z, float size );
bool BlockInFrustum(float x, float z, float size);
private:
// This holds the A B C and D values for each side of our frustum.
float m_Frustum[6][4];
};
}
#endif

68
gl_pagedtexture.h Normal file
View File

@ -0,0 +1,68 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef PAGED_TEXTURE_H
#define PAGED_TEXTURE_H
#include <SDL_opengl.h>
namespace OpenGL {
struct TexCoord {
TexCoord(GLfloat a, GLfloat b) : u(a), v(b) {}
TexCoord() : u(0.0f), v(0.0f) {}
GLfloat u;
GLfloat v;
};
struct PagedTexture {
private:
void _copyCoords(const PagedTexture & other) {
for (int i=0; i < 2; ++i) {
coords[i].u = other.coords[i].u;
coords[i].v = other.coords[i].v;
}
}
public:
PagedTexture(GLuint p, GLfloat a, GLfloat b, GLfloat c, GLfloat d) :
inPage(p) {
coords[0].u = a;
coords[0].v = b;
coords[1].u = c;
coords[1].v = d;
}
PagedTexture() : inPage(0) {}
PagedTexture(const PagedTexture & other) : inPage(other.inPage) {
_copyCoords(other);
}
PagedTexture & operator = (const PagedTexture & other) {
inPage = other.inPage;
_copyCoords(other);
return *this;
}
GLuint inPage;
TexCoord coords[2];
};
}
#endif

185
gl_screen.cpp Normal file
View File

@ -0,0 +1,185 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#include <string>
#include "gl_screen.h"
#include "log.h"
#include "buffercache.h"
#include "m_exceptions.h"
namespace OpenGL {
#ifndef DEFAULT_SCREEN_WIDTH
#define DEFAULT_SCREEN_WIDTH 640
#endif
#ifndef DEFAULT_SCREEN_HEIGHT
#define DEFAULT_SCREEN_HEIGHT 480
#endif
Screen::Screen() {
surface = NULL;
videoFlags = defaultVideoFlags;
width = DEFAULT_SCREEN_WIDTH;
height = DEFAULT_SCREEN_HEIGHT;
bpp = 32;
fieldOfView = 60.0f;
nearPlane = 0.1f;
farPlane = 250.0f;
}
void Screen::activate(Uint32 w, Uint32 h) {
if (w)
width = w;
if (h)
height = h;
initSDL();
resize(width, height);
initGL();
setSystemMouseCursor(false);
}
void Screen::setSystemMouseCursor(bool visible) {
SDL_ShowCursor((visible ? SDL_ENABLE : SDL_DISABLE));
}
Uint32 Screen::getWidth() {
return width;
}
Uint32 Screen::getHeight() {
return height;
}
bool Screen::getFullscreen() {
return (videoFlags & SDL_FULLSCREEN);
}
void Screen::setFullScreenFlag(bool v) {
if (v && getFullscreen())
return;
else if (!v && !getFullscreen())
return;
if (v)
videoFlags |= SDL_FULLSCREEN;
else
videoFlags ^= SDL_FULLSCREEN;
}
Screen::~Screen() {
setSystemMouseCursor(true);
if (SDL_WasInit(SDL_INIT_VIDEO))
SDL_Quit();
surface = NULL;
}
void Screen::toggleFullscreen() {
if (videoFlags & SDL_FULLSCREEN)
videoFlags ^= SDL_FULLSCREEN;
else
videoFlags |= SDL_FULLSCREEN;
resize(width, height);
}
void Screen::initSDL() {
int err = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
if (err)
//throw "SDL_Init failed: " + std::string(SDL_GetError());
throw E_INVALIDFORMAT("SDL_Init failed: " + std::string(SDL_GetError()));
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1);
}
void Screen::initGL() {
//GLfloat LightAmbient[] = { 0.8f, 0.8f, 0.8f, 1.0f };
//GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
//GLfloat LightPosition[] = { 128.0f, 200.0f, 128.0f, 1.0f };
//glShadeModel( GL_SMOOTH );
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glEnable( GL_DEPTH_TEST );
//glEnable( GL_LIGHTING );
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
//glLightfv( GL_LIGHT0, GL_AMBIENT, LightAmbient );
//glLightfv( GL_LIGHT0, GL_DIFFUSE, LightDiffuse );
//glLightfv( GL_LIGHT0, GL_POSITION, LightPosition );
//glEnable( GL_LIGHT0 );
glEnable( GL_COLOR_MATERIAL);
glCullFace(GL_BACK);
//glPolygonMode(GL_FRONT, GL_FILL);
//glPolygonMode(GL_BACK, GL_LINE);
}
void Screen::resize(Uint32 w, Uint32 h) {
if (h == 0)
h = 1;
surface = SDL_SetVideoMode(w, h, bpp, videoFlags);
glViewport(0, 0, w, h);
width = w;
height = h;
}
void Screen::set3DProjection() {
float ratio = float(width) / float(height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective( fieldOfView, ratio, nearPlane, farPlane);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void Screen::setFlatProjection() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, 0, height, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void Screen::makeScreenshot(const char* filename) {
INFO << "saving screen as: " << filename << std::endl;
uint8_t *pixels = Util::BufferCacheHolder::Instance().requestBuffer(width * height * 3);
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid*>(pixels));
SDL_Surface* image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24,
255U << (0),
255U << (8),
255U << (16),
0);
SDL_LockSurface(image);
uint8_t *imagepixels = reinterpret_cast<uint8_t*>(image->pixels);
for (int y = (height - 1); y >= 0; --y) {
uint8_t *row_begin = pixels + y * width * 3;
uint8_t *row_end = row_begin + width * 3;
std::copy(row_begin, row_end, imagepixels);
imagepixels += image->pitch;
}
SDL_UnlockSurface(image);
SDL_SaveBMP(image, filename);
SDL_FreeSurface( image );
}
}

66
gl_screen.h Normal file
View File

@ -0,0 +1,66 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef GL_SCREEN_H
#define GL_SCREEN_H
#include <SDL.h>
#include <SDL_opengl.h>
#include "Singleton.h"
namespace OpenGL {
class Screen {
public:
Screen();
~Screen();
void set3DProjection();
void setFlatProjection();
void setFullScreenFlag(bool v);
void toggleFullscreen();
void activate(Uint32 w = 0, Uint32 h = 0);
void resize(Uint32 w, Uint32 h);
void setSystemMouseCursor(bool visible);
Uint32 getWidth();
Uint32 getHeight();
bool getFullscreen();
void makeScreenshot(const char* filename);
private:
void initGL();
void initSDL();
Uint32 width, height;
Uint32 bpp;
Uint32 videoFlags;
float fieldOfView;
float nearPlane;
float farPlane;
static const Uint32 defaultVideoFlags =
SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE | SDL_HWACCEL;
SDL_Surface *surface;
};
using namespace Loki;
typedef SingletonHolder<Screen, CreateUsingNew, DefaultLifetime, SingleThreaded> ScreenHolder;
}
#endif

261
gl_spritecache.cpp Normal file
View File

@ -0,0 +1,261 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#include <map>
#include <cassert>
#include "gl_spritecache.h"
#include "opengta.h"
#include "dataholder.h"
#include "buffercache.h"
#include "log.h"
namespace OpenGL {
SpriteIdentifier::SpriteIdentifier() : sprNum(0), remap(-1), delta(0) {}
SpriteIdentifier::SpriteIdentifier(PHYSFS_uint16 num, PHYSFS_sint16 map, PHYSFS_uint32 d) :
sprNum(num), remap(map), delta(d) {}
SpriteIdentifier::SpriteIdentifier(const SpriteIdentifier & other) :
sprNum(other.sprNum), remap(other.remap), delta(other.delta) {}
bool SpriteIdentifier::operator ==(const SpriteIdentifier & other) const {
if ((sprNum == other.sprNum) &&
(remap == other.remap) &&
(delta == other.delta))
return true;
return false;
}
bool SpriteIdentifier::operator <(const SpriteIdentifier & other) const {
if (sprNum < other.sprNum)
return true;
else if (sprNum > other.sprNum)
return false;
if (remap < other.remap)
return true;
else if (remap > other.remap)
return false;
if (delta < other.delta)
return true;
return false;
}
SpriteCache::SpriteCache() {
#ifdef DO_SCALE2X
doScale2x = true;
#else
doScale2x = false;
#endif
}
void SpriteCache::setScale2x(bool enabled) {
#ifndef DO_SCALE2X
if (enabled)
// FIXME: for some reason I can not catch this exception, thus it only prints
//throw E_NOTSUPPORTED("Scale2x feature disabled at compile time");
ERROR << "scale2x feature disabled at compile time - ignoring request" << std::endl;
#endif
if (loadedSprites.begin() == loadedSprites.end()) {
doScale2x = enabled;
}
else {
ERROR << "scale2x cannot be set during game - ignoring request" << std::endl;
}
}
bool SpriteCache::getScale2x() {
return doScale2x;
}
SpriteCache::~SpriteCache() {
clearAll();
}
void SpriteCache::clearAll() {
SpriteMapType::iterator i = loadedSprites.begin();
while (i != loadedSprites.end()) {
glDeleteTextures(1, &(*i).second.inPage);
++i;
}
loadedSprites.clear();
}
bool SpriteCache::has(PHYSFS_uint16 sprNum) {
SpriteMapType::iterator i = loadedSprites.find(SpriteIdentifier(sprNum, -1, 0));
if (i != loadedSprites.end())
return true;
INFO << "sprite not loaded sprnum: " << sprNum <<std::endl;
return false;
}
bool SpriteCache::has(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap) {
SpriteMapType::iterator i = loadedSprites.find(SpriteIdentifier(sprNum, remap, 0));
if (i != loadedSprites.end())
return true;
INFO << "sprite not loaded sprnum: " << sprNum << " remap: " << remap <<std::endl;
return false;
}
bool SpriteCache::has(const SpriteIdentifier & si) {
SpriteMapType::iterator i = loadedSprites.find(si);
if (i != loadedSprites.end())
return true;
INFO << "sprite not loaded sprnum: " << si.sprNum << " remap: " << si.remap <<
" delta: " << si.delta << std::endl;
return false;
}
PagedTexture & SpriteCache::get(PHYSFS_uint16 sprNum) {
SpriteMapType::iterator i = loadedSprites.find(SpriteIdentifier(sprNum, -1, 0));
assert(i != loadedSprites.end());
return i->second;
}
PagedTexture & SpriteCache::get(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap) {
SpriteMapType::iterator i = loadedSprites.find(SpriteIdentifier(sprNum, remap, 0));
assert(i != loadedSprites.end());
return i->second;
}
PagedTexture & SpriteCache::get(const SpriteIdentifier & si) {
SpriteMapType::iterator i = loadedSprites.find(si);
assert(i != loadedSprites.end());
return i->second;
}
void SpriteCache::add(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap, PagedTexture & t) {
loadedSprites.insert(
std::make_pair<SpriteIdentifier, PagedTexture>(
SpriteIdentifier(sprNum, remap, 0), t));
}
void SpriteCache::add(const SpriteIdentifier & si, PagedTexture & t) {
loadedSprites.insert(std::make_pair<SpriteIdentifier, PagedTexture>(si, t));
}
PagedTexture SpriteCache::create(PHYSFS_uint16 sprNum,
OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes st, PHYSFS_sint16 remap = -1 ) {
/*
OpenGTA::GraphicsBase & style = OpenGTA::StyleHolder::Instance().get();
PHYSFS_uint16 real_num = style.spriteNumbers.reIndex(sprNum, st);
OpenGTA::GraphicsBase::SpriteInfo* info = style.getSprite(real_num);
assert(info);
OpenGL::PagedTexture t = createSprite(real_num, remap, info);
add(real_num, remap, t);
return t;
*/
return create(sprNum, st, remap, 0);
}
PagedTexture SpriteCache::create(PHYSFS_uint16 sprNum,
OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes st,
PHYSFS_sint16 remap, PHYSFS_uint32 delta) {
OpenGTA::GraphicsBase & style = OpenGTA::StyleHolder::Instance().get();
PHYSFS_uint16 real_num = style.spriteNumbers.reIndex(sprNum, st);
OpenGTA::GraphicsBase::SpriteInfo* info = style.getSprite(real_num);
assert(info);
OpenGL::PagedTexture t = createSprite(real_num, remap, delta, info);
SpriteIdentifier si(real_num, remap, delta);
add(si, t);
return t;
}
OpenGL::PagedTexture SpriteCache::createSprite(size_t sprite_num, PHYSFS_sint16 remap,
PHYSFS_uint32 delta, OpenGTA::GraphicsBase::SpriteInfo* info) {
INFO << "creating new sprite: " << sprite_num << " remap: " << remap << std::endl;
unsigned char* src = OpenGTA::StyleHolder::Instance().get().
getSpriteBitmap(sprite_num, remap , delta);
unsigned int glwidth = 1;
unsigned int glheight = 1;
while(glwidth < info->w)
glwidth <<= 1;
while(glheight < info->h)
glheight <<= 1;
unsigned char* dst = Util::BufferCacheHolder::Instance().requestBuffer(glwidth * glheight * 4);
Util::BufferCacheHolder::Instance().unlockBuffer(src);
assert(dst != NULL);
unsigned char * t = dst;
unsigned char * r = src;
for (unsigned int i = 0; i < info->h; i++) {
memcpy(t, r, info->w * 4);
t += glwidth * 4;
r += info->w * 4;
}
#ifdef DO_SCALE2X
if (doScale2x) {
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
const int srcpitch = glwidth * 4;
const int dstpitch = glwidth * 8;
Uint8* srcpix = dst;
Util::BufferCacheHolder::Instance().lockBuffer(dst);
Uint8* dstpix = Util::BufferCacheHolder::Instance().requestBuffer(glwidth * glheight * 4 * 4);
Uint32 E0, E1, E2, E3, B, D, E, F, H;
for(unsigned int looph = 0; looph < glheight; ++looph)
{
for(unsigned int loopw = 0; loopw < glwidth; ++ loopw)
{
B = *(Uint32*)(srcpix + (MAX(0,looph-1)*srcpitch) + (4*loopw));
D = *(Uint32*)(srcpix + (looph*srcpitch) + (4*MAX(0,loopw-1)));
E = *(Uint32*)(srcpix + (looph*srcpitch) + (4*loopw));
F = *(Uint32*)(srcpix + (looph*srcpitch) + (4*MIN(glwidth-1,loopw+1)));
H = *(Uint32*)(srcpix + (MIN(glheight-1,looph+1)*srcpitch) + (4*loopw));
E0 = D == B && B != F && D != H ? D : E;
E1 = B == F && B != D && F != H ? F : E;
E2 = D == H && D != B && H != F ? D : E;
E3 = H == F && D != H && B != F ? F : E;
*(Uint32*)(dstpix + looph*2*dstpitch + loopw*2*4) = E0;
*(Uint32*)(dstpix + looph*2*dstpitch + (loopw*2+1)*4) = E1;
*(Uint32*)(dstpix + (looph*2+1)*dstpitch + loopw*2*4) = E2;
*(Uint32*)(dstpix + (looph*2+1)*dstpitch + (loopw*2+1)*4) = E3;
}
}
Util::BufferCacheHolder::Instance().unlockBuffer(dst);
dst = dstpix;
}
#endif
GLuint texid;
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#ifdef DO_SCALE2X
if (doScale2x)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth*2, glheight*2, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth, glheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth, glheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst);
#endif
return OpenGL::PagedTexture(texid, 0, 0,
float(info->w)/float(glwidth), float(info->h)/float(glheight));
}
}

81
gl_spritecache.h Normal file
View File

@ -0,0 +1,81 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef SPRITE_CACHE_H
#define SPRITE_CACHE_H
#include <physfs.h>
#include "Singleton.h"
#include "gl_pagedtexture.h"
#include "gl_texturecache.h"
#include "opengta.h"
namespace OpenGL {
struct SpriteIdentifier;
struct SpriteIdentifier {
PHYSFS_uint16 sprNum;
PHYSFS_sint16 remap;
PHYSFS_uint32 delta;
SpriteIdentifier();
SpriteIdentifier(PHYSFS_uint16, PHYSFS_sint16, PHYSFS_uint32);
SpriteIdentifier(const SpriteIdentifier & other);
bool operator ==(const SpriteIdentifier & other) const;
bool operator <(const SpriteIdentifier & other) const;
};
class SpriteCache {
public:
SpriteCache();
~SpriteCache();
void clearAll();
bool getScale2x();
void setScale2x(bool enabled);
bool has(PHYSFS_uint16 sprNum);
bool has(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap);
bool has(const SpriteIdentifier & si);
PagedTexture & get(PHYSFS_uint16 sprNum);
PagedTexture & get(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap);
PagedTexture & get(const SpriteIdentifier & si);
void add(PHYSFS_uint16 sprNum, PagedTexture & t);
void add(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap, PagedTexture & t);
void add(const SpriteIdentifier & si, PagedTexture & t);
PagedTexture create(PHYSFS_uint16 sprNum,
OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes, PHYSFS_sint16 remap);
PagedTexture create(PHYSFS_uint16 sprNum,
OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes,
PHYSFS_sint16 remap, PHYSFS_uint32 delta);
OpenGL::PagedTexture SpriteCache::createSprite(size_t sprite_num, PHYSFS_sint16 remap,
PHYSFS_uint32 delta, OpenGTA::GraphicsBase::SpriteInfo* info);
private:
typedef std::map<SpriteIdentifier, PagedTexture> SpriteMapType;
SpriteMapType loadedSprites;
bool doScale2x;
};
typedef Loki::SingletonHolder<SpriteCache, Loki::CreateUsingNew,
Loki::DefaultLifetime, Loki::SingleThreaded> SpriteCacheHolder;
}
#endif

250
gl_texturecache.cpp Normal file
View File

@ -0,0 +1,250 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#include <iostream>
#include <limits>
#include <sstream>
#include "gl_texturecache.h"
#include "log.h"
namespace OpenGL {
template <typename key_type>
TextureCache<key_type>::TextureCache(const char* with_name) : m_name(with_name) {
instance_id = instance_count++;
clearMagic = 0;
has_cached_query = false;
last_query_result = 0;
minClearElements = 50;
}
template <typename key_type>
TextureCache<key_type>::TextureCache() {
instance_id = instance_count++;
std::ostringstream stream;
stream << "TextureCache_" << instance_count;
m_name = stream.str();
has_cached_query = false;
clearMagic = 0;
minClearElements = 50;
}
template <typename key_type>
TextureCache<key_type>::~TextureCache() {
unsigned int ts = cached.size();
clearAll();
INFO << m_name << " exited - " << ts << " textures recycled" << std::endl;
m_name.clear();
instance_count--;
}
template <typename key_type>
void TextureCache<key_type>::clearAll() {
typename std::map<key_type, texTuple*>::iterator i = cached.begin();
while (i != cached.end()) {
GLuint tid = (i->second->texId);
glDeleteTextures(1, &tid);
delete i->second;
i++;
}
cached.clear();
}
template <typename key_type>
void TextureCache<key_type>::status() {
std::cout << "* " << m_name << " status: " << cached.size() << " textures total" << std::endl
<< "position = game_id : usage_count" << std::endl;
printStats();
}
template <typename key_type>
void TextureCache<key_type>::sink() {
typename std::map<key_type, texTuple*>::iterator i = cached.begin();
while (i != cached.end()) {
if (i->second->refCount <= 1)
i->second->refCount = 0;
else if (i->second->refCount < _max_4)
i->second->refCount = i->second->refCount >> 1;
else if (i->second->refCount < _max_2) {
INFO << m_name << " texture id " << int(i->first) <<
" -- half-count reached" << std::endl;
i->second->refCount = i->second->refCount >> 2;
}
else {
WARN << m_name << " texture id " << int(i->first) <<
" -- going critical" << std::endl;
i->second->refCount = i->second->refCount >> 3;
}
i++;
}
}
template <typename key_type>
void TextureCache<key_type>::clear() {
if (clearMagic == 0)
return;
if (cached.size() < minClearElements)
return;
typename std::map<key_type, texTuple*>::iterator i = cached.begin();
uint32_t numCleared = 0;
while (i != cached.end()) {
if (i->second->refCount < clearMagic) {
//INFO <<"## " << m_name << " clearing: " << int(i->first) << " count: " << i->second->refCount << std::endl;
GLuint tid = (i->second->texId);
glDeleteTextures(1, &tid);
delete i->second;
cached.erase(i);
numCleared++;
}
i++;
}
INFO << m_name << " " << numCleared << " textures recycled" << std::endl;
}
template <typename key_type>
void TextureCache<key_type>::clearStats() {
typename std::map<key_type, texTuple*>::iterator i = cached.begin();
while (i != cached.end()) {
i->second->refCount = 0;
i++;
}
}
template <typename key_type>
void TextureCache<key_type>::printStats() {
typename std::map<key_type, texTuple*>::iterator i = cached.begin();
size_t c = 1;
size_t c_active = 0;
while (i != cached.end()) {
if (i->second->refCount > 0) {
std::cout << c << " = " << uint32_t(i->first) << " : " << i->second->refCount << std::endl;
c_active++;
}
i++;
c++;
}
std::cout << c_active << " different textures used" << std::endl;
}
template <typename key_type>
GLuint TextureCache<key_type>::getTextureWithId(key_type id) {
if (matchingCachedQuery(id)) {
last_query_result->refCount++;
return last_query_result->texId;
}
typename std::map<key_type, texTuple*>::iterator i = cached.find(id);
if (i == cached.end()) {
ERROR << m_name << " failed to find texture " << int(id) << std::endl;
return 0;
}
else {
cacheQuery(id, i->second);
i->second->refCount++;
}
/*
* if (i->second->isAnimated) {
AnimControl->lookup(i->second)
* }
*/
return i->second->texId;
}
template <typename key_type>
bool TextureCache<key_type>::hasTexture(key_type id) {
if (matchingCachedQuery(id))
return true; // last_query_result;
typename std::map<key_type, texTuple*>::iterator i = cached.find(id);
if (i == cached.end())
return false;
cacheQuery(id, i->second);
return true;
}
template <typename key_type>
void TextureCache<key_type>::setToAlpha(key_type id) {
typename std::map<key_type, texTuple*>::iterator i = cached.find(id);
if (i == cached.end()) {
ERROR << m_name << " texture not found when trying to set alpha" << std::endl;
return;
}
i->second->hasAlpha = true;
}
template <typename key_type>
void TextureCache<key_type>::setToAnimated(key_type id) {
typename std::map<key_type, texTuple*>::iterator i = cached.find(id);
if (i == cached.end()) {
ERROR << m_name << " texture not found when trying to set animation" << std::endl;
return;
}
i->second->isAnimated = true;
}
template <typename key_type>
void TextureCache<key_type>::addTexture(key_type id, GLuint texId) {
/*
std::map<uint8_t, texTuple*>::iterator i = cached.find(id);
if (i == cached.end())
return;*/
texTuple* tt = new texTuple();
tt->texId = texId;
tt->refCount = 1;
tt->hasAlpha = false;
tt->isAnimated = false;
cached[id] = tt;
INFO << m_name << " GL texture " << texId << " added for key: " << int(id) << std::endl;
}
template <typename key_type>
void TextureCache<key_type>::cacheQuery(key_type id, texTuple *pos) {
has_cached_query = true;
last_query_id = id;
last_query_result = pos;
}
template <typename key_type>
bool TextureCache<key_type>::matchingCachedQuery(key_type id) {
return ((has_cached_query) && (id == last_query_id));
}
template <typename key_type>
void TextureCache<key_type>::setClearMagic(uint32_t removeLesser) {
clearMagic = removeLesser;
}
template <typename key_type>
void TextureCache<key_type>::setMinClearElements(uint32_t minElements) {
minClearElements = minElements;
}
template <typename key_type>
unsigned int TextureCache<key_type>::instance_count = 0;
std::numeric_limits<uint32_t> _countInfo;
template <typename key_type>
const uint32_t TextureCache<key_type>::_max_4 = _countInfo.max() / 4;
template <typename key_type>
const uint32_t TextureCache<key_type>::_max_2 = _countInfo.max() / 2;
template class TextureCache<uint8_t>;
template class TextureCache<char>;
template class TextureCache<uint16_t>;
template class TextureCache<uint32_t>;
}

142
gl_texturecache.h Normal file
View File

@ -0,0 +1,142 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef TEXTURECACHE_H
#define TEXTURECACHE_H
#include <map>
#include <string>
#include <SDL_opengl.h>
namespace OpenGL {
/** Helper for the render code.
*
* This class handles the storage of GLuint texture ids and
* provides a mapping from the internal ids used by the map files.
*/
template <typename key_type> class TextureCache {
public:
/** Simple constructor.
*
* Name will be set to TextureCache_N, where N is the current instance count.
*/
TextureCache();
/** Constructor with a name.
*
* Set a name for this instance; to clarify status output with multiple caches.
*/
TextureCache(const char * with_name);
/** Just a simple destructor, nothing see here... move along.
*
* As a sidenote: this calls 'glDeleteTextures' on the images
* stored inside.
*/
~TextureCache();
/** Check if texture is already cached.
*
* @param id key from map/block info
* @return true if texture is found
* @return false otherwise
*/
bool hasTexture(key_type id);
/** Maps internal id to GLuint texture id.
*
* @param id from map/block info
* @return texture id
*/
GLuint getTextureWithId(key_type id);
/** Adds a texture to the cache and maps to internal id.
*
* @param id requested internal id from map/block info
* @param texId texture id that contains the image
*/
void addTexture(key_type id, GLuint texId);
/** Set specified texture to hasAlpha.
*
* This doesn't do anything; you can just check for hasAlpha later on.
*/
void setToAlpha(key_type id);
/** probably stupid idea/going to go away
*/
void setToAnimated(key_type id);
/** Dumps some status info to stdout.
*/
void status();
/** Iterate over stored textures and modify refCount.
*
* This is optional functionality; you may skip this. If you don't,
* call this *before* each rendering pass.
*/
void sink();
/** Remove unused textures from cache and video memory.
*
* Call this *after* a rendering pass, but not every frame!
*
* Handle with care, this code is experimental.
*/
void clear();
void clearAll();
void clearStats();
void printStats();
void setClearMagic(uint32_t removeLesser);
void setMinClearElements(uint32_t minElements);
protected:
unsigned int clearMagic;
unsigned int minClearElements;
typedef struct texTuple {
GLuint texId;
uint32_t refCount;
bool hasAlpha;
bool isAnimated;
} texTuple;
typedef std::map<key_type, texTuple*> CacheMapType;
CacheMapType cached;
std::string m_name;
static unsigned int instance_count;
unsigned int instance_id;
const static uint32_t _max_4;
const static uint32_t _max_2;
bool has_cached_query;
key_type last_query_id;
texTuple* last_query_result;
bool matchingCachedQuery(key_type id);
void cacheQuery(key_type id, texTuple *pos);
};
}
#endif

64
license.txt Normal file
View File

@ -0,0 +1,64 @@
Copyright (c) 2005-2006 tok@openlinux.org.uk
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
The act of running this software is not restricted; you may redistribute
it and the corresponding machine-readable source code freely, subject to
the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software.
2. You may not use the executable form in a commercial product.
3. A derived work must either inherit the non-commercial restriction or
all the data structures copyrighted by DMA Design have to be replaced.
Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
5. This notice may not be removed or altered from any distribution.
***************************************************************************
The file formats of the data files are described in documentation released
by DMA Design; this software would not exist without it.
It is my understanding that this documentation was meant for fans of the
original game, thus I cannot place the implementing code under a free
license.
This restriction only applies to a few of the code files (marked as such).
***************************************************************************
This software includes a number of libraries and smaller code blocks;
these remain under their respective licenses.
* SDL - Simple DirectMedia Layer
Copyright (C) 1997-2006 Sam Lantinga
GNU Lesser General Public License
* SDL_image
Copyright (C) 1999-2004 Sam Lantinga
GNU Library General Public License
* PhysicsFS
Copyright (c) 2003 Ryan C. Gordon and others
This library is distributed under the terms of the zlib license
* The Loki Library
Copyright (c) 2001 by Andrei Alexandrescu (and others)
MIT license as well as the following:
Permission to use, copy, modify, distribute and sell this software for any
purpose is hereby granted without fee, provided that the above copyright
notice appear in all copies and that both that copyright notice and this
permission notice appear in supporting documentation.
***************************************************************************
Please do not redistribute the data files of the original game together
with this software.
GTA is a trademark of Take 2 Interactive Software Inc.
This project is not affiliated with either Take 2 Interactive or DMA
Design.

BIN
licenses/LGPL-2.1.gz Normal file

Binary file not shown.

BIN
licenses/LGPL-2.gz Normal file

Binary file not shown.

21
licenses/mit.txt Normal file
View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

5
licenses/readme.txt Normal file
View File

@ -0,0 +1,5 @@
This directory contains a copy of the licenses covering the libraries
used in this project.
To comply with the SDL license at least the GNU licenses have to be
present in any distribution.

21
licenses/zlib.txt Normal file
View File

@ -0,0 +1,21 @@
The zlib/libpng License
Copyright (c) <year> <copyright holders>
This software is provided as-is, without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

90
lid_normal_data.h Normal file
View File

@ -0,0 +1,90 @@
// slope 0
{ 0.000000, 1.000000, -0.000000 },
// slope 1
{ 0.000000, 0.894427, 0.447214 },
// slope 2
{ 0.000000, 0.894427, 0.447214 },
// slope 3
{ 0.000000, 0.894427, -0.447214 },
// slope 4
{ 0.000000, 0.894427, -0.447214 },
// slope 5
{ 0.447214, 0.894427, -0.000000 },
// slope 6
{ 0.447214, 0.894427, -0.000000 },
// slope 7
{ -0.447214, 0.894427, 0.000000 },
// slope 8
{ -0.447214, 0.894427, 0.000000 },
// slope 9
{ 0.000000, 0.991656, 0.128915 },
// slope 10
{ 0.000000, 0.992877, 0.119145 },
// slope 11
{ 0.000000, 0.991656, 0.128915 },
// slope 12
{ 0.000000, 0.992877, 0.119145 },
// slope 13
{ 0.000000, 0.991656, 0.128915 },
// slope 14
{ 0.000000, 0.992877, 0.119145 },
// slope 15
{ 0.000000, 0.991656, 0.128915 },
// slope 16
{ 0.000000, 0.992877, 0.119145 },
// slope 17
{ 0.000000, 0.991656, -0.128915 },
// slope 18
{ 0.000000, 0.992877, -0.119145 },
// slope 19
{ 0.000000, 0.991656, -0.128915 },
// slope 20
{ 0.000000, 0.992877, -0.119145 },
// slope 21
{ 0.000000, 0.991656, -0.128915 },
// slope 22
{ 0.000000, 0.992877, -0.119145 },
// slope 23
{ 0.000000, 0.991656, -0.128915 },
// slope 24
{ 0.000000, 0.992877, -0.119145 },
// slope 25
{ 0.128915, 0.991656, -0.000000 },
// slope 26
{ 0.119145, 0.992877, -0.000000 },
// slope 27
{ 0.128915, 0.991656, -0.000000 },
// slope 28
{ 0.119145, 0.992877, -0.000000 },
// slope 29
{ 0.128915, 0.991656, -0.000000 },
// slope 30
{ 0.119145, 0.992877, -0.000000 },
// slope 31
{ 0.128915, 0.991656, -0.000000 },
// slope 32
{ 0.119145, 0.992877, -0.000000 },
// slope 33
{ -0.128915, 0.991656, 0.000000 },
// slope 34
{ -0.119145, 0.992877, 0.000000 },
// slope 35
{ -0.128915, 0.991656, 0.000000 },
// slope 36
{ -0.119145, 0.992877, 0.000000 },
// slope 37
{ -0.128915, 0.991656, 0.000000 },
// slope 38
{ -0.119145, 0.992877, 0.000000 },
// slope 39
{ -0.128915, 0.991656, 0.000000 },
// slope 40
{ -0.119145, 0.992877, 0.000000 },
// slope 41
{ 0.000000, 0.707107, 0.707107 },
// slope 42
{ 0.000000, 0.707107, -0.707107 },
// slope 43
{ 0.707107, 0.707107, -0.000000 },
// slope 44
{ -0.707107, 0.707107, 0.000000 }

17
localplayer.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef OGTA_LOCAL_PLAYER_H
#define OGTA_LOCAL_PLAYER_H
#include "Singleton.h"
#include "pedestrian.h"
namespace OpenGTA {
class PlayerController : public Pedestrian::Controller {
public:
PlayerController() { turn = 0; move = 0; }
};
typedef Loki::SingletonHolder<PlayerController, Loki::CreateUsingNew,
Loki::DefaultLifetime, Loki::SingleThreaded> LocalPlayer;
}
#endif

15
loki.make Normal file
View File

@ -0,0 +1,15 @@
VER = 0.1.5
all: loki/lib/libloki.a
loki-$(VER).tar.gz:
wget http://surfnet.dl.sourceforge.net/sourceforge/loki-lib/loki-$(VER).tar.gz
loki-$(VER): loki-$(VER).tar.gz
tar zxf loki-$(VER).tar.gz
loki: loki-$(VER)
mv loki-$(VER) loki
loki/lib/libloki.a: loki
make -C loki build-static

19
loki.make.w32_cross Normal file
View File

@ -0,0 +1,19 @@
VER = 0.1.5
all: loki/lib/libloki.a
export CXX = i586-mingw32msvc-g++
export AR = i586-mingw32msvc-ar
loki-$(VER).tar.gz:
wget http://surfnet.dl.sourceforge.net/sourceforge/loki-lib/loki-$(VER).tar.gz
loki-$(VER): loki-$(VER).tar.gz
tar zxf loki-$(VER).tar.gz
loki: loki-$(VER)
mv loki-$(VER) loki
loki/lib/libloki.a: loki
./tools/replace_in_files.sh 's/export OS ?= .*/export OS = Windows/' loki/Makefile
make -C loki build-static build-shared

10
lua_addon/lua.hpp Normal file
View File

@ -0,0 +1,10 @@
#ifndef LUA_CPP_H
#define LUA_CPP_H
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
#endif

92
lua_addon/lua_camera.cpp Normal file
View File

@ -0,0 +1,92 @@
#include "lua_camera.h"
#define method(name) {#name, Camera::l_##name}
namespace OpenGTA {
namespace Script {
using namespace OpenGL;
int Camera::l_setSpeed(lua_State *L) {
float tmp = float(luaL_checknumber(L, 1));
CameraHolder::Instance().setSpeed(tmp);
return 0;
}
int Camera::l_setRotating(lua_State *L) {
bool b = lua_toboolean(L, 1);
CameraHolder::Instance().setRotating(b);
return 0;
}
int Camera::l_getEye(lua_State *L) {
Vector3D & e = CameraHolder::Instance().getEye();
lua_pushnumber(L, e.x);
lua_pushnumber(L, e.y);
lua_pushnumber(L, e.z);
return 3;
}
int Camera::l_setEye(lua_State *L) {
Vector3D & e = CameraHolder::Instance().getEye();
e.x = luaL_checknumber(L, 1);
e.y = luaL_checknumber(L, 2);
e.z = luaL_checknumber(L, 3);
return 0;
}
int Camera::l_getCenter(lua_State *L) {
Vector3D & e = CameraHolder::Instance().getCenter();
lua_pushnumber(L, e.x);
lua_pushnumber(L, e.y);
lua_pushnumber(L, e.z);
return 3;
}
int Camera::l_setCenter(lua_State *L) {
Vector3D & e = CameraHolder::Instance().getCenter();
e.x = luaL_checknumber(L, 1);
e.y = luaL_checknumber(L, 2);
e.z = luaL_checknumber(L, 3);
return 0;
}
int Camera::l_getUp(lua_State *L) {
Vector3D & e = CameraHolder::Instance().getUp();
lua_pushnumber(L, e.x);
lua_pushnumber(L, e.y);
lua_pushnumber(L, e.z);
return 0;
}
int Camera::l_setUp(lua_State *L) {
Vector3D & e = CameraHolder::Instance().getUp();
e.x = luaL_checknumber(L, 1);
e.y = luaL_checknumber(L, 2);
e.z = luaL_checknumber(L, 3);
return 0;
}
int Camera::l_setGravityOn(lua_State *L) {
bool v = lua_toboolean(L, 1);
CameraHolder::Instance().setCamGravity(v);
return 0;
}
int Camera::l_interpolateToPosition(lua_State *L) {
float x, y, z;
x = float(luaL_checknumber(L, 1));
y = float(luaL_checknumber(L, 2));
z = float(luaL_checknumber(L, 3));
Uint32 msecInterval = Uint32(luaL_checkinteger(L, 4));
CameraHolder::Instance().interpolate(Vector3D(x, y, z), 1, msecInterval);
return 0;
}
const luaL_reg Camera::methods[] = {
method(setSpeed),
method(setRotating),
method(setEye),
method(getEye),
method(setCenter),
method(getCenter),
method(getUp),
method(setUp),
method(setGravityOn),
method(interpolateToPosition),
{NULL, NULL}
};
}
}

31
lua_addon/lua_camera.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef LUA_OGTA_CAMERA_H
#define LUA_OGTA_CAMERA_H
#include "gl_camera.h"
#include "lua.hpp"
namespace OpenGTA {
namespace Script {
class Camera {
public:
static int l_setSpeed(lua_State *L);
static int l_setRotating(lua_State *L);
static int l_setGravityOn(lua_State *L);
static int l_interpolateToPosition(lua_State *L);
static int l_getEye(lua_State *L);
static int l_setEye(lua_State *L);
static int l_getCenter(lua_State *L);
static int l_setCenter(lua_State *L);
static int l_getUp(lua_State *L);
static int l_setUp(lua_State *L);
/*static int mute(lua_State *L);
static int unmute(lua_State *L);*/
static const luaL_reg methods[];
};
}
}
#endif

View File

@ -0,0 +1,71 @@
#include "lua_cityview.h"
namespace OpenGTA {
namespace Script {
int CityView::setCamPosition(lua_State *L) {
float x = float(luaL_checknumber(L, 1));
float y = float(luaL_checknumber(L, 2));
float z = float(luaL_checknumber(L, 3));
setPosition(x, y, z);
return 0;
}
int CityView::getCamPosition(lua_State *L) {
lua_pushnumber(L, camPos[0]);
lua_pushnumber(L, camPos[1]);
lua_pushnumber(L, camPos[2]);
return 3;
}
/*
int CityView::setCamVector(lua_State *L) {
float x = float(luaL_checknumber(L, 1));
float y = float(luaL_checknumber(L, 2));
float z = float(luaL_checknumber(L, 3));
OpenGTA::CityView::setCamVector(x, y, z);
return 0;
}
int CityView::getCamVector(lua_State *L) {
lua_pushnumber(L, camVec[0]);
lua_pushnumber(L, camVec[1]);
lua_pushnumber(L, camVec[2]);
return 3;
}
*/
int CityView::setTopDownView(lua_State *L) {
setViewMode(lua_toboolean(L, 1));
return 0;
}
int CityView::setDrawHeadingArrows(lua_State *L) {
OpenGTA::CityView::setDrawHeadingArrows(lua_toboolean(L, 1));
return 0;
}
int CityView::setVisibleRange(lua_State *L) {
OpenGTA::CityView::setVisibleRange(luaL_checkinteger(L, 1));
return 0;
}
int CityView::getVisibleRange(lua_State *L) {
lua_pushnumber(L, visibleRange);
return 1;
}
const char CityView::className[] = "CityView";
#define method(name) {#name, &CityView::name}
Lunar<CityView>::RegType CityView::methods[] = {
method(setCamPosition),
method(getCamPosition),
// method(setCamVector),
// method(getCamVector),
method(setTopDownView),
method(setVisibleRange),
method(getVisibleRange),
method(setDrawHeadingArrows),
{0, 0}
};
}
}

32
lua_addon/lua_cityview.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef OPENGTA_SCRIPT_CV_H
#define OPENGTA_SCRIPT_CV_H
#include "gl_cityview.h"
#include "lunar.h"
namespace OpenGTA {
namespace Script {
class CityView : public OpenGTA::CityView {
public:
int setCamPosition(lua_State *L);
int getCamPosition(lua_State *L);
// int setCamVector(lua_State *L);
// int getCamVector(lua_State *L);
int setTopDownView(lua_State *L);
int setZoom(lua_State *L);
int getZoom(lua_State *L);
int setVisibleRange(lua_State *L);
int getVisibleRange(lua_State *L);
int setDrawHeadingArrows(lua_State *L);
// --
static const char className[];
static Lunar<CityView>::RegType methods[];
};
}
}
#endif

31
lua_addon/lua_screen.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "lua_screen.h"
namespace OpenGTA {
namespace Script {
using namespace OpenGL;
int Screen::getFullscreen(lua_State *L) {
bool b = ScreenHolder::Instance().getFullscreen();
lua_pushboolean(L, b);
return 1;
}
int Screen::setFullscreen(lua_State *L) {
bool b = ScreenHolder::Instance().getFullscreen();
bool v = lua_toboolean(L, 1);
if (b != v)
ScreenHolder::Instance().toggleFullscreen();
return 0;
}
int Screen::makeScreenShot(lua_State *L) {
ScreenHolder::Instance().makeScreenshot(luaL_checkstring(L, 1));
return 0;
}
#define method(name) {#name, Screen::name}
const luaL_reg Screen::methods[] = {
method(setFullscreen),
method(getFullscreen),
method(makeScreenShot),
{NULL, NULL}
};
}
}

20
lua_addon/lua_screen.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef LUA_OGTA_SCREEN_H
#define LUA_OGTA_SCREEN_H
#include "gl_screen.h"
#include "lua.hpp"
namespace OpenGTA {
namespace Script {
class Screen {
public:
static int getFullscreen(lua_State *L);
static int setFullscreen(lua_State *L);
static int makeScreenShot(lua_State *L);
static const luaL_reg methods[];
};
}
}
#endif

View File

@ -0,0 +1,22 @@
#include <cassert>
#include "lua_stackguard.h"
#include "log.h"
namespace Util {
LuaStackguard::LuaStackguard(const char* f, int l, lua_State *L) {
assert(L);
m_state = L;
i_file = f;
i_line = l;
m_top = lua_gettop(m_state);
}
LuaStackguard::~LuaStackguard() {
int now_top = lua_gettop(m_state);
if (now_top > m_top) {
Util::Log::warn(i_file, i_line) << "Stack-balance: " << now_top << " > " << m_top << std::endl;
lua_settop(m_state, m_top);
}
}
}

View File

@ -0,0 +1,23 @@
#ifndef LUA_STACK_GUARD_H
#define LUA_STACK_GUARD_H
#include "lua.hpp"
namespace Util {
class LuaStackguard {
public:
LuaStackguard(const char* f, int l, lua_State *L);
~LuaStackguard();
private:
int m_top;
lua_State* m_state;
// line number and filename where this instance was created
int i_line;
const char* i_file;
};
#define LGUARD(L) LuaStackguard guard(__FILE__, __LINE__, L)
}
#endif

126
lua_addon/lua_vm.cpp Normal file
View File

@ -0,0 +1,126 @@
#include <string>
#include "lua_vm.h"
#include "lunar.h"
#include "lua_cityview.h"
#include "lua_stackguard.h"
#include "lua_camera.h"
#include "lua_screen.h"
#include "lua_spritecache.h"
#include "m_exceptions.h"
using namespace Util;
namespace OpenGTA {
namespace Script {
LuaVM::LuaVM() : L(NULL) {
L = lua_open();
if (L == NULL)
throw E_SCRIPTERROR("Failed to create Lua state!");
luaopen_base(L);
luaopen_math(L);
_registered = false;
lua_settop(L, 0);
prepare();
}
LuaVM::~LuaVM() {
if (L != NULL)
lua_close(L);
L = NULL;
}
void LuaVM::prepare() {
LGUARD(L);
if (!_registered) {
Lunar<CityView>::Register2(L);
luaL_openlib(L, "camera", Camera::methods, 0);
luaL_openlib(L, "screen", Screen::methods, 0);
luaL_openlib(L, "spritecache", SpriteCache::methods, 0);
}
_registered = true;
}
void LuaVM::setCityView(OpenGTA::CityView & cv) {
LGUARD(L);
CityView *scv = static_cast<CityView*>(&cv);
lua_gettable(L, LUA_GLOBALSINDEX);
int scv_ref = Lunar<CityView>::push(L, scv, false);
lua_pushliteral(L, "city_view");
lua_pushvalue(L, scv_ref);
lua_settable(L, LUA_GLOBALSINDEX);
}
void LuaVM::runString(const char* _str) {
LGUARD(L);
if (!_str)
return;
if (luaL_loadbuffer(L, _str, strlen(_str), "cmd") ||
lua_pcall(L, 0, 0, 0))
throw E_SCRIPTERROR("Error running string: " + std::string(lua_tostring(L, -1)));
}
void LuaVM::runFile(const char* filename) {
LGUARD(L);
if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
throw E_SCRIPTERROR("Error running file: " + std::string(lua_tostring(L, -1)));
}
void LuaVM::callSimpleFunction(const char* func_name) {
LGUARD(L);
lua_getglobal(L, func_name);
if (lua_type(L, -1) == LUA_TFUNCTION) {
if (lua_pcall(L, 0, 0, 0) != 0)
throw E_SCRIPTERROR(("Exception calling function: ") + std::string(lua_tostring(L, -1)));
}
else
throw E_SCRIPTERROR("No such function: " + std::string(func_name));
}
int LuaVM::getGlobalInt(const char* key) {
LGUARD(L);
lua_getglobal(L, key);
if (!lua_isnumber(L, -1))
throw E_SCRIPTERROR("Expected int value for key: " + std::string(key));
int v = int(lua_tonumber(L, -1));
return v;
}
float LuaVM::getGlobalFloat(const char* key) {
LGUARD(L);
lua_getglobal(L, key);
if (!lua_isnumber(L, -1))
throw E_SCRIPTERROR("Expected float value for key: " + std::string(key));
float v = float(lua_tonumber(L, -1));
return v;
}
const char* LuaVM::getGlobalString(const char* key) {
LGUARD(L);
lua_getglobal(L, key);
if (!lua_isstring(L, -1))
throw E_SCRIPTERROR("Expected string value for key: " + std::string(key));
const char* v = lua_tostring(L, -1);
return v;
}
void LuaVM::setGlobalInt(const char* key, int v) {
LGUARD(L);
lua_pushnumber(L, v);
lua_setglobal(L, key);
}
void LuaVM::setGlobalFloat(const char* key, float v) {
LGUARD(L);
lua_pushnumber(L, v);
lua_setglobal(L, key);
}
void LuaVM::setGlobalString(const char* key, const char* v) {
LGUARD(L);
lua_pushstring(L, v);
lua_setglobal(L, key);
}
}
}

34
lua_addon/lua_vm.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef OPENGTA_SCRIPT_VM_H
#define OPENGTA_SCRIPT_VM_H
#include "lua.hpp"
#include "Singleton.h"
#include "gl_cityview.h"
namespace OpenGTA {
namespace Script {
class LuaVM {
public:
LuaVM();
~LuaVM();
void runString(const char*);
void runFile(const char*);
void callSimpleFunction(const char*);
void prepare();
void setCityView(OpenGTA::CityView &);
int getGlobalInt(const char*);
float getGlobalFloat(const char*);
const char* getGlobalString(const char*);
void setGlobalInt(const char*, int);
void setGlobalFloat(const char*, float);
void setGlobalString(const char*, const char*);
protected:
lua_State *L;
private:
bool _registered;
};
typedef Loki::SingletonHolder<LuaVM, Loki::CreateUsingNew, Loki::DefaultLifetime,
Loki::SingleThreaded> LuaVMHolder;
}
}
#endif

259
lua_addon/lunar.h Normal file
View File

@ -0,0 +1,259 @@
#ifndef LUNAR_H
#define LUNAR_H
/** See: http://lua-users.org/wiki/CppBindingWithLunar .
*
* # include-guards
* # fixed old-style cast
* # Register2 function
*
*/
#include "lua.hpp"
template <typename T> class Lunar {
typedef struct { T *pT; } userdataType;
public:
typedef int (T::*mfp)(lua_State *L);
typedef struct { const char *name; mfp mfunc; } RegType;
static void Register(lua_State *L) {
lua_newtable(L);
int methods = lua_gettop(L);
luaL_newmetatable(L, T::className);
int metatable = lua_gettop(L);
// store method table in globals so that
// scripts can add functions written in Lua.
lua_pushvalue(L, methods);
set(L, LUA_GLOBALSINDEX, T::className);
// hide metatable from Lua getmetatable()
lua_pushvalue(L, methods);
set(L, metatable, "__metatable");
lua_pushvalue(L, methods);
set(L, metatable, "__index");
lua_pushcfunction(L, tostring_T);
set(L, metatable, "__tostring");
lua_pushcfunction(L, gc_T);
set(L, metatable, "__gc");
lua_newtable(L); // mt for method table
lua_pushcfunction(L, new_T);
lua_pushvalue(L, -1); // dup new_T function
set(L, methods, "new"); // add new_T to method table
set(L, -3, "__call"); // mt.__call = new_T
lua_setmetatable(L, methods);
// fill method table with methods from class T
for (RegType *l = T::methods; l->name; l++) {
lua_pushstring(L, l->name);
lua_pushlightuserdata(L, static_cast<void*>(l));
lua_pushcclosure(L, thunk, 1);
lua_settable(L, methods);
}
lua_pop(L, 2); // drop metatable and method table
}
static void Register2(lua_State *L) {
lua_newtable(L);
int methods = lua_gettop(L);
luaL_newmetatable(L, T::className);
int metatable = lua_gettop(L);
// store method table in globals so that
// scripts can add functions written in Lua.
lua_pushvalue(L, methods);
set(L, LUA_GLOBALSINDEX, T::className);
// hide metatable from Lua getmetatable()
lua_pushvalue(L, methods);
set(L, metatable, "__metatable");
lua_pushvalue(L, methods);
set(L, metatable, "__index");
lua_pushcfunction(L, tostring_T);
set(L, metatable, "__tostring");
lua_pushcfunction(L, gc_T);
set(L, metatable, "__gc");
lua_newtable(L); // mt for method table
lua_setmetatable(L, methods);
// fill method table with methods from class T
for (RegType *l = T::methods; l->name; l++) {
lua_pushstring(L, l->name);
lua_pushlightuserdata(L, static_cast<void*>(l));
lua_pushcclosure(L, thunk, 1);
lua_settable(L, methods);
}
lua_pop(L, 2); // drop metatable and method table
}
// call named lua method from userdata method table
static int call(lua_State *L, const char *method,
int nargs=0, int nresults=LUA_MULTRET, int errfunc=0)
{
int base = lua_gettop(L) - nargs; // userdata index
if (!luaL_checkudata(L, base, T::className)) {
lua_settop(L, base-1); // drop userdata and args
lua_pushfstring(L, "not a valid %s userdata", T::className);
return -1;
}
lua_pushstring(L, method); // method name
lua_gettable(L, base); // get method from userdata
if (lua_isnil(L, -1)) { // no method?
lua_settop(L, base-1); // drop userdata and args
lua_pushfstring(L, "%s missing method '%s'", T::className, method);
return -1;
}
lua_insert(L, base); // put method under userdata, args
int status = lua_pcall(L, 1+nargs, nresults, errfunc); // call method
if (status) {
const char *msg = lua_tostring(L, -1);
if (msg == NULL) msg = "(error with no message)";
lua_pushfstring(L, "%s:%s status = %d\n%s",
T::className, method, status, msg);
lua_remove(L, base); // remove old message
return -1;
}
return lua_gettop(L) - base + 1; // number of results
}
// push onto the Lua stack a userdata containing a pointer to T object
static int push(lua_State *L, T *obj, bool gc=false) {
if (!obj) { lua_pushnil(L); return 0; }
luaL_getmetatable(L, T::className); // lookup metatable in Lua registry
if (lua_isnil(L, -1)) luaL_error(L, "%s missing metatable", T::className);
int mt = lua_gettop(L);
subtable(L, mt, "userdata", "v");
userdataType *ud =
static_cast<userdataType*>(pushuserdata(L, obj, sizeof(userdataType)));
if (ud) {
ud->pT = obj; // store pointer to object in userdata
lua_pushvalue(L, mt);
lua_setmetatable(L, -2);
if (gc == false) {
lua_checkstack(L, 3);
subtable(L, mt, "do not trash", "k");
lua_pushvalue(L, -2);
lua_pushboolean(L, 1);
lua_settable(L, -3);
lua_pop(L, 1);
}
}
lua_replace(L, mt);
lua_settop(L, mt);
return mt; // index of userdata containing pointer to T object
}
// get userdata from Lua stack and return pointer to T object
static T *check(lua_State *L, int narg) {
userdataType *ud =
static_cast<userdataType*>(luaL_checkudata(L, narg, T::className));
if(!ud) luaL_typerror(L, narg, T::className);
return ud->pT; // pointer to T object
}
private:
Lunar(); // hide default constructor
// member function dispatcher
static int thunk(lua_State *L) {
// stack has userdata, followed by method args
T *obj = check(L, 1); // get 'self', or if you prefer, 'this'
lua_remove(L, 1); // remove self so member function args start at index 1
// get member function from upvalue
RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
return (obj->*(l->mfunc))(L); // call member function
}
// create a new T object and
// push onto the Lua stack a userdata containing a pointer to T object
static int new_T(lua_State *L) {
lua_remove(L, 1); // use classname:new(), instead of classname.new()
T *obj = new T(L); // call constructor for T objects
//push(L, obj, false); // gc_T will delete this object
push(L, obj, true); // gc_T will delete this object
return 1; // userdata containing pointer to T object
}
// garbage collection metamethod
static int gc_T(lua_State *L) {
if (luaL_getmetafield(L, 1, "do not trash")) {
lua_pushvalue(L, 1); // dup userdata
lua_gettable(L, -2);
if (!lua_isnil(L, -1)) return 0; // do not delete object
}
userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
T *obj = ud->pT;
if (obj) delete obj; // call destructor for T objects
return 0;
}
static int tostring_T (lua_State *L) {
char buff[32];
userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
T *obj = ud->pT;
sprintf(buff, "%p", obj);
lua_pushfstring(L, "%s (%s)", T::className, buff);
return 1;
}
static void set(lua_State *L, int table_index, const char *key) {
lua_pushstring(L, key);
lua_insert(L, -2); // swap value and key
lua_settable(L, table_index);
}
static void weaktable(lua_State *L, const char *mode) {
lua_newtable(L);
lua_pushvalue(L, -1); // table is its own metatable
lua_setmetatable(L, -2);
lua_pushliteral(L, "__mode");
lua_pushstring(L, mode);
lua_settable(L, -3); // metatable.__mode = mode
}
static void subtable(lua_State *L, int tindex, const char *name, const char *mode) {
lua_pushstring(L, name);
lua_gettable(L, tindex);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_checkstack(L, 3);
weaktable(L, mode);
lua_pushstring(L, name);
lua_pushvalue(L, -2);
lua_settable(L, tindex);
}
}
static void *pushuserdata(lua_State *L, void *key, size_t sz) {
void *ud = 0;
lua_pushlightuserdata(L, key);
lua_gettable(L, -2); // lookup[key]
if (lua_isnil(L, -1)) {
lua_pop(L, 1); // drop nil
lua_checkstack(L, 3);
ud = lua_newuserdata(L, sz); // create new userdata
lua_pushlightuserdata(L, key);
lua_pushvalue(L, -2); // dup userdata
lua_settable(L, -4); // lookup[key] = userdata
}
return ud;
}
};
#endif

32
main.cpp Normal file
View File

@ -0,0 +1,32 @@
#include <iostream>
#include <SDL.h>
void on_exit();
void run_main();
void initGL();
void initVideo(int w, int h, int bpp);
int global_EC = 0;
int global_Done = 0;
SDL_Surface* screen = NULL;
SDL_Surface* surface = NULL;
int city_num = 0;
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cerr << "Fatal error initialising SDL!" << std::endl;
global_EC = 1;
exit(1);
}
atexit(on_exit);
if (argc == 2) {
city_num = atoi(argv[1]);
}
SDL_EnableKeyRepeat( 100, SDL_DEFAULT_REPEAT_INTERVAL );
initVideo(1024, 768, 32);
initGL();
run_main();
exit(0);
}

58
main2.cpp Normal file
View File

@ -0,0 +1,58 @@
#include <cstdlib>
#include <iostream>
#include <string>
#include "m_exceptions.h"
#include "log.h"
extern void parse_args(int argc, char* argv[]);
extern void on_exit();
extern void run_init();
extern void run_main();
int global_EC = 0;
int global_Done = 0;
#ifndef DONT_CATCH
bool catch_exceptions = true;
#else
bool catch_exceptions = false;
#endif
using namespace std;
int main(int argc, char* argv[]) {
if (argc > 1)
parse_args(argc, argv);
atexit(on_exit);
if (!catch_exceptions)
INFO << "ignoring exceptions" << std::endl;
if (!catch_exceptions)
run_init();
else {
try {
run_init();
}
catch (Exception & e) {
ERROR << "Exception during startup: " << e.what() << endl;
global_EC = 1;
exit(1);
}
}
if (!catch_exceptions)
run_main();
else {
try {
run_main();
}
catch (const Exception & e) {
ERROR << "Exception during game: " << e.what() << endl;
global_EC = 1;
exit(1);
}
}
std::exit(0);
}

78
makefile Normal file
View File

@ -0,0 +1,78 @@
include src_list.make
INC_EX_LIBS = $(PHYSFS_INC) $(SDL_INC) $(LUA_INC)
INC_INTERN = -I. -Iloki/include/loki -Iutil -Icoldet -Imath
INC = $(INC_INTERN) $(INC_EX_LIBS)
FLAGS = $(WARN) $(DEBUG) $(OPT)
BUILD_FOR = $(shell if [ -n "$(HOST)" ]; then echo $(HOST) ; else echo "LINUX"; fi)
#SDL_GL_LIB = -lGL -lGLU
LOKI_LIB = -Wl,-Rloki/lib -Lloki/lib -lloki
#COLDET_LIB = -Wl,-Rcoldet -Lcoldet -lcoldet
CXXFLAGS=$(FLAGS) $(DEFS) \
$(INC)
src_list.make: prepare_build.sh
./prepare_build.sh $(BUILD_FOR)
$(MAKE) depend
%.o: %.cpp
$(CXX) $(FLAGS) $(DEFS) \
$(INC) \
-c -o $@ $<
%.o: %.c
$(CC) $(CCFLAGS) \
-c -o $@ $<
loki:
ifeq ($(BUILD_FOR), LINUX)
make -f loki.make
else
make -f loki.make.w32_cross
endif
ctags:
ctags *.cpp util/*.cpp
coldet/libcoldet.a:
make -C coldet -f makefile.g++ all
clean:
rm -f tags depend *.o tools/*.o coldet/*.o lua_addon/*.o math/*.o util/*.o \
spriteplayer gfxextract viewer $(TOOLS) objdump objdump_map minimap slopeview luaviewer g24
html: doxy_main.h
doxygen
doxygen: doxy_main.h
doxygen
doxy_main.h: doc/hacking.txt tools/doxy_doc.sh
./tools/doxy_doc.sh > doxy_main.h
doxyclean:
$(RM) doxy_main.h -r doc/html
package:
(cd .. && tar jvcf ogta_src_`date +%F`.tar.bz2 -T ogta/release_files_sorted)
@echo "saved as: ogta_src_`date +%F.tar.bz2`"
rclean:
make libclean
make clean
make doxyclean
rm -f src_list.make
libclean:
make -C coldet -f makefile.g++ clean
depend: loki src_list.make
$(RM) depend
$(CXX) $(CXXFLAGS) -DGCC -E -MM $(GL_SRC) $(OGTA_SRC) $(UTIL_SRC) > depend

114
math/basis.hpp Normal file
View File

@ -0,0 +1,114 @@
#ifndef GOMEZ_Basis_H
#define GOMEZ_Basis_H
/**
* Taken from:
* http://www.gamasutra.com/features/19990702/data_structures_01.htm
* http://www.gamasutra.com/features/19991018/Gomez_1.htm
*
* Both by Miguel Gomez
*/
#include "matrix.hpp"
namespace GomezMath {
// An orthonormal basis with respect to a parent
//
class Basis
{
public:
Matrix R;
public:
Basis ()
{
}
Basis (const Vector & v0,
const Vector & v1, const Vector & v2):R (v0, v1, v2)
{
}
Basis (const Matrix & m):R (m)
{
}
const Vector & operator [] (long i) const
{
return R.C[i];
}
const Vector & x () const
{
return R.C[0];
}
const Vector & y () const
{
return R.C[1];
}
const Vector & z () const
{
return R.C[2];
}
const Matrix & basis () const
{
return R;
}
void basis (const Vector & v0, const Vector & v1, const Vector & v2)
{
this->R[0] = v0;
this->R[1] = v1;
this->R[2] = v2;
}
// Right-Handed Rotations
void rotateAboutX (const Scalar & a)
{
if (0 != a) //dont rotate by 0
{
Vector b1 = this->y () * cos (a) + this->z () * sin (a);
Vector b2 = -this->y () * sin (a) + this->z () * cos (a);
//set basis
this->R[1] = b1;
this->R[2] = b2;
//x is unchanged
}
}
void rotateAboutY (const Scalar & a)
{
if (0 != a) //dont rotate by 0
{
Vector b2 = this->z () * cos (a) + this->x () * sin (a); //rotate z
Vector b0 = -this->z () * sin (a) + this->x () * cos (a); //rotate x
//set basis
this->R[2] = b2;
this->R[0] = b0;
//y is unchanged
}
}
void rotateAboutZ (const Scalar & a)
{
if (0 != a) //dont rotate by 0
{
//dont over-write basis before calculation is done
Vector b0 = this->x () * cos (a) + this->y () * sin (a); //rotate x
Vector b1 = -this->x () * sin (a) + this->y () * cos (a); //rotate y
//set basis
this->R[0] = b0;
this->R[1] = b1;
//z is unchanged
}
}
//rotate the basis about the unit axis u by theta (radians)
void rotate (const Scalar & theta, const Vector & u);
//rotate, length of da is theta, unit direction of da is u
void rotate (const Vector & da);
// Transformations
const Vector transformVectorToLocal (const Vector & v) const
{
return Vector (R.C[0].dot (v), R.C[1].dot (v), R.C[2].dot (v));
}
const Point transformVectorToParent (const Vector & v) const
{
return R.C[0] * v.x + R.C[1] * v.y + R.C[2] * v.z;
}
};
}
#endif

60
math/coord_frame.hpp Normal file
View File

@ -0,0 +1,60 @@
#ifndef GOMEZ_COORDFRAME_H
#define GOMEZ_COORDFRAME_H
/**
* Taken from:
* http://www.gamasutra.com/features/19990702/data_structures_01.htm
* http://www.gamasutra.com/features/19991018/Gomez_1.htm
*
* Both by Miguel Gomez
*/
#include "basis.hpp"
namespace GomezMath {
// A coordinate frame (basis and origin) with respect to a parent
//
class CoordFrame:public Basis
{
public:
Point O; //this coordinate frames origin, relative to its parent frame
public:
CoordFrame ()
{
}
CoordFrame (const Point & o,
const Vector & v0,
const Vector & v1, const Vector & v2): Basis (v0, v1, v2), O(o)
{
}
CoordFrame (const Point & o, const Basis & b): Basis (b), O(o)
{
}
const Point & position () const
{
return O;
}
void position (const Point & p)
{
O = p;
}
const Point transformPointToLocal (const Point & p) const
{
//translate to this frames origin, then project onto this basis
return transformVectorToLocal (p - O);
}
const Point transformPointToParent (const Point & p) const
{
//transform the coordinate vector and translate by this origin
return transformVectorToParent (p) + O;
}
//translate the origin by the given vector
void translate (const Vector & v)
{
O += v;
}
};
}
#endif

11
math/makefile Normal file
View File

@ -0,0 +1,11 @@
GMATH_FILES = obb.cpp
GMATH_OBJ = $(GMATH_FILES:%.cpp=%.o)
all: libgomezmath.a
libgomezmath.a: $(GMATH_OBJ)
rm -f $@
ar cq $@ $(GMATH_OBJ)
clean:
rm -f libgomezmath.a $(GMATH_OBJ)

151
math/matrix.hpp Normal file
View File

@ -0,0 +1,151 @@
#ifndef GOMEZ_Matrix_H
#define GOMEZ_Matrix_H
#include "vector.hpp"
namespace GomezMath {
/**
* Taken from:
* http://www.gamasutra.com/features/19990702/data_structures_01.htm
* http://www.gamasutra.com/features/19991018/Gomez_1.htm
*
* Both by Miguel Gomez
*/
// A 3x3 matrix
//
class Matrix
{
public:
Vector C[3]; //column vectors
public:
Matrix ()
{
//identity matrix
C[0].x = 1;
C[1].y = 1;
C[2].z = 1;
}
Matrix (const Vector & c0, const Vector & c1, const Vector & c2)
{
C[0] = c0;
C[1] = c1;
C[2] = c2;
}
//index a column, allow assignment
//NOTE: using this index operator along with the vector index
//gives you M[column][row], not the standard M[row][column]
Vector & operator [](long i)
{
return C[i];
}
//compare
const bool operator == (const Matrix & m) const
{
return C[0] == m.C[0] && C[1] == m.C[1] && C[2] == m.C[2];
}
const bool operator != (const Matrix & m) const
{
return !(m == *this);
}
//assign
const Matrix & operator = (const Matrix & m)
{
C[0] = m.C[0];
C[1] = m.C[1];
C[2] = m.C[2];
return *this;
}
//increment
const Matrix & operator += (const Matrix & m)
{
C[0] += m.C[0];
C[1] += m.C[1];
C[2] += m.C[2];
return *this;
}
//decrement
const Matrix & operator -= (const Matrix & m)
{
C[0] -= m.C[0];
C[1] -= m.C[1];
C[2] -= m.C[2];
return *this;
}
//self-multiply by a scalar
const Matrix & operator *= (const Scalar & s)
{
C[0] *= s;
C[1] *= s;
C[2] *= s;
return *this;
}
//self-multiply by a matrix
const Matrix & operator *= (const Matrix & m)
{
//NOTE: dont change the columns
//in the middle of the operation
Matrix temp = (*this);
C[0] = temp * m.C[0];
C[1] = temp * m.C[1];
C[2] = temp * m.C[2];
return *this;
}
//add
const Matrix operator + (const Matrix & m) const
{
return Matrix (C[0] + m.C[0], C[1] + m.C[1], C[2] + m.C[2]);
}
//subtract
const Matrix operator - (const Matrix & m) const
{
return Matrix (C[0] - m.C[0], C[1] - m.C[1], C[2] - m.C[2]);
}
//post-multiply by a scalar
const Matrix operator * (const Scalar & s) const
{
return Matrix (C[0] * s, C[1] * s, C[2] * s);
}
//pre-multiply by a scalar
friend inline const Matrix operator * (const Scalar & s, const Matrix & m)
{
return m * s;
}
//post-multiply by a vector
const Vector operator * (const Vector & v) const
{
return (C[0] * v.x + C[1] * v.y + C[2] * v.z);
}
//pre-multiply by a vector
inline friend const Vector operator * (const Vector & v, const Matrix & m)
{
return Vector (m.C[0].dot (v), m.C[1].dot (v), m.C[2].dot (v));
}
//post-multiply by a matrix
const Matrix operator * (const Matrix & m) const
{
return Matrix ((*this) * m.C[0], (*this) * m.C[1], (*this) * m.C[2]);
}
//transpose
Matrix transpose () const
{
//turn columns on their side
return Matrix (Vector (C[0].x, C[1].x, C[2].x), //column 0
Vector (C[0].y, C[1].y, C[2].y), //column 1
Vector (C[0].z, C[1].z, C[2].z) //column 2
);
}
//scalar determinant
const Scalar determinant () const
{
//Lang, "Linear Algebra", p. 143
return C[0].dot (C[1].cross (C[2]));
}
//matrix inverse
const Matrix inverse () const;
};
}
#endif

126
math/obb.cpp Normal file
View File

@ -0,0 +1,126 @@
#ifndef GOMEZ_OBB_H
#define GOMEZ_OBB_H
/**
* Taken from:
* http://www.gamasutra.com/features/19990702/data_structures_01.htm
* http://www.gamasutra.com/features/19991018/Gomez_1.htm
*
* Both by Miguel Gomez
*/
#include "obb.hpp"
#include "vector.hpp"
namespace GomezMath {
//check if two oriented bounding boxes overlap
const bool OBBOverlap (
//A
Vector & a, //extents
Vector & Pa, //position
Vector * A, //orthonormal basis
//B
Vector & b, //extents
Vector & Pb, //position
Vector * B //orthonormal basis
)
{
//translation, in parent frame
Vector v = Pb - Pa;
//translation, in A's frame
Vector T (v.dot (A[0]), v.dot (A[1]), v.dot (A[2]));
//B's basis with respect to A's local frame
Scalar R[3][3];
float ra, rb, t;
long i, k;
//calculate rotation matrix
for (i = 0; i < 3; i++)
for (k = 0; k < 3; k++)
R[i][k] = A[i].dot (B[k]);
/*ALGORITHM: Use the separating axis test for all 15 potential
separating axes. If a separating axis could not be found, the two
boxes overlap. */
//A's basis vectors
for (i = 0; i < 3; i++)
{
ra = a[i];
rb =
b[0] * fabs (R[i][0]) + b[1] * fabs (R[i][1]) + b[2] * fabs (R[i][2]);
t = fabs (T[i]);
if (t > ra + rb)
return false;
}
//B's basis vectors
for (k = 0; k < 3; k++)
{
ra =
a[0] * fabs (R[0][k]) + a[1] * fabs (R[1][k]) + a[2] * fabs (R[2][k]);
rb = b[k];
t = fabs (T[0] * R[0][k] + T[1] * R[1][k] + T[2] * R[2][k]);
if (t > ra + rb)
return false;
}
//9 cross products
//L = A0 x B0
ra = a[1] * fabs (R[2][0]) + a[2] * fabs (R[1][0]);
rb = b[1] * fabs (R[0][2]) + b[2] * fabs (R[0][1]);
t = fabs (T[2] * R[1][0] - T[1] * R[2][0]);
if (t > ra + rb)
return false;
//L = A0 x B1
ra = a[1] * fabs (R[2][1]) + a[2] * fabs (R[1][1]);
rb = b[0] * fabs (R[0][2]) + b[2] * fabs (R[0][0]);
t = fabs (T[2] * R[1][1] - T[1] * R[2][1]);
if (t > ra + rb)
return false;
//L = A0 x B2
ra = a[1] * fabs (R[2][2]) + a[2] * fabs (R[1][2]);
rb = b[0] * fabs (R[0][1]) + b[1] * fabs (R[0][0]);
t = fabs (T[2] * R[1][2] - T[1] * R[2][2]);
if (t > ra + rb)
return false;
//L = A1 x B0
ra = a[0] * fabs (R[2][0]) + a[2] * fabs (R[0][0]);
rb = b[1] * fabs (R[1][2]) + b[2] * fabs (R[1][1]);
t = fabs (T[0] * R[2][0] - T[2] * R[0][0]);
if (t > ra + rb)
return false;
//L = A1 x B1
ra = a[0] * fabs (R[2][1]) + a[2] * fabs (R[0][1]);
rb = b[0] * fabs (R[1][2]) + b[2] * fabs (R[1][0]);
t = fabs (T[0] * R[2][1] - T[2] * R[0][1]);
if (t > ra + rb)
return false;
//L = A1 x B2
ra = a[0] * fabs (R[2][2]) + a[2] * fabs (R[0][2]);
rb = b[0] * fabs (R[1][1]) + b[1] * fabs (R[1][0]);
t = fabs (T[0] * R[2][2] - T[2] * R[0][2]);
if (t > ra + rb)
return false;
//L = A2 x B0
ra = a[0] * fabs (R[1][0]) + a[1] * fabs (R[0][0]);
rb = b[1] * fabs (R[2][2]) + b[2] * fabs (R[2][1]);
t = fabs (T[1] * R[0][0] - T[0] * R[1][0]);
if (t > ra + rb)
return false;
//L = A2 x B1
ra = a[0] * fabs (R[1][1]) + a[1] * fabs (R[0][1]);
rb = b[0] * fabs (R[2][2]) + b[2] * fabs (R[2][0]);
t = fabs (T[1] * R[0][1] - T[0] * R[1][1]);
if (t > ra + rb)
return false;
//L = A2 x B2
ra = a[0] * fabs (R[1][2]) + a[1] * fabs (R[0][2]);
rb = b[0] * fabs (R[2][1]) + b[1] * fabs (R[2][0]);
t = fabs (T[1] * R[0][2] - T[0] * R[1][2]);
if (t > ra + rb)
return false;
/*no separating axis found,
the two boxes overlap */
return true;
}
}
#endif

42
math/obb.hpp Normal file
View File

@ -0,0 +1,42 @@
#ifndef GOMEZ_OBB_H
#define GOMEZ_OBB_H
/**
* Taken from:
* http://www.gamasutra.com/features/19990702/data_structures_01.htm
* http://www.gamasutra.com/features/19991018/Gomez_1.htm
*
* Both by Miguel Gomez
*/
#include "coord_frame.hpp"
namespace GomezMath {
class OBB : public CoordFrame
{
public:
Vector E; //extents
OBB (const Vector & e) : E (e)
{
}
OBB (const Vector & e, const Vector & origin,
const Vector & v0, const Vector & v1, const Vector & v2) :
CoordFrame(origin, v0, v1, v2), E(e)
{
}
};
const bool OBBOverlap (
//A
Vector & a, //extents
Vector & Pa, //position
Vector * A, //orthonormal basis
//B
Vector & b, //extents
Vector & Pb, //position
Vector * B //orthonormal basis
);
}
#endif

164
math/obox.cpp Normal file
View File

@ -0,0 +1,164 @@
#include "obox.h"
// --------------------------
//
// Oriented Bounding Box Class
//
// --------------------------
//
// Check if a point is in this bounding box
//
bool OBox::IsPointInBox(const Vector3D &InP)
{
// Rotate the point into the box's coordinates
Vector3D P = Transform(InP, m_M.Inverse());
// Now just use an axis-aligned check
if ( fabs(P.x) < m_Extent.x && fabs(P.y) < m_Extent.y && fabs(P.z) < m_Extent.z )
return true;
return false;
}
//
// Check if a sphere overlaps any part of this bounding box
//
bool OBox::IsSphereInBox( const Vector3D &InP, float fRadius)
{
float fDist;
float fDistSq = 0;
Vector3D P = Transform(InP, m_M.Inverse());
// Add distance squared from sphere centerpoint to box for each axis
for ( int i = 0; i < 3; i++ )
{
if ( fabs(P[i]) > m_Extent[i] )
{
fDist = fabs(P[i]) - m_Extent[i];
fDistSq += fDist*fDist;
}
}
return ( fDistSq <= fRadius*fRadius );
}
//
// Check if the bounding box is completely behind a plane( defined by a normal and a point )
//
bool OBox::BoxOutsidePlane( const Vector3D &InNorm, const Vector3D &InP )
{
// Plane Normal in Box Space
Vector3D Norm = rotateVector(InNorm, m_M.Inverse() );
Norm = Vector3D( fabs( Norm.x ), fabs( Norm.y ), fabs( Norm.z ) );
float Extent = Norm * m_Extent; //Norm.Dot( m_Extent ); // Box Extent along the plane normal
//float Distance = InNorm.Dot( GetCenterPoint() - InP ); // Distance from Box Center to the Plane
float Distance = InNorm * (GetCenterPoint() - InP);
// If Box Centerpoint is behind the plane further than its extent, the Box is outside the plane
if ( Distance < -Extent ) return true;
return false;
}
//
// Does the Line (L1, L2) intersect the Box?
//
bool OBox::IsLineInBox( const Vector3D& L1, const Vector3D& L2 )
{
// Put line in box space
Matrix3D MInv = m_M.Inverse();
Vector3D LB1 = Transform(L1, MInv);
Vector3D LB2 = Transform(L2, MInv);
// Get line midpoint and extent
Vector3D LMid = (LB1 + LB2) * 0.5f;
Vector3D L = (LB1 - LMid);
Vector3D LExt = Vector3D( fabs(L.x), fabs(L.y), fabs(L.z) );
// Use Separating Axis Test
// Separation vector from box center to line center is LMid, since the line is in box space
if ( fabs( LMid.x ) > m_Extent.x + LExt.x ) return false;
if ( fabs( LMid.y ) > m_Extent.y + LExt.y ) return false;
if ( fabs( LMid.z ) > m_Extent.z + LExt.z ) return false;
// Crossproducts of line and each axis
if ( fabs( LMid.y * L.z - LMid.z * L.y) > (m_Extent.y * LExt.z + m_Extent.z * LExt.y) ) return false;
if ( fabs( LMid.x * L.z - LMid.z * L.x) > (m_Extent.x * LExt.z + m_Extent.z * LExt.x) ) return false;
if ( fabs( LMid.x * L.y - LMid.y * L.x) > (m_Extent.x * LExt.y + m_Extent.y * LExt.x) ) return false;
// No separating axis, the line intersects
return true;
}
//
// Returns a 3x3 rotation matrix as vectors
//
inline void OBox::GetInvRot( Vector3D *pvRot )
{
pvRot[0] = Vector3D( m_M.m[0][0], m_M.m[0][1], m_M.m[0][2] );
pvRot[1] = Vector3D( m_M.m[1][0], m_M.m[1][1], m_M.m[1][2] );
pvRot[2] = Vector3D( m_M.m[2][0], m_M.m[2][1], m_M.m[2][2] );
}
//
// Check if any part of a box is inside any part of another box
// Uses the separating axis test.
//
bool OBox::IsBoxInBox( OBox &BBox )
{
Vector3D SizeA = m_Extent;
Vector3D SizeB = BBox.m_Extent;
Vector3D RotA[3], RotB[3];
GetInvRot( RotA );
BBox.GetInvRot( RotB );
float R[3][3]; // Rotation from B to A
float AR[3][3]; // absolute values of R matrix, to use with box extents
float ExtentA, ExtentB, Separation;
int i, k;
// Calculate B to A rotation matrix
for( i = 0; i < 3; i++ )
for( k = 0; k < 3; k++ )
{
R[i][k] = RotA[i] * RotB[k];
AR[i][k] = fabs(R[i][k]);
}
// Vector separating the centers of Box B and of Box A
Vector3D vSepWS = BBox.GetCenterPoint() - GetCenterPoint();
// Rotated into Box A's coordinates
Vector3D vSepA( vSepWS * RotA[0], vSepWS * RotA[1], vSepWS * RotA[2] );
// Test if any of A's basis vectors separate the box
for( i = 0; i < 3; i++ )
{
ExtentA = SizeA[i];
ExtentB = SizeB * Vector3D( AR[i][0], AR[i][1], AR[i][2] );
Separation = fabs( vSepA[i] );
if( Separation > ExtentA + ExtentB ) return false;
}
// Test if any of B's basis vectors separate the box
for( k = 0; k < 3; k++ )
{
ExtentA = SizeA * Vector3D( AR[0][k], AR[1][k], AR[2][k] );
ExtentB = SizeB[k];
Separation = fabs( vSepA * Vector3D(R[0][k],R[1][k],R[2][k]) );
if( Separation > ExtentA + ExtentB ) return false;
}
// Now test Cross Products of each basis vector combination ( A[i], B[k] )
for( i=0 ; i<3 ; i++ )
for( k=0 ; k<3 ; k++ )
{
int i1 = (i+1)%3, i2 = (i+2)%3;
int k1 = (k+1)%3, k2 = (k+2)%3;
ExtentA = SizeA[i1] * AR[i2][k] + SizeA[i2] * AR[i1][k];
ExtentB = SizeB[k1] * AR[i][k2] + SizeB[k2] * AR[i][k1];
Separation = fabs( vSepA[i2] * R[i1][k] - vSepA[i1] * R[i2][k] );
if( Separation > ExtentA + ExtentB ) return false;
}
// No separating axis found, the boxes overlap
return true;
}

43
math/obox.h Normal file
View File

@ -0,0 +1,43 @@
#include "math3d.h"
// see: from: http://www.3dkingdoms.com/weekly/weekly.php?a=21
// basically the same as bbox.h/.cpp but using coldet math
class OBox
{
public:
OBox() {}
OBox( const Matrix3D & m, const Vector3D & extent )
{ Set( m, extent ); }
OBox( const Matrix3D & m, const Vector3D & low, const Vector3D & high )
{ Set( m, low, high ); }
void Set( const Matrix3D & m, const Vector3D & extent )
{
m_M = m;
m_Extent = extent;
}
void Set( const Matrix3D & m, const Vector3D & low, const Vector3D & high )
{
m_M = m;
m_M.Translate( 0.5f * (low + high) );
m_Extent = 0.5f * (high - low);
}
Vector3D GetSize()
{ return 2.0f * m_Extent; }
Vector3D GetCenterPoint()
{ return m_M.GetTranslate(); }
void GetInvRot( Vector3D *pvRot );
bool IsPointInBox( const Vector3D & p );
bool IsBoxInBox( OBox & box );
bool IsSphereInBox( const Vector3D & p, float fRadius );
bool IsLineInBox( const Vector3D & l1, const Vector3D & l2 );
bool BoxOutsidePlane( const Vector3D & normal, const Vector3D & p );
// Data
Matrix3D m_M;
Vector3D m_Extent;
};

184
math/quaternion.h Normal file
View File

@ -0,0 +1,184 @@
#ifndef QUATERNION_MATH
#define QUATERNION_MATH
#include <cmath>
namespace Util { namespace Math {
/*!
* \namespace Util::Math
* \brief Vector/Matrix/Quaternion interpolation
*
* Found at http://www.flipcode.com/files/code/vquats.cpp
*
*
* I think this is very sleek, so I kept it.
*
* Editor's note:
* COTD Entry: Vector Math & Quaternions by Tim Sweeney [tim@epicgames.com]
This is some fairly generic vector math code. Here I want to
illustrate two things:
1. The "vectorSpace" template is a nice way to represent the
general mathematical notion of vectors without regard to
particular data types. The idea is that you can declare
a vector type (like vec4 below), and the vectorSpace template
automatically generates the appropriate vector operators.
I wrote this code for clarity and generality. For performance, you would
want to specialize the vectorSpace template for common types, like 4-
component floating-point vectors. You could implement these using
KNI and 3DNow instructions, for example.
2. Useful quaternion functions, most notably "exp" and "ln".
The traditional and limited way programmers used quaternion
rotations in the past was to use "spherical linear interpolation",
interpolating a rotational arc between two quaternions at a constant
angular rate. Given quaternions q1 and q2, you might have used
a function lik "slerp(q1,q2,0.25)" to find a rotation
one-quarter between the two quaternions. This approach
was unsatisfactory because it was difficult to perform
smooth, higher order, i.e. hermite or bezier, interpolation
between quaternions.
New approach: take the logarithm ("ln" function below) of
your quaternions, then use ordinary linear interpolation
(sampleLinear below) or any higher-order interpolation
scheme -- and then take the exponent ("exp") of the result.
The resulting rotations will have the desired smooth angular
movement rates.
Why does this work? Read up on "geometric algebra" to find out.
Unit quaternions are just a special 3-dimensional case of
spinors, which are the most general representation of
n-dimensional rotations possible.
*/
// Template implementing a "vector space" (in the precise mathematical sense).
// A vector space is a type "u", implemented as
// an array of "n" elements of type "t".
template<class t,int n,class u> struct vectorSpace {
t x[n];
vectorSpace() {for(int i=0; i<n; i++) x[i]=t();}
inline t& operator[](int i) {/*assert(i>=0 && i<n);*/ return x[i];}
inline const t& operator[](int i) const {/*assert(i>=0 && i<n);*/ return x[i];}
friend bool operator== (const u& a,const u& b) {bool c=true; for(int i=0; i<n; i++) c=c&&a[i]==b[i]; return c;}
friend bool operator!= (const u& a,const u& b) {bool c=false; for(int i=0; i<n; i++) c=c||a[i]!=b[i]; return c;}
friend inline t vectorSum (const u& a ) {t c=0; for(int i=0; i<n; i++) c+=a[i]; return c;}
friend inline t vectorProd(const u& a ) {t c=1; for(int i=0; i<n; i++) c*=a[i]; return c;}
friend inline u operator- (const u& a ) {u c; for(int i=0; i<n; i++) c[i] = -a[i]; return c;}
friend inline u operator+ (const u& a,const u& b) {u c; for(int i=0; i<n; i++) c[i] =a[i]+b[i]; return c;}
friend inline u operator- (const u& a,const u& b) {u c; for(int i=0; i<n; i++) c[i] =a[i]-b[i]; return c;}
friend inline u operator* (const float a,const u& b) {u c; for(int i=0; i<n; i++) c[i] =a *b[i]; return c;}
friend inline u operator* (const u& a,const float b) {u c; for(int i=0; i<n; i++) c[i] =a[i]*b; return c;}
friend inline u operator/ (const u& a,const float b) {u c; for(int i=0; i<n; i++) c[i] =a[i]/b; return c;}
friend inline u operator+=( u& a,const u& b) { for(int i=0; i<n; i++) a[i]+=b[i]; return a;}
friend inline u operator-=( u& a,const u& b) { for(int i=0; i<n; i++) a[i]-=b[i]; return a;}
friend inline u operator*=( u& a,const float b) { for(int i=0; i<n; i++) a[i]*=b; return a;}
friend inline u operator/=( u& a,const float b) { for(int i=0; i<n; i++) a[i]/=b; return a;}
};
template<class t> inline t sampleLinear(const t& a,const t& b,float s) {
return (1-s)*a+s*b;
}
template<class t> inline t sampleHermite(const t& a,const t& b,const t& at,const t& bt,float s) {
return
(+2*s*s*s -3*s*s +1)*a+
(-2*s*s*s +3*s*s )*b+
(+1*s*s*s -2*s*s +s )*at+
(+1*s*s*s -1*s*s )*bt;
}
template<class t> inline t sampleBezier(const t& a,const t& b,const t& ac,const t& bc,float s) {
return
(-1*s*s*s +3*s*s -3*s +1)*a+
(+3*s*s*s -6*s*s +3*s )*b+
(-3*s*s*s +3*s*s )*ac+
(+1*s*s*s )*bc;
}
// 4-component floating point vectors.
struct vec4: public vectorSpace<float,4,vec4> {
inline vec4() {x[0]=x[1]=x[2]=x[3]=0;}
inline vec4(float _x,float _y,float _z,float _w) {x[0]=_x; x[1]=_y; x[2]=_z; x[3]=_w;}
};
// 4x4 matrices.
struct mtx44: public vectorSpace<vec4,4,mtx44> {
mtx44() {}
mtx44(const vec4& _x,const vec4& _y, const vec4& _z,const vec4& _w) {x[0]=_x; x[1]=_y; x[2]=_z; x[3]=_w;}
};
// A 3-dimensional (4-component) quaternion.
struct quat: public vectorSpace<float,4,quat> {
quat() {x[0]=x[1]=x[2]=0; x[3]=0;}
quat(float _x,float _y,float _z,float _w) {x[0]=_x; x[1]=_y; x[2]=_z; x[3]=_w;}
};
inline quat operator*(const quat& a,const quat& b) {
return quat(
+a[0]*b[3] +a[1]*b[2] -a[2]*b[1] +a[3]*b[0],
-a[0]*b[2] +a[1]*b[3] +a[2]*b[0] +a[3]*b[1],
+a[0]*b[1] -a[1]*b[0] +a[2]*b[3] +a[3]*b[2],
-a[0]*b[0] -a[1]*b[1] -a[2]*b[2] +a[3]*b[3]
);
}
inline float norm(const quat& a) {
return a[0]*a[0] + a[1]*a[1] + a[2]*a[2] + a[3]*a[3];
}
inline float mag(const quat& a) {
return sqrt(norm(a));
}
inline quat normal(const quat& a) {
return a/mag(a);
}
inline quat conj(const quat& a) {
return quat(-a[0],-a[1],-a[2],a[3]);
}
inline quat inv(const quat& a) {
return 1.f/norm(a)*conj(a);
}
inline quat exp(const quat& a) {
float r = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);
float et = std::exp(a[3]);
float s = r>=0.00001f? et*sin(r)/r: 0.f;
return quat(s*a[0],s*a[1],s*a[2],et*cos(r));
}
inline quat ln(const quat& a) {
float r = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);
float t = r>0.00001f? atan2(r,a[3])/r: 0.f;
return quat(t*a[0],t*a[1],t*a[2],0.5*log(norm(a)));
}
inline vec4 quatToRotationAxisAngle(const quat& a) {
float t = a[3]*a[3];
float rsa = t<0.99999999? 1.f/sqrt(1.f-t): 1.f;
return vec4(a[0]*rsa,a[1]*rsa,a[2]*rsa,acos(a[3])*2.f);
}
inline quat quatFromRotationAxisAngle(const vec4& a) {
float s = sin(a[3]/2.f);
float c = cos(a[3]/2.f);
return quat(a[0]*s,a[1]*s,a[2]*s,c);
}
inline mtx44 quatToRotationMatrix(const quat& a) {
float xx=a[0]*a[0];
float xy=a[0]*a[1], yy=a[1]*a[1];
float xz=a[0]*a[2], yz=a[1]*a[2], zz=a[2]*a[2];
float xw=a[0]*a[3], yw=a[1]*a[3], zw=a[2]*a[3], ww=a[3]*a[3];
return mtx44(
vec4(+xx-yy-zz+ww, +xy+zw+xy+zw, +xz-yw+xz-yw, 0),
vec4(+xy-zw+xy-zw, -xx+yy-zz+ww, +yz+xw+yz+xw, 0),
vec4(+xz+yw+xz+yw, +yz-xw+yz-xw, -xx-yy+zz+ww, 0),
vec4(0 , 0 , 0 , 1)
);
}
inline quat quatFromRotationMatrix(const mtx44& m) {
float w=0.5*sqrt(m[0][0]+m[1][1]+m[2][2]+m[3][3]);
float s=0.25/w;
return quat(
(m[1][2]-m[2][1])*s,
(m[2][0]-m[0][2])*s,
(m[0][1]-m[1][0])*s,
w
);
}
}
}
#endif

159
math/vector.hpp Normal file
View File

@ -0,0 +1,159 @@
#ifndef GOMEZ_Vector_H
#define GOMEZ_Vector_H
#include <cmath>
/**
* Taken from:
* http://www.gamasutra.com/features/19990702/data_structures_01.htm
* http://www.gamasutra.com/features/19991018/Gomez_1.htm
*
* Both by Miguel Gomez
*/
namespace GomezMath {
// A floating point number
//
typedef float Scalar;
//
// A 3D vector
//
class Vector
{
public:
Scalar x, y, z; //x,y,z coordinates
public:
Vector ():x (0), y (0), z (0)
{
}
Vector (const Scalar & a, const Scalar & b, const Scalar & c):x (a), y (b),
z (c)
{
}
//index a component
//NOTE: returning a reference allows
//you to assign the indexed element
Scalar & operator [](const long i)
{
return *((&x) + i);
}
//compare
const bool operator == (const Vector & v) const
{
return (v.x == x && v.y == y && v.z == z);
}
const bool operator != (const Vector & v) const
{
return !(v == *this);
}
//negate
const Vector operator - () const
{
return Vector (-x, -y, -z);
}
//assign
const Vector & operator = (const Vector & v)
{
x = v.x;
y = v.y;
z = v.z;
return *this;
}
//increment
const Vector & operator += (const Vector & v)
{
x += v.x;
y += v.y;
z += v.z;
return *this;
}
//decrement
const Vector & operator -= (const Vector & v)
{
x -= v.x;
y -= v.y;
z -= v.z;
return *this;
}
//self-multiply
const Vector & operator *= (const Scalar & s)
{
x *= s;
y *= s;
z *= s;
return *this;
}
//self-divide
const Vector & operator /= (const Scalar & s)
{
const Scalar r = 1 / s;
x *= r;
y *= r;
z *= r;
return *this;
}
//add
const Vector operator + (const Vector & v) const
{
return Vector (x + v.x, y + v.y, z + v.z);
}
//subtract
const Vector operator - (const Vector & v) const
{
return Vector (x - v.x, y - v.y, z - v.z);
}
//post-multiply by a scalar
const Vector operator * (const Scalar & s) const
{
return Vector (x * s, y * s, z * s);
}
//pre-multiply by a scalar
friend inline const Vector operator * (const Scalar & s, const Vector & v)
{
return v * s;
}
//divide
const Vector operator / (Scalar s) const
{
s = 1 / s;
return Vector (s * x, s * y, s * z);
}
//cross product
const Vector cross (const Vector & v) const
{
//Davis, Snider, "Introduction to Vector Analysis", p. 44
return Vector (y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
}
//scalar dot product
const Scalar dot (const Vector & v) const
{
return x * v.x + y * v.y + z * v.z;
}
//length
const Scalar length () const
{
return (Scalar) sqrt ((double) this->dot (*this));
}
//unit vector
const Vector unit () const
{
return (*this) / length ();
}
//make this a unit vector
void normalize ()
{
(*this) /= length ();
}
//equal within an error e
const bool nearlyEquals (const Vector & v, const Scalar e) const
{
return fabs (x - v.x) < e && fabs (y - v.y) < e && fabs (z - v.z) < e;
}
};
//
// A 3D position
//
typedef Vector Point;
}
#endif

184
navdata.cpp Normal file
View File

@ -0,0 +1,184 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This file contains code derived from information copyrighted by *
* DMA Design. It may not be used in a commercial product. *
* *
* See license.txt for details. *
* *
* This notice may not be removed or altered. *
************************************************************************/
#include <cassert>
#include <iostream>
#include <sstream>
#include "navdata.h"
#include "log.h"
#include "m_exceptions.h"
namespace OpenGTA {
Rect2D::Rect2D() {
x = y = w = h = 0;
}
Rect2D::Rect2D(PHYSFS_uint8 a, PHYSFS_uint8 b, PHYSFS_uint8 c, PHYSFS_uint8 d) {
x = a;
y = b;
w = c;
h = d;
}
bool Rect2D::isInside(PHYSFS_uint8 _x, PHYSFS_uint8 _y) {
if ((_x >= x ) && (_y >= y) &&
(PHYSFS_uint16(_x) <= PHYSFS_uint16(x) + w) &&
(PHYSFS_uint16(_y) <= PHYSFS_uint16(y) + h)) {
lastSubLocation = subLocation(_x, _y);
return true;
}
return false;
}
PHYSFS_uint16 Rect2D::getSize() {
PHYSFS_uint16 res = w;
res *= h;
return res;
}
// 0 = central, 1 = north, 2 = south, 4 = east, 8 = west
PHYSFS_uint8 Rect2D::subLocation(PHYSFS_uint8 _x, PHYSFS_uint8 _y) {
PHYSFS_uint8 in_x = _x - x; // offset in rect; assume: x <= _x
PHYSFS_uint8 in_y = _y - y;
float rel_x = float(in_x)/w;
float rel_y = float(in_y)/h;
PHYSFS_uint8 res = 0;
#define ONE_THIRD 1.0f/3.0f
#define TWO_THIRDS 2.0f/3.0f
if (rel_x <= ONE_THIRD)
// INFO << "west" << std::endl;
res |= 8;
else if (rel_x >= TWO_THIRDS)
//INFO << "east" << std::endl;
res |= 4;
if (rel_y <= ONE_THIRD)
res |= 1;
//INFO << "north" << std::endl;
else if (rel_y >= TWO_THIRDS)
res |= 2;
//INFO << "south" << std::endl;
return res;
}
NavData::Sector::Sector(PHYSFS_file* fd) : Rect2D() {
sam = 0;
isADummy = 0;
std::memset(&name, 0, 30);
assert(fd);
PHYSFS_read(fd, static_cast<void*>(&x), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&y), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&w), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&h), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&sam), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&name), 30, 1);
}
NavData::Sector::Sector() : Rect2D() {
x = 0;
y = 0;
w = 255;
h = 255;
sam = 0;
std::memset(&name, 0, 30);
isADummy = 1;
}
const char* NavData::Sector::getFullName() {
if (isADummy)
return "";
std::string n;
if (lastSubLocation == 0)
n.append("Central ");
else if (lastSubLocation == 1)
n.append("North ");
else if (lastSubLocation == 2)
n.append("South ");
else if (lastSubLocation == 4)
n.append("East ");
else if (lastSubLocation == 8)
n.append("West ");
else if (lastSubLocation == 9)
n.append("Northwest ");
else if (lastSubLocation == 10)
n.append("Southwest ");
else if (lastSubLocation == 5)
n.append("Northeast ");
else if (lastSubLocation == 6)
n.append("Southeast ");
n.append(name);
return n.c_str();
}
NavData::NavData(PHYSFS_uint32 size, PHYSFS_file *fd) {
if (size % 35) {
std::ostringstream o;
o << "Navdata size: " << size << " % 35 != 0";
throw E_INVALIDFORMAT(o.str());
//throw std::string("Invalid NavData size in mapfile");
}
PHYSFS_uint32 c = size / 35;
assert(fd);
for (PHYSFS_uint32 i = 0; i < c; ++i) {
Sector *sec = new Sector(fd);
if (sec->getSize() == 0) { // workaround for 'NYC.CMP' (empty sectors)
delete sec;
WARN << "skipping zero size sector" << std::endl;
continue;
}
else
areas.insert(std::pair<PHYSFS_uint16, Sector*>(sec->getSize(), sec));
}
// dummy catch-all sector for gta london maps
areas.insert(std::pair<PHYSFS_uint16, Sector*>(255*255, new Sector()));
/*
std::cout << "map areas (by size)" << std::endl;
SectorMapType::iterator i = areas.begin();
while (i != areas.end()) {
std::cout << " " << i->first << " : " << i->second->name << " @ " <<
int(i->second->x) << "," << int(i->second->y) << " " << int(i->second->w) << "x" <<
int(i->second->h) << " sample " << int(i->second->sam) << std::endl;
++i;
}
*/
}
NavData::~NavData() {
clear();
}
NavData::Sector* NavData::getSectorAt(PHYSFS_uint8 x, PHYSFS_uint8 y) {
SectorMapType::iterator it = areas.begin();
while (it != areas.end()) {
if (it->second->isInside(x, y))
return it->second;
++it;
}
std::ostringstream o;
o << "Querying invalid sector at " << int(x) << ", " << int(y);
throw E_OUTOFRANGE(o.str());
return NULL;
}
void NavData::clear() {
SectorMapType::iterator it = areas.begin();
while (it != areas.end()) {
delete it->second;
++it;
}
areas.clear();
}
}
#if 0
int main(int argc, char* argv[]) {
OpenGTA::Rect2D a(3, 4, 10, 20);
a.subLocation(atoi(argv[1]), atoi(argv[2]));
}
#endif

97
navdata.h Normal file
View File

@ -0,0 +1,97 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This file contains code derived from information copyrighted by *
* DMA Design. It may not be used in a commercial product. *
* *
* See license.txt for details. *
* *
* This notice may not be removed or altered. *
************************************************************************/
#ifndef NAVDATA_H
#define NAVDATA_H
#include <map>
#include <physfs.h>
namespace OpenGTA {
/** Helper class for area names.
*
* A simple box; beware uint8 overflow!
*
* @see NavData
*/
struct Rect2D {
public:
/** Zero-constructor.
* Everything is 0
*/
Rect2D();
/** Constructor with full params.
* @param x
* @param y
* @param w
* @param h
*/
Rect2D(PHYSFS_uint8, PHYSFS_uint8, PHYSFS_uint8, PHYSFS_uint8);
/** Test: point-in-box.
* @param x
* @param y
* @note If the point is inside 'lastSubLocation' is updated before returning.
*/
bool isInside(PHYSFS_uint8, PHYSFS_uint8);
/** Calculate north/south/east/west/central of point (which has to be inside).
* @param x
* @param y
* @return uint8 bitfield
*/
PHYSFS_uint8 subLocation(PHYSFS_uint8, PHYSFS_uint8);
PHYSFS_uint16 getSize();
PHYSFS_uint8 x, y;
PHYSFS_uint8 w, h;
/** Last sub-area location.
* 0 = central
* 1 = north
* 2 = south
* 4 = east
* 8 = west
* ... valid combinations of the last four
*/
PHYSFS_uint8 lastSubLocation;
};
/** Container of all named sectors.
* @see Sector
*/
class NavData {
public:
/** A named sector of the map.
*/
struct Sector : public Rect2D {
/** Constructor from valid PHYSFS handle.
*/
Sector(PHYSFS_file*);
Sector();
/** Sample number.
* 1) see $LANGUAGE.FXT file for actual name
* 2) probably sound?
*/
PHYSFS_uint8 sam; // sample number
char name[30]; // FIXME: should not be used
/** Returns the name prefixed with sub-area location.
*/
const char* getFullName();
private:
bool isADummy;
};
NavData(PHYSFS_uint32 size, PHYSFS_file *fd);
~NavData();
Sector* getSectorAt(PHYSFS_uint8, PHYSFS_uint8);
private:
void clear();
typedef std::multimap<PHYSFS_uint16, Sector*> SectorMapType;
SectorMapType areas;
};
}
#endif

523
opengta.h Normal file
View File

@ -0,0 +1,523 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This file contains code derived from information copyrighted by *
* DMA Design. It may not be used in a commercial product. *
* *
* See license.txt for details. *
* *
* This notice may not be removed or altered. *
************************************************************************/
#ifndef OPENGTA_MAIN_H
#define OPENGTA_MAIN_H
#include <string>
#include <map>
#include <vector>
#include <SDL.h>
#include <physfs.h>
#include "set.h"
namespace OpenGTA {
/** The common class for all graphics wrappers.
* Contains a number of common variables; does essentially nothing.
*/
class GraphicsBase {
public:
GraphicsBase();
virtual ~GraphicsBase();
typedef struct ObjectInfo {
PHYSFS_uint32 width, height, depth;
PHYSFS_uint16 sprNum, weight, aux;
PHYSFS_sint8 status;
PHYSFS_uint8 numInto;
//PHYSFS_uint16 into[255]; // FIXME: MAX_INTO ???
} ObjectInfo;
typedef struct LoadedAnim {
LoadedAnim(size_t size) : frame(size) {}
PHYSFS_uint8 block;
PHYSFS_uint8 which;
PHYSFS_uint8 speed;
PHYSFS_uint8 frameCount;
std::vector<PHYSFS_uint8> frame;
} LoadedAnim;
typedef struct DoorInfo {
PHYSFS_sint16 rpx, rpy;
PHYSFS_sint16 object;
PHYSFS_sint16 delta;
} DoorInfo;
typedef struct HlsInfo {
PHYSFS_sint16 h, l, s;
} HlsInfo;
typedef struct CarInfo {
PHYSFS_sint16 width, height, depth;
PHYSFS_sint16 sprNum;
PHYSFS_sint16 weightDescriptor;
PHYSFS_sint16 maxSpeed, minSpeed;
PHYSFS_sint16 acceleration, braking;
PHYSFS_sint16 grip, handling;
// ... remaps
HlsInfo remap24[12];
PHYSFS_uint8 remap8[12];
PHYSFS_uint8 vtype;
PHYSFS_uint8 model;
PHYSFS_uint8 turning;
PHYSFS_uint8 damagable;
PHYSFS_uint16 value[4];
PHYSFS_sint8 cx,cy;
PHYSFS_uint32 moment;
float rbpMass;
float g1_Thrust;
float tyreAdhesionX, tyreAdhesionY;
float handBrakeFriction;
float footBrakeFriction;
float frontBrakeBias;
PHYSFS_sint16 turnRatio;
PHYSFS_sint16 driveWheelOffset;
PHYSFS_sint16 steeringWheelOffset;
float backEndSlideValue;
float handBrakeSlideValue;
PHYSFS_uint8 convertible;
PHYSFS_uint8 engine;
PHYSFS_uint8 radio;
PHYSFS_uint8 horn;
PHYSFS_uint8 soundFunction;
PHYSFS_uint8 fastChangeFlag;
PHYSFS_sint16 numDoors;
DoorInfo door[4]; // FIXME: MAX_DOORS
} CarInfo;
/*
* float->fixed:
* fixed = int(floatnum * 65536)
*
* fixed->float
* float = float(fixedNum)/65536
*
* int->fixed
* fixed = intNum << 16
*
* fixed->int
* int = fixedNum >> 16
*/
typedef struct DeltaInfo {
PHYSFS_uint16 size;
unsigned char* ptr;
} DeltaInfo;
typedef struct SpriteInfo {
PHYSFS_uint8 w;
PHYSFS_uint8 h;
PHYSFS_uint8 deltaCount;
PHYSFS_uint16 size;
PHYSFS_uint16 clut;
PHYSFS_uint8 xoffset;
PHYSFS_uint8 yoffset;
PHYSFS_uint16 page;
//unsigned char* ptr;
DeltaInfo delta[33]; //FIXME: GTA_SPRITE_MAX_DELTAS
} SpriteInfo;
typedef struct SpriteNumbers {
PHYSFS_uint16 GTA_SPRITE_ARROW;
PHYSFS_uint16 GTA_SPRITE_DIGITS;
PHYSFS_uint16 GTA_SPRITE_BOAT;
PHYSFS_uint16 GTA_SPRITE_BOX;
PHYSFS_uint16 GTA_SPRITE_BUS;
PHYSFS_uint16 GTA_SPRITE_CAR;
PHYSFS_uint16 GTA_SPRITE_OBJECT;
PHYSFS_uint16 GTA_SPRITE_PED;
PHYSFS_uint16 GTA_SPRITE_SPEEDO;
PHYSFS_uint16 GTA_SPRITE_TANK;
PHYSFS_uint16 GTA_SPRITE_TRAFFIC_LIGHTS;
PHYSFS_uint16 GTA_SPRITE_TRAIN;
PHYSFS_uint16 GTA_SPRITE_TRDOORS;
PHYSFS_uint16 GTA_SPRITE_BIKE;
PHYSFS_uint16 GTA_SPRITE_TRAM;
PHYSFS_uint16 GTA_SPRITE_WBUS;
PHYSFS_uint16 GTA_SPRITE_WCAR;
PHYSFS_uint16 GTA_SPRITE_EX;
PHYSFS_uint16 GTA_SPRITE_TUMCAR;
PHYSFS_uint16 GTA_SPRITE_TUMTRUCK;
PHYSFS_uint16 GTA_SPRITE_FERRY;
enum SpriteTypes {
ARROW = 0,
DIGIT,
BOAT,
BOX,
BUS,
CAR,
OBJECT,
PED,
SPEEDO,
TANK,
TRAFFIC_LIGHT,
TRAIN,
TRDOOR,
BIKE,
TRAM,
WBUS,
WCAR,
EX,
TUMCAR,
TUMTRUCK,
FERRY
};
PHYSFS_uint16 reIndex(const PHYSFS_uint16 & id, const enum SpriteTypes & st) const;
PHYSFS_uint16 countByType(const SpriteTypes & t) const;
} SpriteNumbers;
bool isAnimatedBlock(uint8_t area_code, uint8_t id);
void prepareSideTexture(unsigned int idx, unsigned char* dst);
void prepareLidTexture(unsigned int idx, unsigned char* dst);
void prepareAuxTexture(unsigned int idx, unsigned char* dst);
SpriteNumbers spriteNumbers;
CarInfo* findCarByModel(PHYSFS_uint8);
unsigned char* getTmpBuffer(bool rgba);
SpriteInfo* getSprite(size_t id) { return spriteInfos[id]; }
virtual unsigned char* getSide(unsigned int idx, unsigned int palIdx, bool rgba) = 0;
virtual unsigned char* getLid(unsigned int idx, unsigned int palIdx, bool rgba) = 0;
virtual unsigned char* getAux(unsigned int idx, unsigned int palIdx, bool rgba) = 0;
virtual unsigned char* getSpriteBitmap(size_t id, int remap, Uint32 delta) = 0;
std::vector<LoadedAnim*> animations;
std::vector<SpriteInfo*> spriteInfos;
std::vector<ObjectInfo*> objectInfos;
std::vector<CarInfo*> carInfos;
bool getDeltaHandling();
void setDeltaHandling(bool delta_as_set);
bool isBlockingSide(uint8_t id);
void setupBlocking(const std::string & file);
protected:
void loadTileTextures();
void loadAnim();
void loadObjectInfo_shared(PHYSFS_uint64 offset);
void loadSpriteNumbers_shared(PHYSFS_uint64 offset);
void loadCarInfo_shared(PHYSFS_uint64 offset);
void loadSpriteInfo_shared(PHYSFS_uint64 offset);
void handleDeltas(const SpriteInfo & spriteinfo, unsigned char* buffer,
Uint32 delta);
void applyDelta(const SpriteInfo & spriteInfo, unsigned char* buffer,
Uint32 offset, const DeltaInfo & deltaInfo, bool mirror = false);
PHYSFS_file* fd;
unsigned char* rawTiles;
unsigned char* rawSprites;
PHYSFS_uint32 sideSize;
PHYSFS_uint32 lidSize;
PHYSFS_uint32 auxSize;
PHYSFS_uint32 animSize;
PHYSFS_uint32 objectInfoSize;
PHYSFS_uint32 carInfoSize;
PHYSFS_uint32 spriteInfoSize;
PHYSFS_uint32 spriteGraphicsSize;
PHYSFS_uint32 spriteNumberSize;
PHYSFS_uint32 auxBlockTrailSize;
/*
int loadSide();
int loadLid();
int loadAux();
int loadAnim();
int loadObject();
int loadCar();
int loadSpriteInfo();
int loadSpriteGraphics();
int loadSpriteNumbers();*/
PHYSFS_uint8 _topHeaderSize;
unsigned char tileTmp[4096];
unsigned char tileTmpRGB[4096*3];
unsigned char tileTmpRGBA[4096*4];
bool delta_is_a_set;
Util::Set sideTexBlockMove;
};
// just a forward declaration
class CityView;
/** Loader for STYLE*.GRY files.
*
* Implements loading the 8-bit graphic files.
*/
class Graphics8Bit : public GraphicsBase {
/** allow renderer direct access to members */
friend class CityView;
public:
/** Constructor for graphics loader.
* @param style a valid filename (maybe uppercase depending on your files)
*/
Graphics8Bit(const std::string& style);
/** Destructor cleans all rawdata caches. */
~Graphics8Bit();
/** Helper to apply palettes to various raw bitmaps.
* @see Graphics8Bit
* @see Font
*/
class RGBPalette {
private:
unsigned char data[256*3];
public:
/** Empty constructor.
* You HAVE to call loadFromFile() function when using this constructor!.
*/
RGBPalette();
/** Formerly private member, now exposed for Font class; take care.
* @param fd PHYSFS_file* handle.
*/
int loadFromFile(PHYSFS_file* fd);
/** Constructor from PHYFS_file.
* @param fd PHYSFS_file* handle
*/
RGBPalette(PHYSFS_file* fd);
/** Constructor from filename.
* @param filename a palette file name
*/
RGBPalette(const std::string& palette);
/** Transforms an input buffer using the palette stored in this instance.
* @param len length of the src buffer (in byte)
* @param src pointer to src buffer
* @param dst pointer to dst buffer (must exist and be large enough)
* @param rgba use 'true' to create a RGBA image, or 'false' (default) for RGB
*/
void apply(unsigned int len, const unsigned char* src, unsigned char* dst, bool rgba = false);
};
unsigned char* getSide(unsigned int idx, unsigned int palIdx, bool rgba);
unsigned char* getLid(unsigned int idx, unsigned int palIdx, bool rgba);
unsigned char* getAux(unsigned int idx, unsigned int palIdx, bool rgba);
unsigned char* getSpriteBitmap(size_t id, int remap, Uint32 delta);
void dump();
private:
PHYSFS_uint32 paletteSize;
PHYSFS_uint32 remapSize;
PHYSFS_uint32 remapIndexSize;
protected:
void loadHeader();
void loadPalette();
void loadRemapTables();
void loadRemapIndex();
void loadObjectInfo();
void loadCarInfo();
void loadSpriteInfo();
void loadSpriteGraphics();
void loadSpriteNumbers();
void applyRemap(unsigned int len, unsigned int which, unsigned char* buffer);
RGBPalette* masterRGB;
PHYSFS_uint8 remapTables[256][256];
PHYSFS_uint8 remapIndex[256][4];
};
class Graphics24Bit : public GraphicsBase {
public:
Graphics24Bit(const std::string & style);
~Graphics24Bit();
unsigned char* getSide(unsigned int idx, unsigned int palIdx, bool rgba);
unsigned char* getLid(unsigned int idx, unsigned int palIdx, bool rgba);
unsigned char* getAux(unsigned int idx, unsigned int palIdx, bool rgba);
unsigned char* getSpriteBitmap(size_t id, int remap, Uint32 delta);
void dumpClut(const char* fname);
protected:
void loadHeader();
void loadClut();
void loadPalIndex();
void loadObjectInfo();
void loadCarInfo();
void loadSpriteInfo();
void loadSpriteGraphics();
void loadSpriteNumbers();
void applyClut(unsigned char* src, unsigned char* dst,
const size_t & len, const PHYSFS_uint16 & clutIdx, bool rgba);
private:
PHYSFS_uint32 clutSize;
PHYSFS_uint32 pagedClutSize;
PHYSFS_uint32 tileclutSize;
PHYSFS_uint32 spriteclutSize;
PHYSFS_uint32 newcarclutSize;
PHYSFS_uint32 fontclutSize;
PHYSFS_uint32 paletteIndexSize;
unsigned char* rawClut;
PHYSFS_uint16* palIndex;
};
class NavData; // see navdata.h
#define GTA_MAP_MAXDIMENSION 256
/** the wrapper for the CMP (compressed map) files */
class Map {
friend class MapViewGL;
public:
Map(const std::string& filename);
~Map();
typedef struct BlockInfo {
PHYSFS_uint16 typeMap;
PHYSFS_uint8 typeMapExt;
PHYSFS_uint8 left, right, top, bottom, lid;
inline bool upOk() { return (typeMap & 1); }
inline bool downOk() { return (typeMap & 2); }
inline bool leftOk() { return (typeMap & 4); }
inline bool rightOk() { return (typeMap & 8); }
inline uint8_t blockType() { return ((typeMap & 16 ? 1 : 0) + (typeMap & 32 ? 2 : 0) + (typeMap & 64 ? 4 : 0)); }
inline bool isFlat() { return (typeMap & 128); }
inline uint8_t slopeType() { return ((typeMap & 256 ? 1 : 0) + (typeMap & 512 ? 2 : 0) +
(typeMap & 1024 ? 4 : 0) + (typeMap & 2048 ? 8 : 0) + (typeMap & 4096 ? 16 : 0) + (typeMap & 8192 ? 32 : 0));}
inline uint8_t rotation() { return ((typeMap & 16384 ? 1 : 0) + (typeMap & 32768 ? 2 : 0)); }
/* m1win seems to indicate:
* 000 - Nothing
* 001 - traffic lights
* 010 - invalid
* 011 - invalid
* 100 - railway end turn
* 101 - railway start turn
* 110 - railway station
* 111 - railway station train
*/
inline bool trafficLights() { return (typeMapExt & 1); }
inline bool railEndTurn() { return (typeMapExt & 4); }
inline bool railStartTurn() { return ((typeMapExt & 4) && (typeMapExt &1)); }
inline bool railStation() { return ((typeMapExt & 4) && (typeMapExt & 2));}
inline bool railStationTrain() { return ((typeMapExt & 4) && (typeMapExt & 2) && (typeMapExt & 1)); }
inline uint8_t remapIndex() { return ((typeMapExt & 8 ? 1 : 0) + (typeMapExt & 16 ? 2 : 0));}
inline bool flipTopBottom() { return (typeMapExt & 32); }
inline bool flipLeftRight() { return (typeMapExt & 64); }
inline bool railway() { return (typeMapExt & 128); }
} BlockInfo;
typedef struct {
PHYSFS_uint16 x, y, z;
PHYSFS_uint8 type;
PHYSFS_uint8 remap;
PHYSFS_uint16 rotation; // see: cds.doc
PHYSFS_uint16 pitch;
PHYSFS_uint16 roll;
} ObjectPosition;
typedef struct Location {
Location();
Location(const Location & other);
PHYSFS_uint8 x, y, z;
} Location;
typedef std::multimap<PHYSFS_uint8, Location*> LocationMap;
//...
PHYSFS_uint16 getNumBlocksAt(PHYSFS_uint8 x, PHYSFS_uint8 y);
PHYSFS_uint16 getNumBlocksAtNew(PHYSFS_uint8 x, PHYSFS_uint8 y);
BlockInfo* getBlockAt(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z);
BlockInfo* getBlockAtNew(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z);
BlockInfo* getBlockByInternalId(PHYSFS_uint16 id);
PHYSFS_uint16 getInternalIdAt(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z);
void dump();
NavData *nav;
ObjectPosition *objects;
PHYSFS_uint16 numObjects;
protected:
PHYSFS_uint32 base[GTA_MAP_MAXDIMENSION][GTA_MAP_MAXDIMENSION];
PHYSFS_uint16 *column;
BlockInfo *block;
LocationMap locations;
private:
PHYSFS_file* fd;
PHYSFS_uint8 styleNumber;
PHYSFS_uint32 routeSize;
PHYSFS_uint32 objectPosSize;
PHYSFS_uint32 columnSize;
PHYSFS_uint32 blockSize;
PHYSFS_uint32 navDataSize;
int loadHeader();
int loadBase();
int loadColumn();
int loadBlock();
void loadObjects();
void loadRoutes();
void loadLocations();
void loadNavData();
static const PHYSFS_uint8 _topHeaderSize = 28;
static const PHYSFS_uint64 _baseSize = 262144;
};
class MessageDB {
public:
MessageDB();
MessageDB(const std::string &file);
~MessageDB();
void load(const std::string &file);
const std::string& getText(const char* id);
const std::string& getText(const std::string &id);
const std::string& getText(const uint32_t id);
private:
std::map<std::string, std::string> messages;
std::string _error;
};
class Font {
public:
class Character {
public:
Character(PHYSFS_file*, uint8_t);
~Character();
uint8_t width;
uint8_t *rawData;
};
Font(const std::string &file);
~Font();
uint8_t getNumChars() { return numChars; }
uint8_t getCharHeight() { return charHeight; }
Character *getCharById(size_t num) ;
size_t getIdByChar(const char c) ;
uint8_t getMoveWidth(const char c);
void addMapping(char c, size_t num);
void dumpAs(const char* filename, size_t id) ;
unsigned char* getCharacterBitmap(size_t num, unsigned int *width,
unsigned int *height);
private:
void loadMapping(const std::string &name);
void readHeader(PHYSFS_file*);
uint8_t charHeight;
uint8_t numChars;
std::vector<Character*> chars;
std::map<char, size_t> mapping;
Graphics8Bit::RGBPalette palette;
unsigned char *workBuffer;
};
}
#endif

515
pedestrian.cpp Normal file
View File

@ -0,0 +1,515 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#include "pedestrian.h"
#include "spritemanager.h"
#include "opengta.h"
#include "dataholder.h"
#include "log.h"
float slope_height_offset(unsigned char slope_type, float dx, float dz);
namespace OpenGTA {
Projectile::Projectile(uint8_t id, float r, Vector3D p, Vector3D d, Uint32 now) :
typeId(id), rot(r), pos(p), delta(d), endsAtTick(now + 5000) {}
SpriteObject::Animation::Animation() :
Util::Animation(7, 7),
firstFrameOffset(0), moveSpeed(0.0f) {}
SpriteObject::Animation::Animation(const Animation & other) :
Util::Animation(other.numFrames, 1000 / other.delay),
firstFrameOffset(other.firstFrameOffset),
//numFrames(other.numFrames),
moveSpeed(other.moveSpeed) {}
SpriteObject::Animation::Animation(Uint16 foff, Uint8 num) :
Util::Animation(num, 7),
firstFrameOffset(foff), moveSpeed(0.0f) {}
SpriteObject::Animation::Animation(Uint16 foff, Uint8 num, float speed) :
Util::Animation(num, 7),
firstFrameOffset(foff), moveSpeed(speed) {}
SpriteObject::SpriteObject(const Vector3D & p) :
pos(p), //animRef(&SpriteManagerHolder::Instance().getAnimationById(0)) {
anim(SpriteManagerHolder::Instance().getAnimationById(0)) {
sprNum = 0;
remap = -1;
//curFrame = 0;
lastFrameUpdateAt = 0;
lastUpdateAt = 0;
rot = 0.0f;
animActive = false;
sprType = GraphicsBase::SpriteNumbers::ARROW;
}
Uint8 SpriteObject::calcCurrentFrame(Uint32 ticks) {
Uint32 delta = ticks - lastFrameUpdateAt;
//assert(animRef);
if (delta > 100) {
//curFrame += 1;
//INFO << "new frame: " << int(curFrame) << " total: " << sprNum + curFrame + anim.firstFrameOffset << std::endl;
lastFrameUpdateAt = ticks;
}
/*
if (curFrame > anim.numFrames)
curFrame = 0;
*/
return 0;//curFrame;
}
SpriteObject::SpriteObject(const SpriteObject & other) :
pos(other.pos), anim(other.anim) {
copyValues(other);
}
void SpriteObject::copyValues(const SpriteObject & other) {
sprNum = other.sprNum;
//curFrame = other.curFrame;
remap = other.remap;
lastFrameUpdateAt = other.lastFrameUpdateAt;
lastUpdateAt = other.lastUpdateAt;
rot = other.rot;
sprType = other.sprType;
animActive = other.animActive;
}
float SpriteObject::heightOverTerrain(const Vector3D & v) {
float x, y, z;
x = floor(v.x);
y = floor(v.y);
z = floor(v.z);
PHYSFS_uint8 x_b, z_b;
x_b = (PHYSFS_uint8)x;
z_b = (PHYSFS_uint8)z;
if (y < 0.0f) {
ERROR << "Below level! at coords: " << v.x << ", " << v.y << ", " << v.z << std::endl;
return 1.0f;
}
OpenGTA::Map & map = OpenGTA::MapHolder::Instance().get();
while (y >= map.getNumBlocksAtNew(x_b, z_b) && y > 0.0f)
y -= 1.0f;
while (y < map.getNumBlocksAtNew(x_b, z_b) && y > 0.0f) {
OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(x_b, z_b, (PHYSFS_uint8)y);
assert(block);
if (block->blockType() > 0) {
float bz = slope_height_offset(block->slopeType(), v.x - x, v.z - z);
if (block->slopeType() == 0 && block->blockType() != 5)
bz -= 1.0f;
return v.y - (y + bz);
}
y -= 1.0f;
}
y = floor(v.y) + 1.0f;
while (y < map.getNumBlocksAtNew(x_b, z_b) && y > 0.0f) {
OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(x_b, z_b, (PHYSFS_uint8)y);
assert(block);
if (block->blockType() > 0) {
float bz = slope_height_offset(block->slopeType(), v.x - x, v.z - z);
if (block->slopeType() == 0 && block->blockType() != 5)
bz -= 1.0f;
return v.y - (y + bz);
}
y += 1.0f;
}
INFO << "should this be reached?" << std::endl;
return 1.0f;
}
Pedestrian::Pedestrian(const Vector3D & e,
const Vector3D & p) : SpriteObject(p),
OBox(RollMatrix3D(0), e * 0.5f) {
m_M.Translate(p);
pedId = 0;
m_control = 0;
animId = 0;
inGroundContact = 0;
sprType = GraphicsBase::SpriteNumbers::PED;
activeWeapon = 0;
speedForces = Vector3D(0, 0, 0);
weaponReloadedAt = 0;
}
Pedestrian::Pedestrian(const Vector3D & e,
const Vector3D & p, const Uint32 & asId) : SpriteObject(p),
OBox(RollMatrix3D(0), e * 0.5f) {
m_M.Translate(p);
pedId = asId;
m_control = 0;
animId = 0;
inGroundContact = 0;
sprType = GraphicsBase::SpriteNumbers::PED;
activeWeapon = 0;
speedForces = Vector3D(0, 0, 0);
weaponReloadedAt = 0;
}
Pedestrian::Pedestrian(const Pedestrian & other) : SpriteObject(other),
OBox(other.m_M, other.m_Extent) {
//animRef(SpriteManagerHolder::Instance().getAnimationById(other.animId)) {
copyValues(other);
}
void Pedestrian::switchToAnim(const Uint32 & newId) {
anim = Animation(SpriteManagerHolder::Instance().getAnimationById(newId));
anim.set(Util::Animation::PLAY_FORWARD, Util::Animation::LOOP);
animId = newId;
//curFrame = 0;
}
void SpriteObject::setAnimation(Animation & otherAnim) {
anim = Animation(otherAnim);
//curFrame = 0;
// FIXME: animId?
}
void Pedestrian::copyValues(const Pedestrian & other) {
m_control = other.m_control;
animId = other.animId;
pedId = other.pedId;
inGroundContact = other.inGroundContact;
sprType = other.sprType;
speedForces = other.speedForces;
activeWeapon = other.activeWeapon;
inventory = other.inventory;
weaponReloadedAt = other.weaponReloadedAt;
}
void Pedestrian::giveItem(uint8_t id, uint32_t amount) {
InventoryType::iterator i = inventory.find(id);
if (i == inventory.end())
inventory[id] = amount;
else
i->second += amount;
}
void Pedestrian::tryMove(Vector3D nPos) {
float x, y, z;
x = floor(nPos.x);
y = floor(nPos.y);
z = floor(nPos.z);
OpenGTA::Map & map = OpenGTA::MapHolder::Instance().get();
OpenGTA::GraphicsBase & graphics = OpenGTA::StyleHolder::Instance().get();
//INFO << heightOverTerrain(nPos) << std::endl;
float hot = heightOverTerrain(nPos);
if (hot > 0.3f)
inGroundContact = 0;
else if (hot < 0.0) {
INFO << "gone below: " << hot << " at " << nPos.x << ", " << nPos.y << ", " << nPos.z << std::endl;
nPos.y -= (hot - 0.3f);
INFO << nPos.y << std::endl;
inGroundContact = 1;
}
else {
inGroundContact = 1;
}
if (y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z)) && y > 0.0f) {
//INFO << x << ", " << y << ", " << z << ": " << int(map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z))) << std::endl;
OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z), PHYSFS_uint8(y));
assert(block);/*
if (block->blockType() > 0) {
float bz = slope_height_offset(block->slopeType(), nPos.x - x, nPos.z - z);
if (block->slopeType() == 0)
bz -= 1.0f;
if (inGroundContact) {
nPos.y = y + bz + 0.3f;
//pos = nPos;
}
else if (nPos.y - y - bz < 0.3f) {
INFO << "ped near ground (type: " << int(block->blockType()) << " float-pos: "
<< nPos.x << ", " << nPos.y << ", " << nPos.z << std::endl;
inGroundContact = 1;
nPos.y = y + bz + 0.3f;
INFO << "height rewritten to: " << nPos.y << std::endl;
//pos = nPos;
}
}
else {
inGroundContact = 0;
INFO << "lost footing: " << x << ", " << y << ", " << z << std::endl;
}*/
if (block->left && graphics.isBlockingSide(block->left)) { // && block->isFlat() == false) {
if (block->isFlat()) {
//INFO << "xblock left: " << x - pos.x << std::endl;
if (x - pos.x < 0 && x - pos.x > -0.2f) {
nPos.x = (nPos.x < pos.x) ? pos.x : nPos.x;
}
else if (x - pos.x > 0 && x - pos.x < 0.2f)
nPos.x = pos.x;
}
else {
INFO << "xblock left: " << x - pos.x << " tex: " << int(block->left) << std::endl;
if (x - pos.x > 0 && x - pos.x < 0.2f)
nPos.x = pos.x;
else if (x - pos.x < 0 && x - pos.x > -0.2f)
nPos.x = (nPos.x < pos.x) ? pos.x : nPos.x;
}
}
if (block->right && block->isFlat() == false) {
INFO << "xblock right: " << pos.x - x - 1 << " tex: " << int(block->right) << std::endl;
if (pos.x - x - 1 > 0 && pos.x - x - 1 < 0.2f) {
nPos.x = pos.x;
}
else if (pos.x - x - 1 < 0 && pos.x - x - 1 > -0.2f)
nPos.x = (nPos.x > pos.x) ? pos.x : nPos.x;
}
if (block->top && graphics.isBlockingSide(block->top)) { // && block->isFlat() == false) {
if (block->isFlat()) {
INFO << "zblock top: " << z - pos.z << " tex: " << int(block->top) << std::endl;
if (z - pos.z > 0 && z - pos.z < 0.2f)
nPos.z = pos.z;
else if (z - pos.z < 0 && z - pos.z > -0.2f)
nPos.z = (nPos.z < pos.z) ? pos.z : nPos.z;
}
else {
INFO << "zblock top: " << z - pos.z << " tex: " << int(block->top)<< std::endl;
if (z - pos.z > 0 && z - pos.z < 0.2f)
nPos.z = pos.z;
else if (z - pos.z < 0 && z - pos.z > -0.2f)
nPos.z = (nPos.z < pos.z) ? pos.z : nPos.z;
}
}
if (block->bottom && block->isFlat() == false) {
INFO << "zblock bottom: " << pos.z - z - 1<< " tex: " << int(block->bottom)<< std::endl;
if (pos.z - z - 1 > 0 && pos.z - z - 1 < 0.2f) {
nPos.z = pos.z;
}
else if (pos.z - z - 1 < 0 && pos.z - z - 1 > -0.2f)
nPos.z = (nPos.z > pos.z) ? pos.z : nPos.z;
}
if (x >= 1 && y < map.getNumBlocksAtNew(PHYSFS_uint8(x-1), PHYSFS_uint8(z))) {
block = map.getBlockAtNew(PHYSFS_uint8(x-1), PHYSFS_uint8(z), PHYSFS_uint8(y));
if (block->right && block->isFlat() == false) {
INFO << "xblock right: " << pos.x - x << " tex: " << int(block->right)<< std::endl;
if (pos.x - x < 0.2f) {
nPos.x = (nPos.x < pos.x ? pos.x : nPos.x);
}
}
}
if (x < 255 && y < map.getNumBlocksAtNew(PHYSFS_uint8(x+1), PHYSFS_uint8(z))) {
block = map.getBlockAtNew(PHYSFS_uint8(x+1), PHYSFS_uint8(z), PHYSFS_uint8(y));
if (block->left && graphics.isBlockingSide(block->left)) { // && block->isFlat() == false) {
INFO << "xblock left: " << x + 1 - pos.x << " tex: " << int(block->left)<< std::endl;
if (block->isFlat()) {
if (x + 1 - pos.x > 0 && x + 1 - pos.x < 0.2f)
nPos.x = (nPos.x < pos.x ? nPos.x : pos.x);
}
else {
if (x + 1 - pos.x < 0.2f)
nPos.x = (nPos.x < pos.x ? nPos.x : pos.x);
}
}
}
if (z >= 1 && y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z-1))) {
block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z-1), PHYSFS_uint8(y));
if (block->bottom && block->isFlat() == false) {
INFO << "zblock bottom: " << pos.z - z<< " tex: " << int(block->bottom)<< std::endl;
if (pos.z - z < 0.2f) {
nPos.z = (nPos.z < pos.z ? pos.z : nPos.z);
}
}
}
if (z < 255 && y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z+1))) {
block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z+1), PHYSFS_uint8(y));
if (block->top && graphics.isBlockingSide(block->top)) { // && block->isFlat() == false) {
INFO << "zblock top: " << z + 1 - pos.z<< " tex: " << int(block->top) << std::endl;
if (block->isFlat()) {
if (z + 1 - pos.z > 0 && z + 1 - pos.z < 0.2f)
nPos.z = (nPos.z < pos.z ? nPos.z : pos.z);
}
else {
if (z + 1 - pos.z < 0.2f)
nPos.z = (nPos.z < pos.z ? nPos.z : pos.z);
}
}
}
//if (inGroundContact)
// pos = nPos;
}
if (inGroundContact)
pos = nPos;
//else
// inGroundContact = 0;
}
void Pedestrian::equip(uint8_t id) {
if (id == 0) {
activeWeapon = 0;
}
else {
InventoryType::iterator i = inventory.find(id);
if (i != inventory.end()) {
activeWeapon = i->first;
}
else
ERROR << "Ped does not have item type " << int(id) << std::endl;
}
}
void Pedestrian::fireWeapon(Uint32 ticks) {
if (activeWeapon == 0)
return; // FIXME: punching!
InventoryType::iterator i = inventory.find(activeWeapon);
if (i->second == 0)
return; // no ammo
if (ticks < weaponReloadedAt)
return;
weaponReloadedAt = ticks + 2000;
OpenGTA::SpriteManagerHolder::Instance().createProjectile(i->first, rot, pos, Vector3D(0.2f, 0, 0.2f), ticks);
}
void Pedestrian::update(Uint32 ticks) {
// update the animation
if (m_control) {
switch(m_control->move) {
case 1:
if (!(animId == 2u + activeWeapon*3))
switchToAnim(2 + activeWeapon*3);
break;
case 2:
if (!(animId == 3u + activeWeapon*3))
switchToAnim(3 + activeWeapon*3);
break;
case 0:
if (!(animId == 1u + activeWeapon*3))
switchToAnim(1 + activeWeapon*3);
break;
case -1:
if (!(animId == 2u + activeWeapon*3)) {
switchToAnim(2 + activeWeapon*3);
anim.set(Util::Animation::PLAY_BACKWARD, Util::Animation::LOOP);
}
}
}
anim.update(ticks);
// update position/rotation
Uint32 delta = ticks - lastUpdateAt;
Vector3D moveDelta(0, 0, 0);
if (m_control) {
switch(m_control->turn) {
case -1:
rot -= 0.2f * delta;
break;
case 1:
rot += 0.2f * delta;
break;
case 0:
break;
}
if (rot >= 360.0f)
rot -= 360.0f;
if (rot < 0.0f)
rot += 360.0f;
switch(m_control->move) {
case -1:
moveDelta.x -= sin(rot * M_PI / 180.0f) * anim.moveSpeed * delta;
moveDelta.z -= cos(rot * M_PI / 180.0f) * anim.moveSpeed * delta;
break;
case 1:
moveDelta.x += sin(rot * M_PI / 180.0f) * anim.moveSpeed * delta;
moveDelta.z += cos(rot * M_PI / 180.0f) * anim.moveSpeed * delta;
break;
case 2:
moveDelta.x += sin(rot * M_PI / 180.0f) * anim.moveSpeed * delta;
moveDelta.z += cos(rot * M_PI / 180.0f) * anim.moveSpeed * delta;
break;
case 0:
break;
}
}
tryMove(pos + moveDelta);
if (!inGroundContact) {
speedForces.y += 0.1f;
pos.y -= speedForces.y;
if (speedForces.y < 0.2f)
INFO << "bridge step? height: " << pos.y << " speed: " << speedForces.y << std::endl;
else
INFO << "FALLING" << pos.y << " speed " << speedForces.y << std::endl;
}
else {
if (speedForces.y > 0.1)
INFO << "impacting with speed: " << speedForces.y << std::endl;
speedForces.y = 0.0f;
}
lastUpdateAt = ticks;
}
#define INT2FLOAT_WRLD(c) (float(c >> 6) + float(c % 64) / 64.0f)
#define INT2F_DIV64(v) (float(v) / 64.0f)
#define INT2F_DIV128(v) (float(v) / 128.0f)
Car::Car(const OpenGTA::Map::ObjectPosition & op) :
SpriteObject(Vector3D(INT2FLOAT_WRLD(op.x), 6.05f-INT2FLOAT_WRLD(op.z), INT2FLOAT_WRLD(op.y))),
OBox(RollMatrix3D(0), Vector3D()),
c_info(*StyleHolder::Instance().get().findCarByModel(op.type)) {
type = op.type;
sprType = GraphicsBase::SpriteNumbers::CAR;
sprNum = c_info.sprNum;
m_Extent = Vector3D(INT2F_DIV128(c_info.width),
INT2F_DIV128(c_info.depth),
INT2F_DIV128(c_info.height));
m_M.Translate(pos);
rot = op.rotation * 360 / 1024;
}
Car::Car(const Car & other) : SpriteObject(other),
OBox(other.m_M, other.m_Extent),
c_info(*StyleHolder::Instance().get().findCarByModel(other.type)) {
copyValues(other);
}
void Car::copyValues(const Car & other) {
sprType = other.sprType;
delta = other.delta;
carId = other.carId;
type = other.type;
}
GameObject::GameObject(const OpenGTA::Map::ObjectPosition & op) :
SpriteObject(Vector3D(INT2FLOAT_WRLD(op.x), 6.05f-INT2FLOAT_WRLD(op.z), INT2FLOAT_WRLD(op.y))),
OBox(RollMatrix3D(0), Vector3D()) {
sprType = GraphicsBase::SpriteNumbers::OBJECT;
GraphicsBase & style = StyleHolder::Instance().get();
sprNum = style.objectInfos[op.type]->sprNum;
m_Extent = Vector3D(INT2F_DIV128(style.objectInfos[op.type]->width),
INT2F_DIV128(style.objectInfos[op.type]->depth),
INT2F_DIV128(style.objectInfos[op.type]->height));
m_M.Translate(pos);
rot = op.rotation * 360 / 1024;
}
GameObject::GameObject(const GameObject & other) : SpriteObject(other),
OBox(other.m_M, other.m_Extent) {
copyValues(other);
}
void GameObject::copyValues(const GameObject & other) {
sprType = other.sprType;
}
}

134
pedestrian.h Normal file
View File

@ -0,0 +1,134 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This software is provided as-is, without any express or implied *
* warranty. In no event will the authors be held liable for any *
* damages arising from the use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute *
* it freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must *
* not claim that you wrote the original software. If you use this *
* software in a product, an acknowledgment in the product documentation *
* would be appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must *
* not be misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source *
* distribution. *
************************************************************************/
#ifndef OGTA_PEDESTRIAN_H
#define OGTA_PEDESTRIAN_H
#include <SDL.h>
#include "math3d.h"
#include "obox.h"
#include "animation.h"
#include "opengta.h"
namespace OpenGTA {
struct Projectile {
Projectile(uint8_t, float, Vector3D, Vector3D, Uint32);
uint8_t typeId;
float rot;
Vector3D pos;
Vector3D delta;
Uint32 endsAtTick;
};
class SpriteObject {
public:
SpriteObject(const Vector3D & p);
SpriteObject(const SpriteObject & other);
struct Animation : public Util::Animation {
Animation();
Animation(const Animation & other);
Animation(Uint16 foff, Uint8 num);
Animation(Uint16 foff, Uint8 num, float speed);
Uint16 firstFrameOffset;
//Uint8 numFrames;
float moveSpeed;
};
Vector3D pos;
//Uint8 curFrame;
Uint16 sprNum;
Sint16 remap;
//Animation * animRef;
Animation anim;
bool animActive;
void update(Uint32 ticks);
Uint32 lastFrameUpdateAt;
Uint32 lastUpdateAt;
float rot;
GraphicsBase::SpriteNumbers::SpriteTypes sprType;
void setAnimation(Animation & otherAnim);
protected:
Uint8 calcCurrentFrame(Uint32 ticks);
float heightOverTerrain(const Vector3D & v);
private:
void copyValues(const SpriteObject & other);
SpriteObject & operator = (const SpriteObject & other) { return *this; }
};
class Pedestrian : public SpriteObject, public OBox {
public:
struct Controller {
// turn, move,
// run/walk/swim/crawl
// jump, shoot/punch,
Sint8 turn;
Sint8 move;
};
Pedestrian(const Vector3D & e, const Vector3D & pos);
Pedestrian(const Vector3D & e, const Vector3D & pos, const Uint32 & asId);
Pedestrian(const Pedestrian & other);
Uint32 pedId;
Controller* m_control;
Uint32 animId;
Vector3D speedForces;
void switchToAnim(const Uint32 & newId);
void update(Uint32 ticks);
void equip(uint8_t eq_id);
void giveItem(uint8_t id, uint32_t amount);
void fireWeapon(Uint32 ticks);
private:
typedef std::map<uint8_t, uint32_t> InventoryType;
InventoryType inventory;
uint8_t activeWeapon;
bool inGroundContact;
void tryMove(Vector3D nPos);
void copyValues(const Pedestrian & other);
Uint32 weaponReloadedAt;
//Uint8 calcCurrentFrame(Uint32 ticks);
Pedestrian & operator = (const Pedestrian & other) { return *this; }
};
class Car : public SpriteObject, public OBox {
public:
Car(const OpenGTA::Map::ObjectPosition & op);
Car(const Car & other);
Uint32 delta;
Uint32 carId;
Uint8 type;
private:
GraphicsBase::CarInfo & c_info;
void copyValues(const Car & other);
Car & operator = (const Car & other) { return *this; }
};
class GameObject : public SpriteObject, public OBox {
public:
GameObject(const OpenGTA::Map::ObjectPosition & op);
GameObject(const GameObject & other);
Uint32 objId;
private:
void copyValues(const GameObject & other);
GameObject & operator = (const GameObject & o) { return *this; }
};
}
#endif

277
prepare_build.sh Executable file
View File

@ -0,0 +1,277 @@
#!/bin/bash
function program_exists() {
$1 $2 1>/dev/null 2>&1
if [ $? -eq 127 ]; then
return 0
fi
upname=$(echo $1 | tr '[a-z]' '[A-Z]' | tr '-' '_')
eval "$upname=$1"
return 1
}
function print_make_file_list() {
FOO=GL_SRC
FOOO=GL_OBJ
( grep -l "^namespace OpenGL" *.cpp ; echo "gl_frustum.cpp";echo "math/obox.cpp coldet/math3d.cpp" ) | sort | xargs echo "$FOO ="
echo "$FOOO = \${$FOO:.cpp=.o}"
FOO=OGTA_SRC
FOOO=OGTA_OBJ
( grep -l "^namespace OpenGTA" *.cpp ; echo "slope_height_func.cpp" ) | sort | xargs echo "$FOO ="
echo "$FOOO = \${$FOO:.cpp=.o}"
UTIL_SRC=$(ls util/*.cpp | grep -v color.cpp | grep -v sound | xargs echo)
SOUND_SRC=$(ls util/*.cpp | grep sound.* | xargs echo)
LUA_SRC=$(ls lua_addon/*.cpp | xargs echo)
cat <<EOF
# list of used source files
UTIL_SRC = $UTIL_SRC
UTIL_OBJ = \${UTIL_SRC:.cpp=.o}
LUA_SRC = $LUA_SRC
LUA_OBJ = \${LUA_SRC:.cpp=.o}
SOUND_SRC = $SOUND_SRC
SOUND_OBJ = \${SOUND_SRC:.cpp=.o}
EOF
}
if [ "$1" == "WIN32" ]; then
EXE_PFIX=.exe
fi
function print_target_list() {
cat <<EOF
# list of make targets (programs)
TARGETS = viewer${EXE_PFIX}
LUA_TARGETS = luaviewer${EXE_PFIX}
all: loki \${TARGETS}
gfxextract${EXE_PFIX}: gfx_extract.cpp read_gry.o read_g24.o read_cmp.o navdata.o dataholder.o \
\$(UTIL_OBJ)
\$(CXX) \$(CATCH_E) \$(GFX_DDUMP) \$(FLAGS) \$(DEFS) \$(LINK_LAZY) \\
\$(INC) \\
-o \$@ \$+ \\
\$(SDL_LIB) \$(SDL_GL_LIB) \$(SDL_IMG_LIB) \$(PHYSFS_LIB) \$(LOKI_LIB)
viewer${EXE_PFIX}: main2.cpp viewer.o \$(OGTA_OBJ) \$(GL_OBJ) \$(UTIL_OBJ)
\$(CXX) \$(CATCH_E) \$(FLAGS) \$(DEFS) \\
\$(INC) \\
-o \$@ \$+ \\
\$(SDL_LIB) \$(SDL_GL_LIB) \$(PHYSFS_LIB) \$(LOKI_LIB) \$(COLDET_LIB)
luaviewer${EXE_PFIX}: main2.cpp viewer.cpp \$(OGTA_OBJ) \$(GL_OBJ) \$(UTIL_OBJ) \
\$(LUA_OBJ)
\$(CXX) \$(CATCH_E) -DWITH_LUA \$(FLAGS) \$(DEFS) \\
\$(INC) \\
-o \$@ \$+ \\
\$(SDL_LIB) \$(SDL_GL_LIB) \$(PHYSFS_LIB) \$(LOKI_LIB) \$(COLDET_LIB) \$(LUA_LIB)
spriteplayer${EXE_PFIX}: sprite_anim_player.o \$(OGTA_OBJ) \$(GL_OBJ) \$(UTIL_OBJ) main2.cpp
\$(CXX) \$(CATCH_E) \$(FLAGS) \$(DEFS) \\
\$(INC) \\
-o \$@ \$+ \\
\$(SDL_LIB) \$(SDL_GL_LIB) \$(PHYSFS_LIB) \$(LOKI_LIB) \$(COLDET_LIB)
slopeview: main.o tools/display_slopes.o navdata.o read_cmp.o \
\$(UTIL_OBJ) common_sdl_gl.o
\$(CXX) \$(CXXFLAGS) -o \$@ \$+ \$(SDL_LIB) \$(PHYSFS_LIB) -lSDL_image
g24: read_g24.cpp read_gry.o \$(UTIL_OBJ)
\$(CXX) -DG24_DUMPER \$(CXXFLAGS) -o \$@ \$+ \$(SDL_LIB) \$(PHYSFS_LIB)
objdump: tools/obj_dump.cpp read_gry.o \$(UTIL_OBJ) main2.o
\$(CXX) \$(CXXFLAGS) -o \$@ \$+ \$(SDL_LIB) \$(PHYSFS_LIB)
objdump_map: tools/obj_dump.cpp read_gry.o \$(UTIL_OBJ) main2.o read_cmp.o navdata.o
\$(CXX) \$(CXXFLAGS) -DDUMP_OBJ_IN_MAP -o \$@ \$+ \$(SDL_LIB) \$(PHYSFS_LIB)
EOF
}
#print_make_file_list > src_list.make
#print_target_list >> src_list.make
function pkg_config_haslib () {
$PKG_CONFIG $1 1>/dev/null 2>&1
if [ $? -eq 0 ]; then
return 1
fi
return 0
}
function check_sdl () {
program_exists sdl-config
if [ $? -eq 1 ]; then
SDL_INC=$($SDL_CONFIG --cflags)
SDL_LIB=$($SDL_CONFIG --libs)
else
program_exists pkg-config
if [ $? -eq 1 ]; then
pkg_config_try_multiple SDL SDL sdl
fi
fi
}
function pkg_config_try_multiple () {
local _prefixName=$1
shift
while [ $# -gt 0 ]; do
pkg_config_haslib $1
if [ $? -eq 1 ]; then
eval "${_prefixName}_INC=\$($PKG_CONFIG --cflags $1)"
eval "${_prefixName}_LIB=\$($PKG_CONFIG --libs $1)"
return
fi
shift
done
}
function check_lua () {
program_exists pkg-config
if [ $? -eq 1 ]; then
pkg_config_try_multiple LUA lua5.1 lua51 lua5 lua
else
program_exists lua-config
if [ $? -eq 1 ]; then
LUA_INC=$($LUA_CONFIG --include)
LUA_LIB=$($LUA_CONFIG --libs)
fi
fi
}
function check_physfs () {
program_exists pkg-config
if [ $? -eq 1 ]; then
pkg_config_try_multiple PHYSFS physfs
fi
}
function check_compiler () {
g++ 1>/dev/null 2>&1
if [ $? -eq 1 ]; then
CXX=g++
else
CXX=
fi
gcc 1>/dev/null 2>&1
if [ $? -eq 1 ]; then
CC=gcc
else
CC=
fi
}
# defaults
DEBUG=-ggdb
WARN=-Wall
OPT=-O2
if [ "$1" == "LINUX" ]; then
DEFS="-DLINUX -DDO_SCALEX"
else
DEFS="-DWIN32 -DDO_SCALEX"
fi
PHYSFS_LIB=-lphysfs
SDL_LIB=-lSDL
LUA_LIB=-llua51
function print_detected() {
cat <<EOF
# using these compilers
CXX = $CXX
CC = $CC
DEBUG = $DEBUG
OPT = $OPT
WARN = $WARN
DEFS = $DEFS -DGCC
# def only for 'main' programs to let gdb handle the exception
#CATCH_E = -DDONT_CATCH
#GFX_DDUMP = -DDUMP_DELTA_DEBUG
# the external libraries
PHYSFS_INC = $PHYSFS_INC
PHYSFS_LIB = $PHYSFS_LIB
SDL_INC = $SDL_INC
SDL_LIB = $SDL_LIB
SDL_GL_LIB = -lGL -lGLU
SDL_IMG_LIB = -lSDL_image
# this better be lua >= 5.1 (but not 5.0); not detected
LUA_INC = $LUA_INC
LUA_LIB = $LUA_LIB
LINK_LAZY = -Xlinker --unresolved-symbols -Xlinker ignore-all
EOF
}
function print_w32settings() {
cat <<EOF
# using these compilers
CXX = i586-mingw32msvc-g++
CC = i586-mingw32msvc-gcc
#DEBUG = $DEBUG
OPT = $OPT
WARN = $WARN
DEFS = $DEFS -DGCC
# def only for 'main' programs to let gdb handle the exception
#CATCH_E = -DDONT_CATCH
#GFX_DDUMP = -DDUMP_DELTA_DEBUG
# the external libraries
PHYSFS_INC = -Iinc
PHYSFS_LIB = -Llibs -lphysfs -lz
SDL_INC = -Iinc -D_GNU_SOURCE=1 -D_REENTRANT
SDL_LIB = -Llibs -lSDLmain -lSDL
SDL_GL_LIB = -lopengl32 -lglu32
SDL_IMG_LIB = -lSDL_image
# this better be lua >= 5.1 (but not 5.0); not detected
LUA_INC = $LUA_INC
LUA_LIB = $LUA_LIB
# good idea here?
LINK_LAZY = -Xlinker --unresolved-symbols -Xlinker ignore-all
EOF
}
function print_all() {
check_sdl
check_lua
check_physfs
check_compiler
print_detected
print_make_file_list
print_target_list
cat <<EOF
include depend
EOF
}
if [ "$1" == "LINUX" ]; then
echo "*** LINUX ***"
print_all > src_list.make
else
echo "*** WIN32 ***"
print_w32settings > src_list.make
print_make_file_list >> src_list.make
print_target_list >> src_list.make
fi

284
read_cmp.cpp Normal file
View File

@ -0,0 +1,284 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This file contains code derived from information copyrighted by *
* DMA Design. It may not be used in a commercial product. *
* *
* See license.txt for details. *
* *
* This notice may not be removed or altered. *
************************************************************************/
#include <iostream>
#include <cassert>
#include <sstream>
#include "opengta.h"
#include "navdata.h"
#include "log.h"
#include "m_exceptions.h"
/* see http://members.aol.com/form1/fixed.htm for fixed point floats:
* int_var = (long) fixed_var >> 8; // for 8 bits after point
*/
namespace OpenGTA {
Map::Map(const std::string& filename) {
nav = 0;
fd = PHYSFS_openRead(filename.c_str());
if (fd == NULL) {
std::string f2(filename);
transform(f2.begin(), f2.end(), f2.begin(), tolower);
fd = PHYSFS_openRead(f2.c_str());
}
if (!fd) {
//throw std::string("FileNotFound: ") + filename;
std::ostringstream o;
o << filename << " with error: " << SDL_GetError();
throw E_FILENOTFOUND(o.str());
}
loadHeader();
loadBase();
loadColumn();
loadBlock();
loadObjects();
loadRoutes();
loadLocations();
loadNavData();
//dump();
}
Map::~Map() {
if (column) delete [] column;
if (block) delete [] block;
if (objects) delete [] objects;
if (nav) delete nav;
LocationMap::iterator i = locations.begin();
while (i != locations.end()) {
delete i->second;
++i;
}
locations.clear();
if (fd)
PHYSFS_close(fd);
}
int Map::loadHeader() {
PHYSFS_uint32 vc;
PHYSFS_readULE32(fd, &vc);
//INFO << "Map version code: " << vc << std::endl;
PHYSFS_uint8 sn;
PHYSFS_read(fd, static_cast<void*>(&styleNumber), 1, 1);
//INFO << "Style number: " << int(styleNumber) << std::endl;
PHYSFS_read(fd, static_cast<void*>(&sn), 1, 1);
//INFO << "Sample number: " << int(sn) << std::endl;
PHYSFS_uint16 reserved;
PHYSFS_readULE16(fd, &reserved);
PHYSFS_readULE32(fd, &routeSize);
PHYSFS_readULE32(fd, &objectPosSize);
PHYSFS_readULE32(fd, &columnSize);
PHYSFS_readULE32(fd, &blockSize);
PHYSFS_readULE32(fd, &navDataSize);
/*
INFO << "Route size: " << routeSize << std::endl;
INFO << "Object size: " << objectPosSize << std::endl;
INFO << "Column size: " << columnSize << std::endl;
INFO << "Block size: " << blockSize << " (" <<
blockSize / sizeof(BlockInfo) << " blocks " << blockSize % sizeof(BlockInfo)
<< " overcount)" << std::endl;
INFO << "Navdata size: " << navDataSize << std::endl;
*/
column = new PHYSFS_uint16[columnSize/2];
block = new BlockInfo[blockSize / sizeof(BlockInfo) ];
objects = new ObjectPosition[objectPosSize / sizeof(ObjectPosition)];
return 0;
}
int Map::loadBase() {
PHYSFS_seek(fd, static_cast<PHYSFS_uint64>(_topHeaderSize));
for (int y = 0; y < GTA_MAP_MAXDIMENSION; y++) {
for(int x = 0; x < GTA_MAP_MAXDIMENSION; x++) {
PHYSFS_readULE32(fd, &base[x][y]);
//std::cout << x << "," << y << " : " << base[x][y] << std::endl;
}
}
return 0;
}
int Map::loadColumn() {
if (!PHYSFS_seek(fd, _baseSize + _topHeaderSize)) {
//throw std::string("IO Error while seeking in mapfile");
throw E_IOERROR(PHYSFS_getLastError());
}
//PHYSFS_uint16 v;
for (unsigned int i = 0; i < columnSize/2; i++) {
PHYSFS_readULE16(fd, &column[i]);
//std::cout << i << ": " << v << std::endl;
}
return 0;
}
int Map::loadBlock() {
PHYSFS_seek(fd, _baseSize + columnSize + _topHeaderSize);
int i, max;
max = blockSize / sizeof(BlockInfo);
//uint8_t tmp;
for (i = 0; i < max; i++) {
PHYSFS_readULE16(fd, &block[i].typeMap);
PHYSFS_read(fd, static_cast<void*>(&block[i].typeMapExt), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&block[i].left), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&block[i].right), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&block[i].top), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&block[i].bottom), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&block[i].lid), 1, 1);
//block[i].animMode = 0;
}
return 0;
}
void Map::loadObjects() {
PHYSFS_seek(fd, _baseSize + columnSize + _topHeaderSize + blockSize);
int c = objectPosSize / sizeof(ObjectPosition);
numObjects = c;
assert(objectPosSize % sizeof(ObjectPosition) == 0);
for (int i=0; i < c; i++) {
PHYSFS_readULE16(fd, &objects[i].x);
PHYSFS_readULE16(fd, &objects[i].y);
PHYSFS_readULE16(fd, &objects[i].z);
PHYSFS_read(fd, static_cast<void*>(&objects[i].type), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&objects[i].remap), 1, 1);
PHYSFS_readULE16(fd, &objects[i].rotation);
PHYSFS_readULE16(fd, &objects[i].pitch);
PHYSFS_readULE16(fd, &objects[i].roll);
// shift every coord? or just if any > 255
/*
objects[i].x = objects[i].x >> 6;
objects[i].y = objects[i].y >> 6;
objects[i].z = objects[i].z >> 6;*/
/*
std::cout << objects[i].x << "," << objects[i].y << "," << objects[i].z << " " << int(objects[i].type)
<< " remap " << int(objects[i].remap)
<< " rot " << objects[i].rotation << " " << objects[i].pitch << " " << objects[i].roll << std::endl;
*/
}
}
void Map::loadRoutes() {
//FIXME: missing
PHYSFS_uint32 _si = _baseSize + columnSize + _topHeaderSize +
objectPosSize + blockSize;
PHYSFS_seek(fd, _si);
PHYSFS_uint32 _counted = 0;
while (_counted < routeSize) {
PHYSFS_uint8 num_vertices = 0;
PHYSFS_uint8 route_type = 0;
PHYSFS_read(fd, static_cast<void*>(&num_vertices), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&route_type), 1, 1);
//INFO << "route-t " << int(route_type) << " with " << int(num_vertices) << " vertices" << std::endl;
PHYSFS_uint8 x, y, z;
for (int i=0; i < num_vertices; i++) {
PHYSFS_read(fd, static_cast<void*>(&x), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&y), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&z), 1, 1);
//INFO << int(x) << "," << int(y) << "," << int(z) << std::endl;
_counted += 3;
}
_counted += 2;
}
}
Map::Location::Location() : x(0), y(0), z(0) {}
Map::Location::Location(const Map::Location & other) : x(other.x), y(other.y), z(other.z) {}
void Map::loadLocations() {
//FIXME: missing
PHYSFS_uint32 _si = _baseSize + columnSize + _topHeaderSize +
objectPosSize + routeSize + blockSize;
PHYSFS_seek(fd, _si);
// police
// hospital
// unused
// unused
// fire
// unused
Location loc;
PHYSFS_uint8 loc_type = 0;
for (int i = 0; i < 36; ++i) {
PHYSFS_read(fd, static_cast<void*>(&loc.x), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&loc.y), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&loc.z), 1, 1);
if ((loc.x == 0) && (loc.y == 0) && (loc.z == 0))
continue;
if (i < 6)
loc_type = 0;
else if ((i >= 6) && (i < 12))
loc_type = 1;
else if ((i >= 24) && (i < 30))
loc_type = 2;
else
continue;
//std::cout << int(loc_type) <<": " << int(loc.x) << ", " << int(loc.y) << ", " << int(loc.z) << std::endl;
locations.insert(std::pair<PHYSFS_uint8, Location*>(loc_type, new Location(loc)));
}
}
void Map::loadNavData() {
PHYSFS_uint32 _si = _baseSize + columnSize + _topHeaderSize +
objectPosSize + routeSize + 3 * 6 * 6 + blockSize;
PHYSFS_seek(fd, _si);
nav = new NavData(navDataSize, fd);
assert(nav);
}
PHYSFS_uint16 Map::getNumBlocksAt(PHYSFS_uint8 x, PHYSFS_uint8 y) {
return column[base[x][y] / 2];
}
PHYSFS_uint16 Map::getNumBlocksAtNew(PHYSFS_uint8 x, PHYSFS_uint8 y) {
return 6 - column[base[x][y] / 2];
}
Map::BlockInfo* Map::getBlockAt(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z) {
PHYSFS_uint16 v = column[base[x][y] / 2 + z];
return &block[v];
}
Map::BlockInfo* Map::getBlockAtNew(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z) {
PHYSFS_uint16 idx0 = 6 - column[base[x][y] / 2];
if (idx0 > z)
idx0 -= z;
else
assert(idx0 > z);
idx0 = column[base[x][y] / 2 + idx0];
return &block[idx0];
}
PHYSFS_uint16 Map::getInternalIdAt(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z) {
return column[base[x][y] / 2 + z];
}
Map::BlockInfo* Map::getBlockByInternalId(PHYSFS_uint16 id) {
return &block[id];
}
void Map::dump() {
for (int y = 0; y < GTA_MAP_MAXDIMENSION; y++) {
for(int x = 0; x < GTA_MAP_MAXDIMENSION; x++) {
std::cout << x << "," << y << ":" << column[base[x][y] / 2] << "||";
PHYSFS_uint16 ts = column[base[x][y] / 2];
std::cout << "(";
for(int t=1; t <= (6 - ts); t++) {
BlockInfo *info = &block[column[base[x][y] / 2 + t]];
std::cout << int(info->slopeType()) << ", ";
}
std::cout << ")" << std::endl;
}
}
}
}
#if 0
#include <stdlib.h>
void do_exit() {
PHYSFS_deinit();
}
int main(int argc, char* argv[]) {
PHYSFS_init(argv[0]);
atexit(do_exit);
std::cout << "Physfs-Base: " << PHYSFS_getBaseDir() << std::endl;
PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1);
std::cout << "Has: " << argv[1] << " : " << PHYSFS_exists(argv[1]) << std::endl;
OpenGTA::Map a(argv[1]);
a.dump();
return 0;
}
#endif

428
read_fnt.cpp Normal file
View File

@ -0,0 +1,428 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This file contains code derived from information copyrighted by *
* DMA Design. It may not be used in a commercial product. *
* *
* See license.txt for details. *
* *
* This notice may not be removed or altered. *
************************************************************************/
#include <iostream>
#include <cassert>
#include "cistring.h"
#include "opengta.h"
#include "m_exceptions.h"
#include "log.h"
namespace OpenGTA {
Font::Font(const std::string &file) {
PHYSFS_file *fd = PHYSFS_openRead(file.c_str());
if (fd == NULL) {
std::string f2(file);
transform(f2.begin(), f2.end(), f2.begin(), tolower);
fd = PHYSFS_openRead(f2.c_str());
}
if (!fd)
throw E_FILENOTFOUND(file);
//throw std::string("FileNotFound: ") + file;
readHeader(fd);
int ww = 0;
int lw = 0;
for (uint8_t i = 0; i < numChars; i++) {
Character * ch = new Character(fd, charHeight);
ww += ch->width;
if (ch->width > lw)
lw = ch->width;
chars.push_back(ch);
}
INFO << "total width " << ww << " largest width " << lw << std::endl;
palette.loadFromFile(fd);
PHYSFS_close(fd);
size_t ih = charHeight;
while (ww > 1024) {
ih *= 2;
ww /= 2;
}
workBuffer = new unsigned char[lw*charHeight*4];
loadMapping(file);
}
Font::~Font() {
std::vector<Character*>::iterator i = chars.begin();
while(i != chars.end()) {
delete *i;
i++;
}
chars.clear();
delete [] workBuffer;
}
void Font::readHeader(PHYSFS_file *fd) {
PHYSFS_read(fd, static_cast<void*>(&numChars), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&charHeight), 1, 1);
INFO << "Font contains " << int(numChars) <<
" characters of height " << int(charHeight) << std::endl;
}
void Font::addMapping(char c, size_t num) {
mapping[c] = num;
}
Font::Character* Font::getCharById(size_t num) {
return chars[num];
}
size_t Font::getIdByChar(const char c) {
std::map<char, size_t>::iterator i = mapping.find(c);
if (i == mapping.end())
return 0;
else
return i->second;
}
uint8_t Font::getMoveWidth(const char c) {
std::map<char, size_t>::iterator i = mapping.find(c);
if (i == mapping.end()) {
return chars[0]->width;
}
return chars[i->second]->width;
}
unsigned char* Font::getCharacterBitmap(size_t num, unsigned int *width, unsigned int *height) {
unsigned int len = chars[num]->width;
len *= charHeight;
palette.apply(len, chars[num]->rawData, workBuffer, true);
if (width != NULL)
*width = chars[num]->width;
if (height != NULL)
*height = charHeight;
return workBuffer;
/*
unsigned int glwidth = 1;
unsigned int glheight = 1;
while(glwidth < chars[num]->width)
glwidth <<= 1;
while(glheight < charHeight)
glheight <<= 1;
unsigned char *res = new unsigned char[glwidth*glheight*4];
*/
}
void Font::dumpAs(const char* filename, size_t id) {
unsigned int len = chars[id]->width;
len *= charHeight;
palette.apply(len, chars[id]->rawData, workBuffer, true);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define rmask 0xff000000
#define gmask 0x00ff0000
#define bmask 0x0000ff00
#define amask 0x000000ff
#else
#define rmask 0x000000ff
#define gmask 0x0000ff00
#define bmask 0x00ff0000
#define amask 0xff000000
#endif
SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,
chars[id]->width, charHeight, 32, rmask, gmask, bmask, amask);
SDL_LockSurface(s);
unsigned char* dst = static_cast<unsigned char*>(s->pixels);
unsigned char * rp = workBuffer;
for (unsigned int i=0; i<len; i++) {
*dst = *rp; ++dst;++rp;
*dst = *rp; ++dst;++rp;
*dst = *rp; ++dst;++rp;
//*dst = 0xff; ++dst;
*dst = *rp; ++dst;++rp;
}
SDL_UnlockSurface(s);
SDL_SaveBMP(s, filename);
SDL_FreeSurface(s);
}
Font::Character::Character(PHYSFS_file *fd, uint8_t height) {
PHYSFS_read(fd, static_cast<void*>(&width), 1, 1);
int c = int(width);
c *= int(height);
//std::cout <<"width " << int(width) << " going to read " << c << " bytes" << std::endl;
rawData = new uint8_t[c];
PHYSFS_read(fd, static_cast<void*>(rawData), 1, c);
}
Font::Character::~Character() {
delete [] rawData;
}
void Font::loadMapping(const std::string & name) {
Util::ci_string name2(name.c_str());
#define chr(n) ((char)(n))
if (name2.find("big1.fon") != std::string::npos) {
INFO << "found mapping: big1.fon - " << name << std::endl;
addMapping('!', 0);
addMapping('-', 12);
for (int j = 65; j < 91; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 192; j < 195; j++) {
addMapping(chr(j), j - 97);
}
addMapping(196, 98);
addMapping(198, 99);
addMapping(199, 100);
for (int j=200; j < 208; j++)
addMapping(j, j - 99);
for (int j=210; j < 213; j++)
addMapping(j, j - 101);
addMapping(214, 112);
for (int j=217; j < 221; j++)
addMapping(j, j - 104);
addMapping(223, 117);
}
else if ((name2.find("pager1.fon") != std::string::npos) ||
(name2.find("pager2.fon") != std::string::npos)) {
addMapping('!', 0);
addMapping('"', 1);
addMapping('$', 3);
addMapping('\'', 6);
addMapping('(', 7);
addMapping(')', 8);
addMapping(',', 11);
addMapping('.', 13);
for (int j = 48; j < 58; j++) {
addMapping(chr(j), j - 33);
}
addMapping(':', 25);
addMapping(';', 26);
addMapping('<', 27);
addMapping('>', 29);
addMapping('?', 30);
addMapping('_', 62);
for (int j = 65; j < 91; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 192; j < 195; j++) {
addMapping(chr(j), j - 97);
}
addMapping(196, 98);
addMapping(198, 99);
addMapping(199, 100);
for (int j=200; j < 208; j++)
addMapping(j, j - 99);
for (int j=210; j < 213; j++)
addMapping(j, j - 101);
addMapping(214, 112);
for (int j=217; j < 221; j++)
addMapping(j, j - 104);
addMapping(223, 117);
}
else if (name2.find("street1.fon") != std::string::npos) {
INFO << "found mapping: street1.fon - " << name << std::endl;
for (int j = 65; j < 91; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 48; j < 58; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 97; j < 123; j++) {
addMapping(chr(j), j - 33);
}
WARN << "incomplete mapping" << std::endl;
}
else if ((name2.find("m_mmiss.fon") != std::string::npos)) {
addMapping('!', 0);
addMapping('"', 1);
addMapping('#', 2);
addMapping('$', 3);
addMapping('\'', 6);
addMapping('(', 7);
addMapping(')', 8);
addMapping('+', 10);
addMapping(',', 11);
addMapping('.', 13);
addMapping('/', 14);
addMapping(':', 25);
addMapping(';', 26);
addMapping('<', 27);
addMapping('=', 28);
addMapping('>', 29);
addMapping('?', 30);
addMapping('\\', 59);
addMapping('[', 58);
addMapping(']', 60);
addMapping('|', 91);
addMapping('~', 93);
for (int j = 65; j < 91; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 97; j < 123; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 48; j < 58; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 192; j < 195; j++) {
addMapping(chr(j), j - 97);
}
// incomplete
}
else if ((name2.find("f_mtext.fon") != std::string::npos)) {
addMapping('!', 0);
addMapping('"', 1);
addMapping('#', 2);
addMapping('$', 3);
addMapping('%', 4);
addMapping('\'', 6);
addMapping('(', 7);
addMapping(')', 8);
addMapping(169, 9); // copyright
addMapping(',', 11);
addMapping('-', 12);
addMapping('.', 13);
addMapping('/', 14);
addMapping(':', 25);
addMapping(';', 26);
addMapping('<', 27);
addMapping('=', 28);
addMapping('>', 29);
addMapping('?', 30);
for (int j = 48; j < 58; j++) {
addMapping(chr(j), j - 33);
}
addMapping('\\', 59);
for (int j = 65; j < 91; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 97; j < 123; j++) {
addMapping(chr(j), j - 33);
}
// incomplete
}
else if ((name2.find("f_mhead.fon") != std::string::npos)) {
addMapping('!', 0);
addMapping('"', 1);
addMapping('#', 2);
addMapping('$', 3);
addMapping('\'', 6);
addMapping('(', 7);
addMapping(')', 8);
addMapping(',', 11);
addMapping('.', 13);
addMapping('/', 14);
addMapping(':', 25);
addMapping(';', 26);
addMapping('<', 27);
addMapping('>', 29);
addMapping('?', 30);
addMapping('\\', 59);
for (int j = 65; j < 91; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 97; j < 123; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 48; j < 58; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 192; j < 195; j++) {
addMapping(chr(j), j - 97);
}
addMapping(196, 98);
addMapping(198, 99);
addMapping(199, 100);
for (int j=200; j < 208; j++)
addMapping(j, j - 99);
for (int j=210; j < 213; j++)
addMapping(j, j - 101);
addMapping(214, 112);
for (int j=217; j < 221; j++)
addMapping(j, j - 104);
addMapping(223, 117);
for (int j=224; j < 227; j++)
addMapping(j, j - 106);
addMapping(228, 121);
for (int j=230; j < 240; j++)
addMapping(j, j - 108);
for (int j=242; j < 245; j++)
addMapping(j, j - 110);
addMapping(246, 135);
for (int j=249; j < 253; j++)
addMapping(j, j - 113);
}
else if ((name2.find("sub1.fon") != std::string::npos) ||
(name2.find("sub2.fon") != std::string::npos)) {
addMapping('!', 0);
addMapping('"', 1);
addMapping('$', 3);
addMapping('\'', 6); // ´
addMapping('(', 7);
addMapping(')', 8);
addMapping(',', 11);
addMapping('-', 12); // not in street1/2
addMapping('.', 13);
addMapping('/', 14);
addMapping(':', 25);
addMapping(';', 26);
addMapping('<', 27);
addMapping('>', 29);
addMapping('?', 30);
for (int j = 65; j < 91; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 97; j < 123; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 48; j < 58; j++) {
addMapping(chr(j), j - 33);
}
for (int j = 192; j < 195; j++) {
addMapping(chr(j), j - 97);
}
addMapping(196, 98);
addMapping(198, 99);
addMapping(199, 100);
for (int j=200; j < 208; j++)
addMapping(j, j - 99);
for (int j=210; j < 213; j++)
addMapping(j, j - 101);
addMapping(214, 112);
for (int j=217; j < 221; j++)
addMapping(j, j - 104);
addMapping(223, 117);
for (int j=224; j < 227; j++)
addMapping(j, j - 106);
addMapping(228, 121);
for (int j=230; j < 240; j++)
addMapping(j, j - 108);
for (int j=242; j < 245; j++)
addMapping(j, j - 110);
addMapping(246, 135);
for (int j=249; j < 253; j++)
addMapping(j, j - 113);
}
else if ((name2.find("score1.fon") != std::string::npos)||
(name2.find("score2.fon") != std::string::npos) ||
(name2.find("score8.fon") != std::string::npos)) {
for (int j = 48; j < 58; j++) {
addMapping(chr(j), j - 48);
}
}
else {
ERROR << "mapping for font " << name << " is not known" << std::endl;
}
}
}
#ifdef FONT_DUMP_TOOL
#include <stdlib.h>
void do_exit() {
PHYSFS_deinit();
}
int main(int argc, char* argv[]) {
PHYSFS_init(argv[0]);
atexit(do_exit);
std::cout << "Physfs-Base: " << PHYSFS_getBaseDir() << std::endl;
PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1);
PHYSFS_addToSearchPath("gtadata.zip", 1);
std::cout << "Has: " << argv[1] << " : " << PHYSFS_exists(argv[1]) << std::endl;
OpenGTA::Font a(argv[1]);
a.dumpAs("out.bmp", atoi(argv[2]));
return 0;
}
#endif

135
read_fxt.cpp Normal file
View File

@ -0,0 +1,135 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This file contains code derived from information copyrighted by *
* DMA Design. It may not be used in a commercial product. *
* *
* See license.txt for details. *
* *
* This notice may not be removed or altered. *
************************************************************************/
#include <stdio.h>
#include <iostream>
#include "opengta.h"
namespace OpenGTA {
MessageDB::MessageDB() {
load("ENGLISH.FXT");
_error = "ERROR";
}
MessageDB::MessageDB(const std::string &file) {
load(file);
}
MessageDB::~MessageDB() {
messages.clear();
}
void MessageDB::load(const std::string &file) {
PHYSFS_file* f = PHYSFS_openRead(file.c_str());
if (f == NULL) {
std::string f2(file);
transform(f2.begin(), f2.end(), f2.begin(), tolower);
f = PHYSFS_openRead(f2.c_str());
}
if (f == NULL) {
std::cerr << "Error: could not open " << file << " for reading" << std::endl;
return;
}
messages.clear();
unsigned char v;
int c = -1;
unsigned char addVal = 0;
char buff[200];
int i = 0;
std::string tmp;
while (!PHYSFS_eof(f)) {
PHYSFS_read(f, static_cast<void*>(&v), 1, 1);
/* thanks to: Michael Mendelsohn
* http://gta.mendelsohn.de/
*/
v--; // decode: decrease by one
c++; // helper: count bytes read
if (c <= 7) // polyalphabetic code for the first 8 bytes
v -= (0x63 << c);
/* another twist: skip and add 64 (minus decoding) to next */
if (v == 195) {
addVal = 64;
}
else {
v += addVal;
addVal = 0;
if (v == '[') {
i = 0;
}
else if (v == ']') {
buff[i] = 0x00;
tmp = std::string(buff);
i = 0;
}
else if (v == 0x00) {
buff[i] = 0x00;
if (tmp.length() > 0)
messages[tmp] = std::string(buff);
//std::cout << tmp << " : " << buff << std::endl;
/*else
std::cout << "Skipping: " << tmp << ": " << buff << std::endl;*/
}
else {
buff[i] = v;
i++;
if (i>199)
i=0;
}
}
}
PHYSFS_close(f);
}
const std::string& MessageDB::getText(const char* id) {
std::map<std::string, std::string>::iterator i = messages.find(std::string(id));
if (i == messages.end()) {
std::cerr << "Error: string lookup failed for key: " << id << std::endl;
return _error;
}
return i->second;
}
const std::string& MessageDB::getText(const std::string &id) {
std::map<std::string, std::string>::iterator i = messages.find(id);
if (i == messages.end()) {
std::cerr << "Error: string lookup failed for key: " << id << std::endl;
return _error;
}
return i->second;
}
const std::string& MessageDB::getText(const uint32_t id) {
char tmp[10];
snprintf(reinterpret_cast<char*>(&tmp), 10, "%i", id);
std::map<std::string, std::string>::iterator i = messages.find(std::string(tmp));
if (i == messages.end()) {
std::cerr << "Error: string lookup failed for key: " << id << std::endl;
return _error;
}
return i->second;
}
}
#if FXT_TEST
int main(int argc, char* argv[]) {
PHYSFS_init(argv[0]);
PHYSFS_addToSearchPath("gtadata.zip", 1);
OpenGTA::MessageDB* strings = new OpenGTA::MessageDB();
std::cout << strings->getText(1001) << std::endl;
delete strings;
PHYSFS_deinit();
}
#endif

473
read_g24.cpp Normal file
View File

@ -0,0 +1,473 @@
/************************************************************************
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
* *
* This file contains code derived from information copyrighted by *
* DMA Design. It may not be used in a commercial product. *
* *
* See license.txt for details. *
* *
* This notice may not be removed or altered. *
************************************************************************/
#include <iostream>
#include <cassert>
#include "opengta.h"
#include "buffercache.h"
#include "log.h"
using namespace Util;
namespace OpenGTA {
#define GTA_GRAPHICS_GRX 290
#define GTA_GRAPHICS_GRY 325
#define GTA_GRAPHICS_G24 336
Graphics24Bit::Graphics24Bit(const std::string& style) : GraphicsBase() {
fd = PHYSFS_openRead(style.c_str());
assert(fd!=NULL);
_topHeaderSize = 64;
rawClut = NULL;
palIndex = NULL;
loadHeader();
setupBlocking(style);
}
Graphics24Bit::~Graphics24Bit() {
if (rawClut)
delete [] rawClut;
if (palIndex)
delete [] palIndex;
PHYSFS_close(fd);
}
void Graphics24Bit::loadHeader() {
PHYSFS_uint32 vc;
PHYSFS_readULE32(fd, &vc);
if(vc != GTA_GRAPHICS_G24) {
ERROR << "graphics file specifies version " << vc <<
" instead of " << GTA_GRAPHICS_G24 << std::endl;
return;
}
PHYSFS_readULE32(fd, &sideSize);
PHYSFS_readULE32(fd, &lidSize);
PHYSFS_readULE32(fd, &auxSize);
PHYSFS_readULE32(fd, &animSize);
PHYSFS_readULE32(fd, &clutSize);
PHYSFS_readULE32(fd, &tileclutSize);
PHYSFS_readULE32(fd, &spriteclutSize);
PHYSFS_readULE32(fd, &newcarclutSize);
PHYSFS_readULE32(fd, &fontclutSize);
PHYSFS_readULE32(fd, &paletteIndexSize);
PHYSFS_readULE32(fd, &objectInfoSize);
PHYSFS_readULE32(fd, &carInfoSize);
PHYSFS_readULE32(fd, &spriteInfoSize);
PHYSFS_readULE32(fd, &spriteGraphicsSize);
PHYSFS_readULE32(fd, &spriteNumberSize);
/*
INFO << "Version: " << vc << std::endl << " Block textures: S " << sideSize / 4096 << " L " <<
lidSize / 4096 << " A " << auxSize / 4096 << std::endl;
*/
if (sideSize % 4096 != 0) {
ERROR << "Side-Block texture size is not a multiple of 4096" << std::endl;
return;
}
if (lidSize % 4096 != 0) {
ERROR << "Lid-Block texture size is not a multiple of 4096" << std::endl;
return;
}
if (auxSize % 4096 != 0) {
ERROR << "Aux-Block texture size is not a multiple of 4096" << std::endl;
return;
}
PHYSFS_uint32 tmp = sideSize / 4096 + lidSize / 4096 + auxSize / 4096;
tmp = tmp % 4;
if (tmp) {
auxBlockTrailSize = (4 - tmp) * 4096;
INFO << "adjusting aux-block by " << auxBlockTrailSize << std::endl;
}
INFO << "Anim size: " << animSize << std::endl;
INFO << "Obj-info size: " << objectInfoSize << " car-size: " << carInfoSize <<
" sprite-info size: " << spriteInfoSize << " graphic size: " << spriteGraphicsSize <<
" numbers s: " << spriteNumberSize << std::endl;
if (spriteNumberSize != 42) {
ERROR << "spriteNumberSize is " << spriteNumberSize << " (should be 42)" << std::endl;
return;
}
INFO << " clut: " << clutSize << " tileclut: " << tileclutSize << " spriteclut: "<< spriteclutSize <<
" newcar: " << newcarclutSize << " fontclut: " << fontclutSize << std::endl <<
"Obj-info size: " << objectInfoSize << " car-size: " << carInfoSize <<
" pal-index size: " << paletteIndexSize <<
std::endl;
loadTileTextures();
loadAnim();
loadClut();
loadPalIndex();
loadObjectInfo();
loadCarInfo();
loadSpriteInfo();
loadSpriteGraphics();
loadSpriteNumbers();
}
void Graphics24Bit::loadClut() {
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize;
PHYSFS_seek(fd, st);
pagedClutSize = clutSize;
if (clutSize % 65536 != 0)
pagedClutSize += (65536 - (clutSize % 65536));
rawClut = new unsigned char[pagedClutSize];
assert(rawClut);
PHYSFS_read(fd, rawClut, 1, pagedClutSize);
//write(2, rawClut, pagedClutSize);
}
void Graphics24Bit::loadPalIndex() {
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
pagedClutSize;
PHYSFS_seek(fd, st);
PHYSFS_uint16 pal_index_count = paletteIndexSize / 2;
assert(paletteIndexSize % 2 == 0);
palIndex = new PHYSFS_uint16[pal_index_count];
for (PHYSFS_uint16 i = 0; i < pal_index_count; i++) {
PHYSFS_readULE16(fd, &palIndex[i]);
}
}
void Graphics24Bit::loadCarInfo() {
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
pagedClutSize + paletteIndexSize + objectInfoSize;
loadCarInfo_shared(st);
}
void Graphics24Bit::loadSpriteInfo() {
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
pagedClutSize + paletteIndexSize + objectInfoSize +
carInfoSize;
PHYSFS_seek(fd, st);
PHYSFS_uint8 v;
PHYSFS_uint32 w;
PHYSFS_uint32 _bytes_read = 0;
while (_bytes_read < spriteInfoSize) {
SpriteInfo *si = new SpriteInfo();
PHYSFS_read(fd, static_cast<void*>(&si->w), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&si->h), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&si->deltaCount), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&v), 1, 1);
PHYSFS_readULE16(fd, &si->size);
_bytes_read += 6;
PHYSFS_readULE16(fd, &si->clut);
PHYSFS_read(fd, static_cast<void*>(&si->xoffset), 1, 1);
PHYSFS_read(fd, static_cast<void*>(&si->yoffset), 1, 1);
PHYSFS_readULE16(fd, &si->page);
_bytes_read += 6;
/*
std::cout << "sprite: " << int(si->w) << "x" << int(si->h) << " deltas: " << int(si->deltaCount)
<< " clut: " << si->clut << " x: " << int(si->xoffset) << " y: " << int(si->yoffset) <<
" page: " << si->page << std::endl;
*/
// sanity check
if (v)
WARN << "Compression flag active in sprite!" << std::endl;
if (int(si->w) * int(si->h) != int(si->size)) {
ERROR << "Sprite info size mismatch: " << int(si->w) << "x" << int(si->h) <<
" != " << si->size << std::endl;
return;
}
if (si->deltaCount > 32) {
ERROR << "Delta count of sprite is " << si->deltaCount << std::endl;
return;
}
for (PHYSFS_uint8 j = 0; j < si->deltaCount; j++) {
si->delta[j].size = 0;
si->delta[j].ptr = 0;
if (si->deltaCount && (j < si->deltaCount)) {
PHYSFS_readULE16(fd, &si->delta[j].size);
PHYSFS_readULE32(fd, &w);
_bytes_read += 6;
si->delta[j].ptr = reinterpret_cast<unsigned char*>(w);
}
}
spriteInfos.push_back(si);
}
st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
pagedClutSize + paletteIndexSize + objectInfoSize +
carInfoSize + spriteInfoSize;
assert(PHYSFS_tell(fd) == PHYSFS_sint64(st));
}
void Graphics24Bit::loadSpriteNumbers() {
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
pagedClutSize + paletteIndexSize + objectInfoSize +
carInfoSize + spriteInfoSize + spriteGraphicsSize;
loadSpriteNumbers_shared(st);
}
void Graphics24Bit::loadSpriteGraphics() {
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
pagedClutSize + paletteIndexSize + objectInfoSize +
carInfoSize + spriteInfoSize;
PHYSFS_seek(fd, st);
rawSprites = new unsigned char[spriteGraphicsSize];
assert(rawSprites != NULL);
PHYSFS_read(fd, static_cast<void*>(rawSprites), spriteGraphicsSize, 1);
std::vector<SpriteInfo*>::const_iterator i = spriteInfos.begin();
std::vector<SpriteInfo*>::const_iterator end = spriteInfos.end();
PHYSFS_uint32 _pagewise = 256 * 256;
while (i != end) {
SpriteInfo *info = *i;
for (uint8_t k = 0; k < info->deltaCount; ++k) {
PHYSFS_uint32 offset = reinterpret_cast<PHYSFS_uint32>(info->delta[k].ptr);
PHYSFS_uint32 page = offset / 65536;
PHYSFS_uint32 y = (offset % 65536) / 256;
PHYSFS_uint32 x = (offset % 65536) % 256;
info->delta[k].ptr = rawSprites + page * _pagewise + 256 * y + x;
}
i++;
}
}
void Graphics24Bit::loadObjectInfo() {
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
pagedClutSize + paletteIndexSize;
loadObjectInfo_shared(st);
}
void Graphics24Bit::applyClut(unsigned char* src, unsigned char* dst,
const size_t & len, const PHYSFS_uint16 & clutIdx, bool rgba) {
PHYSFS_uint32 off = 65536 * (clutIdx / 64) + 4 * (clutIdx % 64);
for (size_t i= 0; i < len; i++) {
PHYSFS_uint32 coff = PHYSFS_uint32(*src) * 256 + off;
*dst = rawClut[coff+2];
++dst;
*dst = rawClut[coff+1];
++dst;
*dst = rawClut[coff+0];
++dst;
if (rgba) {
if (*src == 0)
*dst = 0;
else
*dst = 0xff;
++dst;
}
++src;
}
}
unsigned char *Graphics24Bit::getLid(unsigned int idx, unsigned int _not_used, bool rgba = false) {
prepareLidTexture(idx - 1, reinterpret_cast<unsigned char*>(tileTmp));
unsigned char* src = tileTmp;
unsigned char* dst = (rgba) ? tileTmpRGBA : tileTmpRGB;
PHYSFS_uint16 clutIdx = palIndex[4 * (idx + sideSize / 4096)];
applyClut(src, dst, 4096, clutIdx, rgba);
return (rgba) ? tileTmpRGBA : tileTmpRGB;
}
unsigned char *Graphics24Bit::getSide(unsigned int idx, unsigned int _not_used, bool rgba = false) {
prepareSideTexture(idx-1, reinterpret_cast<unsigned char*>(tileTmp));
unsigned char* src = tileTmp;
unsigned char* dst = (rgba) ? tileTmpRGBA : tileTmpRGB;
PHYSFS_uint16 clutIdx = palIndex[idx*4];
applyClut(src, dst, 4096, clutIdx, rgba);
return (rgba) ? tileTmpRGBA : tileTmpRGB;
}
unsigned char *Graphics24Bit::getAux(unsigned int idx, unsigned int _not_used, bool rgba = false) {
prepareAuxTexture(idx - 1, reinterpret_cast<unsigned char*>(tileTmp));
unsigned char* src = tileTmp;
unsigned char* dst = (rgba) ? tileTmpRGBA : tileTmpRGB;
PHYSFS_uint16 clutIdx = palIndex[4 * (idx + sideSize / 4096 + lidSize / 4096)];
applyClut(src, dst, 4096, clutIdx, rgba);
return (rgba) ? tileTmpRGBA : tileTmpRGB;
}
void Graphics24Bit::dumpClut(const char* fname) {
assert(pagedClutSize % 1024 == 0);
//PHYSFS_uint32 num_clut = pagedClutSize / 1024;
PHYSFS_uint32 num_pal = paletteIndexSize / 2;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define rmask 0xff000000
#define gmask 0x00ff0000
#define bmask 0x0000ff00
#define amask 0x000000ff
#else
#define rmask 0x000000ff
#define gmask 0x0000ff00
#define bmask 0x00ff0000
#define amask 0xff000000
#endif
SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,
num_pal, 256, 32, rmask, gmask, bmask, amask);
SDL_LockSurface(s);
unsigned char* dst = static_cast<unsigned char*>(s->pixels);
for (PHYSFS_uint32 color = 0; color < 256; color++) {
for (PHYSFS_uint32 pal_id = 0; pal_id < num_pal; pal_id++) {
PHYSFS_uint32 clut_id = palIndex[pal_id];
PHYSFS_uint32 off = 65536 * (clut_id / 64) + 4 * (clut_id % 64);
*dst = rawClut[off+color*256];
++dst;
*dst = rawClut[off+color*256+1];
++dst;
*dst = rawClut[off+color*256+2];
++dst;
*dst = 0xff;
++dst;
}
}
SDL_UnlockSurface(s);
SDL_SaveBMP(s, fname);
SDL_FreeSurface(s);
}
unsigned char* Graphics24Bit::getSpriteBitmap(size_t id, int remap = -1, Uint32 delta = 0) {
const SpriteInfo *info = spriteInfos[id];
assert(info != NULL);
const PHYSFS_uint32 y = info->yoffset;
const PHYSFS_uint32 x = info->xoffset;
const PHYSFS_uint32 page_size = 256 * 256;
unsigned char * page_start = rawSprites + info->page * page_size;
BufferCache & bcache = BufferCacheHolder::Instance();
unsigned char * dest = bcache.requestBuffer(page_size);
bcache.lockBuffer(dest);
memcpy(dest, page_start, page_size);
if (delta > 0) {
handleDeltas(*info, dest, delta);
/*
assert(delta < info->deltaCount);
DeltaInfo & di = info->delta[delta];
applyDelta(*info, dest+256*y+x, di);
*/
}
unsigned char* bigbuf = bcache.requestBuffer(page_size * 4);
unsigned char* result = dest;
unsigned int skip_cluts = 0;
if (remap > -1)
skip_cluts = spriteclutSize / 1024 + remap + 1;
PHYSFS_uint16 clutIdx = palIndex[info->clut + tileclutSize / 1024] + skip_cluts;
// PHYSFS_uint16 clutIdx = palIndex[info->clut + (spriteclutSize + tileclutSize) / 1024] + (remap > -1 ? remap+2 : 0);
applyClut(dest, bigbuf, page_size, clutIdx, true);
assert(page_size > PHYSFS_uint32(info->w * info->h * 4));
for (uint16_t i = 0; i < info->h; i++) {
memcpy(result, bigbuf+(256*y+x)*4, info->w * 4);
result += info->w * 4;
bigbuf += 256 * 4;
}
return dest;
}
}
#ifdef G24_DUMPER
SDL_Surface* image = NULL;
void on_exit() {
if (image)
SDL_FreeSurface(image);
PHYSFS_deinit();
SDL_Quit();
}
SDL_Surface* get_image(unsigned char* rp, unsigned int w, unsigned int h) {
assert(rp);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define rmask 0xff000000
#define gmask 0x00ff0000
#define bmask 0x0000ff00
#define amask 0x000000ff
#else
#define rmask 0x000000ff
#define gmask 0x0000ff00
#define bmask 0x00ff0000
#define amask 0xff000000
#endif
SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,
w, h, 32, rmask, gmask, bmask, amask);
SDL_LockSurface(s);
unsigned char* dst = static_cast<unsigned char*>(s->pixels);
for (unsigned int i=0; i<w*h; i++) {
*dst = *rp; ++dst;++rp;
*dst = *rp; ++dst;++rp;
*dst = *rp; ++dst;++rp;
*dst = *rp; ++dst;++rp;
//*dst = 0xff; ++dst;
}
SDL_UnlockSurface(s);
return s;
}
void display_image(SDL_Surface* s) {
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, SDL_DOUBLEBUF);
SDL_Event event;
SDL_BlitSurface(s, NULL, screen, NULL);
SDL_Flip(screen);
while (1) {
while (SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_QUIT:
return;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE)
return;
default:
break;
}
}
SDL_Delay(100);
}
}
int main(int argc, char* argv[]) {
PHYSFS_init(argv[0]);
SDL_Init(SDL_INIT_VIDEO);
atexit(on_exit);
int idx = 0;
PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1);
PHYSFS_addToSearchPath("gtadata.zip", 1);
OpenGTA::Graphics24Bit graphics(argv[1]);
graphics.dumpClut("foo.bmp");
if (argc > 2)
idx = atoi(argv[2]);
//image = get_image(graphics.getAux(idx, 0, true), 64,64);
OpenGTA::GraphicsBase::SpriteInfo * sinfo = graphics.getSprite(idx);
image = get_image(graphics.getSpriteBitmap(idx, -1, 0), sinfo->w, sinfo->h);
if (argc == 4)
SDL_SaveBMP(image, argv[3]);
else
display_image(image);
return 0;
}
#endif

1009
read_gry.cpp Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More