2480 lines
80 KiB
C++
Executable File
2480 lines
80 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "map2dif/createLightmaps.h"
|
|
#include "dgl/gBitmap.h"
|
|
#include "math/mMath.h"
|
|
#include "core/bitMatrix.h"
|
|
#include "interior/interior.h"
|
|
|
|
#ifdef DUMP_LIGHTMAPS
|
|
#include "core/fileStream.h"
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------------
|
|
// globals
|
|
//------------------------------------------------------------------------------
|
|
namespace {
|
|
static char * newStrDup(const char * src)
|
|
{
|
|
char * buffer = new char [dStrlen(src) + 1];
|
|
dStrcpy(buffer, src);
|
|
return(buffer);
|
|
}
|
|
|
|
static const F32 AnimationTimes[] = { 2.f, 1.f, 0.5f, 0.25f, 0.125f };
|
|
|
|
Lighting * gWorkingLighting;
|
|
}
|
|
|
|
extern bool gBuildAsLowDetail;
|
|
|
|
|
|
void sgIlluminateSurface(EditGeometry::Surface &surface)
|
|
{
|
|
if(!surface.pLMap)
|
|
return;
|
|
|
|
for(U32 y=0; y<surface.pLMap->getHeight(); y++)
|
|
{
|
|
for(U32 x=0; x<surface.pLMap->getWidth(); x++)
|
|
{
|
|
U8 *lexel = surface.pLMap->getAddress(x,y);
|
|
|
|
for(U32 c=0; c<3; c++)
|
|
{
|
|
U32 l = lexel[c];
|
|
l += surface.sgSelfIllumination[c];
|
|
lexel[c] = mClamp(l, U32(0), U32(255));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class Lighting
|
|
//------------------------------------------------------------------------------
|
|
|
|
Lighting::Lighting() :
|
|
mNumAmbiguousPlanes(0),
|
|
mNodeRepository(0),
|
|
mWindingStore(0),
|
|
mSurfaceEmitterInfos(0),
|
|
mEmitterSurfaceIndices(0)
|
|
{
|
|
}
|
|
|
|
Lighting::~Lighting()
|
|
{
|
|
for(U32 i = 0; i < mLights.size(); i++)
|
|
delete mLights[i];
|
|
for(U32 i = 0; i < mEmitters.size(); i++)
|
|
delete mEmitters[i];
|
|
|
|
// destroy all the vectors
|
|
for(U32 i = 0; i < mSurfaces.size(); i++)
|
|
for(U32 j = 0; j < mSurfaces[i]->mNumEmitters; j++)
|
|
mSurfaces[i]->mEmitters[j]->mShadowed.~UniqueVector();
|
|
|
|
//
|
|
mEmitterInfoChunker.clear();
|
|
mNodeChunker.clear();
|
|
mSurfaceChunker.clear();
|
|
|
|
//
|
|
delete [] mSurfaceEmitterInfos;
|
|
delete [] mEmitterSurfaceIndices;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
Lighting::SVNode * Lighting::getShadowVolume(U32 index)
|
|
{
|
|
return(mShadowVolumes[index]);
|
|
}
|
|
|
|
Lighting::Surface * Lighting::getSurface(U32 index)
|
|
{
|
|
return(mSurfaces[index]);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
/*F64 Lighting::getLumelScale()
|
|
{
|
|
return(gWorkingGeometry->mWorldEntity->mLumelScale);
|
|
}*/
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::grabLights(bool alarmMode)
|
|
{
|
|
// grab all the light emitters and targets first..
|
|
for(U32 i = 0; i < gWorkingGeometry->mEntities.size(); i++)
|
|
{
|
|
BaseLightEmitterEntity * emitter = dynamic_cast<BaseLightEmitterEntity*>(gWorkingGeometry->mEntities[i]);
|
|
if(emitter)
|
|
mBaseLightEmitters.push_back(emitter);
|
|
|
|
TargetEntity * target = dynamic_cast<TargetEntity*>(gWorkingGeometry->mEntities[i]);
|
|
if(target)
|
|
mTargets.push_back(target);
|
|
}
|
|
|
|
// process all the lights
|
|
for(U32 i = 0; i < gWorkingGeometry->mEntities.size(); i++)
|
|
{
|
|
BaseLightEntity * entity = dynamic_cast<BaseLightEntity*>(gWorkingGeometry->mEntities[i]);
|
|
if(entity)
|
|
{
|
|
if((entity->mAlarmStatus == BaseLightEntity::NormalOnly && alarmMode) ||
|
|
(entity->mAlarmStatus == BaseLightEntity::AlarmOnly && !alarmMode))
|
|
continue;
|
|
|
|
Light * light = new Light;
|
|
if(!light->build(entity))
|
|
{
|
|
delete light;
|
|
continue;
|
|
}
|
|
|
|
light->alarm = alarmMode;
|
|
mLights.push_back(light);
|
|
}
|
|
}
|
|
|
|
// Merge the animated lights that allow it...
|
|
//
|
|
for (U32 i = 0; i < mLights.size(); i++)
|
|
{
|
|
Light* lightCompare = mLights[i];
|
|
if (!lightCompare->mAnimated)
|
|
continue;
|
|
|
|
for (U32 j = i + 1; j < mLights.size();)
|
|
{
|
|
Light* lightPossible = mLights[j];
|
|
if (!lightPossible->mAnimated ||
|
|
(lightCompare->mNumStates != lightPossible->mNumStates ||
|
|
lightCompare->mAnimType != lightPossible->mAnimType ||
|
|
lightCompare->alarm != lightPossible->alarm))
|
|
{
|
|
j++;
|
|
continue;
|
|
}
|
|
|
|
|
|
bool valid = true;
|
|
for (U32 k = 0; k < lightCompare->mNumStates && valid == true; k++)
|
|
{
|
|
const Light::LightState& state1 = mLightStates[lightCompare->mStateIndex + k];
|
|
const Light::LightState& state2 = mLightStates[lightPossible->mStateIndex + k];
|
|
|
|
if (state1.mDuration != state2.mDuration)
|
|
valid = false;
|
|
|
|
// All the emitters in an animated light share the same color, so
|
|
// we can just check the first...
|
|
if (mEmitters[state1.mEmitterIndex]->mColor != mEmitters[state2.mEmitterIndex]->mColor)
|
|
valid = false;
|
|
}
|
|
|
|
if (valid)
|
|
{
|
|
// Since the states are the same, we just want to loop through them, and reassign their
|
|
// emitters. We'll just put the new emitter range at the end of the mEmitters array,
|
|
// null the current pointers.
|
|
for (U32 k = 0; k < lightCompare->mNumStates; k++)
|
|
{
|
|
Light::LightState* state1 = &mLightStates[lightCompare->mStateIndex + k];
|
|
const Light::LightState* state2 = &mLightStates[lightPossible->mStateIndex + k];
|
|
|
|
U32 newEmitterIndex = mEmitters.size();
|
|
Emitter* pEmitter;
|
|
for (U32 l = 0; l < state1->mNumEmitters; l++)
|
|
{
|
|
pEmitter = mEmitters[state1->mEmitterIndex + l];
|
|
pEmitter->mIndex = mEmitters.size();
|
|
mEmitters.push_back(pEmitter);
|
|
mEmitters[state1->mEmitterIndex + l] = NULL;
|
|
}
|
|
for (U32 l = 0; l < state2->mNumEmitters; l++)
|
|
{
|
|
pEmitter = mEmitters[state2->mEmitterIndex + l];
|
|
pEmitter->mIndex = mEmitters.size();
|
|
mEmitters.push_back(pEmitter);
|
|
mEmitters[state2->mEmitterIndex + l] = NULL;
|
|
}
|
|
state1->mEmitterIndex = newEmitterIndex;
|
|
state1->mNumEmitters = mEmitters.size() - newEmitterIndex;
|
|
}
|
|
|
|
delete mLights[j];
|
|
mLights.erase(j);
|
|
}
|
|
else
|
|
{
|
|
// Step to the next light...
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::convertToFan(MiniWinding & winding, U32 fanMask)
|
|
{
|
|
U32 tmpIndices[LightingMaxWindingPoints];
|
|
U32 fanIndices[LightingMaxWindingPoints];
|
|
|
|
tmpIndices[0] = 0;
|
|
|
|
U32 idx = 1;
|
|
for(U32 i = 1; i < winding.mNumIndices; i += 2)
|
|
tmpIndices[idx++] = i;
|
|
for(U32 i = ((winding.mNumIndices - 1) & (~0x1)); i > 0; i -= 2)
|
|
tmpIndices[idx++] = i;
|
|
|
|
idx = 0;
|
|
for(U32 i = 0; i < winding.mNumIndices; i++)
|
|
if(fanMask & (1 << i))
|
|
fanIndices[idx++] = winding.mIndices[tmpIndices[i]];
|
|
|
|
// set the data
|
|
winding.mNumIndices = idx;
|
|
for(U32 i = 0; i < winding.mNumIndices; i++)
|
|
winding.mIndices[i] = fanIndices[i];
|
|
}
|
|
|
|
void Lighting::copyWinding(MiniWinding & dest, Winding & src)
|
|
{
|
|
dest.mNumIndices = src.numIndices;
|
|
dMemcpy(dest.mIndices, src.indices, src.numIndices * sizeof(U32));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::grabSurfaces()
|
|
{
|
|
for(U32 i = 0; i < gWorkingGeometry->mSurfaces.size(); i++)
|
|
{
|
|
Surface * surface = createSurface();
|
|
surface->mSurfaceIndex = i;
|
|
|
|
surface->sgLightingScale = gWorkingGeometry->mSurfaces[i].sgLightingScale;
|
|
|
|
copyWinding(surface->mWinding, gWorkingGeometry->mSurfaces[i].winding);
|
|
surface->mPlaneIndex = gWorkingGeometry->mSurfaces[i].planeIndex;
|
|
|
|
// convert the surface...
|
|
convertToFan(surface->mWinding, gWorkingGeometry->mSurfaces[i].fanMask);
|
|
mSurfaces.push_back(surface);
|
|
}
|
|
}
|
|
|
|
Lighting::EmitterInfo * Lighting::createEmitterInfo()
|
|
{
|
|
// clear it out (will not call vector ctor's)
|
|
EmitterInfo * info = mEmitterInfoChunker.alloc();
|
|
dMemset(info, 0, sizeof(EmitterInfo));
|
|
return(info);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::processSurfaces()
|
|
{
|
|
Vector<U32> list;
|
|
list.reserve(mSurfaces.size() * 6);
|
|
|
|
// create a run list for the surfaces..
|
|
for(U32 i = 0; i < mSurfaces.size(); i++)
|
|
{
|
|
mSurfaces[i]->mNumEmitters = 0;
|
|
|
|
//
|
|
for(U32 j = 0; j < mEmitters.size(); j++)
|
|
{
|
|
if(mEmitters[j] != NULL && mEmitters[j]->isSurfaceLit(mSurfaces[i]))
|
|
{
|
|
mSurfaces[i]->mNumEmitters++;
|
|
list.push_back(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!list.size())
|
|
return;
|
|
|
|
mSurfaceEmitterInfos = new EmitterInfo* [list.size()];
|
|
|
|
U32 curOffset = 0;
|
|
|
|
// now walk this and set the emitterinfo pntr/size, also create the infos
|
|
for(U32 i = 0; i < mSurfaces.size(); i++)
|
|
{
|
|
mSurfaces[i]->mEmitters = &mSurfaceEmitterInfos[curOffset];
|
|
|
|
const U32 & numEmitters = mSurfaces[i]->mNumEmitters;
|
|
|
|
if(numEmitters)
|
|
{
|
|
for(U32 j = 0; j < numEmitters; j++)
|
|
{
|
|
EmitterInfo * eInfo = createEmitterInfo();
|
|
eInfo->mEmitter = list[curOffset++];
|
|
mSurfaces[i]->mEmitters[j] = eInfo;
|
|
mEmitters[eInfo->mEmitter]->mNumSurfaces++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// now do the same for the emitters - equally sized lists..
|
|
mEmitterSurfaceIndices = new U32 [list.size()];
|
|
|
|
curOffset = 0;
|
|
for(U32 i = 0; i < mEmitters.size(); i++)
|
|
{
|
|
if (mEmitters[i] == NULL)
|
|
continue;
|
|
|
|
mEmitters[i]->mSurfaces = &mEmitterSurfaceIndices[curOffset];
|
|
|
|
U32 count = mEmitters[i]->mNumSurfaces;
|
|
curOffset += count;
|
|
U32 curSurface = 0;
|
|
|
|
// go until filled
|
|
for(U32 j = 0; curSurface != count; j++)
|
|
for(U32 k = 0; k < mSurfaces[j]->mNumEmitters; k++)
|
|
if(mSurfaces[j]->mEmitters[k]->mEmitter == i)
|
|
mEmitters[i]->mSurfaces[curSurface++] = j;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
U32 Lighting::constructPlane(const Point3D& Point1, const Point3D& Point2, const Point3D& Point3) const
|
|
{
|
|
// |yi zi 1| |xi zi 1| |xi yi 1| |xi yi zi|
|
|
// |yj zj 1| x + |xj zj 1| y + |xj yj 1| z = |xj yj zj|
|
|
// |yk zk 1| |xk zk 1| |xk yk 1| |xk yk zk|
|
|
//
|
|
Point3D normal;
|
|
F64 dist;
|
|
|
|
normal.x = Point1.y * Point2.z - Point1.y * Point3.z +
|
|
Point3.y * Point1.z - Point2.y * Point1.z +
|
|
Point2.y * Point3.z - Point3.y * Point2.z;
|
|
normal.y = Point1.x * Point2.z - Point1.x * Point3.z +
|
|
Point3.x * Point1.z - Point2.x * Point1.z +
|
|
Point2.x * Point3.z - Point3.x * Point2.z;
|
|
normal.z = Point1.x * Point2.y - Point1.x * Point3.y +
|
|
Point3.x * Point1.y - Point2.x * Point1.y +
|
|
Point2.x * Point3.y - Point3.x * Point2.y;
|
|
dist = Point1.x * Point2.y * Point3.z - Point1.x * Point2.z * Point3.y +
|
|
Point1.y * Point2.z * Point3.x - Point1.y * Point2.x * Point3.z +
|
|
Point1.z * Point2.x * Point3.y - Point1.z * Point2.y * Point3.x;
|
|
|
|
normal.x = -normal.x;
|
|
normal.z = -normal.z;
|
|
|
|
//
|
|
return(gWorkingGeometry->insertPlaneEQ(normal, dist));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::createShadowVolumes()
|
|
{
|
|
for(U32 i = 0; i < mSurfaces.size(); i++)
|
|
{
|
|
//
|
|
if(!mSurfaces[i]->mNumEmitters)
|
|
continue;
|
|
|
|
// create this surface's poly node
|
|
mSurfaces[i]->mPolyNode = createNode(SVNode::PolyPlane);
|
|
*(mSurfaces[i]->mPolyNode->mWinding) = mSurfaces[i]->mWinding;
|
|
mSurfaces[i]->mPolyNode->mPlaneIndex = mSurfaces[i]->mPlaneIndex;
|
|
|
|
// create a shadow volume for each of the emitters for this surface
|
|
for(U32 j = 0; j < mSurfaces[i]->mNumEmitters; j++)
|
|
{
|
|
const MiniWinding & winding = mSurfaces[i]->mWinding;
|
|
const Emitter * emitter = mEmitters[mSurfaces[i]->mEmitters[j]->mEmitter];
|
|
|
|
SVNode * root = 0;
|
|
SVNode ** current = &root;
|
|
|
|
for(U32 k = 0; k < winding.mNumIndices; k++)
|
|
{
|
|
SVNode * sNode = createNode(SVNode::ShadowPlane);
|
|
|
|
// create the plane for this node
|
|
sNode->mPlaneIndex = constructPlane(
|
|
gWorkingGeometry->getPoint(winding.mIndices[(k+1) % winding.mNumIndices]),
|
|
gWorkingGeometry->getPoint(winding.mIndices[k]),
|
|
gWorkingGeometry->getPoint(emitter->mPoint));
|
|
|
|
// target the original poly
|
|
sNode->mTarget = mSurfaces[i]->mPolyNode;
|
|
|
|
// add to the front
|
|
*current = sNode;
|
|
current = &(*current)->mFront;
|
|
}
|
|
|
|
mSurfaces[i]->mEmitters[j]->mShadowVolume = mShadowVolumes.size();
|
|
mShadowVolumes.push_back(root);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::processEmitterBSPs()
|
|
{
|
|
for(U32 i = 0; i < mEmitters.size(); i++)
|
|
if (mEmitters[i] != NULL)
|
|
mEmitters[i]->processBSP();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// simple point store for use with the lexel nodes
|
|
|
|
void Lighting::flushLexelPoints()
|
|
{
|
|
mLexelPoints.setSize(0);
|
|
mLexelPoints.reserve(LexelPointStoreSize);
|
|
}
|
|
|
|
const Point3D & Lighting::getLexelPoint(U32 index)
|
|
{
|
|
AssertFatal(index < mLexelPoints.size(), "Lighting::getLexelPoint: index out of range");
|
|
return(mLexelPoints[index]);
|
|
}
|
|
|
|
U32 Lighting::insertLexelPoint(const Point3D & pnt)
|
|
{
|
|
if(!mLexelPoints.size())
|
|
mLexelPoints.reserve(LexelPointStoreSize);
|
|
|
|
if(mLexelPoints.size() == (mLexelPoints.capacity() - 1))
|
|
mLexelPoints.reserve(mLexelPoints.size() + LexelPointStoreSize);
|
|
|
|
mLexelPoints.push_back(pnt);
|
|
return(mLexelPoints.size() - 1);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::lightSurfaces()
|
|
{
|
|
for(U32 i = 0; i < mSurfaces.size(); i++)
|
|
{
|
|
Surface * surface = mSurfaces[i];
|
|
|
|
// must have emitters on this surface
|
|
if(!surface->mNumEmitters)
|
|
continue;
|
|
|
|
const PlaneEQ & plane = gWorkingGeometry->getPlaneEQ(surface->mPlaneIndex);
|
|
const EditGeometry::Surface * editSurface = &gWorkingGeometry->mSurfaces[surface->mSurfaceIndex];
|
|
|
|
// must have a lightmap
|
|
if(!editSurface->pLMap)
|
|
continue;
|
|
|
|
const F32* const & lGenX = editSurface->lmapTexGenX;
|
|
const F32* const & lGenY = editSurface->lmapTexGenY;
|
|
|
|
//
|
|
AssertFatal((lGenX[0] * lGenX[1] == 0.f) &&
|
|
(lGenX[0] * lGenX[2] == 0.f) &&
|
|
(lGenX[1] * lGenX[2] == 0.f), "Bad texgen!");
|
|
AssertFatal((lGenY[0] * lGenY[1] == 0.f) &&
|
|
(lGenY[0] * lGenY[2] == 0.f) &&
|
|
(lGenY[1] * lGenY[2] == 0.f), "Bad texgen!");
|
|
|
|
|
|
// get the axis index for the texgens (could be swapped)
|
|
S32 si;
|
|
S32 ti;
|
|
S32 axis = -1;
|
|
|
|
//
|
|
if(lGenX[0] == 0.f && lGenY[0] == 0.f) // YZ
|
|
{
|
|
axis = 0;
|
|
if(lGenX[1] == 0.f) { // swapped?
|
|
si = 2;
|
|
ti = 1;
|
|
} else {
|
|
si = 1;
|
|
ti = 2;
|
|
}
|
|
}
|
|
else if(lGenX[1] == 0.f && lGenY[1] == 0.f) // XZ
|
|
{
|
|
axis = 1;
|
|
if(lGenX[0] == 0.f) { // swapped?
|
|
si = 2;
|
|
ti = 0;
|
|
} else {
|
|
si = 0;
|
|
ti = 2;
|
|
}
|
|
}
|
|
else if(lGenX[2] == 0.f && lGenY[2] == 0.f) // XY
|
|
{
|
|
axis = 2;
|
|
if(lGenX[0] == 0.f) { // swapped?
|
|
si = 1;
|
|
ti = 0;
|
|
} else {
|
|
si = 0;
|
|
ti = 1;
|
|
}
|
|
}
|
|
AssertFatal(!(axis == -1), "Lighting::lightSurfaces: bad TexGen!");
|
|
|
|
// get the start point for this lightmap (project min to surface)
|
|
Point3D start;
|
|
F64 * pStart = ((F64*)start);
|
|
const F64 * pNormal = ((const F64*)plane.normal);
|
|
|
|
// TexGens come in scaled by (1/lightmapscale)
|
|
pStart[si] = -lGenX[3] * surface->sgLightingScale;
|
|
pStart[ti] = -lGenY[3] * surface->sgLightingScale;
|
|
|
|
pStart[axis] = ((pNormal[si] * pStart[si]) +
|
|
(pNormal[ti] * pStart[ti]) + plane.dist) / -pNormal[axis];
|
|
|
|
// get the s/t vecs oriented on the surface
|
|
Point3D sVec;
|
|
Point3D tVec;
|
|
|
|
F64 * pSVec = ((F64*)sVec);
|
|
F64 * pTVec = ((F64*)tVec);
|
|
|
|
Point3D planeNormal;
|
|
F64 angle;
|
|
|
|
// s
|
|
pSVec[si] = 1.f;
|
|
pSVec[ti] = 0.f;
|
|
|
|
planeNormal = plane.normal;
|
|
|
|
((F64*)planeNormal)[ti] = 0.f;
|
|
planeNormal.normalize();
|
|
|
|
angle = mAcos(mClampF(((F64*)planeNormal)[axis], -1.f, 1.f));
|
|
pSVec[axis] = (((F64*)planeNormal)[si] < 0.f) ? mTan(angle) : -mTan(angle);
|
|
|
|
// t
|
|
pTVec[ti] = 1.f;
|
|
pTVec[si] = 0.f;
|
|
|
|
planeNormal = plane.normal;
|
|
|
|
((F64*)planeNormal)[si] = 0.f;
|
|
planeNormal.normalize();
|
|
|
|
angle = mAcos(mClampF(((F64*)planeNormal)[axis], -1.f, 1.f));
|
|
pTVec[axis] = (((F64*)planeNormal)[ti] < 0.f) ? mTan(angle) : -mTan(angle);
|
|
|
|
// scale them
|
|
sVec *= surface->sgLightingScale;
|
|
tVec *= surface->sgLightingScale;
|
|
|
|
// create all the animated light bitmaps
|
|
for(U32 j = 0; j < surface->mNumEmitters; j++)
|
|
{
|
|
EmitterInfo * emitterInfo = surface->mEmitters[j];
|
|
Emitter * emitter = mEmitters[emitterInfo->mEmitter];
|
|
if(!emitter->mAnimated)
|
|
continue;
|
|
|
|
emitterInfo->mLightMap = new GBitmap(editSurface->lMapDimX, editSurface->lMapDimY, false, GBitmap::Intensity);
|
|
}
|
|
|
|
Point3D & curPos = start;
|
|
Point3D sRun = sVec * editSurface->lMapDimX;
|
|
|
|
// get the lexel area
|
|
Point3D cross;
|
|
mCross(sVec, tVec, &cross);
|
|
F64 maxLexelArea = cross.len();
|
|
|
|
// BitMatrix outsideLexels(editSurface->lMapDimX, editSurface->lMapDimY);
|
|
// outsideLexels.clearAllBits();
|
|
|
|
// get the world coordinate for each lexel
|
|
for(U32 y = 0; y < editSurface->lMapDimY; y++)
|
|
{
|
|
for(U32 x = 0; x < editSurface->lMapDimX; x++)
|
|
{
|
|
SVNode * lNode = createNode(SVNode::LexelPlane);
|
|
lNode->mPlaneIndex = surface->mPlaneIndex;
|
|
lNode->mWinding->mNumIndices = 4;
|
|
|
|
// set the poly indices
|
|
lNode->mWinding->mIndices[0] = insertLexelPoint(curPos);
|
|
lNode->mWinding->mIndices[1] = insertLexelPoint(curPos + sVec);
|
|
lNode->mWinding->mIndices[2] = insertLexelPoint(curPos + sVec + tVec);
|
|
lNode->mWinding->mIndices[3] = insertLexelPoint(curPos + tVec);
|
|
|
|
ColorF colSum(0,0,0);
|
|
|
|
// walk the emitters for each that is not shadowed, light it
|
|
for(U32 j = 0; j < surface->mNumEmitters; j++)
|
|
{
|
|
EmitterInfo * emitterInfo = surface->mEmitters[j];
|
|
Emitter * emitter = mEmitters[emitterInfo->mEmitter];
|
|
|
|
// get the light value
|
|
F64 intensity = emitter->calcIntensity(lNode);
|
|
F64 shadowScale = 1.f;
|
|
|
|
if(emitterInfo->mShadowed.size())
|
|
{
|
|
// insert a copy of the lexel node, unless last emitter
|
|
SVNode * nodeStore = 0;
|
|
if(j == (surface->mNumEmitters-1))
|
|
{
|
|
// use the lexel node
|
|
nodeStore = lNode;
|
|
lNode = 0;
|
|
}
|
|
else
|
|
{
|
|
// copy
|
|
nodeStore = createNode(SVNode::LexelPlane);
|
|
*nodeStore->mWinding = *lNode->mWinding;
|
|
nodeStore->mPlaneIndex = lNode->mPlaneIndex;
|
|
}
|
|
|
|
// // clip the node to own shadow volume
|
|
// SVNode * tmp = 0;
|
|
// nodeStore->clipToInverseVolume(&tmp, getShadowVolume(emitterInfo->mShadowVolume));
|
|
// nodeStore = tmp;
|
|
//
|
|
// // mark the lexel if it is not within own poly
|
|
// if(!nodeStore)
|
|
// {
|
|
// outsideLexels.setBit(x, y);
|
|
// continue;
|
|
// }
|
|
|
|
F64 lexelArea = nodeStore->getWindingSurfaceArea();
|
|
|
|
// clip the lexel node(s) to the shadow volumes
|
|
for(U32 k = 0; nodeStore && (k < emitterInfo->mShadowed.size()); k++)
|
|
{
|
|
SVNode * nodeList = 0;
|
|
SVNode * traverse = nodeStore;
|
|
|
|
while(traverse)
|
|
{
|
|
SVNode * next = traverse->mFront;
|
|
SVNode * currentStore = 0;
|
|
|
|
traverse->clipToVolume(¤tStore, getShadowVolume(emitterInfo->mShadowed[k]));
|
|
|
|
if(currentStore)
|
|
currentStore->move(&nodeList);
|
|
|
|
traverse = next;
|
|
}
|
|
nodeStore = nodeList;
|
|
}
|
|
|
|
// sum the lexel area of the remaining poly fragments
|
|
F64 area = nodeStore ? nodeStore->getWindingSurfaceArea() : 0.f;
|
|
|
|
// clamp it
|
|
if(area > lexelArea)
|
|
area = lexelArea;
|
|
|
|
// set the scale
|
|
shadowScale = area / lexelArea;
|
|
recycleNode(nodeStore);
|
|
}
|
|
|
|
// fill in animated light here...
|
|
if(emitterInfo->mLightMap)
|
|
*(emitterInfo->mLightMap->getAddress(x,y)) = U8(intensity * shadowScale * 255.f);
|
|
else if(shadowScale != 0.f)
|
|
{
|
|
ColorF col = emitter->mColor;
|
|
col *= intensity;
|
|
col *= shadowScale;
|
|
colSum += col;
|
|
colSum.clamp();
|
|
}
|
|
}
|
|
|
|
//
|
|
recycleNode(lNode);
|
|
|
|
// set the col
|
|
U8 * pDest = editSurface->pLMap->getAddress(x,y);
|
|
|
|
// add in the ambient...
|
|
if(!(editSurface->flags & Interior::SurfaceOutsideVisible))
|
|
colSum += mAmbientColor;
|
|
|
|
colSum.clamp();
|
|
|
|
pDest[0] = U8(colSum.red * 255.f);
|
|
pDest[1] = U8(colSum.green * 255.f);
|
|
pDest[2] = U8(colSum.blue * 255.f);
|
|
|
|
//
|
|
flushLexelPoints();
|
|
curPos += sVec;
|
|
}
|
|
|
|
//
|
|
curPos -= sRun;
|
|
curPos += tVec;
|
|
}
|
|
|
|
// // filter the outside lexels
|
|
// static F32 filterTable[3][3] =
|
|
// {{1, 2, 1},
|
|
// {2, 0, 2},
|
|
// {1, 2, 1}};
|
|
//
|
|
// for(S32 y = 0; y < editSurface->lMapDimY; y++)
|
|
// for(S32 x = 0; x < editSurface->lMapDimX; x++)
|
|
// {
|
|
// if(!outsideLexels.isSet(x, y))
|
|
// continue;
|
|
//
|
|
// F32 rSum = 0.f;
|
|
// F32 gSum = 0.f;
|
|
// F32 bSum = 0.f;
|
|
//
|
|
// F32 totalWeight = 0.f;
|
|
//
|
|
// for(S32 i = -1; i <= 1; i++)
|
|
// for(S32 j = -1; j <= 1; j++)
|
|
// {
|
|
// Point2I pos(x+j, y+i);
|
|
//
|
|
// if(pos.x < 0 || pos.x >= editSurface->lMapDimX ||
|
|
// pos.y < 0 || pos.y >= editSurface->lMapDimY)
|
|
// continue;
|
|
//
|
|
// //
|
|
// if(!outsideLexels.isSet(pos.x, pos.y))
|
|
// {
|
|
// U8 * pCol = editSurface->pLMap->getAddress(pos.x, pos.y);
|
|
// F32 weight = filterTable[i+1][j+1];
|
|
//
|
|
// rSum += weight * F32(pCol[0]);
|
|
// gSum += weight * F32(pCol[1]);
|
|
// bSum += weight * F32(pCol[2]);
|
|
//
|
|
// totalWeight += weight;
|
|
// }
|
|
// }
|
|
//
|
|
// //
|
|
// if(totalWeight != 0.f)
|
|
// {
|
|
// U8 * pCol = editSurface->pLMap->getAddress(x, y);
|
|
// pCol[0] = U8(rSum / totalWeight);
|
|
// pCol[1] = U8(gSum / totalWeight);
|
|
// pCol[2] = U8(bSum / totalWeight);
|
|
// }
|
|
// }
|
|
|
|
#ifdef DUMP_LIGHTMAPS
|
|
//
|
|
static U32 majCnt = 0;
|
|
U32 minCnt = 0;
|
|
for(U32 j = 0; j < surface->mNumEmitters; j++)
|
|
{
|
|
EmitterInfo * emitterInfo = surface->mEmitters[j];
|
|
|
|
if(emitterInfo->mLightMap)
|
|
{
|
|
// convert the intensity bitmap to RGB
|
|
GBitmap bitmap(emitterInfo->mLightMap->getWidth(), emitterInfo->mLightMap->getHeight());
|
|
for(U32 y = 0; y < emitterInfo->mLightMap->getHeight(); y++)
|
|
for(U32 x = 0; x < emitterInfo->mLightMap->getWidth(); x++)
|
|
{
|
|
U8 * pDest = bitmap.getAddress(x, y);
|
|
U8 src = *emitterInfo->mLightMap->getAddress(x, y);
|
|
|
|
*pDest++ = src;
|
|
*pDest++ = src;
|
|
*pDest++ = src;
|
|
}
|
|
|
|
//
|
|
FileStream output;
|
|
output.open(avar("lightmap_%d_%d.png", majCnt, minCnt++), FileStream::Write);
|
|
bitmap.writePNG(output);
|
|
}
|
|
}
|
|
majCnt++;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class Lighting::SVNode
|
|
//------------------------------------------------------------------------------
|
|
|
|
Lighting::MiniWinding * Lighting::createWinding()
|
|
{
|
|
if(mWindingStore)
|
|
{
|
|
MiniWinding * winding = mWindingStore;
|
|
mWindingStore = mWindingStore->mNext;
|
|
|
|
//
|
|
winding->mNumIndices = 0;
|
|
return(winding);
|
|
}
|
|
|
|
// create it
|
|
MiniWinding * winding = mWindingChunker.alloc();
|
|
winding->mNumIndices = 0;
|
|
return(winding);
|
|
}
|
|
|
|
void Lighting::recycleWinding(MiniWinding * winding)
|
|
{
|
|
if(!winding)
|
|
return;
|
|
|
|
// add to head
|
|
winding->mNext = mWindingStore;
|
|
mWindingStore = winding;
|
|
}
|
|
|
|
void Lighting::recycleNode(SVNode * node)
|
|
{
|
|
if(!node)
|
|
return;
|
|
|
|
recycleNode(node->mFront);
|
|
recycleNode(node->mBack);
|
|
|
|
// fresh'n it up a bit
|
|
node->mFront = 0;
|
|
node->mTarget = 0;
|
|
node->mEmitterInfo = 0;
|
|
|
|
recycleWinding(node->mWinding);
|
|
node->mWinding = 0;
|
|
|
|
// add
|
|
node->mBack = mNodeRepository;
|
|
mNodeRepository = node;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
Lighting::SVNode * Lighting::createNode(SVNode::Type type)
|
|
{
|
|
// try to get a recycled node first...
|
|
if(mNodeRepository)
|
|
{
|
|
SVNode * node = mNodeRepository;
|
|
mNodeRepository = mNodeRepository->mBack;
|
|
|
|
//
|
|
node->mType = type;
|
|
node->mBack = 0;
|
|
node->mWinding = 0;
|
|
|
|
// winding..
|
|
if(type == SVNode::PolyPlane || type == SVNode::LexelPlane)
|
|
node->mWinding = createWinding();
|
|
return(node);
|
|
}
|
|
|
|
// create the node
|
|
SVNode * node = mNodeChunker.alloc();
|
|
node->mFront = node->mBack = 0;
|
|
node->mTarget = 0;
|
|
node->mEmitterInfo = 0;
|
|
node->mWinding = 0;
|
|
|
|
// winding..
|
|
if(type == SVNode::PolyPlane || type == SVNode::LexelPlane)
|
|
node->mWinding = createWinding();
|
|
node->mType = type;
|
|
|
|
return(node);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
Lighting::SVNode::Side Lighting::SVNode::whichSide(SVNode * node)
|
|
{
|
|
AssertFatal(node, "SVNode::whichSide - NULL node");
|
|
|
|
bool front = false;
|
|
bool back = false;
|
|
|
|
const PlaneEQ & plane = gWorkingGeometry->getPlaneEQ(mPlaneIndex);
|
|
|
|
for(U32 i = 0; i < node->mWinding->mNumIndices; i++)
|
|
{
|
|
switch(plane.whichSide(node->mType == LexelPlane ? gWorkingLighting->getLexelPoint(node->mWinding->mIndices[i]) :
|
|
gWorkingGeometry->getPoint(node->mWinding->mIndices[i])))
|
|
{
|
|
case PlaneFront:
|
|
if(back)
|
|
return(Split);
|
|
front = true;
|
|
break;
|
|
|
|
case PlaneBack:
|
|
if(front)
|
|
return(Split);
|
|
back = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
AssertFatal(!(front && back), "SVNode::whichSide - failed to classify node");
|
|
|
|
if(!front && !back)
|
|
return(On);
|
|
|
|
return(front ? Front : Back);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::SVNode::addToList(SVNode ** list)
|
|
{
|
|
AssertFatal(list, "Lighting::SVNode::addToList - invalid list");
|
|
|
|
SVNode * head = (*list);
|
|
|
|
// add at the head...
|
|
(*list) = this;
|
|
mFront = head;
|
|
mBack = 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::SVNode::split(SVNode ** front, SVNode ** back, U32 planeIndex)
|
|
{
|
|
AssertFatal(front && !*front, "Lighting::SVNode::splitNode - invalid front param");
|
|
AssertFatal(back && !*back, "Lighting::SVNode::splitNode - invalid back param");
|
|
|
|
*front = gWorkingLighting->createNode(mType);
|
|
*back = gWorkingLighting->createNode(mType);
|
|
|
|
// copy the node
|
|
*(*front)->mWinding = *(*back)->mWinding = *mWinding;
|
|
(*front)->mPlaneIndex = (*back)->mPlaneIndex = mPlaneIndex;
|
|
(*front)->mEmitterInfo = (*back)->mEmitterInfo = mEmitterInfo;
|
|
(*front)->mTarget = (*back)->mTarget = mTarget;
|
|
|
|
bool success;
|
|
success = (*front)->clipWindingToPlaneFront(planeIndex);
|
|
AssertFatal(success, "Lighting::SVNode::splitNode - failed to generate front lexel poly");
|
|
|
|
success = (*back)->clipWindingToPlaneFront(gWorkingGeometry->getPlaneInverse(planeIndex));
|
|
AssertFatal(success, "Lighting::SVNode::splitNode - failed to generate back lexel poly");
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::SVNode::insertShadowVolume(SVNode ** root)
|
|
{
|
|
AssertFatal(root, "Lighting::SVNode::insertShadowVolume- invalid root");
|
|
AssertFatal(!*root, "Lighting::SVNode::insertShadowVolume- root non-null");
|
|
AssertFatal(mType == PolyPlane, "Lighting::SVNode::insertShadowVolume - invalid node type");
|
|
AssertFatal(mEmitterInfo, "Lighting::SVNode::insertShadowVolume - no emitter info");
|
|
|
|
SVNode ** current = root;
|
|
SVNode * traverse = gWorkingLighting->getShadowVolume(mEmitterInfo->mShadowVolume);
|
|
|
|
AssertFatal(traverse, "Lighting::Emitter::insertShadowVolume - bad shadow volume");
|
|
|
|
// walk the shadow planes and add at current root
|
|
while(traverse)
|
|
{
|
|
SVNode * sNode = gWorkingLighting->createNode(ShadowPlane);
|
|
sNode->mPlaneIndex = traverse->mPlaneIndex;
|
|
|
|
*current = sNode;
|
|
current = &(*current)->mFront;
|
|
|
|
traverse = traverse->mFront;
|
|
}
|
|
|
|
// add the poly node
|
|
*current = this;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::SVNode::insertFront(SVNode ** root)
|
|
{
|
|
AssertFatal(root, "Lighting::SVNode::insertFront - invalid root");
|
|
AssertFatal(mType == PolyPlane, "Lighting::SVNode::insertFront - invalid node type");
|
|
|
|
if(!(*root)->mFront)
|
|
{
|
|
AssertFatal((*root)->mType == PolyPlane, "Lighting::SVNode::insertFront - invalid node");
|
|
mTarget = *root;
|
|
insert(&(*root)->mBack);
|
|
}
|
|
else
|
|
insert(&(*root)->mFront);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::SVNode::insertBack(SVNode ** root)
|
|
{
|
|
AssertFatal(root, "Lighting::SVNode::insertBack - invalid root");
|
|
AssertFatal(mType == PolyPlane, "Lighting::SVNode::insertBack - invalid node type");
|
|
|
|
if((*root)->mType == PolyPlane)
|
|
{
|
|
mEmitterInfo->mShadowed.pushBackUnique((*root)->mEmitterInfo->mShadowVolume);
|
|
gWorkingLighting->recycleNode(this);
|
|
}
|
|
else
|
|
insert(&(*root)->mBack);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::SVNode::insert(SVNode ** root)
|
|
{
|
|
AssertFatal(root, "Lighting::SVNode::insert - invalid root");
|
|
AssertFatal(mType == PolyPlane, "Lighting::SVNode::insert - invalid node type");
|
|
|
|
if(!*root)
|
|
{
|
|
insertShadowVolume(root);
|
|
if(mTarget)
|
|
mTarget->mEmitterInfo->mShadowed.pushBackUnique(mEmitterInfo->mShadowVolume);
|
|
}
|
|
else
|
|
{
|
|
switch((*root)->whichSide(this))
|
|
{
|
|
case Front:
|
|
insertFront(root);
|
|
break;
|
|
|
|
case Back:
|
|
insertBack(root);
|
|
break;
|
|
case Split:
|
|
{
|
|
SVNode * front = 0;
|
|
SVNode * back = 0;
|
|
split(&front, &back, (*root)->mPlaneIndex);
|
|
AssertFatal(front && back, "Lighting::SVNode::insert - failed to split node");
|
|
|
|
gWorkingLighting->recycleNode(this);
|
|
|
|
// push down both sides
|
|
front->insertFront(root);
|
|
back->insertBack(root);
|
|
break;
|
|
}
|
|
default:
|
|
// sliver poly encountered (just remove for now..)
|
|
gWorkingLighting->mNumAmbiguousPlanes++;
|
|
gWorkingLighting->recycleNode(this);
|
|
//AssertFatal(false, "Lighting::SVNode::insert - invalid classification!");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// * this functino is used to clip a lexel node to it's own surface
|
|
void Lighting::SVNode::clipToInverseVolume(SVNode ** store, SVNode * volume)
|
|
{
|
|
AssertFatal(store, "Lighting::SVNode::clipToInverseVolume: invalid store param");
|
|
AssertFatal(mType == LexelPlane, "Lighting::SVNode::clipToInverseVolume: node must be of lexelPlane");
|
|
|
|
if(!volume)
|
|
{
|
|
addToList(store);
|
|
return;
|
|
}
|
|
|
|
AssertFatal(volume->mType == ShadowPlane, "Lighting::SVNode::clipToInverseVolume: invalid node in volume");
|
|
|
|
switch(volume->whichSide(this))
|
|
{
|
|
case Front:
|
|
clipToInverseVolume(store, volume->mFront);
|
|
break;
|
|
case Back:
|
|
{
|
|
mFront = mBack = 0;
|
|
gWorkingLighting->recycleNode(this);
|
|
break;
|
|
}
|
|
case Split:
|
|
{
|
|
SVNode * front = 0;
|
|
SVNode * back = 0;
|
|
split(&front, &back, volume->mPlaneIndex);
|
|
AssertFatal(front && back, "Lighting::SVNode::clipToInverseVolume - invalid split!");
|
|
|
|
mFront = mBack = 0;
|
|
gWorkingLighting->recycleNode(this);
|
|
|
|
back->mFront = back->mBack = 0;
|
|
gWorkingLighting->recycleNode(back);
|
|
|
|
front->clipToInverseVolume(store, volume->mFront);
|
|
break;
|
|
}
|
|
case On:
|
|
{
|
|
// sliver poly:
|
|
mFront = mBack = 0;
|
|
gWorkingLighting->recycleNode(this);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Lighting::SVNode::clipToVolume(SVNode ** store, SVNode * volume)
|
|
{
|
|
AssertFatal(store, "Lighting::SVNode::clipToVolume - invalid store param");
|
|
AssertFatal(mType == LexelPlane, "Lighting::SVNode::clipToVolume - node must be a lexelPlane");
|
|
|
|
if(!volume)
|
|
{
|
|
mFront = mBack = 0;
|
|
gWorkingLighting->recycleNode(this);
|
|
return;
|
|
}
|
|
|
|
AssertFatal(volume->mType == ShadowPlane, "Lighting::SVNode::clipToVolume - volume must be shadow planes");
|
|
|
|
switch(volume->whichSide(this))
|
|
{
|
|
case Back:
|
|
addToList(store);
|
|
break;
|
|
case Front:
|
|
clipToVolume(store, volume->mFront);
|
|
break;
|
|
case Split:
|
|
{
|
|
SVNode * front = 0;
|
|
SVNode * back = 0;
|
|
split(&front, &back, volume->mPlaneIndex);
|
|
AssertFatal(front && back, "Lighting::SVNode::clipToVolume - invalid split!");
|
|
|
|
mFront = mBack = 0;
|
|
gWorkingLighting->recycleNode(this);
|
|
|
|
back->addToList(store);
|
|
front->clipToVolume(store, volume->mFront);
|
|
break;
|
|
}
|
|
case On:
|
|
{
|
|
// sliver poly
|
|
mFront = mBack = 0;
|
|
gWorkingLighting->recycleNode(this);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
Point3D Lighting::SVNode::getCenter()
|
|
{
|
|
AssertFatal(mWinding, "Lighting::SVNode::getCenter - no winding");
|
|
AssertFatal(mWinding->mNumIndices >= 3, "Lighting::SVNode::getCenter - invalid node");
|
|
|
|
Point3D pnt(0,0,0);
|
|
for(U32 i = 0; i < mWinding->mNumIndices; i++)
|
|
pnt += gWorkingLighting->getLexelPoint(mWinding->mIndices[i]);
|
|
pnt /= mWinding->mNumIndices;
|
|
|
|
return(pnt);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::SVNode::move(SVNode ** list)
|
|
{
|
|
AssertFatal(list, "Lighting::SVNode::moveNodes - invalid list param");
|
|
|
|
SVNode * node = this;
|
|
while(node)
|
|
{
|
|
AssertFatal(node->mType == LexelPlane, "Lighting::SVNode::moveNodes - invalid node type");
|
|
SVNode * next = node->mFront;
|
|
node->addToList(list);
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
F64 Lighting::SVNode::getWindingSurfaceArea()
|
|
{
|
|
Point3D areaNormal(0, 0, 0);
|
|
for (U32 i = 0; i < mWinding->mNumIndices; i++) {
|
|
U32 j = (i + 1) % mWinding->mNumIndices;
|
|
|
|
Point3D temp;
|
|
mCross(getPoint(mWinding->mIndices[i]), getPoint(mWinding->mIndices[j]), &temp);
|
|
areaNormal += temp;
|
|
}
|
|
|
|
F64 area = mDot(gWorkingGeometry->getPlaneEQ(mPlaneIndex).normal, areaNormal);
|
|
if (area < 0.0)
|
|
area *= -0.5;
|
|
else
|
|
area *= 0.5;
|
|
|
|
//
|
|
if(mFront)
|
|
area += mFront->getWindingSurfaceArea();
|
|
|
|
return(area);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
const Point3D & Lighting::SVNode::getPoint(U32 index)
|
|
{
|
|
if(mType == LexelPlane)
|
|
return(gWorkingLighting->getLexelPoint(index));
|
|
return(gWorkingGeometry->getPoint(index));
|
|
}
|
|
|
|
U32 Lighting::SVNode::insertPoint(const Point3D & pnt)
|
|
{
|
|
if(mType == LexelPlane)
|
|
return(gWorkingLighting->insertLexelPoint(pnt));
|
|
else
|
|
return(gWorkingGeometry->insertPoint(pnt));
|
|
}
|
|
|
|
bool Lighting::SVNode::clipWindingToPlaneFront(U32 planeEQIndex)
|
|
{
|
|
const PlaneEQ& rClipPlane = gWorkingGeometry->getPlaneEQ(planeEQIndex);
|
|
|
|
U32 start;
|
|
for (start = 0; start < mWinding->mNumIndices; start++) {
|
|
const Point3D& rPoint = getPoint(mWinding->mIndices[start]);
|
|
if (rClipPlane.whichSide(rPoint) == PlaneFront)
|
|
break;
|
|
}
|
|
|
|
if (start == mWinding->mNumIndices) {
|
|
mWinding->mNumIndices = 0;
|
|
return true;
|
|
}
|
|
|
|
U32 finalIndices[LightingMaxWindingPoints];
|
|
U32 numFinalIndices = 0;
|
|
|
|
U32 baseStart = start;
|
|
U32 end = (start + 1) % mWinding->mNumIndices;
|
|
|
|
bool modified = false;
|
|
while (end != baseStart) {
|
|
const Point3D& rStartPoint = getPoint(mWinding->mIndices[start]);
|
|
const Point3D& rEndPoint = getPoint(mWinding->mIndices[end]);
|
|
|
|
PlaneSide fSide = rClipPlane.whichSide(rStartPoint);
|
|
PlaneSide eSide = rClipPlane.whichSide(rEndPoint);
|
|
|
|
S32 code = fSide * 3 + eSide;
|
|
switch (code) {
|
|
case 4: // f f
|
|
case 3: // f o
|
|
case 1: // o f
|
|
case 0: // o o
|
|
// No Clipping required
|
|
finalIndices[numFinalIndices++] = mWinding->mIndices[start];
|
|
start = end;
|
|
end = (end + 1) % mWinding->mNumIndices;
|
|
break;
|
|
|
|
|
|
case 2: { // f b
|
|
// In this case, we emit the front point, Insert the intersection,
|
|
// and advancing to point to first point that is in front or on...
|
|
//
|
|
finalIndices[numFinalIndices++] = mWinding->mIndices[start];
|
|
|
|
Point3D vector = rEndPoint - rStartPoint;
|
|
F64 t = -(rClipPlane.distanceToPlane(rStartPoint) / mDot(rClipPlane.normal, vector));
|
|
|
|
Point3D intersection = rStartPoint + (vector * t);
|
|
AssertFatal(rClipPlane.whichSide(intersection) == PlaneOn,
|
|
"Lighting::SVNode::clipWindingToPlaneFront: error in computing intersection");
|
|
finalIndices[numFinalIndices++] = insertPoint(intersection);
|
|
|
|
U32 endSeek = (end + 1) % mWinding->mNumIndices;
|
|
while (rClipPlane.whichSide(getPoint(mWinding->mIndices[endSeek])) == PlaneBack)
|
|
endSeek = (endSeek + 1) % mWinding->mNumIndices;
|
|
|
|
PlaneSide esSide = rClipPlane.whichSide(getPoint(mWinding->mIndices[endSeek]));
|
|
if(esSide == PlaneFront) {
|
|
end = endSeek;
|
|
start = (end + (mWinding->mNumIndices - 1)) % mWinding->mNumIndices;
|
|
|
|
const Point3D& rNewStartPoint = getPoint(mWinding->mIndices[start]);
|
|
const Point3D& rNewEndPoint = getPoint(mWinding->mIndices[end]);
|
|
|
|
vector = rNewEndPoint - rNewStartPoint;
|
|
t = -(rClipPlane.distanceToPlane(rNewStartPoint) / mDot(rClipPlane.normal, vector));
|
|
|
|
intersection = rNewStartPoint + (vector * t);
|
|
AssertFatal(rClipPlane.whichSide(intersection) == PlaneOn,
|
|
"Lighting::SVNode::clipWindingToPlaneFront: error in computing intersection");
|
|
|
|
mWinding->mIndices[start] = insertPoint(intersection);
|
|
AssertFatal(mWinding->mIndices[start] != mWinding->mIndices[end], "Error");
|
|
} else {
|
|
start = endSeek;
|
|
end = (endSeek + 1) % mWinding->mNumIndices;
|
|
}
|
|
modified = true;
|
|
}
|
|
break;
|
|
|
|
case -1: {// o b
|
|
// In this case, we emit the front point, and advance to point to first
|
|
// point that is in front or on...
|
|
//
|
|
finalIndices[numFinalIndices++] = mWinding->mIndices[start];
|
|
|
|
U32 endSeek = (end + 1) % mWinding->mNumIndices;
|
|
while (rClipPlane.whichSide(getPoint(mWinding->mIndices[endSeek])) == PlaneBack)
|
|
endSeek = (endSeek + 1) % mWinding->mNumIndices;
|
|
|
|
PlaneSide esSide = rClipPlane.whichSide(getPoint(mWinding->mIndices[endSeek]));
|
|
if(esSide == PlaneFront) {
|
|
|
|
end = endSeek;
|
|
start = (end + (mWinding->mNumIndices - 1)) % mWinding->mNumIndices;
|
|
|
|
const Point3D& rNewStartPoint = getPoint(mWinding->mIndices[start]);
|
|
const Point3D& rNewEndPoint = getPoint(mWinding->mIndices[end]);
|
|
|
|
Point3D vector = rNewEndPoint - rNewStartPoint;
|
|
F64 t = -(rClipPlane.distanceToPlane(rNewStartPoint) / mDot(rClipPlane.normal, vector));
|
|
|
|
Point3D intersection = rNewStartPoint + (vector * t);
|
|
AssertFatal(rClipPlane.whichSide(intersection) == PlaneOn,
|
|
"Lighting::SVNode::clipWindingToPlaneFront: error in computing intersection");
|
|
|
|
mWinding->mIndices[start] = insertPoint(intersection);
|
|
AssertFatal(mWinding->mIndices[start] != mWinding->mIndices[end], "Error");
|
|
} else {
|
|
start = endSeek;
|
|
end = (endSeek + 1) % mWinding->mNumIndices;
|
|
}
|
|
modified = true;
|
|
}
|
|
break;
|
|
|
|
case -2: // b f
|
|
case -3: // b o
|
|
case -4: // b b
|
|
// In the algorithm used here, this should never happen...
|
|
AssertISV(false, "Lighting::SVNode::clipWindingToPlaneFront: error in polygon clipper");
|
|
break;
|
|
|
|
default:
|
|
AssertFatal(false, "Lighting::SVNode::clipWindingToPlaneFront: bad outcode");
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// Emit the last point.
|
|
AssertFatal(rClipPlane.whichSide(getPoint(mWinding->mIndices[start])) != PlaneBack,
|
|
"Lighting::SVNode::clipWindingToPlaneFront: bad final point in clipper");
|
|
finalIndices[numFinalIndices++] = mWinding->mIndices[start];
|
|
AssertFatal(numFinalIndices >= 3, "Error, line out of clip!");
|
|
|
|
// Copy the new rWinding, and we're set!
|
|
//
|
|
dMemcpy(mWinding->mIndices, finalIndices, numFinalIndices * sizeof(U32));
|
|
mWinding->mNumIndices = numFinalIndices;
|
|
|
|
AssertISV(mWinding->mNumIndices <= LightingMaxWindingPoints,
|
|
avar("Increase lightingMaxWindingPoints. Talk to JohnF(%d, %d)",
|
|
LightingMaxWindingPoints, numFinalIndices));
|
|
|
|
#if defined(TORQUE_DEBUG)
|
|
// valid point indices?
|
|
for(U32 i = 0; i < mWinding->mNumIndices; i++)
|
|
{
|
|
U32 size = mType == LexelPlane ? gWorkingLighting->mLexelPoints.size() :
|
|
gWorkingGeometry->mPoints.size();
|
|
AssertFatal(mWinding->mIndices[i] < size, "Lighting::SVNode::clipWindingToPlaneFront: invalid point index");
|
|
}
|
|
#endif
|
|
|
|
return(modified);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class Lighting::Surface
|
|
//------------------------------------------------------------------------------
|
|
|
|
Lighting::Surface * Lighting::createSurface()
|
|
{
|
|
Surface * surface = mSurfaceChunker.alloc();
|
|
|
|
// clear it out (will not call vector ctor's)
|
|
dMemset(surface, 0, sizeof(Surface));
|
|
return(surface);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
Lighting::EmitterInfo * Lighting::Surface::getEmitterInfo(U32 emitter)
|
|
{
|
|
for(U32 i = 0; i < mNumEmitters; i++)
|
|
if(mEmitters[i]->mEmitter == emitter)
|
|
return(mEmitters[i]);
|
|
|
|
AssertFatal(false, "Lighting::Surface::getEmitterInfo - failed to get emitter info");
|
|
return NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class Lighting::Emitter
|
|
//------------------------------------------------------------------------------
|
|
|
|
Lighting::Emitter::Emitter()
|
|
{
|
|
mIndex = 0;
|
|
mNumSurfaces = 0;
|
|
mSurfaces = 0;
|
|
mAnimated = false;
|
|
}
|
|
|
|
void Lighting::Emitter::processBSP()
|
|
{
|
|
SVNode * root = 0;
|
|
|
|
// the polynode gets trashed, so insert a copy
|
|
for(U32 i = 0; i < mNumSurfaces; i++)
|
|
{
|
|
Surface * surface = gWorkingLighting->getSurface(mSurfaces[i]);
|
|
AssertFatal(surface && surface->mPolyNode, "Lighting::Emitter::processBSP - invalid surface");
|
|
|
|
SVNode * pNode = gWorkingLighting->createNode(Lighting::SVNode::PolyPlane);
|
|
*pNode->mWinding = *surface->mPolyNode->mWinding;
|
|
pNode->mPlaneIndex = surface->mPolyNode->mPlaneIndex;
|
|
pNode->mEmitterInfo = surface->getEmitterInfo(mIndex);
|
|
|
|
pNode->insert(&root);
|
|
}
|
|
|
|
gWorkingLighting->recycleNode(root);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class Lighting::PointEmitter
|
|
//------------------------------------------------------------------------------
|
|
bool Lighting::PointEmitter::isSurfaceLit(const Surface * surface)
|
|
{
|
|
AssertFatal(surface, "Lighting::PointEmitter::isSurfaceLit - NULL surface");
|
|
const PlaneEQ & plane = gWorkingGeometry->getPlaneEQ(surface->mPlaneIndex);
|
|
|
|
if(plane.whichSide(mPos) != PlaneFront)
|
|
return(false);
|
|
|
|
// can light something on the surface?
|
|
F64 distance = plane.distanceToPlane(mPos);
|
|
if(distance > mFalloffs[1])
|
|
return(false);
|
|
|
|
// check if the point is inside the poly..
|
|
Point3D pnt = mPos - (plane.normal * distance);
|
|
const MiniWinding & winding = surface->mWinding;
|
|
|
|
// inside this surface?
|
|
bool inside = true;
|
|
for(U32 i = 0; inside && (i < winding.mNumIndices); i++)
|
|
{
|
|
U32 j = (i+1) % winding.mNumIndices;
|
|
Point3D vec1 = gWorkingGeometry->getPoint(winding.mIndices[j]) - gWorkingGeometry->getPoint(winding.mIndices[i]);
|
|
Point3D vec2 = pnt - gWorkingGeometry->getPoint(winding.mIndices[i]);
|
|
|
|
Point3D cross;
|
|
mCross(vec1, vec2, &cross);
|
|
|
|
if(mDot(plane.normal, cross) > 0.f)
|
|
inside = false;
|
|
}
|
|
|
|
if(inside)
|
|
return(true);
|
|
|
|
// check if any of the line segments intersect the light sphere
|
|
for(U32 i = 0; i < winding.mNumIndices; i++)
|
|
{
|
|
// substitute the line equation into the sphere and
|
|
// solve the quadratic
|
|
U32 j = (i+1) % winding.mNumIndices;
|
|
Point3D p1 = gWorkingGeometry->getPoint(winding.mIndices[i]);
|
|
Point3D p2 = gWorkingGeometry->getPoint(winding.mIndices[j]);
|
|
Point3D & p3 = mPos;
|
|
F64 r = mFalloffs[1];
|
|
|
|
// get the quadratic terms
|
|
F64 a = (p2.x - p1.x) * (p2.x - p1.x) +
|
|
(p2.y - p1.y) * (p2.y - p1.y) +
|
|
(p2.z - p1.z) * (p2.z - p1.z);
|
|
|
|
F64 b = 2 * ((p2.x - p1.x) * (p1.x - p3.x) +
|
|
(p2.y - p1.y) * (p1.y - p3.y) +
|
|
(p2.z - p1.z) * (p1.z - p3.z));
|
|
|
|
F64 c = p3.x * p3.x + p3.y * p3.y + p3.z * p3.z +
|
|
p1.x * p1.x + p1.y * p1.y + p1.z * p1.z -
|
|
2 * (p3.x * p1.x + p3.y * p1.y + p3.z * p1.z) - r * r;
|
|
|
|
// if the expression within the square root is:
|
|
// <0: the line does not intersect the sphere
|
|
// 0: the line is tangential
|
|
// >0: intersects at two points
|
|
F64 d = (b * b - 4 * a * c);
|
|
|
|
if(d > 0.f)
|
|
{
|
|
// get the times of intersection...
|
|
// if either time in 0>1 then intersected, otherwise
|
|
// if there is a t<0 && t>1 then contained inside the sphere
|
|
d = mSqrtD(d);
|
|
F64 t1 = (-b + d) / (2 * a);
|
|
if(t1 > 0.f && t1 < 1.f)
|
|
return(true);
|
|
|
|
F64 t2 = (-b - d) / (2 * a);
|
|
if(t2 > 0.f && t2 < 1.f)
|
|
return(true);
|
|
|
|
// contained?
|
|
if(t1 * t2 < 0.f)
|
|
return(true);
|
|
}
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
F64 Lighting::PointEmitter::calcIntensity(SVNode * lSurface)
|
|
{
|
|
AssertFatal(lSurface, "Lighting::PointEmitter::calcIntensity - NULL surface");
|
|
AssertFatal(lSurface->mType == SVNode::LexelPlane, "Lighting::PointEmitter::calcIntensity - invalid node passed");
|
|
|
|
Point3D center = lSurface->getCenter();
|
|
F64 len = Point3D(mPos - center).len();
|
|
|
|
if(len > mFalloffs[1])
|
|
return(0.f);
|
|
|
|
if(len > mFalloffs[0])
|
|
return(1.f - (len - F64(mFalloffs[0])) / F64((mFalloffs[1]-mFalloffs[0])));
|
|
|
|
return(1);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class Lighting::SpotEmitter
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::SpotEmitter::isSurfaceLit(const Surface * surface)
|
|
{
|
|
AssertFatal(surface, "Lighting::SpotEmitter::isSurfaceLit - NULL surface");
|
|
const PlaneEQ & plane = gWorkingGeometry->getPlaneEQ(surface->mPlaneIndex);
|
|
|
|
if(plane.whichSide(mPos) != PlaneFront)
|
|
return(false);
|
|
|
|
// can light something on the surface?
|
|
F64 distance = plane.distanceToPlane(mPos);
|
|
if(distance > mFalloffs[1])
|
|
return(false);
|
|
|
|
// check if the point is inside the poly..
|
|
Point3D pnt = mPos - (plane.normal * distance);
|
|
const MiniWinding & winding = surface->mWinding;
|
|
|
|
// inside this surface?
|
|
bool inside = true;
|
|
for(U32 i = 0; inside && (i < winding.mNumIndices); i++)
|
|
{
|
|
U32 j = (i+1) % winding.mNumIndices;
|
|
Point3D vec1 = gWorkingGeometry->getPoint(winding.mIndices[j]) - gWorkingGeometry->getPoint(winding.mIndices[i]);
|
|
Point3D vec2 = pnt - gWorkingGeometry->getPoint(winding.mIndices[i]);
|
|
|
|
Point3D cross;
|
|
mCross(vec1, vec2, &cross);
|
|
|
|
if(mDot(plane.normal, cross) > 0.f)
|
|
inside = false;
|
|
}
|
|
|
|
if(inside)
|
|
return(true);
|
|
|
|
// check if any of the line segments intersect the light sphere
|
|
for(U32 i = 0; i < winding.mNumIndices; i++)
|
|
{
|
|
// substitute the line equation into the sphere and
|
|
// solve the quadratic
|
|
U32 j = (i+1) % winding.mNumIndices;
|
|
Point3D p1 = gWorkingGeometry->getPoint(winding.mIndices[i]);
|
|
Point3D p2 = gWorkingGeometry->getPoint(winding.mIndices[j]);
|
|
Point3D & p3 = mPos;
|
|
F64 r = mFalloffs[1];
|
|
|
|
// get the quadratic terms
|
|
F64 a = (p2.x - p1.x) * (p2.x - p1.x) +
|
|
(p2.y - p1.y) * (p2.y - p1.y) +
|
|
(p2.z - p1.z) * (p2.z - p1.z);
|
|
|
|
F64 b = 2 * ((p2.x - p1.x) * (p1.x - p3.x) +
|
|
(p2.y - p1.y) * (p1.y - p3.y) +
|
|
(p2.z - p1.z) * (p1.z - p3.z));
|
|
|
|
F64 c = p3.x * p3.x + p3.y * p3.y + p3.z * p3.z +
|
|
p1.x * p1.x + p1.y * p1.y + p1.z * p1.z -
|
|
2 * (p3.x * p1.x + p3.y * p1.y + p3.z * p1.z) - r * r;
|
|
|
|
// if the expression within the square root is:
|
|
// <0: the line does not intersect the sphere
|
|
// 0: the line is tangential
|
|
// >0: intersects at two points
|
|
F64 d = (b * b - 4 * a * c);
|
|
|
|
if(d > 0.f)
|
|
{
|
|
// get the times of intersection...
|
|
// if either time in 0>1 then intersected, otherwise
|
|
// if there is a t<0 && t>1 then contained inside the sphere
|
|
d = mSqrtD(d);
|
|
F64 t1 = (-b + d) / (2 * a);
|
|
if(t1 > 0.f && t1 < 1.f)
|
|
return(true);
|
|
|
|
F64 t2 = (-b - d) / (2 * a);
|
|
if(t2 > 0.f && t2 < 1.f)
|
|
return(true);
|
|
|
|
// contained?
|
|
if(t1 * t2 < 0.f)
|
|
return(true);
|
|
}
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
F64 Lighting::SpotEmitter::calcIntensity(SVNode * lSurface)
|
|
{
|
|
AssertFatal(lSurface, "Lighting::SpotEmitter::calcIntensity - NULL surface");
|
|
AssertFatal(lSurface->mType == SVNode::LexelPlane, "Lighting::SpotEmitter::calcIntensity - invalid node passed");
|
|
|
|
Point3D center = lSurface->getCenter();
|
|
F64 len = Point3D(center - mPos).len();
|
|
|
|
if(len > mFalloffs[1])
|
|
return(0.f);
|
|
|
|
//
|
|
F64 intensity = (len > mFalloffs[0]) ? 1 - ((len - F64(mFalloffs[0])) / F64((mFalloffs[1]-mFalloffs[0]))) : 1;
|
|
|
|
Point3D vec = Point3D(center - mPos);
|
|
vec.normalize();
|
|
|
|
F64 angle = mAcos(mDot(mDirection, vec));
|
|
|
|
if(angle <= mAngles[0])
|
|
return(intensity);
|
|
|
|
if(angle > mAngles[1])
|
|
return(0.f);
|
|
|
|
return(intensity * (1 - ((angle - mAngles[0]) / (mAngles[1] - mAngles[0]))));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class Lighting::Light
|
|
//------------------------------------------------------------------------------
|
|
|
|
Lighting::Light::Light()
|
|
{
|
|
mNumStates = 0;
|
|
mStateIndex = 0;
|
|
mAnimated = false;
|
|
mAnimType = 0;
|
|
mName = 0;
|
|
}
|
|
|
|
Lighting::Light::~Light()
|
|
{
|
|
if(!mAnimated)
|
|
delete [] mName;
|
|
mName = NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::Light::getTargetPosition(const char * name, Point3D & pos)
|
|
{
|
|
TargetEntity * target = 0;
|
|
for(U32 i = 0; !target && i < gWorkingLighting->mTargets.size(); i++)
|
|
if(!dStricmp(gWorkingLighting->mTargets[i]->mTargetName, name))
|
|
target = gWorkingLighting->mTargets[i];
|
|
|
|
//
|
|
if(!target)
|
|
{
|
|
dPrintf(" ! Target entity not found: '%s'", name); dFflushStdout();
|
|
return(false);
|
|
}
|
|
|
|
pos = target->mOrigin;
|
|
return(true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::Light::buildLight(BaseLightEntity * entity)
|
|
{
|
|
AssertFatal(entity, "Lighting::Light::buildLight - NULL entity");
|
|
|
|
LightEntity * light = static_cast<LightEntity*>(entity);
|
|
|
|
AssertFatal(light->mLightName[0], "Lighting::Light::buildLight - unnamed light encountered.");
|
|
AssertFatal(light->mNumStates, avar("Lighting::Light::buildLight - light '%s' has no states.", light->mLightName));
|
|
|
|
mName = newStrDup(light->mLightName);
|
|
mNumStates = light->mNumStates;
|
|
mStateIndex = gWorkingLighting->mLightStates.size();
|
|
|
|
// assume the light to be animated if there is more than one state
|
|
if(mNumStates > 1)
|
|
{
|
|
// animated stuff...
|
|
mAnimType = light->mFlags;
|
|
mAnimated = true;
|
|
}
|
|
|
|
// grab the emitters for this light
|
|
Vector<BaseLightEmitterEntity *> myEmitters;
|
|
for(U32 i = 0; i < gWorkingLighting->mBaseLightEmitters.size(); i++)
|
|
if(gWorkingLighting->mBaseLightEmitters[i]->mTargetLight[0] &&
|
|
!dStricmp(gWorkingLighting->mBaseLightEmitters[i]->mTargetLight, mName))
|
|
myEmitters.push_back(gWorkingLighting->mBaseLightEmitters[i]);
|
|
|
|
// process the states....
|
|
for(U32 i = 0; i < mNumStates; i++)
|
|
{
|
|
LightState state;
|
|
state.mNumEmitters = 0;
|
|
state.mEmitterIndex = gWorkingLighting->mLightStateEmitterStore.size();
|
|
state.mDuration = light->mStates[i].mDuration;
|
|
|
|
// grab them emitters
|
|
for(U32 j = 0; j < myEmitters.size(); j++)
|
|
{
|
|
if(myEmitters[j]->mStateIndex != i)
|
|
continue;
|
|
|
|
// process the entity
|
|
if(dynamic_cast<PointEmitterEntity*>(myEmitters[j]))
|
|
{
|
|
PointEmitterEntity * pointEntity = (PointEmitterEntity*)myEmitters[j];
|
|
|
|
// add a point emitter
|
|
PointEmitter * emitter = new PointEmitter;
|
|
emitter->mPos = light->mOrigin;
|
|
emitter->mColor = light->mStates[i].mColor;
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(pointEntity->mOrigin);
|
|
emitter->mIndex = state.mEmitterIndex + state.mNumEmitters++;
|
|
emitter->mAnimated = mAnimated;
|
|
|
|
emitter->mFalloffs[0] = pointEntity->mFalloff1;
|
|
emitter->mFalloffs[1] = pointEntity->mFalloff2;
|
|
|
|
state.mNumEmitters++;
|
|
}
|
|
else if(dynamic_cast<SpotEmitterEntity*>(myEmitters[j]))
|
|
{
|
|
SpotEmitterEntity * spotEntity = (SpotEmitterEntity*)myEmitters[j];
|
|
|
|
// add the spot emitter
|
|
SpotEmitter * emitter = new SpotEmitter;
|
|
|
|
emitter->mPos = light->mOrigin;
|
|
emitter->mColor = light->mStates[i].mColor;
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(spotEntity->mOrigin);
|
|
emitter->mIndex = state.mEmitterIndex + state.mNumEmitters++;
|
|
emitter->mAnimated = mAnimated;
|
|
|
|
emitter->mFalloffs[0] = spotEntity->mFalloff1;
|
|
emitter->mFalloffs[1] = spotEntity->mFalloff2;
|
|
|
|
emitter->mDirection = spotEntity->mDirection;
|
|
emitter->mAngles[0] = spotEntity->mInnerAngle;
|
|
emitter->mAngles[1] = spotEntity->mOuterAngle;
|
|
|
|
state.mNumEmitters++;
|
|
}
|
|
else
|
|
AssertFatal(false, "Lighting::Light::buildLight - invalid entity encountered.");
|
|
}
|
|
|
|
gWorkingLighting->mLightStates.push_back(state);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// All the scripted lights:
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::Light::buildOmniLight(BaseLightEntity * entity)
|
|
{
|
|
AssertFatal(entity, "Lighting::Light::buildOmniLight - NULL entity");
|
|
|
|
LightOmniEntity * omniLight = static_cast<LightOmniEntity*>(entity);
|
|
AssertFatal(omniLight, "Lighting::Light::buildOmniLight - invalid entity");
|
|
|
|
// fill the light, state and then the emittter info
|
|
mNumStates = 1;
|
|
mStateIndex = gWorkingLighting->mLightStates.size();
|
|
mName = newStrDup(omniLight->mLightName);
|
|
|
|
Light::LightState state;
|
|
state.mNumEmitters = 1;
|
|
state.mEmitterIndex = gWorkingLighting->mEmitters.size();
|
|
gWorkingLighting->mLightStateEmitterStore.push_back(state.mEmitterIndex);
|
|
|
|
PointEmitter * emitter = new PointEmitter;
|
|
emitter->mPos = omniLight->mOrigin;
|
|
emitter->mColor = omniLight->mColor;
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(emitter->mPos);
|
|
emitter->mIndex = state.mEmitterIndex;
|
|
|
|
emitter->mFalloffs[0] = omniLight->mFalloff1;
|
|
emitter->mFalloffs[1] = omniLight->mFalloff2;
|
|
|
|
gWorkingLighting->mEmitters.push_back(emitter);
|
|
gWorkingLighting->mLightStates.push_back(state);
|
|
|
|
return(true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::Light::buildSpotLight(BaseLightEntity * entity)
|
|
{
|
|
AssertFatal(entity, "Lighting::Light::buildSpotLight - NULL entity");
|
|
|
|
LightSpotEntity * spotLight = static_cast<LightSpotEntity*>(entity);
|
|
AssertFatal(spotLight, "Lighting::Light::buildSpotLight - invalid entity");
|
|
|
|
//
|
|
Point3D target;
|
|
if(!getTargetPosition(spotLight->mTarget, target))
|
|
return(false);
|
|
|
|
// fill the light, state and then the emittter info
|
|
mNumStates = 1;
|
|
mStateIndex = gWorkingLighting->mLightStates.size();
|
|
mName = newStrDup(spotLight->mLightName);
|
|
|
|
Light::LightState state;
|
|
state.mNumEmitters = 1;
|
|
state.mDuration = 0.f;
|
|
state.mEmitterIndex = gWorkingLighting->mEmitters.size();
|
|
gWorkingLighting->mLightStateEmitterStore.push_back(state.mEmitterIndex);
|
|
|
|
SpotEmitter * emitter = new SpotEmitter;
|
|
emitter->mPos = spotLight->mOrigin;
|
|
emitter->mColor = spotLight->mColor;
|
|
|
|
// calculate the direction and angles...
|
|
emitter->mDirection = target - spotLight->mOrigin;
|
|
F32 len = emitter->mDirection.len();
|
|
|
|
//
|
|
emitter->mDirection.normalize();
|
|
emitter->mAngles[0] = mAtan(spotLight->mInnerDistance, len);
|
|
emitter->mAngles[1] = mAtan(spotLight->mOuterDistance, len);
|
|
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(emitter->mPos);
|
|
emitter->mIndex = state.mEmitterIndex;
|
|
|
|
emitter->mFalloffs[0] = spotLight->mFalloff1;
|
|
emitter->mFalloffs[1] = spotLight->mFalloff2;
|
|
|
|
gWorkingLighting->mEmitters.push_back(emitter);
|
|
gWorkingLighting->mLightStates.push_back(state);
|
|
|
|
return(true);
|
|
}
|
|
|
|
// Animated script lights: -----------------------------------------------------
|
|
|
|
bool Lighting::Light::buildStrobeLight(BaseLightEntity * entity)
|
|
{
|
|
AssertFatal(entity, "Lighting::Light::buildStrobeLight - NULL entity");
|
|
|
|
LightStrobeEntity * strobeLight = static_cast<LightStrobeEntity*>(entity);
|
|
AssertFatal(strobeLight, "Lighting::Light::buildStrobeLight - invalid entity");
|
|
|
|
if(strobeLight->mSpeed > LightStrobeEntity::VeryFast)
|
|
{
|
|
dPrintf(" ! Invalid speed for strobe light: %s", strobeLight->mLightName);
|
|
return(false);
|
|
}
|
|
|
|
// total of 4 states
|
|
mNumStates = 4;
|
|
mStateIndex = gWorkingLighting->mLightStates.size();
|
|
mAnimated = true;
|
|
mAnimType = (strobeLight->mFlags & ~Interior::AnimationFlicker) | Interior::AnimationLoop;
|
|
mName = newStrDup(strobeLight->mLightName);
|
|
|
|
for(U32 i = 0; i < 4; i++)
|
|
{
|
|
// create this state
|
|
Light::LightState state;
|
|
state.mNumEmitters = 1;
|
|
state.mEmitterIndex = gWorkingLighting->mEmitters.size();
|
|
gWorkingLighting->mLightStateEmitterStore.push_back(state.mEmitterIndex);
|
|
|
|
// create the emitter
|
|
PointEmitter * emitter = new PointEmitter;
|
|
emitter->mPos = strobeLight->mOrigin;
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(emitter->mPos);
|
|
emitter->mIndex = state.mEmitterIndex;
|
|
emitter->mFalloffs[0] = strobeLight->mFalloff1;
|
|
emitter->mFalloffs[1] = strobeLight->mFalloff2;
|
|
emitter->mAnimated = true;
|
|
|
|
state.mDuration = (i & 0x01) ? 0.f : AnimationTimes[strobeLight->mSpeed];
|
|
emitter->mColor = (i & 0x02) ? strobeLight->mColor2 : strobeLight->mColor1;
|
|
|
|
gWorkingLighting->mEmitters.push_back(emitter);
|
|
gWorkingLighting->mLightStates.push_back(state);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::Light::buildPulseLight(BaseLightEntity * entity)
|
|
{
|
|
AssertFatal(entity, "Lighting::Light::buildPulseLight - NULL entity");
|
|
|
|
LightPulseEntity * pulseLight = static_cast<LightPulseEntity*>(entity);
|
|
AssertFatal(pulseLight, "Lighting::Light::buildPulseLight - invalid entity");
|
|
|
|
if(pulseLight->mSpeed > LightPulseEntity::VeryFast)
|
|
{
|
|
dPrintf(" ! Invalid speed for pulse light: %s", pulseLight->mLightName);
|
|
return(false);
|
|
}
|
|
|
|
// total of 2 states
|
|
mNumStates = 2;
|
|
mStateIndex = gWorkingLighting->mLightStates.size();
|
|
mAnimated = true;
|
|
mAnimType = (pulseLight->mFlags & ~Interior::AnimationFlicker) | Interior::AnimationLoop;
|
|
mName = newStrDup(pulseLight->mLightName);
|
|
|
|
for(U32 i = 0; i < 2; i++)
|
|
{
|
|
// create this state
|
|
Light::LightState state;
|
|
state.mNumEmitters = 1;
|
|
state.mEmitterIndex = gWorkingLighting->mEmitters.size();
|
|
gWorkingLighting->mLightStateEmitterStore.push_back(state.mEmitterIndex);
|
|
|
|
// create the emitter
|
|
PointEmitter * emitter = new PointEmitter;
|
|
emitter->mPos = pulseLight->mOrigin;
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(emitter->mPos);
|
|
emitter->mIndex = state.mEmitterIndex;
|
|
emitter->mFalloffs[0] = pulseLight->mFalloff1;
|
|
emitter->mFalloffs[1] = pulseLight->mFalloff2;
|
|
emitter->mAnimated = true;
|
|
|
|
// color and duration
|
|
state.mDuration = AnimationTimes[pulseLight->mSpeed];
|
|
emitter->mColor = i ? pulseLight->mColor2 : pulseLight->mColor1;
|
|
|
|
gWorkingLighting->mEmitters.push_back(emitter);
|
|
gWorkingLighting->mLightStates.push_back(state);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::Light::buildPulse2Light(BaseLightEntity * entity)
|
|
{
|
|
AssertFatal(entity, "Lighting::Light::buildPulse2Light - NULL entity");
|
|
|
|
LightPulse2Entity * pulseLight = static_cast<LightPulse2Entity*>(entity);
|
|
AssertFatal(pulseLight, "Lighting::Light::buildPulse2Light - invalid entity");
|
|
|
|
// total of 4 states
|
|
mNumStates = 4;
|
|
mStateIndex = gWorkingLighting->mLightStates.size();
|
|
mAnimated = true;
|
|
mAnimType = (pulseLight->mFlags & ~Interior::AnimationFlicker) | Interior::AnimationLoop;
|
|
mName = newStrDup(pulseLight->mLightName);
|
|
|
|
F32 durations[] = { pulseLight->mAttack, pulseLight->mSustain1, pulseLight->mDecay, pulseLight->mSustain2 };
|
|
|
|
for(U32 i = 0; i < 4; i++)
|
|
{
|
|
// create this state
|
|
Light::LightState state;
|
|
state.mNumEmitters = 1;
|
|
state.mEmitterIndex = gWorkingLighting->mEmitters.size();
|
|
gWorkingLighting->mLightStateEmitterStore.push_back(state.mEmitterIndex);
|
|
|
|
// create the emitter
|
|
PointEmitter * emitter = new PointEmitter;
|
|
emitter->mPos = pulseLight->mOrigin;
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(emitter->mPos);
|
|
emitter->mIndex = state.mEmitterIndex;
|
|
emitter->mFalloffs[0] = pulseLight->mFalloff1;
|
|
emitter->mFalloffs[1] = pulseLight->mFalloff2;
|
|
emitter->mAnimated = true;
|
|
|
|
// color and duration
|
|
state.mDuration = durations[i];
|
|
emitter->mColor = (i == 0 || i == 3) ? pulseLight->mColor1 : pulseLight->mColor2;
|
|
|
|
gWorkingLighting->mEmitters.push_back(emitter);
|
|
gWorkingLighting->mLightStates.push_back(state);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::Light::buildFlickerLight(BaseLightEntity * entity)
|
|
{
|
|
AssertFatal(entity, "Lighting::Light::buildFlickerLight - NULL entity");
|
|
|
|
LightFlickerEntity * flickerLight = static_cast<LightFlickerEntity*>(entity);
|
|
AssertFatal(flickerLight, "Lighting::Light::buildFlickerLight - invalid entity");
|
|
|
|
if(flickerLight->mSpeed > LightFlickerEntity::VeryFast)
|
|
{
|
|
dPrintf(" ! Invalid speed for flicker light: %s", flickerLight->mLightName);
|
|
return(false);
|
|
}
|
|
|
|
// total of 5 states
|
|
mNumStates = 5;
|
|
mStateIndex = gWorkingLighting->mLightStates.size();
|
|
mAnimated = true;
|
|
|
|
mAnimType = (flickerLight->mFlags & ~Interior::AnimationLoop) | Interior::AnimationFlicker;
|
|
mName = newStrDup(flickerLight->mLightName);
|
|
|
|
ColorF colors [] = {
|
|
flickerLight->mColor1,
|
|
flickerLight->mColor2,
|
|
flickerLight->mColor3,
|
|
flickerLight->mColor4,
|
|
flickerLight->mColor5};
|
|
|
|
for(U32 i = 0; i < 5; i++)
|
|
{
|
|
// create this state
|
|
Light::LightState state;
|
|
state.mNumEmitters = 1;
|
|
state.mEmitterIndex = gWorkingLighting->mEmitters.size();
|
|
gWorkingLighting->mLightStateEmitterStore.push_back(state.mEmitterIndex);
|
|
|
|
// create the emitter
|
|
PointEmitter * emitter = new PointEmitter;
|
|
emitter->mPos = flickerLight->mOrigin;
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(emitter->mPos);
|
|
emitter->mIndex = state.mEmitterIndex;
|
|
emitter->mFalloffs[0] = flickerLight->mFalloff1;
|
|
emitter->mFalloffs[1] = flickerLight->mFalloff2;
|
|
emitter->mAnimated = true;
|
|
|
|
// color and duration
|
|
state.mDuration = AnimationTimes[flickerLight->mSpeed];
|
|
emitter->mColor = colors[i];
|
|
|
|
gWorkingLighting->mEmitters.push_back(emitter);
|
|
gWorkingLighting->mLightStates.push_back(state);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::Light::buildRunwayLight(BaseLightEntity * entity)
|
|
{
|
|
AssertFatal(entity, "Lighting::Light::buildRunwayLight - NULL entity");
|
|
|
|
LightRunwayEntity * runwayLight = static_cast<LightRunwayEntity*>(entity);
|
|
AssertFatal(runwayLight, "Lighting::Light::buildRunwayLight - invalid entity");
|
|
|
|
if(runwayLight->mSpeed > LightRunwayEntity::VeryFast)
|
|
{
|
|
dPrintf(" ! Invalid speed for runway light: %s", runwayLight->mLightName);
|
|
return(false);
|
|
}
|
|
|
|
Point3D endPos;
|
|
if(!getTargetPosition(runwayLight->mEndTarget, endPos))
|
|
return(false);
|
|
|
|
//
|
|
mNumStates = 0;
|
|
mStateIndex = gWorkingLighting->mLightStates.size();
|
|
mAnimated = true;
|
|
mAnimType = (runwayLight->mFlags & ~Interior::AnimationFlicker) | Interior::AnimationLoop;
|
|
mName = newStrDup(runwayLight->mLightName);
|
|
|
|
Point3D step = (endPos - runwayLight->mOrigin) / (runwayLight->mSteps + 1);
|
|
Point3D lightPos = runwayLight->mOrigin;
|
|
|
|
for(U32 i = 0; i < (runwayLight->mSteps+2); i++)
|
|
{
|
|
// create this state
|
|
Light::LightState state;
|
|
state.mNumEmitters = 1;
|
|
state.mEmitterIndex = gWorkingLighting->mEmitters.size();
|
|
state.mDuration = AnimationTimes[runwayLight->mSpeed];
|
|
gWorkingLighting->mLightStateEmitterStore.push_back(state.mEmitterIndex);
|
|
|
|
// create the emitter
|
|
PointEmitter * emitter = new PointEmitter;
|
|
emitter->mIndex = state.mEmitterIndex;
|
|
emitter->mFalloffs[0] = runwayLight->mFalloff1;
|
|
emitter->mFalloffs[1] = runwayLight->mFalloff2;
|
|
emitter->mAnimated = true;
|
|
emitter->mColor = runwayLight->mColor;
|
|
|
|
// figure out the position...
|
|
emitter->mPos = lightPos;
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(emitter->mPos);
|
|
lightPos += step;
|
|
|
|
mNumStates++;
|
|
gWorkingLighting->mEmitters.push_back(emitter);
|
|
gWorkingLighting->mLightStates.push_back(state);
|
|
}
|
|
|
|
// pingpong???
|
|
if(runwayLight->mPingPong)
|
|
{
|
|
lightPos -= step;
|
|
|
|
for(U32 i = 0; i < runwayLight->mSteps; i++)
|
|
{
|
|
// create this state
|
|
Light::LightState state;
|
|
state.mNumEmitters = 1;
|
|
state.mEmitterIndex = gWorkingLighting->mEmitters.size();
|
|
state.mDuration = AnimationTimes[runwayLight->mSpeed];
|
|
gWorkingLighting->mLightStateEmitterStore.push_back(state.mEmitterIndex);
|
|
|
|
// create the emitter
|
|
PointEmitter * emitter = new PointEmitter;
|
|
emitter->mIndex = state.mEmitterIndex;
|
|
emitter->mFalloffs[0] = runwayLight->mFalloff1;
|
|
emitter->mFalloffs[1] = runwayLight->mFalloff2;
|
|
emitter->mAnimated = true;
|
|
emitter->mColor = runwayLight->mColor;
|
|
|
|
// figure out the position...
|
|
lightPos -= step;
|
|
emitter->mPos = lightPos;
|
|
emitter->mPoint = gWorkingGeometry->insertPoint(emitter->mPos);
|
|
|
|
mNumStates++;
|
|
gWorkingLighting->mEmitters.push_back(emitter);
|
|
gWorkingLighting->mLightStates.push_back(state);
|
|
}
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool Lighting::Light::build(BaseLightEntity * entity)
|
|
{
|
|
AssertFatal(entity, "Lighting::Light::build - NULL entity");
|
|
|
|
// build the light here....
|
|
if(dynamic_cast<LightEntity*>(entity))
|
|
return(buildLight(entity));
|
|
|
|
// scripted lights...
|
|
if(dynamic_cast<LightOmniEntity*>(entity))
|
|
return(buildOmniLight(entity));
|
|
if(dynamic_cast<LightSpotEntity*>(entity))
|
|
return(buildSpotLight(entity));
|
|
|
|
// animated lights...
|
|
if(dynamic_cast<LightStrobeEntity*>(entity))
|
|
return(buildStrobeLight(entity));
|
|
if(dynamic_cast<LightPulseEntity*>(entity))
|
|
return(buildPulseLight(entity));
|
|
if(dynamic_cast<LightPulse2Entity*>(entity))
|
|
return(buildPulse2Light(entity));
|
|
if(dynamic_cast<LightFlickerEntity*>(entity))
|
|
return(buildFlickerLight(entity));
|
|
if(dynamic_cast<LightRunwayEntity*>(entity))
|
|
return(buildRunwayLight(entity));
|
|
|
|
// unsupported light type
|
|
return(false);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class Lighting::Light::LightState
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::Light::LightState::fillStateData(EditGeometry::LightState * state)
|
|
{
|
|
AssertFatal(state, "Lighting::Light::LightState::fillStateData - invalid state");
|
|
AssertFatal(mNumEmitters, "Lighting::Light::LightState::fillStateData - no emitters in state");
|
|
|
|
// all emitters are same color.. just grab from first
|
|
Emitter * emitter = gWorkingLighting->mEmitters[mEmitterIndex];
|
|
state->color.set(U8(emitter->mColor.red * 255.0f),
|
|
U8(emitter->mColor.green * 255.0f),
|
|
U8(emitter->mColor.blue * 255.0f));
|
|
|
|
//
|
|
state->duration = mDuration;
|
|
|
|
// merge the lightmaps?
|
|
for(U32 i = 0; i < mNumEmitters; i++)
|
|
{
|
|
emitter = gWorkingLighting->mEmitters[mEmitterIndex + i];
|
|
|
|
for(U32 k = 0; k < emitter->mNumSurfaces; k++)
|
|
{
|
|
Surface * surface = gWorkingLighting->mSurfaces[emitter->mSurfaces[k]];
|
|
EmitterInfo * eInfo = surface->getEmitterInfo(mEmitterIndex + i);
|
|
AssertFatal(eInfo->mLightMap, "Lighting::Light::LightState::fillStateData - null Lightmap");
|
|
|
|
EditGeometry::StateData stateData;
|
|
stateData.pLMap = eInfo->mLightMap;
|
|
|
|
#ifdef DUMP_LIGHTMAPS
|
|
//
|
|
if(stateData.pLMap)
|
|
{
|
|
// convert the intensity bitmap to RGB
|
|
GBitmap bitmap(stateData.pLMap->getWidth(), stateData.pLMap->getHeight());
|
|
for(U32 y = 0; y < stateData.pLMap->getHeight(); y++)
|
|
for(U32 x = 0; x < stateData.pLMap->getWidth(); x++)
|
|
{
|
|
U8 * pDest = bitmap.getAddress(x, y);
|
|
U8 src = *stateData.pLMap->getAddress(x, y);
|
|
|
|
*pDest++ = src;
|
|
*pDest++ = src;
|
|
*pDest++ = src;
|
|
}
|
|
|
|
//
|
|
FileStream output;
|
|
output.open(avar("anim_%x_%d_%d.png", state, i, k), FileStream::Write);
|
|
bitmap.writePNG(output);
|
|
}
|
|
#endif
|
|
|
|
stateData.surfaceIndex = surface->mSurfaceIndex;
|
|
|
|
state->stateData.push_back(stateData);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Lighting::processAnimatedLights()
|
|
{
|
|
// walk the animated lights...
|
|
for(U32 i = 0; i < mLights.size(); i++)
|
|
{
|
|
Light * light = mLights[i];
|
|
if(!light->mAnimated)
|
|
continue;
|
|
|
|
EditGeometry::AnimatedLight * animLight = new EditGeometry::AnimatedLight;
|
|
|
|
// fill in state info
|
|
for(U32 j = 0; j < light->mNumStates; j++)
|
|
{
|
|
EditGeometry::LightState * destLightState = new EditGeometry::LightState;
|
|
Light::LightState srcLightState = mLightStates[light->mStateIndex + j];
|
|
|
|
// should we be merging the lightmaps here?
|
|
srcLightState.fillStateData(destLightState);
|
|
|
|
animLight->states.push_back(destLightState);
|
|
}
|
|
|
|
// Copy the properties
|
|
animLight->name = light->mName;
|
|
animLight->type = light->mAnimType;
|
|
animLight->alarm = light->alarm;
|
|
|
|
gWorkingGeometry->mAnimatedLights.push_back(animLight);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class EditGeometry
|
|
//------------------------------------------------------------------------------
|
|
|
|
void EditGeometry::computeLightmaps(const bool alarmMode)
|
|
{
|
|
// clear the current surfaces - low detail just white's out the lightmaps
|
|
ColorF ambientF = alarmMode ? mWorldEntity->mEmergencyAmbientColor :
|
|
mWorldEntity->mAmbientColor;
|
|
ColorI ambientI(ambientF.red * 255.f, ambientF.green * 255.f, ambientF.blue * 255.f);
|
|
|
|
for(U32 i = 0; i < mSurfaces.size(); i++)
|
|
{
|
|
Surface & surface = mSurfaces[i];
|
|
if(!alarmMode)
|
|
fillInLightmapInfo(surface);
|
|
|
|
surface.pLMap = alarmMode ? surface.pAlarmLMap : surface.pNormalLMap;
|
|
|
|
//
|
|
if(surface.pLMap)
|
|
{
|
|
if(gBuildAsLowDetail)
|
|
{
|
|
for(U32 y = 0; y < surface.pLMap->getHeight(); y++)
|
|
dMemset(surface.pLMap->getAddress(0, y), 0xff, (surface.pLMap->getWidth() *
|
|
surface.pLMap->bytesPerPixel));
|
|
}
|
|
else if(!(surface.flags & Interior::SurfaceOutsideVisible))
|
|
{
|
|
// fill with ambient
|
|
for(U32 y = 0; y < surface.pLMap->getHeight(); y++)
|
|
for(U32 x = 0; x < surface.pLMap->getWidth(); x++)
|
|
{
|
|
U8 * pPixel = surface.pLMap->getAddress(x,y);
|
|
*(pPixel++) = ambientI.red;
|
|
*(pPixel++) = ambientI.green;
|
|
*(pPixel) = ambientI.blue;
|
|
}
|
|
}
|
|
else // clear out the outside lightmaps
|
|
{
|
|
for(U32 y = 0; y < surface.pLMap->getHeight(); y++)
|
|
dMemset(surface.pLMap->getAddress(0, y), 0, (surface.pLMap->getWidth() *
|
|
surface.pLMap->bytesPerPixel));
|
|
}
|
|
|
|
sgIlluminateSurface(surface);
|
|
}
|
|
}
|
|
|
|
if(gBuildAsLowDetail)
|
|
return;
|
|
|
|
Lighting * lighting = new Lighting;
|
|
gWorkingLighting = lighting;
|
|
gWorkingLighting->mAmbientColor = ambientF;
|
|
|
|
//
|
|
lighting->grabLights(alarmMode);
|
|
|
|
//
|
|
if(alarmMode == true)
|
|
{
|
|
if((lighting->mLights.size() != 0))
|
|
mHasAlarmState = true;
|
|
else if((mWorldEntity->mEmergencyAmbientColor != ColorF(0,0,0)) &&
|
|
(mWorldEntity->mEmergencyAmbientColor != mWorldEntity->mAmbientColor))
|
|
{
|
|
delete lighting;
|
|
mHasAlarmState = true;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
delete lighting;
|
|
mHasAlarmState = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
lighting->grabSurfaces();
|
|
|
|
lighting->processSurfaces();
|
|
lighting->createShadowVolumes();
|
|
lighting->processEmitterBSPs();
|
|
lighting->lightSurfaces();
|
|
lighting->processAnimatedLights();
|
|
|
|
if(lighting->mNumAmbiguousPlanes)
|
|
dPrintf("\n * Number of ambiguous planes (dropped): %d\n", lighting->mNumAmbiguousPlanes);
|
|
|
|
//
|
|
delete lighting;
|
|
}
|
|
|
|
|