added everything

This commit is contained in:
Metario
2017-04-17 06:17:10 -06:00
commit 9c6ff74f19
6121 changed files with 1625704 additions and 0 deletions

121
engine/interior/floorPlanRes.cc Executable file
View File

@ -0,0 +1,121 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "core/stream.h"
#include "interior/floorPlanRes.h"
#include "math/mathIO.h"
const U32 FloorPlanResource::smFileVersion = 0;
FloorPlanResource::FloorPlanResource()
{
}
FloorPlanResource::~FloorPlanResource()
{
}
//--------------------------------------------------------------------------
// Define these so we can just write two vector IO functions
static bool mathRead(Stream & S, FloorPlanResource::Area * a){
return S.read(&a->pointCount) && S.read(&a->pointStart) && S.read(&a->plane);
}
static bool mathWrite(Stream & S, const FloorPlanResource::Area & a){
return S.write(a.pointCount) && S.write(a.pointStart) && S.write(a.plane);
}
inline bool mathRead(Stream & S, S32 * s) { return S.read(s); }
inline bool mathWrite(Stream & S, S32 s) { return S.write(s); }
//--------------------------------------------------------------------------
// Read a vector of items which define mathRead().
template <class T>
bool mathReadVector(Vector<T> & vec, Stream & stream, const char * msg)
{
U32 num, i;
bool Ok = true;
stream.read( & num );
vec.setSize( num );
for( i = 0; i < num && Ok; i++ ){
Ok = mathRead(stream, & vec[i]);
AssertISV( Ok, avar("math vec read error (%s) on elem %d", msg, i) );
}
return Ok;
}
// Write a vector of items which define mathWrite().
template <class T>
bool mathWriteVector(const Vector<T> & vec, Stream & stream, const char * msg)
{
bool Ok = true;
stream.write( vec.size() );
for( U32 i = 0; i < vec.size() && Ok; i++ ) {
Ok = mathWrite(stream, vec[i]);
AssertISV( Ok, avar("math vec write error (%s) on elem %d", msg, i) );
}
return Ok;
}
//--------------------------------------------------------------------------
bool FloorPlanResource::read(Stream& stream)
{
AssertFatal(stream.hasCapability(Stream::StreamRead), "FLR::read: non-readable stream");
AssertFatal(stream.getStatus() == Stream::Ok, "FLR::read: Error, weird stream state");
// Version this stream
U32 fileVersion, DohVal;
stream.read(&fileVersion);
if (fileVersion != smFileVersion) {
AssertFatal(false, "FLR::read: incompatible file version found.");
return false;
}
// For expansion purposes
stream.read(&DohVal); stream.read(&DohVal); stream.read(&DohVal);
// Read the vectors
mathReadVector( mPlaneTable, stream, "FLR: mPlaneTable" );
mathReadVector( mPointTable, stream, "FLR: mPointTable" );
mathReadVector( mPointLists, stream, "FLR: mPointLists" );
mathReadVector( mAreas, stream, "FLR: mAreas" );
return (stream.getStatus() == Stream::Ok);
}
//--------------------------------------------------------------------------
bool FloorPlanResource::write(Stream& stream) const
{
AssertFatal(stream.hasCapability(Stream::StreamWrite), "FLR::write: non-writeable stream");
AssertFatal(stream.getStatus() == Stream::Ok, "FLR::write: Error, weird stream state");
// Version the stream
stream.write(smFileVersion);
U32 Doh = 0xD0bD0b; // So we don't later say Doh!
stream.write(Doh); stream.write(Doh); stream.write(Doh);
// Write the vectors
mathWriteVector( mPlaneTable, stream, "FLR: mPlaneTable" );
mathWriteVector( mPointTable, stream, "FLR: mPointTable" );
mathWriteVector( mPointLists, stream, "FLR: mPointLists" );
mathWriteVector( mAreas, stream, "FLR: mAreas" );
return( stream.getStatus() == Stream::Ok );
}
//------------------------------------------------------------------------------
// FloorPlan Resource constructor
//
ResourceInstance * constructFloorPlanFLR(Stream& stream)
{
FloorPlanResource * pResource = new FloorPlanResource;
if (pResource->read(stream) == true)
return pResource;
else {
delete pResource;
return NULL;
}
}

50
engine/interior/floorPlanRes.h Executable file
View File

@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _FLOORPLANRES_H_
#define _FLOORPLANRES_H_
#ifndef _RESMANAGER_H_
#include "core/resManager.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
#ifndef _MPLANE_H_
#include "math/mPlane.h"
#endif
class Stream;
class FloorPlanResource : public ResourceInstance
{
typedef ResourceInstance Parent;
static const U32 smFileVersion;
public:
struct Area // basically a Winding, the info we need from it
{
S16 pointCount;
S32 pointStart;
S32 plane;
Area(S16 C, S32 S, S32 P) { pointCount=C; pointStart=S; plane=P; }
};
protected:
Vector<PlaneF> mPlaneTable;
Vector<Point3F> mPointTable;
Vector<S32> mPointLists;
Vector<Area> mAreas;
public:
FloorPlanResource();
~FloorPlanResource();
bool read(Stream& stream);
bool write(Stream& stream) const;
};
extern ResourceInstance * constructFloorPlanFLR(Stream& stream);
#endif // _H_FLOORPLANRES_

72
engine/interior/fogCalc.h Executable file
View File

@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef __FOG_CALC_H__
#define __FOG_CALC_H__
#include "sceneGraph/sceneState.h"
#include "sceneGraph/sceneGraph.h"
// This class is a fancy inlined implementation of the fog calculations with all invariants moved
// into the constructor.
class FogCalc
{
public:
FogCalc::FogCalc( const PlaneF &dPlane, F32 distOffset, const Point3F &zVec, F32 worldZ, F32 worldPz, const SceneState *state )
: fc_distOffset( distOffset ),
fc_newWorldZ( worldZ - worldPz ),
fc_fogDistance( state->getFogDistance() ),
fc_visibleDistance( state->getVisibleDistance() ),
fc_fogScale( state->getFogScale() ),
fc_PosFogBands( state->getPosFogBands() ),
fc_NegFogBands( state->getNegFogBands() ),
distPlane( dPlane ),
osZVec( zVec ),
sState( state )
{
// For Textured
F32 heightOffset;
gClientSceneGraph->getFogCoordData( tex_invVisibleDistance, heightOffset, tex_invHeightRange );
tex_visibleDistanceMod = gClientSceneGraph->getVisibleDistanceMod() - distOffset - distPlane.d;
tex_newWorldZ = worldZ - heightOffset;
}
inline F32 calcFC( const Point3F &point ) const
{
return( sState->getHazeAndFog(mFabs(distPlane.distToPlane(point)) + fc_distOffset, (mDot(point, osZVec) + fc_newWorldZ) ) );
}
inline const Point2F calcTextured( const Point3F &point ) const
{
// inline version of SceneGraph::getFogCoordPair
return( Point2F(
(tex_visibleDistanceMod - (point.x * distPlane.x + point.y * distPlane.y + point.z * distPlane.z)) * tex_invVisibleDistance,
(tex_newWorldZ + point.x * osZVec.x + point.y * osZVec.y + point.z * osZVec.z) * tex_invHeightRange
) );
}
private:
const F32 fc_distOffset;
const F32 fc_newWorldZ;
const F32 fc_fogDistance;
const F32 fc_visibleDistance;
const F32 fc_fogScale;
const Vector<SceneState::FogBand> *fc_PosFogBands;
const Vector<SceneState::FogBand> *fc_NegFogBands;
F32 tex_invVisibleDistance;
F32 tex_invHeightRange;
F32 tex_visibleDistanceMod;
F32 tex_newWorldZ;
const PlaneF distPlane;
const Point3F osZVec;
const SceneState *sState;
};
#endif //__OK_FOG_CALC_H__

469
engine/interior/forceField.cc Executable file
View File

@ -0,0 +1,469 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "interior/forceField.h"
#include "core/stream.h"
#include "math/mathIO.h"
#include "console/console.h"
#include "dgl/gTexManager.h"
#include "dgl/dgl.h"
#include "collision/abstractPolyList.h"
#include "sim/sceneObject.h"
//--------------------------------------------------------------------------
ForceField::ForceField()
{
mPreppedForRender = false;
mWhite = NULL;
}
ForceField::~ForceField()
{
mPreppedForRender = false;
delete mWhite;
mWhite = NULL;
}
bool ForceField::prepForRendering()
{
if (mPreppedForRender == true)
return true;
mPreppedForRender = true;
return true;
}
void ForceField::render(const ColorF& rColor, const F32 fade)
{
// All our transform what not has already been specified...
glDisable(GL_TEXTURE_2D);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glVertexPointer(3, GL_FLOAT, sizeof(Point3F), mPoints.address());
glEnable(GL_VERTEX_ARRAY);
for (U32 i = 0; i < mSurfaces.size(); i++) {
Surface& rSurface = mSurfaces[i];
glColor4f(rColor.red, rColor.green, rColor.blue, fade);
glBegin(GL_TRIANGLE_STRIP);
for (U32 j = rSurface.windingStart; j < rSurface.windingStart + rSurface.windingCount; j++)
glArrayElement(mWindings[j]);
glEnd();
}
glDisable(GL_VERTEX_ARRAY);
glDisable(GL_CULL_FACE);
}
//--------------------------------------------------------------------------
//-------------------------------------- Persistence interfaces
//
const U32 ForceField::smFileVersion = 0;
bool ForceField::read(Stream& stream)
{
AssertFatal(stream.hasCapability(Stream::StreamRead), "ForceField::read: non-read capable stream passed");
AssertFatal(stream.getStatus() == Stream::Ok, "ForceField::read: Error, stream in inconsistent state");
U32 i;
// Version this stream
U32 fileVersion;
stream.read(&fileVersion);
if (fileVersion != smFileVersion) {
Con::errorf(ConsoleLogEntry::General, "ForceField::read: incompatible file version found.");
return false;
}
mName = stream.readSTString();
U32 numTriggers;
stream.read(&numTriggers);
mTriggers.setSize(numTriggers);
for (i = 0; i < mTriggers.size(); i++)
mTriggers[i] = stream.readSTString();
// Geometry factors...
mathRead(stream, &mBoundingBox);
mathRead(stream, &mBoundingSphere);
// Now read in our data vectors.
U32 vectorSize;
// mPlanes
readPlaneVector(stream);
// mPoints
stream.read(&vectorSize);
mPoints.setSize(vectorSize);
for (i = 0; i < mPoints.size(); i++)
mathRead(stream, &mPoints[i]);
// mBSPNodes;
stream.read(&vectorSize);
mBSPNodes.setSize(vectorSize);
for (i = 0; i < mBSPNodes.size(); i++) {
stream.read(&mBSPNodes[i].planeIndex);
stream.read(&mBSPNodes[i].frontIndex);
stream.read(&mBSPNodes[i].backIndex);
}
// mBSPSolidLeaves
stream.read(&vectorSize);
mBSPSolidLeaves.setSize(vectorSize);
for (i = 0; i < mBSPSolidLeaves.size(); i++) {
stream.read(&mBSPSolidLeaves[i].surfaceIndex);
stream.read(&mBSPSolidLeaves[i].surfaceCount);
}
// mWindings
stream.read(&vectorSize);
mWindings.setSize(vectorSize);
for (i = 0; i < mWindings.size(); i++) {
stream.read(&mWindings[i]);
}
// mSurfaces
stream.read(&vectorSize);
mSurfaces.setSize(vectorSize);
for (i = 0; i < mSurfaces.size(); i++) {
stream.read(&mSurfaces[i].windingStart);
stream.read(&mSurfaces[i].windingCount);
stream.read(&mSurfaces[i].planeIndex);
stream.read(&mSurfaces[i].surfaceFlags);
stream.read(&mSurfaces[i].fanMask);
}
// mSolidLeafSurfaces
stream.read(&vectorSize);
mSolidLeafSurfaces.setSize(vectorSize);
for (i = 0; i < mSolidLeafSurfaces.size(); i++) {
stream.read(&mSolidLeafSurfaces[i]);
}
stream.read(&mColor);
return stream.getStatus() == Stream::Ok;
}
bool ForceField::write(Stream& stream) const
{
AssertFatal(stream.hasCapability(Stream::StreamWrite), "Interior::write: non-write capable stream passed");
AssertFatal(stream.getStatus() == Stream::Ok, "Interior::write: Error, stream in inconsistent state");
U32 i;
// Version this stream
stream.write(smFileVersion);
stream.writeString(mName);
stream.write(mTriggers.size());
for (i = 0; i < mTriggers.size(); i++)
stream.writeString(mTriggers[i]);
mathWrite(stream, mBoundingBox);
mathWrite(stream, mBoundingSphere);
// Now write out our data vectors. Remember, for cross-platform capability, no
// structure writing is allowed...
// mPlanes
writePlaneVector(stream);
// mPoints
stream.write(mPoints.size());
for (i = 0; i < mPoints.size(); i++)
mathWrite(stream, mPoints[i]);
// mBSPNodes;
stream.write(mBSPNodes.size());
for (i = 0; i < mBSPNodes.size(); i++) {
stream.write(mBSPNodes[i].planeIndex);
stream.write(mBSPNodes[i].frontIndex);
stream.write(mBSPNodes[i].backIndex);
}
// mBSPSolidLeaves
stream.write(mBSPSolidLeaves.size());
for (i = 0; i < mBSPSolidLeaves.size(); i++) {
stream.write(mBSPSolidLeaves[i].surfaceIndex);
stream.write(mBSPSolidLeaves[i].surfaceCount);
}
// mWindings
stream.write(mWindings.size());
for (i = 0; i < mWindings.size(); i++) {
stream.write(mWindings[i]);
}
// mSurfaces
stream.write(mSurfaces.size());
for (i = 0; i < mSurfaces.size(); i++) {
stream.write(mSurfaces[i].windingStart);
stream.write(mSurfaces[i].windingCount);
stream.write(mSurfaces[i].planeIndex);
stream.write(mSurfaces[i].surfaceFlags);
stream.write(mSurfaces[i].fanMask);
}
// mSolidLeafSurfaces
stream.write(mSolidLeafSurfaces.size());
for (i = 0; i < mSolidLeafSurfaces.size(); i++) {
stream.write(mSolidLeafSurfaces[i]);
}
stream.write(mColor);
return stream.getStatus() == Stream::Ok;
}
bool ForceField::writePlaneVector(Stream& stream) const
{
// This is pretty slow, but who cares?
//
Vector<Point3F> uniqueNormals(mPlanes.size());
Vector<U16> uniqueIndices(mPlanes.size());
U32 i;
for (i = 0; i < mPlanes.size(); i++) {
bool inserted = false;
for (U32 j = 0; j < uniqueNormals.size(); j++) {
if (mPlanes[i] == uniqueNormals[j]) {
// Hah! Already have this one...
uniqueIndices.push_back(j);
inserted = true;
break;
}
}
if (inserted == false) {
// Gotta do it ourselves...
uniqueIndices.push_back(uniqueNormals.size());
uniqueNormals.push_back(Point3F(mPlanes[i].x, mPlanes[i].y, mPlanes[i].z));
}
}
// Ok, what we have now, is a list of unique normals, a set of indices into
// that vector, and the distances that we still have to write out by hand.
// Hop to it!
stream.write(uniqueNormals.size());
for (i = 0; i < uniqueNormals.size(); i++)
mathWrite(stream, uniqueNormals[i]);
stream.write(mPlanes.size());
for (i = 0; i < mPlanes.size(); i++) {
stream.write(uniqueIndices[i]);
stream.write(mPlanes[i].d);
}
return (stream.getStatus() == Stream::Ok);
}
bool ForceField::readPlaneVector(Stream& stream)
{
Vector<Point3F> normals;
U32 vectorSize;
stream.read(&vectorSize);
normals.setSize(vectorSize);
U32 i;
for (i = 0; i < normals.size(); i++)
mathRead(stream, &normals[i]);
U16 index;
stream.read(&vectorSize);
mPlanes.setSize(vectorSize);
for (i = 0; i < mPlanes.size(); i++) {
stream.read(&index);
stream.read(&mPlanes[i].d);
mPlanes[i].x = normals[index].x;
mPlanes[i].y = normals[index].y;
mPlanes[i].z = normals[index].z;
}
return (stream.getStatus() == Stream::Ok);
}
//--------------------------------------------------------------------------
//-------------------------------------- Collision support. Essentially
// copied from the interiorCollision
//
void ForceField::collisionFanFromSurface(const Surface& rSurface, U32* fanIndices, U32* numIndices) const
{
U32 tempIndices[32];
tempIndices[0] = 0;
U32 idx = 1;
U32 i;
for (i = 1; i < rSurface.windingCount; i += 2)
tempIndices[idx++] = i;
for (i = ((rSurface.windingCount - 1) & (~0x1)); i > 0; i -= 2)
tempIndices[idx++] = i;
idx = 0;
for (i = 0; i < rSurface.windingCount; i++) {
if (rSurface.fanMask & (1 << i)) {
fanIndices[idx++] = mWindings[rSurface.windingStart + tempIndices[i]];
}
}
*numIndices = idx;
}
bool ForceField::castRay(const Point3F& s, const Point3F& e, RayInfo* info)
{
bool hit = castRay_r(0, s, e, info);
if (hit) {
Point3F vec = e - s;
F32 len = vec.len();
vec /= len;
info->t = mDot(info->point - s, vec) / len;
}
return hit;
}
bool ForceField::castRay_r(const U16 node,
const Point3F& s,
const Point3F& e,
RayInfo* info)
{
if (isBSPLeafIndex(node) == false) {
const IBSPNode& rNode = mBSPNodes[node];
const PlaneF& rPlane = getPlane(rNode.planeIndex);
PlaneF::Side sSide = rPlane.whichSide(s);
PlaneF::Side eSide = rPlane.whichSide(e);
switch (PlaneSwitchCode(sSide, eSide)) {
case PlaneSwitchCode(PlaneF::Front, PlaneF::Front):
case PlaneSwitchCode(PlaneF::Front, PlaneF::On):
case PlaneSwitchCode(PlaneF::On, PlaneF::Front):
return castRay_r(rNode.frontIndex, s, e, info);
break;
case PlaneSwitchCode(PlaneF::On, PlaneF::Back):
case PlaneSwitchCode(PlaneF::Back, PlaneF::On):
case PlaneSwitchCode(PlaneF::Back, PlaneF::Back):
return castRay_r(rNode.backIndex, s, e, info);
break;
case PlaneSwitchCode(PlaneF::On, PlaneF::On):
// Line lies on the plane
if (isBSPLeafIndex(rNode.backIndex) == false) {
if (castRay_r(rNode.backIndex, s, e, info))
return true;
}
if (isBSPLeafIndex(rNode.frontIndex) == false) {
if (castRay_r(rNode.frontIndex, s, e, info))
return true;
}
return false;
break;
case PlaneSwitchCode(PlaneF::Front, PlaneF::Back): {
Point3F ip;
F32 intersectT = rPlane.intersect(s, e);
AssertFatal(intersectT != PARALLEL_PLANE, "Error, this should never happen in this case!");
ip.interpolate(s, e, intersectT);
if (castRay_r(rNode.frontIndex, s, ip, info))
return true;
return castRay_r(rNode.backIndex, ip, e, info);
}
break;
case PlaneSwitchCode(PlaneF::Back, PlaneF::Front): {
Point3F ip;
F32 intersectT = rPlane.intersect(s, e);
AssertFatal(intersectT != PARALLEL_PLANE, "Error, this should never happen in this case!");
ip.interpolate(s, e, intersectT);
if (castRay_r(rNode.backIndex, s, ip, info))
return true;
return castRay_r(rNode.frontIndex, ip, e, info);
}
break;
default:
AssertFatal(false, "Misunderstood switchCode in ForceField::castRay_r");
return false;
}
}
if (isBSPSolidLeaf(node)) {
// DMM: Set material info here
info->point = s;
return true;
}
return false;
}
void ForceField::buildPolyList_r(const U16 node, Vector<U16>& collPlanes, AbstractPolyList* list, SphereF& s)
{
if (isBSPLeafIndex(node) == false) {
const IBSPNode& rNode = mBSPNodes[node];
const PlaneF& rPlane = getPlane(rNode.planeIndex);
F32 dist = rPlane.distToPlane(s.center);
if (mFabs(dist) <= s.radius) {
// Have to do both, and push the plane back on the list...
collPlanes.push_back(rNode.planeIndex);
buildPolyList_r(rNode.frontIndex, collPlanes, list, s);
buildPolyList_r(rNode.backIndex, collPlanes, list, s);
collPlanes.pop_back();
} else if (dist > 0.0f) {
buildPolyList_r(rNode.frontIndex, collPlanes, list, s);
} else {
buildPolyList_r(rNode.backIndex, collPlanes, list, s);
}
return;
}
if (isBSPSolidLeaf(node)) {
const IBSPLeafSolid& rLeaf = mBSPSolidLeaves[getBSPSolidLeafIndex(node)];
for (U32 i = 0; i < rLeaf.surfaceCount; i++) {
U32 surfaceIndex = mSolidLeafSurfaces[rLeaf.surfaceIndex + i];
const Surface& rSurface = mSurfaces[surfaceIndex];
for (U32 j = 0; j < collPlanes.size(); j++) {
if (areEqualPlanes(rSurface.planeIndex, collPlanes[j]) == true) {
U32 fanVerts[32];
U32 numVerts;
collisionFanFromSurface(rSurface, fanVerts, &numVerts);
// DMM: Material here
list->begin(0, rSurface.planeIndex);
U32 vertStart = list->addPoint(mPoints[fanVerts[0]]);
list->vertex(vertStart);
for (U32 k = 1; k < numVerts; k++) {
list->addPoint(mPoints[fanVerts[k]]);
list->vertex(vertStart + k);
}
list->plane(vertStart, vertStart + 1, vertStart + 2);
list->end();
break;
}
}
}
}
}
bool ForceField::buildPolyList(AbstractPolyList* list, SphereF& sphere)
{
Vector<U16> planes;
buildPolyList_r(0, planes, list, sphere);
AssertFatal(planes.size() == 0, "Error, unbalanced plane stack!");
return !list->isEmpty();
}

180
engine/interior/forceField.h Executable file
View File

@ -0,0 +1,180 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _FORCEFIELD_H_
#define _FORCEFIELD_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _MBOX_H_
#include "math/mBox.h"
#endif
#ifndef _MSPHERE_H_
#include "math/mSphere.h"
#endif
#ifndef _MPLANE_H_
#include "math/mPlane.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
//-------------------------------------- forward decls.
class EditGeometry;
class InteriorInstance;
class Stream;
class TextureHandle;
class AbstractPolyList;
struct RayInfo;
//--------------------------------------------------------------------------
class ForceField
{
static const U32 smFileVersion;
friend class EditGeometry;
friend class InteriorInstance;
//-------------------------------------- Public interfaces
public:
ForceField();
~ForceField();
bool prepForRendering();
void render(const ColorF& color, const F32 fadeLevel);
const Box3F& getBoundingBox() const;
bool castRay(const Point3F&, const Point3F&, RayInfo*);
bool buildPolyList(AbstractPolyList*, SphereF&);
//-------------------------------------- Persistence interface
public:
bool read(Stream& stream);
bool write(Stream& stream) const;
public:
static U16 getPlaneIndex(U16 index);
static bool planeIsFlipped(U16 index);
//-------------------------------------- BSP Structures
private:
struct IBSPNode {
U16 planeIndex;
U16 frontIndex;
U16 backIndex;
U16 __padding__;
};
struct IBSPLeafSolid {
U32 surfaceIndex;
U16 surfaceCount;
U16 __padding__;
};
bool isBSPLeafIndex(U16 index) const;
bool isBSPSolidLeaf(U16 index) const;
bool isBSPEmptyLeaf(U16 index) const;
U16 getBSPSolidLeafIndex(U16 index) const;
bool writePlaneVector(Stream&) const;
bool readPlaneVector(Stream&);
private:
const PlaneF& getPlane(U16 index) const;
bool areEqualPlanes(U16, U16) const;
struct Surface {
U32 windingStart;
U32 fanMask;
U16 planeIndex;
U8 windingCount;
U8 surfaceFlags;
};
protected:
StringTableEntry mName;
ColorF mColor;
Vector<StringTableEntry> mTriggers;
Box3F mBoundingBox;
SphereF mBoundingSphere;
Vector<PlaneF> mPlanes;
Vector<Point3F> mPoints;
Vector<IBSPNode> mBSPNodes;
Vector<IBSPLeafSolid> mBSPSolidLeaves;
Vector<U32> mSolidLeafSurfaces;
bool mPreppedForRender;
TextureHandle* mWhite;
Vector<U32> mWindings;
Vector<Surface> mSurfaces;
protected:
bool castRay_r(const U16, const Point3F&, const Point3F&, RayInfo*);
void buildPolyList_r(const U16, Vector<U16>&, AbstractPolyList*, SphereF&);
void collisionFanFromSurface(const Surface&, U32* fan, U32* numIndices) const;
};
//------------------------------------------------------------------------------
inline bool ForceField::isBSPLeafIndex(U16 index) const
{
return (index & 0x8000) != 0;
}
inline bool ForceField::isBSPSolidLeaf(U16 index) const
{
AssertFatal(isBSPLeafIndex(index) == true, "Error, only call for leaves!");
return (index & 0x4000) != 0;
}
inline bool ForceField::isBSPEmptyLeaf(U16 index) const
{
AssertFatal(isBSPLeafIndex(index) == true, "Error, only call for leaves!");
return (index & 0x4000) == 0;
}
inline U16 ForceField::getBSPSolidLeafIndex(U16 index) const
{
AssertFatal(isBSPSolidLeaf(index) == true, "Error, only call for leaves!");
return (index & ~0xC000);
}
inline const PlaneF& ForceField::getPlane(U16 index) const
{
AssertFatal(U32(index & ~0x8000) < mPlanes.size(),
"ForceField::getPlane: planeIndex out of range");
return mPlanes[index & ~0x8000];
}
inline U16 ForceField::getPlaneIndex(U16 index)
{
return index & ~0x8000;
}
inline bool ForceField::planeIsFlipped(U16 index)
{
return (index & 0x8000) != 0;
}
inline bool ForceField::areEqualPlanes(U16 o, U16 t) const
{
return (o & ~0x8000) == (t & ~0x8000);
}
inline const Box3F& ForceField::getBoundingBox() const
{
return mBoundingBox;
}
#endif // _H_FORCEFIELD_

2221
engine/interior/interior.cc Executable file

File diff suppressed because it is too large Load Diff

994
engine/interior/interior.h Executable file
View File

