/*==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 . 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 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; }