Microsoft-3D-Movie-Maker/cd12/SRC/ENGINE/BKGD.CPP
2022-05-03 16:31:19 -07:00

697 lines
18 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
bkgd.cpp: Background class
Primary Author: ******
Review Status: REVIEWED - any changes to this file must be reviewed!
A BKGD (background) consists of a set of light sources (GLLT), a
background sound (SND), and one or more camera views. Each camera view
consists of a camera specification, a pre-rendered RGB bitmap, and a
pre-rendered Z-buffer. Here's how the chunks look:
BKGD // Contains stage bounding cuboid
|
+---GLLT (chid 0) // GL of light position specs (LITEs)
|
+---SND (chid 0) // Background sound/music
|
+---CAM (chid 0) // Contains camera pos/orient matrix, hither, yon
| |
| +---MBMP (chid 0) // Background RGB bitmap
| |
| +---ZBMP (chid 0) // Background Z-buffer
|
+---CAM (chid 1)
| |
| +---MBMP (chid 0)
| |
| +---ZBMP (chid 0)
|
+---CAM (chid 2)
. |
. +---MBMP (chid 0)
. |
+---ZBMP (chid 0)
***************************************************************************/
#include "soc.h"
ASSERTNAME
RTCLASS(BKGD)
const CHID kchidBds = 0; // Background default sound
const CHID kchidGllt = 0; // GL of LITEs
const CHID kchidGlcr = 0; // Palette
const br_colour kbrcLight = BR_COLOUR_RGB(0xff, 0xff, 0xff);
/***************************************************************************
Add the background's chunks (excluding camera views) to the tag list
***************************************************************************/
bool BKGD::FAddTagsToTagl(PTAG ptagBkgd, PTAGL ptagl)
{
AssertVarMem(ptagBkgd);
AssertPo(ptagl, 0);
if (!ptagl->FInsertTag(ptagBkgd, fFalse))
return fFalse;
if (!ptagl->FInsertChild(ptagBkgd, kchidBds, kctgBds))
return fFalse;
if (!ptagl->FInsertChild(ptagBkgd, kchidGllt, kctgGllt))
return fFalse;
if (!ptagl->FInsertChild(ptagBkgd, kchidGlcr, kctgColorTable))
return fFalse;
// Have to cache first camera view since scene switches to it
// automatically
if (!ptagl->FInsertChild(ptagBkgd, 0, kctgCam))
return fFalse;
return fTrue;
}
/***************************************************************************
Cache the background's chunks (excluding camera views) to HD
***************************************************************************/
bool BKGD::FCacheToHD(PTAG ptagBkgd)
{
AssertVarMem(ptagBkgd);
TAG tagBds;
TAG tagGllt;
TAG tagGlcr;
TAG tagCam;
// Build the child tags
if (!vptagm->FBuildChildTag(ptagBkgd, kchidBds, kctgBds, &tagBds))
return fFalse;
if (!vptagm->FBuildChildTag(ptagBkgd, kchidGllt, kctgGllt, &tagGllt))
return fFalse;
if (!vptagm->FBuildChildTag(ptagBkgd, kchidGlcr, kctgColorTable, &tagGlcr))
return fFalse;
if (!vptagm->FBuildChildTag(ptagBkgd, 0, kctgCam, &tagCam))
return fFalse;
// Cache the BKGD chunk
if (!vptagm->FCacheTagToHD(ptagBkgd, fFalse))
return fFalse;
// Cache the child chunks
if (!vptagm->FCacheTagToHD(&tagBds))
return fFalse;
if (!vptagm->FCacheTagToHD(&tagGllt))
return fFalse;
if (!vptagm->FCacheTagToHD(&tagGlcr))
return fFalse;
// Have to cache first camera view since scene switches to it
// automatically
if (!vptagm->FCacheTagToHD(&tagCam))
return fFalse;
return fTrue;
}
/***************************************************************************
A PFNRPO to read a BKGD from a file
***************************************************************************/
bool BKGD::FReadBkgd(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
PBACO *ppbaco, long *pcb)
{
AssertPo(pcrf, 0);
AssertPo(pblck, 0);
AssertNilOrVarMem(ppbaco);
AssertVarMem(pcb);
BKGD *pbkgd;
*pcb = size(BKGD) + size(BACT) + size(BLIT); // estimate BKGD size
if (pvNil == ppbaco)
return fTrue;
pbkgd = NewObj BKGD;
if (pvNil == pbkgd || !pbkgd->_FInit(pcrf->Pcfl(), ctg, cno))
{
TrashVar(ppbaco);
TrashVar(pcb);
ReleasePpo(&pbkgd);
return fFalse;
}
AssertPo(pbkgd, 0);
*ppbaco = pbkgd;
*pcb = size(BKGD) + LwMul(pbkgd->_cbactLight, size(BACT)) +
LwMul(pbkgd->_cbactLight, size(BLIT)); // actual BKGD size
return fTrue;
}
/***************************************************************************
Read a BKGD from the given chunk of the given CFL.
Note: Although we read the data for the lights here, we don't turn
them on yet because we don't have a BWLD to add them to. The lights
are turned on with the first FSetCamera() call.
***************************************************************************/
bool BKGD::_FInit(PCFL pcfl, CTG ctg, CNO cno)
{
AssertBaseThis(0);
AssertPo(pcfl, 0);
BLCK blck;
BKGDF bkgdf;
KID kid;
PGL pgllite = pvNil;
short bo;
_ccam = _Ccam(pcfl, ctg, cno); // compute # of views in this background
_icam = ivNil;
if (!pcfl->FFind(ctg, cno, &blck) || !blck.FUnpackData())
goto LFail;
if (blck.Cb() != size(BKGDF))
goto LFail;
if (!blck.FReadRgb(&bkgdf, size(BKGDF), 0))
goto LFail;
if (kboCur != bkgdf.bo)
SwapBytesBom(&bkgdf, kbomBkgdf);
Assert(kboCur == bkgdf.bo, "bad BKGDF");
if (!pcfl->FGetName(ctg, cno, &_stn))
goto LFail;
// Get the default sound
if (pcfl->FGetKidChidCtg(ctg, cno, kchidBds, kctgBds, &kid))
{
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck) || !blck.FUnpackData())
goto LFail;
if (blck.Cb() != size(BDS))
goto LFail;
if (!blck.FReadRgb(&_bds, size(BDS), 0))
goto LFail;
if (kboCur != _bds.bo)
SwapBytesBom(&_bds, kbomBds);
Assert(kboCur == _bds.bo, "bad BDS");
}
else
{
_bds.tagSnd.sid = ksidInvalid;
}
// If there is a GLCR child, get it
if (pcfl->FGetKidChidCtg(ctg, cno, kchidGlcr, kctgColorTable, &kid))
{
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck))
goto LFail;
_pglclr = GL::PglRead(&blck, &bo);
if (_pglclr != pvNil)
{
if (kboOther == bo)
SwapBytesRglw(_pglclr->QvGet(0), _pglclr->IvMac());
}
_bIndexBase = bkgdf.bIndexBase;
}
else
_pglclr = pvNil;
// Read the GL of LITEs (GLLT)
if (!pcfl->FGetKidChidCtg(ctg, cno, kchidGllt, kctgGllt, &kid))
goto LFail;
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck))
goto LFail;
pgllite = GL::PglRead(&blck, &bo);
if (pvNil == pgllite)
goto LFail;
Assert(pgllite->CbEntry() == size(LITE),
"bad pgllite...you may need to update bkgds.chk");
AssertBomRglw(kbomLite, size(LITE));
if (kboOther == bo)
{
SwapBytesRglw(pgllite->QvGet(0), LwMul(pgllite->IvMac(),
size(LITE) / size(long)));
}
_cbactLight = pgllite->IvMac();
if (!FAllocPv((void **)&_prgbactLight, LwMul(_cbactLight, size(BACT)),
fmemClear, mprNormal))
{
goto LFail;
}
if (!FAllocPv((void **)&_prgblitLight, LwMul(_cbactLight, size(BLIT)),
fmemClear, mprNormal))
{
goto LFail;
}
_SetupLights(pgllite);
ReleasePpo(&pgllite);
return fTrue;
LFail:
Warn("Error reading background");
ReleasePpo(&pgllite);
return fFalse;
}
/***************************************************************************
Return the number of camera views in this scene. CAM chunks need to be
contiguous CHIDs starting at CHID 0.
***************************************************************************/
long BKGD::_Ccam(PCFL pcfl, CTG ctg, CNO cno)
{
AssertBaseThis(0);
AssertPo(pcfl, 0);
KID kid;
long ccam;
for (ccam = 0; pcfl->FGetKidChidCtg(ctg, cno, ccam, kctgCam, &kid);
ccam++)
{
}
#ifdef DEBUG
// Make sure chids are consecutive
long ckid;
long ccamT = 0;
for (ckid = 0; pcfl->FGetKid(ctg, cno, ckid, &kid); ckid++)
{
if (kid.cki.ctg == kctgCam)
ccamT++;
}
Assert(ccamT == ccam, "cam chids are not consecutive!");
#endif
return ccam;
}
/***************************************************************************
Fill _prgbactLight and _prgblitLight using a GL of LITEs
***************************************************************************/
void BKGD::_SetupLights(PGL pgllite)
{
AssertBaseThis(0);
AssertPo(pgllite, 0);
long ilite;
LITE *qlite;
BACT *pbact;
BLIT *pblit;
for (ilite = 0; ilite < _cbactLight; ilite++)
{
qlite = (LITE *)pgllite->QvGet(ilite);
pbact = &_prgbactLight[ilite];
pblit = &_prgblitLight[ilite];
pblit->type = (byte)qlite->lt;
pblit->colour = kbrcLight;
pblit->attenuation_c = qlite->rIntensity;
pbact->type = BR_ACTOR_LIGHT;
pbact->type_data = pblit;
pbact->t.type = BR_TRANSFORM_MATRIX34;
pbact->t.t.mat = qlite->bmat34;
}
}
/***************************************************************************
Clean up and delete this background
***************************************************************************/
BKGD::~BKGD(void)
{
AssertBaseThis(0);
Assert(!_fLeaveLitesOn, "Shouldn't be freeing background now");
if (pvNil != _prgbactLight && pvNil != _prgblitLight)
TurnOffLights();
FreePpv((void **)&_prgbactLight);
FreePpv((void **)&_prgblitLight);
ReleasePpo(&_pglclr);
ReleasePpo(&_pglapos);
}
/***************************************************************************
Get the background's name
***************************************************************************/
void BKGD::GetName(PSTN pstn)
{
AssertThis(0);
AssertPo(pstn, 0);
*pstn = _stn;
}
/***************************************************************************
Get the custom palette for this background, if any. Returns fFalse if
an error occurs. Sets *ppglclr to an empty GL and *piclrMin to 0 if
this background has no custom palette.
***************************************************************************/
bool BKGD::FGetPalette(PGL *ppglclr, long *piclrMin)
{
AssertThis(0);
AssertVarMem(ppglclr);
AssertVarMem(piclrMin);
*piclrMin = _bIndexBase;
if (pvNil == _pglclr) // no custom palette
{
*ppglclr = GL::PglNew(size(CLR)); // "palette" with 0 entries
}
else
{
*ppglclr = _pglclr->PglDup();
}
return (pvNil != *ppglclr);
}
/***************************************************************************
Get the camera position in worldspace
***************************************************************************/
void BKGD::GetCameraPos(BRS *pxr, BRS *pyr, BRS *pzr)
{
AssertThis(0);
AssertNilOrVarMem(pxr);
AssertNilOrVarMem(pyr);
AssertNilOrVarMem(pzr);
if (pvNil != pxr)
*pxr = _xrCam;
if (pvNil != pyr)
*pyr = _yrCam;
if (pvNil != pzr)
*pzr = _zrCam;
}
/***************************************************************************
Turn on lights in pbwld
***************************************************************************/
void BKGD::TurnOnLights(PBWLD pbwld)
{
AssertThis(0);
AssertPo(pbwld, 0);
long ilite;
BACT *pbact;
if (!_fLites)
{
for (ilite = 0; ilite < _cbactLight; ilite++)
{
pbact = &_prgbactLight[ilite];
pbwld->AddActor(pbact);
BrLightEnable(pbact);
}
_fLites = fTrue;
}
}
/***************************************************************************
Turn off lights in pbwld
***************************************************************************/
void BKGD::TurnOffLights(void)
{
AssertThis(0);
long ilite;
BACT *pbact;
if (!_fLites || _fLeaveLitesOn)
return;
for (ilite = 0; ilite < _cbactLight; ilite++)
{
pbact = &_prgbactLight[ilite];
BrLightDisable(pbact);
BrActorRemove(pbact);
}
_fLites = fFalse;
}
/***************************************************************************
Set the camera and associated bitmaps to icam
***************************************************************************/
bool BKGD::FSetCamera(PBWLD pbwld, long icam)
{
AssertThis(0);
AssertPo(pbwld, 0);
AssertIn(icam, 0, Ccam());
long capos;
KID kidCam;
KID kidRGB;
KID kidZ;
BLCK blck;
CAM cam;
PCFL pcfl = Pcrf()->Pcfl();
BREUL breul;
TurnOnLights(pbwld);
// read new camera data
if (!pcfl->FGetKidChidCtg(Ctg(), Cno(), icam, kctgCam, &kidCam))
return fFalse;
if (!pcfl->FFind(kidCam.cki.ctg, kidCam.cki.cno, &blck) || !blck.FUnpackData())
return fFalse;
// Need at least one actor position
if (blck.Cb() < size(CAM))
{
Bug("CAM chunk not large enough");
return fFalse;
}
capos = (blck.Cb() - size(CAM)) / size(APOS);
if ((capos * size(APOS) + size(CAM)) != blck.Cb())
{
Bug("CAM chunk's extra data not an even multiple of size(APOS)");
return fFalse;
}
if (!blck.FReadRgb(&cam, size(CAM), 0))
return fFalse;
#ifdef DEBUG
{
BOM bomCam = kbomCam, bomCamOld = kbomCamOld;
Assert(bomCam == bomCamOld, "BOM macros aren't right");
}
#endif // DEBUG
Assert((size(APOS) / size(long)) * size(long) == size(APOS),
"APOS not an even number of longs");
if (kboOther == cam.bo)
{
SwapBytesBom(&cam, kbomCam);
SwapBytesRglw(PvAddBv(&cam, offset(CAM, bmat34Cam)),
size(cam.bmat34Cam) / size(long));
SwapBytesRglw(PvAddBv(&cam, size(CAM)),
capos * (size(APOS) / size(long)));
}
Assert(kboCur == cam.bo, "bad cam");
// find RGB pict
if (!pcfl->FGetKidChidCtg(kidCam.cki.ctg, kidCam.cki.cno, 0, kctgMbmp,
&kidRGB))
{
return fFalse;
}
// find Z pict
if (!pcfl->FGetKidChidCtg(kidCam.cki.ctg, kidCam.cki.cno, 0, kctgZbmp,
&kidZ))
{
return fFalse;
}
if (!pbwld->FSetBackground(Pcrf(), kidRGB.cki.ctg, kidRGB.cki.cno,
kidZ.cki.ctg, kidZ.cki.cno))
{
return fFalse;
}
// Get actor placements
ReleasePpo(&_pglapos);
_iaposNext = _iaposLast = 0;
if (capos > 0 && (_pglapos = GL::PglNew(size(APOS), capos)) != pvNil)
{
AssertDo(_pglapos->FSetIvMac(capos), "Should never fail");
_pglapos->Lock();
if (!blck.FReadRgb(_pglapos->QvGet(0), size(APOS) * capos, size(CAM)))
{
ReleasePpo(&_pglapos);
return fFalse;
}
_pglapos->Unlock();
}
_icam = icam;
_xrPlace = cam.apos.xrPlace;
_yrPlace = cam.apos.yrPlace;
_zrPlace = cam.apos.zrPlace;
_xrCam = cam.bmat34Cam.m[3][0];
_yrCam = cam.bmat34Cam.m[3][1];
_zrCam = cam.bmat34Cam.m[3][2];
// Find bmat34 without X & Z rotation
breul.order = BR_EULER_YXY_R;
BrMatrix34ToEuler(&breul, &cam.bmat34Cam);
_braRotY = breul.a + breul.c;
BrMatrix34RotateY(&_bmat34Mouse, _braRotY);
BrMatrix34PostTranslate(&_bmat34Mouse, cam.bmat34Cam.m[3][0],
cam.bmat34Cam.m[3][1], cam.bmat34Cam.m[3][2]);
pbwld->SetCamera(&cam.bmat34Cam, cam.zrHither, cam.zrYon, cam.aFov);
pbwld->MarkDirty();
return fTrue;
}
/***************************************************************************
Gets the matrix for mouse-dragging relative to the camera
***************************************************************************/
void BKGD::GetMouseMatrix(BMAT34 *pbmat34)
{
AssertThis(0);
AssertVarMem(pbmat34);
*pbmat34 = _bmat34Mouse;
}
/***************************************************************************
Gets the point at which to place new actors for this bkgd/view.
***************************************************************************/
void BKGD::GetActorPlacePoint(BRS *pxr, BRS *pyr, BRS *pzr)
{
AssertThis(0);
AssertVarMem(pxr);
AssertVarMem(pyr);
AssertVarMem(pzr);
APOS apos;
if (_iaposNext == 0)
{
*pxr = _xrPlace;
*pyr = _yrPlace;
*pzr = _zrPlace;
}
else
{
_pglapos->Get(_iaposNext - 1, &apos);
*pxr = apos.xrPlace;
*pyr = apos.yrPlace;
*pzr = apos.zrPlace;
}
_iaposLast = _iaposNext;
if (_pglapos != pvNil)
{
_iaposNext++;
if (_iaposNext > _pglapos->IvMac())
_iaposNext = 0;
}
}
/******************************************************************************
ReuseActorPlacePoint
Resets the current actor place point to the last one used. Call this
from the actor placement code if the actor was placed at a point other
than the one you just asked for.
************************************************************ PETED ***********/
void BKGD::ReuseActorPlacePoint(void)
{
_iaposNext = _iaposLast;
}
#ifdef DEBUG
/***************************************************************************
Authoring only. Writes a special file with the given place info.
***************************************************************************/
bool BKGD::FWritePlaceFile(BRS xrPlace, BRS yrPlace, BRS zrPlace)
{
AssertThis(0);
Assert(yrPlace == rZero, "are you sure you want non-zero Y?");
STN stnFile;
FNI fni;
PFIL pfil = pvNil;
STN stnData;
FP fp;
long xr1 = BrScalarToInt(xrPlace);
long xr2 = LwAbs((long)(1000000.0 * BrScalarToFloat(xrPlace -
BrIntToScalar(xr1))));
long yr1 = BrScalarToInt(yrPlace);
long yr2 = LwAbs((long)(1000000.0 * BrScalarToFloat(yrPlace -
BrIntToScalar(yr1))));
long zr1 = BrScalarToInt(zrPlace);
long zr2 = LwAbs((long)(1000000.0 * BrScalarToFloat(zrPlace -
BrIntToScalar(zr1))));
if (!stnFile.FFormatSz("%s-cam.1-%d.pos", &_stn, _icam + 1))
goto LFail;
if (!fni.FBuildFromPath(&stnFile))
goto LFail;
if (fni.TExists() == tYes)
pfil = FIL::PfilOpen(&fni, ffilWriteEnable);
else
pfil = FIL::PfilCreate(&fni);
if (pvNil == pfil)
goto LFail;
if (!stnData.FFormatSz("NEW_ACTOR_POS %d.%06d %d.%06d %d.%06d\n\r",
xr1, xr2, yr1, yr2, zr1, zr2))
{
goto LFail;
}
if ((fp = pfil->FpMac()) > 0)
{
// Go to the end of the file (and write over null byte at end
// of previous string)
fp--;
}
if (!pfil->FWriteRgb(stnData.Psz(), stnData.Cch() + 1, fp))
goto LFail;
ReleasePpo(&pfil);
return fTrue;
LFail:
ReleasePpo(&pfil);
return fFalse;
}
#endif // DEBUG
#ifdef DEBUG
/***************************************************************************
Assert the validity of the BKGD.
***************************************************************************/
void BKGD::AssertValid(ulong grf)
{
BKGD_PAR::AssertValid(fobjAllocated);
AssertIn(_cbactLight, 1, 100); // 100 is sanity check
AssertIn(_ccam, 1, 100); // 100 is sanity check
Assert(_icam == ivNil || (_icam >= 0 && _icam < _ccam), "bad _icam");
AssertPvCb(_prgbactLight, LwMul(_cbactLight, size(BACT)));
AssertPvCb(_prgblitLight, LwMul(_cbactLight, size(BLIT)));
AssertPo(&_stn, 0);
AssertNilOrPo(_pglapos, 0);
}
/***************************************************************************
Mark memory used by the BKGD
***************************************************************************/
void BKGD::MarkMem(void)
{
AssertThis(0);
BKGD_PAR::MarkMem();
MarkPv(_prgbactLight);
MarkPv(_prgblitLight);
MarkMemObj(_pglclr);
MarkMemObj(_pglapos);
}
#endif //DEBUG