@ -0,0 +1,994 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _INTERIOR_H_
#define _INTERIOR_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _COLLISION_H_
#include "collision/collision.h"
#endif
#ifndef _RESMANAGER_H_
#include "core/resManager.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
#ifndef _MPLANE_H_
#include "math/mPlane.h"
#endif
#ifndef _MBOX_H_
#include "math/mBox.h"
#endif
#ifndef _MSPHERE_H_
#include "math/mSphere.h"
#endif
#ifndef _CONVEX_H_
#include "collision/convex.h"
#endif
#ifndef _INTERIORLMMANAGER_H_
#include "interior/interiorLMManager.h"
#endif
#ifndef _CONSTRUCTORSIMPLEMESH_H_
#include "constructor/constructorSimpleMesh.h"
#endif
#include "lightingSystem/sgDetailMapping.h"
//-------------------------------------- Forward declarations
class Stream;
class EditGeometry;
class InteriorInstance;
class GBitmap;
class TextureHandle;
class RectD;
class SphereF;
class MatrixF;
class SceneState;
class MaterialList;
class AbstractPolyList;
class InteriorSubObject;
class TranslucentSubObject;
class BitVector;
struct RayInfo;
struct EdgeList;
class SurfaceHash;
class InteriorPolytope;
class FloorPlan;
class LightInfo;
class PlaneRange;
class EditInteriorResource;
//--------------------------------------------------------------------------
class InteriorConvex : public Convex
{
typedef Convex Parent;
friend class Interior;
friend class InteriorInstance;
protected:
Interior* pInterior;
public:
S32 hullId;
Box3F box;
InteriorConvex() { mType = InteriorConvexType; }
InteriorConvex(const InteriorConvex& cv)
{
mObject = cv.mObject;
pInterior = cv.pInterior;
hullId = cv.hullId;
box = box;
}
Box3F getBoundingBox() const;
Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const;
Point3F support(const VectorF& v) const;
void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
void getPolyList(AbstractPolyList* list);
};
class ZoneVisDeterminer
{
enum Mode
{
FromState,
FromRects
};
Mode mMode;
SceneState* mState;
U32 mZoneRangeOffset;
U32 mParentZone;
public:
ZoneVisDeterminer() : mMode(FromRects), mState(NULL) { }
void runFromState(SceneState*, U32, U32);
void runFromRects(SceneState*, U32, U32);
bool isZoneVisible(const U32) const;
};
struct ItrPaddedPoint
{
Point3F point;
union
{
F32 fogCoord;
U8 fogColor[4];
};
};
//------------------------------------------------------------------------------
//-------------------------------------- CLASS NOTES
// Interior: Base for all interior geometries. Contains all lighting, poly,
// portal zone, bsp info, etc. to render an interior.
//
// Internal Structure Notes:
// IBSPNode:
// planeIndex: Obv.
// frontIndex/backIndex: Top bit indicates if children are leaves.
// Next bit indicates if leaf children are solid.
//
// IBSPLeafSolid:
// planeIndex: obv.
// surfaceIndex/surfaceCount: Polys that are on the faces of this leaf. Only
// used for collision/surface info detection.
//
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
class Interior
{
friend class FloorPlan;
friend class EditGeometry;
friend class InteriorInstance;
friend class SceneLighting;
friend class InteriorProxy;
friend class TranslucentSubObject;
friend class MirrorSubObject;
friend class InteriorConvex;
friend class InteriorLMManager;
friend class EditInteriorResource;
public:
Interior();
~Interior();
// Support for interior light map border sizes.
private:
U32 mLightMapBorderSize;
public:
sgDetailMapping detailMapping;
U32 getLightMapBorderSize() const {return mLightMapBorderSize;}
void setLightMapBorderSize(U32 value) {mLightMapBorderSize = value;}
void buildSurfaceZones();
//void sgSetupLighting(InteriorInstance *intInst, SceneGraphData &sgData);
//bool sgRenderLights(InteriorInstance *intInst, SceneGraphData &sgData);
// Misc
U32 getDetailLevel() const;
U32 getMinPixels() const;
const Box3F& getBoundingBox() const;
S32 getNumZones() const;
// Rendering
bool prepForRendering(const char* path);
void rebuildVertexColors(LM_HANDLE instanceHandle,
Vector<ColorI>* normal,
Vector<ColorI>* alarm);
bool prepRender(SceneState* state,
S32 containingZone,
S32 baseZone,
U32 zoneOffset,
const MatrixF& OSToWS,
const Point3F& objScale,
const bool modifyBaseState,
const bool dontRestrictOutside,
const bool flipClipPlanes);
void prepTempRender(SceneState* state,
S32 containingZone,
S32 baseZone,
const MatrixF& OSToWS,
const Point3F& objScale,
const bool flipClipPlanes);
void render(const bool useAlarmLighting, MaterialList* pMaterials,
const LM_HANDLE instanceHandle,
const Vector<ColorI>* normalVLights,
const Vector<ColorI>* alarmVLights);
void render_vc_tf(const bool useAlarmLighting, MaterialList* pMaterials, const LM_HANDLE instanceHandle,
const Vector<ColorI>* normalVLights,
const Vector<ColorI>* alarmVLights);
void render_vc_fc(const bool useAlarmLighting, MaterialList* pMaterials, const LM_HANDLE instanceHandle,
const Vector<ColorI>* normalVLights,
const Vector<ColorI>* alarmVLights);
void renderARB_vc_tf(const bool useAlarmLighting, MaterialList* pMaterials, const LM_HANDLE instanceHandle,
const Vector<ColorI>* normalVLights,
const Vector<ColorI>* alarmVLights);
void renderARB(const bool useAlarmLighting, MaterialList* pMaterials, const LM_HANDLE instanceHandle);
void renderARB_FC(const bool useAlarmLighting, MaterialList* pMaterials, const LM_HANDLE instanceHandle);
void renderLights(LightInfo* pInfo,
const MatrixF& transform,
const Point3F& scale,
Vector<U32> surfaces);
void renderAsShape();
void renderStaticMeshes(bool texture, bool lightmap, LM_HANDLE instancelmhandle);
bool useFogCoord();
bool scopeZones(const S32 baseZone,
const Point3F& interiorRoot,
bool* interiorScopingState);
//-------------------------------------- Collision Interface and zone scans
bool scanZones(const Box3F&, const MatrixF&, U16* zones, U32* numZones);
bool castRay(const Point3F&, const Point3F&, RayInfo*);
bool buildPolyList(AbstractPolyList*, const Box3F&, const MatrixF&, const Point3F&);
bool buildLightPolyList(U32* lightSurfaces, U32* numLightSurfaces,
const Box3F&, const MatrixF&, const Point3F&);
bool getIntersectingHulls(const Box3F&, U16* hulls, U32* numHulls);
bool getIntersectingVehicleHulls(const Box3F&, U16* hulls, U32* numHulls);
protected:
bool castRay_r(const U16, const U16, const Point3F&, const Point3F&, RayInfo*);
void buildPolyList_r(InteriorPolytope& polytope,
SurfaceHash& hash);
void scanZone_r(const U16 node,
const Point3F& center,
const Point3F& axisx,
const Point3F& axisy,
const Point3F& axisz,
U16* zones,
U32* numZones);
void scanZoneNew(InteriorPolytope& polytope,
U16* zones,
U32* numZones);
void scopeZone(const U32 currZone,
bool* interiorScopingState,
const Point3F& interiorRoot,
Vector<U32>& zoneStack,
Vector<PlaneF>& planeStack,
Vector<PlaneRange>& planeRangeStack);
//-------------------------------------- Global rendering control
public:
enum RenderModes
{
NormalRender = 0,
NormalRenderLines = 1,
ShowDetail = 2,
ShowAmbiguous = 3,
ShowOrphan = 4,
ShowLightmaps = 5,
ShowTexturesOnly = 6,
ShowPortalZones = 7,
ShowOutsideVisible = 8,
ShowCollisionFans = 9,
ShowStrips = 10,
ShowNullSurfaces = 11,
ShowLargeTextures = 12,
ShowHullSurfaces = 13,
ShowVehicleHullSurfaces = 14,
ShowVertexColors = 15,
ShowDetailLevel = 16
};
enum Constants
{
NumCoordBins = 16,
BinsXY = 0,
BinsXZ = 1,
BinsYZ = 2
};
static U32 smRenderMode;
static bool smFocusedDebug;
static bool smRenderEnvironmentMaps;
static bool smUseVertexLighting;
static bool smUseTexturedFog;
static bool smLockArrays;
static U32 smFileVersion;
static bool smLightingCastRays;
//-------------------------------------- Persistence interface
bool read(Stream& stream);
bool write(Stream& stream) const;
bool readVehicleCollision(Stream& stream);
bool writeVehicleCollision(Stream& stream) const;
protected:
bool writePlaneVector(Stream&) const;
bool readPlaneVector(Stream&);
bool readLMapTexGen(Stream&, PlaneF&, PlaneF&);
bool writeLMapTexGen(Stream&, const PlaneF&, const PlaneF&) const;
void setupTexCoords();
void setupZonePlanes();
//-------------------------------------- For morian only...
public:
void processHullPolyLists();
void processVehicleHullPolyLists();
//-------------------------------------- BSP Structures
protected:
struct IBSPNode
{
U16 planeIndex;
U16 frontIndex;
U16 backIndex;
U16 terminalZone; // if high bit set, then the lower 15 bits are the zone
// of any of the subsidiary nodes. Note that this is
// going to overestimate some, since an object could be
// completely contained in solid, but it's probably
// going to turn out alright.
};
struct IBSPLeafSolid
{
U32 surfaceIndex;
U16 surfaceCount;
};
bool isBSPLeafIndex(U16 index) const;
bool isBSPSolidLeaf(U16 index) const;
bool isBSPEmptyLeaf(U16 index) const;
U16 getBSPSolidLeafIndex(U16 index) const;
U16 getBSPEmptyLeafZone(U16 index) const;
void setupAveTexGenLength();
void truncateZoneTree();
void truncateZoneNode(const U16);
bool getUnifiedZone(const U16, S32*);
public:
static U16 getPlaneIndex(const U16 index);
static bool planeIsFlipped(const U16 index);
const PlaneF& getPlane(const U16 index) const;
PlaneF getFlippedPlane(const U16 index) const;
const Point3F getPointNormal(const U32 surfaceIndex, const U32 pointOffset) const;
protected:
bool areEqualPlanes(U16, U16) const;
bool isNullSurfaceIndex(const U32 index) const;
bool isVehicleNullSurfaceIndex(const U32 index) const;
U32 getNullSurfaceIndex(const U32 index) const;
U32 getVehicleNullSurfaceIndex(const U32 index) const;
//-------------------------------------- Portals and Zone structures
struct Zone
{
U16 portalStart;
U16 portalCount;
U32 surfaceStart;
U32 planeStart;
U16 surfaceCount;
U16 planeCount;
U32 staticMeshStart;
U32 staticMeshCount;
U16 flags;
U16 zoneId; // This is ephemeral, not persisted out.
};
struct Portal
{
U16 planeIndex;
U16 triFanCount;
U32 triFanStart; // portals can have multiple windings
U16 zoneFront;
U16 zoneBack;
};
//-------------------------------------- Poly/Surface structures
public:
enum SurfaceFlags
{
SurfaceDetail = BIT(0),
SurfaceAmbiguous = BIT(1),
SurfaceOrphan = BIT(2),
SurfaceSharedLMaps = BIT(3), // Indicates that the alarm and normal states share a lightmap (for mission lighter)
SurfaceOutsideVisible = BIT(4),
SurfaceStaticMesh = BIT(5), // This surface belongs to a static mesh collision hull
SurfaceFlagMask = (SurfaceDetail |
SurfaceAmbiguous |
SurfaceOrphan |
SurfaceSharedLMaps |
SurfaceOutsideVisible |
SurfaceStaticMesh)
};
enum ZoneFlags
{
ZoneInside = BIT(0)
};
const bool isSurfaceOutsideVisible(U32 surface) const;
struct TexMatrix
{
S32 T;
S32 N;
S32 B;
TexMatrix::TexMatrix() { T = -1; N = -1; B = -1; };
};
struct Edge
{
S32 vertexes[2];
S32 faces[2];
};
struct TexGenPlanes
{
PlaneF planeX;
PlaneF planeY;
};
struct TriFan
{
U32 windingStart;
U32 windingCount;
};
struct Surface
{
U32 windingStart; // 1
U16 planeIndex; // 2
U16 textureIndex;
U32 texGenIndex; // 3
U16 lightCount; // 4
U8 surfaceFlags;
U32 windingCount;
U32 fanMask; // 5
U32 lightStateInfoStart; // 6
U32 mapOffsetX; // 7
U32 mapOffsetY;
U32 mapSizeX;
U32 mapSizeY;
bool unused;
};
struct NullSurface
{
U32 windingStart;
U16 planeIndex;
U8 surfaceFlags;
U32 windingCount;
};
//-------------------------------------- Animated lighting structures
enum LightFlags
{
AnimationAmbient = BIT(0),
AnimationLoop = BIT(1),
AnimationFlicker = BIT(2),
AnimationTypeMask = BIT(3) - 1,
AlarmLight = BIT(3)
};
enum LightType
{
AmbientLooping = AnimationAmbient | AnimationLoop,
AmbientFlicker = AnimationAmbient | AnimationFlicker,
TriggerableLoop = AnimationLoop,
TriggerableFlicker = AnimationFlicker,
TriggerableRamp = 0
};
struct AnimatedLight
{
U32 nameIndex; ///< Light's name
U32 stateIndex; ///< start point in the state list
U16 stateCount; ///< number of states in this light
U16 flags; ///< flags (Apply AnimationTypeMask to get type)
U32 duration; ///< total duration of animation (ms)
};
protected:
struct LightState
{
U8 red; ///< state's color
U8 green;
U8 blue;
U8 _color_padding_;
U32 activeTime; ///< Time (ms) at which this state becomes active
U32 dataIndex; ///< StateData count and index for this state
U16 dataCount;
U16 __32bit_padding__;
};
struct LightStateData
{
U32 surfaceIndex; ///< Surface affected by this data
U32 mapIndex; ///< Index into StateDataBuffer (0xFFFFFFFF indicates none)
U16 lightStateIndex; ///< Entry to modify in InteriorInstance
U16 __32bit_padding__;
};
// convex hull collision structures...
protected:
struct ConvexHull
{
F32 minX;
F32 maxX;
F32 minY;
F32 maxY;
F32 minZ;
F32 maxZ;
U32 hullStart;
U32 surfaceStart;
U32 planeStart;
U16 hullCount;
U16 surfaceCount;
U32 polyListPlaneStart;
U32 polyListPointStart;
U32 polyListStringStart;
U16 searchTag;
bool staticMesh;
};
struct CoordBin
{
U32 binStart;
U32 binCount;
};
protected:
LM_HANDLE mLMHandle;
public:
LM_HANDLE getLMHandle() {return(mLMHandle);}
// SceneLighting::InteriorProxy interface
const Surface & getSurface(const U32 surface) const;
const U32 getSurfaceCount() const;
const U32 getNormalLMapIndex(const U32 surface) const;
const U32 getAlarmLMapIndex(const U32 surface) const;
const U32 getStaticMeshCount() const;
const ConstructorSimpleMesh *getStaticMesh(const U32 index) const;
const U32 getWinding(const U32 index) const;
const Point3F & getPoint(const U32 index) const;
const TexGenPlanes & getLMTexGenEQ(const U32 index) const;
bool hasAlarmState() const;
const U32 getWindingCount() const;
//-------------------------------------- Instance Data Members
protected:
U32 mFileVersion;
U32 mDetailLevel;
U32 mMinPixels;
F32 mAveTexGenLength; // Set in Interior::read after loading the texgen planes.
Box3F mBoundingBox;
SphereF mBoundingSphere;
Vector<PlaneF> mPlanes;
Vector<ItrPaddedPoint> mPoints;
Vector<U8> mPointVisibility;
Vector<Point3F> mNormals;
Vector<TexMatrix> mTexMatrices;
Vector<U32> mTexMatIndices;
ColorF mBaseAmbient;
ColorF mAlarmAmbient;
Vector<IBSPNode> mBSPNodes;
Vector<IBSPLeafSolid> mBSPSolidLeaves;
bool mPreppedForRender;
MaterialList* mMaterialList;
TextureHandle* mWhite;
TextureHandle* mWhiteRGB;
TextureHandle* mLightFalloff;
Vector<TextureHandle*> mEnvironMaps;
Vector<F32> mEnvironFactors;
U32 mValidEnvironMaps;
Vector<U32> mWindings;
Vector<TexGenPlanes> mTexGenEQs;
Vector<TexGenPlanes> mLMTexGenEQs;
Vector<TriFan> mWindingIndices;
Vector<Surface> mSurfaces;
Vector<NullSurface> mNullSurfaces;
Vector<U32> mSolidLeafSurfaces;
Vector<Edge> mEdges;
// Portals and zones
Vector<Zone> mZones;
Vector<U16> mZonePlanes;
Vector<U16> mZoneSurfaces;
Vector<U16> mZonePortalList;
Vector<Portal> mPortals;
Vector<U32> mZoneStaticMeshes;
Vector<S32> mSurfaceZone;
// Subobjects: Doors, translucencies, mirrors, etc.
Vector<InteriorSubObject*> mSubObjects;
// Lighting info
bool mHasAlarmState;
U32 mNumLightStateEntries;
//Vector<TextureHandle> mLightDirMapsTex;
Vector<GBitmap*> mLightDirMaps;
Vector<GBitmap*> mLightmaps;
Vector<bool> mLightmapKeep;
Vector<U32> mNormalLMapIndices;
Vector<U32> mAlarmLMapIndices;
U32 mNumTriggerableLights; // Note: not persisted
// Persistent animated light structures
Vector<AnimatedLight> mAnimatedLights;
Vector<LightState> mLightStates;
Vector<LightStateData> mStateData;
Vector<U8> mStateDataBuffer;
Vector<char> mNameBuffer;
Vector<ConvexHull> mConvexHulls;
Vector<U8> mConvexHullEmitStrings;
Vector<U32> mHullIndices;
Vector<U32> mHullEmitStringIndices;
Vector<U32> mHullSurfaceIndices;
Vector<U16> mHullPlaneIndices;
Vector<U16> mPolyListPlanes;
Vector<U32> mPolyListPoints;
Vector<U8> mPolyListStrings;
CoordBin mCoordBins[NumCoordBins * NumCoordBins];
Vector<U16> mCoordBinIndices;
U32 mCoordBinMode;
Vector<ConvexHull> mVehicleConvexHulls;
Vector<U8> mVehicleConvexHullEmitStrings;
Vector<U32> mVehicleHullIndices;
Vector<U32> mVehicleHullEmitStringIndices;
Vector<U32> mVehicleHullSurfaceIndices;
Vector<U16> mVehicleHullPlaneIndices;
Vector<U16> mVehiclePolyListPlanes;
Vector<U32> mVehiclePolyListPoints;
Vector<U8> mVehiclePolyListStrings;
Vector<ItrPaddedPoint> mVehiclePoints;
Vector<NullSurface> mVehicleNullSurfaces;
Vector<PlaneF> mVehiclePlanes;
Vector<U32> mVehicleWindings;
Vector<TriFan> mVehicleWindingIndices;
VectorPtr<ConstructorSimpleMesh*> mStaticMeshes;
// Some data used only by Constructor during export
Vector<U32> mBspNodeList;
U16 mSearchTag;
//-------------------------------------- Private interface
protected:
const char* getName(const U32 nameIndex) const;
static const char* getLightTypeString(const LightType);
S32 getZoneForPoint(const Point3F&) const;
void debugRender(MaterialList* pMaterials, LM_HANDLE instanceHandle);
void debugRenderPortals();
void debugNormalRenderLines();
void debugShowDetail();
void debugShowAmbiguous();
void debugShowLightmaps(LM_HANDLE instanceHandle);
void debugShowPortalZones();
void debugShowCollisionFans();
void debugShowOrphan();
void debugShowStrips();
void debugShowTexturesOnly(MaterialList* pMaterials);
void debugShowLargeTextures(MaterialList* pMaterials);
void debugShowNullSurfaces(MaterialList* pMaterials);
void debugShowOutsideVisible();
void debugShowHullSurfaces();
void debugShowVehicleHullSurfaces(MaterialList* pMaterials);
// void debugShowVertexColors(MaterialList* pMaterials);
void debugShowDetailLevel();
void debugShowOrphansFinish();
public:
void collisionFanFromSurface(const Surface&, U32* fan, U32* numIndices) const;
private:
void fullWindingFromSurface(const Surface&, U32* fan, U32* numIndices) const;
bool projectClipAndBoundFan(U32 fanIndex, F64* pResult);
void zoneTraversal(S32 baseZone, const bool flipClipPlanes);
void createZoneRectVectors();
void destroyZoneRectVectors();
void traverseZone(const RectD* inRects, const U32 numInputRects, U32 currZone, Vector<U32>& zoneStack);
void doFogActive( const bool environmentActive,
const SceneState* state,
const U32 mergeArrayCount, const U16* mergeArray,
const PlaneF &distPlane,
const F32 distOffset,
const Point3F &worldP,
const Point3F &osZVec,
const F32 worldZ );
void setupActivePolyList(ZoneVisDeterminer&, SceneState*,
const Point3F&, const Point3F& rViewVector,
const Point3F&,
const F32 worldz, const Point3F& scale);
void setupFog(SceneState* state);
void clearFog();
void setOSCamPosition(const Point3F&);
public:
void purgeLODData();
};
//------------------------------------------------------------------------------
inline bool Interior::isBSPLeafIndex(U16 index) const
{
return (index & 0x8000) != 0;
}
inline bool Interior::isBSPSolidLeaf(U16 index) const
{
AssertFatal(isBSPLeafIndex(index) == true, "Error, only call for leaves!");
return (index & 0x4000) != 0;
}
inline bool Interior::isBSPEmptyLeaf(U16 index) const
{
AssertFatal(isBSPLeafIndex(index) == true, "Error, only call for leaves!");
return (index & 0x4000) == 0;
}
inline U16 Interior::getBSPSolidLeafIndex(U16 index) const
{
AssertFatal(isBSPSolidLeaf(index) == true, "Error, only call for leaves!");
return U16(index & ~0xC000);
}
inline U16 Interior::getBSPEmptyLeafZone(U16 index) const
{
AssertFatal(isBSPEmptyLeaf(index) == true, "Error, only call for leaves!");
return U16(index & ~0xC000);
}
inline const PlaneF& Interior::getPlane(const U16 index) const
{
AssertFatal(U32(index & ~0x8000) < mPlanes.size(),
"Interior::getPlane: planeIndex out of range");
return mPlanes[index & ~0x8000];
}
inline PlaneF Interior::getFlippedPlane(const U16 index) const
{
PlaneF plane = getPlane(index);
if(Interior::planeIsFlipped(index))
plane.neg();
return plane;
}
inline U16 Interior::getPlaneIndex(const U16 index)
{
return U16(index & ~0x8000);
}
inline bool Interior::planeIsFlipped(const U16 index)
{
return (index >> 15)!=0;
}
inline bool Interior::areEqualPlanes(U16 o, U16 t) const
{
return (o & ~0x8000) == (t & ~0x8000);
}
inline const Point3F Interior::getPointNormal(const U32 surfaceIndex, const U32 pointOffset) const
{
Surface rSurface = mSurfaces[surfaceIndex];
Point3F normal(0.0f, 0.0f, 0.0f);
if (mFileVersion >= 11)
{
U32 texMatIndex = mTexMatIndices[rSurface.windingStart + pointOffset];
TexMatrix texMat = mTexMatrices[texMatIndex];
if (texMat.N > -1)
normal = mNormals[texMat.N];
}
else
normal = getPlane(rSurface.planeIndex);
return normal;
}
inline U32 Interior::getDetailLevel() const
{
return mDetailLevel;
}
inline U32 Interior::getMinPixels() const
{
return mMinPixels;
}
inline const Box3F& Interior::getBoundingBox() const
{
return mBoundingBox;
}
inline S32 Interior::getNumZones() const
{
return mZones.size();
}
inline bool Interior::isNullSurfaceIndex(const U32 index) const
{
return (index & 0x80000000) != 0;
}
inline bool Interior::isVehicleNullSurfaceIndex(const U32 index) const
{
return (index & 0x40000000) != 0;
}
inline U32 Interior::getNullSurfaceIndex(const U32 index) const
{
AssertFatal(isNullSurfaceIndex(index), "Not a proper index!");
AssertFatal(!isVehicleNullSurfaceIndex(index), "Not a proper index");
return (index & ~0x80000000);
}
inline U32 Interior::getVehicleNullSurfaceIndex(const U32 index) const
{
AssertFatal(isVehicleNullSurfaceIndex(index), "Not a proper index!");
return (index & ~(0x80000000 | 0x40000000));
}
inline const char* Interior::getLightTypeString(const LightType type)
{
switch (type) {
case AmbientLooping:
return "AmbientLooping";
case AmbientFlicker:
return "AmbientFlicker";
case TriggerableLoop:
return "TriggerableLoop";
case TriggerableFlicker:
return "TriggerableFlicker";
case TriggerableRamp:
return "TriggerableRamp";
default:
return "<UNKNOWN>";
}
}
inline const char* Interior::getName(const U32 nameIndex) const
{
return &mNameBuffer[nameIndex];
}
inline const U32 Interior::getSurfaceCount() const
{
return(mSurfaces.size());
}
inline const Interior::Surface & Interior::getSurface(const U32 surface) const
{
AssertFatal(surface < mSurfaces.size(), "invalid index");
return(mSurfaces[surface]);
}
inline const U32 Interior::getStaticMeshCount() const
{
return mStaticMeshes.size();
}
inline const ConstructorSimpleMesh *Interior::getStaticMesh(const U32 index) const
{
AssertFatal(index < mStaticMeshes.size(), "invalid index");
return mStaticMeshes[index];
}
inline const U32 Interior::getNormalLMapIndex(const U32 surface) const
{
AssertFatal(surface < mNormalLMapIndices.size(), "invalid index");
return(mNormalLMapIndices[surface]);
}
inline const U32 Interior::getAlarmLMapIndex(const U32 surface) const
{
AssertFatal(surface < mAlarmLMapIndices.size(), "invalid index");
return(mAlarmLMapIndices[surface]);
}
inline const U32 Interior::getWinding(const U32 index) const
{
AssertFatal(index < mWindings.size(), "invalid index");
return(mWindings[index]);
}
inline const Point3F & Interior::getPoint(const U32 index) const
{
AssertFatal(index < mPoints.size(), "invalid index");
return(mPoints[index].point);
}
inline const Interior::TexGenPlanes & Interior::getLMTexGenEQ(const U32 index) const
{
AssertFatal(index < mLMTexGenEQs.size(), "invalid index");
return(mLMTexGenEQs[index]);
}
inline bool Interior::hasAlarmState() const
{
return(mHasAlarmState);
}
inline const bool Interior::isSurfaceOutsideVisible(U32 surface) const
{
AssertFatal(surface < mSurfaces.size(), "Interior::isSurfaceOutsideVisible: Invalid surface index");
return ((mSurfaces[surface].surfaceFlags & SurfaceOutsideVisible)!=0);
}
inline const U32 Interior::getWindingCount() const
{
return(mWindings.size());
}
#endif //_INTERIOR_H_

File diff suppressed because it is too large Load Diff

802
engine/interior/interiorDebug.cc Executable file
View File

