added everything

This commit is contained in:
Metario
2017-04-17 06:17:10 -06:00
commit 9c6ff74f19
6121 changed files with 1625704 additions and 0 deletions

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

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

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

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

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

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

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

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

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

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