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

BIN
tools/BUTT.UFT Normal file

Binary file not shown.

52
tools/Makefile Executable file
View File

@ -0,0 +1,52 @@
#--------------------------------------
# include and verify the users mk/conf.mk
-include ../mk/conf.mk
ifndef CONFIG_STATUS
doConfigure:
$(error Configuration file not defined. Please run make -f mk/configure.mk)
#@make --no-print-directory -f ../mk/configure.mk
else
ifeq ($(CONFIG_STATUS),INVALID)
doConfigure:
$(error Invalid Configuration file. Please run make -f mk/configure.mk)
#@make --no-print-directory -f mk/configure.mk
else
include ../mk/conf.$(COMPILER).$(OS).mk
include ../mk/conf.$(COMPILER).mk
endif
endif
all: max2mapExporter map2dif buildWad texture2bm8
ifeq "$(COMPILER)" "VC6"
# The MAX31 SDK requires VC++ :(
include targets.max2dtsExporter.mk
include targets.max2mapExporter.mk
else
# dummy target for other compilers
max2dtsExporter: ;
max2mapExporter: ;
endif
include targets.map2dif.mk
include targets.buildWad.mk
engine$(EXT.LIB):
@make -s -C ../engine $(DIR.OBJ)/engine$(EXT.LIB)
@make -s -C ../lib default
include ../mk/conf.common.mk
#default:
# echo default.
ifneq ($(MAKECMDGOALS),clean)
-include $(addprefix $(DIR.OBJ)/, $(addsuffix $(DEP), $(basename $(filter %.cc %.c,$(SOURCE.ALL)))))
endif

BIN
tools/__fonttool.exe Normal file

Binary file not shown.

BIN
tools/_fonttool.exe Normal file

Binary file not shown.

508
tools/blender/ExportDTS.py Executable file
View File

@ -0,0 +1,508 @@
#
# Torque Game Engine DTS exporter for blender
#
import Blender
import string
#from Blender import NMesh,Object
from math import *
VERSION = "0.1"
##############################
# Vector class
##############################
class Vector:
members = [0, 0, 0]
def __init__(self, x, y, z):
self.members = [float(x), float(y), float(z)]
def __getitem__(self, key):
return(self.members[key])
def __setitem__(self, key, value):
self.members[key] = value
def __add__(self, other):
# iterate through the members
for i in range(len(self.members)):
# add them together
self[i] = float(self[i]) + float(other[i])
return(self)
def __sub__(self, other):
# iterate through the members
for i in range(len(self.members)):
# subtract them
self[i] = float(self[i]) - float(other[i])
return(self)
def __mul__(self, other):
# iterate through the members
for i in range(len(self.members)):
# multiply by the val stored in other
self[i] *= float(other)
return(self)
def __div__(self, other):
# iterate through the members
for i in range(len(self.members)):
# divide by the val stored in other
self[i] /= float(other)
return(self)
#def normalize(self):
##############################
# end of Vector class
##############################
##############################
# Node class - DTS tree node
##############################
class Node:
name = 0 # index of its name in the DTS string table
parent = -1 # number of the parent node; -1 if root
firstObject = -1 # deprecated; set to -1
child = -1 # deprecated; set to -1
sibling = -1 # deprecated; set to -1
##############################
# end of Node class
##############################
##############################
# Object class - DTS object
##############################
class Object:
name = 0 # index of its name in the DTS string table
numMeshes = 0 # number of meshes (only one for detail level)
firstMesh = 0 # number of the first mesh (meshes must be consecutive)
node = 0 # number of the node where the object is stored
sibling = -1 # deprecated; set to -1
firstDecal = -1 # deprecated; set to -1
##############################
# end of Object class
##############################
##############################
# Material class - DTS material
##############################
class Material:
name = 0 # texture name; materials don't use the DTS string table
flags = 0 # boolean properties
reflectance = 0 # number of reflectance map?
bump = 0 # number of bump map? or -1 if none
detail = 0 # number of detail map? or -1 if none
detailScale = 0 # ?
reflection = 0 # ?
# material flags
SWrap = 0x00000001
TWrap = 0x00000002
Translucent = 0x00000004
Additive = 0x00000008
Subtractive = 0x00000010
SelfIlluminating = 0x00000020
NeverEnvMap = 0x00000040
NoMipMap = 0x00000080
MipMapZeroBorder = 0x00000100
IFLMaterial = 0x00000000
IFLFrame = 0x10000000
DetailMap = 0x20000000
BumpMap = 0x40000000
ReflectanceMap = 0x80000000
AuxiliaryMask = 0xF0000000
##############################
# end of Material class
##############################
##############################
# IFLMaterial class - DTS animated material
##############################
class IFLMaterial:
name = 0
slot = 0
firstFrame = 0
time = 0
numFrames = 0
##############################
# end of IFLMaterial class
##############################
##############################
# DetailLevel class - DTS detail level
##############################
class DetailLevel:
name = 0 # index of the name in the DTS string table
subshape = 0 # number of the subshape it belongs to
objectDetail = 0 # number of object mesh to draw for each object
size = 0 # minimum pixel size
avgError = -1 # ?
maxError = -1 # ?
polyCount = 0 # polygon count of the meshes to draw
##############################
# end of DetailLevel class
##############################
##############################
# Subshape class - DTS subshape (defines a range of nodes and objects)
##############################
class Subshape:
firstNode = 0
firstObject = 0
firstDecal = 0
numNodes = 0
numObjects = 0
numDecals = 0
firstTranslucent = 0 # not used?
##############################
# end of Subshape class
##############################
##############################
# ObjectState class - related to animated materials?
##############################
class ObjectState:
vis = 0
frame = 0
matFrame = 0
##############################
# end of ObjectState class
##############################
##############################
# Trigger class
##############################
class Trigger:
state = 0
pos = 0
##############################
# end of Trigger class
##############################
##############################
# Sequence class
##############################
nameIndex = 0
flags = 0
numKeyFrames = 0
duration = 0
priority = 0
firstGroundFrame = 0
numGroundFrames = 0
baseRotation = 0
baseTranslation = 0
baseScale = 0
baseObjectState = 0
baseDecalState = 0
firstTrigger = 0
numTriggers = 0
toolBegin = 0
matters = 0
# flags
UniformScale = 0x0001
AlignedScale = 0x0002
ArbitraryScale = 0x0004
Blend = 0x0008
Cyclic = 0x0010
MakePath = 0x0020
IFLInit = 0x0040
HasTranslucency = 0x0080
##############################
# end of Sequence class
##############################
##############################
# Matters class
##############################
class Matters:
rotation = 0
translation = 0
scale = 0
decal = 0
ifl = 0
vis = 0
frame = 0
matframe = 0
##############################
# end of Matters class
##############################
##############################
# Box class
##############################
class Box:
min = Vector
max = Vector
##############################
# end of Box class
##############################
##############################
# Primitive class
##############################
class Primitive:
firstElement = 0
numElements = 0
type = 0
# types
Triangles = 0x00000000
Strip = 0x40000000
Fan = 0x80000000 # may not be supported in the engine?
TypeMask = 0xC0000000
Indexed = 0x20000000 # not supported in the engine?
NoMaterial = 0x10000000
MaterialMask = 0x0FFFFFFF
##############################
# end of Primitive class
##############################
##############################
# Primitive class
##############################
class Mesh:
# members
type = 0
numFrames = 0
matFRames = 0
parent = 0
verts = []
tverts = []
normals = []
enormals = []
primitives = []
indices = []
mindices = []
### BOX ###
bounds = Box
### BOX ###
center = Vector(0, 0, 0)
radius = 0
vertsPerFrame = 0
flags = 0
# data used by skin meshes
vindex = 0
vbone = 0
vweight = 0
nodeIndex = 0
nodeTransform = 0
# types
T_Standard = 0
T_Skin = 1
T_Decal = 2
T_Sorted = 3
T_Null = 4
# flags
Billboard = 0x80000000
HasDetail = 0x40000000
BillboardZ = 0x20000000
EncodedNormals = 0x10000000
def __init__(self, t):
self.bounds.min = Vector(0, 0, 0)
self.bounds.max = Vector(0, 0, 0)
self.center = Vector(0, 0, 0)
self.radius = float(0.0)
self.numFrames = 1
self.matFrames = 1
self.vertsPerFrame = 0
self.parent = -1
self.flags = 0
self.type = t
def getType(self):
return(type)
def setType(self, t):
type = t
def setFlag(self, f):
flags |= f
def getPolyCount(self):
count = 0
for p in range(len(primitives)):
if (primitives[p].type & Primitive.Stripe):
count += primitives[p].numElements - 2
else:
count += primitives[p].numElements / 3
return(count)
def getRadius(self):
return(radius)
def getCenter(self):
return(center)
def getBounds(self):
return(bounds)
def setMaterial(self, n):
for p in range(len(primitives)):
p.type = (p.type & ~Primitive.MaterialMask) | (n & Primitive.MaterialMask)
def getVertexBone(self, node):
b = 0
for b in range(len(nodeIndex)):
if (nodeIndex[b] == node):
return(b)
def getNodeIndexCount(self):
return(len(nodeIndex))
def getNodeIndex(self, node):
if node >= 0:
if node < len((nodeIndex)):
return(nodeIndex[node])
return(-1)
#def setNodeTransform(self, node, t, q):
def setCenter(self, c):
center = c
def setBounds(self, b):
bounds = b
def setRadius(self, r):
radius = r
def setFrames(self, n):
self.numFrames = n
self.vertsPerFrame = len(self.verts)/n
def setParent(self, n):
parent = n
def calculateBounds(self):
self.bounds.max = Vector(-10e30, -10e30, -10e30)
self.bounds.min = Vector(10e30, 10e30, 10e30)
for vertex in self.verts:
if vertex[0] < self.bounds.min[0]:
self.bounds.min[0] = vertex[0]
if vertex[1] < self.bounds.min[1]:
self.bounds.min[1] = vertex[1]
if vertex[2] < self.bounds.min[2]:
self.bounds.min[2] = vertex[2]
if vertex[0] > self.bounds.max[0]:
self.bounds.max[0] = vertex[0]
if vertex[1] > self.bounds.max[1]:
self.bounds.max[1] = vertex[1]
if vertex[2] > self.bounds.max[2]:
self.bounds.max[2] = vertex[2]
def calculateCenter(self):
for v in range(len(self.bounds.max.members)):
self.center[v] = ((self.bounds.min.members[v] - self.bounds.max.members[v])/2) + self.bounds.max.members[v]
def calculateRadius(self):
for vertex in self.verts:
tV = vertex - self.center
result = 0
for n in range(len(tV.members)):
result += tV.members[n] * tV.members[n]
distance = sqrt(result)
if distance > self.radius:
self.radius = distance
def encodeNormal(self, p):
bestIndex = 0
#def save(self):
##############################
# end of Mesh class
##############################
##############################
# Shape class
##############################
class Shape:
nodes = 0
objects = 0
decals = 0
subshapes = 0
IFLmaterials = 0
materials = 0
nodeDefRotations = 0
nodeDefTranslations = 0
#def save(self):
#def read(self):
#def getBounds(self):
#def getRadius(self):
#def getTubeRadius(self):
#def addName(self, s):
#def calculateBounds(self):
#def calculateRadius(self):
#def calculateTubeRadius(self):
#def calculateCenter(self):
#def setSmallestSize(self, i):
#def setCenter(self, p):
#def getNodeWorldPosRot(self, n, trans, rot):
#def write(self):
##############################
# end of Shape class
##############################
# functions
# strips the path off of a filepath specified.
def basename(filepath):
if "\\" in filepath:
words = string.split(filepath, "\\")
else:
words = string.split(filepath, "/")
words = string.split(words[-1], ".")
return string.join(words[:-1], ".")
def process_objects(objnames):
# objects
dtsobjs = []
numdtsobjs = 0
print "Objects:"
for objname in objnames:
if not type(objname.data) == Blender.Types.NMeshType:
continue
print "--> ", objname.data.name
print "location = ", objname.getLocation()
meshobj = Blender.NMesh.GetRaw(objname.data.name)
dtsobj = Mesh(Mesh.T_Standard)
dtsobj.numFrames = 1
dtsobj.matFrames = 1
dtsobj.verts = []
dtsobj.tverts = []
dtsobj.normals = []
dtsobj.enormals = []
# get verts, uv coords, normals, and enormals
for v in range(len(meshobj.verts)):
###print "v = ", v, "x = ", meshobj.verts[v].co[0], ", y = ", meshobj.verts[v].co[1], ", z = ", meshobj.verts[v].co[2]
# verts
dtsobj.verts.append(Vector(meshobj.verts[v].co[0], meshobj.verts[v].co[1], meshobj.verts[v].co[2]))
# uv coords
dtsobj.tverts.append(meshobj.verts[v].uvco[0])
dtsobj.tverts.append(meshobj.verts[v].uvco[1])
# normals
dtsobj.normals.append(meshobj.verts[v].no[0])
dtsobj.normals.append(meshobj.verts[v].no[1])
dtsobj.normals.append(meshobj.verts[v].no[2])
# enormals
dtsobj.enormals.append(meshobj.verts[v].no[0])
dtsobj.enormals.append(meshobj.verts[v].no[1])
dtsobj.enormals.append(meshobj.verts[v].no[2])
for f in meshobj.faces:
print "vIx = ", meshobj.verts.index(f.v[0]), "vIy = ", meshobj.verts.index(f.v[1]), "vIz = ", meshobj.verts.index(f.v[2])
dtsobj.indices.append(meshobj.verts.index(f.v[2]))
dtsobj.indices.append(meshobj.verts.index(f.v[1]))
dtsobj.indices.append(meshobj.verts.index(f.v[0]))
# create our primitive
p = Primitive
p.firstElement = len(dtsobj.indices)
p.numElements = 3
p.type = Primitive.Strip | Primitive.Indexed
# no materials for now
p.type |= Primitive.NoMaterial
dtsobj.setFrames(1)
dtsobj.setParent(-1)
dtsobj.calculateBounds()
dtsobj.calculateCenter()
dtsobj.calculateRadius()
dtsobj.vertsPerFrame = len(dtsobj.verts)
dtsobj.primitives.append(p)
dtsobjs.insert(numdtsobjs, dtsobj)
numdtsobjs += 1
##############################
# main logic
##############################
if __name__ == "__main__":
print "DTS Exporter running..."
# filename to write out
filename = basename(Blender.Get("filename")) + ".dts"
print "Writing file: \"%s\"" % filename
# get all of the objects
objs = Blender.Object.get()
# count the objects
numdtsobjs = 0
for objname in objs:
if not type(objname.data) == Blender.Types.NMeshType:
continue
numdtsobjs += 1
if numdtsobjs <= 0:
print "Nothing to export"
exit
# process our objects
process_objects(objs)
# fun times
t = Vector(1, 2, 3)
s = Vector(3, 3, 4)
v = s / 2
print "x = ", v[0], ", y = ", v[1], ", z = ", v[2]
print "Primitive.Strip = 0x%x" % Primitive.Strip
# end of the fun times
print "DTS Exporter complete."

1
tools/broken.dat Normal file
View File

@ -0,0 +1 @@
 !"$%&'()+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]_abcdefghijklmnopqrstuvwxyz{}、。あいうえおかがきぎくけげこごさざしじすずせそぞただちっつづてでとどなにねのはばびぶへべほまみむめもやゆょよらりるれろわをんァアィイウエォオカガキギクグケゲコゴサザシジスズセゾタダチッテデトドナニネノハバパビピフブプヘベホボポマミムメモャヤュョラリルレロワンー一三上下不世中乗了予事二人今他以仲任会伴位体作使依保信個側備傾元入全内再出分切初利制前力加効動勝勢勾化十危原参反取受叩可右各合名向否含吹周器回因困囲在地坂城場増壁変外多大天失始子存安完定宝実害家対小少層山岐工左巨巻席平広床度引弾影待後得心必忍急性意成戦戻所手抜択披押拒招指挑探接損摩操敗散数整斜断新方旋早易時暴更替最期木未果検極楼楽横橋機欠次止残段気氷汰況法注洳浮淘済渡満源準滑火点無然版物特状狭獲玉現生用画界番登的盤目直相着瞬短石破確磨示秒移穴究空突窟端競素索細終経続総線練縮績繰羅者耐胸能脱腕自色落螺行表裁要覆見視解触言計記設試認語読調貴購走起越足路跳踏転軸軽込近返迷追退送途通速連進遅過道達違適選避都配重針録長門開間限除険階隙障隠集難雲雷震霜露面音響頂頼風飛高黒!&?

BIN
tools/buildWad.exe Normal file

Binary file not shown.

170
tools/buildWad/main.cc Executable file
View File

@ -0,0 +1,170 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/platformAssert.h"
#include "math/mMath.h"
#include "core/resManager.h"
#include "console/console.h"
#include "dgl/gBitmap.h"
#include "dgl/gTexManager.h"
#include "console/consoleTypes.h"
#include "math/mathTypes.h"
#include "platform/event.h"
#include "core/frameAllocator.h"
#include "buildWad/wadProcessor.h"
//------------------------------------------------------------------------------
#include "platform/gameInterface.h"
class WadToolGame : public GameInterface
{
public:
S32 main(S32 argc, const char **argv);
} GameObject;
// FOR SILLY LINK DEPENDANCY
bool gEditingMission = false;
void GameHandleNotify(NetConnectionId, bool)
{
}
#if defined(TORQUE_DEBUG)
const char * const gProgramVersion = "0.900d-beta";
#else
const char * const gProgramVersion = "0.900r-beta";
#endif
//------------------------------------------------------------------------------
static bool initLibraries()
{
// asserts should be created FIRST
PlatformAssert::create();
FrameAllocator::init(1 << 20);
FrameAllocator::setWaterMark(0);
_StringTable::create();
TextureManager::create();
Con::init();
// Processor::init();
Math::init();
Platform::init(); // platform specific initialization
return(true);
}
//------------------------------------------------------------------------------
static void shutdownLibraries()
{
// shut down
Platform::shutdown();
Con::shutdown();
TextureManager::destroy();
_StringTable::destroy();
FrameAllocator::destroy();
// asserts should be destroyed LAST
PlatformAssert::destroy();
}
//------------------------------------------------------------------------------
void (*terrMipBlit)(U16 *dest, U32 destStride, U32 squareSize, const U8 *sourcePtr, U32 sourceStep, U32 sourceRowAdd) = 0;
struct Event;
void GamePostEvent(const Event& /*event*/)
{
//
}
S32 WadToolGame::main(int argc, const char ** argv)
{
if(!initLibraries())
return(0);
// give the console the command line
Con::setIntVariable("Game::argc", argc);
for(S32 i = 0; i < argc; i++)
Con::setVariable(avar("Game::argv%d", i), argv[i]);
// so the debugger can actually do something....
// if(argc<3)
// {
// const char* argvFake[] = { "wadtool", "lushTest", "lushtest.wad"};
// argc = 3;
// argv = argvFake;
// }
// info
if(argc <= 2)
{
dPrintf("\nbuildWad - Torque wad file creator\n"
" Copyright (C) GarageGames.com, Inc.\n"
" Programming by John Folliard\n"
" Program version: %s\n"
" Built: %s at %s\n", gProgramVersion, __DATE__, __TIME__);
dPrintf("\n Usage: buildWad -x <[@]source> <outputFile>.wad\n");
dPrintf("\n buildWad -x <directory>|<source.png>... <outputFile>.wad\n");
dPrintf( " -x: allow each texture to have a unique palette.\n");
shutdownLibraries();
return(0);
}
// create the wad processor - have it write a header then allow the lumps
// to write themseleves out, then close and fixup the wad
WadProcessor * waddy = new WadProcessor();
if (!waddy->open(argv[argc-1])) {
dPrintf(" *** Could not open wad file: %s\n",argv[argc-1]);
return(1);
}
U32 startArg = 1;
bool singlePalette = true;
// check for a switch
if(argv[1][0] == '-')
{
if(U8(dTolower(argv[1][1])) == 'x')
{
startArg++;
singlePalette = false;
}
}
// process the list of bitmaps...
for(S32 i = startArg; i < (argc-1); i++)
waddy->addBitmaps(argv[i]);
//
if(singlePalette)
waddy->processSinglePalette();
else
waddy->processBitmaps();
waddy->close();
delete waddy;
shutdownLibraries();
return(0);
}
void GameReactivate()
{
}
void GameDeactivate( bool )
{
}

305
tools/buildWad/palQuantization.cc Executable file
View File

@ -0,0 +1,305 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "buildWad/palQuantization.h"
#include "math/mPoint.h"
//------------------------------------------------------------------------------
// Class PalQuantizer:
//------------------------------------------------------------------------------
PalQuantizer::PalQuantizer() :
mLeafLevel(MaxDepth+1),
mNumLeafs(0),
mRoot(0),
mNumColors(0),
mPalette(0)
{
for(U32 i = 0; i < (MaxDepth+1); i++)
mReduceList[i] = 0;
}
PalQuantizer::~PalQuantizer()
{
deleteNode(mRoot);
delete [] mPalette;
}
//------------------------------------------------------------------------------
PalNode * PalQuantizer::makeNode(U32 level)
{
bool leaf = (level >= mLeafLevel) ? true : false;
if(leaf)
mNumLeafs++;
return(new PalNode(level, leaf));
}
//------------------------------------------------------------------------------
void PalQuantizer::addToTree(GBitmap * bitmap, U32 numColors)
{
AssertFatal(bitmap && numColors, "PalQuantizer::addToTree - invalid params");
U32 width = bitmap->getWidth();
U32 height = bitmap->getHeight();
// check for first run - create root and palette
if(!mRoot)
{
mRoot = makeNode(0);
mPalette = new ColorI[numColors];
for(U32 i = 0; i < numColors; i++)
mPalette[i].set(0,0,0);
}
// walk this bitmap
for(U32 y = 0; y < height; y++)
for(U32 x = 0; x < width; x++)
{
ColorI col;
bitmap->getColor(x,y,col);
insertNode(mRoot, col);
while(mNumLeafs > numColors)
reduceTree();
}
}
//------------------------------------------------------------------------------
void PalQuantizer::buildTree(GBitmap * bitmap, U32 numColors)
{
AssertFatal(bitmap && numColors, "PalQuantizer::buildTree - invalid params");
AssertFatal(!mRoot, "PalQuantizer::buildTree - already built");
AssertFatal(!mPalette, "PalQuantizer::buildTree - already build");
mPalette = new ColorI[numColors];
for(U32 i = 0; i < numColors; i++)
mPalette[i].set(0,0,0);
U32 width = bitmap->getWidth();
U32 height = bitmap->getHeight();
mRoot = makeNode(0);
// build it...
for(U32 y = 0; y < height; y++)
{
for(U32 x = 0; x < width; x++)
{
ColorI col;
AssertISV(bitmap->getColor(x,y,col), "PalQuantizer::buildTree: failed to get pixel.");
insertNode(mRoot, col);
while(mNumLeafs > numColors)
reduceTree();
}
}
// fill in the color palette...
fillPalette(mRoot, &mNumColors);
}
//------------------------------------------------------------------------------
void PalQuantizer::fillPalette(PalNode * node, U32 * index)
{
if(node)
{
if(node->mLeaf || (node->mLevel == mLeafLevel))
{
mPalette[*index] = node->getColor();
node->mIndex = *index;
(*index)++;
}
else
{
// do children
for(U32 i = 0; i < 8; i++)
fillPalette(node->mChild[i], index);
}
}
}
//------------------------------------------------------------------------------
void PalQuantizer::insertNode(PalNode * node, const ColorI & col)
{
AssertFatal(node, "PalQuantizer::insertNode: invalid args");
// add the color to the node
node->addColor(col);
if(!node->mLeaf && (node->mLevel < mLeafLevel))
{
U32 index = node->findChild(col);
AssertFatal(index < 8, "PalQuantizer::insertNode - bad child index");
if(node->mChild[index])
{
if((node->mCount > 1) && !node->mMarked)
makeReducible(node);
}
else
{
// create this child
node->mChild[index] = makeNode(node->mLevel + 1);
node->mNumChildren++;
}
// insert into child's octree
insertNode(node->mChild[index], col);
}
}
//------------------------------------------------------------------------------
void PalQuantizer::makeReducible(PalNode * node)
{
AssertFatal(node, "PalQuantizer::makeReducible: invalid args");
// add to the reduce list
PalNode * head = mReduceList[node->mLevel];
node->mNext = head;
if(head)
head->mPrev = node;
mReduceList[node->mLevel] = node;
node->mMarked = true;
}
//------------------------------------------------------------------------------
PalNode * PalQuantizer::getReducibleNode()
{
U32 newLevel = mLeafLevel - 1;
while(!mReduceList[newLevel])
newLevel--;
// get the node with the largest pixel count..
PalNode * node = mReduceList[newLevel];
PalNode * current = 0;
while(node)
{
if(!current)
current = node;
else if(node->mCount < current->mCount)
current = node;
node = node->mNext;
}
return(current);
}
//------------------------------------------------------------------------------
void PalQuantizer::deleteNode(PalNode * node)
{
if(!node)
return;
if(!node->mLeaf)
{
for(U32 i = 0; i < 8; i++)
{
if(node->mChild[i])
{
deleteNode(node->mChild[i]);
node->mChild[i] = 0;
node->mNumChildren--;
}
}
}
else
mNumLeafs--;
delete node;
}
//------------------------------------------------------------------------------
void PalQuantizer::reduceTree()
{
PalNode * node = getReducibleNode();
AssertFatal(node, "PalQuantizer::reduceTree - failed to get reducible node");
// remove lowest child
U32 lowest = -1;
for(U32 i = 0; i < 8; i++)
if(node->mChild[i])
{
if(lowest == -1)
lowest = i;
else if(node->mChild[i]->mCount < node->mChild[lowest]->mCount)
lowest = i;
}
AssertFatal(lowest != -1, "PalQuantizer::reduceTree - bad node");
deleteNode(node->mChild[lowest]);
node->mChild[lowest] = 0;
node->mNumChildren--;
if(!node->mNumChildren)
{
node->mLeaf = true;
mNumLeafs++;
// remove the node from the reduce list
PalNode * next = node->mNext;
PalNode * prev = node->mPrev;
if(!prev)
{
mReduceList[node->mLevel] = next;
if(next)
next->mPrev = 0;
}
else
{
prev->mNext = next;
if(next)
next->mPrev = prev;
}
node->mNext = node->mPrev = 0;
}
}
//------------------------------------------------------------------------------
U32 PalQuantizer::quantizeColor(PalNode * node, const ColorI & col)
{
if(node->mLeaf || (node->mLevel == mLeafLevel))
return(node->mIndex);
U32 index = node->findChild(col);
if(!node->mChild[index])
{
// get the child that is closest..
S32 closest = -1;
F64 dist = 0.f;
for(U32 i = 0; i < 8; i++)
{
if(node->mChild[i])
{
ColorI childCol = node->mChild[i]->getColor();
Point3D pnt(col.red - childCol.red, col.green - childCol.green, col.blue - childCol.blue);
F64 len = pnt.len();
if((closest == -1) || (len < dist))
{
closest = i;
dist = len;
}
break;
}
}
AssertFatal(closest != -1, "PalQuantizer::quantizeColor - failed to get child node");
index = closest;
}
return(quantizeColor(node->mChild[index], col));
}

176
tools/buildWad/palQuantization.h Executable file
View File

@ -0,0 +1,176 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _INC_PALQUANTIZATION
#define _INC_PALQUANTIZATION
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _GBITMAP_H_
#include "dgl/gBitmap.h"
#endif
static const U32 MaxDepth = 7;
static const U32 NumColors = 256;
// Octree Color Quantization
class PalNode
{
public:
U32 mLevel;
bool mLeaf;
bool mMarked; // has been added to reduce list
U32 mCount;
ColorF mColSum;
U32 mIndex;
U32 mNumChildren;
PalNode * mChild[8]; // children
PalNode * mNext; // next reducible node
PalNode * mPrev; // previous reducible node
inline PalNode(U32 level, bool leaf);
inline ColorI getColor();
inline void addColor(const ColorI & col);
inline bool testBit(U8 val, U32 index);
inline U32 findChild(const ColorI & col);
};
//------------------------------------------------------------------------------
class PalQuantizer
{
public:
U32 mLeafLevel;
U32 mNumLeafs;
PalNode * mReduceList[MaxDepth+1]; // reducible node list
PalNode * mRoot;
ColorI * mPalette;
U32 mNumColors;
PalQuantizer();
~PalQuantizer();
PalNode * makeNode(U32 level);
void reduceTree();
void makeReducible(PalNode * node);
PalNode * getReducibleNode();
void deleteNode(PalNode * node);
void fillPalette(PalNode * node, U32 * index);
void insertNode(PalNode * node, const ColorI & col);
void buildTree(GBitmap * bitmap, U32 numColors = NumColors);
U32 getColorIndex(const ColorI & col){return(quantizeColor(mRoot, col));}
U32 quantizeColor(PalNode * node, const ColorI & col);
void addToTree(GBitmap * bitmap, U32 numColors);
};
//------------------------------------------------------------------------------
// Class PalNode:
//------------------------------------------------------------------------------
PalNode::PalNode(U32 level, bool leaf) :
mLevel(level),
mLeaf(leaf),
mMarked(false),
mCount(0),
mColSum(0,0,0),
mIndex(0),
mNumChildren(0),
mNext(0),
mPrev(0)
{
for(U32 i = 0; i < 8; i++)
mChild[i] = 0;
}
//------------------------------------------------------------------------------
ColorI PalNode::getColor()
{
ColorI col(0,0,0);
col.red = (U8)(mColSum.red / mCount);
col.green = (U8)(mColSum.green / mCount);
col.blue = (U8)(mColSum.blue / mCount);
return(col);
}
//------------------------------------------------------------------------------
void PalNode::addColor(const ColorI & col)
{
mColSum.red += col.red;
mColSum.green += col.green;
mColSum.blue += col.blue;
mCount++;
}
//------------------------------------------------------------------------------
bool PalNode::testBit(U8 val, U32 index)
{
return((val & (1<<index)) ? true : false);
}
//------------------------------------------------------------------------------
U32 PalNode::findChild(const ColorI & col)
{
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
U32 bit = MaxDepth-mLevel;
U32 index;
__asm {
push eax
push ebx
push ecx
push edx
push esi
//
mov ecx, bit
//
mov ebx, 0
mov edx, 0
mov esi, [col]
// red
mov bl, BYTE PTR [esi]
bt ebx, ecx
setc dl
shl dl, 2
mov eax, edx
// green
mov bl, BYTE PTR [esi+1]
bt ebx, ecx
setc dl
shl dl, 1
or eax, edx
// blue
mov bl, BYTE PTR [esi+2]
bt ebx, ecx
setc dl
or eax, edx
//
mov index, eax
pop esi
pop edx
pop ecx
pop ebx
pop eax
}
#else
U32 index = testBit(col.red, (MaxDepth - mLevel)) << 2 |
testBit(col.green, (MaxDepth - mLevel)) << 1 |
testBit(col.blue, (MaxDepth - mLevel));
#endif
return(index);
}
#endif

797
tools/buildWad/wadProcessor.cc Executable file
View File

@ -0,0 +1,797 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "buildWad/wadProcessor.h"
#include "platform/platformAssert.h"
#include "core/fileStream.h"
#include "dgl/gPalette.h"
#include "math/mPoint.h"
#include "buildWad/palQuantization.h"
#include <stdarg.h>
//------------------------------------------------------------------------------
// globals
//------------------------------------------------------------------------------
namespace {
//
Vector<char *> ErrorMessages;
const char * addErrorMessage(const char * message, ...)
{
static char buffer[1024];
va_list args;
va_start(args, message);
dVsprintf(buffer, sizeof(buffer), message, args);
// copy this string into our vector...
char * errorCopy = new char[dStrlen(buffer) + 1];
dStrcpy(errorCopy, buffer);
ErrorMessages.push_back(errorCopy);
// spam a message now...
dPrintf(errorCopy);
return(errorCopy);
}
bool getBaseName(StringTableEntry src, char * dest, U32 destLen)
{
AssertFatal(src && dest, "getBaseName: invalid params");
const char * end = dStrrchr(src, '.');
AssertISV(end, avar("getBaseName: invalid src file '%s'", src));
const char * start = dStrrchr(src, '/');
if (!start)
start = dStrrchr(src, '\\');
start ? start++ : start = src;
if((end - start) >= destLen)
{
addErrorMessage(" *** BaseFileName too long for %s [max %d].\n", src, destLen);
return(false);
}
U32 count = 0;
while(start != end)
dest[count++] = *start++;
dest[count] = '\0';
return(true);
}
//---------------------------------------------------------------------------
bool getPath(StringTableEntry src, char * dest, U32 destLen)
{
AssertFatal(src && dest, "getPath: invalid params");
const char * end = dStrrchr(src, '/');
if (!end)
end = dStrrchr(src, '\\');
if(!end)
{
AssertFatal(destLen <= 3, "getPath - dest buffer too small.");
dest[0] = '.';
dest[1] = '\0';
}
const char * start = src;
if((end - start) >= destLen)
{
addErrorMessage(" *** getPath: path too long for %s [max %d].\n", src, destLen);
return(false);
}
U32 count = 0;
while(start != end)
dest[count++] = *start++;
dest[count] = '\0';
return(true);
}
//---------------------------------------------------------------------------
GBitmap* loadBitmap(StringTableEntry file)
{
const char *dot = dStrrchr(file, '.');
if (!dot)
return 0;
FileStream loadStream;
loadStream.open(file, FileStream::Read);
if (loadStream.getStatus() != Stream::Ok)
{
addErrorMessage(" *** Unable to load texture: %s.\n", file);
return 0;
}
GBitmap * bitmap = new GBitmap;
if (!dStricmp(dot, ".png"))
{
if(!bitmap->readPNG(loadStream))
{
addErrorMessage(" *** Bad PNG file: %s.\n", file);
delete bitmap;
return 0;
}
}
else
if (!dStricmp(dot, ".jpg"))
{
if(!bitmap->readJPEG(loadStream))
{
addErrorMessage(" *** Bad JPEG file: %s.\n", file);
delete bitmap;
return 0;
}
}
return bitmap;
}
}
//------------------------------------------------------------------------------
// Class WadProcessor
//------------------------------------------------------------------------------
WadProcessor::WadProcessor()
{
// fill in the header info
mHeader.mNumLumps = 0;
mHeader.mInfoTableOffset = 0;
mHeader.mID = U32('3') << 24 | U32('D') << 16 | U32('A') << 8 | U32('W');
}
WadProcessor::~WadProcessor()
{
// destroy the error message list...
for(U32 i = 0; i < ErrorMessages.size(); i++)
delete [] ErrorMessages[i];
ErrorMessages.clear();
}
//------------------------------------------------------------------------------
void WadProcessor::addBitmaps(StringTableEntry source)
{
AssertFatal(source, "WadProcessor::addBitmaps - invalid 'source' param");
AssertISV(dStrlen(source) < (WadMaxFile-1), avar("WadProcess::addBitmaps - 'source' param too long - %s", source));
// check if a response file and add each line of it
if(source[0] == '@')
{
//....
return;
}
// check if file or path...
const char * dot = dStrrchr(source, '.');
if(dot)
{
if(dStricmp(dot, ".png") && dStricmp(dot, ".jpg"))
{
addErrorMessage(" *** Invalid extension for file '%s'.", source);
return;
}
mSrcBitmaps.push_back(StringTable->insert(source));
}
else
{
char path[WadMaxFile+1];
dStrcpy(path, source);
Vector<Platform::FileInfo> srcFiles;
Platform::dumpPath(path, srcFiles);
for(U32 i = 0; i < srcFiles.size(); i++)
{
dot = dStrrchr(srcFiles[i].pFileName, '.');
if(!dot || (dStricmp(dot, ".png") && dStricmp(dot, ".jpg")))
continue;
AssertISV(srcFiles[i].pFileName && srcFiles[i].pFullPath, "WadProcess::addBitmaps - bad filename encountered");
AssertISV((dStrlen(srcFiles[i].pFileName) + dStrlen(srcFiles[i].pFullPath)) < (WadMaxFile-1),
avar("WadProcess::addBitmaps - filename too long %s%s", srcFiles[i].pFullPath, srcFiles[i].pFileName));
// add to list of bitmaps...
mSrcBitmaps.push_back(StringTable->insert(avar("%s/%s", srcFiles[i].pFullPath, srcFiles[i].pFileName)));
}
}
}
//------------------------------------------------------------------------------
void WadProcessor::processBitmaps()
{
for(U32 i = 0; i < mSrcBitmaps.size(); i++)
{
dPrintf(" Adding lump: %s\n", mSrcBitmaps[i]);
Lump * lump = new Lump;
if(!lump->load(mSrcBitmaps[i]))
{
delete lump;
continue;
}
// looks like worldcraft does not use the mip levels - no need to create them...
// lump->process();
lump->write(mFileStream, mLumps);
delete lump;
}
}
//------------------------------------------------------------------------------
void WadProcessor::processSinglePalette()
{
Vector<Lump *> lumps;
PalQuantizer * quantizer = new PalQuantizer;
U32 numColors = 256;
// add the colors
for(U32 i = 0; i < mSrcBitmaps.size(); i++)
{
dPrintf(" Processing lump: %s\n", mSrcBitmaps[i]);
Lump * lump = new Lump;
if(!lump->open(mSrcBitmaps[i]))
{
delete lump;
continue;
}
quantizer->addToTree(lump->mBitmap, numColors);
lumps.push_back(lump);
}
// create the master palette
quantizer->mNumColors = 0;
quantizer->fillPalette(quantizer->mRoot, &quantizer->mNumColors);
// do the palette
for(U32 i = 0; i < lumps.size(); i++)
{
dPrintf(" Adding lump: %s\n", lumps[i]->mMipInfo.mName);
lumps[i]->colorBits(quantizer);
lumps[i]->write(mFileStream, mLumps);
delete lumps[i];
}
delete quantizer;
}
//------------------------------------------------------------------------------
bool WadProcessor::open(StringTableEntry wadFile)
{
mFileStream.close();
AssertFatal(wadFile, "WadProcessor::open - invalid args");
if(!mFileStream.open(wadFile, FileStream::Write))
return(false);
// move ahead of the header - 12bytes!
for(U32 i = 0; i < sizeof(HeaderInfo); i++)
mFileStream.write(U8(0));
AssertFatal(mFileStream.getPosition() == sizeof(HeaderInfo), "WadProcessor::open - failed to offset by header");
return(true);
}
void WadProcessor::close()
{
U32 tablePos = mFileStream.getPosition();
// write out all the lump info's
for(U32 i = 0; i < mLumps.size(); i++)
{
mFileStream.write(mLumps[i].mFilePos);
mFileStream.write(mLumps[i].mDiskSize);
mFileStream.write(mLumps[i].mSize);
mFileStream.write(mLumps[i].mType);
mFileStream.write(mLumps[i].mCompression);
mFileStream.write(mLumps[i].mPad1);
mFileStream.write(mLumps[i].mPad2);
for(U32 j = 0; j < 16; j++)
mFileStream.write(U8(dToupper(mLumps[i].mName[j])));
}
// set back to beginning and write out the header...
mFileStream.setPosition(0);
mFileStream.write(mHeader.mID);
mFileStream.write(mLumps.size());
mFileStream.write(tablePos);
mFileStream.close();
// dump any errors
if(ErrorMessages.size())
{
dPrintf("\n ***************************************************\n");
dPrintf(" *** ERRORS ENCOUNTERED WHILE CREATING WAD FILE! ***\n");
dPrintf(" ***************************************************\n\n");
for(U32 i = 0; i < ErrorMessages.size(); i++)
dPrintf(ErrorMessages[i]);
dPrintf("\n");
}
}
//------------------------------------------------------------------------------
// Class WadProcessor::Lump
//------------------------------------------------------------------------------
WadProcessor::Lump::Lump() :
mPaletteEntries(0),
mPalQuantizer(0),
mBitmap(0)
{
for(U32 i = 0; i < 4; i++)
mBits[i] = 0;
}
WadProcessor::Lump::~Lump()
{
for(U32 i = 0; i < 4; i++)
delete [] mBits[i];
delete mPalQuantizer;
delete mBitmap;
}
//------------------------------------------------------------------------------
bool WadProcessor::Lump::load(StringTableEntry file)
{
AssertFatal(file, "WadProcessor::Lump::load - invalid argument");
char baseName[16];
if(!getBaseName(file, baseName, 16))
return(false);
char * path = new char[WadMaxFile];
if(!getPath(file, path, WadMaxFile))
{
delete path;
return(false);
}
GBitmap * bitmap = loadBitmap(file);
if (!bitmap)
return(false);
// fill in the lump info...
for(U32 i = 0; i < 16; i++)
mMipInfo.mName[i] = 0;
dStrcpy((char*)mMipInfo.mName, baseName);
for(U32 j = 0; j < 16; j++)
mMipInfo.mName[j] = U8(dTolower(mMipInfo.mName[j]));
mMipInfo.mWidth = bitmap->getWidth();
mMipInfo.mHeight = bitmap->getHeight();
// check the width/height of the bitmap
bool wp = false;
bool hp = false;
// 16x16 -> 512x512
for(U32 i = 4; i < 10; i++)
{
if(mMipInfo.mWidth == 1<<i)
wp = true;
if(mMipInfo.mHeight == 1<<i)
hp = true;
}
if(!wp || !hp)
{
addErrorMessage(" *** Improper ditmap dimension: %s [%d/%d].\n",
file, mMipInfo.mWidth, mMipInfo.mHeight);
delete bitmap;
return(false);
}
// copy the bits...
switch(bitmap->getFormat())
{
case GBitmap::Palettized:
{
mBits[0] = new U8[mMipInfo.mWidth * mMipInfo.mHeight];
for(U32 y = 0; y < mMipInfo.mHeight; y++)
for(U32 x = 0; x < mMipInfo.mWidth; x++)
mBits[0][x+mMipInfo.mWidth * y] = *bitmap->getAddress(x,y);
// grab the palette info...
const GPalette * pal = bitmap->getPalette();
mPaletteEntries = 256;
for(U32 i = 0; i < 256; i++)
mPalette[i] = pal->getColor(i);
break;
}
case GBitmap::RGB:
case GBitmap::RGBA:
{
// need to create a palette for this guy...
mPalQuantizer = new PalQuantizer;
mPalQuantizer->buildTree(bitmap);
mBits[0] = new U8[mMipInfo.mWidth * mMipInfo.mHeight];
for(U32 y = 0; y < mMipInfo.mHeight; y++)
for(U32 x = 0; x < mMipInfo.mWidth; x++)
{
ColorI col;
bitmap->getColor(x, y, col);
mBits[0][x+mMipInfo.mWidth * y] = (U8)mPalQuantizer->getColorIndex(col);
}
mPaletteEntries = mPalQuantizer->mNumColors;
AssertFatal(mPaletteEntries <= 256, "WadProcessor::Lump::Load - failed to quantize colors.");
for(U32 i = 0; i < mPaletteEntries; i++)
mPalette[i] = mPalQuantizer->mPalette[i];
break;
}
default:
{
addErrorMessage(" *** Bitmap format not supported [%d] for: %s.\n", bitmap->getFormat(), file);
delete bitmap;
return(false);
break;
}
}
delete bitmap;
return(true);
}
//------------------------------------------------------------------------------
bool WadProcessor::Lump::open(StringTableEntry file)
{
AssertFatal(file, "WadProcessor::Lump::open - invalid argument");
char baseName[16];
if(!getBaseName(file, baseName, 16))
return(false);
char * path = new char[WadMaxFile];
if(!getPath(file, path, WadMaxFile))
{
delete path;
return(false);
}
GBitmap * bitmap = loadBitmap(file);
if (!bitmap)
return(false);
// fill in the lump info...
for(U32 i = 0; i < 16; i++)
mMipInfo.mName[i] = 0;
dStrcpy((char*)mMipInfo.mName, baseName);
for(U32 j = 0; j < 16; j++)
mMipInfo.mName[j] = U8(dTolower(mMipInfo.mName[j]));
mMipInfo.mWidth = bitmap->getWidth();
mMipInfo.mHeight = bitmap->getHeight();
// check the width/height of the bitmap
bool wp = false;
bool hp = false;
// 16x16 -> 512x512
for(U32 i = 4; i < 10; i++)
{
if(mMipInfo.mWidth == 1<<i)
wp = true;
if(mMipInfo.mHeight == 1<<i)
hp = true;
}
if(!wp || !hp)
{
addErrorMessage(" *** Improper ditmap dimension: %s [%d/%d].\n",
file, mMipInfo.mWidth, mMipInfo.mHeight);
delete bitmap;
return(false);
}
switch(bitmap->getFormat())
{
case GBitmap::RGB:
case GBitmap::RGBA:
break;
case GBitmap::Palettized:
addErrorMessage(" *** Palettized format unsupported for global palette in: %s.\n", file);
delete bitmap;
return(false);
break;
default:
addErrorMessage(" *** Bitmap format not supported [%d] for: %s.\n", bitmap->getFormat(), file);
delete bitmap;
return(false);
break;
}
mBitmap = bitmap;
return(true);
}
//------------------------------------------------------------------------------
void WadProcessor::Lump::colorBits(PalQuantizer * quantizer)
{
AssertFatal(quantizer, "WadProcessor::Lump::colorBits - invalid args");
AssertFatal(mBitmap, "WadProcessor::Lump::colorBits - bitmap not loaded");
mBits[0] = new U8[mMipInfo.mWidth * mMipInfo.mHeight];
for(U32 y = 0; y < mMipInfo.mHeight; y++)
for(U32 x = 0; x < mMipInfo.mWidth; x++)
{
ColorI col;
mBitmap->getColor(x, y, col);
mBits[0][x+mMipInfo.mWidth * y] = (U8)quantizer->getColorIndex(col);
}
mPaletteEntries = quantizer->mNumColors;
AssertFatal(mPaletteEntries <= 256, "WadProcessor::Lump::colorBits - failed to quantize colors.");
for(U32 i = 0; i < mPaletteEntries; i++)
mPalette[i] = quantizer->mPalette[i];
}
//------------------------------------------------------------------------------
// * needs to generate the other mip levels for this bitmap(4total)
// * just average pixels for mip's
// * WorldCraft does not appear to use the mip's.. just ignore those...
void WadProcessor::Lump::process()
{
AssertFatal(mBits[0], "WadProcessor::Lump::process: lump not loaded");
for(U32 mipLevel = 1; mipLevel < 4; mipLevel++)
{
U32 size = (mMipInfo.mWidth>>mipLevel) * (mMipInfo.mHeight>>mipLevel);
mBits[mipLevel] = new U8[size];
// walk through and average the pixels for this mip
U32 mipStep = 1<<mipLevel;
for(U32 y = 0; y < mMipInfo.mHeight; y+=mipStep)
for(U32 x = 0; x < mMipInfo.mWidth; x+=mipStep)
{
ColorF colSum(0,0,0);
U8 palIndex = 0;
// average them..
for(U32 yy=0; yy < mipStep; yy++)
for(U32 xx=0; xx < mipStep; xx++)
{
AssertFatal((x+xx+mMipInfo.mWidth*(y+yy)) < (mMipInfo.mWidth * mMipInfo.mHeight), "WadProcess::Lump::process - bad index");
U8 index = mBits[0][x + xx + mMipInfo.mWidth * (y + yy)];
// add the color from this guy...
colSum.red += F64(mPalette[index].red) / 255.f;
colSum.green += F64(mPalette[index].green) / 255.f;
colSum.blue += F64(mPalette[index].blue) / 255.f;
}
colSum /= F32(mipStep * mipStep);
colSum.clamp();
ColorI col;
col.red = U8(colSum.red * 255.f);
col.green = U8(colSum.green * 255.f);
col.blue = U8(colSum.blue * 255.f);
F64 minDist = 1e10;
S32 minIndex = -1;
Point3D src(col.red, col.green, col.blue);
// just walk through and grab the closest one... lame and slow :) Bonus!
for(U32 i =0; i < 256; i++)
{
Point3D dest(mPalette[i].red, mPalette[i].green, mPalette[i].blue);
dest -= src;
F64 dist = dest.len();
if(dist < minDist)
{
minDist = dist;
minIndex = i;
}
}
AssertFatal(minIndex != -1, "WadProcessor::Lump::process: bad palette!");
// set the index...
AssertFatal((x>>mipLevel)+(y>>mipLevel)*(mMipInfo.mWidth>>mipLevel) < size,
"WadProcessor::Lump::Processs - bad mip index");
mBits[mipLevel][(x>>mipLevel)+(y>>mipLevel)*(mMipInfo.mWidth>>mipLevel)] = minIndex;
}
}
}
//------------------------------------------------------------------------------
#if defined(TORQUE_DEBUG)
void WadProcessor::dumpHeader(HeaderInfo & header)
{
dPrintf("------ HeaderInfo\n");
dPrintf("tag: %d\n", header.mID);
dPrintf("numLumps: %d\n", header.mNumLumps);
dPrintf("tableOffset: %d\n", header.mInfoTableOffset);
}
void WadProcessor::dumpLumpInfo(LumpInfo & info)
{
dPrintf("\t------ LumpInfo\n");
dPrintf("\tfilePos: %d\n", info.mFilePos);
dPrintf("\tdiskSize: %d\n", info.mDiskSize);
dPrintf("\tsize: %d\n", info.mSize);
dPrintf("\ttype: %d\n", info.mType);
dPrintf("\tcompression: %d\n", info.mCompression);
dPrintf("\tpad1: %d\n", info.mPad1);
dPrintf("\tpad2: %d\n", info.mPad2);
dPrintf("\tname: %s\n", info.mName);
}
void WadProcessor::dumpMipInfo(MipTexInfo & info)
{
dPrintf("\t------ MipTexInfo\n");
dPrintf("\tname: %s\n", info.mName);
dPrintf("\twidth: %d\n", info.mWidth);
dPrintf("\theight: %d\n", info.mHeight);
dPrintf("\tmOffsets: %d %d %d %d\n", info.mOffsets[0], info.mOffsets[1], info.mOffsets[2], info.mOffsets[3]);
}
void WadProcessor::dumpWad(StringTableEntry name)
{
FileStream file;
file.open(name, FileStream::ReadWrite);
// do the header
HeaderInfo header;
file.read(&header.mID);
file.read(&header.mNumLumps);
file.read(&header.mInfoTableOffset);
dumpHeader(header);
// do the lump's
for(U32 i = 0; i < header.mNumLumps; i++)
{
// lump info
file.setPosition(header.mInfoTableOffset + i * sizeof(LumpInfo));
LumpInfo info;
file.read(&info.mFilePos);
file.read(&info.mDiskSize);
file.read(&info.mDiskSize);
file.read(&info.mSize);
file.read(&info.mType);
file.read(&info.mCompression);
file.read(&info.mPad1);
file.read(&info.mPad2);
for(U32 j = 0; j < 16; j++)
file.read(&info.mName[j]);
dumpLumpInfo(info);
// miptex info
file.setPosition(info.mFilePos);
MipTexInfo mipInfo;
for(U32 j = 0; j < 16; j++)
file.read(&mipInfo.mName[j]);
file.read(&mipInfo.mWidth);
file.read(&mipInfo.mHeight);
for(U32 j = 0; j < 4; j++)
file.read(&mipInfo.mOffsets[j]);
dumpMipInfo(mipInfo);
// palette
U32 pos = info.mFilePos + mipInfo.mOffsets[3];
pos += (mipInfo.mWidth >> 3) * (mipInfo.mHeight >> 3);
file.setPosition(pos);
U16 colors;
file.read(&colors);
dPrintf("\tcolors: %d\n", colors);
}
}
#endif
//------------------------------------------------------------------------------
void WadProcessor::Lump::write(FileStream & stream, Vector<LumpInfo> & lumps)
{
U32 lumpPos = stream.getPosition();
U32 size = sizeof(MipTexInfo);
// fill in the header info
for(U32 i = 0; i < 4; i++)
{
mMipInfo.mOffsets[i] = size;
size += ((mMipInfo.mWidth>>i) * (mMipInfo.mHeight>>i));
}
// write the mip header
for(U32 i = 0; i < 16; i++)
stream.write(mMipInfo.mName[i]);
stream.write(mMipInfo.mWidth);
stream.write(mMipInfo.mHeight);
for(U32 i = 0; i < 4; i++)
stream.write(mMipInfo.mOffsets[i]);
// just write from the first mip level - worldcraft does not use (but requires!)
// the mip levels...
// write the data..
for(U32 i = 0; i < 4; i++)
{
U32 len = ((mMipInfo.mWidth>>i) * (mMipInfo.mHeight>>i));
for(U32 j = 0; j < len; j++)
stream.write(mBits[0][j]);
// stream.write(mBits[i][j]);
}
// write the palette out - dump all possible 256 entries
// stream.write(U16(mPaletteEntries));
stream.write(U16(256));
for(U32 i = 0; i < mPaletteEntries; i++)
{
stream.write(U8(mPalette[i].red));
stream.write(U8(mPalette[i].green));
stream.write(U8(mPalette[i].blue));
}
for(U32 i = mPaletteEntries; i < 256; i++)
{
stream.write(U8(0x00));
stream.write(U8(0x00));
stream.write(U8(0x00));
}
// fill in the lump info...
LumpInfo info;
for(U32 i = 0; i < 16; i++)
info.mName[i] = 0;
// --- adjust the size of the lump ---
// * MipTexInfo size (already in size)
// * number of mip bytes (already in size)
// * dword align padding
// * (short) num of palette entries
// * palette (3*numEntries)
// padding
while(stream.getPosition()&3)
{
stream.write(U8(0));
size++;
}
// num palette entries
size += sizeof(U16);
// palette entries (always 256... stupid)
size += 768;
info.mFilePos = lumpPos;
info.mDiskSize = size;
info.mSize = size;
info.mType = Lumpy + MipTex;
info.mCompression = NoComp;
info.mPad1 = info.mPad2 = 0;
dStrcpy((char*)info.mName, (char*)mMipInfo.mName);
// add the info to the table of lumps
lumps.push_back(info);
}

135
tools/buildWad/wadProcessor.h Executable file
View File

@ -0,0 +1,135 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _INC_WADPROCESSOR
#define _INC_WADPROCESSOR
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
#ifndef _GBITMAP_H_
#include "dgl/gBitmap.h"
#endif
#ifndef _FILESTREAM_H_
#include "core/fileStream.h"
#endif
#define WAD_MAXFILE 1024
//------------------------------------------------------------------------------
class PalQuantizer;
class WadProcessor
{
public:
enum {
WadMaxFile = 1024
};
enum LumpType {
NoType = 0,
Label = 1,
Lumpy = 64, //64 + grab code..
};
enum GrabCode {
Palette = 0,
ColorMap = 1,
Pic = 2,
MipTex = 3, //type that WorldCraft uses and we are interested in
Raw = 4,
ColorMap2 = 5,
};
enum CompType {
NoComp = 0,
LZSS = 1,
};
struct MipTexInfo
{
U8 mName[16]; // must be null terminated
U32 mWidth;
U32 mHeight;
U32 mOffsets[4]; // 4 mip level offsets...
};
struct HeaderInfo
{
U32 mID; // 'WAD3'
U32 mNumLumps;
U32 mInfoTableOffset; // offset to table of lumpInfo's
};
struct LumpInfo
{
U32 mFilePos; // pos in file for this lump
U32 mDiskSize; // size of lump
U32 mSize; // same as mDiskSize
U8 mType; // lump type
U8 mCompression; // compression type
U8 mPad1, mPad2;
U8 mName[16]; // must be null terminated
};
// only concerned with miptex lumps...
class Lump
{
public:
U8 * mBits[4];
U32 mPaletteEntries;
ColorI mPalette[256];
MipTexInfo mMipInfo;
GBitmap * mBitmap;
//
PalQuantizer * mPalQuantizer;
Lump();
~Lump();
bool load(StringTableEntry file);
void process();
void write(FileStream & stream, Vector<LumpInfo> & lumps);
bool open(StringTableEntry file);
void addColors(PalQuantizer * quantizer);
void colorBits(PalQuantizer * quantizer);
};
private:
//
Vector<StringTableEntry> mSrcBitmaps;
Vector<LumpInfo> mLumps;
HeaderInfo mHeader;
FileStream mFileStream;
public:
void addBitmaps(StringTableEntry source);
void processBitmaps();
void processSinglePalette();
// these functions take care of writing the header/table of contents...
bool open(StringTableEntry wadFile);
void close();
#if defined(TORQUE_DEBUG)
void dumpHeader(HeaderInfo & header);
void dumpLumpInfo(LumpInfo & info);
void dumpMipInfo(MipTexInfo & info);
void dumpWad(StringTableEntry name);
#endif
WadProcessor();
~WadProcessor();
};
#endif

BIN
tools/butt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
tools/fonttool.exe Normal file

Binary file not shown.

BIN
tools/fonttool.zip Normal file

Binary file not shown.

140
tools/fonttool/Getopt.cc Executable file
View File

@ -0,0 +1,140 @@
/*
** Slab NG - The Next Generation of Slab
** (c) Copyright 2002-2004 Tom Bampton
** All Rights Reserved.
**
** $Id: Getopt.cpp,v 1.1 2003/10/30 23:55:29 tom Exp $
**
** Filename: Getopt.cpp
** Author: Tom Bampton
** Created: 30/10/2003
** Purpose:
** Command Line Parser
**
*/
/*
* Based on getopt.c from FreeBSD, bearing the following copyright message:
*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "platform/platform.h"
#include "Getopt.h"
#define EMSG ""
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Getopt::Getopt(char *sOptions)
{
m_sOptions = sOptions;
m_sPlace = EMSG;
m_nOptInd = 1;
}
Getopt::~Getopt()
{
}
int Getopt::getopt(int nargc, char **nargv)
{
char *oli; /* option letter list index */
m_nargc = nargc;
m_nargv = nargv;
if (!*m_sPlace) { /* update scanning pointer */
if (m_nOptInd >= nargc || *(m_sPlace = nargv[m_nOptInd]) != '-') {
m_sPlace = EMSG;
return -1;
}
if (m_sPlace[1] && *++m_sPlace == '-') { /* found "--" */
++m_nOptInd;
m_sPlace = EMSG;
return -1;
}
} /* option letter okay? */
if ((m_nOptOpt = (int)*m_sPlace++) == (int)':' ||
!(oli = dStrchr(m_sOptions, m_nOptOpt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (m_nOptOpt == (int)'-')
return (-1);
if (!*m_sPlace)
++m_nOptInd;
if (*m_sOptions != ':' && m_nOptOpt != GO_BAD_CHAR)
return GO_INVALID_CHAR;
return (GO_BAD_CHAR);
}
if (*++oli != ':') { /* don't need argument */
m_sOptArg = NULL;
if (!*m_sPlace)
++m_nOptInd;
}
else { /* need an argument */
if (*m_sPlace) /* no white space */
m_sOptArg = m_sPlace;
else if (nargc <= ++m_nOptInd) { /* no arg */
m_sPlace = EMSG;
//if (*m_sOptions == ':')
return (GO_BAD_ARG);
//return (GO_BAD_CHAR);
}
else /* white space */
m_sOptArg = nargv[m_nOptInd];
m_sPlace = EMSG;
++m_nOptInd;
}
return (m_nOptOpt); /* dump back option letter */
}
void Getopt::Reset(void)
{
if (!*m_sPlace) { /* update scanning pointer */
if (m_nOptInd >= m_nargc || *(m_sPlace = m_nargv[m_nOptInd]) != '-') {
m_sPlace = EMSG;
return;
}
if (m_sPlace[1] && *++m_sPlace == '-') { /* found "--" */
++m_nOptInd;
m_sPlace = EMSG;
return;
}
}
}

115
tools/fonttool/Getopt.h Executable file
View File

@ -0,0 +1,115 @@
/*
** Slab NG - The Next Generation of Slab
** (c) Copyright 2002-2004 Tom Bampton
** All Rights Reserved.
**
** $Id: Getopt.h,v 1.1 2003/10/30 23:55:29 tom Exp $
**
** Filename: Getopt.h
** Author: Tom Bampton
** Created: 30/10/2003
** Purpose:
** Command Line Parser
**
*/
//////////////////////////////////////////////////////////////////////////
/// \file Getopt.h
/// \brief Header for Getopt
//////////////////////////////////////////////////////////////////////////
#ifndef SLAB_GETOPT_H
#define SLAB_GETOPT_H
#define GO_BAD_CHAR '?'
#define GO_BAD_ARG ':'
#define GO_INVALID_CHAR '!'
//////////////////////////////////////////////////////////////////////
/// \brief Command Line Parser
///
/// Getopt provides a command line parser similar to Unix's getopt()
///
/// Note that this version of getopt() will not print any messages to
/// the terminal, you will need to manage this yourself.
///
/// Parts of this section have been lifted from the getopt() manual page.
//////////////////////////////////////////////////////////////////////
class Getopt
{
private:
char *m_sOptions;
char *m_sPlace;
int m_nargc;
char **m_nargv;
public:
/*! \brief Current argv Index
*/
int m_nOptInd;
/*! \brief Current Option
*/
int m_nOptOpt;
/*! \brief Argument to current option if applicable
*/
char *m_sOptArg;
/*! \brief Construct a Getopt
The string tells Getopt what arguments this program takes. It may
contain the following elements: individual characters, and characters
followed by a colon to indicate an option argument is to follow. For
example, an option string "x" recognizes an option ``-x'', and an
option string "x:" recognizes an option and argu- ment ``-x argument''.
It does not matter to Getopt if a following argument has leading
white space.
\param sOptions Option string
\sa getopt(), Reset()
*/
Getopt(char *sOptions);
virtual ~Getopt();
/*! \brief Parse arguments
On return from getopt(), m_sOptArg points to an option argument, if it
is anticipated, and the variable m_nOptInd contains the index to the
next argv argument for a subsequent call to getopt(). The variable
m_nOptOpt saves the last known option character returned by getopt().
The m_nOptInd variable is set to 1, but may be set to another value
before a set of calls to getopt() in order to skip over more or less
argv entries.
In order to use getopt() to evaluate multiple sets of arguments, or to
evaluate a single set of arguments multiple times, call Reset() before
the second and each additional set of calls to getopt()
The getopt() function returns -1 when the argument list is exhausted,
or GO_INVALID_CHAR if a non-recognized option is encountered. You may
use m_nOptOpt to find the invalid character, and display a warning. If
an option takes an argument, but the user did not supply one on the
command line, getopt() returns GO_BAD_ARG. m_nOptOpt will then contain
the option, for printing of warning messages. The interpretation of
options in the argument list may be cancelled by the option `--'
(double dash) which causes getopt() to signal the end of argument
processing and return -1. When all options have been processed (i.e.,
up to the first non-option argument), getopt() returns -1.
\param nargc The argc from your main() function
\param nargv The argv from your main() function
\return -1 on end of processing, the character of the current option
or one of the error values as described above.
\sa Reset(), Getopt(char *sOptions)
*/
int getopt(int nargc, char **nargv);
/*! \brief Reset getopt() for subsequent calls
See the description of getopt() for information on Reset()
\sa getopt()
*/
void Reset(void);
};
#endif // SLAB_GETOPT_H

380
tools/fonttool/fonttool.cc Executable file
View File

@ -0,0 +1,380 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/event.h"
#include "platform/platformAssert.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "math/mathTypes.h"
#include "dgl/gNewFont.h"
#include "core/frameAllocator.h"
#include "core/unicode.h"
#include "core/fileStream.h"
#include "fonttool.h"
#include "Getopt.h"
#include <stdlib.h>
//////////////////////////////////////////////////////////////////////////
FontToolGame GameObject;
// FOR SILLY LINK DEPENDANCY. REMOVE THIS AT YOUR PERIL.
bool gEditingMission = false;
//////////////////////////////////////////////////////////////////////////
static bool initLibraries()
{
// asserts should be created FIRST
PlatformAssert::create();
_StringTable::create();
Con::init();
Math::init();
Platform::init();
FrameAllocator::init(3 * 1024 * 1024);
TextureManager::create();
return(true);
}
static void shutdownLibraries()
{
Platform::shutdown();
Con::shutdown();
_StringTable::destroy();
PlatformAssert::destroy();
}
//////////////////////////////////////////////////////////////////////////
static void usage()
{
dPrintf("Usage: fonttool [options] <font> <size> <cachefile>\n\n");
dPrintf("Where options is one or more of:\n\n");
dPrintf(" -r Populate cache with range. Followup parameters are:\n");
dPrintf(" fonttool [options] <font> <size> <cachefile> <beginChar> <endChar>\n");
dPrintf(" Note that characters are provided in decimal.\n\n");
dPrintf(" -s Populate cache with strings. You provide a file; the\n");
dPrintf(" cache is populated with all the characters that the\n");
dPrintf(" file contains. Followup parameters are:\n");
dPrintf(" fonttool [options] <font> <size> <cachefile> <stringFile>\n\n");
dPrintf(" -i Get info about a cache file. Only parameter is a cache file.\n\n");
dPrintf("fonttool ONLY supports UTF8 encoded files. Extended ASCII characters\n");
dPrintf("and other encodings will not work and may result in strange results!\n\n");
dPrintf("\nMore information can be found in the documentation at:\n %s\n", FONTTOOL_DOC_URL);
}
static void printfConsumer(ConsoleLogEntry::Level level, const char *consoleLine)
{
char *pref = "";
switch(level)
{
case ConsoleLogEntry::Error:
pref="[error] ";
break;
case ConsoleLogEntry::Warning:
pref = "[warn] ";
break;
}
dPrintf("%s%s\n", pref, consoleLine);
}
S32 FontToolGame::main(S32 argc, const char **argv)
{
S32 i, ch;
Getopt opts("rsi:");
if(! initLibraries())
{
dPrintf("Failed to initialize libraries.\n");
return 0;
}
Con::addConsumer(printfConsumer);
FontToolMode mode = FTM_SHOW_USAGE;
const char *paramFile = NULL;
Con::printf("fonttool v1.0.1");
while((ch = opts.getopt(argc, (char **)argv)) != -1)
{
switch(ch)
{
case 'r':
mode = FTM_CACHE_RANGE;
break;
case 's':
mode = FTM_CACHE_STRINGS;
break;
case 'i':
paramFile = opts.m_sOptArg;
mode = FTM_CACHE_INFO;
break;
case GO_BAD_ARG:
dPrintf("option %c requires an argument\n", opts.m_nOptOpt);
break;
case GO_INVALID_CHAR:
dPrintf("%c is an invalid option\n", opts.m_nOptOpt);
break;
case GO_BAD_CHAR:
usage();
shutdownLibraries();
return 0;
}
}
argc -= opts.m_nOptInd;
argv += opts.m_nOptInd;
if(mode == FTM_SHOW_USAGE)
{
usage();
shutdownLibraries();
return 0;
}
if(mode == FTM_CACHE_RANGE || mode == FTM_CACHE_STRINGS)
{
// Get the cache file.
Con::printf("Loading cache file '%s' for processing...", argv[2]);
GFont infoFont;
{
FileStream fs;
if(!fs.open(argv[2], FileStream::Read))
{
Con::errorf(" - Could not open '%s'!", argv[2]);
shutdownLibraries();
return 0;
}
if(!infoFont.read(fs))
{
Con::errorf(" - Could not parse GFT file!!\n");
shutdownLibraries();
return 0;
}
fs.close();
}
// Ok, now we've got a font file. So let's load the appropriate
// platform font and do the cache population.
Con::printf("Creating platform font '%s' %d...", argv[0], dAtoi(argv[1]));
PlatformFont *pf = createPlatformFont(argv[0], dAtoi(argv[1]));
if(!pf)
{
Con::errorf(" - Could not create font %s (%dpt)", argv[0], dAtoi(argv[1]));
shutdownLibraries();
return 0;
}
// Stuff the platform font into the GFont...
infoFont.forcePlatformFont(pf);
// Now do the appropriate caching logic.
if(mode == FTM_CACHE_RANGE)
{
if(argc < 5)
{
Con::errorf("Too few arguments!");
shutdownLibraries();
return 0;
}
U32 rangeStart = dAtoi(argv[3]);
U32 rangeEnd = dAtoi(argv[4]);
if(rangeStart > rangeEnd)
{
Con::errorf("populateFontCacheRange - range start is after end!");
shutdownLibraries();
return 0;
}
Con::printf("Populating cache with characters from %d to %d", rangeStart, rangeEnd);
// This has the side effect of generating character info, including the bitmaps.
for(U32 i=rangeStart; i<=rangeEnd; i++)
{
if(infoFont.isValidChar(i))
infoFont.getCharWidth(i);
else
Con::warnf("populateFontCacheRange - skipping invalid char 0x%x", i);
}
// Ok, all done.
Con::printf("Done populating cache!");
}
else if(mode == FTM_CACHE_STRINGS)
{
if(argc < 4)
{
Con::errorf("Too few arguments!");
shutdownLibraries();
return 0;
}
// Ok, we want to open the file, read every line, and run it
// through the cache.
FileStream fs;
if(!fs.open(argv[3], FileStream::Read))
{
Con::errorf(" - Could not open string file '%s'!", argv[3]);
shutdownLibraries();
return 0;
}
// Peek the first 4 chars so we can check the BOM.
// Bytes Encoding Form
// 00 00 FE FF UTF-32, big-endian
// FF FE 00 00 UTF-32, little-endian
// FE FF UTF-16, big-endian
// FF FE UTF-16, little-endian
// EF BB BF UTF-8
U8 bom[4];
fs.read(&bom[0]);
fs.read(&bom[1]);
fs.read(&bom[2]);
fs.read(&bom[3]);
// And reset the position...
fs.setPosition(0);
// Is it a BOM?
if(bom[0] == 0)
{
// Could be UTF32BE
if(bom[1] == 0 && bom[2] == 0xFE && bom[3] == 0xFF)
Con::warnf("Encountered a UTF32 BE BOM in this file; fonttool does NOT support this file encoding. Use UTF8!");
}
else if(bom[0] == 0xFF)
{
// It's little endian, either UTF16 or UTF 32
if(bom[1] == 0xFE)
{
if(bom[2] == 0 && bom[3] == 0)
Con::warnf("Encountered a UTF32 LE BOM in this file; fonttool does NOT support this file encoding. Use UTF8!");
else
Con::warnf("Encountered a UTF16 LE BOM in this file; fonttool does NOT support this file encoding. Use UTF8!");
}
}
else if(bom[0] == 0xFE && bom[1] == 0xFF)
Con::warnf("Encountered a UTF16 BE BOM in this file; fonttool does NOT support this file encoding. Use UTF8!");
else if(bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF)
Con::printf("Encountered a UTF8 BOM. Fonttool supports this.");
// Ok, read each line and run it through.
const U32 buffSize = 64 * 1024;
UTF8 lineBuff[buffSize];
UTF16 convBuffer[buffSize];
Con::printf("Populating cache with strings from %s", argv[3]);
U32 lineCount = 0;
while(fs.getStatus() != FileStream::EOS)
{
fs.readLine((U8*)lineBuff, buffSize);
convertUTF8toUTF16(lineBuff, convBuffer, buffSize);
infoFont.getStrNWidth(convBuffer, dStrlen(convBuffer));
lineCount++;
}
fs.close();
Con::printf("Done populating cache! Processed %d line(s).", lineCount);
}
else
{
AssertISV(false, "FontToolGame::main - unknown mode!");
}
// Ok, now write it back out.
{
FileStream fs;
if(!fs.open(argv[2], FileStream::Write))
{
Con::errorf(" - Could not open '%s' for writing!", argv[2]);
shutdownLibraries();
return 0;
}
if(!infoFont.write(fs))
{
Con::errorf(" - Could not write GFT file!");
shutdownLibraries();
return 0;
}
fs.close();
}
}
if(mode == FTM_CACHE_INFO)
{
Con::printf("Getting info on cache file '%s'...", paramFile);
// Get a stream to the file.
GFont infoFont;
FileStream fs;
if(!fs.open(paramFile, FileStream::Read))
{
Con::errorf(" - Could not open file!\n");
shutdownLibraries();
return 0;
}
if(!infoFont.read(fs))
{
Con::errorf(" - Could not load GFT!\n");
shutdownLibraries();
return 0;
}
fs.close();
infoFont.dumpInfo();
// All done.
}
shutdownLibraries();
return 0;
}
//////////////////////////////////////////////////////////////////////////
void GameReactivate()
{
}
void GameDeactivate( bool )
{
}

27
tools/fonttool/fonttool.h Executable file
View File

@ -0,0 +1,27 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _FONTTOOL_H_
#define _FONTTOOL_H_
#include "platform/gameInterface.h"
#define FONTTOOL_DOC_URL "http://tdn.garagegames.com/wiki/TorqueUnicode#Caching.2C_.gft_.2F_.uft"
class FontToolGame : public GameInterface
{
public:
enum FontToolMode
{
FTM_SHOW_USAGE,
FTM_CACHE_RANGE,
FTM_CACHE_STRINGS,
FTM_CACHE_INFO
};
S32 main(S32 argc, const char **argv);
};
#endif // _FONTTOOL_H_

BIN
tools/langc.exe Normal file

Binary file not shown.

140
tools/langc/Getopt.cc Executable file
View File

@ -0,0 +1,140 @@
/*
** Slab NG - The Next Generation of Slab
** (c) Copyright 2002-2004 Tom Bampton
** All Rights Reserved.
**
** $Id: Getopt.cpp,v 1.1 2003/10/30 23:55:29 tom Exp $
**
** Filename: Getopt.cpp
** Author: Tom Bampton
** Created: 30/10/2003
** Purpose:
** Command Line Parser
**
*/
/*
* Based on getopt.c from FreeBSD, bearing the following copyright message:
*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "platform/platform.h"
#include "Getopt.h"
#define EMSG ""
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Getopt::Getopt(char *sOptions)
{
m_sOptions = sOptions;
m_sPlace = EMSG;
m_nOptInd = 1;
}
Getopt::~Getopt()
{
}
int Getopt::getopt(int nargc, char **nargv)
{
char *oli; /* option letter list index */
m_nargc = nargc;
m_nargv = nargv;
if (!*m_sPlace) { /* update scanning pointer */
if (m_nOptInd >= nargc || *(m_sPlace = nargv[m_nOptInd]) != '-') {
m_sPlace = EMSG;
return -1;
}
if (m_sPlace[1] && *++m_sPlace == '-') { /* found "--" */
++m_nOptInd;
m_sPlace = EMSG;
return -1;
}
} /* option letter okay? */
if ((m_nOptOpt = (int)*m_sPlace++) == (int)':' ||
!(oli = dStrchr(m_sOptions, m_nOptOpt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (m_nOptOpt == (int)'-')
return (-1);
if (!*m_sPlace)
++m_nOptInd;
if (*m_sOptions != ':' && m_nOptOpt != GO_BAD_CHAR)
return GO_INVALID_CHAR;
return (GO_BAD_CHAR);
}
if (*++oli != ':') { /* don't need argument */
m_sOptArg = NULL;
if (!*m_sPlace)
++m_nOptInd;
}
else { /* need an argument */
if (*m_sPlace) /* no white space */
m_sOptArg = m_sPlace;
else if (nargc <= ++m_nOptInd) { /* no arg */
m_sPlace = EMSG;
//if (*m_sOptions == ':')
return (GO_BAD_ARG);
//return (GO_BAD_CHAR);
}
else /* white space */
m_sOptArg = nargv[m_nOptInd];
m_sPlace = EMSG;
++m_nOptInd;
}
return (m_nOptOpt); /* dump back option letter */
}
void Getopt::Reset(void)
{
if (!*m_sPlace) { /* update scanning pointer */
if (m_nOptInd >= m_nargc || *(m_sPlace = m_nargv[m_nOptInd]) != '-') {
m_sPlace = EMSG;
return;
}
if (m_sPlace[1] && *++m_sPlace == '-') { /* found "--" */
++m_nOptInd;
m_sPlace = EMSG;
return;
}
}
}

115
tools/langc/Getopt.h Executable file
View File

@ -0,0 +1,115 @@
/*
** Slab NG - The Next Generation of Slab
** (c) Copyright 2002-2004 Tom Bampton
** All Rights Reserved.
**
** $Id: Getopt.h,v 1.1 2003/10/30 23:55:29 tom Exp $
**
** Filename: Getopt.h
** Author: Tom Bampton
** Created: 30/10/2003
** Purpose:
** Command Line Parser
**
*/
//////////////////////////////////////////////////////////////////////////
/// \file Getopt.h
/// \brief Header for Getopt
//////////////////////////////////////////////////////////////////////////
#ifndef SLAB_GETOPT_H
#define SLAB_GETOPT_H
#define GO_BAD_CHAR '?'
#define GO_BAD_ARG ':'
#define GO_INVALID_CHAR '!'
//////////////////////////////////////////////////////////////////////
/// \brief Command Line Parser
///
/// Getopt provides a command line parser similar to Unix's getopt()
///
/// Note that this version of getopt() will not print any messages to
/// the terminal, you will need to manage this yourself.
///
/// Parts of this section have been lifted from the getopt() manual page.
//////////////////////////////////////////////////////////////////////
class Getopt
{
private:
char *m_sOptions;
char *m_sPlace;
int m_nargc;
char **m_nargv;
public:
/*! \brief Current argv Index
*/
int m_nOptInd;
/*! \brief Current Option
*/
int m_nOptOpt;
/*! \brief Argument to current option if applicable
*/
char *m_sOptArg;
/*! \brief Construct a Getopt
The string tells Getopt what arguments this program takes. It may
contain the following elements: individual characters, and characters
followed by a colon to indicate an option argument is to follow. For
example, an option string "x" recognizes an option ``-x'', and an
option string "x:" recognizes an option and argu- ment ``-x argument''.
It does not matter to Getopt if a following argument has leading
white space.
\param sOptions Option string
\sa getopt(), Reset()
*/
Getopt(char *sOptions);
virtual ~Getopt();
/*! \brief Parse arguments
On return from getopt(), m_sOptArg points to an option argument, if it
is anticipated, and the variable m_nOptInd contains the index to the
next argv argument for a subsequent call to getopt(). The variable
m_nOptOpt saves the last known option character returned by getopt().
The m_nOptInd variable is set to 1, but may be set to another value
before a set of calls to getopt() in order to skip over more or less
argv entries.
In order to use getopt() to evaluate multiple sets of arguments, or to
evaluate a single set of arguments multiple times, call Reset() before
the second and each additional set of calls to getopt()
The getopt() function returns -1 when the argument list is exhausted,
or GO_INVALID_CHAR if a non-recognized option is encountered. You may
use m_nOptOpt to find the invalid character, and display a warning. If
an option takes an argument, but the user did not supply one on the
command line, getopt() returns GO_BAD_ARG. m_nOptOpt will then contain
the option, for printing of warning messages. The interpretation of
options in the argument list may be cancelled by the option `--'
(double dash) which causes getopt() to signal the end of argument
processing and return -1. When all options have been processed (i.e.,
up to the first non-option argument), getopt() returns -1.
\param nargc The argc from your main() function
\param nargv The argv from your main() function
\return -1 on end of processing, the character of the current option
or one of the error values as described above.
\sa Reset(), Getopt(char *sOptions)
*/
int getopt(int nargc, char **nargv);
/*! \brief Reset getopt() for subsequent calls
See the description of getopt() for information on Reset()
\sa getopt()
*/
void Reset(void);
};
#endif // SLAB_GETOPT_H

194
tools/langc/langc.cc Executable file
View File

@ -0,0 +1,194 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/event.h"
#include "platform/platformAssert.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "math/mathTypes.h"
#include "langc/langc.h"
#include "core/fileStream.h"
#include "i18n/lang.h"
#include "langcomp.h"
#include "Getopt.h"
#include <stdlib.h>
//////////////////////////////////////////////////////////////////////////
LangCGame GameObject;
// FOR SILLY LINK DEPENDANCY. REMOVE THIS AT YOUR PERIL.
bool gEditingMission = false;
//////////////////////////////////////////////////////////////////////////
static bool initLibraries()
{
// asserts should be created FIRST
PlatformAssert::create();
_StringTable::create();
// ResManager::create();
Con::init();
Math::init();
Platform::init(); // platform specific initialization
return(true);
}
static void shutdownLibraries()
{
// Purge any resources on the timeout list...
// if (ResourceManager)
// ResourceManager->purge();
// shut down
Platform::shutdown();
Con::shutdown();
// ResManager::destroy();
_StringTable::destroy();
// asserts should be destroyed LAST
PlatformAssert::destroy();
}
//////////////////////////////////////////////////////////////////////////
static void usage(void)
{
dPrintf("Usage: langc [options] <filename> <outbasename>\n\n");
dPrintf("Where options is one or more of:\n\n");
dPrintf(" -l Write Language File -h Write C++ Header\n");
dPrintf(" -s Write Script -d Write C++ Defaults\n");
dPrintf(" -t Compile a translation -r Write translation file\n");
dPrintf(" -e <filename> Specify english file when compiling translations\n");
dPrintf("\n");
dPrintf(" -S Don't strip leading spaces -T Strip trailing spaces\n");
dPrintf(" -I Don't warn for invalid chars -W Don't warn for empty identifiers\n");
dPrintf(" -q Quiet mode, no warnings at all\n");
dPrintf("\nMore information can be found in the documentation at:\n %s\n", I18N_DOC_URL);
}
S32 LangCGame::main(S32 argc, const char **argv)
{
S32 i, ch;
U32 flags = LCO_WARNNOSTRING;
Getopt opts("STIWqhsdlrte:");
char *englishFile = NULL;
if(! initLibraries())
return 0;
while((ch = opts.getopt(argc, (char **)argv)) != -1)
{
switch(ch)
{
case 't':
// Compile a translation
flags |= LCO_COMPILETRANSLATION;
break;
case 'e':
// Specify english file
englishFile = opts.m_sOptArg;
break;
case 'S':
// Don't strip spaces
flags |= LCO_DONTSTRIPSPACES;
break;
case 'T':
// Strip trailing space
flags |= LCO_STRIPTRAILINGSPACE;
break;
case 'I':
// Don't warn for invalid chars
flags |= LCO_DONTWARNINVALIDCHAR;
break;
case 'W':
// Don't warn for empty identifiers
flags &= ~LCO_WARNNOSTRING;
break;
case 'q':
// Quiet mode, no warnings at all
flags |= LCO_NOWARNINGS;
break;
case 'h':
// Write Header
flags |= LCO_WRITEHEADER;
break;
case 's':
// Write Script
flags |= LCO_WRITESCRIPT;
break;
case 'd':
// Write C++ Defaults
flags |= LCO_WRITECDEFAULTS;
break;
case 'l':
// Write Lang Table
flags |= LCO_WRITELANGTABLE;
break;
case 'r':
// Write translation
flags |= LCO_WRITETRANSLATION;
break;
case GO_BAD_ARG:
dPrintf("option %c requires an argument\n", opts.m_nOptOpt);
break;
case GO_INVALID_CHAR:
dPrintf("%c is an invalid option\n", opts.m_nOptOpt);
break;
case GO_BAD_CHAR:
usage();
shutdownLibraries();
return 0;
}
}
argc -= opts.m_nOptInd;
argv += opts.m_nOptInd;
if(argc < 2)
{
usage();
return 0;
}
LangComp c(flags);
dPrintf("Compiling ... \n");
c.Compile(argv[0], argv[1], englishFile);
shutdownLibraries();
return 0;
}
//////////////////////////////////////////////////////////////////////////
void GameReactivate()
{
}
void GameDeactivate( bool )
{
}

19
tools/langc/langc.h Executable file
View File

@ -0,0 +1,19 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _LANGC_H_
#define _LANGC_H_
#include "platform/gameInterface.h"
#define I18N_DOC_URL "http://tdn.garagegames.com/wiki/TorqueLocalization"
class LangCGame : public GameInterface
{
public:
S32 main(S32 argc, const char **argv);
};
#endif // _LANGC_H_

702
tools/langc/langcomp.cc Executable file
View File

@ -0,0 +1,702 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/event.h"
#include "platform/platformAssert.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "math/mathTypes.h"
#include "langc/langc.h"
#include "core/fileStream.h"
#include "i18n/lang.h"
#include <stdlib.h>
#include "langcomp.h"
//////////////////////////////////////////////////////////////////////////
// Simple hash function for ID lookups in translations
//////////////////////////////////////////////////////////////////////////
static U32 hashString(const UTF8 *str)
{
S32 i;
U32 h = 0;
for(i = 0;str[i];i++)
{
h += (str[i] * i);
}
return h;
}
//////////////////////////////////////////////////////////////////////////
// LFileWriter Class
//////////////////////////////////////////////////////////////////////////
LFileWriter::LFileWriter(LangFile *langFile) : mLangFile(langFile)
{
}
bool LFileWriter::Open(const UTF8 *basename)
{
UTF8 filename[256];
GetFilename(basename, filename, sizeof(filename));
return mStream.open((const char*)filename, FileStream::Write);
}
void LFileWriter::Close()
{
mStream.close();
}
//////////////////////////////////////////////////////////////////////////
// Writer Classes
//////////////////////////////////////////////////////////////////////////
class LHeaderWriter : public LFileWriter
{
public:
LHeaderWriter(LangFile *l) : LFileWriter(l)
{
}
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize)
{
dSprintf(buf, bufsize, "%s.h", basename);
}
virtual void WriteHeader()
{
mStream.writeLine((U8 *)"// Automatically generated. DO NOT EDIT\n");
mStream.writeLine((U8 *)"#ifndef _TGE_I18N_AUTOGEN_H_");
mStream.writeLine((U8 *)"#define _TGE_I18N_AUTOGEN_H_\n");
}
virtual void WriteFooter()
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "\n#define I18N_NUM_STRINGS\t%d", mLangFile->getNumStrings());
mStream.writeLine((U8 *)&buf);
mStream.writeLine((U8 *)"\n#endif // _TGE_I18N_AUTOGEN_H_");
}
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "// %s", str);
mStream.writeLine((U8 *)&buf);
dSprintf(buf, sizeof(buf), "#define %s\t%d", idstr, idnum);
mStream.writeLine((U8 *)&buf);
}
};
class LScriptWriter : public LFileWriter
{
public:
LScriptWriter(LangFile *l) : LFileWriter(l)
{
}
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize)
{
dSprintf(buf, bufsize, "%s.cs", basename);
}
virtual void WriteHeader()
{
mStream.writeLine((U8 *)"// Automatically generated. DO NOT EDIT\n");
}
virtual void WriteFooter()
{
}
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "// %s", str);
mStream.writeLine((U8 *)&buf);
dSprintf(buf, sizeof(buf), "$%s = %d;", idstr, idnum);
mStream.writeLine((U8 *)&buf);
}
};
class LDefaultsWriter : public LFileWriter
{
public:
LDefaultsWriter(LangFile *l) : LFileWriter(l)
{
}
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize)
{
dSprintf(buf, bufsize, "%sDefaults.cc", basename);
}
virtual void WriteHeader()
{
mStream.writeLine((U8 *)"// Automatically generated. DO NOT EDIT\n");
mStream.writeLine((U8 *)"const UTF8 *gI18NDefaultStrings[] =\n{");
}
virtual void WriteFooter()
{
mStream.writeLine((U8 *)"};");
}
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "\t// %s = %d", idstr, idnum);
mStream.writeLine((U8 *)&buf);
dSprintf(buf, sizeof(buf), "\t(const UTF8*)\"%s\",", str);
mStream.writeLine((U8 *)&buf);
}
};
class LTransWriter : public LFileWriter
{
public:
LTransWriter(LangFile *l) : LFileWriter(l)
{
}
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize)
{
dSprintf(buf, bufsize, "%s.tran", basename);
}
virtual void WriteHeader()
{
mStream.writeLine((U8 *)"# Automatically generated.\n");
}
virtual void WriteFooter()
{
}
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "# [%s:%d] %s", idstr, idnum, str);
mStream.writeLine((U8 *)&buf);
dSprintf(buf, sizeof(buf), "%s=\n", idstr);
mStream.writeLine((U8 *)&buf);
}
};
//////////////////////////////////////////////////////////////////////////
// LString Class
//////////////////////////////////////////////////////////////////////////
LString::LString(UTF8 *i /* = NULL */, UTF8 *s /* = NULL */, UTF8 *sclean /* = NULL */) : str(NULL), strclean(NULL), id(NULL)
{
if(s)
{
str = new UTF8 [dStrlen(s) + 1];
dStrcpy(str, s);
}
if(sclean)
{
strclean = new UTF8 [dStrlen(sclean) + 1];
dStrcpy(strclean, sclean);
}
if(i)
{
id = new UTF8 [dStrlen(i) + 1];
dStrcpy(id, i);
}
}
LString::~LString()
{
if(str)
delete [] str;
if(id)
delete [] id;
}
//////////////////////////////////////////////////////////////////////////
// LangComp Class
//////////////////////////////////////////////////////////////////////////
LangComp::LangComp(U32 options /* = 0L */) : mOptions(options), mLangFile(NULL),
mCurrentFilename(NULL), mCurrentLine(0), mNumErrors(0), mNumWarn(0),
mTransLookup(LC_ID_LOOKUP_SIZE)
{
}
LangComp::~LangComp()
{
Free();
}
//////////////////////////////////////////////////////////////////////////
// Protected Methods
//////////////////////////////////////////////////////////////////////////
void LangComp::Free()
{
if(mLangFile)
{
delete mLangFile;
mLangFile = NULL;
}
if(mCurrentFilename)
{
delete [] mCurrentFilename;
mCurrentFilename = NULL;
}
mCurrentLine = 0;
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
delete mFileWriters[i];
}
mFileWriters.empty();
}
void LangComp::Error(const UTF8 *msg, ...)
{
UTF8 buf[512];
va_list va;
va_start(va, msg);
dVsprintf(buf, sizeof(buf), msg, va);
va_end(va);
if(mCurrentFilename)
dPrintf("error at %s:%d: %s\n", mCurrentFilename, mCurrentLine, buf);
else
dPrintf("error: %s\n", buf);
mNumErrors++;
}
void LangComp::Warn(const UTF8 *msg, ...)
{
UTF8 buf[512];
va_list va;
if(mOptions & LCO_NOWARNINGS)
return;
va_start(va, msg);
dVsprintf(buf, sizeof(buf), msg, va);
va_end(va);
if(mCurrentFilename)
dPrintf("warning at %s:%d: %s\n", mCurrentFilename, mCurrentLine, buf);
else
dPrintf("warning: %s\n", buf);
mNumWarn++;
}
LString * LangComp::ParseLine(UTF8 *line)
{
UTF8 *p, id[LCL_MAXIDLENGTH], str[LCL_MAXSTRINGLENGTH];
S32 i, warnCount;
i = dStrlen(line)-1;
if(line[i] == '\n') line[i] = 0;
i = dStrlen(line)-1;
if(line[i] == '\r') line[i] = 0;
p = line;
// Allowable comment delimiters: # ; //
if(*p == '#' || *p == ';' || (*p == '/' && *(p+1) == '/') || *p == 0)
return new LString();
i = 0;
while(*p)
{
if(*p == '=')
break;
if(i < (sizeof(id)-1))
{
if(dIsalnum(*p) || *p == '_')
{
id[i] = *p;
i++;
}
else if(! dIsspace(*p) && ! (mOptions & LCO_DONTWARNINVALIDCHAR))
Warn("invalid character ('%c') in identifier ignored", *p);
}
p++;
}
id[i] = 0;
p++;
// Identifiers cannot start with a number
if(dIsdigit(id[0]))
{
Error("identifiers cannot start with a number");
return NULL;
}
// Identifiers must be there
if(id[0] == 0)
{
Error("no identifier");
return NULL;
}
i = 0;
bool foundStart = (mOptions & LCO_DONTSTRIPSPACES);
while(*p)
{
if(i < (sizeof(str)-1))
{
if(!foundStart && ! dIsspace(*p))
foundStart = true;
if(foundStart && i < (sizeof(str)-1))
{
str[i] = *p;
i++;
}
}
p++;
}
str[i] = 0;
if(mOptions & LCO_STRIPTRAILINGSPACE)
{
p = dStrchr(str, 0);
while(dIsspace(*(--p)))
*p = 0;
}
if(mOptions & LCO_WARNNOSTRING && str[0] == 0)
{
Warn("identifier '%s' is empty", id);
}
UTF8 strbuf[LCL_MAXSTRINGLENGTH];
processSlashes(str, strbuf);
return new LString(id, strbuf, str);
}
void LangComp::WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
mFileWriters[i]->WriteString(idstr, idnum, str);
}
}
void LangComp::WriteFileHeader()
{
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
mFileWriters[i]->WriteHeader();
}
}
void LangComp::WriteFileFooter()
{
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
mFileWriters[i]->WriteFooter();
}
}
// [tom, 3/7/2005] There is no buffer size limit here as the size of the buffer
// in ParseLine() is the same size as the buffer the string is in. If this function
// is ripped for use elsewhere, I suggest adding a buffer size check.
UTF8 *LangComp::processSlashes(const UTF8 *string, UTF8 *buffer)
{
const UTF8 *s = string;
UTF8 *d = buffer;
while(*s)
{
if(*s == '\\')
{
s++;
switch(*s)
{
case 'n':
*d++ = '\n';
s++;
break;
case 'r':
*d++ = '\r';
s++;
break;
case 't':
*d++ = '\t';
s++;
break;
default:
*d++ = *s++;
break;
}
}
else
{
*d++ = *s++;
}
}
*d = 0;
return buffer;
}
// [tom, 3/17/2005] No buffer check here either, this time out of lazyness.
// [tom, 3/17/2005] This function isnt actually used anymore. I'm keeping it
// here in case its useful in the future.
UTF8 *LangComp::quoteString(const UTF8 *string, UTF8 *buffer)
{
static struct {
unsigned char c;
unsigned char q;
} quoteTab[]=
{
{ '\n', 'n' },
{ '\r', 'r' },
{ '\t', 't' },
{ '"', '"' },
{ '\'', '\'' },
{ '\\', '\\' },
{ 0, 0 }
};
const UTF8 *s = string;
UTF8 *d = buffer;
while(*s)
{
int i;
bool rep = false;
for(i = 0;quoteTab[i].c;i++)
{
if(*s == quoteTab[i].c)
{
*d++ = '\\';
*d++ = quoteTab[i].q;
rep = true;
break;
}
}
if(!rep)
*d++ = *s;
s++;
}
*d = 0;
return buffer;
}
bool LangComp::AddFileWriter(LFileWriter *lfw, const UTF8 *basename)
{
if(lfw->Open(basename))
{
mFileWriters.push_back(lfw);
return true;
}
UTF8 buf[256];
lfw->GetFilename(basename, buf, sizeof(buf));
Warn("Could not open file \"%s\"", buf);
delete lfw;
return false;
}
//////////////////////////////////////////////////////////////////////////
// Public Methods
//////////////////////////////////////////////////////////////////////////
bool LangComp::Compile(const UTF8 *filename, const UTF8 *outbasename, const UTF8 *englishTable /* = NULL */)
{
bool ret = true;
UTF8 lsoname[256];
Free();
if((mOptions & LCO_COMPILETRANSLATION) && englishTable == NULL)
{
Error("you must specify the english language file when compiling translations.");
return false;
}
if(mOptions & LCO_COMPILETRANSLATION)
{
if(! LoadLangForTranslation(englishTable))
{
Error("could not load %s", englishTable);
return false;
}
}
dSprintf(lsoname, sizeof(lsoname), "%s.lso", outbasename);
mCurrentFilename = new UTF8 [dStrlen(filename) + 1];
dStrcpy(mCurrentFilename, filename);
mLangFile = new LangFile;
FileStream fs;
if(fs.open(filename, FileStream::Read))
{
if(mOptions & LCO_WRITEHEADER)
AddFileWriter(new LHeaderWriter(mLangFile), outbasename);
if(mOptions & LCO_WRITESCRIPT)
AddFileWriter(new LScriptWriter(mLangFile), outbasename);
if(mOptions & LCO_WRITECDEFAULTS)
AddFileWriter(new LDefaultsWriter(mLangFile), outbasename);
if(mOptions & LCO_WRITETRANSLATION)
AddFileWriter(new LTransWriter(mLangFile), outbasename);
WriteFileHeader();
// This buffer must be able to hold the max lengths, the equals, end of line chars and a bit of leeway for spaces
UTF8 buf[LCL_MAXIDLENGTH + LCL_MAXSTRINGLENGTH + 28];
while(fs.getStatus() != Stream::EOS)
{
mCurrentLine++;
fs.readLine((U8 *)&buf[0], sizeof(buf));
LString *ls;
if((ls = ParseLine(buf)) == NULL)
break;
if(ls->id)
{
U32 idnum;
if(mOptions & LCO_COMPILETRANSLATION)
{
idnum = (U32)mTransLookup.retreive(hashString(ls->id));
if(idnum == 0)
Warn("id %s does not exist in the english table", ls->id);
else
idnum--;
mLangFile->setString(idnum, ls->str);
}
else
idnum = mLangFile->addString(ls->str);
WriteString(ls->id, idnum, ls->strclean);
}
delete ls;
}
WriteFileFooter();
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
mFileWriters[i]->Close();
}
fs.close();
}
if(mOptions & LCO_WRITELANGTABLE)
{
if(ret && fs.open(lsoname, FileStream::Write))
{
mLangFile->save(&fs);
fs.close();
}
}
dPrintf("%s - ", lsoname);
if(ret)
dPrintf("%d string(s), ", mLangFile->getNumStrings());
dPrintf("%d error(s), %d warning(s)\n", mNumErrors, mNumWarn);
return ret;
}
bool LangComp::LoadLangForTranslation(const UTF8 *filename)
{
bool ret = true;
FileStream fs;
UTF8 *fnBak = mCurrentFilename;
U32 lineBak = mCurrentLine;
mCurrentFilename = new UTF8 [dStrlen(filename) + 1];
dStrcpy(mCurrentFilename, filename);
mCurrentLine = 0;
if(fs.open(filename, FileStream::Read))
{
// This buffer must be able to hold the max lengths, the equals, end of line chars and a bit of leeway for spaces
UTF8 buf[LCL_MAXIDLENGTH + LCL_MAXSTRINGLENGTH + 28];
U32 lastID = 1; // [tom, 4/25/2005] 0 is used as an error indicator
while(fs.getStatus() != Stream::EOS)
{
mCurrentLine++;
fs.readLine((U8 *)&buf[0], sizeof(buf));
LString *ls;
if((ls = ParseLine(buf)) == NULL)
break;
if(ls->id)
{
U32 h = hashString(ls->id);
mTransLookup.insert((U32 *)lastID, h);
lastID++;
}
delete ls;
}
fs.close();
}
delete [] mCurrentFilename;
mCurrentFilename = fnBak;
mCurrentLine = lineBak;
return ret;
}