@ -0,0 +1,802 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "interior/interior.h"
#include "console/console.h"
#include "core/color.h"
#include "dgl/gTexManager.h"
#include "dgl/dgl.h"
#include "math/mMatrix.h"
#include "dgl/materialList.h"
#include "dgl/gBitmap.h"
extern U16* sgActivePolyList;
extern U32 sgActivePolyListSize;
namespace {
void lineLoopFromStrip(Vector<ItrPaddedPoint>& points,
Vector<U32>& windings,
U32 windingStart,
U32 windingCount)
{
glBegin(GL_LINE_LOOP);
glVertex3fv(points[windings[windingStart]].point);
S32 skip = windingStart + 1;
while (skip < (windingStart + windingCount)) {
glVertex3fv(points[windings[skip]].point);
skip += 2;
}
skip -= 1;
while (skip > windingStart) {
if (skip < (windingStart + windingCount))
glVertex3fv(points[windings[skip]].point);
skip -= 2;
}
glEnd();
}
void lineStrip(Vector<ItrPaddedPoint>& points,
Vector<U32>& windings,
U32 windingStart,
U32 windingCount)
{
U32 end = 2;
while (end < windingCount) {
// Even
glBegin(GL_LINE_LOOP);
glVertex3fv(points[windings[windingStart + end - 2]].point);
glVertex3fv(points[windings[windingStart + end - 1]].point);
glVertex3fv(points[windings[windingStart + end - 0]].point);
glEnd();
end++;
if (end >= windingCount)
break;
glBegin(GL_LINE_LOOP);
glVertex3fv(points[windings[windingStart + end - 1]].point);
glVertex3fv(points[windings[windingStart + end - 2]].point);
glVertex3fv(points[windings[windingStart + end - 0]].point);
glEnd();
end++;
}
}
} // namespace {}
void Interior::debugRender(MaterialList* pMaterials, LM_HANDLE instanceHandle)
{
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(ItrPaddedPoint), mPoints.address());
switch (smRenderMode) {
case NormalRenderLines:
debugNormalRenderLines();
break;
case ShowDetail:
debugShowDetail();
break;
case ShowAmbiguous:
debugShowAmbiguous();
break;
case ShowLightmaps:
debugShowLightmaps(instanceHandle);
break;
case ShowPortalZones:
debugShowPortalZones();
break;
case ShowCollisionFans:
debugShowCollisionFans();
break;
case ShowOrphan:
debugShowOrphan();
break;
case ShowStrips:
debugShowStrips();
break;
case ShowTexturesOnly:
debugShowTexturesOnly(pMaterials);
break;
case ShowNullSurfaces:
debugShowNullSurfaces(pMaterials);
break;
case ShowLargeTextures:
debugShowLargeTextures(pMaterials);
break;
case ShowOutsideVisible:
debugShowOutsideVisible();
break;
case ShowHullSurfaces:
debugShowHullSurfaces();
break;
case ShowVehicleHullSurfaces:
debugShowVehicleHullSurfaces(pMaterials);
break;
case ShowVertexColors:
// debugShowVertexColors(pMaterials);
break;
case ShowDetailLevel:
debugShowDetailLevel();
break;
default:
AssertWarn(false, "Warning! Misunderstood debug render mode. Defaulting to ShowDetail");
debugShowDetail();
break;
}
glDisableClientState(GL_VERTEX_ARRAY);
}
void Interior::debugNormalRenderLines()
{
// Ok, our verts are set up, draw our polys.
U32 currentlyBound = U32(-1);
U32 currentTexGen = U32(-1);
// Base textures
glBlendFunc(GL_ONE, GL_ZERO);
glDisable(GL_TEXTURE_2D);
glColor3f(1, 0, 1);
for (U32 i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
}
glEnable(GL_TEXTURE_2D);
}
void Interior::debugShowDetail()
{
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
for (U32 i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
if (rSurface.surfaceFlags & SurfaceDetail)
glColor3f(1.0f, 0, 0);
else {
if (smFocusedDebug == true)
continue;
else
glColor3f(1.0f, 1.0f, 1.0f);
}
glDrawElements(GL_TRIANGLE_STRIP, rSurface.windingCount, GL_UNSIGNED_INT, &mWindings[rSurface.windingStart]);
}
if (smFocusedDebug == false) {
glColor3f(0, 0, 0);
for (U32 i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
}
}
glEnable(GL_TEXTURE_2D);
}
void Interior::debugShowAmbiguous()
{
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
for (U32 i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
if (rSurface.surfaceFlags & SurfaceAmbiguous)
glColor3f(0, 1.0f, 0);
else {
if (smFocusedDebug == true)
continue;
else
glColor3f(1.0f, 1.0f, 1.0f);
}
glDrawElements(GL_TRIANGLE_STRIP, rSurface.windingCount, GL_UNSIGNED_INT, &mWindings[rSurface.windingStart]);
}
if (smFocusedDebug == false) {
glColor3f(0, 0, 0);
for (U32 i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
}
}
glEnable(GL_TEXTURE_2D);
}
void Interior::debugShowLightmaps(LM_HANDLE instanceHandle)
{
glBlendFunc(GL_ONE, GL_ZERO);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
U32 currentlyBound = U32(-1);
U32 i;
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
if (mNormalLMapIndices[sgActivePolyList[i]] != currentlyBound) {
glBindTexture(GL_TEXTURE_2D, gInteriorLMManager.getHandle(mLMHandle, instanceHandle, mNormalLMapIndices[sgActivePolyList[i]])->getGLName());
currentlyBound = mNormalLMapIndices[sgActivePolyList[i]];
}
// Draw the poly
glBegin(GL_TRIANGLE_STRIP);
for (U32 j = rSurface.windingStart; j < rSurface.windingStart + rSurface.windingCount; j++) {
glTexCoord2f(mLMTexGenEQs[sgActivePolyList[i]].planeX.distToPlane(mPoints[mWindings[j]].point),
mLMTexGenEQs[sgActivePolyList[i]].planeY.distToPlane(mPoints[mWindings[j]].point));
glVertex3fv(mPoints[mWindings[j]].point);
}
glEnd();
}
glDisable(GL_TEXTURE_2D);
glColor3f(0, 0, 0);
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
}
glEnable(GL_TEXTURE_2D);
renderStaticMeshes(false, true, instanceHandle);
}
void Interior::debugShowPortalZones()
{
static U8 colors[14][3] = {
{ 0xFF, 0xFF, 0xFF },
{ 0x00, 0x00, 0xFF },
{ 0x00, 0xFF, 0x00 },
{ 0xFF, 0x00, 0x00 },
{ 0xFF, 0xFF, 0x00 },
{ 0xFF, 0x00, 0xFF },
{ 0x00, 0xFF, 0xFF },
{ 0x80, 0x80, 0x80 },
{ 0xFF, 0x80, 0x80 },
{ 0x80, 0xFF, 0x80 },
{ 0x80, 0x80, 0xFF },
{ 0x80, 0xFF, 0xFF },
{ 0xFF, 0x80, 0xFF },
{ 0xFF, 0x80, 0x80 }
};
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
for (U32 i = 0; i < mZones.size(); i++) {
U8* color;
if (i == 0)
color = colors[0];
else
color = colors[(i % 13) + 1];
for (U32 j = mZones[i].surfaceStart; j < mZones[i].surfaceStart + mZones[i].surfaceCount; j++) {
const Surface& rSurface = mSurfaces[mZoneSurfaces[j]];
glColor3ub(color[0], color[1], color[2]);
glDrawElements(GL_TRIANGLE_STRIP, rSurface.windingCount, GL_UNSIGNED_INT, &mWindings[rSurface.windingStart]);
glColor3ub(0, 0, 0);
lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
}
}
glEnable(GL_TEXTURE_2D);
debugRenderPortals();
}
void Interior::debugRenderPortals()
{
//-------------------------------------- Render portals...
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (U32 i = 0; i < mPortals.size(); i++) {
const Portal& rPortal = mPortals[i];
for (U16 j = 0; j < rPortal.triFanCount; j++) {
const TriFan& rFan = mWindingIndices[rPortal.triFanStart + j];
U32 k;
glColor4f(0.75, 0.5, 0.75, 0.45);
glBegin(GL_TRIANGLE_FAN);
for (k = 0; k < rFan.windingCount; k++)
glVertex3fv(mPoints[mWindings[rFan.windingStart + k]].point);
glEnd();
glColor4f(0, 0, 1, 1);
glBegin(GL_LINE_LOOP);
for (k = 0; k < rFan.windingCount; k++)
glVertex3fv(mPoints[mWindings[rFan.windingStart + k]].point);
glEnd();
}
}
}
void Interior::debugShowCollisionFans()
{
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
U32 i;
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
U32 numIndices;
U32 fanIndices[32];
collisionFanFromSurface(rSurface, fanIndices, &numIndices);
glColor3f(1.0f, 1.0f, 1.0f);
glDrawElements(GL_TRIANGLE_FAN, numIndices, GL_UNSIGNED_INT, fanIndices);
}
glColor3f(0, 0, 0);
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
U32 numIndices;
U32 fanIndices[32];
collisionFanFromSurface(rSurface, fanIndices, &numIndices);
glBegin(GL_LINE_LOOP);
for (U32 j = 0; j < numIndices; j++)
glVertex3fv(mPoints[fanIndices[j]].point);
glEnd();
}
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
U32 numIndices;
U32 fanIndices[32];
collisionFanFromSurface(rSurface, fanIndices, &numIndices);
glColor3f(1, 0, 0);
glBegin(GL_LINES);
for (U32 j = 0; j < numIndices; j++) {
Point3F up = mPoints[fanIndices[j]].point;
Point3F norm = getPlane(rSurface.planeIndex);
if (planeIsFlipped(rSurface.planeIndex))
up -= norm * 0.4;
else
up += norm * 0.4;
glVertex3fv(mPoints[fanIndices[j]].point);
glVertex3fv(up);
}
glEnd();
}
glEnable(GL_TEXTURE_2D);
}
void Interior::debugShowOrphan()
{
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
U32 i;
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
if (rSurface.surfaceFlags & SurfaceOrphan)
glColor3f(0.0f, 0.0f, 1.0f);
else {
if (smFocusedDebug == true)
continue;
else
glColor3f(1.0f, 1.0f, 1.0f);
}
glDrawElements(GL_TRIANGLE_STRIP, rSurface.windingCount, GL_UNSIGNED_INT, &mWindings[rSurface.windingStart]);
}
if (smFocusedDebug == false) {
glColor3f(0, 0, 0);
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
}
}
glEnable(GL_TEXTURE_2D);
}
void Interior::debugShowOrphansFinish()
{
}
void Interior::debugShowStrips()
{
static U8 colors[14][3] = {
{ 0xFF, 0xFF, 0xFF },
{ 0x00, 0x00, 0xFF },
{ 0x00, 0xFF, 0x00 },
{ 0xFF, 0x00, 0x00 },
{ 0xFF, 0xFF, 0x00 },
{ 0xFF, 0x00, 0xFF },
{ 0x00, 0xFF, 0xFF },
{ 0x80, 0x80, 0x80 },
{ 0xFF, 0x80, 0x80 },
{ 0x80, 0xFF, 0x80 },
{ 0x80, 0x80, 0xFF },
{ 0x80, 0xFF, 0xFF },
{ 0xFF, 0x80, 0xFF },
{ 0xFF, 0x80, 0x80 }
};
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
U32 color = 0;
U32 i;
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
glColor3ub(colors[sgActivePolyList[i]%14][0],
colors[sgActivePolyList[i]%14][1],
colors[sgActivePolyList[i]%14][2]);
color++;
glDrawElements(GL_TRIANGLE_STRIP, rSurface.windingCount, GL_UNSIGNED_INT, &mWindings[rSurface.windingStart]);
}
glColor3f(0, 0, 0);
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
lineStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
}
glEnable(GL_TEXTURE_2D);
}
void Interior::debugShowNullSurfaces(MaterialList* pMaterials)
{
glBlendFunc(GL_ONE, GL_ZERO);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// Base textures
U32 currentlyBound = U32(-1);
U32 i;
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
U32 baseName = pMaterials->getMaterial(rSurface.textureIndex).getGLName();
if (baseName != currentlyBound) {
glBindTexture(GL_TEXTURE_2D, baseName);
currentlyBound = baseName;
}
// Draw the poly
glBegin(GL_TRIANGLE_STRIP);
for (U32 j = rSurface.windingStart; j < rSurface.windingStart + rSurface.windingCount; j++) {
glTexCoord2f(mTexGenEQs[rSurface.texGenIndex].planeX.distToPlane(mPoints[mWindings[j]].point),
mTexGenEQs[rSurface.texGenIndex].planeY.distToPlane(mPoints[mWindings[j]].point));
glVertex3fv(mPoints[mWindings[j]].point);
}
glEnd();
}
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
glColor3f(1, 0, 0);
for (i = 0; i < mNullSurfaces.size(); i++) {
const NullSurface& rSurface = mNullSurfaces[i];
glDrawElements(GL_TRIANGLE_FAN, rSurface.windingCount, GL_UNSIGNED_INT, &mWindings[rSurface.windingStart]);
}
glEnable(GL_TEXTURE_2D);
}
void Interior::debugShowTexturesOnly(MaterialList* pMaterials)
{
glBlendFunc(GL_ONE, GL_ZERO);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// Base textures
U32 currentlyBound = U32(-1);
U32 i;
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
if (pMaterials->getMaterial(rSurface.textureIndex).getGLName() != currentlyBound) {
glBindTexture(GL_TEXTURE_2D, pMaterials->getMaterial(rSurface.textureIndex).getGLName());
currentlyBound = mMaterialList->getMaterial(rSurface.textureIndex).getGLName();
}
// Draw the poly
glBegin(GL_TRIANGLE_STRIP);
for (U32 j = rSurface.windingStart; j < rSurface.windingStart + rSurface.windingCount; j++) {
glTexCoord2f(mTexGenEQs[rSurface.texGenIndex].planeX.distToPlane(mPoints[mWindings[j]].point),
mTexGenEQs[rSurface.texGenIndex].planeY.distToPlane(mPoints[mWindings[j]].point));
glVertex3fv(mPoints[mWindings[j]].point);
}
glEnd();
}
}
void Interior::debugShowLargeTextures(MaterialList* pMaterials)
{
glBlendFunc(GL_ONE, GL_ZERO);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
// Base textures
U32 currentlyBound = U32(-1);
U32 currentTexGen = U32(-1);
U32 i;
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
U32 baseName = pMaterials->getMaterial(rSurface.textureIndex).getGLName();
if (baseName != currentlyBound) {
glBindTexture(GL_TEXTURE_2D, baseName);
currentlyBound = baseName;
U32 width = pMaterials->getMaterial(rSurface.textureIndex).getWidth();
U32 height = pMaterials->getMaterial(rSurface.textureIndex).getHeight();
if (width >= 256 || height >= 256) {
if (width == 256 && height == 256) {
// small large
glColor3f(0.25, 0.25, 1);
} else if (width != 512 || height != 512) {
// thin large
glColor3f(0.25, 1, 0.25);
} else {
// oh god.
glColor3f(1, 0.25, 0.25);
}
} else {
glColor3f(0.35, 0.35, 0.35);
}
}
// Draw the poly
glBegin(GL_TRIANGLE_STRIP);
for (U32 j = rSurface.windingStart; j < rSurface.windingStart + rSurface.windingCount; j++) {
glTexCoord2f(mTexGenEQs[rSurface.texGenIndex].planeX.distToPlane(mPoints[mWindings[j]].point),
mTexGenEQs[rSurface.texGenIndex].planeY.distToPlane(mPoints[mWindings[j]].point));
glVertex3fv(mPoints[mWindings[j]].point);
}
glEnd();
}
}
void Interior::debugShowOutsideVisible()
{
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
for (U32 i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
if (rSurface.surfaceFlags & SurfaceOutsideVisible)
glColor3f(1.0f, 0, 0);
else {
if (smFocusedDebug == true)
continue;
else
glColor3f(1.0f, 1.0f, 1.0f);
}
glDrawElements(GL_TRIANGLE_STRIP, rSurface.windingCount, GL_UNSIGNED_INT, &mWindings[rSurface.windingStart]);
}
if (smFocusedDebug == false) {
glColor3f(0, 0, 0);
for (U32 i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
}
}
glEnable(GL_TEXTURE_2D);
}
void Interior::debugShowHullSurfaces()
{
glDisable(GL_TEXTURE_2D);
static U8 colors[14][3] = {
{ 0xFF, 0xFF, 0xFF },
{ 0x00, 0x00, 0xFF },
{ 0x00, 0xFF, 0x00 },
{ 0xFF, 0x00, 0x00 },
{ 0xFF, 0xFF, 0x00 },
{ 0xFF, 0x00, 0xFF },
{ 0x00, 0xFF, 0xFF },
{ 0x80, 0x80, 0x80 },
{ 0xFF, 0x80, 0x80 },
{ 0x80, 0xFF, 0x80 },
{ 0x80, 0x80, 0xFF },
{ 0x80, 0xFF, 0xFF },
{ 0xFF, 0x80, 0xFF },
{ 0xFF, 0x80, 0x80 }
};
U32 color = 0;
glBlendFunc(GL_ONE, GL_ZERO);
for (U32 i = 0; i < mConvexHulls.size(); i++) {
const ConvexHull& rHull = mConvexHulls[i];
for (U32 j = rHull.surfaceStart; j < rHull.surfaceCount + rHull.surfaceStart; j++) {
U32 index = mHullSurfaceIndices[j];
if (isNullSurfaceIndex(index)) {
} else {
const Interior::Surface& rSurface = mSurfaces[index];
U32 fanVerts[32];
U32 numVerts;
collisionFanFromSurface(rSurface, fanVerts, &numVerts);
glColor3ub(colors[(i%13)+1][0], colors[(i%13)+1][1], colors[(i%13)+1][2]);
color++;
Point3F center(0, 0, 0);
glBegin(GL_TRIANGLE_FAN);
for (U32 k = 0; k < numVerts; k++) {
glVertex3fv(mPoints[fanVerts[k]].point);
center += mPoints[fanVerts[k]].point;
}
glEnd();
center /= F32(numVerts);
glColor3f(0, 0, 0);
lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
PlaneF plane;
plane.set(mPoints[fanVerts[0]].point, mPoints[fanVerts[1]].point, mPoints[fanVerts[2]].point);
glBegin(GL_LINES);
glVertex3fv(center);
glVertex3fv(center + (plane * 0.25));
glEnd();
}
}
}
glEnable(GL_TEXTURE_2D);
}
void Interior::debugShowVehicleHullSurfaces(MaterialList* pMaterials)
{
glBlendFunc(GL_ONE, GL_ZERO);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// Base textures
U32 currentlyBound = U32(-1);
U32 i;
for (i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
U32 baseName = pMaterials->getMaterial(rSurface.textureIndex).getGLName();
if (baseName != currentlyBound) {
glBindTexture(GL_TEXTURE_2D, baseName);
currentlyBound = baseName;
}
// Draw the poly
glBegin(GL_TRIANGLE_STRIP);
for (U32 j = rSurface.windingStart; j < rSurface.windingStart + rSurface.windingCount; j++) {
glTexCoord2f(mTexGenEQs[rSurface.texGenIndex].planeX.distToPlane(mPoints[mWindings[j]].point),
mTexGenEQs[rSurface.texGenIndex].planeY.distToPlane(mPoints[mWindings[j]].point));
glVertex3fv(mPoints[mWindings[j]].point);
}
glEnd();
}
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
glVertexPointer(3, GL_FLOAT, sizeof(ItrPaddedPoint), mVehiclePoints.address());
glColor3f(1, 0, 0);
for (i = 0; i < mVehicleNullSurfaces.size(); i++) {
const NullSurface& rSurface = mNullSurfaces[i];
glDrawElements(GL_TRIANGLE_FAN, rSurface.windingCount, GL_UNSIGNED_INT, &mVehicleWindings[rSurface.windingStart]);
}
glEnable(GL_TEXTURE_2D);
}
// void Interior::debugShowVertexColors(MaterialList* /*pMaterials*/)
// {
// glDisable(GL_TEXTURE_2D);
// glBlendFunc(GL_ONE, GL_ZERO);
// for (U32 i = 0; i < sgActivePolyListSize; i++) {
// const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
// glBegin(GL_TRIANGLE_STRIP);
// for (U32 j = rSurface.windingStart; j < rSurface.windingStart + rSurface.windingCount; j++)
// {
// const ItrPaddedPoint& rPoint = mPoints[mWindings[j]];
// glColor3ub(mVertexColorsNormal[j].red,
// mVertexColorsNormal[j].green,
// mVertexColorsNormal[j].blue);
// glVertex3fv(rPoint.point);
// }
// glEnd();
// }
// if (smFocusedDebug == false) {
// glColor3f(0, 0, 0);
// for (U32 i = 0; i < sgActivePolyListSize; i++) {
// const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
// lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
// }
// }
// glEnable(GL_TEXTURE_2D);
// }
void Interior::debugShowDetailLevel()
{
static U8 colors[14][3] = {
{ 0xFF, 0xFF, 0xFF },
{ 0x00, 0x00, 0xFF },
{ 0x00, 0xFF, 0x00 },
{ 0xFF, 0x00, 0x00 },
{ 0xFF, 0xFF, 0x00 },
{ 0xFF, 0x00, 0xFF },
{ 0x00, 0xFF, 0xFF },
{ 0x80, 0x80, 0x80 },
{ 0xFF, 0x80, 0x80 },
{ 0x80, 0xFF, 0x80 },
{ 0x80, 0x80, 0xFF },
{ 0x80, 0xFF, 0xFF },
{ 0xFF, 0x80, 0xFF },
{ 0xFF, 0x80, 0x80 }
};
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
for (U32 i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
glColor3ubv(colors[getDetailLevel()]);
glDrawElements(GL_TRIANGLE_STRIP, rSurface.windingCount, GL_UNSIGNED_INT, &mWindings[rSurface.windingStart]);
}
if (smFocusedDebug == false) {
glColor3f(0, 0, 0);
for (U32 i = 0; i < sgActivePolyListSize; i++) {
const Surface& rSurface = mSurfaces[sgActivePolyList[i]];
lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount);
}
}
glEnable(GL_TEXTURE_2D);
}

1501
engine/interior/interiorIO.cc Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,465 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _INTERIORINSTANCE_H_
#define _INTERIORINSTANCE_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
#ifndef _RESMANAGER_H_
#include "core/resManager.h"
#endif
#ifndef _INTERIORRES_H_
#include "interior/interiorRes.h"
#endif
#ifndef _INTERIORLMMANAGER_H_
#include "interior/interiorLMManager.h"
#endif
#ifndef _BITVECTOR_H_
#include "core/bitVector.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
class AbstractPolyList;
class LightUpdateGrouper;
class InteriorSubObject;
class InteriorResTrigger;
class MaterialList;
class TextureObject;
class FloorPlan;
class Convex;
class AudioProfile;
class AudioEnvironment;
//--------------------------------------------------------------------------
class InteriorInstance : public SceneObject
{
typedef SceneObject Parent;
friend class SceneLighting;
friend class FloorPlan;
public:
InteriorInstance();
~InteriorInstance();
S32 getSurfaceZone(U32 surfaceindex, Interior *detail);
void processLightSurfaceList(U32 *lightSurfaces, U32 *numLightSurfaces,
InteriorInstance *interiorinstance, Interior *detail, ::LightInfo *light);
static void init();
static void destroy();
// Collision
public:
bool buildPolyList(AbstractPolyList *polyList, const Box3F &box, const SphereF &sphere);
bool castRay(const Point3F &start, const Point3F &end, RayInfo *info);
virtual void setTransform(const MatrixF &mat);
void buildConvex(const Box3F& box,Convex* convex);
private:
Convex* mConvexList;
public:
/// @name Lighting control
/// @{
/// This returns true if the interior is in an alarm state. Alarm state
/// will put different lighting into the interior and also possibly
/// have an audio element also.
bool inAlarmState() {return(mAlarmState);}
/// This sets the alarm mode of the interior.
/// @param alarm If true the interior will be in an alarm state next frame
void setAlarmMode(const bool alarm);
/// Activates a light with the given name on all detail levels of the interior
/// @param pLightName Name of the light
void activateLight(const char* pLightName);
/// Deactivates a light with the given name on all detail levels of the interior
/// @param pLightName Name of the light
void deactivateLight(const char* pLightName);
/// Echos out all the lights in the interior, starting with triggerable then
/// animated lights
void echoTriggerableLights();
/// @}
public:
/// @name Subobject access interface
/// @{
/// Returns the number of detail levels for an object
U32 getNumDetailLevels();
/// Gets the interior associated with a particular detail level
/// @param level Detail level
Interior* getDetailLevel(const U32 level);
/// Sets the detail level to render manually
/// @param level Detail level to force
void setDetailLevel(S32 level = -1) { mForcedDetailLevel = level; }
/// @}
// Material management for overlays
public:
/// Reloads material information if the interior skin changes
void renewOverlays();
/// Sets the interior skin to something different
/// @param newBase New base skin
void setSkinBase(const char *newBase);
public:
static bool smDontRestrictOutside;
static F32 smDetailModification;
DECLARE_CONOBJECT(InteriorInstance);
static void initPersistFields();
static void consoleInit();
void onStaticModified( const char* slotName );
/// Reads the lightmaps of the interior into the provided pointer
/// @param lightmaps Lightmaps in the interior (out)
bool readLightmaps(GBitmap ****lightmaps);
protected:
bool onAdd();
void onRemove();
void inspectPreApply();
void inspectPostApply();
static U32 smLightUpdatePeriod;
static bool smRenderDynamicLights;
U32 mLightUpdatedTime;
void setLightUpdatedTime(const U32);
U32 getLightUpdatedTime() const;
bool onSceneAdd(SceneGraph *graph);
void onSceneRemove();
U32 getPointZone(const Point3F& p);
bool getOverlappingZones(SceneObject* obj, U32* zones, U32* numZones);
bool getLightingAmbientColor(ColorF * col)
{
*col = ColorF(1.0, 1.0, 1.0);
return true;
}
U32 calcDetailLevel(SceneState*, const Point3F&);
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
void renderObject(SceneState *state, SceneRenderImage *image);
bool scopeObject(const Point3F& rootPosition,
const F32 rootDistance,
bool* zoneScopeState);
public:
/// This is used to store the preferred lighting method for this interior. It is networked.
bool mUseGLLighting;
/// This indicates what we're actually doing; that way we can bump things to use GL lighting when they are moved.
bool mDoSimpleDynamicRender;
/// Not yet implemented
void addChildren();
/// Returns true if the interiors are rendering dynamic lighting
static bool getRenderDynamicLights() { return(smRenderDynamicLights); }
/// Turns on or off dynamic lighting of interiors
/// @param val If true dynamic lighting is enabled
static void setRenderDynamicLights(bool val) { smRenderDynamicLights = val; }
private:
/// @name Light utility methods
/// These should not be called directly. Use the public activateLight(const char *)
/// method instead because unless the detail level is rendering and it's much
/// easier to not manage the lights on a per-detail level basis.
/// @{
/// Activates a specific light for a detail level
/// @param detail Detail level
/// @param lightIndex Index of light in light list
void activateLight(const U32 detail, const U32 lightIndex);
/// Deactivates a specific light for a detail level
/// @param detail Detail level
/// @param lightIndex Index of light in the light list
void deactivateLight(const U32 detail, const U32 lightIndex);
/// @}
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
enum UpdateMaskBits {
InitMask = BIT(0),
TransformMask = BIT(1),
AlarmMask = BIT(2),
// Reserved for light updates (8 bits for now)
_lightupdate0 = BIT(3),
_lightupdate1 = BIT(4),
_lightupdate2 = BIT(5),
_lightupdate3 = BIT(6),
_lightupdate4 = BIT(7),
_lightupdate5 = BIT(8),
_lightupdate6 = BIT(9),
_lightupdate7 = BIT(10),
SkinBaseMask = BIT(11),
AudioMask = BIT(12),
NextFreeMask = BIT(13)
};
enum Constants {
LightUpdateBitStart = 3,
LightUpdateBitEnd = 10
};
private:
StringTableEntry mInteriorFileName; ///< File name of the interior this instance encapuslates
U32 mInteriorFileHash; ///< Hash for interior file name, used for sorting
Resource<InteriorResource> mInteriorRes; ///< Interior managed by resource manager
Vector<MaterialList*> mMaterialMaps; ///< Materials for this interior
StringTableEntry mSkinBase; ///< Skin for this interior
Vector< Vector<InteriorSubObject*> > mInteriorSubObjects; ///< Sub objects of this interior
bool mShowTerrainInside; ///< Enables or disables terrain showing through the interior
LM_HANDLE mLMHandle; ///< Handle to the light manager
AudioProfile * mAudioProfile; ///< Audio profile
AudioEnvironment * mAudioEnvironment; ///< Audio environment
S32 mForcedDetailLevel; ///< Forced LOD, if -1 auto LOD
U32 mCRC; ///< CRC for the interior
public:
/// Returns the Light Manager handle
LM_HANDLE getLMHandle() { return(mLMHandle); }
/// Returns the audio profile
AudioProfile * getAudioProfile() { return(mAudioProfile); }
/// Returns the audio environment
AudioEnvironment * getAudioEnvironment() { return(mAudioEnvironment); }
/// This is used to determine just how 'inside' a point is in an interior.
/// This is used by the environmental audio code for audio properties and the
/// function always returns true.
/// @param pos Point to test
/// @param pScale How inside is the point 0 = totally outside, 1 = totally inside (out)
bool getPointInsideScale(const Point3F & pos, F32 * pScale); // ~0: outside -> 1: inside
/// Returns the interior resource
Resource<InteriorResource> & getResource() {return(mInteriorRes);} // SceneLighting::InteriorProxy interface
/// Returns the CRC for validation
U32 getCRC() { return(mCRC); }
/// @name Vertex Lighting
/// Vertex lighting is the alternative to lightmapped interiors
/// @{
Vector<Vector<ColorI>*> mVertexColorsNormal; ///< Vertex colors under normal lighting per detail level
Vector<Vector<ColorI>*> mVertexColorsAlarm; ///< Vertex colors under alarm lighting per detail level
/// Rebuilds the vertex colors for alarm and normal states for all detail levels
void rebuildVertexColors();
/// Returns the normal vertex lighting colors for a detail level
/// @param detail Detail level
Vector<ColorI>* getVertexColorsNormal(U32 detail);
/// Returns the alarm vertex lighting colors for a detail level
/// @param detail Detail level
Vector<ColorI>* getVertexColorsAlarm(U32 detail);
/// @}
// Alarm state information
private:
enum AlarmState {
Normal = 0,
Alarm = 1
};
bool mAlarmState; ///< Alarm state of the interior
// LightingAnimation information
private:
struct LightInfo {
struct Light {
U32 curState;
U32 curTime;
ColorI curColor;
bool active;
bool alarm;
};
struct StateDataInfo {
ColorI curColor;
U8* curMap;
bool alarm;
};
Vector<Light> mLights;
BitVector mSurfaceInvalid;
Vector<StateDataInfo> mStateDataInfo;
};
Vector<LightInfo> mLightInfo; ///< Light info, one per detail level
LightUpdateGrouper* mUpdateGrouper; ///< Designed to group net updates for lights to reduce traffic
/// @name Light Grouper
/// This is for managing light updates across the network
/// @{
/// Creates an update key for the LightGrouper
/// @param detail Detail level
/// @param lightIndex Index of light in the interior
static U32 makeUpdateKey(const U32 detail, const U32 lightIndex);
/// Takes an update key and returns the detail level part of it
/// @param key Update key
static U32 detailFromUpdateKey(const U32 key);
/// Takes an update key and returns the light index part of it
/// 2param key Update key
static U32 indexFromUpdateKey(const U32 key);
/// @}
/// @name Animated light functions
/// @{
/// Steps the animated light simulation by a delta
/// @param detail Detail level of interior
/// @param lightIndex Index of light to work on
/// @param ms Time delta from last update in miliseconds
void updateLightTime(const U32 detail, const U32 lightIndex, const U32 ms);
/// This loops through all the surfaces in an interior and calls updateLightMap on them
/// @param state SceneState - Not used
/// @param pInterior Interior to operate on
/// @param rLightInfo Light to use
void downloadLightmaps(SceneState *state, Interior *pInterior, LightInfo &rLightInfo);
/// This will set up a particular light in a particular detail level
/// @param detail Detail level
/// @param lightIndex Light to install
void installLight(const U32 detail, const U32 lightIndex);
/// Called by updateLightTime to update a light with a looping animation
/// @param interior Interior to work on
/// @param light Light to update
/// @param lightIndex Index of animated light
/// @param ms Time delta from last update in miliseconds
void updateLoopingLight(Interior *interior, LightInfo::Light &light, const U32 lightIndex, const U32 ms);
/// Called by updateLightTime to update a light with a flicker animation
/// @param interior Interior to work on
/// @param light Light to update
/// @param lightIndex Index of animated light
/// @param ms Time delta from last update in miliseconds
void updateFlickerLight(Interior *interior, LightInfo::Light &light, const U32 lightIndex, const U32 ms);
/// Called by updateLightTime to update a light with a fade-up (ramp) animation light
/// @param interior Interior to work on
/// @param light Light to update
/// @param lightIndex Index of animated light
/// @param ms Time delta from last update in miliseconds
void updateRampLight(Interior *interior, LightInfo::Light &light, const U32 lightIndex, const U32 ms);
/// Updates the animation for all lights
/// @param ms Time delta since last call in ms
void updateAllLights(const U32 ms);
/// Takes the original lightmap and adds the animated lights to it and then
/// binds the texture to it
/// @param pInterior Interior object to map
/// @param rLightInfo Light info to use to update the light map
/// @param surfaceIndex The surface to operate on inside the interior
void updateLightMap(Interior *pInterior, LightInfo &rLightInfo, const U32 surfaceIndex);
/// lightMap is a 24-bit RGB texture, intensityMap is an 8 bit intensity map.
/// This generates lightmap = [lightmap + (intensityMap * color)]
/// @param lightMap Lightmap to operate on (in/out)
/// @param width width of the ligth map
/// @param height hight of the light map
/// @param intensityMap Intensity map
/// @param color Color
void intensityMapMerge(U8* lightMap,
const U32 width, const U32 height,
const U8* intensityMap, const ColorI& color);
/// @}
private:
/// Creates a transform based on an trigger area
/// @param trigger Trigger to create a transform for
/// @param transform Transform generated (out)
void createTriggerTransform(const InteriorResTrigger *trigger, MatrixF *transform);
};
inline void InteriorInstance::setLightUpdatedTime(const U32 now)
{
mLightUpdatedTime = now;
}
inline U32 InteriorInstance::getLightUpdatedTime() const
{
return mLightUpdatedTime;
}
inline U32 InteriorInstance::makeUpdateKey(const U32 detail, const U32 lightIndex)
{
AssertFatal(detail < (1 << 16) && lightIndex < (1 << 16), "Error, out of bounds key params");
return (detail << 16) | (lightIndex & 0x0000FFFF);
}
inline U32 InteriorInstance::detailFromUpdateKey(const U32 key)
{
return (key >> 16) & 0xFFFF;
}
inline U32 InteriorInstance::indexFromUpdateKey(const U32 key)
{
return (key >> 0) & 0xFFFF;
}
inline Vector<ColorI>* InteriorInstance::getVertexColorsNormal(U32 detail)
{
if (bool(mInteriorRes) == false || detail > mInteriorRes->getNumDetailLevels())
return NULL;
return mVertexColorsNormal[detail];
}
inline Vector<ColorI>* InteriorInstance::getVertexColorsAlarm(U32 detail)
{
if (bool(mInteriorRes) == false || detail > mInteriorRes->getNumDetailLevels())
return NULL;
return mVertexColorsAlarm[detail];
}
#endif //_INTERIORBLOCK_H_

View File

