📄 radgen.cpp
字号:
geom::Color3 ae = totalAbsorbedEnergy();
geom::Color3 ee = totalEscapedEnergy();
float initial = ie.r() + ie.g() + ie.b();
float absorbed = ae.r() + ae.g() + ae.b();
float escaped = ee.r() + ee.g() + ee.b();
float remaining = initial - absorbed - escaped;
float percentThisPass = energyThisPass() / remaining * 100;
float percentEnergy = absorbed / (initial - escaped) * 100;
prog.setCurrentPercent(percentEnergy);
prog.setLabel1("Iterations:");
if (maxIterations())
{
prog.setText1(fstl::string(iterationsProcessed()) + " of " + fstl::string(maxIterationsCount()));
}
else
{
prog.setText1(fstl::string(iterationsProcessed()));
}
prog.setLabel2("Initial (abs/esc) Energy:");
char disp[/*SK:MAX_PATH*/256];
if (lightsToProcess())
{
sprintf(disp, "[processing lights]");
}
else
{
sprintf(disp, "%u (%.2f%% / %.2f%%)", static_cast<unsigned int>(initial * displayMultiplier), absorbed/initial*100, escaped/initial*100);
}
prog.setText2(disp);
prog.setLabel3("Energy remaining:");
prog.setText3(fstl::string(static_cast<unsigned int>(remaining * displayMultiplier)));
prog.setLabel4("Convergence target:");
if (checkConvergence && !directLightOnly())
{
sprintf(disp, "%u (%.5f%%)", convergence(), static_cast<float>(convergence()) / (energyThisPass() * displayMultiplier) * 100);
prog.setText4(disp);
}
else
{
if (directLightOnly()) prog.setText4("N/A (direct light only)");
else prog.setText4("N/A");
}
prog.setLabel5("Energy shot this pass:");
sprintf(disp, "%.2f", energyThisPass() * displayMultiplier);
prog.setText5(disp);
prog.setLabel6("Form factor calculation method:");
if (lightsToProcess())
{
prog.setText6("Point light estimation");
}
else
{
if (useNusselt()) prog.setText6("Nusselt's Analogy");
else prog.setText6("Standard estimation");
}
prog.setLabel7("Input file:");
fstl::string ifn = inputFilename();
int idx = ifn.rfind("\\");
if (idx != -1) ifn = ifn.substring(idx+1);
prog.setText7(ifn);
if (adaptivePatchSubdivision())
{
prog.setLabel8("Adaptive subdivision threshold:");
sprintf(disp, "%u (%.3f%% there)", adaptiveThreshold(), adaptiveThreshold() / (energyThisPass() * displayMultiplier) * 100);
prog.setText8(disp);
}
else
{
prog.setLabel8("");
prog.setText8("");
}
prog.setLabel9("Total patches:");
prog.setText9(fstl::string(totalPatches()) + " (cur: " + fstl::string(subdivisionU()) + "x" + fstl::string(subdivisionV()) + ", max: " + fstl::string(adaptiveMaxSubdivisionU()) + "x" + fstl::string(adaptiveMaxSubdivisionV()) + ")");
prog.setLabel10("Total elements:");
prog.setText10(fstl::string(totalElements()));
// If they've reached convergence, tell them to stop
//
// Note that we don't check for convergence when told, and when there are lights to process, because we want at least
// all light sources processed, as a minimum.
if (!lightsToProcess() && checkConvergence && (energyThisPass() * displayMultiplier) <= convergence()) return false;
// Or tell them to stop when the user requests it...
return !prog.stopRequested();
}
// ---------------------------------------------------------------------------------------------------------------------------------
void RadGen::distributeInitialEnergy()
{
progress()->setCurrentStatus("Slicing up geometry into patches/elements");
unsigned int idx = 0;
for (RadPrimList::node *i = geometry().polys().head(); i; i = i->next(), ++idx)
{
if (!(idx&0xf))
{
progress()->setCurrentPercent(static_cast<float>(idx) / static_cast<float>(geometry().polys().size()) * 100.0f);
if (progress()->stopRequested()) throw "";
}
RadPrim & prim = i->data();
prim.originalPrimitive() = &prim;
prim.prepare(subdivisionU(), subdivisionV());
if (prim.illuminationColor() != geom::Color3(0,0,0))
{
int minU, maxU, minV, maxV;
prim.calcIntegerUVExtents(minU, maxU, minV, maxV);
RadLMap & lm = geometry().lightmaps()[prim.textureID()];
float totalArea = (prim.uXFormVector() % prim.vXFormVector()).length();
float * areas = &prim.elementAreas()[0];
for (unsigned int v = minV; v <= static_cast<unsigned int>(maxV); ++v)
{
for (unsigned int u = minU; u <= static_cast<unsigned int>(maxU); ++u, ++areas)
{
if (*areas)
{
float partialArea = *areas / totalArea;
lm.data()[v*lm.width()+u] = prim.illuminationColor() * static_cast<float>(areaLightMultiplier()) * partialArea;
}
}
}
}
}
}
// ---------------------------------------------------------------------------------------------------------------------------------
void RadGen::countPatchesAndElements()
{
progress()->setCurrentStatus("Counting patches and elements");
totalPatches() = 0;
totalElements() = 0;
unsigned int idx = 0;
for (RadPrimList::node *i = geometry().polys().head(); i; i = i->next(), ++idx)
{
if (!(idx&0xf))
{
progress()->setCurrentPercent(static_cast<float>(idx) / static_cast<float>(geometry().polys().size()) * 100.0f);
if (progress()->stopRequested()) throw "";
}
RadPrim & prim = i->data();
// Count patches
for (unsigned int j = 0; j < prim.patches().size(); ++j)
{
if (prim.patches()[j].area()) totalPatches()++;
}
for (unsigned int k = 0; k < prim.elementAreas().size(); ++k)
{
if (prim.elementAreas()[k]) totalElements()++;
}
}
}
// ---------------------------------------------------------------------------------------------------------------------------------
geom::Color3 RadGen::calcAmbientTerm()
{
progress()->setCurrentStatus("Calculating the ambient term");
// We'll need the total area and total average reflectivity
float totalArea = 0;
geom::Color3 pAve(0,0,0);
{
for (RadPrimList::node *i = geometry().polys().head(); i; i = i->next())
{
RadPrim & p = i->data();
float area = p.calcArea();
totalArea += area;
pAve += p.reflectanceColor() * area;
}
}
// Calculate the ambient term
pAve /= totalArea;
geom::Color3 totalUnshotEnergy = (initialEnergy() - totalAbsorbedEnergy() - totalEscapedEnergy());
geom::Color3 estimatedAmbientTerm = pAve * totalUnshotEnergy / totalArea;
unsigned int idx = 0;
for (RadPrimList::node *i = geometry().polys().head(); i; i = i->next(), ++idx)
{
if (!(idx&0xf))
{
progress()->setCurrentPercent(static_cast<float>(idx) / static_cast<float>(geometry().polys().size()) * 100.0f);
if (progress()->stopRequested()) throw "";
}
RadPrim & p = i->data();
RadLMap & lm = geometry().lightmaps()[p.textureID()];
int minU, maxU, minV, maxV;
p.calcIntegerUVExtents(minU, maxU, minV, maxV);
float totalArea = (p.uXFormVector() % p.vXFormVector()).length();
// Remove softened edges around polygons
{
int fIndex = 0;
for (unsigned int v = minV; v <= static_cast<unsigned int>(maxV); ++v)
{
for (unsigned int u = minU; u <= static_cast<unsigned int>(maxU); ++u, ++fIndex)
{
// Area
float eArea = p.elementAreas()[fIndex];
// Skip unused texels
if (eArea <= 0) continue;
// Add the ambient term
lm.data()[v*lm.width()+u] += estimatedAmbientTerm * p.reflectanceColor() * eArea * static_cast<float>(areaLightMultiplier());
}
}
}
}
return estimatedAmbientTerm;
}
// ---------------------------------------------------------------------------------------------------------------------------------
void RadGen::expandEdges()
{
progress()->setCurrentStatus("Correcting edges in the lightmaps");
// Duplicate the lightmaps for mask purposes
// PDNDEBUG -- maskMaps is a bad name.. and using lightmaps is wasteful... could be just an array of float arrays...
RadLMapArray maskMaps = geometry().lightmaps();
// Clear out the maskmaps
{
geom::Color3 blank(0,0,0);
for (unsigned int i = 0; i < maskMaps.size(); ++i)
{
RadLMap & mm = maskMaps[i];
unsigned int size = mm.width() * mm.height();
geom::Color3 * mPtr = &mm.data()[0];
for (unsigned int i = 0; i < size; ++i, ++mPtr)
{
*mPtr = blank;
}
}
}
// PDNDEBUG - hack
float totalArea = 0;
// Fill the mask with element areas (i.e. this is essentially the antialiasing information)
{
unsigned int idx = 0;
for (RadPrimList::node *i = geometry().polys().head(); i; i = i->next(), ++idx)
{
if (!(idx&0xf))
{
progress()->setCurrentPercent(static_cast<float>(idx) / static_cast<float>(geometry().polys().size()) * 50.0f);
if (progress()->stopRequested()) throw "";
}
RadPrim & p = i->data();
RadLMap & mm = maskMaps[p.textureID()];
// PDNDEBUG - hack -- can we replace all calculations of "total area" with a global value, now that we're creating our own lightmaps?
totalArea = (p.uXFormVector() % p.vXFormVector()).length();
int minU, maxU, minV, maxV;
p.calcIntegerUVExtents(minU, maxU, minV, maxV);
// We'll go through and place the element areas where polygons occupy the lightmaps
{
unsigned int fIndex = 0;
for (unsigned int v = minV; v <= static_cast<unsigned int>(maxV); ++v)
{
unsigned int vIndex = v * mm.width();
for (unsigned int u = minU; u <= static_cast<unsigned int>(maxU); ++u, ++fIndex)
{
// If this texel is not occupied, skip it
if (!p.elementAreas()[fIndex]) continue;
// ADD the texel area into the red component of the lightmap for this texel
//
// This allows us to know the total usage of a texel from a series of possibly
// connected polygons
mm.data()[vIndex + u].r() += p.elementAreas()[fIndex];
}
}
}
}
}
// Un-antialias the lightmaps
{
for (unsigned int i = 0; i < maskMaps.size(); ++i)
{
RadLMap & mm = maskMaps[i];
RadLMap & lm = geometry().lightmaps()[i];
geom::Color3 * mPtr = &mm.data()[0];
geom::Color3 * cPtr = &lm.data()[0];
unsigned int size = mm.width() * mm.height();
for (unsigned int i = 0; i < size; ++i, ++mPtr, ++cPtr)
{
// Skip unoccupied or wholly occupied pixels
if (mPtr->r() == 0 || mPtr->r() >= totalArea) continue;
// Adjust lightmap texel -- this is effectively un-antialiasing the texel
*cPtr *= totalArea / mPtr->r();
}
}
}
// Do the filtering...
{
for (unsigned int i = 0; i < geometry().lightmaps().size(); ++i)
{
if (!(i&0xf))
{
progress()->setCurrentPercent(static_cast<float>(i) / static_cast<float>(geometry().polys().size()) * 50.0f + 50.0f);
if (progress()->stopRequested()) throw "";
}
// Lightmap & mask
RadLMap & lm = geometry().lightmaps()[i];
RadLMap & mm = maskMaps[i];
unsigned int lw = lm.width();
unsigned int lh = lm.height();
// Here we do our pixel extension... it works like this:
//
// Each pixel that does _not_ contain any mask area, will be the averge of its neighbors that _do_ have
// a mask area (if any).
{
for (unsigned int v = 0; v < lh; ++v)
{
// Current row for the [M]ask and [C]olor maps (for convenience)...
geom::Color3 * cmRow = &mm.data()[v*lw];
geom::Color3 * ccRow = &lm.data()[v*lw];
// Top row for the [M]ask and [C]olor maps (for convenience)...
geom::Color3 * tmRow = static_cast<geom::Color3 *>(0);
geom::Color3 * tcRow = static_cast<geom::Color3 *>(0);
if (v > 0)
{
tmRow = cmRow - lw;
tcRow = ccRow - lw;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -