Microsoft-3D-Movie-Maker/TOOLS/SITOBREN.CPP

4958 lines
130 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
sitobren.cpp
Main module for sitobren, a utility for converting SoftImage
.hrc files into chunky files with the appropriate TMPL chunk
and all its associated children.
Syntax of a sitobren script (.s2b file):
ACTOR CNO <TMPL-id> NAMED "<actor_name>" [XREST <xaRest>]
[YREST <yaRest>] [ZREST <zaRest>] [FLAGS <cust_mat_only>]
[BPSETS <bpsets>]
ACTION NAMED "<action_name>" FILEBASE "<file_name_base>"
FIRST <start-#> LAST <end-#> FLAGS <action_flags>
[SCALE <dwrScale>] [SKIP <skip_count>] [SUBMODEL "<submodel>"]
BACKGROUND CNO <BKGD-id> NAMED "<bkgd_name>" [LIGHTS <#lights>]
[CAMERAS <#cameras>] [FIRST <first_pal>] [LENGTH <#entries>]
COSTUME FILE "<filename>" USE_SETS <set #> [<set #> [<set #> [...]]]
As many actions as are necessary may be defined. Actions are
defined for the actor which they follow. Actors should only
be defined once; this implies that all actions should be defined
before specifying a new actor.
Parameters in [] are optional. The parameters for a given command
can appear in any order, but all desired parameters must appear before
the next command, of course. Any number of spaces, tabs, line-feeds,
commas, and semi-colons may appear between defined tokens; feel free to
insert them as necessary to improve the readability of your script file.
The tokens are case-insensitive. Feel free to write "ACTOR," "actor,"
"Actor," "aCtOr," etc.
Valid values:
<actor_name>
<action_name>
<bkgd_name> -- any string conforming to actor/action naming
conventions
<TMPL-id>
<BKGD-id> -- Chunk number for TMPL or BKGD chunk
<xaRest>
<yaRest>
<zaRest> -- Any valid integer, units are degrees
<cust_mat_only> -- 1 if actor can only use custom materials, 0 otherwise
<file_name_base> -- A model's action cel filename follows the
following convention: "<drive>:\<path>\<model_action>-<cel#>.hrc"
The <drive> <path> and <model_action> parts, with their delimiters,
must be provided if the files aren't in the current directory. The
converter will supply the remaining portions, reading in all cel
files for the given action
<start-#> -- The # of the 1st cel file (usually either 0 or 1)
<end-#> -- The # of the last cel file
<dwrScale> -- the distance between cels for static actions; for
non-static actions, specifies a scaling to apply to the distance
specified in the cel data. Can be an integer or a decimal
<action_flags> -- action flags; includes three path orientation flags
and one flag to indicate a static action
<skip_count> -- increment for cel file numbers; sitobren will skip
<skip_count>-1 files between each cel.
<submodel> -- name of submodel to extract from action; if this
actor has multiple actions, each action must extract the same
submodel, so that the actor's heirarchy is identical from one
action to the next.
<bpsets> -- 1 if Body Part Sets are required, 0 otherwise
<#lights> -- number of lights in scene
<#cameras> -- number of cameras in scene
<first_pal> -- first palette entry to use
<#entries> -- how many palette entries to use
<filename> -- a complete filename specifying the .hrc to extract
costume information from
<set #> -- a valid body part set number; refers to the body
part set given in the .hrc file, not the mapped Brender body part
set number.
Default values for optional items:
<xaRest> <yaRest> <zaRest> -- 0
<cust_mat_only> -- 0
<dwrScale> -- 1.0
<action_flags> -- 0
<skip_count> -- 1
<submodel> -- "" (simply means extract everything)
<bpsets> -- 1
<#lights> -- 2
<#cameras> -- 9
<first_pal> -- 151
<#entries> -- 95
Command line:
sitobren <script[.s2b]> [<outfile[.cht]>] [flags]
The extensions may be ommited, in which case the defaults listed will
be used. The user can override the default extension by supplying
the full filename, including the extension.
-n[ologo] don't display the banner
-v[#] verbose; optional level 0-2 (quiet to verbose (1 is default))
-h swap the handedness (negate z positions and translations)
-r don't do any rounding
-r# round coordinates of models losing # bits
-k Kontinue on errors
-p include Preprocessing info
-i specify Include filename for preprocessing info
-t# round transformation values losing # bits (default is to
use the "-r" setting)
-w don't attempt to fix up seams in texture wrapping
-b DEBUG-only; break (INT 3) on Assert
-d DEBUG-only; simply dump out the tokens in the source
Flags come come anywhere in the command line. They may be combined
after a single '-' or they may appear separately. The input script
file must be listed before the output Chomp script filename.
***************************************************************************/
#include "sitobren.h"
ASSERTNAME
#ifdef DEBUG
bool _fEnableWarnings = fTrue;
bool _fBreak = fFalse;
#endif // DEBUG
int __cdecl main(int cpsz, achar *prgpsz[])
{
STN stnSrc, stnDst;
achar **ppszParm = &prgpsz[1];
bool fBanner = fTrue, fSwapHand, fUsage = fTrue, fDumpLex = fFalse;
bool fContinue = fFalse, fPreprocess = fFalse, fHaveInc = fFalse;
bool fIncNext = fFalse, fHaveRoundXF = fFalse, fFixWrap = fTrue;
uint mdVerbose, iRound = 3, iRet = 1, iRoundXF;
FNI fniSrc, fniDst, fniInc;
PFIL pfilSrc = pvNil;
FILE *pfileDst = pvNil;
MSSIO *pmssioErr = pvNil, *pmssioDst = pvNil;
S2B *ps2b = pvNil;
ulong grfs2b = fs2bNil;
fSwapHand = fFalse;
mdVerbose = kmdQuiet;
if (cpsz == 1)
goto LUsage;
while (--cpsz)
{
PSZ pszParm = *ppszParm;
if (fIncNext)
{
STN stnT;
stnT = pszParm;
fHaveInc = fniInc.FBuildFromPath(&stnT);
fIncNext = fFalse;
}
else if (pszParm[0] == ChLit('-') || pszParm[0] == ChLit('/'))
{
int cch = CchSz(pszParm), ich;
for (ich = 1; ich < cch; ich++)
{
achar *pch;
uint *pi;
switch (pszParm[ich])
{
case ChLit('n'):
fBanner = fFalse;
if ((cch - ich >= 6) && FEqualRgb(PszLit("nologo"), &pszParm[ich], 6 * size(achar)))
ich += 5;
break;
case ChLit('h'):
fSwapHand = !fSwapHand;
break;
case ChLit('v'):
{
achar ch = pszParm[ich + 1];
if (ch >= ChLit('0') && ch <= ChLit('9'))
{
mdVerbose = min(kmdVerbose, ch - ChLit('0'));
ich++;
}
else
mdVerbose = kmdHelpful;
break;
}
case ChLit('t'):
pi = &iRoundXF;
fHaveRoundXF = fTrue;
goto LGetRound;
case ChLit('r'):
pi = &iRound;
LGetRound:
pch = &pszParm[ich + 1];
*pi = 0;
while (*pch && (*pch >= ChLit('0') && *pch <= ChLit('9')))
{
*pi = *pi * 10 + *pch - ChLit('0');
ich++, pch++;
}
*pi = min(*pi, 15);
break;
case ChLit('k'):
fContinue = !fContinue;
break;
case ChLit('p'):
fPreprocess = !fPreprocess;
break;
case ChLit('w'):
fFixWrap = !fFixWrap;
break;
case ChLit('i'):
fIncNext = fTrue;
break;
#ifdef DEBUG
case ChLit('b'):
_fBreak = !_fBreak;
break;
case ChLit('d'):
fDumpLex = !fDumpLex;
break;
#endif // DEBUG
case ChLit('?'):
goto LUsage;
default:
printf("Error: unrecognized option '%c'\n", pszParm[ich]);
goto LUsage;
}
}
}
else if (stnSrc.Cch() == 0)
stnSrc = pszParm;
else if (stnDst.Cch() == 0)
stnDst = pszParm;
else goto LUsage;
ppszParm++;
}
fUsage = fFalse;
if (fContinue)
grfs2b |= kfs2bContinue;
if (fPreprocess)
grfs2b |= kfs2bPreprocess;
if (fFixWrap)
grfs2b |= kfs2bFixWrap;
if (fBanner)
{
printf("\nMicrosoft (R) SoftImage (R) to Chunky File Converter\n");
printf("Copyright (C) Microsoft Corp 1995. All rights reserved.\n\n");
}
if (!fHaveRoundXF)
iRoundXF = iRound;
if (mdVerbose)
{
if (iRound > 0)
printf("Rounding off %d binary places\n", iRound);
if (iRoundXF != iRound)
printf("Rounding off transformations %d binary places\n", iRoundXF);
#if HASH_FIXED
printf("Fixed length hash table\n");
#else
printf("Variable length hash table\n");
#endif
}
if (!fniSrc.FBuildFromPath(&stnSrc, kftgS2b))
{
printf("Invalid script file name\n");
goto LUsage;
}
else if ((pfilSrc = FIL::PfilOpen(&fniSrc)) == pvNil)
{
fniSrc.GetStnPath(&stnSrc);
printf("Could not open source file (%s)\n", stnSrc.Psz());
goto LUsage;
}
if (fDumpLex)
{
STN stn;
S2BLX s2blx(pfilSrc);
S2BTK s2btk;
long cch = 0;
while (s2blx.FGetS2btk(&s2btk))
{
if (!s2blx.FTextFromS2btk(&s2btk, &stn))
stn.FFormatSz(PszLit("<Token %d>"), s2btk.tok.tt);
if (cch + stn.Cch() + 1 >= 80)
{
printf("\n");
cch = 0;
}
printf("%s ", stn.Psz());
cch += stn.Cch() + 1;
}
fUsage = fFalse;
goto LUsage;
}
if (stnDst.Cch() == 0)
{
fniDst = fniSrc;
AssertDo(fniDst.FChangeFtg(kftgCht), 0);
}
else if (!fniDst.FBuildFromPath(&stnDst, kftgCht))
{
printf("Invalid destination file name\n");
goto LUsage;
}
fniDst.GetStnPath(&stnDst);
if ((pfileDst = fopen(stnDst.Psz(), "wt")) == pvNil)
{
printf("Could not create destination file (%s)\n", stnDst.Psz());
goto LUsage;
}
if (fPreprocess && !fHaveInc)
{
fniInc = fniDst;
fHaveInc = fniInc.FChangeFtg(kftgInc);
if (!fHaveInc)
printf("Warning: couldn't build include file name\n");
}
ps2b = S2B::Ps2bNew(pfilSrc, fSwapHand, mdVerbose, iRound, iRoundXF, prgpsz[0]);
if (ps2b == pvNil)
goto LOom;
pmssioErr = NewObj MSSIO(stderr);
if (pmssioErr == pvNil)
goto LOom;
pmssioDst = NewObj MSSIO(pfileDst);
if (pmssioDst == pvNil)
{
LOom:
printf("Error: not enough memory\n");
}
else if (ps2b->FConvertSI(pmssioErr, pmssioDst, fHaveInc ? &fniInc : pvNil, grfs2b))
iRet = 0;
ReleasePpo(&ps2b);
ReleasePpo(&pmssioDst);
ReleasePpo(&pmssioErr);
LUsage:
ReleasePpo(&pfilSrc);
Assert(FIL::PfilFirst() == pvNil, "Files left in the file chain");
if (pfileDst != pvNil)
fclose(pfileDst);
#ifdef DEBUG
UnmarkAllMem();
UnmarkAllObjs();
MarkUtilMem(); //marks all util memory
AssertUnmarkedMem();
AssertUnmarkedObjs();
#endif // DEBUG
if (fUsage)
{
Assert(iRet != 0, "won't return error status");
printf("Usage:\n sitobren [-<flag>] <srcScriptFile> "
"[<dstChunkFile>]\n");
printf( " flags:\n"
" -n[ologo] don't print banner\n"
" -v[#] Verbose, optional # 0-2 (1 is default)\n");
printf( " -h swap z-axis (Handedness)\n"
" -r don't Round positions\n"
" -r# Round off # places\n");
printf( " -t# Round off transformations # places\n"
" -w don't attempt to fix up seams in texture wrapping\n");
printf( " -k Kontinue after errors\n"
" -p include Preprocessing info\n"
" -i specify Include filename for preprocessing info\n");
printf( " -? This information\n");
printf( "\n The default extension for the script file is .s2b\n"
" and the default extension for the output file is .cht\n");
}
return iRet;
}
RTCLASS(S2B)
RTCLASS(S2BLX)
#ifdef DEBUG
void S2B::AssertValidBmhr(PBMHR pbmhr)
{
while (pbmhr != pvNil)
{
AssertVarMem(pbmhr);
AssertValidBmhr(pbmhr->pbmhrChild);
pbmhr = pbmhr->pbmhrSibling;
}
}
void S2B::AssertValid(ulong grf)
{
S2B_PAR::AssertValid(grf);
AssertPo(_ps2blx, grf);
AssertPo(&_chse, grf);
AssertNilOrPo(_pglibactPar, grf);
AssertNilOrPo(_pglbs, grf);
AssertNilOrPo(_pglcmtld, grf);
AssertNilOrPo(_pggcl, grf);
AssertNilOrPo(_pglxf, grf);
AssertNilOrPo(_pglcrng, grf);
AssertNilOrPo(_pglclr, grf);
AssertNilOrPo(_pglibps, grf);
AssertNilOrPo(_pggcm, grf);
AssertNilOrPo(_pggtmapd, grf);
#if HASH_FIXED
AssertNilOrPvCb(_prgpbmdb, kcbrgpbmdb);
#else /* HASH_FIXED */
AssertNilOrPo(_pglpbmdb, grf);
AssertNilOrPo(_pglpbmatdb, grf);
#endif /* HASH_FIXED */
AssertValidBmhr(_pbmhr);
AssertNilOrPvCb(_prgcps, size(CPS) * _cMesh);
}
void S2BLX::AssertValid(ulong grf)
{
S2BLX_PAR::AssertValid(grf);
}
void S2B::MarkBmhr(PBMHR pbmhr)
{
while (pbmhr != pvNil)
{
MarkPv(pbmhr);
MarkPv(pbmhr->pmodlf);
MarkPv(pbmhr->pszName);
MarkBmhr(pbmhr->pbmhrChild);
pbmhr = pbmhr->pbmhrSibling;
}
}
void S2B::MarkMem(void)
{
S2B_PAR::MarkMem();
MarkMemObj(_ps2blx);
MarkMemObj(_pglibactPar);
MarkMemObj(_pglbs);
MarkMemObj(_pglcmtld);
MarkMemObj(_pggcl);
MarkMemObj(_pglxf);
MarkMemObj(_pglcrng);
MarkMemObj(_pglclr);
MarkMemObj(_pglibps);
MarkMemObj(_pggcm);
MarkMemObj(_pggtmapd);
#if HASH_FIXED
MarkPv(_prgpbmdb);
#else /* HASH_FIXED */
MarkMemObj(_pglpbmdb);
MarkMemObj(_pglpbmatdb);
#endif /* !HASH_FIXED */
MarkPv(_prgcps);
MarkBmhr(_pbmhr);
MarkMemObj(&_chse);
}
void S2BLX::MarkMem(void)
{
S2BLX_PAR::MarkMem();
}
#endif // DEBUG
/*-----------------------------------------------------------------------------
| Ps2bNew
| Static instantiator for the S2B class. Will attempt to allocate the
| S2BLX member and will fail if it cannot.
|
| Arguments:
| PFIL pfilSrc -- file to read script from
| bool fSwapHand -- the rest are all passed to the S2B constructor
| bool mdVerbose
| int iRound
| PSZ pszApp
|
| Returns: a pointer to the new S2B instance, pvNil if it fails
|
-------------------------------------------------------------PETED-----------*/
PS2B S2B::Ps2bNew(PFIL pfilSrc, bool fSwapHand, uint mdVerbose, int iRound, int iRoundXF, PSZ pszApp)
{
PS2B ps2b = NewObj S2B(fSwapHand, mdVerbose, iRound, iRoundXF, pszApp);
if (ps2b == pvNil)
return pvNil;
ps2b->_ps2blx = NewObj S2BLX(pfilSrc);
if (ps2b->_ps2blx == pvNil)
ReleasePpo(&ps2b);
return ps2b;
}
/*-----------------------------------------------------------------------------
| S2B
| Constructor for the S2B class. Initializes the S2BLX and sets
| some pointers to pvNil.
|
| Arguments:
| PFIL pfilSrc -- used to initialize the S2BLX
|
-------------------------------------------------------------PETED-----------*/
S2B::S2B(bool fSwapHand, uint mdVerbose, int iRound, int iRoundXF, PSZ pszApp)
{
short bo;
FNI fni;
CFL *pcfl;
BLCK blck;
STN stnPal;
_ibpCur = 0;
_chidActn = 0;
_chidBmdl = 0;
_chidCmtl = 0;
_pglibactPar = pvNil;
_pglbs = pvNil;
_pglcmtld = pvNil;
_pggcl = pvNil;
#if HASH_FIXED
_prgpbmdb = pvNil;
#else /* HASH_FIXED */
_pglpbmdb = pvNil;
_pglpbmatdb = pvNil;
#endif /* !HASH_FIXED */
_InitCrcTable();
_pbmhr = pvNil;
_pglxf = pvNil;
_pglibps = pvNil;
_pggcm = pvNil;
_pggtmapd = pvNil;
_iZsign = fSwapHand ? -1 : 1;
_mdVerbose = mdVerbose;
Assert(_mdVerbose == mdVerbose, "mdVerbose overflow");
_uRound = iRound;
_uRoundXF = iRoundXF;
_mdBPS = kmdbpsAllMesh;
_fColorOnly = fFalse;
_cnoCur = cnoNil;
_pglclr = _pglcrng = pvNil;
stnPal = pszApp;
AssertDo(fni.FBuildFromPath(&stnPal), "Couldn't build .chk path?");
AssertDo(fni.FChangeFtg(kftgContent), "Couldn't change FTG?");
fni.GetLeaf(&stnPal);
if (fni.FSearchInPath(&stnPal) &&
(pcfl = CFL::PcflOpen(&fni, fcflNil)) != pvNil)
{
if (pcfl->FFind(kctgColorTable, 0, &blck))
{
_pglclr = GL::PglRead(&blck, &bo);
if (_pglclr != pvNil && bo == kboCur)
{
KID kidGlcg;
if (pcfl->FGetKidChidCtg(kctgColorTable, 0, 0, kctgGlcg, &kidGlcg) &&
pcfl->FFind(kidGlcg.cki.ctg, kidGlcg.cki.cno, &blck))
{
_pglcrng = GL::PglRead(&blck, &bo);
Assert(bo == kboCur, "GLCG byte-order not same as GLCR");
}
else
_pglcrng = PglcrngFromPal(_pglclr);
}
else
printf("Unable to read GLCR chunk\n");
}
else
printf("Couldn't find GLCR chunk\n");
ReleasePpo(&pcfl);
}
else
{
if (fni.Ftg() != ftgNil)
fni.GetStnPath(&stnPal);
printf("Couldn't find my resource file (%s);"
"no color information available\n", stnPal.Psz());
}
}
/*-----------------------------------------------------------------------------
| ~S2B
| Destructor for the S2B class. Simply asserts that things have
| already been cleaned up properly.
|
-------------------------------------------------------------PETED-----------*/
S2B::~S2B(void)
{
#ifdef DEBUG
UnmarkAllMem();
UnmarkAllObjs();
MarkMem(); //marks all S2B memory
MarkUtilMem(); //marks all util memory
AssertUnmarkedMem();
AssertUnmarkedObjs();
#endif // DEBUG
// All allocated objects should already be free
Assert(_pglibactPar == pvNil, 0);
Assert(_pglbs == pvNil, 0);
Assert(_pglcmtld == pvNil, 0);
Assert(_pggcl == pvNil, 0);
Assert(_pglxf == pvNil, 0);
Assert(_pglibps == pvNil, 0);
Assert(_pggcm == pvNil, 0);
Assert(_pggtmapd == pvNil, 0);
#if HASH_FIXED
Assert(_prgpbmdb == pvNil, 0);
#else /* HASH_FIXED */
Assert(_pglpbmdb == pvNil, 0);
Assert(_pglpbmatdb == pvNil, 0);
#endif /* !HASH_FIXED */
Assert(_prgcps == pvNil, 0);
Assert(_pbmhr == pvNil, 0);
// Except these
ReleasePpo(&_pglcrng);
ReleasePpo(&_pglclr);
ReleasePpo(&_ps2blx);
}
/******************************************************************************
_DumpHeader
Dumps a customized "#ifdef"'d header for a chunk. This allows
sitobren-generate .cht files to only keep chunks named for those
that need names in the final product.
Arguments:
CTG ctg -- the CTG of the chunk; used in the #ifdef
CNO cno -- the remainder of the arguments are just passed
PSTN pstnName directly to the CHSE DumpHeader()
bool fPack
************************************************************ PETED ***********/
void S2B::_DumpHeader(CTG ctg, CNO cno, PSTN pstnName, bool fPack)
{
if (_fPreprocess)
{
long ich;
STN stnCtg;
STN stnT;
stnCtg.FFormatSz(PszLit("%f"), ctg);
ich = 0;
while (ich < stnCtg.Cch())
{
achar ch;
ch = stnCtg.Psz()[ich];
if ((ch >= ChLit('A') && ch <= ChLit('Z')) ||
ch >= ChLit('a') && ch <= ChLit('z') ||
ch >= ChLit('0') && ch <= ChLit('9'))
{
ich++;
continue;
}
stnCtg.Delete(ich, 1);
}
if (stnCtg.Cch() > 0)
{
stnT.FFormatSz(
PszLit("#if !defined(DEBUG) && !defined(%s_NAMED)"), &stnCtg);
_chse.DumpSz(stnT.Psz());
_chse.DumpHeader(ctg, cno, pvNil, fPack);
_chse.DumpSz(PszLit("#else"));
_chse.DumpHeader(ctg, cno, pstnName, fPack);
_chse.DumpSz(PszLit("#endif"));
return;
}
printf("Warning: CTG w/out any alphanumerics; preprocessor directives"
" skipped for that chunk\n");
}
_chse.DumpHeader(ctg, cno, pstnName, fPack);
}
/*-----------------------------------------------------------------------------
| FConvertSI
| Reads commands from the current _s2blx file, and generates an
| appropriate Chunky source file.
|
| Arguments:
| PMSNK pmsnkErr -- where to send error messages
| PMSNK pmsnkDst -- where to actually send the source file
|
| Returns:
| fTrue if it succeeds, fFalse otherwise
|
-------------------------------------------------------------PETED-----------*/
bool S2B::FConvertSI(PMSNK pmsnkErr, PMSNK pmsnkDst, PFNI pfniInc, ulong grfs2b)
{
bool fGotTok, fHaveActor = fFalse, fRet = fFalse;
_fContinue = FPure(grfs2b & kfs2bContinue);
_fPreprocess = FPure(grfs2b & kfs2bPreprocess);
_fFixWrap = FPure(grfs2b & kfs2bFixWrap);
if (_fFixWrap)
BrBegin();
_chse.Init(pmsnkDst, pmsnkErr);
if (_fPreprocess && pfniInc != pvNil)
{
STN stnT;
pfniInc->GetStnPath(&stnT);
_stnT.FFormatSz(PszLit("#include \"%s\""), &stnT);
_chse.DumpSz(_stnT.Psz());
}
_chse.DumpSz(PszLit("BYTE"));
_chse.DumpSz(PszLit(""));
#if HASH_FIXED
if (!FAllocPv((void **)&_prgpbmdb, kcbrgpbmdb, fmemClear, mprNormal))
#else /* HASH_FIXED */
if ((_pglpbmatdb = GL::PglNew(size(PBMATDB))) == pvNil)
goto LFail;
if ((_pglpbmdb = GL::PglNew(size(PBMDB))) == pvNil)
#endif /* !HASH_FIXED */
goto LFail;
fGotTok = _ps2blx->FGetS2btk(&_s2btk);
while (fGotTok)
{
switch (_s2btk.tok.tt)
{
case ttActor:
fGotTok = _FDoTtActor(&fHaveActor);
break;
case ttActionS2B:
if (fHaveActor)
fGotTok = _FDoTtActionS2B();
else
{
printf("Error: found 'action' token before 'actor' definition\n");
goto LContinue;
}
break;
case ttBackgroundS2B:
fGotTok = _FDoTtBackgroundS2B();
break;
case ttCostume:
if (fHaveActor)
fGotTok = _FDoTtCostume();
else
{
printf("Error: found 'costume' token before 'actor' definition\n");
goto LContinue;
}
break;
default:
{
STN stnTok;
printf("Unexpected token at line %d, character %d: \n",
_ps2blx->LwLine(), _ps2blx->IchLine());
if (_ps2blx->FTextFromS2btk(&_s2btk, &stnTok))
printf(" %s\n", stnTok.Psz());
else
printf(" %08x\n", _s2btk.tok.tt);
}
LContinue:
fGotTok = _ps2blx->FGetS2btk(&_s2btk);
break;
}
}
_FlushTmplKids();
#if HASH_FIXED
#ifdef DEBUG
{
PBMDB *ppbmdb;
for (ppbmdb = _prgpbmdb; ppbmdb < (_prgpbmdb + kcpbmdb); ppbmdb++)
Assert(*ppbmdb == pvNil, "Didn't empty BMDL database!");
}
#endif // DEBUG
FreePpv((void **)&_prgpbmdb);
#else /* HASH_FIXED */
Assert(_pglpbmdb->IvMac() == 0, "Didn't empty BMDL database");
ReleasePpo(&_pglpbmdb);
Assert(_pglpbmatdb->IvMac() == 0, "Didn't empty XF database");
ReleasePpo(&_pglpbmatdb);
#endif /* !HASH_FIXED */
LFail:
if (_fFixWrap)
BrEnd();
return fRet;
}
/******************************************************************************
_FReadCmdline
General purpose script command reader. With an array of parameter
descriptions, and pointers to the appropriate variables to take the
results, this will read the specific parameters, putting the appropriate
data into the appropriate variable for the given token.
Arguments:
PSZ pszResult -- Error string for command
bool *pfGotTok -- pointer to flag to set based on lex state
const SCRP rgscrp[] -- description of command parameters
... -- Additional variable parameters
Returns: fTrue if all required parameters were found, and all parameters
were successfully read. If a token could be pre-fetched from the
lexer, *pfGotTok is set to fTrue, otherwise it's set to fFalse.
************************************************************ PETED ***********/
bool S2B::_FReadCmdline(PSZ pszResult, bool *pfGotTok, const SCRP rgscrp[], ...)
{
AssertVarMem(pfGotTok);
Assert(kcscrpMax <= size(long) * 8, "Too many parms");
va_list args;
bool fRet = fFalse;
long cscrpMac = 0, iscrp, grfFound = 0, grfMatch = 0;
void *rgpv[kcscrpMax];
/* Grab the arguments */
va_start(args, rgscrp);
while (rgscrp[cscrpMac].pt != ptNil)
{
Assert(rgscrp[cscrpMac].tt != ttNil, "Bad RGSCRP");
if (rgscrp[cscrpMac].szErr[0] != 0)
grfMatch |= (0x01 << cscrpMac);
rgpv[cscrpMac] = va_arg(args, void *);
cscrpMac++;
}
va_end(args);
/* Try to read the data, quit when we get an unknown token */
while (*pfGotTok = _ps2blx->FGetS2btk(&_s2btk))
{
for (iscrp = 0; iscrp < cscrpMac; iscrp++)
if (rgscrp[iscrp].tt == _s2btk.tok.tt)
break;
if (iscrp == cscrpMac)
break;
if (!(*pfGotTok = _ps2blx->FGetS2btk(&_s2btk)))
goto LDone;
switch (rgscrp[iscrp].pt)
{
case ptString:
if (_s2btk.tok.tt != ttString)
{
printf("Error: expected string\n");
goto LDone;
}
*((PSTN)rgpv[iscrp]) = _s2btk.tok.stn;
break;
case ptLong:
if (_s2btk.tok.tt != ttLong)
{
printf("Error: expected integer\n");
goto LDone;
}
*((long *)rgpv[iscrp]) = _s2btk.tok.lw;
break;
case ptBRS:
if (!_FBrsFromS2btk(&_s2btk, (BRS *)rgpv[iscrp]))
{
printf("Error: expected integer or floating point\n");
goto LDone;
}
break;
case ptBRA:
if (_s2btk.tok.tt != ttLong)
{
printf("Error: expected degrees as an integer\n");
goto LDone;
}
if (_s2btk.tok.lw >= 360 || _s2btk.tok.lw < 0)
_s2btk.tok.lw %= 360;
*((BRA *)rgpv[iscrp]) = BrDegreeToAngle(BrIntToFixed(_s2btk.tok.lw));
break;
}
grfFound |= ((0x01 << iscrp) & grfMatch);
}
if (!(fRet = (grfFound == grfMatch)))
{
for (iscrp = 0, grfMatch ^= grfFound;
iscrp < cscrpMac && !(grfMatch & 0x01);
iscrp++, grfMatch >>= 0x01)
;
Assert(iscrp < cscrpMac, "Inconsistent error info");
printf("Error: %s; %s\n", rgscrp[iscrp].szErr, pszResult);
}
LDone:
return fRet;
}
const SCRP rgscrpActor[] =
{
{ ptLong, ttCno, "missing CNO for actor" },
{ ptString, ttCalled, "missing name for actor" },
{ ptBRA, ttXRest, "" },
{ ptBRA, ttYRest, "" },
{ ptBRA, ttZRest, "" },
{ ptLong, ttFlags, "" },
{ ptLong, ttBPS, ""},
{ ptLong, ttMaterials, "" },
{ ptNil, ttNil, "" }
};
/*-----------------------------------------------------------------------------
| _FDoTtActor
| Processes a token of type ttActor. That is, the ttActor
| indicates the start of a new TMPL to create. This handles all of
| the tokens for that command, and prefetches the next token following
| all data for this command.
|
| Returns:
| fTrue if another token is available, fFalse otherwise.
|
-------------------------------------------------------------PETED-----------*/
bool S2B::_FDoTtActor(bool *pfHaveActor)
{
bool fRet = fTrue;
uint mdBPS = _mdBPS, mdMaterials = 2;
CNO cno;
TMPLF tmplf;
_FlushTmplKids();
tmplf.bo = kboCur;
tmplf.osk = koskCur;
tmplf.xaRest = tmplf.yaRest = tmplf.zaRest = 0;
tmplf.grftmpl = 0;
if (!_FReadCmdline("actor ignored", &fRet, rgscrpActor, &cno, &_stnTmpl,
&tmplf.xaRest, &tmplf.yaRest, &tmplf.zaRest, &tmplf.grftmpl,
&mdBPS, &mdMaterials))
goto LFail1;
if (mdBPS < kmdbpsNone || mdBPS >= kmdbpsLim)
{
printf("Error: body part sets state out of range [%d-%d]\n",
kmdbpsNone, kmdbpsLim-1);
goto LFail;
}
_mdBPS = mdBPS;
_fColorOnly = (mdMaterials < 2);
if (_mdVerbose > kmdQuiet)
{
printf("New actor: %s\n", _stnTmpl.Psz());
switch (_mdBPS)
{
case kmdbpsNone:
printf("Body part sets will not be generated\n");
break;
case kmdbpsNumberedOnly:
printf("Only nodes with body part sets will be included\n");
break;
case kmdbpsAllMesh:
printf("All mesh nodes, and null nodes with a body part set, will be included\n");
break;
case kmdbpsAllMeshNull:
printf("All mesh and null nodes will be included\n");
break;
}
}
_ctgPar = kctgTmpl;
_cnoPar = cno;
if (_cnoCur == cnoNil)
_cnoCur = cno;
_DumpHeader(kctgTmpl, cno, &_stnTmpl, fTrue);
_chse.DumpRgb(&tmplf, size(tmplf));
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
*pfHaveActor = fTrue;
LFail:
if (!*pfHaveActor)
{
printf("Error occured during processing for command line:\n");
printf(" ACTOR CNO %d, NAMED \"%s\",\n"
" XREST %f, YREST %f, ZREST %f FLAGS %08x, BPSETS %d\n",
cno, _stnTmpl.Psz(),
BrScalarToFloat(BrAngleToDegree(tmplf.xaRest)),
BrScalarToFloat(BrAngleToDegree(tmplf.yaRest)),
BrScalarToFloat(BrAngleToDegree(tmplf.zaRest)),
tmplf.grftmpl, mdBPS);
}
LFail1:
return fRet;
}
const SCRP rgscrpAction[] =
{
{ ptString, ttCalled, "missing name for action" },
{ ptString, ttFilebase, "missing filename for action" },
{ ptLong, ttFirst, "missing starting cel for action" },
{ ptLong, ttLast, "missing ending cel for action" },
{ ptLong, ttFlags, "missing flags for action" },
{ ptBRS, ttScale, "" },
{ ptLong, ttSkip, "" },
{ ptString, ttSubmodel, "" },
{ ptBRS, ttStep, "" },
{ ptNil, ttNil, "" }
};
/*-----------------------------------------------------------------------------
| _FDoTtActionS2B
| Processes a token of type ttNameS2B. That is, the ttNameS2B
| indicates the start of a new ACTN to create. This handles all of
| the tokens for that command, and prefetches the next token following
| all data for this command.
|
| Returns:
| fTrue if another token is available, fFalse otherwise.
|
-------------------------------------------------------------PETED-----------*/
bool S2B::_FDoTtActionS2B(void)
{
bool fRet = fTrue, fSuccess = fTrue;
STN stnFileBase, stnFileAddon, stnSubmodel;
int cCel, iCel, iCelBase, dCel = 1, iCelMac;
long grfactn;
ACTNF actnf;
CNO cnoActn;
BRS brsScale = BrFloatToScalar(1.0), brsStep = BR_SCALAR(-1);
BRS rgbrsDwr[3] = {BR_SCALAR(0), BR_SCALAR(0), BR_SCALAR(0)};
/* Read in all of the command data */
if (!_FReadCmdline("action ignored", &fRet, rgscrpAction, &_stnActn,
&stnFileBase, &iCelBase, &iCelMac, &grfactn, &brsScale, &dCel,
&stnSubmodel, &brsStep))
goto LFail;
if (dCel < 1)
{
printf("Error: non-positive increment for cel files (%d)\n", dCel);
goto LFail;
}
cCel = iCelMac - iCelBase + 1;
if (!(grfactn & factnStatic))
{
if (cCel == 1)
printf("Warning: single-cel non-static action\n");
if (brsStep != BR_SCALAR(-1))
printf("Warning: step size parameter ignored for non-static action\n");
}
else if (brsStep == BR_SCALAR(-1))
brsStep = BR_SCALAR(5.0);
/* Create the ACTN chunk itself */
actnf.bo = kboCur;
actnf.osk = koskCur;
actnf.grfactn = grfactn;
cnoActn = CnoNext();
_stnT = _stnActn;
_DumpHeader(kctgActn, cnoActn, &_stnT, fTrue);
_chse.DumpParentCmd(_ctgPar, _cnoPar, _chidActn++);
_chse.DumpRgb(&actnf, size(actnf));
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
/* Process the model data files for this action */
for (iCel = 0; iCel < cCel && (fSuccess || _fContinue); iCel += dCel)
{
Model *pmodel;
BMAT34 bmat34;
CEL cel;
long cbrgcps;
/* Get the SoftImage data */
AssertDo(stnFileAddon.FFormatSz(PszLit("-%d.hrc"), iCel + iCelBase), 0);
_stnT = stnFileBase;
if (!_stnT.FAppendStn(&stnFileAddon))
{
printf("Error: Computed filename too long (%s%s)\n", _stnT.Psz(),
stnFileAddon.Psz());
goto LFail;
}
if ((pmodel = DK_modelReadFile(_stnT.Psz())) == pvNil)
{
printf("Error: Can't read cel file (%s)\n", _stnT.Psz());
goto LFail;
}
/* Convert the SoftImage data to Brender data */
/* Determine step size for this cel */
cel.chidSnd = 0;
if (grfactn & factnStatic)
cel.dwr = brsStep;
else
cel.dwr = _BrsdwrFromModel(pmodel, rgbrsDwr);
if (cel.dwr == BR_SCALAR(0))
cel.dwr = BrFloatToScalar(0.01);
cel.dwr = BR_MUL(brsScale, cel.dwr);
/* Actually process the model hierarchy */
BrMatrix34Identity(&bmat34);
_cFace = _cMesh = 0;
if (!_FInitGlpiCost(fFalse))
{
Assert(_pbmhr == pvNil, "Bad error state");
Assert(_pglibactPar == pvNil, "Bad error state");
Assert(_pglbs == pvNil, "Bad error state");
Assert(_pglcmtld == pvNil, "Bad error state");
goto LFail;
}
if (_fMakeCostume)
printf("Warning: costume info will be extracted from ACTION\n");
fSuccess = _FProcessModel(pmodel, bmat34, &_pbmhr, &stnSubmodel) &&
_FEnsureOneRoot(&_pbmhr);
DK_modelDispose(&pmodel);
if (!fSuccess)
{
printf("Error: processing cel heirarchy (%s)\n", _stnT.Psz());
if (!_fContinue)
goto LFail;
else
continue;
}
/* Create GGCL and GLXF for action */
/* Also, generate GLPI if necessary, and accumulate BMDLs */
if (_mdVerbose > kmdQuiet)
{
printf("Distance from previous cel is %5.2f\n", BrScalarToFloat(cel.dwr));
if (_mdVerbose > kmdHelpful || (iCel + dCel >= cCel))
printf("Found %d mesh nodes, totalling %d polygons\n", _cMesh, _cFace);
}
cbrgcps = size(CPS) * _cMesh;
fSuccess = fFalse;
if (FAllocPv((void **)&_prgcps, cbrgcps, fmemNil, mprNormal))
{
if ((_pglxf != pvNil) || (_pglxf = GL::PglNew(size(BMAT34))) != pvNil)
{
_ibpCur = 0;
if (_FProcessBmhr(&_pbmhr))
{
if (_pggcl != pvNil || (_pggcl = GG::PggNew(size(CEL))) != pvNil)
fSuccess = _pggcl->FAdd(cbrgcps, pvNil, _prgcps, &cel);
}
}
else
printf("Couldn't create GLXF -- OOM\n");
FreePpv((void **)&_prgcps);
}
else
printf("Couldn't create CPS array -- OOM\n");
if (!fSuccess)
{
_DisposeBmhr(&_pbmhr);
if (_fMakeGlpi)
{
ReleasePpo(&_pglibactPar);
ReleasePpo(&_pglbs);
}
}
#ifdef DEBUG
else
Assert(_pbmhr == pvNil, "Didn't free model hierarchy");
#endif // DEBUG
}
ReleasePpo(&_pglcmtld);
if (fSuccess)
{
/* Write out the GGCL and GLXF for the action*/
CnoNext();
_stnT.FFormatSz(PszLit("%s %s Cels"), &_stnTmpl, &_stnActn);
_DumpHeader(kctgGgcl, _cnoCur, &_stnT, fTrue);
_chse.DumpParentCmd(kctgActn, cnoActn, 0);
_chse.DumpGroup(_pggcl);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
ReleasePpo(&_pggcl);
CnoNext();
_stnT.FFormatSz(PszLit("%s %s Transforms"), &_stnTmpl, &_stnActn);
_DumpHeader(kctgGlxf, _cnoCur, &_stnT, fTrue);
_chse.DumpParentCmd(kctgActn, cnoActn, 0);
_chse.DumpList(_pglxf);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
ReleasePpo(&_pglxf);
}
LFail:
#if !HASH_FIXED
/* Empty the XF database */
{
PBMATDB pbmatdb, pbmatdbNext;
long ipbmatdb = _pglpbmatdb->IvMac();
long cChain = 0, cEntriesTot = 0, cEntries;
if (_mdVerbose > kmdQuiet)
printf("Emptying XF database\n");
while (ipbmatdb > 0)
{
ipbmatdb--;
_pglpbmatdb->Get(ipbmatdb, &pbmatdb);
cEntries = 0;
while (pbmatdb != pvNil)
{
cEntries++;
pbmatdbNext = (PBMATDB)pbmatdb->hshdb.phshdbNext;
FreePpv((void **)&pbmatdb);
pbmatdb = pbmatdbNext;
}
_pglpbmatdb->Delete(ipbmatdb);
cEntriesTot += cEntries;
if (_mdVerbose > kmdHelpful)
printf("XF collision chain had %d entries\n", cEntries);
cChain++;
}
if (_mdVerbose > kmdQuiet && cChain > 0)
{
double flAvg = (double)cEntriesTot / (double)cChain;
printf("Average XF collision chain had %1.2f entries (%d chains)\n", flAvg, cChain);
}
Assert(_pglpbmatdb->IvMac() == 0, 0);
}
#endif /* !HASH_FIXED */
if (!fSuccess)
{
printf("Error occured during processing for command line:\n");
printf(" ACTION NAMED \"%s\" FILEBASE \"%s\",\n"
" FIRST %d, LAST %d, FLAGS %08x, SCALE %f, SKIP %d,\n"
" SUBMODEL \"%s\", STEP %f\n",
_stnActn.Psz(), stnFileBase.Psz(), iCelBase, iCelMac, grfactn,
BrScalarToFloat(brsScale), dCel, stnSubmodel.Psz(),
BrScalarToFloat(brsStep));
}
return fRet;
}
/******************************************************************************
_FInitGlpiCost
Routine to setup the GLPI and Costume creation states. By default,
if either does not yet exist, we will create them. The caller can
override the _fMakeCostume if it wants to create additional costumes,
by setting the flag to fTrue if it's fFalse when we return from here.
Arguments:
fForceCost -- fTrue if we always want to generate a costume
Returns: fTrue if no fatal errors occurred
************************************************************ PETED ***********/
bool S2B::_FInitGlpiCost(bool fForceCost)
{
_fMakeGlpi = (_pglibactPar == pvNil);
_fMakeCostume = (_pggcm == pvNil || fForceCost);
if (_fMakeGlpi)
{
Assert(_pglbs == pvNil, "Non-nil body part set");
if ((_pglibactPar = GL::PglNew(size(short))) == pvNil)
{
printf("Error: Couldn't create GLPI -- OOM\n");
goto LFail;
}
if ((_pglbs = GL::PglNew(size(short))) == pvNil)
{
printf("Warning: no body part set info -- OOM\n");
_fMakeCostume = fFalse;
}
}
if (_fMakeCostume)
{
Assert(_pglcmtld == pvNil, "Non-nil GLCMTLD");
if ((_pglcmtld = GL::PglNew(size(CMTLD))) == pvNil)
goto LFailCost;
if (_pggcm == pvNil && (_pggcm = GG::PggNew(size(long))) == pvNil)
{
ReleasePpo(&_pglcmtld);
LFailCost:
printf("Warning: no custom material info -- OOM\n");
_fMakeCostume = fFalse;
}
}
return fTrue;
LFail:
return fFalse;
}
const SCRP rgscrpBackground[] =
{
{ ptLong, ttCno, "missing CNO for background" },
{ ptString, ttCalled, "missing name for background" },
{ ptLong, ttLights, "" },
{ ptLong, ttCameras, "" },
{ ptLong, ttFirst, "" },
{ ptLong, ttLength, "" },
{ ptNil, ttNil, "" }
};
/******************************************************************************
_FDoTtBackgroundS2B
Handles a ttBackground command from the script. Will generate the
necessary .Zbmp files (Brender z-buffer), and output appropriate .cht
data for the given background.
Returns: fTrue if it returns with a token available
************************************************************ PETED ***********/
bool S2B::_FDoTtBackgroundS2B(void)
{
bool fGotTok, fSuccess = fFalse;
long cLite = 2, cCam = 9, iPalBase = 151, cPal = 95;
CTG ctgSav;
CNO cnoBkgd, cnoSav;
STN stnBkgd;
BKGDF bkgdf;
ctgSav = _ctgPar;
cnoSav = _cnoPar;
/* Get the command parameters */
if (!_FReadCmdline("background ignored", &fGotTok, rgscrpBackground,
&cnoBkgd, &stnBkgd, &cLite, &cCam, &iPalBase, &cPal))
goto LFail;
/* Generate the BKGD chunk */
bkgdf.bo = kboCur;
bkgdf.osk = koskCur;
Assert(iPalBase >= 0 && iPalBase <= kbMax, "Palette base out of range");
bkgdf.bIndexBase = (byte)iPalBase;
_ctgPar = kctgBkgd;
_cnoPar = cnoBkgd;
if (_cnoCur == cnoNil)
_cnoCur = cnoBkgd;
_stnT.FFormatSz(PszLit("%s Background"), &stnBkgd);
_DumpHeader(kctgBkgd, cnoBkgd, &_stnT, fTrue);
_chse.DumpRgb(&bkgdf, size(bkgdf));
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
/* Create the lights */
if (!_FDumpLites(cLite, &stnBkgd))
goto LFail;
/* Create the cameras */
if (!_FDumpCameras(cCam, &stnBkgd, iPalBase, cPal))
goto LFail;
fSuccess = fTrue;
LFail:
if (!fSuccess)
{
printf("Error occured during processing for command line:\n");
printf(" BACKGROUND CNO %d, NAMED \"%s\""
" LIGHTS %d, CAMERAS %d, FIRST %d, LENGTH %d\n",
cnoBkgd, stnBkgd.Psz(), cLite, cCam, iPalBase, cPal);
}
_ctgPar = ctgSav;
_cnoPar = cnoSav;
return fGotTok;
}
const SCRP rgscrpCostume[] =
{
{ ptString, ttFilename, "missing filename for costume" },
{ ptLong, ttUseSets, "no body part sets specified" },
{ ptNil, ttNil, "" }
};
/******************************************************************************
_FDoTtCostume
Handles a COSTUME script command. Reads the specified .hrc file
and extracts costume information from the file.
Returns: fTrue if there's a new token available
************************************************************ PETED ***********/
bool S2B::_FDoTtCostume(void)
{
bool fRet, fSuccess = fFalse;
long ibpsCur;
Model *pmodel;
STN stnCostume;
BMAT34 bmat34;
/* Read in all of the command data */
if (!_FReadCmdline("costume ignored", &fRet, rgscrpCostume, &stnCostume, &ibpsCur))
goto LFail;
/* Get list of body part sets to pay attention to */
if ((_pglibps = GL::PglNew(size(long))) == pvNil)
goto LOOM1;
if (!_pglibps->FAdd(&ibpsCur))
goto LOOM1;
while (fRet && (_s2btk.tok.tt == ttLong))
{
if (!_pglibps->FAdd(&_s2btk.tok.lw))
{
LOOM1:
printf("Error: can't build list of body part sets -- OOM\n");
goto LFail;
}
fRet = _ps2blx->FGetS2btk(&_s2btk);
}
pmodel = DK_modelReadFile(stnCostume.Psz());
if (pmodel == pvNil)
{
printf("Error: Can't read costume file (%s)\n", stnCostume.Psz());
goto LFail;
}
/* Read and process the SoftImage .hrc file */
if (!_FInitGlpiCost(fTrue))
goto LFail;
_fCostumeOnly = fTrue;
BrMatrix34Identity(&bmat34);
_cFace = _cMesh = 0;
BrMatrix34Identity(&bmat34);
_cFace = _cMesh = 0;
Assert(_pglibactPar != pvNil, "haven't processed base hierarchy yet");
fSuccess = _FProcessModel(pmodel, bmat34, &_pbmhr) && _FEnsureOneRoot(&_pbmhr);
DK_modelDispose(&pmodel);
if (!fSuccess)
{
printf("Error: processing SoftImage data for costume (%s)\n", stnCostume.Psz());
goto LFail;
}
_ibpCur = 0;
fSuccess = _FProcessBmhr(&_pbmhr);
if (!fSuccess)
{
_DisposeBmhr(&_pbmhr);
printf("Error: processing hierarchy for costume (%s)\n", stnCostume.Psz());
goto LFail;
}
else
Assert(_pbmhr == pvNil, "Didn't free model hierarchy");
LFail:
if (!fSuccess)
{
long iibpsMac = _pglibps != pvNil ? _pglibps->IvMac() : 0;
long cchLine = 16; /* length of " USE_SETS" */
printf("Error occured during processing for command line:\n");
printf(" COSTUME FILE \"%s\",\n", stnCostume.Psz());
printf(" USE_SETS");
for (long iibps = 0; iibps < iibpsMac; iibps++)
{
long ibps;
STN stnT;
_pglibps->Get(iibps, &ibps);
stnT.FFormatSz(PszLit(", %d"), ibps);
if (cchLine + stnT.Cch() > 72)
{
printf("\n");
cchLine = 0;
}
printf("%s", stnT.Psz());
cchLine += stnT.Cch();
}
printf("\n");
}
_fCostumeOnly = fFalse;
ReleasePpo(&_pglcmtld);
ReleasePpo(&_pglibps);
return fRet;
}
/******************************************************************************
_FDumpLites
Outputs the appropriate .cht data for light definitions. Reads
the necessary SoftImage ASCII light files, converts to Brender data,
and generates the proper chunk data.
Arguments:
int cLite -- the number of lights
PSTN pstnBkgd -- string containing the background name
Returns: fTrue if it succeeds, fFalse otherwise
************************************************************ PETED ***********/
bool S2B::_FDumpLites(int cLite, PSTN pstnBkgd)
{
bool fRet = fFalse;
int iLite;
LITE lite;
PGL pgllite;
/* Create a GL of LITEs */
if ((pgllite = GL::PglNew(size(LITE))) == pvNil)
{
printf("Couldn't allocate GLLT\n");
goto LFail;
}
/* Read in the SoftImage lights, adding each to the GL */
for (iLite = 0; iLite < cLite; iLite++)
{
if (!_stnT.FFormatSz(kszLight, pstnBkgd, iLite+1))
{
printf("Computed light filename too long (" kszLight ")\n",
pstnBkgd->Psz(), iLite+1);
goto LFail;
}
BrMatrix34Identity(&lite.bmat34);
lite.rIntensity = BrIntToScalar(1.0);
_ReadLite(&_stnT, &lite);
lite.lt = BR_LIGHT_DIRECT;
if (!pgllite->FAdd(&lite))
{
printf("Couldn't add light to GLLT -- OOM\n");
goto LFail;
}
}
/* Emit the LITE chunk */
CnoNext();
_stnT.FFormatSz(PszLit("%s Lights"), pstnBkgd);
_DumpHeader(kctgGllt, _cnoCur, &_stnT, fTrue);
Assert(_ctgPar == kctgBkgd, "Odd parent for GLLT");
_chse.DumpParentCmd(_ctgPar, _cnoPar, 0);
_chse.DumpList(pgllite);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
fRet = fTrue;
LFail:
ReleasePpo(&pgllite);
return fRet;
}
/******************************************************************************
_FDumpCameras
Outputs the necessary camera chunk data. Reads in the appropriate
SoftImage ASCII camera file, converts the data to Brender data, and
generates the proper chunk data.
Arguments:
int cCam -- the number of cameras
PSTN pstnBkgd -- the name of the background
int iPalBase -- the first palette entry used by the background
int cPal -- the number of palette entries used by the background
Returns: fTrue if it succeeds, fFalse otherwise
************************************************************ PETED ***********/
bool S2B::_FDumpCameras(int cCam, PSTN pstnBkgd, int iPalBase, int cPal)
{
int iCam;
long dxp, dyp;
CAM cam;
STN stnFile;
/* Cameras are kept as individual chunks;
read each file and create the chunk */
for (iCam = 1; iCam <= cCam; iCam++)
{
CNO cnoCam;
FNI fni;
PGL pglapos = pvNil;
/* Get the file */
if (!stnFile.FFormatSz(kszCam, pstnBkgd, iCam))
{
printf("Computed camera filename too long (" kszCam ")\n",
pstnBkgd->Psz(), iCam);
goto LFail;
}
cam.bo = kboCur;
cam.osk = koskCur;
_ReadCam(&stnFile, &cam, &pglapos);
/* Process camera's background bitmap */
if (!stnFile.FFormatSz(kszBmp, pstnBkgd, iCam))
{
printf("Computed bitmap filename too long (" kszBmp ")\n",
pstnBkgd->Psz(), iCam);
goto LFail;
}
_ps2blx->GetFni(&fni);
if (!fni.FSetLeaf(&stnFile, kftgBmp))
{
printf("Error: Couldn't create palette filename\n");
goto LFail;
}
fni.GetStnPath(&stnFile);
/* Only extract the palette once */
if (iCam == 1)
{
PGL pglclr;
/* Read the palette */
if (FReadBitmap(&fni, pvNil, &pglclr, &dxp, &dyp, pvNil))
{
/* Chop off unneeded data from the palette */
Assert(iPalBase + cPal <= pglclr->IvMac(),
"Not enough colors in bitmap");
if (iPalBase > 0)
pglclr->Delete(0, iPalBase);
if (cPal < pglclr->IvMac())
pglclr->Delete(cPal, pglclr->IvMac() - cPal);
/* Generate the palette chunk */
_stnT.FFormatSz(PszLit("%s Palette"), pstnBkgd);
_DumpHeader(kctgColorTable, _cnoCur, &_stnT, fTrue);
Assert(_ctgPar == kctgBkgd, "Odd parent for GLCR");
_chse.DumpParentCmd(_ctgPar, _cnoPar, 0);
_chse.DumpList(pglclr);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
ReleasePpo(&pglclr);
}
else
{
_stnT.FFormatSz(
PszLit("Error: Couldn't read camera palette (%s)"),
&stnFile);
printf("%s\n", _stnT.Psz());
goto LFail;
}
}
/* Generate the camera chunk */
cnoCam = CnoNext();
_stnT.FFormatSz(PszLit("%s Camera %d"), pstnBkgd, iCam);
_DumpHeader(kctgCam, cnoCam, &_stnT, fTrue);
Assert(_ctgPar == kctgBkgd, "Odd parent for CAM");
_chse.DumpParentCmd(_ctgPar, _cnoPar, iCam-1);
_chse.DumpRgb(&cam, size(cam));
if (pglapos != pvNil)
{
long capos = pglapos->IvMac();
APOS apos;
for (long iapos = 0; iapos < capos; iapos++)
{
pglapos->Get(iapos, &apos);
_chse.DumpRgb(&apos, size(APOS));
}
ReleasePpo(&pglapos);
}
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
/* Generate the background chunk */
CnoNext();
_stnT.FFormatSz(PszLit("%s Bitmap %d"), pstnBkgd, iCam);
_DumpHeader(kctgMbmp, _cnoCur, &_stnT, fTrue);
_chse.DumpParentCmd(kctgCam, cnoCam, 0);
_chse.DumpBitmapCmd(0, dxp, dyp, &stnFile);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
/* Generate the z-buffer chunk for the camera */
if (!_FZbmpFromZpic(pstnBkgd, cnoCam, iCam, dxp, dyp, &cam))
goto LFail;
}
return fTrue;
LFail:
return fFalse;
}
/******************************************************************************
_FZbmpFromZpic
Converts a SoftImage z-buffer data file into Brender z-buffer
data. If it can write the data to a new file, it will do so and
generate a chunk that refers to that file; otherwise, it will include
the chunk data explicitly.
Arguments:
PSTN pstnBkgd -- the name of the background
CNO cnoPar -- the CNO of the parent camera chunk
int iCam -- the number of the camera
long dxp -- the width of the background
long dyp -- the height of the background
CAM *pcam -- pointer to the camera data
Returns: fTrue if it could successfully read and process the z-buffer
data, fFalse otherwise.
************************************************************ PETED ***********/
bool S2B::_FZbmpFromZpic(PSTN pstnBkgd, CNO cnoPar, int iCam,
long dxp, long dyp, CAM *pcam)
{
Assert(dxp > 0, "Invalid z-buffer width");
Assert(dyp > 0, "Invalid z-buffer height");
bool fRet = fFalse;
short *prgsw = pvNil;
float *prgfl = pvNil;
FNI fni;
FIL *pfil = pvNil;
/* Try to find and open the SoftImage data file */
if (!_stnT.FFormatSz(kszZpic, pstnBkgd, iCam))
{
printf("Computed z-buffer filename too long (" kszZpic ")\n",
pstnBkgd->Psz(), iCam);
goto LFail;
}
_ps2blx->GetFni(&fni);
if (fni.FSetLeaf(&_stnT, kftgZpic))
{
if ((pfil = FIL::PfilOpen(&fni)) != pvNil)
{
bool fWroteZbmp = fFalse;
short *psw, *prgsw;
long cPix = dxp * dyp, cbSw, cbBuf, cbLeft;
float fl, *prgfl;
FNI fniZbmp;
FP fpZbmp;
FIL *pfilZbmp = pvNil;
FP fpRead = 0;
ZBMPF zbmpf;
/* Allocate buffer for Zbmp and buffer for reading */
cbSw = cPix * size(short);
if (!FAllocPv((void **)&prgsw, cbSw, fmemNil, mprNormal))
{
printf("Error: Not enough memory for z-buffer\n");
goto LFail;
}
cbBuf = dxp * size(float);
if (!FAllocPv((void **)&prgfl, cbBuf, fmemNil, mprNormal))
{
prgfl = &fl;
cbBuf = size(float);
}
/* Read the available floats */
cbLeft = pfil->FpMac();
psw = prgsw;
while (cbLeft && cPix)
{
long cFl, cbRead;
float *pfl = prgfl;
/* Process a buffer's-worth of data */
cbRead = LwMin(cbBuf, cbLeft);
cFl = cbRead / size(float);
Assert(cFl * size(float) == cbRead, "Partial data at EOF");
AssertDo(pfil->FReadRgbSeq(prgfl, cbRead, &fpRead),
"Failed z-buffer read");
SwapBytesRglw(prgfl, cFl);
cPix -= cFl;
while (cFl-- > 0)
{
BRS brsZCam = BrFloatToScalar(*pfl++);
/* Convert the SoftImage data to Brender data */
Assert(*(pfl-1) < 0, "Non-negative zbuffer values");
if (BR_ABS(brsZCam) > pcam->zrYon)
*psw++ = -1;
else
{
BRS brsZ, brsDepth = BR_SUB(pcam->zrYon,pcam->zrHither);
brsZ = BR_ADD(
BR_MULDIV(brsZCam,
BR_ADD(pcam->zrYon,pcam->zrHither), brsDepth),
BR_CONST_MUL(BR_MULDIV(pcam->zrYon,pcam->zrHither,
brsDepth),2));
*psw++ = (short)(BrScalarToFloat(brsZ) * -32768.0 /
BrScalarToFloat(-brsZCam));
Assert(*(psw-1) > 0, "Non-positive calculated value");
}
}
cbLeft -= cbRead;
}
/* Fill in the rest with the max value */
while (cPix-- > 0)
*psw++ = -1;
/* Write the chunk */
CnoNext();
_stnT.FFormatSz(PszLit("%s Z-Buffer %d"), pstnBkgd, iCam);
_DumpHeader(kctgZbmp, _cnoCur, &_stnT, fTrue);
_chse.DumpParentCmd(kctgCam, cnoPar, 0);
zbmpf.bo = kboCur;
zbmpf.osk = koskCur;
zbmpf.xpLeft = zbmpf.ypTop = 0;
Assert(dxp <= kswMax, "Zbmp too wide");
Assert(dyp <= kswMax, "Zbmp too tall");
zbmpf.dxp = (short)dxp;
zbmpf.dyp = (short)dyp;
if (_stnT.FFormatSz(kszZbmp, pstnBkgd, iCam))
{
_ps2blx->GetFni(&fniZbmp);
if (fniZbmp.FSetLeaf(&_stnT, kftgZbmp))
{
pfilZbmp = FIL::PfilCreate(&fniZbmp);
fpZbmp = 0;
if (pfilZbmp != pvNil)
{
if (pfilZbmp->FWriteRgbSeq(&zbmpf, size(zbmpf), &fpZbmp) &&
pfilZbmp->FWriteRgbSeq(prgsw, cbSw, &fpZbmp))
{
fWroteZbmp = fTrue;
fniZbmp.GetStnPath(&_stnT);
}
else
pfilZbmp->SetTemp();
ReleasePpo(&pfilZbmp);
}
}
}
if (!fWroteZbmp)
{
_chse.DumpRgb(&zbmpf, size(zbmpf));
_chse.DumpRgb(prgsw, cbSw);
}
else
_chse.DumpFileCmd(&_stnT);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
/* Prevent us from freeing a local */
if (prgfl != &fl)
FreePpv((void **)&prgfl);
FreePpv((void **)&prgsw);
ReleasePpo(&pfil);
}
else
{
printf("Error: Couldn't open z-buffer file\n");
goto LFail;
}
}
else
{
printf("Error: Couldn't build z-buffer filename\n");
goto LFail;
}
fRet = fTrue;
LFail:
ReleasePpo(&pfil);
return fRet;
}
/******************************************************************************
_FBvec3Read
Reads a vector (triplet) from the given token stream
Arguments:
PS2BLX ps2blx -- the lexer to use (token stream)
BVEC3 *pbvec3 -- the vector to fill in
S2BTK *ps2btk -- the token to use
Returns: fTrue if ptok contains a new unused token
************************************************************ PETED ***********/
bool S2B::_FBvec3Read(PS2BLX ps2blx, BVEC3 *pbvec3, S2BTK *ps2btk)
{
bool fRet;
int iBrs;
for (iBrs = 0; iBrs < 3; iBrs++)
{
fRet = ps2blx->FGetS2btk(ps2btk);
if (!fRet || !_FBrsFromS2btk(ps2btk, &pbvec3->v[iBrs]))
{
printf("Error: Invalid triplet data\n");
goto LFail;
}
}
LFail:
return fRet;
}
/******************************************************************************
_Bmat34FromVec3
Given a vector, will calculate a transformation matrix that rotates
the actor's coordinate system so that the positive Z axis points along
the vector, and the X axis is horizontal.
Arguments:
BVEC3 *pbvec3 -- The vector to use
BMAT34 *pbmat34 -- the matrix to fill in
************************************************************ PETED ***********/
void S2B::_Bmat34FromVec3(BVEC3 *pbvec3, BMAT34 *pbmat34)
{
BRS brs, brsAdj, brsHyp;
BRXFM brxfm;
/* Set up */
ClearPb(&brxfm, size(brxfm));
brxfm.type = BR_TRANSFORM_EULER;
brxfm.t.euler.e.order = BR_EULER_YXZ_R;
/* Calculate rotation about Y */
brsHyp = BR_LENGTH2(pbvec3->v[0], pbvec3->v[2]);
brs = BrAngleToScalar(BR_ACOS(BR_DIV(pbvec3->v[2], brsHyp)));
if (pbvec3->v[0] < 0)
brs = BR_SUB(BR_SCALAR(1), brs);
Assert(brs >= 0 && BrScalarToFloat(brs) <= 1.0, "Bad angle");
brxfm.t.euler.e.a = BrScalarToAngle(brs);;
/* Calculate rotation about X */
brsAdj = brsHyp;
brsHyp = BR_LENGTH2(pbvec3->v[1], brsAdj);
brs = BrAngleToScalar(BR_ACOS(BR_DIV(brsAdj, brsHyp)));
if (pbvec3->v[1] > 0)
brs = BR_SUB(BR_SCALAR(1), brs);
Assert(brs >= 0 && BrScalarToFloat(brs) <= 1.0, "Bad angle");
brxfm.t.euler.e.b = BrScalarToAngle(brs);
/* Actually perform the rotations */
brxfm.t.euler.e.c = BrScalarToAngle(BR_SCALAR(0));
BrMatrix34Transform(pbmat34, &brxfm);
}
/******************************************************************************
_ReadLite
Reads a SoftImage ASCII light file and fills in the LITE structure
as appropriate.
Arguments:
PSTN pstnLite -- the name of the light file
LITE *plite -- pointer to LITE structure to fill in
************************************************************ PETED ***********/
void S2B::_ReadLite(PSTN pstnLite, LITE *plite)
{
FNI fni;
FIL *pfil;
/* Attempt to open the file */
_ps2blx->GetFni(&fni);
if (fni.FSetLeaf(pstnLite, kftgALite))
{
Assert(fni.Ftg() == kftgALite, "Light file has wrong extension");
if ((pfil = FIL::PfilOpen(&fni)) != pvNil)
{
bool fGotTok;
BVEC3 bvec3Int = { BR_SCALAR(0), BR_SCALAR(0), BR_SCALAR(0) };
BVEC3 bvec3Pos;
S2BLX s2blx(pfil, fFalse);
S2BTK s2btk;
/* Parse the data in the file */
fGotTok = s2blx.FGetS2btk(&s2btk);
while (fGotTok)
{
switch (s2btk.tok.tt)
{
case ttPosition:
if (!(fGotTok = s2blx.FGetS2btk(&s2btk)) ||
s2btk.tok.tt != ttStatic)
{
printf("Error: Expected " DK_A_POS_STATIC_TOKEN
" after " DK_A_LIGHT_POS_TOKEN "\n");
goto LFail;
}
/* Read the vector */
if (!_FBvec3Read(&s2blx, &bvec3Pos, &s2btk))
goto LFail;
break;
case ttInterest:
if (!(fGotTok = s2blx.FGetS2btk(&s2btk)) ||
s2btk.tok.tt != ttStatic)
{
printf("Error: Expected " DK_A_POS_STATIC_TOKEN
" after " DK_A_LIGHT_INT_TOKEN "\n");
goto LFail;
}
/* Read the vector */
if (!_FBvec3Read(&s2blx, &bvec3Int, &s2btk))
goto LFail;
/* Generate vector for light's +Z axis */
BrVector3Sub(&bvec3Int, &bvec3Pos, &bvec3Int);
/* Create light's transformation matrix */
_Bmat34FromVec3(&bvec3Int, &plite->bmat34);
BrMatrix34PostTranslate(&plite->bmat34, bvec3Pos.v[0],
bvec3Pos.v[1], bvec3Pos.v[2]);
break;
default:
break;
}
fGotTok = s2blx.FGetS2btk(&s2btk);
}
ReleasePpo(&pfil);
}
else
printf("Error: Couldn't open light file\n");
}
else
printf("Error: Couldn't build light filename\n");
LFail:
return;
}
/******************************************************************************
_ReadCam
Reads a SoftImage ASCII camera file and fills in the given CAM
structure as appropriate.
Arguments:
PSTN pstnCam -- the name of the camera file
CAM *pcam -- pointer to the CAM to fill in
************************************************************ PETED ***********/
void S2B::_ReadCam(PSTN pstnCam, CAM *pcam, PGL *ppglapos)
{
bool fGotActorPos = fFalse;
FNI fni;
FIL *pfil;
/* Attempt to open the camera file */
_ps2blx->GetFni(&fni);
if (fni.FSetLeaf(pstnCam, kftgACam))
{
Assert(fni.Ftg() == kftgACam, "Camera file has wrong extension");
if ((pfil = FIL::PfilOpen(&fni)) != pvNil)
{
bool fGotTok;
BVEC3 bvec3Int;
BVEC3 bvec3Pos;
S2BLX s2blx(pfil, fFalse);
S2BTK s2btk;
/* Parse the data in the camera file */
fGotTok = s2blx.FGetS2btk(&s2btk);
while (fGotTok)
{
switch (s2btk.tok.tt)
{
case ttPosition:
if (!(fGotTok = s2blx.FGetS2btk(&s2btk)) ||
s2btk.tok.tt != ttStatic)
{
printf("Error: Expected " DK_A_POS_STATIC_TOKEN
" after " DK_A_CAMERA_POS_TOKEN "\n");
goto LFail;
}
/* Read the position */
if (!_FBvec3Read(&s2blx, &bvec3Pos, &s2btk))
goto LFail;
break;
case ttInterest:
if (!(fGotTok = s2blx.FGetS2btk(&s2btk)) ||
s2btk.tok.tt != ttStatic)
{
printf("Error: Expected " DK_A_POS_STATIC_TOKEN
" after " DK_A_CAMERA_INT_TOKEN "\n");
goto LFail;
}
/* Read the interest (where the camera's looking) */
if (!_FBvec3Read(&s2blx, &bvec3Int, &s2btk))
goto LFail;
/* Create vector for camera's positive X-axis (away from
where it's looking */
BrVector3Sub(&bvec3Int, &bvec3Pos, &bvec3Int);
/* Get transformation for camera */
_Bmat34FromVec3(&bvec3Int, &pcam->bmat34Cam);
BrMatrix34PostTranslate(&pcam->bmat34Cam, bvec3Pos.v[0],
bvec3Pos.v[1], bvec3Pos.v[2]);
break;
case ttNearCam: // Near plane
if (!(fGotTok = s2blx.FGetS2btk(&s2btk)) ||
!_FBrsFromS2btk(&s2btk, &pcam->zrHither))
{
printf("Error: Invalid " DK_A_CAMERA_NEAR_TOKEN " data\n");
goto LFail;
}
break;
case ttFarCam: // Far plane
if (!(fGotTok = s2blx.FGetS2btk(&s2btk)) ||
!_FBrsFromS2btk(&s2btk, &pcam->zrYon))
{
printf("Error: Invalid " DK_A_CAMERA_FAR_TOKEN " data\n");
goto LFail;
}
break;
case ttFovCam: // Field Of View
{
BRS brs;
if (!(fGotTok = s2blx.FGetS2btk(&s2btk)) ||
!_FBrsFromS2btk(&s2btk, &brs))
{
printf("Error: Invalid " DK_A_CAMERA_FOV_TOKEN " data\n");
goto LFail;
}
pcam->aFov = BrRadianToAngle(brs);
break;
}
case ttNewActorPos:
{
BVEC3 bvec3Actor;
if (!_FBvec3Read(&s2blx, &bvec3Actor, &s2btk))
goto LFail;
if (!fGotActorPos)
{
pcam->apos.bvec3Actor = bvec3Actor;
fGotActorPos = fTrue;
}
else
{
if (*ppglapos != pvNil ||
(*ppglapos = GL::PglNew(size(APOS))) != pvNil)
{
APOS apos;
apos.bvec3Actor = bvec3Actor;
(*ppglapos)->FAdd(&apos); // Ignore failure
}
}
break;
}
default:
break;
}
fGotTok = s2blx.FGetS2btk(&s2btk);
}
ReleasePpo(&pfil);
if (!fGotActorPos)
{
BRS zrCam = kzrDefault;
/* Ensure depth of location is within the bounds of the cam */
if (BR_ABS(zrCam) > pcam->zrYon)
zrCam = BR_CONST_DIV(BR_ADD(pcam->zrYon, pcam->zrHither), -2);
/* Translate (0,0,zrCam) in cam coords into world coords */
pcam->apos.bvec3Actor.v[0] = BR_ADD(
BR_MUL(kzrDefault, pcam->bmat34Cam.m[2][0]),
pcam->bmat34Cam.m[3][0]);
pcam->apos.bvec3Actor.v[1] = rZero;
pcam->apos.bvec3Actor.v[2] = BR_ADD(
BR_MUL(kzrDefault, pcam->bmat34Cam.m[2][2]),
pcam->bmat34Cam.m[3][2]);
}
}
else
printf("Error: Couldn't open camera file\n");
}
else
printf("Error: Couldn't build camera filename\n");
LFail:
return;
}
/*-----------------------------------------------------------------------------
| _FProcessModel
| Converts a SoftImage model hierarchy into a tree of Brender models.
| Only mesh nodes are saved, but transformation information in propogated
| to children nodes as necessary.
|
| Arguments:
| Model *pmodel -- points to the root of the model hierarchy to convert
| BMAT34 bmat34Acc -- transformation matrix to be included in current
| node's transformation.
| PBMHR *ppbmhr -- points to the pointer to the root of the Brender
| model hierarchy.
| PBMHR pbmhrParent -- points to the actual parent node of this node
|
| Returns:
| fTrue if succeeds, fFalse otherwise (usually due to OOM)
|
-------------------------------------------------------------PETED-----------*/
bool S2B::_FProcessModel(Model *pmodel, BMAT34 bmat34Acc, PBMHR *ppbmhr,
PSTN pstnSubmodel, PBMHR pbmhrParent, int cLevel)
{
bool fRet = fTrue, fSubmodel, fKeepNode;
BRA braX, braY, braZ;
BRS brsX, brsY, brsZ;
Model *pmodelChild = pmodel->child;
if ((fSubmodel = (pstnSubmodel == pvNil || (pstnSubmodel->Cch() == 0) ||
pstnSubmodel->FEqualSz(pmodel->name))) != fFalse)
{
bool fHaveBPS = fFalse;
pstnSubmodel = pvNil;
/* Include translation */
brsX = BrFloatToFixed(pmodel->translation.x);
brsY = BrFloatToFixed(pmodel->translation.y);
brsZ = BrFloatToFixed(_iZsign * pmodel->translation.z);
if (_uRoundXF > 0)
{
brsX = BR_ROUND(brsX, _uRoundXF);
brsY = BR_ROUND(brsY, _uRoundXF);
brsZ = BR_ROUND(brsZ, _uRoundXF);
}
BrMatrix34PreTranslate(&bmat34Acc, brsX, brsY, brsZ);
/* Include rotation */
braX = BrRadianToAngle(BrFloatToFixed(pmodel->rotation.x));
braY = BrRadianToAngle(BrFloatToFixed(pmodel->rotation.y));
braZ = BrRadianToAngle(BrFloatToFixed(pmodel->rotation.z));
if (_uRoundXF > 0)
{
braX = BrScalarToAngle(BR_ROUND(BrAngleToScalar(braX), _uRoundXF));
braY = BrScalarToAngle(BR_ROUND(BrAngleToScalar(braY), _uRoundXF));
braZ = BrScalarToAngle(BR_ROUND(BrAngleToScalar(braZ), _uRoundXF));
}
BrMatrix34PreRotateZ(&bmat34Acc, braZ);
BrMatrix34PreRotateY(&bmat34Acc, braY);
BrMatrix34PreRotateX(&bmat34Acc, braX);
/* Include scaling (comes last for SoftImage) */
brsX = BrFloatToFixed(pmodel->scaling.x);
brsY = BrFloatToFixed(pmodel->scaling.y);
brsZ = BrFloatToFixed(pmodel->scaling.z);
if (_uRoundXF > 0)
{
brsX = BR_ROUND(brsX, _uRoundXF);
brsY = BR_ROUND(brsY, _uRoundXF);
brsZ = BR_ROUND(brsZ, _uRoundXF);
}
BrMatrix34PreScale(&bmat34Acc, brsX, brsY, brsZ);
fHaveBPS = CchSz(pmodel->name) > 1 && _ps2blx->FIsDigit(pmodel->name[0])
&& _ps2blx->FIsDigit(pmodel->name[1]);
fKeepNode = (pmodel->type==DK_MDL_MESH || pmodel->type==DK_MDL_NILL);
switch (_mdBPS)
{
case kmdbpsNone:
fHaveBPS = fFalse;
break;
case kmdbpsNumberedOnly:
fKeepNode = fKeepNode && fHaveBPS;
break;
case kmdbpsAllMesh:
fKeepNode = fKeepNode && (fHaveBPS || pmodel->type == DK_MDL_MESH);
break;
case kmdbpsAllMeshNull:
break;
}
if (fKeepNode)
{
bool fAccessory;
int ibps;
if (fHaveBPS)
{
fAccessory = (CchSz(pmodel->name) > 4) &&
(pmodel->name[3] == ChLit('A')) &&
(pmodel->name[4] == ChLit('C'));
ibps = (pmodel->name[0] - ChLit('0')) * 10 +
pmodel->name[1] - ChLit('0');
}
else
{
fAccessory = fFalse;
ibps = 0;
}
/* Output info */
if (_fMakeGlpi && (_mdVerbose > kmdQuiet) || _mdVerbose > kmdHelpful)
{
if (cLevel > 0)
printf("%*c", cLevel * 2, ' ');
printf("node \"%s\" (body part set %d)\n", pmodel->name, ibps);
}
cLevel++;
/* create model node */
if ((pbmhrParent = _PbmhrFromModel(pmodel, &bmat34Acc, ppbmhr,
pbmhrParent, ibps, fAccessory)) == pvNil)
{
printf("Out of memory allocating model node\n");
goto LFail;
}
pbmhrParent->fAccessory = fAccessory;
/* Anchor this node */
_cMesh++;
_cFace += pbmhrParent->pmodlf->cfac;
ppbmhr = &(pbmhrParent->pbmhrChild);
BrMatrix34Identity(&bmat34Acc);
}
else if (pmodel->type == DK_MDL_MESH && _fMakeGlpi)
printf("Warning: mesh node \"%s\" has no body part set\n",
pmodel->name);
}
/* Descend and process any children */
while (pmodelChild != pvNil)
{
Assert(*ppbmhr == pvNil, "Non-nil parent pointer");
if (!_FProcessModel(pmodelChild, bmat34Acc, ppbmhr, pstnSubmodel, pbmhrParent, cLevel))
goto LFail;
while (*ppbmhr != pvNil)
ppbmhr = &((*ppbmhr)->pbmhrSibling);
pmodelChild = pmodelChild->sibling;
}
return fTrue;
/* Clean up if we fail */
LFail:
_DisposeBmhr(&_pbmhr);
return fFalse;
}
/*-----------------------------------------------------------------------------
| _PbmhrFromModel
| Converts a single SoftImage model node into a single Brender model
| node. Adds the new node as a child to the parent node.
|
| Arguments:
| Model *pmodel -- points to SoftImage model node to convert
| BMAT34 *pbmat34 -- specifies the transformation for this node
| PBMHR *ppbmhr -- points to pointer that points to this new child
| PBMHR pbmhrParent -- points to the actual parent of this new child
| int ibps -- body part set number
| bool fAccessory -- fTrue if the body part is an accessory
|
| Returns:
| A pointer to the new node, pvNil if there was some error
|
-------------------------------------------------------------PETED-----------*/
PBMHR S2B::_PbmhrFromModel(Model *pmodel, BMAT34 *pbmat34, PBMHR *ppbmhr,
PBMHR pbmhrParent, int ibps, bool fAccessory)
{
bool fMesh;
long cb;
long cbrgver;
long cbrgfac;
PBMHR pbmhrCur = pvNil;
MODLF *pmodlf;
Mesh *pmesh = (Mesh *)pmodel->definition;
Material *pmaterial;
CRNG crng;
BRCLR brclr;
/* Ignore geometry if it doesn't exist, or if we're only interested in
the material information. Theoretically, we should really just not
bother with the modlf at all if we're only making the costume, but
we gain back most of the overhead with this simple change. */
fMesh = pmesh != pvNil && (!_fCostumeOnly || fAccessory);
/* Allocate necessary pieces: BMHR, appropriately sized MODLF, and name */
if (fMesh)
{
Assert(pmodel->type == DK_MDL_MESH, "Data present in non-mesh model");
cbrgver = LwMul(pmesh->nbVertices, size(br_vertex));
cbrgfac = LwMul(pmesh->nbPolygons, size(br_face));
cb = size(MODLF) + cbrgver + cbrgfac;
}
else
{
Assert(_fCostumeOnly || pmodel->type != DK_MDL_MESH, "Mesh model has no data");
cb = size(MODLF);
}
if (!FAllocPv((void **)&pmodlf, cb, fmemClear, mprNormal))
{
printf("Couldn't allocate MODLF structure\n");
goto LFail;
}
if (!FAllocPv((void **)&pbmhrCur, size(BMHR), fmemClear, mprNormal))
{
printf("Couldn't allocate Brender hierarchy node\n");
FreePpv((void **)&pmodlf);
goto LFail;
}
if (FAllocPv((void **)&pbmhrCur->pszName,
CchTotSz(pmodel->name) * size(achar), fmemNil, mprNormal))
{
CopyPb(pmodel->name, pbmhrCur->pszName,
CchTotSz(pmodel->name) * size(achar));
}
#ifdef DEBUG
else
Assert(pbmhrCur->pszName == pvNil, "Bad pointer");
#endif // DEBUG
pbmhrCur->pmodlf = pmodlf;
_InitBmhr(pbmhrCur);
#ifdef DEBUG
/* Just to be nice, keep siblings in same order as in SoftImage data */
/* This housework is done in FProcessModel now so as to avoid iterating
over the sibling chain over and over */
Assert(*ppbmhr == pvNil, "Improper call to _PbmhrFromModel");
while (*ppbmhr != pvNil)
ppbmhr = &((*ppbmhr)->pbmhrSibling);
#endif // DEBUG
/* Fill in MODLF */
if (fMesh)
{
pmodlf->cver = (short)pmesh->nbVertices;
pmodlf->cfac = (short)pmesh->nbPolygons;
_CopyVertices(pmesh->vertices,
PvAddBv(pmodlf, size(MODLF)), pmesh->nbVertices);
_CopyFaces(pmesh->polygons,
PvAddBv(pmodlf, size(MODLF) + cbrgver), pmesh->nbPolygons,
(BRV *)PvAddBv(pmodlf, size(MODLF)), pmodlf->cver);
/* I considered having the _Copy... routines do the hashing, since
they're already stepping through the bytes. However, they do
such little pieces at a time, I believe that the function-call
overhead would obliterate any savings from doing so. */
}
else
pmodlf->cver = pmodlf->cfac = 0;
/* Fill in BMHR */
pbmhrCur->pmodlf = pmodlf;
pbmhrCur->cbModlf = cb;
pbmhrCur->bmat34 = *pbmat34;
pbmhrCur->ibps = ibps;
pbmhrCur->fFixWrap = fFalse;
/* Handle material information */
pmaterial = pmodel->materials;
if (pmaterial != pvNil)
_TextureFileFromModel(pmodel, pbmhrCur, !_fMakeCostume || _pglclr == pvNil);
if (_fMakeCostume && _pglclr != pvNil)
{
Assert(pmodel->currentMaterial == 0, "Using the wrong material");
/* Get material from this model, if it exists */
if (pmaterial != pvNil)
{
brclr = BR_DK_TO_BR(pmaterial->ambient);
pbmhrCur->fMtrlf = fTrue;
_pglcrng->Get(LwcrngNearestBrclr(brclr, _pglclr, _pglcrng), &crng);
if (_fColorOnly)
goto LColorOnly;
pbmhrCur->mtrlf.brufKa = BR_DKRGB_TO_FRGRAY(pmaterial->ambient);
pbmhrCur->mtrlf.brufKd = BR_DKRGB_TO_FRGRAY(pmaterial->diffuse);
if (pmaterial->type == DK_MAT_LAMBERT)
pbmhrCur->mtrlf.brufKs = BR_SCALAR(0);
else
pbmhrCur->mtrlf.brufKs = BR_DKRGB_TO_FRGRAY(pmaterial->specular);
goto LFinishMtrl;
}
/* If this model has no material of its own and it has a parent,
get the parent's material */
else if (pbmhrParent != pvNil)
{
Assert(!fMesh || pbmhrParent->fMtrlf, "Warning: mesh node has no material parent");
pbmhrCur->mtrlf = pbmhrParent->mtrlf;
/* REVIEW peted: bogus...if an STN were derived from the BASE class,
I could just AddRef it and copy the pointer here rather than
allocating a whole new STN */
if (pbmhrParent->pstnMtrlFile != pvNil &&
(pbmhrCur->pstnMtrlFile = new STN()) != pvNil)
{
*pbmhrCur->pstnMtrlFile = *pbmhrParent->pstnMtrlFile;
}
pbmhrCur->fMtrlf = pbmhrParent->fMtrlf;
}
/* If there's no parent, and no material, make something up */
else
{
if (fMesh)
printf("Warning: missing material for mesh node!\n");
Assert(!pbmhrCur->fMtrlf, 0);
_pglcrng->Get(0, &crng);
LColorOnly:
pbmhrCur->mtrlf.brufKa = BR_UFRACTION(0);
pbmhrCur->mtrlf.brufKd = BR_UFRACTION(1.0);
pbmhrCur->mtrlf.brufKs = BR_UFRACTION(0);
/* Finish filling in the material information */
LFinishMtrl:
pbmhrCur->mtrlf.brc = 0; // unused by Socrates
Assert(crng.lwBase < kbMax, "Color index base too high");
Assert(crng.lwRange < kbMax, "Color index range too high");
pbmhrCur->mtrlf.bIndexBase = (byte)crng.lwBase;
/* Brender color range is defined by max val rather than count */
pbmhrCur->mtrlf.cIndexRange = (byte)crng.lwRange - 1;
pbmhrCur->mtrlf.rPower = BrIntToScalar(50); // REVIEW peted: need real val here
}
}
Assert(pbmhrCur->pbmhrChild == pvNil, 0);
/* Hook it in */
*ppbmhr = pbmhrCur;
LFail:
return pbmhrCur;
}
/******************************************************************************
_TextureFileFromModel
Extracts the base texture name from the file name given in a model's
material information. First looks in the model itself for a texture
filename, and if no texture is found there, checks the first material
listed for the model. Copies texture translation and cropping info
if the texture is present.
Arguments:
Model *pmodel -- The model to process
PBMHR pbmhr -- the actual hiearchy node
bool fWrapOnly -- just fill in the wrap-fix stuff
************************************************************ PETED ***********/
void S2B::_TextureFileFromModel(Model *pmodel, PBMHR pbmhr, bool fWrapOnly)
{
AssertVarMem(pmodel);
AssertVarMem(pmodel->materials);
AssertVarMem(pbmhr);
char *pch, *pszName;
PSTN pstn = pvNil;
Texture *ptexture;
/* Look for a texture in the model; if the model doesn't have one, check
its first material */
if (pmodel->nbTextures < 1)
{
if (pmodel->materials->nbTextures < 1)
goto LNotexture;
ptexture = pmodel->materials->textures;
}
else
ptexture = pmodel->textures;
if (fWrapOnly)
goto LDoWrap;
pstn = new STN();
if (pstn != pvNil)
{
pszName = ptexture->pic_name;
pch = pszName + strlen(pszName);
while (*(--pch) != ChLit('/') && pch > pszName);
if (*pch == ChLit('/'))
pch++;
*pstn = pch;
pbmhr->brufrUOffset = BrScalarToUFraction(BrFloatToScalar(ptexture->uOffset));
pbmhr->brufrVOffset = BrScalarToUFraction(BrFloatToScalar(ptexture->vOffset));
pbmhr->fCrop = FPure(ptexture->cropping) && (ptexture->uMinCrop ||
ptexture->uMaxCrop || ptexture->vMinCrop || ptexture->vMaxCrop);
if (pbmhr->fCrop)
{
pbmhr->uMinCrop = ptexture->uMinCrop;
pbmhr->uMaxCrop = ptexture->uMaxCrop;
pbmhr->vMinCrop = ptexture->vMinCrop;
pbmhr->vMaxCrop = ptexture->vMaxCrop;
}
LDoWrap:
switch (ptexture->method)
{
case DK_TXT_SPHERICAL:
pbmhr->fSpherical = fTrue;
case DK_TXT_CYLINDRICAL:
pbmhr->fFixWrap = fTrue;
break;
default:
Assert(!pbmhr->fFixWrap, "Inconsistent fixwrap setting for node");
break;
}
}
LNotexture:
pbmhr->pstnMtrlFile = pstn;
}
/******************************************************************************
_FTmapFromBmp
Given a texture name, adds the texture to the MTRL with the given CNO.
If this texture has never been seen before, the .bmp file is converted
to an appropriate TMAP chunk file. The reference to the parent MTRL's
CNO is added to our list of generated TMAPs for use later in actually
dumping out the TMAP chunk definition.
Arguments:
PSTN pstnBmpFile -- the name of the texture
CNO cnoPar -- the CNO of the parent MTRL
pstnMtrl -- the string used for the MTRL name
Returns: fTrue if the texture was successfully added
************************************************************ PETED ***********/
bool S2B::_FTmapFromBmp(PBMHR pbmhr, CNO cnoPar, PSTN pstnMtrl)
{
Assert(pbmhr != pvNil, 0);
AssertPo(pbmhr->pstnMtrlFile, 0);
bool fRet = fFalse;
long itmapd, itmapdMac;
FNI fni;
PTMAP ptmap = pvNil;
TMAPD tmapd;
PSTN pstnBmpFile = pbmhr->pstnMtrlFile;
/* Look for the bitmap file in our list */
if (_pggtmapd == pvNil && (_pggtmapd = GG::PggNew(size(TMAPD))) == pvNil)
goto LFail;
itmapdMac = _pggtmapd->IvMac();
for (itmapd = 0; itmapd < itmapdMac; itmapd++)
{
_pggtmapd->GetFixed(itmapd, &tmapd);
if (pstnBmpFile->FEqual(tmapd.pstn))
break;
}
if (itmapd == itmapdMac)
{
_stnT = *pstnBmpFile;
_ps2blx->GetFni(&fni);
if (!fni.FSetLeaf(&_stnT, kftgBmp))
goto LFail;
ptmap = TMAP::PtmapReadNative(&fni);
if (ptmap == pvNil)
goto LFail;
if (!fni.FChangeFtg(kftgTmapChkFile))
goto LFail;
if ((tmapd.pstn = new STN()) == pvNil)
goto LFail;
*tmapd.pstn = *pstnBmpFile;
tmapd.ccnoPar = 1;
tmapd.xp = ptmap->Pbpmp()->width;
tmapd.yp = ptmap->Pbpmp()->height;
if (!_pggtmapd->FAdd(size(CNO), pvNil, &cnoPar, &tmapd))
goto LFailAdd;
/* Write the file out last; it's easier to remove the reference to a
failed file than to delete a file that's not referenced :) */
if(!ptmap->FWriteTmapChkFile(&fni, fTrue))
{
_pggtmapd->Delete(itmapd);
LFailAdd:
delete tmapd.pstn;
goto LFail;
}
}
else
{
if (!_pggtmapd->FInsertRgb(itmapd, tmapd.ccnoPar * size(CNO), size(CNO), &cnoPar))
goto LFail;
tmapd.ccnoPar++;
_pggtmapd->PutFixed(itmapd, &tmapd);
}
if (pbmhr->brufrUOffset != BR_UFRACTION(0) ||
pbmhr->brufrVOffset != BR_UFRACTION(0) || pbmhr->fCrop)
{
BRS brsXScale, brsYScale;
BRS brsdu, brsdv;
TXXFF txxff;
txxff.bo = kboCur;
txxff.osk = koskCur;
/* Scaling is the ratio of the width and height of the bitmap left
after removing the cropped areas to the width and height of the
original bitmap. The pixel u/vMax is included in the cropped
and offset texture. */
brsXScale = BR_DIV(
BrIntToScalar(pbmhr->uMaxCrop - pbmhr->uMinCrop + 1),
BrIntToScalar(tmapd.xp));
brsYScale = BR_DIV(
BrIntToScalar(pbmhr->vMaxCrop - pbmhr->vMinCrop + 1),
BrIntToScalar(tmapd.yp));
BrMatrix23Scale(&txxff.bmat23, brsXScale, brsYScale);
/* Total offset is the specified offset, plus the necessary offset
for any cropping */
brsdu = BR_ADD(BrUFractionToScalar(pbmhr->brufrUOffset),
BR_DIV(BrIntToScalar(pbmhr->uMinCrop), BrIntToScalar(tmapd.xp)));
brsdv = BR_SUB(
BR_DIV(BrIntToScalar(tmapd.yp - pbmhr->vMaxCrop),
BrIntToScalar(tmapd.yp)),
BrUFractionToScalar(pbmhr->brufrVOffset));
BrMatrix23PostTranslate(&txxff.bmat23, brsdu, brsdv);
/* Dump out the TXXFF chunk for this material */
pstnMtrl->FAppendSz(PszLit(" Texture transform"));
CnoNext();
_DumpHeader(kctgTxxf, _cnoCur, pstnMtrl, fTrue);
_chse.DumpParentCmd(kctgMtrl, cnoPar, 0);
_chse.DumpRgb(&txxff, size(txxff));
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
}
fRet = fTrue;
LFail:
ReleasePpo(&ptmap);
return fRet;
}
/******************************************************************************
_FFlushTmaps
Actually writes out the TMAP definitions to the chunk source file.
Each unique TMAP chunk is added once, with each MTRL that refers to
it being included as a parent of the TMAP chunk.
Returns: fTrue if all of the TMAP declarations could be generated; in
theory, since we would have failed to even add a given TMAP to the list
of TMAPs if we couldn't successfully generate the complete filename
*and* failing to generate the complete filename is the only way for
this routine to fail, this routine should never fail.
************************************************************ PETED ***********/
bool S2B::_FFlushTmaps(void)
{
AssertPo(_pggtmapd, 0);
bool fRet = fTrue;
long itmapd, itmapdMac = _pggtmapd->IvMac();
long icnoPar;
CNO cnoPar;
TMAPD tmapd;
for (itmapd = 0; itmapd < itmapdMac; itmapd++)
{
FNI fni;
_pggtmapd->GetFixed(itmapd, &tmapd);
/* Generate the full filename for this texture */
_stnT = *tmapd.pstn;
_ps2blx->GetFni(&fni);
if (!fni.FSetLeaf(&_stnT, kftgTmapChkFile))
{
fRet = fFalse;
continue;
}
/* Emit the header */
CnoNext();
fni.GetLeaf(&_stnT);
_DumpHeader(kctgTmap, _cnoCur, &_stnT, fFalse);
/* Dump out all the necessary PARENT declarations */
for (icnoPar = 0; icnoPar < tmapd.ccnoPar; icnoPar++)
{
_pggtmapd->GetRgb(itmapd, icnoPar * size(CNO), size(CNO), &cnoPar);
_chse.DumpParentCmd(kctgMtrl, cnoPar, 0);
}
/* Finish up the chunk */
fni.GetStnPath(&_stnT);
_chse.DumpFileCmd(&_stnT, fTrue);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
}
Assert(fRet, "_FFlushTmaps should never fail");
return fRet;
}
/******************************************************************************
_BrsdwrFromModel
Computes a distance-between-cels for the given model, given the
position of the last model.
Arguments:
Model *pmodel -- the new model
BRS rgbrsDwr[] -- the position of the last model
Returns: a Brender scalar that's the distance
************************************************************ PETED ***********/
BRS S2B::_BrsdwrFromModel(Model *pmodel, BRS rgbrsDwr[])
{
BRS brsDwr, brsX, brsY, brsZ;
/* Calculate difference */
brsX = BrFloatToScalar(pmodel->translation.x);
brsY = BrFloatToScalar(pmodel->translation.y);
brsZ = BrFloatToScalar(pmodel->translation.z);
brsDwr = BR_LENGTH3(BR_SUB(rgbrsDwr[0], brsX),
BR_SUB(rgbrsDwr[1], brsY),
BR_SUB(rgbrsDwr[2], brsZ));
/* Remember current model position */
rgbrsDwr[0] = brsX;
rgbrsDwr[1] = brsY;
rgbrsDwr[2] = brsZ;
/* Set model back to origin */
pmodel->translation.x = pmodel->translation.y =
pmodel->translation.z = (float)0.0;
return brsDwr;
}
/*-----------------------------------------------------------------------------
| _CopyVertices
| Copy floating point vertices from the SoftImage data structure to
| Brender scalar vertices needed by the Brender data structure.
|
| Arguments:
| DK_Vertex *vertices -- pointer to SoftImage vertices array
| void *pvDst -- pointer to start of Brender vertices in MODLF
| long cVertices -- count of vertices
|
-------------------------------------------------------------PETED-----------*/
void S2B::_CopyVertices(DK_Vertex *vertices, void *pvDst, long cVertices)
{
BRV *pbrv = (BRV *)pvDst;
DK_Point vtxMin = {(float)klwMax, (float)klwMax, (float)klwMax},
vtxMax = {(float)klwMin, (float)klwMin, (float)klwMin};
for ( ; cVertices; cVertices--, vertices++, pbrv++)
{
BRS *pbrs = &pbrv->p.v[0], *pbrsMac = &pbrv->p.v[3];
float *pflPos = &vertices->position.x;
vertices->position.z *= _iZsign;
while (pbrs < pbrsMac)
{
*pbrs = BrFloatToScalar(*pflPos);
if (_uRound > 0)
*pbrs = BR_ROUND(*pbrs, _uRound);
pbrs++;
pflPos++;
}
if (_mdVerbose > kmdHelpful)
{
if (vertices->position.x < vtxMin.x)
vtxMin.x = vertices->position.x;
if (vertices->position.x > vtxMax.x)
vtxMax.x = vertices->position.x;
if (vertices->position.y < vtxMin.y)
vtxMin.y = vertices->position.y;
if (vertices->position.y > vtxMax.y)
vtxMax.y = vertices->position.y;
if (vertices->position.z < vtxMin.z)
vtxMin.z = vertices->position.z;
if (vertices->position.z > vtxMax.z)
vtxMax.z = vertices->position.z;
}
}
if (_mdVerbose > kmdHelpful)
{
if (_iZsign < 0)
SwapVars(&vtxMin.z, &vtxMax.z);
printf("Node bounds: (% 6.2f, % 6.2f, % 6.2f) - (% 6.2f, % 6.2f, % 6.2f)\n",
vtxMin.x, vtxMin.y, _iZsign * vtxMin.z,
vtxMax.x, vtxMax.y, _iZsign * vtxMax.z );
}
}
/*-----------------------------------------------------------------------------
| _CopyFaces
| Copy all of the face data from the SoftImage list to the
| Brender list.
|
| Arguments:
| DK_Polygon *polygons -- list of SoftImage polygons (faces)
| void *pvDst -- pointer to array of Brender faces
| long cFaces -- count of faces to copy
|
-------------------------------------------------------------PETED-----------*/
void S2B::_CopyFaces(DK_Polygon *polygons, void *pvDst, long cFaces, BRV rgbrv[], long cVertices)
{
BRFC *pbrfc = (BRFC *)pvDst;
for ( ; cFaces; cFaces--, polygons++, pbrfc++)
{
int cNodes = min(polygons->nbNodes, 3), i;
DK_PolyNode *ppolynode = polygons->nodes;
br_uint_16 *pvtx = &pbrfc->vertices[0];
/* If we get an invalid node count, warn about it and then fill in
if necessary (REVIEW peted: seems highly unlikely that we'll
ever have too few vertices, seeing as how any less than 3
gives a very uninteresting face indeed) */
if (polygons->nbNodes != 3)
{
printf("SoftImage data has %d vertices for face\n", polygons->nbNodes);
Assert(cNodes == 3, "Better keep that 'fill-in' code");
for (i=cNodes; i < 3; i++)
pvtx[i] = (br_uint_16)ppolynode->vertex;
}
/* copy the vertices for this face */
for ( ; cNodes; cNodes--, ppolynode++, pvtx++)
{
*pvtx = (br_uint_16)ppolynode->vertex;
if (*pvtx < cVertices)
{
rgbrv[*pvtx].map.v[0] = BrFloatToScalar(ppolynode->uTxt);
rgbrv[*pvtx].map.v[1] = BrFloatToScalar(ppolynode->vTxt);
}
else
printf("Warning: vertex out of range for mesh\n");
}
pbrfc->smoothing = 0x0001;
}
}
/******************************************************************************
_FDoBodyPart
Handles a single body part. Sets the Brender body part set for the
given body part and stores that in the GL for the body part sets, and
then handles actually generating whatever costume information for this
body part is needed.
Arguments:
PBMHR pbmhr -- The BMHR that describes this body part
long ibp -- the actual body part number for this body part
Returns: fTrue if the body part could be added, and its basic costume
information could be written out; failing to add the TMAP information
simply generates a warning without causing this routine to return a
failure.
************************************************************ PETED ***********/
bool S2B::_FDoBodyPart(PBMHR pbmhr, long ibp)
{
bool fRet = fFalse, fDoMtrl = (_pglclr != pvNil && _pglcmtld != pvNil);
bool fUseMtrl;
short ibps;
long ibpGl;
CMTLD cmtld;
long icmtld, icmtldMac, iibps, iibpsMac;
/* Find the appropriate CMTL */
if (fDoMtrl)
{
/* We may be only making costumes for a subset of the body part sets */
if (_pglibps != pvNil)
{
iibpsMac = _pglibps->IvMac();
for (iibps = 0; iibps < iibpsMac; iibps++)
{
long ibpsUse;
_pglibps->Get(iibps, &ibpsUse);
if (ibpsUse == pbmhr->ibps)
break;
}
fUseMtrl = iibps < iibpsMac;
}
else
fUseMtrl = fTrue;
/* Go ahead and figure out the mapping from SI body part sets to
Brender body part sets */
icmtldMac = _pglcmtld->IvMac();
for (icmtld = 0; icmtld < icmtldMac; icmtld++)
{
_pglcmtld->Get(icmtld, &cmtld);
if (cmtld.ibps == pbmhr->ibps)
break;
}
AssertIn(icmtld, kswMin, kswMax + 1);
ibps = (short)icmtld;
}
else
ibps = pbmhr->ibps;
if (_fMakeGlpi && !_pglbs->FAdd(&ibps, &ibpGl))
goto LFail;
if (_fMakeCostume)
{
Assert(!_fMakeGlpi || ibpGl == ibp, "Weird order for GLBS");
/* Handle material stuff, if we have color info */
if (fDoMtrl)
{
long ccmid;
/* Create the CMTL chunk if necessary */
if (icmtld == icmtldMac)
{
long icmtldT;
/* Add it to our list */
cmtld.cno = CnoNext();
cmtld.chidCur = 0;
cmtld.ibps = pbmhr->ibps;
cmtld.chid = _chidCmtl;
if (!_pglcmtld->FAdd(&cmtld, &icmtldT))
goto LFail;
Assert(icmtldT == icmtld, "Bogus icmtld");
/* Add it to the GGCM */
Assert(ibps <= _pggcm->IvMac(), "ibps out of range");
if (ibps == _pggcm->IvMac())
{
long iv;
ccmid = 0;
if (!_pggcm->FAdd(0, &iv, pvNil, &ccmid))
goto LFail;
Assert(iv == ibps, "bogus iv");
}
if (fUseMtrl)
{
CMTLF cmtlf;
_chidCmtl++;
_pggcm->GetFixed(ibps, &ccmid);
if (!_pggcm->FInsertRgb(ibps, size(long) * ccmid, size(long), &cmtld.chid))
goto LFail;
ccmid++;
_pggcm->PutFixed(ibps, &ccmid);
/* Write a nice CMTL chunk */
cmtlf.bo = kboCur;
cmtlf.osk = koskCur;
cmtlf.ibset = ibps;
if (ccmid == 1)
{
_stnT.FFormatSz(PszLit("%s Body Part Set %d Default"),
&_stnTmpl, ibps);
}
else
{
_stnT.FFormatSz(PszLit("%s Body Part Set %d Costume %d"),
&_stnTmpl, ibps, ccmid);
}
_DumpHeader(kctgCmtl, cmtld.cno, &_stnT, fTrue);
_chse.DumpParentCmd(kctgTmpl, _cnoPar, cmtld.chid);
_chse.DumpRgb(&cmtlf, size(cmtlf));
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
}
}
else if (fUseMtrl)
_pggcm->GetFixed(ibps, &ccmid);
/* Write a nice MTRL chunk for this body part */
if (fUseMtrl)
{
if (ccmid == 1)
{
_stnT.FFormatSz(PszLit("%s Part %d Default MTRL"),
&_stnTmpl, ibp);
}
else
{
_stnT.FFormatSz(PszLit("%s Part %d Costume %d MTRL"),
&_stnTmpl, ibp, ccmid);
}
CnoNext();
_DumpHeader(kctgMtrl, _cnoCur, &_stnT, fTrue);
_chse.DumpParentCmd(kctgCmtl, cmtld.cno, cmtld.chidCur);
_chse.DumpRgb(&pbmhr->mtrlf, size(MTRLF));
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
if (pbmhr->pstnMtrlFile != pvNil)
{
if (!_FTmapFromBmp(pbmhr, _cnoCur, &_stnT))
{
printf("Warning: texture BMP file does not exist or is invalid (%s)\n",
pbmhr->pstnMtrlFile->Psz());
}
}
if (pbmhr->fAccessory)
{
PBMDB pbmdb;
KID kidBmdl;
_ApplyBmdlXF(pbmhr);
if (!_FChidFromModlf(pbmhr, pvNil, &pbmdb))
goto LFail;
kidBmdl.cki.ctg = ctgNil;
kidBmdl.cki.cno = cmtld.cno;
kidBmdl.chid = cmtld.chidCur;
if (!_FAddBmdlParent(pbmdb, &kidBmdl))
goto LFail;
}
cmtld.chidCur++;
_pglcmtld->Put(icmtld, &cmtld);
}
}
}
fRet = fTrue;
LFail:
return fRet;
}
void S2B::_ApplyBmdlXF(PBMHR pbmhr)
{
long cver = pbmhr->pmodlf->cver;
BRV *pbrv = (BRV *)PvAddBv(pbmhr->pmodlf, size(MODLF));
while (cver--)
{
BrMatrix34ApplyP(&pbrv->p, &pbrv->p, &pbmhr->bmat34);
pbrv++;
}
}
/*-----------------------------------------------------------------------------
| _FProcessBmhr
| Iterates through a Brender model hierarchy, creating the necessary
| chunk data as it goes. The GLPI is made the first time, and then
| verified each subsequent time for the template. The array of CPSs
| is filled in for the given node of the given cel. If the MODLF is no
| longer needed, it's freed; the model node is always freed.
|
| Arguments:
| PBMHR *ppbmhr -- points to pointer that points to this node
| short ibpPar -- index of this node's parent body part
|
| Returns:
| fTrue if it succeeds, fFalse otherwise
|
-------------------------------------------------------------PETED-----------*/
bool S2B::_FProcessBmhr(PBMHR *ppbmhr, short ibpPar)
{
short ibpThis;
PBMHR pbmhrCur = *ppbmhr, pbmhrNext;
while (pbmhrCur != pvNil)
{
pbmhrNext = pbmhrCur->pbmhrSibling;
/* Create Cel Part Spec for this node */
ibpThis = _ibpCur++;
if (!_fCostumeOnly)
{
if (!_FSetCps(pbmhrCur, &_prgcps[ibpThis]))
goto LFail; // _FSetCps already displayed the error
}
else
Assert(_prgcps == pvNil, "Why is there an array of CPS allocated?");
/* Update or verify GLPI */
if (_fMakeGlpi)
{
long ibpGl;
if (!_pglibactPar->FAdd(&ibpPar, &ibpGl))
{
printf("Error: Couldn't add node to GLPI -- OOM\n");
goto LFail;
}
Assert(ibpGl == ibpThis, "Weird order for GLPI");
}
else
{
short ibpParGlpi;
_pglibactPar->Get(ibpThis, &ibpParGlpi);
if (ibpPar != ibpParGlpi)
{
printf("Error: cel doesn't match previous GLPI hierarchy");
if (pbmhrCur->pszName != pvNil)
printf(" (node %s)\n", pbmhrCur->pszName);
else
printf("\n");
goto LFail;
}
}
if (_pglbs != pvNil && (_fMakeGlpi || _fMakeCostume) && !_FDoBodyPart(pbmhrCur, ibpThis))
{
printf("Warning: no body part set information -- OOM\n");
ReleasePpo(&_pglbs);
ReleasePpo(&_pglcmtld);
}
/* Process the children */
if (!_FProcessBmhr(&pbmhrCur->pbmhrChild, ibpThis))
{
Assert(*ppbmhr == pbmhrCur, 0);
goto LFail;
}
/* Clean up and go to next sibling */
FreePpv((void **)&pbmhrCur->pmodlf);
FreePpv((void **)&pbmhrCur->pszName);
if ((*ppbmhr)->pstnMtrlFile != pvNil)
delete (*ppbmhr)->pstnMtrlFile;
FreePpv((void **)&pbmhrCur);
*ppbmhr = pbmhrCur = pbmhrNext;
}
return fTrue;
LFail:
return fFalse;
}
/*-----------------------------------------------------------------------------
| _FEnsureOneRoot
| Makes sure that the root of our Brender hierarchy has no siblings.
| Will create a new null root if required. Will delete the entire tree
| if it was unsuccessful.
|
| Arguments:
| PBMHR *ppbmhr -- points to pointer to the root of the hierarchy
|
| Returns:
| fTrue if it could make sure the tree had siblingless root
|
-------------------------------------------------------------PETED-----------*/
bool S2B::_FEnsureOneRoot(PBMHR *ppbmhr)
{
MODLF *pmodlf;
PBMHR pbmhr;
/* REVIEW peted: don't bother doing this, 'cause it messes up the material
stuff. If we really care (which I'm not sure we do, since Socrates
now handles multiple root-level nodes), we can fix this so that any
automatically generated body part sets are also included in the list
of body part sets to create material info for. Alternatively, Socrates
could handle not having a material for a body part set more gracefully */
return fTrue;
/* If the tree's empty, fail since there's no use making a null model */
if (*ppbmhr == pvNil)
{
printf("Error: no valid nodes found in model\n");
return fFalse;
}
/* If the tree's already good, great! */
if ((*ppbmhr)->pbmhrSibling == pvNil)
return fTrue;
/* Allocate necessary structures. The MODLF and the BMHR */
if (!FAllocPv((void **)&pmodlf, size(MODLF), fmemClear, mprNormal))
goto LFail;
if (!FAllocPv((void **)&pbmhr, size(BMHR), fmemClear, mprNormal))
{
FreePpv((void **)&pmodlf);
goto LFail;
}
pbmhr->pmodlf = pmodlf;
_InitBmhr(pbmhr);
/* Fill in the MODLF; no faces or vertices */
Assert(pmodlf->cver == 0, "Didn't clear MODLF");
Assert(pmodlf->cfac == 0, "Didn't clear MODLF");
/* Fill in the BMHR */
pbmhr->cbModlf = size(MODLF);
BrMatrix34Identity(&pbmhr->bmat34);
pbmhr->pbmhrSibling = pvNil;
/* Hook it in and count it */
pbmhr->pbmhrChild = *ppbmhr;
*ppbmhr = pbmhr;
_cMesh++;
return fTrue;
LFail:
_DisposeBmhr(ppbmhr);
return fFalse;
}
/******************************************************************************
_InitBmhr
Initalize the chunk "F" structures for the BMHR
Arguments:
PBMHR pbmhr -- the BMHR to init
************************************************************ PETED ***********/
void S2B::_InitBmhr(PBMHR pbmhr)
{
pbmhr->pmodlf->bo = pbmhr->mtrlf.bo = kboCur;
pbmhr->pmodlf->osk = pbmhr->mtrlf.osk = koskCur;
}
/* Wrap fixing code from Sam at Argonaut begins here */
/*
* Flags to ModelFixWrapping()
*/
#define WRAP_U_0 0x40
#define WRAP_V_0 0x80
#define WRAP_U_1 0x10
#define WRAP_V_1 0x20
/*
* Copyright (c) 1993-1995 by Argonaut Technologies Limited. All rights reserved.
*
* $Id: $
* $Locker: $
*/
/*
* Scratch structures used by ModelFixWrapping()
*/
struct wrap_face_info {
br_uint_8 edge_dir[3];
br_uint_8 vflags[3];
};
struct wrap_edge_info {
br_uint_16 old_vertices[2];
br_uint_16 vertices[2];
br_uint_8 count;
br_uint_8 flags;
br_uint_8 vflags[2];
};
/*
* Temp. workspace used to build new vertices
*/
static br_vertex *WrapNewVertices;
static int WrapTotalVertices;
static int WrapNextVertex;
/*
* Adds a new vertex to model that has had it's
* wrapping fixed
*/
static int WrapAddVertex(br_vertex *new_vp)
{
int i;
struct br_vertex *vp;
/*
* Try and find vertex in current set
*/
vp = WrapNewVertices;
for(i=0; i < WrapNextVertex; i++, vp++) {
if( new_vp->p.v[0] == vp->p.v[0] &&
new_vp->p.v[1] == vp->p.v[1] &&
new_vp->p.v[2] == vp->p.v[2] &&
new_vp->map.v[0] == vp->map.v[0] &&
new_vp->map.v[1] == vp->map.v[1])
return i;
}
/*
* Add to end of list
*/
if(WrapNextVertex >= WrapTotalVertices)
BR_ERROR0("WrapAddVertex: no space");
WrapNewVertices[WrapNextVertex] = *new_vp;
return WrapNextVertex++;
}
/*
* ModelFixWrapping()
*
* Processes a model and attempts to fix up seams where the texture
* coordinates implicitly wrap.
*
* Takes a pointer to an integer mask of [WRAP_U , WRAP_V]
* Each bit indicates that wrapping should be explored in that
* texture axis.
*/
bool FModelFixWrapping(br_model *model, int *mask)
{
Assert(!(*mask & WRAP_V_0), "WRAP_V NYI");
bool fRet = fFalse;
br_vertex *vp0,*vp1;
br_face *fp;
int f,e,e0,e1,v,num0_wraps,num1_wraps,v0,v1;
struct wrap_edge_info *edge_info,*eip;
struct wrap_face_info *face_info,*fip;
br_vertex tempv;
BRS brsMinDist0, brsMinDist1;
/* Ensure edge info */
BrModelPrepare(model, BR_MPREP_EDGES);
// edge_info = BrMemCalloc(model->nedges,sizeof(*edge_info),BR_MEMORY_APPLICATION);
if (!FAllocPv((void **)&edge_info, model->nedges * size(wrap_edge_info),
fmemClear, mprNormal))
{
goto LFail;
}
// face_info = BrMemCalloc(model->nfaces,sizeof(*face_info),BR_MEMORY_APPLICATION);
if (!FAllocPv((void **)&face_info, model->nfaces * size(wrap_face_info),
fmemClear, mprNormal))
{
goto LFail;
}
/*
* Go through faces to build edge table
*/
fp = model->faces;
fip = face_info;
for(f=0 ; f < model->nfaces; f++, fp++,fip++) {
for(e0 = 0; e0 < 3; e0++) {
e1 = (e0 == 2)?0:(e0 + 1);
eip = edge_info + fp->edges[e0];
eip->count++;
if(eip->count == 1) {
/*
* First time edge has been used
*/
eip->vertices[0] = fp->vertices[e0];
eip->vertices[1] = fp->vertices[e1];
eip->old_vertices[0] = eip->vertices[0];
eip->old_vertices[1] = eip->vertices[1];
}
/*
* See which direction edge is used
*/
if(eip->vertices[0] == fp->vertices[e0] &&
eip->vertices[1] == fp->vertices[e1]) {
fip->edge_dir[e0] = 0;
} else if(
eip->vertices[0] == fp->vertices[e1] &&
eip->vertices[1] == fp->vertices[e0]) {
fip->edge_dir[e0] = 1;
} else
BR_ERROR1("Face %d has a bad edge\n",f);
}
}
/*
* For each edge - find if U or V wraps
*/
num0_wraps = 0;
num1_wraps = 0;
brsMinDist0 = brsMinDist1 = BR_SCALAR(1.0);
eip = edge_info;
for(e=0; e< model->nedges; e++, eip++) {
vp0 = model->vertices + eip->vertices[0];
vp1 = model->vertices + eip->vertices[1];
if((*mask & WRAP_U_0) && (BR_ABS(vp0->map.v[0]-vp1->map.v[0]) >
BR_SCALAR(0.5)))
{
br_uint_8 vf0, vf1;
Assert(vp0->map.v[0] >= BR_SCALAR(0.0), "Vertex U less than 0");
Assert(vp0->map.v[0] <= BR_SCALAR(1.0), "Vertex U greater than 1");
Assert(vp1->map.v[0] >= BR_SCALAR(0.0), "Vertex U less than 0");
Assert(vp1->map.v[0] <= BR_SCALAR(1.0), "Vertex U greater than 1");
vf0 = vf1 = 0;
if (vp0->map.v[0] > vp1->map.v[0])
{
BRS brsDist;
brsDist = BR_SUB(BR_SCALAR(1.0), vp0->map.v[0]);
/* Put the new vertex colocated with whatever vertex is closest
to the 0/1 boundary */
if (brsDist < vp1->map.v[0])
vf0 = WRAP_U_0;
else
vf1 = WRAP_U_1;
}
else
{
BRS brsDist;
brsDist = BR_SUB(BR_SCALAR(1.0), vp1->map.v[0]);
/* Ditto */
if (brsDist < vp0->map.v[0])
vf1 = WRAP_U_0;
else
vf0 = WRAP_U_1;
}
/*
* Mark lower vertex as wrapping - keep a count
* new wraps
*/
if (!(eip->vflags[0] & vf0))
{
eip->vflags[0] |= vf0;
if (vf0 & WRAP_U_0)
num0_wraps++;
else
num1_wraps++;
}
if (!(eip->vflags[1] & vf1))
{
eip->vflags[1] |= vf1;
if (vf1 & WRAP_U_0)
num0_wraps++;
else
num1_wraps++;
}
/*
* mark face
*/
eip->flags |= WRAP_U_0;
}
#ifdef NOT
if((*mask & WRAP_V) && (BR_ABS(vp0->map.v[1]-vp1->map.v[1]) > BR_SCALAR(0.5))) {
vw = (vp0->map.v[1] > vp1->map.v[1])?1:0;
if(!(eip->vflags[vw] & WRAP_V)) {
eip->vflags[vw] |= WRAP_V;
num_wraps++;
}
/*
* mark face
*/
eip->flags |= WRAP_V;
}
#endif
}
/*
* Go through faces and accumulate per face-vertex flags
*/
fp = model->faces;
fip = face_info;
for(f=0; f< model->nfaces; f++, fp++, fip++) {
for(e0 = 0; e0 < 3; e0++) {
e1 = (e0 == 2)?0:(e0 + 1);
eip = edge_info + fp->edges[e0];
if(fip->edge_dir[e0])
v0 = 1, v1 = 0;
else
v0 = 0, v1 = 1;
fip->vflags[e0] |= eip->vflags[v0];
fip->vflags[e1] |= eip->vflags[v1];
}
}
/*
* Allocate a new vertex array (may be bigger than required)
*/
WrapTotalVertices = model->nvertices + num1_wraps + num0_wraps;
WrapNewVertices = (BRV *)BrResAllocate(model,WrapTotalVertices * sizeof(*WrapNewVertices),BR_MEMORY_VERTICES);
memcpy(WrapNewVertices,model->vertices,sizeof(*WrapNewVertices) * model->nvertices);
WrapNextVertex = model->nvertices;
/*
* Go through faecs and generate new vertices
*/
fp = model->faces;
fip = face_info;
for(f=0; f< model->nfaces; f++, fp++, fip++)
{
br_uint_8 vflags = 0, vbad = 0;
int v0, v1;
v0 = v1 = -1;
for(v=0; v < 3; v++)
{
br_vertex *pbrv;
if(!(fip->vflags[v]))
continue;
Assert((fip->vflags[v] & (WRAP_U_0 | WRAP_U_1)) !=
(WRAP_U_0 | WRAP_U_1), "Both wrap flags set!");
Assert(v0 == -1 || v1 == -1, "Already seen two bad vertices");
/*
* Make a copy of the vertex, and wrap
* mapping coordinates
*/
pbrv = &WrapNewVertices[fp->vertices[v]];
tempv = *pbrv;
vbad |= (0x01 << v);
if (fip->vflags[v] & WRAP_U_0)
{
if (vflags & WRAP_U_0)
v1 = v;
else
{
vflags |= WRAP_U_0;
v0 = v;
}
}
else
{
Assert(fip->vflags[v] & WRAP_U_1, "Unknown wrap flag set");
if (vflags & WRAP_U_1)
v0 = v;
else
{
vflags |= WRAP_U_1;
v1 = v;
}
}
#ifdef NOT
if(fip->vflags[v] & WRAP_U)
tempv.map.v[0] = /* tempv.map.v[0] + */ BR_SCALAR(1.0);
if(fip->vflags[v] & WRAP_V)
tempv.map.v[1] = /* tempv.map.v[1] + */ BR_SCALAR(1.0);
#endif
}
if (!vflags)
continue;
if ((vflags & (WRAP_U_0 | WRAP_U_1)) == (WRAP_U_0 | WRAP_U_1))
{
int v_move, v_good;
br_vertex *pbrv_good, *pbrv_move;
br_scalar brs_new_u;
/* Need to figure out which to actually move */
vbad ^= 0x07;
Assert(!((vbad - 1) & vbad), "Should only have one bit set");
for (v_good = 0; vbad > 1; v_good++, vbad >>= 1);
Assert(FIn(v_good, 0, 3), "vbad out of range");
pbrv_good = &WrapNewVertices[fp->vertices[v_good]];
/* The good vertex will be within 0.5 of the new u value */
if (pbrv_good->map.v[0] >= BR_SCALAR(0.5))
{
brs_new_u = BR_SCALAR(1.0);
v_move = v1;
}
else
{
brs_new_u = BR_SCALAR(0.0);
v_move = v0;
}
pbrv_move = &WrapNewVertices[fp->vertices[v_move]];
Assert(BR_ABS(BR_SUB(pbrv_good->map.v[0], pbrv_move->map.v[0])) > BR_SCALAR(0.5),
"Moved vertex is within 0.5 of good vertex");
tempv = *pbrv_move;
tempv.map.v[0] = brs_new_u;
fp->vertices[v_move] = WrapAddVertex(&tempv);
}
else
{
br_scalar brs_new_u;
if (vflags & WRAP_U_0)
brs_new_u = BR_SCALAR(0.0);
else
{
Assert(vflags & WRAP_U_1, "Unknown wrap flag");
brs_new_u = BR_SCALAR(1.0);
}
/* Possibly need to move both vertices over to new u value */
if (v0 != -1)
{
tempv = WrapNewVertices[fp->vertices[v0]];
tempv.map.v[0] = brs_new_u;
fp->vertices[v0] = WrapAddVertex(&tempv);
}
else
Assert(vflags & WRAP_U_1, "Inconsistent wrap flag");
if (v1 != -1)
{
tempv = WrapNewVertices[fp->vertices[v1]];
tempv.map.v[0] = brs_new_u;
fp->vertices[v1] = WrapAddVertex(&tempv);
}
else
Assert(vflags & WRAP_U_0, "Inconsistent wrap flag");
}
}
// BrMemFree(face_info);
FreePpv((void **)&face_info);
// BrMemFree(edge_info);
FreePpv((void **)&edge_info);
/*
* Connect new vertex array to faces
*/
BrResFree(model->vertices);
model->vertices = WrapNewVertices;
model->nvertices = WrapNextVertex;
WrapNewVertices = NULL;
fRet = fTrue;
LFail:
return fRet;
}
/* Wrap fix code from Sam at Argonaut ends here */
/*-----------------------------------------------------------------------------
| _FlushTmplKids
| Writes whatever the current template accumulators contain and
| resets them.
|
-------------------------------------------------------------PETED-----------*/
void S2B::_FlushTmplKids(void)
{
int cBmdbTotal = 0, cBmdbChains = 0;
#if HASH_FIXED
PBMDB *ppbmdb;
#endif /* HASH_FIXED */
PBMDB pbmdb, pbmdbNext;
if (_pglibactPar != pvNil)
{
#if HASH_FIXED
Assert(_prgpbmdb != pvNil, 0);
PBMDB *ppbmdbMac = (_prgpbmdb + kcpbmdb);
#else /* HASH_FIXED */
Assert(_pglpbmdb != pvNil, 0);
long ipbmdb = _pglpbmdb->IvMac();
#endif /* !HASH_FIXED */
Assert(_ctgPar == kctgTmpl, "Odd parent for body part chunks");
CnoNext();
_stnT.FFormatSz(PszLit("%s Tree"), &_stnTmpl);
_DumpHeader(kctgGlpi, _cnoCur, &_stnT, fTrue);
_chse.DumpParentCmd(_ctgPar, _cnoPar, 0);
_chse.DumpList(_pglibactPar);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
/* Create dummy GLBS if necessary */
if (_pglbs == pvNil)
{
short swZero = 0;
Assert(_pglcmtld == pvNil,
"Custom material info with no body part set info");
for (long lw = 0; lw < _pglibactPar->IvMac(); lw++)
_pglibactPar->Put(lw, &swZero);
_pglbs = _pglibactPar;
_pglibactPar = pvNil;
}
else
{
/* Don't need this any more */
ReleasePpo(&_pglibactPar);
/* Do material stuff if we have the CMTL GL and there's valid
color info */
if (_pggcm != pvNil && _pglclr != pvNil)
{
/* Dump out GGCM */
Assert(_ctgPar == kctgTmpl, "Weird parent for GGCM");
CnoNext();
_stnT.FFormatSz(PszLit("%s Costumes"), &_stnTmpl);
_DumpHeader(kctgGgcm, _cnoCur, &_stnT, fTrue);
_chse.DumpParentCmd(_ctgPar, _cnoPar, 0);
_chse.DumpGroup(_pggcm);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
}
#ifdef DEBUG
else if (_pggcm != pvNil)
Assert(_pggcm->IvMac() == 0,
"Non-empty GGCM without any color info");
#endif // DEBUG
ReleasePpo(&_pggcm);
if (_pggtmapd != pvNil)
{
if (!_FFlushTmaps())
printf("Warning: couldn't write all TMAPs; some textures may not be present\n");
ReleasePpo(&_pggtmapd);
}
}
CnoNext();
_stnT.FFormatSz(PszLit("%s Body Part Sets"), &_stnTmpl);
_DumpHeader(kctgGlbs, _cnoCur, &_stnT, fTrue);
_chse.DumpParentCmd(_ctgPar, _cnoPar, 0);
_chse.DumpList(_pglbs);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
ReleasePpo(&_pglbs);
#if HASH_FIXED
for (ppbmdb = _prgpbmdb; ppbmdb < ppbmdbMac; ppbmdb++)
#else /* HASH_FIXED */
while (ipbmdb > 0)
#endif /* !HASH_FIXED */
{
int cBmdbList = 0;
#if HASH_FIXED
pbmdb = *ppbmdb;
#else /* HASH_FIXED */
ipbmdb--;
_pglpbmdb->Get(ipbmdb, &pbmdb);
#endif /* !HASH_FIXED */
while (pbmdb != pvNil)
{
/* Don't bother fixing up a model with no vertices; it wastes
time, and the Argonaut code doesn't handle that case
gracefully anyway */
if (_fFixWrap && pbmdb->pmodlf->cver > 0 && pbmdb->fFixWrap)
{
bool fSuccess = fFalse;
PBMDL pbmdl = pvNil;
if (_FModlfToBmdl(pbmdb->pmodlf, &pbmdl))
{
int mask;
mask = pbmdb->fSpherical ? (WRAP_U_0 | WRAP_V_0) : WRAP_U_0;
if (FModelFixWrapping(pbmdl, &mask))
{
fSuccess = _FBmdlToModlf(pbmdl, &pbmdb->pmodlf,
&pbmdb->cbModlf);
}
BrModelFree(pbmdl);
}
if (!fSuccess)
{
printf("Warning: couldn't fix up texture wrapping for");
if (pbmdb->pszName != pvNil)
printf(" node %s\n", pbmdb->pszName);
else
printf(" unnamed node\n");
}
}
cBmdbList++;
if (pbmdb->pszName == pvNil)
_stnT.FFormatSz(PszLit("%s Model %d"), &_stnTmpl, pbmdb->chidBmdl);
else
_stnT.FFormatSz(PszLit("%s Model %z"), &_stnTmpl, pbmdb->pszName);
_DumpHeader(kctgBmdl, pbmdb->cnoBmdl, &_stnT, fTrue);
if (pbmdb->pglkidCmtl != pvNil)
{
long icno, icnoMac = pbmdb->pglkidCmtl->IvMac();
KID kidBmdl;
for (icno = 0; icno < icnoMac; icno++)
{
pbmdb->pglkidCmtl->Get(icno, &kidBmdl);
_chse.DumpParentCmd(kctgCmtl, kidBmdl.cki.cno, kidBmdl.chid);
}
ReleasePpo(&pbmdb->pglkidCmtl);
}
if (pbmdb->chidBmdl != ivNil)
_chse.DumpParentCmd(_ctgPar, _cnoPar, pbmdb->chidBmdl);
_chse.DumpRgb(pbmdb->pmodlf, pbmdb->cbModlf);
_chse.DumpSz(PszLit("ENDCHUNK"));
_chse.DumpSz(PszLit(""));
pbmdbNext = (PBMDB)pbmdb->hshdb.phshdbNext;
FreePpv((void **)&pbmdb->pmodlf);
FreePpv((void **)&pbmdb->pszName);
FreePpv((void **)&pbmdb);
pbmdb = pbmdbNext;
}
cBmdbTotal += cBmdbList;
if (_mdVerbose > kmdQuiet && cBmdbList > 0)
{
cBmdbChains++;
printf("Model collision chain has %d entries\n", cBmdbList);
}
#if HASH_FIXED
*ppbmdb = pvNil;
#else /* HASH_FIXED */
_pglpbmdb->Delete(ipbmdb);
#endif /* !HASH_FIXED */
}
if (_mdVerbose > kmdQuiet)
{
float flAvg = (float)cBmdbTotal / (float)cBmdbChains;
printf("Average collision chain has %1.2f entries (%d chains)\n", flAvg, cBmdbChains);
}
}
Assert(_pglibactPar == pvNil, 0);
Assert(_pglbs == pvNil, 0);
Assert(_pglcmtld == pvNil, 0);
#if HASH_FIXED
Assert(*_prgpbmdb == pvNil, 0);
#else /* HASH_FIXED */
Assert(_pglpbmdb->IvMac() == 0, 0);
#endif /* !HASH_FIXED */
_chidActn = 0;
_chidBmdl = 0;
_chidCmtl = 0;
}
/******************************************************************************
_FModlfToBmdl
Converts a MODLF structure to the corresponding Brender br_model
structure (BMDL in Socrates nomenclature). Simply allocates a
Brender model and copies the vertices.
Arguments:
PMODLF pmodlf -- the MODLF to convert
PBMDL *ppbmdl -- takes the pointer to the new Brender model
Returns: fTrue on success, fFalse otherwise
************************************************************ PETED ***********/
bool S2B::_FModlfToBmdl(PMODLF pmodlf, PBMDL *ppbmdl)
{
AssertVarMem(pmodlf);
AssertVarMem(ppbmdl);
Assert(*ppbmdl == pvNil, "Already have BMDL");
bool fRet = fFalse;
long cbrgbrv, cbrgbrf;
PBMDL pbmdl;
pbmdl = BrModelAllocate(pvNil, pmodlf->cver, pmodlf->cfac);
if (pbmdl == pvNil)
goto LFail;
cbrgbrv = LwMul(pmodlf->cver, size(BRV));
cbrgbrf = LwMul(pmodlf->cfac, size(BRF));
CopyPb(PvAddBv(pmodlf, size(MODLF)), pbmdl->vertices, cbrgbrv);
CopyPb(PvAddBv(pmodlf, size(MODLF) + cbrgbrv), pbmdl->faces, cbrgbrf);
*ppbmdl = pbmdl;
fRet = fTrue;
LFail:
return fRet;
}
/******************************************************************************
_FBmdlToModlf
Converts a Brender br_model to a MODLF structure. Ensures that the
Brender model has been prepared and then copies all relevant
information from the Brender model to the MODLF. Uses the passed in
MODLF to initialize the newly created one so that untouched fields
remain the same. Frees the old MODLF only on success.
Arguments:
PBMDL pbmdl -- the Brender model to convert
PMODLF *ppmodlf -- provides the template for the MODLF, and takes
the pointer to the new MODLF structure on success
Returns: fTrue on success, fFalse otherwise
************************************************************ PETED ***********/
bool S2B::_FBmdlToModlf(PBMDL pbmdl, PMODLF *ppmodlf, long *pcb)
{
AssertVarMem(pbmdl);
AssertVarMem(ppmodlf);
AssertVarMem(*ppmodlf);
bool fRet = fFalse;
long cbrgbrv, cbrgbrf;
PMODLF pmodlf;
BrModelPrepare(pbmdl, BR_MPREP_ALL);
/* REVIEW peted: is there an accepted way of determining if BrModelPrepare
has failed? Can it fail? I think I can at least do the following... */
if (pbmdl->nvertices > 0 && pbmdl->nprepared_vertices == 0)
goto LFail;
if (pbmdl->nfaces > 0 && pbmdl->nprepared_faces == 0)
goto LFail;
cbrgbrv = LwMul(pbmdl->nprepared_vertices, size(BRV));
cbrgbrf = LwMul(pbmdl->nprepared_faces, size(BRF));
if (!FAllocPv((void **)&pmodlf, size(MODLF) + cbrgbrv + cbrgbrf, fmemClear, mprNormal))
goto LFail;
CopyPb(*ppmodlf, pmodlf, size(MODLF));
pmodlf->cver = pbmdl->nprepared_vertices;
pmodlf->cfac = pbmdl->nprepared_faces;
pmodlf->rRadius = pbmdl->radius;
pmodlf->brb = pbmdl->bounds;
CopyPb(pbmdl->prepared_vertices, PvAddBv(pmodlf, size(MODLF)), cbrgbrv);
CopyPb(pbmdl->prepared_faces, PvAddBv(pmodlf, size(MODLF) + cbrgbrv), cbrgbrf);
FreePpv((void **)ppmodlf);
*ppmodlf = pmodlf;
*pcb = size(MODLF) + cbrgbrv + cbrgbrf;
fRet = fTrue;
LFail:
return fRet;
}
/*-----------------------------------------------------------------------------
| _FSetCps
| Sets a Cel Part Spec.
|
| Arguments:
| PBMHR pbmhr -- pointer to Brender mode hierarchy node to use
| CPS *pcps -- pointer to CPS to fill in
|
| Returns:
| fTrue if it was successful, fFalse otherwise
| Keywords:
|
-------------------------------------------------------------PETED-----------*/
bool S2B::_FSetCps(PBMHR pbmhr, CPS *pcps)
{
long imat34;
CHID chid;
if (pbmhr->fAccessory)
{
if (pbmhr->pmodlf->cver != 0)
printf("Warning: non-null accessory node during action definition;"
" discarding mesh info\n");
pcps->chidModl = (short)ivNil;
BrMatrix34Identity(&pbmhr->bmat34);
}
else
{
if (!_FChidFromModlf(pbmhr, &chid))
goto LFail;
pcps->chidModl = (short)chid;
}
if (!_FImat34GetBmat34(&pbmhr->bmat34, &imat34))
{
printf("Couldn't add entry to GLXF -- OOM\n");
goto LFail;
}
Assert(imat34 <= kswMax, 0);
pcps->imat34 = (short)imat34;
return fTrue;
LFail:
return fFalse;
}
/*-----------------------------------------------------------------------------
| _FChidFromModlf
| Looks up a given MODLF, and adds a new one to the MODLF database
| if necessary.
|
| Arguments:
| PBMHR pbmhr -- points to BMHR node that contains the pmodlf
| CHID *pchid -- points to CHID var that takes the result
|
| Returns:
| fTrue if it could find or allocate the new MODLF node, fFalse otherwise
| *pchid takes the CHID for the MODL chunk
|
-------------------------------------------------------------PETED-----------*/
bool S2B::_FChidFromModlf(PBMHR pbmhr, CHID *pchid, PBMDB *ppbmdb)
{
AssertNilOrVarMem(pchid);
AssertNilOrVarMem(ppbmdb);
#if HASH_FIXED
PBMDB *ppbmdb;
#endif /* HASH_FIXED */
PBMDB pbmdb;
uint luHashList;
if ((pbmdb = _PbmdbFindModlf(pbmhr->pmodlf, pbmhr->cbModlf, &luHashList)) == pvNil)
{
if (FAllocPv((void **)&pbmdb, size(BMDB), fmemNil, mprNormal))
{
/* Copy the pointers */
pbmdb->pmodlf = pbmhr->pmodlf;
pbmhr->pmodlf = pvNil;
pbmdb->pszName = pbmhr->pszName;
pbmhr->pszName = pvNil;
pbmdb->cbModlf = pbmhr->cbModlf;
if (ppbmdb != pvNil)
pbmdb->chidBmdl = _chidBmdl++;
else
pbmdb->chidBmdl = chidNil;
pbmdb->cnoBmdl = CnoNext();
pbmdb->pglkidCmtl = pvNil;
pbmdb->hshdb.luHash = luHashList;
pbmdb->fSpherical = pbmhr->fSpherical;
pbmdb->fFixWrap = pbmhr->fFixWrap;
#if HASH_FIXED
pbmdb->hshdb.phshdbNext = (PHSHDB)*(ppbmdb = (_prgpbmdb + luHashList));
*ppbmdb = pbmdb;
#else /* HASH_FIXED */
Assert(pbmdb == (PBMDB)&pbmdb->hshdb, "Bad struct definition for BMDB");
if (!_FInsertPhshdb(&pbmdb->hshdb, _pglpbmdb))
{
FreePpv((void **)&pbmdb);
printf("Couldn't add node to model database\n");
return fFalse;
}
#endif /* !HASH_FIXED */
}
else
{
printf("Couldn't allocate Model DB node\n");
return fFalse;
}
}
else if (pbmdb->fSpherical != pbmhr->fSpherical ||
pbmdb->fFixWrap != pbmdb->fFixWrap)
{
printf("Warning: mesh found with identical vertices and u/v coordinates"
"\n but different texture mapping modes\n");
}
if (ppbmdb != pvNil)
*ppbmdb = pbmdb;
else if (pbmdb->chidBmdl == chidNil)
pbmdb->chidBmdl = _chidBmdl++;
if (pchid != pvNil)
*pchid = pbmdb->chidBmdl;
return fTrue;
}
bool S2B::_FAddBmdlParent(PBMDB pbmdb, KID *pkid)
{
AssertVarMem(pkid);
if (pbmdb->pglkidCmtl == pvNil &&
(pbmdb->pglkidCmtl = GL::PglNew(size(KID))) == pvNil)
{
goto LFail;
}
if (!pbmdb->pglkidCmtl->FAdd(pkid))
goto LFail;
return fTrue;
LFail:
return fFalse;
}
#if !HASH_FIXED
/******************************************************************************
_FInsertPhshdb
Inserts an hash database entry into a sparse, sorted hash table
Arguments:
PHSHDB phshdb -- pointer to the hash DB entry to insert
PGL pglphshdb -- the GL used to maintain the sorted hash table
Returns: fTrue if the entry could be inserted
************************************************************ PETED ***********/
bool S2B::_FInsertPhshdb(PHSHDB phshdb, PGL pglphshdb)
{
long iphshdb;
if (!_FIphshdbFromLuHash(phshdb->luHash, &iphshdb, pglphshdb))
{
phshdb->phshdbNext = pvNil;
if (!pglphshdb->FInsert(iphshdb, &phshdb))
return fFalse;
}
else
{
pglphshdb->Get(iphshdb, &phshdb->phshdbNext);
pglphshdb->Put(iphshdb, &phshdb);
}
return fTrue;
}
/******************************************************************************
_FIphshdbFromLuHash
Determines the position that a given hash value does, or should be,
located in the given sparse, sorted hash table.
Arguments:
uint luHash -- the hash value to look for
long *piphshdb -- takes the position in the hash table
PGL pglphshdb -- the hash table
Returns: fTrue if the hash value is already in the hash table, fFalse if
it needs to be added
************************************************************ PETED ***********/
bool S2B::_FIphshdbFromLuHash(uint luHash, long *piphshdb, PGL pglphshdb)
{
bool fRet = fFalse;
long iphshdbMin = 0, iphshdbMac = pglphshdb->IvMac();
long iphshdbCur;
PHSHDB phshdb;
while (iphshdbMin != iphshdbMac)
{
iphshdbCur = (iphshdbMin + iphshdbMac) / 2;
pglphshdb->Get(iphshdbCur, &phshdb);
if (luHash > phshdb->luHash)
iphshdbMin = iphshdbCur + 1;
else
iphshdbMac = iphshdbCur;
}
if (iphshdbMin < pglphshdb->IvMac())
{
if (iphshdbCur != iphshdbMin)
pglphshdb->Get(iphshdbMin, &phshdb);
if (luHash == phshdb->luHash)
fRet = fTrue;
}
*piphshdb = iphshdbMin;
return fRet;
}
#endif /* !HASH_FIXED */
/******************************************************************************
_PbmdbFindModlf
Given a MODLF, look for an identical one in our hash table.
Arguments:
MODLF *pmodlf -- the MODLF to look for
int *pluHashList -- returns the hash value for the MODLF
Returns: if the MODLF could be found, returns the pointer to the
BMDB for that MODLF, otherwise returns pvNil.
************************************************************ PETED ***********/
PBMDB S2B::_PbmdbFindModlf(MODLF *pmodlf, long cbModlf, uint *pluHashList)
{
PBMDB pbmdb;
#if !HASH_FIXED
long ipbmdb;
#endif /* !HASH_FIXED */
*pluHashList = _LuHashBytes(kluHashInit, pmodlf, cbModlf);
#if HASH_FIXED
pbmdb = *(_prgpbmdb + *pluHashList);
#else /* HASH_FIXED */
if (!_FIphshdbFromLuHash(*pluHashList, &ipbmdb, _pglpbmdb))
return pvNil;
_pglpbmdb->Get(ipbmdb, &pbmdb);
#endif /* !HASH_FIXED */
while (pbmdb != pvNil)
{
if (pbmdb->cbModlf == cbModlf &&
(FcmpCompareRgb(pbmdb->pmodlf, pmodlf, cbModlf) == fcmpEq))
break;
pbmdb = (PBMDB)pbmdb->hshdb.phshdbNext;
}
return pbmdb;
}
uint _rgluHashCrc[256];
Debug(bool _fTableValid = fFalse;)
/******************************************************************************
_InitCrcTable
Initialize the 8-bit hash value look-up table
************************************************************ PETED ***********/
void S2B::_InitCrcTable(void)
{
int icrc;
/* Compute the hash value for each 8-bit byte value */
for (icrc = 0; icrc < 256; icrc++)
{
byte bcrc = (byte)icrc;
_rgluHashCrc[bcrc] = _LuHashBytesNoTable(0, &bcrc, 1);
}
Debug(_fTableValid = fTrue;)
}
/******************************************************************************
_LuHashBytesNoTable
Compute a hash value the hard way.
Arguments:
uint luHash -- initial hash value
void *pv -- pointer to the data to hash
long cb -- number bytes in the data to hash
Returns: the hash value for the bytes
************************************************************ PETED ***********/
uint S2B::_LuHashBytesNoTable(uint luHash, void *pv, long cb)
{
byte *pb = (byte *)pv;
int ibit;
while (cb--)
{
luHash = (luHash ^ *pb++) & 0xFF;
for (ibit = 0; ibit < 8; ibit++)
if (luHash & 0x01)
luHash = (luHash >> 1) ^ kluCrcPoly;
else
luHash = luHash >> 1;
}
return luHash;
}
/******************************************************************************
_LuHashBytes
Compute a hash value using the lookup table
Arguments:
uint luHash -- initial hash value
void *pv -- pointer to the data to hash
long cb -- number of bytes in the data to hash
Returns: the hash value for the bytes
************************************************************ PETED ***********/
uint S2B::_LuHashBytes(uint luHash, void *pv, long cb)
{
byte *pb = (byte *)pv;
byte bCval;
Assert(_fTableValid, "Can't use table hashing with uninited table");
while (cb--)
{
bCval = (luHash ^ *pb++) & 0xFF;
luHash = (luHash >> 8) ^ _rgluHashCrc[bCval];
}
return luHash;
}
/*-----------------------------------------------------------------------------
| _FImat34GetBmat34
| Looks up a given BMAT34 in the transformation database, and returns
| with the index for the found or created transformation. (NOTE: for now,
| always adds a new transformation)
|
| Arguments:
| BMAT34 *pbmat34 -- points to BMAT34 to look for
| long *pimat34 -- points to long var that takes the result
|
| Returns:
| fTrue if it succeeds, fFalse otherwise
| *pimat34 takes the index for the BMAT34 in the GL
|
-------------------------------------------------------------PETED-----------*/
bool S2B::_FImat34GetBmat34(BMAT34 *pbmat34, long *pimat34)
{
BMAT34 *pbmat34T;
#if HASH_FIXED
long i, iMac = _pglxf->IvMac();
#else /* HASH_FIXED */
long ipbmatdb;
uint luHash;
PBMATDB pbmatdb;
#endif /* !HASH_FIXED */
#if HASH_FIXED
for (i = 0; i < iMac; i++)
{
pbmat34T = (BMAT34 *)_pglxf->QvGet(i);
if (FcmpCompareRgb(pbmat34, pbmat34T, size(BMAT34)) == fcmpEq)
{
*pimat34 = i;
return fTrue;
}
}
return _pglxf->FAdd(pbmat34, pimat34);
#else /* HASH_FIXED */
luHash = _LuHashBytes(kluHashInit, pbmat34, size(BMAT34));
if (!_FIphshdbFromLuHash(luHash, &ipbmatdb, _pglpbmatdb))
goto LAddXF;
_pglpbmatdb->Get(ipbmatdb, &pbmatdb);
while (pbmatdb != pvNil)
{
pbmat34T = (BMAT34 *)_pglxf->QvGet(pbmatdb->ixf);
if (FcmpCompareRgb(pbmat34, pbmat34T, size(BMAT34)) == fcmpEq)
break;
pbmatdb = (PBMATDB)pbmatdb->hshdb.phshdbNext;
}
if (pbmatdb == pvNil)
{
LAddXF:
/* Add the XF to the GL and to the database */
if (_pglxf->FAdd(pbmat34, pimat34))
{
if (FAllocPv((void **)&pbmatdb, size(BMATDB), fmemNil, mprNormal))
{
pbmatdb->ixf = *pimat34;
pbmatdb->hshdb.luHash = luHash;
Assert(pbmatdb == (PBMATDB)&pbmatdb->hshdb, "Bad struct definition for BMATDB");
if (!_FInsertPhshdb(&pbmatdb->hshdb, _pglpbmatdb))
{
FreePpv((void **)&pbmatdb);
printf("Error: Couldn't add node to transform database\n");
goto LFail;
}
}
else
{
printf("Error: Couldn't allocate Transform DB node\n");
goto LFail;
}
}
else
goto LFail;
}
*pimat34 = pbmatdb->ixf;
return fTrue;
LFail:
return fFalse;
#endif /* !HASH_FIXED */
}
/*-----------------------------------------------------------------------------
| _DisposeBmhr
| Deletes the entire tree of Brender model nodes.
|
| Arguments:
| PBMHR *ppbmhr -- points to pointer that points to the tree
|
-------------------------------------------------------------PETED-----------*/
void S2B::_DisposeBmhr(PBMHR *ppbmhr)
{
PBMHR pbmhrNext;
while (*ppbmhr != pvNil)
{
pbmhrNext = (*ppbmhr)->pbmhrSibling;
_DisposeBmhr(&(*ppbmhr)->pbmhrChild);
FreePpv((void **)&(*ppbmhr)->pmodlf);
FreePpv((void **)&(*ppbmhr)->pszName);
if ((*ppbmhr)->pstnMtrlFile != pvNil)
delete (*ppbmhr)->pstnMtrlFile;
FreePpv((void **)ppbmhr);
*ppbmhr = pbmhrNext;
}
}
/******************************************************************************
PglcrngFromPal
Will attempt to extract color ranges from a palette. May be
obsolete.
Arguments:
PGL pglclr -- the palette to use
Returns: a GL of CRNGs, each entry is the description of one color range
************************************************************ PETED ***********/
PGL PglcrngFromPal(PGL pglclr)
{
long lwCur, lwStart, lwMac = pglclr->IvMac();
BRCLR brclr;
BRS brsRLast, brsGLast, brsBLast;
BRS brsNorm, brsR, brsG, brsB;
CRNG crng;
PGL pglcrng = GL::PglNew(size(CRNG));
if (pglcrng == pvNil)
goto LFail;
for (lwCur = 0, lwStart = 0; lwCur < lwMac; lwCur++)
{
/* Get a normalized color */
pglclr->Get(lwCur, &brclr);
brsR = BrIntToScalar(BR_RED(brclr));
brsG = BrIntToScalar(BR_GRN(brclr));
brsB = BrIntToScalar(BR_BLU(brclr));
brsNorm = max(max(brsR, brsG), brsB);
if (brsNorm > 0)
{
brsR = BR_ROUND(BR_DIV(brsR, brsNorm), 2);
brsG = BR_ROUND(BR_DIV(brsG, brsNorm), 2);
brsB = BR_ROUND(BR_DIV(brsB, brsNorm), 2);
}
/* Is it possible that we can include it in our range? */
if (lwCur > 0 && brsNorm > 0)
{
/* If the potential starting entry was 0,0,0 then set it to
the current normalized color, and continue */
if (brsRLast == 0 && brsGLast == 0 && brsBLast == 0)
{
brsRLast = brsR;
brsGLast = brsG;
brsBLast = brsB;
continue;
}
/* Well, at least this criteria catches exactly the ranges we
currently define. I have no idea how well it will fare with
the final ranges. */
if (brsRLast == brsGLast && brsR == brsG)
continue;
if (brsRLast == brsBLast && brsR == brsB)
continue;
if (brsGLast == brsBLast && brsG == brsB)
continue;
}
/* Should we add this range to our list? */
if ((lwCur - lwStart) > 2)
{
crng.lwBase = lwStart;
crng.lwRange = lwCur - lwStart;
if (!pglcrng->FAdd(&crng))
{
ReleasePpo(&pglcrng);
goto LFail;
}
}
brsRLast = brsR;
brsGLast = brsG;
brsBLast = brsB;
lwStart = lwCur;
}
LFail:
return pglcrng;
}
/******************************************************************************
LwcrngNearestBrclr
Given a Brender color, find the color range that the color will most
accurately fit into.
Arguments:
BRCLR brclr -- the Brender color
PGL pglclr -- the palette
PGL pglcrng -- description of the color ranges in the palette
Returns: returns the entry in the color range GL that corresponds to the
given color
************************************************************ PETED ***********/
long LwcrngNearestBrclr(BRCLR brclr, PGL pglclr, PGL pglcrng)
{
long lwclr, lwclrMac = pglclr->IvMac(), lwclrNear,
lwcrng, lwcrngMac = pglcrng->IvMac(), lwcrngNear,
dclrNear = klwMax, dclrT;
CRNG crng;
BRCLR brclrT, brclrNear;
/* For each color range */
for (lwcrng = 0; lwcrng < lwcrngMac; lwcrng++)
{
pglcrng->Get(lwcrng, &crng);
lwclrMac = crng.lwBase + crng.lwRange;
Assert(crng.lwRange > 0 && lwclrMac <= pglclr->IvMac(), "Invalid range");
/* For each color in the given range */
for (lwclr = crng.lwBase; lwclr < lwclrMac; lwclr++)
{
/* Calculate a city-block distance for the color */
pglclr->Get(lwclr, &brclrT);
dclrT = LwAbs(BR_RED(brclrT) - BR_RED(brclr));
dclrT += LwAbs(BR_GRN(brclrT) - BR_GRN(brclr));
dclrT += LwAbs(BR_BLU(brclrT) - BR_BLU(brclr));
/* If it's closer than what we've found so far, remember it */
if (dclrT < dclrNear)
{
lwclrNear = lwclr;
lwcrngNear = lwcrng;
dclrNear = dclrT;
brclrNear = brclrT;
if (dclrT == 0)
break;
}
}
/* If we found an exact match, look no further */
if (dclrT == 0)
break;
}
/* Report and return */
if (dclrT != 0)
printf("Didn't find exact match for color %06x; nearest was %06x\n",
brclr, brclrNear);
return lwcrngNear;
}
/* Array of keywords known by our simple script interpreter */
static KEYTT _rgkeyttS2B[] =
{
"ACTOR", ttActor,
"ACTION", ttActionS2B,
"BACKGROUND", ttBackgroundS2B,
DK_A_CAMERA_POS_TOKEN, ttPosition,
DK_A_CAMERA_INT_TOKEN, ttInterest,
DK_A_CAMERA_NEAR_TOKEN, ttNearCam,
DK_A_CAMERA_FAR_TOKEN, ttFarCam,
DK_A_CAMERA_FOV_TOKEN, ttFovCam,
DK_A_POS_STATIC_TOKEN, ttStatic,
"CNO", ttCno,
"NAMED", ttCalled,
"XREST", ttXRest,
"YREST", ttYRest,
"ZREST", ttZRest,
"FILEBASE", ttFilebase,
"FIRST", ttFirst,
"LAST", ttLast,
"FLAGS", ttFlags,
"SCALE", ttScale,
"SKIP", ttSkip,
"SUBMODEL", ttSubmodel,
"BPSETS", ttBPS,
"LIGHTS", ttLights,
"CAMERAS", ttCameras,
"LENGTH", ttLength,
"STEP", ttStep,
"COSTUME", ttCostume,
"USE_SETS", ttUseSets,
"NEW_ACTOR_POS", ttNewActorPos,
"MATERIALS", ttMaterials,
"FILE", ttFilename
};
#define kckeyttS2B (size(_rgkeyttS2B)/size(_rgkeyttS2B[0]))
/***************************************************************************
Read a number. The first character is passed in ch. lwBase is the base
of the number (must be <= 10).
***************************************************************************/
void S2BLX::_ReadNumTok(PTOK ptok, achar ch, long lwBase, long cchMax)
{
AssertThis(0);
AssertVarMem(ptok);
AssertIn(ch, ChLit('0'), ChLit('0') + lwBase);
AssertIn(lwBase, 2, 11);
long lw;
lw = ch - ChLit('0');
while (--cchMax > 0 && _FFetchRgch(&ch) &&
(_GrfctCh(ch) & fctDec) && (ch - ChLit('0') < lwBase))
{
lw = lw * lwBase + (ch - ChLit('0'));
_Advance();
}
if (ch == ChLit('.'))
{
/* HACK: if we didn't read any non-zero digits yet, and we hit
a decimal point, assume we're really reading lwBase 10 */
if (lw == 0)
lwBase = 10;
long lwFrac = lwBase;
double fl = (double)0.0;
_Advance();
while (--cchMax > 0 && _FFetchRgch(&ch) &&
(_GrfctCh(ch) & fctDec) && (ch - ChLit('0') < lwBase))
{
fl += ((double)(ch - ChLit('0'))) / (double)lwFrac;
lwFrac *= lwBase;
_Advance();
}
ptok->tt = ttFloat;
_fl = lw + fl;
}
else
ptok->lw = lw;
}
/***************************************************************************
Reads in the next token. Resolves certain names to keyword tokens.
***************************************************************************/
bool S2BLX::FGetS2btk(PS2BTK ps2btk)
{
AssertThis(0);
AssertVarMem(ps2btk);
bool fRet;
long ikeytt;
PTOK ptok = &ps2btk->tok;
while ((fRet = S2BLX_PAR::FGetTok(ptok)) &&
(ptok->tt == ttComma || ptok->tt == ttSemi))
;
if (fRet)
{
/* Ignore minus signs, unless they precede a number */
if (ptok->tt == ttSub)
{
if (!(fRet = S2BLX_PAR::FGetTok(ptok)))
goto LDone;
switch (ptok->tt)
{
case ttLong:
ptok->lw = -ptok->lw;
goto LDone;
case ttFloat:
_fl = -_fl;
goto LDone;
default:
break;
}
}
if (ptok->tt != ttName)
goto LDone;
//check for a keyword
for (ikeytt = 0; ikeytt < kckeyttS2B; ikeytt++)
{
if (ptok->stn.FEqualUserSz(_rgkeyttS2B[ikeytt].pszKeyword))
{
ptok->tt = _rgkeyttS2B[ikeytt].tt;
break;
}
}
}
LDone:
_ttCur = ptok->tt;
if (_ttCur == ttFloat)
ps2btk->fl = _fl;
return fRet;
}
/******************************************************************************
FGetTok
Gets a base TOK. Will filter out ttFloat.
Arguments:
PTOK ptok -- pointer to TOK to fill in
Returns: fTrue if it got a valid token
************************************************************ PETED ***********/
bool S2BLX::FGetTok(PTOK ptok)
{
bool fRet;
S2BTK s2btk;
fRet = FGetS2btk(&s2btk);
if (fRet)
{
*ptok = s2btk.tok;
/* Convert token if necessary */
if (ptok->tt == ttFloat)
{
ptok->lw = (long)_fl;
ptok->tt = ttLong;
}
}
return fRet;
}
/******************************************************************************
FTextFromTt
Sets an STN to the text string that corresponds to the given token.
Arguments:
long tt -- the token
PSTN pstn -- the STN to set
Returns: fTrue if if knew about the token, fFalse otherwise
************************************************************ PETED ***********/
bool S2BLX::FTextFromTt(long tt, PSTN pstn)
{
AssertPo(pstn, 0);
long ikeytt;
for (ikeytt = 0; ikeytt < kckeyttS2B; ikeytt++)
if (tt == _rgkeyttS2B[ikeytt].tt)
break;
if (ikeytt == kckeyttS2B)
// return S2BLX_PAR::FTextFromTt(tt, pstn);
return fFalse;
pstn->SetSz(_rgkeyttS2B[ikeytt].pszKeyword);
return fTrue;
}
/******************************************************************************
FTextFromS2btk
Fills in the given STN with text that represents the given token.
Arguments:
PS2BTK ps2btk -- the token to use
PSTN pstn -- the STN to fill in
Returns: fTrue if the token was successfully recognized and converted
to text, fFalse otherwise
************************************************************ PETED ***********/
bool S2BLX::FTextFromS2btk(PS2BTK ps2btk, PSTN pstn)
{
AssertPo(pstn, 0);
pstn->SetNil();
if (ps2btk->tok.tt == ttFloat)
{
long lwInt, lwFrac;
lwInt = (long)ps2btk->fl;
lwFrac = (long)((ps2btk->fl - lwInt) * 100000.0);
AssertIn(lwFrac, 0, 100000);
AssertDo(pstn->FFormatSz(PszLit("%d.%05d"), lwInt, lwFrac), 0);
return fTrue;
}
// return FTextFromTok(&ps2btk->tok, pstn);
PTOK ptok = &ps2btk->tok;
switch (ptok->tt)
{
case ttLong:
AssertDo(pstn->FFormatSz(PszLit("%d"), ptok->lw), 0);
return fTrue;
case ttName:
*pstn = ptok->stn;
return fTrue;
case ttString:
/* Creating the string this way will guarantee the double-quotes will
make it into the string; the source string *may* be truncated by
a character or two. */
*pstn = PszLit("\"");
pstn->FAppendStn(&ptok->stn);
pstn->FInsertCh(pstn->Cch(), ChLit('"'));
return fTrue;
default:
return FTextFromTt(ptok->tt, pstn);
}
}
#ifdef DEBUG
/***************************************************************************
Warning proc called by Warn() macro
***************************************************************************/
void WarnProc(PSZ pszFile, long lwLine, PSZ pszMessage)
{
if (_fEnableWarnings)
{
if (pszMessage != pvNil)
fprintf(stderr, "Warning: %s\n", pszMessage);
else
fprintf(stderr, "%s(%ld) : warning\n", pszFile, lwLine);
}
}
/***************************************************************************
Returning true breaks into the debugger.
***************************************************************************/
bool FAssertProc(PSZ pszFile, long lwLine, PSZ pszMessage,
void *pv, long cb)
{
fprintf(stderr, "An assert occurred: \n");
if (pszMessage != pvNil)
fprintf(stderr, " Message: %s\n", pszMessage);
if (pv != pvNil)
{
fprintf(stderr, " Address %x\n", pv);
if (cb != 0)
{
fprintf(stderr, " Value: ");
switch (cb)
{
default:
{
byte *pb;
byte *pbLim;
for (pb = (byte *)pv, pbLim = pb + cb; pb < pbLim; pb++)
fprintf(stderr, "%02x", (int)*pb);
}
break;
case 2:
fprintf(stderr, "%04x", (int)*(short *)pv);
break;
case 4:
fprintf(stderr, "%08lx", *(long *)pv);
break;
}
printf("\n");
}
}
fprintf(stderr, " File: %s\n", pszFile);
fprintf(stderr, " Line: %ld\n", lwLine);
return _fBreak;
}
#endif