@ -0,0 +1,570 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "interior/interiorLMManager.h"
#include "dgl/gTexManager.h"
#include "dgl/gBitmap.h"
#include "platform/platformGL.h"
#include "interior/interiorRes.h"
#include "interior/interiorInstance.h"
#include "interior/interior.h"
#include "sceneGraph/sceneLighting.h"
//------------------------------------------------------------------------------
// Globals
InteriorLMManager gInteriorLMManager;
//------------------------------------------------------------------------------
namespace {
void interiorLMTextureCallback(const U32 eventCode, void * userData)
{
AssertFatal(&gInteriorLMManager == reinterpret_cast<InteriorLMManager*>(userData), "Bunk ptr!");
gInteriorLMManager.processTextureEvent(eventCode);
}
// '<interior>_<instance index>_lm_<lightmap index>.png'
const char * getTextureName(Interior * interior, U32 instance, U32 lightmap)
{
static char buffer[256];
dSprintf(buffer, sizeof(buffer), "%p_%d_lm_%d.png", interior, instance, lightmap);
return(buffer);
}
}
//------------------------------------------------------------------------------
U32 InteriorLMManager::smTextureCallbackKey = U32(-1);
// D3D vertex buffers for Interiors are in here so they get dumped/reallocated
// along with the lightmaps on the texture events
S32 InteriorLMManager::smMTVertexBuffer = -1;
S32 InteriorLMManager::smFTVertexBuffer = -1;
S32 InteriorLMManager::smFMTVertexBuffer = -1;
InteriorLMManager::InteriorLMManager()
{
}
InteriorLMManager::~InteriorLMManager()
{
for(U32 i = 0; i < mInteriors.size(); i++)
removeInterior(LM_HANDLE(i));
}
//------------------------------------------------------------------------------
void InteriorLMManager::init()
{
smTextureCallbackKey = TextureManager::registerEventCallback(interiorLMTextureCallback, &gInteriorLMManager);
}
void InteriorLMManager::destroy()
{
if(smTextureCallbackKey != U32(-1))
{
TextureManager::unregisterEventCallback(smTextureCallbackKey);
smTextureCallbackKey = U32(-1);
}
if (smMTVertexBuffer != -1)
{
if (dglDoesSupportVertexBuffer())
glFreeVertexBufferEXT(smMTVertexBuffer);
else
AssertFatal(false,"Vertex buffer should have already been freed!");
smMTVertexBuffer = -1;
}
if (smFTVertexBuffer != -1)
{
if (dglDoesSupportVertexBuffer())
glFreeVertexBufferEXT(smFTVertexBuffer);
else
AssertFatal(false,"Vertex buffer should have already been freed!");
smFTVertexBuffer = -1;
}
if (smFMTVertexBuffer != -1)
{
if (dglDoesSupportVertexBuffer())
glFreeVertexBufferEXT(smFMTVertexBuffer);
else
AssertFatal(false,"Vertex buffer should have already been freed!");
smFMTVertexBuffer = -1;
}
}
void InteriorLMManager::processTextureEvent(U32 eventCode)
{
switch(eventCode)
{
case TextureManager::BeginZombification:
purgeGLTextures();
if (smMTVertexBuffer != -1)
{
if (dglDoesSupportVertexBuffer())
glFreeVertexBufferEXT(smMTVertexBuffer);
else
AssertFatal(false,"Vertex buffer should have already been freed!");
smMTVertexBuffer = -1;
}
if (smFTVertexBuffer != -1)
{
if (dglDoesSupportVertexBuffer())
glFreeVertexBufferEXT(smFTVertexBuffer);
else
AssertFatal(false,"Vertex buffer should have already been freed!");
smFTVertexBuffer = -1;
}
if (smFMTVertexBuffer != -1)
{
if (dglDoesSupportVertexBuffer())
glFreeVertexBufferEXT(smFMTVertexBuffer);
else
AssertFatal(false,"Vertex buffer should have already been freed!");
smFMTVertexBuffer = -1;
}
break;
case TextureManager::CacheResurrected:
// relighting the scene will take care of things for us
if(mInteriors.size())
SceneLighting::lightScene(0, SceneLighting::LoadOnly);
break;
}
}
//------------------------------------------------------------------------------
void InteriorLMManager::addInterior(LM_HANDLE & interiorHandle, U32 numLightmaps, Interior * interior)
{
interiorHandle = mInteriors.size();
mInteriors.increment();
mInteriors.last() = new InteriorLMInfo;
mInteriors.last()->mInterior = interior;
mInteriors.last()->mHandlePtr = &interiorHandle;
mInteriors.last()->mNumLightmaps = numLightmaps;
// create base instance
addInstance(interiorHandle, mInteriors.last()->mBaseInstanceHandle, 0);
AssertFatal(mInteriors.last()->mBaseInstanceHandle == LM_HANDLE(0), "InteriorLMManager::addInterior: invalid base instance handle");
// steal the lightmaps from the interior
Vector<TextureHandle*>& texHandles = getHandles(interiorHandle, 0);
for(U32 i = 0; i < interior->mLightmaps.size(); i++)
{
AssertFatal(interior->mLightmaps[i], "InteriorLMManager::addInterior: interior missing lightmap");
texHandles[i] = new TextureHandle(getTextureName(interior, 0, i), interior->mLightmaps[i], BitmapNoDownloadTexture);
}
interior->mLightmaps.clear();
}
void InteriorLMManager::removeInterior(LM_HANDLE interiorHandle)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::removeInterior: invalid interior handle");
AssertFatal(mInteriors[interiorHandle]->mInstances.size() == 1, "InteriorLMManager::removeInterior: cannot remove base interior");
// remove base instance
removeInstance(interiorHandle, 0);
*mInteriors[interiorHandle]->mHandlePtr = LM_HANDLE(-1);
delete mInteriors[interiorHandle];
// last one? otherwise move it
if((mInteriors.size()-1) != interiorHandle)
{
mInteriors[interiorHandle] = mInteriors.last();
*(mInteriors[interiorHandle]->mHandlePtr) = interiorHandle;
}
mInteriors.decrement();
}
//------------------------------------------------------------------------------
void InteriorLMManager::addInstance(LM_HANDLE interiorHandle, LM_HANDLE & instanceHandle, InteriorInstance * instance)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::addInstance: invalid interior handle");
AssertFatal(interiorHandle == *(mInteriors[interiorHandle]->mHandlePtr), "InteriorLMManager::addInstance: invalid handle value");
InteriorLMInfo * interiorInfo = mInteriors[interiorHandle];
// create the instance info and fill
InstanceLMInfo * instanceInfo = new InstanceLMInfo;
instanceInfo->mInstance = instance;
instanceInfo->mHandlePtr = &instanceHandle;
instanceHandle = interiorInfo->mInstances.size();
interiorInfo->mInstances.push_back(instanceInfo);
// create/clear list
instanceInfo->mLightmapHandles.setSize(interiorInfo->mNumLightmaps);
dMemset(instanceInfo->mLightmapHandles.address(), 0, sizeof(TextureHandle*) * instanceInfo->mLightmapHandles.size());
}
void InteriorLMManager::removeInstance(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::removeInstance: invalid interior handle");
AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::removeInstance: invalid instance handle");
AssertFatal(!(instanceHandle == mInteriors[interiorHandle]->mBaseInstanceHandle &&
mInteriors[interiorHandle]->mInstances.size() > 1), "InteriorLMManager::removeInstance: invalid base instance");
InteriorLMInfo * itrInfo = mInteriors[interiorHandle];
// kill it
InstanceLMInfo * instInfo = itrInfo->mInstances[instanceHandle];
for(U32 i = 0; i < instInfo->mLightmapHandles.size(); i++)
delete instInfo->mLightmapHandles[i];
// reset on last instance removal only (multi detailed shapes share the same instance handle)
if(itrInfo->mInstances.size() == 1)
*instInfo->mHandlePtr = LM_HANDLE(-1);
delete instInfo;
// last one? otherwise move it
if((itrInfo->mInstances.size()-1) != instanceHandle)
{
itrInfo->mInstances[instanceHandle] = itrInfo->mInstances.last();
*(itrInfo->mInstances[instanceHandle]->mHandlePtr) = instanceHandle;
}
itrInfo->mInstances.decrement();
}
void InteriorLMManager::useBaseTextures(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::useBaseTextures: invalid interior handle");
AssertFatal(interiorHandle == *(mInteriors[interiorHandle]->mHandlePtr), "InteriorLMManager::useBaseTextures: invalid handle value");
// Make sure the base light maps are loaded
loadBaseLightmaps(interiorHandle,instanceHandle);
// Install base lightmaps for this instance...
Vector<TextureHandle*>& baseHandles = getHandles(interiorHandle, 0);
Vector<TextureHandle*>& texHandles = getHandles(interiorHandle, instanceHandle);
for(U32 i = 0; i < baseHandles.size(); i++)
texHandles[i] = new TextureHandle(*baseHandles[i]);
}
//------------------------------------------------------------------------------
void InteriorLMManager::destroyBitmaps()
{
for(S32 i = mInteriors.size() - 1; i >= 0; i--)
{
InteriorLMInfo * interiorInfo = mInteriors[i];
for(S32 j = interiorInfo->mInstances.size() - 1; j >= 0; j--)
{
InstanceLMInfo * instanceInfo = interiorInfo->mInstances[j];
for(S32 k = instanceInfo->mLightmapHandles.size() - 1; k >= 0; k--)
{
if(!instanceInfo->mLightmapHandles[k])
continue;
TextureObject * texObj = *instanceInfo->mLightmapHandles[k];
if(!texObj || !texObj->bitmap)
continue;
// don't remove 'keep' bitmaps
if(!interiorInfo->mInterior->mLightmapKeep[k])
{
delete texObj->bitmap;
texObj->bitmap = 0;
}
}
}
}
}
void InteriorLMManager::destroyTextures()
{
for(S32 i = mInteriors.size() - 1; i >= 0; i--)
{
InteriorLMInfo * interiorInfo = mInteriors[i];
for(S32 j = interiorInfo->mInstances.size() - 1; j >= 0; j--)
{
InstanceLMInfo * instanceInfo = interiorInfo->mInstances[j];
for(S32 k = interiorInfo->mNumLightmaps - 1; k >= 0; k--)
{
// will want to remove the vector here eventually... so don't clear
delete instanceInfo->mLightmapHandles[k];
instanceInfo->mLightmapHandles[k] = 0;
}
}
}
}
void InteriorLMManager::purgeGLTextures()
{
Vector<GLuint> purgeList(4096);
for(S32 i = mInteriors.size() - 1; i >= 0; i--)
{
InteriorLMInfo * interiorInfo = mInteriors[i];
for(S32 j = interiorInfo->mInstances.size() - 1; j >= 0; j--)
{
InstanceLMInfo * instanceInfo = interiorInfo->mInstances[j];
for(S32 k = instanceInfo->mLightmapHandles.size() - 1; k >= 0; k--)
{
if(!instanceInfo->mLightmapHandles[k])
continue;
TextureObject * texObj = *instanceInfo->mLightmapHandles[k];
if(!texObj || !texObj->texGLName)
continue;
#ifdef TORQUE_GATHER_METRICS
AssertFatal(texObj->textureSpace <= TextureManager::smTextureSpaceLoaded, "Doh!");
TextureManager::smTextureSpaceLoaded -= texObj->textureSpace;
texObj->textureSpace = 0;
#endif
purgeList.push_back(texObj->texGLName);
texObj->texGLName = 0;
}
}
}
glDeleteTextures(purgeList.size(), purgeList.address());
}
void InteriorLMManager::downloadGLTextures()
{
for(S32 i = mInteriors.size() - 1; i >= 0; i--)
downloadGLTextures(i);
}
void InteriorLMManager::downloadGLTextures(LM_HANDLE interiorHandle)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::downloadGLTextures: invalid interior handle");
InteriorLMInfo * interiorInfo = mInteriors[interiorHandle];
// The bit vector is used to keep track of which lightmap sets need
// to be loaded from the shared "base" instance. Every instance
// can have it's own lightmap set due to mission lighting.
BitVector needTexture;
needTexture.setSize(interiorInfo->mNumLightmaps);
needTexture.clear();
for(S32 j = interiorInfo->mInstances.size() - 1; j >= 0; j--)
{
InstanceLMInfo * instanceInfo = interiorInfo->mInstances[j];
for(S32 k = instanceInfo->mLightmapHandles.size() - 1; k >= 0; k--)
{
// All instances can share the base instances static lightmaps.
// Test here to see if we need to load those lightmaps.
if ((j == 0) && !needTexture.test(k))
continue;
if (!instanceInfo->mLightmapHandles[k]) {
needTexture.set(k);
continue;
}
TextureObject * texObj = *instanceInfo->mLightmapHandles[k];
if (!texObj || !texObj->bitmap) {
needTexture.set(k);
continue;
}
// Make sure this one is not already loaded.
if (texObj->texGLName)
continue;
// Go ahead and download this set to GL
#ifdef TORQUE_GATHER_METRICS
texObj->textureSpace = texObj->downloadedWidth * texObj->downloadedHeight;
TextureManager::smTextureSpaceLoaded += texObj->textureSpace;
#endif
TextureManager::createGLName(texObj->bitmap, texObj->clamp, 0, texObj->type, texObj);
}
}
}
bool InteriorLMManager::loadBaseLightmaps(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::loadBaseLightmaps: invalid interior handle");
AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::loadBaseLightmaps: invalid instance handle");
// must use a valid instance handle
if(!instanceHandle)
return(false);
InteriorLMInfo * interiorInfo = mInteriors[interiorHandle];
if(!interiorInfo->mNumLightmaps)
return(false);
InstanceLMInfo * baseInstanceInfo = interiorInfo->mInstances[0];
// already loaded? (if any bitmap is present, then assumed that all will be)
TextureHandle * texture = baseInstanceInfo->mLightmapHandles[0];
if(texture && texture->getBitmap())
return(true);
InstanceLMInfo * instanceInfo = interiorInfo->mInstances[instanceHandle];
Resource<InteriorResource> & interiorRes = instanceInfo->mInstance->getResource();
if(!bool(interiorRes))
return(false);
GBitmap *** pBitmaps = 0;
if(!instanceInfo->mInstance->readLightmaps(&pBitmaps))
return(false);
for(U32 i = 0; i < interiorRes->getNumDetailLevels(); i++)
{
Interior * interior = interiorRes->getDetailLevel(i);
AssertFatal(interior, "InteriorLMManager::loadBaseLightmaps: invalid detail level in resource");
AssertFatal(interior->getLMHandle() != LM_HANDLE(-1), "InteriorLMManager::loadBaseLightmaps: interior not added to manager");
AssertFatal(interior->getLMHandle() < mInteriors.size(), "InteriorLMManager::loadBaseLightmaps: invalid interior");
InteriorLMInfo * interiorInfo = mInteriors[interior->getLMHandle()];
InstanceLMInfo * baseInstanceInfo = interiorInfo->mInstances[0];
for(U32 j = 0; j < interiorInfo->mNumLightmaps; j++)
{
AssertFatal(pBitmaps[i][j], "InteriorLMManager::loadBaseLightmaps: invalid bitmap");
if (baseInstanceInfo->mLightmapHandles[j])
{
TextureObject * texObj = *baseInstanceInfo->mLightmapHandles[j];
texObj->bitmap = pBitmaps[i][j];
}
else
baseInstanceInfo->mLightmapHandles[j] = new TextureHandle(getTextureName(interior, 0, j), pBitmaps[i][j], BitmapNoDownloadTexture);
}
delete [] pBitmaps[i];
}
delete [] pBitmaps;
return(true);
}
//------------------------------------------------------------------------------
TextureHandle * InteriorLMManager::getHandle(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::getHandle: invalid interior handle");
AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::getHandle: invalid instance handle");
AssertFatal(index < mInteriors[interiorHandle]->mNumLightmaps, "InteriorLMManager::getHandle: invalid texture index");
// valid? if not, then get base lightmap handle
if(!mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index])
{
AssertFatal(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index], "InteriorLMManager::getHandle: invalid base texture handle");
return(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index]);
}
return(mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index]);
}
GBitmap * InteriorLMManager::getBitmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::getBitmap: invalid interior handle");
AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::getBitmap: invalid instance handle");
AssertFatal(index < mInteriors[interiorHandle]->mNumLightmaps, "InteriorLMManager::getBitmap: invalid texture index");
if(!mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index])
{
AssertFatal(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index], "InteriorLMManager::getBitmap: invalid base texture handle");
return(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index]->getBitmap());
}
return(mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index]->getBitmap());
}
Vector<TextureHandle*> & InteriorLMManager::getHandles(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::getHandles: invalid interior handle");
AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::getHandles: invalid instance handle");
return(mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles);
}
//------------------------------------------------------------------------------
U32 InteriorLMManager::getNumLightmaps(LM_HANDLE interiorHandle)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::getNumLightmaps: invalid interior handle");
return(mInteriors[interiorHandle]->mNumLightmaps);
}
//------------------------------------------------------------------------------
void InteriorLMManager::deleteLightmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::deleteLightmap: invalid interior handle");
AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::deleteLightmap: invalid instance handle");
AssertFatal(index < mInteriors[interiorHandle]->mNumLightmaps, "InteriorLMManager::deleteLightmap: invalid texture index");
delete mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index];
mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index] = 0;
}
void InteriorLMManager::clearLightmaps(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::clearLightmaps: invalid interior handle");
AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::clearLightmaps: invalid instance handle");
for(U32 i = 0; i < mInteriors[interiorHandle]->mNumLightmaps; i++)
{
delete mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[i];
mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[i] = 0;
}
}
//------------------------------------------------------------------------------
TextureHandle * InteriorLMManager::duplicateBaseLightmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index)
{
AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::duplicateBaseLightmap: invalid interior handle");
AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::duplicateBaseLightmap: invalid instance handle");
AssertFatal(index < mInteriors[interiorHandle]->mNumLightmaps, "InteriorLMManager::duplicateBaseLightmap: invalid texture index");
// already exists?
TextureHandle * texHandle = mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index];
if(texHandle && static_cast<TextureObject*>(*texHandle)->bitmap)
return(texHandle);
AssertFatal(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index], "InteriorLMManager::duplicateBaseLightmap: invalid base handle");
// copy it
GBitmap * src = mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index]->getBitmap();
GBitmap * dest = new GBitmap(*src);
// don't want this texture to be downloaded yet (SceneLighting will take care of that)
TextureHandle * tHandle = new TextureHandle(getTextureName(mInteriors[interiorHandle]->mInterior, instanceHandle, index), dest, BitmapNoDownloadTexture);
mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index] = tHandle;
return(tHandle);
}
S32 InteriorLMManager::getVertexBuffer(S32 format)
{
switch (format)
{
case GL_V12MTVFMT_EXT:
{
if (smMTVertexBuffer != -1)
return smMTVertexBuffer;
smMTVertexBuffer = glAllocateVertexBufferEXT(512,GL_V12MTVFMT_EXT,false);
return smMTVertexBuffer;
}
case GL_V12FTVFMT_EXT:
{
if (smFTVertexBuffer != -1)
return smFTVertexBuffer;
smFTVertexBuffer = glAllocateVertexBufferEXT(512,GL_V12FTVFMT_EXT,false);
return smFTVertexBuffer;
}
case GL_V12FMTVFMT_EXT:
{
if (smFMTVertexBuffer != -1)
return smFMTVertexBuffer;
smFMTVertexBuffer = glAllocateVertexBufferEXT(512,GL_V12FMTVFMT_EXT,false);
return smFMTVertexBuffer;
}
}
AssertFatal(false,"InteriorLMManager::getVertexBuffer() What? We should never get here!!!");
return -1;
}

View File

@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _INTERIORLMMANAGER_H_
#define _INTERIORLMMANAGER_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
class TextureHandle;
class GBitmap;
class Interior;
class InteriorInstance;
typedef U32 LM_HANDLE;
class InteriorLMManager
{
private:
struct InstanceLMInfo
{
InteriorInstance * mInstance;
LM_HANDLE * mHandlePtr;
Vector<TextureHandle*> mLightmapHandles;
};
struct InteriorLMInfo
{
Interior * mInterior;
LM_HANDLE * mHandlePtr;
U32 mNumLightmaps;
LM_HANDLE mBaseInstanceHandle;
Vector<InstanceLMInfo*> mInstances;
};
Vector<InteriorLMInfo*> mInteriors;
static S32 smMTVertexBuffer;
static S32 smFTVertexBuffer;
static S32 smFMTVertexBuffer;
public:
static U32 smTextureCallbackKey;
InteriorLMManager();
~InteriorLMManager();
static void init();
static void destroy();
void processTextureEvent(U32 eventCode);
void destroyBitmaps();
void destroyTextures();
void purgeGLTextures();
void downloadGLTextures();
void downloadGLTextures(LM_HANDLE interiorHandle);
bool loadBaseLightmaps(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle);
void addInterior(LM_HANDLE & interiorHandle, U32 numLightmaps, Interior * interior);
void removeInterior(LM_HANDLE interiorHandle);
void addInstance(LM_HANDLE interiorHandle, LM_HANDLE & instanceHandle, InteriorInstance * instance);
void removeInstance(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle);
void useBaseTextures(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle);
U32 getNumLightmaps(LM_HANDLE interiorHandle);
void deleteLightmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index);
void clearLightmaps(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle);
TextureHandle * getHandle(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index);
Vector<TextureHandle*> & getHandles(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle);
// helper's
TextureHandle * duplicateBaseLightmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index);
GBitmap * getBitmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index);
S32 getVertexBuffer(S32 format);
};
extern InteriorLMManager gInteriorLMManager;
#endif

View File

@ -0,0 +1,404 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "interior/interiorInstance.h"
#include "interior/lightUpdateGrouper.h"
#include "interior/interior.h"
#include "math/mRandom.h"
void InteriorInstance::echoTriggerableLights()
{
// DMMFIX: Only the first detail for now...
Interior* pInterior = mInteriorRes->getDetailLevel(0);
Con::printf("Interior: %s", mInteriorFileName);
Con::printf(" %d Triggerable lights:", pInterior->mNumTriggerableLights);
// Triggerable lights are always the first in the array...
for (U32 i = 0; i < pInterior->mNumTriggerableLights; i++) {
const char* pName = pInterior->getName(pInterior->mAnimatedLights[i].nameIndex);
U32 type = pInterior->mAnimatedLights[i].flags & Interior::AnimationTypeMask;
float duration = pInterior->mAnimatedLights[i].duration;
U32 numStates = pInterior->mAnimatedLights[i].stateCount;
Con::printf(" - %s [%s, Duration: %g, NumStates: %d]",
pName, Interior::getLightTypeString(Interior::LightType(type)),
duration, numStates);
}
}
void InteriorInstance::activateLight(const char* pLightName)
{
if (bool(mInteriorRes) == false) {
AssertWarn(false, "Activating a light on an unloaded interior!");
return;
}
// Now, it's a real pain in the ass to try to keep track of light states on detail
// changes as we did in tribes 1. There, we analyzed the state on a detail change
// and tried to duplicate that state on the detail level we were switching to.
// Inspiration: forget that, and just animate the lights on all the details all
// the time. Unless the detail is rendering, the lightmap data will never be
// downloaded, and the amount of time necessary to keep the lights updated on
// a detail level is absolutely miniscule. Much easier.
//
for (U32 i = 0; i < mInteriorRes->getNumDetailLevels(); i++) {
Interior* pInterior = mInteriorRes->getDetailLevel(i);
for (U32 j = 0; j < pInterior->mNumTriggerableLights; j++) {
const char* pILightName = pInterior->getName(pInterior->mAnimatedLights[j].nameIndex);
if (dStricmp(pLightName, pILightName) == 0) {
activateLight(i, j);
break;
}
}
}
}
void InteriorInstance::deactivateLight(const char* pLightName)
{
if (bool(mInteriorRes) == false) {
AssertWarn(false, "Deactivating a light on an unloaded interior!");
return;
}
for (U32 i = 0; i < mInteriorRes->getNumDetailLevels(); i++) {
Interior* pInterior = mInteriorRes->getDetailLevel(i);
for (U32 j = 0; j < pInterior->mNumTriggerableLights; j++) {
const char* pILightName = pInterior->getName(pInterior->mAnimatedLights[j].nameIndex);
if (dStricmp(pLightName, pILightName) == 0) {
deactivateLight(i, j);
break;
}
}
}
}
void InteriorInstance::updateAllLights(const U32 ms)
{
if (bool(mInteriorRes) == false)
return;
for (U32 i = 0; i < mInteriorRes->getNumDetailLevels(); i++) {
LightInfo& rLightInfo = mLightInfo[i];
for (U32 j = 0; j < rLightInfo.mLights.size(); j++) {
if (mAlarmState == Normal) {
if (rLightInfo.mLights[j].active == true && rLightInfo.mLights[j].alarm == false)
updateLightTime(i, j, ms);
} else {
if (rLightInfo.mLights[j].alarm == true)
updateLightTime(i, j, ms);
}
}
}
}
//--------------------------------------------------------------------------
void InteriorInstance::activateLight(const U32 detail, const U32 lightIndex)
{
AssertFatal(bool(mInteriorRes) && detail < mInteriorRes->getNumDetailLevels(), "Error, no interior resource, or out of range detail level");
AssertFatal(lightIndex < mInteriorRes->getDetailLevel(detail)->mAnimatedLights.size(), "Error, out of bounds light index");
LightInfo& rLightInfo = mLightInfo[detail];
LightInfo::Light& rLight = rLightInfo.mLights[lightIndex];
if (rLight.active == false) {
rLight.active = true;
rLight.curState = 0;
rLight.curTime = 0;
Interior* pInterior = mInteriorRes->getDetailLevel(detail);
Interior::LightState& rState = pInterior->mLightStates[pInterior->mAnimatedLights[lightIndex].stateIndex];
rLight.curColor.set(rState.red, rState.green, rState.blue);
installLight(detail, lightIndex);
if (isServerObject() && lightIndex < pInterior->mNumTriggerableLights) {
U32 key = makeUpdateKey(detail, lightIndex);
U32 mask = mUpdateGrouper->getKeyMask(key);
setMaskBits(mask);
}
} else {
// Light is already active, no need to play around further...
//
}
}
//--------------------------------------------------------------------------
void InteriorInstance::deactivateLight(const U32 detail, const U32 lightIndex)
{
AssertFatal(bool(mInteriorRes) && detail < mInteriorRes->getNumDetailLevels(), "Error, no interior resource, or out of range detail level");
AssertFatal(lightIndex < mInteriorRes->getDetailLevel(detail)->mAnimatedLights.size(), "Error, out of bounds light index");
LightInfo& rLightInfo = mLightInfo[detail];
LightInfo::Light& rLight = rLightInfo.mLights[lightIndex];
if (rLight.active == true) {
// DMMFIX
rLight.active = false;
rLight.curState = 0;
rLight.curTime = 0;
Interior* pInterior = mInteriorRes->getDetailLevel(detail);
Interior::LightState& rState = pInterior->mLightStates[pInterior->mAnimatedLights[lightIndex].stateIndex];
rLight.curColor.set(rState.red, rState.green, rState.blue);
installLight(detail, lightIndex);
if (isServerObject() && lightIndex < pInterior->mNumTriggerableLights) {
U32 key = makeUpdateKey(detail, lightIndex);
U32 mask = mUpdateGrouper->getKeyMask(key);
setMaskBits(mask);
}
} else {
// Light is already inactive, no need to play around further...
//
}
}
//--------------------------------------------------------------------------
void InteriorInstance::updateLightTime(const U32 detail, const U32 lightIndex, const U32 ms)
{
AssertFatal(bool(mInteriorRes) && detail < mInteriorRes->getNumDetailLevels(), "Error, no interior resource, or out of range detail level");
AssertFatal(lightIndex < mInteriorRes->getDetailLevel(detail)->mAnimatedLights.size(), "Error, out of bounds light index");
LightInfo& rLightInfo = mLightInfo[detail];
Interior* pInterior = mInteriorRes->getDetailLevel(detail);
LightInfo::Light& rLight = rLightInfo.mLights[lightIndex];
Interior::AnimatedLight& rILight = pInterior->mAnimatedLights[lightIndex];
U32 oldState = rLight.curState;
ColorI oldColor = rLight.curColor;
// Ok, now we need to break this down a bit. We pass the update along to
// the specialized updating functions based on lightType.
switch (rILight.flags & Interior::AnimationTypeMask) {
case Interior::AmbientLooping:
case Interior::TriggerableLoop:
updateLoopingLight(pInterior, rLight, lightIndex, ms);
break;
case Interior::AmbientFlicker:
case Interior::TriggerableFlicker:
updateFlickerLight(pInterior, rLight, lightIndex, ms);
break;
case Interior::TriggerableRamp:
updateRampLight(pInterior, rLight, lightIndex, ms);
break;
default:
AssertFatal(false, "Bad light type in updateLightTime");
}
if (rLight.curState != oldState ||
rLight.curColor != oldColor) {
// Need to reinstall the light
installLight(detail, lightIndex);
}
}
//--------------------------------------------------------------------------
void InteriorInstance::updateLoopingLight(Interior* interior, LightInfo::Light& light,
const U32 lightIndex, const U32 ms)
{
AssertISV( lightIndex < interior->mAnimatedLights.size( ), "out of bounds array access in InteriorInstance::updateLoopingLight" );
Interior::AnimatedLight& rILight = interior->mAnimatedLights[lightIndex];
light.curTime += ms;
light.curTime %= rILight.duration;
// Find the last state that has a active time below this new time...
light.curState = 0;
for (U32 i = 1; i < rILight.stateCount; i++) {
Interior::LightState& rState = interior->mLightStates[rILight.stateIndex + i];
if (rState.activeTime <= light.curTime)
light.curState = i;
else
break;
}
// interpolate the color
Interior::LightState& rState = interior->mLightStates[rILight.stateIndex + light.curState];
Interior::LightState* pNextState;
U32 msIntoState = light.curTime - rState.activeTime;
U32 msTotal;
if (light.curState != (rILight.stateCount - 1)) {
// Have one more good state
pNextState = &interior->mLightStates[rILight.stateIndex + light.curState + 1];
msTotal = pNextState->activeTime - rState.activeTime;
} else {
// Have to interpolate against the first state...
pNextState = &interior->mLightStates[rILight.stateIndex];
msTotal = rILight.duration - rState.activeTime;
}
F32 interp = F32(msIntoState) / F32(msTotal);
F32 red = F32(rState.red) * (1.0f - interp) + F32(pNextState->red) * interp;
F32 green = F32(rState.green) * (1.0f - interp) + F32(pNextState->green) * interp;
F32 blue = F32(rState.blue) * (1.0f - interp) + F32(pNextState->blue) * interp;
light.curColor.set(U8(red + 0.5f), U8(green + 0.5f), U8(blue + 0.5f));
}
//--------------------------------------------------------------------------
void InteriorInstance::updateFlickerLight(Interior* interior, LightInfo::Light& light,
const U32 lightIndex, const U32 ms)
{
Interior::AnimatedLight& rILight = interior->mAnimatedLights[lightIndex];
U32 switchPeriod = interior->mLightStates[interior->mAnimatedLights[lightIndex].stateIndex + 1].activeTime;
U32 oldTime = light.curTime;
light.curTime += ms;
if (light.curTime < switchPeriod)
return;
light.curTime = 0;
// Ok, pick a random number from 0 to the light duration, and find the state that
// it falls in.
static MRandomLCG randomGen;
U32 pickedTime = randomGen.randI(0, rILight.duration);
light.curState = 0;
for (U32 i = 1; i < rILight.stateCount; i++) {
Interior::LightState& rState = interior->mLightStates[rILight.stateIndex + i];
if (rState.activeTime <= pickedTime)
light.curState = i;
else
break;
}
Interior::LightState& rState = interior->mLightStates[rILight.stateIndex + light.curState];
light.curColor.set(rState.red, rState.green, rState.blue);
}
//--------------------------------------------------------------------------
void InteriorInstance::updateRampLight(Interior* interior, LightInfo::Light& light,
const U32 lightIndex, const U32 ms)
{
Interior::AnimatedLight& rILight = interior->mAnimatedLights[lightIndex];
light.curTime += ms;
if (light.curTime > rILight.duration)
light.curTime = rILight.duration;
// Find the last state that has a active time below this new time...
light.curState = 0;
for (U32 i = 1; i < rILight.stateCount; i++) {
Interior::LightState& rState = interior->mLightStates[rILight.stateIndex + i];
if (rState.activeTime <= light.curTime)
light.curState = i;
else
break;
}
// interpolate the color
Interior::LightState& rState = interior->mLightStates[rILight.stateIndex + light.curState];
Interior::LightState* pNextState;
U32 msIntoState = light.curTime - rState.activeTime;
U32 msTotal;
if (light.curState != (rILight.stateCount - 1)) {
// Have one more good state
pNextState = &interior->mLightStates[rILight.stateIndex + light.curState + 1];
msTotal = pNextState->activeTime - rState.activeTime;
} else {
// A ramp light does NOT NOT NOT interp against the first state
pNextState = &rState;
msTotal = msIntoState;
}
F32 interp = F32(msIntoState) / F32(msTotal);
F32 red = F32(rState.red) * (1.0f - interp) + F32(pNextState->red) * interp;
F32 green = F32(rState.green) * (1.0f - interp) + F32(pNextState->green) * interp;
F32 blue = F32(rState.blue) * (1.0f - interp) + F32(pNextState->blue) * interp;
light.curColor.set(U8(red + 0.5f), U8(green + 0.5f), U8(blue + 0.5f));
}
//--------------------------------------------------------------------------
void InteriorInstance::installLight(const U32 detail, const U32 lightIndex)
{
AssertFatal(bool(mInteriorRes) && detail < mInteriorRes->getNumDetailLevels(), "Error, no interior resource, or out of range detail level");
AssertFatal(lightIndex < mInteriorRes->getDetailLevel(detail)->mAnimatedLights.size(), "Error, out of bounds light index");
LightInfo& rLightInfo = mLightInfo[detail];
LightInfo::Light& rLight = rLightInfo.mLights[lightIndex];
// All we are allowed to assume is that the light time, color, and state are
// correct here. We must install all statedata, and invalidate all surfaces.
// First, let's retrieve the actual light from the Interior
//
Interior* pInterior = mInteriorRes->getDetailLevel(detail);
Interior::AnimatedLight& rILight = pInterior->mAnimatedLights[lightIndex];
Interior::LightState& rIState = pInterior->mLightStates[rILight.stateIndex + rLight.curState];
// Ok. Now, cycle through the light's state data, and install it
for (U32 i = rIState.dataIndex; i < (rIState.dataIndex + rIState.dataCount); i++) {
Interior::LightStateData& rIData = pInterior->mStateData[i];
LightInfo::StateDataInfo& rData = rLightInfo.mStateDataInfo[rIData.lightStateIndex];
if (rIData.mapIndex != 0xFFFFFFFF) {
rData.curMap = &pInterior->mStateDataBuffer[rIData.mapIndex];
} else {
rData.curMap = NULL;
}
rData.curColor = rLight.curColor;
rData.alarm = (rILight.flags & Interior::AlarmLight) != 0;
rLightInfo.mSurfaceInvalid.set(rIData.surfaceIndex);
}
}
//--------------------------------------------------------------------------
void InteriorInstance::intensityMapMerge(U8* lightMap,
const U32 width,
const U32 height,
const U8* intensityMap,
const ColorI& color)
{
// lightmap is a 24bit RGB texture, intensitymap is an 8 bit intensity
// map. We want lightmap = [lightmap + (intensityMap * color)]
// DMMFIX: SLOWSLOWSLOW! Need MMX version of this at the very least,
// this version is only for clarity;
for (U32 y = 0; y < height; y++) {
for (U32 x = 0; x < width; x++) {
U8* data = &lightMap[(y * width + x) * 3];
U32 intensity = intensityMap[(y * width + x)];
U32 newRed = data[0];
U32 newGreen = data[1];
U32 newBlue = data[2];
U32 addRed = (U32(color.red) * intensity + 0x80) >> 8;
U32 addGreen = (U32(color.green) * intensity + 0x80) >> 8;
U32 addBlue = (U32(color.blue) * intensity + 0x80) >> 8;
newRed += addRed;
newGreen += addGreen;
newBlue += addBlue;
data[0] = (newRed <= 255) ? U8(newRed) : 0xFF;
data[1] = (newGreen <= 255) ? U8(newGreen) : 0xFF;
data[2] = (newBlue <= 255) ? U8(newBlue) : 0xFF;
}
}
}

784
engine/interior/interiorMap.cc Executable file
View File

@ -0,0 +1,784 @@
#include "interiorMap.h"
#include "dgl/dgl.h"
#include "core/bitStream.h"
#include "game/gameConnection.h"
#include "math/mathIO.h"
#include "console/consoleTypes.h"
#include "collision/concretePolyList.h"
#include "dgl/gBitmap.h"
#include "math/mPlaneTransformer.h"
#define FRONTEPSILON 0.00001
//------------------------------------------------------------------------------
IMPLEMENT_CO_NETOBJECT_V1(InteriorMap);
// Return the bounding box transformed into world space
Box3F InteriorMapConvex::getBoundingBox() const
{
return getBoundingBox(mObject->getTransform(), mObject->getScale());
}
// Transform and scale the bounding box by the inputs
Box3F InteriorMapConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
{
Box3F newBox = box;
newBox.min.convolve(scale);
newBox.max.convolve(scale);
mat.mul(newBox);
return newBox;
}
// Return the point furthest from the input vector
Point3F InteriorMapConvex::support(const VectorF& v) const
{
Point3F ret(0.0f, 0.0f, 0.0f);
F32 dp = 0.0f;
F32 tp = 0.0f;
// Loop through the points and save the furthest one
//for (U32 i = 0; i < 8; i++)
//{
// tp = mDot(v, pOwner->mPoints[i]);
// if (tp > dp)
// {
// dp = tp;
// ret = pOwner->mPoints[i];
// }
//}
return ret;
}
// This function simply checks to see if the edge already exists on the edgelist
// If it doesn't the edge gets added
void InteriorMapConvex::addEdge(U32 zero, U32 one, U32 base, ConvexFeature* cf)
{
U32 newEdge0, newEdge1;
// Sort the vertex indexes by magnitude (lowest number first, largest second)
newEdge0 = getMin(zero, one) + base;
newEdge1 = getMax(zero, one) + base;
// Assume that it isn't found
bool found = false;
// Loop through the edgelist
// Start with base so we don't search *all* of the edges if there already are some on the list
for (U32 k = base; k < cf->mEdgeList.size(); k++)
{
// If we find a match flag found and break out (no need to keep searching)
if (cf->mEdgeList[k].vertex[0] == newEdge0 && cf->mEdgeList[k].vertex[1] == newEdge1)
{
found = true;
break;
}
}
// If we didn't find it then add it
// Otherwise we return without doing anything
if (!found)
{
cf->mEdgeList.increment();
cf->mEdgeList.last().vertex[0] = newEdge0;
cf->mEdgeList.last().vertex[1] = newEdge1;
}
};
// Return the vertices, faces, and edges of the convex
// Used by the vehicle collisions
void InteriorMapConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf)
{
}
// Return list(s) of convex faces
// Used by player collisions
void InteriorMapConvex::getPolyList(AbstractPolyList* list)
{
// Be sure to transform the list into model space
list->setTransform(&mObject->getTransform(), mObject->getScale());
// Set the object
list->setObject(mObject);
// Get the brush
ConvexBrush* brush = pOwner->mInteriorRes->mBrushes[brushIndex];
brush->getPolyList(list);
}
InteriorMap::InteriorMap(void)
{
// Setup NetObject.
// Note that we set this as a TutorialObjectType object
// TutorialObjectType is defined in game/objectTypes.h in the SimObjectTypes enum
// You should also notify the scripting engine of it existance in game/main.cc in initGame()
mTypeMask = StaticObjectType | StaticRenderedObjectType | InteriorMapObjectType;
mNetFlags.set(Ghostable);
// Haven't loaded yet
mLoaded = false;
// Give it a nonexistant bounding box
mObjBox.min.set(0, 0, 0);
mObjBox.max.set(0, 0, 0);
mConvexList = new Convex;
mTexPath = NULL;
mRenderMode = TexShaded;
mEnableLights = true;
mTexHandles = NULL;
}
InteriorMap::~InteriorMap(void)
{
delete mConvexList;
mConvexList = NULL;
}
void InteriorMap::initPersistFields()
{
// Initialise parents' persistent fields.
Parent::initPersistFields();
addField( "File", TypeFilename, Offset( mFileName, InteriorMap ) );
}
bool InteriorMap::onAdd()
{
if(!Parent::onAdd()) return(false);
// Set our path info
setPath(mFileName);
// Load resource
mInteriorRes = ResourceManager->load(mFileName, true);
if (bool(mInteriorRes) == false)
{
Con::errorf(ConsoleLogEntry::General, "Unable to load interior: %s", mFileName);
NetConnection::setLastError("Unable to load interior: %s", mFileName);
return false;
}
else
mLoaded = true;
// Scale our brushes
if (mInteriorRes->mBrushScale != 1.0f)
{
for (U32 i = 0; i < mInteriorRes->mBrushes.size(); i++)
mInteriorRes->mBrushes[i]->setScale(mInteriorRes->mBrushScale);
}
loadTextures();
mInteriorRes->mTexGensCalced = calcTexgenDiv();
// Set the bounds
mObjBox.min.set(1e10, 1e10, 1e10);
mObjBox.max.set(-1e10, -1e10, -1e10);
for (U32 i = 0; i < mInteriorRes->mBrushes.size(); i++)
{
if (mInteriorRes->mBrushes[i]->mStatus == ConvexBrush::Good)
{
// Transform the bounding boxes
MatrixF& mat = mInteriorRes->mBrushes[i]->mTransform;
Point3F& scale = mInteriorRes->mBrushes[i]->mScale;
Point3F min = mInteriorRes->mBrushes[i]->mBounds.min;
Point3F max = mInteriorRes->mBrushes[i]->mBounds.max;
mat.mulP(min);
mat.mulP(max);
min.convolveInverse(scale);
max.convolveInverse(scale);
mObjBox.min.setMin(min);
mObjBox.max.setMax(max);
}
}
mWhite = new TextureHandle("common/lighting/whiteAlpha255", MeshTexture);
// Reset the World Box.
resetWorldBox();
// Set the Render Transform.
setRenderTransform(mObjToWorld);
// Add to Scene.
addToScene();
// Return OK.
return true;
}
bool InteriorMap::setPath(const char* filename)
{
// Save our filename just in case it hasn't already been done
mFileName = StringTable->insert(filename);
// Get the directory
char dir[4096]; // FIXME: no hardcoded lengths
if (dStrrchr(filename, '/'))
{
dStrncpy(dir, filename, (int)(dStrrchr(filename, '/') - filename + 1));
dir[(int)(dStrrchr(filename, '/') - filename + 1)] = 0;
}
else if (dStrrchr(filename, '\\'))
{
dStrncpy(dir, filename, (int)(dStrrchr(filename, '\\') - filename + 1));
dir[(int)(dStrrchr(filename, '\\') - filename + 1)] = 0;
}
else
{
dSprintf(dir, dStrlen(Platform::getWorkingDirectory()) + 1, "%s/\0", Platform::getWorkingDirectory());
}
mFilePath = StringTable->insert(dir);
if (!mTexPath)
mTexPath = mFilePath;
return true;
}
bool InteriorMap::loadTextures()
{
if (mTexHandles)
return true;
// Load our textures
mTexHandles = new TextureHandle[mInteriorRes->mMaterials.size()];
for (int t = 0; t < mInteriorRes->mMaterials.size(); t++)
{
mTexHandles[t] = NULL;
char fullname[8192];
// First go for standard
dSprintf(fullname, 8192, "%s%s\0", mTexPath, mInteriorRes->mMaterials[t]);
mTexHandles[t] = TextureHandle(fullname, MeshTexture);
}
return true;
}
bool InteriorMap::calcTexgenDiv()
{
// If we have already calculated our texgen scale then we don't need to do it again
// This occurs when the client and server are on the same machine (they share Resources)
if (mInteriorRes->mTexGensCalced)
return true;
if (mInteriorRes->mBrushFormat != InteriorMapResource::QuakeOld && mInteriorRes->mBrushFormat != InteriorMapResource::Valve220)
return false;
for (U32 i = 0; i < mInteriorRes->mMaterials.size(); i++)
{
F32 width = 16.0f;
F32 height = 16.0f;
if (mTexHandles[i].getWidth() != 0UL)
{
width = mTexHandles[i].getWidth();
height = mTexHandles[i].getHeight();
}
for (U32 j = 0; j < mInteriorRes->mBrushes.size(); j++)
{
for (U32 k = 0; k < mInteriorRes->mBrushes[j]->mFaces.mPolyList.size(); k++)
{
if (i == mInteriorRes->mBrushes[j]->mFaces.mPolyList[k].material)
{
mInteriorRes->mBrushes[j]->mTexInfos[k].texDiv[0] = width;
mInteriorRes->mBrushes[j]->mTexInfos[k].texDiv[1] = height;
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[0].x /= width;
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[0].y /= width;
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[0].z /= width;
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[0].d /= width;
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[1].x /= height;
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[1].y /= height;
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[1].z /= height;
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[1].d /= height;
}
}
}
}
// MDFFIX: Probably should move this to its own function
// Scale the texgens by the brushscale
for (U32 j = 0; j < mInteriorRes->mBrushes.size(); j++)
{
for (U32 k = 0; k < mInteriorRes->mBrushes[j]->mFaces.mPolyList.size(); k++)
{
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[0] *= mInteriorRes->mBrushScale;
mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[1] *= mInteriorRes->mBrushScale;
}
}
return true;
}
void InteriorMap::onRemove()
{
mConvexList->nukeList();
// Remove from Scene.
removeFromScene();
// Do Parent.
Parent::onRemove();
}
void InteriorMap::inspectPostApply()
{
// Set Parent.
Parent::inspectPostApply();
// Set Move Mask.
setMaskBits(MoveMask);
}
//------------------------------------------------------------------------------
void InteriorMap::onEditorEnable()
{
}
//------------------------------------------------------------------------------
void InteriorMap::onEditorDisable()
{
}
bool InteriorMap::prepRenderImage( SceneState* state, const U32 stateKey, const U32 startZone,
const bool modifyBaseZoneState)
{
// Return if last state.
if (isLastState(state, stateKey)) return false;
// Set Last State.
setLastState(state, stateKey);
// Is Object Rendered?
if (state->isObjectRendered(this))
{
// Yes, so get a SceneRenderImage.
SceneRenderImage* image = new SceneRenderImage;
// Populate it.
image->obj = this;
image->isTranslucent = false;
image->sortType = SceneRenderImage::Normal;
// Insert it into the scene images.
state->insertRenderImage(image);
}
return false;
}
void InteriorMap::renderObject(SceneState* state, SceneRenderImage*)
{
// Check we are in Canonical State.
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
// Save state.
RectI viewport;
if (mEnableLights && mRenderMode != BrushColors && mRenderMode != FaceColors && mRenderMode != BspPolys && mRenderMode != CollisionHulls)
{
ColorF oneColor(1,1,1,1);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, oneColor);
//gClientSceneGraph->getLightManager()->sgSetupLights();
}
// Save Projection Matrix so we can restore Canonical state at exit.
glMatrixMode(GL_PROJECTION);
glPushMatrix();
// Save Viewport so we can restore Canonical state at exit.
dglGetViewport(&viewport);
// Setup the projection to the current frustum.
//
// NOTE:- You should let the SceneGraph drive the frustum as it
// determines portal clipping etc.
// It also leaves us with the MODELVIEW current.
//
state->setupBaseProjection();
// Save ModelView Matrix so we can restore Canonical state at exit.
glPushMatrix();
// Transform by the objects' transform e.g move it.
dglMultMatrix(&getTransform());
glScalef(mObjScale.x, mObjScale.y, mObjScale.z);
// I separated out the actual render function to make this a little cleaner
if (mRenderMode != Edges && mRenderMode != BspPolys && mRenderMode != CollisionHulls)
render();
//if (mEnableLights && mRenderMode != BrushColors && mRenderMode != FaceColors && mRenderMode != BspPolys && mRenderMode != CollisionHulls)
// gClientSceneGraph->getLightManager()->sgResetLights();
// Restore our canonical matrix state.
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
// Restore our canonical viewport state.
dglSetViewport(viewport);
// Check we have restored Canonical State.
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}
bool InteriorMap::render(void)
{
gRandGen.setSeed(1978);
//glEnable(GL_CULL_FACE);
ColorF oneColor;
if (mRenderMode == TexShaded || mRenderMode == TexWireframe ||
mRenderMode == Lighting || mRenderMode == TexLighting)
{
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mWhite->getGLName());
if(mRenderMode == Lighting || mRenderMode == TexLighting)
{
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mWhite->getGLName());
if(mRenderMode == TexLighting)
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
else
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
else
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
S32 bound = -2;
for (U32 i = 0; i < mInteriorRes->mBrushes.size(); i++)
{
if (mInteriorRes->mBrushes[i]->mType == InteriorMapResource::Portal || mInteriorRes->mBrushes[i]->mType == InteriorMapResource::Trigger)
continue;
if (mInteriorRes->mBrushes[i]->mStatus == ConvexBrush::Deleted)
continue;
if (mRenderMode == BrushColors)
{
//oneColor = ColorF(gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), 1.0f);
//glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, oneColor);
glColor4f(gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), 1.0f);
}
// Save ModelView Matrix so we can restore Canonical state at exit.
glPushMatrix();
// Transform by the objects' transform e.g move it.
dglMultMatrix(&mInteriorRes->mBrushes[i]->mTransform);
// Scale
//glScalef(mInteriorRes->mBrushes[i]->mScale.x, mInteriorRes->mBrushes[i]->mScale.y, mInteriorRes->mBrushes[i]->mScale.z);
for (U32 j = 0; j < mInteriorRes->mBrushes[i]->mFaces.mPolyList.size(); j++)
{
S32 tx = mInteriorRes->mBrushes[i]->mFaces.mPolyList[j].material;
if (tx == -2)
continue;
if (mRenderMode == TexShaded || mRenderMode == TexWireframe ||
mRenderMode == TexLighting)
{
if (tx != bound)
{
// Really cheesy way to see if I have a TextureObject
if (mTexHandles[tx].getWidth() != 0UL)
{
glActiveTextureARB(GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D, mTexHandles[tx].getGLName());
bound = tx;
}
else
{
glActiveTextureARB(GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D, mWhite->getGLName());
bound = -1;
}
}
}
if (mRenderMode == FaceColors)
{
//oneColor = ColorF(gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), 1.0f);
//glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, oneColor);
glColor4f(gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), 1.0f);
}
mInteriorRes->mBrushes[i]->renderFace(j, (mRenderMode == Lighting || mRenderMode == TexLighting));
}
glPopMatrix();
}
if (mRenderMode == TexShaded || mRenderMode == TexWireframe ||
mRenderMode == Lighting || mRenderMode == TexLighting)
{
glActiveTextureARB(GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDisable(GL_TEXTURE_2D);
}
//glDisable(GL_CULL_FACE);
return true;
}
U32 InteriorMap::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
{
// Pack Parent.
U32 retMask = Parent::packUpdate(con, mask, stream);
// Write fxPortal Mask Flag.
if (stream->writeFlag(mask & MoveMask))
{
// Write Object Transform.
stream->writeAffineTransform(mObjToWorld);
// Write Object Scale.
mathWrite(*stream, mObjScale);
// Write the file name
stream->writeString(mFileName);
}
if (stream->writeFlag(mask & RenderModeMask))
stream->write(mRenderMode);
// Were done ...
return(retMask);
}
//------------------------------------------------------------------------------
void InteriorMap::unpackUpdate(NetConnection * con, BitStream * stream)
{
// Unpack Parent.
Parent::unpackUpdate(con, stream);
// Read fxPortal Mask Flag.
if (stream->readFlag())
{
MatrixF ObjectMatrix;
Point3F scale;
// Read Object Transform.
stream->readAffineTransform(&ObjectMatrix);
// Set Transform.
setTransform(ObjectMatrix);
// Read Object Scale
mathRead(*stream, &scale);
// Set Scale
setScale(scale);
// Reset the World Box.
resetWorldBox();
// Set the Render Transform.
setRenderTransform(mObjToWorld);
// Read the file name
mFileName = stream->readSTString();
}
// New RenderMode?
if (stream->readFlag())
stream->read(&mRenderMode);
}
void InteriorMap::buildConvex(const Box3F& box, Convex* convex)
{
Box3F realBox = box;
mWorldToObj.mul(realBox);
realBox.min.convolveInverse(mObjScale);
realBox.max.convolveInverse(mObjScale);
mConvexList->collectGarbage();
for (U32 i = 0; i < mInteriorRes->mBrushes.size(); i++)
{
if (mInteriorRes->mBrushes[i]->mType == InteriorMapResource::Portal || mInteriorRes->mBrushes[i]->mType == InteriorMapResource::Trigger)
continue;
if (mInteriorRes->mBrushes[i]->mStatus == ConvexBrush::Deleted)
continue;
ConvexBrush* brush = mInteriorRes->mBrushes[i];
if (realBox.isOverlapped(brush->mBounds))
{
// See if this brush exists in the working set already...
Convex* cc = 0;
CollisionWorkingList& wl = convex->getWorkingList();
for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext)
{
if (itr->mConvex->getType() == InteriorMapConvexType &&
(static_cast<InteriorMapConvex*>(itr->mConvex)->getObject() == this &&
static_cast<InteriorMapConvex*>(itr->mConvex)->brushIndex == i))
{
cc = itr->mConvex;
break;
}
}
if (!cc)
{
// Got ourselves a new convex
InteriorMapConvex* cp = new InteriorMapConvex;
mConvexList->registerObject(cp);
convex->addToWorkingList(cp);
cp->mObject = this;
cp->pOwner = this;
cp->brushIndex = i;
cp->box = brush->mBounds;
}
}
}
}
bool InteriorMap::castRay(const Point3F& s, const Point3F& e, RayInfo* info)
{
// This assumes that the collision hulls are convex...otherwise it can return unpredictable results
F32 outputFraction = 1.0f;
VectorF outputNormal;
// Loop through the collision hulls checking for the nearest ray collision
// MDFFIX: Again I really should have the collision hulls sorted into a bsp
for (U32 i = 0; i < mInteriorRes->mBrushes.size(); i++)
{
if (mInteriorRes->mBrushes[i]->mStatus == ConvexBrush::Deleted)
continue;
RayInfo test;
if (mInteriorRes->mBrushes[i]->castRay(s, e, &test))
{
if (test.t < outputFraction)
{
outputFraction = test.t;
outputNormal = test.normal;
}
}
}
// If we had a collision fill in the info structure and return
if (outputFraction >= 0.0f && outputFraction < 1.0f)
{
info->t = outputFraction;
info->normal = outputNormal;
info->point.interpolate(s, e, outputFraction);
info->face = -1;
info->object = this;
return true;
}
// Otherwise we didn't collide
return false;
}
void InteriorMap::removeBrush(S32 brushIndex)
{
if (brushIndex < 0 || brushIndex >= mInteriorRes->mBrushes.size())
return;
// Grab an iterator and point it at the beginning of the VectorPtr
VectorPtr<ConvexBrush*>::iterator cbitr = mInteriorRes->mBrushes.begin();
// Move the pointer up to our brush
cbitr += brushIndex;
// And remove it
delete *cbitr;
mInteriorRes->mBrushes.erase(cbitr);
}
S32 InteriorMap::getBrushIndex(U32 brushID)
{
for (U32 i = 0; i < mInteriorRes->mBrushes.size(); i++)
{
if (mInteriorRes->mBrushes[i]->mID == brushID)
return i;
}
return -1;
}
//****************************************************************************
// Map Texture Management
//****************************************************************************
// Returns the number of textures used in the map
S32 InteriorMap::getTextureCount()
{
if(mInteriorRes)
return mInteriorRes->mMaterials.size();
return 0;
}
// Returns the name of the texture given the texture's index
const char* InteriorMap::getTextureName(S32 index)
{
S32 count = getTextureCount();
if(index >= 0 && index < count)
{
if(mInteriorRes)
return ((const char*) mInteriorRes->getTextureName(index));
}
return "";
}
// Returns the texture for the map
const char* InteriorMap::getTexturePathway()
{
return ((const char*) mTexPath);
}
// Returns the requested texture's dimensions
Point2I InteriorMap::getTextureSize(S32 index)
{
S32 count = getTextureCount();
if(index >= 0 && index < count)
{
if(mTexHandles[index])
return Point2I(mTexHandles[index].getWidth(), mTexHandles[index].getHeight());
}
return Point2I(0,0);
}

196
engine/interior/interiorMap.h Executable file
View File

@ -0,0 +1,196 @@
#ifndef _INTERIORMAP_H_
#define _INTERIORMAP_H_
#include "platform/platform.h"
#include "sim/sceneObject.h"
#include "collision/convex.h"
#include "dgl/gTexManager.h"
#include "core/tokenizer.h"
#include "collision/convexBrush.h"
#include "collision/abstractPolyList.h"
#include "interior/interiorMapRes.h"
// Pre-define the InteriorMap class so that we can reference it in InteriorMapConvex
class InteriorMap;
// This is the convex collision implementation for InteriorMap
// Once something has collided against a InteriorMap then it creates one of
// these for the actual collisions to occur against
class InteriorMapConvex : public Convex
{
typedef Convex Parent;
friend class InteriorMap; // So the "owner" object can set some properties when it creates this
public:
// This is where you set the convex type
// Needs to be defined in convex.h in the ConvexType enum
InteriorMapConvex() { mType = InteriorMapConvexType; };
~InteriorMapConvex() {};
protected:
Box3F box; // The bounding box of the convex (in object space)
InteriorMap* pOwner; // A pointer back to the "owner" object so we can reference the vertices
U32 nodeIndex;
S32 brushIndex;
public:
// Return the bounding box transformed into world space
Box3F getBoundingBox() const;
// Return the bounding box transformed and scaled by the input values
Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const;
// This returns a list of convex faces to collide against
void getPolyList(AbstractPolyList* list);
// Returns the vertices, faces, and edges of our convex
void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
// This returns the furthest point from the input vector
Point3F support(const VectorF& v) const;
// A helper function that checks the edgelist for duplicates
void addEdge(U32 zero, U32 one, U32 base, ConvexFeature* cf);
};
class InteriorMap : public SceneObject
{
friend class InteriorMapConvex; // So the "child" convex(es) can reference the vertices
public:
typedef SceneObject Parent;
// Declare Console Object.
DECLARE_CONOBJECT(InteriorMap);
protected:
// Create and use these to specify custom events.
//
// NOTE:- Using these allows you to group the changes into related
// events. No need to change everything if something minor
// changes. Only really important if you have *lots* of these
// objects at start-up or you send alot of changes whilst the
// game is in progress.
//
// Use "setMaskBits(InteriorMapMask)" to signal.
// - Melv May
enum
{
MoveMask = (1 << 0),
RenderModeMask = (1 << 1)
};
bool mLoaded; // Make sure we don't load this twice
public:
enum
{
TexShaded = 0,
SolidShaded,
TexWireframe,
SolidWireframe,
Edges,
BrushColors,
FaceColors,
BspPolys,
CollisionHulls,
Lighting,
TexLighting
};
TextureHandle* mTexHandles;
TextureHandle* mWhite;
StringTableEntry mFileName; // The name of the level
StringTableEntry mFilePath; // The path of the level
StringTableEntry mTexPath; // The path to the textures
U32 mRenderMode;
bool mEnableLights;
Resource<InteriorMapResource> mInteriorRes;
InteriorMapResource::Entity* getEntity(char* classname)
{
for (U32 i = 0; i < mInteriorRes->mEntities.size(); i++)
{
if (dStricmp(classname, mInteriorRes->mEntities[i]->classname) == 0)
return mInteriorRes->mEntities[i];
}
return NULL;
}
void getEntities(char* classname, VectorPtr<InteriorMapResource::Entity*> ents)
{
for (U32 i = 0; i < mInteriorRes->mEntities.size(); i++)
{
if (dStricmp(classname, mInteriorRes->mEntities[i]->classname) == 0)
{
ents.increment();
ents.last() = mInteriorRes->mEntities[i];
}
}
}
protected:
Convex* mConvexList;
// I split the render and load functions out of renderObject() and onAdd() for clarity
bool render(void);
public:
// Creation and destruction
InteriorMap(void);
virtual ~InteriorMap(void);
// Utility
bool setPath(const char* filename);
bool loadTextures();
bool calcTexgenDiv();
// SceneObject
// renderObject() is the function that gets called for evey SceneObject
void renderObject(SceneState*, SceneRenderImage*);
// This function gets called to let you define a few proprties like translucency
virtual bool prepRenderImage(SceneState*, const U32 stateKey, const U32 startZone,
const bool modifyBaseZoneState = false);
// SimObject
bool onAdd();
void onRemove();
void onEditorEnable();
void onEditorDisable();
void inspectPostApply();
// NetObject
U32 packUpdate(NetConnection *, U32, BitStream *);
void unpackUpdate(NetConnection *, BitStream *);
// ConObject.
static void initPersistFields();
// Collision
// castRay() returns the percentage along the line from a starting and an ending point
// to where something collides with the object
bool castRay(const Point3F&, const Point3F&, RayInfo*);
// Called whenever something overlaps our bounding box
// This is where the SceneObject can submit convexes to the working list of an object interested in collision data
void buildConvex(const Box3F& box, Convex* convex);
// Script access functions
void removeBrush(S32 brushIndex); // Removes brush from mBrushes and deletes it from memory
S32 getTextureCount(); // Returns the number of textures used in the map
const char* getTextureName(S32 index); // Returns the name of the texture given the texture's index
const char* getTexturePathway(); // Returns the texture for the map
Point2I getTextureSize(S32 index); // Returns the requested texture's dimensions
S32 getBrushIndex(U32 brushID);
};
#endif

1034
engine/interior/interiorMapRes.cc Executable file

File diff suppressed because it is too large Load Diff

123
engine/interior/interiorMapRes.h Executable file
View File

@ -0,0 +1,123 @@
#ifndef _INTERIORMAPRES_H_
#define _INTERIORMAPRES_H_
#ifndef _RESMANAGER_H_
#include "core/resManager.h"
#endif
#include "core/tokenizer.h"
#include "dgl/gTexManager.h"
#include "dgl/materialList.h"
#include "math/mPlane.h"
class InteriorMap;
class ConvexBrush;
extern ResourceInstance* constructInteriorMAP(Stream& stream);
class InteriorMapResource : public ResourceInstance
{
typedef ResourceInstance Parent;
public:
// Enums
enum BrushType
{
Structural = 0,
Detail = 1,
Collision = 2,
Portal = 3,
Trigger = 4
};
enum
{
Unknown = 0,
Valve220 = 1,
QuakeOld = 2,
QuakeNew = 3
};
// Structs
struct Property
{
StringTableEntry name;
StringTableEntry value;
};
// Supporting classes
class Entity
{
public:
StringTableEntry classname;
Vector<Property> properties;
Entity() {};
~Entity() {};
char* getValue(char* property)
{
for (U32 i = 0; i < properties.size(); i++)
{
if (dStricmp(property, properties[i].name) == 0)
return (char*)properties[i].value;
}
return NULL;
}
};
// Data
StringTableEntry mFileName; // The name of the level
StringTableEntry mFilePath; // The path of the level
StringTableEntry mTexPath; // The texture path
VectorPtr<Entity*> mEntities;
Vector<StringTableEntry> mMaterials;
VectorPtr<ConvexBrush*> mBrushes;
U32 mBrushFormat;
F32 mBrushScale;
bool mTexGensCalced;
U32 mNextBrushID;
Entity* mWorldSpawn;
public:
InteriorMapResource();
~InteriorMapResource();
// I/O
bool load(const char* filename);
bool read(Stream& stream);
bool write(Stream& stream);
bool writeBrush(U32 brushIndex, Stream& stream);
// Parsing
bool parseMap(Tokenizer* toker);
bool parseEntity(Tokenizer* toker);
bool parsePatch(Tokenizer* toker);
bool parseBrush(Tokenizer* toker, BrushType type);
bool parsePlane(Tokenizer* toker);
bool parseQuakeValve(Tokenizer* toker, VectorF normal, U32& tdx, PlaneF* texGens, F32* scale);
bool parseQuakeNew(Tokenizer* toker, U32& tdx, PlaneF* texGens, F32* scale);
bool parseValve220TexGens(Tokenizer* toker, PlaneF* texGens, F32* scale);
bool parseQuakeTexGens(Tokenizer* toker, VectorF normal, PlaneF* texGens, F32* scale);
// Texture manager
U32 addTexture(char* texture);
U32 getTextureSize(char* texture, F32* texSizes);
bool getTextureSize(U32 texIdx, F32* texSizes);
S32 getTextureIndex(char* texture);
char* getTextureName(U32 texIdx);
// Utility
bool validatePlane(const Point3F &k, const Point3F &j, const Point3F &l);
// Returns the next valid ID and increments mNextBrushID
U32 getNextBrushID() { mNextBrushID++; return mNextBrushID - 1; };
};
#endif

