added everything
This commit is contained in:
54
tools/Makefile
Executable file
54
tools/Makefile
Executable file
@@ -0,0 +1,54 @@
|
||||
|
||||
#--------------------------------------
|
||||
# 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 map2difPlus buildWad fonttool langc
|
||||
|
||||
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.buildWad.mk
|
||||
include targets.fonttool.mk
|
||||
include targets.langc.mk
|
||||
include targets.map2difPlus.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
|
||||
|
||||
508
tools/blender/ExportDTS.py
Executable file
508
tools/blender/ExportDTS.py
Executable 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."
|
||||
170
tools/buildWad/main.cc
Executable file
170
tools/buildWad/main.cc
Executable 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
305
tools/buildWad/palQuantization.cc
Executable 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
176
tools/buildWad/palQuantization.h
Executable 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
797
tools/buildWad/wadProcessor.cc
Executable 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
135
tools/buildWad/wadProcessor.h
Executable 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
|
||||
140
tools/fonttool/Getopt.cc
Executable file
140
tools/fonttool/Getopt.cc
Executable 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
115
tools/fonttool/Getopt.h
Executable 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
380
tools/fonttool/fonttool.cc
Executable 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
27
tools/fonttool/fonttool.h
Executable 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_
|
||||
140
tools/langc/Getopt.cc
Executable file
140
tools/langc/Getopt.cc
Executable 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
115
tools/langc/Getopt.h
Executable 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
194
tools/langc/langc.cc
Executable 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
19
tools/langc/langc.h
Executable 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
702
tools/langc/langcomp.cc
Executable 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
115
tools/langc/langcomp.h
Executable 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
140
tools/langc/tSimpleHash.h
Executable 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_
|
||||
|
||||
BIN
tools/map2dif.exe
Executable file
BIN
tools/map2dif.exe
Executable file
Binary file not shown.
1397
tools/map2dif/bspNode.cc
Executable file
1397
tools/map2dif/bspNode.cc
Executable file
File diff suppressed because it is too large
Load Diff
201
tools/map2dif/bspNode.h
Executable file
201
tools/map2dif/bspNode.h
Executable 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_
|
||||
|
||||
2479
tools/map2dif/createLightmaps.cc
Executable file
2479
tools/map2dif/createLightmaps.cc
Executable file
File diff suppressed because it is too large
Load Diff
269
tools/map2dif/createLightmaps.h
Executable file
269
tools/map2dif/createLightmaps.h
Executable file
@@ -0,0 +1,269 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
U32 sgLightingScale;
|
||||
|
||||
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
|
||||
640
tools/map2dif/csgBrush.cc
Executable file
640
tools/map2dif/csgBrush.cc
Executable file
@@ -0,0 +1,640 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
sgLightingScale = pCopy->sgLightingScale;
|
||||
sgSelfIllumination = pCopy->sgSelfIllumination;
|
||||
|
||||
brushId = pCopy->brushId;
|
||||
materialType = pCopy->materialType;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool CSGBrush::disambiguate()
|
||||
{
|
||||
AssertFatal(mIsAmbiguous == false, "error, already disambiguated?");
|
||||
|
||||
for (U32 i = 0; i < mPlanes.size(); i++) {
|
||||
for (U32 j = i + 1; j < mPlanes.size();) {
|
||||
// Compare i to j. if j == i, with different tex parameters, increment
|
||||
// ambiguous 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;
|
||||
}
|
||||
175
tools/map2dif/csgBrush.h
Executable file
175
tools/map2dif/csgBrush.h
Executable file
@@ -0,0 +1,175 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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),
|
||||
sgLightingScale(32.0f),
|
||||
sgSelfIllumination(0.0f, 0.0f, 0.0f),
|
||||
pNext( NULL )
|
||||
{ }
|
||||
|
||||
Vector<CSGPlane> mPlanes;
|
||||
Point3D mMinBound;
|
||||
Point3D mMaxBound;
|
||||
|
||||
bool mIsAmbiguous;
|
||||
BrushType mBrushType;
|
||||
U32 brushId;
|
||||
U32 materialType;
|
||||
|
||||
U32 sgLightingScale;
|
||||
ColorI sgSelfIllumination;
|
||||
|
||||
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_
|
||||
76
tools/map2dif/editFloorPlanRes.cc
Executable file
76
tools/map2dif/editFloorPlanRes.cc
Executable 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] ] );
|
||||
}
|
||||
}
|
||||
30
tools/map2dif/editFloorPlanRes.h
Executable file
30
tools/map2dif/editFloorPlanRes.h
Executable 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_
|
||||
2015
tools/map2dif/editGeometry.cc
Executable file
2015
tools/map2dif/editGeometry.cc
Executable file
File diff suppressed because it is too large
Load Diff
581
tools/map2dif/editGeometry.h
Executable file
581
tools/map2dif/editGeometry.h
Executable file
@@ -0,0 +1,581 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
U32 sgLightingScale;
|
||||
ColorI sgSelfIllumination;
|
||||
};
|
||||
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_
|
||||
83
tools/map2dif/editInteriorRes.cc
Executable file
83
tools/map2dif/editInteriorRes.cc
Executable 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
30
tools/map2dif/editInteriorRes.h
Executable 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_
|
||||
2129
tools/map2dif/entityTypes.cc
Executable file
2129
tools/map2dif/entityTypes.cc
Executable file
File diff suppressed because it is too large
Load Diff
635
tools/map2dif/entityTypes.h
Executable file
635
tools/map2dif/entityTypes.h
Executable file
@@ -0,0 +1,635 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
private:
|
||||
F32 mLumelScale;
|
||||
public:
|
||||
U32 sgGetLightingScale() {return 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 sgLightingScaleEntity : public EditGeometry::Entity
|
||||
{
|
||||
public:
|
||||
F32 sgLightingScale;
|
||||
U32 sgGetLightingScale() {return sgLightingScale;}
|
||||
|
||||
ColorI sgSelfIllumination;
|
||||
ColorI sgGetSelfIllumination() {return sgSelfIllumination;}
|
||||
|
||||
public:
|
||||
sgLightingScaleEntity()
|
||||
{
|
||||
sgLightingScale = 32.0;
|
||||
sgSelfIllumination = ColorI(0, 0, 0);
|
||||
}
|
||||
~sgLightingScaleEntity() {}
|
||||
|
||||
bool parseEntityDescription(Tokenizer* pToker);
|
||||
BrushType getBrushType() {return StructuralBrush;}
|
||||
bool isPointClass() const {return false;}
|
||||
const char* getName() const
|
||||
{
|
||||
static char bogoChar = '\0';
|
||||
return &bogoChar;
|
||||
}
|
||||
const Point3D& getOrigin() const
|
||||
{
|
||||
static Point3D bogoPoint(0, 0, 0);
|
||||
return bogoPoint;
|
||||
}
|
||||
static const char* getClassName() {return "sgLightingScaleEntity";}
|
||||
};
|
||||
|
||||
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_
|
||||
2394
tools/map2dif/exportGeometry.cc
Executable file
2394
tools/map2dif/exportGeometry.cc
Executable file
File diff suppressed because it is too large
Load Diff
498
tools/map2dif/lmapPacker.cc
Executable file
498
tools/map2dif/lmapPacker.cc
Executable file
@@ -0,0 +1,498 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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);
|
||||
|
||||
|
||||
// Better border color, so we can remove the border size! - John Kabus
|
||||
/*if (overflow == false)
|
||||
{
|
||||
pDst[0] = 0xFF;
|
||||
pDst[1] = 0x00;
|
||||
pDst[2] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
pDst[0] = 0x00;
|
||||
pDst[1] = 0xFF;
|
||||
pDst[2] = 0x00;
|
||||
}*/
|
||||
|
||||
pDst[0] = 63;
|
||||
pDst[1] = 63;
|
||||
pDst[2] = 63;
|
||||
}
|
||||
}
|
||||
|
||||
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
75
tools/map2dif/lmapPacker.h
Executable 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 2
|
||||
|
||||
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_
|
||||
570
tools/map2dif/main.cc
Executable file
570
tools/map2dif/main.cc
Executable file
@@ -0,0 +1,570 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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)
|
||||
{
|
||||
Interior::smFileVersion = 0;
|
||||
|
||||
// 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
102
tools/map2dif/morianBasics.h
Executable 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
19
tools/map2dif/morianGame.h
Executable 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
967
tools/map2dif/morianUtil.cc
Executable 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
78
tools/map2dif/morianUtil.h
Executable 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
165
tools/map2dif/navGraph.cc
Executable 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
189
tools/map2dif/tokenizer.cc
Executable 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
53
tools/map2dif/tokenizer.h
Executable 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_
|
||||
370
tools/map2dif/torque.fgd
Executable file
370
tools/map2dif/torque.fgd
Executable file
@@ -0,0 +1,370 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 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"
|
||||
]
|
||||
|
||||
@SolidClass = sgLightingScaleEntity : "Synapse Gaming Lighting Scale Entity"
|
||||
[
|
||||
light_geometry_scale(string) : "Lighting scale (must be a power of 2)" : "32.0"
|
||||
self_illumination_color(color255) : "Self-Illumination 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"
|
||||
]
|
||||
1416
tools/map2difPlus/bspNode.cc
Executable file
1416
tools/map2difPlus/bspNode.cc
Executable file
File diff suppressed because it is too large
Load Diff
201
tools/map2difPlus/bspNode.h
Executable file
201
tools/map2difPlus/bspNode.h
Executable 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 "map2difPlus/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_
|
||||
|
||||
545
tools/map2difPlus/convert.cc
Executable file
545
tools/map2difPlus/convert.cc
Executable file
@@ -0,0 +1,545 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This file contains all of the functions for converting an InteriorMap into an EditGeometry
|
||||
|
||||
#include "map2difPlus/convert.h"
|
||||
#include "dgl/gBitmap.h"
|
||||
|
||||
extern const char* gWadPath;
|
||||
extern GBitmap* loadBitmap(const char* file);
|
||||
|
||||
void CopyConvexToCSG(ConvexBrush* convex, CSGBrush* csg)
|
||||
{
|
||||
if (convex->mType == InteriorMapResource::Structural)
|
||||
csg->mBrushType = StructuralBrush;
|
||||
else if (convex->mType == InteriorMapResource::Detail)
|
||||
csg->mBrushType = DetailBrush;
|
||||
else if (convex->mType == InteriorMapResource::Portal)
|
||||
csg->mBrushType = PortalBrush;
|
||||
else if (convex->mType == InteriorMapResource::Collision)
|
||||
csg->mBrushType = CollisionBrush;
|
||||
else
|
||||
csg->mBrushType = StructuralBrush;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void loadTextures(InteriorMap* map)
|
||||
{
|
||||
// Insert the dummy textures
|
||||
gWorkingGeometry->insertTexture("NULL");
|
||||
gWorkingGeometry->insertTexture("ORIGIN");
|
||||
gWorkingGeometry->insertTexture("TRIGGER");
|
||||
//gWorkingGeometry->insertTexture("EMITTER");
|
||||
|
||||
// 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);
|
||||
|
||||
// Now find our mirror surfaces
|
||||
for (U32 i = 0; i < gWorkingGeometry->mEntities.size(); i++)
|
||||
{
|
||||
if (dynamic_cast<MirrorSurfaceEntity*>(gWorkingGeometry->mEntities[i]) != NULL)
|
||||
{
|
||||
MirrorSurfaceEntity* entity = static_cast<MirrorSurfaceEntity*>(gWorkingGeometry->mEntities[i]);
|
||||
|
||||
entity->markSurface(gWorkingGeometry->mStructuralBrushes, gWorkingGeometry->mDetailBrushes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getBrushesForEntity(InteriorMap* map, InteriorMapResource::Entity* ent, EditGeometry::Entity* pEntity)
|
||||
{
|
||||
for (U32 i = 0; i < map->mInteriorRes->mBrushes.size(); i++)
|
||||
{
|
||||
CSGBrush* pBrush;
|
||||
|
||||
// Only load good mInteriorRes->mBrushes
|
||||
if (map->mInteriorRes->mBrushes[i]->mStatus != ConvexBrush::Good)
|
||||
continue;
|
||||
|
||||
if (map->mInteriorRes->mBrushes[i]->mOwner == ent)
|
||||
{
|
||||
pEntity->mBrushes.push_back(gBrushArena.allocateBrush());
|
||||
pBrush = pEntity->mBrushes.last();
|
||||
pBrush->mBrushType = StructuralBrush;
|
||||
|
||||
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;
|
||||
else if (dStricmp(ent->classname, DoorEntity::getClassName()) == 0)
|
||||
pEntity = new DoorEntity;
|
||||
else if (dStricmp(ent->classname, sgLightingScaleEntity::getClassName()) == 0)
|
||||
pEntity = new sgLightingScaleEntity;
|
||||
|
||||
// Path classes
|
||||
else if(dStricmp(ent->classname, PathNodeEntity::getClassName()) == 0)
|
||||
pEntity = new PathNodeEntity;
|
||||
|
||||
// Trigger Classes
|
||||
else if (dStricmp(ent->classname, TriggerEntity::getClassName()) == 0)
|
||||
pEntity = new TriggerEntity;
|
||||
|
||||
// Game Classes
|
||||
else
|
||||
pEntity = new GameEntity(ent->classname);
|
||||
|
||||
// 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);
|
||||
|
||||
// Handle entities that have brushes associated with them
|
||||
if (dStricmp(ent->classname, TriggerEntity::getClassName()) == 0)
|
||||
{
|
||||
// First snag the brushes for this entity
|
||||
getBrushesForEntity(map, ent, pEntity);
|
||||
|
||||
TriggerEntity* trigger = static_cast<TriggerEntity*>(pEntity);
|
||||
trigger->generateTrigger();
|
||||
}
|
||||
|
||||
if (dStricmp(ent->classname, DoorEntity::getClassName()) == 0)
|
||||
{
|
||||
// First snag the brushes for this entity
|
||||
getBrushesForEntity(map, ent, pEntity);
|
||||
|
||||
DoorEntity* door = static_cast<DoorEntity*>(pEntity);
|
||||
door->grabOrigin();
|
||||
}
|
||||
|
||||
if (dStricmp(ent->classname, sgLightingScaleEntity::getClassName()) == 0)
|
||||
{
|
||||
// First snag the brushes for this entity
|
||||
getBrushesForEntity(map, ent, pEntity);
|
||||
|
||||
sgLightingScaleEntity* lightScaleEnt = static_cast<sgLightingScaleEntity*>(pEntity);
|
||||
|
||||
for (U32 j = 0; j < lightScaleEnt->mBrushes.size(); j++)
|
||||
{
|
||||
CSGBrush* brush = lightScaleEnt->mBrushes[j];
|
||||
|
||||
brush->sgLightingScale = lightScaleEnt->sgGetLightingScale();
|
||||
brush->sgSelfIllumination = lightScaleEnt->sgGetSelfIllumination();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
tools/map2difPlus/convert.h
Executable file
22
tools/map2difPlus/convert.h
Executable file
@@ -0,0 +1,22 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONVERT_H_
|
||||
#define _CONVERT_H_
|
||||
|
||||
#include "interior/interiorMap.h"
|
||||
#include "map2difPlus/csgBrush.h"
|
||||
#include "map2difPlus/morianBasics.h"
|
||||
#include "map2difPlus/editGeometry.h"
|
||||
#include "map2difPlus/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_
|
||||
2478
tools/map2difPlus/createLightmaps.cc
Executable file
2478
tools/map2difPlus/createLightmaps.cc
Executable file
File diff suppressed because it is too large
Load Diff
269
tools/map2difPlus/createLightmaps.h
Executable file
269
tools/map2difPlus/createLightmaps.h
Executable file
@@ -0,0 +1,269 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _inc_createLightMaps
|
||||
#define _inc_createLightMaps
|
||||
|
||||
#ifndef _ENTITYTYPES_H_
|
||||
#include "map2difPlus/entityTypes.h"
|
||||
#endif
|
||||
#ifndef _EDITGEOMETRY_H_
|
||||
#include "map2difPlus/editGeometry.h"
|
||||
#endif
|
||||
#ifndef _CSGBRUSH_H_
|
||||
#include "map2difPlus/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;
|
||||
|
||||
U32 sgLightingScale;
|
||||
|
||||
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
|
||||
536
tools/map2difPlus/csgBrush.cc
Executable file
536
tools/map2difPlus/csgBrush.cc
Executable file
@@ -0,0 +1,536 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
//
|
||||
// Copyright (c) 2003 GarageGames.Com
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/gBitmap.h"
|
||||
#include "map2difPlus/csgBrush.h"
|
||||
#include "core/tokenizer.h"
|
||||
|
||||
extern int gQuakeVersion;
|
||||
|
||||
// For the moment these exist in InteriorMap.cc
|
||||
// MDFFIX: clean this up
|
||||
extern F32 baseaxis[18][3];
|
||||
extern void textureAxisFromPlane(const VectorF& normal, F32* xv, F32* yv);
|
||||
extern void quakeTextureVecs(const VectorF& normal, F32 offsetX, F32 offsetY, F32 rotate, F32 scaleX, F32 scaleY, PlaneF* u, PlaneF *v);
|
||||
|
||||
bool parseBrush (CSGBrush& rBrush, Tokenizer* pToker, EditGeometry& geom)
|
||||
{
|
||||
while (pToker->getToken()[0] == '(') {
|
||||
// Enter the plane...
|
||||
F64 points[3][3];
|
||||
for (S32 i = 0; i < 3; i++) {
|
||||
if (pToker->getToken()[0] != '(')
|
||||
goto EntityBrushlistError;
|
||||
|
||||
for (S32 j = 0; j < 3; j++) {
|
||||
pToker->advanceToken(false);
|
||||
points[i][j] = dAtof(pToker->getToken());
|
||||
}
|
||||
|
||||
pToker->advanceToken(false);
|
||||
if (pToker->getToken()[0] != ')')
|
||||
goto EntityBrushlistError;
|
||||
pToker->advanceToken(false);
|
||||
}
|
||||
|
||||
CSGPlane& rPlane = rBrush.constructBrushPlane(Point3D(points[0][0], points[0][1], points[0][2]),
|
||||
Point3D(points[1][0], points[1][1], points[1][2]),
|
||||
Point3D(points[2][0], points[2][1], points[2][2]));
|
||||
|
||||
// advanced already...
|
||||
if (pToker->tokenAvailable() == false)
|
||||
goto EntityBrushlistError;
|
||||
rPlane.pTextureName = geom.insertTexture(pToker->getToken());
|
||||
|
||||
// convert tex name to upper case
|
||||
char *str = (char *) rPlane.pTextureName;
|
||||
while( *str )
|
||||
{
|
||||
*str = dToupper( (const char) *str );
|
||||
str++;
|
||||
}
|
||||
|
||||
U32 bmIndex = geom.getMaterialIndex(rPlane.pTextureName);
|
||||
const GBitmap* pBitmap = geom.mTextures[bmIndex];
|
||||
|
||||
PlaneF tGenX;
|
||||
PlaneF tGenY;
|
||||
|
||||
if (gQuakeVersion == 2)
|
||||
{
|
||||
//hl ( 564 -396 -672 ) ( 564 -372 -672 ) ( 512 -372 -672 ) WALL_BLOCK01 [ 1 0 0 -213 ] [ 0 -1 0 -117 ] 0 1 1
|
||||
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
if (pToker->getToken()[0] != '[') goto EntityBrushlistError;
|
||||
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
tGenX.x = dAtof(pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
tGenX.y = dAtof(pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
tGenX.z = dAtof(pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
tGenX.d = dAtof(pToker->getToken());
|
||||
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
if (pToker->getToken()[0] != ']') goto EntityBrushlistError;
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
if (pToker->getToken()[0] != '[') goto EntityBrushlistError;
|
||||
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
tGenY.x = dAtof(pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
tGenY.y = dAtof(pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
tGenY.z = dAtof(pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
tGenY.d = dAtof(pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
if (pToker->getToken()[0] != ']') goto EntityBrushlistError;
|
||||
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
F32 scaleX = dAtof(pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
F32 scaleY = dAtof(pToker->getToken());
|
||||
|
||||
tGenX.x /= scaleX * F32(pBitmap->getWidth());
|
||||
tGenX.y /= scaleX * F32(pBitmap->getWidth());
|
||||
tGenX.z /= scaleX * F32(pBitmap->getWidth());
|
||||
tGenX.d /= F32(pBitmap->getWidth());
|
||||
|
||||
tGenY.x /= scaleY * F32(pBitmap->getHeight());
|
||||
tGenY.y /= scaleY * F32(pBitmap->getHeight());
|
||||
tGenY.z /= scaleY * F32(pBitmap->getHeight());
|
||||
tGenY.d /= F32(pBitmap->getHeight());
|
||||
}
|
||||
else if (gQuakeVersion == 3)
|
||||
{
|
||||
//q1 ( 88 -40 8 ) ( -56 -40 8 ) ( -56 -64 8 ) block10d 0 0 0 1.000000 1.000000
|
||||
//q2 ( 88 -40 8 ) ( -56 -40 8 ) ( -56 -64 8 ) block10d 0 0 0 1.000000 1.000000
|
||||
//q3 ( 88 -40 8 ) ( -56 -40 8 ) ( -56 -64 8 ) block10d 0 0 0 0.500000 0.500000 0 0 0
|
||||
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
F32 shiftU = dAtof (pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
F32 shiftV = dAtof (pToker->getToken());
|
||||
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
F32 rot = dAtof (pToker->getToken());
|
||||
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
F32 scaleX = dAtof(pToker->getToken());
|
||||
if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
F32 scaleY = dAtof(pToker->getToken());
|
||||
|
||||
// Skip last 3 tokens
|
||||
//if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
//if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
//if (pToker->advanceToken(false) == false) goto EntityBrushlistError;
|
||||
|
||||
pToker->advanceToken(false);
|
||||
pToker->advanceToken(false);
|
||||
pToker->advanceToken(false);
|
||||
|
||||
// Compute the normal
|
||||
VectorF normal;
|
||||
VectorF t1, t2;
|
||||
|
||||
t1 = VectorF (points[0][0], points[0][1], points[0][2]) - VectorF (points[1][0], points[1][1], points[1][2]);
|
||||
t2 = VectorF (points[2][0], points[2][1], points[2][2]) - VectorF (points[1][0], points[1][1], points[1][2]);
|
||||
mCross (t1, t2, normal);
|
||||
normal.normalize ();
|
||||
|
||||
quakeTextureVecs (normal, shiftU, shiftV, rot, scaleX, scaleY, &tGenX, &tGenY);
|
||||
|
||||
F32 width = F32(pBitmap->getWidth());
|
||||
F32 height = F32(pBitmap->getHeight());
|
||||
|
||||
tGenX.x /= F32(pBitmap->getWidth());
|
||||
tGenX.y /= F32(pBitmap->getWidth());
|
||||
tGenX.z /= F32(pBitmap->getWidth());
|
||||
tGenX.d /= F32(pBitmap->getWidth());
|
||||
|
||||
tGenY.x /= F32(pBitmap->getHeight());
|
||||
tGenY.y /= F32(pBitmap->getHeight());
|
||||
tGenY.z /= F32(pBitmap->getHeight());
|
||||
tGenY.d /= F32(pBitmap->getHeight());
|
||||
}
|
||||
else
|
||||
{
|
||||
goto EntityBrushlistError;
|
||||
}
|
||||
|
||||
// add it...
|
||||
bool found = false;
|
||||
for(U32 i = 0; !found && (i < geom.mTexGenEQs.size()); i++)
|
||||
if(!dMemcmp(&geom.mTexGenEQs[i].planeX, &tGenX, sizeof(PlaneF)) &&
|
||||
!dMemcmp(&geom.mTexGenEQs[i].planeY, &tGenY, sizeof(PlaneF)))
|
||||
{
|
||||
found = true;
|
||||
rPlane.texGenIndex = i;
|
||||
}
|
||||
|
||||
//
|
||||
if(!found)
|
||||
{
|
||||
geom.mTexGenEQs.increment();
|
||||
geom.mTexGenEQs.last().planeX = tGenX;
|
||||
geom.mTexGenEQs.last().planeY = tGenY;
|
||||
rPlane.texGenIndex = geom.mTexGenEQs.size() - 1;
|
||||
}
|
||||
|
||||
pToker->advanceToken(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
EntityBrushlistError:
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 CSGBrush::addPlane(CSGPlane plane)
|
||||
{
|
||||
for (U32 i = 0; i < mPlanes.size(); i++)
|
||||
{
|
||||
if (mPlanes[i].flags == plane.flags &&
|
||||
mPlanes[i].owningEntity == plane.owningEntity &&
|
||||
mPlanes[i].planeEQIndex == plane.planeEQIndex &&
|
||||
mPlanes[i].pTextureName == plane.pTextureName &&
|
||||
mPlanes[i].texGenIndex == plane.texGenIndex)
|
||||
return i;
|
||||
}
|
||||
|
||||
mPlanes.increment();
|
||||
mPlanes.last() = plane;
|
||||
return mPlanes.size() - 1;
|
||||
}
|
||||
|
||||
void CSGPlane::construct(const Point3D& Point1, const Point3D& Point2, const Point3D& Point3)
|
||||
{
|
||||
// |yi zi 1| |xi zi 1| |xi yi 1| |xi yi zi|
|
||||
// |yj zj 1| x + |xj zj 1| y + |xj yj 1| z = |xj yj zj|
|
||||
// |yk zk 1| |xk zk 1| |xk yk 1| |xk yk zk|
|
||||
//
|
||||
Point3D normal;
|
||||
F64 dist;
|
||||
|
||||
normal.x = Point1.y * Point2.z - Point1.y * Point3.z +
|
||||
Point3.y * Point1.z - Point2.y * Point1.z +
|
||||
Point2.y * Point3.z - Point3.y * Point2.z;
|
||||
normal.y = Point1.x * Point2.z - Point1.x * Point3.z +
|
||||
Point3.x * Point1.z - Point2.x * Point1.z +
|
||||
Point2.x * Point3.z - Point3.x * Point2.z;
|
||||
normal.z = Point1.x * Point2.y - Point1.x * Point3.y +
|
||||
Point3.x * Point1.y - Point2.x * Point1.y +
|
||||
Point2.x * Point3.y - Point3.x * Point2.y;
|
||||
dist = Point1.x * Point2.y * Point3.z - Point1.x * Point2.z * Point3.y +
|
||||
Point1.y * Point2.z * Point3.x - Point1.y * Point2.x * Point3.z +
|
||||
Point1.z * Point2.x * Point3.y - Point1.z * Point2.y * Point3.x;
|
||||
|
||||
normal.x = -normal.x;
|
||||
normal.z = -normal.z;
|
||||
|
||||
//
|
||||
planeEQIndex = gWorkingGeometry->insertPlaneEQ(normal, dist);
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Needs to insert new plane equation. parameter is positive amount
|
||||
// to extrude outward.
|
||||
void CSGPlane::extrude(F64 byAmount)
|
||||
{
|
||||
const PlaneEQ & eq = gWorkingGeometry->getPlaneEQ(planeEQIndex);
|
||||
planeEQIndex = gWorkingGeometry->insertPlaneEQ(eq.normal, eq.dist - byAmount);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool CSGPlane::createBaseWinding(const Vector<U32>& rPoints)
|
||||
{
|
||||
return ::createBaseWinding(rPoints, getNormal(), &winding);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Try a more accurate version of this.
|
||||
PlaneSide CSGPlane::sideCheckEpsilon(const Point3D& testPoint, F64 epsilon) const
|
||||
{
|
||||
F64 distance = distanceToPlane(testPoint);
|
||||
if (distance < - epsilon)
|
||||
return PlaneBack;
|
||||
else if (distance > epsilon)
|
||||
return PlaneFront;
|
||||
else
|
||||
return PlaneOn;
|
||||
}
|
||||
|
||||
// Need more info for asserts.
|
||||
const char* CSGPlane::sideCheckInfo(const Point3D & P, const char * msg, F64 E) const
|
||||
{
|
||||
F64 D = distanceToPlane(P);
|
||||
const char * txt = D < -E ? "BACK" : (D > E ? "FRONT" : "ON");
|
||||
return avar( "%s: Side=%s E=%lf D=%1.20lf P=(%lf,%lf,%lf)", msg, txt, E, D, P.x, P.y, P.z );
|
||||
}
|
||||
|
||||
// See if this plane lies along an edge of the supplied winding. Return the
|
||||
// points if so (they're needed), else return NULL for false.
|
||||
//
|
||||
// Maybe need a better point here.
|
||||
Point3D * CSGPlane::sharesEdgeWith(const Winding & W) const
|
||||
{
|
||||
static Point3D edgePts[2];
|
||||
for( U32 i = 0; i < W.numIndices; i++ )
|
||||
{
|
||||
edgePts[0] = gWorkingGeometry->getPoint(W.indices[i]);
|
||||
// if( sideCheckEpsilon( edgePts[0] ) == PlaneOn )
|
||||
if( whichSide( edgePts[0] ) == PlaneOn )
|
||||
{
|
||||
edgePts[1] = gWorkingGeometry->getPoint(W.indices[(i + 1) % W.numIndices]);
|
||||
// if( sideCheckEpsilon( edgePts[1] ) == PlaneOn )
|
||||
if( whichSide( edgePts[1] ) == PlaneOn )
|
||||
return edgePts;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Assuming this is a brush that has already been selfClip()d, make
|
||||
// and return a new brush that is extruded version of this
|
||||
// one (tho not clipped) in given direction.
|
||||
//
|
||||
// If doBoth, then extrude also in negative of direction.
|
||||
//
|
||||
// Adds additional planes on those edges which are boundaries with
|
||||
// respect to the direction axis. That is, those edges which will
|
||||
// will be on the outside of the shape perimeter when viewing
|
||||
// along the given direction axis, and so planes need to be added
|
||||
// to limit unwanted extrusion outside of the intended axis.
|
||||
//
|
||||
CSGBrush* CSGBrush::createExtruded(const Point3D & extrudeDir, bool doBoth) const
|
||||
{
|
||||
CSGBrush * newBrush = gBrushArena.allocateBrush();
|
||||
|
||||
newBrush->copyBrush( this );
|
||||
|
||||
for( U32 i = 0; i < mPlanes.size(); i++ )
|
||||
{
|
||||
// Get extrusion component along normal.
|
||||
const Point3D & curNormal = mPlanes[i].getNormal();
|
||||
F64 extrudeComponent = mDot( extrudeDir, curNormal );
|
||||
|
||||
if( mFabsD(extrudeComponent) > 0.001 )
|
||||
{
|
||||
const Winding & curWinding = mPlanes[i].winding;
|
||||
|
||||
// Look for planes that are opposite with respect to this
|
||||
// direction and which have a shared edge. We create 'clamping'
|
||||
// planes along such edges to avoid spikes.
|
||||
for( U32 j = i + 1; j < mPlanes.size(); j++ )
|
||||
{
|
||||
if( mDot(mPlanes[j].getNormal(),extrudeDir) * extrudeComponent < -0.0001 )
|
||||
{
|
||||
if( Point3D * edgePts = mPlanes[j].sharesEdgeWith(curWinding) )
|
||||
{
|
||||
Point3D other = edgePts[0] + extrudeDir;
|
||||
CSGPlane & rPlane = (extrudeComponent > 0)
|
||||
?
|
||||
newBrush->constructBrushPlane( other, edgePts[0], edgePts[1] )
|
||||
:
|
||||
newBrush->constructBrushPlane( edgePts[1], edgePts[0], other );
|
||||
|
||||
rPlane.pTextureName = mPlanes[0].pTextureName;
|
||||
rPlane.xShift = 0;
|
||||
rPlane.yShift = 0;
|
||||
rPlane.rotation = 0;
|
||||
rPlane.xScale = rPlane.yScale = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( extrudeComponent > 0 || doBoth )
|
||||
newBrush->mPlanes[i].extrude( mFabsD( extrudeComponent ) );
|
||||
}
|
||||
}
|
||||
|
||||
AssertFatal( newBrush->mPlanes.size() >= mPlanes.size(),
|
||||
"CSGBrush::createExtruded(): new brush shouldn't be smaller" );
|
||||
|
||||
return newBrush;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool CSGBrush::intersectPlanes(U32 i, U32 j, U32 k,
|
||||
Point3D* pOutput)
|
||||
{
|
||||
AssertFatal(i < mPlanes.size() && j < mPlanes.size() && k < mPlanes.size() &&
|
||||
i != j && i != k && j != k, "CSGBrush::intersectPlanes: bad plane indices");
|
||||
|
||||
CSGPlane& rPlane1 = mPlanes[i];
|
||||
CSGPlane& rPlane2 = mPlanes[j];
|
||||
CSGPlane& rPlane3 = mPlanes[k];
|
||||
|
||||
const PlaneEQ& rPlaneEQ1 = gWorkingGeometry->getPlaneEQ(rPlane1.planeEQIndex);
|
||||
const PlaneEQ& rPlaneEQ2 = gWorkingGeometry->getPlaneEQ(rPlane2.planeEQIndex);
|
||||
const PlaneEQ& rPlaneEQ3 = gWorkingGeometry->getPlaneEQ(rPlane3.planeEQIndex);
|
||||
|
||||
F64 bc = (rPlaneEQ2.normal.y * rPlaneEQ3.normal.z) - (rPlaneEQ3.normal.y * rPlaneEQ2.normal.z);
|
||||
F64 ac = (rPlaneEQ2.normal.x * rPlaneEQ3.normal.z) - (rPlaneEQ3.normal.x * rPlaneEQ2.normal.z);
|
||||
F64 ab = (rPlaneEQ2.normal.x * rPlaneEQ3.normal.y) - (rPlaneEQ3.normal.x * rPlaneEQ2.normal.y);
|
||||
F64 det = (rPlaneEQ1.normal.x * bc) - (rPlaneEQ1.normal.y * ac) + (rPlaneEQ1.normal.z * ab);
|
||||
|
||||
if (mFabs(det) < 1e-7) {
|
||||
// Parallel planes
|
||||
return false;
|
||||
}
|
||||
|
||||
F64 dc = (rPlaneEQ2.dist * rPlaneEQ3.normal.z) - (rPlaneEQ3.dist * rPlaneEQ2.normal.z);
|
||||
F64 db = (rPlaneEQ2.dist * rPlaneEQ3.normal.y) - (rPlaneEQ3.dist * rPlaneEQ2.normal.y);
|
||||
F64 ad = (rPlaneEQ3.dist * rPlaneEQ2.normal.x) - (rPlaneEQ2.dist * rPlaneEQ3.normal.x);
|
||||
F64 detInv = 1.0 / det;
|
||||
|
||||
pOutput->x = ((rPlaneEQ1.normal.y * dc) - (rPlaneEQ1.dist * bc) - (rPlaneEQ1.normal.z * db)) * detInv;
|
||||
pOutput->y = ((rPlaneEQ1.dist * ac) - (rPlaneEQ1.normal.x * dc) - (rPlaneEQ1.normal.z * ad)) * detInv;
|
||||
pOutput->z = ((rPlaneEQ1.normal.y * ad) + (rPlaneEQ1.normal.x * db) - (rPlaneEQ1.dist * ab)) * detInv;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool CSGBrush::selfClip()
|
||||
{
|
||||
// MDFFIX: Really hacky way of doing it for now but it will work
|
||||
ConvexBrush brush;
|
||||
|
||||
CopyCSGToConvex(this, &brush);
|
||||
|
||||
CopyConvexToCSG(&brush, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSGBrush::copyBrush(const CSGBrush* pCopy)
|
||||
{
|
||||
mPlanes = pCopy->mPlanes;
|
||||
mIsAmbiguous = pCopy->mIsAmbiguous;
|
||||
mBrushType = pCopy->mBrushType;
|
||||
mMinBound = pCopy->mMinBound;
|
||||
mMaxBound = pCopy->mMaxBound;
|
||||
|
||||
sgLightingScale = pCopy->sgLightingScale;
|
||||
sgSelfIllumination = pCopy->sgSelfIllumination;
|
||||
|
||||
brushId = pCopy->brushId;
|
||||
materialType = pCopy->materialType;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool CSGBrush::disambiguate()
|
||||
{
|
||||
AssertFatal(mIsAmbiguous == false, "error, already disambiguated?");
|
||||
|
||||
for (U32 i = 0; i < mPlanes.size(); i++) {
|
||||
for (U32 j = i + 1; j < mPlanes.size();) {
|
||||
// Compare i to j. if j == i, with different tex parameters, increment
|
||||
// ambiguous mInteriorRes->mBrushes (once only), and remove the plane.
|
||||
//
|
||||
CSGPlane& rPlane1 = mPlanes[i];
|
||||
CSGPlane& rPlane2 = mPlanes[j];
|
||||
if (rPlane1.planeEQIndex == rPlane2.planeEQIndex) {
|
||||
// Possible ambiguous plane pairing...
|
||||
//
|
||||
if (rPlane1.pTextureName != rPlane2.pTextureName ||
|
||||
rPlane1.xShift != rPlane2.xShift ||
|
||||
rPlane1.yShift != rPlane2.yShift ||
|
||||
rPlane1.rotation != rPlane2.rotation ||
|
||||
rPlane1.xScale != rPlane2.xScale ||
|
||||
rPlane1.yScale != rPlane2.yScale) {
|
||||
mIsAmbiguous = true;
|
||||
} else {
|
||||
// Same texture parameters, should be fine, just erase it...
|
||||
//
|
||||
}
|
||||
|
||||
mPlanes.erase(j);
|
||||
} else {
|
||||
// Plane is fine...
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mIsAmbiguous;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/*CSGPlane& CSGBrush::constructBrushPlane(const Point3I& rPoint1,
|
||||
const Point3I& rPoint2,
|
||||
const Point3I& rPoint3)
|
||||
{
|
||||
mPlanes.increment();
|
||||
CSGPlane& rPlane = mPlanes.last();
|
||||
rPlane.flags = 0;
|
||||
rPlane.owningEntity = NULL;
|
||||
rPlane.winding.numNodes = 0;
|
||||
rPlane.winding.numZoneIds = 0;
|
||||
|
||||
Point3D Point1(rPoint1.x, rPoint1.y, rPoint1.z);
|
||||
Point3D Point2(rPoint2.x, rPoint2.y, rPoint2.z);
|
||||
Point3D Point3(rPoint3.x, rPoint3.y, rPoint3.z);
|
||||
|
||||
rPlane.construct(Point1, Point2, Point3);
|
||||
|
||||
if (rPlane.getNormal().x == 0 && rPlane.getNormal().y == 0 && rPlane.getNormal().z == 0) {
|
||||
AssertISV(false, "Error, degenerate plane (colinear points)");
|
||||
mPlanes.decrement();
|
||||
return *((CSGPlane*)NULL);
|
||||
}
|
||||
|
||||
return rPlane;
|
||||
}*/
|
||||
CSGPlane& CSGBrush::constructBrushPlane(const Point3D& Point1,
|
||||
const Point3D& Point2,
|
||||
const Point3D& Point3)
|
||||
{
|
||||
mPlanes.increment();
|
||||
CSGPlane& rPlane = mPlanes.last();
|
||||
rPlane.flags = 0;
|
||||
rPlane.owningEntity = NULL;
|
||||
rPlane.winding.numNodes = 0;
|
||||
rPlane.winding.numZoneIds = 0;
|
||||
|
||||
rPlane.construct(Point1, Point2, Point3);
|
||||
|
||||
if (rPlane.getNormal().x == 0 && rPlane.getNormal().y == 0 && rPlane.getNormal().z == 0) {
|
||||
AssertISV(false, "Error, degenerate plane (colinear points)");
|
||||
mPlanes.decrement();
|
||||
return *((CSGPlane*)NULL);
|
||||
}
|
||||
|
||||
return rPlane;
|
||||
}
|
||||
|
||||
bool CSGBrush::isEquivalent(const CSGBrush& testBrush) const
|
||||
{
|
||||
for (U32 i = 0; i < mPlanes.size(); i++) {
|
||||
U32 j;
|
||||
for (j = 0; j < testBrush.mPlanes.size(); j++) {
|
||||
if (testBrush.mPlanes[j].planeEQIndex == mPlanes[i].planeEQIndex)
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == testBrush.mPlanes.size())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
188
tools/map2difPlus/csgBrush.h
Executable file
188
tools/map2difPlus/csgBrush.h
Executable file
@@ -0,0 +1,188 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "map2difPlus/morianBasics.h"
|
||||
#endif
|
||||
#ifndef _EDITGEOMETRY_H_
|
||||
#include "map2difPlus/editGeometry.h"
|
||||
#endif
|
||||
#ifndef _MORIANUTIL_H_
|
||||
#include "map2difPlus/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),
|
||||
sgLightingScale(32.0f),
|
||||
sgSelfIllumination(0.0f, 0.0f, 0.0f),
|
||||
pNext( NULL )
|
||||
{ }
|
||||
|
||||
Vector<CSGPlane> mPlanes;
|
||||
Point3D mMinBound;
|
||||
Point3D mMaxBound;
|
||||
|
||||
bool mIsAmbiguous;
|
||||
BrushType mBrushType;
|
||||
U32 brushId;
|
||||
U32 materialType;
|
||||
|
||||
U32 sgLightingScale;
|
||||
ColorI sgSelfIllumination;
|
||||
|
||||
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_
|
||||
76
tools/map2difPlus/editFloorPlanRes.cc
Executable file
76
tools/map2difPlus/editFloorPlanRes.cc
Executable file
@@ -0,0 +1,76 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "map2difPlus/editGeometry.h"
|
||||
#include "map2difPlus/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] ] );
|
||||
}
|
||||
}
|
||||
30
tools/map2difPlus/editFloorPlanRes.h
Executable file
30
tools/map2difPlus/editFloorPlanRes.h
Executable 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_
|
||||
1938
tools/map2difPlus/editGeometry.cc
Executable file
1938
tools/map2difPlus/editGeometry.cc
Executable file
File diff suppressed because it is too large
Load Diff
600
tools/map2difPlus/editGeometry.h
Executable file
600
tools/map2difPlus/editGeometry.h
Executable file
@@ -0,0 +1,600 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "map2difPlus/morianBasics.h"
|
||||
#endif
|
||||
#ifndef _MORIANUTIL_H_
|
||||
#include "map2difPlus/morianUtil.h"
|
||||
#endif
|
||||
#ifndef _BSPNODE_H_
|
||||
#include "map2difPlus/bspNode.h"
|
||||
#endif
|
||||
#ifndef _MPLANE_H_
|
||||
#include "math/mPlane.h"
|
||||
#endif
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
#ifndef _EDITFLOORPLANRES_H_
|
||||
#include "map2difPlus/editFloorPlanRes.h"
|
||||
#endif
|
||||
#ifndef _INTERIOR_H_
|
||||
#include "interior/interior.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;
|
||||
|
||||
U32 sgLightingScale;
|
||||
ColorI sgSelfIllumination;
|
||||
};
|
||||
struct NullSurface {
|
||||
U32 planeIndex;
|
||||
Winding winding;
|
||||
U32 flags;
|
||||
};
|
||||
|
||||
class Entity {
|
||||
public:
|
||||
Entity() { }
|
||||
virtual ~Entity() { };
|
||||
InteriorDict mDictionary;
|
||||
Vector<CSGBrush*> mBrushes;
|
||||
|
||||
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_
|
||||
83
tools/map2difPlus/editInteriorRes.cc
Executable file
83
tools/map2difPlus/editInteriorRes.cc
Executable file
@@ -0,0 +1,83 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/gBitmap.h"
|
||||
#include "interior/interior.h"
|
||||
#include "map2difPlus/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/map2difPlus/editInteriorRes.h
Executable file
30
tools/map2difPlus/editInteriorRes.h
Executable 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_
|
||||
1537
tools/map2difPlus/entityTypes.cc
Executable file
1537
tools/map2difPlus/entityTypes.cc
Executable file
File diff suppressed because it is too large
Load Diff
568
tools/map2difPlus/entityTypes.h
Executable file
568
tools/map2difPlus/entityTypes.h
Executable file
@@ -0,0 +1,568 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ENTITYTYPES_H_
|
||||
#define _ENTITYTYPES_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _MORIANBASICS_H_
|
||||
#include "map2difPlus/morianBasics.h"
|
||||
#endif
|
||||
#ifndef _EDITGEOMETRY_H_
|
||||
#include "map2difPlus/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
|
||||
#ifndef _CSGBRUSH_H_
|
||||
#include "csgBrush.h"
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Brush based entities
|
||||
class WorldSpawnEntity : public EditGeometry::Entity
|
||||
{
|
||||
public:
|
||||
U32 mDetailNumber;
|
||||
U32 mMinPixels;
|
||||
|
||||
F32 mGeometryScale;
|
||||
|
||||
F32 mLumelScale;
|
||||
|
||||
U32 sgGetLightingScale() {return 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 sgLightingScaleEntity : public EditGeometry::Entity
|
||||
{
|
||||
public:
|
||||
F32 sgLightingScale;
|
||||
U32 sgGetLightingScale() {return sgLightingScale;}
|
||||
|
||||
ColorI sgSelfIllumination;
|
||||
ColorI sgGetSelfIllumination() {return sgSelfIllumination;}
|
||||
|
||||
public:
|
||||
sgLightingScaleEntity()
|
||||
{
|
||||
sgLightingScale = 32.0;
|
||||
sgSelfIllumination = ColorI(0, 0, 0);
|
||||
}
|
||||
~sgLightingScaleEntity() {}
|
||||
|
||||
bool parseEntityDescription(InteriorMapResource::Entity* ent);
|
||||
BrushType getBrushType() {return StructuralBrush;}
|
||||
bool isPointClass() const {return false;}
|
||||
const char* getName() const
|
||||
{
|
||||
static char bogoChar = '\0';
|
||||
return &bogoChar;
|
||||
}
|
||||
const Point3D& getOrigin() const
|
||||
{
|
||||
static Point3D bogoPoint(0, 0, 0);
|
||||
return bogoPoint;
|
||||
}
|
||||
static const char* getClassName() {return "sgLightingScaleEntity";}
|
||||
};
|
||||
|
||||
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&);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-------------------------------------- PATH TYPES
|
||||
//
|
||||
class PathNodeEntity : public EditGeometry::Entity
|
||||
{
|
||||
public:
|
||||
Point3D mOrigin;
|
||||
|
||||
S32 mNumMSToNextNode;
|
||||
U32 mSmoothingType;
|
||||
|
||||
public:
|
||||
PathNodeEntity();
|
||||
~PathNodeEntity();
|
||||
BrushType getBrushType();
|
||||
bool parseEntityDescription(InteriorMapResource::Entity* ent);
|
||||
|
||||
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;
|
||||
|
||||
U32 mCurrBrushId;
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-------------------------------------- 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;
|
||||
VectorPtr<InteriorPathFollower::WayPoint*> mWayPoints;
|
||||
U32 mTotalMS;
|
||||
|
||||
U32 mCurrBrushId;
|
||||
|
||||
Interior* mInterior;
|
||||
|
||||
void grabOrigin();
|
||||
|
||||
public:
|
||||
DoorEntity();
|
||||
~DoorEntity();
|
||||
void process();
|
||||
|
||||
void addPathNode(PathNodeEntity *ent);
|
||||
static const char* getClassName() { return("Door_Elevator"); }
|
||||
bool parseEntityDescription(InteriorMapResource::Entity* ent);
|
||||
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 GameEntity : public EditGeometry::Entity
|
||||
{
|
||||
public:
|
||||
Point3D mOrigin;
|
||||
|
||||
char mDataBlock[1024];
|
||||
char mGameClass[1024];
|
||||
|
||||
public:
|
||||
GameEntity(const char *gameClassName);
|
||||
~GameEntity();
|
||||
BrushType getBrushType();
|
||||
bool parseEntityDescription(InteriorMapResource::Entity* ent);
|
||||
|
||||
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_
|
||||
2426
tools/map2difPlus/exportGeometry.cc
Executable file
2426
tools/map2difPlus/exportGeometry.cc
Executable file
File diff suppressed because it is too large
Load Diff
498
tools/map2difPlus/lmapPacker.cc
Executable file
498
tools/map2difPlus/lmapPacker.cc
Executable file
@@ -0,0 +1,498 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "map2difPlus/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);
|
||||
|
||||
|
||||
// Better border color, so we can remove the border size! - John Kabus
|
||||
/*if (overflow == false)
|
||||
{
|
||||
pDst[0] = 0xFF;
|
||||
pDst[1] = 0x00;
|
||||
pDst[2] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
pDst[0] = 0x00;
|
||||
pDst[1] = 0xFF;
|
||||
pDst[2] = 0x00;
|
||||
}*/
|
||||
|
||||
pDst[0] = 63;
|
||||
pDst[1] = 63;
|
||||
pDst[2] = 63;
|
||||
}
|
||||
}
|
||||
|
||||
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/map2difPlus/lmapPacker.h
Executable file
75
tools/map2difPlus/lmapPacker.h
Executable 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 2
|
||||
|
||||
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_
|
||||
528
tools/map2difPlus/main.cc
Executable file
528
tools/map2difPlus/main.cc
Executable file
@@ -0,0 +1,528 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "map2difPlus/editGeometry.h"
|
||||
#include "interior/interior.h"
|
||||
#include "map2difPlus/editInteriorRes.h"
|
||||
#include "interior/floorPlanRes.h"
|
||||
#include "map2difPlus/morianGame.h"
|
||||
#include "core/frameAllocator.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "map2difPlus/lmapPacker.h"
|
||||
#include "map2difPlus/convert.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
MorianGame GameObject;
|
||||
|
||||
// FOR SILLY LINK DEPENDANCY
|
||||
bool gEditingMission = false;
|
||||
bool gDedicatedServer = 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)
|
||||
{
|
||||
Interior::smFileVersion = 0;
|
||||
|
||||
// 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();
|
||||
|
||||
// Process any special entitys...
|
||||
for (U32 i = 0; i < gWorkingGeometry->mEntities.size(); i++)
|
||||
{
|
||||
DoorEntity* pDoor = dynamic_cast<DoorEntity*>(gWorkingGeometry->mEntities[i]);
|
||||
if (pDoor != NULL)
|
||||
pDoor->process();
|
||||
}
|
||||
|
||||
// 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 )
|
||||
{
|
||||
|
||||
}
|
||||
105
tools/map2difPlus/morianBasics.h
Executable file
105
tools/map2difPlus/morianBasics.h
Executable 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_
|
||||
19
tools/map2difPlus/morianGame.h
Executable file
19
tools/map2difPlus/morianGame.h
Executable 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_
|
||||
1062
tools/map2difPlus/morianUtil.cc
Executable file
1062
tools/map2difPlus/morianUtil.cc
Executable file
File diff suppressed because it is too large
Load Diff
80
tools/map2difPlus/morianUtil.h
Executable file
80
tools/map2difPlus/morianUtil.h
Executable 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 "map2difPlus/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_
|
||||
|
||||
165
tools/map2difPlus/navGraph.cc
Executable file
165
tools/map2difPlus/navGraph.cc
Executable 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 "map2difPlus/editGeometry.h"
|
||||
#include "map2difPlus/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();
|
||||
}
|
||||
|
||||
336
tools/map2difPlus/torque.fgd
Executable file
336
tools/map2difPlus/torque.fgd
Executable file
@@ -0,0 +1,336 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 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"
|
||||
]
|
||||
|
||||
@SolidClass = sgLightingScaleEntity : "Synapse Gaming Lighting Scale Entity"
|
||||
[
|
||||
light_geometry_scale(string) : "Lighting scale (must be a power of 2)" : "32.0"
|
||||
self_illumination_color(color255) : "Self-Illumination 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"
|
||||
datablock(string) : "Datablock" : ""
|
||||
]
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Doors, elevators, etc...
|
||||
// --------------------------------------------------------------------------
|
||||
@SolidClass = Door_Elevator : "Door or Elevator"
|
||||
[
|
||||
name(string) : "Name" : "MustChange"
|
||||
path_name(string) : "Path Name" : ""
|
||||
datablock(string) : "Datablock" : ""
|
||||
]
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Paths, etc...
|
||||
// --------------------------------------------------------------------------
|
||||
@PointClass = path_node : "Path Node"
|
||||
[
|
||||
next_time(integer) : "MS to next node" : 1000
|
||||
smoothing(integer) : "Smoothing Mode" : 2
|
||||
]
|
||||
|
||||
//@PointClass = path_start : "Path Start"
|
||||
//[
|
||||
// name(target_source) : "Name"
|
||||
// next_node(target_destination) : "Next Node"
|
||||
// next_time(integer) : "MS to next node" : 1000
|
||||
//]
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// GameEntities
|
||||
// --------------------------------------------------------------------------
|
||||
@PointClass = game_entity : "Game Entity"
|
||||
[
|
||||
game_class(string) : "Game Class" : ""
|
||||
datablock(string) : "Datablock" : ""
|
||||
]
|
||||
BIN
tools/map2dif_plus.exe
Executable file
BIN
tools/map2dif_plus.exe
Executable file
Binary file not shown.
1698
tools/max2dtsExporter/NvTriStripObjects.cc
Executable file
1698
tools/max2dtsExporter/NvTriStripObjects.cc
Executable file
File diff suppressed because it is too large
Load Diff
237
tools/max2dtsExporter/NvTriStripObjects.h
Executable file
237
tools/max2dtsExporter/NvTriStripObjects.h
Executable 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
|
||||
|
||||
57
tools/max2dtsExporter/NvVertexCache.cc
Executable file
57
tools/max2dtsExporter/NvVertexCache.cc
Executable 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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
71
tools/max2dtsExporter/NvVertexCache.h
Executable file
71
tools/max2dtsExporter/NvVertexCache.h
Executable 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
1007
tools/max2dtsExporter/SceneEnum.cc
Executable file
File diff suppressed because it is too large
Load Diff
126
tools/max2dtsExporter/SceneEnum.h
Executable file
126
tools/max2dtsExporter/SceneEnum.h
Executable 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
192
tools/max2dtsExporter/Sequence.h
Executable 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
|
||||
8819
tools/max2dtsExporter/ShapeMimic.cc
Executable file
8819
tools/max2dtsExporter/ShapeMimic.cc
Executable file
File diff suppressed because it is too large
Load Diff
461
tools/max2dtsExporter/ShapeMimic.h
Executable file
461
tools/max2dtsExporter/ShapeMimic.h
Executable 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
|
||||
|
||||
657
tools/max2dtsExporter/exportUtil.cc
Executable file
657
tools/max2dtsExporter/exportUtil.cc
Executable 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);
|
||||
}
|
||||
}
|
||||
17
tools/max2dtsExporter/exportUtil.h
Executable file
17
tools/max2dtsExporter/exportUtil.h
Executable 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_
|
||||
7
tools/max2dtsExporter/exporter.def
Executable file
7
tools/max2dtsExporter/exporter.def
Executable 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
114
tools/max2dtsExporter/exporter.h
Executable 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
|
||||
395
tools/max2dtsExporter/exporter.rc
Executable file
395
tools/max2dtsExporter/exporter.rc
Executable file
@@ -0,0 +1,395 @@
|
||||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "exporter.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "windows.h"
|
||||
#include "winres.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
648
tools/max2dtsExporter/main.cc
Executable 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
748
tools/max2dtsExporter/maxUtil.cc
Executable 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
67
tools/max2dtsExporter/maxUtil.h
Executable 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
BIN
tools/max2dtsExporter/readme.doc
Executable file
Binary file not shown.
993
tools/max2dtsExporter/sequence.cc
Executable file
993
tools/max2dtsExporter/sequence.cc
Executable file
@@ -0,0 +1,993 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "max2dtsExporter/Sequence.h"
|
||||
#include "max2dtsExporter/exporter.h"
|
||||
#include "Platform/platformAssert.h"
|
||||
|
||||
#pragma pack(push,8)
|
||||
#include <MAX.H>
|
||||
#include <dummy.h>
|
||||
#include <iparamm.h>
|
||||
#pragma pack(pop)
|
||||
|
||||
// conversion from 1.x to 2.x 3ds Max?
|
||||
//#define TYPE_BOOL TYPE_BOOLEAN
|
||||
|
||||
extern HINSTANCE hInstance;
|
||||
extern TCHAR *GetString(S32);
|
||||
|
||||
//------------------------------------------------------
|
||||
// max data structures used to map between max
|
||||
// user-interface and max parameter blocks...
|
||||
//------------------------------------------------------
|
||||
S32 useFrameRateControls1[] = { IDC_SEQ_USE_FRAME_RATE, IDC_SEQ_USE_N_FRAMES };
|
||||
S32 useFrameRateControls2[] = { IDC_SEQ_USE_GROUND_FRAME_RATE, IDC_SEQ_USE_N_GROUND_FRAMES };
|
||||
S32 tfArray[] = { true, false };
|
||||
|
||||
static ParamUIDesc descParam1[] =
|
||||
{
|
||||
ParamUIDesc(
|
||||
PB_SEQ_CYCLIC,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_CYCLIC ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_BLEND,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_BLEND ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_LAST_FIRST_FRAME_SAME,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_LAST_FIRST_FRAME_SAME ),
|
||||
|
||||
ParamUIDesc(
|
||||
PB_SEQ_USE_FRAME_RATE,
|
||||
TYPE_RADIO,
|
||||
useFrameRateControls1,
|
||||
2,
|
||||
tfArray),
|
||||
|
||||
ParamUIDesc(
|
||||
PB_SEQ_FRAME_RATE,
|
||||
EDITTYPE_FLOAT,
|
||||
IDC_SEQ_FRAME_RATE,IDC_SEQ_FRAME_RATE_SPINNER,
|
||||
0.0001f,100.0f,1.0f),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_NUM_FRAMES,
|
||||
EDITTYPE_INT,
|
||||
IDC_SEQ_NUM_FRAMES,IDC_SEQ_NUM_FRAMES_SPINNER,
|
||||
2.0f,100.0f,1.0f),
|
||||
|
||||
ParamUIDesc(
|
||||
PB_SEQ_OVERRIDE_DURATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_DURATION_OVERRIDE ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_DURATION,
|
||||
EDITTYPE_FLOAT,
|
||||
IDC_SEQ_DURATION,IDC_SEQ_DURATION_SPINNER,
|
||||
0.0001f,1000.0f,1.0f),
|
||||
|
||||
ParamUIDesc(
|
||||
PB_SEQ_DEFAULT_PRIORITY,
|
||||
EDITTYPE_INT,
|
||||
IDC_SEQ_PRIORITY,IDC_SEQ_PRIORITY_SPINNER,
|
||||
0.0f, 1000.0f, 1.0f)
|
||||
};
|
||||
|
||||
static ParamUIDesc descParam2[] =
|
||||
{
|
||||
ParamUIDesc(
|
||||
PB_SEQ_IGNORE_GROUND_TRANSFORM,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_IGNORE_GROUND_TRANSFORM ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_USE_GROUND_FRAME_RATE,
|
||||
TYPE_RADIO,
|
||||
useFrameRateControls2,
|
||||
2,
|
||||
tfArray),
|
||||
|
||||
ParamUIDesc(
|
||||
PB_SEQ_GROUND_FRAME_RATE,
|
||||
EDITTYPE_FLOAT,
|
||||
IDC_SEQ_GROUND_FRAME_RATE,
|
||||
IDC_SEQ_GROUND_FRAME_RATE_SPINNER,
|
||||
0.0001f,100.0f,1.0f),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_NUM_GROUND_FRAMES,
|
||||
EDITTYPE_INT,
|
||||
IDC_SEQ_NUM_GROUND_FRAMES,
|
||||
IDC_SEQ_NUM_GROUND_FRAMES_SPINNER,
|
||||
2.0f,100.0f,1.0f),
|
||||
};
|
||||
|
||||
static ParamUIDesc descParam3[] =
|
||||
{
|
||||
ParamUIDesc(
|
||||
PB_SEQ_ENABLE_MORPH_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_ENABLE_MORPH_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_ENABLE_VIS_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_ENABLE_VIS_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_ENABLE_TRANSFORM_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_ENABLE_TRANSFORM_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_ENABLE_UNIFORM_SCALE_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_ENABLE_UNIFORM_SCALE_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_ENABLE_ARBITRARY_SCALE_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_ENABLE_ARBITRARY_SCALE_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_ENABLE_TEXTURE_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_ENABLE_TEXTURE_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_ENABLE_IFL_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_ENABLE_IFL_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_ENABLE_DECAL_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_ENABLE_DECAL_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_ENABLE_DECAL_FRAME_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_ENABLE_DECAL_FRAME_ANIMATION ),
|
||||
|
||||
ParamUIDesc(
|
||||
PB_SEQ_FORCE_MORPH_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_FORCE_MORPH_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_FORCE_VIS_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_FORCE_VIS_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_FORCE_TRANSFORM_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_FORCE_TRANSFORM_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_FORCE_SCALE_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_FORCE_SCALE_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_FORCE_TEXTURE_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_FORCE_TEXTURE_ANIMATION ),
|
||||
ParamUIDesc(
|
||||
PB_SEQ_FORCE_DECAL_ANIMATION,
|
||||
TYPE_SINGLECHEKBOX,
|
||||
IDC_SEQ_FORCE_DECAL_ANIMATION )
|
||||
};
|
||||
|
||||
#define PARAMDESC1_LENGTH (sizeof( descParam1 ) / sizeof( ParamUIDesc ))
|
||||
#define PARAMDESC2_LENGTH (sizeof( descParam2 ) / sizeof( ParamUIDesc ))
|
||||
#define PARAMDESC3_LENGTH (sizeof( descParam3 ) / sizeof( ParamUIDesc ))
|
||||
|
||||
// This is the current version.
|
||||
static ParamBlockDescID curVer[] =
|
||||
{
|
||||
{ TYPE_BOOL, NULL, TRUE , 0 }, // begin/end
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 1 }, // cyclic
|
||||
{ TYPE_BOOL, NULL, FALSE, 2 }, // blend
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 3 }, // first and last frame same (cyclic seq only)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 4 }, // use frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 5 }, // Frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 6 }, // number of frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 7 }, // ignore ground transform altogether?
|
||||
{ TYPE_BOOL, NULL, FALSE, 8 }, // use ground frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 9 }, // ground frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 10 }, // number of ground frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 11 }, // enable morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 12 }, // enable vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 13 }, // enable transform animation
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 14 }, // force morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 15 }, // force vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 16 }, // force transform animation
|
||||
|
||||
{ TYPE_INT, NULL, FALSE, 17 }, // sequence default priority
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 18 }, // blend sequence reference time (keyframe time determines it, not param value)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 19 }, // enable texture animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 20 }, // enable ifl animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 21 }, // force texture animation
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 22 }, // triggers...keyframes are read to determine trigger times and values
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 23 }, // enable decal animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 24 }, // enable decal frame animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 25 }, // force decal animation
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 26 }, // override sequence duration
|
||||
{ TYPE_FLOAT, NULL, FALSE, 27 }, // sequence duration (if override true)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 28 }, // enable uniform scale animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 29 }, // enable arbitrary scale animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 30 }, // force scale animation
|
||||
};
|
||||
|
||||
static ParamBlockDescID oldVer6[] =
|
||||
{
|
||||
{ TYPE_BOOL, NULL, TRUE , 0 }, // begin/end
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 1 }, // cyclic
|
||||
{ TYPE_BOOL, NULL, FALSE, 2 }, // blend
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 3 }, // first and last frame same (cyclic seq only)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 4 }, // use frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 5 }, // Frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 6 }, // number of frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 7 }, // ignore ground transform altogether?
|
||||
{ TYPE_BOOL, NULL, FALSE, 8 }, // use ground frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 9 }, // ground frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 10 }, // number of ground frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 11 }, // enable morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 12 }, // enable vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 13 }, // enable transform animation
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 14 }, // force morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 15 }, // force vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 16 }, // force transform animation
|
||||
|
||||
{ TYPE_INT, NULL, FALSE, 17 }, // sequence default priority
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 18 }, // blend sequence reference time (keyframe time determines it, not param value)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 19 }, // enable texture animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 20 }, // enable ifl animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 21 }, // force texture animation
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 22 }, // triggers...keyframes are read to determine trigger times and values
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 23 }, // enable decal animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 24 }, // enable decal frame animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 25 }, // force decal animation
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 26 }, // override sequence duration
|
||||
{ TYPE_FLOAT, NULL, FALSE, 27 }, // sequence duration (if override true)
|
||||
};
|
||||
|
||||
static ParamBlockDescID oldVer5[] =
|
||||
{
|
||||
{ TYPE_BOOL, NULL, TRUE , 0 }, // begin/end
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 1 }, // cyclic
|
||||
{ TYPE_BOOL, NULL, FALSE, 2 }, // blend
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 3 }, // first and last frame same (cyclic seq only)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 4 }, // use frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 5 }, // Frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 6 }, // number of frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 7 }, // ignore ground transform altogether?
|
||||
{ TYPE_BOOL, NULL, FALSE, 8 }, // use ground frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 9 }, // ground frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 10 }, // number of ground frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 11 }, // enable morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 12 }, // enable vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 13 }, // enable transform animation
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 14 }, // force morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 15 }, // force vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 16 }, // force transform animation
|
||||
|
||||
{ TYPE_INT, NULL, FALSE, 17 }, // sequence default priority
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 18 }, // blend sequence reference time (keyframe time determines it, not param value)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 19 }, // enable texture animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 20 }, // enable ifl animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 21 }, // force texture animation
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 22 }, // triggers...keyframes are read to determine trigger times and values
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 23 }, // enable decal animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 24 }, // enable decal frame animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 25 } // force decal animation
|
||||
};
|
||||
|
||||
static ParamBlockDescID oldVer4[] =
|
||||
{
|
||||
{ TYPE_BOOL, NULL, TRUE , 0 }, // begin/end
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 1 }, // cyclic
|
||||
{ TYPE_BOOL, NULL, FALSE, 2 }, // blend
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 3 }, // first and last frame same (cyclic seq only)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 4 }, // use frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 5 }, // Frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 6 }, // number of frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 7 }, // ignore ground transform altogether?
|
||||
{ TYPE_BOOL, NULL, FALSE, 8 }, // use ground frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 9 }, // ground frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 10 }, // number of ground frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 11 }, // enable morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 12 }, // enable vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 13 }, // enable transform animation
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 14 }, // force morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 15 }, // force vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 16 }, // force transform animation
|
||||
|
||||
{ TYPE_INT, NULL, FALSE, 17 }, // sequence default priority
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 18 }, // blend sequence reference time (keyframe time determines it, not param value)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 19 }, // enable texture animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 20 }, // enable ifl animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 21 }, // force texture animation
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 22 }, // triggers...keyframes are read to determine trigger times and values
|
||||
};
|
||||
|
||||
// old param block versions
|
||||
static ParamBlockDescID oldVer3[] =
|
||||
{
|
||||
{ TYPE_BOOL, NULL, TRUE , 0 }, // begin/end
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 1 }, // cyclic
|
||||
{ TYPE_BOOL, NULL, FALSE, 2 }, // blend
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 3 }, // first and last frame same (cyclic seq only)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 4 }, // use frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 5 }, // Frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 6 }, // number of frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 7 }, // ignore ground transform altogether?
|
||||
{ TYPE_BOOL, NULL, FALSE, 8 }, // use ground frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 9 }, // ground frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 10 }, // number of ground frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 11 }, // enable morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 12 }, // enable vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 13 }, // enable transform animation
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 14 }, // force morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 15 }, // force vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 16 }, // force transform animation
|
||||
|
||||
{ TYPE_INT, NULL, FALSE, 17 }, // sequence default priority
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 18 }, // blend sequence reference time (keyframe time determines it, not param value)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 19 }, // enable texture animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 20 }, // enable ifl animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 21 } // force texture animation
|
||||
};
|
||||
|
||||
static ParamBlockDescID oldVer2[] =
|
||||
{
|
||||
{ TYPE_BOOL, NULL, TRUE , 0 }, // begin/end
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 1 }, // cyclic
|
||||
{ TYPE_BOOL, NULL, FALSE, 2 }, // blend
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 3 }, // first and last frame same (cyclic seq only)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 4 }, // use frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 5 }, // Frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 6 }, // number of frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 7 }, // ignore ground transform altogether?
|
||||
{ TYPE_BOOL, NULL, FALSE, 8 }, // use ground frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 9 }, // ground frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 10 }, // number of ground frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 11 }, // enable morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 12 }, // enable vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 13 }, // enable transform animation
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 14 }, // force morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 15 }, // force vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 16 }, // force transform animation
|
||||
|
||||
{ TYPE_INT, NULL, FALSE, 17 }, // sequence default priority
|
||||
|
||||
{ TYPE_FLOAT, NULL, TRUE, 18 } // blend sequence reference time (keyframe time determines it, not param value)
|
||||
};
|
||||
|
||||
static ParamBlockDescID oldVer1[] =
|
||||
{
|
||||
{ TYPE_BOOL, NULL, TRUE , 0 }, // begin/end
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 1 }, // cyclic
|
||||
{ TYPE_BOOL, NULL, FALSE, 2 }, // blend
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 3 }, // first and last frame same (cyclic seq only)
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 4 }, // use frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 5 }, // Frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 6 }, // number of frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 7 }, // ignore ground transform altogether?
|
||||
{ TYPE_BOOL, NULL, FALSE, 8 }, // use ground frame rate?
|
||||
{ TYPE_FLOAT, NULL, FALSE, 9 }, // ground frame Rate
|
||||
{ TYPE_INT, NULL, FALSE, 10 }, // number of ground frames
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 11 }, // enable morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 12 }, // enable vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 13 }, // enable transform animation
|
||||
|
||||
{ TYPE_BOOL, NULL, FALSE, 14 }, // force morph animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 15 }, // force vis animation
|
||||
{ TYPE_BOOL, NULL, FALSE, 16 }, // force transform animation
|
||||
|
||||
{ TYPE_INT, NULL, FALSE, 17 } // sequence default priority
|
||||
};
|
||||
|
||||
// Array of old versions
|
||||
static ParamVersionDesc versions[] =
|
||||
{
|
||||
ParamVersionDesc( oldVer1, sizeof(oldVer1)/sizeof(ParamBlockDescID), 0),
|
||||
ParamVersionDesc( oldVer2, sizeof(oldVer2)/sizeof(ParamBlockDescID), 1),
|
||||
ParamVersionDesc( oldVer3, sizeof(oldVer3)/sizeof(ParamBlockDescID), 2),
|
||||
ParamVersionDesc( oldVer4, sizeof(oldVer4)/sizeof(ParamBlockDescID), 3),
|
||||
ParamVersionDesc( oldVer5, sizeof(oldVer5)/sizeof(ParamBlockDescID), 4),
|
||||
ParamVersionDesc( oldVer6, sizeof(oldVer6)/sizeof(ParamBlockDescID), 5)
|
||||
};
|
||||
|
||||
#define NUM_OLDVERSIONS (sizeof(versions) / sizeof(ParamVersionDesc))
|
||||
#define CURRENT_VERSION NUM_OLDVERSIONS
|
||||
|
||||
// Current version
|
||||
#define PBLOCK_LENGTH (sizeof(curVer) / sizeof(ParamBlockDescID))
|
||||
static ParamVersionDesc curVersion( curVer, PBLOCK_LENGTH, CURRENT_VERSION );
|
||||
|
||||
//------------------------------------------------------
|
||||
// sequence class defaults:
|
||||
//------------------------------------------------------
|
||||
U32 SequenceObject::defaultCyclic = true;
|
||||
U32 SequenceObject::defaultBlend = false;
|
||||
U32 SequenceObject::defaultFirstLastFrameSame = false;
|
||||
|
||||
U32 SequenceObject::defaultUseFrameRate = true;
|
||||
F32 SequenceObject::defaultFrameRate = 15.0f;
|
||||
S32 SequenceObject::defaultNumFrames = 2;
|
||||
|
||||
U32 SequenceObject::defaultIgnoreGroundTransform = false;
|
||||
U32 SequenceObject::defaultUseGroundFrameRate = false;
|
||||
F32 SequenceObject::defaultGroundFrameRate = 15.0f;
|
||||
S32 SequenceObject::defaultNumGroundFrames = 2;
|
||||
|
||||
U32 SequenceObject::defaultEnableMorphAnimation = false;
|
||||
U32 SequenceObject::defaultEnableVisAnimation = false;
|
||||
U32 SequenceObject::defaultEnableTransformAnimation = true;
|
||||
U32 SequenceObject::defaultEnableTextureAnimation = false;
|
||||
U32 SequenceObject::defaultEnableIflAnimation = false;
|
||||
U32 SequenceObject::defaultEnableDecalAnimation = false;
|
||||
U32 SequenceObject::defaultEnableDecalFrameAnimation = false;
|
||||
|
||||
U32 SequenceObject::defaultForceMorphAnimation = false;
|
||||
U32 SequenceObject::defaultForceVisAnimation = false;
|
||||
U32 SequenceObject::defaultForceTransformAnimation = false;
|
||||
U32 SequenceObject::defaultForceTextureAnimation = false;
|
||||
U32 SequenceObject::defaultForceDecalAnimation = false;
|
||||
|
||||
S32 SequenceObject::defaultDefaultSequencePriority = 0;
|
||||
|
||||
S32 SequenceObject::defaultBlendReferenceTime = 0; // value not important
|
||||
|
||||
U32 SequenceObject::defaultOverrideDuration = false;
|
||||
F32 SequenceObject::defaultDuration = 1.0f;
|
||||
|
||||
U32 SequenceObject::defaultEnableUniformScaleAnimation = false;
|
||||
U32 SequenceObject::defaultEnableArbitraryScaleAnimation = false;
|
||||
U32 SequenceObject::defaultForceScaleAnimation = false;
|
||||
|
||||
//------------------------------------------------------
|
||||
// Some more statics:
|
||||
//------------------------------------------------------
|
||||
SequenceObject * SequenceObject::editOb = NULL;
|
||||
IParamMap * SequenceObject::pmapParam1 = NULL;
|
||||
IParamMap * SequenceObject::pmapParam2 = NULL;
|
||||
IParamMap * SequenceObject::pmapParam3 = NULL;
|
||||
|
||||
//------------------------------------------------------
|
||||
// Sequence object methods...
|
||||
//------------------------------------------------------
|
||||
|
||||
void SequenceObject::InitNodeName(TSTR& s)
|
||||
{
|
||||
s = GetString(IDS_DB_SEQUENCE);
|
||||
}
|
||||
|
||||
void SequenceObject::GetClassName(TSTR& s)
|
||||
{
|
||||
s = TSTR(GetString(IDS_DB_SEQUENCE));
|
||||
}
|
||||
|
||||
void resetSequenceDefaults()
|
||||
{
|
||||
SequenceObject::defaultCyclic = true;
|
||||
SequenceObject::defaultBlend = false;
|
||||
SequenceObject::defaultFirstLastFrameSame = false;
|
||||
|
||||
SequenceObject::defaultUseFrameRate = true;
|
||||
SequenceObject::defaultFrameRate = 15.0f;
|
||||
SequenceObject::defaultNumFrames = 2;
|
||||
|
||||
SequenceObject::defaultIgnoreGroundTransform = false;
|
||||
SequenceObject::defaultUseGroundFrameRate = false;
|
||||
SequenceObject::defaultGroundFrameRate = 15.0f;
|
||||
SequenceObject::defaultNumGroundFrames = 2;
|
||||
|
||||
SequenceObject::defaultEnableMorphAnimation = false;
|
||||
SequenceObject::defaultEnableVisAnimation = false;
|
||||
SequenceObject::defaultEnableTransformAnimation = true;
|
||||
SequenceObject::defaultEnableTextureAnimation = false;
|
||||
SequenceObject::defaultEnableIflAnimation = false;
|
||||
SequenceObject::defaultEnableDecalAnimation = false;
|
||||
SequenceObject::defaultEnableDecalFrameAnimation = false;
|
||||
|
||||
SequenceObject::defaultForceMorphAnimation = false;
|
||||
SequenceObject::defaultForceVisAnimation = false;
|
||||
SequenceObject::defaultForceTransformAnimation = false;
|
||||
SequenceObject::defaultForceTextureAnimation = false;
|
||||
SequenceObject::defaultForceDecalAnimation = false;
|
||||
|
||||
SequenceObject::defaultDefaultSequencePriority = 0;
|
||||
|
||||
SequenceObject::defaultBlendReferenceTime = 0; // value not important
|
||||
|
||||
SequenceObject::defaultOverrideDuration = false;
|
||||
SequenceObject::defaultDuration = 1.0f;
|
||||
|
||||
SequenceObject::defaultEnableUniformScaleAnimation = false;
|
||||
SequenceObject::defaultEnableArbitraryScaleAnimation = false;
|
||||
SequenceObject::defaultForceScaleAnimation = false;
|
||||
}
|
||||
|
||||
bool getBoolSequenceDefault(S32 index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case PB_SEQ_CYCLIC: return SequenceObject::defaultCyclic;
|
||||
case PB_SEQ_BLEND: return SequenceObject::defaultBlend;
|
||||
case PB_SEQ_LAST_FIRST_FRAME_SAME: return SequenceObject::defaultFirstLastFrameSame;
|
||||
case PB_SEQ_USE_FRAME_RATE: return SequenceObject::defaultUseFrameRate;
|
||||
|
||||
case PB_SEQ_IGNORE_GROUND_TRANSFORM: return SequenceObject::defaultIgnoreGroundTransform;
|
||||
case PB_SEQ_USE_GROUND_FRAME_RATE: return SequenceObject::defaultUseGroundFrameRate;
|
||||
|
||||
case PB_SEQ_ENABLE_MORPH_ANIMATION: return SequenceObject::defaultEnableMorphAnimation;
|
||||
case PB_SEQ_ENABLE_VIS_ANIMATION: return SequenceObject::defaultEnableVisAnimation;
|
||||
case PB_SEQ_ENABLE_TRANSFORM_ANIMATION: return SequenceObject::defaultEnableTransformAnimation;
|
||||
case PB_SEQ_ENABLE_TEXTURE_ANIMATION: return SequenceObject::defaultEnableTextureAnimation;
|
||||
case PB_SEQ_ENABLE_IFL_ANIMATION: return SequenceObject::defaultEnableIflAnimation;
|
||||
case PB_SEQ_ENABLE_DECAL_ANIMATION: return SequenceObject::defaultEnableDecalAnimation;
|
||||
case PB_SEQ_ENABLE_DECAL_FRAME_ANIMATION: return SequenceObject::defaultEnableDecalFrameAnimation;
|
||||
|
||||
case PB_SEQ_FORCE_MORPH_ANIMATION: return SequenceObject::defaultForceMorphAnimation;
|
||||
case PB_SEQ_FORCE_VIS_ANIMATION: return SequenceObject::defaultForceVisAnimation;
|
||||
case PB_SEQ_FORCE_TRANSFORM_ANIMATION: return SequenceObject::defaultForceTransformAnimation;
|
||||
case PB_SEQ_FORCE_TEXTURE_ANIMATION: return SequenceObject::defaultForceTextureAnimation;
|
||||
case PB_SEQ_FORCE_DECAL_ANIMATION: return SequenceObject::defaultForceDecalAnimation;
|
||||
|
||||
case PB_SEQ_OVERRIDE_DURATION: return SequenceObject::defaultOverrideDuration;
|
||||
|
||||
case PB_SEQ_ENABLE_UNIFORM_SCALE_ANIMATION: return SequenceObject::defaultEnableUniformScaleAnimation;
|
||||
case PB_SEQ_ENABLE_ARBITRARY_SCALE_ANIMATION: return SequenceObject::defaultEnableArbitraryScaleAnimation;
|
||||
case PB_SEQ_FORCE_SCALE_ANIMATION: return SequenceObject::defaultForceScaleAnimation;
|
||||
|
||||
// default : AssertFatal(0,"Invalid default when getting sequence default value");
|
||||
// return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
F32 getFloatSequenceDefault(S32 index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case PB_SEQ_FRAME_RATE: return SequenceObject::defaultFrameRate;
|
||||
case PB_SEQ_GROUND_FRAME_RATE: return SequenceObject::defaultGroundFrameRate;
|
||||
case PB_SEQ_DURATION: return SequenceObject::defaultDuration;
|
||||
|
||||
// default : AssertFatal(0,"Invalid default when getting sequence default value");
|
||||
// return 0.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
S32 getIntSequenceDefault(S32 index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case PB_SEQ_NUM_FRAMES: return SequenceObject::defaultNumFrames;
|
||||
case PB_SEQ_NUM_GROUND_FRAMES: return SequenceObject::defaultNumGroundFrames;
|
||||
case PB_SEQ_DEFAULT_PRIORITY: return SequenceObject::defaultDefaultSequencePriority;
|
||||
|
||||
// default : AssertFatal(0,"Invalid default when getting sequence default value");
|
||||
// return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// Sequence object implementation -- not much here
|
||||
//------------------------------------------------------
|
||||
IParamArray *SequenceObject::GetParamBlock()
|
||||
{
|
||||
return pblock;
|
||||
}
|
||||
|
||||
S32 SequenceObject::GetParamBlockIndex(S32 id)
|
||||
{
|
||||
if (pblock && id>=0 && id<pblock->NumParams())
|
||||
return id;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SequenceObject::FreeCaches()
|
||||
{
|
||||
ivalid.SetEmpty();
|
||||
}
|
||||
|
||||
RefResult SequenceObject::NotifyRefChanged( Interval changeInt,
|
||||
RefTargetHandle hTarget,
|
||||
PartID& partID,
|
||||
RefMessage message )
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case REFMSG_CHANGE:
|
||||
{
|
||||
if (editOb==this)
|
||||
InvalidateUI();
|
||||
break;
|
||||
}
|
||||
case REFMSG_GET_PARAM_DIM:
|
||||
{
|
||||
GetParamDim *gpd = (GetParamDim*)partID;
|
||||
gpd->dim = GetParameterDim(gpd->index);
|
||||
return REF_STOP;
|
||||
}
|
||||
case REFMSG_GET_PARAM_NAME:
|
||||
{
|
||||
GetParamName *gpn = (GetParamName*)partID;
|
||||
gpn->name = GetParameterName(gpn->index);
|
||||
return REF_STOP;
|
||||
}
|
||||
}
|
||||
return(REF_SUCCEED);
|
||||
}
|
||||
|
||||
Interval SequenceObject::ObjectValidity(TimeValue time)
|
||||
{
|
||||
return ivalid;
|
||||
}
|
||||
|
||||
TSTR SequenceObject::SubAnimName(S32 i)
|
||||
{
|
||||
return _T("Parameters");
|
||||
}
|
||||
|
||||
void SequenceObject::BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev)
|
||||
{
|
||||
// Save the current sequence object
|
||||
editOb = this;
|
||||
|
||||
// we have mutliple parameter maps...make sure one of each is created:
|
||||
|
||||
if (pmapParam1)
|
||||
{
|
||||
// Left over from last sequence ceated
|
||||
pmapParam1->SetParamBlock(pblock);
|
||||
}
|
||||
else
|
||||
{
|
||||
pmapParam1 = CreateCPParamMap(
|
||||
descParam1,
|
||||
PARAMDESC1_LENGTH,
|
||||
pblock,
|
||||
ip,
|
||||
hInstance,
|
||||
MAKEINTRESOURCE(IDD_SEQUENCE_PARAMS),
|
||||
_T("General"),
|
||||
0);
|
||||
}
|
||||
|
||||
if (pmapParam2)
|
||||
{
|
||||
// Left over from last sequence ceated
|
||||
pmapParam2->SetParamBlock(pblock);
|
||||
}
|
||||
else
|
||||
{
|
||||
pmapParam2 = CreateCPParamMap(
|
||||
descParam2,
|
||||
PARAMDESC2_LENGTH,
|
||||
pblock,
|
||||
ip,
|
||||
hInstance,
|
||||
MAKEINTRESOURCE(IDD_SEQUENCE_PARAMS2),
|
||||
_T("Ground Transform"),
|
||||
APPENDROLL_CLOSED);
|
||||
}
|
||||
|
||||
if (pmapParam3)
|
||||
{
|
||||
// Left over from last sequence ceated
|
||||
pmapParam3->SetParamBlock(pblock);
|
||||
}
|
||||
else
|
||||
{
|
||||
pmapParam3 = CreateCPParamMap(
|
||||
descParam3,
|
||||
PARAMDESC3_LENGTH,
|
||||
pblock,
|
||||
ip,
|
||||
hInstance,
|
||||
MAKEINTRESOURCE(IDD_SEQUENCE_PARAMS3),
|
||||
_T("Export Control"),
|
||||
APPENDROLL_CLOSED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SequenceObject::EndEditParams(IObjParam *ip, ULONG flags,Animatable *next)
|
||||
{
|
||||
editOb = NULL;
|
||||
|
||||
ClearAFlag(A_OBJ_CREATING);
|
||||
|
||||
if ( (flags&END_EDIT_REMOVEUI) && pmapParam1 )
|
||||
{
|
||||
// Remove the rollup pages from the command panel.
|
||||
DestroyCPParamMap(pmapParam1);
|
||||
pmapParam1 = NULL;
|
||||
}
|
||||
|
||||
if ( (flags&END_EDIT_REMOVEUI) && pmapParam2 )
|
||||
{
|
||||
// Remove the rollup pages from the command panel.
|
||||
DestroyCPParamMap(pmapParam2);
|
||||
pmapParam2 = NULL;
|
||||
}
|
||||
|
||||
if ( (flags&END_EDIT_REMOVEUI) && pmapParam3 )
|
||||
{
|
||||
// Remove the rollup pages from the command panel.
|
||||
DestroyCPParamMap(pmapParam3);
|
||||
pmapParam3 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This method is called when the user interface controls need to be
|
||||
// updated to reflect new values because of the user moving the time
|
||||
// slider. Here we simply call a method of the parameter map to
|
||||
// handle this for us.
|
||||
void SequenceObject::InvalidateUI()
|
||||
{
|
||||
if (pmapParam1)
|
||||
pmapParam1->Invalidate();
|
||||
|
||||
if (pmapParam2)
|
||||
pmapParam2->Invalidate();
|
||||
|
||||
if (pmapParam3)
|
||||
pmapParam3->Invalidate();
|
||||
}
|
||||
|
||||
// This method returns the dimension of the parameter requested.
|
||||
// This dimension describes the type and magnitude of the value
|
||||
// stored by the parameter.
|
||||
ParamDimension *SequenceObject::GetParameterDim(S32)
|
||||
{
|
||||
return defaultDim; // just store the parameter...ask no questions
|
||||
}
|
||||
|
||||
// This method returns the name of the parameter requested.
|
||||
TSTR SequenceObject::GetParameterName(S32 pbIndex)
|
||||
{
|
||||
switch (pbIndex)
|
||||
{
|
||||
case PB_SEQ_BEGIN_END:
|
||||
return TSTR(_T("Sequence Begin/End"));
|
||||
|
||||
case PB_SEQ_CYCLIC:
|
||||
return TSTR(_T("Cyclic sequence"));
|
||||
case PB_SEQ_BLEND:
|
||||
return TSTR(_T("Blend sequence"));
|
||||
|
||||
case PB_SEQ_LAST_FIRST_FRAME_SAME:
|
||||
return TSTR(_T("Last frame matches first frame"));
|
||||
|
||||
case PB_SEQ_USE_FRAME_RATE:
|
||||
return TSTR(_T("Use frame rate"));
|
||||
case PB_SEQ_FRAME_RATE:
|
||||
return TSTR(_T("Frame rate"));
|
||||
case PB_SEQ_NUM_FRAMES:
|
||||
return TSTR(_T("Number of frames"));
|
||||
|
||||
case PB_SEQ_IGNORE_GROUND_TRANSFORM:
|
||||
return TSTR(_T("Ignore ground transform"));
|
||||
case PB_SEQ_USE_GROUND_FRAME_RATE:
|
||||
return TSTR(_T("Use ground transform frame rate"));
|
||||
case PB_SEQ_GROUND_FRAME_RATE:
|
||||
return TSTR(_T("Ground transform frame rate"));
|
||||
case PB_SEQ_NUM_GROUND_FRAMES:
|
||||
return TSTR(_T("Number of ground transform frames"));
|
||||
|
||||
case PB_SEQ_ENABLE_MORPH_ANIMATION:
|
||||
return TSTR(_T("Enable morph animation"));
|
||||
case PB_SEQ_ENABLE_VIS_ANIMATION:
|
||||
return TSTR(_T("Enable visibility animation"));
|
||||
case PB_SEQ_ENABLE_TRANSFORM_ANIMATION:
|
||||
return TSTR(_T("Enable transform animation"));
|
||||
case PB_SEQ_ENABLE_TEXTURE_ANIMATION:
|
||||
return TSTR(_T("Enable texture animation"));
|
||||
case PB_SEQ_ENABLE_IFL_ANIMATION:
|
||||
return TSTR(_T("Enable IFL animation"));
|
||||
case PB_SEQ_ENABLE_DECAL_ANIMATION:
|
||||
return TSTR(_T("Enable decal animation"));
|
||||
case PB_SEQ_ENABLE_DECAL_FRAME_ANIMATION:
|
||||
return TSTR(_T("Enable decal frame animation"));
|
||||
|
||||
case PB_SEQ_FORCE_MORPH_ANIMATION:
|
||||
return TSTR(_T("Force morph animation"));
|
||||
case PB_SEQ_FORCE_VIS_ANIMATION:
|
||||
return TSTR(_T("Force visibility animation"));
|
||||
case PB_SEQ_FORCE_TRANSFORM_ANIMATION:
|
||||
return TSTR(_T("Force transform animation"));
|
||||
case PB_SEQ_FORCE_TEXTURE_ANIMATION:
|
||||
return TSTR(_T("Force texture animation"));
|
||||
case PB_SEQ_FORCE_DECAL_ANIMATION:
|
||||
return TSTR(_T("Force decal animation"));
|
||||
|
||||
case PB_SEQ_DEFAULT_PRIORITY:
|
||||
return TSTR(_T("Default sequence priority"));
|
||||
|
||||
case PB_SEQ_BLEND_REFERENCE_TIME:
|
||||
return TSTR(_T("Blend Reference Time"));
|
||||
|
||||
case PB_SEQ_TRIGGERS:
|
||||
return TSTR(_T("Triggers"));
|
||||
|
||||
case PB_SEQ_OVERRIDE_DURATION:
|
||||
return TSTR(_T("Override sequence duration"));
|
||||
case PB_SEQ_DURATION:
|
||||
return TSTR(_T("Sequence duration"));
|
||||
|
||||
case PB_SEQ_ENABLE_UNIFORM_SCALE_ANIMATION:
|
||||
return TSTR(_T("Enable uniform scale animation"));
|
||||
case PB_SEQ_ENABLE_ARBITRARY_SCALE_ANIMATION:
|
||||
return TSTR(_T("Enable arbitrary scale animation"));
|
||||
case PB_SEQ_FORCE_SCALE_ANIMATION:
|
||||
return TSTR(_T("Force scale animation"));
|
||||
|
||||
default:
|
||||
return TSTR(_T(""));
|
||||
}
|
||||
}
|
||||
|
||||
RefTargetHandle SequenceObject::Clone(RemapDir& remap)
|
||||
{
|
||||
SequenceObject* newob = new SequenceObject();
|
||||
newob->ReplaceReference(0,pblock->Clone(remap));
|
||||
newob->ivalid.SetEmpty();
|
||||
return newob;
|
||||
}
|
||||
|
||||
SequenceObject::SequenceObject()
|
||||
{
|
||||
ivalid.SetEmpty();
|
||||
pblock = NULL;
|
||||
SetAFlag(A_OBJ_CREATING);
|
||||
|
||||
// Create the parameter block and make a reference to it.
|
||||
MakeRefByID(FOREVER, 0,
|
||||
CreateParameterBlock( curVersion.desc, PBLOCK_LENGTH, CURRENT_VERSION));
|
||||
|
||||
// assert(pblock);
|
||||
|
||||
// if (!pblock)
|
||||
// // need a way to signal an error
|
||||
// return;
|
||||
|
||||
// Initialize the default values.
|
||||
pblock->SetValue(PB_SEQ_BEGIN_END, 0, false);
|
||||
pblock->SetValue(PB_SEQ_CYCLIC, 0, (bool)defaultCyclic);
|
||||
pblock->SetValue(PB_SEQ_BLEND, 0, (bool)defaultBlend);
|
||||
pblock->SetValue(PB_SEQ_LAST_FIRST_FRAME_SAME, 0, (bool)defaultFirstLastFrameSame);
|
||||
pblock->SetValue(PB_SEQ_USE_FRAME_RATE, 0, (bool)defaultUseFrameRate);
|
||||
pblock->SetValue(PB_SEQ_FRAME_RATE, 0, defaultFrameRate);
|
||||
pblock->SetValue(PB_SEQ_NUM_FRAMES, 0, defaultNumFrames);
|
||||
pblock->SetValue(PB_SEQ_IGNORE_GROUND_TRANSFORM, 0, (bool)defaultIgnoreGroundTransform);
|
||||
pblock->SetValue(PB_SEQ_USE_GROUND_FRAME_RATE, 0, (bool)defaultUseGroundFrameRate);
|
||||
pblock->SetValue(PB_SEQ_GROUND_FRAME_RATE, 0, defaultGroundFrameRate);
|
||||
pblock->SetValue(PB_SEQ_NUM_GROUND_FRAMES, 0, defaultNumGroundFrames);
|
||||
pblock->SetValue(PB_SEQ_ENABLE_MORPH_ANIMATION, 0, (bool)defaultEnableMorphAnimation);
|
||||
pblock->SetValue(PB_SEQ_ENABLE_VIS_ANIMATION, 0, (bool)defaultEnableVisAnimation);
|
||||
pblock->SetValue(PB_SEQ_ENABLE_TRANSFORM_ANIMATION, 0, (bool)defaultEnableTransformAnimation);
|
||||
pblock->SetValue(PB_SEQ_ENABLE_TEXTURE_ANIMATION, 0, (bool)defaultEnableTextureAnimation);
|
||||
pblock->SetValue(PB_SEQ_ENABLE_IFL_ANIMATION, 0, (bool)defaultEnableIflAnimation);
|
||||
pblock->SetValue(PB_SEQ_ENABLE_DECAL_ANIMATION, 0, (bool)defaultEnableDecalAnimation);
|
||||
pblock->SetValue(PB_SEQ_ENABLE_DECAL_FRAME_ANIMATION, 0, (bool)defaultEnableDecalFrameAnimation);
|
||||
pblock->SetValue(PB_SEQ_FORCE_MORPH_ANIMATION, 0, (bool)defaultForceMorphAnimation);
|
||||
pblock->SetValue(PB_SEQ_FORCE_VIS_ANIMATION, 0, (bool)defaultForceVisAnimation);
|
||||
pblock->SetValue(PB_SEQ_FORCE_TRANSFORM_ANIMATION, 0, (bool)defaultForceTransformAnimation);
|
||||
pblock->SetValue(PB_SEQ_FORCE_TEXTURE_ANIMATION, 0, (bool)defaultForceTextureAnimation);
|
||||
pblock->SetValue(PB_SEQ_FORCE_DECAL_ANIMATION, 0, (bool)defaultForceDecalAnimation);
|
||||
pblock->SetValue(PB_SEQ_DEFAULT_PRIORITY, 0, defaultDefaultSequencePriority);
|
||||
pblock->SetValue(PB_SEQ_BLEND_REFERENCE_TIME, 0, defaultBlendReferenceTime);
|
||||
pblock->SetValue(PB_SEQ_OVERRIDE_DURATION, 0, (bool)defaultOverrideDuration);
|
||||
pblock->SetValue(PB_SEQ_DURATION, 0, defaultDuration);
|
||||
pblock->SetValue(PB_SEQ_ENABLE_UNIFORM_SCALE_ANIMATION, 0, (bool)defaultEnableUniformScaleAnimation);
|
||||
pblock->SetValue(PB_SEQ_ENABLE_ARBITRARY_SCALE_ANIMATION, 0, (bool)defaultEnableArbitraryScaleAnimation);
|
||||
pblock->SetValue(PB_SEQ_FORCE_SCALE_ANIMATION, 0, (bool)defaultForceScaleAnimation);
|
||||
}
|
||||
|
||||
SequenceObject::~SequenceObject()
|
||||
{
|
||||
DeleteAllRefsFromMe();
|
||||
}
|
||||
|
||||
//
|
||||
// Reference Managment:
|
||||
//
|
||||
|
||||
IOResult SequenceObject::Load(ILoad *iload)
|
||||
{
|
||||
// This is the callback that corrects for any older versions
|
||||
// of the parameter block structure found in the MAX file
|
||||
// being loaded.
|
||||
iload->RegisterPostLoadCallback(new ParamBlockPLCB(versions,NUM_OLDVERSIONS,&curVersion,this,0));
|
||||
return IO_OK;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------
|
||||
// Sequence object descriptor declaration & implementation
|
||||
//------------------------------------------------------
|
||||
class SequenceObjClassDesc:public ClassDesc
|
||||
{
|
||||
public:
|
||||
S32 IsPublic() { return 1; }
|
||||
void * Create(BOOL loading = FALSE) { return new SequenceObject; }
|
||||
const TCHAR * ClassName() { return GetString(IDS_DB_SEQUENCE); }
|
||||
SClass_ID SuperClassID() { return HELPER_CLASS_ID; }
|
||||
Class_ID ClassID() { return SEQUENCE_CLASS_ID; }
|
||||
const TCHAR * Category() { return GetString(IDS_DB_GENERAL); }
|
||||
|
||||
// following functions allow setting of parameter defaults...
|
||||
// worthless unless all 3 are implemented
|
||||
// void ResetClassParams(BOOL fileReset)
|
||||
// {
|
||||
// if(fileReset)
|
||||
// resetSequenceDefaults();
|
||||
// }
|
||||
// HasClassParams?
|
||||
// EditClassParams?
|
||||
};
|
||||
|
||||
static SequenceObjClassDesc sequenceObjDesc;
|
||||
|
||||
ClassDesc * GetSequenceDesc() { return &sequenceObjDesc; }
|
||||
|
||||
289
tools/max2dtsExporter/skinHelper.cc
Executable file
289
tools/max2dtsExporter/skinHelper.cc
Executable file
@@ -0,0 +1,289 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "max2dtsExporter/skinHelper.h"
|
||||
#include "max2dtsExporter/sceneEnum.h"
|
||||
#include "max2dtsExporter/exportUtil.h"
|
||||
#include "max2dtsExporter/exporter.h"
|
||||
#include "core/tvector.h"
|
||||
|
||||
#pragma pack(push,8)
|
||||
#include <Max.h>
|
||||
#include <decomp.h>
|
||||
#include <dummy.h>
|
||||
#include <ISkin.h>
|
||||
#include <modstack.h>
|
||||
#pragma pack(pop)
|
||||
|
||||
#define printDump SceneEnumProc::printDump
|
||||
|
||||
class SkinHelperClassDesc:public ClassDesc {
|
||||
public:
|
||||
int IsPublic() {return 1;}
|
||||
void * Create(BOOL loading = FALSE) {return new SkinHelper();}
|
||||
const TCHAR * ClassName() {return "Skin Helper";}
|
||||
SClass_ID SuperClassID() {return WSM_CLASS_ID;}
|
||||
Class_ID ClassID() {return SKINHELPER_CLASS_ID;}
|
||||
const TCHAR* Category() {return "General";}
|
||||
void ResetClassParams(BOOL) {}
|
||||
};
|
||||
|
||||
static SkinHelperClassDesc SkinHelperDesc;
|
||||
ClassDesc* GetSkinHelperDesc() {return &SkinHelperDesc;}
|
||||
|
||||
static BOOL CALLBACK SkinHelperDlgProc(
|
||||
HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
SkinHelper *ins = (SkinHelper*)GetWindowLong(hWnd,GWL_USERDATA);
|
||||
if (!ins && msg!=WM_INITDIALOG) return FALSE;
|
||||
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
ins = (SkinHelper*)lParam;
|
||||
SetWindowLong(hWnd,GWL_USERDATA,lParam);
|
||||
ins->hParams = hWnd;
|
||||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
break;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MOUSEMOVE:
|
||||
ins->ip->RollupMouseMessage(hWnd,msg,wParam,lParam);
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
IObjParam *SkinHelper::ip = NULL;
|
||||
HWND SkinHelper::hParams = NULL;
|
||||
|
||||
//--- SkinHelper -------------------------------------------------------
|
||||
SkinHelper::SkinHelper()
|
||||
{
|
||||
}
|
||||
|
||||
SkinHelper::~SkinHelper()
|
||||
{
|
||||
}
|
||||
|
||||
S32 gDamnit=0;
|
||||
|
||||
void SkinHelper::SetReference(S32 i, RefTargetHandle rtarg)
|
||||
{
|
||||
gDamnit++;
|
||||
}
|
||||
|
||||
Interval SkinHelper::LocalValidity(TimeValue t)
|
||||
{
|
||||
// if being edited, return NEVER forces a cache to be built
|
||||
// after previous modifier.
|
||||
if (TestAFlag(A_MOD_BEING_EDITED))
|
||||
return NEVER;
|
||||
//TODO: Return the validity interval of the modifier
|
||||
return NEVER;
|
||||
}
|
||||
|
||||
RefTargetHandle SkinHelper::Clone(RemapDir& remap)
|
||||
{
|
||||
SkinHelper* newmod = new SkinHelper();
|
||||
//TODO: Add the cloning code here
|
||||
return(newmod);
|
||||
}
|
||||
|
||||
void SkinHelper::ModifyObject(TimeValue t, ModContext &mc, ObjectState * os, INode *pNode)
|
||||
{
|
||||
ISkin * skin;
|
||||
ISkinContextData * skinData;
|
||||
if (!pNode)
|
||||
return;
|
||||
findSkinData(pNode,&skin,&skinData);
|
||||
if (!skin || !skinData)
|
||||
return;
|
||||
|
||||
TriObject * triObj = (TriObject*)os->obj->ConvertToType(0,Class_ID(TRIOBJ_CLASS_ID,0));
|
||||
if (triObj!=os->obj)
|
||||
delete triObj;
|
||||
else
|
||||
modifyTriObject(triObj,skin,skinData);
|
||||
PatchObject * patchObj = (PatchObject*)os->obj->ConvertToType(0,Class_ID(PATCHOBJ_CLASS_ID,0));
|
||||
if (patchObj!=os->obj)
|
||||
delete patchObj;
|
||||
else
|
||||
modifyPatchObject(patchObj,skin,skinData);
|
||||
}
|
||||
|
||||
void SkinHelper::modifyTriObject(TriObject * triObj, ISkin * skin, ISkinContextData * skinData)
|
||||
{
|
||||
S32 numBones = skin->GetNumBones();
|
||||
|
||||
Mesh & maxMesh = triObj->mesh;
|
||||
S32 numVerts = maxMesh.getNumVerts();
|
||||
S32 numTVerts = maxMesh.getNumMapVerts(1);
|
||||
if (numVerts!=skinData->GetNumPoints())
|
||||
return;
|
||||
|
||||
S32 numChannels = 2+((numBones+1)>>1);
|
||||
UVVert tv(0,0,0);
|
||||
for (S32 i=2; i<numChannels; i++)
|
||||
{
|
||||
maxMesh.setMapSupport(i,true);
|
||||
maxMesh.setNumMapVerts(i,numVerts);
|
||||
for (S32 j=0; j<numVerts; j++)
|
||||
maxMesh.setMapVert(i,j,tv);
|
||||
maxMesh.setNumMapFaces(i,maxMesh.getNumFaces());
|
||||
// copy map faces from the first channel
|
||||
for (S32 j=0; j<maxMesh.getNumFaces(); j++)
|
||||
{
|
||||
Face & face = maxMesh.faces[j];
|
||||
TVFace & tvFace = maxMesh.mapFaces(i)[j];
|
||||
tvFace.t[0] = face.v[0];
|
||||
tvFace.t[1] = face.v[1];
|
||||
tvFace.t[2] = face.v[2];
|
||||
}
|
||||
}
|
||||
|
||||
for (S32 v=0; v<numVerts; v++)
|
||||
{
|
||||
for (S32 i=0; i<skinData->GetNumAssignedBones(v); i++)
|
||||
{
|
||||
S32 bone = skinData->GetAssignedBone(v,i);
|
||||
F32 w = skinData->GetBoneWeight(v,i);
|
||||
UVVert tv = maxMesh.mapVerts(2+(bone>>1))[v];
|
||||
if (bone&1)
|
||||
tv.y = w;
|
||||
else
|
||||
tv.x = w;
|
||||
maxMesh.setMapVert(2+(bone>>1),v,tv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkinHelper::modifyPatchObject(PatchObject * patchObj, ISkin * skin, ISkinContextData * skinData)
|
||||
{
|
||||
S32 numBones = skin->GetNumBones();
|
||||
S32 numPoints = skinData->GetNumPoints();
|
||||
S32 i;
|
||||
|
||||
PatchMesh & maxMesh = patchObj->patch;
|
||||
S32 numVerts = maxMesh.getNumVerts();
|
||||
if (numVerts>numPoints)
|
||||
// points should be more than verts...first set of points are the verts, the rest are control verts
|
||||
// we don't do anything with those weights...it limits the surface deformations that can take
|
||||
// place, but it's all we can do...
|
||||
return;
|
||||
|
||||
if (!numBones)
|
||||
return;
|
||||
|
||||
S32 numChannels = 2+((numBones+1)>>1);
|
||||
S32 numTVerts = maxMesh.getNumMapVerts(1);
|
||||
UVVert tv(0,0,0);
|
||||
|
||||
maxMesh.setNumMaps(numChannels);
|
||||
for (i=2; i<numChannels; i++)
|
||||
{
|
||||
// prepare each channel...
|
||||
S32 j;
|
||||
maxMesh.setNumMapVerts(i,numVerts);
|
||||
for (j=0; j<numVerts; j++)
|
||||
maxMesh.getMapVert(i,j) = tv;
|
||||
// set up tv patch faces
|
||||
maxMesh.setNumMapPatches(i,maxMesh.getNumPatches());
|
||||
for (j=0; j<maxMesh.getNumPatches(); j++)
|
||||
{
|
||||
Patch & patch = maxMesh.patches[j];
|
||||
TVPatch & tvPatch = maxMesh.getMapPatch(i,j);
|
||||
tvPatch.tv[0] = patch.v[0];
|
||||
tvPatch.tv[1] = patch.v[1];
|
||||
tvPatch.tv[2] = patch.v[2];
|
||||
tvPatch.tv[3] = patch.v[3];
|
||||
}
|
||||
}
|
||||
|
||||
for (S32 v=0; v<numVerts; v++)
|
||||
{
|
||||
for (i=0; i<skinData->GetNumAssignedBones(v); i++)
|
||||
{
|
||||
S32 bone = skinData->GetAssignedBone(v,i);
|
||||
F32 w = skinData->GetBoneWeight(v,i);
|
||||
S32 channel = 2 + (bone>>1);
|
||||
UVVert & tv = maxMesh.getMapVert(channel,v);
|
||||
if (bone&1)
|
||||
tv.y = w;
|
||||
else
|
||||
tv.x = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkinHelper::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev )
|
||||
{
|
||||
this->ip = ip;
|
||||
if (!hParams) {
|
||||
hParams = ip->AddRollupPage(
|
||||
hInstance,
|
||||
MAKEINTRESOURCE(IDD_SKINHELP_PANEL),
|
||||
SkinHelperDlgProc,
|
||||
GetString(IDS_SKINHELP_PARAMS),
|
||||
(LPARAM)this);
|
||||
ip->RegisterDlgWnd(hParams);
|
||||
} else {
|
||||
SetWindowLong(hParams,GWL_USERDATA,(LONG)this);
|
||||
}
|
||||
}
|
||||
|
||||
void SkinHelper::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next)
|
||||
{
|
||||
ip->UnRegisterDlgWnd(hParams);
|
||||
ip->DeleteRollupPage(hParams);
|
||||
hParams = NULL;
|
||||
this->ip = NULL;
|
||||
}
|
||||
|
||||
|
||||
//From ReferenceMaker
|
||||
RefResult SkinHelper::NotifyRefChanged(
|
||||
Interval changeInt, RefTargetHandle hTarget,
|
||||
PartID& partID, RefMessage message)
|
||||
{
|
||||
//TODO: Add code to handle the various reference changed messages
|
||||
return REF_SUCCEED;
|
||||
}
|
||||
|
||||
//From Object
|
||||
BOOL SkinHelper::HasUVW()
|
||||
{
|
||||
//TODO: Return whether the object has UVW coordinates or not
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void SkinHelper::SetGenUVW(BOOL sw)
|
||||
{
|
||||
if (sw==HasUVW()) return;
|
||||
//TODO: Set the plugin internal value to sw
|
||||
}
|
||||
|
||||
IOResult SkinHelper::Load(ILoad *iload)
|
||||
{
|
||||
//TODO: Add code to allow plugin to load its data
|
||||
|
||||
return IO_OK;
|
||||
}
|
||||
|
||||
IOResult SkinHelper::Save(ISave *isave)
|
||||
{
|
||||
//TODO: Add code to allow plugin to save its data
|
||||
|
||||
return IO_OK;
|
||||
}
|
||||
85
tools/max2dtsExporter/skinHelper.h
Executable file
85
tools/max2dtsExporter/skinHelper.h
Executable file
@@ -0,0 +1,85 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef __SKINHELPER__H
|
||||
#define __SKINHELPER__H
|
||||
|
||||
#pragma pack(push,8)
|
||||
#include <Max.h>
|
||||
#include <istdplug.h>
|
||||
#include <iparamb2.h>
|
||||
#include <iparamm2.h>
|
||||
#pragma pack(pop)
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _MAXUTIL_H_
|
||||
#include "max2dtsExporter/maxUtil.h"
|
||||
#endif
|
||||
|
||||
#define SKINHELPER_CLASS_ID Class_ID(0x20dfdba0, 0x60646cb4)
|
||||
extern ClassDesc* GetSkinHelperDesc();
|
||||
extern TCHAR *GetString(S32);
|
||||
|
||||
class SkinHelper : public WSModifier
|
||||
{
|
||||
public:
|
||||
static HWND hParams;
|
||||
static IObjParam *ip; //Access to the interface
|
||||
|
||||
// From Animatable
|
||||
TCHAR *GetObjectName() { return "SkinHelper"; }
|
||||
|
||||
//From Modifier
|
||||
ChannelMask ChannelsUsed() { return TEXMAP_CHANNEL; }
|
||||
ChannelMask ChannelsChanged() { return TEXMAP_CHANNEL; }
|
||||
void ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node);
|
||||
void modifyTriObject(TriObject *, ISkin *, ISkinContextData *);
|
||||
void modifyPatchObject(PatchObject *, ISkin *, ISkinContextData *);
|
||||
|
||||
Class_ID InputType() {return defObjectClassID;}
|
||||
Interval LocalValidity(TimeValue t);
|
||||
|
||||
// From BaseObject
|
||||
BOOL ChangeTopology() {return FALSE;}
|
||||
|
||||
CreateMouseCallBack* GetCreateMouseCallBack() {return NULL;}
|
||||
void BeginEditParams(IObjParam *ip, ULONG flags,Animatable *prev);
|
||||
void EndEditParams(IObjParam *ip, ULONG flags,Animatable *next);
|
||||
|
||||
Interval GetValidity(TimeValue t);
|
||||
|
||||
// Automatic texture support
|
||||
BOOL HasUVW();
|
||||
void SetGenUVW(BOOL sw);
|
||||
|
||||
// Loading/Saving
|
||||
IOResult Load(ILoad *iload);
|
||||
IOResult Save(ISave *isave);
|
||||
|
||||
//From Animatable
|
||||
Class_ID ClassID() {return SKINHELPER_CLASS_ID;}
|
||||
SClass_ID SuperClassID() { return WSM_CLASS_ID; }
|
||||
void GetClassName(TSTR& s) {s = "SkinHelper"; }
|
||||
|
||||
RefTargetHandle Clone( RemapDir &remap );
|
||||
RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget,
|
||||
PartID& partID, RefMessage message);
|
||||
|
||||
int NumSubs() { return 0; }
|
||||
TSTR SubAnimName(int i) { return _T(""); }
|
||||
Animatable* SubAnim(int i) { return NULL; }
|
||||
int NumRefs() { return 0; }
|
||||
RefTargetHandle GetReference(int i) { return NULL; }
|
||||
void SetReference(int i, RefTargetHandle rtarg);
|
||||
|
||||
void DeleteThis() { delete this; }
|
||||
//Constructor/Destructor
|
||||
SkinHelper();
|
||||
~SkinHelper();
|
||||
};
|
||||
|
||||
#endif // __SKINHELPER__H
|
||||
907
tools/max2dtsExporter/stripper.cc
Executable file
907
tools/max2dtsExporter/stripper.cc
Executable file
@@ -0,0 +1,907 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "max2dtsExporter/stripper.h"
|
||||
|
||||
F32 Stripper::adjacencyWeight = 5;
|
||||
F32 Stripper::noswapWeight = 3;
|
||||
F32 Stripper::alreadyCachedWeight = 1;
|
||||
U32 Stripper::cacheSize = 16;
|
||||
U32 Stripper::simK = 5;
|
||||
|
||||
|
||||
Stripper::Stripper(Vector<TSDrawPrimitive> & _faces, Vector<U16> & _faceIndices)
|
||||
: faces(_faces), faceIndices(_faceIndices), adjacent(_faces.size(),_faces.size())
|
||||
{
|
||||
// keep track of whether face used in a strip yet
|
||||
used.setSize(faces.size());
|
||||
for (S32 i=0; i<faces.size(); i++)
|
||||
used[i]=false;
|
||||
|
||||
clearCache();
|
||||
cacheMisses = 0; // definitely don't want to clear this every time we clear the cache!
|
||||
|
||||
bestLength = -1;
|
||||
errorStr = NULL;
|
||||
}
|
||||
|
||||
Stripper::Stripper(Stripper & stripper)
|
||||
: faces(stripper.faces), faceIndices(stripper.faceIndices), adjacent(stripper.faces.size(),stripper.faces.size()),
|
||||
numAdjacent(stripper.numAdjacent), used(stripper.used), vertexCache(stripper.vertexCache),
|
||||
recentFaces(stripper.recentFaces), strips(stripper.strips), stripIndices(stripper.stripIndices)
|
||||
{
|
||||
currentFace = stripper.currentFace;
|
||||
limitStripLength = stripper.limitStripLength;
|
||||
bestLength = stripper.bestLength;
|
||||
cacheMisses = stripper.cacheMisses;
|
||||
adjacent.clearAllBits();
|
||||
for (S32 i=0; i<faces.size(); i++)
|
||||
for (S32 j=0; j<faces.size(); j++)
|
||||
if (stripper.adjacent.isSet(i,j))
|
||||
adjacent.setBit(i,j);
|
||||
errorStr = NULL;
|
||||
}
|
||||
|
||||
Stripper::~Stripper()
|
||||
{
|
||||
dFree(errorStr);
|
||||
}
|
||||
|
||||
void Stripper::testCache(S32 addedFace)
|
||||
{
|
||||
S32 * cache = &vertexCache.last();
|
||||
|
||||
// make sure last 3 verts on strip list are last 3 verts in cache
|
||||
U16 * indices = &stripIndices.last();
|
||||
if (*indices!=*cache || *(indices-1)!=*(cache-1) || *(indices-2)!=*(cache-2))
|
||||
{
|
||||
setExportError("Assertion failed when stripping (11)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (addedFace>=0)
|
||||
{
|
||||
// make sure current face is still last 3 items in the cache
|
||||
TSDrawPrimitive & face = faces[addedFace];
|
||||
U32 idx0 = faceIndices[face.start+0];
|
||||
U32 idx1 = faceIndices[face.start+1];
|
||||
U32 idx2 = faceIndices[face.start+2];
|
||||
if ( idx0!=*(cache) && idx0!=*(cache-1) && idx0!=*(cache-2))
|
||||
{
|
||||
setExportError("Assertion failed when stripping (8)");
|
||||
return;
|
||||
}
|
||||
if ( idx1!=*(cache) && idx1!=*(cache-1) && idx1!=*(cache-2))
|
||||
{
|
||||
setExportError("Assertion failed when stripping (9)");
|
||||
return;
|
||||
}
|
||||
if ( idx2!=*(cache) && idx2!=*(cache-1) && idx2!=*(cache-2))
|
||||
{
|
||||
setExportError("Assertion failed when stripping (10)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stripper::copyParams(Stripper * from)
|
||||
{
|
||||
limitStripLength = from->limitStripLength;
|
||||
}
|
||||
|
||||
void Stripper::makeStrips()
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return;
|
||||
|
||||
// main strip loop...
|
||||
U32 start, end, i, sz;
|
||||
Vector<TSDrawPrimitive> someFaces;
|
||||
Vector<U16> someIndices;
|
||||
for (start = 0; start<faces.size(); start=end)
|
||||
{
|
||||
for (end=start; end<faces.size() && faces[start].matIndex==faces[end].matIndex; end++)
|
||||
;
|
||||
|
||||
if (start==0 && end==faces.size())
|
||||
{
|
||||
// all same material, no need to copy anything
|
||||
makeStripsB();
|
||||
return;
|
||||
}
|
||||
|
||||
// copy start to end faces into new list -- this is so we end up doing less copying
|
||||
// down the road (when we are doing the look ahead simulation)
|
||||
someFaces.clear();
|
||||
someIndices.clear();
|
||||
for (i=start;i<end;i++)
|
||||
{
|
||||
someFaces.push_back(faces[i]);
|
||||
someFaces.last().start = someIndices.size();
|
||||
someIndices.push_back(faceIndices[faces[i].start + 0]);
|
||||
someIndices.push_back(faceIndices[faces[i].start + 1]);
|
||||
someIndices.push_back(faceIndices[faces[i].start + 2]);
|
||||
}
|
||||
|
||||
// strip these...
|
||||
Stripper someStrips(someFaces,someIndices);
|
||||
someStrips.copyParams(this);
|
||||
someStrips.makeStripsB();
|
||||
if (isError()) return;
|
||||
|
||||
// copy these strips into our arrays
|
||||
sz = strips.size();
|
||||
strips.setSize(sz+someStrips.strips.size());
|
||||
for (i=0; i<someStrips.strips.size(); i++)
|
||||
{
|
||||
strips[i+sz] = someStrips.strips[i];
|
||||
strips[i+sz].start += stripIndices.size();
|
||||
}
|
||||
sz = stripIndices.size();
|
||||
stripIndices.setSize(sz+someStrips.stripIndices.size());
|
||||
dMemcpy(&stripIndices[sz],someStrips.stripIndices.address(),someStrips.stripIndices.size()*sizeof(U16));
|
||||
|
||||
// update cache misses
|
||||
cacheMisses += someStrips.getCacheMisses();
|
||||
}
|
||||
}
|
||||
|
||||
void Stripper::makeStripsB()
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return;
|
||||
|
||||
// set adjacency info
|
||||
setAdjacency(0,faces.size());
|
||||
|
||||
while (1)
|
||||
{
|
||||
strips.increment();
|
||||
TSDrawPrimitive & strip = strips.last();
|
||||
strip.start = stripIndices.size();
|
||||
strip.numElements = 0;
|
||||
if (faces[0].matIndex & TSDrawPrimitive::NoMaterial)
|
||||
strip.matIndex = TSDrawPrimitive::NoMaterial;
|
||||
else
|
||||
strip.matIndex = faces[0].matIndex & TSDrawPrimitive::MaterialMask;
|
||||
strip.matIndex |= TSDrawPrimitive::Strip | TSDrawPrimitive::Indexed;
|
||||
|
||||
if (!startStrip(strip,0,faces.size()))
|
||||
{
|
||||
strips.decrement();
|
||||
break;
|
||||
}
|
||||
|
||||
while (addStrip(strip,0,faces.size()));
|
||||
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return;
|
||||
}
|
||||
|
||||
// let's make sure everything is legit up till here
|
||||
U32 i;
|
||||
for (i=0; i<faces.size(); i++)
|
||||
{
|
||||
if (!used[i])
|
||||
{
|
||||
setExportError("Assertion failed when stripping (1)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (i=0; i<numAdjacent.size(); i++)
|
||||
{
|
||||
if (numAdjacent[i])
|
||||
{
|
||||
setExportError("Assertion failed when stripping (2)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all faces were used, o.w. it's an error
|
||||
for (i=0; i<used.size(); i++)
|
||||
{
|
||||
if (!used[i])
|
||||
{
|
||||
setExportError("Assertion failed when stripping (3)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void Stripper::makeStrips()
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return;
|
||||
|
||||
// main strip loop...
|
||||
U32 start, end, i;
|
||||
for (start = 0; start<faces.size(); start=end)
|
||||
{
|
||||
for (end=start; end<faces.size() && faces[start].matIndex==faces[end].matIndex; end++)
|
||||
;
|
||||
|
||||
// set adjacency info
|
||||
setAdjacency(start,end);
|
||||
|
||||
while (1)
|
||||
{
|
||||
strips.increment();
|
||||
TSDrawPrimitive & strip = strips.last();
|
||||
strip.start = stripIndices.size();
|
||||
strip.numElements = 0;
|
||||
if (faces[start].matIndex & TSDrawPrimitive::NoMaterial)
|
||||
strip.matIndex = TSDrawPrimitive::NoMaterial;
|
||||
else
|
||||
strip.matIndex = faces[start].matIndex & TSDrawPrimitive::MaterialMask;
|
||||
strip.matIndex |= TSDrawPrimitive::Strip | TSDrawPrimitive::Indexed;
|
||||
|
||||
if (!startStrip(strip,start,end))
|
||||
{
|
||||
strips.decrement();
|
||||
break;
|
||||
}
|
||||
|
||||
while (addStrip(strip,start,end));
|
||||
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return;
|
||||
}
|
||||
|
||||
// let's make sure everything is legit up till here
|
||||
for (i=start; i<end; i++)
|
||||
{
|
||||
if (!used[i])
|
||||
{
|
||||
setExportError("Assertion failed when stripping (1)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (i=0; i<numAdjacent.size(); i++)
|
||||
{
|
||||
if (numAdjacent[i])
|
||||
{
|
||||
setExportError("Assertion failed when stripping (2)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all faces were used, o.w. it's an error
|
||||
for (i=0; i<used.size(); i++)
|
||||
{
|
||||
if (!used[i])
|
||||
{
|
||||
setExportError("Assertion failed when stripping (3)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// used for simulating addition of "len" more faces with a forced strip restart after "restart" faces
|
||||
S32 Stripper::continueStrip(S32 startFace, S32 endFace, S32 len, S32 restart)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return 0;
|
||||
|
||||
TSDrawPrimitive & strip = strips.last();
|
||||
|
||||
while (restart && addStrip(strip,startFace,endFace))
|
||||
restart--,len--;
|
||||
|
||||
// either restarted when we were forced to or restarted on our own
|
||||
// either way, continue adding faces till len==0
|
||||
while (len)
|
||||
{
|
||||
strips.increment();
|
||||
TSDrawPrimitive & strip = strips.last();
|
||||
strip.start = stripIndices.size();
|
||||
strip.numElements = 0;
|
||||
if (faces[startFace].matIndex & TSDrawPrimitive::NoMaterial)
|
||||
strip.matIndex = TSDrawPrimitive::NoMaterial;
|
||||
else
|
||||
strip.matIndex = faces[startFace].matIndex & TSDrawPrimitive::MaterialMask;
|
||||
strip.matIndex |= TSDrawPrimitive::Strip | TSDrawPrimitive::Indexed;
|
||||
|
||||
if (!startStrip(strip,startFace,endFace))
|
||||
{
|
||||
strips.decrement();
|
||||
break;
|
||||
}
|
||||
len--;
|
||||
|
||||
while (len && addStrip(strip,startFace,endFace))
|
||||
len--;
|
||||
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return 0;
|
||||
}
|
||||
|
||||
return restart;
|
||||
}
|
||||
|
||||
void Stripper::setAdjacency(S32 startFace, S32 endFace)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return;
|
||||
|
||||
// two faces adjacent only if wound the same way (so shared edge must appear in opp. order)
|
||||
S32 i,j;
|
||||
numAdjacent.setSize(faces.size());
|
||||
for (i=0; i<numAdjacent.size(); i++)
|
||||
numAdjacent[i] = 0;
|
||||
|
||||
adjacent.clearAllBits();
|
||||
|
||||
for (i=startFace; i<endFace-1; i++)
|
||||
{
|
||||
// the i-indices...
|
||||
U32 idx0 = faceIndices[faces[i].start + 0];
|
||||
U32 idx1 = faceIndices[faces[i].start + 1];
|
||||
U32 idx2 = faceIndices[faces[i].start + 2];
|
||||
|
||||
for (j=i+1; j<endFace; j++)
|
||||
{
|
||||
// the j-indices...
|
||||
U32 jdx0 = faceIndices[faces[j].start + 0];
|
||||
U32 jdx1 = faceIndices[faces[j].start + 1];
|
||||
U32 jdx2 = faceIndices[faces[j].start + 2];
|
||||
|
||||
// we don't want to be adjacent if we share all our vertices...
|
||||
if ( ( idx0==jdx0 || idx0==jdx1 || idx0==jdx2) &&
|
||||
( idx1==jdx0 || idx1==jdx1 || idx1==jdx2) &&
|
||||
( idx2==jdx0 || idx2==jdx1 || idx2==jdx2) )
|
||||
continue;
|
||||
|
||||
// test adjacency...
|
||||
if ( ((idx0==jdx1) && (idx1==jdx0)) || ((idx0==jdx2) && (idx1==jdx1)) || ((idx0==jdx0) && (idx1==jdx2)) ||
|
||||
((idx1==jdx1) && (idx2==jdx0)) || ((idx1==jdx2) && (idx2==jdx1)) || ((idx1==jdx0) && (idx2==jdx2)) ||
|
||||
((idx2==jdx1) && (idx0==jdx0)) || ((idx2==jdx2) && (idx0==jdx1)) || ((idx2==jdx0) && (idx0==jdx2)) )
|
||||
{
|
||||
// i,j are adjacent
|
||||
if (adjacent.isSet(i,j) || adjacent.isSet(j,i))
|
||||
{
|
||||
setExportError("wtf (1)");
|
||||
return;
|
||||
}
|
||||
adjacent.setBit(i,j);
|
||||
adjacent.setBit(j,i);
|
||||
if (!adjacent.isSet(i,j) || !adjacent.isSet(j,i))
|
||||
{
|
||||
setExportError("wtf (2)");
|
||||
return;
|
||||
}
|
||||
numAdjacent[i]++;
|
||||
numAdjacent[j]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stripper::clearCache()
|
||||
{
|
||||
vertexCache.setSize(cacheSize);
|
||||
for (S32 i=0; i<vertexCache.size(); i++)
|
||||
vertexCache[i] = -1;
|
||||
}
|
||||
|
||||
void Stripper::addToCache(S32 vertexIndex)
|
||||
{
|
||||
S32 i;
|
||||
for (i=0; i<vertexCache.size(); i++)
|
||||
if (vertexCache[i]==vertexIndex)
|
||||
break;
|
||||
if (i==vertexCache.size())
|
||||
cacheMisses++;
|
||||
|
||||
vertexCache.erase((U32)0);
|
||||
vertexCache.push_back(vertexIndex);
|
||||
}
|
||||
|
||||
void Stripper::addToCache(S32 vertexIndex, U32 posFromBack)
|
||||
{
|
||||
// theoretically this could result in a cache miss, but never used that way so
|
||||
// we won't check...
|
||||
|
||||
vertexCache.erase((U32)0);
|
||||
vertexCache.insert(vertexCache.size()-posFromBack);
|
||||
vertexCache[vertexCache.size()-1-posFromBack] = vertexIndex;
|
||||
}
|
||||
|
||||
bool Stripper::startStrip(TSDrawPrimitive & strip, S32 startFace, S32 endFace)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return false;
|
||||
|
||||
S32 i,j;
|
||||
|
||||
S32 bestFace = -1;
|
||||
|
||||
// first search the list of faces with neighbors that were recently visited
|
||||
for (i=0;i<recentFaces.size();i++)
|
||||
{
|
||||
if (!used[recentFaces[i]])
|
||||
{
|
||||
bestFace = recentFaces[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
recentFaces.clear();
|
||||
|
||||
// if we didn't find one above, search for a good face
|
||||
if (bestFace<0)
|
||||
{
|
||||
S32 bestScore = -1;
|
||||
for (i=startFace; i<endFace; i++)
|
||||
{
|
||||
if (used[i])
|
||||
continue;
|
||||
|
||||
// how many of the verts are in the cache?
|
||||
// Question: should we favor verts that occur early/late in the cache?
|
||||
U32 inCache = 0;
|
||||
U32 idx0 = faceIndices[faces[i].start + 0];
|
||||
U32 idx1 = faceIndices[faces[i].start + 1];
|
||||
U32 idx2 = faceIndices[faces[i].start + 2];
|
||||
|
||||
for (j=0; j<vertexCache.size(); j++)
|
||||
if (vertexCache[j] == idx0)
|
||||
{
|
||||
inCache++;
|
||||
break;
|
||||
}
|
||||
for (j=0; j<vertexCache.size(); j++)
|
||||
if (vertexCache[j] == idx1)
|
||||
{
|
||||
inCache++;
|
||||
break;
|
||||
}
|
||||
for (j=0; j<vertexCache.size(); j++)
|
||||
if (vertexCache[j] == idx2)
|
||||
{
|
||||
inCache++;
|
||||
break;
|
||||
}
|
||||
S32 currentScore = (inCache<<4) + numAdjacent[i];
|
||||
if (currentScore>bestScore)
|
||||
{
|
||||
bestFace = i;
|
||||
bestScore = currentScore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no face...
|
||||
if (bestFace<0)
|
||||
return false;
|
||||
|
||||
// start the strip -- add in standard order...may be changed later
|
||||
strip.numElements += 3;
|
||||
stripIndices.push_back(faceIndices[faces[bestFace].start + 0]);
|
||||
addToCache(stripIndices.last());
|
||||
stripIndices.push_back(faceIndices[faces[bestFace].start + 1]);
|
||||
addToCache(stripIndices.last());
|
||||
stripIndices.push_back(faceIndices[faces[bestFace].start + 2]);
|
||||
addToCache(stripIndices.last());
|
||||
|
||||
testCache(bestFace);
|
||||
|
||||
// adjust adjacency information
|
||||
for (j=startFace; j<endFace; j++)
|
||||
{
|
||||
if (j==bestFace || used[j])
|
||||
continue;
|
||||
if (adjacent.isSet(bestFace,j))
|
||||
{
|
||||
numAdjacent[j]--;
|
||||
if (numAdjacent[j]<0)
|
||||
{
|
||||
setExportError("Assertion failed when stripping (4)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mark face as used
|
||||
used[bestFace] = true;
|
||||
numAdjacent[bestFace] = 0;
|
||||
currentFace = bestFace;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Stripper::getVerts(S32 face, S32 & oldVert0, S32 & oldVert1, S32 & addVert)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return;
|
||||
|
||||
Vector<S32> prev;
|
||||
addVert = -1;
|
||||
TSDrawPrimitive & addFace = faces[face];
|
||||
U32 idx0 = faceIndices[addFace.start+0];
|
||||
U32 idx1 = faceIndices[addFace.start+1];
|
||||
U32 idx2 = faceIndices[addFace.start+2];
|
||||
S32 * cache = &vertexCache.last();
|
||||
if (idx0==*cache || idx0==*(cache-1) || idx0==*(cache-2))
|
||||
prev.push_back(idx0);
|
||||
else
|
||||
addVert = idx0;
|
||||
if (idx1==*cache || idx1==*(cache-1) || idx1==*(cache-2))
|
||||
prev.push_back(idx1);
|
||||
else
|
||||
addVert = idx1;
|
||||
if (idx2==*cache || idx2==*(cache-1) || idx2==*(cache-2))
|
||||
prev.push_back(idx2);
|
||||
else
|
||||
addVert = idx2;
|
||||
if (addVert<0 || prev.size()!=2)
|
||||
{
|
||||
setExportError("Assertion failed when stripping (5)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx1==addVert)
|
||||
{
|
||||
// swap order of oldVerts
|
||||
oldVert0 = prev[1];
|
||||
oldVert1 = prev[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// keep order
|
||||
oldVert0 = prev[0];
|
||||
oldVert1 = prev[1];
|
||||
}
|
||||
}
|
||||
|
||||
// assumes start + 0,1,2 is a triangle or first 3 indices of a strip
|
||||
void Stripper::rotateFace(S32 start, Vector<U16> & indices)
|
||||
{
|
||||
U32 tmp = indices[start];
|
||||
indices[start] = indices[start+1];
|
||||
indices[start+1] = indices[start+2];
|
||||
indices[start+2] = tmp;
|
||||
|
||||
S32 * cache = &vertexCache.last();
|
||||
tmp = *(cache-2);
|
||||
*(cache-2) = *(cache-1);
|
||||
*(cache-1) = *cache;
|
||||
*cache = tmp;
|
||||
|
||||
testCache(currentFace);
|
||||
}
|
||||
|
||||
bool Stripper::swapNeeded(S32 oldVert0, S32 oldVert1)
|
||||
{
|
||||
S32 * cache = &vertexCache.last();
|
||||
|
||||
return (*cache!=oldVert0 || *(cache-1)!=oldVert1) && (*cache!=oldVert1 || *(cache-1)!=oldVert0);
|
||||
|
||||
// Long form:
|
||||
// if ( (*cache==oldVert0 && *(cache-1)==oldVert1) || (*cache==oldVert1 && *(cache-1)==oldVert0) )
|
||||
// return false;
|
||||
// else
|
||||
// return true;
|
||||
}
|
||||
|
||||
F32 Stripper::getScore(S32 face, bool ignoreOrder)
|
||||
{
|
||||
// score face depending on following criteria:
|
||||
// -- select face with fewest adjacencies?
|
||||
// -- select face that continues strip without swap?
|
||||
// -- select face with new vertex already in cache?
|
||||
// weighting of each criteria controlled by adjacencyWeight, noswapWeight, and alreadyCachedWeight
|
||||
// if ignoreOrder is true, don't worry about swaps
|
||||
|
||||
if (face<0)
|
||||
return -100000.0f;
|
||||
|
||||
// get the 2 verts from the last face and the 1 new vert
|
||||
S32 oldVert0, oldVert1, addVert;
|
||||
getVerts(face, oldVert0, oldVert1, addVert);
|
||||
|
||||
F32 score = 0.0f;
|
||||
|
||||
// fewer adjacent faces the better...
|
||||
score -= numAdjacent[face] * adjacencyWeight;
|
||||
|
||||
// reward if no swap needed...don't worry about order (only same facing faces get here)
|
||||
if (!ignoreOrder && !swapNeeded(oldVert0,oldVert1))
|
||||
score += noswapWeight;
|
||||
|
||||
// if new vertex in the cache, add the already in cache bonus...
|
||||
for (S32 i=0;i<vertexCache.size();i++)
|
||||
if (vertexCache[i]==addVert)
|
||||
{
|
||||
score += alreadyCachedWeight;
|
||||
break;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
bool Stripper::faceHasEdge(S32 face, U32 idx0, U32 idx1)
|
||||
{
|
||||
// return true if face has edge idx0, idx1 (in that order)
|
||||
S32 start = faces[face].start;
|
||||
U32 vi0 = faceIndices[start+0];
|
||||
U32 vi1 = faceIndices[start+1];
|
||||
U32 vi2 = faceIndices[start+2];
|
||||
|
||||
if ( (vi0==idx0 && vi1==idx1) || (vi1==idx0 && vi2==idx1) || (vi2==idx0 && vi0==idx1) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Stripper::getAdjacentFaces(S32 startFace, S32 endFace, S32 face, S32 & face0, S32 & face1, S32 & face2)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return;
|
||||
|
||||
// we return one face per edge...ties go to face with fewest adjacencies
|
||||
S32 adj0=-1,adj1=-1,adj2=-1;
|
||||
face0=face1=face2=-1;
|
||||
|
||||
U32 idx0 = faceIndices[faces[face].start + 0];
|
||||
U32 idx1 = faceIndices[faces[face].start + 1];
|
||||
U32 idx2 = faceIndices[faces[face].start + 2];
|
||||
for (S32 i=startFace; i<endFace; i++)
|
||||
{
|
||||
if (i==face || used[i])
|
||||
continue;
|
||||
|
||||
if (!adjacent.isSet(face,i))
|
||||
continue;
|
||||
|
||||
// which edge is this face on
|
||||
if (faceHasEdge(i,idx1,idx0))
|
||||
{
|
||||
if (adj0<0 || numAdjacent[i]<adj0)
|
||||
{
|
||||
face0 = i;
|
||||
adj0 = numAdjacent[i];
|
||||
}
|
||||
}
|
||||
else if (faceHasEdge(i,idx2,idx1))
|
||||
{
|
||||
if (adj1<0 || numAdjacent[i]<adj1)
|
||||
{
|
||||
face1 = i;
|
||||
adj1 = numAdjacent[i];
|
||||
}
|
||||
}
|
||||
else if (faceHasEdge(i,idx0,idx2))
|
||||
{
|
||||
if (adj2<0 || numAdjacent[i]<adj2)
|
||||
{
|
||||
face2 = i;
|
||||
adj2 = numAdjacent[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setExportError("Assertion failed when stripping (6)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Stripper::stripLongEnough(S32 startFace, S32 endFace)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return false;
|
||||
|
||||
if (!limitStripLength)
|
||||
return false;
|
||||
|
||||
// simulate stopping the strip here and continuing for cacheSize+simK more rounds
|
||||
Stripper strip0(*this);
|
||||
strip0.setLimitStripLength(false);
|
||||
strip0.resetCacheMisses();
|
||||
strip0.continueStrip(startFace,endFace,cacheSize+simK,0);
|
||||
U32 stop0 = strip0.getCacheMisses();
|
||||
|
||||
// re-simulate previous best length (-1)
|
||||
U32 bestMisses;
|
||||
if (--bestLength<2)
|
||||
bestLength = 1;
|
||||
Stripper stripper(*this);
|
||||
stripper.setLimitStripLength(false);
|
||||
stripper.resetCacheMisses();
|
||||
stripper.continueStrip(startFace,endFace,cacheSize+simK,bestLength);
|
||||
bestMisses = stripper.getCacheMisses();
|
||||
if (bestMisses<=stop0)
|
||||
return false;
|
||||
|
||||
for (S32 i=1; i<cacheSize+simK; i++)
|
||||
{
|
||||
if (i==bestLength)
|
||||
continue;
|
||||
|
||||
Stripper stripper(*this);
|
||||
stripper.setLimitStripLength(false);
|
||||
stripper.resetCacheMisses();
|
||||
S32 underShoot = stripper.continueStrip(startFace,endFace,cacheSize+simK,i);
|
||||
U32 misses = stripper.getCacheMisses();
|
||||
if (misses<bestMisses)
|
||||
{
|
||||
bestMisses = misses;
|
||||
bestLength = i - underShoot;
|
||||
}
|
||||
if (misses<=stop0)
|
||||
return false;
|
||||
// undershoot is how much we missed our restart target by...i.e., if we wanted
|
||||
// to restart after 5 faces and underShoot is 1, then we restarted after 4.
|
||||
// If undershoot is positive, then we're going to end up restarting at the same
|
||||
// place on future iterations, so break out of the loop now to save time
|
||||
if (underShoot>0)
|
||||
break;
|
||||
}
|
||||
|
||||
// survived the gauntlet...
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Stripper::canGo(S32 face)
|
||||
{
|
||||
if (face<0)
|
||||
return false;
|
||||
|
||||
U32 idx0 = faceIndices[faces[face].start + 0];
|
||||
U32 idx1 = faceIndices[faces[face].start + 1];
|
||||
U32 idx2 = faceIndices[faces[face].start + 2];
|
||||
|
||||
U32 last = stripIndices.last();
|
||||
if (last==idx0 || last==idx1 || last==idx2)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Stripper::addStrip(TSDrawPrimitive & strip, S32 startFace, S32 endFace)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (isError()) return false;
|
||||
|
||||
if (strip.numElements>3 && stripLongEnough(startFace,endFace))
|
||||
return false;
|
||||
|
||||
testCache(currentFace);
|
||||
|
||||
// get unused faces adjacent to current face (choose only faces pointing same way)
|
||||
// if more than one face adjacent on an edge (unusual case) chooses one with lowest adjacency count
|
||||
S32 face0, face1, face2;
|
||||
getAdjacentFaces(startFace,endFace,currentFace,face0,face1,face2);
|
||||
|
||||
// one more check -- make sure most recent vert is in face
|
||||
// this can happen in exceptional case where we "back up"
|
||||
// using common edge between previous two faces, but not the
|
||||
// previous face (a v-junction?)
|
||||
bool bad0,bad1,bad2;
|
||||
bad0=bad1=bad2=false;
|
||||
if (strip.numElements!=3 && !canGo(face0))
|
||||
face0=-1;
|
||||
if (strip.numElements!=3 && !canGo(face1))
|
||||
face1=-1;
|
||||
if (strip.numElements!=3 && !canGo(face2))
|
||||
face2=-1;
|
||||
|
||||
if (face0<0 && face1<0 && face2<0)
|
||||
return false;
|
||||
|
||||
testCache(currentFace);
|
||||
|
||||
// score faces, choose highest score
|
||||
F32 score0 = getScore(face0,strip.numElements==3);
|
||||
F32 score1 = getScore(face1,strip.numElements==3);
|
||||
F32 score2 = getScore(face2,strip.numElements==3);
|
||||
S32 bestFace = -1;
|
||||
if (score0>=score1 && score0>=score2)
|
||||
bestFace = face0;
|
||||
else if (score1>=score0 && score1>=score2)
|
||||
bestFace = face1;
|
||||
else if (score2>=score1 && score2>=score0)
|
||||
bestFace = face2;
|
||||
|
||||
testCache(currentFace);
|
||||
|
||||
// add new element
|
||||
S32 oldVert0, oldVert1, addVert;
|
||||
getVerts(bestFace,oldVert0,oldVert1,addVert);
|
||||
|
||||
testCache(currentFace);
|
||||
|
||||
if (strip.numElements==3)
|
||||
{
|
||||
testCache(currentFace);
|
||||
|
||||
// special case...rotate previous element until we can add this face
|
||||
U32 doh=0;
|
||||
while (swapNeeded(oldVert0,oldVert1))
|
||||
{
|
||||
rotateFace(strip.start,stripIndices);
|
||||
if (++doh==3)
|
||||
{
|
||||
setExportError("Assertion error while stripping: infinite loop");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
stripIndices.push_back(addVert);
|
||||
addToCache(addVert);
|
||||
strip.numElements++;
|
||||
|
||||
testCache(bestFace);
|
||||
}
|
||||
else
|
||||
{
|
||||
testCache(currentFace);
|
||||
|
||||
if (swapNeeded(oldVert0,oldVert1))
|
||||
{
|
||||
U32 sz = stripIndices.size();
|
||||
U32 dup = stripIndices[sz-3];
|
||||
stripIndices.insert(sz-1);
|
||||
stripIndices[sz-1] = dup;
|
||||
addToCache(dup,1);
|
||||
strip.numElements++;
|
||||
|
||||
testCache(-1);
|
||||
}
|
||||
|
||||
stripIndices.push_back(addVert);
|
||||
strip.numElements++;
|
||||
addToCache(addVert);
|
||||
|
||||
testCache(bestFace);
|
||||
}
|
||||
|
||||
// add other faces to recentFaces list
|
||||
if (face0>=0 && face0!=bestFace)
|
||||
recentFaces.push_back(face0);
|
||||
if (face1>=0 && face1!=bestFace)
|
||||
recentFaces.push_back(face1);
|
||||
if (face2>=0 && face2!=bestFace)
|
||||
recentFaces.push_back(face2);
|
||||
|
||||
// adjust adjacency information
|
||||
for (S32 j=startFace; j<endFace; j++)
|
||||
{
|
||||
if (j==bestFace || used[j])
|
||||
continue;
|
||||
if (adjacent.isSet(bestFace,j))
|
||||
{
|
||||
numAdjacent[j]--;
|
||||
if (numAdjacent[j]<0)
|
||||
{
|
||||
setExportError("Assertion failed when stripping (7)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mark face as used
|
||||
used[bestFace] = true;
|
||||
numAdjacent[bestFace] = 0;
|
||||
currentFace = bestFace;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
87
tools/max2dtsExporter/stripper.h
Executable file
87
tools/max2dtsExporter/stripper.h
Executable file
@@ -0,0 +1,87 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _BITMATRIX_H_
|
||||
#include "core/bitMatrix.h"
|
||||
#endif
|
||||
|
||||
class Stripper
|
||||
{
|
||||
Vector<S32> numAdjacent;
|
||||
Vector<bool> used;
|
||||
BitMatrix adjacent;
|
||||
Vector<S32> vertexCache;
|
||||
Vector<S32> recentFaces;
|
||||
S32 currentFace;
|
||||
bool limitStripLength;
|
||||
S32 bestLength;
|
||||
U32 cacheMisses;
|
||||
|
||||
Vector<TSDrawPrimitive> strips;
|
||||
Vector<U16> stripIndices;
|
||||
|
||||
Vector<TSDrawPrimitive> & faces;
|
||||
Vector<U16> & faceIndices;
|
||||
|
||||
void clearCache();
|
||||
void addToCache(S32 vertexIndex);
|
||||
void addToCache(S32 vertexIndex, U32 posFromBack);
|
||||
|
||||
void getVerts(S32 face, S32 & oldVert0, S32 & oldVert1, S32 & addVert);
|
||||
void rotateFace(S32 start, Vector<U16> & indices);
|
||||
bool swapNeeded(S32 oldVert0, S32 oldVert1);
|
||||
F32 getScore(S32 face, bool ignoreOrder);
|
||||
bool faceHasEdge(S32 face, U32 idx0, U32 idx1);
|
||||
void getAdjacentFaces(S32 startFace, S32 endFace, S32 face, S32 & face0, S32 & face1, S32 & face2);
|
||||
|
||||
void setAdjacency(S32 startFace, S32 endFace);
|
||||
bool startStrip(TSDrawPrimitive & strip, S32 startFace, S32 endFace);
|
||||
bool addStrip(TSDrawPrimitive & strip, S32 startFace, S32 endFace);
|
||||
bool stripLongEnough(S32 startFace, S32 endFace);
|
||||
|
||||
void testCache(S32 addedFace);
|
||||
bool canGo(S32 face);
|
||||
|
||||
void makeStripsB(); // makeStrips() from faces...assumes all faces have same material index
|
||||
void copyParams(Stripper *from);
|
||||
|
||||
char * errorStr;
|
||||
void setExportError(const char * str) { errorStr = errorStr ? errorStr : dStrdup(str); }
|
||||
|
||||
public:
|
||||
|
||||
Stripper(Vector<TSDrawPrimitive> & faces, Vector<U16> & indices);
|
||||
Stripper(Stripper &);
|
||||
~Stripper();
|
||||
|
||||
void makeStrips();
|
||||
S32 continueStrip(S32 startFace, S32 endFace, S32 len, S32 restart); // used for simulation...
|
||||
void getStrips(Vector<TSDrawPrimitive> & s, Vector<U16> & si) { s=strips; si=stripIndices; }
|
||||
|
||||
void setLimitStripLength(bool lim) { limitStripLength = lim; }
|
||||
void resetCacheMisses() { cacheMisses = 0; }
|
||||
U32 getCacheMisses() { return cacheMisses; }
|
||||
|
||||
const char * getError() { return errorStr; }
|
||||
bool isError() { return errorStr!=NULL; }
|
||||
|
||||
// adjust strip building strategy
|
||||
static F32 adjacencyWeight;
|
||||
static F32 noswapWeight;
|
||||
static F32 alreadyCachedWeight;
|
||||
static U32 cacheSize;
|
||||
static U32 simK;
|
||||
};
|
||||
|
||||
|
||||
1260
tools/max2dtsExporter/translucentSort.cc
Executable file
1260
tools/max2dtsExporter/translucentSort.cc
Executable file
File diff suppressed because it is too large
Load Diff
107
tools/max2dtsExporter/translucentSort.h
Executable file
107
tools/max2dtsExporter/translucentSort.h
Executable file
@@ -0,0 +1,107 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TRANS_SORT
|
||||
#define _TRANS_SORT
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TSINTEGERSET_H_
|
||||
#include "ts/tsIntegerSet.h"
|
||||
#endif
|
||||
#ifndef _TSSORTEDMESH_H_
|
||||
#include "ts/tsSortedMesh.h"
|
||||
#endif
|
||||
|
||||
struct TSDrawPrimitive;
|
||||
class TSIntegerSet;
|
||||
|
||||
class TranslucentSort
|
||||
{
|
||||
Vector<TSIntegerSet*> frontClusters;
|
||||
Vector<TSIntegerSet*> backClusters;
|
||||
Vector<S32> middleCluster;
|
||||
|
||||
Point3F splitNormal;
|
||||
F32 splitK;
|
||||
|
||||
S32 mNumBigFaces;
|
||||
S32 mMaxDepth;
|
||||
bool mZLayerUp;
|
||||
bool mZLayerDown;
|
||||
|
||||
S32 currentDepth;
|
||||
|
||||
TranslucentSort * frontSort;
|
||||
TranslucentSort * backSort;
|
||||
|
||||
struct FaceInfo
|
||||
{
|
||||
bool used;
|
||||
S32 priority;
|
||||
S32 parentFace;
|
||||
S32 childFace1;
|
||||
S32 childFace2;
|
||||
S32 childFace3;
|
||||
Point3F normal;
|
||||
F32 k;
|
||||
TSIntegerSet isInFrontOfMe;
|
||||
TSIntegerSet isBehindMe;
|
||||
TSIntegerSet isCutByMe;
|
||||
TSIntegerSet isCoplanarWithMe;
|
||||
};
|
||||
Vector<FaceInfo*> faceInfoList;
|
||||
Vector<FaceInfo*> saveFaceInfoList;
|
||||
|
||||
Vector<TSDrawPrimitive> & mFaces;
|
||||
Vector<U16> & mIndices;
|
||||
Vector<Point3F> & mVerts;
|
||||
Vector<Point3F> & mNorms;
|
||||
Vector<Point2F> & mTVerts;
|
||||
|
||||
void initFaces();
|
||||
void initFaceInfo(TSDrawPrimitive & face, FaceInfo & faceInfo, bool setPriority = true);
|
||||
void setFaceInfo(TSDrawPrimitive & face, FaceInfo & faceInfo);
|
||||
void clearFaces(TSIntegerSet &);
|
||||
void saveFaceInfo();
|
||||
void restoreFaceInfo();
|
||||
void addFaces(TSIntegerSet *, Vector<TSDrawPrimitive> & faces, Vector<U16> & indices, bool continueLast = false);
|
||||
void addFaces(Vector<TSIntegerSet *> &, Vector<TSDrawPrimitive> & faces, Vector<U16> & indices, bool continueLast = false);
|
||||
void addOrderedFaces(Vector<S32> &, Vector<TSDrawPrimitive> &, Vector<U16> & indices, bool continueLast = false);
|
||||
void splitFace(S32 faceIndex, Point3F normal, F32 k);
|
||||
void splitFace2(S32 faceIndex, Point3F normal, F32 k);
|
||||
void sort();
|
||||
|
||||
// routines for sorting faces when there is no perfect solution for all cases
|
||||
void copeSort(Vector<S32> &);
|
||||
void layerSort(Vector<S32> &, bool upFirst);
|
||||
|
||||
// these are for debugging
|
||||
bool anyInFrontOfPlane(Point3F normal, F32 k);
|
||||
bool anyBehindPlane(Point3F normal, F32 k);
|
||||
|
||||
//
|
||||
void generateClusters(Vector<TSSortedMesh::Cluster> & clusters, Vector<TSDrawPrimitive> & faces, Vector<U16> & indices, S32 retIndex = -1);
|
||||
|
||||
TranslucentSort(TranslucentSort *);
|
||||
TranslucentSort(Vector<TSDrawPrimitive> & faces,
|
||||
Vector<U16> & indices,
|
||||
Vector<Point3F> & verts,
|
||||
Vector<Point3F> & norms,
|
||||
Vector<Point2F> & tverts,
|
||||
S32 numBigFaces, S32 maxDepth, bool zLayerUp, bool zLayerDown);
|
||||
|
||||
~TranslucentSort();
|
||||
|
||||
public:
|
||||
|
||||
static void generateSortedMesh(TSSortedMesh * mesh, S32 numBigFaces, S32 maxDepth, bool zLayerUp, bool zLayerDown);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // _TRANS_SORT
|
||||
|
||||
80
tools/max2dtsExporterPlus/DllEntry.cpp
Executable file
80
tools/max2dtsExporterPlus/DllEntry.cpp
Executable file
@@ -0,0 +1,80 @@
|
||||
/**********************************************************************
|
||||
*<
|
||||
FILE: DllEntry.cpp
|
||||
|
||||
DESCRIPTION: Contains the Dll Entry stuff
|
||||
|
||||
CREATED BY:
|
||||
|
||||
HISTORY:
|
||||
|
||||
*> Copyright (c) 2000, All Rights Reserved.
|
||||
**********************************************************************/
|
||||
#include "max2dtsExporterPlus.h"
|
||||
#include "skinHelper.h"
|
||||
|
||||
extern ClassDesc2* GetMax2dtsExporterPlusDesc();
|
||||
|
||||
HINSTANCE hInstance;
|
||||
int controlsInit = FALSE;
|
||||
|
||||
// This function is called by Windows when the DLL is loaded. This
|
||||
// function may also be called many times during time critical operations
|
||||
// like rendering. Therefore developers need to be careful what they
|
||||
// do inside this function. In the code below, note how after the DLL is
|
||||
// loaded the first time only a few statements are executed.
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved)
|
||||
{
|
||||
hInstance = hinstDLL; // Hang on to this DLL's instance handle.
|
||||
|
||||
if (!controlsInit) {
|
||||
controlsInit = TRUE;
|
||||
InitCustomControls(hInstance); // Initialize MAX's custom controls
|
||||
InitCommonControls(); // Initialize Win95 controls
|
||||
}
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
// This function returns a string that describes the DLL and where the user
|
||||
// could purchase the DLL if they don't have it.
|
||||
__declspec( dllexport ) const TCHAR* LibDescription()
|
||||
{
|
||||
return GetString(IDS_LIBDESCRIPTION);
|
||||
}
|
||||
|
||||
// This function returns the number of plug-in classes this DLL
|
||||
// Must change this number when adding a new class
|
||||
__declspec( dllexport ) int LibNumberClasses()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
// This function returns the number of plug-in classes this DLL
|
||||
__declspec( dllexport ) ClassDesc* LibClassDesc(int i)
|
||||
{
|
||||
switch(i) {
|
||||
case 0: return GetMax2dtsExporterPlusDesc();
|
||||
case 1: return GetSkinHelperDesc();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// This function returns a pre-defined constant indicating the version of
|
||||
// the system under which it was compiled. It is used to allow the system
|
||||
// to catch obsolete DLLs.
|
||||
__declspec( dllexport ) ULONG LibVersion()
|
||||
{
|
||||
return VERSION_3DSMAX;
|
||||
}
|
||||
|
||||
TCHAR *GetString(int id)
|
||||
{
|
||||
static TCHAR buf[256];
|
||||
|
||||
if (hInstance)
|
||||
return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BIN
tools/max2dtsExporterPlus/max2dtsExporterPlus.aps
Executable file
BIN
tools/max2dtsExporterPlus/max2dtsExporterPlus.aps
Executable file
Binary file not shown.
210
tools/max2dtsExporterPlus/max2dtsExporterPlus.cpp
Executable file
210
tools/max2dtsExporterPlus/max2dtsExporterPlus.cpp
Executable file
@@ -0,0 +1,210 @@
|
||||
/**********************************************************************
|
||||
*<
|
||||
FILE: max2dtsExporterPlus.cpp
|
||||
|
||||
DESCRIPTION: Appwizard generated plugin
|
||||
|
||||
CREATED BY:
|
||||
|
||||
HISTORY:
|
||||
|
||||
*> Copyright (c) 2000, All Rights Reserved.
|
||||
**********************************************************************/
|
||||
|
||||
#include "max2dtsExporterPlus.h"
|
||||
#include "maxSceneEnum.h"
|
||||
#include "appConfig.h"
|
||||
#include <fstream>
|
||||
|
||||
|
||||
#define MAX2DTSEXPORTERPLUS_CLASS_ID Class_ID(0x612c5c7, 0x5924d160)
|
||||
|
||||
class Max2dtsExporterPlus : public SceneExport {
|
||||
public:
|
||||
|
||||
|
||||
static HWND hParams;
|
||||
|
||||
|
||||
int ExtCount(); // Number of extensions supported
|
||||
const TCHAR * Ext(int n); // Extension #n (i.e. "3DS")
|
||||
const TCHAR * LongDesc(); // Long ASCII description (i.e. "Autodesk 3D Studio File")
|
||||
const TCHAR * ShortDesc(); // Short ASCII description (i.e. "3D Studio")
|
||||
const TCHAR * AuthorName(); // ASCII Author name
|
||||
const TCHAR * CopyrightMessage(); // ASCII Copyright message
|
||||
const TCHAR * OtherMessage1(); // Other message #1
|
||||
const TCHAR * OtherMessage2(); // Other message #2
|
||||
unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301)
|
||||
void ShowAbout(HWND hWnd); // Show DLL's "About..." box
|
||||
|
||||
BOOL SupportsOptions(int ext, DWORD options);
|
||||
int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0);
|
||||
|
||||
void alert(const char * title, const char * message, BOOL suppressPrompts);
|
||||
|
||||
//Constructor/Destructor
|
||||
|
||||
Max2dtsExporterPlus();
|
||||
~Max2dtsExporterPlus();
|
||||
|
||||
};
|
||||
|
||||
|
||||
class Max2dtsExporterPlusClassDesc:public ClassDesc2 {
|
||||
public:
|
||||
int IsPublic() { return TRUE; }
|
||||
void * Create(BOOL loading = FALSE) { return new Max2dtsExporterPlus(); }
|
||||
const TCHAR * ClassName() { return GetString(IDS_CLASS_NAME); }
|
||||
SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
|
||||
Class_ID ClassID() { return MAX2DTSEXPORTERPLUS_CLASS_ID; }
|
||||
const TCHAR* Category() { return GetString(IDS_CATEGORY); }
|
||||
|
||||
const TCHAR* InternalName() { return _T("Max2dtsExporterPlus"); } // returns fixed parsable name (scripter-visible name)
|
||||
HINSTANCE HInstance() { return hInstance; } // returns owning module handle
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
static Max2dtsExporterPlusClassDesc Max2dtsExporterPlusDesc;
|
||||
ClassDesc2* GetMax2dtsExporterPlusDesc() { return &Max2dtsExporterPlusDesc; }
|
||||
|
||||
|
||||
BOOL CALLBACK Max2dtsExporterPlusOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
|
||||
static Max2dtsExporterPlus *imp = NULL;
|
||||
|
||||
switch(message) {
|
||||
case WM_INITDIALOG:
|
||||
imp = (Max2dtsExporterPlus *)lParam;
|
||||
CenterWindow(hWnd,GetParent(hWnd));
|
||||
return TRUE;
|
||||
|
||||
case WM_CLOSE:
|
||||
EndDialog(hWnd, 0);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
//--- Max2dtsExporterPlus -------------------------------------------------------
|
||||
Max2dtsExporterPlus::Max2dtsExporterPlus()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Max2dtsExporterPlus::~Max2dtsExporterPlus()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int Max2dtsExporterPlus::ExtCount()
|
||||
{
|
||||
// Returns the number of file name extensions supported by the plug-in.
|
||||
return 1;
|
||||
}
|
||||
|
||||
const TCHAR *Max2dtsExporterPlus::Ext(int n)
|
||||
{
|
||||
// Return the 'i-th' file name extension
|
||||
return _T("DTS");
|
||||
}
|
||||
|
||||
const TCHAR *Max2dtsExporterPlus::LongDesc()
|
||||
{
|
||||
//Return long ASCII description
|
||||
return _T("Torque game engine dts shape file");
|
||||
}
|
||||
|
||||
const TCHAR *Max2dtsExporterPlus::ShortDesc()
|
||||
{
|
||||
//Return short ASCII description
|
||||
return _T("DTS Shape");
|
||||
}
|
||||
|
||||
const TCHAR *Max2dtsExporterPlus::AuthorName()
|
||||
{
|
||||
//Return ASCII Author name
|
||||
return _T("Clark Fagot");
|
||||
}
|
||||
|
||||
const TCHAR *Max2dtsExporterPlus::CopyrightMessage()
|
||||
{
|
||||
// Return ASCII Copyright message
|
||||
return _T("Copyright GarageGames");
|
||||
}
|
||||
|
||||
const TCHAR *Max2dtsExporterPlus::OtherMessage1()
|
||||
{
|
||||
//Return Other message #1 if any
|
||||
return _T("");
|
||||
}
|
||||
|
||||
const TCHAR *Max2dtsExporterPlus::OtherMessage2()
|
||||
{
|
||||
//Return other message #2 in any
|
||||
return _T("");
|
||||
}
|
||||
|
||||
unsigned int Max2dtsExporterPlus::Version()
|
||||
{
|
||||
//Return Version number * 100 (i.e. v3.01 = 301)
|
||||
return 100;
|
||||
}
|
||||
|
||||
void Max2dtsExporterPlus::ShowAbout(HWND hWnd)
|
||||
{
|
||||
// Optional
|
||||
}
|
||||
|
||||
BOOL Max2dtsExporterPlus::SupportsOptions(int ext, DWORD options)
|
||||
{
|
||||
// Decide which options to support. Simply return
|
||||
// true for each option supported by each Extension
|
||||
// the exporter supports.
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void Max2dtsExporterPlus::alert(const char * title, const char * message, BOOL suppressPrompts)
|
||||
{
|
||||
if (suppressPrompts)
|
||||
DTS::AppConfig::PrintDump(-1,message);
|
||||
else
|
||||
{
|
||||
TSTR str1(message);
|
||||
TSTR str2(title);
|
||||
MessageBox(GetActiveWindow(), str1, str2, MB_OK);
|
||||
}
|
||||
}
|
||||
|
||||
int Max2dtsExporterPlus::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options)
|
||||
{
|
||||
// Implement the actual file Export here and
|
||||
// return TRUE If the file is exported properly
|
||||
if (!DTS::AppConfig::SetDumpFile(name))
|
||||
alert("Dump file error",DTS::avar("Unable to create dumpfile for shape \"%s\".",name),suppressPrompts);
|
||||
|
||||
// Get config file if it exists...
|
||||
const char * configBase = i->GetCurFilePath();
|
||||
DTS::AppConfig::ReadConfigFile(configBase);
|
||||
|
||||
DTS::MaxSceneEnum maxSceneEnum(ei->theScene);
|
||||
DTS::Shape * shape = maxSceneEnum.processScene();
|
||||
if (!DTS::AppConfig::IsExportError())
|
||||
{
|
||||
std::ofstream os;
|
||||
os.open(name,std::ofstream::binary);
|
||||
shape->save(os);
|
||||
os.close();
|
||||
}
|
||||
DTS::AppConfig::CloseDumpFile();
|
||||
delete shape;
|
||||
|
||||
if (DTS::AppConfig::IsExportError())
|
||||
alert("Export error",DTS::AppConfig::GetExportError(),suppressPrompts);
|
||||
|
||||
return !DTS::AppConfig::IsExportError();
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user