mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-25 11:42:35 +01:00
1473 lines
39 KiB
C++
1473 lines
39 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
|
|
body.cpp: Body class
|
|
|
|
Primary Author: ******
|
|
Review Status: REVIEWED - any changes to this file must be reviewed!
|
|
|
|
A BODY holds the BRender-related data structures that make up what
|
|
Socrates calls an actor. The BODY keeps track of all the body parts'
|
|
models, matrices, and materials that make up the actor's shape,
|
|
position, orientation, and costume. ACTR and TMPL are the main
|
|
clients of BODY. From the client's point of view, a BODY consists
|
|
not of a tree of body parts, but an array of parts and part sets. The
|
|
"ibact"s and "ibset"s in the BODY APIs are indices into these arrays.
|
|
|
|
PbodyNew() takes a parameter called pglibactPar, which is a GL of
|
|
shorts. Each short is the body part number of a body part's parent
|
|
body part. For example, suppose you passed in a pglibactPar of:
|
|
|
|
(ivNil, 0, 0, 1, 2, 2)
|
|
|
|
Body part 0 would have no parent (the first number in the GL is
|
|
always ivNil). Body part 1's parent would be body part 0. Body part
|
|
2's parent would also be body part 0. Body part 3's parent would be
|
|
body part 1, etc. The resulting tree would be:
|
|
|
|
0
|
|
|
|
|
+--+--+
|
|
| |
|
|
1 +-2-+
|
|
| | |
|
|
3 4 5
|
|
|
|
BODY needs to keep track of three sets of BACTs: first, a root BACT
|
|
whose transformation is changed to position and orient the BODY.
|
|
Second, a BACT for the actor hilighting. Finally, there are
|
|
the body parts for the BODY. So the BRender tree *really* looks like:
|
|
|
|
root
|
|
|
|
|
+------+-----+
|
|
| |
|
|
part0 hilite
|
|
|
|
|
+-------+--------+
|
|
| |
|
|
part1 part2
|
|
| |
|
|
| +---+---+
|
|
| | |
|
|
part3 part4 part5
|
|
|
|
|
|
All these BACTs are allocated and released in one _prgbact. The
|
|
root is _prgbact[0]. The hilite BACT is _prgbact[1]. The real
|
|
body parts are _prgbact[2] through _prgbact[1 + 1 + _cbactPart].
|
|
There are APIs to access various members of this array more easily.
|
|
|
|
The second parameter to PbodyNew is pglibset, which divides the body
|
|
parts into "body part sets." A body part set is one or more body
|
|
parts which are texture-mapped as a group. For example, putting
|
|
a shirt on a human would affect the torso and both arms, so those
|
|
three body parts would be grouped into a body part set. If body
|
|
parts 0, 1, and 2 were in set 0; part 3 was in set 1; and parts 4
|
|
and 5 were in set 2, _pglibset would be {0, 0, 0, 1, 2, 2}.
|
|
|
|
When applying a MTRL to a body part set, the same MTRL is
|
|
attached to each body part in the set. When a CMTL is applied,
|
|
the CMTL has a different MTRL per body part in the set.
|
|
|
|
The way that MODLs, MTRLs, and CMTLs attach to the BODY is a little
|
|
strange. In the case of models, the br_actor's model needs to be set
|
|
to the MODL's BMDL (which is a BRender data structure), not the
|
|
MODL itself (which is a Socrates data structure). When it is time
|
|
to remove the model, the BODY has a pointer to the BMDL, but
|
|
not the MODL! Fortunately, MODL sets the BMDL's identifier field
|
|
to point to its owning MODL. So ReleasePpo can then be called on the
|
|
MODL. But I don't want to set the BMDL's identifier to pvNil in
|
|
the process, because other BODYs might be counting on the BMDL's
|
|
identifier to point to the MODL. So this code has to be careful using
|
|
ReleasePpo, since that sets the given pointer to pvNil. Same
|
|
problem for MTRLs:
|
|
|
|
BODY
|
|
| MODL<--+
|
|
v | |
|
|
BACT | |
|
|
| | | |
|
|
| v v |
|
|
|model------------>BMDL |
|
|
| | |
|
|
| v |
|
|
| identifier
|
|
|
|
|
+--+
|
|
| MTRL<--+
|
|
| | |
|
|
v v |
|
|
material--------->BMTL |
|
|
| |
|
|
v |
|
|
identifier
|
|
|
|
CMTLs are potentially even messier, since they're an abstraction on
|
|
top of MTRLs. Here, an array of PCMTLs on the BODY is explicitly kept
|
|
so it is easy to find out what CMTL is attached to what body part set
|
|
instead of working backwards from the BACT's material field.
|
|
|
|
***************************************************************************/
|
|
#include "soc.h"
|
|
ASSERTNAME
|
|
|
|
RTCLASS(BODY)
|
|
RTCLASS(COST)
|
|
|
|
// Specification of hilite color. REVIEW *****: should these
|
|
// values (or the PBMTL itself?) be passed in by the client?
|
|
const br_colour kbrcHilite = BR_COLOUR_RGB(255,255,255);
|
|
const byte kbOpaque = 0xff;
|
|
const br_ufraction kbrufKaHilite = BR_UFRACTION(0.10);
|
|
const br_ufraction kbrufKdHilite = BR_UFRACTION(0.60);
|
|
const br_ufraction kbrufKsHilite = BR_UFRACTION(0.60);
|
|
const BRS krPowerHilite = BR_SCALAR(50);
|
|
const long kiclrHilite = 0; // default to no highlighting
|
|
|
|
// Hilighting material
|
|
PBMTL BODY::_pbmtlHilite = pvNil;
|
|
|
|
PBODY BODY::_pbodyClosestClicked;
|
|
long BODY::_dzpClosestClicked;
|
|
PBACT BODY::_pbactClosestClicked;
|
|
|
|
|
|
/***************************************************************************
|
|
Builds a tree of BACTs for the BODY.
|
|
***************************************************************************/
|
|
BODY *BODY::PbodyNew(PGL pglibactPar, PGL pglibset)
|
|
{
|
|
AssertPo(pglibactPar, 0);
|
|
Assert(pglibactPar->CbEntry() == size(short), "bad pglibactPar");
|
|
AssertPo(pglibset, 0);
|
|
Assert(pglibset->CbEntry() == size(short), "bad pglibset");
|
|
|
|
BODY *pbody;
|
|
|
|
if (pvNil == _pbmtlHilite)
|
|
{
|
|
_pbmtlHilite = BrMaterialAllocate("Hilite");
|
|
if (pvNil == _pbmtlHilite)
|
|
return pvNil;
|
|
_pbmtlHilite->colour = kbrcHilite;
|
|
_pbmtlHilite->ka = kbrufKaHilite;
|
|
_pbmtlHilite->kd = kbrufKdHilite,
|
|
_pbmtlHilite->ks = kbrufKsHilite;
|
|
_pbmtlHilite->power = krPowerHilite;
|
|
_pbmtlHilite->flags = BR_MATF_LIGHT | BR_MATF_GOURAUD | BR_MATF_FORCE_Z_0;
|
|
_pbmtlHilite->index_base = kiclrHilite;
|
|
_pbmtlHilite->index_range = 1;
|
|
BrMaterialAdd(_pbmtlHilite);
|
|
}
|
|
|
|
pbody = NewObj BODY;
|
|
if (pvNil == pbody || !pbody->_FInit(pglibactPar, pglibset))
|
|
{
|
|
ReleasePpo(&pbody);
|
|
return pvNil;
|
|
}
|
|
|
|
AssertPo(pbody, fobjAssertFull);
|
|
return pbody;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Build the BODY
|
|
***************************************************************************/
|
|
bool BODY::_FInit(PGL pglibactPar, PGL pglibset)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(pglibactPar, 0);
|
|
AssertPo(pglibset, 0);
|
|
|
|
_cactHidden = 1; // body starts out hidden
|
|
|
|
if (!_FInitShape(pglibactPar, pglibset))
|
|
return fFalse;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Build the BODY
|
|
***************************************************************************/
|
|
bool BODY::_FInitShape(PGL pglibactPar, PGL pglibset)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(pglibactPar, 0);
|
|
Assert(pglibactPar->CbEntry() == size(short), "bad pglibactPar");
|
|
AssertPo(pglibset, 0);
|
|
Assert(pglibset->CbEntry() == size(short), "bad pglibset");
|
|
Assert(pglibactPar->IvMac() == 0 ||
|
|
ivNil == *(short *)pglibactPar->QvGet(0),
|
|
"bad first item in pglibactPar");
|
|
Assert(pglibactPar->IvMac() == pglibset->IvMac(),
|
|
"pglibactPar must be same size as pglibset");
|
|
|
|
BACT *pbact;
|
|
BACT *pbactPar;
|
|
short ibactPar;
|
|
short ibact;
|
|
short ibset;
|
|
|
|
// Copy pglibset into _pglibset
|
|
_pglibset = pglibset->PglDup();
|
|
if (pvNil == _pglibset)
|
|
return fFalse;
|
|
|
|
// _cbset is (highest entry in _pglibset) + 1.
|
|
_cbset = -1;
|
|
for (ibact = 0; ibact < _pglibset->IvMac(); ibact++)
|
|
{
|
|
_pglibset->Get(ibact, &ibset);
|
|
if (ibset > _cbset)
|
|
_cbset = ibset;
|
|
}
|
|
_cbset++;
|
|
|
|
if (!FAllocPv((void **)&_prgpcmtl, LwMul(_cbset, size(PCMTL)),
|
|
fmemClear, mprNormal))
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
_cbactPart = pglibactPar->IvMac();
|
|
Assert(_cbset <= _cbactPart, "More sets than body parts?");
|
|
if (!FAllocPv((void **)&_prgbact, LwMul(_Cbact(), size(BACT)),
|
|
fmemClear, mprNormal))
|
|
{
|
|
return fFalse;
|
|
}
|
|
// first, set up the root
|
|
pbact = _PbactRoot();
|
|
pbact->type = BR_ACTOR_NONE;
|
|
pbact->t.type = BR_TRANSFORM_MATRIX34;
|
|
BrMatrix34Identity(&pbact->t.t.mat);
|
|
pbact->identifier = (schar *)this; // to find BODY from a BACT
|
|
|
|
// next, set up hilite actor
|
|
pbact = _PbactHilite();
|
|
pbact->type = BR_ACTOR_NONE;
|
|
pbact->t.type = BR_TRANSFORM_MATRIX34;
|
|
BrMatrix34Identity(&pbact->t.t.mat);
|
|
pbact->identifier = (schar *)this; // to find BODY from a BACT
|
|
pbact->material = _pbmtlHilite;
|
|
pbact->render_style = BR_RSTYLE_BOUNDING_EDGES;
|
|
BrActorAdd(_PbactRoot(), pbact);
|
|
|
|
// now set up the body part BACTs
|
|
for (ibact = 0; ibact < _cbactPart; ibact++)
|
|
{
|
|
pbact = _PbactPart(ibact);
|
|
pbact->type = BR_ACTOR_MODEL;
|
|
pbact->render_style = BR_RSTYLE_FACES;
|
|
pbact->t.type = BR_TRANSFORM_MATRIX34;
|
|
BrMatrix34Identity(&pbact->t.t.mat);
|
|
pbact->identifier = (schar *)this; // to find BODY from a BACT
|
|
|
|
// Find parent of this body part
|
|
pglibactPar->Get(ibact, &ibactPar);
|
|
if (ivNil == ibactPar)
|
|
{
|
|
pbactPar = _PbactRoot();
|
|
}
|
|
else
|
|
{
|
|
AssertIn(ibactPar, 0, ibact);
|
|
pbactPar = _PbactPart(ibactPar);
|
|
}
|
|
BrActorAdd(pbactPar, pbact);
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Change the body part hierarchy and/or the body part sets of this BODY.
|
|
Models, materials, and matrices are not changed for body parts that
|
|
exist in both the old and reshaped BODYs.
|
|
***************************************************************************/
|
|
bool BODY::FChangeShape(PGL pglibactPar, PGL pglibset)
|
|
{
|
|
AssertThis(fobjAssertFull);
|
|
AssertPo(pglibactPar, 0);
|
|
AssertPo(pglibset, 0);
|
|
|
|
PBODY pbodyDup;
|
|
long ibset;
|
|
bool fMtrl;
|
|
PMTRL pmtrl;
|
|
PCMTL pcmtl;
|
|
long ibact;
|
|
PBACT pbactDup;
|
|
PBACT pbact;
|
|
long cactHidden = _cactHidden;
|
|
|
|
pbodyDup = PbodyDup();
|
|
if (pvNil == pbodyDup)
|
|
goto LFail;
|
|
|
|
_DestroyShape(); // note: hides the body
|
|
if (!_FInitShape(pglibactPar, pglibset))
|
|
goto LFail;
|
|
if (cactHidden == 0) // if body was visible
|
|
Show();
|
|
// Restore materials
|
|
for (ibset = 0; ibset < LwMin(_cbset, pbodyDup->_cbset); ibset++)
|
|
{
|
|
pbodyDup->GetPartSetMaterial(ibset, &fMtrl, &pmtrl, &pcmtl);
|
|
if (fMtrl)
|
|
SetPartSetMtrl(ibset, pmtrl);
|
|
else
|
|
SetPartSetCmtl(pcmtl);
|
|
}
|
|
// Restore models and matrices
|
|
for (ibact = 0; ibact < LwMin(_cbactPart, pbodyDup->_cbactPart); ibact++)
|
|
{
|
|
pbactDup = pbodyDup->_PbactPart(ibact);
|
|
pbact = _PbactPart(ibact);
|
|
if (pvNil != pbactDup->model)
|
|
SetPartModel(ibact, MODL::PmodlFromBmdl(pbactDup->model));
|
|
pbact->t.t.mat = pbactDup->t.t.mat;
|
|
}
|
|
_PbactRoot()->t.t.mat = pbodyDup->_PbactRoot()->t.t.mat;
|
|
// Restore hilite state
|
|
if (pbodyDup->_PbactHilite()->type == BR_ACTOR_MODEL)
|
|
Hilite(); // body was hilited, so hilite it now
|
|
ReleasePpo(&pbodyDup);
|
|
AssertThis(fobjAssertFull);
|
|
return fTrue;
|
|
LFail:
|
|
if (pvNil != pbodyDup)
|
|
{
|
|
Restore(pbodyDup);
|
|
if (cactHidden == 0)
|
|
Show();
|
|
ReleasePpo(&pbodyDup);
|
|
}
|
|
AssertThis(fobjAssertFull);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns the BODY that owns the given BACT. Also, if pibset is not
|
|
nil, returns what body part set this BACT is in. If the pbact is
|
|
the hilite BACT, pibset is set to ivNil.
|
|
***************************************************************************/
|
|
BODY *BODY::PbodyFromBact(BACT *pbact, long *pibset)
|
|
{
|
|
AssertVarMem(pbact);
|
|
AssertNilOrVarMem(pibset);
|
|
|
|
PBODY pbody;
|
|
long ibact;
|
|
|
|
pbody = (BODY *)pbact->identifier;
|
|
if (pvNil == pbody)
|
|
{
|
|
Bug("What actor is this? It has no BODY");
|
|
return pvNil;
|
|
}
|
|
AssertPo(pbody, 0);
|
|
if (pvNil != pibset)
|
|
{
|
|
*pibset = ivNil;
|
|
for (ibact = 0; ibact < pbody->_cbactPart; ibact++)
|
|
{
|
|
if (pbact == pbody->_PbactPart(ibact))
|
|
{
|
|
*pibset = pbody->_Ibset(ibact);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pbody;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns the BODY that is under the given point. Also, if pibset is not
|
|
nil, returns what body part set this point is in.
|
|
***************************************************************************/
|
|
BODY *BODY::PbodyClicked(long xp, long yp, PBWLD pbwld, long *pibset)
|
|
{
|
|
AssertNilOrVarMem(pibset);
|
|
AssertPo(pbwld, 0);
|
|
|
|
PBODY pbody;
|
|
long ibact;
|
|
|
|
_pbodyClosestClicked = pvNil;
|
|
_dzpClosestClicked = BR_SCALAR_MAX;
|
|
|
|
pbwld->IterateActorsInPt(BODY::_FFilter, pvNil, xp, yp);
|
|
|
|
pbody = _pbodyClosestClicked;
|
|
if (pvNil == _pbodyClosestClicked)
|
|
{
|
|
return pvNil;
|
|
}
|
|
AssertPo(_pbodyClosestClicked, 0);
|
|
if (pvNil != pibset)
|
|
{
|
|
*pibset = ivNil;
|
|
for (ibact = 0; ibact < _pbodyClosestClicked->_cbactPart; ibact++)
|
|
{
|
|
if (_pbactClosestClicked == _pbodyClosestClicked->_PbactPart(ibact))
|
|
{
|
|
*pibset = _pbodyClosestClicked->_Ibset(ibact);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return _pbodyClosestClicked;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Filter callback proc for PbodyClicked(). Saves pbody if it's the
|
|
closest one hit so far and visible
|
|
***************************************************************************/
|
|
int BODY::_FFilter(BACT *pbact, PBMDL pbmdl, PBMTL pbmtl,
|
|
BVEC3 *pbvec3RayPos, BVEC3 *pbvec3RayDir, BRS dzpNear, BRS dzpFar,
|
|
void *pv)
|
|
{
|
|
AssertVarMem(pbact);
|
|
AssertVarMem(pbvec3RayPos);
|
|
AssertVarMem(pbvec3RayDir);
|
|
|
|
PBODY pbody;
|
|
|
|
pbody = BODY::PbodyFromBact(pbact);
|
|
|
|
if ((dzpNear < _dzpClosestClicked) &&
|
|
(pbody != pvNil) &&
|
|
(pbody->FIsInView()))
|
|
{
|
|
_pbodyClosestClicked = pbody;
|
|
_dzpClosestClicked = dzpNear;
|
|
_pbactClosestClicked = pbact;
|
|
}
|
|
|
|
return fFalse; // fFalse means keep searching
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destroy the BODY's body parts, including their attached materials and
|
|
models.
|
|
***************************************************************************/
|
|
void BODY::_DestroyShape(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
long ibset;
|
|
long ibact;
|
|
BACT *pbact;
|
|
MODL *pmodl;
|
|
|
|
// Must hide body before destroying it
|
|
if (_cactHidden == 0)
|
|
Hide();
|
|
|
|
if (pvNil != _prgbact)
|
|
{
|
|
for (ibact = 0; ibact < _cbactPart; ibact++)
|
|
{
|
|
pbact = _PbactPart(ibact);
|
|
if (pvNil != pbact->model)
|
|
{
|
|
pmodl = MODL::PmodlFromBmdl(pbact->model);
|
|
AssertPo(pmodl, 0);
|
|
ReleasePpo(&pmodl);
|
|
pbact->model = pvNil;
|
|
}
|
|
}
|
|
if (pvNil != _prgpcmtl)
|
|
{
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
_RemoveMaterial(ibset);
|
|
}
|
|
FreePpv((void **)&_prgbact);
|
|
}
|
|
FreePpv((void **)&_prgpcmtl);
|
|
ReleasePpo(&_pglibset);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Free the BODY and all attached MODLs, MTRLs, and CMTLs.
|
|
***************************************************************************/
|
|
BODY::~BODY(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
_DestroyShape();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a duplicate of this BODY. The duplicate will be hidden
|
|
(_cactHidden == 1) regardless of the state of this BODY.
|
|
***************************************************************************/
|
|
PBODY BODY::PbodyDup(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
PBODY pbodyDup;
|
|
long ibact;
|
|
long ibset;
|
|
PBACT pbact;
|
|
long bv; // delta in bytes from _prgbact to _pbody->_prgbact
|
|
bool fMtrl;
|
|
PMTRL pmtrl;
|
|
PCMTL pcmtl;
|
|
bool fVis;
|
|
|
|
fVis = (_cactHidden == 0);
|
|
if (fVis)
|
|
Hide(); // temporarily hide this BODY
|
|
|
|
pbodyDup = NewObj BODY;
|
|
if (pvNil == pbodyDup)
|
|
goto LFail;
|
|
|
|
pbodyDup->_cactHidden = 1;
|
|
pbodyDup->_cbset = _cbset;
|
|
pbodyDup->_cbactPart = _cbactPart;
|
|
pbodyDup->_pbwld = _pbwld;
|
|
pbodyDup->_fFound = _fFound;
|
|
pbodyDup->_ibset = _ibset;
|
|
|
|
if (!FAllocPv((void **)&pbodyDup->_prgbact, LwMul(_Cbact(), size(BACT)),
|
|
fmemClear, mprNormal))
|
|
{
|
|
goto LFail;
|
|
}
|
|
CopyPb(_prgbact, pbodyDup->_prgbact, LwMul(_Cbact(), size(BACT)));
|
|
// need to update BACT parent, child, next, prev pointers
|
|
bv = BvSubPvs(pbodyDup->_prgbact, _prgbact);
|
|
for (ibact = 0; ibact < _Cbact(); ibact++)
|
|
{
|
|
pbact = &pbodyDup->_prgbact[ibact];
|
|
pbact->identifier = (schar *)pbodyDup;
|
|
if (ibact == 0)
|
|
{
|
|
pbact->parent = pvNil;
|
|
if (pvNil != pbact->children)
|
|
pbact->children = (PBACT)PvAddBv(pbact->children, bv);
|
|
pbact->next = pvNil;
|
|
pbact->prev = pvNil;
|
|
}
|
|
else
|
|
{
|
|
if (pvNil != pbact->parent)
|
|
pbact->parent = (PBACT)PvAddBv(pbact->parent, bv);
|
|
if (pvNil != pbact->children)
|
|
pbact->children = (PBACT)PvAddBv(pbact->children, bv);
|
|
if (pvNil != pbact->next)
|
|
pbact->next = (PBACT)PvAddBv(pbact->next, bv);
|
|
if (pvNil != pbact->prev)
|
|
pbact->prev = (PBACT *)PvAddBv(pbact->prev, bv);
|
|
}
|
|
}
|
|
for (ibact = 0; ibact < pbodyDup->_cbactPart; ibact++)
|
|
{
|
|
pbact = _PbactPart(ibact);
|
|
if (pvNil != pbact->model)
|
|
MODL::PmodlFromBmdl(pbact->model)->AddRef();
|
|
}
|
|
|
|
pbodyDup->_pglibset = _pglibset->PglDup();
|
|
if (pvNil == pbodyDup->_pglibset)
|
|
goto LFail;
|
|
|
|
if (!FAllocPv((void **)&pbodyDup->_prgpcmtl, LwMul(_cbset, size(PCMTL)),
|
|
fmemClear, mprNormal))
|
|
{
|
|
goto LFail;
|
|
}
|
|
CopyPb(_prgpcmtl, pbodyDup->_prgpcmtl, LwMul(_cbset, size(PCMTL)));
|
|
|
|
pbodyDup->_rcBounds = _rcBounds;
|
|
pbodyDup->_rcBoundsLastVis = _rcBoundsLastVis;
|
|
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
{
|
|
pbodyDup->GetPartSetMaterial(ibset, &fMtrl, &pmtrl, &pcmtl);
|
|
if (fMtrl)
|
|
{
|
|
// need to AddRef once per body part in this set
|
|
for (ibact = 0; ibact < pbodyDup->_cbactPart; ibact++)
|
|
{
|
|
if (ibset == _Ibset(ibact))
|
|
pmtrl->AddRef();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pcmtl->AddRef();
|
|
}
|
|
}
|
|
if (fVis)
|
|
Show(); // re-show this BODY (but not the duplicate)
|
|
|
|
AssertPo(pbodyDup, 0);
|
|
return pbodyDup;
|
|
LFail:
|
|
if (fVis)
|
|
Show();
|
|
ReleasePpo(&pbodyDup);
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Replace this BODY with pbodyDup. Preserve this BODY's hidden-ness.
|
|
***************************************************************************/
|
|
void BODY::Restore(PBODY pbodyDup)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(pbodyDup, 0);
|
|
Assert(pbodyDup->_cactHidden == 1, "dup hidden count must be 1");
|
|
|
|
long cactHidden = _cactHidden;
|
|
long ibact;
|
|
|
|
SwapVars(this, pbodyDup);
|
|
for (ibact = 0; ibact < _Cbact(); ibact++)
|
|
_prgbact[ibact].identifier = (schar *)this;
|
|
if (cactHidden == 0)
|
|
Show();
|
|
_cactHidden = cactHidden;
|
|
|
|
AssertThis(fobjAssertFull);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make this BODY visible, if it was invisible. Keeps a refcount.
|
|
***************************************************************************/
|
|
void BODY::Show(void)
|
|
{
|
|
AssertThis(0);
|
|
Assert(_cactHidden > 0, "object is already visible!");
|
|
if (--_cactHidden == 0)
|
|
{
|
|
AssertPo(_pbwld, 0);
|
|
_pbwld->AddActor(_PbactRoot());
|
|
_pbwld->SetBeginRenderCallback(_PrepareToRender);
|
|
_pbwld->SetActorRenderedCallback(_BactRendered);
|
|
_pbwld->SetGetRcCallback(_GetRc);
|
|
_pbwld->MarkDirty(); // need to render
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make this BODY invisible, if it was visible. Keeps a refcount.
|
|
***************************************************************************/
|
|
void BODY::Hide(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
RC rc;
|
|
|
|
if (_cactHidden++ == 0)
|
|
{
|
|
_rcBounds.Zero();
|
|
BrActorRemove(_PbactRoot());
|
|
AssertPo(_pbwld, 0);
|
|
_pbwld->MarkDirty(); // need to render
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Sets the hilite color to use.
|
|
***************************************************************************/
|
|
void BODY::SetHiliteColor(long iclr)
|
|
{
|
|
if (_pbmtlHilite != pvNil)
|
|
{
|
|
_pbmtlHilite->index_base = (UCHAR)iclr;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Hilites the BODY
|
|
***************************************************************************/
|
|
void BODY::Hilite(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_PbactHilite()->type = BR_ACTOR_MODEL;
|
|
if (_cactHidden == 0)
|
|
{
|
|
AssertPo(_pbwld, 0);
|
|
_pbwld->MarkDirty(); // need to render
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Unhilites the BODY
|
|
***************************************************************************/
|
|
void BODY::Unhilite(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_PbactHilite()->type = BR_ACTOR_NONE;
|
|
if (_cactHidden == 0)
|
|
{
|
|
AssertPo(_pbwld, 0);
|
|
_pbwld->MarkDirty(); // need to render
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Position the body at (xr, yr, zr) in worldspace oriented by pbmat34
|
|
***************************************************************************/
|
|
void BODY::LocateOrient(BRS xr, BRS yr, BRS zr, BMAT34 *pbmat34)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pbmat34);
|
|
|
|
_PbactRoot()->t.t.mat = *pbmat34;
|
|
BrMatrix34PostTranslate(&_PbactRoot()->t.t.mat, xr, yr, zr);
|
|
|
|
if (_cactHidden == 0)
|
|
{
|
|
AssertPo(_pbwld, 0);
|
|
_pbwld->MarkDirty(); // need to render
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the ibact'th body part to use model pmodl
|
|
***************************************************************************/
|
|
void BODY::SetPartModel(long ibact, MODL *pmodl)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ibact, 0, _cbactPart);
|
|
AssertPo(pmodl, 0);
|
|
|
|
BACT *pbact = _PbactPart(ibact);
|
|
PMODL pmodlOld;
|
|
|
|
if (pvNil != pbact->model) // Release old MODL, unless it's pmodl
|
|
{
|
|
pmodlOld = MODL::PmodlFromBmdl(pbact->model);
|
|
AssertPo(pmodlOld, 0);
|
|
if (pmodl == pmodlOld)
|
|
return; // We're already using that MODL, so do nothing
|
|
ReleasePpo(&pmodlOld);
|
|
}
|
|
|
|
pbact->model = pmodl->Pbmdl();
|
|
Assert(MODL::PmodlFromBmdl(pbact->model) == pmodl, "MODL problem");
|
|
pmodl->AddRef();
|
|
if (_cactHidden == 0)
|
|
{
|
|
AssertPo(_pbwld, 0);
|
|
_pbwld->MarkDirty(); // need to render
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the ibact'th body part to use matrix pbmat34
|
|
***************************************************************************/
|
|
void BODY::SetPartMatrix(long ibact, BMAT34 *pbmat34)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ibact, 0, _cbactPart);
|
|
AssertVarMem(pbmat34);
|
|
|
|
_PbactPart(ibact)->t.t.mat = *pbmat34;
|
|
if (_cactHidden == 0)
|
|
{
|
|
AssertPo(_pbwld, 0);
|
|
_pbwld->MarkDirty(); // need to render
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Remove old MTRL or CMTL from ibset. This is nontrivial because there
|
|
could either be a CMTL attached to the bset (in which case we just free
|
|
the CMTL) or a bunch of MTRLs (in which case we free each MTRL).
|
|
Actually, in the latter case, it would be a bunch of copies of the same
|
|
MTRL, but we keep one reference per body part in the set.
|
|
***************************************************************************/
|
|
void BODY::_RemoveMaterial(long ibset)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ibset, 0, _cbset);
|
|
|
|
PCMTL pcmtlOld;
|
|
BACT *pbact;
|
|
long ibact;
|
|
bool fCmtl = fFalse;
|
|
|
|
pcmtlOld = _prgpcmtl[ibset];
|
|
if (pvNil != pcmtlOld) // there was a CMTL on this bset
|
|
{
|
|
fCmtl = fTrue;
|
|
AssertPo(pcmtlOld, 0);
|
|
ReleasePpo(&pcmtlOld); // free all old MTRLs
|
|
_prgpcmtl[ibset] = pvNil;
|
|
}
|
|
// for each body part, if this part is in the set ibset, free the MTRL
|
|
for (ibact = 0; ibact < _cbactPart; ibact++)
|
|
{
|
|
if (ibset == _Ibset(ibact))
|
|
{
|
|
pbact = _PbactPart(ibact);
|
|
if (pbact->material != pvNil) // free old MTRL
|
|
{
|
|
if (!fCmtl)
|
|
MTRL::PmtrlFromBmtl(pbact->material)->Release();
|
|
pbact->material = pvNil;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the ibset'th body part set to use material pmtrl
|
|
***************************************************************************/
|
|
void BODY::SetPartSetMtrl(long ibset, MTRL *pmtrl)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ibset, 0, _cbset);
|
|
AssertPo(pmtrl, 0);
|
|
|
|
BACT *pbact;
|
|
long ibact;
|
|
|
|
_RemoveMaterial(ibset); // remove existing MTRL/CMTL, if any
|
|
|
|
// for each body part, if this part is in the set ibset, set the MTRL
|
|
for (ibact = 0; ibact < _cbactPart; ibact++)
|
|
{
|
|
if (ibset == _Ibset(ibact))
|
|
{
|
|
pbact = _PbactPart(ibact);
|
|
pbact->material = pmtrl->Pbmtl();
|
|
pmtrl->AddRef();
|
|
}
|
|
}
|
|
if (_cactHidden == 0)
|
|
{
|
|
AssertPo(_pbwld, 0);
|
|
_pbwld->MarkDirty(); // need to render
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Apply the given CMTL to the appropriate body part set (the CMTL knows
|
|
which body part set to apply to).
|
|
***************************************************************************/
|
|
void BODY::SetPartSetCmtl(CMTL *pcmtl)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pcmtl, 0);
|
|
|
|
BACT *pbact;
|
|
long ibact;
|
|
long ibmtl = 0;
|
|
long ibset = pcmtl->Ibset();
|
|
PMODL pmodl;
|
|
|
|
pcmtl->AddRef();
|
|
_RemoveMaterial(ibset); // remove existing MTRL/CMTL
|
|
_prgpcmtl[ibset] = pcmtl;
|
|
|
|
// for each body part, if this part is in the set ibset, set the MTRL
|
|
for (ibact = 0; ibact < _cbactPart; ibact++)
|
|
{
|
|
if (ibset == _Ibset(ibact))
|
|
{
|
|
pbact = _PbactPart(ibact);
|
|
// Handle model changes for accessories
|
|
pmodl = pcmtl->Pmodl(ibmtl);
|
|
if (pvNil != pmodl)
|
|
SetPartModel(ibact, pmodl);
|
|
pbact->material = pcmtl->Pbmtl(ibmtl);
|
|
ibmtl++;
|
|
}
|
|
}
|
|
Assert(ibmtl == pcmtl->Cbprt(), "didn't use all custom materials!");
|
|
if (_cactHidden == 0)
|
|
{
|
|
AssertPo(_pbwld, 0);
|
|
_pbwld->MarkDirty(); // need to render
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Determines the current CMTL or MTRL applied to the given ibset.
|
|
If a CMTL is attached, *pfMtrl is fFalse and *ppcmtl holds the
|
|
PCMTL. If a MTRL is attached, *pfMtrl is fTrue and *ppmtrl holds
|
|
the PMTRL.
|
|
***************************************************************************/
|
|
void BODY::GetPartSetMaterial(long ibset, bool *pfMtrl, MTRL **ppmtrl,
|
|
CMTL **ppcmtl)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ibset, 0, _cbset);
|
|
AssertVarMem(pfMtrl);
|
|
AssertVarMem(ppmtrl);
|
|
AssertVarMem(ppcmtl);
|
|
|
|
BACT *pbact;
|
|
long ibact;
|
|
|
|
*ppcmtl = _prgpcmtl[ibset];
|
|
if (pvNil != *ppcmtl) // there is a CMTL on this bset
|
|
{
|
|
*pfMtrl = fFalse;
|
|
AssertPo(*ppcmtl, 0);
|
|
TrashVar(ppmtrl);
|
|
}
|
|
else
|
|
{
|
|
*pfMtrl = fTrue;
|
|
TrashVar(ppcmtl);
|
|
// Find any body part of ibset...they'll all have the same MTRL
|
|
for (ibact = 0; ibact < _cbactPart; ibact++)
|
|
{
|
|
if (ibset == _Ibset(ibact))
|
|
{
|
|
pbact = _PbactPart(ibact);
|
|
Assert(pvNil != pbact->material, "Why does this body part "
|
|
"set have neither MTRL nor CMTL attached?");
|
|
*ppmtrl = MTRL::PmtrlFromBmtl(pbact->material);
|
|
AssertPo(*ppmtrl, 0);
|
|
return;
|
|
}
|
|
}
|
|
Assert(0, "why are we here?");
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Filter callback proc for FPtInActor(). Stops when the BODY is hit.
|
|
***************************************************************************/
|
|
int BODY::_FFilterSearch(BACT *pbact, PBMDL pbmdl, PBMTL pbmtl,
|
|
BVEC3 *ray_pos, BVEC3 *ray_dir, BRS dzpNear, BRS dzpFar, void *pvArg)
|
|
{
|
|
AssertVarMem(pbact);
|
|
AssertVarMem(ray_pos);
|
|
AssertVarMem(ray_dir);
|
|
|
|
PBODY pbody = (PBODY)pvArg;
|
|
PBODY pbodyFound;
|
|
long ibset;
|
|
|
|
AssertPo(pbody, 0);
|
|
|
|
pbodyFound = BODY::PbodyFromBact(pbact, &ibset);
|
|
if (pbodyFound == pvNil)
|
|
{
|
|
Bug("What actor is this? It has no BODY");
|
|
return fFalse;
|
|
}
|
|
|
|
AssertPo(pbodyFound, 0);
|
|
|
|
if (pbody != pbodyFound)
|
|
return fFalse; // keep searching
|
|
|
|
pbody->_fFound = fTrue;
|
|
if (pbact == pbody->_PbactHilite())
|
|
return fFalse; // keep searching
|
|
pbody->_ibset = ibset;
|
|
return fTrue; // stop searching
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns fTrue if this body is under (xp, yp)
|
|
***************************************************************************/
|
|
bool BODY::FPtInBody(long xp, long yp, long *pibset)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_fFound = fFalse;
|
|
_ibset = ivNil;
|
|
_pbwld->IterateActorsInPt(&BODY::_FFilterSearch, (void *)this, xp, yp);
|
|
*pibset = _ibset;
|
|
return _fFound;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
BWLD is about to render the world, so clear out this BODY's _rcBounds
|
|
(but save the last good bounds in _rcBoundsLastVis). Also size the
|
|
bounding box correctly.
|
|
***************************************************************************/
|
|
void BODY::_PrepareToRender(PBACT pbact)
|
|
{
|
|
AssertVarMem(pbact);
|
|
PBODY pbody;
|
|
RC rc;
|
|
BCB bcb;
|
|
|
|
pbody = PbodyFromBact(pbact);
|
|
AssertPo(pbody, 0);
|
|
|
|
if (pbody->FIsInView())
|
|
{
|
|
pbody->_rcBoundsLastVis = pbody->_rcBounds;
|
|
pbody->_rcBounds.Zero();
|
|
}
|
|
|
|
// Prepare bounding box, if BODY is highlighted
|
|
if (pbody->_PbactHilite()->type == BR_ACTOR_MODEL)
|
|
{
|
|
// Need to temporarily change type to 'none' so that the bounding
|
|
// box isn't counted when calculating size of actor
|
|
pbody->GetBcbBounds(&bcb);
|
|
Assert(size(BRB) == size(BCB), "should be same structure");
|
|
BrBoundsToMatrix34(&pbody->_PbactHilite()->t.t.mat, (BRB *)&bcb);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the bounds of the BODY contaning PBACT
|
|
***************************************************************************/
|
|
void BODY::_GetRc(PBACT pbact, RC *prc)
|
|
{
|
|
AssertVarMem(pbact);
|
|
AssertVarMem(prc);
|
|
|
|
PBODY pbody;
|
|
|
|
pbody = PbodyFromBact(pbact);
|
|
AssertPo(pbody, 0);
|
|
|
|
if (pvNil != pbody)
|
|
*prc = pbody->_rcBounds;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Compute the world-space bounding box of the BODY. The code temporarily
|
|
changes the hilite BACT's type to BR_ACTOR_NONE so that BrActorToBounds
|
|
doesn't include the size of the bounding box when computing the size of
|
|
the actor.
|
|
***************************************************************************/
|
|
void BODY::GetBcbBounds(BCB *pbcb, bool fWorld)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pbcb);
|
|
|
|
BRB brb;
|
|
byte type = _PbactHilite()->type;
|
|
long ibv3;
|
|
br_vector3 bv3;
|
|
|
|
_PbactHilite()->type = BR_ACTOR_NONE;
|
|
Assert(size(BRB) == size(BCB), "should be same structure");
|
|
BrActorToBounds(&brb, _PbactRoot());
|
|
_PbactHilite()->type = type;
|
|
*(BRB *)pbcb = brb;
|
|
|
|
if (fWorld)
|
|
{
|
|
br_vector3 rgbv3[8];
|
|
BMAT34 bmat34;
|
|
|
|
rgbv3[0] = brb.min;
|
|
rgbv3[1] = brb.min;
|
|
rgbv3[1].v[0] = brb.max.v[0];
|
|
rgbv3[2] = brb.min;
|
|
rgbv3[2].v[1] = brb.max.v[1];
|
|
rgbv3[3] = brb.min;
|
|
rgbv3[3].v[2] = brb.max.v[2];
|
|
rgbv3[4] = brb.max;
|
|
rgbv3[5] = brb.max;
|
|
rgbv3[5].v[0] = brb.min.v[0];
|
|
rgbv3[6] = brb.max;
|
|
rgbv3[6].v[1] = brb.min.v[1];
|
|
rgbv3[7] = brb.max;
|
|
rgbv3[7].v[2] = brb.min.v[2];
|
|
|
|
bmat34 = _PbactRoot()->t.t.mat;
|
|
|
|
for (ibv3 = 0; ibv3 < 8; ibv3++)
|
|
{
|
|
bv3.v[0] = BR_MAC4(rgbv3[ibv3].v[0], bmat34.m[0][0],
|
|
rgbv3[ibv3].v[1], bmat34.m[1][0], rgbv3[ibv3].v[2],
|
|
bmat34.m[2][0], BR_SCALAR(1.0), bmat34.m[3][0]);
|
|
bv3.v[1] = BR_MAC4(rgbv3[ibv3].v[0], bmat34.m[0][1],
|
|
rgbv3[ibv3].v[1], bmat34.m[1][1], rgbv3[ibv3].v[2],
|
|
bmat34.m[2][1], BR_SCALAR(1.0), bmat34.m[3][1]);
|
|
bv3.v[2] = BR_MAC4(rgbv3[ibv3].v[0], bmat34.m[0][2],
|
|
rgbv3[ibv3].v[1], bmat34.m[1][2], rgbv3[ibv3].v[2],
|
|
bmat34.m[2][2], BR_SCALAR(1.0), bmat34.m[3][2]);
|
|
rgbv3[ibv3].v[0] = bv3.v[0];
|
|
rgbv3[ibv3].v[1] = bv3.v[1];
|
|
rgbv3[ibv3].v[2] = bv3.v[2];
|
|
}
|
|
pbcb->xrMin = pbcb->yrMin = pbcb->zrMin = BR_SCALAR(10000.0);
|
|
pbcb->xrMax = pbcb->yrMax = pbcb->zrMax = BR_SCALAR(-10000.0);
|
|
// Union with body's bounds
|
|
for (ibv3 = 0; ibv3 < 8; ibv3++)
|
|
{
|
|
if (rgbv3[ibv3].v[0] < pbcb->xrMin)
|
|
pbcb->xrMin = rgbv3[ibv3].v[0];
|
|
if (rgbv3[ibv3].v[1] < pbcb->yrMin)
|
|
pbcb->yrMin = rgbv3[ibv3].v[1];
|
|
if (rgbv3[ibv3].v[2] < pbcb->zrMin)
|
|
pbcb->zrMin = rgbv3[ibv3].v[2];
|
|
if (rgbv3[ibv3].v[0] > pbcb->xrMax)
|
|
pbcb->xrMax = rgbv3[ibv3].v[0];
|
|
if (rgbv3[ibv3].v[1] > pbcb->yrMax)
|
|
pbcb->yrMax = rgbv3[ibv3].v[1];
|
|
if (rgbv3[ibv3].v[2] > pbcb->zrMax)
|
|
pbcb->zrMax = rgbv3[ibv3].v[2];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
BWLD calls this function when each BACT is rendered. It unions the
|
|
BACT bounds with the BODY's _rcBounds
|
|
***************************************************************************/
|
|
void BODY::_BactRendered(PBACT pbact, RC *prc)
|
|
{
|
|
AssertVarMem(pbact);
|
|
AssertVarMem(prc);
|
|
PBODY pbody;
|
|
|
|
pbody = PbodyFromBact(pbact);
|
|
if (pvNil != pbody)
|
|
pbody->_rcBounds.Union(prc);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns whether the BODY was in view the last time it was rendered.
|
|
***************************************************************************/
|
|
bool BODY::FIsInView(void)
|
|
{
|
|
AssertThis(0);
|
|
return !_rcBounds.FEmpty();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fills in the 2D bounds of the BODY, the last time it was rendered. If
|
|
the BODY was not in view at the last render, this function fills in the
|
|
bounds of the BODY the last time it was onstage.
|
|
***************************************************************************/
|
|
void BODY::GetRcBounds(RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
|
|
if (FIsInView())
|
|
*prc = _rcBounds;
|
|
else
|
|
*prc = _rcBoundsLastVis;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fills in the 2D coordinates of the center of the BODY, the last time
|
|
it was rendered. If the BODY was not in view at the last render, this
|
|
function fills in the center of the BODY the last time it was onstage.
|
|
***************************************************************************/
|
|
void BODY::GetCenter(long *pxp, long *pyp)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pxp);
|
|
AssertVarMem(pyp);
|
|
|
|
if (FIsInView())
|
|
{
|
|
*pxp = _rcBounds.XpCenter();
|
|
*pyp = _rcBounds.YpCenter();
|
|
}
|
|
else
|
|
{
|
|
*pxp = _rcBoundsLastVis.XpCenter();
|
|
*pyp = _rcBoundsLastVis.YpCenter();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fills in the current position of the origin of this BODY.
|
|
***************************************************************************/
|
|
void BODY::GetPosition(BRS *pxr, BRS *pyr, BRS *pzr)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pxr);
|
|
AssertVarMem(pyr);
|
|
AssertVarMem(pzr);
|
|
|
|
*pxr = _PbactRoot()->t.t.mat.m[3][0];
|
|
*pyr = _PbactRoot()->t.t.mat.m[3][1];
|
|
*pzr = _PbactRoot()->t.t.mat.m[3][2];
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the BODY.
|
|
***************************************************************************/
|
|
void BODY::AssertValid(ulong grf)
|
|
{
|
|
long ibact;
|
|
long ibset;
|
|
BACT *pbact;
|
|
|
|
BODY_PAR::AssertValid(fobjAllocated);
|
|
AssertIn(_cactHidden, 0, 100); // 100 is sanity check
|
|
AssertIn(_cbset, 0, _cbactPart + 1);
|
|
AssertPvCb(_prgbact, LwMul(_Cbact(), size(BACT)));
|
|
AssertPvCb(_prgpcmtl, LwMul(_cbset, size(PCMTL)));
|
|
Assert(pvNil == _PbactRoot()->model,
|
|
"BODY root shouldn't have a model!!");
|
|
Assert(pvNil == _PbactRoot()->material,
|
|
"BODY root shouldn't have a material!!");
|
|
AssertPo(_pglibset, 0);
|
|
Assert(_pglibset->CbEntry() == size(short), "bad _pglibset");
|
|
|
|
if (grf & fobjAssertFull)
|
|
{
|
|
for (ibact = 0; ibact < _cbactPart; ibact++)
|
|
{
|
|
pbact = _PbactPart(ibact);
|
|
if (pvNil != pbact->model)
|
|
AssertPo(MODL::PmodlFromBmdl(pbact->model), 0);
|
|
if (pvNil != pbact->material)
|
|
AssertPo(MTRL::PmtrlFromBmtl(pbact->material), 0);
|
|
}
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
{
|
|
AssertNilOrPo(_prgpcmtl[ibset], 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the BODY
|
|
***************************************************************************/
|
|
void BODY::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
long ibact;
|
|
PBACT pbact;
|
|
PMODL pmodl;
|
|
long ibset;
|
|
bool fMtrl;
|
|
PMTRL pmtrl;
|
|
PCMTL pcmtl;
|
|
|
|
BODY_PAR::MarkMem();
|
|
MarkPv(_prgbact);
|
|
MarkPv(_prgpcmtl);
|
|
MarkMemObj(_pglibset);
|
|
|
|
for (ibact = 0; ibact < _cbactPart; ibact++)
|
|
{
|
|
pbact = _PbactPart(ibact);
|
|
if (pvNil != pbact->model)
|
|
{
|
|
pmodl = MODL::PmodlFromBmdl(pbact->model);
|
|
AssertPo(pmodl, 0);
|
|
MarkMemObj(pmodl);
|
|
}
|
|
}
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
{
|
|
GetPartSetMaterial(ibset, &fMtrl, &pmtrl, &pcmtl);
|
|
if (fMtrl)
|
|
MarkMemObj(pmtrl);
|
|
else
|
|
MarkMemObj(pcmtl);
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Create a blank costume -- no materials are attached yet
|
|
***************************************************************************/
|
|
COST::COST(void)
|
|
{
|
|
TrashVar(&_cbset);
|
|
_prgpo = pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destroy a costume
|
|
***************************************************************************/
|
|
COST::~COST(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
_Clear();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Release all arrays and references
|
|
***************************************************************************/
|
|
void COST::_Clear(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
long ibset;
|
|
|
|
if (pvNil != _prgpo)
|
|
{
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
ReleasePpo(&_prgpo[ibset]); // Release the PCMTL or PMTRL
|
|
FreePpv((void **)&_prgpo);
|
|
}
|
|
TrashVar(&_cbset);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a costume from a BODY
|
|
***************************************************************************/
|
|
bool COST::FGet(BODY *pbody)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pbody, 0);
|
|
|
|
long ibset;
|
|
PCMTL pcmtl;
|
|
PMTRL pmtrl;
|
|
bool fMtrl;
|
|
|
|
_Clear(); // drop previous costume, if any
|
|
|
|
if (!FAllocPv((void **)&_prgpo, LwMul(size(BASE *), pbody->Cbset()), fmemClear,
|
|
mprNormal))
|
|
{
|
|
return fFalse;
|
|
}
|
|
_cbset = pbody->Cbset();
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
{
|
|
pbody->GetPartSetMaterial(ibset, &fMtrl, &pmtrl, &pcmtl);
|
|
if (fMtrl)
|
|
_prgpo[ibset] = pmtrl;
|
|
else
|
|
_prgpo[ibset] = pcmtl;
|
|
_prgpo[ibset]->AddRef();
|
|
}
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set a costume onto a BODY. The flag fAllowDifferentShape should usually
|
|
be fFalse; it should be fTrue in the rare cases where it is appropriate
|
|
to apply a costume with one number of body part sets to a BODY with
|
|
a (possibly) different number of body part sets. In that case, the
|
|
smaller number of materials are copied. For example, when changing
|
|
the number of characters in a 3-D Text object, the code grabs the
|
|
current costume, resizes the TDT and BODY, sets the TDT's BODY to the
|
|
default costume, then restores the old costume to the BODY as much as
|
|
is appropriate.
|
|
***************************************************************************/
|
|
void COST::Set(PBODY pbody, bool fAllowDifferentShape)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pbody, 0);
|
|
|
|
long ibset;
|
|
BASE *po;
|
|
long cbset;
|
|
|
|
if (fAllowDifferentShape) // see comment in function header
|
|
{
|
|
cbset = LwMin(_cbset, pbody->Cbset());
|
|
}
|
|
else
|
|
{
|
|
Assert(_cbset == pbody->Cbset(), "different BODY shapes!");
|
|
cbset = _cbset;
|
|
}
|
|
|
|
for (ibset = 0; ibset < cbset; ibset++)
|
|
{
|
|
po = _prgpo[ibset];
|
|
AssertPo(po, 0);
|
|
if (po->FIs(kclsMTRL))
|
|
pbody->SetPartSetMtrl(ibset, (PMTRL)_prgpo[ibset]);
|
|
else
|
|
pbody->SetPartSetCmtl((PCMTL)_prgpo[ibset]);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the COST.
|
|
***************************************************************************/
|
|
void COST::AssertValid(ulong grf)
|
|
{
|
|
long ibset;
|
|
BASE *po;
|
|
|
|
if (pvNil != _prgpo)
|
|
{
|
|
AssertPvCb(_prgpo, LwMul(size(BASE *), _cbset));
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
{
|
|
po = _prgpo[ibset];
|
|
if (po->FIs(kclsMTRL))
|
|
AssertPo((PMTRL)po, 0);
|
|
else
|
|
AssertPo((PCMTL)po, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the COST
|
|
***************************************************************************/
|
|
void COST::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
long ibset;
|
|
|
|
if (pvNil != _prgpo)
|
|
{
|
|
MarkPv(_prgpo);
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
MarkMemObj(_prgpo[ibset]);
|
|
}
|
|
}
|
|
|
|
#endif DEBUG
|