/*==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 .
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==*/
#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& poly, const hsPoint3 vPos[]) const
{
static hsTArray 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& inPoly, plCutoutHit& hit) const
{
static hsTArray accum;
static hsTArray 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& src, hsTArray& 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& src, hsTArray& 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& 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 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& 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 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& 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 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& 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 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& dst, hsTArray& 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 retIndex;
hsTArray src;
plAccessGeometry::Instance()->OpenRO(di, src);
if( !src.GetCount() )
return;
int i;
for( i = 0; i < src.GetCount(); i++ )
{
static hsTArray 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 pos;
pos.SetCount(numVerts);
hsTArray norm;
norm.SetCount(numVerts);
hsTArray uvw;
uvw.SetCount(numVerts);
hsTArray 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 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 retIndex;
hsTArray drawVis;
node->Harvest(&cutter.GetIsect(), drawVis);
if( !drawVis.GetCount() )
return;
hsTArray 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 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 pos;
pos.SetCount(numVerts);
hsTArray norm;
norm.SetCount(numVerts);
hsTArray uvw;
uvw.SetCount(numVerts);
hsTArray 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 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());
}