1724
engine/interior/interiorRender.cc Executable file

File diff suppressed because it is too large Load Diff

320
engine/interior/interiorRes.cc Executable file
View File

@ -0,0 +1,320 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "core/stream.h"
#include "interior/interior.h"
#include "interior/interiorResObjects.h"
#include "dgl/gBitmap.h"
#include "interior/forceField.h"
#include "interior/interiorRes.h"
const U32 InteriorResource::smFileVersion = 44;
//--------------------------------------------------------------------------
InteriorResource::InteriorResource()
{
VECTOR_SET_ASSOCIATION(mDetailLevels);
VECTOR_SET_ASSOCIATION(mSubObjects);
VECTOR_SET_ASSOCIATION(mTriggers);
VECTOR_SET_ASSOCIATION(mInteriorPathFollowers);
VECTOR_SET_ASSOCIATION(mForceFields);
VECTOR_SET_ASSOCIATION(mAISpecialNodes);
mPreviewBitmap = NULL;
}
InteriorResource::~InteriorResource()
{
U32 i;
for (i = 0; i < mDetailLevels.size(); i++)
delete mDetailLevels[i];
for (i = 0; i < mSubObjects.size(); i++)
delete mSubObjects[i];
for (i = 0; i < mTriggers.size(); i++)
delete mTriggers[i];
for (i = 0; i < mInteriorPathFollowers.size(); i++)
delete mInteriorPathFollowers[i];
for (i = 0; i < mForceFields.size(); i++)
delete mForceFields[i];
for (i = 0; i < mAISpecialNodes.size(); i++)
delete mAISpecialNodes[i];
delete mPreviewBitmap;
mPreviewBitmap = NULL;
}
bool InteriorResource::read(Stream& stream)
{
AssertFatal(stream.hasCapability(Stream::StreamRead), "Interior::read: non-read capable stream passed");
AssertFatal(stream.getStatus() == Stream::Ok, "Interior::read: Error, stream in inconsistent state");
U32 i;
// Version this stream
U32 fileVersion;
stream.read(&fileVersion);
if (fileVersion != smFileVersion) {
Con::errorf(ConsoleLogEntry::General, "InteriorResource::read: incompatible file version found.");
return false;
}
// Handle preview
bool previewIncluded;
stream.read(&previewIncluded);
if (previewIncluded) {
GBitmap bmp;
bmp.readPNG(stream);
}
// Details
U32 numDetailLevels;
stream.read(&numDetailLevels);
mDetailLevels.setSize(numDetailLevels);
for (i = 0; i < mDetailLevels.size(); i++)
mDetailLevels[i] = NULL;
for (i = 0; i < mDetailLevels.size(); i++) {
mDetailLevels[i] = new Interior;
if (mDetailLevels[i]->read(stream) == false) {
Con::errorf(ConsoleLogEntry::General, "Unable to read detail level %d in interior resource", i);
return false;
}
}
// Subobjects: mirrors, translucencies
U32 numSubObjects;
stream.read(&numSubObjects);
mSubObjects.setSize(numSubObjects);
for (i = 0; i < mSubObjects.size(); i++)
mSubObjects[i] = NULL;
for (i = 0; i < mSubObjects.size(); i++) {
mSubObjects[i] = new Interior;
if (mSubObjects[i]->read(stream) == false) {
AssertISV(false, avar("Unable to read subobject %d in interior resource", i));
return false;
}
}
// Triggers
U32 numTriggers;
stream.read(&numTriggers);
mTriggers.setSize(numTriggers);
for (i = 0; i < mTriggers.size(); i++)
mTriggers[i] = NULL;
for (i = 0; i < mTriggers.size(); i++) {
mTriggers[i] = new InteriorResTrigger;
if (mTriggers[i]->read(stream) == false) {
AssertISV(false, avar("Unable to read trigger %d in interior resource", i));
return false;
}
}
U32 numChildren;
stream.read(&numChildren);
mInteriorPathFollowers.setSize(numChildren);
for (i = 0; i < mInteriorPathFollowers.size(); i++)
mInteriorPathFollowers[i] = NULL;
for (i = 0; i < mInteriorPathFollowers.size(); i++) {
mInteriorPathFollowers[i] = new InteriorPathFollower;
if (mInteriorPathFollowers[i]->read(stream) == false) {
AssertISV(false, avar("Unable to read child %d in interior resource", i));
return false;
}
}
U32 numFields;
stream.read(&numFields);
mForceFields.setSize(numFields);
for (i = 0; i < mForceFields.size(); i++)
mForceFields[i] = NULL;
for (i = 0; i < mForceFields.size(); i++) {
mForceFields[i] = new ForceField;
if (mForceFields[i]->read(stream) == false) {
AssertISV(false, avar("Unable to read field %d in interior resource", i));
return false;
}
}
U32 numSpecNodes;
stream.read(&numSpecNodes);
mAISpecialNodes.setSize(numSpecNodes);
for (i = 0; i < mAISpecialNodes.size(); i++)
mAISpecialNodes[i] = NULL;
for (i = 0; i < mAISpecialNodes.size(); i++) {
mAISpecialNodes[i] = new AISpecialNode;
if (mAISpecialNodes[i]->read(stream) == false) {
AssertISV(false, avar("Unable to read SpecNode %d in interior resource", i));
return false;
}
}
U32 dummyInt;
stream.read(&dummyInt);
if (dummyInt == 1)
{
if (mDetailLevels.size() != 0)
getDetailLevel(0)->readVehicleCollision(stream);
}
// For expansion purposes
stream.read(&dummyInt);
if(dummyInt == 2)
{
U32 numGameEnts;
stream.read(&numGameEnts);
mGameEntities.setSize(numGameEnts);
for (i = 0; i < numGameEnts; i++)
mGameEntities[i] = new ItrGameEntity;
for (i = 0; i < numGameEnts; i++) {
if (mGameEntities[i]->read(stream) == false) {
AssertISV(false, avar("Unable to read SpecNode %d in interior resource", i));
return false;
}
}
stream.read(&dummyInt);
}
return (stream.getStatus() == Stream::Ok);
}
bool InteriorResource::write(Stream& stream) const
{
AssertFatal(stream.hasCapability(Stream::StreamWrite), "Interior::write: non-write capable stream passed");
AssertFatal(stream.getStatus() == Stream::Ok, "Interior::write: Error, stream in inconsistent state");
// Version the stream
stream.write(smFileVersion);
// Handle preview
//
if (mPreviewBitmap != NULL) {
stream.write(bool(true));
mPreviewBitmap->writePNG(stream);
} else {
stream.write(bool(false));
}
// Write out the interiors
stream.write(mDetailLevels.size());
U32 i;
for (i = 0; i < mDetailLevels.size(); i++) {
if (mDetailLevels[i]->write(stream) == false) {
AssertISV(false, "Unable to write detail level to stream");
return false;
}
}
stream.write(mSubObjects.size());
for (i = 0; i < mSubObjects.size(); i++) {
if (mSubObjects[i]->write(stream) == false) {
AssertISV(false, "Unable to write subobject to stream");
return false;
}
}
stream.write(mTriggers.size());
for (i = 0; i < mTriggers.size(); i++) {
if (mTriggers[i]->write(stream) == false) {
AssertISV(false, "Unable to write trigger to stream");
return false;
}
}
stream.write(mInteriorPathFollowers.size());
for (i = 0; i < mInteriorPathFollowers.size(); i++) {
if (mInteriorPathFollowers[i]->write(stream) == false) {
AssertISV(false, avar("Unable to write child %d in interior resource", i));
return false;
}
}
stream.write(mForceFields.size());
for (i = 0; i < mForceFields.size(); i++) {
if (mForceFields[i]->write(stream) == false) {
AssertISV(false, avar("Unable to write field %d in interior resource", i));
return false;
}
}
stream.write(mAISpecialNodes.size());
for (i = 0; i < mAISpecialNodes.size(); i++) {
if (mAISpecialNodes[i]->write(stream) == false) {
AssertISV(false, avar("Unable to write SpecNode %d in interior resource", i));
return false;
}
}
stream.write(U32(1));
if (mDetailLevels.size() != 0)
const_cast<Interior*>(mDetailLevels[0])->writeVehicleCollision(stream);
// For expansion purposes
if (mGameEntities.size())
{
stream.write(U32(2));
stream.write(mGameEntities.size());
for(i = 0; i < mGameEntities.size(); i++)
{
if (mGameEntities[i]->write(stream) == false) {
AssertISV(false, avar("Unable to write GameEnt %d in interior resource", i));
return false;
}
}
}
stream.write(U32(0));
return (stream.getStatus() == Stream::Ok);
}
GBitmap* InteriorResource::extractPreview(Stream& stream)
{
AssertFatal(stream.hasCapability(Stream::StreamRead), "Interior::read: non-read capable stream passed");
AssertFatal(stream.getStatus() == Stream::Ok, "Interior::read: Error, stream in inconsistent state");
// Version this stream
U32 fileVersion;
stream.read(&fileVersion);
if (fileVersion != smFileVersion) {
Con::errorf(ConsoleLogEntry::General, "InteriorResource::read: incompatible file version found.");
return NULL;
}
// Handle preview
bool previewIncluded;
stream.read(&previewIncluded);
if (previewIncluded) {
GBitmap* pBmp = new GBitmap;
if (pBmp->readPNG(stream) == true)
return pBmp;
delete pBmp;
}
return NULL;
}
//------------------------------------------------------------------------------
//-------------------------------------- Interior Resource constructor
ResourceInstance* constructInteriorDIF(Stream& stream)
{
InteriorResource* pResource = new InteriorResource;
if (pResource->read(stream) == true)
return pResource;
else {
delete pResource;
return NULL;
}
}

151
engine/interior/interiorRes.h Executable file
View File

@ -0,0 +1,151 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _INTERIORRES_H_
#define _INTERIORRES_H_
#ifndef _RESMANAGER_H_
#include "core/resManager.h"
#endif
class Stream;
class Interior;
class GBitmap;
class InteriorResTrigger;
class InteriorPath;
class InteriorPathFollower;
class ForceField;
class AISpecialNode;
class ItrGameEntity;
class InteriorResource : public ResourceInstance
{
typedef ResourceInstance Parent;
static const U32 smFileVersion;
protected:
Vector<Interior*> mDetailLevels;
Vector<Interior*> mSubObjects;
Vector<InteriorResTrigger*> mTriggers;
Vector<InteriorPathFollower*> mInteriorPathFollowers;
Vector<ForceField*> mForceFields;
Vector<AISpecialNode*> mAISpecialNodes;
Vector<ItrGameEntity*> mGameEntities;
GBitmap* mPreviewBitmap;
public:
InteriorResource();
~InteriorResource();
bool read(Stream& stream);
bool write(Stream& stream) const;
static GBitmap* extractPreview(Stream&);
S32 getNumDetailLevels() const;
S32 getNumSubObjects() const;
S32 getNumTriggers() const;
S32 getNumInteriorPathFollowers() const;
S32 getNumForceFields() const;
S32 getNumSpecialNodes() const;
S32 getNumGameEntities() const;
Interior* getDetailLevel(const U32);
Interior* getSubObject(const U32);
InteriorResTrigger* getTrigger(const U32);
InteriorPathFollower* getInteriorPathFollower(const U32);
ForceField* getForceField(const U32);
AISpecialNode* getSpecialNode(const U32);
ItrGameEntity* getGameEntity(const U32);
};
extern ResourceInstance* constructInteriorDIF(Stream& stream);
//--------------------------------------------------------------------------
inline S32 InteriorResource::getNumDetailLevels() const
{
return mDetailLevels.size();
}
inline S32 InteriorResource::getNumSubObjects() const
{
return mSubObjects.size();
}
inline S32 InteriorResource::getNumTriggers() const
{
return mTriggers.size();
}
inline S32 InteriorResource::getNumSpecialNodes() const
{
return mAISpecialNodes.size();
}
inline S32 InteriorResource::getNumGameEntities() const
{
return mGameEntities.size();
}
inline S32 InteriorResource::getNumInteriorPathFollowers() const
{
return mInteriorPathFollowers.size();
}
inline S32 InteriorResource::getNumForceFields() const
{
return mForceFields.size();
}
inline Interior* InteriorResource::getDetailLevel(const U32 idx)
{
AssertFatal(idx < getNumDetailLevels(), "Error, out of bounds detail level!");
return mDetailLevels[idx];
}
inline Interior* InteriorResource::getSubObject(const U32 idx)
{
AssertFatal(idx < getNumSubObjects(), "Error, out of bounds subObject!");
return mSubObjects[idx];
}
inline InteriorResTrigger* InteriorResource::getTrigger(const U32 idx)
{
AssertFatal(idx < getNumTriggers(), "Error, out of bounds trigger!");
return mTriggers[idx];
}
inline InteriorPathFollower* InteriorResource::getInteriorPathFollower(const U32 idx)
{
AssertFatal(idx < getNumInteriorPathFollowers(), "Error, out of bounds path follower!");
return mInteriorPathFollowers[idx];
}
inline ForceField* InteriorResource::getForceField(const U32 idx)
{
AssertFatal(idx < getNumForceFields(), "Error, out of bounds force field!");
return mForceFields[idx];
}
inline AISpecialNode* InteriorResource::getSpecialNode(const U32 idx)
{
AssertFatal(idx < getNumSpecialNodes(), "Error, out of bounds Special Nodes!");
return mAISpecialNodes[idx];
}
inline ItrGameEntity* InteriorResource::getGameEntity(const U32 idx)
{
AssertFatal(idx < getNumGameEntities(), "Error, out of bounds Game ENts!");
return mGameEntities[idx];
}
#endif // _H_INTERIORRES_

View File

@ -0,0 +1,226 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "interior/interiorResObjects.h"
#include "core/stream.h"
#include "math/mathIO.h"
//--------------------------------------------------------------------------
//--------------------------------------
//
void InteriorDict::read(Stream &stream)
{
U32 sz;
stream.read(&sz);
setSize(sz);
for(U32 i = 0; i < sz; i++)
{
InteriorDictEntry e;
stream.readString(e.name);
stream.readString(e.value);
(*this)[i] = e;
}
}
void InteriorDict::write(Stream &stream) const
{
U32 sz = size();
stream.write(sz);
for(U32 i = 0; i < sz; i++)
{
stream.writeString((*this)[i].name);
stream.writeString((*this)[i].value);
}
}
bool InteriorResTrigger::read(Stream& stream)
{
U32 i, size;
stream.readString(mName);
mDataBlock = stream.readSTString();
mDictionary.read(stream);
// Read the polyhedron
stream.read(&size);
mPolyhedron.pointList.setSize(size);
for (i = 0; i < mPolyhedron.pointList.size(); i++)
mathRead(stream, &mPolyhedron.pointList[i]);
stream.read(&size);
mPolyhedron.planeList.setSize(size);
for (i = 0; i < mPolyhedron.planeList.size(); i++)
mathRead(stream, &mPolyhedron.planeList[i]);
stream.read(&size);
mPolyhedron.edgeList.setSize(size);
for (i = 0; i < mPolyhedron.edgeList.size(); i++) {
Polyhedron::Edge& rEdge = mPolyhedron.edgeList[i];
stream.read(&rEdge.face[0]);
stream.read(&rEdge.face[1]);
stream.read(&rEdge.vertex[0]);
stream.read(&rEdge.vertex[1]);
}
// And the offset
mathRead(stream, &mOffset);
return (stream.getStatus() == Stream::Ok);
}
bool InteriorResTrigger::write(Stream& stream) const
{
U32 i;
stream.writeString(mName);
stream.writeString(mDataBlock);
mDictionary.write(stream);
// Write the polyhedron
stream.write(mPolyhedron.pointList.size());
for (i = 0; i < mPolyhedron.pointList.size(); i++)
mathWrite(stream, mPolyhedron.pointList[i]);
stream.write(mPolyhedron.planeList.size());
for (i = 0; i < mPolyhedron.planeList.size(); i++)
mathWrite(stream, mPolyhedron.planeList[i]);
stream.write(mPolyhedron.edgeList.size());
for (i = 0; i < mPolyhedron.edgeList.size(); i++) {
const Polyhedron::Edge& rEdge = mPolyhedron.edgeList[i];
stream.write(rEdge.face[0]);
stream.write(rEdge.face[1]);
stream.write(rEdge.vertex[0]);
stream.write(rEdge.vertex[1]);
}
// And the offset
mathWrite(stream, mOffset);
return (stream.getStatus() == Stream::Ok);
}
InteriorPathFollower::InteriorPathFollower()
{
mName = "";
mPathIndex = 0;
mOffset.set(0, 0, 0);
}
InteriorPathFollower::~InteriorPathFollower()
{
}
bool InteriorPathFollower::read(Stream& stream)
{
mName = stream.readSTString();
mDataBlock = stream.readSTString();
stream.read(&mInteriorResIndex);
mathRead(stream, &mOffset);
mDictionary.read(stream);
U32 numTriggers;
stream.read(&numTriggers);
mTriggerIds.setSize(numTriggers);
for (U32 i = 0; i < mTriggerIds.size(); i++)
stream.read(&mTriggerIds[i]);
U32 numWayPoints;
stream.read(&numWayPoints);
mWayPoints.setSize(numWayPoints);
for(U32 i = 0; i < numWayPoints; i++)
{
mathRead(stream, &mWayPoints[i].pos);
mathRead(stream, &mWayPoints[i].rot);
stream.read(&mWayPoints[i].msToNext);
stream.read(&mWayPoints[i].smoothingType);
}
stream.read(&mTotalMS);
return (stream.getStatus() == Stream::Ok);
}
bool InteriorPathFollower::write(Stream& stream) const
{
stream.writeString(mName);
stream.writeString(mDataBlock);
stream.write(mInteriorResIndex);
mathWrite(stream, mOffset);
mDictionary.write(stream);
stream.write(mTriggerIds.size());
for (U32 i = 0; i < mTriggerIds.size(); i++)
stream.write(mTriggerIds[i]);
stream.write(U32(mWayPoints.size()));
for (U32 i = 0; i < mWayPoints.size(); i++) {
mathWrite(stream, mWayPoints[i].pos);
mathWrite(stream, mWayPoints[i].rot);
stream.write(mWayPoints[i].msToNext);
stream.write(mWayPoints[i].smoothingType);
}
stream.write(mTotalMS);
return (stream.getStatus() == Stream::Ok);
}
AISpecialNode::AISpecialNode()
{
mName = "";
mPos.set(0, 0, 0);
}
AISpecialNode::~AISpecialNode()
{
}
bool AISpecialNode::read(Stream& stream)
{
mName = stream.readSTString();
mathRead(stream, &mPos);
return (stream.getStatus() == Stream::Ok);
}
bool AISpecialNode::write(Stream& stream) const
{
stream.writeString(mName);
mathWrite(stream, mPos);
return (stream.getStatus() == Stream::Ok);
}
ItrGameEntity::ItrGameEntity()
{
mDataBlock = "";
mGameClass = "";
mPos.set(0, 0, 0);
}
ItrGameEntity::~ItrGameEntity()
{
}
bool ItrGameEntity::read(Stream& stream)
{
mDataBlock = stream.readSTString();
mGameClass = stream.readSTString();
mathRead(stream, &mPos);
mDictionary.read(stream);
return (stream.getStatus() == Stream::Ok);
}
bool ItrGameEntity::write(Stream& stream) const
{
stream.writeString(mDataBlock);
stream.writeString(mGameClass);
mathWrite(stream, mPos);
mDictionary.write(stream);
return (stream.getStatus() == Stream::Ok);
}

View File

@ -0,0 +1,131 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _INTERIORRESOBJECTS_H_
#define _INTERIORRESOBJECTS_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
#ifndef _MBOX_H_
#include "math/mBox.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _POLYHEDRON_H_
#include "collision/polyhedron.h"
#endif
class Stream;
struct InteriorDictEntry
{
char name[32];
char value[32];
};
class InteriorDict : public Vector<InteriorDictEntry>
{
public:
void read(Stream& stream);
void write(Stream& stream) const;
};
class InteriorResTrigger
{
public:
enum Constants {
MaxNameChars = 255
};
char mName[MaxNameChars+1];
StringTableEntry mDataBlock;
InteriorDict mDictionary;
Point3F mOffset;
Polyhedron mPolyhedron;
public:
InteriorResTrigger() { }
bool read(Stream& stream);
bool write(Stream& stream) const;
};
class InteriorPathFollower
{
public:
struct WayPoint {
Point3F pos;
QuatF rot;
U32 msToNext;
U32 smoothingType;
};
StringTableEntry mName;
StringTableEntry mDataBlock;
U32 mInteriorResIndex;
U32 mPathIndex;
Point3F mOffset;
Vector<U32> mTriggerIds;
Vector<WayPoint> mWayPoints;
U32 mTotalMS;
InteriorDict mDictionary;
public:
InteriorPathFollower();
~InteriorPathFollower();
bool read(Stream& stream);
bool write(Stream& stream) const;
};
class AISpecialNode
{
public:
enum
{
chute = 0,
};
public:
StringTableEntry mName;
Point3F mPos;
//U32 mType;
public:
AISpecialNode();
~AISpecialNode();
bool read(Stream& stream);
bool write(Stream& stream) const;
};
class ItrGameEntity
{
public:
StringTableEntry mDataBlock;
StringTableEntry mGameClass;
Point3F mPos;
InteriorDict mDictionary;
public:
ItrGameEntity();
~ItrGameEntity();
bool read(Stream& stream);
bool write(Stream& stream) const;
};
#endif // _H_INTERIORRESOBJECTS_

View File

@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "core/stream.h"
#include "interior/interiorInstance.h"
#include "interior/interiorSubObject.h"
#include "interior/mirrorSubObject.h"
InteriorSubObject::InteriorSubObject()
{
mInteriorInstance = NULL;
}
InteriorSubObject::~InteriorSubObject()
{
mInteriorInstance = NULL;
}
InteriorSubObject* InteriorSubObject::readISO(Stream& stream)
{
U32 soKey;
stream.read(&soKey);
InteriorSubObject* pObject = NULL;
switch (soKey) {
case MirrorSubObjectKey:
pObject = new MirrorSubObject;
break;
default:
Con::errorf(ConsoleLogEntry::General, "Bad key in subObject stream!");
return NULL;
};
if (pObject) {
bool readSuccess = pObject->_readISO(stream);
if (readSuccess == false) {
delete pObject;
pObject = NULL;
}
}
return pObject;
}
bool InteriorSubObject::writeISO(Stream& stream) const
{
stream.write(getSubObjectKey());
return _writeISO(stream);
}
bool InteriorSubObject::_readISO(Stream& stream)
{
return (stream.getStatus() == Stream::Ok);
}
bool InteriorSubObject::_writeISO(Stream& stream) const
{
return (stream.getStatus() == Stream::Ok);
}
const MatrixF& InteriorSubObject::getSOTransform() const
{
static const MatrixF csBadMatrix(true);
if (mInteriorInstance != NULL) {
return mInteriorInstance->getTransform();
} else {
AssertWarn(false, "Returning bad transform for subobject");
return csBadMatrix;
}
}
const Point3F& InteriorSubObject::getSOScale() const
{
return mInteriorInstance->getScale();
}
InteriorInstance* InteriorSubObject::getInstance()
{
return mInteriorInstance;
}
void InteriorSubObject::noteTransformChange()
{
//
}

View File

@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _INTERIORSUBOBJECT_H_
#define _INTERIORSUBOBJECT_H_
#ifndef _SCENESTATE_H_
#include "sceneGraph/sceneState.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
class InteriorInstance;
class SubObjectRenderImage : public SceneRenderImage
{
public:
U32 mDetailLevel;
};
class InteriorSubObject : public SceneObject
{
typedef SceneObject Parent;
protected:
InteriorInstance* mInteriorInstance; // Should NOT be set by derived except in clone
protected:
enum SubObjectKeys {
TranslucentSubObjectKey = 0,
MirrorSubObjectKey = 1
};
virtual U32 getSubObjectKey() const = 0;
virtual bool _readISO(Stream&);
virtual bool _writeISO(Stream&) const;
InteriorInstance* getInstance();
const MatrixF& getSOTransform() const;
const Point3F& getSOScale() const;
public:
InteriorSubObject();
virtual ~InteriorSubObject();
// Render control. A sub-object should return false from renderDetailDependant if
// it exists only at the level-0 detail level, ie, doors, elevators, etc., true
// if should only render at the interiors detail, ie, translucencies.
virtual SubObjectRenderImage* getRenderImage(SceneState*, const Point3F& osPoint) = 0;
virtual bool renderDetailDependant() const = 0;
virtual U32 getZone() const = 0;
virtual void noteTransformChange();
virtual InteriorSubObject* clone(InteriorInstance*) const = 0;
static InteriorSubObject* readISO(Stream&);
bool writeISO(Stream&) const;
};
#endif // _H_INTERIORSUBOBJECT_

111
engine/interior/itf.h Executable file
View File

@ -0,0 +1,111 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _ITF_H_
#define _ITF_H_
#ifndef _TYPES_H_
#include "platform/types.h"
#endif
#ifndef _COLOR_H
#include "core/color.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
#ifndef _INTERIOR_H_
// redecl struct here for now... interior.h brings in the whole fricking codebase.
struct ItrPaddedPoint
{
Point3F point;
union {
F32 fogCoord;
U8 fogColor[4];
};
};
#endif
struct OutputPoint
{
Point3F point;
union {
F32 fogCoord; // to match input struct cleanly..
U32 fogColors;
U8 fogColor[4];
};
Point2F texCoord;
Point2F lmCoord;
};
struct OutputPointFC_VB
{
Point3F point; //0/4/8
union {
U32 currentColors;
U8 currentColor[4];
}; //12
union {
U32 fogColors;
U8 fogColor[4];
}; //16
Point2F texCoord; //20/24
Point2F lmCoord; //28/32
};
struct OutputPointSP_FC_VB
{
Point3F point;
union {
U32 lmColors;
U8 lmColor[4];
};
union {
U32 fogColors;
U8 fogColor[4];
};
Point2F texCoord;
};
extern "C"
{
// Process Globals
extern F32 texGen0[8];
extern F32 texGen1[8];
extern Point2F *fogCoordinatePointer;
// Process Functions
void processTriFan(OutputPoint* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices);
void processTriFanSP(OutputPoint* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices,
const ColorI* srcColors);
void processTriFanVC_TF(OutputPoint* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices,
const ColorI* srcColors);
void processTriFanSP_FC(OutputPoint* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices,
const ColorI* srcColors);
void processTriFanFC_VB(OutputPointFC_VB* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices);
void processTriFanSP_FC_VB(OutputPointSP_FC_VB* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices,
const ColorI* srcColors);
}
#endif

717
engine/interior/itfdump.asm Executable file
View File

