tge/engine/lightingSystem/sgSceneLightingInterior.cc
2017-04-17 06:17:10 -06:00

958 lines
30 KiB
C++
Executable File

//-----------------------------------------------
// Synapse Gaming - Lighting System
// Copyright © Synapse Gaming 2003
// Written by John Kabus
//-----------------------------------------------
#include "editor/editTSCtrl.h"
#include "editor/worldEditor.h"
#include "game/shadow.h"
#include "game/vehicles/wheeledVehicle.h"
#include "game/gameConnection.h"
#include "sceneGraph/sceneGraph.h"
#include "terrain/terrRender.h"
#include "game/shapeBase.h"
#include "gui/core/guiCanvas.h"
#include "ts/tsShape.h"
#include "ts/tsShapeInstance.h"
#include "game/staticShape.h"
#include "game/tsStatic.h"
#include "collision/concretePolyList.h"
#include "lightingSystem/sgSceneLighting.h"
#include "lightingSystem/sgLightMap.h"
#include "lightingSystem/sgSceneLightingGlobals.h"
#include "lightingSystem/sgLightingModel.h"
/// adds the ability to bake point lights into interior light maps.
void SceneLighting::InteriorProxy::sgAddLight(LightInfo *light, InteriorInstance *interior)
{
elapsedTime time = elapsedTime("SceneLighting::InteriorProxy::sgAddLight (%d)");
// need this...
sgInterior = interior;
sgLightingModel &model = sgLightingModelManager::sgGetLightingModel(
light->sgLightingModelName);
model.sgSetState(light);
// test for early out...
if(!model.sgCanIlluminate(interior->getWorldBox()))
{
model.sgResetState();
return;
}
model.sgResetState();
sgLights.push_back(light);
// on first light build surface list...
if(sgLights.size() == 1)
{
// stats...
sgStatistics::sgInteriorObjectIncludedCount++;
// get the global shadow casters...
sgShadowObjects::sgGetObjects(interior);
sgClearSurfaces();
sgCurrentSurfaceIndex = 0;
InteriorResource *res = sgInterior->getResource();
U32 countd = res->getNumDetailLevels();
for(U32 d=0; d<countd; d++)
{
Interior *detail = res->getDetailLevel(d);
U32 counti = detail->getSurfaceCount();
U32 offset = sgSurfaces.size();
sgSurfaces.increment(counti);
for(U32 i=0; i<counti; i++)
{
sgSurfaceInfo *info = new sgSurfaceInfo();
sgSurfaces[i + offset] = info;
sgConvertInteriorSurfaceToSurfaceInfo(detail->getSurface(i), i, detail, *info);
}
U32 countsm = detail->getStaticMeshCount();
for(U32 sm=0; sm<countsm; sm++)
{
const ConstructorSimpleMesh *mesh = detail->getStaticMesh(sm);
if(!mesh)
continue;
counti = mesh->primitives.size();
offset = sgSurfaces.size();
sgSurfaces.increment(counti);
for(U32 i=0; i<counti; i++)
{
sgSurfaceInfo *info = new sgSurfaceInfo();
sgSurfaces[i + offset] = info;
sgConvertStaticMeshPrimitiveToSurfaceInfo(mesh, i, detail, *info);
}
}
}
}
// recalc number of surfaces per pass based on new light count...
sgSurfacesPerPass = sgSurfaces.size() / sgLights.size();
}
void SceneLighting::InteriorProxy::light(LightInfo *light)
{
elapsedTime time = elapsedTime("SceneLighting::InteriorProxy::light (%d)");
U32 i;
U32 countthispass = 0;
// stats...
sgStatistics::sgInteriorObjectIlluminationCount++;
for(i=sgCurrentSurfaceIndex; i<sgSurfaces.size(); i++)
{
sgProcessSurface(*sgSurfaces[i]);
countthispass++;
sgCurrentSurfaceIndex++;
if((countthispass >= sgSurfacesPerPass) && (sgLights.last() != light))
break;
}
}
void SceneLighting::InteriorProxy::sgConvertStaticMeshPrimitiveToSurfaceInfo(const ConstructorSimpleMesh *staticmesh, U32 primitiveindex,
Interior *detail, sgSurfaceInfo &surfaceinfo)
{
const ConstructorSimpleMesh::primitive &prim = staticmesh->primitives[primitiveindex];
const MatrixF &transform = sgInterior->getTransform();
const Point3F &scale = sgInterior->getScale();
// need to generate the plane...
AssertFatal((prim.count >= 3), "Primitive with only 2 verts?!?");
Point3F p0 = staticmesh->verts[prim.start];
Point3F p1 = staticmesh->verts[prim.start+1];
Point3F p2 = staticmesh->verts[prim.start+2];
// and it needs to be in interior/object space, not static mesh space...
p0.convolve(staticmesh->scale);
p1.convolve(staticmesh->scale);
p2.convolve(staticmesh->scale);
staticmesh->transform.mulP(p0);
staticmesh->transform.mulP(p1);
staticmesh->transform.mulP(p2);
// generate the plane...
PlaneF plane = PlaneF(p0, p1, p2);
// also need a world space version...
PlaneF projPlane;
mTransformPlane(transform, scale, plane, &projPlane);
//-----------------------------
// get the generic instance 0 lm...
GBitmap *lm = gInteriorLMManager.getBitmap(detail->getLMHandle(), 0, prim.lightMapIndex);
AssertFatal((lm), "Why was there no base light map??");
sgExtractLightingInformation(detail, prim.lightMapEquationX, prim.lightMapEquationY, plane,
Point2I(prim.lightMapOffset.x, prim.lightMapOffset.y), Point2I(prim.lightMapSize.x, prim.lightMapSize.y), Point2I(lm->getWidth(), lm->getHeight()),
surfaceinfo.sgWorldPos,
surfaceinfo.sgSVector,
surfaceinfo.sgTVector,
surfaceinfo.sgLightMapOffset,
surfaceinfo.sgLightMapExtent);
surfaceinfo.sgDetail = detail;
surfaceinfo.sgSurfaceIndex = SG_NULL_SURFACE;
surfaceinfo.sgStaticMesh = (ConstructorSimpleMesh *)staticmesh;
surfaceinfo.sgSurfacePlane = projPlane;
// currently ctor export has no zones...
// when it does this needs to be fixed - also static mesh zone lighting...
surfaceinfo.sgSurfaceOutsideVisible = true;
surfaceinfo.sgLightMapIndex = prim.lightMapIndex;
surfaceinfo.sgTriStrip.clear();
surfaceinfo.sgTriStrip.increment(prim.count);
for(U32 v=0; v<prim.count; v++)
{
U32 index = prim.start + v;
surfaceinfo.sgTriStrip[v].sgVert = staticmesh->verts[index];
surfaceinfo.sgTriStrip[v].sgNorm = staticmesh->norms[index];
surfaceinfo.sgTriStrip[v].sgVert.convolve(staticmesh->scale);
staticmesh->transform.mulP(surfaceinfo.sgTriStrip[v].sgVert);
staticmesh->transform.mulV(surfaceinfo.sgTriStrip[v].sgNorm);
surfaceinfo.sgTriStrip[v].sgVert.convolve(scale);
transform.mulP(surfaceinfo.sgTriStrip[v].sgVert);
transform.mulV(surfaceinfo.sgTriStrip[v].sgNorm);
}
sgReorganizeSurface(surfaceinfo);
}
void SceneLighting::InteriorProxy::sgConvertInteriorSurfaceToSurfaceInfo(const Interior::Surface &surface, U32 i,
Interior *detail, sgSurfaceInfo &surfaceinfo)
{
// points right way?
PlaneF plane = detail->getPlane(surface.planeIndex);
if(Interior::planeIsFlipped(surface.planeIndex))
plane.neg();
const MatrixF &transform = sgInterior->getTransform();
const Point3F &scale = sgInterior->getScale();
PlaneF projPlane;
mTransformPlane(transform, scale, plane, &projPlane);
const Interior::TexGenPlanes & lmTexGenEQ = detail->getLMTexGenEQ(i);
// get the generic instance 0 lm...
U32 lmindex = detail->getNormalLMapIndex(i);
GBitmap *lm = gInteriorLMManager.getBitmap(detail->getLMHandle(), 0, lmindex);
AssertFatal((lm), "Why was there no base light map??");
sgExtractLightingInformation(detail, lmTexGenEQ.planeX, lmTexGenEQ.planeY, plane,
Point2I(surface.mapOffsetX, surface.mapOffsetY), Point2I(surface.mapSizeX, surface.mapSizeY), Point2I(lm->getWidth(), lm->getHeight()),
surfaceinfo.sgWorldPos,
surfaceinfo.sgSVector,
surfaceinfo.sgTVector,
surfaceinfo.sgLightMapOffset,
surfaceinfo.sgLightMapExtent);
surfaceinfo.sgDetail = detail;
surfaceinfo.sgSurfaceIndex = i;
surfaceinfo.sgStaticMesh = NULL;
surfaceinfo.sgSurfacePlane = projPlane;
surfaceinfo.sgSurfaceOutsideVisible = (surface.surfaceFlags & Interior::SurfaceOutsideVisible);
surfaceinfo.sgLightMapIndex = lmindex;
surfaceinfo.sgTriStrip.clear();
surfaceinfo.sgTriStrip.increment(surface.windingCount);
for(U32 v=0; v<surface.windingCount; v++)
{
Point3F &vert = surfaceinfo.sgTriStrip[v].sgVert;
Point3F &norm = surfaceinfo.sgTriStrip[v].sgNorm;
U32 index = detail->getWinding(surface.windingStart + v);
vert = detail->getPoint(index);
norm = detail->getPointNormal(i, v);
vert.convolve(scale);
transform.mulP(vert);
transform.mulV(norm);
}
sgReorganizeSurface(surfaceinfo);
}
void SceneLighting::InteriorProxy::sgReorganizeSurface(sgSurfaceInfo &surfaceinfo)
{
if(surfaceinfo.sgTriStrip.size() < 3)
return;
// create plane from tri...
Vector<sgPlanarLightMap::sgSmoothingVert> &originalstrip = surfaceinfo.sgTriStrip;
PlaneF plane = PlaneF(originalstrip[0].sgVert, originalstrip[1].sgVert, originalstrip[2].sgVert);
Point3F norm = (originalstrip[0].sgNorm + originalstrip[1].sgNorm + originalstrip[2].sgNorm) * 0.3333;
// compare to average normal for reverse winding test...
// if ok then return...
if(mDot(plane, norm) > 0.0f)
return;
// reverse windings...
U32 vertcount = originalstrip.size();
sgPlanarLightMap::sgSmoothingVert tempvert;
// pull verts in pairs and swap...
for(U32 i=2; i<vertcount; i+=2)
{
dMemcpy(&tempvert, &originalstrip[i-1], sizeof(sgPlanarLightMap::sgSmoothingVert));
dMemcpy(&originalstrip[i-1], &originalstrip[i], sizeof(sgPlanarLightMap::sgSmoothingVert));
dMemcpy(&originalstrip[i], &tempvert, sizeof(sgPlanarLightMap::sgSmoothingVert));
}
// if static mesh regenerate the plane...
if(!surfaceinfo.sgStaticMesh)
return;
surfaceinfo.sgSurfacePlane = PlaneF(originalstrip[0].sgVert, originalstrip[1].sgVert, originalstrip[2].sgVert);
}
void SceneLighting::InteriorProxy::sgExtractLightingInformation(const Interior *detail, const PlaneF &lmEqX, const PlaneF &lmEqY, const PlaneF &surfplane,
const Point2I &lmoff, const Point2I &lmext, const Point2I &lmsheetsize,
Point3D &worldpos, Point3D &vectS, Point3D &vectT, Point2I &lmoffactual, Point2I &lmextactual)
{
const MatrixF &transform = sgInterior->getTransform();
const Point3F &scale = sgInterior->getScale();
S32 xlen, ylen, xoff, yoff;
S32 lmborder = detail->getLightMapBorderSize();
xlen = lmext.x + (lmborder * 2);
ylen = lmext.y + (lmborder * 2);
xoff = lmoff.x - lmborder;
yoff = lmoff.y - lmborder;
// very important check!!!
AssertFatal((
((xoff >= 0) && ((xlen + xoff) < lmsheetsize.x)) &&
((yoff >= 0) && ((ylen + yoff) < lmsheetsize.y))), "Light map extents exceeded bitmap size!");
lmoffactual = Point2I(xoff, yoff);
lmextactual = Point2I(xlen, ylen);
const F32 * const lGenX = lmEqX;
const F32 * const lGenY = lmEqY;
AssertFatal((lGenX[0] * lGenX[1] == 0.f) &&
(lGenX[0] * lGenX[2] == 0.f) &&
(lGenX[1] * lGenX[2] == 0.f), "Bad lmTexGen!");
AssertFatal((lGenY[0] * lGenY[1] == 0.f) &&
(lGenY[0] * lGenY[2] == 0.f) &&
(lGenY[1] * lGenY[2] == 0.f), "Bad lmTexGen!");
// 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), "SceneLighting::lightInterior: bad TexGen!");
const F32 * pNormal = ((const F32*)surfplane);
Point3F start;
F32 *pStart = (F32 *)start;
// get the start point on the lightmap
F32 lumelScale = 1 / (lGenX[si] * lmsheetsize.x);
pStart[si] = (((xoff * lumelScale) / (1 / lGenX[si])) - lGenX[3]) / lGenX[si];
pStart[ti] = (((yoff * lumelScale) / (1 / lGenY[ti])) - lGenY[3]) / lGenY[ti];
pStart[axis] = ((pNormal[si] * pStart[si]) + (pNormal[ti] * pStart[ti]) + surfplane.d) / -pNormal[axis];
start.convolve(scale);
transform.mulP(start);
worldpos = Point3D(start.x, start.y, start.z);
// get the s/t vecs oriented on the surface
Point3F vS, vT;
F32 * pSVec = ((F32*)vS);
F32 * pTVec = ((F32*)vT);
// s
pSVec[si] = 1.f;
pSVec[ti] = 0.f;
F32 angle;
Point3F planeNormal = surfplane;
((F32*)planeNormal)[ti] = 0.f;
planeNormal.normalize();
angle = mAcos(mClampF(((F32*)planeNormal)[axis], -1.f, 1.f));
pSVec[axis] = (((F32*)planeNormal)[si] < 0.f) ? mTan(angle) : -mTan(angle);
// t
pTVec[ti] = 1.f;
pTVec[si] = 0.f;
planeNormal = surfplane;
((F32*)planeNormal)[si] = 0.f;
planeNormal.normalize();
angle = mAcos(mClampF(((F32*)planeNormal)[axis], -1.f, 1.f));
pTVec[axis] = (((F32*)planeNormal)[ti] < 0.f) ? mTan(angle) : -mTan(angle);
Point3D vS64 = Point3D(vS.x, vS.y, vS.z);
Point3D vT64 = Point3D(vT.x, vT.y, vT.z);
// scale the vectors
vS64 *= lumelScale;
vT64 *= lumelScale;
Point3D m0 = Point3D(transform[0], transform[1], transform[2]);
Point3D m1 = Point3D(transform[4], transform[5], transform[6]);
Point3D m2 = Point3D(transform[8], transform[9], transform[10]);
Point3D scale64 = Point3D(scale.x, scale.y, scale.z);
vectS.x = mDot(vS64, m0);
vectS.y = mDot(vS64, m1);
vectS.z = mDot(vS64, m2);
vectS.convolve(scale64);
vectT.x = mDot(vT64, m0);
vectT.y = mDot(vT64, m1);
vectT.z = mDot(vT64, m2);
vectT.convolve(scale64);
// project vecs
//transform.mulV(vectS);
//vectS.convolve(scale);
//transform.mulV(vectT);
//vectT.convolve(scale);
}
void SceneLighting::InteriorProxy::sgProcessSurface(sgSurfaceInfo &surfaceinfo)
{
sgPlanarLightMap *lightmap = new sgPlanarLightMap(surfaceinfo.sgLightMapExtent.x, surfaceinfo.sgLightMapExtent.y,
sgInterior, surfaceinfo.sgDetail, surfaceinfo.sgSurfaceIndex, surfaceinfo.sgStaticMesh, surfaceinfo.sgSurfacePlane, surfaceinfo.sgTriStrip);
lightmap->sgWorldPosition = surfaceinfo.sgWorldPos;
lightmap->sgLightMapSVector = surfaceinfo.sgSVector;
lightmap->sgLightMapTVector = surfaceinfo.sgTVector;
lightmap->sgSetupLighting();
for(U32 ii=0; ii<sgLights.size(); ii++)
{
// should we even bother?
LightInfo *light = sgLights[ii];
if(light->mType != LightInfo::Vector)
{
bool valid = false;
if(light->sgLocalAmbientAmount > SG_MIN_LEXEL_INTENSITY)
valid = true;
if(surfaceinfo.sgSurfacePlane.distToPlane(light->mPos) > 0)
valid = true;
for(U32 v=0; v<surfaceinfo.sgTriStrip.size(); v++)
{
sgPlanarLightMap::sgSmoothingVert &vert = surfaceinfo.sgTriStrip[v];
if(mDot(vert.sgNorm, (light->mPos - vert.sgVect)) > 0)
valid = true;
}
if(!valid)
continue;
}
else
{
if(!surfaceinfo.sgSurfaceOutsideVisible)
continue;
}
lightmap->sgCalculateLighting(light);
}
if(lightmap->sgIsDirty())
{
TextureHandle *normHandle = gInteriorLMManager.duplicateBaseLightmap(
surfaceinfo.sgDetail->getLMHandle(), sgInterior->getLMHandle(), surfaceinfo.sgLightMapIndex);
GBitmap *normLightmap = normHandle->getBitmap();
lightmap->sgMergeLighting(normLightmap, surfaceinfo.sgLightMapOffset.x, surfaceinfo.sgLightMapOffset.y);
}
delete lightmap;
}
void SceneLighting::addInterior(ShadowVolumeBSP * shadowVolume, InteriorProxy & interior, LightInfo * light, S32 level)
{
if(light->mType != LightInfo::Vector)
return;
ColorF ambient = light->mAmbient;
bool shadowedTree = true;
// check if just getting shadow detail
if(level == SHADOW_DETAIL)
{
shadowedTree = false;
level = interior->mInteriorRes->getNumDetailLevels() - 1;
}
Interior * detail = interior->mInteriorRes->getDetailLevel(level);
bool hasAlarm = detail->hasAlarmState();
// make sure surfaces do not get processed more than once
BitVector surfaceProcessed;
surfaceProcessed.setSize(detail->mSurfaces.size());
surfaceProcessed.clear();
bool isoutside = false;
for(U32 zone=0; zone<interior->getNumCurrZones(); zone++)
{
if(interior->getCurrZone(zone) == 0)
{
isoutside = true;
break;
}
}
if(!isoutside)
return;
for(U32 i = 0; i < detail->getNumZones(); i++)
{
Interior::Zone & zone = detail->mZones[i];
for(U32 j = 0; j < zone.surfaceCount; j++)
{
U32 surfaceIndex = detail->mZoneSurfaces[zone.surfaceStart + j];
// dont reprocess a surface
if(surfaceProcessed.test(surfaceIndex))
continue;
surfaceProcessed.set(surfaceIndex);
Interior::Surface & surface = detail->mSurfaces[surfaceIndex];
// outside visible?
if(!(surface.surfaceFlags & Interior::SurfaceOutsideVisible))
continue;
// good surface?
PlaneF plane = detail->getPlane(surface.planeIndex);
if(Interior::planeIsFlipped(surface.planeIndex))
plane.neg();
// project the plane
PlaneF projPlane;
mTransformPlane(interior->getTransform(), interior->getScale(), plane, &projPlane);
// fill with ambient? (need to do here, because surface will not be
// added to the SVBSP tree)
F32 dot = mDot(projPlane, light->mDirection);
if(dot > -gParellelVectorThresh)
continue;
ShadowVolumeBSP::SVPoly * poly = buildInteriorPoly(shadowVolume, interior, detail,
surfaceIndex, light, shadowedTree);
// insert it into the SVBSP tree
shadowVolume->insertPoly(poly);
}
}
}
//------------------------------------------------------------------------------
ShadowVolumeBSP::SVPoly * SceneLighting::buildInteriorPoly(ShadowVolumeBSP * shadowVolumeBSP,
InteriorProxy & interior, Interior * detail, U32 surfaceIndex, LightInfo * light,
bool createSurfaceInfo)
{
// transform and add the points...
const MatrixF & transform = interior->getTransform();
const VectorF & scale = interior->getScale();
const Interior::Surface & surface = detail->mSurfaces[surfaceIndex];
ShadowVolumeBSP::SVPoly * poly = shadowVolumeBSP->createPoly();
poly->mWindingCount = surface.windingCount;
// project these points
for(U32 j = 0; j < poly->mWindingCount; j++)
{
Point3F iPnt = detail->mPoints[detail->mWindings[surface.windingStart + j]].point;
Point3F tPnt;
iPnt.convolve(scale);
transform.mulP(iPnt, &tPnt);
poly->mWinding[j] = tPnt;
}
// convert from fan
U32 tmpIndices[ShadowVolumeBSP::SVPoly::MaxWinding];
Point3F fanIndices[ShadowVolumeBSP::SVPoly::MaxWinding];
tmpIndices[0] = 0;
U32 idx = 1;
U32 i;
for(i = 1; i < poly->mWindingCount; i += 2)
tmpIndices[idx++] = i;
for(i = ((poly->mWindingCount - 1) & (~0x1)); i > 0; i -= 2)
tmpIndices[idx++] = i;
idx = 0;
for(i = 0; i < poly->mWindingCount; i++)
if(surface.fanMask & (1 << i))
fanIndices[idx++] = poly->mWinding[tmpIndices[i]];
// set the data
poly->mWindingCount = idx;
for(i = 0; i < poly->mWindingCount; i++)
poly->mWinding[i] = fanIndices[i];
// flip the plane - shadow volumes face inwards
PlaneF plane = detail->getPlane(surface.planeIndex);
if(!Interior::planeIsFlipped(surface.planeIndex))
plane.neg();
// transform the plane
mTransformPlane(transform, scale, plane, &poly->mPlane);
shadowVolumeBSP->buildPolyVolume(poly, light);
// do surface info?
if(createSurfaceInfo)
{
ShadowVolumeBSP::SurfaceInfo * surfaceInfo = new ShadowVolumeBSP::SurfaceInfo;
shadowVolumeBSP->mSurfaces.push_back(surfaceInfo);
// fill it
surfaceInfo->mSurfaceIndex = surfaceIndex;
surfaceInfo->mShadowVolume = shadowVolumeBSP->getShadowVolume(poly->mShadowVolume);
// POLY and POLY node gets it too
ShadowVolumeBSP::SVNode * traverse = shadowVolumeBSP->getShadowVolume(poly->mShadowVolume);
while(traverse->mFront)
{
traverse->mSurfaceInfo = surfaceInfo;
traverse = traverse->mFront;
}
// get some info from the poly node
poly->mSurfaceInfo = traverse->mSurfaceInfo = surfaceInfo;
surfaceInfo->mPlaneIndex = traverse->mPlaneIndex;
}
return(poly);
}
SceneLighting::InteriorProxy::InteriorProxy(SceneObject * obj) :
Parent(obj)
{
mBoxShadowBSP = 0;
sgCurrentSurfaceIndex = 0;
sgSurfacesPerPass = 0;
}
SceneLighting::InteriorProxy::~InteriorProxy()
{
sgClearSurfaces();
delete mBoxShadowBSP;
}
bool SceneLighting::InteriorProxy::loadResources()
{
InteriorInstance * interior = getObject();
if(!interior)
return(false);
Resource<InteriorResource> & interiorRes = interior->getResource();
if(!bool(interiorRes))
return(false);
if(!gInteriorLMManager.loadBaseLightmaps(interiorRes->getDetailLevel(0)->getLMHandle(),
interior->getLMHandle()))
return(false);
return(true);
}
void SceneLighting::InteriorProxy::init()
{
InteriorInstance * interior = getObject();
if(!interior)
return;
// clear out the lightmaps
for(U32 i = 0; i < interior->getResource()->getNumDetailLevels(); i++)
{
Interior * detail = interior->getResource()->getDetailLevel(i);
gInteriorLMManager.clearLightmaps(detail->getLMHandle(), interior->getLMHandle());
}
}
/// reroutes InteriorProxy::preLight for point light and TSStatic support.
bool SceneLighting::InteriorProxy::preLight(LightInfo * light)
{
// create shadow volume of the bounding box of this object
InteriorInstance * interior = getObject();
if(!interior)
return(false);
if(!sgRelightFilter::sgAllowLighting(interior->getWorldBox(), false))
return false;
// build light list...
sgAddLight(light, interior);
return(true);
}
bool SceneLighting::InteriorProxy::isShadowedBy(InteriorProxy * test)
{
// add if overlapping world box
if((*this)->getWorldBox().isOverlapped((*test)->getWorldBox()))
return(true);
// test the box shadow volume
for(U32 i = 0; i < mLitBoxSurfaces.size(); i++)
{
ShadowVolumeBSP::SVPoly * poly = mBoxShadowBSP->copyPoly(mLitBoxSurfaces[i]);
if(test->mBoxShadowBSP->testPoly(poly))
return(true);
}
return(false);
}
void SceneLighting::InteriorProxy::postLight(bool lastLight)
{
delete mBoxShadowBSP;
mBoxShadowBSP = 0;
InteriorInstance * interior = getObject();
if(!interior)
return;
// only rebuild the vertex colors after the last light
if(lastLight)
interior->rebuildVertexColors();
}
//------------------------------------------------------------------------------
U32 SceneLighting::InteriorProxy::getResourceCRC()
{
InteriorInstance * interior = getObject();
if(!interior)
return(0);
return(interior->getCRC());
}
//------------------------------------------------------------------------------
bool SceneLighting::InteriorProxy::setPersistInfo(PersistInfo::PersistChunk * info)
{
if(!Parent::setPersistInfo(info))
return(false);
PersistInfo::InteriorChunk * chunk = dynamic_cast<PersistInfo::InteriorChunk*>(info);
AssertFatal(chunk, "SceneLighting::InteriorProxy::setPersistInfo: invalid info chunk!");
InteriorInstance * interior = getObject();
if(!interior)
return(false);
U32 numDetails = interior->getNumDetailLevels();
// check the lighting method
AssertFatal(SceneLighting::smUseVertexLighting == Interior::smUseVertexLighting, "SceneLighting::InteriorProxy::setPersistInfo: invalid vertex lighting state");
if(SceneLighting::smUseVertexLighting != Interior::smUseVertexLighting)
return(false);
// process the vertex lighting...
if(chunk->mDetailVertexCount.size() != numDetails)
return(false);
AssertFatal(chunk->mVertexColorsNormal.size(), "SceneLighting::InteriorProxy::setPersistInfo: invalid chunk info");
AssertFatal(!chunk->mHasAlarmState || chunk->mVertexColorsAlarm.size(), "SceneLighting::InteriorProxy::setPersistInfo: invalid chunk info");
AssertFatal(!(chunk->mHasAlarmState ^ interior->getDetailLevel(0)->hasAlarmState()), "SceneLighting::InteriorProxy::setPersistInfo: invalid chunk info");
U32 curPos = 0;
for(U32 i = 0; i < numDetails; i++)
{
U32 count = chunk->mDetailVertexCount[i];
Vector<ColorI>* normal = interior->getVertexColorsNormal(i);
Vector<ColorI>* alarm = interior->getVertexColorsAlarm(i);
AssertFatal(normal != NULL && alarm != NULL, "Error, bad vectors returned!");
normal->setSize(count);
dMemcpy(normal->address(), &chunk->mVertexColorsNormal[curPos], count * sizeof(ColorI));
if(chunk->mHasAlarmState)
{
alarm->setSize(count);
dMemcpy(alarm->address(), &chunk->mVertexColorsAlarm[curPos], count * sizeof(ColorI));
}
curPos += count;
}
// need lightmaps?
if(!SceneLighting::smUseVertexLighting)
{
if(chunk->mDetailLightmapCount.size() != numDetails)
return(false);
LM_HANDLE instanceHandle = interior->getLMHandle();
U32 idx = 0;
for(U32 i = 0; i < numDetails; i++)
{
Interior * detail = interior->getDetailLevel(i);
LM_HANDLE interiorHandle = detail->getLMHandle();
Vector<TextureHandle *> & baseHandles = gInteriorLMManager.getHandles(interiorHandle, 0);
if(chunk->mDetailLightmapCount[i] > baseHandles.size())
return(false);
for(U32 j = 0; j < chunk->mDetailLightmapCount[i]; j++)
{
U32 baseIndex = chunk->mDetailLightmapIndices[idx];
if(baseIndex >= baseHandles.size())
return(false);
AssertFatal(chunk->mLightmaps[idx], "SceneLighting::InteriorProxy::setPersistInfo: bunk bitmap!");
if(chunk->mLightmaps[idx]->getWidth() != baseHandles[baseIndex]->getWidth() ||
chunk->mLightmaps[idx]->getHeight() != baseHandles[baseIndex]->getHeight())
return(false);
TextureHandle *tHandle = gInteriorLMManager.duplicateBaseLightmap(interiorHandle, instanceHandle, baseIndex);
// create the diff bitmap
U8 * pDiff = chunk->mLightmaps[idx]->getAddress(0,0);
U8 * pBase = baseHandles[baseIndex]->getBitmap()->getAddress(0,0);
U8 * pDest = tHandle->getBitmap()->getAddress(0,0);
Point2I extent(tHandle->getWidth(), tHandle->getHeight());
for(U32 y = 0; y < extent.y; y++)
{
for(U32 x = 0; x < extent.x; x++)
{
*pDest++ = *pBase++ + *pDiff++;
*pDest++ = *pBase++ + *pDiff++;
*pDest++ = *pBase++ + *pDiff++;
}
}
idx++;
}
}
}
return(true);
}
bool SceneLighting::InteriorProxy::getPersistInfo(PersistInfo::PersistChunk * info)
{
if(!Parent::getPersistInfo(info))
return(false);
PersistInfo::InteriorChunk * chunk = dynamic_cast<PersistInfo::InteriorChunk*>(info);
AssertFatal(chunk, "SceneLighting::InteriorProxy::getPersistInfo: invalid info chunk!");
InteriorInstance * interior = getObject();
if(!interior)
return(false);
LM_HANDLE instanceHandle = interior->getLMHandle();
AssertFatal(!chunk->mDetailLightmapCount.size(), "SceneLighting::InteriorProxy::getPersistInfo: invalid array!");
AssertFatal(!chunk->mDetailLightmapIndices.size(), "SceneLighting::InteriorProxy::getPersistInfo: invalid array!");
AssertFatal(!chunk->mLightmaps.size(), "SceneLighting::InteriorProxy::getPersistInfo: invalid array!");
U32 numDetails = interior->getNumDetailLevels();
U32 i;
for(i = 0; i < numDetails; i++)
{
Interior * detail = interior->getDetailLevel(i);
LM_HANDLE interiorHandle = detail->getLMHandle();
Vector<TextureHandle*> & baseHandles = gInteriorLMManager.getHandles(interiorHandle, 0);
Vector<TextureHandle*> & instanceHandles = gInteriorLMManager.getHandles(interiorHandle, instanceHandle);
U32 litCount = 0;
// walk all the instance lightmaps and grab diff lighting from them
for(U32 j = 0; j < instanceHandles.size(); j++)
{
if(!instanceHandles[j])
continue;
litCount++;
chunk->mDetailLightmapIndices.push_back(j);
GBitmap * baseBitmap = baseHandles[j]->getBitmap();
GBitmap * instanceBitmap = instanceHandles[j]->getBitmap();
Point2I extent(baseBitmap->getWidth(), baseBitmap->getHeight());
GBitmap * diffLightmap = new GBitmap(extent.x, extent.y, false, GBitmap::RGB);
U8 * pBase = baseBitmap->getAddress(0,0);
U8 * pInstance = instanceBitmap->getAddress(0,0);
U8 * pDest = diffLightmap->getAddress(0,0);
// fill the diff lightmap
for(U32 y = 0; y < extent.y; y++)
for(U32 x = 0; x < extent.x; x++)
{
*pDest++ = *pInstance++ - *pBase++;
*pDest++ = *pInstance++ - *pBase++;
*pDest++ = *pInstance++ - *pBase++;
}
chunk->mLightmaps.push_back(diffLightmap);
}
chunk->mDetailLightmapCount.push_back(litCount);
}
// process the vertex lighting...
AssertFatal(!chunk->mDetailVertexCount.size(), "SceneLighting::InteriorProxy::getPersistInfo: invalid chunk info");
AssertFatal(!chunk->mVertexColorsNormal.size(), "SceneLighting::InteriorProxy::getPersistInfo: invalid chunk info");
AssertFatal(!chunk->mVertexColorsAlarm.size(), "SceneLighting::InteriorProxy::getPersistInfo: invalid chunk info");
chunk->mHasAlarmState = interior->getDetailLevel(0)->hasAlarmState();
chunk->mDetailVertexCount.setSize(numDetails);
U32 size = 0;
for(i = 0; i < numDetails; i++)
{
Interior * detail = interior->getDetailLevel(i);
U32 count = detail->getWindingCount();
chunk->mDetailVertexCount[i] = count;
size += count;
}
chunk->mVertexColorsNormal.setSize(size);
if(chunk->mHasAlarmState)
chunk->mVertexColorsAlarm.setSize(size);
U32 curPos = 0;
for(i = 0; i < numDetails; i++)
{
Vector<ColorI>* normal = interior->getVertexColorsNormal(i);
Vector<ColorI>* alarm = interior->getVertexColorsAlarm(i);
AssertFatal(normal != NULL && alarm != NULL, "Error, no normal or alarm vertex colors!");
U32 count = chunk->mDetailVertexCount[i];
dMemcpy(&chunk->mVertexColorsNormal[curPos], normal->address(), count * sizeof(ColorI));
if(chunk->mHasAlarmState)
dMemcpy(&chunk->mVertexColorsAlarm[curPos], alarm->address(), count * sizeof(ColorI));
curPos += count;
}
return(true);
}