115
tools/langc/langcomp.h Executable file
View File

@ -0,0 +1,115 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "core/tVector.h"
#include "tSimpleHash.h"
#ifndef _LANGCOMP_H_
#define _LANGCOMP_H_
// Limits
#define LCL_MAXIDLENGTH 128 // Maximum ID length
#define LCL_MAXSTRINGLENGTH 512 // Maximum string length
class LFileWriter
{
protected:
FileStream mStream;
LangFile *mLangFile;
public:
LFileWriter(LangFile *langFile);
virtual bool Open(const UTF8 *basename);
virtual void Close();
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize) = 0;
virtual void WriteHeader() = 0;
virtual void WriteFooter() = 0;
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str) = 0;
};
struct LString
{
UTF8 *str;
UTF8 *strclean;
UTF8 *id;
LString(UTF8 *i = NULL, UTF8 *s = NULL, UTF8 *sclean = NULL);
virtual ~LString();
};
// [tom, 4/25/2005] This is the size of the SimpleHash table. Its created in
// the SimpleHash ctor so there is a trade off here between performance and
// memory usage, as the array will always be created. In practice, the memory
// usage probably isnt really going to be that horrendous for an empty table.
// It works out to 12 bytes multiplied by whatever you set LC_ID_LOOKUP_SIZE to.
//
// Since SimpleHash doesnt rehash, the optimal size depends entirely on the
// amount of strings in the table. starter.fps typically has a few hundred,
// whilst the C++ code typically has a few thousand. Compiling translations is
// still going to be pretty fast regardless of how bad the performance of
// the SparseArry is, so on the whole this comment probably doesnt really matter.
#define LC_ID_LOOKUP_SIZE 128
class LangComp
{
protected:
U32 mOptions;
LangFile *mLangFile;
UTF8 *mCurrentFilename;
U32 mCurrentLine;
U32 mNumErrors;
U32 mNumWarn;
Vector<LFileWriter *> mFileWriters;
SimpleHash<U32> mTransLookup;
void Free(void);
void Error(const UTF8 *msg, ...);
void Warn(const UTF8 *msg, ...);
LString * ParseLine(UTF8 *line);
void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str);
void WriteFileHeader();
void WriteFileFooter();
UTF8 *processSlashes(const UTF8 *string, UTF8 *buffer);
UTF8 *quoteString(const UTF8 *string, UTF8 *buffer);
bool AddFileWriter(LFileWriter *lfw, const UTF8 *basename);
public:
LangComp(U32 options = 0L);
virtual ~LangComp();
bool Compile(const UTF8 *filename, const UTF8 *outbasename, const UTF8 *englishTable = NULL);
bool LoadLangForTranslation(const UTF8 *filename);
};
// Option Flags
#define LCO_DONTSTRIPSPACES (1L) // Don't strip leading spaces in strings
#define LCO_STRIPTRAILINGSPACE (1L << 1) // Strip trailing spaces in strings
#define LCO_DONTWARNINVALIDCHAR (1L << 2) // Don't warn for invalid characters
#define LCO_WARNNOSTRING (1L << 3) // Warn for empty identifiers
#define LCO_NOWARNINGS (1L << 4) // Don't warn at all
#define LCO_WRITEHEADER (1L << 5) // Write C++ Header file
#define LCO_WRITESCRIPT (1L << 6) // Write Script IDs file
#define LCO_WRITECDEFAULTS (1L << 7) // Write C++ defaults file
#define LCO_WRITECSDEFAULTS (1L << 8) // Write Script defaults file
#define LCO_WRITELANGTABLE (1L << 9) // Write Lang Table
#define LCO_WRITETRANSLATION (1L << 10) // Write an empty language file for translation
#define LCO_COMPILETRANSLATION (1L << 11) // Compile a translation file
#endif // _LANGCOMP_H_

140
tools/langc/tSimpleHash.h Executable file
View File

@ -0,0 +1,140 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// [tom, 4/26/2005] This was based on SparseArray ...
//
// SparseArray deletes the objects contained within it, and so doesnt really
// work for our purposes. Although SparseArray is not used anywhere in the
// engine, I figured it would be best to copy it and hack the copy rather
// then risk anyone's code breaking for no real reason.
#ifndef _TSIMPLEHASH_H_
#define _TSIMPLEHASH_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _PLATFORMASSERT_H_
#include "platform/platformAssert.h"
#endif
template <class T>
class SimpleHash
{
protected:
struct Node {
T* pObject;
U32 key;
Node* next;
};
protected:
U32 mModulus;
Node* mSentryTables;
void clearTables(); // Note: _deletes_ the objects!
public:
SimpleHash(const U32 modulusSize = 64);
~SimpleHash();
void insert(T* pObject, U32 key);
T* remove(U32 key);
T* retreive(U32 key);
};
template <class T>
inline SimpleHash<T>::SimpleHash(const U32 modulusSize)
{
AssertFatal(modulusSize > 0, "Error, modulus must be > 0");
mModulus = modulusSize;
mSentryTables = new Node[mModulus];
for (U32 i = 0; i < mModulus; i++)
mSentryTables[i].next = NULL;
}
template <class T>
inline SimpleHash<T>::~SimpleHash()
{
clearTables();
}
template <class T>
inline void SimpleHash<T>::clearTables()
{
for (U32 i = 0; i < mModulus; i++) {
Node* pProbe = mSentryTables[i].next;
while (pProbe != NULL) {
Node* pNext = pProbe->next;
//delete pProbe->pObject;
delete pProbe;
pProbe = pNext;
}
}
delete [] mSentryTables;
mSentryTables = NULL;
mModulus = 0;
}
template <class T>
inline void SimpleHash<T>::insert(T* pObject, U32 key)
{
U32 insert = key % mModulus;
Node* pNew = new Node;
pNew->pObject = pObject;
pNew->key = key;
pNew->next = mSentryTables[insert].next;
mSentryTables[insert].next = pNew;
#ifdef TORQUE_DEBUG
Node* probe = pNew->next;
while (probe != NULL) {
AssertFatal(probe->key != key, "error, duplicate keys in sparse array!");
probe = probe->next;
}
#endif
}
template <class T>
inline T* SimpleHash<T>::remove(U32 key)
{
U32 remove = key % mModulus;
Node* probe = mSentryTables[remove];
while (probe->next != NULL) {
if (probe->next->key == key) {
Node* remove = probe->next;
T* pReturn = remove->pObject;
probe->next = remove->next;
delete remove;
return pReturn;
}
probe = probe->next;
}
AssertFatal(false, "Key didn't exist in the array!");
return NULL;
}
template <class T>
inline T* SimpleHash<T>::retreive(U32 key)
{
U32 retrieve = key % mModulus;
Node* probe = &mSentryTables[retrieve];
while (probe->next != NULL) {
if (probe->next->key == key) {
return probe->next->pObject;
}
probe = probe->next;
}
AssertFatal(false, "Key didn't exist in the array!");
return NULL;
}
#endif //_TSIMPLEHASH_H_

File diff suppressed because it is too large Load Diff

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 plus/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 mInteriorRes->mBrushes
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_

View File

@ -0,0 +1,340 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// This file contains all of the functions for converting an InteriorMap into an EditGeometry
#include "map2dif plus/convert.h"
#include "dgl/gBitmap.h"
extern const char* gWadPath;
extern GBitmap* loadBitmap(const char* file);
void loadTextures(InteriorMap* map)
{
// Insert the dummy textures
gWorkingGeometry->insertTexture("NULL");
gWorkingGeometry->insertTexture("TRIGGER");
// Basically we just need to load the textures to get the texture sizes
GBitmap** textures = new GBitmap*[map->mInteriorRes->mMaterials.size()];
Con::printf(" Loading textures:");
dPrintf("\n");
for (int i = 0; i < map->mInteriorRes->mMaterials.size(); i++)
{
// Get the texture file name
char loadBuffer[4096];
dSprintf(loadBuffer, sizeof(loadBuffer), "%s%s", gWadPath, map->mInteriorRes->mMaterials[i]);
// loadBitmap() will walk up the path till it finds the texture
textures[i] = loadBitmap(loadBuffer);
// Notify the user we couldn't load the texture
if (!textures[i])
{
Con::printf(" Unable to load texture %s", map->mInteriorRes->mMaterials[i]);
dPrintf(" Unable to load texture %s\n", map->mInteriorRes->mMaterials[i]);
dFflushStdout();
}
else
Con::printf(" Loaded texture %s", map->mInteriorRes->mMaterials[i]);
}
// Divide our texgens through by the texture sizes
if (map->mInteriorRes->mBrushFormat != InteriorMapResource::QuakeOld && map->mInteriorRes->mBrushFormat != InteriorMapResource::Valve220)
return;
for (U32 i = 0; i < map->mInteriorRes->mMaterials.size(); i++)
{
F32 width = 16.0f;
F32 height = 16.0f;
if (textures[i])
{
width = textures[i]->getWidth();
height = textures[i]->getHeight();
if (!isPow2((U32)width) || !isPow2((U32)height))
{
Con::printf(" Error: Non-power of two sized texture - %s (%d x %d)", map->mInteriorRes->mMaterials[i], (U32)width, (U32)height);
dPrintf(" Error: Non-power of two sized texture - %s (%d x %d)\n", map->mInteriorRes->mMaterials[i], (U32)width, (U32)height);
}
}
dFflushStdout();
for (U32 j = 0; j < map->mInteriorRes->mBrushes.size(); j++)
{
for (U32 k = 0; k < map->mInteriorRes->mBrushes[j]->mFaces.mPolyList.size(); k++)
{
if (i == map->mInteriorRes->mBrushes[j]->mFaces.mPolyList[k].material)
{
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texDiv[0] = width;
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texDiv[1] = height;
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[0].x /= width;
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[0].y /= width;
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[0].z /= width;
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[0].d /= width;
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[1].x /= height;
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[1].y /= height;
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[1].z /= height;
map->mInteriorRes->mBrushes[j]->mTexInfos[k].texGens[1].d /= height;
}
}
}
}
// Flag all of the faces that couldn't load a texture
for (U32 i = 0; i < map->mInteriorRes->mMaterials.size(); i++)
{
if (textures[i])
continue;
for (U32 j = 0; j < map->mInteriorRes->mBrushes.size(); j++)
{
for (U32 k = 0; k < map->mInteriorRes->mBrushes[j]->mFaces.mPolyList.size(); k++)
{
if (i == map->mInteriorRes->mBrushes[j]->mFaces.mPolyList[k].material)
map->mInteriorRes->mBrushes[j]->mFaces.mPolyList[k].material = -1;
}
}
}
// Clean up our textures
for (int i = 0; i < map->mInteriorRes->mMaterials.size(); i++)
delete textures[i];
delete [] textures;
}
void convertInteriorMap(InteriorMap* map)
{
// First copy over the worldspawn entity
getWorldSpawn(map);
gWorkingGeometry->mMinBound.set(1e10, 1e10, 1e10);
gWorkingGeometry->mMaxBound.set(-1e10, -1e10, -1e10);
// Next add our mInteriorRes->mBrushes
getBrushes(map);
// Now get our mInteriorRes->mEntities
getEntities(map);
}
void getBrushes(InteriorMap* map)
{
CSGBrush* pBrush;
for (U32 i = 0; i < map->mInteriorRes->mBrushes.size(); i++)
{
// Only load good mInteriorRes->mBrushes
if (map->mInteriorRes->mBrushes[i]->mStatus != ConvexBrush::Good)
{
Con::printf(" Error: Brush %d - %s", i, map->mInteriorRes->mBrushes[i]->mDebugInfo);
dPrintf(" Error: Brush %d - %s\n", i, map->mInteriorRes->mBrushes[i]->mDebugInfo);
continue;
}
if (map->mInteriorRes->mBrushes[i]->mType == InteriorMapResource::Structural)
{
gWorkingGeometry->mStructuralBrushes.push_back(gBrushArena.allocateBrush());
pBrush = gWorkingGeometry->mStructuralBrushes.last();
pBrush->mBrushType = StructuralBrush;
}
else if (map->mInteriorRes->mBrushes[i]->mType == InteriorMapResource::Detail)
{
gWorkingGeometry->mDetailBrushes.push_back(gBrushArena.allocateBrush());
pBrush = gWorkingGeometry->mDetailBrushes.last();
pBrush->mBrushType = DetailBrush;
}
else if (map->mInteriorRes->mBrushes[i]->mType == InteriorMapResource::Portal)
{
gWorkingGeometry->mPortalBrushes.push_back(gBrushArena.allocateBrush());
pBrush = gWorkingGeometry->mPortalBrushes.last();
pBrush->mBrushType = PortalBrush;
}
else if (map->mInteriorRes->mBrushes[i]->mType == InteriorMapResource::Collision)
{
gWorkingGeometry->mSpecialCollisionBrushes.push_back(gBrushArena.allocateBrush());
pBrush = gWorkingGeometry->mSpecialCollisionBrushes.last();
pBrush->mBrushType = CollisionBrush;
}
else
continue;
pBrush->materialType = 0;
pBrush->brushId = gWorkingGeometry->mCurrBrushId++;
for (U32 j = 0; j < map->mInteriorRes->mBrushes[i]->mFaces.mPolyList.size(); j++)
{
// Copy over the plane for the face
CSGPlane plane;
plane.flags = 0;
plane.owningEntity = NULL;
plane.winding.numNodes = 0;
plane.winding.numZoneIds = 0;
PlaneF normal = map->mInteriorRes->mBrushes[i]->mFaces.mPlaneList[map->mInteriorRes->mBrushes[i]->mFaces.mPolyList[j].plane];
plane.planeEQIndex = gWorkingGeometry->insertPlaneEQ(Point3D(normal.x, normal.y, normal.z), normal.d);
// Set up the texture
S32 tx = map->mInteriorRes->mBrushes[i]->mFaces.mPolyList[j].material;
if (tx > -1)
{
char* texture = (char*)map->mInteriorRes->mMaterials[tx];
plane.pTextureName = gWorkingGeometry->insertTexture(texture);
}
else
{
plane.pTextureName = gWorkingGeometry->insertTexture("WHITE");
}
// Copy over the texgens
PlaneF tGenX = map->mInteriorRes->mBrushes[i]->mTexInfos[j].texGens[0];
PlaneF tGenY = map->mInteriorRes->mBrushes[i]->mTexInfos[j].texGens[1];
plane.texGenIndex = addTexGen(tGenX, tGenY);
pBrush->addPlane(plane);
}
}
}
// Entity conversions
void getWorldSpawn(InteriorMap* map)
{
gWorkingGeometry->mWorldEntity = new WorldSpawnEntity;
InteriorMapResource::Entity* world = map->getEntity("worldspawn");
if (world)
{
char* value;
value = world->getValue("detail_number");
if (value)
gWorkingGeometry->mWorldEntity->mDetailNumber = dAtoi(value);
value = world->getValue("min_pixels");
if (value)
gWorkingGeometry->mWorldEntity->mMinPixels = dAtoi(value);
value = world->getValue("geometry_scale");
if (value)
gWorkingGeometry->mWorldEntity->mGeometryScale = dAtof(value);
value = world->getValue("light_geometry_scale");
if (value)
gWorkingGeometry->mWorldEntity->mLumelScale = dAtof(value);
value = world->getValue("ambient_color");
if (value)
{
dSscanf(value, "%f %f %f",
&gWorkingGeometry->mWorldEntity->mAmbientColor.red,
&gWorkingGeometry->mWorldEntity->mAmbientColor.green,
&gWorkingGeometry->mWorldEntity->mAmbientColor.blue);
}
}
}
void getEntities(InteriorMap* map)
{
for (U32 i = 0; i < map->mInteriorRes->mEntities.size(); i++)
{
// Going to use an odd version of the old map2dif entity code
EditGeometry::Entity* pEntity = NULL;
InteriorMapResource::Entity* ent = map->mInteriorRes->mEntities[i];
if (dStricmp(ent->classname, DetailEntity::getClassName()) == 0)
pEntity = new DetailEntity;
else if (dStricmp(ent->classname, CollisionEntity::getClassName()) == 0)
pEntity = new CollisionEntity;
else if (dStricmp(ent->classname, PortalEntity::getClassName()) == 0)
pEntity = new PortalEntity;
else if (dStricmp(ent->classname, TargetEntity::getClassName()) == 0)
pEntity = new TargetEntity;
// lighting: emitters
if (dStricmp(ent->classname, PointEmitterEntity::getClassName()) == 0)
pEntity = new PointEmitterEntity;
else if (dStricmp(ent->classname, SpotEmitterEntity::getClassName()) == 0)
pEntity = new SpotEmitterEntity;
// lighting: lights
else if (dStricmp(ent->classname, LightEntity::getClassName()) == 0)
{
// For now just shove quake lights into omni_lights
if (map->mInteriorRes->mBrushFormat == InteriorMapResource::QuakeOld)
{
pEntity = new LightOmniEntity;
LightOmniEntity* pLight = static_cast<LightOmniEntity*>(pEntity);
pLight->mColor.set(0.6f, 0.6f, 0.6f);
}
else
pEntity = new LightEntity;
}
else if (dStricmp(ent->classname, "light_globe") == 0)
{
// For now just shove quake lights into omni_lights
pEntity = new LightOmniEntity;
LightOmniEntity* pLight = static_cast<LightOmniEntity*>(pEntity);
pLight->mColor.set(0.6f, 0.6f, 0.6f);
}
// lighting: scripted lights
else if (dStricmp(ent->classname, LightOmniEntity::getClassName()) == 0)
pEntity = new LightOmniEntity;
else if (dStricmp(ent->classname, LightSpotEntity::getClassName()) == 0)
pEntity = new LightSpotEntity;
// lighting: animated lights
else if (dStricmp(ent->classname, LightStrobeEntity::getClassName()) == 0)
pEntity = new LightStrobeEntity;
else if (dStricmp(ent->classname, LightPulseEntity::getClassName()) == 0)
pEntity = new LightPulseEntity;
else if (dStricmp(ent->classname, LightPulse2Entity::getClassName()) == 0)
pEntity = new LightPulse2Entity;
else if (dStricmp(ent->classname, LightFlickerEntity::getClassName()) == 0)
pEntity = new LightFlickerEntity;
else if (dStricmp(ent->classname, LightRunwayEntity::getClassName()) == 0)
pEntity = new LightRunwayEntity;
// Special classes
else if (dStricmp(ent->classname, MirrorSurfaceEntity::getClassName()) == 0)
pEntity = new MirrorSurfaceEntity;
// Trigger Classes
else if (dStricmp(ent->classname, TriggerEntity::getClassName()) == 0)
pEntity = new TriggerEntity;
// Game Classes
else if (dStricmp(ent->classname, GameEntity::getClassName()) == 0)
pEntity = new GameEntity;
// If we have an entity then parse it
if (pEntity)
{
if (pEntity->parseEntityDescription(ent) == false)
{
dPrintf(" Error processing entity %s", ent->classname);
delete pEntity;
}
else
{
gWorkingGeometry->mEntities.push_back(pEntity);
if (dStricmp(ent->classname, PortalEntity::getClassName()) == 0)
gWorkingGeometry->mPortalEntities.push_back(pEntity);
}
}
}
}

View File

@ -0,0 +1,22 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CONVERT_H_
#define _CONVERT_H_
#include "interior/interiorMap.h"
#include "map2dif plus/csgBrush.h"
#include "map2dif plus/morianBasics.h"
#include "map2dif plus/editGeometry.h"
#include "map2dif plus/entityTypes.h"
#include "console/console.h"
void loadTextures(InteriorMap* map);
void convertInteriorMap(InteriorMap* map);
void getBrushes(InteriorMap* map);
void getWorldSpawn(InteriorMap* map);
void getEntities(InteriorMap* map);
#endif //_CONVERT_H_

File diff suppressed because it is too large Load Diff

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 plus/entityTypes.h"
#endif
#ifndef _EDITGEOMETRY_H_
#include "map2dif plus/editGeometry.h"
#endif
#ifndef _CSGBRUSH_H_
#include "map2dif plus/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

View File

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

View File

@ -0,0 +1,180 @@
//-----------------------------------------------------------------------------
// 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 plus/morianBasics.h"
#endif
#ifndef _EDITGEOMETRY_H_
#include "map2dif plus/editGeometry.h"
#endif
#ifndef _MORIANUTIL_H_
#include "map2dif plus/morianUtil.h"
#endif
#ifndef _MMATHFN_H_
#include "math/mMathFn.h"
#endif
#include "collision/convexBrush.h"
bool parseBrush (CSGBrush& brush, Tokenizer* pToker, EditGeometry& geom);
void CopyConvexToCSG(ConvexBrush* convex, CSGBrush* csg);
void CopyCSGToConvex(CSGBrush* csg, ConvexBrush* convex);
U32 addTexGen(PlaneF tGenX, PlaneF tGenY);
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;
PlaneSide whichSideLow(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();
U32 addPlane(CSGPlane plane);
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 PlaneSide CSGPlane::whichSideLow(const Point3D& testPoint) const
{
return gWorkingGeometry->getPlaneEQ(planeEQIndex).whichSideLow(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 plus/editGeometry.h"
#include "map2dif plus/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_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,593 @@
//-----------------------------------------------------------------------------
// 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 plus/morianBasics.h"
#endif
#ifndef _MORIANUTIL_H_
#include "map2dif plus/morianUtil.h"
#endif
#ifndef _BSPNODE_H_
#include "map2dif plus/bspNode.h"
#endif
#ifndef _MPLANE_H_
#include "math/mPlane.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _EDITFLOORPLANRES_H_
#include "map2dif plus/editFloorPlanRes.h"
#endif
#include "interior/interiorMap.h"
#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(InteriorMapResource::Entity* ent) = 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 nudge();
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::whichSideLow(const Point3D& testPoint) const
{
F64 distance = distanceToPlane(testPoint);
if (distance < -gcPointEpsilon)
return PlaneBack;
else if (distance > gcPointEpsilon)
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 plus/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;
}

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_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,470 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _ENTITYTYPES_H_
#define _ENTITYTYPES_H_
//Includes
#ifndef _MORIANBASICS_H_
#include "map2dif plus/morianBasics.h"
#endif
#ifndef _EDITGEOMETRY_H_
#include "map2dif plus/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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
static const char* getClassName() { return("light"); }
};
//------------------------------------------------------------------------------
class LightOmniEntity : public BaseLightEntity
{
public:
ColorF mColor;
U32 mFalloff1;
U32 mFalloff2;
LightOmniEntity();
bool parseEntityDescription(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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(InteriorMapResource::Entity* ent);
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&);
};
//--------------------------------------------------------------------------
//-------------------------------------- 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(InteriorMapResource::Entity* ent);
static const char* getClassName() { return "trigger"; }
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[1024];
char mGameClass[1024];
public:
GameEntity();
~GameEntity();
BrushType getBrushType();
bool parseEntityDescription(InteriorMapResource::Entity* ent);
static const char* getClassName() { return "game_entity"; }
const char* getName() const { return NULL; }
const Point3D& getOrigin() const { return mOrigin; }
bool isPointClass() const { return true; }
};
#endif //_ENTITYTYPES_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,492 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "map2dif plus/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;
}

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_

517
tools/map2dif plus/main.cc Normal file
View File

@ -0,0 +1,517 @@
//-----------------------------------------------------------------------------
// 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 "core/tokenizer.h"
#include "map2dif plus/editGeometry.h"
#include "interior/interior.h"
#include "map2dif plus/editInteriorRes.h"
#include "interior/floorPlanRes.h"
#include "map2dif plus/morianGame.h"
#include "core/frameAllocator.h"
#include "gui/core/guiCanvas.h"
#include "map2dif plus/lmapPacker.h"
#include "map2dif plus/convert.h"
#include <stdlib.h>
MorianGame GameObject;
// FOR SILLY LINK DEPENDANCY
bool gEditingMission = false;
#if defined(TORQUE_DEBUG)
const char* const gProgramVersion = "1.0d";
#else
const char* const gProgramVersion = "1.0r";
#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();
ResManager::create();
// Register known file types here
ResourceManager->registerExtension(".jpg", constructBitmapJPEG);
ResourceManager->registerExtension(".png", constructBitmapPNG);
ResourceManager->registerExtension(".gif", constructBitmapGIF);
ResourceManager->registerExtension(".dbm", constructBitmapDBM);
ResourceManager->registerExtension(".bmp", constructBitmapBMP);
ResourceManager->registerExtension(".bm8", constructBitmapBM8);
ResourceManager->registerExtension(".gft", constructFont);
ResourceManager->registerExtension(".dif", constructInteriorDIF);
ResourceManager->registerExtension(".map", constructInteriorMAP);
Con::init();
Math::init();
Platform::init(); // platform specific initialization
// Create a log file
Con::setLogMode(6);
return(true);
}
static void shutdownLibraries()
{
// shut down
Platform::shutdown();
Con::shutdown();
TextureManager::destroy();
_StringTable::destroy();
// asserts should be destroyed LAST
FrameAllocator::destroy();
PlatformAssert::destroy();
}
void cleanSlashes(char* path)
{
// Clean up path char.
for (char* ptr = path; *ptr != '\0'; ptr++)
if (*ptr == '\\')
*ptr = '/';
}
void terminate(char* path)
{
// Check termination
char* end = &path[dStrlen(path) - 1];
if (*end != '/')
{
end[1] = '/';
end[2] = 0;
}
}
char* cleanPath(const char* _path)
{
char* path = new char[dStrlen(_path) + 2];
dStrcpy(path, _path);
cleanSlashes(path);
terminate(path);
return path;
}
char* getPath(const char* file)
{
char* path = "\0";
if (!dStrchr(file, '/') && !dStrchr(file, '\\'))
return path;
else
{
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;
cleanSlashes(path);
terminate(path);
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;
}
S32 MorianGame::main(int argc, const char** argv)
{
// Set the memory manager page size to 64 megs...
setMinimumAllocUnit((U32)( 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 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 'S':
gTextureSearch = false;
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, and Matthew Fairfax\n"
" Built: %s at %s\n\n"
"Usage: map2dif [-s] [-l] [-h] [-e] [-o outputDirectory] [-t textureDirectory] <file>.map\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"
" -s : Don't search for textures in parent dir.\n"
" -o dir: Directory in which to place the .dif file\n"
" -t dir: Location of textures\n", gProgramVersion, __DATE__, __TIME__);
shutdownLibraries();
return -1;
}
else
dPrintf("\nmap2dif - Torque .MAP file converter\n"
" Copyright (C) GarageGames.com, Inc.\n"
" Program version: %s\n"
" Programmers: John Folliard, Dave Moore, and Matthew Fairfax\n"
" Built: %s at %s\n\n", gProgramVersion, __DATE__, __TIME__);
// 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;
// Wad path
gWadPath = wadPath;
// Print out which file we are loading and what texture search path has been set
Con::printf("Loading %s", mapFile);
dPrintf("\nLoading %s\n", mapFile);
Con::printf("Initial texture search path is set to \"%s\"\n", gWadPath);
dPrintf("Initial texture search path is set to \"%s\"\n\n", gWadPath);
dFflushStdout();
// Dif file name
char* pOutputName = new char[dStrlen(difPath) + dStrlen(baseName) + 5];
dStrcpy(pOutputName, difPath);
dStrcat(pOutputName, baseName);
dStrcat(pOutputName, ".dif");
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);
}
// Fix the slashes
for (U32 i = 0; i < mapFileNames.size(); i++)
cleanSlashes(mapFileNames[i]);
gWorkingResource = new EditInteriorResource;
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
// First we need our object to load the file into
InteriorMap map;
// Set the texture path
map.mTexPath = StringTable->insert(wadPath);
// Set our path info
map.setPath(mapFileNames[i]);
// Load resource
map.mInteriorRes = ResourceManager->load(mapFileNames[i], true);
if (map.mInteriorRes)
{
Con::printf("Successfully opened map file: %s", mapFileNames[i]);
dPrintf("Successfully opened map file: %s\n", mapFileNames[i]);
dFflushStdout();
}
else
{
Con::printf(" Unable to load map file: %s", mapFileNames[i]);
dPrintf(" Unable to load map file: %s\n", mapFileNames[i]);
delete pTokenizer;
delete gWorkingGeometry;
delete gWorkingResource;
shutdownLibraries();
return -1;
}
// Little late for this but it looks nice
dPrintf(" Parsing mapfile...");
dFflushStdout();
loadTextures(&map);
// Reserve enough room for all the brushes
// Not truly "dynamic" but it will do for now
gBrushArena.setSize(map.mInteriorRes->mBrushes.size());
convertInteriorMap(&map);
delete pTokenizer;
dPrintf(" done.\n");
gWorkingGeometry->setGraphGeneration(false,extrusionTest);
dPrintf(" Creating BSP...");
dFflushStdout();
if (gWorkingGeometry->createBSP() == false)
{
Con::printf("Error creating BSP for %s!", mapFileNames[i]);
dPrintf("Error creating BSP for %s!\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();
// Give status
Con::printf("\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());
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());
// 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 )
{
}

View File

@ -0,0 +1,105 @@
//-----------------------------------------------------------------------------
// 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 F64 gcPointEpsilon = 0.01;
static const U32 MaxWindingPoints = 64;
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 whichSideLow(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), numIndices(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_

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_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
//-----------------------------------------------------------------------------
// 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 plus/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();
void setSize(U32 _arenaSize);
CSGBrush* allocateBrush();
void freeBrush(CSGBrush*);
};
extern BrushArena gBrushArena;
#endif //_MORIANUTIL_H_

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 plus/editGeometry.h"
#include "map2dif plus/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 mInteriorRes->mBrushes. 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 mInteriorRes->mBrushes onto structural list for graph generation.
void EditGeometry::xferDetailToStructural()
{
for( S32 i = 0; i < mDetailBrushes.size(); i++ )
mStructuralBrushes.push_back( mDetailBrushes[i] );
mDetailBrushes.clear();
}

View File

@ -0,0 +1,373 @@
//------------------------------------------------------------------------------
// Torque game definition file (.fgd) Version 1.1
// 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"
]
// --------------------------------------------------------------------------
// GameEntities
// --------------------------------------------------------------------------
@PointClass = game_entity : "Game Entity"
[
game_class(string) : "Game Class" : ""
datablock(string) : "Datablock" : ""
]

BIN
tools/map2dif.exe Executable file

Binary file not shown.

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"
]

BIN
tools/map2dif_plus.exe Executable file

Binary file not shown.

BIN
tools/max2dtsExporter.dle Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,237 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef NV_TRISTRIP_OBJECTS_H
#define NV_TRISTRIP_OBJECTS_H
//#include <assert.h>
//#include <windows.h>
//#include <vector>
//#include <list>
#ifndef _NVVERTEXCACHE_H_
#include "max2dtsExporter/NvVertexCache.h"
#endif
#ifndef _TVECTOR_H_
#include "Core/tVector.h"
#endif
#ifndef _PLATFORM_H_
#include "Platform/platform.h"
#endif
//#define int S32
#define UINT U32
//#define float F32
#define WORD U16
/////////////////////////////////////////////////////////////////////////////////
//
// Types defined for stripification
//
/////////////////////////////////////////////////////////////////////////////////
struct MyVertex {
float x, y, z;
float nx, ny, nz;
};
typedef MyVertex MyVector;
struct MyFace {
int v1, v2, v3;
float nx, ny, nz;
};
class NvFaceInfo {
public:
// vertex indices
NvFaceInfo(int v0, int v1, int v2){
m_v0 = v0; m_v1 = v1; m_v2 = v2;
m_stripId = -1;
m_testStripId = -1;
m_experimentId = -1;
}
// data members are left public
int m_v0, m_v1, m_v2;
int m_stripId; // real strip Id
int m_testStripId; // strip Id in an experiment
int m_experimentId; // in what experiment was it given an experiment Id?
};
// nice and dumb edge class that points knows its
// indices, the two faces, and the next edge using
// the lesser of the indices
class NvEdgeInfo {
public:
// constructor puts 1 ref on us
NvEdgeInfo (int v0, int v1){
m_v0 = v0;
m_v1 = v1;
m_face0 = NULL;
m_face1 = NULL;
m_nextV0 = NULL;
m_nextV1 = NULL;
// we will appear in 2 lists. this is a good
// way to make sure we delete it the second time
// we hit it in the edge infos
m_refCount = 2;
}
// ref and unref
void Unref () { if (--m_refCount == 0) delete this; }
// data members are left public
UINT m_refCount;
NvFaceInfo *m_face0, *m_face1;
int m_v0, m_v1;
NvEdgeInfo *m_nextV0, *m_nextV1;
};
// This class is a quick summary of parameters used
// to begin a triangle strip. Some operations may
// want to create lists of such items, so they were
// pulled out into a class
class NvStripStartInfo {
public:
NvStripStartInfo(NvFaceInfo *startFace, NvEdgeInfo *startEdge, bool toV1){
m_startFace = startFace;
m_startEdge = startEdge;
m_toV1 = toV1;
}
NvFaceInfo *m_startFace;
NvEdgeInfo *m_startEdge;
bool m_toV1;
};
typedef Vector<NvFaceInfo*> NvFaceInfoVec;
typedef Vector<NvFaceInfo*> NvFaceInfoList;
typedef Vector<NvFaceInfoVec*> NvStripList;
typedef Vector<NvEdgeInfo*> NvEdgeInfoVec;
typedef Vector<WORD> WordVec;
typedef Vector<MyVertex> MyVertexVec;
typedef Vector<MyFace> MyFaceVec;
// This is a summary of a strip that has been built
class NvStripInfo {
public:
// A little information about the creation of the triangle strips
NvStripInfo(const NvStripStartInfo &startInfo, int stripId, int experimentId = -1) :
m_startInfo(startInfo)
{
m_stripId = stripId;
m_experimentId = experimentId;
visited = false;
}
// This is an experiment if the experiment id is >= 0
inline bool IsExperiment () const { return m_experimentId >= 0; }
inline bool IsInStrip (const NvFaceInfo *faceInfo) const
{
if(faceInfo == NULL)
return false;
return (m_experimentId >= 0 ? faceInfo->m_testStripId == m_stripId : faceInfo->m_stripId == m_stripId);
}
bool SharesEdge(const NvFaceInfo* faceInfo, NvEdgeInfoVec &edgeInfos);
// take the given forward and backward strips and combine them together
void Combine(const NvFaceInfoVec &forward, const NvFaceInfoVec &backward);
//returns true if the face is "unique", i.e. has a vertex which doesn't exist in the faceVec
bool Unique(NvFaceInfoVec& faceVec, NvFaceInfo* face);
// mark the triangle as taken by this strip
bool IsMarked (NvFaceInfo *faceInfo);
void MarkTriangle(NvFaceInfo *faceInfo);
// build the strip
void Build(NvEdgeInfoVec &edgeInfos, NvFaceInfoVec &faceInfos);
// public data members
NvStripStartInfo m_startInfo;
NvFaceInfoVec m_faces;
int m_stripId;
int m_experimentId;
bool visited;
};
typedef Vector<NvStripInfo*> NvStripInfoVec;
//The actual stripifier
class NvStripifier {
public:
// Constructor
NvStripifier();
~NvStripifier();
//the target vertex cache size, the structure to place the strips in, and the input indices
void Stripify(const WordVec &in_indices, const int in_cacheSize, const int in_minStripLength,
NvStripInfoVec &allStrips, NvFaceInfoVec &allFaces);
static int GetUniqueVertexInB(NvFaceInfo *faceA, NvFaceInfo *faceB);
static int GetSharedVertex(NvFaceInfo *faceA, NvFaceInfo *faceB);
protected:
WordVec indices;
int cacheSize;
int minStripLength;
float meshJump;
bool bFirstTimeResetPoint;
/////////////////////////////////////////////////////////////////////////////////
//
// Big mess of functions called during stripification
//
/////////////////////////////////////////////////////////////////////////////////
static int GetNextIndex(const WordVec &indices, NvFaceInfo *face);
static NvEdgeInfo *FindEdgeInfo(NvEdgeInfoVec &edgeInfos, int v0, int v1);
static NvFaceInfo *FindOtherFace(NvEdgeInfoVec &edgeInfos, int v0, int v1, NvFaceInfo *faceInfo);
NvFaceInfo *FindGoodResetPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos);
void FindAllStrips(NvStripInfoVec &allStrips, NvFaceInfoVec &allFaceInfos, NvEdgeInfoVec &allEdgeInfos, int numSamples);
void SplitUpStripsAndOptimize(NvStripInfoVec &allStrips, NvStripInfoVec &outStrips, NvEdgeInfoVec& edgeInfos, NvFaceInfoVec& outFaceList);
void RemoveSmallStrips(NvStripInfoVec& allStrips, NvStripInfoVec& allBigStrips, NvFaceInfoVec& faceList);
bool FindTraversal(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, NvStripInfo *strip, NvStripStartInfo &startInfo);
int CountRemainingTris(Vector<NvStripInfo*>::iterator iter, Vector<NvStripInfo*>::iterator end);
void CommitStrips(NvStripInfoVec &allStrips, const NvStripInfoVec &strips);
float AvgStripSize(const NvStripInfoVec &strips);
int FindStartPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos);
void UpdateCacheStrip(VertexCache* vcache, NvStripInfo* strip);
void UpdateCacheFace(VertexCache* vcache, NvFaceInfo* face);
float CalcNumHitsStrip(VertexCache* vcache, NvStripInfo* strip);
int CalcNumHitsFace(VertexCache* vcache, NvFaceInfo* face);
int NumNeighbors(NvFaceInfo* face, NvEdgeInfoVec& edgeInfoVec);
void BuildStripifyInfo(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos);
bool AlreadyExists(NvFaceInfo* faceInfo, NvFaceInfoVec& faceInfos);
// let our strip info classes and the other classes get
// to these protected stripificaton methods if they want
friend NvStripInfo;
};
#endif

View File

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "max2dtsExporter/NvVertexCache.h"
VertexCache::VertexCache()
{
VertexCache(16);
}
VertexCache::VertexCache(int size)
{
numEntries = size;
entries = new int[numEntries];
for(int i = 0; i < numEntries; i++)
entries[i] = -1;
}
VertexCache::~VertexCache()
{
delete[] entries;
}
int VertexCache::At(int index)
{
return entries[index];
}
void VertexCache::Set(int index, int value)
{
entries[index] = value;
}
void VertexCache::Clear()
{
for(int i = 0; i < numEntries; i++)
entries[i] = -1;
}
void VertexCache::Copy(VertexCache* inVcache)
{
for(int i = 0; i < numEntries; i++)
{
inVcache->Set(i, entries[i]);
}
}

View File

@ -0,0 +1,71 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#if !defined(VERTEX_CACHE_H)
#define VERTEX_CACHE_H
class VertexCache
{
public:
VertexCache(int size);
VertexCache();
~VertexCache();
bool InCache(int entry);
int AddEntry(int entry);
void Clear();
void Copy(VertexCache* inVcache);
int At(int index);
void Set(int index, int value);
private:
int *entries;
int numEntries;
};
inline bool VertexCache::InCache(int entry)
{
bool returnVal = false;
for(int i = 0; i < numEntries; i++)
{
if(entries[i] == entry)
{
returnVal = true;
break;
}
}
return returnVal;
}
inline int VertexCache::AddEntry(int entry)
{
int removed;
removed = entries[numEntries - 1];
//push everything right one
for(int i = numEntries - 2; i >= 0; i--)
{
entries[i + 1] = entries[i];
}
entries[0] = entry;
return removed;
}
#endif

1007
tools/max2dtsExporter/SceneEnum.cc Executable file

File diff suppressed because it is too large Load Diff

126
tools/max2dtsExporter/SceneEnum.h Executable file
View File

@ -0,0 +1,126 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef SCENEENUM_H_
#define SCENEENUM_H_
#pragma pack(push,8)
#include <max.h>
#pragma pack(pop)
#include "max2dtsExporter/shapeMimic.h"
#include "ts/tsShape.h"
#include "platform/platform.h"
#include "core/tvector.h"
#include "core/filestream.h"
class SceneEnumProc : public ITreeEnumProc
{
static FileStream file;
Vector<INode*> meshNodes;
Vector<INode*> skinNodes;
Vector<INode*> subTrees;
Vector<INode*> sequences;
INode *boundsNode;
INode *viconNode;
ShapeMimic shapeMimic;
TSShape * pShape;
bool exportError;
char * exportErrorStr;
S32 getSequence( INode *pNode );
static void clearExportConfig();
void getPolyCount(S32 & polyCount, F32 & minVolPerPoly, F32 & maxVolPerPoly);
void getPolyCountSubtree(INode *, S32 &, F32 & minVolPerPoly, F32 & maxVolPerPoly);
public:
SceneEnumProc();
~SceneEnumProc();
static Vector<char *> alwaysExport;
static Vector<char *> neverExport;
static Vector<char *> neverAnimate;
static char exportType;
static const char * readConfigFile(const char * filename);
static void writeConfigFile(const char * filename);
static void setInitialDefaults();
static bool dumpShape(const char * filename);
void exportTextFile(Stream * file);
static bool isDummy(INode *pNode);
static bool isBounds(INode *pNode);
static bool isVICON(INode *pNode);
static bool isCamera(INode *pNode);
static bool isSubtree(INode *pNode);
static bool isBillboard(INode * pNode);
static bool isBillboardZAxis(INode * pNode);
static bool isSortedObject(INode * pNode);
static bool isDecal(INode * pNode);
static void tweakName(const char ** nodeName); // can only take from front at this point...
static void printDump(U32 mask, const char *);
void startDump(const TCHAR * filename, const TCHAR * maxFile);
void setExportError(const char * errStr) { dFree(exportErrorStr); exportErrorStr = dStrdup(errStr); exportError = true; }
void clearExportError() { exportError = false; dFree(exportErrorStr); exportErrorStr = NULL; }
bool isExportError() { return exportError; }
const char * getExportError() { return exportErrorStr; }
TSShape * getShape() { return pShape; }
TSShape * regenShape(); // for testing purposes only...
bool isEmpty();
S32 callback( INode *node );
void processScene();
void enumScene(IScene *);
};
// enum for printDump
enum
{
PDPass1 = 1 << 0, // collect useful nodes
PDPass2 = 1 << 1, // put together shape structure
PDPass3 = 1 << 2, // cull un-needed nodes
PDObjectOffsets = 1 << 3, // display object offset transform during 2nd pass
PDNodeStates = 1 << 4, // display as added
PDObjectStates = 1 << 5, // ""
PDNodeStateDetails = 1 << 6, // details of above
PDObjectStateDetails = 1 << 7, // ""
PDSequences = 1 << 8,
PDShapeHierarchy = 1 << 9,
PDAlways = 0xFFFFFFFF
};
// parameters of export...
extern U32 dumpMask;
extern U32 allowEmptySubtrees;
extern U32 allowCrossedDetails;
extern U32 allowUnusedMeshes;
extern U32 transformCollapse;
extern U32 viconNeeded;
extern U32 enableSequences;
extern U32 allowOldSequences;
extern U32 enableTwoSidedMaterials;
extern F32 animationDelta;
extern F32 maxFrameRate;
extern S32 weightsPerVertex;
extern F32 weightThreshhold;
extern char baseTexturePath[256];
extern S32 t2AutoDetail;
extern void setBaseTexturePath(const char *);
//--------------------------------------------------------------
#endif

192
tools/max2dtsExporter/Sequence.h Executable file
View File

@ -0,0 +1,192 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _SEQUENCE_H_
#define _SEQUENCE_H_
#pragma pack(push,8)
#include <MAX.H>
#include <dummy.h>
#include <iparamm.h>
#pragma pack(pop)
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#define SEQUENCE_CLASS_ID Class_ID(0x63a64197, 0xfd72a55)
#define PB_SEQ_BEGIN_END 0
#define PB_SEQ_CYCLIC 1
#define PB_SEQ_BLEND 2
#define PB_SEQ_LAST_FIRST_FRAME_SAME 3 // only valid if cyclic
#define PB_SEQ_USE_FRAME_RATE 4 // if this
#define PB_SEQ_FRAME_RATE 5 // then this
#define PB_SEQ_NUM_FRAMES 6 // else this
#define PB_SEQ_IGNORE_GROUND_TRANSFORM 7
#define PB_SEQ_USE_GROUND_FRAME_RATE 8 // if this
#define PB_SEQ_GROUND_FRAME_RATE 9 // then use this
#define PB_SEQ_NUM_GROUND_FRAMES 10// else use this
#define PB_SEQ_ENABLE_MORPH_ANIMATION 11
#define PB_SEQ_ENABLE_VIS_ANIMATION 12
#define PB_SEQ_ENABLE_TRANSFORM_ANIMATION 13
#define PB_SEQ_FORCE_MORPH_ANIMATION 14
#define PB_SEQ_FORCE_VIS_ANIMATION 15
#define PB_SEQ_FORCE_TRANSFORM_ANIMATION 16
#define PB_SEQ_DEFAULT_PRIORITY 17
#define PB_SEQ_BLEND_REFERENCE_TIME 18
#define PB_SEQ_ENABLE_TEXTURE_ANIMATION 19
#define PB_SEQ_ENABLE_IFL_ANIMATION 20
#define PB_SEQ_FORCE_TEXTURE_ANIMATION 21
#define PB_SEQ_TRIGGERS 22
#define PB_SEQ_ENABLE_DECAL_ANIMATION 23
#define PB_SEQ_ENABLE_DECAL_FRAME_ANIMATION 24
#define PB_SEQ_FORCE_DECAL_ANIMATION 25
#define PB_SEQ_OVERRIDE_DURATION 26
#define PB_SEQ_DURATION 27
#define PB_SEQ_ENABLE_UNIFORM_SCALE_ANIMATION 28
#define PB_SEQ_ENABLE_ARBITRARY_SCALE_ANIMATION 29
#define PB_SEQ_FORCE_SCALE_ANIMATION 30
// Class ID for old sequence class -- id for exporter used
// in starsiege, tribes 1, and other games around that time
// You know, games from the previous millenium.
#define OLD_SEQUENCE_CLASS_ID Class_ID(0x09923023,0)
// param block constants for old sequence class
#define PB_OLD_SEQ_ONESHOT 3
#define PB_OLD_SEQ_FORCEVIS 4
#define PB_OLD_SEQ_VISONLY 5
#define PB_OLD_SEQ_NOCOLLAPSE 6
#define PB_OLD_SEQ_USECELRATE 7
extern ClassDesc * GetSequenceDesc();
extern bool getBoolSequenceDefault(S32 index);
extern F32 getFloatSequenceDefault(S32 index);
extern S32 getIntSequenceDefault(S32 index);
extern void resetSequenceDefaults();
class SequenceObject: public DummyObject
{
public:
static U32 defaultCyclic;
static U32 defaultBlend;
static U32 defaultFirstLastFrameSame;
static U32 defaultUseFrameRate;
static F32 defaultFrameRate;
static S32 defaultNumFrames;
static U32 defaultIgnoreGroundTransform;
static U32 defaultUseGroundFrameRate;
static F32 defaultGroundFrameRate;
static S32 defaultNumGroundFrames;
static U32 defaultEnableMorphAnimation;
static U32 defaultEnableVisAnimation;
static U32 defaultEnableTransformAnimation;
static U32 defaultForceMorphAnimation;
static U32 defaultForceVisAnimation;
static U32 defaultForceTransformAnimation;
static S32 defaultDefaultSequencePriority;
static S32 defaultBlendReferenceTime;
static U32 defaultEnableTextureAnimation;
static U32 defaultEnableIflAnimation;
static U32 defaultForceTextureAnimation;
static U32 defaultEnableDecalAnimation;
static U32 defaultEnableDecalFrameAnimation;
static U32 defaultForceDecalAnimation;
static U32 defaultOverrideDuration;
static F32 defaultDuration;
static U32 defaultEnableUniformScaleAnimation;
static U32 defaultEnableArbitraryScaleAnimation;
static U32 defaultForceScaleAnimation;
// Class vars
IParamBlock *pblock;
Interval ivalid;
//
static IParamMap *pmapParam1;
static IParamMap *pmapParam2;
static IParamMap *pmapParam3;
static SequenceObject *editOb;
SequenceObject();
~SequenceObject();
// inherited virtual methods for Reference-management
RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget,
PartID& partID, RefMessage message );
// From BaseObject
void BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev);
void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next);
RefTargetHandle Clone(RemapDir& remap = NoRemap());
// From Object
virtual TCHAR *GetObjectName() { return _T("Sequence II"); }
void InitNodeName(TSTR& s);
Interval ObjectValidity(TimeValue t);
// unimplemented methods from Object class:
// ObjectState Eval(TimeValue time);
// ObjectHandle ApplyTransform(Matrix3& matrix) {return this;}
// Interval ObjectValidity(TimeValue t) {return FOREVER;}
// S32 CanConvertToType(Class_ID obtype) {return FALSE;}
// Object* ConvertToType(TimeValue t, Class_ID obtype) {assert(0);return NULL;}
// void GetWorldBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box );
// void GetLocalBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box );
// S32 DoOwnSelectHilite() { return 1; }
// Animatable methods
void FreeCaches();
S32 NumSubs() { return 1; }
Animatable* SubAnim(S32 i) { return pblock; }
TSTR SubAnimName(S32 i);
void DeleteThis() { delete this; }
Class_ID ClassID() { return SEQUENCE_CLASS_ID; }
void GetClassName(TSTR& s);
// S32 IsKeyable(){ return 0;}
// From ref
S32 NumRefs() {return 1;}
RefTargetHandle GetReference(S32 i) {return pblock;}
void SetReference(S32 i, RefTargetHandle rtarg) {pblock=(IParamBlock*)rtarg;}
IOResult Load(ILoad *iload);
//IOResult Save(ISave *isave);
//RefTargetHandle Clone(RemapDir& remap = NoRemap());
// where do these come from?
virtual void InvalidateUI();
IParamArray *GetParamBlock();
S32 GetParamBlockIndex(S32 id);
virtual ParamDimension *GetParameterDim(S32 pbIndex);
virtual TSTR GetParameterName(S32 pbIndex);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,461 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef SHAPEMIMIC_H_
#define SHAPEMIMIC_H_
#pragma pack(push,8)
#include <max.h>
#include <stdmat.h>
#include <decomp.h>
#pragma pack(pop)
#ifndef _TSSHAPE_H_
#include "ts/tsShape.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
struct ExporterSequenceData;
struct SkinMimic
{
typedef Vector<F32> WeightList;
INode * skinNode;
TSSkinMesh * skinMesh;
S32 detailSize;
S32 skinNum;
S32 meshNum;
F32 multiResPercent;
Vector<TSDrawPrimitive> faces;
Vector<Point3> verts;
Vector<Point3F> normals;
Vector<Point3> tverts;
Vector<U16> indices;
Vector<U16> mergeIndices;
Vector<U32> smoothingGroups;
Vector<U32> vertId;
Vector<INode *> bones;
Vector<WeightList*> weights;
~SkinMimic() { for (S32 i=0;i<weights.size(); i++) delete weights[i]; }
};
struct IflMimic
{
char * fileName;
char * fullFileName;
S32 materialSlot;
~IflMimic() { dFree(fileName); dFree(fullFileName); }
};
struct MeshMimic
{
INode * pNode;
TSMesh * tsMesh;
SkinMimic * skinMimic;
bool billboard; // i.e., face camera
bool sortedObject;
S32 numVerts; // number of unique vertices
S32 meshNum;
Vector<U32> smoothingGroups;
Vector<U32> remap;
Vector<U32> vertId;
Matrix3 objectOffset; // NOTE: not valid till late in the game
F32 multiResPercent;
MeshMimic(INode * meshNode) { pNode = meshNode; skinMimic = NULL; tsMesh = NULL; }
};
struct MultiResMimic
{
INode * pNode;
INode * multiResNode;
S32 totalVerts;
// record off order of vertex merging (we'll figure out tverts later)
Vector<Point3F> mergeFrom;
Vector<Point3F> mergeTo;
MultiResMimic(INode * pn, INode * mrn) { pNode = pn; multiResNode = mrn; }
};
struct ObjectMimic
{
enum { MaxDetails=20 };
struct Detail
{
S32 size;
MeshMimic * mesh;
};
// my object has a name...I'm Jimmy.
const char * name;
const char * fullName; // name of object in tree
// each object has several meshes attached
S32 numDetails;
Detail details[MaxDetails];
// we'll check the object against this list in the end
Vector<S32> * pValidDetails;
INode * inTreeNode; // this is the node that sits in the shape's node hierrarchy
// The next two items are used for sorting objects
// objects are sorted by subTreeNum first, and then
// priority (smallest to highest in both cases).
S32 subtreeNum;
U32 priority;
// all verts in the meshes get multiplied by this matrix
// before being added to ts shape
Matrix3 objectOffset;
// The max node this object hangs on (i.e., the one in the shape not
// the loose objects that make up the detail levels).
INode * maxParent;
// Similar to above: the max node that corresponds to tsNode that will
// be our parent. Starts out the same as maxParent, but is revised
// as we prune unwanted nodes from the tree structure.
INode * maxTSParent;
// ts node index we hang off
S32 tsNodeIndex;
S32 tsObjectIndex;
// This is the eventual payoff
TSObject * tsObject;
//
bool isBone;
bool isSkin;
~ObjectMimic()
{
dFree((char*)name);
dFree((char*)fullName);
for (S32 i=0;i<numDetails;i++)
delete details[i].mesh;
}
};
struct DecalMeshMimic
{
MeshMimic * targetMesh;
TSDecalMesh * tsMesh;
DecalMeshMimic(MeshMimic * target) { targetMesh = target; tsMesh = NULL; }
};
struct DecalObjectMimic
{
enum { MaxDetails=20 };
struct Detail
{
DecalMeshMimic * decalMesh;
};
// each object has several meshes attached
S32 numDetails;
Detail details[MaxDetails];
// The next two items are used for sorting decal objects.
// See ObjectMimic for more info.
S32 subtreeNum;
U32 priority;
// The object used to map the decal onto the object
INode * decalNode;
// This is the object we decal
ObjectMimic * targetObject;
// This is the eventual payoff
TSDecal * tsDecal;
~DecalObjectMimic()
{
for (S32 i=0;i<numDetails;i++)
delete details[i].decalMesh;
}
};
struct NodeMimic
{
// our twin in the max world:
INode * maxNode;
// our neighbors in the mimic world:
NodeMimic * parent;
NodeMimic * child;
NodeMimic * sibling;
// transforms at default time
AffineParts child0;
AffineParts parent0;
// index of our ts version
S32 number;
// our mimic object
Vector<ObjectMimic*> objects;
};
struct CropInfo
{
bool hasTVerts;
Matrix3 uvTransform;
bool uWrap;
bool vWrap;
bool crop;
bool place;
F32 uOffset;
F32 vOffset;
F32 uWidth;
F32 vHeight;
// only valid if crop==true
bool cropLeft;
bool cropRight;
bool cropTop;
bool cropBottom;
bool twoSided;
};
class ShapeMimic
{
friend class SceneEnumProc;
struct Subtree
{
Vector<S32> validDetails;
Vector<const char*> detailNames;
Vector<INode*> detailNodes;
NodeMimic start;
};
Vector<Subtree*> subtrees;
Vector<ObjectMimic*> objectList;
Vector<DecalObjectMimic*> decalObjectList;
Vector<SkinMimic*> skins;
Vector<MultiResMimic*> multiResList;
Vector<IflMimic*> iflList;
Vector<INode*> sequences;
Vector<char*> materials;
Vector<U32> materialFlags;
Vector<U32> materialReflectionMaps;
Vector<U32> materialBumpMaps;
Vector<U32> materialDetailMaps;
Vector<F32> materialDetailScales;
Vector<F32> materialEmapAmounts;
INode * boundsNode;
// this gets filled in late in the game
// it holds the nodes that actually go into the shape
// in the order they appear in the shape
Vector<NodeMimic*> nodes;
// error control
char * errorStr;
void setExportError(const char * str);
// called by generateShape
void generateBounds(TSShape * pShape);
void generateDetails(TSShape * pShape);
void generateSubtrees(TSShape * pShape);
void generateObjects(TSShape * pShape);
void generateDecals(TSShape * pShape);
void generateDefaultStates(TSShape * pShape);
void generateIflMaterials(TSShape * pShape);
void generateSequences(TSShape * pShape);
void generateMaterialList(TSShape * pShape);
void generateSkins(TSShape * pShape);
void optimizeMeshes(TSShape * pShape);
void convertSortObjects(TSShape * pShape);
void generateObjectState(ObjectMimic*,S32 time,TSShape *, bool addFrame, bool addMatFrame);
void generateDecalState(DecalObjectMimic*,S32 time,TSShape *, bool multipleFrames);
void generateFrame(ObjectMimic*,S32 time, bool addFrame, bool addMatFrame);
void generateFaces(INode * meshNode, Mesh & maxMesh, Matrix3 & objectOffset,
Vector<TSDrawPrimitive> & faces, Vector<Point3F> & normals,
Vector<Point3> & verts, Vector<Point3> & tverts, Vector<U16> & indices,
Vector<U32> & smooth, Vector<U32> * vertId = NULL);
S32 getMultiResVerts(INode * meshNode, F32 multiResPercent);
void generateNodeTransform(NodeMimic*,S32 time,TSShape *, bool blend, S32 blendReferenceTime, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale);
void addNodeRotation(NodeMimic*,S32 time,TSShape *, bool blend, Quat16 & rot, bool defaultVal=false);
void addNodeTranslation(NodeMimic*,S32 time,TSShape *, bool blend, Point3F & trans, bool defaultVal=false);
void addNodeUniformScale(NodeMimic*,S32 time,TSShape *, bool blend, F32 scale);
void addNodeAlignedScale(NodeMimic*,S32 time,TSShape *, bool blend, Point3F & scale);
void addNodeArbitraryScale(NodeMimic*,S32 time,TSShape *, bool blend, Quat16 & srot, Point3F & scale);
void computeNormals(Vector<TSDrawPrimitive> &, Vector<U16> & indices, Vector<Point3F> & verts, Vector<Point3F> & norms, Vector<U32> & smooth, S32 vertsPerFrame, S32 numFrames, Vector<U16> * mergeIndices);
void computeNormals(Vector<TSDrawPrimitive> &, Vector<U16> & indices, Vector<Point3> & verts, Vector<Point3F> & norms, Vector<U32> & smooth, S32 vertsPerFrame, S32 numFrames);
void generateNodeAnimation(TSShape * pShape, Interval & range, ExporterSequenceData &);
void generateObjectAnimation(TSShape * pShape, Interval & range, ExporterSequenceData &);
void generateDecalAnimation(TSShape * pShape, Interval & range, ExporterSequenceData &);
void generateGroundAnimation(TSShape * pShape, Interval & range, ExporterSequenceData &);
void generateFrameTriggers(TSShape *, Interval &, ExporterSequenceData &, IParamBlock *);
void addTriggersInOrder(IKeyControl *, F32 duration, TSShape *, U32 & offTriggers);
void setNodeMembership(TSShape *, Interval & range, ExporterSequenceData &, S32 &, S32 &, S32 &, S32 &, S32 &);
S32 setObjectMembership(TSShape::Sequence &, Interval & range, ExporterSequenceData &);
S32 setDecalMembership(TSShape::Sequence &, Interval & range, ExporterSequenceData &);
S32 setIflMembership(TSShape::Sequence &, Interval & range, ExporterSequenceData &);
S32 setRotationMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);
S32 setTranslationMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);
S32 setUniformScaleMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);
S32 setAlignedScaleMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);
S32 setArbitraryScaleMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);
bool animatesAlignedScale(ExporterSequenceData &, S32 numFrames);
bool animatesArbitraryScale(ExporterSequenceData &, S32 numFrames);
void setObjectPriorities(Vector<ObjectMimic*> &);
void setDecalObjectPriorities(Vector<DecalObjectMimic*> &);
bool testCutNodes(Interval & range, ExporterSequenceData &);
// methods used to optimize meshes
void collapseVertices(TSMesh*, Vector<U32> & smooth, Vector<U32> & remap, Vector<U32> * vertId = NULL);
void shareVerts(ObjectMimic *, S32 dl, TSShape *);
void shareVerts(SkinMimic *, Vector<U32> & originalRemap, TSShape *);
void shareBones(SkinMimic *, TSShape *);
bool vertexSame(Point3F &, Point3F &, Point2F &, Point2F &, U32 smooth1, U32 smooth2, U32 idx1, U32 idx2, Vector<U32> * vertId);
void stripify(Vector<TSDrawPrimitive> &, Vector<U16> & indices);
// add a name to the shape and return the index
S32 addName(const char *, TSShape *);
// adds a mesh node to the mesh list and returns the mesh it hangs off
ObjectMimic * getObject(INode *, char * name, S32 size, S32 * detailNum, F32 multiResPercent = -1.0f, bool matchFullName = true, bool isBone=false, bool isSkin=false);
ObjectMimic * addObject(INode *, Vector<S32> *pValidDetails);
ObjectMimic * addObject(INode *, Vector<S32> *pValidDetails, bool multiRes, S32 multiResSize = -1, F32 multiResPercent = -1.0f);
ObjectMimic * addBoneObject(INode * bone, S32 subtreeNum);
MeshMimic * addSkinObject(SkinMimic * skinMimic);
void addDecalObject(INode *, ObjectMimic *);
// functions for adding decals
bool decalOn(DecalObjectMimic*,S32 time);
void interpolateTVert(Point3F v0, Point3F v1, Point3F v2, Point3F planeNormal, Point3F & p, Point3 tv0, Point3 tv1, Point3 tv2, Point3 & tv);
S32 generateDecalFrame(DecalObjectMimic*,S32 time);
void findDecalTexGen(MatrixF &, Point4F &, Point4F &);
S32 findLastDecalFrameNumber(DecalObjectMimic*);
bool prepareDecal(INode * decalNode, S32 time);
void generateDecalFrame(DecalObjectMimic*, S32 time, S32 dl);
bool getDecalTVert(U32 decalType, Point3 vert, Point3F normal, Point3 & tv);
bool getPlaneDecalTVSpecial(Point3 & vert, const Point3F & normal, Point3 & tv);
void findNonOrthogonalComponents(const Point3F & v1, const Point3F & v2, const Point3F & p, F32 & t1, F32 & t2);
bool getDecalProjPoint(U32 decalType, Point3 vert, Point3F & normal, U32 decalFaceIdx, Point3F & p);
bool checkDecalFilter(Point3 v0, Point3 v1, Point3 v2);
bool checkDecalFace(Point3 tv0, Point3 tv1, Point3 tv2);
// utility function for traversing tree in depth first order
// returns NULL when we get back to the top
NodeMimic * findNextNode(NodeMimic * cur);
// utility function called by collapseTransform removes
// the node from the mimic tree structure, taking care
// to otherwise maintain the integrity of the shape tree
void snip(NodeMimic *);
// should we cut this node?
bool cut(NodeMimic *);
// is this node on the never animate list?
bool neverAnimateNode(NodeMimic *);
void splitFaceX(TSDrawPrimitive & face, TSDrawPrimitive & faceA, TSDrawPrimitive & faceB,
Vector<Point3> & verts, Vector<Point3> & tverts, Vector<U16> & indices,
Vector<bool> & flipX, Vector<bool> & flipY,
F32 splitAt,
Vector<U32> & smooth, Vector<U32> * vertId);
void splitFaceY(TSDrawPrimitive & face, TSDrawPrimitive & faceA, TSDrawPrimitive & faceB,
Vector<Point3> & verts, Vector<Point3> & tverts, Vector<U16> & indices,
Vector<bool> & flipX, Vector<bool> & flipY,
F32 splitAt,
Vector<U32> & smooth, Vector<U32> * vertId);
void handleCropAndPlace(Point3 &, CropInfo &);
public:
ShapeMimic();
~ShapeMimic();
// check for errors
bool isError();
const char * getError();
//
void setGlobals(INode * pNode, S32 polyCount, F32 minVolPerPoly, F32 maxVolPerPoly);
void recordMergeOrder(IParamBlock *, MultiResMimic *);
void collapseEdge(Vector<Face> & faces, S32 fromA, S32 toB);
void getMultiResData(INode * pNode, Vector<S32> & multiResSize, Vector<F32> & multiResPercent);
void addMultiRes(INode * pNode, INode * multiResNode);
MultiResMimic * getMultiRes(INode * pNode);
void remapWeights(Vector<SkinMimic::WeightList*> &, INode * skinNode, INode * multiResNode);
void copyWeightsToVerts(SkinMimic *);
void fixupT2AutoDetail();
void applyAutoDetail(Vector<S32> &, Vector<const char*> &, Vector<INode*> &);
// find which vertices merge with which vertices
void findMergeIndices(MeshMimic *, Matrix3 & objectOffset, const Vector<TSDrawPrimitive> &,
Vector<Point3F> &, Vector<Point3F> &, Vector<Point2F> &,
const Vector<U16> & indices, Vector<U16> & mergeIndices,
Vector<U32> & smooth, Vector<U32> & vertId, S32 numChildVerts);
// very specialized function used by above
void addMergeTargetAndMerge(Vector<TSDrawPrimitive> & faces, S32 & faceNum, S32 & v,
Vector<U16> & indices, Vector<U16> & mergeIndices,
Vector<Point3F> & verts, Vector<Point2F> & tverts, Vector<Point3F> & norms,
Vector<U32> & smooth, Vector<U32> & vertId,
Vector<S32> & mergeOrder, Vector<S32> & mergeID, Vector<Point3F> & mergeTarget);
// called in optimizeMeshes
void fixupMergeIndices(TSShape *);
// called after adding first frame to non-skin objects
void generateMergeIndices(ObjectMimic*);
void testMerge(TSShape *, ObjectMimic *, S32 dl, S32 numChildVerts);
// parts added to mimic using these methods...order unimportant
void addBounds(INode * pNode);
void addSubtree(INode * pNode);
void addNode(NodeMimic *,INode *, Vector<S32> &,bool);
void addMesh(INode * pNode) { addObject(pNode,NULL); }
void addSkin(INode * pNode);
void addSkin(INode * pNode, S32 multiResSize, F32 multiResPercent);
void addSequence(INode * pNode);
S32 addMaterial(INode * pNode, S32 materialIndex, CropInfo *);
S32 findMaterial(const char * name, const char * fullName, U32 flags, S32,S32,S32,F32 dscale = 1.0f,F32 emapAmount=1.0f); // finds, and adds if not there
void appendMaterialList(const char * name, U32 flags, S32,S32,S32,F32 dscale = 1.0f,F32 emapAmount=1.0f); // adds to the end
const char * getBaseTextureName(const char *);
// can optionally get rid of nodes with no mesh
void collapseTransforms();
void clearCollapseTransforms();
F32 findMaxDistance(TSMesh * loMesh, TSMesh * hiMesh, F32 & total, S32 & count);
// the payoff...call after adding all of the above
TSShape * generateShape();
void initShape(TSShape*);
void destroyShape(TSShape*);
};
#endif

View File

@ -0,0 +1,657 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#pragma pack(push,8)
#include <MAX.H>
#include <istdplug.h>
#include <utilapi.h>
#pragma pack(pop)
#include "max2dtsExporter/SceneEnum.h"
#include "max2dtsExporter/maxUtil.h"
#include "Platform/platform.h"
#include "Platform/platformAssert.h"
#include "max2dtsExporter/exportUtil.h"
#include "max2dtsExporter/exporter.h"
extern HINSTANCE hInstance;
extern TCHAR *GetString(S32);
extern const char * GetVersionString();
#define EditBoxesToo 0x80000000
class ExportUtil : public UtilityObj
{
public:
IUtil *iu;
Interface *ip;
HWND hPanel1;
HWND hPanel1b;
HWND hPanel2;
HWND hPanel3;
HWND hPanel4;
HWND hPanel5;
//Constructor/Destructor
ExportUtil();
~ExportUtil();
void BeginEditParams(Interface *ip, IUtil *iu);
void EndEditParams(Interface *ip, IUtil *iu);
void DeleteThis() {}
void Init(HWND hWnd);
void Destroy(HWND hWnd);
void initializePanels(S32 mask = -1);
};
static ExportUtil exportUtil;
// This is the Class Descriptor for the U plug-in
class ExportUtilClassDesc:public ClassDesc
{
public:
S32 IsPublic() {return 1;}
void * Create(BOOL loading = FALSE) { loading; return &exportUtil;}
const TCHAR * ClassName() {return "DTS Exporter Utility";}
SClass_ID SuperClassID() {return UTILITY_CLASS_ID;}
Class_ID ClassID() {return ExportUtil_CLASS_ID;}
const TCHAR* Category() {return "Exporter Utility";}
void ResetClassParams (BOOL fileReset);
};
static ExportUtilClassDesc exportUtilDesc;
ClassDesc * GetExportUtilDesc() {return &exportUtilDesc;}
Interface * GetMaxInterface() { return exportUtil.ip; }
//TODO: Should implement this method to reset the plugin params when Max is reset
void ExportUtilClassDesc::ResetClassParams (BOOL fileReset)
{
}
#define MAX_FILENAME_LEN 128
char filenameBuffer[MAX_FILENAME_LEN];
void findFiles(const char * filePattern, Vector<char *> & matches)
{
WIN32_FIND_DATA findData;
HANDLE searchHandle = FindFirstFile(filePattern,&findData);
if (searchHandle != INVALID_HANDLE_VALUE)
{
matches.push_back(dStrdup(findData.cFileName));
while (FindNextFile(searchHandle,&findData))
matches.push_back(dStrdup(findData.cFileName));
}
}
void clearFindFiles(Vector<char*> & matches)
{
for (S32 i=0; i<matches.size(); i++)
dFree(matches[i]);
matches.clear();
}
void initOFN(OPENFILENAME & ofn, char * filter, char * ext, char * title, const char * dir)
{
memset(filenameBuffer,0,MAX_FILENAME_LEN);
memset(&ofn,0,sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = NULL;
ofn.hInstance = hInstance;
ofn.lpstrFilter = filter;
ofn.lpstrCustomFilter = NULL;
ofn.nFilterIndex = 1;
ofn.lpstrFile = filenameBuffer;
ofn.nMaxFile = MAX_FILENAME_LEN;
filenameBuffer[0] = '\0';
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = dir;
ofn.lpstrTitle = title;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER;
ofn.lpstrDefExt = ext;
}
static BOOL CALLBACK ExportUtilDlgProc1(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
exportUtil.Init(hWnd);
break;
case WM_DESTROY:
exportUtil.Destroy(hWnd);
break;
case WM_COMMAND:
break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
exportUtil.ip->RollupMouseMessage(hWnd,msg,wParam,lParam);
break;
default:
return FALSE;
}
return TRUE;
}
static BOOL CALLBACK RenumberDlgFunc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
break;
case WM_DESTROY:
break;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hWnd,-99);
break;
case IDOK:
char buffer[128];
if (GetDlgItemText(hWnd,IDC_EDIT,buffer,128)>0)
{
int newSize = dAtoi(buffer);
EndDialog(hWnd,newSize);
}
else
EndDialog(hWnd,-99);
break;
}
break;
}
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
break;
default:
return FALSE;
}
return TRUE;
}
static BOOL CALLBACK ExportUtilDlgProc1b(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
exportUtil.Init(hWnd);
break;
case WM_DESTROY:
exportUtil.Destroy(hWnd);
break;
case WM_COMMAND:
{
OPENFILENAME ofn;
switch(LOWORD(wParam))
{
case IDC_EXPORT_WHOLE :
initOFN(ofn,"Torque 3Space Shape File (*.dts)\0*.dts\0","dts","Export DTS",exportUtil.ip->GetDir(APP_EXPORT_DIR));
if (GetSaveFileName(&ofn))
exportUtil.ip->ExportToFile(ofn.lpstrFile);
break;
case IDC_EXPORT_SEQUENCES :
initOFN(ofn,"Torque 3Space Sequence File (*.dsq)\0*.dsq\0","dsq","Export DSQ",exportUtil.ip->GetDir(APP_EXPORT_DIR));
if (GetSaveFileName(&ofn))
exportUtil.ip->ExportToFile(ofn.lpstrFile);
break;
case IDC_EXPORT_TEXT:
initOFN(ofn,"Torque 3Space Scene Text Export (*.txt)\0*.txt\0","txt","Scene Text Export",exportUtil.ip->GetDir(APP_EXPORT_DIR));
if (GetSaveFileName(&ofn))
exportUtil.ip->ExportToFile(ofn.lpstrFile);
break;
case IDC_RENUMBER:
{
S32 newNumber = DialogBox(hInstance,MAKEINTRESOURCE(IDD_RENUMBER),hWnd,RenumberDlgFunc);
if (newNumber >=0 && exportUtil.ip)
renumberNodes(exportUtil.ip,newNumber);
break;
}
case IDC_REGISTER_DETAILS:
registerDetails(exportUtil.ip);
break;
case IDC_EMBED:
embedSubtree(exportUtil.ip);
break;
}
break;
}
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
exportUtil.ip->RollupMouseMessage(hWnd,msg,wParam,lParam);
break;
default:
return FALSE;
}
return TRUE;
}
static BOOL CALLBACK ExportUtilDlgProc2(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
exportUtil.Init(hWnd);
break;
case WM_DESTROY:
exportUtil.Destroy(hWnd);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_COLLAPSE : transformCollapse = !transformCollapse; break;
case IDC_ENABLE_SEQUENCES : enableSequences = !enableSequences; break;
case IDC_ENABLE_TWO_SIDED : enableTwoSidedMaterials = !enableTwoSidedMaterials; break;
case IDC_ANIMATION_DELTA:
if (HIWORD(wParam)==EN_CHANGE)
{
ICustEdit * edit = GetICustEdit( GetDlgItem(exportUtil.hPanel2,IDC_ANIMATION_DELTA) );
if (!edit->GotReturn())
return TRUE;
F32 tmp = edit->GetFloat();
if (tmp > 10E-12f)
animationDelta = tmp;
ReleaseICustEdit(edit);
exportUtil.initializePanels(2 + EditBoxesToo);
}
break;
case IDC_BASE_TEXTURE_PATH:
if (HIWORD(wParam)==EN_CHANGE)
{
ICustEdit * edit = GetICustEdit( GetDlgItem(exportUtil.hPanel2,IDC_BASE_TEXTURE_PATH) );
if (!edit->GotReturn())
return TRUE;
char buffer[256];
edit->GetText(buffer,sizeof(buffer));
setBaseTexturePath(buffer);
ReleaseICustEdit(edit);
exportUtil.initializePanels(2 + EditBoxesToo);
}
break;
default : break;
}
exportUtil.initializePanels(2);
break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
exportUtil.ip->RollupMouseMessage(hWnd,msg,wParam,lParam);
break;
default:
return FALSE;
}
return TRUE;
}
static BOOL CALLBACK ExportUtilDlgProc3(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
exportUtil.Init(hWnd);
break;
case WM_DESTROY:
exportUtil.Destroy(hWnd);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_ALLOW_EMPTY : allowEmptySubtrees = !allowEmptySubtrees; break;
case IDC_ALLOW_CROSSED : allowCrossedDetails = !allowCrossedDetails; break;
case IDC_ALLOW_UNUSED : allowUnusedMeshes = !allowUnusedMeshes; break;
case IDC_ALLOW_OLD : allowOldSequences = !allowOldSequences; break;
case IDC_REQUIRE_VICON : viconNeeded = !viconNeeded; break;
}
exportUtil.initializePanels(4);
break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
exportUtil.ip->RollupMouseMessage(hWnd,msg,wParam,lParam);
break;
default:
return FALSE;
}
return TRUE;
}
static BOOL CALLBACK ExportUtilDlgProc4(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
exportUtil.Init(hWnd);
break;
case WM_DESTROY:
exportUtil.Destroy(hWnd);
break;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDC_PASS1 : dumpMask ^= PDPass1; break;
case IDC_PASS2 : dumpMask ^= PDPass2; break;
case IDC_PASS3 : dumpMask ^= PDPass3; break;
case IDC_NODE_STATES : dumpMask ^= PDNodeStates; break;
case IDC_OBJECT_STATES : dumpMask ^= PDObjectStates; break;
case IDC_NODE_STATE_DETAILS : dumpMask ^= PDNodeStateDetails; break;
case IDC_OBJECT_STATE_DETAILS : dumpMask ^= PDObjectStateDetails; break;
case IDC_OBJECT_OFFSETS : dumpMask ^= PDObjectOffsets; break;
case IDC_SEQUENCE_DETAILS : dumpMask ^= PDSequences; break;
case IDC_SHAPE_HIERARCHY : dumpMask ^= PDShapeHierarchy; break;
}
exportUtil.initializePanels(8);
break;
}
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
exportUtil.ip->RollupMouseMessage(hWnd,msg,wParam,lParam);
break;
default:
return FALSE;
}
return TRUE;
}
// ok,ok, so this should probably be in a header somewhere...so sue me
extern char globalConfig[270];
static BOOL CALLBACK ExportUtilDlgProc5(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
exportUtil.Init(hWnd);
break;
case WM_DESTROY:
exportUtil.Destroy(hWnd);
break;
case WM_COMMAND:
{
OPENFILENAME ofn;
switch(LOWORD(wParam))
{
case IDC_CONFIG_LOAD_DEFAULT :
SceneEnumProc::readConfigFile(globalConfig);
break;
case IDC_CONFIG_SAVE_DEFAULT :
SceneEnumProc::writeConfigFile(globalConfig);
break;
case IDC_CONFIG_LOAD :
initOFN(ofn,"Exporter Configuration File (*.cfg)\0*.cfg\0","cfg","Load Configuration File",NULL);
if (GetOpenFileName(&ofn))
SceneEnumProc::readConfigFile(ofn.lpstrFile);
break;
case IDC_CONFIG_SAVE :
initOFN(ofn,"Exporter Configuration File (*.cfg)\0*.cfg\0","dsq","Save Configuration File",NULL);
if (GetSaveFileName(&ofn))
SceneEnumProc::writeConfigFile(ofn.lpstrFile);
break;
}
exportUtil.initializePanels();
break;
}
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
exportUtil.ip->RollupMouseMessage(hWnd,msg,wParam,lParam);
break;
default:
return FALSE;
}
return TRUE;
}
ExportUtil::ExportUtil()
{
iu = NULL;
ip = NULL;
hPanel1 = NULL;
hPanel1b = NULL;
hPanel2 = NULL;
hPanel3 = NULL;
hPanel4 = NULL;
hPanel5 = NULL;
}
ExportUtil::~ExportUtil()
{
}
void ExportUtil::BeginEditParams(Interface *ip, IUtil *iu)
{
this->iu = iu;
this->ip = ip;
hPanel1 = ip->AddRollupPage(
hInstance,
MAKEINTRESOURCE(IDD_UTIL_PANEL1),
ExportUtilDlgProc1,
"About",
0,
APPENDROLL_CLOSED);
hPanel1b = ip->AddRollupPage(
hInstance,
MAKEINTRESOURCE(IDD_UTIL_PANEL1B),
ExportUtilDlgProc1b,
"Utilities",
0);
hPanel2 = ip->AddRollupPage(
hInstance,
MAKEINTRESOURCE(IDD_UTIL_PANEL2),
ExportUtilDlgProc2,
"Parameters",
0,
APPENDROLL_CLOSED);
hPanel3 = ip->AddRollupPage(
hInstance,
MAKEINTRESOURCE(IDD_UTIL_PANEL3),
ExportUtilDlgProc3,
"Error Control",
0,
APPENDROLL_CLOSED);
hPanel4 = ip->AddRollupPage(
hInstance,
MAKEINTRESOURCE(IDD_UTIL_PANEL4),
ExportUtilDlgProc4,
"Dump File Control",
0,
APPENDROLL_CLOSED);
hPanel5 = ip->AddRollupPage(
hInstance,
MAKEINTRESOURCE(IDD_UTIL_PANEL5),
ExportUtilDlgProc5,
"Configuration Control",
0,
APPENDROLL_CLOSED);
initializePanels();
}
void ExportUtil::EndEditParams(Interface *ip, IUtil *iu)
{
this->iu = NULL;
this->ip = NULL;
ip->DeleteRollupPage(hPanel1);
hPanel1 = NULL;
ip->DeleteRollupPage(hPanel1b);
hPanel1b = NULL;
ip->DeleteRollupPage(hPanel2);
hPanel2 = NULL;
ip->DeleteRollupPage(hPanel3);
hPanel3 = NULL;
ip->DeleteRollupPage(hPanel4);
hPanel4 = NULL;
ip->DeleteRollupPage(hPanel5);
hPanel5 = NULL;
}
void ExportUtil::Init(HWND hWnd)
{
}
void ExportUtil::Destroy(HWND hWnd)
{
}
void ExportUtil::initializePanels(S32 mask)
{
ICustButton * button;
ICustEdit * edit;
char buffer[255];
// Panel 1 -- about panel
if (mask & 1)
{
// add text to about panel
dStrcpy(buffer,avar("Torque DTS Exporter\n%s\nCompiled %s\n%s",GetVersionString(),__DATE__,__TIME__));
SetDlgItemText(hPanel1,IDC_STATIC_ABOUT,buffer);
}
// Panel 2 -- Parameters
if (mask&2)
{
CheckDlgButton(hPanel2,IDC_COLLAPSE,transformCollapse);
CheckDlgButton(hPanel2,IDC_ENABLE_SEQUENCES,enableSequences);
CheckDlgButton(hPanel2,IDC_ENABLE_TWO_SIDED,enableTwoSidedMaterials);
if (mask & EditBoxesToo)
{
edit = GetICustEdit(GetDlgItem(hPanel2,IDC_ANIMATION_DELTA));
dStrcpy(buffer,avar("%f",animationDelta));
if (edit) // may already be locked
{
edit->SetText( buffer );
edit->WantReturn(true);
ReleaseICustEdit(edit);
}
edit = GetICustEdit(GetDlgItem(hPanel2,IDC_BASE_TEXTURE_PATH));
dStrcpy(buffer,baseTexturePath);
if (edit)
{
edit->SetText( buffer );
edit->WantReturn(true);
ReleaseICustEdit(edit);
}
}
}
// Panel 3 -- Error Control
if (mask & 4)
{
CheckDlgButton(hPanel3,IDC_ALLOW_EMPTY,allowEmptySubtrees);
CheckDlgButton(hPanel3,IDC_ALLOW_CROSSED,allowCrossedDetails);
CheckDlgButton(hPanel3,IDC_ALLOW_UNUSED,allowUnusedMeshes);
CheckDlgButton(hPanel3,IDC_ALLOW_OLD,allowOldSequences);
CheckDlgButton(hPanel3,IDC_REQUIRE_VICON,viconNeeded);
}
// Panel 4 -- Dump file control
if (mask & 8)
{
CheckDlgButton(hPanel4,IDC_PASS1,dumpMask & PDPass1);
CheckDlgButton(hPanel4,IDC_PASS2,dumpMask & PDPass2);
CheckDlgButton(hPanel4,IDC_PASS3,dumpMask & PDPass3);
CheckDlgButton(hPanel4,IDC_NODE_STATES,dumpMask & PDNodeStates);
CheckDlgButton(hPanel4,IDC_OBJECT_STATES,dumpMask & PDObjectStates);
CheckDlgButton(hPanel4,IDC_NODE_STATE_DETAILS,dumpMask & PDNodeStateDetails);
CheckDlgButton(hPanel4,IDC_OBJECT_STATE_DETAILS,dumpMask & PDObjectStateDetails);
CheckDlgButton(hPanel4,IDC_OBJECT_OFFSETS,dumpMask & PDObjectOffsets);
CheckDlgButton(hPanel4,IDC_SEQUENCE_DETAILS,dumpMask & PDSequences);
CheckDlgButton(hPanel4,IDC_SHAPE_HIERARCHY,dumpMask & PDShapeHierarchy);
}
// Panel 5 -- Configuration control
if (mask & 16)
{
// make buttons push buttons rather than check buttons...
button = GetICustButton(GetDlgItem(hPanel5,IDC_CONFIG_LOAD_DEFAULT));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
button = GetICustButton(GetDlgItem(hPanel5,IDC_CONFIG_SAVE_DEFAULT));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
button = GetICustButton(GetDlgItem(hPanel5,IDC_CONFIG_LOAD));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
button = GetICustButton(GetDlgItem(hPanel5,IDC_CONFIG_SAVE));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
}
// Panel 1b -- Utility panel
if (mask & 32)
{
// make buttons push buttons rather than check buttons...
button = GetICustButton(GetDlgItem(hPanel1b,IDC_EXPORT_WHOLE));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
button = GetICustButton(GetDlgItem(hPanel1b,IDC_EXPORT_SEQUENCES));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
button = GetICustButton(GetDlgItem(hPanel1b,IDC_EXPORT_TEXT));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
button = GetICustButton(GetDlgItem(hPanel1b,IDC_RENUMBER));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
button = GetICustButton(GetDlgItem(hPanel1b,IDC_EMBED));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
button = GetICustButton(GetDlgItem(hPanel1b,IDC_REGISTER_DETAILS));
button->SetType(CBT_PUSH);
ReleaseICustButton(button);
}
}

View File

@ -0,0 +1,17 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _EXPORT_UTIL_H_
#define _EXPORT_UTIL_H_
#pragma pack(push,8)
#include <MAX.H>
#pragma pack(pop)
#define ExportUtil_CLASS_ID Class_ID(0x5959933e, 0x7673c15a)
extern ClassDesc * GetExportUtilDesc();
extern Interface * GetMaxInterface();
#endif // _EXPORT_UTIL_H_

View File

@ -0,0 +1,7 @@
EXPORTS
LibDescription @1
LibNumberClasses @2
LibClassDesc @3
LibVersion @4
SECTIONS
.data READ WRITE

114
tools/max2dtsExporter/exporter.h Executable file
View File

@ -0,0 +1,114 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#define IDD_DTSEXP_UI 101
#define IDD_SEQUENCE_PARAMS 102
#define IDD_SEQUENCE_PARAMS2 103
#define IDD_SEQUENCE_PARAMS3 105
#define IDD_UTIL_PANEL1 106
#define IDD_UTIL_PANEL2 107
#define IDB_BITMAP1 107
#define IDD_UTIL_PANEL3 108
#define IDD_UTIL_PANEL4 109
#define IDD_UTIL_PANEL5 110
#define IDD_UTIL_PANEL1B 111
#define IDD_RENUMBER 112
#define IDD_SKINHELP_PANEL 113
#define IDC_SEQ_CYCLIC 1001
#define IDC_SEQ_BLEND 1002
#define IDC_SEQ_LAST_FIRST_FRAME_SAME 1003
#define IDC_SEQ_USE_FRAME_RATE 1004
#define IDC_SEQ_FRAME_RATE 1005
#define IDC_SEQ_NUM_FRAMES 1006
#define IDC_SEQ_IGNORE_GROUND_TRANSFORM 1007
#define IDC_SEQ_USE_N_FRAMES 1007
#define IDC_SEQ_USE_GROUND_FRAME_RATE 1008
#define IDC_SEQ_PRIORITY 1008
#define IDC_SEQ_GROUND_FRAME_RATE 1009
#define IDC_SEQ_DURATION_OVERRIDE 1009
#define IDC_SEQ_GROUND_FRAME_RATE_SPINNER 1010
#define IDC_SEQ_DURATION 1010
#define IDC_SEQ_NUM_GROUND_FRAMES 1011
#define IDC_SEQ_NUM_GROUND_FRAMES_SPINNER 1012
#define IDC_SEQ_FRAME_RATE_SPINNER 1013
#define IDC_SEQ_NUM_FRAMES_SPINNER 1014
#define IDC_SEQ_PRIORITY_SPINNER 1015
#define IDC_SEQ_DURATION_SPINNER 1016
#define IDC_SEQ_ENABLE_MORPH_ANIMATION 1017
#define IDC_SEQ_ENABLE_VIS_ANIMATION 1018
#define IDC_SEQ_ENABLE_TRANSFORM_ANIMATION 1019
#define IDC_SEQ_USE_N_GROUND_FRAMES 1020
#define IDC_SEQ_ENABLE_TEXTURE_ANIMATION 1020
#define IDC_SEQ_ENABLE_IFL_ANIMATION 1021
#define IDC_SEQ_ENABLE_DECAL_ANIMATION 1022
#define IDC_SEQ_FORCE_MORPH_ANIMATION 1023
#define IDC_SEQ_FORCE_VIS_ANIMATION 1024
#define IDC_SEQ_FORCE_TRANSFORM_ANIMATION 1025
#define IDC_STATIC_ABOUT 1025
#define IDC_EXPORT_WHOLE 1026
#define IDC_SEQ_FORCE_TEXTURE_ANIMATION 1026
#define IDC_EXPORT_SEQUENCES 1027
#define IDC_CONFIG_SAVE_DEFAULT 1027
#define IDC_SEQ_ENABLE_DECAL_FRAME_ANIMATION 1027
#define IDC_EXPORT_TEXT 1028
#define IDC_ANIMATION_DELTA 1028
#define IDC_CONFIG_LOAD 1028
#define IDC_SEQ_FORCE_DECAL_ANIMATION 1028
#define IDC_ALLOW_EMPTY 1029
#define IDC_CONFIG_SAVE 1029
#define IDC_RENUMBER 1029
#define IDC_BASE_TEXTURE_PATH 1029
#define IDC_SEQ_ENABLE_UNIFORM_SCALE_ANIMATION 1029
#define IDC_ALLOW_CROSSED 1030
#define IDC_COLLAPSE 1030
#define IDC_EMBED 1030
#define IDC_SEQ_ENABLE_ARBITRARY_SCALE_ANIMATION 1030
#define IDC_ALLOW_UNUSED 1031
#define IDC_ENABLE_SEQUENCES 1031
#define IDC_REGISTER_DETAILS 1031
#define IDC_SEQ_FORCE_SCALE_ANIMATION 1031
#define IDC_ALLOW_OLD 1032
#define IDC_REQUIRE_VICON 1033
#define IDC_NODE_STATE_DETAILS 1034
#define IDC_ENABLE_TWO_SIDED 1034
#define IDC_OBJECT_STATE_DETAILS 1035
#define IDC_OBJECT_OFFSETS 1036
#define IDC_SEQUENCE_DETAILS 1037
#define IDC_SHAPE_HIERARCHY 1038
#define IDC_PASS1 1039
#define IDC_PASS2 1040
#define IDC_PASS3 1041
#define IDC_NODE_STATES 1042
#define IDC_OBJECT_STATES 1043
#define IDC_CONFIG_LOAD_DEFAULT 1044
#define IDC_EDIT 1045
#define IDC_SPIN 1496
#define IDS_TH_3SPACE 40216
#define IDS_TH_DTSFILE_LONG 40220
#define IDS_TH_DTSFILE_SHORT 40221
#define IDS_TH_COPYRIGHT_COMPANY 40223
#define IDS_TH_DTSEXP 40229
#define IDS_TH_SHAPEEXPORT 40231
#define IDS_TH_DTSEXPORTDLL 40232
#define IDS_TH_NODATATOEXPORT 40233
#define IDS_TH_CANTCREATE 40234
#define IDS_TH_WRITEERROR 40235
#define IDS_OBJ_TOO_LARGE 40236
#define IDS_EXPORT_ERROR 40237
#define IDS_TH_AUTHOR 40238
#define IDS_DB_SEQUENCE 40239
#define IDS_DB_GENERAL 40240
#define IDS_SKINHELP_PARAMS 40241
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 114
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1046
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

394
tools/max2dtsExporter/exporter.rc Executable file
View File

@ -0,0 +1,394 @@
//Microsoft Developer Studio generated resource script.
//
#include "exporter.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"exporter.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_SEQUENCE_PARAMS DIALOG DISCARDABLE 0, 0, 108, 185
STYLE WS_CHILD | WS_VISIBLE
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "",IDC_SEQ_CYCLIC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
61,12,16,8
LTEXT "Cyclic sequence:",IDC_STATIC,3,12,55,8
LTEXT "Blend Sequence:",IDC_STATIC,3,45,56,8
CONTROL "",IDC_SEQ_BLEND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
61,46,11,8
LTEXT "Complete cycle:",IDC_STATIC,22,25,51,8
CONTROL "",IDC_SEQ_LAST_FIRST_FRAME_SAME,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,80,26,12,8
LTEXT "Use frame rate:",IDC_STATIC,3,67,49,8
CONTROL "",IDC_SEQ_FRAME_RATE,"CustEdit",WS_TABSTOP,28,80,27,10
CONTROL "",IDC_SEQ_NUM_FRAMES,"CustEdit",WS_TABSTOP,28,103,27,10
CONTROL "",IDC_SEQ_FRAME_RATE_SPINNER,"SpinnerControl",0x0,63,79,
6,11
CONTROL "",IDC_SEQ_NUM_FRAMES_SPINNER,"SpinnerControl",0x0,63,
102,6,11
CONTROL "",IDC_SEQ_USE_FRAME_RATE,"Button",BS_AUTORADIOBUTTON,61,
67,10,10
CONTROL "",IDC_SEQ_USE_N_FRAMES,"Button",BS_AUTORADIOBUTTON,61,
92,10,10
LTEXT "Use N frames:",IDC_STATIC,3,92,46,8
LTEXT "N=",IDC_STATIC,15,104,10,8
LTEXT "or,",IDC_STATIC,74,80,12,11
CONTROL "",IDC_SEQ_PRIORITY,"CustEdit",WS_TABSTOP,27,167,27,10
CONTROL "",IDC_SEQ_PRIORITY_SPINNER,"SpinnerControl",0x0,63,166,
6,11
LTEXT "Sequence priority:",IDC_STATIC,3,156,58,8
LTEXT "Override duration:",IDC_STATIC,4,123,57,8
CONTROL "",IDC_SEQ_DURATION_OVERRIDE,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,62,124,11,8
CONTROL "",IDC_SEQ_DURATION,"CustEdit",WS_TABSTOP,29,138,27,10
CONTROL "",IDC_SEQ_DURATION_SPINNER,"SpinnerControl",0x0,63,137,
6,11
END
IDD_SEQUENCE_PARAMS2 DIALOG DISCARDABLE 0, 0, 108, 103
STYLE WS_CHILD | WS_VISIBLE
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "Ignore ground transform:",IDC_STATIC,3,13,78,8
CONTROL "",IDC_SEQ_IGNORE_GROUND_TRANSFORM,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,88,13,12,8
LTEXT "Use frame rate:",IDC_STATIC,3,37,49,8
CONTROL "",IDC_SEQ_GROUND_FRAME_RATE,"CustEdit",WS_TABSTOP,34,51,
22,10
CONTROL "",IDC_SEQ_NUM_GROUND_FRAMES,"CustEdit",WS_TABSTOP,36,77,
20,10
CONTROL "",IDC_SEQ_GROUND_FRAME_RATE_SPINNER,"SpinnerControl",
0x0,59,51,10,10
CONTROL "",IDC_SEQ_NUM_GROUND_FRAMES_SPINNER,"SpinnerControl",
0x0,60,77,8,10
CONTROL "",IDC_SEQ_USE_GROUND_FRAME_RATE,"Button",
BS_AUTORADIOBUTTON,58,38,16,8
CONTROL "",IDC_SEQ_USE_N_GROUND_FRAMES,"Button",
BS_AUTORADIOBUTTON,57,67,16,8
LTEXT "Use N frames:",IDC_STATIC,3,66,46,8
LTEXT "or,",IDC_STATIC,75,51,17,12
LTEXT "N=",IDC_STATIC,20,79,10,8
END
IDD_SEQUENCE_PARAMS3 DIALOG DISCARDABLE 0, 0, 109, 200
STYLE WS_CHILD | WS_VISIBLE
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "",IDC_SEQ_ENABLE_MORPH_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,18,16,8
CONTROL "",IDC_SEQ_ENABLE_VIS_ANIMATION,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,87,29,16,8
CONTROL "",IDC_SEQ_ENABLE_TRANSFORM_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,40,16,8
LTEXT "Enable:",IDC_STATIC,4,6,25,8
LTEXT "Morph animation:",IDC_STATIC,12,18,55,8
LTEXT "Visibility animation:",IDC_STATIC,12,29,59,8
LTEXT "Transform animation:",IDC_STATIC,12,40,66,8
CONTROL "",IDC_SEQ_FORCE_MORPH_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,130,16,8
CONTROL "",IDC_SEQ_FORCE_VIS_ANIMATION,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,87,141,16,8
CONTROL "",IDC_SEQ_FORCE_TRANSFORM_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,152,16,8
LTEXT "Force:",IDC_STATIC,4,119,21,8
LTEXT "Morph animation:",IDC_STATIC,12,130,55,8
LTEXT "Visibility animation:",IDC_STATIC,12,141,59,8
LTEXT "Transform animation:",IDC_STATIC,12,152,66,8
CONTROL "",IDC_SEQ_FORCE_TEXTURE_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,174,16,8
LTEXT "Texture animation:",IDC_STATIC,12,174,59,8
CONTROL "",IDC_SEQ_ENABLE_TEXTURE_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,73,16,8
LTEXT "Texture animation:",IDC_STATIC,12,73,59,8
CONTROL "",IDC_SEQ_ENABLE_IFL_ANIMATION,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,87,84,16,8
LTEXT "IFL animation:",IDC_STATIC,12,84,45,8
CONTROL "",IDC_SEQ_ENABLE_DECAL_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,95,16,8
LTEXT "Decal animation:",IDC_STATIC,12,95,54,8
CONTROL "",IDC_SEQ_ENABLE_DECAL_FRAME_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,106,16,8
LTEXT "Decal-frame animation:",IDC_STATIC,12,106,73,8
CONTROL "",IDC_SEQ_FORCE_DECAL_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,185,16,8
LTEXT "Decal animation:",IDC_STATIC,12,185,54,8
CONTROL "",IDC_SEQ_ENABLE_UNIFORM_SCALE_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,51,16,8
LTEXT "Uniform scale anim:",IDC_STATIC,12,51,62,8
CONTROL "",IDC_SEQ_ENABLE_ARBITRARY_SCALE_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,62,16,8
LTEXT "Arbitrary scale anim:",IDC_STATIC,12,62,64,8
CONTROL "",IDC_SEQ_FORCE_SCALE_ANIMATION,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,87,163,16,8
LTEXT "Scale animation:",IDC_STATIC,12,163,53,8
END
IDD_UTIL_PANEL1 DIALOG DISCARDABLE 0, 0, 108, 51
STYLE WS_CHILD
FONT 8, "MS Sans Serif"
BEGIN
CTEXT "About text...",IDC_STATIC_ABOUT,11,2,88,40
END
IDD_UTIL_PANEL2 DIALOG DISCARDABLE 0, 0, 108, 91
STYLE WS_CHILD
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "",IDC_ANIMATION_DELTA,"CustEdit",WS_TABSTOP,57,50,48,9
LTEXT "Animation Delta:",IDC_STATIC,2,50,52,8
LTEXT "Collapse Transforms",IDC_STATIC,19,7,84,11
CONTROL "",IDC_COLLAPSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,
7,8,10
LTEXT "Enable Sequence Export",IDC_STATIC,19,21,84,11
CONTROL "",IDC_ENABLE_SEQUENCES,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,6,21,8,10
CONTROL "",IDC_ENABLE_TWO_SIDED,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,6,35,8,9
LTEXT "Enable 2-sided materials",IDC_STATIC,19,35,81,12
CONTROL "",IDC_BASE_TEXTURE_PATH,"CustEdit",WS_TABSTOP,9,77,91,9
LTEXT "Base Texture Path:",IDC_STATIC,2,66,62,8
END
IDD_UTIL_PANEL3 DIALOG DISCARDABLE 0, 0, 108, 96
STYLE WS_CHILD
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "",IDC_ALLOW_EMPTY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
5,11,8,9
LTEXT "Allow Empty Subtrees",IDC_STATIC,16,11,71,11
CONTROL "",IDC_ALLOW_CROSSED,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,5,29,8,9
LTEXT "Allow Crossed Details",IDC_STATIC,16,29,71,11
CONTROL "",IDC_ALLOW_UNUSED,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,5,46,8,9
LTEXT "Allow Unused Meshes",IDC_STATIC,16,46,71,11
CONTROL "",IDC_ALLOW_OLD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,
64,8,9
LTEXT "Allow Old Style Sequences",IDC_STATIC,16,64,85,12
CONTROL "",IDC_REQUIRE_VICON,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,5,80,8,9
LTEXT "Require VICON/BIP",IDC_STATIC,16,80,74,12
END
IDD_UTIL_PANEL4 DIALOG DISCARDABLE 0, 0, 108, 178
STYLE WS_CHILD
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "",IDC_PASS1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,11,
8,9
LTEXT "Initial Node Collection",IDC_STATIC,16,11,71,11
CONTROL "",IDC_PASS2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,27,
8,9
LTEXT "Initial Shape Construction",IDC_STATIC,16,27,85,11
CONTROL "",IDC_PASS3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,44,
8,9
LTEXT "Node Culling",IDC_STATIC,16,44,71,11
CONTROL "",IDC_NODE_STATES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
5,62,8,9
LTEXT "Node States",IDC_STATIC,16,62,85,12
CONTROL "",IDC_OBJECT_STATES,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,5,78,8,9
LTEXT "Object States",IDC_STATIC,16,78,74,12
CONTROL "",IDC_NODE_STATE_DETAILS,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,5,93,8,9
LTEXT "Node State Details",IDC_STATIC,16,93,71,11
CONTROL "",IDC_OBJECT_STATE_DETAILS,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,5,109,8,9
LTEXT "Object State Details",IDC_STATIC,16,109,85,11
CONTROL "",IDC_OBJECT_OFFSETS,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,5,126,8,9
LTEXT "Object Offsets",IDC_STATIC,16,126,71,11
CONTROL "",IDC_SEQUENCE_DETAILS,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,5,143,8,9
LTEXT "Sequences",IDC_STATIC,16,143,85,12
CONTROL "",IDC_SHAPE_HIERARCHY,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,5,159,8,9
LTEXT "Shape Hierarchy",IDC_STATIC,16,159,74,12
END
IDD_UTIL_PANEL5 DIALOG DISCARDABLE 0, 0, 107, 85
STYLE WS_CHILD
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "Load Default Config",IDC_CONFIG_LOAD_DEFAULT,"CustButton",
WS_TABSTOP,7,8,94,13
CONTROL "Save As Default Config",IDC_CONFIG_SAVE_DEFAULT,
"CustButton",WS_TABSTOP,7,27,94,13
CONTROL "Load Config...",IDC_CONFIG_LOAD,"CustButton",WS_TABSTOP,
7,46,94,13
CONTROL "Save Config...",IDC_CONFIG_SAVE,"CustButton",WS_TABSTOP,
7,65,94,13
END
IDD_UTIL_PANEL1B DIALOG DISCARDABLE 0, 0, 108, 123
STYLE WS_CHILD
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "Whole Shape",IDC_EXPORT_WHOLE,"CustButton",WS_TABSTOP,
14,16,80,11
CONTROL "Sequences",IDC_EXPORT_SEQUENCES,"CustButton",WS_TABSTOP,
14,31,80,11
CONTROL "Text Description",IDC_EXPORT_TEXT,"CustButton",
WS_TABSTOP,14,46,80,11
CONTROL " Export:",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP |
WS_GROUP,4,4,28,11
LTEXT " General:",IDC_STATIC,4,63,30,10
CONTROL "Renumber selection...",IDC_RENUMBER,"CustButton",
WS_TABSTOP,14,75,80,11
CONTROL "Embed Shape",IDC_EMBED,"CustButton",WS_TABSTOP,14,91,80,
11
CONTROL "Register Details",IDC_REGISTER_DETAILS,"CustButton",
WS_TABSTOP,14,107,80,11
END
IDD_RENUMBER DIALOG DISCARDABLE 0, 0, 187, 55
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Renumber"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,130,7,50,14
PUSHBUTTON "Cancel",IDCANCEL,130,24,50,14
LTEXT "New number:",IDC_STATIC,7,14,53,12
EDITTEXT IDC_EDIT,56,13,67,14,ES_AUTOHSCROLL
END
IDD_SKINHELP_PANEL DIALOG DISCARDABLE 0, 0, 108, 156
STYLE WS_CHILD | WS_VISIBLE
FONT 8, "MS Sans Serif"
BEGIN
CTEXT "Generated by Plugin AppWizard\nCode && design by\nRavi Karra - Kinetix",
IDC_STATIC,7,7,94,36
CONTROL "",1490,"CustEdit",WS_TABSTOP,29,114,35,10
CONTROL "",IDC_SPIN,"SpinnerControl",0x0,65,114,7,10
LTEXT "Spinner Cust Control:",IDC_STATIC,20,102,67,8
CTEXT "TODO: Place panel controls here.",IDC_STATIC,15,63,78,
19
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_UTIL_PANEL2, DIALOG
BEGIN
RIGHTMARGIN, 107
END
IDD_RENUMBER, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 180
TOPMARGIN, 7
BOTTOMMARGIN, 48
END
IDD_SKINHELP_PANEL, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 101
TOPMARGIN, 7
BOTTOMMARGIN, 149
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE DISCARDABLE
BEGIN
IDS_TH_3SPACE "3Space"
IDS_TH_DTSFILE_LONG "V12 3Space (DTS) File"
IDS_TH_DTSFILE_SHORT "V12 DTS File"
IDS_TH_COPYRIGHT_COMPANY "Copyright 2001 GarageGames.com"
END
STRINGTABLE DISCARDABLE
BEGIN
IDS_TH_DTSEXP "max2dtsExporter"
IDS_TH_SHAPEEXPORT "Shape Export"
IDS_TH_DTSEXPORTDLL "GarageGames V12 3Space DTS File Export DLL"
IDS_TH_NODATATOEXPORT "No data to export"
IDS_TH_CANTCREATE "Can't create output file"
IDS_TH_WRITEERROR "Write error on file"
IDS_OBJ_TOO_LARGE "Object %s is too large to export"
IDS_EXPORT_ERROR "Export Error"
IDS_TH_AUTHOR "Created by Clark Fagot"
IDS_DB_SEQUENCE "Sequence"
END
STRINGTABLE DISCARDABLE
BEGIN
IDS_DB_GENERAL "General DTS Objects"
IDS_SKINHELP_PARAMS "skin help params"
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

648
tools/max2dtsExporter/main.cc Executable file
View File

@ -0,0 +1,648 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#pragma pack(push,8)
#include <MAX.H>
#pragma pack(pop)
// NOTE:
// if you have problems linking this exporter it is likely you are encountering a
// bug in Microsofts include\basetsd.h header.
//
// The problem is that Visual C++ defines INT_PTR to 'long' when it is
// supposed to be defined as an 'int' (on ia32 platforms). You can either
// use a supported build environment by updating to the platform SDK, or
// you can use the unsupported environment by manually fixing the problem
// in the header "On or around line 123 of include\basetsd.h change:
//
// typedef long INT_PTR, *PINT_PTR;
// typedef unsigned long UINT_PTR, *PUINT_PTR;
// TO
// typedef int INT_PTR, *PINT_PTR;
// typedef unsigned int UINT_PTR, *PUINT_PTR;
#include "max2dtsExporter/exporter.h"
#include "max2dtsExporter/sceneEnum.h"
#include "core/filestream.h"
#include "ts/tsShapeInstance.h"
#include "max2dtsExporter/sequence.h"
#include "max2dtsExporter/exportUtil.h"
#include "max2dtsExporter/skinHelper.h"
#define DLLEXPORT __declspec(dllexport)
#if defined(TORQUE_DEBUG)
const char * const gProgramVersion = "0.900d-beta";
#else
const char * const gProgramVersion = "0.900r-beta";
#endif
HINSTANCE hInstance;
S32 gSupressUI = 0;
TCHAR maxFile[1024];
S32 controlsInit = FALSE;
char dllPath[256];
char globalConfig[270];
TCHAR *GetString(S32 id)
{
static TCHAR buf[256];
if (hInstance)
{
return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL;
}
return NULL;
}
const char * days[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
const char * months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
static S32 Alert(const char * s1, const char * s2, S32 option = MB_OK)
{
if (gSupressUI)
{
FileStream fs;
fs.open(avar("%smax2difExport.error.log",dllPath),FileStream::ReadWrite);
if (fs.getStatus() != Stream::Ok)
{
fs.close();
return -1;
}
fs.setPosition(fs.getStreamSize());
// what's the time and date?
Platform::LocalTime localTime;
Platform::getLocalTime(localTime);
char timeBuffer[256];
dStrcpy(timeBuffer,avar("%i:%i on %s %s %i",localTime.hour,localTime.min,days[localTime.weekday],months[localTime.month],localTime.monthday));
// write the error to the log file
char buffer[1024];
dStrcpy(buffer,avar("Error exporting file \"%s\" at %s:\r\n --> %s\r\n\r\n",maxFile,timeBuffer,s1));
fs.writeLine((unsigned char*) buffer);
fs.close();
return -1;
}
else
{
TSTR str1(s1);
TSTR str2(s2);
return MessageBox(GetActiveWindow(), str1, str2, option);
}
}
static S32 Alert(S32 s1, S32 s2 = IDS_TH_DTSEXP, S32 option = MB_OK)
{
return Alert(GetString(s1),GetString(s2),option);
}
static S32 Alert(const char * s1, S32 s2 = IDS_TH_DTSEXP, S32 option = MB_OK)
{
return Alert(s1,GetString(s2),option);
}
const char * GetVersionString()
{
static char versionString[128];
dStrcpy(versionString,avar("Version %s (dts v%i.%.2i)",gProgramVersion, DTS_EXPORTER_CURRENT_VERSION/100, DTS_EXPORTER_CURRENT_VERSION%100));
return versionString;
}
//------------------------------------------------------
// Our main plugin class:
class _Exporter : public SceneExport
{
char extension[10]; // 'DTS' or 'DSQ'
public:
_Exporter(const char *);
~_Exporter();
S32 ExtCount(); // Number of extensions supported
const TCHAR *Ext(S32); // Extension #n
const TCHAR *LongDesc(); // Long ASCII description
const TCHAR *ShortDesc(); // Short ASCII description
const TCHAR *AuthorName(); // ASCII Author name
const TCHAR *CopyrightMessage(); // ASCII Copyright message
const TCHAR *OtherMessage1(); // Other message #1
const TCHAR *OtherMessage2(); // Other message #2
U32 Version(); // Version number * 100
void ShowAbout(HWND); // Show DLL's "About..." box
S32 DoExport(const TCHAR *, ExpInterface *, Interface *, int, DWORD);
};
//------------------------------------------------------
// Jaguar interface code
extern void mInstallLibrary_C();
namespace Memory
{
extern bool gAlwaysLogLeaks;
};
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/)
{
hInstance = hinstDLL;
if (!controlsInit)
{
controlsInit = TRUE;
// 3DS custom controls
InitCustomControls(hInstance);
// Initialize common Windows controls
InitCommonControls();
// install math library (set up function pointers)
mInstallLibrary_C();
// set "factory" default parameter values
SceneEnumProc::setInitialDefaults();
// always log memory leaks -- some of them aren't really leaks,
// but we want to see them and we don't want the annoying box asking us
// about it...
Memory::gAlwaysLogLeaks = true;
// get DLL path
GetModuleFileName(hInstance,dllPath,sizeof(dllPath));
char * p = dStrrchr(dllPath,'\\');
char * p2 = dStrrchr(dllPath,':');
if (p && *p=='\\')
*(p+1) = '\0';
else if(p2 && *p2==':')
*(p2+1) = '\0';
else
dllPath[0] = '\0';
// load the global config file if we can find it
dStrcpy(globalConfig,dllPath);
dStrcpy(globalConfig+dStrlen(globalConfig),"dtsGlobal.cfg");
SceneEnumProc::readConfigFile(globalConfig);
}
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return(TRUE);
}
// defines for setting exporter type and description
#define _EXPORTER_CLASS_NAME "Torque Shape Exporter"
#define _DTS_EXPORTER_CLASS_ID Class_ID(0x296a4787, 0x2ec557fc)
#define _DSQ_EXPORTER_CLASS_ID Class_ID(0x65d74e76, 0x10da4a97)
#define _TXT_EXPORTER_CLASS_ID Class_ID(0x6bfb02d7, 0x13860666)
#define _EXPORTER_CLASS_SDESC "Torque Shape Exporter"
// more code for interfacing with max
class _DTSClassDesc : public ClassDesc
{
public:
S32 IsPublic() { return 1; }
void *Create(BOOL loading = FALSE) { return new _Exporter("DTS"); }
const TCHAR *ClassName() { return _EXPORTER_CLASS_NAME; }
SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
Class_ID ClassID() { return _DTS_EXPORTER_CLASS_ID; }
const TCHAR *Category() { return GetString(IDS_TH_SHAPEEXPORT); }
};
// more code for interfacing with max
class _DSQClassDesc : public ClassDesc
{
public:
S32 IsPublic() { return 1; }
void *Create(BOOL loading = FALSE) { return new _Exporter("DSQ"); }
const TCHAR *ClassName() { return _EXPORTER_CLASS_NAME; }
SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
Class_ID ClassID() { return _DSQ_EXPORTER_CLASS_ID; }
const TCHAR *Category() { return GetString(IDS_TH_SHAPEEXPORT); }
};
// more code for interfacing with max
class _TXTClassDesc : public ClassDesc
{
public:
S32 IsPublic() { return 1; }
void *Create(BOOL loading = FALSE) { return new _Exporter("TXT"); }
const TCHAR *ClassName() { return _EXPORTER_CLASS_NAME; }
SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
Class_ID ClassID() { return _TXT_EXPORTER_CLASS_ID; }
const TCHAR *Category() { return GetString(IDS_TH_SHAPEEXPORT); }
};
static _DTSClassDesc _DTSDesc;
static _DSQClassDesc _DSQDesc;
static _TXTClassDesc _TXTDesc;
DLLEXPORT const TCHAR *LibDescription()
{
return GetString(IDS_TH_DTSEXPORTDLL);
}
DLLEXPORT S32 LibNumberClasses()
{
return 6;
}
DLLEXPORT ClassDesc *LibClassDesc(S32 i)
{
switch(i)
{
case 0:
return &_DTSDesc;
case 1:
return &_DSQDesc;
case 2:
return &_TXTDesc;
case 3:
return GetSequenceDesc();
case 4:
return GetExportUtilDesc();
case 5:
return GetSkinHelperDesc();
default:
return NULL;
}
}
// Return version so can detect obsolete DLLs
DLLEXPORT ULONG LibVersion()
{
return VERSION_3DSMAX;
}
//
// ._DTS export module functions follow:
//
_Exporter::_Exporter(const char * _extension)
{
dStrcpy(extension,_extension);
}
_Exporter::~_Exporter()
{
}
S32 _Exporter::ExtCount()
{
return 1;
}
// Extensions supported for import/export modules
const TCHAR *_Exporter::Ext(S32 n)
{
switch(n)
{
case 0:
return _T(extension);
default:
return _T("");
}
}
// Long ASCII description (e.g., "Targa 2.0 Image File")
const TCHAR *_Exporter::LongDesc()
{
return GetString(IDS_TH_DTSFILE_LONG);
}
// Short ASCII description (e.g., "Targa")
const TCHAR *_Exporter::ShortDesc()
{
return _EXPORTER_CLASS_SDESC;
}
// ASCII Author name
const TCHAR *_Exporter::AuthorName()
{
return GetString(IDS_TH_AUTHOR);
}
// ASCII Copyright message
const TCHAR *_Exporter::CopyrightMessage()
{
return GetString(IDS_TH_COPYRIGHT_COMPANY);
}
// Other message #1
const TCHAR *_Exporter::OtherMessage1()
{
return _T("");
}
// Other message #2
const TCHAR *_Exporter::OtherMessage2()
{
return _T("");
}
// Version number * 100 (i.e. v3.01 = 301)
U32 _Exporter::Version()
{
return DTS_EXPORTER_CURRENT_VERSION;
}
// Optional
void _Exporter::ShowAbout(HWND hWnd)
{
}
//------------------------------------------------------
// be more thourough about this later...
U32 saveDumpMask;
bool saveEnableSequences;
bool saveCollapse;
bool saveAllowEmptySubtrees;
bool saveAllowCrossedDetails;
bool saveAllowUnusedMeshes;
bool saveViconNeeded;
bool saveAllowOldSequences;
F32 saveAnimationDelta;
F32 saveMaxFrameRate;
S32 saveT2AutoDetail;
void saveConfig()
{
saveDumpMask = dumpMask;
saveEnableSequences = enableSequences;
saveCollapse = transformCollapse;
saveAllowEmptySubtrees = allowEmptySubtrees;
saveAllowCrossedDetails = allowCrossedDetails;
saveAllowUnusedMeshes = allowUnusedMeshes;
saveViconNeeded = viconNeeded;
saveAllowOldSequences = allowOldSequences;
saveAnimationDelta = animationDelta;
saveMaxFrameRate = maxFrameRate;
saveT2AutoDetail = t2AutoDetail;
}
void restoreConfig()
{
dumpMask = saveDumpMask;
enableSequences = saveEnableSequences;
transformCollapse = saveCollapse;
allowEmptySubtrees = saveAllowEmptySubtrees;
allowCrossedDetails = saveAllowCrossedDetails;
allowUnusedMeshes = saveAllowUnusedMeshes;
viconNeeded = saveViconNeeded;
allowOldSequences = saveAllowOldSequences;
animationDelta = saveAnimationDelta;
maxFrameRate = saveMaxFrameRate;
t2AutoDetail = saveT2AutoDetail;
}
S32 cheapSemaphore = 0;
S32 _dts_save(const TCHAR *fname, ExpInterface *ei, Interface *gi)
{
if (cheapSemaphore)
{
Alert("One export at a time: Export code not re-entrant.");
return 1;
}
cheapSemaphore = 1;
char filename[1024];
dStrcpy(filename,fname);
char * ch = filename;
while (*ch!='\0')
{
*ch = tolower(*ch);
ch++;
}
SceneEnumProc myScene;
dStrcpy(maxFile,gi->GetCurFilePath());
//-----------------------------------------------
// read in the config file...
char configFilename[256];
dStrcpy(configFilename,maxFile);
char * p = dStrrchr(configFilename,'\\');
char * p2 = dStrrchr(configFilename,':');
if (p && *p=='\\')
dStrcpy(p+1,"*.cfg");
else if (p2 && *p2==':')
dStrcpy(p2+1,"*.cfg");
else
dStrcpy(configFilename,"*.cfg");
const char * error = myScene.readConfigFile(configFilename);
if (error)
myScene.setExportError(error);
//-----------------------------------------------
// Error?
if (myScene.isExportError())
{
Alert(myScene.getExportError());
cheapSemaphore = 0;
return 1;
}
//-----------------------------------------------
// create dump file...
myScene.startDump(filename,maxFile);
//-----------------------------------------------
// read config file again so we have a record of it...
myScene.readConfigFile(configFilename);
//-----------------------------------------------
// Error?
if (myScene.isExportError())
{
Alert(myScene.getExportError());
cheapSemaphore = 0;
return 1;
}
//-----------------------------------------------
// tweak some parameters depending on export type...
if (dStrstr((const char*)filename,".dsq"))
{
// we're exporting sequences, so turn this on
if (!enableSequences)
{
enableSequences = true;
SceneEnumProc::printDump(PDAlways,"\r\nEnabling \"Param::SequenceExport\" (doing *.dsq export).\r\n");
}
SceneEnumProc::exportType = 's'; // sequence
}
if (dStrstr((const char*)filename,".txt"))
{
// doing a text file dump -- export everything
if (!enableSequences)
{
enableSequences = true;
SceneEnumProc::printDump(PDAlways,"\r\nEnabling \"Param::SequenceExport\" (doing *.txt export).\r\n");
}
if (transformCollapse)
{
transformCollapse = false;
SceneEnumProc::printDump(PDAlways,"\r\nDisabling \"Param::CollapseTransforms\" (doing *.txt export).\r\n");
}
SceneEnumProc::exportType = 't'; // text
}
if (dStrstr((const char*)filename,".dts"))
SceneEnumProc::exportType = 'w'; // whole shape
//-----------------------------------------------
// Error?
if (myScene.isExportError())
{
Alert(myScene.getExportError());
cheapSemaphore = 0;
return 1;
}
//-----------------------------------------------
// Get the nodes we're interested in!
// We also do some checking to make sure everything
// we need is present ... if something is missing,
// an error will be returned.
myScene.enumScene(ei->theScene);
//-----------------------------------------------
// Error?
if (myScene.isExportError())
{
Alert(myScene.getExportError());
cheapSemaphore = 0;
return 1;
}
//-----------------------------------------------
// Any useful nodes?
if (myScene.isEmpty())
{
Alert("No data to export");
cheapSemaphore = 0;
return 1;
}
//-----------------------------------------------
// make sure we get rid of target file before opening it
// otherwise, it'll never shrink
File zap;
zap.open(filename,File::Write);
zap.close();
//-----------------------------------------------
// open a file to save the exported shape to:
FileStream file;
file.open(filename,FileStream::Write);
if( file.getStatus() != Stream::Ok )
{
Alert(IDS_TH_CANTCREATE);
cheapSemaphore = 0;
return(0);
}
//-----------------------------------------------
// actually do the export:
myScene.processScene();
//-----------------------------------------------
// Error?
if (myScene.isExportError())
{
Alert(myScene.getExportError());
file.close();
cheapSemaphore = 0;
return 1;
}
//-----------------------------------------------
// now save the shape
TSShape * pShape = myScene.getShape();
if (dStrstr((const char*)filename,".dts"))
pShape->write(&file);
else if (dStrstr((const char*)filename,".dsq"))
pShape->exportSequences(&file);
else if (dStrstr((const char*)filename,".txt"))
myScene.exportTextFile(&file);
//-----------------------------------------------
// close the file and report any problems:
if( file.getStatus() != Stream::Ok )
{
Alert(IDS_TH_WRITEERROR);
file.close();
remove(filename);
cheapSemaphore = 0;
return(0);
}
else
file.close();
//-----------------------------------------------
// write the shape out to dump file
if (PDShapeHierarchy & dumpMask && dStrstr((const char*)filename,".dts"))
{
// write out the structure of the newly created shape
// but read it from the file first...
if (!SceneEnumProc::dumpShape(filename))
{
Alert(avar("Error opening created file \"%s\".",filename));
file.close();
cheapSemaphore = 0;
return 1;
}
}
cheapSemaphore = 0;
return 1;
}
S32 _Exporter::DoExport(const TCHAR *filename,ExpInterface *ei,Interface *gi, S32 supressUI, DWORD)
{
gSupressUI = supressUI;
// so we can go back to defaults when we're done
saveConfig();
S32 status;
status = _dts_save(filename, ei, gi);
restoreConfig();
if(status == 0)
return 1; // Dialog cancelled
if(status < 0)
return 0; // Real, honest-to-goodness error
return status;
}

