tge/tools/map2difPlus/csgBrush.cc
2017-04-17 06:17:10 -06:00

537 lines
20 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2003 GarageGames.Com
//-----------------------------------------------------------------------------
#include "dgl/gBitmap.h"
#include "map2difPlus/csgBrush.h"
#include "core/tokenizer.h"
extern int gQuakeVersion;
// For the moment these exist in InteriorMap.cc
// MDFFIX: clean this up
extern F32 baseaxis[18][3];
extern void textureAxisFromPlane(const VectorF& normal, F32* xv, F32* yv);
extern void quakeTextureVecs(const VectorF& normal, F32 offsetX, F32 offsetY, F32 rotate, F32 scaleX, F32 scaleY, PlaneF* u, PlaneF *v);
bool parseBrush (CSGBrush& rBrush, Tokenizer* pToker, EditGeometry& geom)
{
while (pToker->getToken()[0] == '(') {
// Enter the plane...
F64 points[3][3];
for (S32 i = 0; i < 3; i++) {
if (pToker->getToken()[0] != '(')
goto EntityBrushlistError;
for (S32 j = 0; j < 3; j++) {
pToker->advanceToken(false);
points[i][j] = dAtof(pToker->getToken());
}
pToker->advanceToken(false);
if (pToker->getToken()[0] != ')')
goto EntityBrushlistError;
pToker->advanceToken(false);
}
CSGPlane& rPlane = rBrush.constructBrushPlane(Point3D(points[0][0], points[0][1], points[0][2]),
Point3D(points[1][0], points[1][1], points[1][2]),
Point3D(points[2][0], points[2][1], points[2][2]));
// advanced already...
if (pToker->tokenAvailable() == false)
goto EntityBrushlistError;
rPlane.pTextureName = geom.insertTexture(pToker->getToken());
// convert tex name to upper case
char *str = (char *) rPlane.pTextureName;
while( *str )
{
*str = dToupper( (const char) *str );
str++;
}
U32 bmIndex = geom.getMaterialIndex(rPlane.pTextureName);
const GBitmap* pBitmap = geom.mTextures[bmIndex];
PlaneF tGenX;
PlaneF tGenY;
if (gQuakeVersion == 2)
{
//hl ( 564 -396 -672 ) ( 564 -372 -672 ) ( 512 -372 -672 ) WALL_BLOCK01 [ 1 0 0 -213 ] [ 0 -1 0 -117 ] 0 1 1
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
if (pToker->getToken()[0] != '[') goto EntityBrushlistError;
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
tGenX.x = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
tGenX.y = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
tGenX.z = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
tGenX.d = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
if (pToker->getToken()[0] != ']') goto EntityBrushlistError;
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
if (pToker->getToken()[0] != '[') goto EntityBrushlistError;
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
tGenY.x = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
tGenY.y = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
tGenY.z = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
tGenY.d = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
if (pToker->getToken()[0] != ']') goto EntityBrushlistError;
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
F32 scaleX = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
F32 scaleY = dAtof(pToker->getToken());
tGenX.x /= scaleX * F32(pBitmap->getWidth());
tGenX.y /= scaleX * F32(pBitmap->getWidth());
tGenX.z /= scaleX * F32(pBitmap->getWidth());
tGenX.d /= F32(pBitmap->getWidth());
tGenY.x /= scaleY * F32(pBitmap->getHeight());
tGenY.y /= scaleY * F32(pBitmap->getHeight());
tGenY.z /= scaleY * F32(pBitmap->getHeight());
tGenY.d /= F32(pBitmap->getHeight());
}
else if (gQuakeVersion == 3)
{
//q1 ( 88 -40 8 ) ( -56 -40 8 ) ( -56 -64 8 ) block10d 0 0 0 1.000000 1.000000
//q2 ( 88 -40 8 ) ( -56 -40 8 ) ( -56 -64 8 ) block10d 0 0 0 1.000000 1.000000
//q3 ( 88 -40 8 ) ( -56 -40 8 ) ( -56 -64 8 ) block10d 0 0 0 0.500000 0.500000 0 0 0
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
F32 shiftU = dAtof (pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
F32 shiftV = dAtof (pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
F32 rot = dAtof (pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
F32 scaleX = dAtof(pToker->getToken());
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
F32 scaleY = dAtof(pToker->getToken());
// Skip last 3 tokens
//if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
//if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
//if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
pToker->advanceToken(false);
pToker->advanceToken(false);
pToker->advanceToken(false);
// Compute the normal
VectorF normal;
VectorF t1, t2;
t1 = VectorF (points[0][0], points[0][1], points[0][2]) - VectorF (points[1][0], points[1][1], points[1][2]);
t2 = VectorF (points[2][0], points[2][1], points[2][2]) - VectorF (points[1][0], points[1][1], points[1][2]);
mCross (t1, t2, normal);
normal.normalize ();
quakeTextureVecs (normal, shiftU, shiftV, rot, scaleX, scaleY, &tGenX, &tGenY);
F32 width = F32(pBitmap->getWidth());
F32 height = F32(pBitmap->getHeight());
tGenX.x /= F32(pBitmap->getWidth());
tGenX.y /= F32(pBitmap->getWidth());
tGenX.z /= F32(pBitmap->getWidth());
tGenX.d /= F32(pBitmap->getWidth());
tGenY.x /= F32(pBitmap->getHeight());
tGenY.y /= F32(pBitmap->getHeight());
tGenY.z /= F32(pBitmap->getHeight());
tGenY.d /= F32(pBitmap->getHeight());
}
else
{
goto EntityBrushlistError;
}
// add it...
bool found = false;
for(U32 i = 0; !found && (i < geom.mTexGenEQs.size()); i++)
if(!dMemcmp(&geom.mTexGenEQs[i].planeX, &tGenX, sizeof(PlaneF)) &&
!dMemcmp(&geom.mTexGenEQs[i].planeY, &tGenY, sizeof(PlaneF)))
{
found = true;
rPlane.texGenIndex = i;
}
//
if(!found)
{
geom.mTexGenEQs.increment();
geom.mTexGenEQs.last().planeX = tGenX;
geom.mTexGenEQs.last().planeY = tGenY;
rPlane.texGenIndex = geom.mTexGenEQs.size() - 1;
}
pToker->advanceToken(true);
}
return true;
EntityBrushlistError:
return false;
}
U32 CSGBrush::addPlane(CSGPlane plane)
{
for (U32 i = 0; i < mPlanes.size(); i++)
{
if (mPlanes[i].flags == plane.flags &&
mPlanes[i].owningEntity == plane.owningEntity &&
mPlanes[i].planeEQIndex == plane.planeEQIndex &&
mPlanes[i].pTextureName == plane.pTextureName &&
mPlanes[i].texGenIndex == plane.texGenIndex)
return i;
}
mPlanes.increment();
mPlanes.last() = plane;
return mPlanes.size() - 1;
}
void CSGPlane::construct(const Point3D& Point1, const Point3D& Point2, const Point3D& Point3)
{
// |yi zi 1| |xi zi 1| |xi yi 1| |xi yi zi|
// |yj zj 1| x + |xj zj 1| y + |xj yj 1| z = |xj yj zj|
// |yk zk 1| |xk zk 1| |xk yk 1| |xk yk zk|
//
Point3D normal;
F64 dist;
normal.x = Point1.y * Point2.z - Point1.y * Point3.z +
Point3.y * Point1.z - Point2.y * Point1.z +
Point2.y * Point3.z - Point3.y * Point2.z;
normal.y = Point1.x * Point2.z - Point1.x * Point3.z +
Point3.x * Point1.z - Point2.x * Point1.z +
Point2.x * Point3.z - Point3.x * Point2.z;
normal.z = Point1.x * Point2.y - Point1.x * Point3.y +
Point3.x * Point1.y - Point2.x * Point1.y +
Point2.x * Point3.y - Point3.x * Point2.y;
dist = Point1.x * Point2.y * Point3.z - Point1.x * Point2.z * Point3.y +
Point1.y * Point2.z * Point3.x - Point1.y * Point2.x * Point3.z +
Point1.z * Point2.x * Point3.y - Point1.z * Point2.y * Point3.x;
normal.x = -normal.x;
normal.z = -normal.z;
//
planeEQIndex = gWorkingGeometry->insertPlaneEQ(normal, dist);
flags = 0;
}
//------------------------------------------------------------------------------
// Needs to insert new plane equation. parameter is positive amount
// to extrude outward.
void CSGPlane::extrude(F64 byAmount)
{
const PlaneEQ & eq = gWorkingGeometry->getPlaneEQ(planeEQIndex);
planeEQIndex = gWorkingGeometry->insertPlaneEQ(eq.normal, eq.dist - byAmount);
}
//------------------------------------------------------------------------------
bool CSGPlane::createBaseWinding(const Vector<U32>& rPoints)
{
return ::createBaseWinding(rPoints, getNormal(), &winding);
}
//------------------------------------------------------------------------------
// Try a more accurate version of this.
PlaneSide CSGPlane::sideCheckEpsilon(const Point3D& testPoint, F64 epsilon) const
{
F64 distance = distanceToPlane(testPoint);
if (distance < - epsilon)
return PlaneBack;
else if (distance > epsilon)
return PlaneFront;
else
return PlaneOn;
}
// Need more info for asserts.
const char* CSGPlane::sideCheckInfo(const Point3D & P, const char * msg, F64 E) const
{
F64 D = distanceToPlane(P);
const char * txt = D < -E ? "BACK" : (D > E ? "FRONT" : "ON");
return avar( "%s: Side=%s E=%lf D=%1.20lf P=(%lf,%lf,%lf)", msg, txt, E, D, P.x, P.y, P.z );
}
// See if this plane lies along an edge of the supplied winding. Return the
// points if so (they're needed), else return NULL for false.
//
// Maybe need a better point here.
Point3D * CSGPlane::sharesEdgeWith(const Winding & W) const
{
static Point3D edgePts[2];
for( U32 i = 0; i < W.numIndices; i++ )
{
edgePts[0] = gWorkingGeometry->getPoint(W.indices[i]);
// if( sideCheckEpsilon( edgePts[0] ) == PlaneOn )
if( whichSide( edgePts[0] ) == PlaneOn )
{
edgePts[1] = gWorkingGeometry->getPoint(W.indices[(i + 1) % W.numIndices]);
// if( sideCheckEpsilon( edgePts[1] ) == PlaneOn )
if( whichSide( edgePts[1] ) == PlaneOn )
return edgePts;
}
}
return NULL;
}
// Assuming this is a brush that has already been selfClip()d, make
// and return a new brush that is extruded version of this
// one (tho not clipped) in given direction.
//
// If doBoth, then extrude also in negative of direction.
//
// Adds additional planes on those edges which are boundaries with
// respect to the direction axis. That is, those edges which will
// will be on the outside of the shape perimeter when viewing
// along the given direction axis, and so planes need to be added
// to limit unwanted extrusion outside of the intended axis.
//
CSGBrush* CSGBrush::createExtruded(const Point3D & extrudeDir, bool doBoth) const
{
CSGBrush * newBrush = gBrushArena.allocateBrush();
newBrush->copyBrush( this );
for( U32 i = 0; i < mPlanes.size(); i++ )
{
// Get extrusion component along normal.
const Point3D & curNormal = mPlanes[i].getNormal();
F64 extrudeComponent = mDot( extrudeDir, curNormal );
if( mFabsD(extrudeComponent) > 0.001 )
{
const Winding & curWinding = mPlanes[i].winding;
// Look for planes that are opposite with respect to this
// direction and which have a shared edge. We create 'clamping'
// planes along such edges to avoid spikes.
for( U32 j = i + 1; j < mPlanes.size(); j++ )
{
if( mDot(mPlanes[j].getNormal(),extrudeDir) * extrudeComponent < -0.0001 )
{
if( Point3D * edgePts = mPlanes[j].sharesEdgeWith(curWinding) )
{
Point3D other = edgePts[0] + extrudeDir;
CSGPlane & rPlane = (extrudeComponent > 0)
?
newBrush->constructBrushPlane( other, edgePts[0], edgePts[1] )
:
newBrush->constructBrushPlane( edgePts[1], edgePts[0], other );
rPlane.pTextureName = mPlanes[0].pTextureName;
rPlane.xShift = 0;
rPlane.yShift = 0;
rPlane.rotation = 0;
rPlane.xScale = rPlane.yScale = 1.0;
}
}
}
if( extrudeComponent > 0 || doBoth )
newBrush->mPlanes[i].extrude( mFabsD( extrudeComponent ) );
}
}
AssertFatal( newBrush->mPlanes.size() >= mPlanes.size(),
"CSGBrush::createExtruded(): new brush shouldn't be smaller" );
return newBrush;
}
//------------------------------------------------------------------------------
bool CSGBrush::intersectPlanes(U32 i, U32 j, U32 k,
Point3D* pOutput)
{
AssertFatal(i < mPlanes.size() && j < mPlanes.size() && k < mPlanes.size() &&
i != j && i != k && j != k, "CSGBrush::intersectPlanes: bad plane indices");
CSGPlane& rPlane1 = mPlanes[i];
CSGPlane& rPlane2 = mPlanes[j];
CSGPlane& rPlane3 = mPlanes[k];
const PlaneEQ& rPlaneEQ1 = gWorkingGeometry->getPlaneEQ(rPlane1.planeEQIndex);
const PlaneEQ& rPlaneEQ2 = gWorkingGeometry->getPlaneEQ(rPlane2.planeEQIndex);
const PlaneEQ& rPlaneEQ3 = gWorkingGeometry->getPlaneEQ(rPlane3.planeEQIndex);
F64 bc = (rPlaneEQ2.normal.y * rPlaneEQ3.normal.z) - (rPlaneEQ3.normal.y * rPlaneEQ2.normal.z);
F64 ac = (rPlaneEQ2.normal.x * rPlaneEQ3.normal.z) - (rPlaneEQ3.normal.x * rPlaneEQ2.normal.z);
F64 ab = (rPlaneEQ2.normal.x * rPlaneEQ3.normal.y) - (rPlaneEQ3.normal.x * rPlaneEQ2.normal.y);
F64 det = (rPlaneEQ1.normal.x * bc) - (rPlaneEQ1.normal.y * ac) + (rPlaneEQ1.normal.z * ab);
if (mFabs(det) < 1e-7) {
// Parallel planes
return false;
}
F64 dc = (rPlaneEQ2.dist * rPlaneEQ3.normal.z) - (rPlaneEQ3.dist * rPlaneEQ2.normal.z);
F64 db = (rPlaneEQ2.dist * rPlaneEQ3.normal.y) - (rPlaneEQ3.dist * rPlaneEQ2.normal.y);
F64 ad = (rPlaneEQ3.dist * rPlaneEQ2.normal.x) - (rPlaneEQ2.dist * rPlaneEQ3.normal.x);
F64 detInv = 1.0 / det;
pOutput->x = ((rPlaneEQ1.normal.y * dc) - (rPlaneEQ1.dist * bc) - (rPlaneEQ1.normal.z * db)) * detInv;
pOutput->y = ((rPlaneEQ1.dist * ac) - (rPlaneEQ1.normal.x * dc) - (rPlaneEQ1.normal.z * ad)) * detInv;
pOutput->z = ((rPlaneEQ1.normal.y * ad) + (rPlaneEQ1.normal.x * db) - (rPlaneEQ1.dist * ab)) * detInv;
return true;
}
//------------------------------------------------------------------------------
bool CSGBrush::selfClip()
{
// MDFFIX: Really hacky way of doing it for now but it will work
ConvexBrush brush;
CopyCSGToConvex(this, &brush);
CopyConvexToCSG(&brush, this);
return true;
}
void CSGBrush::copyBrush(const CSGBrush* pCopy)
{
mPlanes = pCopy->mPlanes;
mIsAmbiguous = pCopy->mIsAmbiguous;
mBrushType = pCopy->mBrushType;
mMinBound = pCopy->mMinBound;
mMaxBound = pCopy->mMaxBound;
sgLightingScale = pCopy->sgLightingScale;
sgSelfIllumination = pCopy->sgSelfIllumination;
brushId = pCopy->brushId;
materialType = pCopy->materialType;
}
//------------------------------------------------------------------------------
bool CSGBrush::disambiguate()
{
AssertFatal(mIsAmbiguous == false, "error, already disambiguated?");
for (U32 i = 0; i < mPlanes.size(); i++) {
for (U32 j = i + 1; j < mPlanes.size();) {
// Compare i to j. if j == i, with different tex parameters, increment
// ambiguous mInteriorRes->mBrushes (once only), and remove the plane.
//
CSGPlane& rPlane1 = mPlanes[i];
CSGPlane& rPlane2 = mPlanes[j];
if (rPlane1.planeEQIndex == rPlane2.planeEQIndex) {
// Possible ambiguous plane pairing...
//
if (rPlane1.pTextureName != rPlane2.pTextureName ||
rPlane1.xShift != rPlane2.xShift ||
rPlane1.yShift != rPlane2.yShift ||
rPlane1.rotation != rPlane2.rotation ||
rPlane1.xScale != rPlane2.xScale ||
rPlane1.yScale != rPlane2.yScale) {
mIsAmbiguous = true;
} else {
// Same texture parameters, should be fine, just erase it...
//
}
mPlanes.erase(j);
} else {
// Plane is fine...
j++;
}
}
}
return mIsAmbiguous;
}
//------------------------------------------------------------------------------
/*CSGPlane& CSGBrush::constructBrushPlane(const Point3I& rPoint1,
const Point3I& rPoint2,
const Point3I& rPoint3)
{
mPlanes.increment();
CSGPlane& rPlane = mPlanes.last();
rPlane.flags = 0;
rPlane.owningEntity = NULL;
rPlane.winding.numNodes = 0;
rPlane.winding.numZoneIds = 0;
Point3D Point1(rPoint1.x, rPoint1.y, rPoint1.z);
Point3D Point2(rPoint2.x, rPoint2.y, rPoint2.z);
Point3D Point3(rPoint3.x, rPoint3.y, rPoint3.z);
rPlane.construct(Point1, Point2, Point3);
if (rPlane.getNormal().x == 0 && rPlane.getNormal().y == 0 && rPlane.getNormal().z == 0) {
AssertISV(false, "Error, degenerate plane (colinear points)");
mPlanes.decrement();
return *((CSGPlane*)NULL);
}
return rPlane;
}*/
CSGPlane& CSGBrush::constructBrushPlane(const Point3D& Point1,
const Point3D& Point2,
const Point3D& Point3)
{
mPlanes.increment();
CSGPlane& rPlane = mPlanes.last();
rPlane.flags = 0;
rPlane.owningEntity = NULL;
rPlane.winding.numNodes = 0;
rPlane.winding.numZoneIds = 0;
rPlane.construct(Point1, Point2, Point3);
if (rPlane.getNormal().x == 0 && rPlane.getNormal().y == 0 && rPlane.getNormal().z == 0) {
AssertISV(false, "Error, degenerate plane (colinear points)");
mPlanes.decrement();
return *((CSGPlane*)NULL);
}
return rPlane;
}
bool CSGBrush::isEquivalent(const CSGBrush& testBrush) const
{
for (U32 i = 0; i < mPlanes.size(); i++) {
U32 j;
for (j = 0; j < testBrush.mPlanes.size(); j++) {
if (testBrush.mPlanes[j].planeEQIndex == mPlanes[i].planeEQIndex)
break;
}
if (j == testBrush.mPlanes.size())
return false;
}
return true;
}