tge/engine/interior/interiorMap.cc
2017-04-17 06:17:10 -06:00

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);
}