748
tools/max2dtsExporter/maxUtil.cc Executable file
View File

@ -0,0 +1,748 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "max2dtsExporter/maxUtil.h"
#pragma pack(push,8)
#include <decomp.h>
#include <dummy.h>
#include <ISkin.h>
#include <modstack.h>
#pragma pack(pop)
#include "Core/tVector.h"
// Special for biped stuff -- class id in no header, so we just def it here
// NOTE/WARNING: This could change with Character Studio updates
#define BipedObjectClassID Class_ID(37157,0)
Point3F & Point3ToPoint3F(Point3 & p3, Point3F & p3f)
{
p3f.x = p3.x;
p3f.y = p3.y;
p3f.z = p3.z;
return p3f;
}
void convertToTransform(Matrix3 & mat, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
AffineParts parts;
decomp_affine(mat,&parts);
Point3ToPoint3F(parts.t,trans);
rot.set(QuatF(parts.q[0],parts.q[1],parts.q[2],parts.q[3]));
srot.set(QuatF(parts.u[0],parts.u[1],parts.u[2],parts.u[3]));
Point3ToPoint3F(parts.k,scale);
}
MatrixF & convertToMatrixF(Matrix3 & mat3,MatrixF &matf)
{
matf.identity();
Point3F x,y,z,p;
Point3 x3 = mat3 * Point3(1.0f,0.0f,0.0f);
Point3 y3 = mat3 * Point3(0.0f,1.0f,0.0f);
Point3 z3 = mat3 * Point3(0.0f,0.0f,1.0f);
Point3 p3 = mat3 * Point3(0.0f,0.0f,0.0f);
x = Point3ToPoint3F(x3,x);
y = Point3ToPoint3F(y3,y);
z = Point3ToPoint3F(z3,z);
p = Point3ToPoint3F(p3,p);
x -= p;
y -= p;
z -= p;
matf.setColumn(0,x);
matf.setColumn(1,y);
matf.setColumn(2,z);
matf.setColumn(3,p);
return matf;
}
void getNodeTransform(INode *pNode, S32 time, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
Matrix3 nodeMat = pNode->GetNodeTM( time );
convertToTransform(nodeMat,rot,trans,srot,scale);
}
TriObject * getTriObject( INode *pNode, S32 time, S32 multiResVerts, bool & deleteIt)
{
TriObject * tri = NULL;
IParamBlock * paramBlock = NULL;
// if we're a multiRes object, dial us down
if (multiResVerts>0)
{
Animatable * obj = (Animatable*)pNode->GetObjectRef();
for (S32 i=0; i<obj->NumSubs(); i++)
{
if (!dStrcmp(obj->SubAnimName(i),"MultiRes"))
{
paramBlock = (IParamBlock*)obj->SubAnim(i)->SubAnim(0);
Interval range = pNode->GetTimeRange(TIMERANGE_ALL|TIMERANGE_CHILDNODES|TIMERANGE_CHILDANIMS);
paramBlock->SetValue(0,time,multiResVerts);
break;
}
}
}
// if the object can't convert to a tri-mesh, eval world state to
// get an object that can:
const ObjectState &os = pNode->EvalWorldState(time);
if ( os.obj->CanConvertToType(triObjectClassID) )
tri = (TriObject *)os.obj->ConvertToType( time, triObjectClassID );
deleteIt = (tri && (tri != os.obj));
return tri;
}
F32 findVolume(INode * pNode, S32 & polyCount)
{
bool deleteTri;
TriObject * tri = getTriObject(pNode,DEFAULT_TIME,-1,deleteTri);
if (!tri)
{
polyCount=0;
return 0.0f;
}
Mesh & mesh = tri->mesh;
polyCount = mesh.getNumFaces();
Box3F bounds;
bounds.min.set( 10E30f, 10E30f, 10E30f);
bounds.max.set(-10E30f,-10E30f,-10E30f);
for (S32 i=0; i<mesh.numVerts; i++)
{
Point3F v = Point3ToPoint3F(mesh.verts[i],v);
bounds.min.setMin(v);
bounds.max.setMax(v);
}
if (deleteTri)
delete tri;
return (bounds.max.x-bounds.min.x)*(bounds.max.y-bounds.min.y)*(bounds.max.z-bounds.min.z);
}
void findSkinData(INode * pNode, ISkin **skin, ISkinContextData ** skinData)
{
// till proven otherwise...
*skin = NULL;
*skinData = NULL;
// Get object from node. Abort if no object.
Object* obj = pNode->GetObjectRef();
if (!obj)
return;
Modifier * mod = NULL;
// Is derived object ?
S32 i;
while (obj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
{
IDerivedObject* dobj = (IDerivedObject*) obj;
// Iterate over all entries of the modifier stack.
for (i=0;i<dobj->NumModifiers();i++)
if (dobj->GetModifier(i)->ClassID() == SKIN_CLASSID)
break;
if (i!=dobj->NumModifiers())
{
mod = dobj->GetModifier(i);
break;
}
obj = dobj->GetObjRef();
}
if (!mod)
return;
*skin = (ISkin*) mod->GetInterface(I_SKIN);
if (!*skin)
return;
*skinData = (*skin)->GetContextInterface(pNode);
if (!*skinData)
*skin=NULL; // return both or neither
}
bool hasSkin(INode * pNode)
{
ISkin * skin;
ISkinContextData * skinData;
findSkinData(pNode,&skin,&skinData);
return skin != NULL;
}
bool hasMesh(INode * pNode)
{
ObjectState os = pNode->EvalWorldState(0);
return( os.obj->CanConvertToType(triObjectClassID) && !(os.obj->ClassID() == BipedObjectClassID) );
}
void zapScale(Matrix3 & mat)
{
AffineParts parts;
decomp_affine(mat,&parts);
// now put the matrix back together again without the scale:
// mat = mat.rot * mat.pos
mat.IdentityMatrix();
mat.PreTranslate(parts.t);
PreRotateMatrix(mat,parts.q);
}
Matrix3 & getLocalNodeMatrix(INode *pNode, INode *parent, S32 time, Matrix3 & matrix, AffineParts & a10, AffineParts & a20)
{
// Here's the story: the default transforms have no scale. In order to account for scale, the
// scale at the default time is folded into the object offset (which is multiplied into the points
// before exporting). Because of this, the local transform at a given time must take into account
// the scale of the parent and child node at time 0 in addition to the current time. In particular,
// the world transform at a given time is WT(time) = inverse(Tscale(0)) * T(time)
// Note: above formula is max style. Torque style would have the order reveresed. :(
// in order to avoid recomputing matrix at default time over and over, we assume that the first request
// for the matrix will be at the default time, and thereafter, we will pass that matrix in and reuse it...
Matrix3 m1 = pNode->GetNodeTM(time);
Matrix3 m2 = parent ? parent->GetNodeTM(time) : Matrix3(true);
if (time==DEFAULT_TIME)
{
decomp_affine(m1,&a10);
decomp_affine(m2,&a20);
}
// build the inverse scale matrices
Matrix3 stretchRot10,stretchRot20;
Point3 sfactor10, sfactor20;
Matrix3 invScale10, invScale20;
a10.u.MakeMatrix(stretchRot10);
a20.u.MakeMatrix(stretchRot20);
sfactor10 = Point3(a10.f/a10.k.x,a10.f/a10.k.y,a10.f/a10.k.z);
sfactor20 = Point3(a20.f/a20.k.x,a20.f/a20.k.y,a20.f/a20.k.z);
invScale10 = Inverse(stretchRot10) * ScaleMatrix(sfactor10) * stretchRot10;
invScale20 = Inverse(stretchRot20) * ScaleMatrix(sfactor20) * stretchRot20;
// build world transforms
m1 = invScale10 * m1;
m2 = invScale20 * m2;
// build local transform
matrix = m1 * Inverse(m2);
return matrix;
}
void getLocalNodeTransform(INode *pNode, INode *parent, AffineParts & child0, AffineParts & parent0, S32 time, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
Matrix3 local;
getLocalNodeMatrix(pNode,parent,time,local,parent0,child0);
convertToTransform(local,rot,trans,srot,scale);
}
void getBlendNodeTransform(INode *pNode, INode *parent, AffineParts & child0, AffineParts & parent0, S32 time, S32 referenceTime, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
Matrix3 m1;
Matrix3 m2;
getLocalNodeMatrix(pNode, parent, referenceTime, m1, child0, parent0);
getLocalNodeMatrix(pNode, parent, time, m2, child0, parent0);
m1 = Inverse(m1);
m2 *= m1;
convertToTransform(m2, rot,trans,srot,scale);
}
void computeObjectOffset(INode * pNode, Matrix3 & mat)
{
// compute the object offset transform...
// this will be applied to all the verts of the meshes...
// we grab this straight from the node, but we also
// multiply in any scaling on the node transform because
// the scaling will be stripped out when making a tsshape
Matrix3 nodeMat = pNode->GetNodeTM(DEFAULT_TIME);
zapScale(nodeMat);
nodeMat = Inverse(nodeMat);
mat = pNode->GetObjTMAfterWSM(DEFAULT_TIME);
mat *= nodeMat;
}
void getDeltaTransform(INode * pNode, S32 time1, S32 time2, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
Matrix3 m1 = pNode->GetNodeTM(time2);
Matrix3 m2 = pNode->GetNodeTM(time1);
m2 = Inverse(m2);
m1 *= m2;
convertToTransform(m1,rot,trans,srot,scale);
}
bool isVis(INode * pNode, S32 time)
{
return pNode->GetVisibility(time) > 0.0000001f;
}
F32 getVisValue(INode * pNode, S32 time)
{
return pNode->GetVisibility(time);
}
bool animatesVis(INode * pNode, const Interval & range, bool & error)
{
// running error...exit if already encountered an error
if (error)
return false;
F32 startVis = getVisValue(pNode,range.Start());
for (S32 i=range.Start()+1; i<=range.End(); i++)
if (mFabs(startVis-getVisValue(pNode,i))>0.01f)
return true;
return false;
}
bool animatesFrame(INode * pNode, const Interval & range, bool & error)
{
// running error...exit if already encountered an error
if (error)
return false;
bool deleteIt;
TriObject * tri = getTriObject(pNode,range.Start(),-1,deleteIt);
if (!tri)
{
error = true;
return false;
}
Interval ivalid;
ivalid = tri->ChannelValidity(range.Start(), GEOM_CHAN_NUM);
if (deleteIt)
tri->DeleteMe();
return (ivalid.Start() > range.Start() || ivalid.End() < range.End());
}
bool animatesMatFrame(INode * pNode, const Interval & range, bool & error)
{
// running error...exit if already encountered an error
if (error)
return false;
bool deleteIt;
TriObject * tri = getTriObject(pNode,range.Start(),-1,deleteIt);
if (!tri)
{
error = true;
return false;
}
Interval ivalid;
ivalid = tri->ChannelValidity(range.Start(), TEXMAP_CHAN_NUM);
if (deleteIt)
tri->DeleteMe();
return (ivalid.Start() > range.Start() || ivalid.End() < range.End());
}
bool isdigit(char ch)
{
return (ch>='0' && ch<='9');
}
//----------------------------------------------------------------------------
// If 's' ends in a number, chopNum returns
// a pointer to first digit in this number.
// If not, then returns pointer to '\0'
// at end of name or first of any trailing
// spaces.
char * chopNum(char * s)
{
if (s==NULL)
return NULL;
char * p = s + dStrlen(s);
if (p==s)
return s;
p--;
// trim spaces from the end
while (p!=s && *p==' ')
p--;
// back up until we reach a non-digit
// gotta be better way than this...
if (isdigit(*p))
do
{
if (p--==s)
return p+1;
} while (isdigit(*p));
// allow negative numbers
if (*p=='-')
p--;
// trim spaces separating name and number
while (*p==' ')
{
p--;
if (p==s)
return p;
}
// return first space if there was one,
// o.w. return first digit
return p+1;
}
//----------------------------------------------------------------------------
// separates nodes names into base name
// and detail number
char * chopTrailingNumber(const char * fullName, S32 & size)
{
if (!fullName)
{
size = -1;
return NULL;
}
char * buffer = dStrdup(fullName);
char * p = chopNum(buffer);
if (*p=='\0')
{
size = -1;
return buffer;
}
size = dAtoi(p);
*p='\0'; // terminate string
return buffer;
}
//----------------------------------------------------------------------------
// returns a list of detail levels of this shape...grabs meshes off shape
// and finds meshes on root level with same name but different trailing
// detail number (i.e., head32 on shape, head16 at root, leads to detail
// levels 32 and 16).
void findDetails(INode * base, INode * root, Vector<S32> & details)
{
S32 i,j,k;
// run through all the descendents of this node
// and look for numbers indicating detail levels
Vector<INode*> nodeStack;
nodeStack.push_back(base);
while (!nodeStack.empty())
{
INode * node = nodeStack.last();
nodeStack.pop_back();
if (hasMesh(node))
{
// have a mesh...a little obscure below:
// add # of this mesh + any mesh w/ same
// name on the root level to details vector
S32 detailSize;
char * baseName = chopTrailingNumber(node->GetName(),detailSize);
INode * meshNode = node;
for (j=-1;j<root->NumberOfChildren();j++)
{
if (j>=0)
meshNode = root->GetChildNode(j);
if (!hasMesh(meshNode))
continue;
char * meshName = chopTrailingNumber(meshNode->GetName(),detailSize);
if (!dStricmp(baseName,meshName))
{
for (k=0; k<details.size(); k++)
if (detailSize==details[k])
break;
if (k==details.size())
details.push_back(detailSize);
}
dFree(meshName);
}
dFree(baseName);
}
for (j=0; j<node->NumberOfChildren();j++)
nodeStack.push_back(node->GetChildNode(j));
}
}
//----------------------------------------------------------------------------
// find selected subtrees (nodes on root level only) and embed in subtree of
// the style the exporter looks for:
// Root
// |
// |-base
// |-start
// | |
// | |-<selected node 1>
// | | .
// | | .
// | |-<selected node N>
// |
// |-detail 1
// | .
// | .
// |-detail N
void embedSubtree(Interface * ip)
{
S32 i,j,k;
// make dummy node named baseName
TSTR baseName("base");
ip->MakeNameUnique(baseName);
INode * base = ip->CreateObjectNode(new DummyObject);
base->SetName(baseName);
// make dummy node named startName
TSTR startName("start");
ip->MakeNameUnique(startName);
INode * start = ip->CreateObjectNode(new DummyObject);
start->SetName(startName);
// link later to former
base->AttachChild(start);
Vector<S32> details;
// loop through selection set, link to start (only if child of root)
S32 count = ip->GetSelNodeCount();
INode * root = ip->GetRootNode();
for (i=0; i<count; i++)
{
INode * node = ip->GetSelNode(i);
if (!node->GetParentNode()->IsRootNode())
continue;
start->AttachChild(node);
findDetails(node,root,details);
}
// now create detail markers
for (i=0; i<details.size();i++)
{
char detailName[20];
INode * detailNode = ip->CreateObjectNode(new DummyObject);
dSprintf(detailName,20,"detail%i",details[i]);
detailNode->SetName(detailName);
base->AttachChild(detailNode);
}
}
//----------------------------------------------------------------------------
void registerDetails(Interface * ip)
{
S32 i,j,k;
// loop through selection set, only care about nodes off root
S32 count = ip->GetSelNodeCount();
INode * root = ip->GetRootNode();
for (i=0; i<count; i++)
{
INode * node = ip->GetSelNode(i);
if (!node->GetParentNode()->IsRootNode())
continue;
Vector<S32> details;
// search branches of tree for meshes and find their details
for (j=0; j<node->NumberOfChildren(); j++)
{
INode * child = node->GetChildNode(j);
if (child->NumberOfChildren()>0)
findDetails(child,root,details);
}
// go through children of base node and cull detail numbers
// that don't need to be added (because detail marker already
// present)
for (j=0; j<node->NumberOfChildren(); j++)
{
INode * child = node->GetChildNode(j);
if (child->NumberOfChildren()==0)
{
// look for #
S32 detailSize;
char * baseName = chopTrailingNumber(child->GetName(),detailSize);
dFree(baseName);
for (k=0;k<details.size();k++)
{
if (details[k]==detailSize)
{
// found it
details.erase(k);
break;
}
}
}
}
// items left in details list are unique -- add markers
for (j=0;j<details.size();j++)
{
char detailName[20];
INode * detailNode = ip->CreateObjectNode(new DummyObject);
dSprintf(detailName,20,"detail%i",details[j]);
detailNode->SetName(detailName);
node->AttachChild(detailNode);
}
}
}
//----------------------------------------------------------------------------
void renumberNodes(Interface * ip, S32 newSize)
{
S32 i, count = ip->GetSelNodeCount();
char newName[128];
for (i=0; i<count; i++)
{
INode * node = ip->GetSelNode(i);
S32 oldSize;
char * baseName = chopTrailingNumber(node->GetName(),oldSize);
dSprintf(newName,128,"%s%i",baseName,newSize);
node->SetName(newName);
dFree(baseName);
}
}
// this version of doDot used by m_pointInPoly
inline float doDot(F32 v1x, F32 v1y, F32 v2x, F32 v2y, F32 px, F32 py)
{
return (v1x-px) * (v1y-v2y) +
(v1y-py) * (v2x-v1x);
}
//------------------------------------------------------------------------------------
// pointInPoly returns true if point is inside poly defined by verts on plane w/
// normal "normal" -- based on m_pointInTriangle.
//------------------------------------------------------------------------------------
bool pointInPoly(const Point3F & point,
const Point3F & normal,
const Point3F * verts,
U32 n)
{
F32 thisDot, lastDot=0;
U32 i;
// we can ignore one of the dimensions because all points are on the same plane...
if (mFabs(normal.y)>mFabs(normal.x)&&mFabs(normal.y)>mFabs(normal.z))
{
// drop y coord
thisDot = doDot(verts[n-1].x,verts[n-1].z,verts[0].x,verts[0].z,point.x,point.z);
if (thisDot*lastDot<0)
return false;
lastDot = thisDot;
for (i=0;i<n-1;i++)
{
thisDot = doDot(verts[i].x,verts[i].z,verts[i+1].x,verts[i+1].z,point.x,point.z);
if (thisDot*lastDot<0)
return false; // different sign, point outside one of the edges
lastDot = thisDot;
}
}
else if (mFabs(normal.x)>mFabs(normal.y)&&mFabs(normal.x)>mFabs(normal.z))
{
// drop x coord
thisDot = doDot(verts[n-1].y,verts[n-1].z,verts[0].y,verts[0].z,point.y,point.z);
if (thisDot*lastDot<0)
return false;
lastDot = thisDot;
for (i=0;i<n-1;i++)
{
thisDot = doDot(verts[i].y,verts[i].z,verts[i+1].y,verts[i+1].z,point.y,point.z);
if (thisDot*lastDot<0)
return false; // different sign, point outside one of the edges
lastDot = thisDot;
}
}
else
{
// drop z coord
thisDot = doDot(verts[n-1].x,verts[n-1].y,verts[0].x,verts[0].y,point.x,point.y);
if (thisDot*lastDot<0)
return false;
lastDot = thisDot;
for (i=0;i<n-1;i++)
{
thisDot = doDot(verts[i].x,verts[i].y,verts[i+1].x,verts[i+1].y,point.x,point.y);
if (thisDot*lastDot<0)
return false; // different sign, point outside one of the edges
lastDot = thisDot;
}
}
return true;
}
// deal with some unresolved externals...
#include "platform/event.h"
#include "core/bitStream.h"
void GamePostEvent(struct Event const &) {}
void GameHandleInfoPacket(struct NetAddress const *, BitStream *){}
void GameHandleDataPacket(S32, BitStream *){}
void GameConnectionAccepted(S32, BitStream *){}
void GameConnectionRejected(S32, BitStream *){}
void GameConnectionDisconnected(S32, BitStream *){}
void GameConnectionRequested(struct NetAddress const *, BitStream *){}
void GameConnectionEstablished(S32){}
void GameHandleNotify(S32,bool){}
void GameConnectionTimedOut(S32){}
void GameDeactivate(bool) {}
void GameReactivate() {}
S32 GameMain(S32,char const * *){ return 0; }
U32 getTickCount() { return 0; }
void (*terrMipBlit)(U16 *dest, U32 destStride, U32 squareSize, const U8 *sourcePtr, U32 sourceStep, U32 sourceRowAdd);
bool gEditingMission;
class SimGroup;
SimGroup * gDataBlockGroup;
SimGroup * gActionMapGroup;
SimGroup * gClientGroup;
SimGroup * gGuiGroup;
SimGroup * gGuiDataGroup;
SimGroup * gTCPGroup;
class SimSet;
SimSet * gActiveActionMapSet;
SimSet * gGhostAlwaysSet;
SimSet * gLightSet;
//------------------------------------------------------
// These routines aren't currently used, but could prove
// useful at a later time...
//------------------------------------------------------
/*
MatrixF & getNodeMatrix(INode *pNode, S32 time, MatrixF & matrix)
{
Matrix3 nodeMat = pNode->GetNodeTM( time );
convertToMatrixF(nodeMat,matrix);
return matrix;
}
MatrixF & getLocalNodeMatrix(INode *pNode, INode *parent, S32 time, MatrixF & matrix)
{
if (!parent)
return getNodeMatrix(pNode,time,matrix);
MatrixF m1,m2;
getNodeMatrix(pNode,time,m1);
getNodeMatrix(parent,time ,m2);
m2.inverse();
matrix.mul(m1,m2);
return matrix;
}
*/

67
tools/max2dtsExporter/maxUtil.h Executable file
View File

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _DYN_MAX_UTIL_H
#define _DYN_MAX_UTIL_H
#pragma pack(push,8)
#include <max.h>
#include <iparamb2.h>
#include <ISkin.h>
#include <decomp.h>
#pragma pack(pop)
#include "math/mMath.h"
#include "ts/tsTransform.h"
#define DEFAULT_TIME 0
extern Point3F & Point3ToPoint3F(Point3 & p3, Point3F & p3f);
extern MatrixF & convertToMatrixF(Matrix3 & mat3,MatrixF & matf);
extern void zapScale(Matrix3 & mat);
extern TriObject * getTriObject( INode *pNode, S32 time, S32 multiResVerts, bool & deleteIt);
extern void getLocalNodeTransform(INode *pNode, INode *parent, AffineParts & child0, AffineParts & parent0, S32 time, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale);
extern void getBlendNodeTransform(INode *pNode, INode *parent, AffineParts & child0, AffineParts & parent0, S32 time, S32 referenceTime, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale);
extern Matrix3 & getLocalNodeMatrix(INode *pNode, INode *parent, S32 time, Matrix3 & matrix, AffineParts & child0, AffineParts & parent0);
extern void computeObjectOffset(INode * pNode, Matrix3 & mat);
extern F32 findVolume(INode * pNode, S32 & polyCount);
extern void getDeltaTransform(INode * pNode, S32 time1, S32 time2, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale);
extern bool isVis(INode * pNode, S32 time);
extern F32 getVisValue(INode * pNode, S32 time);
extern bool animatesVis(INode * pNode, const Interval & range, bool & error);
extern bool animatesFrame(INode * pNode, const Interval & range, bool & error);
extern bool animatesMatFrame(INode * pNode, const Interval & range, bool & error);
extern void embedSubtree(Interface *);
extern void renumberNodes(Interface *, S32 newSize);
extern void registerDetails(Interface * ip);
extern char * chopTrailingNumber(const char *, S32 & size);
extern bool hasMesh(INode *pNode);
extern void findSkinData(INode * pNode, ISkin **skin, ISkinContextData ** skinData);
extern bool hasSkin(INode * pNode);
extern bool pointInPoly(const Point3F & point, const Point3F & normal, const Point3F * verts, U32 n);
#endif // _DYN_MAX_UTIL_H

BIN
tools/max2dtsExporter/readme.doc Executable file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More