@ -0,0 +1,717 @@
;-----------------------------------------------------------------------------
; Torque Game Engine
; Copyright (C) GarageGames.com, Inc.
;-----------------------------------------------------------------------------
segment .data
storeebp dd 0
srcPoints dd 0
srcColors dd 0
srcIndices dd 0
numPoints dd 0
two55 dd 0x437F0000
alpha dd 0
%ifdef LINUX
; No underscore needed for ELF object files
%define _texGen0 texGen0
%define _texGen1 texGen1
%define _fogCoordinatePointer fogCoordinatePointer
%endif
extern _texGen0
extern _texGen1
extern _fogCoordinatePointer
segment .text
;
; these macros are good for both functions
;
%define in_dst [ebp+8]
%define in_src_points [ebp+12]
%define in_src_indices [ebp+16]
%define in_numpoints [ebp+20]
%define in_srcColors [ebp+24] ; Valid only for SP
; CodeWarrior sucks :P
%ifdef LINUX
global processTriFan
processTriFan:
%else
global _processTriFan
_processTriFan:
%endif
; prologue
push ebp
mov ebp, esp
push eax
push ebx
push ecx
push edi
push esi
; Store the destination and source pointers
mov eax, in_src_points
mov [srcPoints], eax
mov eax, in_src_indices
mov [srcIndices], eax
mov eax, in_numpoints
mov [numPoints], eax
mov edi, in_dst
mov [storeebp], ebp
xor ebp, ebp
procPointLp1:
; This could be faster
mov esi, [srcIndices]
lea esi, [esi + ebp*4]
mov eax, dword [esi]
shl eax, 4 ; idx *= 16
mov esi, [srcPoints]
lea esi, [esi + eax]
mov eax, [esi + 0] ; x
mov ebx, [esi + 4] ; y
mov ecx, [esi + 8] ; z
mov edx, [esi + 12] ; f
mov [edi + 0], eax ; <- x
mov [edi + 4], ebx ; <- y
mov [edi + 8], ecx ; <- z
mov [edi + 12], edx ; <- f
; tc0.s
fld dword [_texGen0 + 0] ; tg0.s.x
fmul dword [esi + 0]
fld dword [_texGen0 + 4] ; tg0.s.y
fmul dword [esi + 4]
fld dword [_texGen0 + 8] ; tg0.s.z
fmul dword [esi + 8]
fld dword [_texGen0 + 12] ; tg0.s.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 16] ; tc0.s
; tc0.t
fld dword [_texGen0 + 16] ; tg0.t.x
fmul dword [esi + 0]
fld dword [_texGen0 + 20] ; tg0.t.y
fmul dword [esi + 4]
fld dword [_texGen0 + 24] ; tg0.t.z
fmul dword [esi + 8]
fld dword [_texGen0 + 28] ; tg0.t.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 20] ; tc0.t
; tc1.s
fld dword [_texGen1 + 0] ; tg1.s.x
fmul dword [esi + 0]
fld dword [_texGen1 + 4] ; tg1.s.y
fmul dword [esi + 4]
fld dword [_texGen1 + 8] ; tg1.s.z
fmul dword [esi + 8]
fld dword [_texGen1 + 12] ; tg1.s.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 24] ; tc1.s
; tc1.t
fld dword [_texGen1 + 16] ; tg1.t.x
fmul dword [esi + 0]
fld dword [_texGen1 + 20] ; tg1.t.y
fmul dword [esi + 4]
fld dword [_texGen1 + 24] ; tg1.t.z
fmul dword [esi + 8]
fld dword [_texGen1 + 28] ; tg1.t.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 28] ; tc1.t
add edi, 32
inc ebp
cmp ebp, [numPoints]
jl near procPointLp1
mov ebp, [storeebp]
; epilogue
pop esi
pop edi
pop ecx
pop ebx
pop eax
pop ebp
ret
; More suckage
%ifdef LINUX
global processTriFanSP
processTriFanSP:
%else
global _processTriFanSP
_processTriFanSP:
%endif
; prologue
push ebp
mov ebp, esp
push eax
push ebx
push ecx
push edi
push esi
; Store the destination and source pointers
mov eax, in_src_points
mov [srcPoints], eax
mov eax, in_src_indices
mov [srcIndices], eax
mov eax, in_numpoints
mov [numPoints], eax
mov eax, in_srcColors
mov [srcColors], eax
mov edi, in_dst
mov [storeebp], ebp
xor ebp, ebp
procPointLp2:
; This could be faster
mov esi, [srcIndices]
lea esi, [esi + ebp*4]
mov eax, dword [esi]
shl eax, 4 ; idx *= 16
mov esi, [srcPoints]
lea esi, [esi + eax]
mov eax, [esi + 0] ; x
mov ebx, [esi + 4] ; y
mov ecx, [esi + 8] ; z
mov edx, [srcColors] ; color
mov [edi + 0], eax ; <- x
lea edx, [edx + ebp*4] ; color
mov [edi + 4], ebx ; <- y
mov edx, [edx] ; color
mov [edi + 8], ecx ; <- z
mov [edi + 12], edx ; color
; tc0.s
fld dword [_texGen0 + 0] ; tg0.s.x
fmul dword [esi + 0]
fld dword [_texGen0 + 4] ; tg0.s.y
fmul dword [esi + 4]
fld dword [_texGen0 + 8] ; tg0.s.z
fmul dword [esi + 8]
fld dword [_texGen0 + 12] ; tg0.s.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 16] ; tc0.s
; tc0.t
fld dword [_texGen0 + 16] ; tg0.t.x
fmul dword [esi + 0]
fld dword [_texGen0 + 20] ; tg0.t.y
fmul dword [esi + 4]
fld dword [_texGen0 + 24] ; tg0.t.z
fmul dword [esi + 8]
fld dword [_texGen0 + 28] ; tg0.t.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 20] ; tc0.t
add edi, 32
inc ebp
cmp ebp, [numPoints]
jl near procPointLp2
mov ebp, [storeebp]
; epilogue
pop esi
pop edi
pop ecx
pop ebx
pop eax
pop ebp
ret
; More suckage
%ifdef LINUX
global processTriFanVC_TF
processTriFanVC_TF:
%else
global _processTriFanVC_TF
_processTriFanVC_TF:
%endif
; prologue
push ebp
mov ebp, esp
push eax
push ebx
push ecx
push edi
push esi
; Store the destination and source pointers
mov eax, in_src_points
mov [srcPoints], eax
mov eax, in_src_indices
mov [srcIndices], eax
mov eax, in_numpoints
mov [numPoints], eax
mov eax, in_srcColors
mov [srcColors], eax
mov edi, in_dst
mov [storeebp], ebp
xor ebp, ebp
procPointLp4:
; This could be faster
mov esi, [srcIndices]
lea esi, [esi + ebp*4]
mov eax, dword [esi]
shl eax, 4 ; idx *= 16
mov esi, [srcPoints]
lea esi, [esi + eax]
; Fog tex coord
mov ebx, [_fogCoordinatePointer]
shr eax, 1 ; idx /= 2
lea ebx, [ebx + eax]
mov ecx, [ebx + 0];
mov edx, [ebx + 4];
mov [edi + 16], ecx
mov [edi + 20], edx
mov eax, [esi + 0] ; x
mov ebx, [esi + 4] ; y
mov ecx, [esi + 8] ; z
mov edx, [srcColors] ; color
mov [edi + 0], eax ; <- x
lea edx, [edx + ebp*4] ; color
mov [edi + 4], ebx ; <- y
mov edx, [edx] ; color
mov [edi + 8], ecx ; <- z
mov [edi + 12], edx ; color
; tc0.s
fld dword [_texGen0 + 0] ; tg0.s.x
fmul dword [esi + 0]
fld dword [_texGen0 + 4] ; tg0.s.y
fmul dword [esi + 4]
fld dword [_texGen0 + 8] ; tg0.s.z
fmul dword [esi + 8]
fld dword [_texGen0 + 12] ; tg0.s.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 24] ; tc0.s
; tc0.t
fld dword [_texGen0 + 16] ; tg0.t.x
fmul dword [esi + 0]
fld dword [_texGen0 + 20] ; tg0.t.y
fmul dword [esi + 4]
fld dword [_texGen0 + 24] ; tg0.t.z
fmul dword [esi + 8]
fld dword [_texGen0 + 28] ; tg0.t.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 28] ; tc0.t
add edi, 32
inc ebp
cmp ebp, [numPoints]
jl near procPointLp4
mov ebp, [storeebp]
; epilogue
pop esi
pop edi
pop ecx
pop ebx
pop eax
pop ebp
pop ebp
ret
; More suckagea
%ifdef LINUX
global processTriFanSP_FC
processTriFanSP_FC:
%else
global _processTriFanSP_FC
_processTriFanSP_FC:
%endif
; prologue
push ebp
mov ebp, esp
push eax
push ebx
push ecx
push edi
push esi
; Store the destination and source pointers
mov eax, in_src_points
mov [srcPoints], eax
mov eax, in_src_indices
mov [srcIndices], eax
mov eax, in_numpoints
mov [numPoints], eax
mov eax, in_srcColors
mov [srcColors], eax
mov edi, in_dst
mov [storeebp], ebp
xor ebp, ebp
procPointLp2_fc:
; This could be faster
mov esi, [srcIndices]
lea esi, [esi + ebp*4]
mov eax, dword [esi]
shl eax, 4 ; idx *= 16
mov esi, [srcPoints]
lea esi, [esi + eax]
mov eax, [esi + 0] ; x
mov ebx, [esi + 4] ; y
mov ecx, [esi + 8] ; z
mov edx, [esi + 12] ; fc
mov [edi + 0], eax ; <- x
mov [edi + 4], ebx ; <- y
mov [edi + 8], ecx ; <- z
mov [edi + 24], edx ; <- fc (lmcoord.x)
mov edx, [srcColors] ; color
lea edx, [edx + ebp*4] ; color
mov edx, [edx] ; color
mov [edi + 12], edx ; color
; tc0.s
fld dword [_texGen0 + 0] ; tg0.s.x
fmul dword [esi + 0]
fld dword [_texGen0 + 4] ; tg0.s.y
fmul dword [esi + 4]
fld dword [_texGen0 + 8] ; tg0.s.z
fmul dword [esi + 8]
fld dword [_texGen0 + 12] ; tg0.s.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 16] ; tc0.s
; tc0.t
fld dword [_texGen0 + 16] ; tg0.t.x
fmul dword [esi + 0]
fld dword [_texGen0 + 20] ; tg0.t.y
fmul dword [esi + 4]
fld dword [_texGen0 + 24] ; tg0.t.z
fmul dword [esi + 8]
fld dword [_texGen0 + 28] ; tg0.t.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 20] ; tc0.t
add edi, 32
inc ebp
cmp ebp, [numPoints]
jl near procPointLp2_fc
mov ebp, [storeebp]
; epilogue
pop esi
pop edi
pop ecx
pop ebx
pop eax
pop ebp
ret
; CodeWarrior still sucks :P
%ifdef LINUX
global processTriFanFC_VB
processTriFanFC_VB:
%else
global _processTriFanFC_VB
_processTriFanFC_VB:
%endif
; prologue
push ebp
mov ebp, esp
push eax
push ebx
push ecx
push edi
push esi
; Store the destination and source pointers
mov eax, in_src_points
mov [srcPoints], eax
mov eax, in_src_indices
mov [srcIndices], eax
mov eax, in_numpoints
mov [numPoints], eax
mov edi, in_dst
mov [storeebp], ebp
xor ebp, ebp
procPointLp1_fc_vb:
; This could be faster
mov esi, [srcIndices]
lea esi, [esi + ebp*4]
mov eax, dword [esi]
shl eax, 4 ; idx *= 16
mov esi, [srcPoints]
lea esi, [esi + eax]
mov eax, [esi + 0] ; x
mov ebx, [esi + 4] ; y
mov ecx, [esi + 8] ; z
mov edx, 0xFFFFFFFF ; c
mov [edi + 0], eax ; <- x
mov [edi + 4], ebx ; <- y
mov [edi + 8], ecx ; <- z
mov [edi + 12], edx ; <- c
fld dword [esi + 12]
fld dword [two55]
fmulp st1, st0
fistp dword [alpha]
mov eax, 255
sub eax, [alpha]
cmp eax, 0
jge near procPointLp1a_fc_vb
mov eax, 0
procPointLp1a_fc_vb:
shl eax, 24
mov [edi + 16], eax ; <- f
; tc0.s
fld dword [_texGen0 + 0] ; tg0.s.x
fmul dword [esi + 0]
fld dword [_texGen0 + 4] ; tg0.s.y
fmul dword [esi + 4]
fld dword [_texGen0 + 8] ; tg0.s.z
fmul dword [esi + 8]
fld dword [_texGen0 + 12] ; tg0.s.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 28] ; tc0.s
; tc0.t
fld dword [_texGen0 + 16] ; tg0.t.x
fmul dword [esi + 0]
fld dword [_texGen0 + 20] ; tg0.t.y
fmul dword [esi + 4]
fld dword [_texGen0 + 24] ; tg0.t.z
fmul dword [esi + 8]
fld dword [_texGen0 + 28] ; tg0.t.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 32] ; tc0.t
; tc1.s
fld dword [_texGen1 + 0] ; tg1.s.x
fmul dword [esi + 0]
fld dword [_texGen1 + 4] ; tg1.s.y
fmul dword [esi + 4]
fld dword [_texGen1 + 8] ; tg1.s.z
fmul dword [esi + 8]
fld dword [_texGen1 + 12] ; tg1.s.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 20] ; tc1.s
; tc1.t
fld dword [_texGen1 + 16] ; tg1.t.x
fmul dword [esi + 0]
fld dword [_texGen1 + 20] ; tg1.t.y
fmul dword [esi + 4]
fld dword [_texGen1 + 24] ; tg1.t.z
fmul dword [esi + 8]
fld dword [_texGen1 + 28] ; tg1.t.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 24] ; tc1.t
add edi, 36
inc ebp
cmp ebp, [numPoints]
jl near procPointLp1_fc_vb
mov ebp, [storeebp]
; epilogue
pop esi
pop edi
pop ecx
pop ebx
pop eax
pop ebp
ret
; More suckagea
%ifdef LINUX
global processTriFanSP_FC_VB
processTriFanSP_FC_VB:
%else
global _processTriFanSP_FC_VB
_processTriFanSP_FC_VB:
%endif
; prologue
push ebp
mov ebp, esp
push eax
push ebx
push ecx
push edi
push esi
; Store the destination and source pointers
mov eax, in_src_points
mov [srcPoints], eax
mov eax, in_src_indices
mov [srcIndices], eax
mov eax, in_numpoints
mov [numPoints], eax
mov eax, in_srcColors
mov [srcColors], eax
mov edi, in_dst
mov [storeebp], ebp
xor ebp, ebp
procPointLp2_fc_vb:
; This could be faster
mov esi, [srcIndices]
lea esi, [esi + ebp*4]
mov eax, dword [esi]
shl eax, 4 ; idx *= 16
mov esi, [srcPoints]
lea esi, [esi + eax]
mov eax, [esi + 0] ; x
mov ebx, [esi + 4] ; y
mov ecx, [esi + 8] ; z
mov [edi + 0], eax ; <- x
mov [edi + 4], ebx ; <- y
mov [edi + 8], ecx ; <- z
fld dword [esi + 12]
fld dword [two55]
fmulp st1, st0
fistp dword [alpha]
mov eax, 255
sub eax, [alpha]
cmp eax, 0
jge near procPointLp2a_fc_vb
mov eax, 0
procPointLp2a_fc_vb:
shl eax, 24
mov [edi + 16], eax ; <- fc
mov edx, [srcColors] ; color
lea edx, [edx + ebp*4] ; color
mov edx, [edx] ; color
mov eax, edx
mov ebx, 0x00FF00FF
and edx, ebx
not ebx
rol edx, 16
and eax, ebx
or edx, eax
mov [edi + 12], edx ; color
; tc0.s
fld dword [_texGen0 + 0] ; tg0.s.x
fmul dword [esi + 0]
fld dword [_texGen0 + 4] ; tg0.s.y
fmul dword [esi + 4]
fld dword [_texGen0 + 8] ; tg0.s.z
fmul dword [esi + 8]
fld dword [_texGen0 + 12] ; tg0.s.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 20] ; tc0.s
; tc0.t
fld dword [_texGen0 + 16] ; tg0.t.x
fmul dword [esi + 0]
fld dword [_texGen0 + 20] ; tg0.t.y
fmul dword [esi + 4]
fld dword [_texGen0 + 24] ; tg0.t.z
fmul dword [esi + 8]
fld dword [_texGen0 + 28] ; tg0.t.w
faddp st3, st0
faddp st1, st0
faddp st1, st0
fstp dword [edi + 24] ; tc0.t
add edi, 28
inc ebp
cmp ebp, [numPoints]
jl near procPointLp2_fc_vb
mov ebp, [storeebp]
; epilogue
pop esi
pop edi
pop ecx
pop ebx
pop eax
pop ebp
ret

310
engine/interior/itfdump_c.cc Executable file
View File

@ -0,0 +1,310 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "interior/itf.h"
//two55 dd 0x437F0000
//alpha dd 0
//============================================================
void FN_CDECL processTriFan(OutputPoint* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices)
{
U32 i, j;
F32 x,y,z;
for (i=0; i<numIndices; i++)
{
j = srcIndices[i];
x = srcPoints[j].point.x;
y = srcPoints[j].point.y;
z = srcPoints[j].point.z;
dst->point.x = x;
dst->point.y = y;
dst->point.z = z;
dst->fogCoord = srcPoints[j].fogCoord;
dst->texCoord.x = (texGen0[0]*x)
+ (texGen0[1]*y)
+ (texGen0[2]*z)
+ (texGen0[3]);
dst->texCoord.y = (texGen0[4]*x)
+ (texGen0[5]*y)
+ (texGen0[6]*z)
+ (texGen0[7]);
dst->lmCoord.x = (texGen1[0]*x)
+ (texGen1[1]*y)
+ (texGen1[2]*z)
+ (texGen1[3]);
dst->lmCoord.y = (texGen1[4]*x)
+ (texGen1[5]*y)
+ (texGen1[6]*z)
+ (texGen1[7]);
// move to next ptr.
dst++;
}
}
//============================================================
void FN_CDECL processTriFanSP(OutputPoint* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices,
const ColorI* srcColors)
{
U32 i, j;
float x,y,z;
for (i=0; i<numIndices; i++)
{
j = srcIndices[i];
x = srcPoints[j].point.x;
y = srcPoints[j].point.y;
z = srcPoints[j].point.z;
dst->point.x = x;
dst->point.y = y;
dst->point.z = z;
dst->fogColors = srcColors[j].getARGBEndian();
dst->texCoord.x = (texGen0[0]*x)
+ (texGen0[1]*y)
+ (texGen0[2]*z)
+ (texGen0[3]);
dst->texCoord.y = (texGen0[4]*x)
+ (texGen0[5]*y)
+ (texGen0[6]*z)
+ (texGen0[7]);
// move to next ptr.
dst++;
}
}
//============================================================
void FN_CDECL processTriFanVC_TF(OutputPoint* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices,
const ColorI* srcColors)
{
U32 i, j;
float x,y,z;
for (i=0; i<numIndices; i++)
{
j = srcIndices[i];
x = srcPoints[j].point.x;
y = srcPoints[j].point.y;
z = srcPoints[j].point.z;
dst->point.x = x;
dst->point.y = y;
dst->point.z = z;
dst->fogColors = srcColors[j].getARGBEndian();
// dc - I >think< I got this right...
dst->texCoord.x = fogCoordinatePointer[j].x;
dst->texCoord.y = fogCoordinatePointer[j].y;
dst->lmCoord.x = (texGen0[0]*x)
+ (texGen0[1]*y)
+ (texGen0[2]*z)
+ (texGen0[3]);
dst->lmCoord.y = (texGen0[4]*x)
+ (texGen0[5]*y)
+ (texGen0[6]*z)
+ (texGen0[7]);
// move to next ptr.
dst++;
}
}
//============================================================
void FN_CDECL processTriFanSP_FC(OutputPoint* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices,
const ColorI* srcColors)
{
U32 i, j;
float x,y,z;
for (i=0; i<numIndices; i++)
{
j = srcIndices[i];
x = srcPoints[j].point.x;
y = srcPoints[j].point.y;
z = srcPoints[j].point.z;
dst->point.x = x;
dst->point.y = y;
dst->point.z = z;
dst->fogColors = srcColors[j].getARGBEndian();
dst->lmCoord.x = srcPoints[j].fogCoord;
dst->texCoord.x = (texGen0[0]*x)
+ (texGen0[1]*y)
+ (texGen0[2]*z)
+ (texGen0[3]);
dst->texCoord.y = (texGen0[4]*x)
+ (texGen0[5]*y)
+ (texGen0[6]*z)
+ (texGen0[7]);
// move to next ptr.
dst++;
}
}
//============================================================
// helpers:
const float two55 = (F32)0x473F0000; // !!!!!!TBD -- not sure this is right...
#define ALPHA_CALC(a, c) \
a = c * two55; \
a = 255 - a; /* flip direction of value. */ \
if (a < 0) a = 0;
/* ASM for previous calculation:
fld dword [esi + 12]
fld dword [two55]
fmulp st1, st0
fistp dword [alpha]
mov eax, 255
sub eax, [alpha]
cmp eax, 0
jge near procPointLp1a_fc_vb
mov eax, 0
procPointLp1a_fc_vb:
shl eax, 24 // left this in the function instead of the macro.
mov [edi + 16], eax ; <- f
*/
//============================================================
void FN_CDECL processTriFanFC_VB(OutputPointFC_VB* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices)
{
S32 alpha;
U32 i, j;
float x,y,z;
for (i=0; i<numIndices; i++)
{
j = srcIndices[i];
x = srcPoints[j].point.x;
y = srcPoints[j].point.y;
z = srcPoints[j].point.z;
dst->point.x = x;
dst->point.y = y;
dst->point.z = z;
dst->currentColors = 0xFFFFFFFF;
ALPHA_CALC(alpha, srcPoints[j].fogCoord);
dst->fogColors = ((U32)alpha)<<24; // move into alpha position.
// dc - note the texGens are used in reverse order. that's what the ASM did...
dst->texCoord.x = (texGen1[0]*x)
+ (texGen1[1]*y)
+ (texGen1[2]*z)
+ (texGen1[3]);
dst->texCoord.y = (texGen1[4]*x)
+ (texGen1[5]*y)
+ (texGen1[6]*z)
+ (texGen1[7]);
dst->lmCoord.x = (texGen0[0]*x)
+ (texGen0[1]*y)
+ (texGen0[2]*z)
+ (texGen0[3]);
dst->lmCoord.y = (texGen0[4]*x)
+ (texGen0[5]*y)
+ (texGen0[6]*z)
+ (texGen0[7]);
// move to next ptr.
dst++;
}
}
//!!!!!!TBD -- is there a rotate intrinsic?????
#define ROL16(x) (x) = ((((x)<<16)&0xFFFF0000) | (((x)>>16)&0x0000FFFF))
//============================================================
void FN_CDECL processTriFanSP_FC_VB(OutputPointSP_FC_VB* dst,
const ItrPaddedPoint* srcPoints,
const U32* srcIndices,
const U32 numIndices,
const ColorI* srcColors)
{
S32 alpha;
U32 i, j, tmp, tmp2;
float x,y,z;
for (i=0; i<numIndices; i++)
{
j = srcIndices[i];
x = srcPoints[j].point.x;
y = srcPoints[j].point.y;
z = srcPoints[j].point.z;
dst->point.x = x;
dst->point.y = y;
dst->point.z = z;
/*
mov edx, [srcColors] ; color
lea edx, [edx + ebp*4] ; color
mov edx, [edx] ; color
mov eax, edx
mov ebx, 0x00FF00FF
and edx, ebx
not ebx
and eax, ebx
rol edx, 16
or edx, eax
mov [edi + 12], edx ; color
*/
tmp = srcColors[j].getARGBEndian();
tmp2 = tmp;
tmp = (tmp & 0x00FF00FF);
tmp2 = (tmp2 & 0xFF00FF00);
ROL16(tmp);
dst->lmColors = (tmp | tmp2);
ALPHA_CALC(alpha, srcPoints[j].fogCoord);
dst->fogColors = ((U32)alpha)<<24; // move into alpha position.
dst->texCoord.x = (texGen0[0]*x)
+ (texGen0[1]*y)
+ (texGen0[2]*z)
+ (texGen0[3]);
dst->texCoord.y = (texGen0[4]*x)
+ (texGen0[5]*y)
+ (texGen0[6]*z)
+ (texGen0[7]);
// move to next ptr.
dst++;
}
}

View File

@ -0,0 +1,75 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "interior/lightUpdateGrouper.h"
LightUpdateGrouper::LightUpdateGrouper(const U32 bitStart, const U32 bitEnd)
{
AssertFatal(bitEnd >= bitStart, "Error, bitend must be greater than bit start");
AssertFatal(bitEnd < 32, "Error, bitend too large. must be in the range 0..31");
mBitStart = bitStart;
mBitEnd = bitEnd;
}
LightUpdateGrouper::~LightUpdateGrouper()
{
mBitStart = 0xFFFFFFFF;
mBitEnd = 0xFFFFFFFF;
}
void LightUpdateGrouper::addKey(const U32 key)
{
#ifdef TORQUE_DEBUG
for (U32 i = 0; i < mKeys.size(); i++)
AssertFatal(mKeys[i] != key, "Error, key already in the array!");
#endif
mKeys.push_back(key);
}
U32 LightUpdateGrouper::getKeyMask(const U32 key) const
{
U32 numBits = mBitEnd - mBitStart + 1;
for (U32 i = 0; i < mKeys.size(); i++) {
if (mKeys[i] == key) {
U32 idx = i % numBits;
return (1 << (idx + mBitStart));
}
}
AssertFatal(false, "Error, key not in the array!");
return 0;
}
LightUpdateGrouper::BitIterator LightUpdateGrouper::begin()
{
BitIterator itr;
itr.mGrouper = this;
itr.mCurrBit = mBitStart;
itr.resetKeyArray();
return itr;
}
void LightUpdateGrouper::BitIterator::resetKeyArray()
{
mKeyArray.clear();
if (valid() == false)
return;
// Ok, we need to select out every (mBitEnd - mBitStart - 1)th key,
// starting at mCurrBit.
U32 numBits = mGrouper->mBitEnd - mGrouper->mBitStart + 1;
U32 numKeys = mGrouper->mKeys.size();
for (U32 i = mCurrBit - mGrouper->mBitStart; i < numKeys; i += numBits) {
mKeyArray.push_back(mGrouper->mKeys[i]);
}
}

View File

@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _LIGHTUPDATEGROUPER_H_
#define _LIGHTUPDATEGROUPER_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
class LightUpdateGrouper
{
Vector<U32> mKeys;
U32 mBitStart;
U32 mBitEnd;
public:
class BitIterator {
friend class LightUpdateGrouper;
private:
Vector<U32> mKeyArray;
U32 mCurrBit;
LightUpdateGrouper* mGrouper;
void resetKeyArray();
public:
typedef U32 const* iterator;
bool valid();
U32 getNumKeys();
U32 getMask();
BitIterator& operator++(int);
BitIterator& operator++();
iterator begin();
iterator end();
};
friend class BitIterator;
public:
LightUpdateGrouper(const U32 bitStart, const U32 bitEnd);
~LightUpdateGrouper();
void addKey(const U32 key);
U32 getKeyMask(const U32 key) const;
BitIterator begin();
};
//--------------------------------------------------------------------------
inline LightUpdateGrouper::BitIterator& LightUpdateGrouper::BitIterator::operator++()
{
mCurrBit++;
resetKeyArray();
return *this;
}
inline LightUpdateGrouper::BitIterator& LightUpdateGrouper::BitIterator::operator++(int)
{
return operator++();
}
inline LightUpdateGrouper::BitIterator::iterator LightUpdateGrouper::BitIterator::begin()
{
if (valid() == false)
return NULL;
return mKeyArray.begin();
}
inline LightUpdateGrouper::BitIterator::iterator LightUpdateGrouper::BitIterator::end()
{
if (valid() == false)
return NULL;
return mKeyArray.end();
}
inline bool LightUpdateGrouper::BitIterator::valid()
{
return mCurrBit <= mGrouper->mBitEnd;
}
inline U32 LightUpdateGrouper::BitIterator::getNumKeys()
{
return mKeyArray.size();
}
inline U32 LightUpdateGrouper::BitIterator::getMask()
{
return (1 << mCurrBit);
}
#endif // _H_LIGHTUPDATEGROUPER_

View File

