//----------------------------------------------- // 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; dgetDetailLevel(d); U32 counti = detail->getSurfaceCount(); U32 offset = sgSurfaces.size(); sgSurfaces.increment(counti); for(U32 i=0; igetSurface(i), i, detail, *info); } U32 countsm = detail->getStaticMeshCount(); for(U32 sm=0; smgetStaticMesh(sm); if(!mesh) continue; counti = mesh->primitives.size(); offset = sgSurfaces.size(); sgSurfaces.increment(counti); for(U32 i=0; i= 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; vverts[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; vgetWinding(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 &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; igetTransform(); 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; iimType != 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; vmPos - 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; zonegetNumCurrZones(); 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 & 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(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* normal = interior->getVertexColorsNormal(i); Vector* 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 & 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(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 & baseHandles = gInteriorLMManager.getHandles(interiorHandle, 0); Vector & 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* normal = interior->getVertexColorsNormal(i); Vector* 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); }