785 lines
22 KiB
C++
Executable File
785 lines
22 KiB
C++
Executable File
#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);
|
|
}
|