958 lines
30 KiB
C++
Executable File
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);
|
|
} |