/* 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