Microsoft-3D-Movie-Maker/SRC/ENGINE/MODL.CPP

465 lines
12 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
modl.cpp: Model class
Primary Author: ******
Review Status: REVIEWED - any changes to this file must be reviewed!
***************************************************************************/
#include "soc.h"
ASSERTNAME
RTCLASS(MODL)
/***************************************************************************
Create a new PMODL based on some vertices and faces.
***************************************************************************/
PMODL MODL::PmodlNew(long cbrv, BRV *prgbrv, long cbrf, BRF *prgbrf)
{
AssertIn(cbrv, 0, ksuMax); // ushort in br_model
AssertPvCb(prgbrv, LwMul(cbrv, size(BRV)));
AssertIn(cbrf, 0, ksuMax); // ushort in br_model
AssertPvCb(prgbrf, LwMul(cbrf, size(BRF)));
PMODL pmodl;
char szIdentifier[size(PMODL) + 1];
pmodl = NewObj MODL;
if (pvNil == pmodl)
goto LFail;
ClearPb(szIdentifier, size(PMODL) + 1);
pmodl->_pbmdl = BrModelAllocate(szIdentifier, cbrv, cbrf);
if (pvNil == pmodl->_pbmdl)
goto LFail;
CopyPb(&pmodl, pmodl->_pbmdl->identifier, size(PMODL));
CopyPb(prgbrv, pmodl->_pbmdl->vertices, LwMul(cbrv, size(BRV)));
CopyPb(prgbrf, pmodl->_pbmdl->faces, LwMul(cbrf, size(BRF)));
BrModelAdd(pmodl->_pbmdl);
AssertPo(pmodl, 0);
return pmodl;
LFail:
ReleasePpo(&pmodl);
return pvNil;
}
/***************************************************************************
A PFNRPO to read a MODL from a file
***************************************************************************/
bool MODL::FReadModl(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
PBACO *ppbaco, long *pcb)
{
AssertPo(pcrf, 0);
AssertPo(pblck, 0);
AssertNilOrVarMem(ppbaco);
AssertVarMem(pcb);
MODL *pmodl;
*pcb = pblck->Cb(fTrue);
if (pvNil == ppbaco)
return fTrue;
if (!pblck->FUnpackData())
goto LFail;
*pcb = pblck->Cb();
pmodl = NewObj MODL;
if (pvNil == pmodl || !pmodl->_FInit(pblck))
{
ReleasePpo(&pmodl);
LFail:
TrashVar(ppbaco);
TrashVar(pcb);
return pvNil;
}
AssertPo(pmodl, 0);
*ppbaco = pmodl;
return fTrue;
}
/***************************************************************************
Reads a MODL from a BLCK
***************************************************************************/
bool MODL::_FInit(PBLCK pblck)
{
AssertBaseThis(0);
AssertPo(pblck, 0);
MODLF modlf;
long cbrgbrv;
long cbrgbrf;
long ibrv;
BRV *pbrv;
long ibrf;
BRF *pbrf;
MODL *pmodlThis = this;
char szIdentifier[size(PMODL) + 1];
ClearPb(szIdentifier, size(PMODL) + 1);
if (!pblck->FUnpackData())
return fFalse;
if (pblck->Cb() < size(MODLF))
return fFalse;
if (!pblck->FReadRgb(&modlf, size(MODLF), 0))
return fFalse;
if (kboOther == modlf.bo)
SwapBytesBom(&modlf, kbomModlf);
Assert(kboCur == modlf.bo, "bad MODL!");
// Allocate space for the BMDL, array of vertices, and array of faces
cbrgbrv = LwMul(modlf.cver, size(BRV));
cbrgbrf = LwMul(modlf.cfac, size(BRF));
if (modlf.rRadius == rZero)
{
// unprepared model. Gotta prepare it.
_pbmdl = BrModelAllocate(szIdentifier, modlf.cver, modlf.cfac);
if (pvNil == _pbmdl)
return fFalse;
CopyPb(&pmodlThis, _pbmdl->identifier, size(PMODL));
if (!pblck->FReadRgb(_pbmdl->vertices, cbrgbrv, size(MODLF)))
return fFalse;
if (!pblck->FReadRgb(_pbmdl->faces, cbrgbrf, size(MODLF) + cbrgbrv))
return fFalse;
BrModelAdd(_pbmdl);
// REVIEW *****: uncomment and expand the following code to prelight models
// BVEC3 bvec3;
// if (!_FPrelight(1, &bvec3))
// return fFalse;
}
else
{
// pre-prepared model.
_pbmdl = BrModelAllocate(szIdentifier, 0, 0);
if (pvNil == _pbmdl)
return fFalse;
CopyPb(&pmodlThis, _pbmdl->identifier, size(PMODL));
_pbmdl->prepared_vertices = (BRV *)BrResAllocate(_pbmdl,
LwMul(modlf.cver, size(BRV)), BR_MEMORY_PREPARED_VERTICES);
if (pvNil == _pbmdl->prepared_vertices)
return fFalse;
_pbmdl->prepared_faces = (BRF *)BrResAllocate(_pbmdl,
LwMul(modlf.cfac, size(BRF)), BR_MEMORY_PREPARED_FACES);
if (pvNil == _pbmdl->prepared_faces)
return fFalse;
if (!pblck->FReadRgb(_pbmdl->prepared_vertices, cbrgbrv,
size(MODLF)))
{
return fFalse;
}
if (kboOther == modlf.bo)
{
for (ibrv = 0, pbrv = _pbmdl->prepared_vertices;
ibrv < modlf.cver; ibrv++, pbrv++)
{
SwapBytesBom(pbrv, kbomBrv);
}
}
if (!pblck->FReadRgb(_pbmdl->prepared_faces, cbrgbrf,
size(MODLF) + cbrgbrv))
{
return fFalse;
}
if (kboOther == modlf.bo)
{
for (ibrf = 0, pbrf = _pbmdl->prepared_faces; ibrf < modlf.cfac;
ibrf++, pbrf++)
{
SwapBytesBom(pbrf, kbomBrf);
}
}
_pbmdl->flags = BR_MODF_PREPREPARED;
_pbmdl->nprepared_vertices = (ushort)modlf.cver;
_pbmdl->nprepared_faces = (ushort)modlf.cfac;
// The following code assumes that there is no material data
// in the models. If there is material data, the code will have
// to change to read vertex groups and face groups from file.
_pbmdl->nvertex_groups = 1;
_pbmdl->nface_groups = 1;
_pbmdl->vertex_groups = (br_vertex_group *)BrResAllocate(_pbmdl,
size(br_vertex_group), BR_MEMORY_GROUPS);
if (pvNil == _pbmdl->vertex_groups)
return fFalse;
_pbmdl->vertex_groups->material = pvNil;
_pbmdl->vertex_groups->vertices = _pbmdl->prepared_vertices;
_pbmdl->vertex_groups->nvertices = _pbmdl->nprepared_vertices;
_pbmdl->face_groups = (br_face_group *)BrResAllocate(_pbmdl,
size(br_face_group), BR_MEMORY_GROUPS);
if (pvNil == _pbmdl->face_groups)
return fFalse;
_pbmdl->face_groups->material = pvNil;
_pbmdl->face_groups->faces = _pbmdl->prepared_faces;
_pbmdl->face_groups->nfaces = _pbmdl->nprepared_faces;
_pbmdl->radius = modlf.rRadius;
_pbmdl->bounds = modlf.brb;
_pbmdl->pivot = modlf.bvec3Pivot;
BrModelAdd(_pbmdl);
}
return fTrue;
}
/***************************************************************************
Reads a BRender model from a .DAT file
***************************************************************************/
PMODL MODL::PmodlReadFromDat(FNI *pfni)
{
AssertPo(pfni, ffniFile);
STN stn;
PMODL pmodl;
pmodl = NewObj MODL;
if (pvNil == pmodl)
goto LFail;
pfni->GetStnPath(&stn);
pmodl->_pbmdl = BrModelLoad(stn.Psz());
if (pvNil == pmodl->_pbmdl)
goto LFail;
pmodl->_pbmdl->flags |= BR_MODF_KEEP_ORIGINAL;
BrModelPrepare(pmodl->_pbmdl, BR_MPREP_ALL);
Assert(CchSz(pmodl->_pbmdl->identifier) >= size(void *),
"no room for pmodl ptr");
CopyPb(&pmodl, pmodl->_pbmdl->identifier, size(void *));
AssertPo(pmodl, 0);
return pmodl;
LFail:
ReleasePpo(&pmodl);
return pvNil;
}
/***************************************************************************
Returns a pointer to the MODL that owns this BMDL
***************************************************************************/
PMODL MODL::PmodlFromBmdl(PBMDL pbmdl)
{
AssertVarMem(pbmdl);
PMODL pmodl = (PMODL)*(long *)pbmdl->identifier;
AssertPo(pmodl, 0);
return pmodl;
}
/***************************************************************************
Destructor
***************************************************************************/
MODL::~MODL(void)
{
AssertBaseThis(0);
if (pvNil != _pbmdl)
{
BrModelRemove(_pbmdl);
BrModelFree(_pbmdl);
}
}
/***************************************************************************
Writes a MODL to a chunk
***************************************************************************/
bool MODL::FWrite(PCFL pcfl, CTG ctg, CNO cno)
{
AssertThis(0);
AssertPo(pcfl, 0);
long cb;
long cbrgbrv;
long cbrgbrf;
MODLF *pmodlf;
cbrgbrv = LwMul(_pbmdl->nprepared_vertices, size(br_vertex));
cbrgbrf = LwMul(_pbmdl->nprepared_faces, size(br_face));
cb = size(MODLF) + cbrgbrv + cbrgbrf;
if (!FAllocPv((void **)&pmodlf, cb, fmemClear, mprNormal))
goto LFail;
pmodlf->bo = kboCur;
pmodlf->osk = koskCur;
pmodlf->cver = _pbmdl->nprepared_vertices;
pmodlf->cfac = _pbmdl->nprepared_faces;
pmodlf->rRadius = _pbmdl->radius;
pmodlf->brb = _pbmdl->bounds;
pmodlf->bvec3Pivot = _pbmdl->pivot;
CopyPb(_pbmdl->prepared_vertices, PvAddBv(pmodlf, size(MODLF)), cbrgbrv);
CopyPb(_pbmdl->prepared_faces, PvAddBv(pmodlf, size(MODLF) + cbrgbrv),
cbrgbrf);
if (!pcfl->FPutPv(pmodlf, cb, ctg, cno))
goto LFail;
FreePpv((void **)&pmodlf);
return fTrue;
LFail:
Warn("model save failed.");
FreePpv((void **)&pmodlf);
return fFalse;
}
/***************************************************************************
Adjust glyph for a TDF. It is centered in X and Z, with Y at the
baseline, and we do some voodoo to get "kerning" (really "variable
interletter spacing") to work.
***************************************************************************/
void MODL::AdjustTdfCharacter(void)
{
AssertThis(0);
BRS dxrModl = Dxr();
BRS dxrSpacing = _pbmdl->bounds.min.v[0];
BRS dxr = BrsHalf(dxrModl) + dxrSpacing;
BRS dzrModl = Dzr();
BRS dzr = BrsHalf(dzrModl);
long cbrv = _pbmdl->nprepared_vertices;
long ibrv;
for (ibrv = 0; ibrv < cbrv; ibrv++)
{
_pbmdl->prepared_vertices[ibrv].p.v[0] -= dxr;
_pbmdl->prepared_vertices[ibrv].p.v[2] -= dzr;
}
_pbmdl->bounds.min.v[0] -= dxr;
_pbmdl->bounds.max.v[0] -= dxr;
_pbmdl->pivot.v[0] -= dxr;
_pbmdl->bounds.min.v[0] -= dxrSpacing;
if (dxrSpacing < BR_SCALAR(-0.01))
dxrSpacing = BR_SCALAR(0.5);
_pbmdl->bounds.max.v[0] += dxrSpacing;
_pbmdl->bounds.min.v[2] -= dzr;
_pbmdl->bounds.max.v[2] -= dzr;
_pbmdl->pivot.v[2] -= dzr;
}
/***************************************************************************
Prelight a model
REVIEW *****: make this code more general
***************************************************************************/
bool MODL::_FPrelight(long cblit, BVEC3 *prgbvec3Light)
{
AssertIn(cblit, 1, 10);
AssertPvCb(prgbvec3Light, LwMul(cblit, size(BVEC3)));
AssertBaseThis(0);
PBACT pbactWorld;
PBACT pbactCamera;
PBPMP pbpmpRGB;
PBPMP pbpmpZ;
PBACT pbactLight;
BLIT blit;
PBMTL pbmtl;
long iblit;
BCAM bcam =
{
"bah",
BR_CAMERA_PERSPECTIVE,
BR_ANGLE_DEG(60.0), // REVIEW *****
BR_SCALAR(1.0),
BR_SCALAR(100.0),
BR_SCALAR(16.0/9.0),
544,
306
};
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.00);
const BRS krPowerHilite = BR_SCALAR(50);
const long kiclrHilite = 108; // palette index for hilite color
ClearPb(&blit, size(BLIT));
blit.colour = BR_COLOUR_RGB(0xff, 0xff, 0xff);
blit.type = BR_LIGHT_DIRECT;
blit.attenuation_c = BR_SCALAR(1.0);
pbactWorld = BrActorAllocate(BR_ACTOR_NONE, pvNil);
if (pvNil == pbactWorld)
goto LFail;
pbactCamera = BrActorAllocate(BR_ACTOR_CAMERA, &bcam);
if (pvNil == pbactCamera)
goto LFail;
BrActorAdd(pbactWorld, pbactCamera);
for (iblit = 0; iblit < cblit; iblit++)
{
pbactLight = BrActorAllocate(BR_ACTOR_LIGHT, pvNil);
if (pvNil == pbactLight)
goto LFail;
pbactLight->type_data = &blit;
BrActorAdd(pbactWorld, pbactLight);
BrLightEnable(pbactLight);
}
pbpmpRGB = BrPixelmapAllocate(BR_PMT_INDEX_8, 544, 306, 0, 0);
pbpmpZ = BrPixelmapAllocate(BR_PMT_DEPTH_16, 544, 306, 0, 0);
pbmtl = BrMaterialAllocate("Prelighting material");
if (pvNil == pbmtl)
goto LFail;
pbmtl->colour = kbrcHilite;
pbmtl->ka = kbrufKaHilite;
pbmtl->kd = kbrufKdHilite,
pbmtl->ks = kbrufKsHilite;
pbmtl->power = krPowerHilite;
pbmtl->flags = BR_MATF_LIGHT | BR_MATF_GOURAUD;
pbmtl->index_base = kiclrHilite;
pbmtl->index_range = 8;
BrMaterialAdd(pbmtl);
BrZbSceneRenderBegin(pbactWorld, pbactCamera, pbpmpRGB, pbpmpZ);
BrSceneModelLight(_pbmdl, pbmtl, pbactWorld, pvNil);
BrZbSceneRenderEnd();
BrMaterialRemove(pbmtl);
Assert(cblit == 1, "code needs to get smarter...");
BrLightDisable(pbactLight);
BrActorFree(pbactWorld);
return fTrue;
LFail:
BrActorFree(pbactWorld);
return fFalse;
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of the MODL.
***************************************************************************/
void MODL::AssertValid(ulong grf)
{
MODL_PAR::AssertValid(fobjAllocated);
AssertVarMem(_pbmdl);
Assert((PMODL)*(long *)_pbmdl->identifier == this,
"Bad MODL identifier");
}
/***************************************************************************
Mark memory used by the MODL
***************************************************************************/
void MODL::MarkMem(void)
{
AssertThis(0);
MODL_PAR::MarkMem();
}
#endif //DEBUG