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.

201 lines
6.6 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/>.
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==*/
#include "hsTypes.h"
#include "plShadowSlave.h"
#include "plTweak.h"
#include <float.h>
static const hsScalar kMinMinZ = 1.f; // totally random arbitrary number (has to be > 0).
bool plShadowSlave::ISetupOrthoViewTransform()
{
hsBounds3Ext bnd = fCasterWorldBounds;
bnd.Transform(&fWorldToLight);
hsScalar minZ = bnd.GetMins().fZ;
hsScalar maxZ = bnd.GetCenter().fZ + fAttenDist;
hsScalar minX = bnd.GetMins().fX;
hsScalar maxX = bnd.GetMaxs().fX;
hsScalar minY = bnd.GetMins().fY;
hsScalar maxY = bnd.GetMaxs().fY;
hsMatrix44 proj;
proj.Reset();
proj.NotIdentity();
// First the LightToTexture, which uses the above pretty much as is.
// Note the remapping to range [0.5..width-0.5] etc.
// About this kAdjustBias. According to the docs, it should be 0.5,
// and on the perspective projection 0.5 works great. But on the
// directional (ortho) projection, it just makes the mapping wrong,
// and an offset of zero works great. This could be a driver bug, or
// hardware "dependency" (read IHV bug), but whatever, zero is working
// now. Might need to adjust for new drivers or other hardware.
const hsScalar kAdjustBias = 0.0f;
proj.fMap[0][0] = 1.f / (maxX - minX);
proj.fMap[0][3] = -minX / (maxX - minX) + kAdjustBias / fWidth;
proj.fMap[1][1] = -1.f / (maxY - minY);
proj.fMap[1][3] = -minY / (maxY - minY) + kAdjustBias / fHeight;
proj.fMap[2][2] = 1.f;
proj.fMap[3][3] = 1.f;
fWorldToTexture = proj * fWorldToLight;
// Now the LightToNDC. This one's a little trickier, because we want to compensate for
// having brought in the viewport to keep our border constant, so we can clamp the
// projected texture and not have the edges smear off to infinity.
// Like the adjust bias above, this part is correct in theory, but only
// screws things up (increases Z-acne).
#if 0
hsScalar delX = maxX - minX;
minX += delX / (fWidth * 0.5f);
maxX -= delX / (fWidth * 0.5f);
hsScalar delY = maxY - minY;
minY += delY / (fHeight * 0.5f);
maxY -= delY / (fHeight * 0.5f);
#endif
fView.SetView(hsPoint3(minX, minY, minZ), hsPoint3(maxX, maxY, maxZ));
fView.SetScreenSize((UInt16)fWidth, (UInt16)fHeight);
fView.SetCameraTransform(fWorldToLight, fLightToWorld);
fView.SetPerspective(false);
fView.SetViewPort(0, 0, hsScalar(fWidth), hsScalar(fHeight), false);
fLightDir = fLightToWorld.GetAxis(hsMatrix44::kUp);
SetFlag(kPositional, false);
SetFlag(kReverseCull, true);
return true;
}
bool plShadowSlave::ISetupPerspViewTransform()
{
hsBounds3Ext bnd = fCasterWorldBounds;
bnd.Transform(&fWorldToLight);
hsScalar minZ = bnd.GetMins().fZ;
hsScalar maxZ = bnd.GetCenter().fZ + fAttenDist;
if( minZ < kMinMinZ )
minZ = kMinMinZ;
// EAP
// This is my hack to get the Nexus age working. The real problem
// is probably data-side. I take full responsibility for this
// hack-around breaking the entire system, loosing data, causing
// unauthorized credit card transactions, etc.
if (_isnan(bnd.GetMins().fX) || _isnan(bnd.GetMins().fY))
return false;
if (_isnan(bnd.GetMaxs().fX) || _isnan(bnd.GetMaxs().fY))
return false;
hsScalar cotX, cotY;
if( -bnd.GetMins().fX > bnd.GetMaxs().fX )
{
hsAssert(bnd.GetMins().fX < 0, "Empty shadow caster bounds?");
cotX = -minZ / bnd.GetMins().fX;
}
else
{
hsAssert(bnd.GetMaxs().fX > 0, "Empty shadow caster bounds?");
cotX = minZ / bnd.GetMaxs().fX;
}
if( -bnd.GetMins().fY > bnd.GetMaxs().fY )
{
hsAssert(bnd.GetMins().fY < 0, "Empty shadow caster bounds?");
cotY = -minZ / bnd.GetMins().fY;
}
else
{
hsAssert(bnd.GetMaxs().fY > 0, "Empty shadow caster bounds?");
cotY = minZ / bnd.GetMaxs().fY;
}
hsMatrix44 proj;
proj.Reset();
proj.NotIdentity();
// First the LightToTexture, which uses the above pretty much as is.
// Note the remapping to range [0.5..width-0.5] etc. Also, the perspective
// divide is by the 3rd output (not the fourth), so we make the 3rd
// output be W (instead of Z).
// This also means that our translate goes into [i][2] instead of [i][3].
#if 0
proj.fMap[0][0] = cotX * 0.5f;
proj.fMap[0][2] = -0.5f * (1.f + 0.5f/fWidth);
proj.fMap[1][1] = cotY * 0.5f;
proj.fMap[1][2] = -0.5f * (1.f + 0.5f/fHeight);
#else
plConst(hsScalar) kBiasScale(1.f);
plConst(hsScalar) kBiasTrans(1.f);
proj.fMap[0][0] = cotX * 0.5f * ( hsScalar(fWidth-2.f) / hsScalar(fWidth) ) * kBiasScale;
proj.fMap[0][2] = 0.5f * (1.f - kBiasTrans * 0.5f/fWidth);
proj.fMap[1][1] = -cotY * 0.5f * ( hsScalar(fHeight-2.f) / hsScalar(fHeight) ) * kBiasScale;
proj.fMap[1][2] = 0.5f * (1.f - kBiasTrans * 0.5f/fHeight);
#endif
#if 0 // This computes correct Z, but we really just want W in 3rd component. HACKFISH
proj.fMap[2][2] = maxZ / (maxZ - minZ);
proj.fMap[2][3] = -minZ * maxZ / (maxZ - minZ);
#elif 1
proj.fMap[2][2] = 1.f;
proj.fMap[2][3] = 0;
#endif
proj.fMap[3][2] = 1.f;
proj.fMap[3][3] = 0;
fWorldToTexture = proj * fWorldToLight;
// Now the LightToNDC. This one's a little trickier, because we want to compensate for
// having brought in the viewport to keep our border constant, so we can clamp the
// projected texture and not have the edges smear off to infinity.
cotX -= cotX / (fWidth * 0.5f);
cotY -= cotY / (fHeight * 0.5f);
hsScalar tanX = 1.f / cotX;
hsScalar tanY = 1.f / cotY;
fView.SetView(hsPoint3(-tanX, -tanY, minZ), hsPoint3(tanX, tanY, maxZ));
fView.SetScreenSize((UInt16)fWidth, (UInt16)fHeight);
fView.SetCameraTransform(fWorldToLight, fLightToWorld);
fView.SetPerspective(true);
fView.SetViewPort(0, 0, hsScalar(fWidth), hsScalar(fHeight), false);
fLightPos = fLightToWorld.GetTranslate();
SetFlag(kPositional, true);
return true;
}