You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1630 lines
46 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//#define MF_NEW_RGC
#include "hsTypes.h"
#include "Max.h"
#include "dummy.h"
#include "notify.h"
#include "plLightMapGen.h"
#include "plGImage/plMipmap.h"
#include "MaxMain/plMaxNode.h"
#include "MaxExport/plErrorMsg.h"
#include "plRenderGlobalContext.h"
#include "plMaxLightContext.h"
#include "plSurface/plLayer.h"
#include "plSurface/hsGMaterial.h"
#include "MaxMain/plPluginResManager.h"
#include "plDrawable/plGeometrySpan.h"
#include "hsFastMath.h"
#include "hsControlConverter.h"
#include "plBitmapCreator.h"
#include "pnKeyedObject/plKey.h"
#include "plResMgr/plKeyFinder.h"
#include "plResMgr/plPageInfo.h"
#include "plMessage/plLayRefMsg.h"
#include "plMessage/plMatRefMsg.h"
#include "MaxComponent/plLightMapComponent.h"
#include "plGImage/hsCodecManager.h"
#include "plAgeDescription/plAgeDescription.h"
static plLightMapGen theLMG;
static const float kBlurMapRange = 20.f;
#ifdef MF_NEW_RGC
void getRGC(void* param, NotifyInfo* info)
{
if( info->intcode == NOTIFY_PRE_RENDERFRAME )
{
plLightMapGen* lmg = (plLightMapGen*)param;
RenderGlobalContext* rgc = (RenderGlobalContext*)info->callParam;
lmg->SetRGC(rgc);
}
}
#endif // MF_NEW_RGC
#ifndef MF_NEW_RGC
#define MF_NO_RAY_SHADOW
#endif // MF_NEW_RGC
#define MF_NO_SHADOW_BLUR
#if defined(MF_NEW_RGC) && !defined(MF_NO_SHADOW_BLUR)
#define MF_NO_SHADOW_BLUR
#endif // MF_NEW_RGC
class LMGScanPoint
{
public:
hsScalar fU;
hsPoint3 fBary;
};
class LMGScanlineData
{
public:
LMGScanlineData() : fEmpty(true) {}
hsBool fEmpty;
LMGScanPoint fNear;
LMGScanPoint fFar;
};
static int kDefaultSize = 64;
static UInt32 MakeUInt32Color(float r, float g, float b, float a)
{
return (UInt32(a * 255.9f) << 24)
|(UInt32(r * 255.9f) << 16)
|(UInt32(g * 255.9f) << 8)
|(UInt32(b * 255.9f) << 0);
}
plLightMapGen& plLightMapGen::Instance()
{
return theLMG;
}
#ifdef MF_NEW_RGC
// Don't call this ever ever ever. I mean really. Never.
void plLightMapGen::SetRGC(RenderGlobalContext* rgc)
{
fRGC = rgc;
}
#endif // MF_NEW_RGC
plLightMapGen::plLightMapGen()
: fWidth(64),
fHeight(64),
fScale(1.f),
fUVWSrc(-1),
fMapRange(-1.f),
fInterface(nil),
fRenderer(nil),
fRecalcLightMaps(true),
fRGC(nil),
fRP(nil)
{
fWidth = kDefaultSize;
fHeight = kDefaultSize;
}
plLightMapGen::~plLightMapGen()
{
Close();
}
// Set up the structures we'll need to compute the lighting.
// You could turn off shadows by commenting out the call to
// MakeRenderInstances, since those are the guys that will
// cast shadows. Modify which lights contribute by changing
// the criteria in IFindLightsRecur.
hsBool plLightMapGen::Open(Interface* ip, TimeValue t, bool forceRegen)
{
if( !fInterface && ip )
{
fInterface = ip;
fTime = t;
fRP = TRACKED_NEW RendParams;
fRP->SetRenderElementMgr(fInterface->GetRenderElementMgr(RS_Production));
#ifdef MF_NEW_RGC
RegisterNotification(
getRGC,
this,
NOTIFY_PRE_RENDERFRAME
);
fRenderer = (Renderer*)CreateInstance(RENDERER_CLASS_ID, Class_ID(SREND_CLASS_ID,0));
ViewParams vp;
vp.prevAffineTM = Matrix3(true);
vp.affineTM = Matrix3(true);
vp.projType = PROJ_PERSPECTIVE;
vp.hither = 1.f;
vp.yon = 30.f;
vp.distance = 1.f;
vp.zoom = 1.f;
vp.fov = hsScalarPI / 4.f;
vp.nearRange = 1.f;
vp.farRange = 30.f;
fRenderer->Open(fInterface->GetRootNode(),
nil,
&vp,
*fRP,
fInterface->GetMAXHWnd());
FrameRendParams frp;
frp.ambient.Black();
frp.background.Black();
frp.globalLightLevel.Black();
frp.frameDuration = 1.f;
frp.relSubFrameDuration = 1.f;
frp.regxmin = 0;
frp.regxmax = 1;
frp.regymin = 0;
frp.regymax = 1;
frp.blowupCenter = Point2(0.5f,0.5f);
frp.blowupFactor = Point2(1.f, 1.f);
BitmapInfo bminfo;
bminfo.SetType(BMM_TRUE_32);
bminfo.SetWidth(2);
bminfo.SetHeight(2);
bminfo.SetCustWidth(1);
bminfo.SetCustHeight(1);
if( !bminfo.Validate() )
{
// oops!
return false;
}
Bitmap* tobm = TheManager->Create(&bminfo);
fRenderer->Render(fTime,
tobm,
frp,
fInterface->GetMAXHWnd()
);
tobm->DeleteThis();
#else MF_NEW_RGC
fRGC = TRACKED_NEW plRenderGlobalContext(fInterface, fTime);
fRGC->MakeRenderInstances((plMaxNode*)fInterface->GetRootNode(), fTime);
#endif // MF_NEW_RGC
fPreppedMipmaps.SetCount(0);
fCreatedLayers.SetCount(0);
fNewMaps.SetCount(0);
fAllLights.SetCount(0);
fActiveLights.SetCount(0);
IFindLightsRecur((plMaxNode*)fInterface->GetRootNode());
}
fRecalcLightMaps = forceRegen;
return fAllLights.GetCount() > 0;
}
hsBool plLightMapGen::Close()
{
// HACK to get rid of keys held by the lightmap components, because
// we can't delete the bitmaps in ICompressLightMaps unless these
// refs are gone
for (int i = 0; i < fSharedComponents.size(); i++)
{
if (fSharedComponents[i]->GetLightMapKey()) // if it has a key
fSharedComponents[i]->SetLightMapKey(nil); // nil it out
}
fSharedComponents.clear();
ICompressLightMaps();
#ifndef MF_NEW_RGC
delete fRGC;
#else // MF_NEW_RGC
if( fRenderer )
fRenderer->Close(fInterface->GetMAXHWnd());
fRenderer = nil;
#endif // MF_NEW_RGC
fRGC = nil;
delete fRP;
fRP = nil;
fPreppedMipmaps.SetCount(0);
fCreatedLayers.SetCount(0);
fNewMaps.SetCount(0);
IReleaseActiveLights();
IReleaseAllLights();
fInterface = nil;
return true;
}
//#define MIPMAP_LOG
#ifdef MIPMAP_LOG
void DumpMipmap(plMipmap* mipmap, const char* prefix)
{
hsUNIXStream dump;
char buf[256];
sprintf(buf, "log\\%s.txt", prefix);
dump.Open(buf, "wt");
for (int i = 0; i < mipmap->GetNumLevels(); i++)
{
mipmap->SetCurrLevel(i);
UInt32 width = mipmap->GetCurrWidth();
UInt32 height = mipmap->GetCurrHeight();
sprintf(buf, "----- Level %d (%dx%d) -----\n", i, width, height);
dump.WriteString(buf);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
UInt32 color = *(mipmap->GetAddr32(x, y));
UInt8 r = ((UInt8)((color)>>16));
UInt8 g = ((UInt8)((color)>>8));
UInt8 b = ((UInt8)((color)>>0));
UInt8 a = ((UInt8)((color)>>24));
sprintf(buf, "[%3d,%3d,%3d,%3d]", r, g, b, a);
dump.WriteString(buf);
}
dump.WriteString("\n");
}
}
dump.Close();
}
#endif // MIPMAP_LOG
hsBool plLightMapGen::ICompressLightMaps()
{
int i;
for( i = 0; i < fPreppedMipmaps.GetCount(); i++ )
{
plMipmap* orig = fPreppedMipmaps[i];
if( orig )
{
const hsScalar kFilterSigma = 1.0f;
if( IsFresh(orig) )
{
hsAssert(!orig->IsCompressed(), "How did we just generate a compressed texture?");
orig->Filter(kFilterSigma);
}
if( !orig->IsCompressed() && !(orig->GetFlags() & plMipmap::kForceNonCompressed) )
{
#ifdef MIPMAP_LOG
DumpMipmap(orig, orig->GetKeyName());
#endif // MIPMAP_LOG
plMipmap *compressed =
hsCodecManager::Instance().CreateCompressedMipmap(plMipmap::kDirectXCompression, orig);
if( compressed )
{
const plLocation &textureLoc = plPluginResManager::ResMgr()->GetCommonPage(orig->GetKey()->GetUoid().GetLocation(),
plAgeDescription::kTextures );
char name[512];
sprintf(name, "%s_DX", orig->GetKey()->GetName());
plKey compKey = hsgResMgr::ResMgr()->FindKey(plUoid(textureLoc, plMipmap::Index(), name));
if( compKey )
plBitmapCreator::Instance().DeleteExportedBitmap(compKey);
hsgResMgr::ResMgr()->NewKey( name, compressed, textureLoc );
int j;
for( j = 0; j < fCreatedLayers.GetCount(); j++ )
{
if( orig == fCreatedLayers[j]->GetTexture() )
{
fCreatedLayers[j]->GetKey()->Release(orig->GetKey());
hsgResMgr::ResMgr()->AddViaNotify(compressed->GetKey(), TRACKED_NEW plLayRefMsg(fCreatedLayers[j]->GetKey(), plRefMsg::kOnReplace, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef);
}
}
plBitmapCreator::Instance().DeleteExportedBitmap(orig->GetKey());
}
}
}
}
return true;
}
hsBool plLightMapGen::MakeMaps(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, hsTArray<plGeometrySpan *> &spans, plErrorMsg *pErrMsg, plConvertSettings *settings)
{
const char* dbgNodeName = node->GetName();
plLightMapComponent* lmapComp = node->GetLightMapComponent();
if( !lmapComp )
return false;
SetUVWSrc(lmapComp->GetUVWSrc());
SetScale(lmapComp->GetScale());
// If we don't want maps here, don't bother.
if( !IWantsMaps(node) )
{
pErrMsg->Set(true, node->GetName(), "Lightmap generation requested on bogus object").CheckAndAsk();
return false;
}
if( !IValidateUVWSrc(spans) )
{
pErrMsg->Set(true, node->GetName(), "Lightmap generation requested but UVW src bogus. Check mapping.").CheckAndAsk();
return false;
}
// If there aren't any lights, don't bother
if( !InitNode(node, false) )
{
pErrMsg->Set(true, node->GetName(), "Lightmap generation requested but no lights on object. Kind of wasteful.").CheckAndAsk();
return true;
}
// If we have trouble getting a bitmap size, there's probably something wrong with the geometry
if( !ISelectBitmapDimension(node, l2w, w2l, spans) )
{
pErrMsg->Set(true, node->GetName(), "Lightmap generation failure determining bitmap size, probably geometry problem.").CheckAndAsk();
return false;
}
// Okay, we're going to do it. The lights are
// set up for this guy so we just need some geometry.
// Find the drawable and which spans correspond
// to this node, and feed them through.
//
// IShadeGeometrySpans() and lower return whether any light was actually found.
if( !IShadeGeometrySpans(node, l2w, w2l, spans) )
{
pErrMsg->Set(true, node->GetName(), "Lightmap generation requested but no light found on object. Kind of wasteful.").CheckAndAsk();
}
DeInitNode();
return true;
}
// The next couple of functions don't do anything interesting except
// get us down to the face level where we can work.
hsBool plLightMapGen::IShadeGeometrySpans(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, hsTArray<plGeometrySpan *> &spans)
{
hsBool retVal = false;
int i;
for( i = 0; i < spans.GetCount(); i++ )
{
retVal |= IShadeSpan(node, l2w, w2l, *spans[i]);
}
return retVal;
}
hsBool plLightMapGen::IsFresh(plBitmap* map) const
{
return fRecalcLightMaps || fNewMaps.Find(map) != fNewMaps.kMissingIndex;
}
hsBool plLightMapGen::IShadeSpan(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, plGeometrySpan& span)
{
// This will look for a suitable lightmap layer and return that. If there
// isn't one already, it will set one up for us.
plLayerInterface* lay = IGetLightMapLayer(node, span);
// This next check should never happen, since we've created the layer ourselves.
if( !lay || !lay->GetTexture() )//|| !lay->GetTexture()->GetBitmap() )
return false;
int i;
if( !(span.fProps & plGeometrySpan::kDiffuseFoldedIn) )
{
hsBool foldin = 0 != (span.fProps & plGeometrySpan::kLiteVtxNonPreshaded);
hsScalar opacity = 1.f;
hsColorRGBA dif = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f);
if( foldin )
{
// Find the opacity to fold in.
// There should be one (or less) layers in this material using layer opacity,
// or else we should have put this span as kLiteMaterial instead of kLiteVtxNonPreshaded,
// so we're safe just getting the first opacity on a blended layer.
// Likewise we're safe getting the first diffuse color that's not on an
// emissive layer (since emissive layers ignore lightmapping).
// If we are using kLiteMaterial, we still need to copy from InitColor to Stuff,
// just don't do the modulate.
for( i = 0; i < span.fMaterial->GetNumLayers(); i++ )
{
if( span.fMaterial->GetLayer(i)->GetBlendFlags() & hsGMatState::kBlendAlpha )
{
opacity = span.fMaterial->GetLayer(i)->GetOpacity();
break;
}
}
for( i = 0; i < span.fMaterial->GetNumLayers(); i++ )
{
if( !(span.fMaterial->GetLayer(i)->GetShadeFlags() & hsGMatState::kShadeEmissive) )
{
dif = span.fMaterial->GetLayer(i)->GetRuntimeColor();
break;
}
}
}
for( i = 0; i < span.fNumVerts; i++ )
{
hsColorRGBA multColor, addColor;
span.ExtractInitColor( i, &multColor, &addColor);
if( foldin )
{
multColor *= dif; // We like to use kVertexNonPreshaded for lightmapped objects, which needs the runtime diffuse folded in
multColor.a *= opacity;
}
addColor.Set(0,0,0,0);
span.StuffVertex(i, &multColor, &addColor);
}
if( span.fInstanceRefs )
{
int j;
for( j = 0; j < span.fInstanceRefs->GetCount(); j++ )
{
plGeometrySpan* inst = (*span.fInstanceRefs)[j];
inst->fProps |= plGeometrySpan::kDiffuseFoldedIn;
}
}
}
else
{
for( i = 0; i < span.fNumVerts; i++ )
{
hsColorRGBA multColor, addColor;
span.ExtractInitColor( i, &multColor, &addColor);
addColor.Set(0,0,0,0);
span.StuffVertex(i, &multColor, &addColor);
}
return true;
}
// If we aren't recalculating all our lightmaps, then we only want to compute lightmaps
// which have a creation time of now.
if( !IsFresh(lay->GetTexture()) )
return true;
plMipmap* accum = IMakeAccumBitmap(lay);
hsBool retVal = false;
int nFaces = span.fNumIndices / 3;
for( i = 0; i < nFaces; i++ )
{
retVal |= IShadeFace(node, l2w, w2l, span, i, accum);
}
IAddToLightMap(lay, accum);
return retVal;
}
plMipmap* plLightMapGen::IMakeAccumBitmap(plLayerInterface* lay) const
{
plMipmap* dst = plMipmap::ConvertNoRef( lay->GetTexture() );//->GetBitmap();
hsAssert( dst != nil, "nil mipmap in IMakeAccumBitmap()" );
int width = dst->GetWidth();
int height = dst->GetHeight();
// Temporary mipmap here, so we don't have to worry about using plBitmapCreator
plMipmap* bitmap = TRACKED_NEW plMipmap( width, height, plMipmap::kRGB32Config, 1 );
HSMemory::Clear(bitmap->GetImage(), bitmap->GetHeight() * bitmap->GetRowBytes() );
return bitmap;
}
hsBool plLightMapGen::IAddToLightMap(plLayerInterface* lay, plMipmap* src) const
{
plMipmap* dst = plMipmap::ConvertNoRef( lay->GetTexture() );//->GetBitmap();
hsAssert( dst != nil, "nil mipmap in IAddToLightMap()" );
src->SetCurrLevel( 0 );
dst->SetCurrLevel( 0 );
// BLURLATER
// static hsScalar kFilterSigma = 0.5f;
// src->Filter(kFilterSigma);
// What we really want to do here is antialias our rasterization, so we can
// just sum in contributions of lighting at the boarder between spans sharing
// a light map. A quick hackaround is to use the max between existing color
// (in dst) and current spans illumination contribution (in src).
int i, j;
for( j = 0; j < dst->GetHeight(); j++ )
{
for( i = 0; i < dst->GetWidth(); i++ )
{
UInt32 srcRed = (*src->GetAddr32(i, j) >> 16) & 0xff;
UInt32 dstRed = (*dst->GetAddr32(i, j) >> 16) & 0xff;
// dstRed += srcRed;
if( dstRed < srcRed )
dstRed = srcRed;
if( dstRed > 0xff )
dstRed = 0xff;
UInt32 srcGreen = (*src->GetAddr32(i, j) >> 8) & 0xff;
UInt32 dstGreen = (*dst->GetAddr32(i, j) >> 8) & 0xff;
// dstGreen += srcGreen;
if( dstGreen < srcGreen )
dstGreen = srcGreen;
if( dstGreen > 0xff )
dstGreen = 0xff;
UInt32 srcBlue = (*src->GetAddr32(i, j) >> 0) & 0xff;
UInt32 dstBlue = (*dst->GetAddr32(i, j) >> 0) & 0xff;
// dstBlue += srcBlue;
if( dstBlue < srcBlue )
dstBlue = srcBlue;
if( dstBlue > 0xff )
dstBlue = 0xff;
*dst->GetAddr32(i, j) = 0xff000000
| (dstRed << 16)
| (dstGreen << 8)
| (dstBlue << 0);
}
}
dst->MakeDirty();
delete src;
return true;
}
hsBool plLightMapGen::IShadeFace(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, plGeometrySpan& span, int iFace, plMipmap* bitmap)
{
// Okay, here's where the metal hits the road, whatever that means.
// We're going to get our bitmap, and step along the face texel by texel,
// summing up the light at each texel and stuffing it in the bitmap.
// Set up a light context for the shading below.
Box3 bbox;
node->EvalWorldState(fTime).obj->GetDeformBBox(fTime, bbox, &node->GetObjectTM(fTime));
plMaxLightContext ctx(bbox, fTime);
// First, get the face info we'll be using.
// This will look for a suitable lightmap layer and return that.
// There should be one there already, because we called this
// in IShadeSpan
plLayerInterface* lay = IGetLightMapLayer(node, span);
int iOurUv = IGetUVWSrc();
// A little late to be checking this, but whatever...
if( iOurUv < 0 )
return false;
int width = bitmap->GetWidth();
int height = bitmap->GetHeight();
hsMatrix44 norml2w;
hsMatrix44 temp;
l2w.GetInverse( &temp);
temp.GetTranspose( &norml2w );
hsPoint3 pt[3];
hsVector3 norm[3];
hsPoint3 uv[3];
int i;
for( i = 0; i < 3; i++ )
{
hsColorRGBA trash;
span.ExtractVertex(span.fIndexData[iFace*3 + i], &pt[i], &norm[i], &trash);
span.ExtractUv(span.fIndexData[iFace*3 + i], iOurUv, &uv[i]);
pt[i] = l2w * pt[i];
norm[i] = norml2w * norm[i];
uv[i] = lay->GetTransform() * uv[i];
uv[i].fX *= width-1;
uv[i].fX += 0.5f;
uv[i].fY *= height-1;
uv[i].fY += 0.5f;
}
Color amb(0,0,0);
return IShadeVerts(ctx, amb, pt, norm, uv, bitmap);
}
hsBool plLightMapGen::IShadeVerts(plMaxLightContext& ctx, const Color& amb, const hsPoint3 pt[3], const hsVector3 norm[3], const hsPoint3 uv[3], plMipmap* bitmap)
{
int width = bitmap->GetWidth();
int height = bitmap->GetHeight();
bitmap->SetCurrLevel( 0 );
hsTArray<LMGScanlineData> scanline;
scanline.SetCount(height);
int lowestV = height;
int highestV = 0;
int i0, i1, i2;
for( i0 = 0; i0 < 3; i0++ )
{
i1 = i0 == 2 ? 0 : i0+1;
i2 = i1 == 2 ? 0 : i1+1;
hsScalar v0 = uv[i0].fY;
hsScalar v1 = uv[i1].fY;
int vStart = int(v0);
int vEnd = int(v1);
if( vStart == vEnd )
continue;
int vStep = vStart < vEnd ? 1 : -1;
int vMid;
for( vMid = vStart; vMid != vEnd + vStep; vMid += vStep )
{
// This shouldn't really happen, but might with some slop.
if( (vMid < 0) || (vMid >= height) )
continue;
hsPoint3 bary;
bary[i0] = (v1 - float(vMid)) / (v1 - v0);
bary[i1] = 1.f - bary[i0];
bary[i2] = 0;
hsScalar u = uv[i0].fX * bary[i0]
+ uv[i1].fX * bary[i1];
if( scanline[vMid].fEmpty )
{
scanline[vMid].fNear.fU = u;
scanline[vMid].fNear.fBary = bary;
scanline[vMid].fFar = scanline[vMid].fNear;
scanline[vMid].fEmpty = false;
if( vMid < lowestV )
lowestV = vMid;
if( vMid > highestV )
highestV = vMid;
}
else
{
if( u < scanline[vMid].fNear.fU )
{
scanline[vMid].fNear.fU = u;
scanline[vMid].fNear.fBary = bary;
}
else if( u > scanline[vMid].fFar.fU )
{
scanline[vMid].fFar.fU = u;
scanline[vMid].fFar.fBary = bary;
}
}
}
}
int i;
for( i = lowestV; i <= highestV; i++ )
{
if( !scanline[i].fEmpty )
{
int uStart = int(scanline[i].fNear.fU);
if( uStart < 0 )
uStart = 0;
int uEnd = int(scanline[i].fFar.fU);
if( uEnd >= width )
uEnd = width - 1;
if( uStart == uEnd )
continue;
int uMid;
for( uMid = uStart; uMid <= uEnd; uMid++ )
{
hsScalar t = (scanline[i].fFar.fU - float(uMid)) / (scanline[i].fFar.fU - scanline[i].fNear.fU);
hsPoint3 bary = scanline[i].fNear.fBary * t;
bary += scanline[i].fFar.fBary * (1.f - t);
hsPoint3 p = pt[0] * bary[0] + pt[1] * bary[1] + pt[2] * bary[2];
hsVector3 n = norm[0] * bary[0] + norm[1] * bary[1] + norm[2] * bary[2];
hsFastMath::NormalizeAppr(n);
UInt32 color = IShadePoint(ctx, amb, p, n);
*bitmap->GetAddr32(uMid, i) = color;
}
}
}
return true;
}
hsBool plLightMapGen::IGetLight(INode* node)
{
if( node->UserPropExists("RunTimeLight") )
return false;
Object *obj = node->EvalWorldState(fTime).obj;
if (obj && (obj->SuperClassID() == SClass_ID(LIGHT_CLASS_ID)))
{
plLightMapInfo* liInfo = fAllLights.Push();
LightObject* liObj = (LightObject*)obj;
liInfo->fResetShadowType = 0;
liInfo->fResetMapRange = -1.f;
liInfo->fMapRange = -1.f;
liInfo->fLiNode = node;
liInfo->fObjLiDesc = nil;
liInfo->fNewRender = true;
return true;
}
return false;
}
hsBool plLightMapGen::Update(TimeValue t)
{
fTime = t;
#ifndef MF_NEW_RGC
if( fRGC )
fRGC->Update(t);
#endif // MF_NEW_RGC
return fAllLights.GetCount() != 0;
}
hsBool plLightMapGen::IFindLightsRecur(INode* node)
{
IGetLight(node);
int i;
for( i = 0; i < node->NumberOfChildren(); i++ )
IFindLightsRecur(node->GetChildNode(i));
return fAllLights.GetCount() > 0;
}
hsBool plLightMapGen::InitNode(INode* node, hsBool softShadow)
{
fActiveLights.SetCount(0);
plMaxNode* maxNode = (plMaxNode*)node;
if( !maxNode->CanConvert() )
return false;
if( maxNode->GetNoPreShade() )
return false;
#ifndef MF_NO_SHADOW_BLUR
fMapRange = softShadow ? kBlurMapRange : -1.f;
#endif // MF_NO_SHADOW_BLUR
IFindActiveLights((plMaxNode*)node);
return fActiveLights.GetCount() > 0;
}
hsBool plLightMapGen::DeInitNode()
{
IReleaseActiveLights();
return true;
}
hsBounds3Ext plLightMapGen::IGetBoundsLightSpace(INode* node, INode* liNode)
{
TimeValue currTime(0);
hsBounds3Ext bnd;
bnd.MakeEmpty();
Object *obj = node->EvalWorldState(currTime).obj;
if( !obj )
return bnd;
Box3 box;
if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) )
{
DummyObject* dummy = (DummyObject*)obj;
box = dummy->GetBox();
}
else
if( obj->CanConvertToType(triObjectClassID) )
{
TriObject *meshObj = (TriObject *)obj->ConvertToType(currTime, triObjectClassID);
if( !meshObj )
return bnd;
Mesh& mesh = meshObj->mesh;
box = mesh.getBoundingBox();
if( meshObj != obj )
meshObj->DeleteThis();
}
bnd.Union(&hsPoint3(box.pmin.x, box.pmin.y, box.pmin.z));
bnd.Union(&hsPoint3(box.pmax.x, box.pmax.y, box.pmax.z));
Matrix3 maxL2W = node->GetObjectTM(currTime);
Matrix3 maxW2Light = Inverse(liNode->GetObjectTM(currTime));
Matrix3 maxL2Light = maxL2W * maxW2Light;
hsMatrix44 l2l;
hsControlConverter::Instance().Matrix3ToHsMatrix44(&maxL2Light, &l2l);
bnd.Transform(&l2l);
return bnd;
}
hsBool plLightMapGen::IDirAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node)
{
hsBounds3Ext bnd = IGetBoundsLightSpace(node, liInfo->fLiNode);
if( bnd.GetType() != kBoundsNormal )
return false;
if( bnd.GetMins().fZ > 0 )
return false;
LightState ls;
liObj->EvalLightState(TimeValue(0), FOREVER, &ls);
hsScalar radX = ls.fallsize;
hsScalar radY = radX;
if( ls.shape == RECT_LIGHT )
radY /= ls.aspect;
if( bnd.GetMins().fX > radX )
return false;
if( bnd.GetMaxs().fX < -radX )
return false;
if( bnd.GetMins().fY > radY )
return false;
if( bnd.GetMaxs().fY < -radY )
return false;
if( !ls.useAtten )
return true;
if( bnd.GetMaxs().fZ < -ls.attenEnd )
return false;
return true;
}
hsBool plLightMapGen::ISpotAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node)
{
hsBounds3Ext bnd = IGetBoundsLightSpace(node, liInfo->fLiNode);
if( bnd.GetType() != kBoundsNormal )
return false;
if( bnd.GetMins().fZ > 0 )
return false;
LightState ls;
liObj->EvalLightState(TimeValue(0), FOREVER, &ls);
hsScalar coneRad[2];
coneRad[0] = ls.fallsize * hsScalarPI / 180.f;
coneRad[1] = coneRad[0];
if( ls.shape == RECT_LIGHT )
coneRad[1] /= ls.aspect;
hsPoint3 corners[8];
bnd.GetCorners(corners);
int numPos[4] = { 0, 0, 0, 0 };
int j;
for( j = 0; j < 8; j++ )
{
hsScalar rad;
rad = hsScalar(atan2(corners[j].fX, -corners[j].fZ));
if( rad > coneRad[0] )
numPos[0]++;
if( rad < -coneRad[0] )
numPos[2]++;
rad = hsScalar(atan2(corners[j].fY, -corners[j].fZ));
if( rad > coneRad[1] )
numPos[1]++;
if( rad < -coneRad[1] )
numPos[3]++;
}
for( j = 0; j < 4; j++ )
{
if( numPos[j] >= 8 )
return false;
}
if( ls.useAtten )
{
if( bnd.GetMaxs().fZ < -ls.attenEnd )
return false;
}
return true;
}
hsBool plLightMapGen::IOmniAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node)
{
LightState ls;
liObj->EvalLightState(TimeValue(0), FOREVER, &ls);
if( !ls.useAtten )
return true;
hsBounds3Ext bnd = IGetBoundsLightSpace(node, liInfo->fLiNode);
if( bnd.GetType() != kBoundsNormal )
return false;
hsScalar radius = ls.attenEnd;
int i;
for( i = 0; i < 3; i++ )
{
if( bnd.GetMins()[i] > radius )
return false;
if( bnd.GetMaxs()[i] < -radius )
return false;
}
return true;
}
hsBool plLightMapGen::ILightAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node)
{
const char* liName = liInfo->fLiNode->GetName();
const char* nodeName = node->GetName();
LightState ls;
liObj->EvalLightState(TimeValue(0), FOREVER, &ls);
hsBool excluded = false;
if( !liObj->GetUseLight() )
{
excluded = true;
}
if( !excluded && liObj->GetExclList() && liObj->GetExclList()->TestFlag(NT_AFFECT_ILLUM) )
{
hsBool inExc = -1 != liObj->GetExclList()->FindNode(node);
if( (!inExc) ^ (!liObj->GetExclList()->TestFlag(NT_INCLUDE)) )
excluded = true;
}
if( excluded )
return false;
switch( ls.type )
{
case OMNI_LGT:
return IOmniAffectsNode(liInfo, liObj, node);
case SPOT_LGT:
return ISpotAffectsNode(liInfo, liObj, node);
case DIRECT_LGT:
return IDirAffectsNode(liInfo, liObj, node);
default:
case AMBIENT_LGT:
return true;
}
return false;
}
hsBool plLightMapGen::IPrepLight(plLightMapInfo* liInfo, INode* node)
{
const char* liName = liInfo->fLiNode->GetName();
const char* nodeName = node->GetName();
INode* liNode = liInfo->fLiNode;
LightObject* liObj = (LightObject*)liNode->EvalWorldState(fTime).obj;
// redundant check, if it doesn't have a light object it shouldn't be in the list
if( liObj )
{
hsBool affectsNode = ILightAffectsNode(liInfo, liObj, node);
if( affectsNode )
{
#ifdef MF_NO_RAY_SHADOW
// for reasons known only to god and someone deep in the bowels of kinetix,
// the lighting is (sometimes) barfing if the shadow type is ray-traced.
// until i can track that down, i'll force shadow mapped shadows.
liInfo->fResetShadowType = liObj->GetShadowType();
if( liInfo->fResetShadowType > 0 )
{
liObj->SetShadowType(0);
liInfo->fNewRender = true;
}
#endif MF_NO_RAY_SHADOW
if( fMapRange > 0 )
{
if( liInfo->fMapRange != fMapRange )
{
liInfo->fResetMapRange = liObj->GetMapRange(fTime);
liObj->SetMapRange(fTime, fMapRange);
liInfo->fMapRange = fMapRange;
liInfo->fNewRender = true;
}
}
else if( liInfo->fResetMapRange > 0 )
{
if( liInfo->fMapRange != liInfo->fResetMapRange )
{
liObj->SetMapRange(fTime, liInfo->fResetMapRange);
liInfo->fMapRange = liInfo->fResetMapRange;
liInfo->fNewRender = true;
}
}
ObjLightDesc* objLiDesc = liInfo->fObjLiDesc;
if( !objLiDesc )
objLiDesc = liObj->CreateLightDesc(liNode);
plMaxRendContext rc;
objLiDesc->Update(fTime, rc, fRGC, node->RcvShadows(), liInfo->fNewRender);
objLiDesc->UpdateViewDepParams(Matrix3(true));
liInfo->fNewRender = false;
liInfo->fObjLiDesc = objLiDesc;
fActiveLights.Append(liInfo);
}
}
return true;
}
hsBool plLightMapGen::IFindActiveLights(plMaxNode* node)
{
fActiveLights.SetCount(0);
int i;
for( i = 0; i < fAllLights.GetCount(); i++ )
{
IPrepLight(&fAllLights[i], node);
}
return fActiveLights.GetCount() > 0;
}
hsBool plLightMapGen::IReleaseAllLights()
{
int i;
for( i = 0; i < fAllLights.GetCount(); i++ )
{
if( fAllLights[i].fResetMapRange > 0 )
{
LightObject* liObj = (LightObject*)fAllLights[i].fLiNode->EvalWorldState(fTime).obj;
liObj->SetMapRange(fTime, fAllLights[i].fResetMapRange);
}
#ifdef MF_NO_RAY_SHADOW
// Fix the shadow method back.
if( fAllLights[i].fResetShadowType > 0 )
{
LightObject* liObj = (LightObject*)fAllLights[i].fLiNode->EvalWorldState(fTime).obj;
liObj->SetShadowType(fAllLights[i].fResetShadowType);
}
#endif // MF_NO_RAY_SHADOW
if( fAllLights[i].fObjLiDesc )
fAllLights[i].fObjLiDesc->DeleteThis();
fAllLights[i].fObjLiDesc = nil;
}
fAllLights.SetCount(0);
return true;
}
hsBool plLightMapGen::IReleaseActiveLights()
{
fActiveLights.SetCount(0);
return true;
}
hsBool plLightMapGen::IWantsMaps(plMaxNode* node)
{
if( !(node->CanConvert() && node->GetDrawable()) )
return false;
return nil != node->GetLightMapComponent();
}
hsBool plLightMapGen::IValidateUVWSrc(hsTArray<plGeometrySpan *>& spans) const
{
int i;
for( i = 0; i < spans.GetCount(); i++ )
{
int numUVWs = spans[i]->GetNumUVs();
if( IGetUVWSrc() >= numUVWs )
return false;
}
return true;
}
void plLightMapGen::IInitBitmapColor(plMipmap* bitmap, const hsColorRGBA& col) const
{
UInt32 initColor = MakeUInt32Color(col.r, col.g, col.b, col.a);
UInt32* pix = (UInt32*)bitmap->GetImage();
UInt32* pixEnd = ((UInt32*)bitmap->GetImage()) + bitmap->GetWidth() * bitmap->GetHeight();
while( pix < pixEnd )
*pix++ = initColor;
}
plLayerInterface* plLightMapGen::IGetLightMapLayer(plMaxNode* node, plGeometrySpan& span)
{
plLayerInterface* lay = IMakeLightMapLayer(node, span);
plMipmap* mip = plMipmap::ConvertNoRef(lay->GetTexture());
hsAssert(mip, "This should have been a mipmap we created ourselves.");
if( !mip )
return nil;
if( fPreppedMipmaps.Find(mip) == fPreppedMipmaps.kMissingIndex )
{
if( IsFresh(mip) )
{
hsColorRGBA initColor = node->GetLightMapComponent()->GetInitColor();
// Get this off the node, where the lightmap component has stashed it.
IInitBitmapColor(mip, initColor);
}
fPreppedMipmaps.Append(mip);
}
if( fCreatedLayers.Find(lay) == fCreatedLayers.kMissingIndex )
{
fCreatedLayers.Append(lay);
}
return lay;
}
plLayerInterface* plLightMapGen::IMakeLightMapLayer(plMaxNode* node, plGeometrySpan& span)
{
hsGMaterial* mat = span.fMaterial;
int i;
for( i = 0; i < mat->GetNumPiggyBacks(); i++ )
{
if( mat->GetPiggyBack(i)->GetMiscFlags() & hsGMatState::kMiscLightMap )
return mat->GetPiggyBack(i);
}
char newMatName[256];
sprintf(newMatName, "%s_%s_LIGHTMAPGEN", mat->GetKey()->GetName(), node->GetName());
plLocation nodeLoc = node->GetLocation();
plKey matKey = hsgResMgr::ResMgr()->FindKey(plUoid(nodeLoc, hsGMaterial::Index(), newMatName));
if( matKey )
{
mat = hsGMaterial::ConvertNoRef(matKey->ObjectIsLoaded());
for( i = 0; i < mat->GetNumPiggyBacks(); i++ )
{
if( mat->GetPiggyBack(i)->GetMiscFlags() & hsGMatState::kMiscLightMap )
{
span.fMaterial = mat;
return mat->GetPiggyBack(i);
}
}
hsAssert(false, "Something not a light map material registered with our name?");
}
hsGMaterial* objMat = nil;
hsBool sharemaps = node->GetLightMapComponent()->GetShared();
if( sharemaps )
{
objMat = mat;
}
else
{
objMat = TRACKED_NEW hsGMaterial;
hsgResMgr::ResMgr()->NewKey(newMatName, objMat, nodeLoc);
for( i = 0; i < mat->GetNumLayers(); i++ )
hsgResMgr::ResMgr()->AddViaNotify(mat->GetLayer(i)->GetKey(), TRACKED_NEW plMatRefMsg(objMat->GetKey(), plRefMsg::kOnCreate, -1, plMatRefMsg::kLayer), plRefFlags::kActiveRef);
}
objMat->SetCompositeFlags(objMat->GetCompositeFlags() | hsGMaterial::kCompIsLightMapped);
// Make sure layer (and mip) name are unique across pages by putting the page name in
const plPageInfo* pageInfo = plKeyFinder::Instance().GetLocationInfo(node->GetLocation());
char layName[256];
sprintf(layName, "%s_%s_LIGHTMAPGEN", pageInfo->GetPage(), node->GetName());
plKey layKey = node->FindPageKey(plLayer::Index(), layName);
if( !layKey )
{
int w = fWidth;
int h = fHeight;
plKey mipKey;
if( node->GetLightMapComponent()->GetLightMapKey() )
{
mipKey = node->GetLightMapComponent()->GetLightMapKey();
}
else
{
char mipmapName[ 256 ];
sprintf( mipmapName, "%s_mip", layName );
// Deleted the NOTE here because it was incorrect in every meaningful sense of the word. - mf
const plLocation &textureLoc = plPluginResManager::ResMgr()->GetCommonPage( nodeLoc, plAgeDescription::kTextures );
mipKey = hsgResMgr::ResMgr()->FindKey(plUoid(textureLoc, plMipmap::Index(), mipmapName));
if( !mipKey && !fRecalcLightMaps )
{
char compressedName[512];
sprintf(compressedName, "%s_DX", mipmapName);
plKey compKey = hsgResMgr::ResMgr()->FindKey(plUoid(textureLoc, plMipmap::Index(), compressedName));
if( compKey )
mipKey = compKey;
}
if( mipKey )
{
plBitmap* bitmap = plBitmap::ConvertNoRef(mipKey->ObjectIsLoaded());
if( bitmap )
{
if( node->GetLightMapComponent()->GetCompress() != bitmap->IsCompressed() )
{
// make sure the lightmap component isn't holding a key,
// it will get assigned one a few lines later anyway
if (node->GetLightMapComponent()->GetLightMapKey())
node->GetLightMapComponent()->SetLightMapKey(nil);
plBitmapCreator::Instance().DeleteExportedBitmap(mipKey);
}
}
}
if( !mipKey )
{
plMipmap* bitmap = plBitmapCreator::Instance().CreateBlankMipmap(w, h, plMipmap::kRGB32Config, 1, mipmapName, nodeLoc);
mipKey = bitmap->GetKey();
fNewMaps.Append(bitmap);
if( !node->GetLightMapComponent()->GetCompress() )
bitmap->SetFlags(bitmap->GetFlags() | plMipmap::kForceNonCompressed);
}
if( node->GetLightMapComponent()->GetShared() )
{
// HACK since we are setting the key, save the pointer to the light map
// component so we can get rid of the key it holds later
fSharedComponents.push_back(node->GetLightMapComponent());
node->GetLightMapComponent()->SetLightMapKey(mipKey);
}
}
plLayer* layer = TRACKED_NEW plLayer;
layer->InitToDefault();
layKey = hsgResMgr::ResMgr()->NewKey(layName, layer, nodeLoc);
hsgResMgr::ResMgr()->AddViaNotify(mipKey, TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef);
layer->SetAmbientColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f));
layer->SetZFlags(hsGMatState::kZNoZWrite);
layer->SetBlendFlags(hsGMatState::kBlendMult);
layer->SetClampFlags(hsGMatState::kClampTexture);
layer->SetUVWSrc(IGetUVWSrc());
layer->SetMiscFlags(hsGMatState::kMiscLightMap);
}
hsgResMgr::ResMgr()->AddViaNotify(layKey, TRACKED_NEW plMatRefMsg(objMat->GetKey(), plRefMsg::kOnCreate, -1, plMatRefMsg::kPiggyBack), plRefFlags::kActiveRef);
span.fMaterial = objMat;
return plLayerInterface::ConvertNoRef(layKey->GetObjectPtr());
}
// Like ShadePoint, but only computes the amount of light striking the surface,
// so ignoring the N dot L term.
Color plLightMapGen::ShadowPoint(plMaxLightContext& ctx)
{
ctx.globContext = fRGC;
Color accum;
accum.Black();
int i;
for( i = 0; i < fActiveLights.GetCount(); i++ )
{
const char* dbgLiName = fActiveLights[i]->fLiNode->GetName();
Color color;
Point3 liDir;
float dot_nl, diffuseCoef;
BOOL hit = fActiveLights[i]->fObjLiDesc->Illuminate(ctx, ctx.Normal(), color, liDir, dot_nl, diffuseCoef);
if( hit )
{
accum += color;
}
}
return accum;
}
Color plLightMapGen::ShadePoint(plMaxLightContext& ctx)
{
ctx.globContext = fRGC;
Color accum;
accum.Black();
int i;
for( i = 0; i < fActiveLights.GetCount(); i++ )
{
Color color;
Point3 liDir;
float dot_nl, diffuseCoef;
BOOL hit = fActiveLights[i]->fObjLiDesc->Illuminate(ctx, ctx.Normal(), color, liDir, dot_nl, diffuseCoef);
if( hit )
{
accum += color * diffuseCoef;
}
}
return accum;
}
Color plLightMapGen::ShadePoint(plMaxLightContext& ctx, const Point3& p, const Point3& n)
{
ctx.SetPoint(p, n);
return ShadePoint(ctx);
}
Color plLightMapGen::ShadePoint(plMaxLightContext& ctx, const hsPoint3& p, const hsVector3& n)
{
ctx.SetPoint(p, n);
return ShadePoint(ctx);
}
UInt32 plLightMapGen::IShadePoint(plMaxLightContext& ctx, const Color& amb, const hsPoint3& p, const hsVector3& n)
{
ctx.globContext = fRGC;
ctx.SetPoint(p, n);
Color accum = ShadePoint(ctx);
accum += amb;
accum.ClampMinMax();
UInt32 retVal;
retVal = MakeUInt32Color(accum.r, accum.g, accum.b, 1.f);
return retVal;
}
hsBool plLightMapGen::ISelectBitmapDimension(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, hsTArray<plGeometrySpan *>& spans)
{
float duDr = 0;
float dvDr = 0;
float totFaces = 0;
int i;
for( i = 0; i < spans.GetCount(); i++ )
{
plGeometrySpan *span = spans[i];
int nFaces = span->fNumIndices / 3;
int j;
for( j = 0; j < nFaces; j++ )
{
hsPoint3 pt[3];
hsPoint3 uv[3];
int k;
for( k = 0; k < 3; k++ )
{
hsVector3 vTrash;
hsColorRGBA cTrash;
span->ExtractVertex(span->fIndexData[j*3 + k], &pt[k], &vTrash, &cTrash);
pt[k] = l2w * pt[k];
span->ExtractUv(span->fIndexData[j*3 + k], IGetUVWSrc(), &uv[k]);
}
if( (uv[0].fX >= 1.f)
&&(uv[1].fX >= 1.f)
&&(uv[2].fX >= 1.f) )
continue;
if( (uv[0].fY >= 1.f)
&&(uv[1].fY >= 1.f)
&&(uv[2].fY >= 1.f) )
continue;
if( (uv[0].fX <= 0)
&&(uv[1].fX <= 0)
&&(uv[2].fX <= 0) )
continue;
if( (uv[0].fY <= 0)
&&(uv[1].fY <= 0)
&&(uv[2].fY <= 0) )
continue;
float magDU[2];
magDU[0] = fabsf(uv[1].fX - uv[0].fX);
magDU[1] = fabsf(uv[2].fX - uv[0].fX);
if( magDU[0] > magDU[1] )
{
float dist = hsVector3(pt+1, pt+0).Magnitude();
if( dist > 1.e-3f )
duDr += magDU[0] / dist;
}
else
{
float dist = hsVector3(pt+2, pt+0).Magnitude();
if( dist > 1.e-3f )
duDr += magDU[1] / dist;
}
float magDV[2];
magDV[0] = fabsf(uv[1].fY - uv[0].fY);
magDV[1] = fabsf(uv[2].fY - uv[0].fY);
if( magDV[0] > magDV[1] )
{
float dist = hsVector3(pt+1, pt+0).Magnitude();
if( dist > 1.e-3f )
dvDr += magDV[0] / dist;
}
else
{
float dist = hsVector3(pt+2, pt+0).Magnitude();
if( dist > 1.e-3f )
dvDr += magDV[1] / dist;
}
totFaces++;
}
}
if( totFaces < 1.f )
return false;
duDr /= totFaces;
dvDr /= totFaces;
const int kMaxSize = 256;
const int kMinSize = 32;
const int kMaxAspect = 8;
const float kTexPerFoot = 1.f;
if( duDr > 0 )
{
fWidth = kTexPerFoot / duDr;
if( fWidth > kMaxSize )
fWidth = kMaxSize;
if( fWidth < kMinSize )
fWidth = kMinSize;
}
else
{
fWidth = kMinSize;
}
fWidth *= fScale;
fWidth = IPowerOfTwo(fWidth);
if( dvDr > 0 )
{
fHeight = kTexPerFoot / duDr;
if( fHeight > kMaxSize )
fHeight = kMaxSize;
if( fHeight < kMinSize )
fHeight = kMinSize;
}
else
{
fHeight = kMinSize;
}
fHeight *= fScale;
fHeight = IPowerOfTwo(fHeight);
if( fHeight / fWidth > kMaxAspect )
fWidth = fHeight / kMaxAspect;
if( fWidth / fHeight > kMaxAspect )
fHeight = fWidth / kMaxAspect;
if( fWidth > 512 )
fWidth = 512;
if( fHeight > 512 )
fHeight = 512;
return true;
}
int plLightMapGen::IPowerOfTwo(int sz) const
{
int i = 0;
while( (1 << i) < sz )
i++;
int p2sz = 1 << i;
if( p2sz - sz > sz - (p2sz >> 1) )
p2sz >>= 1;
return p2sz;
}