mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 10:22:40 +01:00
743 lines
19 KiB
C++
743 lines
19 KiB
C++
|
/* Copyright (c) Microsoft Corporation.
|
||
|
Licensed under the MIT License. */
|
||
|
|
||
|
/***************************************************************************
|
||
|
|
||
|
mtrl.cpp: Material (MTRL) and custom material (CMTL) classes
|
||
|
|
||
|
Primary Author: ******
|
||
|
Review Status: REVIEWED - any changes to this file must be reviewed!
|
||
|
|
||
|
***************************************************************************/
|
||
|
#include "soc.h"
|
||
|
ASSERTNAME
|
||
|
|
||
|
RTCLASS(MTRL)
|
||
|
RTCLASS(CMTL)
|
||
|
|
||
|
// REVIEW *****: kiclrBaseDefault and kcclrDefault are palette-specific
|
||
|
const byte kiclrBaseDefault = 15; // base index of default color
|
||
|
const byte kcclrDefault = 15; // count of shades in default color
|
||
|
|
||
|
const br_ufraction kbrufKaDefault = BR_UFRACTION(0.10);
|
||
|
const br_ufraction kbrufKdDefault = BR_UFRACTION(0.60);
|
||
|
const br_ufraction kbrufKsDefault = BR_UFRACTION(0.60);
|
||
|
const BRS krPowerDefault = BR_SCALAR(50);
|
||
|
const byte kbOpaque = 0xff;
|
||
|
|
||
|
PTMAP MTRL::_ptmapShadeTable = pvNil; // shade table for all MTRLs
|
||
|
|
||
|
/***************************************************************************
|
||
|
Call this function to assign the global shade table. It is read from
|
||
|
the given chunk.
|
||
|
***************************************************************************/
|
||
|
bool MTRL::FSetShadeTable(PCFL pcfl, CTG ctg, CNO cno)
|
||
|
{
|
||
|
AssertPo(pcfl, 0);
|
||
|
|
||
|
ReleasePpo(&_ptmapShadeTable);
|
||
|
_ptmapShadeTable = TMAP::PtmapRead(pcfl, ctg, cno);
|
||
|
return (pvNil != _ptmapShadeTable);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Create a new solid-color material
|
||
|
***************************************************************************/
|
||
|
PMTRL MTRL::PmtrlNew(long iclrBase, long cclr)
|
||
|
{
|
||
|
if (ivNil != iclrBase)
|
||
|
AssertIn(iclrBase, 0, kbMax);
|
||
|
if (ivNil != cclr)
|
||
|
AssertIn(cclr, 0, kbMax - iclrBase);
|
||
|
|
||
|
PMTRL pmtrl;
|
||
|
|
||
|
pmtrl = NewObj MTRL;
|
||
|
if (pvNil == pmtrl)
|
||
|
return pvNil;
|
||
|
|
||
|
// An arbitrary 4-character string is passed to BrMaterialAllocate (to
|
||
|
// be stored in a string pointed to by _pbmtl->identifier). The
|
||
|
// contents of the string are then replaced by the "this" pointer.
|
||
|
pmtrl->_pbmtl = BrMaterialAllocate("1234");
|
||
|
if (pvNil == pmtrl->_pbmtl)
|
||
|
{
|
||
|
ReleasePpo(&pmtrl);
|
||
|
return pvNil;
|
||
|
}
|
||
|
CopyPb(&pmtrl, pmtrl->_pbmtl->identifier, size(long));
|
||
|
|
||
|
pmtrl->_pbmtl->ka = kbrufKaDefault;
|
||
|
pmtrl->_pbmtl->kd = kbrufKdDefault;
|
||
|
pmtrl->_pbmtl->ks = kbrufKsDefault;
|
||
|
pmtrl->_pbmtl->power = krPowerDefault;
|
||
|
if (ivNil == iclrBase)
|
||
|
pmtrl->_pbmtl->index_base = kiclrBaseDefault;
|
||
|
else
|
||
|
pmtrl->_pbmtl->index_base = (byte)iclrBase;
|
||
|
if (ivNil == cclr)
|
||
|
pmtrl->_pbmtl->index_range = kcclrDefault;
|
||
|
else
|
||
|
pmtrl->_pbmtl->index_range = (byte)cclr;
|
||
|
pmtrl->_pbmtl->opacity = kbOpaque; // all socrates objects are opaque
|
||
|
pmtrl->_pbmtl->flags = BR_MATF_LIGHT | BR_MATF_GOURAUD;
|
||
|
BrMaterialAdd(pmtrl->_pbmtl);
|
||
|
AssertPo(pmtrl, 0);
|
||
|
return pmtrl;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
A PFNRPO to read MTRL objects.
|
||
|
***************************************************************************/
|
||
|
bool MTRL::FReadMtrl(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
|
||
|
PBACO *ppbaco, long *pcb)
|
||
|
{
|
||
|
AssertPo(pcrf, 0);
|
||
|
AssertPo(pblck, 0);
|
||
|
AssertNilOrVarMem(ppbaco);
|
||
|
AssertVarMem(pcb);
|
||
|
|
||
|
PMTRL pmtrl;
|
||
|
|
||
|
*pcb = size(MTRL);
|
||
|
if (pvNil == ppbaco)
|
||
|
return fTrue;
|
||
|
pmtrl = NewObj MTRL;
|
||
|
if (pvNil == pmtrl || !pmtrl->_FInit(pcrf, ctg, cno))
|
||
|
{
|
||
|
TrashVar(ppbaco);
|
||
|
TrashVar(pcb);
|
||
|
ReleasePpo(&pmtrl);
|
||
|
return fFalse;
|
||
|
}
|
||
|
AssertPo(pmtrl, 0);
|
||
|
*ppbaco = pmtrl;
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Read the given MTRL chunk from file
|
||
|
***************************************************************************/
|
||
|
bool MTRL::_FInit(PCRF pcrf, CTG ctg, CNO cno)
|
||
|
{
|
||
|
AssertBaseThis(0);
|
||
|
AssertPo(pcrf, 0);
|
||
|
|
||
|
PCFL pcfl = pcrf->Pcfl();
|
||
|
BLCK blck;
|
||
|
MTRLF mtrlf;
|
||
|
KID kid;
|
||
|
MTRL *pmtrlThis = this; // to get MTRL from BMTL
|
||
|
PTMAP ptmap = pvNil;
|
||
|
|
||
|
if (!pcfl->FFind(ctg, cno, &blck) || !blck.FUnpackData())
|
||
|
return fFalse;
|
||
|
|
||
|
if (blck.Cb() < size(MTRLF))
|
||
|
return fFalse;
|
||
|
if (!blck.FReadRgb(&mtrlf, size(MTRLF), 0))
|
||
|
return fFalse;
|
||
|
if (kboOther == mtrlf.bo)
|
||
|
SwapBytesBom(&mtrlf, kbomMtrlf);
|
||
|
Assert(kboCur == mtrlf.bo, "bad MTRLF");
|
||
|
|
||
|
// An arbitrary 4-character string is passed to BrMaterialAllocate (to
|
||
|
// be stored in a string pointed to by _pbmtl->identifier). The
|
||
|
// contents of the string are then replaced by the "this" pointer.
|
||
|
_pbmtl = BrMaterialAllocate("1234");
|
||
|
if (pvNil == _pbmtl)
|
||
|
return fFalse;
|
||
|
CopyPb(&pmtrlThis, _pbmtl->identifier, size(long));
|
||
|
_pbmtl->colour = mtrlf.brc;
|
||
|
_pbmtl->ka = mtrlf.brufKa;
|
||
|
_pbmtl->kd = mtrlf.brufKd;
|
||
|
// Note: for socrates, mtrlf.brufKs should be zero
|
||
|
_pbmtl->ks = mtrlf.brufKs;
|
||
|
|
||
|
_pbmtl->power = mtrlf.rPower;
|
||
|
_pbmtl->index_base = mtrlf.bIndexBase;
|
||
|
_pbmtl->index_range = mtrlf.cIndexRange;
|
||
|
_pbmtl->opacity = kbOpaque; // all socrates objects are opaque
|
||
|
|
||
|
// REVIEW *****: also set the BR_MATF_PRELIT flag to use prelit models
|
||
|
_pbmtl->flags = BR_MATF_LIGHT | BR_MATF_SMOOTH;
|
||
|
|
||
|
// now read texture map, if any
|
||
|
if (pcfl->FGetKidChidCtg(ctg, cno, 0, kctgTmap, &kid))
|
||
|
{
|
||
|
ptmap = (PTMAP)pcrf->PbacoFetch(kid.cki.ctg, kid.cki.cno,
|
||
|
TMAP::FReadTmap);
|
||
|
if (pvNil == ptmap)
|
||
|
return fFalse;
|
||
|
_pbmtl->colour_map = ptmap->Pbpmp();
|
||
|
Assert((PTMAP)_pbmtl->colour_map->identifier == ptmap, "lost tmap!");
|
||
|
AssertPo(_ptmapShadeTable, 0);
|
||
|
_pbmtl->index_shade = _ptmapShadeTable->Pbpmp();
|
||
|
_pbmtl->flags |= BR_MATF_MAP_COLOUR;
|
||
|
_pbmtl->index_base = 0;
|
||
|
_pbmtl->index_range = _ptmapShadeTable->Pbpmp()->height - 1;
|
||
|
|
||
|
/* Look for a texture transform for the MTRL */
|
||
|
if (pcfl->FGetKidChidCtg(ctg, cno, 0, kctgTxxf, &kid))
|
||
|
{
|
||
|
TXXFF txxff;
|
||
|
|
||
|
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck) || !blck.FUnpackData())
|
||
|
goto LFail;
|
||
|
if (blck.Cb() < size(TXXFF))
|
||
|
goto LFail;
|
||
|
if (!blck.FReadRgb(&txxff, size(TXXFF), 0))
|
||
|
goto LFail;
|
||
|
if (kboCur != txxff.bo)
|
||
|
SwapBytesBom(&txxff, kbomTxxff);
|
||
|
Assert(kboCur == txxff.bo, "bad TXXFF");
|
||
|
_pbmtl->map_transform = txxff.bmat23;
|
||
|
}
|
||
|
}
|
||
|
BrMaterialAdd(_pbmtl);
|
||
|
AssertThis(0);
|
||
|
return fTrue;
|
||
|
LFail:
|
||
|
/* REVIEW ***** (peted): Only the code that I added uses this LFail
|
||
|
case. It's my opinion that any API which can fail should clean up
|
||
|
after itself. It happens that in the case of this MTRL class, when
|
||
|
the caller releases this instance, the TMAP and BMTL are freed anyway,
|
||
|
but I don't think that it's good to count on that */
|
||
|
ReleasePpo(&ptmap);
|
||
|
_pbmtl->colour_map = pvNil;
|
||
|
BrMaterialFree(_pbmtl);
|
||
|
_pbmtl = pvNil;
|
||
|
return fFalse;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Read a PIX and build a PMTRL from it
|
||
|
***************************************************************************/
|
||
|
PMTRL MTRL::PmtrlNewFromPix(PFNI pfni)
|
||
|
{
|
||
|
AssertPo(pfni, ffniFile);
|
||
|
|
||
|
STN stn;
|
||
|
PMTRL pmtrl;
|
||
|
PBMTL pbmtl;
|
||
|
PTMAP ptmap;
|
||
|
|
||
|
pmtrl = NewObj MTRL;
|
||
|
if (pvNil == pmtrl)
|
||
|
goto LFail;
|
||
|
|
||
|
// An arbitrary 4-character string is passed to BrMaterialAllocate (to
|
||
|
// be stored in a string pointed to by _pbmtl->identifier). The
|
||
|
// contents of the string are then replaced by the "this" pointer.
|
||
|
pmtrl->_pbmtl = BrMaterialAllocate("1234");
|
||
|
if (pvNil == pmtrl->_pbmtl)
|
||
|
goto LFail;
|
||
|
pbmtl = pmtrl->_pbmtl;
|
||
|
CopyPb(&pmtrl, pbmtl->identifier, size(long));
|
||
|
pbmtl->colour = 0; // this field is ignored
|
||
|
pbmtl->ka = kbrufKaDefault;
|
||
|
pbmtl->kd = kbrufKdDefault;
|
||
|
pbmtl->ks = kbrufKsDefault;
|
||
|
pbmtl->power = krPowerDefault;
|
||
|
pbmtl->opacity = kbOpaque; // all socrates objects are opaque
|
||
|
pbmtl->flags = BR_MATF_LIGHT | BR_MATF_GOURAUD;
|
||
|
pfni->GetStnPath(&stn);
|
||
|
pbmtl->colour_map = BrPixelmapLoad(stn.Psz());
|
||
|
if (pvNil == pbmtl->colour_map)
|
||
|
goto LFail;
|
||
|
|
||
|
// Create a TMAP for this BPMP. We don't directly save
|
||
|
// the ptmap...it's automagically attached to the
|
||
|
// BPMP's identifier.
|
||
|
ptmap = TMAP::PtmapNewFromBpmp(pbmtl->colour_map);
|
||
|
if (pvNil == ptmap)
|
||
|
{
|
||
|
BrPixelmapFree(pbmtl->colour_map);
|
||
|
goto LFail;
|
||
|
}
|
||
|
Assert((PTMAP)pbmtl->colour_map->identifier == ptmap, "lost our TMAP!");
|
||
|
AssertPo(_ptmapShadeTable, 0);
|
||
|
pbmtl->index_shade = _ptmapShadeTable->Pbpmp();
|
||
|
pbmtl->flags |= BR_MATF_MAP_COLOUR;
|
||
|
pbmtl->index_base = 0;
|
||
|
pbmtl->index_range = _ptmapShadeTable->Pbpmp()->height - 1;
|
||
|
AssertPo(pmtrl, 0);
|
||
|
return pmtrl;
|
||
|
LFail:
|
||
|
ReleasePpo(&pmtrl);
|
||
|
return pvNil;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Read a BMP and build a PMTRL from it
|
||
|
***************************************************************************/
|
||
|
PMTRL MTRL::PmtrlNewFromBmp(PFNI pfni, PGL pglclr)
|
||
|
{
|
||
|
AssertPo(pfni, ffniFile);
|
||
|
AssertPo(_ptmapShadeTable, 0);
|
||
|
|
||
|
PMTRL pmtrl;
|
||
|
PTMAP ptmap;
|
||
|
|
||
|
pmtrl = PmtrlNew();
|
||
|
if (pvNil == pmtrl)
|
||
|
return pvNil;
|
||
|
|
||
|
ptmap = TMAP::PtmapReadNative(pfni, pglclr);
|
||
|
if (pvNil == ptmap)
|
||
|
{
|
||
|
ReleasePpo(&pmtrl);
|
||
|
return pvNil;
|
||
|
}
|
||
|
pmtrl->_pbmtl->index_base = 0;
|
||
|
pmtrl->_pbmtl->index_range = _ptmapShadeTable->Pbpmp()->height - 1;
|
||
|
pmtrl->_pbmtl->index_shade = _ptmapShadeTable->Pbpmp();
|
||
|
pmtrl->_pbmtl->flags |= BR_MATF_MAP_COLOUR;
|
||
|
pmtrl->_pbmtl->colour_map = ptmap->Pbpmp();
|
||
|
// The reference for ptmap has been transfered to pmtrl by the previous
|
||
|
// line, so I don't need to ReleasePpo(&ptmap) in this function.
|
||
|
|
||
|
return pmtrl;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return a pointer to the MTRL that owns this BMTL
|
||
|
***************************************************************************/
|
||
|
PMTRL MTRL::PmtrlFromBmtl(PBMTL pbmtl)
|
||
|
{
|
||
|
AssertVarMem(pbmtl);
|
||
|
|
||
|
PMTRL pmtrl = (PMTRL)*(long *)pbmtl->identifier;
|
||
|
AssertPo(pmtrl, 0);
|
||
|
return pmtrl;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return this MTRL's TMAP, or pvNil if it's a solid-color MTRL.
|
||
|
Note: This function doesn't AssertThis because it gets called on
|
||
|
objects which are not necessarily valid (e.g., from the destructor and
|
||
|
from AssertThis())
|
||
|
***************************************************************************/
|
||
|
PTMAP MTRL::Ptmap(void)
|
||
|
{
|
||
|
AssertBaseThis(0);
|
||
|
|
||
|
if (pvNil == _pbmtl)
|
||
|
return pvNil;
|
||
|
else if (pvNil == _pbmtl->colour_map)
|
||
|
return pvNil;
|
||
|
else
|
||
|
return (PTMAP)_pbmtl->colour_map->identifier;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Write a MTRL to a chunky file
|
||
|
***************************************************************************/
|
||
|
bool MTRL::FWrite(PCFL pcfl, CTG ctg, CNO *pcno)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pcfl, 0);
|
||
|
AssertVarMem(pcno);
|
||
|
|
||
|
MTRLF mtrlf;
|
||
|
CNO cnoChild;
|
||
|
PTMAP ptmap;
|
||
|
|
||
|
mtrlf.bo = kboCur;
|
||
|
mtrlf.osk = koskCur;
|
||
|
mtrlf.brc = _pbmtl->colour;
|
||
|
mtrlf.brufKa = _pbmtl->ka;
|
||
|
mtrlf.brufKd = _pbmtl->kd;
|
||
|
mtrlf.brufKs = _pbmtl->ks;
|
||
|
mtrlf.bIndexBase = _pbmtl->index_base;
|
||
|
mtrlf.cIndexRange = _pbmtl->index_range;
|
||
|
mtrlf.rPower = _pbmtl->power;
|
||
|
|
||
|
if (!pcfl->FAddPv(&mtrlf, size(MTRLF), ctg, pcno))
|
||
|
return fFalse;
|
||
|
ptmap = Ptmap();
|
||
|
if (pvNil != ptmap)
|
||
|
{
|
||
|
if (!ptmap->FWrite(pcfl, kctgTmap, &cnoChild))
|
||
|
{
|
||
|
pcfl->Delete(ctg, *pcno);
|
||
|
return fFalse;
|
||
|
}
|
||
|
if (!pcfl->FAdoptChild(ctg, *pcno, kctgTmap, cnoChild, 0))
|
||
|
{
|
||
|
pcfl->Delete(kctgTmap, cnoChild);
|
||
|
pcfl->Delete(ctg, *pcno);
|
||
|
return fFalse;
|
||
|
}
|
||
|
}
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Free the MTRL
|
||
|
***************************************************************************/
|
||
|
MTRL::~MTRL(void)
|
||
|
{
|
||
|
AssertBaseThis(0);
|
||
|
|
||
|
PTMAP ptmap;
|
||
|
|
||
|
ptmap = Ptmap();
|
||
|
|
||
|
if (pvNil != ptmap)
|
||
|
{
|
||
|
ReleasePpo(&ptmap);
|
||
|
_pbmtl->colour_map = pvNil;
|
||
|
}
|
||
|
BrMaterialRemove(_pbmtl);
|
||
|
BrMaterialFree(_pbmtl);
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/***************************************************************************
|
||
|
Assert the validity of the MTRL.
|
||
|
***************************************************************************/
|
||
|
void MTRL::AssertValid(ulong grf)
|
||
|
{
|
||
|
MTRL_PAR::AssertValid(fobjAllocated);
|
||
|
|
||
|
AssertNilOrPo(Ptmap(), 0);
|
||
|
Assert(pvNil != _ptmapShadeTable,
|
||
|
"Why do we have MTRLs but no shade table?");
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Mark memory used by the MTRL
|
||
|
***************************************************************************/
|
||
|
void MTRL::MarkMem(void)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
|
||
|
PTMAP ptmap;
|
||
|
|
||
|
MTRL_PAR::MarkMem();
|
||
|
ptmap = Ptmap();
|
||
|
if (pvNil != ptmap)
|
||
|
MarkMemObj(ptmap);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
Mark memory used by the shade table
|
||
|
***************************************************************************/
|
||
|
void MTRL::MarkShadeTable(void)
|
||
|
{
|
||
|
MarkMemObj(_ptmapShadeTable);
|
||
|
}
|
||
|
|
||
|
#endif //DEBUG
|
||
|
|
||
|
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
// CMTL (custom material) stuff begins here
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Static function to see if the given chunk has MODL children
|
||
|
***************************************************************************/
|
||
|
bool CMTL::FHasModels(PCFL pcfl, CTG ctg, CNO cno)
|
||
|
{
|
||
|
AssertPo(pcfl, 0);
|
||
|
|
||
|
KID kid;
|
||
|
|
||
|
return pcfl->FGetKidChidCtg(ctg, cno, 0, kctgBmdl, &kid);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Static function to see if the two given CMTLs have the same child
|
||
|
MODLs
|
||
|
***************************************************************************/
|
||
|
bool CMTL::FEqualModels(PCFL pcfl, CNO cno1, CNO cno2)
|
||
|
{
|
||
|
AssertPo(pcfl, 0);
|
||
|
|
||
|
CHID chid = 0;
|
||
|
KID kid1;
|
||
|
KID kid2;
|
||
|
|
||
|
while (pcfl->FGetKidChidCtg(kctgCmtl, cno1, chid, kctgBmdl, &kid1))
|
||
|
{
|
||
|
if (!pcfl->FGetKidChidCtg(kctgCmtl, cno2, chid, kctgBmdl, &kid2))
|
||
|
return fFalse;
|
||
|
if (kid1.cki.cno != kid2.cki.cno)
|
||
|
return fFalse;
|
||
|
chid++;
|
||
|
}
|
||
|
// End of cno1's BMDLs...make sure cno2 doesn't have any more
|
||
|
if (pcfl->FGetKidChidCtg(kctgCmtl, cno2, chid, kctgBmdl, &kid2))
|
||
|
return fFalse;
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Create a new custom material
|
||
|
***************************************************************************/
|
||
|
PCMTL CMTL::PcmtlNew(long ibset, long cbprt, PMTRL *prgpmtrl)
|
||
|
{
|
||
|
AssertPvCb(prgpmtrl, LwMul(cbprt, size(PMTRL)));
|
||
|
PCMTL pcmtl;
|
||
|
long imtrl;
|
||
|
|
||
|
pcmtl = NewObj CMTL;
|
||
|
if (pvNil == pcmtl)
|
||
|
return pvNil;
|
||
|
|
||
|
pcmtl->_ibset = ibset;
|
||
|
pcmtl->_cbprt = cbprt;
|
||
|
if (!FAllocPv((void **)&pcmtl->_prgpmtrl, LwMul(pcmtl->_cbprt,
|
||
|
size(PMTRL)), fmemClear, mprNormal))
|
||
|
{
|
||
|
ReleasePpo(&pcmtl);
|
||
|
return pvNil;
|
||
|
}
|
||
|
if (!FAllocPv((void **)&pcmtl->_prgpmodl, LwMul(pcmtl->_cbprt,
|
||
|
size(PMODL)), fmemClear, mprNormal))
|
||
|
{
|
||
|
ReleasePpo(&pcmtl);
|
||
|
return pvNil;
|
||
|
}
|
||
|
for (imtrl = 0; imtrl < cbprt; imtrl++)
|
||
|
{
|
||
|
AssertPo(prgpmtrl[imtrl], 0);
|
||
|
pcmtl->_prgpmtrl[imtrl] = prgpmtrl[imtrl];
|
||
|
pcmtl->_prgpmtrl[imtrl]->AddRef();
|
||
|
}
|
||
|
AssertPo(pcmtl, 0);
|
||
|
return pcmtl;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
A PFNRPO to read CMTL objects.
|
||
|
***************************************************************************/
|
||
|
bool CMTL::FReadCmtl(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
|
||
|
PBACO *ppbaco, long *pcb)
|
||
|
{
|
||
|
AssertPo(pcrf, 0);
|
||
|
AssertPo(pblck, 0);
|
||
|
AssertNilOrVarMem(ppbaco);
|
||
|
AssertVarMem(pcb);
|
||
|
|
||
|
PCMTL pcmtl;
|
||
|
|
||
|
*pcb = size(CMTL);
|
||
|
if (pvNil == ppbaco)
|
||
|
return fTrue;
|
||
|
pcmtl = NewObj CMTL;
|
||
|
if (pvNil == pcmtl || !pcmtl->_FInit(pcrf, ctg, cno))
|
||
|
{
|
||
|
ReleasePpo(&pcmtl);
|
||
|
TrashVar(ppbaco);
|
||
|
TrashVar(pcb);
|
||
|
return fFalse;
|
||
|
}
|
||
|
AssertPo(pcmtl, 0);
|
||
|
*ppbaco = pcmtl;
|
||
|
*pcb += LwMul(size(PMTRL) + size(PMODL), pcmtl->_cbprt);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Read a CMTL from file
|
||
|
***************************************************************************/
|
||
|
bool CMTL::_FInit(PCRF pcrf, CTG ctg, CNO cno)
|
||
|
{
|
||
|
AssertBaseThis(0);
|
||
|
AssertPo(pcrf, 0);
|
||
|
|
||
|
long ikid;
|
||
|
long imtrl;
|
||
|
KID kid;
|
||
|
BLCK blck;
|
||
|
PCFL pcfl = pcrf->Pcfl();
|
||
|
CMTLF cmtlf;
|
||
|
|
||
|
if (!pcfl->FFind(ctg, cno, &blck) || !blck.FUnpackData())
|
||
|
return fFalse;
|
||
|
|
||
|
if (blck.Cb() != size(CMTLF))
|
||
|
{
|
||
|
Bug("bad CMTLF...you may need to update tmpls.chk");
|
||
|
return fFalse;
|
||
|
}
|
||
|
if (!blck.FReadRgb(&cmtlf, size(CMTLF), 0))
|
||
|
return fFalse;
|
||
|
if (kboOther == cmtlf.bo)
|
||
|
SwapBytesBom(&cmtlf, kbomCmtlf);
|
||
|
Assert(kboCur == cmtlf.bo, "bad CMTLF");
|
||
|
_ibset = cmtlf.ibset;
|
||
|
|
||
|
// Highest chid is number of body part sets - 1
|
||
|
_cbprt = 0;
|
||
|
// note: there might be a faster way to compute _cbprt
|
||
|
for (ikid = 0; pcfl->FGetKid(ctg, cno, ikid, &kid); ikid++)
|
||
|
{
|
||
|
if ((long)kid.chid > (_cbprt - 1))
|
||
|
_cbprt = kid.chid + 1;
|
||
|
}
|
||
|
if (!FAllocPv((void **)&_prgpmtrl, LwMul(_cbprt, size(PMTRL)),
|
||
|
fmemClear, mprNormal))
|
||
|
{
|
||
|
return fFalse;
|
||
|
}
|
||
|
if (!FAllocPv((void **)&_prgpmodl, LwMul(_cbprt, size(PMODL)),
|
||
|
fmemClear, mprNormal))
|
||
|
{
|
||
|
return fFalse;
|
||
|
}
|
||
|
for (imtrl = 0; imtrl < _cbprt; imtrl++)
|
||
|
{
|
||
|
if (pcfl->FGetKidChidCtg(ctg, cno, imtrl, kctgMtrl, &kid))
|
||
|
{
|
||
|
_prgpmtrl[imtrl] = (MTRL *)pcrf->PbacoFetch(kid.cki.ctg,
|
||
|
kid.cki.cno, MTRL::FReadMtrl);
|
||
|
if (pvNil == _prgpmtrl[imtrl])
|
||
|
return fFalse;
|
||
|
}
|
||
|
if (pcfl->FGetKidChidCtg(ctg, cno, imtrl, kctgBmdl, &kid))
|
||
|
{
|
||
|
_prgpmodl[imtrl] = (MODL *)pcrf->PbacoFetch(kid.cki.ctg,
|
||
|
kid.cki.cno, MODL::FReadModl);
|
||
|
if (pvNil == _prgpmodl[imtrl])
|
||
|
return fFalse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Free the CMTL
|
||
|
***************************************************************************/
|
||
|
CMTL::~CMTL(void)
|
||
|
{
|
||
|
AssertBaseThis(0);
|
||
|
|
||
|
long imtrl;
|
||
|
|
||
|
if (pvNil != _prgpmtrl)
|
||
|
{
|
||
|
for (imtrl = 0; imtrl < _cbprt; imtrl++)
|
||
|
ReleasePpo(&_prgpmtrl[imtrl]);
|
||
|
FreePpv((void **)&_prgpmtrl);
|
||
|
}
|
||
|
|
||
|
if (pvNil != _prgpmodl)
|
||
|
{
|
||
|
for (imtrl = 0; imtrl < _cbprt; imtrl++)
|
||
|
ReleasePpo(&_prgpmodl[imtrl]);
|
||
|
FreePpv((void **)&_prgpmodl);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return ibmtl'th BMTL
|
||
|
***************************************************************************/
|
||
|
BMTL *CMTL::Pbmtl(long ibmtl)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(ibmtl, 0, _cbprt);
|
||
|
|
||
|
return _prgpmtrl[ibmtl]->Pbmtl();
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return imodl'th MODL
|
||
|
***************************************************************************/
|
||
|
PMODL CMTL::Pmodl(long imodl)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(imodl, 0, _cbprt);
|
||
|
|
||
|
AssertNilOrPo(_prgpmodl[imodl], 0);
|
||
|
return _prgpmodl[imodl];
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Returns whether this CMTL has any models attached
|
||
|
***************************************************************************/
|
||
|
bool CMTL::FHasModels(void)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
|
||
|
long imodl;
|
||
|
|
||
|
for (imodl = 0; imodl < _cbprt; imodl++)
|
||
|
{
|
||
|
if (pvNil != _prgpmodl[imodl])
|
||
|
return fTrue;
|
||
|
}
|
||
|
return fFalse;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/***************************************************************************
|
||
|
Assert the validity of the CMTL
|
||
|
***************************************************************************/
|
||
|
void CMTL::AssertValid(ulong grf)
|
||
|
{
|
||
|
long imtrl;
|
||
|
|
||
|
MTRL_PAR::AssertValid(fobjAllocated);
|
||
|
AssertPvCb(_prgpmtrl, LwMul(_cbprt, size(MTRL *)));
|
||
|
AssertPvCb(_prgpmodl, LwMul(_cbprt, size(MODL *)));
|
||
|
|
||
|
for (imtrl = 0; imtrl < _cbprt; imtrl++)
|
||
|
{
|
||
|
AssertPo(_prgpmtrl[imtrl], 0);
|
||
|
AssertNilOrPo(_prgpmodl[imtrl], 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Mark memory used by the MTRL
|
||
|
***************************************************************************/
|
||
|
void CMTL::MarkMem(void)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
|
||
|
long imtrl;
|
||
|
|
||
|
MTRL_PAR::MarkMem();
|
||
|
MarkPv(_prgpmtrl);
|
||
|
MarkPv(_prgpmodl);
|
||
|
for (imtrl = 0; imtrl < _cbprt; imtrl++)
|
||
|
{
|
||
|
MarkMemObj(_prgpmtrl[imtrl]);
|
||
|
MarkMemObj(_prgpmodl[imtrl]);
|
||
|
}
|
||
|
}
|
||
|
#endif //DEBUG
|
||
|
|
||
|
|