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.

1371 lines
35 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 "plCutter.h"
#include "plAccessSpan.h"
#include "hsFastMath.h"
#include "plAccessGeometry.h"
#include "hsStream.h"
// Test hack
#include "plDrawableSpans.h"
#include "plDrawableGenerator.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../pnSceneObject/plDrawInterface.h"
#include "../plScene/plSceneNode.h"
#include "../plScene/plPageTreeMgr.h"
#include "../plSurface/hsGMaterial.h"
#include "../plSurface/plLayerInterface.h"
void plCutter::Read(hsStream* stream, hsResMgr* mgr)
{
plCreatable::Read(stream, mgr);
fLengthU = stream->ReadSwapScalar();
fLengthV = stream->ReadSwapScalar();
fLengthW = stream->ReadSwapScalar();
}
void plCutter::Write(hsStream* stream, hsResMgr* mgr)
{
plCreatable::Write(stream, mgr);
stream->WriteSwapScalar(fLengthU);
stream->WriteSwapScalar(fLengthV);
stream->WriteSwapScalar(fLengthW);
}
void plCutter::Set(const hsPoint3& pos, const hsVector3& dir, const hsVector3& out, hsBool flip)
{
hsVector3 du = dir % out;
hsVector3 dv = out % du;
hsVector3 dw = out;
hsFastMath::NormalizeAppr(du);
hsFastMath::NormalizeAppr(dv);
hsFastMath::NormalizeAppr(dw);
fBackDir = dw;
if( flip )
du = -du;
fDirU = du / fLengthU;
fDirV = dv / -fLengthV;
fDirW = dw / fLengthW;
du *= fLengthU * 0.5f;
dv *= fLengthV * 0.5f;
dw *= fLengthW * 0.5f;
hsPoint3 corner = pos;
corner += -du;
corner += dv;
corner += -dw;
fDistU = corner.InnerProduct(fDirU);
fDistV = corner.InnerProduct(fDirV);
fDistW = corner.InnerProduct(fDirW);
hsMatrix44 l2w;
l2w.NotIdentity();
int i;
for( i = 0; i < 3; i++ )
{
l2w.fMap[i][0] = du[i];
l2w.fMap[i][1] = dv[i];
l2w.fMap[i][2] = dw[i];
l2w.fMap[i][3] = pos[i];
}
l2w.fMap[3][0] = l2w.fMap[3][1] = l2w.fMap[3][2] = 0;
l2w.fMap[3][3] = 1.f;
hsPoint3 p;
p.Set(1.f, 1.f, 1.f);
fWorldBounds.Reset(&p);
p.Set(-1.f, -1.f, -1.f);
fWorldBounds.Union(&p);
fWorldBounds.Transform(&l2w);
fIsect.SetBounds(fWorldBounds);
}
inline void plCutter::ISetPosNorm(hsScalar parm, const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
dst.fPos = outVtx.fPos;
dst.fPos += parm * (inVtx.fPos - outVtx.fPos);
dst.fNorm = outVtx.fNorm;
dst.fNorm += parm * (inVtx.fNorm - outVtx.fNorm);
dst.fColor = outVtx.fColor;
dst.fColor += parm * (inVtx.fColor - outVtx.fColor);
}
// A note on where the interpolation parameter is coming from.
//
// For the lower cases, we're looking for the point where Dot(pos, fDir) - fDist = 0.
// Starting with p = outVtx + parm * (inVtx - outVtx) and Dot(p, fDir) == fDist, we get:
// parm = (fDist - Dot(fDir,outVtx.fPos)) / (Dot(fDir, invVtx.fPos) - Dot(fDir, outVtx.fPos))
//
// UVW = Dot(fDir, fPos) - fDist
// Dot(fDir, fPos) = UVW + fDist
// So:
// parm = (fDist - (outVtx.fUVW - fDist)) / ((invVtx.fUVW - fDist) - (outVtx.fUVW - fDist))
// = -outVtx.fUVW / (inVtx.fUVW - outVtx.fUVW)
// = outVtx.fUVW / (outVtx.fUVW - inVtx.fUVW)
inline void plCutter::ICutoutVtxLoU(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
hsScalar parm = outVtx.fUVW.fX / (outVtx.fUVW.fX - inVtx.fUVW.fX);
ISetPosNorm(parm, inVtx, outVtx, dst);
dst.fUVW.fX = 0;
dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY);
dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ);
}
inline void plCutter::ICutoutVtxLoV(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
hsScalar parm = outVtx.fUVW.fY / (outVtx.fUVW.fY - inVtx.fUVW.fY);
ISetPosNorm(parm, inVtx, outVtx, dst);
dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX);
dst.fUVW.fY = 0;
dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ);
}
inline void plCutter::ICutoutVtxLoW(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
hsScalar parm = outVtx.fUVW.fZ / (outVtx.fUVW.fZ - inVtx.fUVW.fZ);
ISetPosNorm(parm, inVtx, outVtx, dst);
dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX);
dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY);
dst.fUVW.fZ = 0;
}
// Now for the upper cases, we start with Dot(pos, fDir) - fDist = 1.f
// So parm = (fDist + 1.f - Dot(fDir, outVtx.fPos) / (Dot(fDir, invVtx.fPos) - Dot(fDir, outVtx.fPos))
// and doing the same substitution gets
// parm = (outVtx.fUVW - 1.f) / (outVtx.fUVW - inVtx.fUVW)
inline void plCutter::ICutoutVtxHiU(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
hsScalar parm = (outVtx.fUVW.fX - 1.f) / (outVtx.fUVW.fX - inVtx.fUVW.fX);
ISetPosNorm(parm, inVtx, outVtx, dst);
dst.fUVW.fX = 1.f;
dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY);
dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ);
}
inline void plCutter::ICutoutVtxHiV(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
hsScalar parm = (outVtx.fUVW.fY - 1.f) / (outVtx.fUVW.fY - inVtx.fUVW.fY);
ISetPosNorm(parm, inVtx, outVtx, dst);
dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX);
dst.fUVW.fY = 1.f;
dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ);
}
inline void plCutter::ICutoutVtxHiW(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
hsScalar parm = (outVtx.fUVW.fZ - 1.f) / (outVtx.fUVW.fZ - inVtx.fUVW.fZ);
ISetPosNorm(parm, inVtx, outVtx, dst);
dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX);
dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY);
dst.fUVW.fZ = 1.f;
}
// Now for the split down the middle cases, we start with Dot(pos, fDir) - fDist = 0.5f
// So parm = (fDist + 0.5f - Dot(fDir, outVtx.fPos) / (Dot(fDir, invVtx.fPos) - Dot(fDir, outVtx.fPos))
// and doing the same substitution gets
// parm = (outVtx.fUVW - 0.5f) / (outVtx.fUVW - inVtx.fUVW)
inline void plCutter::ICutoutVtxMidU(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
hsScalar parm = (outVtx.fUVW.fX - 0.5f) / (outVtx.fUVW.fX - inVtx.fUVW.fX);
ISetPosNorm(parm, inVtx, outVtx, dst);
dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX); // TEST
dst.fUVW.fX = 0.5f;
dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY);
dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ);
}
inline void plCutter::ICutoutVtxMidV(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
hsScalar parm = (outVtx.fUVW.fY - 0.5f) / (outVtx.fUVW.fY - inVtx.fUVW.fY);
ISetPosNorm(parm, inVtx, outVtx, dst);
dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY); // TEST
dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX);
dst.fUVW.fY = 0.5f;
dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ);
}
inline void plCutter::ICutoutVtxMidW(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const
{
hsScalar parm = (outVtx.fUVW.fZ - 0.5f) / (outVtx.fUVW.fZ - inVtx.fUVW.fZ);
ISetPosNorm(parm, inVtx, outVtx, dst);
dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ); // TEST
dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX);
dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY);
dst.fUVW.fZ = 0.5f;
}
// IPolyClip
hsBool plCutter::IPolyClip(hsTArray<plCutoutVtx>& poly, const hsPoint3 vPos[]) const
{
static hsTArray<plCutoutVtx> accum;
accum.SetCount(0);
poly[0].fUVW.fX = vPos[0].InnerProduct(fDirU) - fDistU;
poly[0].fUVW.fY = vPos[0].InnerProduct(fDirV) - fDistV;
poly[0].fUVW.fZ = vPos[0].InnerProduct(fDirW) - fDistW;
poly[1].fUVW.fX = vPos[1].InnerProduct(fDirU) - fDistU;
poly[1].fUVW.fY = vPos[1].InnerProduct(fDirV) - fDistV;
poly[1].fUVW.fZ = vPos[1].InnerProduct(fDirW) - fDistW;
poly[2].fUVW.fX = vPos[2].InnerProduct(fDirU) - fDistU;
poly[2].fUVW.fY = vPos[2].InnerProduct(fDirV) - fDistV;
poly[2].fUVW.fZ = vPos[2].InnerProduct(fDirW) - fDistW;
// Try an early out test.
int i;
for( i = 0; i < 3; i++ )
{
int lo = 1;
int hi = 1;
int j;
for( j = 0; j < 3; j++ )
{
lo &= poly[j].fUVW[i] <= 0;
hi &= poly[j].fUVW[i] >= 1.f;
}
if( lo || hi )
{
poly.SetCount(0);
return false;
}
}
// First trim to lower bounds.
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fX < 0) << 1) | (poly[j].fUVW.fX < 0);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxLoU(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxLoU(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
poly.Swap(accum);
accum.SetCount(0);
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fY < 0) << 1) | (poly[j].fUVW.fY < 0);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxLoV(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxLoV(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
poly.Swap(accum);
accum.SetCount(0);
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fZ < 0) << 1) | (poly[j].fUVW.fZ < 0);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxLoW(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxLoW(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
poly.Swap(accum);
accum.SetCount(0);
// Now upper bounds
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fX > 1.f) << 1) | (poly[j].fUVW.fX > 1.f);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxHiU(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxHiU(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
poly.Swap(accum);
accum.SetCount(0);
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fY > 1.f) << 1) | (poly[j].fUVW.fY > 1.f);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxHiV(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxHiV(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
poly.Swap(accum);
accum.SetCount(0);
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fZ > 1.f) << 1) | (poly[j].fUVW.fZ > 1.f);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxHiW(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxHiW(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
poly.Swap(accum);
accum.SetCount(0);
return poly.GetCount() > 2;
}
// IPolyClip
hsBool plCutter::IFindHitPoint(const hsTArray<plCutoutVtx>& inPoly, plCutoutHit& hit) const
{
static hsTArray<plCutoutVtx> accum;
static hsTArray<plCutoutVtx> poly;
accum.SetCount(0);
poly = inPoly;
// First trim to lower bounds.
int i;
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fX < 0.5f) << 1) | (poly[j].fUVW.fX < 0.5f);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxMidU(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxMidU(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
poly.Swap(accum);
accum.SetCount(0);
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fY < 0.5f) << 1) | (poly[j].fUVW.fY < 0.5f);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxMidV(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxMidV(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
poly.Swap(accum);
accum.SetCount(0);
// Now upper bounds
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fX > 0.5f) << 1) | (poly[j].fUVW.fX > 0.5f);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxMidU(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxMidU(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
poly.Swap(accum);
accum.SetCount(0);
for( i = 0; i < poly.GetCount(); i++ )
{
int j = i ? i-1 : poly.GetCount()-1;
int test = ((poly[i].fUVW.fY > 0.5f) << 1) | (poly[j].fUVW.fY > 0.5f);
switch(test)
{
case 0:
// Both in
// Add this vert to outList
accum.Append(poly[i]);
break;
case 1:
// This in, last out
// Add ClipVert(j, j-1) to outList
// Add this vert to outList
accum.Push();
ICutoutVtxMidV(poly[i], poly[j], accum[accum.GetCount()-1]);
accum.Append(poly[i]);
break;
case 2:
// This out, last in
// Add ClipVert(j-1, j) to outList
accum.Push();
ICutoutVtxMidV(poly[j], poly[i], accum[accum.GetCount()-1]);
break;
case 3:
// Both out
break;
}
}
// At this point, if we hit, all verts should be identical, interpolated
// into the center of the cutter.
// No verts means no hit.
if( !accum.GetCount() )
return false;
if( accum[0].fNorm.InnerProduct(fDirW) < 0 )
return false;
hit.fPos = accum[0].fPos;
hit.fNorm = accum[0].fNorm;
return true;
}
hsBool plCutter::FindHitPoints(const hsTArray<plCutoutPoly>& src, hsTArray<plCutoutHit>& hits) const
{
hits.SetCount(0);
int iPoly;
for( iPoly = 0; iPoly < src.GetCount(); iPoly++ )
{
hsBool loU = false;
hsBool hiU = false;
hsBool loV = false;
hsBool hiV = false;
const plCutoutPoly& poly = src[iPoly];
int iv;
for( iv = 0; iv < poly.fVerts.GetCount(); iv++ )
{
const hsPoint3& uvw = poly.fVerts[iv].fUVW;
if( uvw.fX < 0.5f )
loU = true;
else
hiU = true;
if( uvw.fY < 0.5f )
loV = true;
else
hiV = true;
if( loU && hiU && loV && hiV )
{
plCutoutHit hit;
if( IFindHitPoint(poly.fVerts, hit) )
hits.Append(hit);
break;
}
}
}
return hits.GetCount() > 0;
}
hsBool plCutter::FindHitPointsConstHeight(const hsTArray<plCutoutPoly>& src, hsTArray<plCutoutHit>& hits, hsScalar height) const
{
if( FindHitPoints(src, hits) )
{
int i;
for( i = 0; i < hits.GetCount(); i++ )
hits[i].fPos.fZ = height;
return true;
}
return false;
}
void plCutter::ICutoutTransformedConstHeight(plAccessSpan& src, hsTArray<plCutoutPoly>& dst) const
{
const hsMatrix44& l2w = src.GetLocalToWorld();
hsMatrix44 l2wNorm;
src.GetWorldToLocal().GetTranspose(&l2wNorm);
hsBool baseHasAlpha = 0 != (src.GetMaterial()->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha);
plAccTriIterator tri(&src.AccessTri());
// For each tri
for( tri.Begin(); tri.More(); tri.Advance() )
{
// Do a polygon clip of tri to box
static hsTArray<plCutoutVtx> poly;
poly.SetCount(3);
// Not sure about this, whether the constant water height should be world space or local.
// We'll leave it in local for now.
const hsVector3 up(0, 0, 1.f);
hsPoint3 vPos[3];
vPos[0] = l2w * hsPoint3(tri.Position(0).fX, tri.Position(0).fY, src.GetWaterHeight());
vPos[1] = l2w * hsPoint3(tri.Position(1).fX, tri.Position(1).fY, src.GetWaterHeight());
vPos[2] = l2w * hsPoint3(tri.Position(2).fX, tri.Position(2).fY, src.GetWaterHeight());
poly[0].Init(l2w * hsPoint3(tri.Position(0).fX, tri.Position(0).fY, tri.Position(0).fZ), l2wNorm * up, tri.DiffuseRGBA(0));
poly[1].Init(l2w * hsPoint3(tri.Position(1).fX, tri.Position(1).fY, tri.Position(1).fZ), l2wNorm * up, tri.DiffuseRGBA(1));
poly[2].Init(l2w * hsPoint3(tri.Position(2).fX, tri.Position(2).fY, tri.Position(2).fZ), l2wNorm * up, tri.DiffuseRGBA(2));
// If we got a polygon
if( IPolyClip(poly, vPos) )
{
// tessalate the polygon into dst
IConstruct(dst, poly, baseHasAlpha);
}
}
}
// We usually don't need to do any transform, because the kind of surface you
// would leave prints on tends to be static, with the transform folded into the
// verts. So it's worth having 2 separate versions of the function.
void plCutter::ICutoutTransformed(plAccessSpan& src, hsTArray<plCutoutPoly>& dst) const
{
const hsMatrix44& l2w = src.GetLocalToWorld();
hsMatrix44 l2wNorm;
src.GetWorldToLocal().GetTranspose(&l2wNorm);
hsBool baseHasAlpha = 0 != (src.GetMaterial()->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha);
plAccTriIterator tri(&src.AccessTri());
// For each tri
for( tri.Begin(); tri.More(); tri.Advance() )
{
// Do a polygon clip of tri to box
static hsTArray<plCutoutVtx> poly;
poly.SetCount(3);
hsPoint3 vPos[3];
vPos[0] = l2w * tri.Position(0);
vPos[1] = l2w * tri.Position(1);
vPos[2] = l2w * tri.Position(2);
poly[0].Init(vPos[0], l2wNorm * tri.Normal(0), tri.DiffuseRGBA(0));
poly[1].Init(vPos[1], l2wNorm * tri.Normal(1), tri.DiffuseRGBA(1));
poly[2].Init(vPos[2], l2wNorm * tri.Normal(2), tri.DiffuseRGBA(2));
// If we got a polygon
if( IPolyClip(poly, vPos) )
{
// tessalate the polygon into dst
IConstruct(dst, poly, baseHasAlpha);
}
}
}
void plCutter::ICutoutConstHeight(plAccessSpan& src, hsTArray<plCutoutPoly>& dst) const
{
if( !(src.GetLocalToWorld().fFlags & hsMatrix44::kIsIdent) )
{
ICutoutTransformedConstHeight(src, dst);
return;
}
hsBool baseHasAlpha = 0 != (src.GetMaterial()->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha);
plAccTriIterator tri(&src.AccessTri());
// For each tri
for( tri.Begin(); tri.More(); tri.Advance() )
{
// Do a polygon clip of tri to box
static hsTArray<plCutoutVtx> poly;
poly.SetCount(3);
const hsVector3 up(0, 0, 1.f);
hsPoint3 vPos[3];
vPos[0].Set(tri.Position(0).fX, tri.Position(0).fY, src.GetWaterHeight());
vPos[1].Set(tri.Position(1).fX, tri.Position(1).fY, src.GetWaterHeight());
vPos[2].Set(tri.Position(2).fX, tri.Position(2).fY, src.GetWaterHeight());
poly[0].Init(hsPoint3(tri.Position(0).fX, tri.Position(0).fY, tri.Position(0).fZ), up, tri.DiffuseRGBA(0));
poly[1].Init(hsPoint3(tri.Position(1).fX, tri.Position(1).fY, tri.Position(1).fZ), up, tri.DiffuseRGBA(1));
poly[2].Init(hsPoint3(tri.Position(2).fX, tri.Position(2).fY, tri.Position(2).fZ), up, tri.DiffuseRGBA(2));
// If we got a polygon
if( IPolyClip(poly, vPos) )
{
// tessalate the polygon into dst
IConstruct(dst, poly, baseHasAlpha);
}
}
}
// Cutout
void plCutter::Cutout(plAccessSpan& src, hsTArray<plCutoutPoly>& dst) const
{
if( !src.HasAccessTri() )
return;
if( src.HasWaterHeight() )
{
ICutoutConstHeight(src, dst);
return;
}
if( !(src.GetLocalToWorld().fFlags & hsMatrix44::kIsIdent) )
{
ICutoutTransformed(src, dst);
return;
}
hsBool baseHasAlpha = 0 != (src.GetMaterial()->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha);
plAccTriIterator tri(&src.AccessTri());
// For each tri
for( tri.Begin(); tri.More(); tri.Advance() )
{
// Do a polygon clip of tri to box
static hsTArray<plCutoutVtx> poly;
poly.SetCount(3);
hsPoint3 vPos[3];
vPos[0] = tri.Position(0);
vPos[1] = tri.Position(1);
vPos[2] = tri.Position(2);
poly[0].Init(vPos[0], tri.Normal(0), tri.DiffuseRGBA(0));
poly[1].Init(vPos[1], tri.Normal(1), tri.DiffuseRGBA(1));
poly[2].Init(vPos[2], tri.Normal(2), tri.DiffuseRGBA(2));
// If we got a polygon
if( IPolyClip(poly, vPos) )
{
// tessalate the polygon into dst
IConstruct(dst, poly, baseHasAlpha);
}
}
}
void plCutter::IConstruct(hsTArray<plCutoutPoly>& dst, hsTArray<plCutoutVtx>& poly, hsBool baseHasAlpha) const
{
int iDst = dst.GetCount();
dst.Push();
dst[iDst].fVerts.Swap(poly);
dst[iDst].fBaseHasAlpha = baseHasAlpha;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
hsBool plCutter::CutoutGrid(int nWid, int nLen, plFlatGridMesh& grid) const
{
hsVector3 halfU = fDirU * (fLengthU * fLengthU * 0.5f);
hsVector3 halfV = fDirV * (fLengthV * fLengthV * 0.5f);
return MakeGrid(nWid, nLen, fWorldBounds.GetCenter(), halfU, halfV, grid);
}
hsBool plCutter::MakeGrid(int nWid, int nLen, const hsPoint3& center, const hsVector3& halfU, const hsVector3& halfV, plFlatGridMesh& grid)
{
if( nWid < 3 )
nWid = 3;
if( !(nWid & 0x1) )
nWid++;
if( nLen < 3 )
nLen = 3;
if( !(nLen & 0x1) )
nLen++;
grid.fVerts.SetCount(nWid * nLen);
hsVector3 dux = halfU;
hsVector3 dvx = halfV;
dux.fZ = 0;
dvx.fZ = 0;
hsPoint3 corner = center;
corner.fZ = 0;
corner += -dux;
corner += -dvx;
hsScalar sWid = 1.f / hsScalar(nWid-1);
hsScalar sLen = 1.f / hsScalar(nLen-1);
dux *= 2.f * sWid;
dvx *= 2.f * sLen;
hsScalar du = sWid;
hsScalar dv = sLen;
int j;
for( j = 0; j < nLen; j++ )
{
int i;
for( i = 0; i < nWid; i++ )
{
plCutoutMiniVtx& vtx = grid.fVerts[j * nWid + i];
vtx.fPos = corner;
vtx.fPos += dux * (hsScalar)i;
vtx.fPos += dvx * (hsScalar)j;
vtx.fUVW.fX = du * i;
vtx.fUVW.fY = dv * j;
vtx.fUVW.fZ = 0.5f;
}
}
int idx = 0;
grid.fIdx.SetCount(2 * (nWid-1) * (nLen-1) * 3);
for( j = 1; j < nLen; )
{
int i;
for( i = 1; i < nWid; )
{
grid.fIdx[idx++] = j * nWid + (i-1);
grid.fIdx[idx++] = j * nWid + i;
grid.fIdx[idx++] = (j-1) * nWid + (i-1);
grid.fIdx[idx++] = (j-1) * nWid + (i-1);
grid.fIdx[idx++] = j * nWid + i;
grid.fIdx[idx++] = (j-1) * nWid + i;
i++;
grid.fIdx[idx++] = (j-1) * nWid + (i-1);
grid.fIdx[idx++] = j * nWid + (i-1);
grid.fIdx[idx++] = (j-1) * nWid + i;
grid.fIdx[idx++] = (j-1) * nWid + i;
grid.fIdx[idx++] = j * nWid + (i-1);
grid.fIdx[idx++] = j * nWid + i;
i++;
}
j++;
for( i = 1; i < nWid; )
{
grid.fIdx[idx++] = (j-1) * nWid + (i-1);
grid.fIdx[idx++] = j * nWid + (i-1);
grid.fIdx[idx++] = (j-1) * nWid + i;
grid.fIdx[idx++] = (j-1) * nWid + i;
grid.fIdx[idx++] = j * nWid + (i-1);
grid.fIdx[idx++] = j * nWid + i;
i++;
grid.fIdx[idx++] = j * nWid + (i-1);
grid.fIdx[idx++] = j * nWid + i;
grid.fIdx[idx++] = (j-1) * nWid + (i-1);
grid.fIdx[idx++] = (j-1) * nWid + (i-1);
grid.fIdx[idx++] = j * nWid + i;
grid.fIdx[idx++] = (j-1) * nWid + i;
i++;
}
j++;
}
return grid.fIdx.GetCount() > 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Test hackage ensues
/////////////////////////////////////////////////////////////////////////////////////////////////////
void TestCutter(const plKey& key, const hsVector3& size, const hsPoint3& pos)
{
plCutter cutter;
cutter.SetLength(size);
static hsVector3 dir(0, 1.f, 0);
static hsVector3 up(0, 0, 1.f);
cutter.Set(pos, dir, up);
plSceneObject* so = plSceneObject::ConvertNoRef(key->ObjectIsLoaded());
if( !so )
return;
const plDrawInterface* di = so->GetDrawInterface();
if( !di )
return;
static plDrawableSpans* drawable = nil;
hsBool newDrawable = !drawable;
hsBool haveNormal = true;
hsTArray<UInt32> retIndex;
hsTArray<plAccessSpan> src;
plAccessGeometry::Instance()->OpenRO(di, src);
if( !src.GetCount() )
return;
int i;
for( i = 0; i < src.GetCount(); i++ )
{
static hsTArray<plCutoutPoly> dst;
dst.SetCount(0);
#if 1
cutter.Cutout(src[i], dst);
#else
hsPoint3 corner;
hsVector3 ax[3];
cutter.GetWorldBounds().GetCorner(&corner);
cutter.GetWorldBounds().GetAxes(ax+0, ax+1, ax+2);
int iAx = 0;
int jAx = 1;
dst.SetCount(6);
int xx;
for( xx = 0; xx < 3; xx++ )
{
dst[xx].fVerts.SetCount(4);
dst[xx].fVerts[0].fPos = corner;
dst[xx].fVerts[0].fNorm.Set(0,0,1.f);
dst[xx].fVerts[0].fUVW.Set(0,0,0);
dst[xx].fVerts[1].fPos = corner;
dst[xx].fVerts[1].fPos += ax[iAx];
dst[xx].fVerts[1].fNorm.Set(0,0,1.f);
dst[xx].fVerts[1].fUVW.Set(1,0,0);
dst[xx].fVerts[2].fPos = corner;
dst[xx].fVerts[2].fPos += ax[iAx];
dst[xx].fVerts[2].fPos += ax[jAx];
dst[xx].fVerts[2].fNorm.Set(0,0,1.f);
dst[xx].fVerts[2].fUVW.Set(1.f,1.f,0);
dst[xx].fVerts[3].fPos = corner;
dst[xx].fVerts[3].fPos += ax[jAx];
dst[xx].fVerts[3].fNorm.Set(0,0,1.f);
dst[xx].fVerts[3].fUVW.Set(0,1.f,0);
iAx++;
jAx = iAx > 1 ? 0 : iAx+1;
}
corner += ax[0];
corner += ax[1];
corner += ax[2];
ax[0] = -ax[0];
ax[1] = -ax[1];
ax[2] = -ax[2];
iAx = 0;
jAx = 1;
for( xx = 3; xx < 6; xx++ )
{
dst[xx].fVerts.SetCount(4);
dst[xx].fVerts[0].fPos = corner;
dst[xx].fVerts[0].fNorm.Set(0,0,1.f);
dst[xx].fVerts[0].fUVW.Set(0,0,0);
dst[xx].fVerts[3].fPos = corner;
dst[xx].fVerts[3].fPos += ax[iAx];
dst[xx].fVerts[3].fNorm.Set(0,0,1.f);
dst[xx].fVerts[3].fUVW.Set(1.f,0,0);
dst[xx].fVerts[2].fPos = corner;
dst[xx].fVerts[2].fPos += ax[iAx];
dst[xx].fVerts[2].fPos += ax[jAx];
dst[xx].fVerts[2].fNorm.Set(0,0,1.f);
dst[xx].fVerts[2].fUVW.Set(1.f,1.f,0);
dst[xx].fVerts[1].fPos = corner;
dst[xx].fVerts[1].fPos += ax[jAx];
dst[xx].fVerts[1].fNorm.Set(0,0,1.f);
dst[xx].fVerts[1].fUVW.Set(0,1.f,0);
iAx++;
jAx = iAx > 1 ? 0 : iAx+1;
}
haveNormal = false;
#endif
// What's our total number of verts?
// Total number of tris?
int numVerts = 0;
int numTris = 0;
int j;
for( j = 0; j < dst.GetCount(); j++ )
{
if( dst[j].fVerts.GetCount() )
{
numVerts += dst[j].fVerts.GetCount();
numTris += dst[j].fVerts.GetCount()-2;
}
}
if( !numTris )
continue;
hsTArray<hsPoint3> pos;
pos.SetCount(numVerts);
hsTArray<hsVector3> norm;
norm.SetCount(numVerts);
hsTArray<hsPoint3> uvw;
uvw.SetCount(numVerts);
hsTArray<hsColorRGBA> col;
col.SetCount(numVerts);
int iPoly = 0;
int iVert = 0;
int iv;
for( iv = 0; iv < numVerts; iv++ )
{
pos[iv] = dst[iPoly].fVerts[iVert].fPos;
norm[iv] = dst[iPoly].fVerts[iVert].fNorm;
uvw[iv] = dst[iPoly].fVerts[iVert].fUVW;
col[iv] = dst[iPoly].fVerts[iVert].fColor;
hsScalar opac = uvw[iv].fZ < 0.25f
? uvw[iv].fZ * 4.f
: uvw[iv].fZ > 0.75f
? (1.f - uvw[iv].fZ) * 4.f
: 1.f;
opac *= norm[iv].fZ;
if( opac < 0 )
opac = 0;
if( dst[iPoly].fBaseHasAlpha )
col[iv].a *= opac;
else
col[iv].a = opac;
if( ++iVert >= dst[iPoly].fVerts.GetCount() )
{
iVert = 0;
iPoly++;
}
}
hsTArray<UInt16> idx;
UInt16 base = 0;
for( j = 0; j < dst.GetCount(); j++ )
{
UInt16 next = base+1;
int k;
for( k = 2; k < dst[j].fVerts.GetCount(); k++ )
{
idx.Append(base);
idx.Append(next++);
idx.Append(next);
}
base = ++next;
}
drawable = plDrawableGenerator::GenerateDrawable( numVerts, pos.AcquireArray(),
haveNormal ? norm.AcquireArray() : nil,
uvw.AcquireArray(), 1,
col.AcquireArray(),
true,
nil,
idx.GetCount(), idx.AcquireArray(),
src[i].GetMaterial(),
hsMatrix44::IdentityMatrix(),
true,
&retIndex,
drawable);
}
if( drawable && newDrawable )
drawable->SetSceneNode(so->GetSceneNode());
}
void TestCutter2(const plKey& key, const hsVector3& size, const hsPoint3& pos, hsBool flip)
{
plCutter cutter;
cutter.SetLength(size);
static hsVector3 dir(0, 1.f, 0);
static hsVector3 up(0, 0, 1.f);
cutter.Set(pos, dir, up, flip);
plSceneObject* so = plSceneObject::ConvertNoRef(key->ObjectIsLoaded());
if( !so )
return;
plSceneNode* node = plSceneNode::ConvertNoRef(so->GetSceneNode()->ObjectIsLoaded());
if( !node )
return;
static plDrawableSpans* drawable = nil;
hsBool newDrawable = !drawable;
hsBool haveNormal = true;
hsTArray<UInt32> retIndex;
hsTArray<plDrawVisList> drawVis;
node->Harvest(&cutter.GetIsect(), drawVis);
if( !drawVis.GetCount() )
return;
hsTArray<plAccessSpan> src;
int numSpan = 0;
int iDraw;
for( iDraw = 0; iDraw < drawVis.GetCount(); iDraw++ )
numSpan += drawVis[iDraw].fVisList.GetCount();
src.SetCount(numSpan);
int i;
iDraw = 0;
int iSpan = 0;
for( i = 0; i < numSpan; i++ )
{
plAccessGeometry::Instance()->OpenRO(drawVis[iDraw].fDrawable, drawVis[iDraw].fVisList[iSpan], src[i]);
if( ++iSpan >= drawVis[iDraw].fVisList.GetCount() )
{
iDraw++;
iSpan = 0;
}
}
for( i = 0; i < src.GetCount(); i++ )
{
static hsTArray<plCutoutPoly> dst;
dst.SetCount(0);
cutter.Cutout(src[i], dst);
// What's our total number of verts?
// Total number of tris?
int numVerts = 0;
int numTris = 0;
int j;
for( j = 0; j < dst.GetCount(); j++ )
{
if( dst[j].fVerts.GetCount() )
{
numVerts += dst[j].fVerts.GetCount();
numTris += dst[j].fVerts.GetCount()-2;
}
}
if( !numTris )
continue;
hsTArray<hsPoint3> pos;
pos.SetCount(numVerts);
hsTArray<hsVector3> norm;
norm.SetCount(numVerts);
hsTArray<hsPoint3> uvw;
uvw.SetCount(numVerts);
hsTArray<hsColorRGBA> col;
col.SetCount(numVerts);
int iPoly = 0;
int iVert = 0;
int iv;
for( iv = 0; iv < numVerts; iv++ )
{
pos[iv] = dst[iPoly].fVerts[iVert].fPos;
norm[iv] = dst[iPoly].fVerts[iVert].fNorm;
uvw[iv] = dst[iPoly].fVerts[iVert].fUVW;
col[iv] = dst[iPoly].fVerts[iVert].fColor;
hsScalar opac = uvw[iv].fZ < 0.25f
? uvw[iv].fZ * 4.f
: uvw[iv].fZ > 0.75f
? (1.f - uvw[iv].fZ) * 4.f
: 1.f;
opac *= norm[iv].fZ;
if( opac < 0 )
opac = 0;
if( dst[iPoly].fBaseHasAlpha )
col[iv].a *= opac;
else
col[iv].a = opac;
if( ++iVert >= dst[iPoly].fVerts.GetCount() )
{
iVert = 0;
iPoly++;
}
}
hsTArray<UInt16> idx;
UInt16 base = 0;
for( j = 0; j < dst.GetCount(); j++ )
{
UInt16 next = base+1;
int k;
for( k = 2; k < dst[j].fVerts.GetCount(); k++ )
{
idx.Append(base);
idx.Append(next++);
idx.Append(next);
}
base = ++next;
}
drawable = plDrawableGenerator::GenerateDrawable( numVerts, pos.AcquireArray(),
haveNormal ? norm.AcquireArray() : nil,
uvw.AcquireArray(), 1,
col.AcquireArray(),
false,
nil,
idx.GetCount(), idx.AcquireArray(),
src[i].GetMaterial(),
hsMatrix44::IdentityMatrix(),
true,
&retIndex,
drawable);
}
for( i = 0; i < numSpan; i++ )
{
plAccessGeometry::Instance()->Close(src[i]);
}
if( drawable && newDrawable )
drawable->SetSceneNode(so->GetSceneNode());
}