Initial commit

This commit is contained in:
Eagle517
2025-02-17 23:17:30 -06:00
commit 7cad314c94
4726 changed files with 1145203 additions and 0 deletions

1397
tools/map2dif/bspNode.cc Executable file

File diff suppressed because it is too large Load Diff

201
tools/map2dif/bspNode.h Executable file
View File

@ -0,0 +1,201 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _BSPNODE_H_
#define _BSPNODE_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _MORIANBASICS_H_
#include "map2dif/morianBasics.h"
#endif
class CSGBrush;
class CSGPlane;
//------------------------------------------------------------------------------
class EditBSPNode {
public:
struct PortalEntry {
public:
~PortalEntry();
Vector<Winding*> windings;
U32 planeEQIndex;
U32 portalId;
Point3D ortho1;
Point3D ortho2;
void copyEntry(const PortalEntry&);
};
struct VisLink {
public:
Winding winding;
U32 planeEQIndex;
S32 portalId;
EditBSPNode* pFront;
EditBSPNode* pBack;
VisLink* pNext;
public:
bool intersect(U32 planeEQIndex);
PlaneSide whichSide(U32 planeEQIndex);
};
enum PlaneType {
Structural = 0,
Portal = 1,
Detail = 2
};
private:
void createPortalWindings(const PortalEntry& rBaseWindings, PortalEntry* pFinalEntry);
private:
S32 calcPlaneRating(U32 testPlane);
bool planeInParents(U32 testPlane);
U32 selectBestPlane();
void eraseReferencingLinks();
void splitBrushList();
void splitVisLinks();
void splitPortalEntries();
CSGBrush* findSolidBrush();
public:
S32 planeEQIndex;
EditBSPNode* pParent;
EditBSPNode* pFront;
EditBSPNode* pBack;
PlaneType planeType;
bool isSolid;
CSGBrush* solidBrush;
S32 zoneId;
U32 nodeId;
// Portal information
Vector<PortalEntry*> portals;
// Vising information
Vector<VisLink*> visLinks;
void enterLink(VisLink*);
// For building BSP. List of Brushes
Vector<CSGBrush*> brushList;
public:
EditBSPNode() : planeEQIndex(-1), planeType(Detail),
pParent(NULL), pFront(NULL), pBack(NULL) { isSolid = false; solidBrush = NULL; }
~EditBSPNode();
//-------------------------------------- BSP/Portal/Zone creation/manipulation
public:
void createBSP(PlaneType _planeType);
void createVisLinks();
void zoneFlood();
void floodZone(S32 markZoneId);
void gatherPortals();
S32 findZone(const Point3D& rPoint);
PortalEntry* mungePortalBrush(CSGBrush* pBrush);
void insertPortalEntry(PortalEntry* pPortal);
void breakWindingS(const Vector<Winding>& windings,
U32 windingPlaneIndex,
const CSGBrush* sourceBrush,
Vector<Winding>& outputWindings);
void breakWinding(const Vector<Winding>& windings,
U32 windingPlaneIndex,
const CSGBrush* sourceBrush,
Vector<Winding>& outputWindings);
//-------------------------------------- Statistics
public:
void gatherBSPStats(U32* pNumLeaves,
U32* pTotalLeafDepth,
U32* pMaxLeafDepth,
U32 depth);
void gatherVISStats(U32* pEmptyLeaves,
U32* pTotalLinks,
U32* pMaxLinks);
void removeVISInfo();
};
inline void EditBSPNode::enterLink(VisLink* pEnter)
{
visLinks.push_back(pEnter);
}
//------------------------------------------------------------------------------
class NodeArena {
public:
Vector<EditBSPNode*> mBuffers;
U32 arenaSize;
EditBSPNode* currBuffer;
U32 currPosition;
public:
NodeArena(U32 _arenaSize);
~NodeArena();
EditBSPNode* allocateNode(EditBSPNode* pParent);
};
inline EditBSPNode* NodeArena::allocateNode(EditBSPNode* _pParent)
{
static U32 sNodeIdAlloc = 0;
AssertFatal(currPosition <= arenaSize, "How did this happen? Arenasize is absolute!");
if (currPosition == arenaSize) {
// Need to allocate a new buffer...
currBuffer = new EditBSPNode[arenaSize];
currPosition = 0;
mBuffers.push_back(currBuffer);
}
EditBSPNode* pRet = &currBuffer[currPosition++];
pRet->pParent = _pParent;
pRet->zoneId = -1;
pRet->nodeId = sNodeIdAlloc++;
return pRet;
}
class VisLinkArena {
Vector<EditBSPNode::VisLink*> mBuffers;
U32 arenaSize;
EditBSPNode::VisLink* pCurrent;
public:
VisLinkArena(U32 _arenaSize);
~VisLinkArena();
EditBSPNode::VisLink* allocateLink();
void releaseLink(EditBSPNode::VisLink*);
void clearAll();
};
#endif //_BSPNODE_H_

2451
tools/map2dif/createLightmaps.cc Executable file

File diff suppressed because it is too large Load Diff

267
tools/map2dif/createLightmaps.h Executable file
View File

@ -0,0 +1,267 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _inc_createLightMaps
#define _inc_createLightMaps
#ifndef _ENTITYTYPES_H_
#include "map2dif/entityTypes.h"
#endif
#ifndef _EDITGEOMETRY_H_
#include "map2dif/editGeometry.h"
#endif
#ifndef _CSGBRUSH_H_
#include "map2dif/csgBrush.h"
#endif
#ifndef _DATACHUNKER_H_
#include "core/dataChunker.h"
#endif
#ifndef _INTERIOR_H_
#include "interior/interior.h"
#endif
static const U32 LexelPointStoreSize = 100;
static const U32 LightingMaxWindingPoints = 36;
//------------------------------------------------------------------------------
class Lighting
{
public:
// 132 bytes from 396
struct MiniWinding
{
union {
U32 mNumIndices;
MiniWinding * mNext;
};
U32 mIndices[LightingMaxWindingPoints];
};
class EmitterInfo
{
public:
U32 mEmitter;
U32 mShadowVolume;
UniqueVector mShadowed;
// info for animated lights.
GBitmap * mLightMap;
};
Chunker<EmitterInfo> mEmitterInfoChunker;
EmitterInfo * createEmitterInfo();
//
class SVNode
{
public:
enum Side
{
Front = 0,
Back = 1,
On = 2,
Split = 3
};
enum Type
{
PolyPlane = 0,
ShadowPlane = 1,
LexelPlane = 2,
};
Type mType;
SVNode * mFront;
SVNode * mBack;
U32 mPlaneIndex;
MiniWinding * mWinding;
// polyPlane member info
SVNode * mTarget;
EmitterInfo * mEmitterInfo;
Point3D getCenter();
void split(SVNode ** front, SVNode ** back, U32 planeIndex);
void insertShadowVolume(SVNode ** root);
void insertFront(SVNode ** root);
void insertBack(SVNode ** root);
void insert(SVNode ** root);
void addToList(SVNode ** list);
void move(SVNode ** list);
void clipToVolume(SVNode ** store, SVNode * volume);
void clipToInverseVolume(SVNode ** store, SVNode * volume);
// help functions
const Point3D & getPoint(U32);
U32 insertPoint(const Point3D &);
// use only with lexel plane nodes
F64 getWindingSurfaceArea();
bool clipWindingToPlaneFront(U32 planeEQIndex);
SVNode::Side whichSide(SVNode * node);
};
// have only lexel/poly nodes with windings (keeps size of
// shadow volume down by 128 bytes each... with possibly well
// over 100k of them, saves alot of mem)
MiniWinding * createWinding();
void recycleWinding(MiniWinding *);
Chunker<MiniWinding> mWindingChunker;
MiniWinding * mWindingStore;
//
Chunker<SVNode> mNodeChunker;
SVNode * mNodeRepository;
SVNode * createNode(SVNode::Type type);
void recycleNode(SVNode * node);
class Surface
{
public:
U32 mPlaneIndex;
MiniWinding mWinding;
U32 mSurfaceIndex;
SVNode * mPolyNode;
U32 mNumEmitters;
EmitterInfo ** mEmitters;
EmitterInfo * getEmitterInfo(U32 emitter);
};
Surface * createSurface();
Chunker<Surface> mSurfaceChunker;
class Emitter
{
public:
Point3D mPos;
ColorF mColor;
U32 mPoint;
U32 mIndex;
U32 mNumSurfaces;
U32 * mSurfaces;
bool mAnimated;
Emitter();
void processBSP();
virtual bool isSurfaceLit(const Surface * surface) = 0;
virtual F64 calcIntensity(SVNode * lSurface) = 0;
};
class PointEmitter : public Emitter
{
public:
F64 mFalloffs[2];
bool isSurfaceLit(const Surface * surface);
F64 calcIntensity(SVNode * lSurface);
};
class SpotEmitter : public Emitter
{
public:
F64 mFalloffs[2];
F64 mAngles[2];
Point3D mDirection;
bool isSurfaceLit(const Surface * surface);
F64 calcIntensity(SVNode * lSurface);
};
class Light
{
public:
Light();
~Light();
class LightState {
public:
U32 mNumEmitters;
U32 mEmitterIndex;
F32 mDuration;
void fillStateData(EditGeometry::LightState * state);
};
U32 mNumStates;
U32 mStateIndex;
char * mName;
bool mAnimated;
U32 mAnimType;
bool alarm;
bool getTargetPosition(const char * name, Point3D & pos);
// each of the worldcraft light types will be processed here
bool buildLight(BaseLightEntity * entity);
// scripted lights
bool buildOmniLight(BaseLightEntity * entity);
bool buildSpotLight(BaseLightEntity * entity);
// animated...
bool buildStrobeLight(BaseLightEntity * entity);
bool buildPulseLight(BaseLightEntity * entity);
bool buildPulse2Light(BaseLightEntity * entity);
bool buildFlickerLight(BaseLightEntity * entity);
bool buildRunwayLight(BaseLightEntity * entity);
bool build(BaseLightEntity * entity);
};
Vector<Light::LightState> mLightStates;
Vector<U32> mLightStateEmitterStore;
//
Lighting();
~Lighting();
void grabLights(bool alarmMode);
void convertToFan(MiniWinding &, U32);
void copyWinding(MiniWinding &, Winding &);
U32 constructPlane(const Point3D&, const Point3D&, const Point3D&) const;
void grabSurfaces();
void processSurfaces();
void createShadowVolumes();
void processEmitterBSPs();
void lightSurfaces();
void processAnimatedLights();
//
Vector<Light *> mLights;
Vector<BaseLightEmitterEntity *> mBaseLightEmitters;
Vector<TargetEntity *> mTargets;
Vector<Emitter *> mEmitters;
Vector<Surface *> mSurfaces;
Vector<SVNode *> mShadowVolumes;
EmitterInfo ** mSurfaceEmitterInfos;
U32 * mEmitterSurfaceIndices;
ColorF mAmbientColor;
SVNode * getShadowVolume(U32 index);
Surface * getSurface(U32 index);
F64 getLumelScale();
//
Vector<Point3D> mLexelPoints;
U32 mNumAmbiguousPlanes;
const Point3D & getLexelPoint(U32 index);
U32 insertLexelPoint(const Point3D & pnt);
void flushLexelPoints();
};
#endif

637
tools/map2dif/csgBrush.cc Executable file
View File

@ -0,0 +1,637 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2003 GarageGames.Com
//-----------------------------------------------------------------------------
#include "dgl/gBitmap.h"
#include "map2dif/csgBrush.h"
#include "map2dif/tokenizer.h"
extern int gQuakeVersion;
F32 baseaxis[18][3] =
{
{0,0,1}, {1,0,0}, {0,-1,0}, // floor
{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
{1,0,0}, {0,1,0}, {0,0,-1}, // west wall
{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
{0,1,0}, {1,0,0}, {0,0,-1}, // south wall
{0,-1,0}, {1,0,0}, {0,0,-1} // north wall
};
//------------------------------------------------------------------------------
void textureAxisFromPlane(const VectorF& normal, F32* xv, F32* yv)
{
int bestaxis;
F32 dot,best;
int i;
best = 0;
bestaxis = 0;
for (i=0 ; i<6 ; i++) {
dot = mDot (normal, VectorF (baseaxis[i*3][0], baseaxis[i*3][1], baseaxis[i*3][2]));
if (dot > best)
{
best = dot;
bestaxis = i;
}
}
xv[0] = baseaxis [bestaxis * 3 + 1][0];
xv[1] = baseaxis [bestaxis * 3 + 1][1];
xv[2] = baseaxis [bestaxis * 3 + 1][2];
yv[0] = baseaxis [bestaxis * 3 + 2][0];
yv[1] = baseaxis [bestaxis * 3 + 2][1];
yv[2] = baseaxis [bestaxis * 3 + 2][2];
}
//------------------------------------------------------------------------------
void quakeTextureVecs(const VectorF& normal,
F32 offsetX,
F32 offsetY,
F32 rotate,
F32 scaleX,
F32 scaleY,
PlaneF* u,
PlaneF *v)
{
F32 vecs[2][3];
int sv, tv;
F32 ang, sinv, cosv;
F32 ns, nt;
int i, j;
textureAxisFromPlane(normal, vecs[0], vecs[1]);
ang = rotate / 180 * M_PI;
sinv = sin(ang);
cosv = cos(ang);
if (vecs[0][0] != 0.0)
sv = 0;
else if (vecs[0][1] != 0.0)
sv = 1;
else
sv = 2;
if (vecs[1][0] != 0.0)
tv = 0;
else if (vecs[1][1] != 0.0)
tv = 1;
else
tv = 2;
for (i=0 ; i<2 ; i++) {
ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
vecs[i][sv] = ns;
vecs[i][tv] = nt;
}
u->x = vecs[0][0] / scaleX;
u->y = vecs[0][1] / scaleX;
u->z = vecs[0][2] / scaleX;
u->d = offsetX;
v->x = vecs[1][0] / scaleY;
v->y = vecs[1][1] / scaleY;
v->z = vecs[1][2] / scaleY;
v->d = offsetY;
}
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());
U32 bmIndex = geom.getMaterialIndex(rPlane.pTextureName);
const GBitmap* pBitmap = geom.mTextures[bmIndex];
PlaneF tGenX;
PlaneF tGenY;
if (gQuakeVersion == 2)
{
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)
{
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;
// 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;
}
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()
{
static const U32 VectorBufferSize = 64;
static UniqueVector pPlanePoints[VectorBufferSize];
AssertISV(mPlanes.size() < VectorBufferSize,
"CSGBrush::selfClip: talk to Dave Moore. Must increase planepoint buffer size");
for (U32 i = 0; i < mPlanes.size(); i++) {
for (U32 j = i+1; j < mPlanes.size(); j++) {
for (U32 k = j+1; k < mPlanes.size(); k++) {
Point3D intersection;
bool doesSersect = intersectPlanes(i, j, k, &intersection);
if (doesSersect) {
F64 E = gcPlaneDistanceEpsilon;
const char * msg = "CSGBrush::selfClip: intersectionPoint not on plane";
AssertFatal( mPlanes[i].sideCheckEpsilon(intersection,E) == PlaneOn,
mPlanes[i].sideCheckInfo(intersection, msg, E) );
AssertFatal( mPlanes[j].sideCheckEpsilon(intersection,E) == PlaneOn,
mPlanes[j].sideCheckInfo(intersection, msg, E) );
AssertFatal( mPlanes[k].sideCheckEpsilon(intersection,E) == PlaneOn,
mPlanes[k].sideCheckInfo(intersection, msg, E) );
bool inOrOn = true;
for (U32 l = 0; l < mPlanes.size(); l++) {
if (mPlanes[l].whichSide(intersection) == PlaneFront) {
inOrOn = false;
break;
}
}
if (inOrOn == true) {
U32 pIndex = gWorkingGeometry->insertPoint(intersection);
pPlanePoints[i].pushBackUnique(pIndex);
pPlanePoints[j].pushBackUnique(pIndex);
pPlanePoints[k].pushBackUnique(pIndex);
gWorkingGeometry->mMinBound.setMin(intersection);
gWorkingGeometry->mMaxBound.setMax(intersection);
}
}
}
}
}
mMinBound.set(1e10, 1e10, 1e10);
mMaxBound.set(-1e10, -1e10, -1e10);
for (U32 i = 0; i < mPlanes.size(); i++) {
bool success = mPlanes[i].createBaseWinding(pPlanePoints[i]);
if(!success)
return false;
for (U32 j = 0; j < mPlanes[i].winding.numIndices; j++) {
const Point3D& rPoint = gWorkingGeometry->getPoint(mPlanes[i].winding.indices[j]);
mMinBound.setMin(rPoint);
mMaxBound.setMax(rPoint);
}
AssertISV(success, "Error creating winding. DMM - Need to remove brush here.");
pPlanePoints[i].clear();
}
return true;
}
void CSGBrush::copyBrush(const CSGBrush* pCopy)
{
mPlanes = pCopy->mPlanes;
mIsAmbiguous = pCopy->mIsAmbiguous;
mBrushType = pCopy->mBrushType;
mMinBound = pCopy->mMinBound;
mMaxBound = pCopy->mMaxBound;
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 brushes (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;
}

167
tools/map2dif/csgBrush.h Executable file
View File

@ -0,0 +1,167 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CSGBRUSH_H_
#define _CSGBRUSH_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _MORIANBASICS_H_
#include "map2dif/morianBasics.h"
#endif
#ifndef _EDITGEOMETRY_H_
#include "map2dif/editGeometry.h"
#endif
#ifndef _MORIANUTIL_H_
#include "map2dif/morianUtil.h"
#endif
#ifndef _MMATHFN_H_
#include "math/mMathFn.h"
#endif
bool parseBrush (CSGBrush& brush, Tokenizer* pToker, EditGeometry& geom);
class CSGPlane {
public:
// Plane equation given as mDot(normal, (x, y, z)) = d;
U32 planeEQIndex;
const char* pTextureName;
F32 xShift;
F32 yShift;
F32 rotation;
F32 xScale;
F32 yScale;
U32 texGenIndex;
Winding winding;
U32 flags;
EditGeometry::Entity* owningEntity;
public:
const Point3D& getNormal() const;
F64 getDist() const;
PlaneSide whichSide(const Point3D&) const;
F64 distanceToPlane(const Point3D&) const;
void snapPointToPlane(Point3D&) const;
Point3D * sharesEdgeWith(const Winding &) const;
void extrude(F64 byAmount);
PlaneSide sideCheckEpsilon(const Point3D& testPoint, F64 E=0.000000001) const;
const char* sideCheckInfo(const Point3D & point, const char * msg, F64 epsilon) const;
bool createBaseWinding(const Vector<U32>&);
bool clipWindingToPlaneFront(const U32 planeEQIndex);
void construct(const Point3D& Point1, const Point3D& Point2, const Point3D& Point3);
enum Flags {
Inserted = 1 << 0
};
void markInserted() { flags |= Inserted; }
void markUninserted() { flags &= ~Inserted; }
bool isInserted() const { return (flags & Inserted) != 0; }
};
class CSGBrush
{
public:
bool intersectPlanes(U32 i, U32 j, U32 k, Point3D* pOutput);
public:
CSGBrush() : mIsAmbiguous(false) { }
Vector<CSGPlane> mPlanes;
Point3D mMinBound;
Point3D mMaxBound;
bool mIsAmbiguous;
BrushType mBrushType;
U32 brushId;
U32 materialType;
CSGBrush* pNext;
public:
CSGPlane& constructBrushPlane(const Point3I& rPoint1,
const Point3I& rPoint2,
const Point3I& rPoint3);
CSGPlane& constructBrushPlane(const Point3D&, const Point3D&,const Point3D&);
bool disambiguate();
bool selfClip();
bool doesBBoxSersect(const CSGBrush& testBrush) const;
bool isEquivalent(const CSGBrush& testBrush) const;
bool noMoreInsertables() const;
public:
void copyBrush(const CSGBrush* pCopy);
CSGBrush * createExtruded(const Point3D & extDir, bool bothWays) const;
};
//------------------------------------------------------------------------------
inline const Point3D& CSGPlane::getNormal() const
{
return gWorkingGeometry->getPlaneEQ(planeEQIndex).normal;
}
inline F64 CSGPlane::getDist() const
{
AssertFatal(gWorkingGeometry != NULL, "No working geometry?");
return gWorkingGeometry->getPlaneEQ(planeEQIndex).dist;
}
inline PlaneSide CSGPlane::whichSide(const Point3D& testPoint) const
{
return gWorkingGeometry->getPlaneEQ(planeEQIndex).whichSide(testPoint);
}
inline F64 CSGPlane::distanceToPlane(const Point3D& rPoint) const
{
return gWorkingGeometry->getPlaneEQ(planeEQIndex).distanceToPlane(rPoint);
}
inline bool CSGPlane::clipWindingToPlaneFront(const U32 _planeEQIndex)
{
return ::clipWindingToPlaneFront(&winding, _planeEQIndex);
}
inline void CSGPlane::snapPointToPlane(Point3D& rPoint) const
{
F64 distance = mDot(rPoint, getNormal()) + getDist();
rPoint -= getNormal() * distance;
}
inline bool CSGBrush::doesBBoxSersect(const CSGBrush& testBrush) const
{
if (testBrush.mMinBound.x > mMaxBound.x ||
testBrush.mMinBound.y > mMaxBound.y ||
testBrush.mMinBound.z > mMaxBound.z)
return false;
if (testBrush.mMaxBound.x < mMinBound.x ||
testBrush.mMaxBound.y < mMinBound.y ||
testBrush.mMaxBound.z < mMinBound.z)
return false;
return true;
}
inline bool CSGBrush::noMoreInsertables() const
{
for (U32 i = 0; i < mPlanes.size(); i++)
if (mPlanes[i].isInserted() == false)
return false;
return true;
}
#endif //_CSGBRUSH_H_

View File

@ -0,0 +1,76 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "map2dif/editGeometry.h"
#include "map2dif/editFloorPlanRes.h"
void EditFloorPlanResource::pushArea(const Winding & winding, U32 planeEQ)
{
mWindings.push_back(winding);
mPlaneEQs.push_back(planeEQ);
}
void EditFloorPlanResource::clearFloorPlan()
{
mPlaneTable.clear();
mPointTable.clear();
mPointLists.clear();
mAreas.clear();
}
// Build data to persist.
void EditFloorPlanResource::constructFloorPlan()
{
UniqueVector planes, points;
U32 i, j, maxPointIndex=0, maxPlaneIndex=0, totalPoints=0;
clearFloorPlan();
// Get the lists of unique points and planes, figure out max for remap tables.
for( i = 0; i < mWindings.size(); i++ )
{
const Winding & W = mWindings[i];
for( j = 0; j < W.numIndices; j++, totalPoints++ )
{
if( W.indices[j] > maxPointIndex )
maxPointIndex = W.indices[j];
points.pushBackUnique( W.indices[j] );
}
if( mPlaneEQs[i] > maxPlaneIndex )
maxPlaneIndex = mPlaneEQs[i];
planes.pushBackUnique( mPlaneEQs[i] );
}
// Allocate index remap tables
Vector<U32> remapPoints; remapPoints.setSize(maxPointIndex + 1);
Vector<U32> remapPlanes; remapPlanes.setSize(maxPlaneIndex + 1);
// Build the index remap tables while copying over the point and plane
// vector data into the FloorPlanResource.
for( i = 0, mPointTable.reserve(points.size()); i < points.size(); i++ )
{
Point3D point = gWorkingGeometry->getPoint(points[i]) / 32.0;
mPointTable.push_back( Point3F(point.x,point.y,point.z) );
remapPoints[ points[i] ] = i;
}
for( i = 0, mPlaneTable.reserve(planes.size()); i < planes.size(); i++ )
{
PlaneEQ pl64 = gWorkingGeometry->getPlaneEQ(planes[i]);
Point3F norm ( pl64.normal.x, pl64.normal.y, pl64.normal.z );
F64 dist ( pl64.dist / 32.0 );
mPlaneTable.push_back( PlaneF(norm.x, norm.y, norm.z, dist) );
remapPlanes[ planes[i] ] = i;
}
// Construct the areas
for( i = 0, mPointLists.reserve(totalPoints); i < mWindings.size(); i++ )
{
const Winding & W = mWindings[i];
Area area(W.numIndices, mPointLists.size(), remapPlanes[mPlaneEQs[i]] );
mAreas.push_back( area );
for( j = 0; j < W.numIndices; j++ )
mPointLists.push_back( remapPoints[ W.indices[j] ] );
}
}

View File

@ -0,0 +1,30 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _H_EDITFLOORPLANRES_
#define _H_EDITFLOORPLANRES_
#ifndef _FLOORPLANRES_H_
#include "interior/floorPlanRes.h"
#endif
class EditFloorPlanResource : public FloorPlanResource
{
protected:
Vector<Winding> mWindings;
Vector<U32> mPlaneEQs;
void clearFloorPlan();
public:
// In Nav graph generation mode, windings are saved during the BSP process.
void pushArea(const Winding & winding, U32 planeEQ);
// When done, this assembles the FloorPlanResource data.
// Uses gWorkingGeometry...
void constructFloorPlan();
};
#endif // _H_EDITFLOORPLANRES_

1991
tools/map2dif/editGeometry.cc Executable file

File diff suppressed because it is too large Load Diff

578
tools/map2dif/editGeometry.h Executable file
View File

@ -0,0 +1,578 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _EDITGEOMETRY_H_
#define _EDITGEOMETRY_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _PLATFORMASSERT_H_
#include "platform/platformAssert.h"
#endif
#ifndef _MORIANBASICS_H_
#include "map2dif/morianBasics.h"
#endif
#ifndef _MORIANUTIL_H_
#include "map2dif/morianUtil.h"
#endif
#ifndef _BSPNODE_H_
#include "map2dif/bspNode.h"
#endif
#ifndef _MPLANE_H_
#include "math/mPlane.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _EDITFLOORPLANRES_H_
#include "map2dif/editFloorPlanRes.h"
#endif
#include "interior/interiorResObjects.h"
class Tokenizer;
class Interior;
class CSGBrush;
class GBitmap;
class CSGPlane;
class WorldSpawnEntity;
class MirrorSurfaceEntity;
class TriggerEntity;
class DoorEntity;
class ForceFieldEntity;
class PathStartEntity;
class SpecialNodeEntity;
class GameEntity;
class InteriorResource;
struct TempHullReference;
//------------------------------------------------------------------------------
class EditGeometry
{
friend class EditBSPNode;
friend class Lighting;
friend void parseBrushList(Vector<CSGBrush*>& brushes, Tokenizer* pToker);
//-------------------------------------- first class structures
public:
struct Portal {
U32 portalId;
S32 planeEQIndex;
S32 frontZone;
S32 backZone;
bool passAmbientLight;
Vector<Winding> windings;
Point3D mMin;
Point3D mMax;
Point3D x;
Point3D y;
};
struct Zone {
U32 zoneId;
bool active;
bool ambientLit;
Vector<U32> referencingPortals;
};
struct Surface {
bool isMemberOfZone(U32);
U32 uniqueKey;
U32 planeIndex;
U32 textureIndex;
Winding winding;
Winding originalWinding;
U32 texGenIndex;
U32 lMapDimX;
U32 lMapDimY;
U32 offsetX;
U32 offsetY;
bool lmapTexGenSwapped;
F32 lmapTexGenX[4];
F32 lmapTexGenY[4];
F32 tempScale[2];
GBitmap* pLMap;
GBitmap* pNormalLMap;
GBitmap* pAlarmLMap;
U32 sheetIndex;
U32 alarmSheetIndex;
U32 flags;
U32 fanMask;
U32 numLights;
U32 stateDataStart;
bool mustPortalExtend;
U32 temptemptemp;
};
struct NullSurface {
U32 planeIndex;
Winding winding;
U32 flags;
};
class Entity {
public:
Entity() { }
virtual ~Entity() { };
InteriorDict mDictionary;
virtual bool parseEntityDescription(Tokenizer* pToker) = 0;
virtual BrushType getBrushType() = 0;
virtual bool isPointClass() const = 0;
virtual const char* getName() const = 0;
virtual const Point3D& getOrigin() const = 0;
virtual void grabSurface(Surface&) { }
};
struct PlaneHashEntry {
U32 planeIndex;
PlaneHashEntry* pNext;
};
struct PointHashEntry {
U32 pointIndex;
PointHashEntry* pNext;
};
struct TexGenPlanes {
PlaneF planeX;
PlaneF planeY;
};
//-------------------------------------- animated lighting structures
public:
struct StateData {
GBitmap* pLMap; // 8-Bit intensity map
U32 surfaceIndex; // Surface affected by this state
U32 stateDataIndex; // index
};
struct LightState {
ColorI color;
F32 duration;
Vector<StateData> stateData;
};
struct AnimatedLight {
char* name;
U32 type; // From Interior::LightType
bool alarm; // entity->mAlarmStatus != NormalOnly
Vector<LightState*> states;
AnimatedLight() : name(NULL), type(0) { }
};
Vector<AnimatedLight*> mAnimatedLights;
//-------------------------------------- arenas
public:
class PlaneHashArena {
Vector<PlaneHashEntry*> mBuffers;
U32 arenaSize;
PlaneHashEntry* currBuffer;
U32 currPosition;
public:
PlaneHashArena(U32 _arenaSize);
~PlaneHashArena();
PlaneHashEntry* allocateEntry();
};
class PointHashArena {
Vector<PointHashEntry*> mBuffers;
U32 arenaSize;
PointHashEntry* currBuffer;
U32 currPosition;
public:
PointHashArena(U32 _arenaSize);
~PointHashArena();
PointHashEntry* allocateEntry();
};
//------------------------------------------------------------------------------
//-------------------------------------- DATA
private:
//-------------------------------------- Error variables
U32 mNumAmbiguousBrushes;
U32 mNumOrphanPolys;
//-------------------------------------- Brushes and texture vars
public:
Point3D mMinBound;
Point3D mMaxBound;
public:
U32 mCurrBrushId;
U32 mSurfaceKey;
Vector<CSGBrush*> mStructuralBrushes;
Vector<CSGBrush*> mDetailBrushes;
Vector<CSGBrush*> mSpecialCollisionBrushes;
Vector<CSGBrush*> mVehicleCollisionBrushes;
Vector<NullSurface> mVehicleNullSurfaces;
Vector<CSGBrush*> mPortalBrushes;
Vector<Entity*> mPortalEntities;
Vector<char*> mTextureNames;
Vector<GBitmap*> mTextures;
Vector<Portal*> mPortals;
Vector<Zone*> mZones;
U32 mOutsideZoneIndex;
Vector<TexGenPlanes> mTexGenEQs;
Vector<Surface> mSurfaces;
Vector<NullSurface> mNullSurfaces;
Vector<GBitmap*> mLightmaps;
bool mHasAlarmState;
//-------------------------------------- Nav Graph Generation
bool mPerformExtrusion;
bool mGenerateGraph;
bool mHashPlanes, mHashPoints;
UniqueVector mGraphSurfacePts;
EditFloorPlanResource mEditFloorPlanRes;
//-------------------------------------- Planes and Points
PlaneHashEntry mPlaneHashTable[1 << 12];
Vector<PlaneEQ> mPlaneEQs;
Vector<S32> mPlaneRemaps;
U16 remapPlaneIndex(S32 inPlaneIndex);
U16 remapVehiclePlaneIndex(S32 inPlaneIndex, Interior*);
PointHashEntry mPointHashTable[1 << 12];
Vector<Point3D> mPoints;
struct ExportPointMapEntry {
U32 originalIndex;
U32 runtimeIndex;
};
Vector<ExportPointMapEntry> mExportPointMap;
Vector<ExportPointMapEntry> mVehicleExportPointMap;
public:
PlaneHashArena mPlaneHashArena;
PointHashArena mPointHashArena;
//-------------------------------------- BSP info
public:
NodeArena mNodeArena;
VisLinkArena mVisLinkArena;
private:
EditBSPNode* mBSPRoot;
//-------------------------------------- Entity information
public:
WorldSpawnEntity* mWorldEntity;
Vector<Entity*> mEntities;
//------------------------------------------------------------------------------
//-------------------------------------- FUNCTIONS
private:
Entity* parseEntity(Tokenizer* pToker);
const Entity* getNamedEntity(const char*) const;
public:
const char* insertTexture(const char*);
void fixSparkles();
void convertToStrips();
void markSurfaceOriginalPoints();
void sortSurfaces();
void sortLitSurfaces();
private:
void createBrushPolys();
void buildBSP();
void findOutsideZone();
void enterPortalZoneRefs();
void ambientVisitZone(const U32 zone);
void floodAmbientLight();
public:
void preprocessLighting();
private:
void postProcessLighting(Interior*);
U32 exportIntensityMap(Interior* pRuntime, GBitmap*);
void exportLightsToRuntime(Interior* pRuntime);
void exportDMLToRuntime(Interior* pRuntime, Vector<char*>&);
U16 exportBSPToRuntime(Interior* pRuntime, EditBSPNode* pNode);
void exportWindingToRuntime(Interior* pRuntime, const Winding& rWinding);
void exportVehicleWindingToRuntime(Interior* pRuntime, const Winding& rWinding);
U32 exportPointToRuntime(Interior* pRuntime, const U32 pointIndex);
void exportHullToRuntime(Interior* pRuntime, CSGBrush* pBrush);
U32 exportVehiclePointToRuntime(Interior* pRuntime, const U32 pointIndex);
void exportVehicleHullToRuntime(Interior* pRuntime, CSGBrush* pBrush);
void exportHullBins(Interior* pRuntime);
void exportPlanes(Interior*);
U32 exportEmitStringToRuntime(Interior* pRuntime,
const U8* pString,
const U32 stringLen);
U32 exportVehicleEmitStringToRuntime(Interior* pRuntime,
const U8* pString,
const U32 stringLen);
bool fixTJuncs();
void rotateSurfaceToNonColinear(Surface& surface);
void dumpSurfaceToRuntime(Interior* pRuntime,
Surface& editSurface);
void dumpNullSurfaceToRuntime(Interior* pRuntime,
NullSurface& editSurface);
void dumpVehicleNullSurfaceToRuntime(Interior* pRuntime,
NullSurface& editSurface);
void dumpMirrorToRuntime(Interior* pRuntime,
MirrorSurfaceEntity* pEntity);
void dumpTriggerToRuntime(Interior* pRuntime,
InteriorResource* pResource,
TriggerEntity* pEntity);
void dumpPathToRuntime(Interior* pRuntime,
InteriorResource* pResource,
PathStartEntity* pEntity);
void dumpAISpecialToRuntime(Interior* pRuntime,
InteriorResource* pResource,
SpecialNodeEntity* pEntity);
void dumpGameEntityToRuntime(Interior* pRuntime,
InteriorResource* pResource,
GameEntity* pEntity);
void dumpDoorToRuntime(Interior* /*pRuntime*/,
InteriorResource* pResource,
DoorEntity* pEntity);
void dumpForceFieldToRuntime(Interior* /*pRuntime*/,
InteriorResource* pResource,
ForceFieldEntity* pEntity);
void fillInLightmapInfo(Surface& rSurface);
void adjustLMapTexGen(Surface&);
void createBrushSurfaces(const CSGBrush& brush);
void createBoundingVolumes(Interior* pRuntime);
public:
EditGeometry();
~EditGeometry();
//-------------------------------------- High level functions
public:
bool parseMapFile(Tokenizer*);
bool createBSP();
void createSurfaces();
void markEmptyZones();
void packLMaps();
void computeLightmaps(const bool alarmMode);
bool exportToRuntime(Interior*, InteriorResource*);
U16 getMaterialIndex(const char* texName) const;
//-------------------------------------- Point/Plane buffer functions
public:
U32 insertPlaneEQ(const Point3D& normal, const F64);
const U32 getPlaneInverse(const U32 planeIndex);
const PlaneEQ& getPlaneEQ(const U32) const;
bool isCoplanar(const U32, const U32) const;
U32 insertPoint(const Point3D&);
const Point3D& getPoint(const U32) const;
void giftWrapPortal(Winding& winding, Portal* portal);
//-------------------------------------- NavGraph functions
public:
void setGraphGeneration(bool generate=false,bool extrude=false);
void gatherLinksForGraph(EditBSPNode * pLeafNode);
U32 writeGraphInfo();
void doGraphExtrusions(Vector<CSGBrush*> & brushList);
void xferDetailToStructural();
//-------------------------------------- Statistics
public:
U32 getTotalNumBrushes() const;
U32 getNumStructuralBrushes() const;
U32 getNumDetailBrushes() const;
U32 getNumPortalBrushes() const;
U32 getNumAmbiguousBrushes() const;
U32 getNumOrphanPolys() const;
U32 getNumUniquePlanes() const;
U32 getNumUniquePoints() const;
U32 getNumZones() const;
U32 getNumSurfaces() const;
const Point3D& getMinBound() const;
const Point3D& getMaxBound() const;
};
extern EditGeometry* gWorkingGeometry;
inline U32 EditGeometry::getNumStructuralBrushes() const
{
return mStructuralBrushes.size();
}
inline U32 EditGeometry::getNumDetailBrushes() const
{
return mDetailBrushes.size();
}
inline U32 EditGeometry::getNumPortalBrushes() const
{
return mPortalBrushes.size();
}
inline U32 EditGeometry::getNumAmbiguousBrushes() const
{
return mNumAmbiguousBrushes;
}
inline U32 EditGeometry::getNumOrphanPolys() const
{
return mNumOrphanPolys;
}
inline U32 EditGeometry::getTotalNumBrushes() const
{
return getNumStructuralBrushes() + getNumDetailBrushes() + getNumPortalBrushes();
}
inline U32 EditGeometry::getNumUniquePlanes() const
{
return mPlaneEQs.size() / 2;
}
inline U32 EditGeometry::getNumUniquePoints() const
{
return mPoints.size();
}
inline U32 EditGeometry::getNumZones() const
{
return mZones.size();
}
inline U32 EditGeometry::getNumSurfaces() const
{
return mSurfaces.size();
}
inline const Point3D& EditGeometry::getMinBound() const
{
return mMinBound;
}
inline const Point3D& EditGeometry::getMaxBound() const
{
return mMaxBound;
}
//------------------------------------------------------------------------------
inline const PlaneEQ& EditGeometry::getPlaneEQ(const U32 planeIndex) const
{
AssertFatal(planeIndex < mPlaneEQs.size(), "EditGeometry::getPlaneEQ: planeIndex out of range");
return mPlaneEQs[planeIndex];
}
inline bool EditGeometry::isCoplanar(const U32 planeIndex1, const U32 planeIndex2) const
{
AssertFatal(planeIndex1 < mPlaneEQs.size() && planeIndex2 < mPlaneEQs.size(),
"EditGeometry::isCoplanar: planeIndex out of range");
return (planeIndex1 & ~1) == (planeIndex2 & ~1);
}
inline const Point3D& EditGeometry::getPoint(const U32 index) const
{
AssertFatal(index < mPoints.size(), "EditGeometry::getPoint: out of bounds point index");
return mPoints[index];
}
inline const U32 EditGeometry::getPlaneInverse(const U32 planeIndex)
{
return (planeIndex ^ 0x1);
}
//------------------------------------------------------------------------------
inline EditGeometry::PlaneHashEntry* EditGeometry::PlaneHashArena::allocateEntry()
{
if (currPosition < arenaSize)
return &currBuffer[currPosition++];
// Need to add another buffer
currBuffer = new EditGeometry::PlaneHashEntry[arenaSize];
currPosition = 1;
mBuffers.push_back(currBuffer);
return currBuffer;
}
inline EditGeometry::PointHashEntry* EditGeometry::PointHashArena::allocateEntry()
{
if (currPosition < arenaSize)
return &currBuffer[currPosition++];
// Need to add another buffer
currBuffer = new EditGeometry::PointHashEntry[arenaSize];
currPosition = 1;
mBuffers.push_back(currBuffer);
return currBuffer;
}
//------------------------------------------------------------------------------
inline F64 PlaneEQ::distanceToPlane(const Point3D& rPoint) const
{
return mDot(rPoint, normal) + dist;
}
inline PlaneSide PlaneEQ::whichSide(const Point3D& testPoint) const
{
F64 distance = distanceToPlane(testPoint);
if (distance < -gcPlaneDistanceEpsilon)
return PlaneBack;
else if (distance > gcPlaneDistanceEpsilon)
return PlaneFront;
else
return PlaneOn;
}
inline PlaneSide PlaneEQ::whichSidePerfect(const Point3D& testPoint) const
{
F64 distance = distanceToPlane(testPoint);
if (distance < 0)
return PlaneBack;
else if (distance > 0)
return PlaneFront;
else
return PlaneOn;
}
#endif //_EDITGEOMETRY_H_

View File

@ -0,0 +1,83 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "dgl/gBitmap.h"
#include "interior/interior.h"
#include "map2dif/editInteriorRes.h"
namespace {
int FN_CDECL detailCmp(const void* p1, const void* p2)
{
const Interior* pInterior1 = *(reinterpret_cast<Interior**>(const_cast<void*>(p1)));
const Interior* pInterior2 = *(reinterpret_cast<Interior**>(const_cast<void*>(p2)));
return S32(pInterior1->getDetailLevel()) - S32(pInterior2->getDetailLevel());
}
} // namespace {}
void EditInteriorResource::sortDetailLevels()
{
AssertFatal(mDetailLevels.size() != 0, "Error, no detail levels to sort!");
dQsort(mDetailLevels.address(), mDetailLevels.size(), sizeof(Interior*), detailCmp);
for (U32 i = 0; i < mDetailLevels.size(); i++) {
mDetailLevels[i]->mDetailLevel = i;
if (i == 0)
continue;
AssertFatal(mDetailLevels[i]->getMinPixels() < mDetailLevels[i - 1]->getMinPixels(),
avar("Error, detail %d has greater or same minpixels as %d! (%d %d)",
i, i - 1, mDetailLevels[i]->getMinPixels(),
mDetailLevels[i - 1]->getMinPixels()));
}
}
void EditInteriorResource::addDetailLevel(Interior* pInterior)
{
mDetailLevels.push_back(pInterior);
}
void EditInteriorResource::setPreviewBitmap(GBitmap* bmp)
{
delete mPreviewBitmap;
mPreviewBitmap = bmp;
}
void EditInteriorResource::insertTrigger(InteriorResTrigger* pTrigger)
{
mTriggers.push_back(pTrigger);
}
void EditInteriorResource::insertPathedChild(InteriorPathFollower *pPathFollower)
{
mInteriorPathFollowers.push_back(pPathFollower);
}
void EditInteriorResource::insertGameEntity(ItrGameEntity *ent)
{
mGameEntities.push_back(ent);
}
U32 EditInteriorResource::insertSubObject(Interior* pInterior)
{
mSubObjects.push_back(pInterior);
return mSubObjects.size() - 1;
}
U32 EditInteriorResource::insertField(ForceField* object)
{
mForceFields.push_back(object);
return mForceFields.size() - 1;
}
U32 EditInteriorResource::insertSpecialNode(AISpecialNode* object)
{
mAISpecialNodes.push_back(object);
return mAISpecialNodes.size() - 1;
}

30
tools/map2dif/editInteriorRes.h Executable file
View File

@ -0,0 +1,30 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _EDITINTERIORRES_H_
#define _EDITINTERIORRES_H_
#ifndef _INTERIORRES_H_
#include "interior/interiorRes.h"
#endif
class EditInteriorResource : public InteriorResource
{
public:
void sortDetailLevels();
void addDetailLevel(Interior*);
void setPreviewBitmap(GBitmap*);
void insertTrigger(InteriorResTrigger* pTrigger);
void insertPath(InteriorPath* pPath);
void insertPathedChild(InteriorPathFollower * pPathFollower);
U32 insertSubObject(Interior* pInterior);
U32 insertField(ForceField*);
U32 insertSpecialNode(AISpecialNode*);
void insertGameEntity(ItrGameEntity*);
};
#endif // _H_EDITINTERIORRES_

2090
tools/map2dif/entityTypes.cc Executable file

File diff suppressed because it is too large Load Diff

598
tools/map2dif/entityTypes.h Executable file
View File

@ -0,0 +1,598 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _ENTITYTYPES_H_
#define _ENTITYTYPES_H_
//Includes
#ifndef _MORIANBASICS_H_
#include "map2dif/morianBasics.h"
#endif
#ifndef _EDITGEOMETRY_H_
#include "map2dif/editGeometry.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
#ifndef _POLYHEDRON_H_
#include "collision/polyhedron.h"
#endif
//------------------------------------------------------------------------------
//-------------------------------------- Brush based entities
class WorldSpawnEntity : public EditGeometry::Entity
{
public:
U32 mDetailNumber;
U32 mMinPixels;
F32 mGeometryScale;
F32 mLumelScale;
ColorF mAmbientColor;
ColorF mEmergencyAmbientColor;
char mWadPrefix[256];
public:
WorldSpawnEntity();
~WorldSpawnEntity();
bool parseEntityDescription(Tokenizer* pToker);
BrushType getBrushType();
bool isPointClass() const;
const char* getName() const;
const Point3D& getOrigin() const;
static const char* getClassName();
};
class DetailEntity : public EditGeometry::Entity
{
public:
DetailEntity();
~DetailEntity();
bool parseEntityDescription(Tokenizer* pToker);
BrushType getBrushType();
bool isPointClass() const;
const char* getName() const;
const Point3D& getOrigin() const;
static const char* getClassName();
};
class CollisionEntity : public EditGeometry::Entity
{
public:
CollisionEntity();
~CollisionEntity();
bool parseEntityDescription(Tokenizer* pToker);
BrushType getBrushType();
bool isPointClass() const;
const char* getName() const;
const Point3D& getOrigin() const;
static const char* getClassName();
};
class VehicleCollisionEntity : public EditGeometry::Entity
{
public:
VehicleCollisionEntity();
~VehicleCollisionEntity();
bool parseEntityDescription(Tokenizer* pToker);
BrushType getBrushType();
bool isPointClass() const;
const char* getName() const;
const Point3D& getOrigin() const;
static const char* getClassName();
};
class PortalEntity : public EditGeometry::Entity
{
public:
PortalEntity();
~PortalEntity();
bool passAmbientLight;
bool parseEntityDescription(Tokenizer* pToker);
BrushType getBrushType();
bool isPointClass() const;
const char* getName() const;
const Point3D& getOrigin() const;
static const char* getClassName();
};
//------------------------------------------------------------------------------
//-------------------------------------- Lighting and Target types
class TargetEntity : public EditGeometry::Entity
{
public:
char mTargetName[256];
Point3D mOrigin;
public:
TargetEntity();
~TargetEntity();
bool parseEntityDescription(Tokenizer* pToker);
BrushType getBrushType();
bool isPointClass() const { return(true); }
const char* getName() const { return(mTargetName); }
const Point3D& getOrigin() const { return(mOrigin); }
static const char* getClassName() { return("target"); }
};
//------------------------------------------------------------------------------
class BaseLightEmitterEntity : public EditGeometry::Entity
{
public:
enum FalloffType {
Distance = 0,
Linear = 1
};
char mTargetLight[256];
U32 mStateIndex;
Point3D mOrigin;
BaseLightEmitterEntity();
bool isPointClass() const { return(true); }
const char* getName() const { return(""); }
const Point3D& getOrigin() const { return(mOrigin); }
BrushType getBrushType();
};
class PointEmitterEntity : public BaseLightEmitterEntity
{
public:
BaseLightEmitterEntity::FalloffType mFalloffType;
U32 mFalloff1;
U32 mFalloff2;
U32 mFalloff3;
PointEmitterEntity();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() { return("light_emitter_point"); }
};
class SpotEmitterEntity : public BaseLightEmitterEntity
{
public:
BaseLightEmitterEntity::FalloffType mFalloffType;
U32 mFalloff1;
U32 mFalloff2;
U32 mFalloff3;
Point3D mDirection;
F32 mInnerAngle;
F32 mOuterAngle;
SpotEmitterEntity();
bool parseEntityDescription(Tokenizer * pToker);
static const char * getClassName() { return("light_emitter_spot"); }
};
//------------------------------------------------------------------------------
class BaseLightEntity : public EditGeometry::Entity
{
public:
enum Speed {
VerySlow = 0,
Slow = 1,
Normal = 3,
Fast = 4,
VeryFast = 5
};
// enum Flags {
// AutoStart = 1 << 0,
// LoopToEndFrame = 1 << 1,
// RandomFrame = 1 << 2,
//
// FlagMask = AutoStart | LoopToEndFrame | RandomFrame
// };
enum AlarmStatus {
NormalOnly = 0,
AlarmOnly = 1,
Both = 2
};
U32 mFlags;
AlarmStatus mAlarmStatus;
public:
Point3D mOrigin;
char mLightName[256];
BaseLightEntity();
bool isPointClass() const { return(true); }
const char* getName() const { return(mLightName); }
const Point3D& getOrigin() const { return(mOrigin); }
BrushType getBrushType();
};
class LightEntity : public BaseLightEntity
{
public:
struct StateInfo {
enum {
DurationValid = 1 << 0,
ColorValid = 1 << 1,
Valid = DurationValid | ColorValid,
MaxStates = 32,
};
F32 mDuration;
ColorF mColor;
U8 mFlags;
};
U32 mNumStates;
StateInfo mStates[StateInfo::MaxStates];
public:
LightEntity();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() { return("light"); }
};
//------------------------------------------------------------------------------
class LightOmniEntity : public BaseLightEntity
{
public:
ColorF mColor;
U32 mFalloff1;
U32 mFalloff2;
LightOmniEntity();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() { return("light_omni"); }
};
//------------------------------------------------------------------------------
class LightSpotEntity : public BaseLightEntity
{
public:
char mTarget[256];
ColorF mColor;
U32 mInnerDistance;
U32 mOuterDistance;
U32 mFalloff1;
U32 mFalloff2;
LightSpotEntity();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() { return("light_spot"); }
};
//------------------------------------------------------------------------------
class LightStrobeEntity : public BaseLightEntity
{
public:
U32 mSpeed;
U32 mFalloff1;
U32 mFalloff2;
ColorF mColor1;
ColorF mColor2;
LightStrobeEntity();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() {return("light_strobe");}
};
//------------------------------------------------------------------------------
class LightPulseEntity : public BaseLightEntity
{
public:
U32 mSpeed;
U32 mFalloff1;
U32 mFalloff2;
ColorF mColor1;
ColorF mColor2;
LightPulseEntity();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() {return("light_pulse");}
};
//------------------------------------------------------------------------------
class LightPulse2Entity : public BaseLightEntity
{
public:
ColorF mColor1;
ColorF mColor2;
U32 mFalloff1;
U32 mFalloff2;
F32 mAttack;
F32 mSustain1;
F32 mDecay;
F32 mSustain2;
LightPulse2Entity();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() {return("light_pulse2");}
};
//------------------------------------------------------------------------------
class LightFlickerEntity : public BaseLightEntity
{
public:
U32 mSpeed;
ColorF mColor1;
ColorF mColor2;
ColorF mColor3;
ColorF mColor4;
ColorF mColor5;
U32 mFalloff1;
U32 mFalloff2;
LightFlickerEntity();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() {return("light_flicker");}
};
//------------------------------------------------------------------------------
class LightRunwayEntity : public BaseLightEntity
{
public:
char mEndTarget[256];
ColorF mColor;
U32 mSpeed;
bool mPingPong;
U32 mSteps;
U32 mFalloff1;
U32 mFalloff2;
LightRunwayEntity();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() {return("light_runway");}
};
//--------------------------------------------------------------------------
//-------------------------------------- SPECIAL TYPES
//
class MirrorSurfaceEntity : public EditGeometry::Entity
{
static U32 smZoneKeyAllocator;
public:
Point3D mOrigin;
float mAlphaLevel;
U32 mZoneKey;
U32 mRealZone;
MirrorSurfaceEntity();
static const char* getClassName() { return("MirrorSurface"); }
bool parseEntityDescription(Tokenizer* pToker);
bool isPointClass() const { return true; }
const char* getName() const { return NULL; }
const Point3D& getOrigin() const { return mOrigin; }
BrushType getBrushType();
void markSurface(Vector<CSGBrush*>&, Vector<CSGBrush*>&);
void grabSurface(EditGeometry::Surface&);
};
//--------------------------------------------------------------------------
//-------------------------------------- PATH TYPES
//
class PathNodeEntity : public EditGeometry::Entity
{
public:
Point3D mOrigin;
S32 mNumMSToNextNode;
U32 mSmoothingType;
public:
PathNodeEntity();
~PathNodeEntity();
BrushType getBrushType();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() { return "path_node"; }
const char* getName() const { return("a_path_node"); }
const Point3D& getOrigin() const { return mOrigin; }
bool isPointClass() const { return true; }
};
//--------------------------------------------------------------------------
//-------------------------------------- Trigger types
//
class TriggerEntity : public EditGeometry::Entity
{
public:
char mName[256];
char mDataBlock[256];
Point3D mOrigin;
bool mValid;
Polyhedron triggerPolyhedron;
Vector<CSGBrush*> mBrushes;
U32 mCurrBrushId;
protected:
void generateTrigger();
public:
TriggerEntity();
~TriggerEntity();
BrushType getBrushType();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() { return "trigger"; }
const char* getName() const { return mName; }
const Point3D& getOrigin() const { return mOrigin; }
bool isPointClass() const { return true; }
};
//--------------------------------------------------------------------------
//-------------------------------------- REALLY REALLY SPECIAL TYPES
//
static Point3D gOrigin(0, 0, 0);
class DoorEntity : public EditGeometry::Entity
{
public:
Point3D mOrigin;
char mName[256];
char mPath[256];
char mDataBlock[256];
Vector<U32> mTriggerIds;
Vector<InteriorPathFollower::WayPoint> mWayPoints;
U32 mTotalMS;
Vector<CSGBrush*> mBrushes;
U32 mCurrBrushId;
Interior* mInterior;
public:
DoorEntity();
~DoorEntity();
void process();
void addPathNode(PathNodeEntity *ent);
static const char* getClassName() { return("Door_Elevator"); }
bool parseEntityDescription(Tokenizer* pToker);
const char* getName() const { return mName; }
const Point3D& getOrigin() const { return gOrigin; }
BrushType getBrushType();
// ok, not really, but as far as the parser is concered, yes.
bool isPointClass() const { return true; }
};
class ForceFieldEntity : public EditGeometry::Entity
{
public:
char mName[256];
U32 mMSToFade;
char mTrigger[8][256];
Vector<CSGBrush*> mBrushes;
U32 mCurrBrushId;
ColorF mColor;
Interior* mInterior;
public:
ForceFieldEntity();
~ForceFieldEntity();
void process();
static const char* getClassName() { return("Force_Field"); }
bool parseEntityDescription(Tokenizer* pToker);
const char* getName() const { return mName; }
const Point3D& getOrigin() const { return gOrigin; }
BrushType getBrushType();
// ok, not really, but as far as the parser is concered, yes.
bool isPointClass() const { return true; }
};
//--------------------------------------------------------------------------
//-----AI special Node type
//
class SpecialNodeEntity : public EditGeometry::Entity
{
public:
Point3D mOrigin;
char mName[256];
public:
SpecialNodeEntity();
~SpecialNodeEntity();
BrushType getBrushType();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() { return "ai_special_node"; }
const char* getName() const { return mName; }
const Point3D& getOrigin() const { return mOrigin; }
bool isPointClass() const { return true; }
};
class GameEntity : public EditGeometry::Entity
{
public:
Point3D mOrigin;
char mDatablock[256];
char mGameClass[256];
public:
GameEntity(const char *gameClassName);
~GameEntity();
BrushType getBrushType();
bool parseEntityDescription(Tokenizer* pToker);
static const char* getClassName() { return "game_entity_unused"; }
const char* getName() const { return NULL; }
const Point3D& getOrigin() const { return mOrigin; }
bool isPointClass() const { return true; }
};
#endif //_ENTITYTYPES_H_

2390
tools/map2dif/exportGeometry.cc Executable file

File diff suppressed because it is too large Load Diff

492
tools/map2dif/lmapPacker.cc Executable file
View File

@ -0,0 +1,492 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "map2dif/lmapPacker.h"
#include "platform/platformAssert.h"
#include "math/mPoint.h"
/* for FN_CDECL defs */
#include "platform/types.h"
const U32 SheetManager::csm_sheetSize = 256;
SheetManager::SheetManager()
{
m_currSheet = -1;
m_currX = 0;
m_currY = 0;
m_lowestY = 0;
}
SheetManager::~SheetManager()
{
for (U32 i = 0; i < m_sheets.size(); i++)
{
delete m_sheets[i].pData;
m_sheets[i].pData = NULL;
}
m_currSheet = -1;
m_currX = 0;
m_currY = 0;
m_lowestY = 0;
}
void SheetManager::begin()
{
numPixels = 0;
numSheetPixels = 0;
}
void SheetManager::end()
{
repackBlock();
// dPrintf("\n\n"
// " Total Pixels: %d\n"
// " Total SheetP: %d\n"
// " Efficiency: %g\n\n", numPixels, numSheetPixels, F32(numPixels)/F32(numSheetPixels));
m_currY = m_lowestY = csm_sheetSize + 1;
m_currSheet = -1;
}
U32 SheetManager::enterLightMap(const GBitmap* lm)
{
U32 width = lm->getWidth() + (SG_LIGHTMAP_BORDER_SIZE * 2);
U32 height = lm->getHeight() + (SG_LIGHTMAP_BORDER_SIZE * 2);
numPixels += width * height;
if (m_currSheet < 0)
{
// Must initialize the sheets...
setupNewSheet();
}
if (m_currX + width >= csm_sheetSize)
{
// Carriage return...
m_currX = 0;
m_currY = m_lowestY;
}
if (m_currY + height >= csm_sheetSize)
{
// new sheet needed
setupNewSheet();
}
m_lightMaps.increment();
m_lightMaps.last().sheetId = m_currSheet;
m_lightMaps.last().x = m_currX;
m_lightMaps.last().y = m_currY;
m_lightMaps.last().width = width;
m_lightMaps.last().height = height;
// And place the lightmap...
//
AssertFatal(lm->bytesPerPixel == m_sheets[m_currSheet].pData->bytesPerPixel, "Um, bad mismatch of bitmap types...");
U32 y, b;
U32 pixoffset = lm->bytesPerPixel;
U32 borderwidth = SG_LIGHTMAP_BORDER_SIZE * 2;
U32 nonborderpixlen = (width - borderwidth) * pixoffset;
U32 lmheightindex = lm->getHeight() - 1;
U32 lmborderheightindex = lmheightindex + SG_LIGHTMAP_BORDER_SIZE;
U32 borderpixlen = pixoffset * SG_LIGHTMAP_BORDER_SIZE;
for(y=0; y<height; y++)
{
U8 *srun, *drun;
if(y < SG_LIGHTMAP_BORDER_SIZE)
srun = (U8 *)lm->getAddress(0, 0);
else if(y > lmborderheightindex)
srun = (U8 *)lm->getAddress(0, lmheightindex);
else
srun = (U8 *)lm->getAddress(0, (y - SG_LIGHTMAP_BORDER_SIZE));
drun = (U8 *)m_sheets[m_currSheet].pData->getAddress(m_currX, (m_currY + y));
dMemcpy(&drun[borderpixlen], srun, nonborderpixlen);
U8 *ss, *se;
ss = srun;
se = &srun[(nonborderpixlen - pixoffset)];
for(b=0; b<SG_LIGHTMAP_BORDER_SIZE; b++)
{
U32 i = b * pixoffset;
drun[i] = ss[0];
drun[i+1] = ss[1];
drun[i+2] = ss[2];
i = (lm->getWidth() + SG_LIGHTMAP_BORDER_SIZE + b) * pixoffset;
drun[i] = se[0];
drun[i+1] = se[1];
drun[i+2] = se[2];
}
}
m_currX += width;
if (m_currY + height > m_lowestY)
m_lowestY = m_currY + height;
return m_lightMaps.size() - 1;
}
const SheetManager::LightMapEntry &SheetManager::getLightmap(const S32 in_lightMapIndex) const
{
AssertFatal(U32(in_lightMapIndex) < m_lightMaps.size(), "Out of bounds lightmap");
return m_lightMaps[in_lightMapIndex];
}
SheetManager::LightMapEntry &SheetManager::getLightmapNC(const S32 in_lightMapIndex)
{
AssertFatal(U32(in_lightMapIndex) < m_lightMaps.size(), "Out of bounds lightmap");
return m_lightMaps[in_lightMapIndex];
}
void SheetManager::setupNewSheet()
{
m_sheets.increment();
m_currSheet = m_sheets.size() - 1;
m_sheets.last().pData = new GBitmap(csm_sheetSize, csm_sheetSize);
for (U32 y = 0; y < m_sheets.last().pData->getHeight(); y++)
{
for (U32 x = 0; x < m_sheets.last().pData->getWidth(); x++)
{
U8* pDst = m_sheets.last().pData->getAddress(x, y);
pDst[0] = 0xFF;
pDst[1] = 0x00;
pDst[2] = 0xFF;
}
}
m_currX = 0;
m_currY = 0;
m_lowestY = 0;
}
void SheetManager::repackBlock()
{
if (m_lightMaps.size() == 0)
return;
U32 currLightMapStart = 0;
U32 currLightMapEnd = m_lightMaps.size() - 1;
S32 first = 0;
U32 num = m_sheets.size();
// OK, so at this point, we have a block of sheets, and a block of lightmaps on
// that sheet. What we want to do is loop on the lightmaps until we have none
// left, and pack them into new sheets that are as small as possible.
//
do
{
const U32 numSizes = 16;
U32 sizes[numSizes][2] = {
{32, 32}, // 1024
{32, 64}, // 2048
{64, 32}, // 2048
{64, 64}, // 4096
{32, 128}, // 4096
{128, 32}, // 4096
{64, 128}, // 8192
{128, 64}, // 8192
{32, 256}, // 8192
{256, 32}, // 8192
{128, 128}, // 16384
{64, 256}, // 16384
{256, 64}, // 16384
{128, 256}, // 32768
{256, 128}, // 32768
{256, 256} // 65536
};
// const U32 numSizes = 4;
// U32 sizes[numSizes][2] = {
// {32, 32}, // 1024
// {64, 64}, // 4096
// {128, 128}, // 16384
// {256, 256} // 65536
// };
bool success = false;
for (U32 i = 0; i < numSizes; i++)
{
if (doesFit(currLightMapStart, currLightMapEnd, sizes[i][0], sizes[i][1]) == true)
{
// Everything left fits into a 32
//
numSheetPixels += sizes[i][0] * sizes[i][1];
repackSection(currLightMapStart, currLightMapEnd, sizes[i][0], sizes[i][1]);
currLightMapStart = currLightMapEnd;
success = true;
break;
}
}
if (success == false)
{
// BSearch for the max we can get into a 256, then keep going...
//
U32 searchStart = currLightMapStart;
U32 searchEnd = currLightMapEnd - 1;
while (searchStart != searchEnd)
{
U32 probe = (searchStart + searchEnd) >> 1;
if (doesFit(currLightMapStart, probe, 256, 256) == true)
{
if (searchStart != probe)
searchStart = probe;
else
searchEnd = searchStart;
}
else
{
if (searchEnd != probe)
searchEnd = probe;
else
searchEnd = searchStart;
}
}
numSheetPixels += 256*256;
repackSection(currLightMapStart, searchStart, 256, 256, true);
currLightMapStart = searchStart + 1;
}
} while (currLightMapStart < currLightMapEnd);
// All the sheets from the same block id are contigous, so all we have to do is
// decrement the sheetIds of the affected lightmaps...
//
for (U32 i = 0; i < m_lightMaps.size(); i++)
{
if (m_lightMaps[i].sheetId >= (first + num))
{
m_lightMaps[i].sheetId -= num;
}
}
for (U32 i = num; i != 0; i--)
{
SheetEntry& rEntry = m_sheets[first];
delete rEntry.pData;
rEntry.pData = NULL;
m_sheets.erase(first);
}
}
S32 FN_CDECL compY(const void* p1, const void* p2)
{
const Point2I* point1 = (const Point2I*)p1;
const Point2I* point2 = (const Point2I*)p2;
if (point1->y != point2->y)
return point2->y - point1->y;
else
return point2->x - point1->x;
}
SheetManager* g_pManager = NULL;
S32 FN_CDECL compMapY(const void* in_p1, const void* in_p2)
{
const S32* p1 = (const S32*)in_p1;
const S32* p2 = (const S32*)in_p2;
const SheetManager::LightMapEntry& rEntry1 = g_pManager->getLightmap(*p1);
const SheetManager::LightMapEntry& rEntry2 = g_pManager->getLightmap(*p2);
if (rEntry1.height != rEntry2.height)
return rEntry2.height - rEntry1.height;
else
return rEntry2.width - rEntry1.width;
}
void
SheetManager::repackSection(const S32 in_start,
const S32 in_end,
const U32 in_sizeX,
const U32 in_sizeY,
const bool overflow)
{
Vector<S32> sheetIndices;
for (S32 i = in_start; i <= in_end; i++)
sheetIndices.push_back(i);
g_pManager = this;
dQsort(sheetIndices.address(), sheetIndices.size(), sizeof(S32), compMapY);
g_pManager = NULL;
// Ok, we now have the list of entries that we'll be entering, in the correct
// order. Go for it! Create the "right-sized" sheet, and pack.
//
m_sheets.increment();
m_sheets.last().pData = new GBitmap(in_sizeX, in_sizeY);
for (U32 y = 0; y < m_sheets.last().pData->getHeight(); y++)
{
for (U32 x = 0; x < m_sheets.last().pData->getWidth(); x++)
{
U8* pDst = m_sheets.last().pData->getAddress(x, y);
if (overflow == false)
{
pDst[0] = 0xFF;
pDst[1] = 0x00;
pDst[2] = 0xFF;
}
else
{
pDst[0] = 0x00;
pDst[1] = 0xFF;
pDst[2] = 0x00;
}
}
}
U32 currX = 0;
U32 currY = 0;
U32 lowestY = 0;
while (sheetIndices.size() != 0)
{
LightMapEntry& rEntry = getLightmapNC(sheetIndices[0]);
S32 lastOfHeight = 0;
for (U32 j = 1; j < sheetIndices.size(); j++)
{
LightMapEntry& rEntryTest = getLightmapNC(sheetIndices[j]);
if (rEntryTest.height != rEntry.height)
break;
lastOfHeight = j;
}
S32 insert = -1;
for (S32 k = 0; k <= lastOfHeight; k++)
{
LightMapEntry& rEntryTest = getLightmapNC(sheetIndices[k]);
if (currX + rEntryTest.width < in_sizeX)
{
insert = k;
break;
}
}
if (insert == -1)
{
// CR
currY = lowestY;
currX = 0;
insert = 0;
}
LightMapEntry* pInsert = &getLightmapNC(sheetIndices[insert]);
AssertFatal(currY + pInsert->height < in_sizeY, "Error, too many lmaps for this size");
for (S32 y = 0; y < pInsert->height; y++)
{
const U8* pSrc = m_sheets[pInsert->sheetId].pData->getAddress(pInsert->x, (pInsert->y + y));
U8* pDst = m_sheets.last().pData->getAddress(currX, (currY + y));
dMemcpy(pDst, pSrc, pInsert->width * m_sheets[pInsert->sheetId].pData->bytesPerPixel);
}
pInsert->sheetId = m_sheets.size() - 1;
pInsert->x = currX;
pInsert->y = currY;
AssertFatal(pInsert->x + pInsert->width <= m_sheets.last().pData->getWidth(), "very bad");
AssertFatal(pInsert->y + pInsert->height <= m_sheets.last().pData->getHeight(), "also bad");
currX += pInsert->width;
if (currY + pInsert->height > lowestY)
lowestY = currY + pInsert->height;
sheetIndices.erase(insert);
}
}
bool SheetManager::doesFit(const S32 startMap,
const S32 endMap,
const U32 sizeX,
const U32 sizeY) const
{
Vector<Point2I> mapSizes;
mapSizes.setSize(endMap - startMap + 1);
for (S32 i = startMap; i <= endMap; i++)
{
mapSizes[i - startMap].x = m_lightMaps[i].width;
mapSizes[i - startMap].y = m_lightMaps[i].height;
if (m_lightMaps[i].width > sizeX ||
m_lightMaps[i].height > sizeY)
return false;
}
dQsort(mapSizes.address(), mapSizes.size(), sizeof(Point2I), compY);
U32 currX = 0;
U32 currY = 0;
U32 lowestY = 0;
while (mapSizes.size() != 0) {
S32 lastOfHeight = 0;
for (U32 j = 1; j < mapSizes.size(); j++) {
if (mapSizes[j].y != mapSizes[0].y)
break;
lastOfHeight = j;
}
S32 insert = -1;
for (S32 k = 0; k <= lastOfHeight; k++)
{
if (currX + mapSizes[k].x < sizeX)
{
insert = k;
break;
}
}
if (insert == -1)
{
// CR
currX = 0;
currY = lowestY;
insert = 0;
}
if (currY + mapSizes[insert].y >= sizeY)
{
// Failure.
return false;
}
currX += mapSizes[insert].x;
if (currY + mapSizes[insert].y > lowestY)
lowestY = currY + mapSizes[insert].y;
mapSizes.erase(insert);
}
return true;
}

75
tools/map2dif/lmapPacker.h Executable file
View File

@ -0,0 +1,75 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _ITRSHEETMANAGER_H_
#define _ITRSHEETMANAGER_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _GBITMAP_H_
#include "dgl/gBitmap.h"
#endif
//
// Defines the interior light map border size.
//
// Light map borders prevent visual artifacts
// caused by colors bleeding from adjacent light maps
// or dead texture space.
//
#define SG_LIGHTMAP_BORDER_SIZE 10
class SheetManager
{
public:
struct LightMapEntry
{
U32 sheetId;
U16 x, y;
U16 width, height;
};
struct SheetEntry
{
GBitmap* pData;
};
public:
static const U32 csm_sheetSize;
public:
Vector<LightMapEntry> m_lightMaps;
Vector<SheetEntry> m_sheets;
S32 m_currSheet;
U32 m_currX;
U32 m_currY;
U32 m_lowestY;
void setupNewSheet();
void repackSection(const S32, const S32, const U32 in_sizeX, const U32 in_sizeY, const bool = false);
void repackBlock();
bool doesFit(const S32, const S32, const U32 sizeX, const U32 sizeY) const;
LightMapEntry& getLightmapNC(const S32 in_lightMapIndex);
public:
SheetManager();
~SheetManager();
U32 numPixels;
U32 numSheetPixels;
void begin();
void end();
U32 enterLightMap(const GBitmap* in_pData);
const LightMapEntry& getLightmap(const S32 in_lightMapIndex) const;
};
#endif // _ITRSHEETMANAGER_H_

568
tools/map2dif/main.cc Executable file
View File

@ -0,0 +1,568 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/event.h"
#include "platform/platformAssert.h"
#include "platform/platformVideo.h"
#include "math/mMath.h"
#include "console/console.h"
#include "dgl/gBitmap.h"
#include "core/tVector.h"
#include "core/fileStream.h"
#include "dgl/gTexManager.h"
#include "console/consoleTypes.h"
#include "math/mathTypes.h"
#include "map2dif/tokenizer.h"
#include "map2dif/editGeometry.h"
#include "interior/interior.h"
#include "map2dif/editInteriorRes.h"
#include "map2dif/entityTypes.h"
#include "interior/floorPlanRes.h"
#include "map2dif/morianGame.h"
#include "core/frameAllocator.h"
#include "gui/core/guiCanvas.h"
#include "map2dif/lmapPacker.h"
#include <stdlib.h>
MorianGame GameObject;
// FOR SILLY LINK DEPENDANCY
bool gEditingMission = false;
#if defined(TORQUE_DEBUG)
const char* const gProgramVersion = "0.900r-beta";
#else
const char* const gProgramVersion = "0.900d-beta";
#endif
//bool gRenderPreview = false;
bool gSpecifiedDetailOnly = false;
bool gBuildAsLowDetail = false;
bool gVerbose = false;
const char* gWadPath = "base/textures/";
bool gTextureSearch = true;
int gQuakeVersion = 2;
U32 gMaxPlanesConsidered = 32;
EditInteriorResource* gWorkingResource = NULL;
//#if defined(TORQUE_OS_WIN32) // huger hack
// huge hack
//GuiCanvas *Canvas;
//void GuiCanvas::paint() {}
//#endif
//
static bool initLibraries()
{
// asserts should be created FIRST
PlatformAssert::create();
FrameAllocator::init(2 << 20);
_StringTable::create();
TextureManager::create();
Con::init();
Math::init();
Platform::init(); // platform specific initialization
return(true);
}
static void shutdownLibraries()
{
// shut down
Platform::shutdown();
Con::shutdown();
TextureManager::destroy();
_StringTable::destroy();
// asserts should be destroyed LAST
FrameAllocator::destroy();
PlatformAssert::destroy();
}
S32 getGraphNodes( char * mapFileName )
{
// setup the tokenizer
Tokenizer* pTokenizer = new Tokenizer();
if (pTokenizer->openFile(mapFileName) == false) {
dPrintf("getGraphNodes(): Error opening map file: %s", mapFileName);
delete pTokenizer;
shutdownLibraries();
return -1;
}
// Create a geometry object
AssertFatal(gWorkingGeometry == NULL, "EditGeometry already exists");
gWorkingGeometry = new EditGeometry;
// configure graph for generation. not doing extrusion approach now.
gWorkingGeometry->setGraphGeneration(true, false);
// parse and create the geometry
dPrintf("Map file opened for graph work: %s\n"
" Parsing mapfile...", mapFileName); dFflushStdout();
if (gWorkingGeometry->parseMapFile(pTokenizer) == false) {
dPrintf("getGraphNodes(): Error parsing map file: %s\n", mapFileName);
delete pTokenizer;
delete gWorkingGeometry;
shutdownLibraries();
return -1;
}
delete pTokenizer;
dPrintf("done.\n");
// The code that gives us the node list is down in the createBSP()
// call tree. Kind of klunky but simpler for now.
dPrintf(" Creating graph node list"); dFflushStdout();
gWorkingGeometry->xferDetailToStructural();
bool result = gWorkingGeometry->createBSP();
if( result )
gWorkingGeometry->writeGraphInfo();
delete gWorkingGeometry; gWorkingGeometry = NULL;
delete gWorkingResource; gWorkingResource = NULL;
shutdownLibraries();
if( result == false ){
dPrintf( "getGraphNodes(): Error in BSP processing (%s)!\n", mapFileName);
return -1;
}
else{
dPrintf( "getGraphNodes(): Seemed to work... \n" );
return 0;
}
}
char* cleanPath(const char* _path)
{
char* path = new char[dStrlen(_path) + 2];
dStrcpy(path, _path);
// Clean up path char.
for (char* ptr = path; *ptr != '\0'; ptr++)
if (*ptr == '\\')
*ptr = '/';
// Check termination
char* end = &path[dStrlen(path) - 1];
if (*end != '/') {
end[1] = '/';
end[2] = 0;
}
return path;
}
char* getPath(const char* file)
{
char* path = new char[dStrlen(file) + 2];
dStrcpy(path, file);
// Strip back to first path char.
char* slash = dStrrchr(path, '/');
if (!slash)
slash = dStrrchr(path, '\\');
if (slash)
*slash = 0;
// Clean up path char.
char* ptr = path;
for (; *ptr != '\0'; ptr++)
if (*ptr == '\\')
*ptr = '/';
// Check termination
ptr--;
if (*ptr != '/') {
ptr[1] = '/';
ptr[2] = 0;
}
return path;
}
char* getBaseName(const char* file)
{
// Get rid of path
const char* slash = dStrrchr(file, '/');
if (!slash)
slash = dStrrchr(file, '\\');
if (!slash)
slash = file;
else
slash++;
char* name = new char[dStrlen(slash) + 1];
dStrcpy(name, slash);
// Strip extension & trailing _N
char* dot = dStrrchr(name, '.') - 2;
if (dot[0] == '_' && (dot[1] >= '0' && dot[1] <= '9'))
dot[0] = '\0';
else
dot[2] = '\0';
return name;
}
char* mergePath(const char* path1, const char* path2)
{
// Will merge and strip off leading ".." from path2
char* base = new char[dStrlen(path1) + dStrlen(path2) + 2];
dStrcpy(base,path1);
// Strip off ending path char.
char* end = &base[dStrlen(base) - 1];
if (*end == '/' || *end == '\\')
*end = 0;
// Deal with lead ./ and ../
while (path2[0] == '.')
if (path2[1] == '.') {
// Chop off ../ and remove the trailing dir from base
path2 += 2;
if (*path2 == '/' || *path2 == '\\')
path2++;
char *ptr = dStrrchr(base, '/');
if (!ptr)
ptr = dStrrchr(base, '\\');
AssertISV(ptr,"Error, could not merge relative path past root");
*ptr = 0;
}
else {
// Simply swallow the ./
path2 += 1;
if (*path2 == '/' || *path2 == '\\')
path2++;
}
dStrcat(base,"/");
dStrcat(base,path2);
return base;
}
S32 MorianGame::main(int argc, const char** argv)
{
// Set the memory manager page size to 64 megs...
setMinimumAllocUnit(64 << 20);
if(!initLibraries())
return 0;
// Set up the command line args for the console scripts...
Con::setIntVariable("Game::argc", argc);
for (S32 i = 0; i < argc; i++)
Con::setVariable(avar("Game::argv%d", i), argv[i]);
// Parse command line args...
bool isForNavigation = false, extrusionTest = false;
const char* wadPath = 0;
const char* difPath = 0;
S32 i = 1;
for (; i < argc; i++) {
if (argv[i][0] != '-')
break;
switch(dToupper(argv[i][1])) {
case 'D':
gSpecifiedDetailOnly = true;
break;
case 'L':
gSpecifiedDetailOnly = true;
gBuildAsLowDetail = true;
break;
case 'H':
gMaxPlanesConsidered = U32(1 << 30);
break;
case 'N':
gVerbose = true;
break;
case 'G':
isForNavigation = true;
extrusionTest = true;
break;
case 'E':
extrusionTest = true;
break;
case 'S':
gTextureSearch = false;
break;
case 'Q':
gQuakeVersion = atoi (argv[++i]);
break;
case 'T':
wadPath = cleanPath(argv[++i]);
break;
case 'O':
difPath = cleanPath(argv[++i]);
break;
}
}
U32 args = argc - i;
if (args != 1) {
dPrintf("\nmap2dif - Torque .MAP file converter\n"
" Copyright (C) GarageGames.com, Inc.\n"
" Program version: %s\n"
" Programmers: John Folliard & Dave Moore\n"
" Built: %s at %s\n\n"
"Usage: map2dif [-v] [-p] [-s] [-l] [-h] [-g] [-e] [-n] [-q ver] [-o outputDirectory] [-t textureDirectory] <file>.map\n"
" -p : Include a preview bitmap in the interior file\n"
" -d : Process only the detail specified on the command line\n"
" -l : Process as a low detail shape (implies -s)\n"
" -h : Process for final build (exhaustive BSP search)\n"
" -g : Generate navigation graph info\n"
" -e : Do extrusion test\n"
" -s : Don't search for textures in parent dir.\n"
" -n : Noisy error/statistic reporting\n"
" -q ver: Quake map file version (2, 3)\n"
" -o dir: Directory in which to place the .dif file\n"
" -t dir: Location of textures\n", gProgramVersion, __DATE__, __TIME__);
shutdownLibraries();
return -1;
}
// Check map file extension
const char* mapFile = argv[i];
const char* pDot = dStrrchr(mapFile, '.');
AssertISV(pDot && ((dStricmp(pDot, ".map") == 0)),
"Error, the map file must have a .MAP extension.");
// Get path and file name arguments
const char* mapPath = getPath(mapFile);
const char* baseName = getBaseName(mapFile);
if (!wadPath)
wadPath = mapPath;
if (!difPath)
difPath = mapPath;
// Old relative path merge, should think about what to do with it.
// wadPath = mergePath(mapPath,wadPath);
// difPath = mergePath(mapPath,difPath);
// Dif file name
char* pOutputName = new char[dStrlen(difPath) + dStrlen(baseName) + 5];
dStrcpy(pOutputName,difPath);
dStrcat(pOutputName,baseName);
dStrcat(pOutputName,".dif");
// Wad path
gWadPath = wadPath;
//
Vector<char*> mapFileNames;
if (gSpecifiedDetailOnly == false) {
const char* pDot = dStrrchr(mapFile, '.');
if (pDot && *(pDot - 2) == '_') {
// This is a detail based interior
char buffer[1024];
dStrcpy(buffer, mapFile);
char* pBufDot = dStrrchr(buffer, '.');
AssertFatal(pBufDot, "Error, why isn't it in this buffer too?");
*(pBufDot-1) = '\0';
for (U32 i = 0; i <= 9; i++) {
mapFileNames.push_back(new char[1024]);
dSprintf(mapFileNames.last(), 1023, "%s%d%s", buffer, i, pDot);
}
// Now, eliminate all mapFileNames that aren't actually map files
for (S32 i = S32(mapFileNames.size() - 1); i >= 0; i--) {
Tokenizer* pTokenizer = new Tokenizer();
if (pTokenizer->openFile(mapFileNames[i]) == false) {
delete [] mapFileNames[i];
mapFileNames.erase(i);
}
delete pTokenizer;
}
} else {
// normal interior
mapFileNames.push_back(new char[dStrlen(mapFile) + 1]);
dStrcpy(mapFileNames.last(), mapFile);
}
} else {
mapFileNames.push_back(new char[dStrlen(mapFile) + 1]);
dStrcpy(mapFileNames.last(), mapFile);
}
gWorkingResource = new EditInteriorResource;
if( isForNavigation ){
S32 retCode = getGraphNodes( mapFileNames[0] );
delete [] pOutputName;
for (U32 i = 0; i < mapFileNames.size(); i++)
delete [] mapFileNames[i];
return retCode;
}
for (U32 i = 0; i < mapFileNames.size(); i++) {
// setup the tokenizer
Tokenizer* pTokenizer = new Tokenizer();
if (pTokenizer->openFile(mapFileNames[i]) == false) {
dPrintf("Error opening map file: %s", mapFileNames[i]);
delete pTokenizer;
shutdownLibraries();
return -1;
}
// Create a geometry object
AssertFatal(gWorkingGeometry == NULL, "Already working?");
gWorkingGeometry = new EditGeometry;
// parse and create the geometry
dPrintf("Successfully opened map file: %s\n"
" Parsing mapfile...", mapFileNames[i]);
dFflushStdout();
if (gWorkingGeometry->parseMapFile(pTokenizer) == false) {
dPrintf("Error parsing map file: %s\n", mapFileNames[i]);
delete pTokenizer;
delete gWorkingGeometry;
delete gWorkingResource;
shutdownLibraries();
return -1;
}
delete pTokenizer;
dPrintf("done.\n");
gWorkingGeometry->setGraphGeneration(false,extrusionTest);
dPrintf(" Creating BSP...");
dFflushStdout();
if (gWorkingGeometry->createBSP() == false) {
dPrintf("Error creating BSP!\n", mapFileNames[i]);
// delete pTokenizer; (already)
delete gWorkingGeometry;
delete gWorkingResource;
shutdownLibraries();
return -1;
}
dPrintf("done.\n Marking active zones...");
gWorkingGeometry->markEmptyZones();
dPrintf("done\n Creating surfaces..."); dFflushStdout();
gWorkingGeometry->createSurfaces();
dPrintf("done.\n Lightmaps: Normal...");
dFflushStdout();
gWorkingGeometry->computeLightmaps(false);
dPrintf("Alarm...");
dFflushStdout();
gWorkingGeometry->computeLightmaps(true);
dPrintf("done.\n Resorting and Packing LightMaps..."); dFflushStdout();
gWorkingGeometry->preprocessLighting();
gWorkingGeometry->sortLitSurfaces();
gWorkingGeometry->packLMaps();
dPrintf("done.\n");
dFflushStdout();
// Process any special entitys...
for (U32 i = 0; i < gWorkingGeometry->mEntities.size(); i++)
{
if (dynamic_cast<DoorEntity*>(gWorkingGeometry->mEntities[i]) != NULL) {
DoorEntity* pDoor = static_cast<DoorEntity*>(gWorkingGeometry->mEntities[i]);
pDoor->process();
}
// else if (dynamic_cast<ForceFieldEntity*>(gWorkingGeometry->mEntities[i]) != NULL) {
// ForceFieldEntity* pForceField = static_cast<ForceFieldEntity*>(gWorkingGeometry->mEntities[i]);
// pForceField->process();
// }
}
// Give status
dPrintf("\n STATISTICS\n"
" - Total brushes: %d\n"
" + structural: %d\n"
" + detail: %d\n"
" + portal: %d\n"
" - Number of zones: %d\n"
" - Number of surfaces: %d\n", gWorkingGeometry->getTotalNumBrushes(),
gWorkingGeometry->getNumStructuralBrushes(),
gWorkingGeometry->getNumDetailBrushes(),
gWorkingGeometry->getNumPortalBrushes(),
gWorkingGeometry->getNumZones(),
gWorkingGeometry->getNumSurfaces());
if (gWorkingGeometry->getNumAmbiguousBrushes() != 0 ||
gWorkingGeometry->getNumOrphanPolys() != 0) {
dPrintf(
"\n ** *** WARNING WARNING WARNING *** **\n"
" *** ** WARNING WARNING WARNING ** ***\n"
"\n Errors exists in this interior. Please use the debug rendering modes\n"
" to find and correct the following problems:\n"
"\n * Ambiguous brushes: %d"
"\n * Orphaned Polygons: %d\n"
"\n *** ** WARNING WARNING WARNING ** ***\n"
" ** *** WARNING WARNING WARNING *** **\n",
gWorkingGeometry->getNumAmbiguousBrushes(),
gWorkingGeometry->getNumOrphanPolys());
}
// DMMTODO: store new geometry in the correct instance location
Interior* pRuntime = new Interior;
//
// Support for interior light map border sizes.
//
pRuntime->setLightMapBorderSize(SG_LIGHTMAP_BORDER_SIZE);
dPrintf("\n Exporting to runtime..."); dFflushStdout();
gWorkingGeometry->exportToRuntime(pRuntime, gWorkingResource);
dPrintf("done.\n\n");
dFflushStdout();
gWorkingResource->addDetailLevel(pRuntime);
delete gWorkingGeometry;
gWorkingGeometry = NULL;
}
if (gWorkingResource->getNumDetailLevels() > 0) {
dPrintf(" Writing Resource: "); dFflushStdout();
dPrintf("persist..(%s) ", pOutputName); dFflushStdout();
gWorkingResource->sortDetailLevels();
gWorkingResource->getDetailLevel(0)->processHullPolyLists();
gWorkingResource->getDetailLevel(0)->processVehicleHullPolyLists();
for (U32 i = 1; i < gWorkingResource->getNumDetailLevels(); i++)
gWorkingResource->getDetailLevel(i)->purgeLODData();
FileStream fws;
fws.open(pOutputName, FileStream::Write);
gWorkingResource->write(fws);
fws.close();
dPrintf("Done.\n\n");
dFflushStdout();
delete gWorkingResource;
}
delete [] pOutputName;
for (U32 i = 0; i < mapFileNames.size(); i++)
delete [] mapFileNames[i];
shutdownLibraries();
return 0;
}
void GameReactivate()
{
}
void GameDeactivate( bool )
{
}

102
tools/map2dif/morianBasics.h Executable file
View File

@ -0,0 +1,102 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _MORIANBASICS_H_
#define _MORIANBASICS_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
static const F64 gcPlaneDistanceEpsilon = 0.00001;
static const U32 MaxWindingPoints = 32;
static const U32 MaxWindingNodes = 96;
enum BrushType {
StructuralBrush = 0,
PortalBrush = 1,
DetailBrush = 2,
CollisionBrush = 3,
VehicleCollisionBrush = 4
};
enum PlaneSide {
PlaneFront = 1,
PlaneOn = 0,
PlaneBack = -1
};
enum SpecialPlaneSide {
PlaneSpecialFront = 1 << 0,
PlaneSpecialOn = 1 << 1,
PlaneSpecialBack = 1 << 2
};
class UniqueVector : public Vector<U32>
{
public:
UniqueVector(U32 size) : Vector<U32>(size) { }
UniqueVector() : Vector<U32>(32) { }
void pushBackUnique(U32);
};
struct PlaneEQ
{
Point3D normal;
F64 dist;
PlaneSide whichSide(const Point3D&) const;
PlaneSide whichSidePerfect(const Point3D&) const;
F64 distanceToPlane(const Point3D&) const;
void neg() { normal.neg(); dist = -dist; }
};
struct Winding {
U32 numIndices;
U32 numNodes;
U32 numZoneIds;
U32 indices[MaxWindingPoints];
U32 solidNodes[MaxWindingNodes];
U32 zoneIds[8];
U32 brushId;
Winding* pNext;
Winding() : numNodes(0), numZoneIds(0) { }
void insertZone(U32 zone)
{
// FIXME: lame sort to make sure the zones stay sorted
for (U32 i = 0; i < numZoneIds; i++)
if (zoneIds[i] == zone)
return;
AssertFatal(numZoneIds < 8, "Error, too many zones on a surface. Increase count from 8");
zoneIds[numZoneIds++] = zone;
extern int FN_CDECL cmpZoneNum(const void*, const void*);
dQsort(zoneIds, numZoneIds, sizeof(U32), cmpZoneNum);
}
};
//------------------------------------------------------------------------------
//-------------------------------------- Inlines
//
inline void UniqueVector::pushBackUnique(U32 newElem)
{
for (U32 i = 0; i < size(); i++)
if (operator[](i) == newElem)
return;
push_back(newElem);
}
#endif //_MORIANBASICS_H_

19
tools/map2dif/morianGame.h Executable file
View File

@ -0,0 +1,19 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _H_MORIANGAME_
#define _H_MORIANGAME_
#ifndef _GAMEINTERFACE_H_
#include "platform/gameInterface.h"
#endif
class MorianGame : public GameInterface
{
public:
S32 main(S32 argc, const char **argv);
};
#endif // _H_MORIANGAME_

967
tools/map2dif/morianUtil.cc Executable file
View File

@ -0,0 +1,967 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "map2dif/morianUtil.h"
#include "map2dif/editGeometry.h"
#include "platform/platformAssert.h"
#include "math/mMath.h"
#include "map2dif/csgBrush.h"
// Global brush arena...
BrushArena gBrushArena(1024);
S32 euclidGCD(S32 one, S32 two)
{
AssertFatal(one > 0 && two > 0, "Error");
S32 m = one >= two ? one : two;
S32 n = one >= two ? two : one;
while (true) {
S32 test = m % n;
if (test == 0)
return n;
m = n;
n = test;
}
}
S32 planeGCD(S32 x, S32 y, S32 z, S32 d)
{
S32* pNonZero = NULL;
if (x != 0) pNonZero = &x;
else if (y != 0) pNonZero = &y;
else if (z != 0) pNonZero = &z;
else if (d != 0) pNonZero = &d;
else {
AssertFatal(false, "Cannot pass all zero to GCD!");
return 1;
}
if (x == 0) return planeGCD(*pNonZero, y, z, d);
if (y == 0) return planeGCD(x, *pNonZero, z, d);
if (z == 0) return planeGCD(x, y, *pNonZero, d);
if (d == 0) return planeGCD(x, y, z, *pNonZero);
if (x < 0) x = -x;
if (y < 0) y = -y;
if (z < 0) z = -z;
if (d < 0) d = -d;
// Do a Euclid on these...
return euclidGCD(euclidGCD(euclidGCD(x, y), z), d);
}
void dumpWinding(const Winding& winding, const char* prefixString)
{
for (U32 i = 0; i < winding.numIndices; i++) {
const Point3D& rPoint = gWorkingGeometry->getPoint(winding.indices[i]);
dPrintf("%s(%d) %lg, %lg, %lg\n", prefixString, winding.indices[i], rPoint.x, rPoint.y, rPoint.z);
}
}
//------------------------------------------------------------------------------
F64 getWindingSurfaceArea(const Winding& rWinding, U32 planeEQIndex)
{
// DMMTODO poly area...
Point3D areaNormal(0, 0, 0);
for (U32 i = 0; i < rWinding.numIndices; i++) {
U32 j = (i + 1) % rWinding.numIndices;
Point3D temp;
mCross(gWorkingGeometry->getPoint(rWinding.indices[i]),
gWorkingGeometry->getPoint(rWinding.indices[j]), &temp);
areaNormal += temp;
}
F64 area = mDot(gWorkingGeometry->getPlaneEQ(planeEQIndex).normal, areaNormal);
if (area < 0.0)
area *= -0.5;
else
area *= 0.5;
return area;
}
//------------------------------------------------------------------------------
// Simple in-place clipper. Should be all that's required...
//
bool clipWindingToPlaneFront(Winding* winding, const PlaneEQ& rClipPlane)
{
U32 start;
for (start = 0; start < winding->numIndices; start++) {
const Point3D& rPoint = gWorkingGeometry->getPoint(winding->indices[start]);
if (rClipPlane.whichSide(rPoint) == PlaneFront)
break;
}
if (start == winding->numIndices) {
winding->numIndices = 0;
return true;
}
U32 finalIndices[MaxWindingPoints];
U32 numFinalIndices = 0;
U32 baseStart = start;
U32 end = (start + 1) % winding->numIndices;
bool modified = false;
while (end != baseStart) {
const Point3D& rStartPoint = gWorkingGeometry->getPoint(winding->indices[start]);
const Point3D& rEndPoint = gWorkingGeometry->getPoint(winding->indices[end]);
PlaneSide fSide = rClipPlane.whichSide(rStartPoint);
PlaneSide eSide = rClipPlane.whichSide(rEndPoint);
S32 code = fSide * 3 + eSide;
switch (code) {
case 4: // f f
case 3: // f o
case 1: // o f
case 0: // o o
// No Clipping required
finalIndices[numFinalIndices++] = winding->indices[start];
start = end;
end = (end + 1) % winding->numIndices;
break;
case 2: { // f b
// In this case, we emit the front point, Insert the intersection,
// and advancing to point to first point that is in front or on...
//
finalIndices[numFinalIndices++] = winding->indices[start];
Point3D vector = rEndPoint - rStartPoint;
F64 t = -(rClipPlane.distanceToPlane(rStartPoint) / mDot(rClipPlane.normal, vector));
Point3D intersection = rStartPoint + (vector * t);
AssertFatal(rClipPlane.whichSide(intersection) == PlaneOn,
"CSGPlane::clipWindingToPlaneFront: error in computing intersection");
finalIndices[numFinalIndices++] = gWorkingGeometry->insertPoint(intersection);
U32 endSeek = (end + 1) % winding->numIndices;
while (rClipPlane.whichSide(gWorkingGeometry->getPoint(winding->indices[endSeek])) == PlaneBack)
endSeek = (endSeek + 1) % winding->numIndices;
PlaneSide esSide = rClipPlane.whichSide(gWorkingGeometry->getPoint(winding->indices[endSeek]));
if (esSide == PlaneFront) {
end = endSeek;
start = (end + (winding->numIndices - 1)) % winding->numIndices;
const Point3D& rNewStartPoint = gWorkingGeometry->getPoint(winding->indices[start]);
const Point3D& rNewEndPoint = gWorkingGeometry->getPoint(winding->indices[end]);
vector = rNewEndPoint - rNewStartPoint;
t = -(rClipPlane.distanceToPlane(rNewStartPoint) / mDot(rClipPlane.normal, vector));
intersection = rNewStartPoint + (vector * t);
AssertFatal(rClipPlane.whichSide(intersection) == PlaneOn,
"CSGPlane::clipWindingToPlaneFront: error in computing intersection");
winding->indices[start] = gWorkingGeometry->insertPoint(intersection);
AssertFatal(winding->indices[start] != winding->indices[end], "Error");
} else {
start = endSeek;
end = (endSeek + 1) % winding->numIndices;
}
modified = true;
}
break;
case -1: {// o b
// In this case, we emit the front point, and advance to point to first
// point that is in front or on...
//
finalIndices[numFinalIndices++] = winding->indices[start];
U32 endSeek = (end + 1) % winding->numIndices;
while (rClipPlane.whichSide(gWorkingGeometry->getPoint(winding->indices[endSeek])) == PlaneBack)
endSeek = (endSeek + 1) % winding->numIndices;
PlaneSide esSide = rClipPlane.whichSide(gWorkingGeometry->getPoint(winding->indices[endSeek]));
if (esSide == PlaneFront) {
end = endSeek;
start = (end + (winding->numIndices - 1)) % winding->numIndices;
const Point3D& rNewStartPoint = gWorkingGeometry->getPoint(winding->indices[start]);
const Point3D& rNewEndPoint = gWorkingGeometry->getPoint(winding->indices[end]);
Point3D vector = rNewEndPoint - rNewStartPoint;
F64 t = -(rClipPlane.distanceToPlane(rNewStartPoint) / mDot(rClipPlane.normal, vector));
Point3D intersection = rNewStartPoint + (vector * t);
AssertFatal(rClipPlane.whichSide(intersection) == PlaneOn,
"CSGPlane::clipWindingToPlaneFront: error in computing intersection");
winding->indices[start] = gWorkingGeometry->insertPoint(intersection);
AssertFatal(winding->indices[start] != winding->indices[end], "Error");
} else {
start = endSeek;
end = (endSeek + 1) % winding->numIndices;
}
modified = true;
}
break;
case -2: // b f
case -3: // b o
case -4: // b b
// In the algorithm used here, this should never happen...
AssertISV(false, "CSGPlane::clipWindingToPlaneFront: error in polygon clipper");
break;
default:
AssertFatal(false, "CSGPlane::clipWindingToPlaneFront: bad outcode");
break;
}
}
// Emit the last point.
AssertFatal(rClipPlane.whichSide(gWorkingGeometry->getPoint(winding->indices[start])) != PlaneBack,
"CSGPlane::clipWindingToPlaneFront: bad final point in clipper");
finalIndices[numFinalIndices++] = winding->indices[start];
AssertFatal(numFinalIndices >= 3, "Error, line out of clip!");
// Copy the new rWinding, and we're set!
//
dMemcpy(winding->indices, finalIndices, numFinalIndices * sizeof(U32));
winding->numIndices = numFinalIndices;
AssertISV(winding->numIndices <= MaxWindingPoints,
avar("Increase maxWindingPoints. Talk to DMoore (%d, %d)",
MaxWindingPoints, numFinalIndices));
// DEBUG POINT: No colinear points in the winding
// DEBUG POINT: all points unique in the winding
return modified;
}
bool clipWindingToPlaneFront(Winding* winding, U32 planeEQIndex)
{
// DEBUG POINT: winding contains no colinear points...
const PlaneEQ& rClipPlane = gWorkingGeometry->getPlaneEQ(planeEQIndex);
return clipWindingToPlaneFront(winding, rClipPlane);
}
U32 windingWhichSide(const Winding& rWinding, U32 windingPlaneIndex, U32 planeIndex)
{
AssertFatal(rWinding.numIndices > 2, "Error, winding with invalid number of indices...");
if (gWorkingGeometry->isCoplanar(windingPlaneIndex, planeIndex))
return PlaneSpecialOn;
const PlaneEQ& rPlane = gWorkingGeometry->getPlaneEQ(planeIndex);
U32 retSide = 0;
for (U32 i = 0; i < rWinding.numIndices; i++) {
switch (rPlane.whichSide(gWorkingGeometry->getPoint(rWinding.indices[i]))) {
case PlaneOn:
retSide |= PlaneSpecialOn;
break;
case PlaneFront:
retSide |= PlaneSpecialFront;
break;
case PlaneBack:
retSide |= PlaneSpecialBack;
break;
}
}
return retSide;
}
struct EdgeConnection {
U32 start;
U32 end;
} boxEdges[] = {
{ 0, 1 },
{ 1, 2 },
{ 2, 3 },
{ 3, 0 },
{ 4, 5 },
{ 5, 6 },
{ 6, 7 },
{ 7, 4 },
{ 3, 7 },
{ 0, 4 },
{ 1, 5 },
{ 2, 6 }
};
void createBoundedWinding(const Point3D& minBound,
const Point3D& maxBound,
U32 planeEQIndex,
Winding* finalWinding)
{
Point3D boxPoints[8];
boxPoints[0].set(minBound.x, minBound.y, minBound.z);
boxPoints[1].set(maxBound.x, minBound.y, minBound.z);
boxPoints[2].set(maxBound.x, minBound.y, maxBound.z);
boxPoints[3].set(minBound.x, minBound.y, maxBound.z);
boxPoints[4].set(minBound.x, maxBound.y, minBound.z);
boxPoints[5].set(maxBound.x, maxBound.y, minBound.z);
boxPoints[6].set(maxBound.x, maxBound.y, maxBound.z);
boxPoints[7].set(minBound.x, maxBound.y, maxBound.z);
const PlaneEQ& rPlaneEQ = gWorkingGeometry->getPlaneEQ(planeEQIndex);
PlaneSide sides[8];
for (U32 i = 0; i < 8; i++)
sides[i] = rPlaneEQ.whichSide(boxPoints[i]);
UniqueVector unorderedWinding;
for (U32 i = 0; i < 8; i++)
if (sides[i] == PlaneOn)
unorderedWinding.pushBackUnique(gWorkingGeometry->insertPoint(boxPoints[i]));
for (U32 i = 0; i < sizeof(boxEdges) / sizeof(EdgeConnection); i++) {
if ((sides[boxEdges[i].start] == PlaneBack && sides[boxEdges[i].end] == PlaneFront) ||
(sides[boxEdges[i].end] == PlaneBack && sides[boxEdges[i].start] == PlaneFront)) {
// Emit the intersection...
//
Point3D vector = boxPoints[boxEdges[i].end] - boxPoints[boxEdges[i].start];
F64 t = -(rPlaneEQ.distanceToPlane(boxPoints[boxEdges[i].start]) / mDot(rPlaneEQ.normal, vector));
Point3D intersection = boxPoints[boxEdges[i].start] + (vector * t);
unorderedWinding.pushBackUnique(gWorkingGeometry->insertPoint(intersection));
}
}
if (unorderedWinding.size() <= 2) {
AssertISV(false, "createBoundedWinding: Bad face on brush. < 3 points. DMMNOTE: Handle better?");
finalWinding->numIndices = 0;
return;
}
Vector<U32> modPoints = unorderedWinding;
// Create a centroid first...
Point3D centroid(0, 0, 0);
for (U32 i = 0; i < modPoints.size(); i++)
centroid += gWorkingGeometry->getPoint(modPoints[i]);
centroid /= F64(modPoints.size());
// Ok, we have a centroid. We (arbitrarily) take the last point, and start from
// there...
finalWinding->indices[0] = modPoints[modPoints.size() - 1];
finalWinding->numIndices = 1;
modPoints.erase(modPoints.size() - 1);
while (modPoints.size() != 0) {
// Find the largest dot product of a point with a positive facing
// cross product.
//
F64 maxDot = -1e10;
S32 maxIndex = -1;
const Point3D& currPoint = gWorkingGeometry->getPoint(finalWinding->indices[finalWinding->numIndices - 1]);
Point3D currVector = currPoint - centroid;
currVector.normalize();
for (U32 i = 0; i < modPoints.size(); i++) {
const Point3D& testPoint = gWorkingGeometry->getPoint(modPoints[i]);
Point3D testVector = testPoint - centroid;
testVector.normalize();
Point3D crossVector;
mCross(currVector, testVector, &crossVector);
if (mDot(crossVector, rPlaneEQ.normal) < 0.0) {
// Candidate
F32 val = mDot(testVector, currVector);
if (val > maxDot) {
maxDot = val;
maxIndex = i;
}
}
}
AssertISV(maxIndex != -1, "createBoundedWinding: no point found for winding?");
finalWinding->indices[finalWinding->numIndices++] = modPoints[maxIndex];
modPoints.erase(maxIndex);
}
}
struct FooeyDebug {
Point3D p;
F64 d;
};
void splitBrush(CSGBrush*& inBrush,
U32 planeEQIndex,
CSGBrush*& outFront,
CSGBrush*& outBack)
{
AssertFatal(outFront == NULL && outBack == NULL, "Must be null on entry");
CSGBrush* pFront = gBrushArena.allocateBrush();
CSGBrush* pBack = gBrushArena.allocateBrush();
pFront->copyBrush(inBrush);
pBack->copyBrush(inBrush);
bool regen = false;
for (S32 i = S32(pFront->mPlanes.size()) - 1; i >= 0; i--) {
CSGPlane& rPlane = pFront->mPlanes[i];
if (rPlane.planeEQIndex == planeEQIndex ||
rPlane.planeEQIndex == gWorkingGeometry->getPlaneInverse(planeEQIndex))
continue;
if (rPlane.clipWindingToPlaneFront(planeEQIndex) == true) {
// Winding was modified
regen = true;
if (rPlane.winding.numIndices == 0) {
// Plane is completely gone. nuke it.
pFront->mPlanes.erase(i);
}
}
}
if (pFront->mPlanes.size() <= 1) {
// Nuke it.
gBrushArena.freeBrush(pFront);
pFront = NULL;
} else {
// Otherwise, we have a good brush still. Check to see if we need to add
// a plane, and regen, or can just return it...
if (regen == true) {
pFront->mPlanes.increment();
CSGPlane& rPlane = pFront->mPlanes.last();
rPlane.planeEQIndex = gWorkingGeometry->getPlaneInverse(planeEQIndex);
rPlane.pTextureName = NULL;
rPlane.flags = 0;
for (U32 i = 0; i < pFront->mPlanes.size(); i++)
pFront->mPlanes[i].winding.numIndices = 0;
pFront->selfClip();
}
outFront = pFront;
}
//-------------------------------------- now do the back...
regen = false;
for (S32 i = S32(pBack->mPlanes.size()) - 1; i >= 0; i--) {
CSGPlane& rPlane = pBack->mPlanes[i];
if (rPlane.planeEQIndex == planeEQIndex ||
rPlane.planeEQIndex == gWorkingGeometry->getPlaneInverse(planeEQIndex))
continue;
if (rPlane.clipWindingToPlaneFront(gWorkingGeometry->getPlaneInverse(planeEQIndex)) == true) {
// Winding was modified
regen = true;
if (rPlane.winding.numIndices == 0) {
// Plane is completely gone. nuke it.
pBack->mPlanes.erase(i);
}
}
}
if (pBack->mPlanes.size() <= 1) {
// Nuke it.
gBrushArena.freeBrush(pBack);
pBack = NULL;
} else {
// Otherwise, we have a good brush still. Check to see if we need to add
// a plane, and regen, or can just return it...
if (regen == true) {
pBack->mPlanes.increment();
CSGPlane& rPlane = pBack->mPlanes.last();
rPlane.planeEQIndex = planeEQIndex;
rPlane.pTextureName = NULL;
rPlane.flags = 0;
for (U32 i = 0; i < pBack->mPlanes.size(); i++)
pBack->mPlanes[i].winding.numIndices = 0;
pBack->selfClip();
}
outBack = pBack;
}
}
void assessPlane(const U32 testPlane,
const CSGBrush& rTestBrush,
S32* numCoplanar,
S32* numTinyWindings,
S32* numSplits,
S32* numFront,
S32* numBack)
{
for (U32 i = 0; i < rTestBrush.mPlanes.size(); i++) {
if (gWorkingGeometry->isCoplanar(testPlane, rTestBrush.mPlanes[i].planeEQIndex)) {
// Easy. Brush abuts the plane.
(*numCoplanar)++;
if (testPlane == rTestBrush.mPlanes[i].planeEQIndex)
(*numBack)++;
else
(*numFront)++;
return;
}
}
// Ah well, more work...
const PlaneEQ& rPlane = gWorkingGeometry->getPlaneEQ(testPlane);
static UniqueVector uniquePoints(64);
for (U32 i = 0; i < rTestBrush.mPlanes.size(); i++) {
for (U32 j = 0; j < rTestBrush.mPlanes[i].winding.numIndices; j++)
uniquePoints.pushBackUnique(rTestBrush.mPlanes[i].winding.indices[j]);
}
F64 maxFront = 0.0;
F64 minBack = 0.0;
for (U32 i = 0; i < uniquePoints.size(); i++) {
const Point3D& rPoint = gWorkingGeometry->getPoint(uniquePoints[i]);
F64 dist = rPlane.distanceToPlane(rPoint);
if (dist > maxFront)
maxFront = dist;
if (dist < minBack)
minBack = dist;
}
//AssertFatal(maxFront > gcPlaneDistanceEpsilon || minBack < -gcPlaneDistanceEpsilon,
// "This should only happen for coplanar windings...");
if (maxFront > gcPlaneDistanceEpsilon)
(*numFront)++;
if (minBack < -gcPlaneDistanceEpsilon)
(*numBack)++;
if (maxFront > gcPlaneDistanceEpsilon &&
minBack < -gcPlaneDistanceEpsilon)
(*numSplits)++;
if ((maxFront > 0.0 && maxFront < 1.0) ||
(minBack < 0.0 && minBack > -1.0))
(*numTinyWindings)++;
// done.
uniquePoints.clear();
}
//------------------------------------------------------------------------------
bool windingsEquivalent(const Winding& winding1, const Winding& winding2)
{
if (winding1.numIndices != winding2.numIndices)
return false;
U32 oneCurr = 0;
S32 twoCurr = -1;
for (U32 i = 0; i < winding2.numIndices; i++) {
if (winding2.indices[i] == winding1.indices[0]) {
twoCurr = i;
break;
}
}
if (twoCurr == -1)
return false;
for (U32 i = 0; i < winding1.numIndices; i++) {
if (winding1.indices[oneCurr] != winding2.indices[twoCurr])
return false;
oneCurr = (oneCurr + 1) % winding1.numIndices;
twoCurr = (twoCurr + 1) % winding2.numIndices;
}
return true;
}
//------------------------------------------------------------------------------
bool canCollapseWinding(const Winding& winding1, const Winding& winding2)
{
// We are looking for an edge that is backwards in winding2 that is present
// in winding1. Note that this is not a general purpose algorithm, it's
// very tailored to the break*Winding function from the BSP.
//
AssertFatal(winding1.numIndices >= 3 && winding2.numIndices >= 3, "improper windings");
// First, lets check the ambient light condition...
if (winding1.numZoneIds == 0 || winding2.numZoneIds == 0)
return false;
if (gWorkingGeometry->mZones[winding1.zoneIds[0]]->ambientLit !=
gWorkingGeometry->mZones[winding2.zoneIds[0]]->ambientLit)
return false;
for (U32 i = 0; i < winding1.numIndices; i++) {
U32 edgeStart = winding1.indices[(i + 1) % winding1.numIndices];
U32 edgeEnd = winding1.indices[i];
for (U32 j = 0; j < winding2.numIndices; j++) {
if (winding2.indices[j] == edgeStart &&
winding2.indices[(j + 1) % winding2.numIndices] == edgeEnd) {
// Yay!
return true;
}
}
}
return false;
}
//------------------------------------------------------------------------------
bool pointsAreColinear(U32 p1, U32 p2, U32 p3)
{
const Point3D& rP1 = gWorkingGeometry->getPoint(p1);
const Point3D& rP2 = gWorkingGeometry->getPoint(p2);
const Point3D& rP3 = gWorkingGeometry->getPoint(p3);
// We are testing the distance from p3 to the line defined
// by p1 and p2.
//
F64 f = rP2.x - rP1.x;
F64 g = rP2.y - rP1.y;
F64 h = rP2.z - rP1.z;
// P0 in our case is rP1. Note the the following is taken from
// "A Programmer's Geometry"
//
F64 denom = f*f + g*g + h*h;
if (denom < gcPlaneDistanceEpsilon * gcPlaneDistanceEpsilon) {
// If two points are the same, then all three are colinear...
return true;
}
F64 xj0 = rP3.x - rP1.x;
F64 yj0 = rP3.y - rP1.y;
F64 zj0 = rP3.z - rP1.z;
F64 fygx = f * yj0 - g * xj0;
F64 fzhx = f * zj0 - h * xj0;
F64 gzhy = g * zj0 - h * yj0;
F64 v1 = g * fygx + h * fzhx;
F64 v2 = h * gzhy - f * fygx;
F64 v3 = -f * fzhx - g * gzhy;
F64 dist = mSqrt(v1*v1 + v2*v2 + v3*v3) / denom;
if (dist < gcPlaneDistanceEpsilon)
return true;
else
return false;
}
//------------------------------------------------------------------------------
bool collapseWindings(Winding& into, const Winding& from)
{
if (canCollapseWinding(into, from) == false)
return false;
// DEBUG POINT: Check that into contains no colinear
// DEBUG POINT: Check that from contains no colinear
U32 oneStart;
U32 twoStart;
bool found = false;
for (U32 i = 0; i < into.numIndices && !found; i++) {
U32 edgeStart = into.indices[(i + 1) % into.numIndices];
U32 edgeEnd = into.indices[i];
for (U32 j = 0; j < from.numIndices && !found; j++) {
if (from.indices[j] == edgeStart &&
from.indices[(j + 1) % from.numIndices] == edgeEnd) {
found = true;
oneStart = i;
twoStart = j;
}
}
}
AssertFatal(found == true, "error, something missing in dodge city, no common edge!");
AssertFatal((into.numIndices + from.numIndices - 2) < MaxWindingPoints,
"Uh, need to increase MaxWindingPoints. Talk to DMoore");
U32 newIndices[64];
U32 newCount = 0;
for (U32 i = 0; i <= oneStart; i++) {
newIndices[newCount++] = into.indices[i];
}
for (U32 i = (twoStart + 2) % from.numIndices; i != twoStart;) {
newIndices[newCount++] = from.indices[i];
i = (i+1) % from.numIndices;
}
for (U32 i = (oneStart + 1) % into.numIndices; i > 0;) {
newIndices[newCount++] = into.indices[i];
i = (i+1) % into.numIndices;
}
// Remove any colinear edges...
for (U32 i = 0; i < newCount; i++) {
U32 index0 = i;
U32 index1 = (i + 1) % newCount;
U32 index2 = (i + 2) % newCount;
if (pointsAreColinear(newIndices[index0], newIndices[index1], newIndices[index2])) {
// Remove the edge...
U32 size = (newCount - index1 - 1) * sizeof(U32);
dMemmove(&newIndices[index1], &newIndices[index1+1], size);
newCount--;
}
}
AssertFatal(newCount >= 3 && newCount <= MaxWindingPoints, "Ah, crap, something goofed in collapseWindings. Talk to DMoore");
// See if the new winding is convex
Point3D reference;
for (U32 i = 0; i < newCount; i++) {
U32 j = (i + 1) % newCount;
U32 k = (i + 2) % newCount;
Point3D vec1 = gWorkingGeometry->getPoint(newIndices[i]) - gWorkingGeometry->getPoint(newIndices[j]);
Point3D vec2 = gWorkingGeometry->getPoint(newIndices[k]) - gWorkingGeometry->getPoint(newIndices[j]);
Point3D result;
mCross(vec1, vec2, &result);
if (i == 0) {
reference = result;
} else {
F64 dot = mDot(reference, result);
if (dot < 0.0) {
return false;
}
}
}
// Copy the new rWinding, and we're set!
//
dMemcpy(into.indices, newIndices, newCount * sizeof(U32));
into.numIndices = newCount;
// Make sure the nodes are merged
for (U32 i = 0; i < from.numNodes; i++) {
bool found = false;
for (U32 j = 0; j < into.numNodes; j++) {
if (into.solidNodes[j] == from.solidNodes[i]) {
found = true;
break;
}
}
if (found == false) {
AssertFatal(into.numNodes < MaxWindingNodes, "Error, too many solid nodes affecting poly. Plase ask DMoore to increase the allowed number!");
into.solidNodes[into.numNodes++] = from.solidNodes[i];
}
}
for (U32 i = 0; i < from.numZoneIds; i++)
into.insertZone(from.zoneIds[i]);
// DEBUG POINT: into must contain no colinear
return true;
}
//------------------------------------------------------------------------------
void extendName(char* pName, const char* pExt)
{
AssertFatal(pName != NULL && dStrlen(pName) != 0 && pExt != NULL && pExt[0] == '.',
"Error, bad inputs to reextendName");
dStrcat(pName, pExt);
}
void reextendName(char* pName, const char* pExt)
{
AssertFatal(pName != NULL && dStrlen(pName) != 0 && pExt != NULL && pExt[0] == '.',
"Error, bad inputs to reextendName");
char* pDot = dStrrchr(pName, '.');
if (pDot != NULL) {
if (dStricmp(pDot, pExt) != 0) {
dStrcpy(pDot, pExt);
}
} else {
dStrcat(pName, pExt);
}
}
bool createBaseWinding(const Vector<U32>& rPoints, const Point3D& normal, Winding* pWinding)
{
if (rPoints.size() <= 2) {
dPrintf("::createBaseWinding: Bad face on brush. < 3 points. DMMNOTE: Handle better?\n");
return false;
}
if (rPoints.size() > 32) {
dPrintf("::createBaseWinding: Bad face on brush. > 32 points. DMMNOTE: Increase max winding points?\n");
return false;
}
Vector<U32> modPoints = rPoints;
// Create a centroid first...
Point3D centroid(0, 0, 0);
for (U32 i = 0; i < modPoints.size(); i++)
centroid += gWorkingGeometry->getPoint(modPoints[i]);
centroid /= F64(modPoints.size());
// Ok, we have a centroid. We (arbitrarily) take the last point, and start from
// there...
pWinding->indices[0] = modPoints[modPoints.size() - 1];
pWinding->numIndices = 1;
modPoints.erase(modPoints.size() - 1);
while (modPoints.size() != 0) {
// Find the largest dot product of a point with a positive facing
// cross product.
//
F64 maxDot = -1e10;
S32 maxIndex = -1;
const Point3D& currPoint = gWorkingGeometry->getPoint(pWinding->indices[pWinding->numIndices - 1]);
Point3D currVector = currPoint - centroid;
currVector.normalize();
for (U32 i = 0; i < modPoints.size(); i++) {
const Point3D& testPoint = gWorkingGeometry->getPoint(modPoints[i]);
Point3D testVector = testPoint - centroid;
testVector.normalize();
Point3D crossVector;
mCross(currVector, testVector, &crossVector);
crossVector.normalize();
if (mDot(crossVector, normal) < 0.0) {
// Candidate
F64 val = mDot(testVector, currVector);
F64 dVal = mDot(testVector, currVector);
if (val > maxDot) {
maxDot = val;
maxIndex = i;
}
}
}
//AssertISV(maxIndex != -1, "::createBaseWinding: no point found for winding?");
if(maxIndex == -1)
return false;
pWinding->indices[pWinding->numIndices++] = modPoints[maxIndex];
modPoints.erase(maxIndex);
}
pWinding->numNodes = 0;
pWinding->numZoneIds = 0;
return true;
}
BrushArena::BrushArena(U32 _arenaSize)
{
arenaValid = true;
arenaSize = _arenaSize;
mFreeListHead = NULL;
}
BrushArena::~BrushArena()
{
arenaValid = false;
for (U32 i = 0; i < mBuffers.size(); i++) {
delete [] mBuffers[i];
mBuffers[i] = NULL;
}
mFreeListHead = NULL;
arenaSize = 0;
}
void BrushArena::newBuffer()
{
// Otherwise, we set up some more free windings...
CSGBrush* pNewBuffer = new CSGBrush[arenaSize];
mBuffers.push_back(pNewBuffer);
for (U32 i = 0; i < arenaSize - 1; i++) {
pNewBuffer[i].pNext = &pNewBuffer[i+1];
}
pNewBuffer[arenaSize - 1].pNext = NULL;
mFreeListHead = pNewBuffer;
}
CSGBrush* BrushArena::allocateBrush()
{
AssertFatal(arenaValid == true, "Arena invalid!");
if (mFreeListHead == NULL)
newBuffer();
AssertFatal(mFreeListHead != NULL, "error, that shouldn't happen!");
CSGBrush* pRet = mFreeListHead;
mFreeListHead = pRet->pNext;
pRet->pNext = NULL;
pRet->mIsAmbiguous = false;
pRet->mPlanes.clear();
return pRet;
}
void BrushArena::freeBrush(CSGBrush* junk)
{
AssertFatal(arenaValid == true, "Arena invalid!");
AssertFatal(junk->pNext == NULL, "Error, this is still on the free list!");
junk->pNext = mFreeListHead;
mFreeListHead = junk;
}
bool intersectWGPlanes(U32 i, U32 j, U32 k,
Point3D* pOutput)
{
const PlaneEQ& rPlaneEQ1 = gWorkingGeometry->getPlaneEQ(i);
const PlaneEQ& rPlaneEQ2 = gWorkingGeometry->getPlaneEQ(j);
const PlaneEQ& rPlaneEQ3 = gWorkingGeometry->getPlaneEQ(k);
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-9) {
// 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;
}

78
tools/map2dif/morianUtil.h Executable file
View File

@ -0,0 +1,78 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _MORIANUTIL_H_
#define _MORIANUTIL_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _MORIANBASICS_H_
#include "map2dif/morianBasics.h"
#endif
#ifndef _MBOX_H_
#include "math/mBox.h"
#endif
//-------------------------------------- Util functions
S32 planeGCD(S32 x, S32 y, S32 z, S32 d);
F64 getWindingSurfaceArea(const Winding& rWinding, U32 planeEQIndex);
bool clipWindingToPlaneFront(Winding* rWinding, const PlaneEQ&);
bool clipWindingToPlaneFront(Winding* rWinding, U32 planeEQIndex);
U32 windingWhichSide(const Winding& rWinding, U32 windingPlaneIndex, U32 planeEQIndex);
bool windingsEquivalent(const Winding& winding1, const Winding& winding2);
bool pointsAreColinear(U32 p1, U32 p2, U32 p3);
bool collapseWindings(Winding& into, const Winding& from);
void createBoundedWinding(const Point3D& minBound,
const Point3D& maxBound,
U32 planeEQIndex,
Winding* finalWinding);
bool createBaseWinding(const Vector<U32>& rPoints, const Point3D& normal, Winding* pWinding);
void dumpWinding(const Winding& winding, const char* prefixString);
bool intersectWGPlanes(const U32, const U32, const U32, Point3D*);
class CSGBrush;
void splitBrush(CSGBrush*& inBrush,
U32 planeEQIndex,
CSGBrush*& outFront,
CSGBrush*& outBack);
struct PlaneEQ;
void assessPlane(const U32 testPlane,
const CSGBrush& rTestBrush,
S32* numCoplanar,
S32* numTinyWindings,
S32* numSplits,
S32* numFront,
S32* numBack);
void reextendName(char* pName, const char* pExt);
void extendName(char* pName, const char* pExt);
class BrushArena {
private:
Vector<CSGBrush*> mBuffers;
bool arenaValid;
U32 arenaSize;
CSGBrush* mFreeListHead;
void newBuffer();
public:
BrushArena(U32 _arenaSize);
~BrushArena();
CSGBrush* allocateBrush();
void freeBrush(CSGBrush*);
};
extern BrushArena gBrushArena;
#endif //_MORIANUTIL_H_

165
tools/map2dif/navGraph.cc Executable file
View File

@ -0,0 +1,165 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
/*
lhdo:
overview above the types / process for generating the floor plan resource.
probably axe this file and migrate methods, several of which should be on the
resource editing class.
*/
#include "map2dif/editGeometry.h"
#include "map2dif/csgBrush.h"
#include "core/fileStream.h"
#include "math/mMath.h"
// (rendered TMarker list as initial test).
#define MARKER_LIST_TEST 0
// In addition to a flag, it looks like the graph generation will need
// to adjust the precision variables that are used.
void EditGeometry::setGraphGeneration(bool doGeneration, bool doExtrusion)
{
mGenerateGraph = doGeneration;
mPerformExtrusion = doExtrusion;
if( mPerformExtrusion ){
mHashPoints = mHashPlanes = false;
}
else{
mHashPoints = mHashPlanes = true;
}
// Leaves as before:
mHashPoints = mHashPlanes = true;
}
// Called from bottom of VisLink creating recursion
void EditGeometry::gatherLinksForGraph(EditBSPNode * pLeaf)
{
// Just do solid-to-empty links for now.
if( pLeaf->isSolid )
{
// iterate all visLinks that go to empty, save off winding and the plane.
for( U32 i = 0; i < pLeaf->visLinks.size(); i++ )
{
Winding * pWinding = NULL;
EditBSPNode::VisLink * pLink = pLeaf->visLinks[i];
U32 planeEq;
if( pLink->pBack == pLeaf )
{
if( ! pLink->pFront->isSolid )
{
pWinding = & pLink->winding;
planeEq = pLink->planeEQIndex;
}
}
else
{
if( ! pLink->pBack->isSolid )
{
pWinding = & pLink->winding;
planeEq = getPlaneInverse( pLink->planeEQIndex );
}
}
if( pWinding )
{
#if MARKER_LIST_tEST
for( U32 j = 0; j < pWinding->numIndices; j++ )
mGraphSurfacePts.pushBackUnique( pWinding->indices[j] );
#endif
// Save the windings for parsing later.
mEditFloorPlanRes.pushArea(* pWinding, planeEq);
}
}
}
}
// CS file gives list with element count first
// $list[0] = "19";
// $list[1] = "0 0 20";
// $list[2] = "0 0 30";
// ....
static const char formatStr0[] = "$list[0] = \"%d\";\n";
static const char formatStr1[] = "$list[%d] = \"%lf %lf %lf\";\n";
U32 EditGeometry::writeGraphInfo()
{
#if MARKER_LIST_TEST
if( U32 numPoints = mGraphSurfacePts.size() )
{
FileStream pointFile;
char buff[1024];
if( pointFile.open("points.cs", FileStream::Write) )
{
pointFile.write( dSprintf(buff,sizeof(buff),formatStr0,numPoints), buff );
for( U32 i = 0; i < numPoints; i++ )
{
Point3D pt = getPoint( mGraphSurfacePts[i] );
pt /= 32.0;
dSprintf(buff, sizeof(buff), formatStr1, i + 1, pt.x, pt.y, pt.z);
pointFile.write( dStrlen(buff), buff );
}
pointFile.close();
}
else
dPrintf( "Couldn't open point file for write" );
mGraphSurfacePts.clear();
}
else
dPrintf( "No points were generated for graph" );
#endif
// Test writing the resource
mEditFloorPlanRes.constructFloorPlan();
FileStream fws;
fws.open("sample.flr", FileStream::Write);
mEditFloorPlanRes.write(fws);
fws.close();
return mGraphSurfacePts.size();
}
static Point3D extrusions[3] =
{
Point3D( 0.5, 0.0, 0.0 ),
Point3D( 0.0, 0.5, 0.0 ),
Point3D( 0.0, 0.0, -2.0 )
};
// Perform extrusions on the given brushes. These have not yet been
// selfClip()ped, which is how we leave things. Extrusion routine
// needs the work done by the clipping though (Windings, etc).
void EditGeometry::doGraphExtrusions(Vector<CSGBrush*> & brushList)
{
for( U32 i = 0; i < brushList.size(); i++ )
{
for( U32 axis = 0; axis < 3; axis++ )
{
bool extrudeBothWays = (axis != 2);
CSGBrush * old = brushList[i];
// Create a new extruded brush.
old->selfClip();
brushList[i] = old->createExtruded( extrusions[axis] * 32.0, extrudeBothWays );
gBrushArena.freeBrush( old );
}
}
}
// Just move detail brushes onto structural list for graph generation.
void EditGeometry::xferDetailToStructural()
{
for( S32 i = 0; i < mDetailBrushes.size(); i++ )
mStructuralBrushes.push_back( mDetailBrushes[i] );
mDetailBrushes.clear();
}

189
tools/map2dif/tokenizer.cc Executable file
View File

@ -0,0 +1,189 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "map2dif/tokenizer.h"
#include "platform/platform.h"
#include "core/fileStream.h"
Tokenizer::Tokenizer()
{
dMemset(mFileName, 0, sizeof(mFileName));
mpBuffer = NULL;
mBufferSize = 0;
mCurrPos = 0;
mCurrLine = 0;
dMemset(mCurrTokenBuffer, 0, sizeof(mCurrTokenBuffer));
mTokenIsCurrent = false;
}
Tokenizer::~Tokenizer()
{
dMemset(mFileName, 0, sizeof(mFileName));
delete [] mpBuffer;
mpBuffer = NULL;
mBufferSize = 0;
mCurrPos = 0;
mCurrLine = 0;
dMemset(mCurrTokenBuffer, 0, sizeof(mCurrTokenBuffer));
mTokenIsCurrent = false;
}
bool Tokenizer::openFile(const char* pFileName)
{
AssertFatal(mFileName[0] == '\0', "Reuse of Tokenizers not allowed!");
FileStream* pStream = new FileStream;
if (pStream->open(pFileName, FileStream::Read) == false) {
delete pStream;
return false;
}
dStrcpy(mFileName, pFileName);
mBufferSize = pStream->getStreamSize();
mpBuffer = new char[mBufferSize];
pStream->read(mBufferSize, mpBuffer);
pStream->close();
delete pStream;
// Not really necessary, but couldn't hurt...
mCurrPos = 0;
mCurrLine = 0;
dMemset(mCurrTokenBuffer, 0, sizeof(mCurrTokenBuffer));
mTokenIsCurrent = false;
return true;
}
bool Tokenizer::advanceToken(const bool crossLine, const bool assertAvail)
{
if (mTokenIsCurrent == true) {
AssertFatal(mCurrTokenBuffer[0] != '\0', "No token, but marked as current?");
mTokenIsCurrent = false;
return true;
}
U32 currPosition = 0;
mCurrTokenBuffer[0] = '\0';
while (mCurrPos < mBufferSize) {
char c = mpBuffer[mCurrPos];
bool cont = true;
switch (c) {
case ' ':
case '\t':
if (currPosition == 0) {
// Token hasn't started yet...
mCurrPos++;
} else {
// End of token
mCurrPos++;
cont = false;
}
break;
case '\r':
case '\n':
if (crossLine == true) {
if (currPosition == 0) {
// Haven't started getting token, but we're crossing lines...
while (mpBuffer[mCurrPos] == '\r' || mpBuffer[mCurrPos] == '\n')
mCurrPos++;
mCurrLine++;
} else {
// Getting token, stop here, leave pointer at newline...
cont = false;
}
} else {
cont = false;
break;
}
break;
default:
if (c == '\"') {
// Quoted token
AssertISV(currPosition == 0,
avar("Error, quotes MUST be at start of token. Error: (%s: %d)",
getFileName(), getCurrentLine()));
U32 startLine = getCurrentLine();
mCurrPos++;
while (mpBuffer[mCurrPos] != '\"') {
AssertISV(mCurrPos < mBufferSize,
avar("End of file before quote closed. Quote started: (%s: %d)",
getFileName(), startLine));
AssertISV((mpBuffer[mCurrPos] != '\n' && mpBuffer[mCurrPos] != '\r'),
avar("End of line reached before end of quote. Quote started: (%s: %d)",
getFileName(), startLine));
mCurrTokenBuffer[currPosition++] = mpBuffer[mCurrPos++];
}
mCurrPos++;
cont = false;
} else if (c == '/' && mpBuffer[mCurrPos+1] == '/') {
// Line quote...
if (currPosition == 0) {
// continue to end of line, then let crossLine determine on the next pass
while (mCurrPos < mBufferSize && (mpBuffer[mCurrPos] != '\n' && mpBuffer[mCurrPos] != '\r'))
mCurrPos++;
} else {
// This is the end of the token. Continue to EOL
while (mCurrPos < mBufferSize && (mpBuffer[mCurrPos] != '\n' && mpBuffer[mCurrPos] != '\r'))
mCurrPos++;
cont = false;
}
} else {
mCurrTokenBuffer[currPosition++] = c;
mCurrPos++;
}
break;
}
if (cont == false)
break;
}
mCurrTokenBuffer[currPosition] = '\0';
if (assertAvail == true) {
AssertISV(currPosition != 0, avar("Error parsing: %s at or around line: %d", getFileName(), getCurrentLine()));
}
return currPosition != 0;
}
void Tokenizer::regressToken()
{
AssertFatal(mTokenIsCurrent == false && mCurrPos != 0,
"Error, token already regressed, or no token has been taken yet...");
}
bool Tokenizer::tokenAvailable()
{
// Note: this implies that when advanceToken(false) fails, it must cap the
// token buffer.
//
return mCurrTokenBuffer[0] != '\0';
}
const char* Tokenizer::getToken() const
{
return mCurrTokenBuffer;
}
bool Tokenizer::tokenICmp(const char* pCmp) const
{
return dStricmp(mCurrTokenBuffer, pCmp) == 0;
}

53
tools/map2dif/tokenizer.h Executable file
View File

@ -0,0 +1,53 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _TOKENIZER_H_
#define _TOKENIZER_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
class SizedStream;
class Tokenizer
{
public:
enum {
MaxTokenSize = 1023
};
private:
char mFileName[1024];
char* mpBuffer;
U32 mBufferSize;
U32 mCurrPos;
U32 mCurrLine;
char mCurrTokenBuffer[MaxTokenSize + 1];
bool mTokenIsCurrent;
public:
Tokenizer();
~Tokenizer();
bool openFile(const char* pFileName);
bool advanceToken(const bool crossLine, const bool assertAvailable = false);
void regressToken();
bool tokenAvailable();
const char* getToken() const;
bool tokenICmp(const char* pCmp) const;
const char* getFileName() const { return mFileName; }
U32 getCurrentLine() const { return mCurrLine; }
};
#endif //_TOKENIZER_H_

364
tools/map2dif/torque.fgd Executable file
View File

@ -0,0 +1,364 @@
//------------------------------------------------------------------------------
// Torque game definition file (.fgd) Version 1.0
// for Worldcraft 3.+ and the Torque engine
//------------------------------------------------------------------------------
// worldspawn
//------------------------------------------------------------------------------
@SolidClass = worldspawn : "World entity"
[
detail_number(integer) : "Shape's detail index" : 0
min_pixels(integer) : "Minimum pixels for detail" : 250
geometry_scale(string) : "Geometry scale" : "32.0"
light_geometry_scale(string) : "Lighting scale (must be a power of 2)" : "32.0"
ambient_color(color255) : "Ambient color" : "0 0 0"
emergency_ambient_color(color255) : "Emergency ambient color" : "0 0 0"
]
// --------------------------------------
// special classes
// --------------------------------------
@SolidClass = detail : "Detail Brush Entity"
[
]
@SolidClass = collision : "Collision Brush Entity"
[
]
@SolidClass = vehicle_collision : "Vehicle Collision Brush Entity"
[
]
@SolidClass = portal : "Portal Brush Entity"
[
ambient_light(choices) : "Ambient Light" : 0 =
[
0 : "Does not pass through"
1 : "Passes through"
]
]
@PointClass = MirrorSurface : "Mirror Surface Entity"
[
alpha_level(Choices) : "Translucency" : 0 =
[
0 : "Fully Mirrored"
1 : "Barely there"
2 : "Very Translucent"
3 : "Half-and-Half"
4 : "Muddy Pond"
5 : "Don't shave with this"
6 : "Use texture's alpha channel"
]
]
//------------------------------------------------------------------------------
// BaseClasses
//------------------------------------------------------------------------------
@BaseClass = Targetname
[
name(target_source) : "Name"
]
@BaseClass = Target
[
target(target_destination) : "Target"
]
//------------------------------------------------------------------------------
@BaseClass = LightAnimFlags
[
spawnflags(Flags) =
[
1 : "Auto start" : 1
2 : "Loop to end frame" : 1
4 : "Random frame" : 0
]
]
@BaseClass = LightAnimSpeed
[
speed(choices) : "Speed" : 2 =
[
0: "Very slow"
1: "Slow"
2: "Normal"
3: "Fast"
4: "Very fast"
]
]
@BaseClass = LightFalloffs
[
]
//------------------------------------------------------------------------------
// PointClasses - our entities
//------------------------------------------------------------------------------
@PointClass base(Targetname) = target : "Target" []
@PointClass base(TargetName, LightAnimFlags) = light : "Light"
[
name(target_source) : "Name"
state0_duration(string) : "State0 duration" : "1.0"
state0_color(color255) : "State0 color (R G B)" : "255 255 255"
]
//------------------------------------------------------------------------------
@PointClass base(Target) = light_emitter_point : "Point emitter"
[
state_index(integer) : "State index" : 0
falloff_type(choices) : "Falloff type" : 1 =
[
0 : "Distance"
1 : "Linear"
]
falloff1(integer) : "Falloff1" : 10
falloff2(integer) : "Falloff2" : 100
falloff3(integer) : "Falloff3" : 0
]
//------------------------------------------------------------------------------
@PointClass base(light_emitter_point) = light_emitter_spot : "Spot emitter"
[
direction(string) : "Direction" : "0 0 -1"
theta(string) : "Inner angle" : "0.2"
phi(string) : "Outer angle" : "0.4"
]
//------------------------------------------------------------------------------
// Stock static lights...
//------------------------------------------------------------------------------
@PointClass = light_omni : "Omni Light"
[
color(color255) : "Color (R G B)" : "255 255 255"
alarm_type(choices) : "Alarm Type" : 0 =
[
0 : "Normal only"
1 : "Alarm only"
2 : "Both Alarm and Normal"
]
falloff1(integer) : "Falloff1" : 10
falloff2(integer) : "Falloff2" : 100
]
@PointClass base(Target) = light_spot : "Spot Light"
[
color(color255) : "Color (R G B)" : "255 255 255"
alarm_type(choices) : "Alarm Type" : 0 =
[
0 : "Normal only"
1 : "Alarm only"
2 : "Both Alarm and Normal"
]
falloff1(integer) : "Falloff1" : 10
falloff2(integer) : "Falloff2" : 100
distance1(integer) : "Inner distance" : 10
distance2(integer) : "Outer distance" : 100
]
//------------------------------------------------------------------------------
// Animated lights...
//------------------------------------------------------------------------------
@PointClass base(Targetname, LightAnimSpeed, LightAnimFlags) = light_strobe : "Strobe Light"
[
spawnflags(Flags) =
[
1 : "Auto start" : 1
]
alarm_type(choices) : "Alarm Type" : 0 =
[
0 : "Normal only"
1 : "Alarm only"
]
color1(color255) : "Color1 (R G B)" : "255 255 255"
color2(color255) : "Color2 (R G B)" : "0 0 0"
falloff1(integer) : "Falloff1" : 10
falloff2(integer) : "Falloff2" : 100
]
@PointClass base(Targetname, LightAnimSpeed) = light_pulse : "Pulse Light"
[
spawnflags(Flags) =
[
1 : "Auto start" : 1
]
alarm_type(choices) : "Alarm Type" : 0 =
[
0 : "Normal only"
1 : "Alarm only"
]
color1(color255) : "Color1 (R G B)" : "255 255 255"
color2(color255) : "Color2 (R G B)" : "0 0 0"
falloff1(integer) : "Falloff1" : 10
falloff2(integer) : "Falloff2" : 100
]
@PointClass base(Targetname) = light_pulse2 : "Prog. Pulse Light"
[
spawnflags(Flags) =
[
1 : "Auto start" : 1
]
alarm_type(choices) : "Alarm Type" : 0 =
[
0 : "Normal only"
1 : "Alarm only"
]
color1(color255) : "Color1 (R G B)" : "255 255 255"
color2(color255) : "Color2 (R G B)" : "0 0 0"
falloff1(integer) : "Falloff1" : 10
falloff2(integer) : "Falloff2" : 100
attack(string) : "Attack" : "1.0"
sustain1(string) : "Sustain1" : "1.0"
decay(string) : "Decay" : "1.0"
sustain2(string) : "Sustain2" : "1.0"
]
@PointClass base(Targetname, LightAnimSpeed) = light_flicker : "Flicker Light"
[
spawnflags(Flags) =
[
1 : "Auto start" : 1
]
alarm_type(choices) : "Alarm Type" : 0 =
[
0 : "Normal only"
1 : "Alarm only"
]
color1(color255) : "Color1 (R G B)" : "255 255 255"
color2(color255) : "Color2 (R G B)" : "0 0 0"
color3(color255) : "Color3 (R G B)" : "0 0 0"
color4(color255) : "Color4 (R G B)" : "0 0 0"
color5(color255) : "Color5 (R G B)" : "0 0 0"
falloff1(integer) : "Falloff1" : 10
falloff2(integer) : "Falloff2" : 100
]
@PointClass base(Targetname, Target, LightAnimSpeed) = light_runway : "Runway Light"
[
color(color255) : "Color (R G B)" : "255 255 255"
spawnflags(Flags) =
[
1 : "Auto start" : 1
]
alarm_type(choices) : "Alarm Type" : 0 =
[
0 : "Normal only"
1 : "Alarm only"
]
pingpong(choices) : "Ping pong?" : 0 =
[
0 : "No"
1 : "Yes"
]
steps(integer) : "Steps" : 0
falloff1(integer) : "Falloff1" : 10
falloff2(integer) : "Falloff2" : 100
]
// --------------------------------------------------------------------------
// Triggers, etc...
// --------------------------------------------------------------------------
@SolidClass = trigger : "Trigger Entity"
[
name(string) : "Trigger Name" : "MustChange"
]
// --------------------------------------------------------------------------
// Doors, elevators, etc...
// --------------------------------------------------------------------------
@SolidClass = Door_Elevator : "Door or Elevator"
[
name(string) : "Name" : "MustChange"
path_name(string) : "Path subscription" : ""
trigger0_name(string) : "Trigger 0" : ""
trigger1_name(string) : "Trigger 1" : ""
trigger2_name(string) : "Trigger 2" : ""
trigger3_name(string) : "Trigger 3" : ""
trigger4_name(string) : "Trigger 4" : ""
trigger5_name(string) : "Trigger 5" : ""
trigger6_name(string) : "Trigger 6" : ""
trigger7_name(string) : "Trigger 7" : ""
]
@SolidClass = Force_Field : "Force Field"
[
name(string) : "Name" : "MustChange"
color(color255) : "Field color" : "125 216 232"
trigger0_name(string) : "Trigger 0" : ""
trigger1_name(string) : "Trigger 1" : ""
trigger2_name(string) : "Trigger 2" : ""
trigger3_name(string) : "Trigger 3" : ""
trigger4_name(string) : "Trigger 4" : ""
trigger5_name(string) : "Trigger 5" : ""
trigger6_name(string) : "Trigger 6" : ""
trigger7_name(string) : "Trigger 7" : ""
]
// --------------------------------------------------------------------------
// Paths, etc...
// --------------------------------------------------------------------------
@PointClass = path_node : "Path Node"
[
name(target_source) : "Name"
next_node(target_destination) : "Next Node"
next_time(integer) : "MS to next node" : 1000
]
@PointClass = path_start : "Path Start"
[
name(target_source) : "Name"
next_node(target_destination) : "Next Node"
next_time(integer) : "MS to next node" : 1000
]
// --------------------------------------------------------------------------
// Triggers, etc...
// --------------------------------------------------------------------------
//@SolidClass = Volume_Trigger : "Volume Trigger"
//[
// team_only(choices) : "Team Activated" : 0 =
// [
// 0 : "No"
// 1 : "Yes"
// ]
// trigger_name(string) : "Trigger Name" : ""
//]
// --------------------------------------------------------------------------
// AI Special Node - for chutes and special interior cases for Bots
// --------------------------------------------------------------------------
@PointClass = ai_special_node : "AI Special Node"
[
name(target_source) : "Name"
]