592 lines
16 KiB
C++
Executable File
592 lines
16 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "sim/decalManager.h"
|
|
#include "dgl/dgl.h"
|
|
#include "sceneGraph/sceneGraph.h"
|
|
#include "sceneGraph/sceneState.h"
|
|
#include "ts/tsShapeInstance.h"
|
|
#include "core/bitStream.h"
|
|
#include "console/consoleTypes.h"
|
|
|
|
bool DecalManager::smDecalsOn = true;
|
|
bool DecalManager::sgThisIsSelfIlluminated = false;
|
|
bool DecalManager::sgLastWasSelfIlluminated = false;
|
|
const U32 DecalManager::csmFreePoolBlockSize = 256;
|
|
U32 DecalManager::smMaxNumDecals = 256;
|
|
U32 DecalManager::smDecalTimeout = 5000;
|
|
|
|
DecalManager* gDecalManager = NULL;
|
|
IMPLEMENT_CONOBJECT(DecalManager);
|
|
IMPLEMENT_CO_DATABLOCK_V1(DecalData);
|
|
|
|
namespace {
|
|
|
|
int QSORT_CALLBACK cmpDecalInstance(const void* p1, const void* p2)
|
|
{
|
|
const DecalInstance** pd1 = (const DecalInstance**)p1;
|
|
const DecalInstance** pd2 = (const DecalInstance**)p2;
|
|
|
|
return int(((char *)(*pd1)->decalData) - ((char *)(*pd2)->decalData));
|
|
}
|
|
|
|
} // namespace {}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
DecalData::DecalData()
|
|
{
|
|
sizeX = 1;
|
|
sizeY = 1;
|
|
textureName = "";
|
|
|
|
selfIlluminated = false;
|
|
lifeSpan = DecalManager::smDecalTimeout;
|
|
}
|
|
|
|
DecalData::~DecalData()
|
|
{
|
|
if(gDecalManager)
|
|
gDecalManager->dataDeleted(this);
|
|
}
|
|
|
|
|
|
void DecalData::packData(BitStream* stream)
|
|
{
|
|
Parent::packData(stream);
|
|
|
|
stream->write(sizeX);
|
|
stream->write(sizeY);
|
|
stream->writeString(textureName);
|
|
|
|
stream->write(selfIlluminated);
|
|
stream->write(lifeSpan);
|
|
}
|
|
|
|
void DecalData::unpackData(BitStream* stream)
|
|
{
|
|
Parent::unpackData(stream);
|
|
|
|
stream->read(&sizeX);
|
|
stream->read(&sizeY);
|
|
textureName = stream->readSTString();
|
|
|
|
stream->read(&selfIlluminated);
|
|
stream->read(&lifeSpan);
|
|
}
|
|
|
|
bool DecalData::preload(bool server, char errorBuffer[256])
|
|
{
|
|
if (Parent::preload(server, errorBuffer) == false)
|
|
return false;
|
|
|
|
if (sizeX < 0.0) {
|
|
Con::warnf("DecalData::preload: sizeX < 0");
|
|
sizeX = 0;
|
|
}
|
|
if (sizeY < 0.0) {
|
|
Con::warnf("DecalData::preload: sizeX < 0");
|
|
sizeY = 0;
|
|
}
|
|
if (textureName == NULL || textureName[0] == '\0') {
|
|
Con::errorf("No texture name for decal!");
|
|
return false;
|
|
}
|
|
|
|
if (!server) {
|
|
textureHandle = TextureHandle(textureName, MeshTexture);
|
|
if (textureHandle.getGLName() == 0) {
|
|
Con::errorf("Unable to load texture: %s for decal!", textureName);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
IMPLEMENT_CONSOLETYPE(DecalData)
|
|
IMPLEMENT_SETDATATYPE(DecalData)
|
|
IMPLEMENT_GETDATATYPE(DecalData)
|
|
|
|
void DecalData::initPersistFields()
|
|
{
|
|
addField("sizeX", TypeF32, Offset(sizeX, DecalData));
|
|
addField("sizeY", TypeF32, Offset(sizeY, DecalData));
|
|
addField("textureName", TypeFilename, Offset(textureName, DecalData));
|
|
|
|
addField("SelfIlluminated", TypeBool, Offset(selfIlluminated, DecalData));
|
|
addField("LifeSpan", TypeS32, Offset(lifeSpan, DecalData));
|
|
}
|
|
|
|
DecalManager::DecalManager()
|
|
{
|
|
mTypeMask |= DecalManagerObjectType;
|
|
|
|
mObjBox.min.set(-1e7, -1e7, -1e7);
|
|
mObjBox.max.set( 1e7, 1e7, 1e7);
|
|
mWorldBox.min.set(-1e7, -1e7, -1e7);
|
|
mWorldBox.max.set( 1e7, 1e7, 1e7);
|
|
|
|
mFreePool = NULL;
|
|
VECTOR_SET_ASSOCIATION(mDecalQueue);
|
|
VECTOR_SET_ASSOCIATION(mFreePoolBlocks);
|
|
}
|
|
|
|
|
|
DecalManager::~DecalManager()
|
|
{
|
|
mFreePool = NULL;
|
|
for (S32 i = 0; i < mFreePoolBlocks.size(); i++)
|
|
{
|
|
delete [] mFreePoolBlocks[i];
|
|
}
|
|
mDecalQueue.clear();
|
|
}
|
|
|
|
|
|
DecalInstance* DecalManager::allocateDecalInstance()
|
|
{
|
|
if (mFreePool == NULL)
|
|
{
|
|
// Allocate a new block of decals
|
|
mFreePoolBlocks.push_back(new DecalInstance[csmFreePoolBlockSize]);
|
|
|
|
// Init them onto the free pool chain
|
|
DecalInstance* pNewInstances = mFreePoolBlocks.last();
|
|
for (U32 i = 0; i < csmFreePoolBlockSize - 1; i++)
|
|
pNewInstances[i].next = &pNewInstances[i + 1];
|
|
pNewInstances[csmFreePoolBlockSize - 1].next = NULL;
|
|
mFreePool = pNewInstances;
|
|
}
|
|
AssertFatal(mFreePool != NULL, "Error, should always have a free pool available here!");
|
|
|
|
DecalInstance* pRet = mFreePool;
|
|
mFreePool = pRet->next;
|
|
pRet->next = NULL;
|
|
return pRet;
|
|
}
|
|
|
|
|
|
void DecalManager::freeDecalInstance(DecalInstance* trash)
|
|
{
|
|
AssertFatal(trash != NULL, "Error, no trash pointer to free!");
|
|
|
|
trash->next = mFreePool;
|
|
mFreePool = trash;
|
|
}
|
|
|
|
|
|
void DecalManager::dataDeleted(DecalData *data)
|
|
{
|
|
for(S32 i = mDecalQueue.size() - 1; i >= 0; i--)
|
|
{
|
|
DecalInstance *inst = mDecalQueue[i];
|
|
if(inst->decalData == data)
|
|
{
|
|
freeDecalInstance(inst);
|
|
mDecalQueue.erase(U32(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DecalManager::consoleInit()
|
|
{
|
|
Con::addVariable("$pref::decalsOn", TypeBool, &smDecalsOn);
|
|
Con::addVariable("$pref::Decal::maxNumDecals", TypeS32, &smMaxNumDecals);
|
|
Con::addVariable("$pref::Decal::decalTimeout", TypeS32, &smDecalTimeout);
|
|
}
|
|
|
|
void DecalManager::addDecal(const Point3F& pos,
|
|
Point3F normal,
|
|
DecalData* decalData)
|
|
{
|
|
if (smMaxNumDecals == 0)
|
|
return;
|
|
|
|
// DMM: Rework this, should be based on time
|
|
if(mDecalQueue.size() >= smMaxNumDecals)
|
|
{
|
|
findSpace();
|
|
}
|
|
|
|
Point3F vecX, vecY;
|
|
DecalInstance* newDecal = allocateDecalInstance();
|
|
newDecal->decalData = decalData;
|
|
newDecal->allocTime = Platform::getVirtualMilliseconds();
|
|
|
|
if(mFabs(normal.z) > 0.9f)
|
|
mCross(normal, Point3F(0.0f, 1.0f, 0.0f), &vecX);
|
|
else
|
|
mCross(normal, Point3F(0.0f, 0.0f, 1.0f), &vecX);
|
|
|
|
mCross(vecX, normal, &vecY);
|
|
|
|
normal.normalizeSafe();
|
|
Point3F position = Point3F(pos.x + (normal.x * 0.008), pos.y + (normal.y * 0.008), pos.z + (normal.z * 0.008));
|
|
|
|
vecX.normalizeSafe();
|
|
vecY.normalizeSafe();
|
|
|
|
vecX *= decalData->sizeX;
|
|
vecY *= decalData->sizeY;
|
|
|
|
newDecal->point[0] = position + vecX + vecY;
|
|
newDecal->point[1] = position + vecX - vecY;
|
|
newDecal->point[2] = position - vecX - vecY;
|
|
newDecal->point[3] = position - vecX + vecY;
|
|
|
|
mDecalQueue.push_back(newDecal);
|
|
mQueueDirty = true;
|
|
}
|
|
|
|
void DecalManager::addDecal(const Point3F& pos,
|
|
const Point3F& rot,
|
|
Point3F normal,
|
|
DecalData* decalData)
|
|
{
|
|
if (smMaxNumDecals == 0)
|
|
return;
|
|
|
|
addDecal( pos, rot, normal, Point3F( 1, 1, 1 ), decalData );
|
|
}
|
|
|
|
void DecalManager::addDecal(const Point3F& pos,
|
|
const Point3F& rot,
|
|
Point3F normal,
|
|
const Point3F& scale,
|
|
DecalData* decalData)
|
|
{
|
|
if (smMaxNumDecals == 0)
|
|
return;
|
|
|
|
if(mDot(rot, normal) < 0.98)
|
|
{
|
|
// DMM: Rework this, should be based on time
|
|
if(mDecalQueue.size() >= smMaxNumDecals)
|
|
{
|
|
findSpace();
|
|
}
|
|
|
|
Point3F vecX, vecY;
|
|
DecalInstance* newDecal = allocateDecalInstance();
|
|
newDecal->decalData = decalData;
|
|
newDecal->allocTime = Platform::getVirtualMilliseconds();
|
|
|
|
mCross(rot, normal, &vecX);
|
|
mCross(normal, vecX, &vecY);
|
|
|
|
normal.normalize();
|
|
Point3F position = Point3F(pos.x + (normal.x * 0.008), pos.y + (normal.y * 0.008), pos.z + (normal.z * 0.008));
|
|
|
|
vecX.normalize();
|
|
vecX.convolve( scale );
|
|
vecY.normalize();
|
|
vecY.convolve( scale );
|
|
|
|
vecX *= decalData->sizeX;
|
|
vecY *= decalData->sizeY;
|
|
|
|
newDecal->point[0] = position + vecX + vecY;
|
|
newDecal->point[1] = position + vecX - vecY;
|
|
newDecal->point[2] = position - vecX - vecY;
|
|
newDecal->point[3] = position - vecX + vecY;
|
|
|
|
mDecalQueue.push_back(newDecal);
|
|
mQueueDirty = true;
|
|
}
|
|
}
|
|
|
|
bool DecalManager::prepRenderImage(SceneState* state, const U32 stateKey,
|
|
const U32 /*startZone*/, const bool /*modifyBaseState*/)
|
|
{
|
|
if (!smDecalsOn) return false;
|
|
|
|
if (isLastState(state, stateKey))
|
|
return false;
|
|
setLastState(state, stateKey);
|
|
|
|
if (mDecalQueue.size() == 0)
|
|
return false;
|
|
|
|
// This should be sufficient for most objects that don't manage zones, and
|
|
// don't need to return a specialized RenderImage...
|
|
SceneRenderImage* image = new SceneRenderImage;
|
|
image->obj = this;
|
|
image->isTranslucent = true;
|
|
image->sortType = SceneRenderImage::BeginSort;
|
|
state->insertRenderImage(image);
|
|
|
|
U32 currMs = Platform::getVirtualMilliseconds();
|
|
for (S32 i = mDecalQueue.size() - 1; i >= 0; i--)
|
|
{
|
|
U32 age = currMs - mDecalQueue[i]->allocTime;
|
|
U32 timeout = mDecalQueue[i]->decalData->lifeSpan;
|
|
if (age > timeout)
|
|
{
|
|
freeDecalInstance(mDecalQueue[i]);
|
|
mDecalQueue.erase(i);
|
|
}
|
|
else if (age > ((3 * timeout) / 4))
|
|
{
|
|
mDecalQueue[i]->fade = 1.0f - (F32(age - ((3 * timeout) / 4)) / F32(timeout / 4));
|
|
}
|
|
else
|
|
{
|
|
mDecalQueue[i]->fade = 1.0f;
|
|
}
|
|
}
|
|
|
|
if (mQueueDirty == true)
|
|
{
|
|
// Sort the decals based on the data pointers...
|
|
dQsort(mDecalQueue.address(),
|
|
mDecalQueue.size(),
|
|
sizeof(DecalInstance*),
|
|
cmpDecalInstance);
|
|
mQueueDirty = false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void DecalManager::renderObject(SceneState* state, SceneRenderImage*)
|
|
{
|
|
if (!smDecalsOn) return;
|
|
|
|
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
|
|
|
|
RectI viewport;
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
dglGetViewport(&viewport);
|
|
|
|
state->setupBaseProjection();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
|
|
renderDecal();
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
dglSetViewport(viewport);
|
|
|
|
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
|
|
}
|
|
|
|
struct DecalVert
|
|
{
|
|
Point3F vert;
|
|
Point2F texCoord;
|
|
ColorI color;
|
|
};
|
|
|
|
void DecalManager::renderDecal()
|
|
{
|
|
static Vector<DecalVert> renderVerts(__FILE__, __LINE__);
|
|
static Vector<U16> indices(__FILE__, __LINE__);
|
|
renderVerts.clear();
|
|
indices.clear();
|
|
U32 decalCount = 0;
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnable(GL_BLEND);
|
|
glEnable(GL_ALPHA_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
glAlphaFunc(GL_GREATER, 0.1f);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
sgThisIsSelfIlluminated = false;
|
|
sgLastWasSelfIlluminated = false;
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
glPolygonOffset(-1,-1);
|
|
glDepthMask(GL_FALSE);
|
|
|
|
DecalData* pLastData = NULL;
|
|
for (S32 x = 0; x < mDecalQueue.size(); x++)
|
|
{
|
|
if (mDecalQueue[x]->decalData != pLastData)
|
|
{
|
|
glVertexPointer(3, GL_FLOAT, sizeof(DecalVert), &(renderVerts[0].vert));
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(DecalVert), &(renderVerts[0].color));
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(DecalVert), &(renderVerts[0].texCoord));
|
|
|
|
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, indices.address());
|
|
|
|
renderVerts.clear();
|
|
indices.clear();
|
|
decalCount = 0;
|
|
|
|
glBindTexture(GL_TEXTURE_2D, mDecalQueue[x]->decalData->textureHandle.getGLName());
|
|
pLastData = mDecalQueue[x]->decalData;
|
|
}
|
|
|
|
sgThisIsSelfIlluminated = mDecalQueue[x]->decalData->selfIlluminated;
|
|
if(sgThisIsSelfIlluminated != sgLastWasSelfIlluminated)
|
|
{
|
|
glVertexPointer(3, GL_FLOAT, sizeof(DecalVert), &(renderVerts[0].vert));
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(DecalVert), &(renderVerts[0].color));
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(DecalVert), &(renderVerts[0].texCoord));
|
|
|
|
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, indices.address());
|
|
|
|
renderVerts.clear();
|
|
indices.clear();
|
|
decalCount = 0;
|
|
|
|
if(sgThisIsSelfIlluminated)
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
else
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
sgLastWasSelfIlluminated = sgThisIsSelfIlluminated;
|
|
}
|
|
|
|
renderVerts.increment(4);
|
|
indices.increment(6);
|
|
DecalVert *verts = &(renderVerts[decalCount * 4]);
|
|
U16 *ind = &(indices[decalCount * 6]);
|
|
|
|
const U8 fade = mDecalQueue[x]->fade * 255;
|
|
|
|
verts[0].vert = mDecalQueue[x]->point[3];
|
|
verts[0].texCoord.set(0, 0);
|
|
verts[0].color.set(255, 255, 255, fade);
|
|
ind[0] = decalCount * 4;
|
|
ind[5] = decalCount * 4;
|
|
|
|
verts[1].vert = mDecalQueue[x]->point[2];
|
|
verts[1].texCoord.set(0, 1);
|
|
verts[1].color.set(255, 255, 255, fade);
|
|
ind[1] = decalCount * 4 + 1;
|
|
|
|
verts[2].vert = mDecalQueue[x]->point[1];
|
|
verts[2].texCoord.set(1, 1);
|
|
verts[2].color.set(255, 255, 255, fade);
|
|
ind[2] = decalCount * 4 + 2;
|
|
ind[3] = decalCount * 4 + 2;
|
|
|
|
verts[3].vert = mDecalQueue[x]->point[0];
|
|
verts[3].texCoord.set(1, 0);
|
|
verts[3].color.set(255, 255, 255, fade);
|
|
ind[4] = decalCount * 4 + 3;
|
|
|
|
decalCount++;
|
|
}
|
|
|
|
if(decalCount)
|
|
{
|
|
glVertexPointer(3, GL_FLOAT, sizeof(DecalVert), &(renderVerts[0].vert));
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(DecalVert), &(renderVerts[0].color));
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(DecalVert), &(renderVerts[0].texCoord));
|
|
|
|
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, indices.address());
|
|
}
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
glDepthMask(GL_TRUE);
|
|
|
|
glDepthMask(GL_TRUE);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_ALPHA_TEST);
|
|
}
|
|
|
|
void DecalManager::findSpace()
|
|
{
|
|
S32 besttime = S32_MAX;
|
|
U32 bestindex = 0;
|
|
DecalInstance *bestdecal = NULL;
|
|
|
|
U32 time = Platform::getVirtualMilliseconds();
|
|
|
|
for(U32 i=0; i<mDecalQueue.size(); i++)
|
|
{
|
|
DecalInstance *inst = mDecalQueue[i];
|
|
U32 age = time - inst->allocTime;
|
|
U32 timeleft = inst->decalData->lifeSpan - age;
|
|
if(besttime > timeleft)
|
|
{
|
|
besttime = timeleft;
|
|
bestindex = i;
|
|
bestdecal = inst;
|
|
}
|
|
}
|
|
|
|
AssertFatal((bestdecal), "No good decals?");
|
|
|
|
mDecalQueue.erase_fast(bestindex);
|
|
freeDecalInstance(bestdecal);
|
|
}
|
|
|
|
void DecalManager::addDecal(const Point3F& pos, const Point3F& rot, Point3F normal,
|
|
const Point3F& scale, DecalData *decaldata, U32 ownerid)
|
|
{
|
|
if(smMaxNumDecals == 0)
|
|
return;
|
|
|
|
if(mDot(rot, normal) < 0.98)
|
|
{
|
|
if(mDecalQueue.size() >= smMaxNumDecals)
|
|
findSpace();
|
|
|
|
Point3F vecX, vecY;
|
|
DecalInstance* newDecal = allocateDecalInstance();
|
|
newDecal->decalData = decaldata;
|
|
newDecal->allocTime = Platform::getVirtualMilliseconds();
|
|
newDecal->ownerId = ownerid;
|
|
|
|
mCross(rot, normal, &vecX);
|
|
mCross(normal, vecX, &vecY);
|
|
|
|
normal.normalize();
|
|
Point3F position = Point3F(pos.x + (normal.x * 0.008), pos.y + (normal.y * 0.008), pos.z + (normal.z * 0.008));
|
|
|
|
vecX.normalize();
|
|
vecX.convolve( scale );
|
|
vecY.normalize();
|
|
vecY.convolve( scale );
|
|
|
|
vecX *= decaldata->sizeX;
|
|
vecY *= decaldata->sizeY;
|
|
|
|
newDecal->point[0] = position + vecX + vecY;
|
|
newDecal->point[1] = position + vecX - vecY;
|
|
newDecal->point[2] = position - vecX - vecY;
|
|
newDecal->point[3] = position - vecX + vecY;
|
|
|
|
mDecalQueue.push_back(newDecal);
|
|
mQueueDirty = true;
|
|
}
|
|
}
|
|
|
|
void DecalManager::ageDecal(U32 ownerid)
|
|
{
|
|
for(U32 i=0; i<mDecalQueue.size(); i++)
|
|
{
|
|
DecalInstance *inst = mDecalQueue[i];
|
|
if(inst->ownerId == ownerid)
|
|
{
|
|
freeDecalInstance(inst);
|
|
mDecalQueue.erase(U32(i));
|
|
}
|
|
}
|
|
}
|
|
|