2017-04-17 06:17:10 -06:00

1042 lines
30 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "max2mapExporter/exporter.h"
#include "platformWin32/winConsole.h"
#include "dgl/materialList.h"
#include "dgl/gBitmap.h"
// static data
static HINSTANCE gInstance;
static PluginDescription gPluginDesc;
static Interface * gInterface;
static EditGeometry * gWorkingGeometry;
static CollisionDetail * gCollisionDetail;
static MapProperties gMapProperties;
static const S32 gcProgramVersion = 69;
static const F32 gcCenterOffsetScale = 0.1f;
static const F32 gcMinCenterOffset = 2.f;
static const F32 gcMaxCenterOffset = 200.f;
static const F32 gcPlaneNormThresh = 0.999f;
static const F32 gcPlaneDistThresh = 0.001f;
// ------------------------------------------------------------------
namespace // helper functions
{
//
Point3F toPoint3F(const Point3 & pnt){return(Point3F(pnt.x, pnt.y, pnt.z));}
// returns (a mod b)
F32 floatMod(F32 a, F32 b)
{
F32 multiple = mFabs(a / b);
F32 remainder = multiple - mFloor(multiple);
return(a < 0 ? -(remainder * b) : (remainder * b));
}
//
void writeString(FileStream & file, const char * str)
{
file.write(dStrlen(str), str);
}
//------------------------------------------------------------------------------
// from Numerical Recipes in C
#define idx(i,j) (i*4 + j)
void gaussjInverse(MatrixF & mat)
{
U32 indxc[4];
U32 indxr[4];
U32 ipiv[4];
F32 * a = (F32*)mat;
S32 i, icol, irow, j, k, l, ll;
F32 big, dum, pivinv, temp;
for(j = 0; j < 4; j++) ipiv[j] = 0;
for(i = 0; i < 4; i++)
{
big = 0.f;
for(j = 0; j < 4; j++)
if(ipiv[j] != 1)
for(k = 0; k < 4; k++)
{
if(ipiv[k] == 0)
{
if(mFabs(a[idx(j,k)]) >= big)
{
big = mFabs(a[idx(j,k)]);
irow = j;
icol = k;
}
}
else if(ipiv[k] > 1)
AssertFatal(0, "Doh!");
}
++(ipiv[icol]);
if(irow != icol)
{
for(l = 0; l < 4; l++)
{
F32 tmp = a[idx(irow, l)];
a[idx(irow, l)] = a[idx(icol, l)];
a[idx(icol, l)] = tmp;
}
}
indxr[i] = irow;
indxc[i] = icol;
if(a[idx(icol, icol)] == 0.0)
AssertFatal(0, "Doh!");
pivinv = 1.0 / a[idx(icol, icol)];
a[idx(icol, icol)] = 1.f;
for(l = 0; l < 4; l++) a[idx(icol, l)] *= pivinv;
for(ll = 0; ll < 4; ll++)
if(ll != icol)
{
dum = a[idx(ll, icol)];
a[idx(ll, icol)] = 0.f;
for(l = 0; l < 4; l++) a[idx(ll, l)] -= a[idx(icol, l)] * dum;
}
}
for(l = 3; l >= 0; l--)
{
if(indxr[l] != indxc[l])
for(k = 0; k < 4; k++)
{
F32 tmp = a[idx(k, indxr[l])];
a[idx(k, indxr[l])] = a[idx(k, indxc[l])];
a[idx(k, indxc[l])] = tmp;
}
}
}
// size of the material
Point2I getMaterialSize(Mtl * material)
{
Texmap * texMap = material->GetSubTexmap(ID_DI);
Bitmap * bitmap = ((BitmapTex*)texMap)->GetBitmap(0);
Point2I size(bitmap->Width(), bitmap->Height());
return(size);
}
// get the base name of the material
const char * getMaterialName(Mtl * material)
{
Texmap * texMap = material->GetSubTexmap(ID_DI);
char buf[1024];
dSprintf(buf, sizeof(buf), ((BitmapTex*)texMap)->GetMapName());
// just get the base name...
char * cur = dStrrchr(buf, '.');
if(!cur)
return(0);
*cur = '\0';
cur--;
while(1)
{
// end of filebase?
if(*cur == '\\' || *cur == '/')
{
cur++;
break;
}
// bad char?
if(*cur == ' ')
return(0);
// only base?
if(cur == buf)
break;
cur--;
}
// good length?
U32 len = dStrlen(cur);
if(!len || len > 15)
return(0);
return(StringTable->insert(cur));
}
//
bool isValidMaterial(StdMat * material)
{
// grab the diffuse map
Texmap * texMap = material->GetSubTexmap(ID_DI);
// valid name?
if(!getMaterialName(material))
return(false);
// make sure a bitmap
if(texMap && texMap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0))
{
Bitmap * bitmap = ((BitmapTex*)texMap)->GetBitmap(0);
// make sure has a name
if(!((BitmapTex*)texMap)->GetMapName())
return(false);
// make sure a valid bitmap width/height
if(bitmap->Width() < 0x10 || bitmap->Height() < 0x10)
return(false);
if(bitmap->Width() & 0x07 || bitmap->Height() & 0x07)
return(false);
return(true);
}
return(false);
}
};
// ------------------------------------------------------------------
// MapProperties
// ------------------------------------------------------------------
MapProperties::MapProperties()
{
mWadName = 0;
mGeometryScale = 64.f;
mLightingScale = 64.f;
mAmbientColor.set(0,0,0);
mEmergencyAmbientColor.set(0,0,0);
}
// ------------------------------------------------------------------
// DetailEnumerator
// ------------------------------------------------------------------
DetailEnumerator::DetailEnumerator()
{
}
DetailEnumerator::~DetailEnumerator()
{
for(U32 i = 0; i < mDetails.size(); i++)
delete mDetails[i];
}
// ------------------------------------------------------------------
// enumerates the scene creating details and assigning SceneObjects to them
void DetailEnumerator::enumScene(IScene * scene)
{
scene->EnumTree(this);
}
// ------------------------------------------------------------------
int DetailEnumerator::callback(INode * node)
{
const char * name = node->GetName();
if(node->IsHidden())
return(TREE_CONTINUE);
Object * obj = node->EvalWorldState(gInterface->GetTime()).obj;
if(!obj->IsRenderable())
return(TREE_CONTINUE);
if(!obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID,0)))
return(TREE_CONTINUE);
// get the detail this belongs in
EditGeometry * detail = getNodeDetail(node);
if(!detail)
{
// collision hull?
if(!isCollisionHullObject(node))
return(TREE_CONTINUE);
}
TriObject * triObj = (TriObject*)obj->ConvertToType(gInterface->GetTime(),
Class_ID(TRIOBJ_CLASS_ID, 0));
// ownership of the TriObject will be transfered
if(detail)
detail->addSceneObject(new SceneObject(TriObjRef(triObj, triObj != obj), node));
else
mCollisionDetail.addConvexHullObject(new ConvexHullObject(TriObjRef(triObj, triObj != obj), node));
return(TREE_CONTINUE);
}
// ------------------------------------------------------------------
EditGeometry * DetailEnumerator::getNodeDetail(INode * node)
{
// search for a detail node
while(!node->IsRootNode())
{
char buf[80];
dSprintf(buf, sizeof(buf), "%s", node->GetName());
if(!dStrnicmp(buf, "detail_", 7))
return(createDetail(dAtoi(buf+7)));
node = node->GetParentNode();
}
return(0);
}
bool DetailEnumerator::isCollisionHullObject(INode * node)
{
while(!node->IsRootNode())
{
if(!dStrnicmp(node->GetName() ? node->GetName() : "", "collision", 9))
return(true);
node = node->GetParentNode();
}
return(false);
}
// ------------------------------------------------------------------
EditGeometry * DetailEnumerator::createDetail(U32 minPixels)
{
// search for it
for(U32 i = 0; i < mDetails.size(); i++)
if(mDetails[i]->mMinPixels == minPixels)
return(mDetails[i]);
// create a new one
EditGeometry * detail = new EditGeometry;
detail->mMinPixels = minPixels;
mDetails.push_back(detail);
return(detail);
}
// ------------------------------------------------------------------
static S32 QSORT_CALLBACK detailCompare(const void * a, const void * b)
{
return(((S32)(*(EditGeometry**)b)->mMinPixels) -
((S32)(*(EditGeometry**)a)->mMinPixels));
}
void DetailEnumerator::sortDetails()
{
dQsort((void*)&mDetails[0], mDetails.size(), sizeof(EditGeometry*), detailCompare);
}
bool DetailEnumerator::sequentialDetails()
{
for(U32 i = 0; i < (mDetails.size() - 1); i++)
if(mDetails[i]->mMinPixels <= mDetails[i+1]->mMinPixels)
return(false);
return(true);
}
// ------------------------------------------------------------------
// Class TriObjRef
// ------------------------------------------------------------------
TriObjRef::TriObjRef(TriObject * obj, bool ownObject) :
mRef(obj),
mOwnObject(ownObject)
{
}
TriObjRef::TriObjRef(TriObjRef & rhs)
{
mOwnObject = rhs.mOwnObject;
mRef = rhs.mRef;
rhs.mOwnObject = false;
}
TriObjRef::~TriObjRef()
{
if(mOwnObject)
delete mRef;
}
// ------------------------------------------------------------------
// Class ObjectBase
// ------------------------------------------------------------------
ObjectBase::ObjectBase(TriObjRef objRef, INode * node) :
mTriObjRef(objRef),
mNode(node)
{
}
// ------------------------------------------------------------------
// Class SceneObject
// ------------------------------------------------------------------
SceneObject::SceneObject(TriObjRef objRef, INode * node) :
ObjectBase(objRef, node)
{
}
// ------------------------------------------------------------------
bool SceneObject::process()
{
// do materials first
Mtl * material = mNode->GetMtl();
if(!material)
return(false);
Vector<StdMat *> materials;
Vector<U32> materialIDs;
// single material
if(material->ClassID() == Class_ID(DMTL_CLASS_ID, 0))
{
if(!isValidMaterial((StdMat*)material))
return(false);
materials.push_back((StdMat*)material);
materialIDs.push_back(gWorkingGeometry->insertMaterial(material));
}
// multiple materials
if(material->ClassID() == Class_ID(MULTI_CLASS_ID, 0))
{
U32 numMat = material->NumSubMtls();
for(U32 i = 0; i < numMat; i++)
{
Mtl * subMat = material->GetSubMtl(i);
// check it
if(subMat->ClassID() != Class_ID(DMTL_CLASS_ID, 0))
return(false);
if(!isValidMaterial((StdMat*)subMat))
return(false);
materials.push_back((StdMat*)subMat);
materialIDs.push_back(gWorkingGeometry->insertMaterial(subMat));
}
}
if(!materials.size())
return(false);
// get the mesh and walk the faces
Mesh & mesh = mTriObjRef->GetMesh();
Matrix3 nodeTM = mNode->GetNodeTM(gInterface->GetTime());
Point3 offset = mNode->GetObjOffsetPos();
// create all the surfaces from the mesh
for(U32 i = 0; i < mesh.numFaces; i++)
{
TVFace & texFace = mesh.tvFace[i];
Face & face = mesh.faces[i];
U32 matID = face.getMatID() % materials.size();
// create the surface and add to the current detail
Surface surface;
dMemset(&surface, 0, sizeof(Surface));
surface.materialIndex = materialIDs[matID];
// 'Face' is a triangular surface...
surface.winding.numIndices = 3;
Vector<Point3F> textureCoords;
// insert the vertices
for(U32 j = 0; j < surface.winding.numIndices; j++)
{
Point3 maxPnt = nodeTM.PointTransform(mesh.getVert(face.v[j]));
maxPnt += offset;
// scale it
maxPnt *= gMapProperties.mGeometryScale;
surface.winding.indices[j] = gWorkingGeometry->insertPoint(toPoint3F(maxPnt));
// get the texture coordinate
const UVVert & texVert = mesh.getTVert(texFace.t[j]);
textureCoords.push_back(toPoint3F(texVert));
textureCoords[j].z = 0;
}
// insert the plane - normal to empty
PlaneF plane(gWorkingGeometry->getPoint(surface.winding.indices[2]),
gWorkingGeometry->getPoint(surface.winding.indices[1]),
gWorkingGeometry->getPoint(surface.winding.indices[0]));
surface.planeIndex = gWorkingGeometry->insertPlane(plane);
// now do the texture stuff...
//---------------------------------------
// get some info
BitmapTex * bitmapTex = (BitmapTex*)materials[matID]->GetSubTexmap(ID_DI);
Bitmap * bitmap = bitmapTex->GetBitmap(gInterface->GetTime());
// grab the points for easy access
Vector<Point3F> mappedCoords;
mappedCoords.setSize(surface.winding.numIndices);
for(U32 l = 0; l < surface.winding.numIndices; l++)
{
const Point3F & pnt = gWorkingGeometry->getPoint(surface.winding.indices[l]);
mappedCoords[l] = pnt;
}
MatrixF mat;
Point4F sb, tb;
sb.w = tb.w = 0;
for(U32 row = 0; row < 3; row++)
{
mat.setRow(row, mappedCoords[row]);
((F32*)sb)[row] = textureCoords[row].x;
((F32*)tb)[row] = textureCoords[row].y;
}
Point3F norm;
Point3F na = mappedCoords[1] - mappedCoords[0];
Point3F nb = mappedCoords[2] - mappedCoords[1];
na.normalize();
nb.normalize();
mCross(na, nb, &norm);
mat.setRow(3, norm);
mat.setColumn(3, Point4F(1,1,1,0));
// inverse
gaussjInverse(mat);
mat.mul(sb);
mat.mul(tb);
// set the gen's
surface.texGenX.x = sb.x;
surface.texGenX.y = sb.y;
surface.texGenX.z = sb.z;
surface.texGenX.d = sb.w;
surface.texGenY.x = -tb.x;
surface.texGenY.y = -tb.y;
surface.texGenY.z = -tb.z;
surface.texGenY.d = -tb.w;
gWorkingGeometry->mSurfaces.push_back(surface);
}
return(true);
}
// ------------------------------------------------------------------
// Class ConvexHullObject
// ------------------------------------------------------------------
ConvexHullObject::ConvexHullObject(TriObjRef objRef, INode * node) :
ObjectBase(objRef, node)
{
mIsConvex = false;
}
void ConvexHullObject::process()
{
AssertFatal(!mPlanes.size(), "ConvexHullObject::process: already processed");
// grab all the planes
Mesh & mesh = mTriObjRef->GetMesh();
Matrix3 nodeTM = mNode->GetNodeTM(gInterface->GetTime());
Point3 offset = mNode->GetObjOffsetPos();
// create all the surfaces from the mesh
for(U32 i = 0; i < mesh.numFaces; i++)
{
Face & face = mesh.faces[i];
Point3F pnts[3];
for(U32 j = 0; j < 3; j++)
{
Point3 maxPnt = nodeTM.PointTransform(mesh.getVert(face.v[j]));
maxPnt += offset;
maxPnt *= gMapProperties.mGeometryScale;
pnts[j] = toPoint3F(maxPnt);
}
PlaneF plane(pnts[0], pnts[1], pnts[2]);
// dont insert duplicate planes...
bool found = false;
for(S32 k = mPlanes.size() - 1; (k >= 0) && !found; k--)
{
if((mDot(plane, mPlanes[k]) > gcPlaneNormThresh) &&
(mFabs(plane.d - mPlanes[k].d) < gcPlaneDistThresh))
found = true;
}
if(!found)
{
mPlanes.push_back(plane);
mPoints.push_back(pnts[0]);
mPoints.push_back(pnts[1]);
mPoints.push_back(pnts[2]);
}
}
// converting into a MNMesh does not correctly solve convex problem.. so we
// are to assume that this is convex
if(mPlanes.size() >= 4)
mIsConvex = true;
}
// ------------------------------------------------------------------
// Class CollisionDetail
// ------------------------------------------------------------------
CollisionDetail::~CollisionDetail()
{
for(S32 i = mConvexHulls.size() - 1; i >= 0; i--)
delete mConvexHulls[i];
}
void CollisionDetail::processConvexHullObjects()
{
for(S32 i = mConvexHulls.size() - 1; i >= 0; i--)
mConvexHulls[i]->process();
}
void CollisionDetail::export(FileStream & file)
{
if(!mConvexHulls.size())
return;
writeString(file, "{\n");
writeString(file, "\"classname\" \"collision\"\n");
char buf[1024];
for(U32 i = 0; i < mConvexHulls.size(); i++)
{
ConvexHullObject * hull = mConvexHulls[i];
if(!hull->mIsConvex)
continue;
writeString(file, "{\n");
for(U32 j = 0; j < hull->mPlanes.size(); j++)
{
U32 k = (j * 3);
dSprintf(buf, sizeof(buf), "( %d %d %d ) ( %d %d %d ) ( %d %d %d ) NULL [ 1 0 0 0 ] [ 0 1 0 0 ] 0 1 1\n",
(S32)hull->mPoints[k+2].x, (S32)hull->mPoints[k+2].y, (S32)hull->mPoints[k+2].z,
(S32)hull->mPoints[k+1].x, (S32)hull->mPoints[k+1].y, (S32)hull->mPoints[k+1].z,
(S32)hull->mPoints[k].x, (S32)hull->mPoints[k].y, (S32)hull->mPoints[k].z);
writeString(file, buf);
}
writeString(file, "}\n");
}
writeString(file, "}\n");
}
// ------------------------------------------------------------------
// DLL Entry point
// ------------------------------------------------------------------
BOOL WINAPI DllMain(HINSTANCE hInst, ULONG dwReason, LPVOID pReserved)
{
static bool initialized = false;
if(!initialized)
{
initialized = true;
_StringTable::create();
}
gInstance = hInst;
return(TRUE);
}
DLLEXPORT int LibNumberClasses()
{
return(1);
}
DLLEXPORT ClassDesc * LibClassDesc(int index)
{
if(index != 0)
return(0);
return(&gPluginDesc);
}
DLLEXPORT const TCHAR * LibDescription()
{
return(_T("WorldCraft 3.3 Map Exporter"));
}
DLLEXPORT ULONG LibVersion()
{
return(VERSION_3DSMAX);
}
//------------------------------------------------------------------------------
// Class Detail
//------------------------------------------------------------------------------
EditGeometry::EditGeometry()
{
}
EditGeometry::~EditGeometry()
{
// remove the scene info
for(U32 i = 0; i < mSceneObjects.size(); i++)
delete mSceneObjects[i];
}
void EditGeometry::processSceneObjects()
{
// have each object create it's surfaces...
for(U32 i = 0; i < mSceneObjects.size(); i++)
mSceneObjects[i]->process();
}
//------------------------------------------------------------------------------
U32 EditGeometry::insertMaterial(Mtl * material)
{
const char * matName = getMaterialName(material);
for(U32 i = 0; i < mMaterialNames.size(); i++)
if(!dStricmp(matName, mMaterialNames[i]))
return(i);
// insert the name and size
mMaterialNames.push_back(matName);
mMaterialSizes.push_back(getMaterialSize(material));
return(mMaterialNames.size() - 1);
}
U32 EditGeometry::insertPoint(const Point3F & pnt)
{
for(U32 i = 0; i < mPoints.size(); i++)
if(mPoints[i] == pnt)
return(i);
mPoints.push_back(pnt);
return(mPoints.size() - 1);
}
U32 EditGeometry::insertPlane(const PlaneF & plane)
{
for(U32 i = 0; i < mPlanes.size(); i++)
if(mPlanes[i] == plane)
return(i);
mPlanes.push_back(plane);
return(mPlanes.size() - 1);
}
//------------------------------------------------------------------------------
Point3F EditGeometry::getSurfaceCenter(Surface & surface)
{
AssertFatal(surface.winding.numIndices, "EditGeometry::getSurfaceCenter: invalid surface");
Point3F center(0,0,0);
for(U32 i = 0; i < surface.winding.numIndices; i++)
center += getPoint(surface.winding.indices[i]);
center /= surface.winding.numIndices;
return(center);
}
//------------------------------------------------------------------------------
void EditGeometry::exportToMap(const char * fileName, U32 detailNum)
{
AssertISV(false, "This plugin IS BROKEN. It is UNSUPPORTED. Use it at YOUR OWN RISK. You should really be using QUARK, not this tool.");
FileStream file;
file.open(fileName, FileStream::Write);
char buf[1024];
writeString(file, "{\n");
writeString(file, "\"classname\" \"worldspawn\"\n");
writeString(file, "\"mapversion\" \"220\"\n");
// wadname
dSprintf(buf, sizeof(buf), "\"wad\" \"%s\"\n", gMapProperties.mWadName);
writeString(file, buf);
// geometry scale
dSprintf(buf, sizeof(buf), "\"geometry_scale\" \"%f\"\n", gMapProperties.mGeometryScale);
writeString(file, buf);
// lighting scale
dSprintf(buf, sizeof(buf), "\"light_geometry_scale\" \"%f\"\n", gMapProperties.mLightingScale);
writeString(file, buf);
// ambient color
dSprintf(buf, sizeof(buf), "\"ambient_color\", \"%d %d %d\"\n",
gMapProperties.mAmbientColor.red, gMapProperties.mAmbientColor.green, gMapProperties.mAmbientColor.blue);
writeString(file, buf);
// emergency lighting color
dSprintf(buf, sizeof(buf), "\"emergency_ambient_color\", \"%d %d %d\"\n",
gMapProperties.mEmergencyAmbientColor.red, gMapProperties.mEmergencyAmbientColor.green, gMapProperties.mEmergencyAmbientColor.blue);
writeString(file, buf);
// minpixels
dSprintf(buf, sizeof(buf), "\"min_pixels\" \"%d\"\n", mMinPixels);
writeString(file, buf);
// detailNum
dSprintf(buf, sizeof(buf), "\"detail_number\" \"%d\"\n", detailNum);
writeString(file, buf);
// create a brush per face..
for(U32 i = 0; i < mSurfaces.size(); i++)
{
Surface & surface = mSurfaces[i];
writeString(file, "{\n");
char buf[1024];
// surface plane..
Point3F pnts[3];
for(U32 j = 0; j < 3; j++)
pnts[j] = getPoint(surface.winding.indices[j]);
// unscale texgens
surface.texGenX.x *= mMaterialSizes[surface.materialIndex].x;
surface.texGenX.y *= mMaterialSizes[surface.materialIndex].x;
surface.texGenX.z *= mMaterialSizes[surface.materialIndex].x;
surface.texGenX.d *= mMaterialSizes[surface.materialIndex].x;
surface.texGenY.x *= mMaterialSizes[surface.materialIndex].y;
surface.texGenY.y *= mMaterialSizes[surface.materialIndex].y;
surface.texGenY.z *= mMaterialSizes[surface.materialIndex].y;
surface.texGenY.d *= mMaterialSizes[surface.materialIndex].y;
// write out this plane
dSprintf(buf, sizeof(buf), "( %d %d %d ) ( %d %d %d ) ( %d %d %d ) %s [ %f %f %f %f ] [ %f %f %f %f ] 0 1 1\n",
(S32)pnts[2].x, (S32)pnts[2].y, (S32)pnts[2].z,
(S32)pnts[1].x, (S32)pnts[1].y, (S32)pnts[1].z,
(S32)pnts[0].x, (S32)pnts[0].y, (S32)pnts[0].z,
getMaterial(surface.materialIndex),
surface.texGenX.x, surface.texGenX.y, surface.texGenX.z, surface.texGenX.d,
surface.texGenY.x, surface.texGenY.y, surface.texGenY.z, surface.texGenY.d);
writeString(file, buf);
PlaneF plane = getPlane(surface.planeIndex);
// get the surface area
Point3F areaNorm(0.f,0.f,0.f);
Point3F tmp;
mCross(pnts[0], pnts[1], &tmp); areaNorm += tmp;
mCross(pnts[1], pnts[2], &tmp); areaNorm += tmp;
mCross(pnts[2], pnts[0], &tmp); areaNorm += tmp;
F32 len = mSqrt(mFabs(mDot(plane, areaNorm) * 0.5f));
// null bounding planes
Point3F center = getSurfaceCenter(surface);
center -= (plane * mClampF(len * gcCenterOffsetScale, gcMinCenterOffset, gcMaxCenterOffset));
// write out the null planes
for(U32 k = 0; k < surface.winding.numIndices; k++)
{
U32 l = (k+1) % surface.winding.numIndices;
pnts[0] = getPoint(surface.winding.indices[l]);
pnts[1] = getPoint(surface.winding.indices[k]);
pnts[2] = center;
// bunk texture info (null surfaces)
dSprintf(buf, sizeof(buf), "( %d %d %d ) ( %d %d %d ) ( %d %d %d ) NULL [ 1 0 0 0 ] [ 0 1 0 0 ] 0 1 1\n",
(S32)pnts[2].x, (S32)pnts[2].y, (S32)pnts[2].z,
(S32)pnts[1].x, (S32)pnts[1].y, (S32)pnts[1].z,
(S32)pnts[0].x, (S32)pnts[0].y, (S32)pnts[0].z);
writeString(file, buf);
}
writeString(file, "}\n");
}
// wad file
writeString(file, "}\n");
// the collision info is dumped on highest detail
if(!detailNum)
{
gCollisionDetail->processConvexHullObjects();
gCollisionDetail->export(file);
}
file.close();
}
//------------------------------------------------------------------------------
// Class Exporter
//------------------------------------------------------------------------------
const TCHAR * Exporter::Ext(int index)
{
if(index != 0)
return(_T(""));
return(_T("map"));
}
//------------------------------------------------------------------------------
const char * Exporter::generateMapName(const char * baseName, U32 detail, U32 numDetails)
{
if(numDetails == 1)
return(baseName);
// work on a copy
char buf[1024];
dSprintf(buf, sizeof(buf), baseName);
char * cur = dStrrchr(buf, '.');
if(!cur)
return(0);
char ext[16];
dSprintf(ext, sizeof(ext), "_%d.map", detail);
*cur = '\0';
// fixup the name
dStrcat(buf, ext);
return(StringTable->insert(buf));
}
bool Exporter::getMapProperties()
{
bool gotLightingScale = false;
// walk and grab anything we like
for(U32 i = 0; i < gInterface->GetNumProperties(PROPSET_USERDEFINED); i++)
{
const PROPSPEC * propSpec = gInterface->GetPropertySpec(PROPSET_USERDEFINED, i);
const PROPVARIANT * propVar = gInterface->GetPropertyVariant(PROPSET_USERDEFINED, i);
if(propVar->vt != VT_LPWSTR && propVar->vt != VT_LPSTR)
continue;
char key[1024];
char value[1024];
_tcscpy(key, TSTR(propSpec->lpwstr));
_tcscpy(value, propVar->vt == VT_LPWSTR ? TSTR(propVar->pwszVal) : TSTR(propVar->pszVal));
//
if(!dStricmp(key, "wad"))
{
char buf[256];
dSprintf(buf, sizeof(buf), "\\%s.wad", value);
gMapProperties.mWadName = StringTable->insert(buf);
}
else if(!dStricmp(key, "geometry_scale"))
{
dSscanf(value, "%f", &gMapProperties.mGeometryScale);
}
else if(!dStricmp(key, "lighting_scale"))
{
gotLightingScale = true;
dSscanf(value, "%f", &gMapProperties.mLightingScale);
}
else if(!dStricmp(key, "ambient_color"))
{
S32 r, g, b;
dSscanf(value, "%d %d %d", &r, &g, &b);
gMapProperties.mAmbientColor.red = U8(r);
gMapProperties.mAmbientColor.green = U8(g);
gMapProperties.mAmbientColor.blue = U8(b);
}
else if(!dStricmp(key, "emergency_ambient_color"))
{
S32 r, g, b;
dSscanf(value, "%d %d %d", &r, &g, &b);
gMapProperties.mEmergencyAmbientColor.red = U8(r);
gMapProperties.mEmergencyAmbientColor.green = U8(g);
gMapProperties.mEmergencyAmbientColor.blue = U8(b);
}
}
// if there was no lighting scale, then set to the geometry scale
if(!gotLightingScale)
gMapProperties.mLightingScale = gMapProperties.mLightingScale;
return(gMapProperties.mWadName ? true : false);
}
//------------------------------------------------------------------------------
int Exporter::DoExport(const TCHAR * name, ExpInterface * expIFace, Interface * iFace, BOOL suppressPrompts, DWORD options)
{
suppressPrompts;options;
MessageBox(NULL, "This exporter is useful for exporting ONLY simple convex shapes.\n\n"
"If you try more complicated shapes it will not work.\n\n"
"Even if it does work you probably don't want to use the output.\n\n"
"You use it at your own risk! You should really be using Quark, not this tool! "
"Max is fundamentally unsuited to creating .maps and this tool does NOTHING to change that! "
"If this doesn't work DON'T ASK US FOR HELP. This is UNSUPPORTED and UNMAINTAINED SOFTWARE."
, "Exporter WARNING!", MB_OK);
// setup globals
gInterface = iFace;
mDetailEnum.enumScene(expIFace->theScene);
gCollisionDetail = mDetailEnum.getCollisionDetail();
if(!getMapProperties())
return(-1);
// has detail levels?
if(!mDetailEnum.numDetails())
{
MessageBox(NULL, "No details found to export", "Export failed!", MB_OK);
return(-1);
}
// sort them
mDetailEnum.sortDetails();
if(!mDetailEnum.sequentialDetails())
{
MessageBox(NULL, "Details' minPixels are not sequential or are not unique!", "Export failed!", MB_OK);
return(-1);
}
// work with each of the details
U32 i;
for(i = 0; i < mDetailEnum.numDetails(); i++)
{
gWorkingGeometry = mDetailEnum.getDetail(i);
gWorkingGeometry->processSceneObjects();
const char * mapName = generateMapName(name, i, mDetailEnum.numDetails());
if(!mapName)
{
MessageBox(NULL, "Failed to generate mapname", "Export failed!", MB_OK);
return(-1);
}
// multiple detail levels?
gWorkingGeometry->exportToMap(mapName, i);
}
return(-1);
}
//------------------------------------------------------------------------------
// Resolve external's
//------------------------------------------------------------------------------
U32 GameAddTaggedString(const char *) {return(0);}
void GameReactivate() {}
void GameDeactivate(bool) {}