tge/tools/map2difPlus/createLightmaps.cc
2017-04-17 06:17:10 -06:00

2479 lines
80 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "map2difPlus/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(&currentStore, 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.whichSideLow(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.whichSideLow(rStartPoint);
PlaneSide eSide = rClipPlane.whichSideLow(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.whichSideLow(intersection) == PlaneOn,
"Lighting::SVNode::clipWindingToPlaneFront: error in computing intersection");
finalIndices[numFinalIndices++] = insertPoint(intersection);
U32 endSeek = (end + 1) % mWinding->mNumIndices;
while (rClipPlane.whichSideLow(getPoint(mWinding->mIndices[endSeek])) == PlaneBack)
endSeek = (endSeek + 1) % mWinding->mNumIndices;
PlaneSide esSide = rClipPlane.whichSideLow(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.whichSideLow(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.whichSideLow(getPoint(mWinding->mIndices[endSeek])) == PlaneBack)
endSeek = (endSeek + 1) % mWinding->mNumIndices;
PlaneSide esSide = rClipPlane.whichSideLow(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.whichSideLow(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.whichSideLow(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;
}