Microsoft-3D-Movie-Maker/BREN/BWLD.CPP

839 lines
23 KiB
C++
Raw Normal View History

2022-05-04 01:31:19 +02:00
/***************************************************************************
bwld.cpp: BRender world class
Primary Author: ******
Review Status: REVIEWED - any changes to this file must be reviewed!
To improve performance, BWLD can render into a reduced area, then
enlarge the resulting image at display time. _fHalfX reduces the
horizontal resolution by half, and _fHalfY reduces the vertical
resolution by half. Both modes can be used together to render 1/4 as
many pixels. _rcBuffer is the area being rendered into; _rcView is
the area to copy _rcBuffer into (with stretching, if necessary).
_pregnDirtyWorking and _pregnDirtyScreen are in _rcBuffer's coordinate
system. However, in MarkRenderedRegn, _pregnDirtyScreen is briefly
enlarged to _rcView's coordinate system, since the gob will be drawn at
full view resolution.
***************************************************************************/
#include "bren.h"
// REVIEW *****: _pgptStretch is completely unused...remove it!
ASSERTNAME
RTCLASS(BWLD)
const long kcbitPixelRGB = 8; // RGB buffers are 8 bits deep
const long kcbPixelRGB = 1;
const long kcbitPixelZ = 16; // Z buffers are 16 bits deep
const long kcbPixelZ = 2;
bool BWLD::_fBRenderInited = fFalse;
/***************************************************************************
Allocate a new BRender world
***************************************************************************/
PBWLD BWLD::PbwldNew(long dxp, long dyp, bool fHalfX, bool fHalfY)
{
AssertIn(dxp, 1, ksuMax); // BPMP's width and height are ushorts
AssertIn(dyp, 1, ksuMax);
Assert(dxp % 2 == 0, "dxp should be even");
Assert(dyp % 2 == 0, "dyp should be even");
PBWLD pbwld;
pbwld = NewObj BWLD;
if (pbwld == pvNil || !pbwld->_FInit(dxp, dyp, fHalfX, fHalfY))
{
ReleasePpo(&pbwld);
return pvNil;
}
AssertPo(pbwld, 0);
return pbwld;
}
/***************************************************************************
Initialize the BWLD
***************************************************************************/
bool BWLD::_FInit(long dxp, long dyp, bool fHalfX, bool fHalfY)
{
AssertBaseThis(0);
AssertIn(dxp, 1, ksuMax); // BPMP's width and height are ushorts
AssertIn(dyp, 1, ksuMax);
if (!_fBRenderInited)
{
BrBegin();
BrZbBegin(BR_PMT_INDEX_8, BR_PMT_DEPTH_16);
_fBRenderInited = fTrue;
}
_rcView.Set(0, 0, dxp, dyp);
if (!_FInitBuffers(dxp, dyp, fHalfX, fHalfY))
return fFalse;
// Create the world and initial camera
_bactWorld.type = BR_ACTOR_NONE;
_bactWorld.t.type = BR_TRANSFORM_MATRIX34;
BrMatrix34Identity(&_bactWorld.t.t.mat);
_bactWorld.identifier = (char *)this;
_bactCamera.type = BR_ACTOR_CAMERA;
_bactCamera.t.type = BR_TRANSFORM_MATRIX34;
_bactCamera.type_data = &_bcam;
_bcam.type = BR_CAMERA_PERSPECTIVE;
// Note that the aspect ratio of the view is specified rather than the
// ratio of the buffer so that even when rendering into a reduced
// buffer, the actors come out right when stretched to _rcView in Draw().
_bcam.aspect = BR_SCALAR((double)_rcView.Dxp() / (double)_rcView.Dyp());
BrActorAdd(&_bactWorld, &_bactCamera);
// Set up dirty region stuff
_pregnDirtyWorking = REGN::PregnNew(&_rcBuffer);
if (pvNil == _pregnDirtyWorking)
return fFalse;
_pregnDirtyScreen = REGN::PregnNew(pvNil);
if (pvNil == _pregnDirtyScreen)
return fFalse;
BrZbSetRenderBoundsCallback(_ActorRendered);
return fTrue;
}
/***************************************************************************
Initialize or reinitialize members that depend on the values of _fHalfX
and _fHalfY. This function gets called by _FInit, and again every time
FSetHalfMode is called.
***************************************************************************/
bool BWLD::_FInitBuffers(long dxp, long dyp, bool fHalfX, bool fHalfY)
{
AssertBaseThis(0);
AssertIn(dxp, 1, ksuMax); // BPMP's width and height are ushorts
AssertIn(dyp, 1, ksuMax);
_fHalfX = fHalfX;
_fHalfY = fHalfY;
if (_fHalfX)
dxp /= 2;
if (_fHalfY)
dyp /= 2;
_rcBuffer.Set(0, 0, dxp, dyp);
ReleasePpo(&_pzbmpBackground);
_pzbmpBackground = ZBMP::PzbmpNew(dxp, dyp);
if (pvNil == _pzbmpBackground)
return fFalse;
// Set up the working Z-buffer
ReleasePpo(&_pzbmpWorking);
_pzbmpWorking = ZBMP::PzbmpNew(dxp, dyp);
if (pvNil == _pzbmpWorking)
return fFalse;
Assert(kcbitPixelZ == 16, "change _bpmpZ.type");
_bpmpZ.type = BR_PMT_DEPTH_16;
_bpmpZ.row_bytes = (short)LwMul(dxp, kcbPixelZ);
_bpmpZ.width = (ushort)dxp;
_bpmpZ.height = (ushort)dyp;
_bpmpZ.origin_x = dxp / 2;
_bpmpZ.origin_y = dyp / 2;
_bpmpZ.pixels = _pzbmpWorking->Prgb();
// Set up background RGB buffer
ReleasePpo(&_pgptBackground);
_pgptBackground = GPT::PgptNewOffscreen(&_rcBuffer, kcbitPixelRGB);
if (pvNil == _pgptBackground)
return fFalse;
// Set up the working RGB buffer
if (pvNil != _pgptWorking)
{
_pgptWorking->Unlock();
ReleasePpo(&_pgptWorking);
}
_pgptWorking = GPT::PgptNewOffscreen(&_rcBuffer, kcbitPixelRGB);
if (pvNil == _pgptWorking)
return fFalse;
Assert(kcbitPixelRGB == 8, "change _bpmpRGB.type");
_bpmpRGB.type = BR_PMT_INDEX_8;
_bpmpRGB.row_bytes = (short)LwMul(dxp, kcbPixelRGB);
_bpmpRGB.width = (ushort)dxp;
_bpmpRGB.height = (ushort)dyp;
_bpmpRGB.origin_x = dxp / 2;
_bpmpRGB.origin_y = dyp / 2;
_bpmpRGB.pixels = _pgptWorking->PrgbLockPixels();
// If in _fHalfY and not _fHalfX, allocated a _rcView-sized buffer
// for faster blitting -- see Draw()
ReleasePpo(&_pgptStretch);
if (!_fHalfX && _fHalfY)
{
_pgptStretch = GPT::PgptNewOffscreen(&_rcView, kcbitPixelRGB);
if (pvNil == _pgptStretch)
return fFalse;
}
return fTrue;
}
/***************************************************************************
Destructor for BWLD
***************************************************************************/
BWLD::~BWLD(void)
{
AssertBaseThis(0);
if (pvNil != _pgptWorking)
{
_pgptWorking->Unlock();
ReleasePpo(&_pgptWorking);
}
ReleasePpo(&_pgptStretch);
ReleasePpo(&_pgptBackground);
ReleasePpo(&_pzbmpWorking);
ReleasePpo(&_pzbmpBackground);
ReleasePpo(&_pregnDirtyWorking);
ReleasePpo(&_pregnDirtyScreen);
ReleasePpo(&_pcrf);
}
/***************************************************************************
Change reduced rendering mode
***************************************************************************/
bool BWLD::FSetHalfMode(bool fHalfX, bool fHalfY)
{
AssertThis(0);
if (FPure(_fHalfX) == FPure(fHalfX) && FPure(_fHalfY) == FPure(fHalfY))
return fTrue;
bool fHalfXSave = _fHalfX;
bool fHalfYSave = _fHalfY;
RC rcBufferSave = _rcBuffer;
PGPT pgptWorkingSave = _pgptWorking;
PGPT pgptStretchSave = _pgptStretch;
PGPT pgptBackgroundSave = _pgptBackground;
BPMP bpmpRGBSave = _bpmpRGB;
PZBMP pzbmpWorkingSave = _pzbmpWorking;
PZBMP pzbmpBackgroundSave = _pzbmpBackground;
BPMP bpmpZSave = _bpmpZ;
_pgptWorking = pvNil;
_pgptStretch = pvNil;
_pgptBackground = pvNil;
_pzbmpWorking = pvNil;
_pzbmpBackground = pvNil;
if (!_FInitBuffers(_rcView.Dxp(), _rcView.Dyp(), fHalfX, fHalfY))
goto LFail;
if (pvNil != _pcrf)
{
// Reload the background at the new resolution
if (!FSetBackground(_pcrf, _ctgRGB, _cnoRGB, _ctgZ, _cnoZ))
goto LFail;
}
if (pvNil != pgptWorkingSave)
{
pgptWorkingSave->Unlock();
ReleasePpo(&pgptWorkingSave);
}
ReleasePpo(&pgptStretchSave);
ReleasePpo(&pgptBackgroundSave);
ReleasePpo(&pzbmpWorkingSave);
ReleasePpo(&pzbmpBackgroundSave);
AssertThis(0);
return fTrue;
LFail:
// Get rid of newly allocated buffers
if (pvNil != _pgptWorking)
{
_pgptWorking->Unlock();
ReleasePpo(&_pgptWorking);
}
ReleasePpo(&_pgptStretch);
ReleasePpo(&_pgptBackground);
ReleasePpo(&_pzbmpWorking);
ReleasePpo(&_pzbmpBackground);
// restore everything
_fHalfX = fHalfXSave;
_fHalfY = fHalfYSave;
_rcBuffer = rcBufferSave;
_bpmpZ = bpmpZSave;
_bpmpRGB = bpmpRGBSave;
_pgptWorking = pgptWorkingSave;
_pgptStretch = pgptStretchSave;
_pgptBackground = pgptBackgroundSave;
_pzbmpWorking = pzbmpWorkingSave;
_pzbmpBackground = pzbmpBackgroundSave;
AssertThis(0);
return fFalse;
}
/***************************************************************************
Completely close BRender, freeing all data structures that BRender
knows about. This invalidates all MODLs and MTRLs in existence.
***************************************************************************/
void BWLD::CloseBRender(void)
{
if (_fBRenderInited)
{
BrZbEnd();
BrEnd();
_fBRenderInited = fFalse;
}
}
/***************************************************************************
Copy pvSrc into pvDst, skipping every other short. This is called by
FSetBackground for each row in a ZBMP when _fHalfX is fTrue.
***************************************************************************/
inline void SqueezePb(void *pvSrc, void *pvDst, long cbSrc)
{
AssertIn(cbSrc, 0, kcbMax);
Assert(cbSrc % (LwMul(2, kcbPixelZ)) == 0, "cbSrc is not aligned");
AssertPvCb(pvSrc, cbSrc);
AssertPvCb(pvDst, cbSrc / 2);
Assert(size(short) == kcbPixelZ, 0);
short *pswSrc = (short *)pvSrc;
short *pswDst = (short *)pvDst;
while (cbSrc != 0)
{
*pswDst++ = *pswSrc++;
pswSrc++;
cbSrc -= LwMul(2, kcbPixelZ);
}
}
/***************************************************************************
Load bitmaps from the given chunks into _pgptBackground and
_pzbmpBackground.
***************************************************************************/
bool BWLD::FSetBackground(PCRF pcrf, CTG ctgRGB, CNO cnoRGB, CTG ctgZ,
CNO cnoZ)
{
AssertThis(0);
AssertPo(pcrf, 0);
PMBMP pmbmpNew;
PZBMP pzbmpNew;
pmbmpNew = (PMBMP)pcrf->PbacoFetch(ctgRGB, cnoRGB, MBMP::FReadMbmp);
if (pvNil == pmbmpNew)
return fFalse;
pzbmpNew = (PZBMP)pcrf->PbacoFetch(ctgZ, cnoZ, ZBMP::FReadZbmp);
if (pvNil == pzbmpNew)
{
ReleasePpo(&pmbmpNew);
return fFalse;
}
// It's nice to cache these bitmaps if we can, but they should be
// tossed first when memory gets tight because they take up a lot
// of space, they're pretty fast to reload, and they are reloaded
// at a time when it's okay for a pause (between scenes/views).
pmbmpNew->SetCrep(crepTossFirst);
pzbmpNew->SetCrep(crepTossFirst);
if (_fHalfX || _fHalfY)
{
// Need to squeeze pmbmpNew and pzbmpNew into _pgptBackground
// and _pzbmpBackground. For pmbmpNew, it is drawn into a
// full-size buffer, then reduced with CopyPixels. For the
// ZBMP, it is necessary to reduce it in code here.
PGPT pgptFull;
long yp;
byte *pbSrc;
byte *pbDst;
long cbRowSrc;
long cbRowDst;
pgptFull = GPT::PgptNewOffscreen(&_rcView, kcbitPixelRGB);
if (pvNil == _pgptBackground)
{
ReleasePpo(&pmbmpNew);
ReleasePpo(&pzbmpNew);
return fFalse;
}
GNV gnvFull(pgptFull);
GNV gnvHalf(_pgptBackground);
gnvFull.DrawMbmp(pmbmpNew, 0, 0);
ReleasePpo(&pmbmpNew);
gnvHalf.CopyPixels(&gnvFull, &_rcView, &_rcBuffer);
ReleasePpo(&pgptFull);
pbSrc = pzbmpNew->Prgb();
pbDst = _pzbmpBackground->Prgb();
cbRowSrc = pzbmpNew->CbRow();
cbRowDst = _pzbmpBackground->CbRow();
Assert(cbRowSrc - cbRowDst == (_fHalfX ? cbRowDst : 0),
"bad src/dest width ratio");
for (yp = 0; yp < _rcBuffer.Dyp(); yp++)
{
if (_fHalfX)
SqueezePb(pbSrc, pbDst, cbRowSrc);
else
CopyPb(pbSrc, pbDst, cbRowSrc);
pbSrc += cbRowSrc;
if (_fHalfY)
pbSrc += cbRowSrc; // skip rows in source
pbDst += cbRowDst;
}
ReleasePpo(&pzbmpNew);
}
else // not in half mode
{
GNV gnv(_pgptBackground);
gnv.DrawMbmp(pmbmpNew, 0, 0);
ReleasePpo(&pmbmpNew);
ReleasePpo(&_pzbmpBackground);
_pzbmpBackground = pzbmpNew;
}
// entire working buffer is dirty because of background change
_pregnDirtyWorking->SetRc(&_rcBuffer);
_fWorldChanged = fTrue;
// Keep a reference to the background, in case we change to/from
// halfmode and need to reload it.
pcrf->AddRef();
ReleasePpo(&_pcrf);
_pcrf = pcrf;
_ctgRGB = ctgRGB;
_cnoRGB = cnoRGB;
_ctgZ = ctgZ;
_cnoZ = cnoZ;
return fTrue;
}
/***************************************************************************
Change the camera matrix
***************************************************************************/
void BWLD::SetCamera(BMAT34 *pbmat34, BRS zrHither, BRS zrYon, BRA aFov)
{
AssertThis(0);
AssertVarMem(pbmat34);
Assert(zrYon > zrHither, "Yon must be further than hither");
_bactCamera.t.t.mat = *pbmat34;
_bcam.hither_z = zrHither;
_bcam.yon_z = zrYon;
_bcam.field_of_view = aFov;
// entire working buffer is dirty because of camera change
_pregnDirtyWorking->SetRc(&_rcBuffer);
}
/***************************************************************************
Get the camera matrix
***************************************************************************/
void BWLD::GetCamera(BMAT34 *pbmat34, BRS *pzrHither, BRS *pzrYon,
BRA *paFov)
{
AssertThis(0);
AssertVarMem(pbmat34);
AssertNilOrVarMem(pzrHither);
AssertNilOrVarMem(pzrYon);
AssertNilOrVarMem(paFov);
*pbmat34 = _bactCamera.t.t.mat;
if (pvNil != pzrHither)
*pzrHither = _bcam.hither_z;
if (pvNil != pzrYon)
*pzrYon = _bcam.yon_z;
if (pvNil != paFov)
*paFov = _bcam.field_of_view;
}
/***************************************************************************
Render the world. First, notify all BODYs that we're about to render,
so they can clear their _pregn's. Then clean the RGB and Z working
buffers, since they're probably dirty from the last render. Update
some regions, and render everything.
***************************************************************************/
void BWLD::Render(void)
{
AssertThis(0);
PBACT pbact;
RC rc;
if (!_fWorldChanged)
return;
// Note that we only call pfnbeginrend on immediate children of
// the world, because that will hit all the BODYs in Socrates.
if (pvNil != _pfnbeginrend)
{
for (pbact = _bactWorld.children; pvNil != pbact;
pbact = pbact->next)
{
// BODY roots are BR_ACTOR_NONE
if (pbact->type == BR_ACTOR_NONE)
_pfnbeginrend(pbact);
}
}
_CleanWorkingBuffers();
// Now the working buffer is clean, but we should mark everything that
// we just cleaned in _CleanWorkingBuffers as dirty in the screen buffer
// so that the background shows through where actors used to be.
// In most cases, _pregnDirtyScreen is empty now, so it's tempting to
// just SwapVars the two regions. But if Render() was called but Draw()
// never was (as when you press play then stop playing before the end of
// the movie), _pregnDirtyScreen may have some stuff still to copy. So
// we have to merge (union) _pregnDirtyWorking into _pregnDirtyScreen,
// then clear _pregnDirtyWorking.
_pregnDirtyScreen->FUnion(_pregnDirtyWorking);
_pregnDirtyWorking->SetRc(pvNil);
// Render the scene. This will add stuff to _pregnDirtyWorking
BrZbSceneRender(&_bactWorld, &_bactCamera, &_bpmpRGB, &_bpmpZ);
for (pbact = _bactWorld.children; pvNil != pbact; pbact = pbact->next)
{
// BODY roots are BR_ACTOR_NONE
if (pbact->type == BR_ACTOR_NONE && pvNil != _pfngetrect)
{
_pfngetrect(pbact, &rc);
_pregnDirtyWorking->FUnionRc(&rc);
}
}
// Everything dirty in working buffer is dirty on screen too
_pregnDirtyScreen->FUnion(_pregnDirtyWorking);
_fWorldChanged = fFalse;
}
/***************************************************************************
"Prerender" the world. That is, render the world, then copy it into
the background buffers
***************************************************************************/
void BWLD::Prerender(void)
{
AssertThis(0);
GNV gnvBackground(_pgptBackground);
GNV gnvWorking(_pgptWorking);
Render();
_pzbmpWorking->Draw((byte *)_pzbmpBackground->Prgb(),
_pzbmpBackground->CbRow(), _rcBuffer.Dyp(), 0, 0, &_rcBuffer, pvNil);
// Need to detach _pzbmpBackground from the CRF so when we unprerender,
// a fresh copy of the ZBMP is fetched
_pzbmpBackground->Detach();
gnvBackground.CopyPixels(&gnvWorking, &_rcBuffer, &_rcBuffer);
// Need to ensure that the current contents of _pgptWorking (just the
// prerenderable actors) go into _pgptBackground
GPT::Flush();
}
/***************************************************************************
"Un-Prerender" the world. That is, restore the background bitmaps to
the way they were before prerendering any actors
***************************************************************************/
void BWLD::Unprerender(void)
{
AssertThis(0);
// Ignore error...you'll just get weird visual effects and an error
// will be reported elsewhere
FSetBackground(_pcrf, _ctgRGB, _cnoRGB, _ctgZ, _cnoZ);
}
/***************************************************************************
Copy _pregnDirtyWorking from background Z and RGB buffers to working
Z and RGB buffers.
***************************************************************************/
void BWLD::_CleanWorkingBuffers(void)
{
AssertThis(0);
REGSC regsc;
long yp;
long xpLeft;
RC rcRegnBounds;
RC rcClippedRegnBounds;
byte *pbSrc;
byte *pbDst;
long cbRowCopy;
RC rc;
long cbRowSrc, cbRowDst;
if (_pregnDirtyWorking->FEmpty(&rcRegnBounds))
return;
if (!rcClippedRegnBounds.FIntersect(&rcRegnBounds, &_rcBuffer))
return;
// Clean the Z buffer
_pzbmpBackground->Draw((byte*)_bpmpZ.pixels, _bpmpZ.row_bytes,
_bpmpZ.height, 0, 0, &rcClippedRegnBounds, _pregnDirtyWorking);
// Clean the RGB buffer
regsc.Init(_pregnDirtyWorking, &rcClippedRegnBounds);
yp = rcClippedRegnBounds.ypTop;
cbRowSrc = _pgptBackground->CbRow();
pbSrc = _pgptBackground->PrgbLockPixels() + LwMul(yp, cbRowSrc) +
rcClippedRegnBounds.xpLeft;
cbRowDst = _pgptWorking->CbRow();
pbDst = _pgptWorking->PrgbLockPixels() + LwMul(yp, cbRowDst) +
rcClippedRegnBounds.xpLeft;
for ( ; yp < rcClippedRegnBounds.ypBottom; yp++)
{
while (regsc.XpCur() < klwMax)
{
xpLeft = regsc.XpCur();
cbRowCopy = regsc.XpFetch() - xpLeft;
regsc.XpFetch();
CopyPb(pbSrc + xpLeft, pbDst + xpLeft, cbRowCopy);
}
pbSrc += cbRowSrc;
pbDst += cbRowDst;
regsc.ScanNext(1);
}
_pgptWorking->Unlock();
_pgptBackground->Unlock();
}
/***************************************************************************
Callback for when a BACT is rendered. Need to union with dirty
region.
***************************************************************************/
void BWLD::_ActorRendered(PBACT pbact, PBMDL pbmdl, PBMTL pbmtl,
br_uint_8 bStyle, br_matrix4 *pbmat4ModelToScreen, br_int_32 bounds[4])
{
AssertVarMem(pbact);
PBACT pbactT = pbact;
PBWLD pbwld;
RC rc(bounds[0], bounds[1], bounds[2] + 1, bounds[3] + 1);
while (pbactT->parent != pvNil)
pbactT = pbactT->parent;
pbwld = (PBWLD)pbactT->identifier;
AssertPo(pbwld, 0);
if (pvNil != pbwld->_pfnbactrend)
pbwld->_pfnbactrend(pbact, &rc); // call client callback
}
/***************************************************************************
Mark the region that has been rendered (and needs to be copied to the
screen)
***************************************************************************/
void BWLD::MarkRenderedRegn(PGOB pgob, long dxp, long dyp)
{
AssertThis(0);
AssertPo(pgob, 0);
_pregnDirtyScreen->Scale((_fHalfX ? 2 : 1), 1, (_fHalfY ? 2 : 1), 1);
_pregnDirtyScreen->Offset(dxp, dyp);
vpappb->MarkRegn(_pregnDirtyScreen, pgob);
_pregnDirtyScreen->SetRc(pvNil); // screen is clean
}
/***************************************************************************
Draw the BWLD's working RGB buffer into pgnv. The movie engine should
have called BWLD::MarkRenderedRegn before calling this, so only
_pregnDirtyScreen's bits will be copied.
***************************************************************************/
void BWLD::Draw(PGNV pgnv, RC *prcClip, long dxp, long dyp)
{
AssertThis(0);
AssertPo(pgnv, 0);
AssertVarMem(prcClip);
RC rc;
GNV gnvTemp(_pgptWorking);
rc.OffsetCopy(&_rcView, -dxp, -dyp);
pgnv->CopyPixels(&gnvTemp, &_rcBuffer, &rc);
}
/***************************************************************************
Add an actor to the world
***************************************************************************/
void BWLD::AddActor(BACT *pbact)
{
AssertThis(0);
AssertVarMem(pbact);
BrActorAdd(&_bactWorld, pbact);
}
/***************************************************************************
Filter callback proc for FClickedActor(). Saves pbact if it's the
closest one hit so far.
***************************************************************************/
int BWLD::_FFilter(BACT *pbact, PBMDL pbmdl, PBMTL pbmtl,
BVEC3 *pbvec3RayPos, BVEC3 *pbvec3RayDir, BRS dzpNear, BRS dzpFar,
void *pvData)
{
AssertVarMem(pbact);
AssertVarMem(pbvec3RayPos);
AssertVarMem(pbvec3RayDir);
PBWLD pbwld = (PBWLD)pvData;
AssertPo(pbwld, 0);
if (dzpNear < pbwld->_dzpClosestClicked)
{
pbwld->_pbactClosestClicked = pbact;
pbwld->_dzpClosestClicked = dzpNear;
}
return fFalse; // fFalse means keep searching
}
/***************************************************************************
Call pfnCallback for each actor under the point (xp, yp)
***************************************************************************/
void BWLD::IterateActorsInPt(br_pick2d_cbfn *pfnCallback, void *pvArg,
long xp, long yp)
{
AssertThis(0);
// Convert to _rcBuffer coordinates:
if (_fHalfX)
xp /= 2;
if (_fHalfY)
yp /= 2;
xp -= _bpmpRGB.origin_x;
yp -= _bpmpRGB.origin_y;
BrScenePick2D(&_bactWorld, &_bactCamera, &_bpmpRGB, xp, yp, pfnCallback,
pvArg);
}
/***************************************************************************
If an actor is under (xp, yp), function returns fTrue and **pbact is the
actor. If no actor is under (xp, yp), function returns fFalse.
***************************************************************************/
bool BWLD::FClickedActor(long xp, long yp, BACT **ppbact)
{
AssertThis(0);
AssertVarMem(ppbact);
_pbactClosestClicked = pvNil;
_dzpClosestClicked = BR_SCALAR_MAX;
IterateActorsInPt(BWLD::_FFilter, this, xp, yp);
if (pvNil != _pbactClosestClicked)
{
*ppbact = _pbactClosestClicked;
return fTrue;
}
// nothing was clicked
TrashVar(ppbact);
return fFalse;
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of the BWLD.
***************************************************************************/
void BWLD::AssertValid(ulong grf)
{
BWLD_PAR::AssertValid(fobjAllocated);
AssertPo(_pgptWorking, 0);
AssertPo(_pgptBackground, 0);
AssertPo(_pzbmpWorking, 0);
AssertPo(_pzbmpBackground, 0);
AssertPo(_pregnDirtyWorking, 0);
AssertPo(_pregnDirtyScreen, 0);
AssertNilOrPo(_pcrf, 0);
if (!_fHalfX && _fHalfY)
AssertPo(_pgptStretch, 0);
else
Assert(pvNil == _pgptStretch, "don't need _pgptStretch!");
}
/***************************************************************************
Mark memory used by the BWLD
***************************************************************************/
void BWLD::MarkMem(void)
{
AssertThis(0);
BWLD_PAR::MarkMem();
MarkMemObj(_pgptWorking);
MarkMemObj(_pgptBackground);
MarkMemObj(_pzbmpWorking);
MarkMemObj(_pzbmpBackground);
MarkMemObj(_pregnDirtyWorking);
MarkMemObj(_pregnDirtyScreen);
MarkMemObj(_pcrf);
MarkMemObj(_pgptStretch);
}
/******************************************************************************
FWriteBmp
Writes the current rendered buffer out to the given file
Arguments:
PFNI pfni -- the name of the file
Returns: fTrue if the file could be written successfully
************************************************************ PETED ***********/
bool BWLD::FWriteBmp(PFNI pfni)
{
AssertPo(pfni, 0);
bool fRet;
RC rc;
fRet = FWriteBitmap(pfni, _pgptWorking->PrgbLockPixels(&rc),
GPT::PglclrGetPalette(), _rcBuffer.Dxp(), _rcBuffer.Dyp());
_pgptWorking->Unlock();
return fRet;
}
#endif //DEBUG