@ -0,0 +1,501 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//
// 09/03/03 MM: Changes made by JF to use a stencil mask for mirrors.
//
//-----------------------------------------------------------------------------
#include "interior/mirrorSubObject.h"
#include "interior/interiorInstance.h"
#include "interior/interior.h"
#include "dgl/materialList.h"
#include "core/stream.h"
#include "dgl/dgl.h"
#include "sceneGraph/sgUtil.h"
IMPLEMENT_CONOBJECT(MirrorSubObject);
#include "platform/platformVideo.h"
#include "sceneGraph/sceneGraph.h"
//--------------------------------------------------------------------------
MirrorSubObject::MirrorSubObject()
{
mTypeMask = StaticObjectType;
mInitialized = false;
}
MirrorSubObject::~MirrorSubObject()
{
}
//--------------------------------------------------------------------------
void MirrorSubObject::initPersistFields()
{
Parent::initPersistFields();
//
}
//--------------------------------------------------------------------------
void MirrorSubObject::renderObject(SceneState* state, SceneRenderImage* image)
{
// the surface is rendered when the portal is closed
return;
}
//--------------------------------------------------------------------------
void MirrorSubObject::transformModelview(const U32 portalIndex, const MatrixF& oldMV,
MatrixF* pNewMV)
{
AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!");
AssertFatal(portalIndex == 0, "Error, we only have one portal!");
*pNewMV = oldMV;
pNewMV->mul(mReflectionMatrix);
}
//--------------------------------------------------------------------------
void MirrorSubObject::transformPosition(const U32 portalIndex, Point3F& ioPosition)
{
AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!");
AssertFatal(portalIndex == 0, "Error, we only have one portal!");
mReflectionMatrix.mulP(ioPosition);
}
//--------------------------------------------------------------------------
bool MirrorSubObject::computeNewFrustum(const U32 portalIndex,
const F64* oldFrustum,
const F64 nearPlane,
const F64 farPlane,
const RectI& oldViewport,
F64* newFrustum,
RectI& newViewport,
const bool flippedMatrix)
{
AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!");
AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!");
Interior* interior = getInstance()->getDetailLevel(mDetailLevel);
static Vector<SGWinding> mirrorWindings;
mirrorWindings.setSize(surfaceCount);
for (U32 i = 0; i < surfaceCount; i++) {
SGWinding& rSGWinding = mirrorWindings[i];
const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart + i];
U32 fanIndices[32];
U32 numFanIndices = 0;
interior->collisionFanFromSurface(rSurface, fanIndices, &numFanIndices);
for (U32 j = 0; j < numFanIndices; j++)
rSGWinding.points[j] = interior->mPoints[fanIndices[j]].point;
rSGWinding.numPoints = numFanIndices;
}
MatrixF finalModelView;
dglGetModelview(&finalModelView);
finalModelView.mul(getSOTransform());
finalModelView.scale(getSOScale());
return sgComputeNewFrustum(oldFrustum, nearPlane, farPlane,
oldViewport,
mirrorWindings.address(), mirrorWindings.size(),
finalModelView,
newFrustum, newViewport,
flippedMatrix);
}
//--------------------------------------------------------------------------
void MirrorSubObject::openPortal(const U32 portalIndex,
SceneState* pCurrState,
SceneState* pParentState)
{
AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!");
AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!");
if (mZone == 0)
pParentState->setupZoneProjection(getInstance()->getCurrZone(0));
else
pParentState->setupZoneProjection(mZone + getInstance()->getZoneRangeStart() - 1);
// setup transformation
glPushMatrix();
dglMultMatrix(&getSOTransform());
glScalef(getSOScale().x, getSOScale().y, getSOScale().z);
// setup stencil buffer:
glClearStencil(0x0);
glStencilMask(~0u);
glEnable(GL_STENCIL_TEST);
static U32 lastStateKey = 0;
U32 stateKey = gClientSceneGraph->getStateKey();
// dont clear the stencil for every portal
if(lastStateKey != stateKey)
{
lastStateKey = stateKey;
glClear(GL_STENCIL_BUFFER_BIT);
}
// clear with the fog color
ColorF fogColor = gClientSceneGraph->getFogColor();
glColor3f(fogColor.red, fogColor.green, fogColor.blue);
Interior * interior = getInstance()->getDetailLevel(mDetailLevel);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(ItrPaddedPoint), interior->mPoints.address());
// render the visible surface into the stencil buffer (also render the fog color
// since terrain may not be rendered and it assumes a fog clear)
glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
for (U32 i = 0; i < surfaceCount; i++)
{
glDrawElements(GL_TRIANGLE_STRIP,
interior->mSurfaces[surfaceStart+i].windingCount,
GL_UNSIGNED_INT,
&interior->mWindings[interior->mSurfaces[surfaceStart+i].windingStart]);
}
// now clear the visible surface depth (use stencil). disable color buffers (already
// rendered fog in previous step).
glDepthRange(1, 1);
glDepthFunc(GL_ALWAYS);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilFunc(GL_EQUAL, 1, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
for (U32 i = 0; i < surfaceCount; i++)
{
glDrawElements(GL_TRIANGLE_STRIP,
interior->mSurfaces[surfaceStart+i].windingCount,
GL_UNSIGNED_INT,
&interior->mWindings[interior->mSurfaces[surfaceStart+i].windingStart]);
}
// reset states (stencil is setup correctly)
glDepthFunc(GL_LEQUAL);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthRange(0, 1);
glDisableClientState(GL_VERTEX_ARRAY);
// reset transform
glPopMatrix();
dglSetCanonicalState();
}
void MirrorSubObject::closePortal(const U32 portalIndex,
SceneState* pCurrState,
SceneState* pParentState)
{
AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!");
AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!");
// setup transformation
glPushMatrix();
dglMultMatrix(&getSOTransform());
glScalef(getSOScale().x, getSOScale().y, getSOScale().z);
// update depth over portal
Interior * interior = getInstance()->getDetailLevel(mDetailLevel);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(ItrPaddedPoint), interior->mPoints.address());
// want to clear stencil value
glStencilFunc(GL_EQUAL, 1, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
// render the alpha surface
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_GEN_S);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glEnable(GL_TEXTURE_GEN_T);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_GEN_S);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glEnable(GL_TEXTURE_GEN_T);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnableClientState(GL_VERTEX_ARRAY);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D, interior->mMaterialList->getMaterial(interior->mSurfaces[surfaceStart].textureIndex).getGLName());
glActiveTextureARB(GL_TEXTURE1_ARB);
Vector<U32>* pLMapIndices = &interior->mNormalLMapIndices;
U32 baseIndex = (*pLMapIndices)[surfaceStart];
TextureHandle *tex = gInteriorLMManager.getHandle( interior->mLMHandle, getInstance()->getLMHandle(), baseIndex);
glBindTexture(GL_TEXTURE_2D, tex->getGLName() );
glActiveTextureARB(GL_TEXTURE0_ARB);
glTexGenfv(GL_S, GL_OBJECT_PLANE, (GLfloat*)interior->mTexGenEQs[interior->mSurfaces[surfaceStart].texGenIndex].planeX);
glTexGenfv(GL_T, GL_OBJECT_PLANE, (GLfloat*)interior->mTexGenEQs[interior->mSurfaces[surfaceStart].texGenIndex].planeY);
glActiveTextureARB(GL_TEXTURE1_ARB);
glColor4f(1, 1, 1, mAlphaLevel);
for (U32 i = 0; i < surfaceCount; i++) {
glTexGenfv(GL_S, GL_OBJECT_PLANE, (GLfloat*)interior->mLMTexGenEQs[surfaceStart+i].planeX);
glTexGenfv(GL_T, GL_OBJECT_PLANE, (GLfloat*)interior->mLMTexGenEQs[surfaceStart+i].planeY);
glDrawElements(GL_TRIANGLE_STRIP,
interior->mSurfaces[surfaceStart+i].windingCount,
GL_UNSIGNED_INT,
&interior->mWindings[interior->mSurfaces[surfaceStart+i].windingStart]);
}
glDisableClientState(GL_VERTEX_ARRAY);
glActiveTextureARB(GL_TEXTURE0_ARB);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
// reset transform
glPopMatrix();
// disable stencil test
glDisable(GL_STENCIL_TEST);
dglSetCanonicalState();
}
//--------------------------------------------------------------------------
void MirrorSubObject::getWSPortalPlane(const U32 portalIndex, PlaneF* pPlane)
{
AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!");
Interior* interior = getInstance()->getDetailLevel(mDetailLevel);
const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart];
PlaneF temp = interior->getPlane(rSurface.planeIndex);
if (Interior::planeIsFlipped(rSurface.planeIndex))
temp.neg();
mTransformPlane(getSOTransform(), getSOScale(), temp, pPlane);
}
//--------------------------------------------------------------------------
U32 MirrorSubObject::getSubObjectKey() const
{
return InteriorSubObject::MirrorSubObjectKey;
}
bool MirrorSubObject::_readISO(Stream& stream)
{
AssertFatal(isInitialized() == false, "Error, should not be initialized here!");
if (Parent::_readISO(stream) == false)
return false;
stream.read(&mDetailLevel);
stream.read(&mZone);
stream.read(&mAlphaLevel);
stream.read(&surfaceCount);
stream.read(&surfaceStart);
stream.read(&mCentroid.x);
stream.read(&mCentroid.y);
stream.read(&mCentroid.z);
return true;
}
bool MirrorSubObject::_writeISO(Stream& stream) const
{
if (Parent::_writeISO(stream) == false)
return false;
stream.write(mDetailLevel);
stream.write(mZone);
stream.write(mAlphaLevel);
stream.write(surfaceCount);
stream.write(surfaceStart);
stream.write(mCentroid.x);
stream.write(mCentroid.y);
stream.write(mCentroid.z);
return true;
}
SubObjectRenderImage* MirrorSubObject::getRenderImage(SceneState* state,
const Point3F& osPoint)
{
if (isInitialized() == false)
setupTransforms();
// Check to make sure that we&#180;re on the right side of the plane...
Interior* interior = getInstance()->getDetailLevel(mDetailLevel);
const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart];
PlaneF plane = interior->getPlane(rSurface.planeIndex);
if (Interior::planeIsFlipped(rSurface.planeIndex))
plane.neg();
if (plane.whichSide(osPoint) != PlaneF::Front)
return NULL;
// On the right side, guess we have to return an image and a portal...
//
SubObjectRenderImage* ri = new SubObjectRenderImage;
ri->obj = this;
ri->isTranslucent = false;
U32 realZone;
if (getInstance()->getZoneRangeStart() == 0xFFFFFFFF || mZone == 0) {
realZone = getInstance()->getCurrZone(0);
} else {
realZone = getInstance()->getZoneRangeStart() + mZone - 1;
}
// Create the WS start point. this will be the centroid of the first poly in os space,
// transformed out for the sceneGraph, with a smidge of our normal added in to pull
// it off the surface plane...
Point3F startPoint = mCentroid;
PlaneF temp = interior->getPlane(rSurface.planeIndex);
if (Interior::planeIsFlipped(rSurface.planeIndex))
temp.neg();
startPoint += Point3F(temp.x, temp.y, temp.z) * 0.01f;
getSOTransform().mulP(startPoint);
startPoint.convolve(getSOScale());
state->insertTransformPortal(this, 0, realZone, startPoint, true);
return ri;
}
bool MirrorSubObject::renderDetailDependant() const
{
return true;
}
U32 MirrorSubObject::getZone() const
{
return mZone;
}
void MirrorSubObject::setupTransforms()
{
mInitialized = true;
Interior* interior = getInstance()->getDetailLevel(mDetailLevel);
const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart];
for( U32 i=0; i<surfaceCount; i++ )
{
Interior::Surface& surface = interior->mSurfaces[surfaceStart + i];
//surface.mirrored = true;
}
PlaneF plane = interior->getPlane(rSurface.planeIndex);
if (Interior::planeIsFlipped(rSurface.planeIndex))
plane.neg();
Point3F n(plane.x, plane.y, plane.z);
Point3F q = n;
q *= -plane.d;
MatrixF t(true);
t.scale(getSOScale());
t.mul(getSOTransform());
t.mulV(n);
t.mulP(q);
F32* ra = mReflectionMatrix;
ra[0] = 1.0f - 2.0f*(n.x*n.x); ra[1] = 0.0f - 2.0f*(n.x*n.y); ra[2] = 0.0f - 2.0f*(n.x*n.z); ra[3] = 0.0f;
ra[4] = 0.0f - 2.0f*(n.y*n.x); ra[5] = 1.0f - 2.0f*(n.y*n.y); ra[6] = 0.0f - 2.0f*(n.y*n.z); ra[7] = 0.0f;
ra[8] = 0.0f - 2.0f*(n.z*n.x); ra[9] = 0.0f - 2.0f*(n.z*n.y); ra[10] = 1.0f - 2.0f*(n.z*n.z); ra[11] = 0.0f;
Point3F qnn = n * mDot(n, q);
ra[12] = qnn.x * 2.0f;
ra[13] = qnn.y * 2.0f;
ra[14] = qnn.z * 2.0f;
ra[15] = 1.0f;
// Now, the GGems series (as of v1) uses row vectors (arg)
mReflectionMatrix.transpose();
}
void MirrorSubObject::noteTransformChange()
{
setupTransforms();
Parent::noteTransformChange();
}
InteriorSubObject* MirrorSubObject::clone(InteriorInstance* instance) const
{
MirrorSubObject* pClone = new MirrorSubObject;
pClone->mDetailLevel = mDetailLevel;
pClone->mZone = mZone;
pClone->mAlphaLevel = mAlphaLevel;
pClone->mCentroid = mCentroid;
pClone->surfaceCount = surfaceCount;
pClone->surfaceStart = surfaceStart;
pClone->mInteriorInstance = instance;
return pClone;
}

View File

@ -0,0 +1,85 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _MIRRORSUBOBJECT_H_
#define _MIRRORSUBOBJECT_H_
#ifndef _INTERIORSUBOBJECT_H_
#include "interior/interiorSubObject.h"
#endif
class TextureHandle;
class MirrorSubObject : public InteriorSubObject
{
typedef InteriorSubObject Parent;
public:
U32 mDetailLevel;
U32 mZone;
F32 mAlphaLevel;
Point3F mCentroid;
U32 surfaceCount;
U32 surfaceStart;
private:
bool mInitialized;
TextureHandle* mWhite;
MatrixF mReflectionMatrix;
bool isInitialized() const { return mInitialized; }
void setupTransforms();
// ISO overrides
protected:
U32 getSubObjectKey() const;
bool _readISO(Stream&);
bool _writeISO(Stream&) const;
// Render control. A sub-object should return false from renderDetailDependant if
// it exists only at the level-0 detail level, ie, doors, elevators, etc., true
// if should only render at the interiors detail, ie, translucencies.
SubObjectRenderImage* getRenderImage(SceneState *state, const Point3F&);
bool renderDetailDependant() const;
U32 getZone() const;
void noteTransformChange();
InteriorSubObject* clone(InteriorInstance*) const;
// Rendering
protected:
void renderObject(SceneState *state, SceneRenderImage *image);
void transformModelview(const U32 portalIndex, const MatrixF &oldMV, MatrixF *newMV);
void transformPosition(const U32 portalIndex, Point3F &point);
bool computeNewFrustum(const U32 portalIndex,
const F64* oldFrustum,
const F64 nearPlane,
const F64 farPlane,
const RectI& oldViewport,
F64* newFrustum,
RectI& newViewport,
const bool flippedMatrix);
void openPortal(const U32 portalIndex,
SceneState* pCurrState,
SceneState* pParentState);
void closePortal(const U32 portalIndex,
SceneState* pCurrState,
SceneState* pParentState);
void getWSPortalPlane(const U32 portalIndex, PlaneF *plane);
public:
MirrorSubObject();
~MirrorSubObject();
DECLARE_CONOBJECT(MirrorSubObject);
static void initPersistFields();
};
#endif // _H_MIRRORSUBOBJECT

625
engine/interior/pathedInterior.cc Executable file
View File

@ -0,0 +1,625 @@
//--------------------------------------------------------------------------
// PathedInterior.cc:
//
//
//--------------------------------------------------------------------------
#include "interior/pathedInterior.h"
#include "core/stream.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "sceneGraph/sceneState.h"
#include "math/mathIO.h"
#include "core/bitStream.h"
#include "interior/interior.h"
#include "sim/simPath.h"
#include "sim/pathManager.h"
#include "core/frameAllocator.h"
#include "sceneGraph/sceneGraph.h"
IMPLEMENT_CO_NETOBJECT_V1(PathedInterior);
IMPLEMENT_CO_DATABLOCK_V1(PathedInteriorData);
//--------------------------------------------------------------------------
PathedInteriorData::PathedInteriorData()
{
for(U32 i = 0; i < MaxSounds; i++)
sound[i] = NULL;
}
void PathedInteriorData::initPersistFields()
{
addField("StartSound", TypeAudioProfilePtr, Offset(sound[StartSound], PathedInteriorData));
addField("SustainSound", TypeAudioProfilePtr, Offset(sound[SustainSound], PathedInteriorData));
addField("StopSound", TypeAudioProfilePtr, Offset(sound[StopSound], PathedInteriorData));
Parent::initPersistFields();
}
void PathedInteriorData::packData(BitStream *stream)
{
for (S32 i = 0; i < MaxSounds; i++)
{
if (stream->writeFlag(sound[i]))
stream->writeRangedU32(packed? SimObjectId(sound[i]):
sound[i]->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
}
Parent::packData(stream);
}
void PathedInteriorData::unpackData(BitStream* stream)
{
for (S32 i = 0; i < MaxSounds; i++) {
sound[i] = NULL;
if (stream->readFlag())
sound[i] = (AudioProfile*)stream->readRangedU32(DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
Parent::unpackData(stream);
}
bool PathedInteriorData::preload(bool server, char errorBuffer[256])
{
if(!Parent::preload(server, errorBuffer))
return false;
// Resolve objects transmitted from server
if (!server)
{
for (S32 i = 0; i < MaxSounds; i++)
if (sound[i])
Sim::findObject(SimObjectId(sound[i]),sound[i]);
}
return true;
}
PathedInterior::PathedInterior()
{
mNetFlags.set(Ghostable);
mTypeMask = InteriorObjectType;
mCurrentPosition = 0;
mTargetPosition = 0;
mPathKey = 0xFFFFFFFF;
mStopped = false;
mSustainHandle = 0;
}
PathedInterior::~PathedInterior()
{
//
}
PathedInterior *PathedInterior::mClientPathedInteriors = NULL;
//--------------------------------------------------------------------------
void PathedInterior::initPersistFields()
{
Parent::initPersistFields();
addField("interiorResource", TypeFilename, Offset(mInteriorResName, PathedInterior));
addField("interiorIndex", TypeS32, Offset(mInteriorResIndex, PathedInterior));
addField("basePosition", TypeMatrixPosition, Offset(mBaseTransform, PathedInterior));
addField("baseRotation", TypeMatrixRotation, Offset(mBaseTransform, PathedInterior));
addField("baseScale", TypePoint3F, Offset(mBaseScale, PathedInterior));
}
//--------------------------------------------------------------------------
bool PathedInterior::onAdd()
{
if(!Parent::onAdd())
return false;
// Load the interior resource and extract the interior that is us.
char buffer[256];
mInteriorRes = ResourceManager->load(mInteriorResName);
if (bool(mInteriorRes) == false)
return false;
mInterior = mInteriorRes->getSubObject(mInteriorResIndex);
if (mInterior == NULL)
return false;
// Setup bounding information
mObjBox = mInterior->getBoundingBox();
resetWorldBox();
setScale(mBaseScale);
setTransform(mBaseTransform);
if (isClientObject()) {
mNextClientPI = mClientPathedInteriors;
mClientPathedInteriors = this;
mInterior->prepForRendering(mInteriorRes.getFilePath());
// gInteriorLMManager.addInstance(mInterior->getLMHandle(), mLMHandle, NULL, this);
}
if(isClientObject())
{
Point3F initialPos;
mBaseTransform.getColumn(3, &initialPos);
Point3F pathPos;
//gClientPathManager->getPathPosition(mPathKey, 0, pathPos);
mOffset = initialPos - pathPos;
//gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, pathPos);
MatrixF mat = getTransform();
mat.setColumn(3, pathPos + mOffset);
setTransform(mat);
}
addToScene();
return true;
}
bool PathedInterior::onSceneAdd(SceneGraph *g)
{
if(!Parent::onSceneAdd(g))
return false;
return true;
}
void PathedInterior::onSceneRemove()
{
Parent::onSceneRemove();
}
bool PathedInterior::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<PathedInteriorData*>(dptr);
return Parent::onNewDataBlock(dptr);
}
void PathedInterior::onRemove()
{
if(isClientObject())
{
if(mSustainHandle)
{
alxStop(mSustainHandle);
mSustainHandle = 0;
}
PathedInterior **walk = &mClientPathedInteriors;
while(*walk)
{
if(*walk == this)
{
*walk = mNextClientPI;
break;
}
walk = &((*walk)->mNextClientPI);
}
/* if(bool(mInteriorRes) && mLMHandle != 0xFFFFFFFF)
{
if (mInterior->getLMHandle() != 0xFFFFFFFF)
gInteriorLMManager.removeInstance(mInterior->getLMHandle(), mLMHandle);
}*/
}
removeFromScene();
Parent::onRemove();
}
//------------------------------------------------------------------------------
bool PathedInterior::buildPolyList(AbstractPolyList* list, const Box3F& wsBox, const SphereF&)
{
if (bool(mInteriorRes) == false)
return false;
// Setup collision state data
list->setTransform(&getTransform(), getScale());
list->setObject(this);
return mInterior->buildPolyList(list, wsBox, mWorldToObj, getScale());
}
//--------------------------------------------------------------------------
bool PathedInterior::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
if (mPathKey == Path::NoPathIndex)
return false;
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
bool render = false;
for (U32 i = 0; i < getNumCurrZones(); i++)
if (state->getZoneState(getCurrZone(i)).render == true)
render = true;
if (render == true) {
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
state->insertRenderImage(image);
}
return false;
}
extern ColorF gInteriorFogColor;
void PathedInterior::renderObject(SceneState* state, SceneRenderImage*)
{
U32 storedWaterMark = FrameAllocator::getWaterMark();
Point3F worldOrigin;
getRenderTransform().getColumn(3, &worldOrigin);
// Set up the model view and the global render state...
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
ColorF oneColor(1,1,1,1);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, oneColor);
gClientSceneGraph->getLightManager()->sgSetupLights(this);
// dglMultMatrix(&newMat);
dglMultMatrix(&mRenderObjToWorld);
glScalef(mObjScale.x, mObjScale.y, mObjScale.z);
glEnable(GL_CULL_FACE);
// We need to decide if we're going to use the low-res textures
//mInterior->setupFog(state);
//gInteriorFogColor = state->getFogColor();
mInterior->renderAsShape();
//mInterior->clearFog();
gClientSceneGraph->getLightManager()->sgResetLights();
glDisable(GL_CULL_FACE);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
FrameAllocator::setWaterMark(storedWaterMark);
dglSetRenderPrimType(0);
// Reset the small textures...
TextureManager::setSmallTexturesActive(false);
}
void PathedInterior::resolvePathKey()
{
if(mPathKey == 0xFFFFFFFF && !isGhost())
{
mPathKey = getPathKey();
Point3F pathPos;
Point3F initialPos;
mBaseTransform.getColumn(3, &initialPos);
//gServerPathManager->getPathPosition(mPathKey, 0, pathPos);
mOffset = initialPos - pathPos;
}
}
//--------------------------------------------------------------------------
U32 PathedInterior::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
resolvePathKey();
if (stream->writeFlag(mask & InitialUpdateMask))
{
// Inital update...
stream->writeString(mInteriorResName);
stream->write(mInteriorResIndex);
stream->writeAffineTransform(mBaseTransform);
mathWrite(*stream, mBaseScale);
stream->write(mPathKey);
}
if(stream->writeFlag((mask & NewPositionMask) && mPathKey != Path::NoPathIndex))
stream->writeInt(S32(mCurrentPosition), gServerPathManager->getPathTimeBits(mPathKey));
if(stream->writeFlag((mask & NewTargetMask) && mPathKey != Path::NoPathIndex))
{
if(stream->writeFlag(mTargetPosition < 0))
{
stream->writeFlag(mTargetPosition == -1);
}
else
stream->writeInt(S32(mTargetPosition), gServerPathManager->getPathTimeBits(mPathKey));
}
return retMask;
}
void PathedInterior::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
MatrixF tempXForm;
Point3F tempScale;
if (stream->readFlag())
{
// Initial
mInteriorResName = stream->readSTString();
stream->read(&mInteriorResIndex);
stream->readAffineTransform(&tempXForm);
mathRead(*stream, &tempScale);
mBaseTransform = tempXForm;
mBaseScale = tempScale;
stream->read(&mPathKey);
}
if(stream->readFlag())
{
Point3F pathPos;
mCurrentPosition = stream->readInt(gClientPathManager->getPathTimeBits(mPathKey));
if(isProperlyAdded())
{
//gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, pathPos);
MatrixF mat = getTransform();
mat.setColumn(3, pathPos + mOffset);
setTransform(mat);
}
}
if(stream->readFlag())
{
if(stream->readFlag())
{
mTargetPosition = stream->readFlag() ? -1 : -2;
}
else
mTargetPosition = stream->readInt(gClientPathManager->getPathTimeBits(mPathKey));
}
}
void PathedInterior::processTick(const Move* move)
{
if(isServerObject())
{
U32 timeMs = 32;
if(mCurrentPosition != mTargetPosition)
{
S32 delta;
if(mTargetPosition == -1)
delta = timeMs;
else if(mTargetPosition == -2)
delta = -timeMs;
else
{
delta = mTargetPosition - mCurrentPosition;
if(delta < -timeMs)
delta = -timeMs;
else if(delta > timeMs)
delta = timeMs;
}
mCurrentPosition += delta;
U32 totalTime = gClientPathManager->getPathTotalTime(mPathKey);
while(mCurrentPosition > totalTime)
mCurrentPosition -= totalTime;
while(mCurrentPosition < 0)
mCurrentPosition += totalTime;
}
}
}
void PathedInterior::computeNextPathStep(U32 timeDelta)
{
S32 timeMs = timeDelta;
mStopped = false;
if(mCurrentPosition == mTargetPosition)
{
mExtrudedBox = getWorldBox();
mCurrentVelocity.set(0,0,0);
}
else
{
S32 delta;
if(mTargetPosition < 0)
{
if(mTargetPosition == -1)
delta = timeMs;
else if(mTargetPosition == -2)
delta = -timeMs;
mCurrentPosition += delta;
U32 totalTime = gClientPathManager->getPathTotalTime(mPathKey);
while(mCurrentPosition >= totalTime)
mCurrentPosition -= totalTime;
while(mCurrentPosition < 0)
mCurrentPosition += totalTime;
}
else
{
delta = mTargetPosition - mCurrentPosition;
if(delta < -timeMs)
delta = -timeMs;
else if(delta > timeMs)
delta = timeMs;
mCurrentPosition += delta;
}
Point3F curPoint;
Point3F newPoint = Point3F(0,0,0); //BJGNOTE - this shuts up the compiler, three lines down will actually set it.
MatrixF mat = getTransform();
mat.getColumn(3, &curPoint);
//gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, newPoint);
newPoint += mOffset;
Point3F displaceDelta = newPoint - curPoint;
mExtrudedBox = getWorldBox();
if(displaceDelta.x < 0)
mExtrudedBox.min.x += displaceDelta.x;
else
mExtrudedBox.max.x += displaceDelta.x;
if(displaceDelta.y < 0)
mExtrudedBox.min.y += displaceDelta.y;
else
mExtrudedBox.max.y += displaceDelta.y;
if(displaceDelta.z < 0)
mExtrudedBox.min.z += displaceDelta.z;
else
mExtrudedBox.max.z += displaceDelta.z;
mCurrentVelocity = displaceDelta * 1000 / F32(timeDelta);
}
Point3F pos;
mExtrudedBox.getCenter(&pos);
MatrixF mat = getTransform();
mat.setColumn(3, pos);
if(!mSustainHandle && mDataBlock->sound[PathedInteriorData::SustainSound])
{
//F32 volSave = mDataBlock->sound[PathedInteriorData::SustainSound]->mDescriptionObject->mDescription.mVolume;
//mDataBlock->sound[PathedInteriorData::SustainSound]->mDescriptionObject->mDescription.mVolume = 0;
mSustainHandle = alxPlay(mDataBlock->sound[PathedInteriorData::SustainSound], &mat);
//mDataBlock->sound[PathedInteriorData::SustainSound]->mDescriptionObject->mDescription.mVolume = volSave;
}
if(mSustainHandle)
{
Point3F pos;
mExtrudedBox.getCenter(&pos);
MatrixF mat = getTransform();
mat.setColumn(3, pos);
alxSourceMatrixF(mSustainHandle, &mat);
}
}
Point3F PathedInterior::getVelocity()
{
return mCurrentVelocity;
}
void PathedInterior::advance(F64 timeDelta)
{
if(mStopped)
return;
F64 timeMs = timeDelta;
if(mCurrentVelocity.len() == 0)
{
// if(mSustainHandle)
// {
// alxStop(mSustainHandle);
// mSustainHandle = 0;
// }
return;
}
MatrixF mat = getTransform();
Point3F newPoint;
mat.getColumn(3, &newPoint);
newPoint += mCurrentVelocity * timeDelta / 1000.0f;
//gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, newPoint);
mat.setColumn(3, newPoint);// + mOffset);
setTransform(mat);
setRenderTransform(mat);
}
U32 PathedInterior::getPathKey()
{
AssertFatal(isServerObject(), "Error, must be a server object to call this...");
SimGroup* myGroup = getGroup();
AssertFatal(myGroup != NULL, "No group for this object?");
for (SimGroup::iterator itr = myGroup->begin(); itr != myGroup->end(); itr++) {
Path* pPath = dynamic_cast<Path*>(*itr);
if (pPath != NULL) {
U32 pathKey = pPath->getPathIndex();
AssertFatal(pathKey != Path::NoPathIndex, "Error, path must have event over at this point...");
return pathKey;
}
}
return Path::NoPathIndex;
}
void PathedInterior::setPathPosition(S32 newPosition)
{
resolvePathKey();
if(newPosition < 0)
newPosition = 0;
if(newPosition > gServerPathManager->getPathTotalTime(mPathKey))
newPosition = gServerPathManager->getPathTotalTime(mPathKey);
mCurrentPosition = mTargetPosition = newPosition;
setMaskBits(NewPositionMask | NewTargetMask);
}
void PathedInterior::setTargetPosition(S32 newPosition)
{
resolvePathKey();
if(newPosition < -2)
newPosition = 0;
if(newPosition > S32(gServerPathManager->getPathTotalTime(mPathKey)))
newPosition = gServerPathManager->getPathTotalTime(mPathKey);
if(mTargetPosition != newPosition)
{
mTargetPosition = newPosition;
setMaskBits(NewTargetMask);
}
}
ConsoleMethod(PathedInterior, setPathPosition, void, 3, 3, "")
{
object->setPathPosition(dAtoi(argv[2]));
}
ConsoleMethod(PathedInterior, setTargetPosition, void, 3, 3, "")
{
object->setTargetPosition(dAtoi(argv[2]));
}
//--------------------------------------------------------------------------
bool PathedInterior::readPI(Stream& stream)
{
mName = stream.readSTString();
mInteriorResName = stream.readSTString();
stream.read(&mInteriorResIndex);
stream.read(&mPathIndex);
mathRead(stream, &mOffset);
U32 numTriggers;
stream.read(&numTriggers);
mTriggers.setSize(numTriggers);
for (U32 i = 0; i < mTriggers.size(); i++)
mTriggers[i] = stream.readSTString();
return (stream.getStatus() == Stream::Ok);
}
bool PathedInterior::writePI(Stream& stream) const
{
stream.writeString(mName);
stream.writeString(mInteriorResName);
stream.write(mInteriorResIndex);
stream.write(mPathIndex);
mathWrite(stream, mOffset);
stream.write(mTriggers.size());
for (U32 i = 0; i < mTriggers.size(); i++)
stream.writeString(mTriggers[i]);
return (stream.getStatus() == Stream::Ok);
}
PathedInterior* PathedInterior::clone() const
{
PathedInterior* pClone = new PathedInterior;
pClone->mName = mName;
pClone->mInteriorResName = mInteriorResName;
pClone->mInteriorResIndex = mInteriorResIndex;
pClone->mPathIndex = mPathIndex;
pClone->mOffset = mOffset;
return pClone;
}

139
engine/interior/pathedInterior.h Executable file
View File

@ -0,0 +1,139 @@
//--------------------------------------------------------------------------
// PathedInterior.h:
//
//
//--------------------------------------------------------------------------
#ifndef _H_PATHEDINTERIOR
#define _H_PATHEDINTERIOR
#include "interior/interior.h"
#include "game/gameBase.h"
#include "core/resManager.h"
#include "interior/interiorRes.h"
#ifndef _INTERIORLMMANAGER_H_
#include "interior/interiorLMManager.h"
#endif
#include "audio/audioDataBlock.h"
class InteriorInstance;
class EditGeometry;
class EditInteriorResource;
struct PathedInteriorData : public GameBaseData {
typedef GameBaseData Parent;
public:
enum Sounds {
StartSound,
SustainSound,
StopSound,
MaxSounds
};
AudioProfile *sound[MaxSounds];
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
bool preload(bool server, char errorBuffer[256]);
PathedInteriorData();
DECLARE_CONOBJECT(PathedInteriorData);
};
class PathedInterior : public GameBase
{
typedef GameBase Parent;
friend class InteriorInstance;
friend class EditGeometry;
friend class EditInteriorResource;
PathedInteriorData *mDataBlock;
public:
enum UpdateMasks {
NewTargetMask = Parent::NextFreeMask,
NewPositionMask = Parent::NextFreeMask << 1,
NextFreeMask = Parent::NextFreeMask << 2,
};
private:
U32 getPathKey(); // only used on the server
// Persist fields
protected:
StringTableEntry mName;
S32 mPathIndex;
Vector<StringTableEntry> mTriggers;
Point3F mOffset;
Box3F mExtrudedBox;
bool mStopped;
// Loaded resources and fields
protected:
static PathedInterior *mClientPathedInteriors;
AUDIOHANDLE mSustainHandle;
StringTableEntry mInteriorResName;
S32 mInteriorResIndex;
Resource<InteriorResource> mInteriorRes;
Interior* mInterior;
Vector<ColorI> mVertexColorsNormal;
Vector<ColorI> mVertexColorsAlarm;
LM_HANDLE mLMHandle;
MatrixF mBaseTransform;
Point3F mBaseScale;
U32 mPathKey; // only used on the client
F64 mCurrentPosition;
S32 mTargetPosition;
Point3F mCurrentVelocity;
PathedInterior *mNextClientPI;
// Rendering
protected:
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
void renderObject(SceneState *state, SceneRenderImage *image);
void renderShadowVolumes(SceneState *state);
protected:
bool onAdd();
void onRemove();
bool onSceneAdd(SceneGraph *graph);
void onSceneRemove();
public:
PathedInterior();
~PathedInterior();
PathedInterior *getNext() { return mNextClientPI; }
static PathedInterior *getClientPathedInteriors() { return mClientPathedInteriors; }
void processTick(const Move* move);
void setStopped() { mStopped = true; }
void resolvePathKey();
bool onNewDataBlock(GameBaseData* dptr);
bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
bool readPI(Stream&);
bool writePI(Stream&) const;
PathedInterior* clone() const;
DECLARE_CONOBJECT(PathedInterior);
static void initPersistFields();
void setPathPosition(S32 newPosition);
void setTargetPosition(S32 targetPosition);
void computeNextPathStep(U32 timeDelta);
Box3F getExtrudedBox() { return mExtrudedBox; }
Point3F getVelocity();
void advance(F64 timeDelta);
U32 packUpdate(NetConnection *conn, U32 mask, BitStream* stream);
void unpackUpdate(NetConnection *conn, BitStream* stream);
};
#endif // _H_PATHEDINTERIOR