////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// scene.cpp
// Project: Nostril (aka Postal)
//
// This module impliments the CScene class
//
// History:
//		01/05/97 MJR	Started.
//
//		01/31/97	JMI	Added cheezy alpha to Render().
//
//		02/06/97	JMI	Added 3D transform and render to Render() for CSprite3's.
//							For now, the world transform is set up on each 3D render,
//							but I will fix that next to be affected once.
//
//		02/07/97	JMI	Now uses m_transWorld member as the world transform.
//
//		02/07/97	JMI	Added the alpha blit for alphable sprite.
//							May not work yet, but Jeff wants it anyways.
//
//		02/07/97	JMI	Now it works, but there's some fudge in presuming the 3D
//							guy's diameter for the collision detection necessary for
//							the alpha X Ray.
//
//		02/07/97	JMI	Now the scene has its own pipeline and CSprite3 does not.
//							B/c of this we no longer needed the m_transWorld, b/c we
//							could incorporate it right into the view.
//							Also, removed m_ppipe from CSprite3 and added m_sRadius
//							so the X Ray logic could do correct collision between
//							the X Rayee and X Rayables.
//							Also, added a function to initialize/create the pipeline.
//
//		02/10/97	JMI	Changed the number of "Randy Units" represented by our
//							SCREEN_DIAMETER_FOR_3D to 25.5 (was 30.0).
//
//		02/10/97	JMI	Added support for child objects of 3D sprites (CSprite3's).
//
//		02/11/97	JMI	Changed 3D support to recursive inline so indefinite child
//							depths can be supported.
//
//		02/11/97	JMI	Fixed bug in SetupPipeline() that was incorrectly trans-
//							lating the view transform along Z a distance of
//							dModelRadius.  Now it transforms 0 along Z.
//							Also, changed MODEL_DIAMETER to the actual units used in
//							SoftImage and, consequently, set SCREEN_DIAMETER_FOR_3D
//							to the actual pixel size we want to use.  I imagine we'll
//							have to make both of these larger eventually to fit all
//							the animations.
//
//		02/13/97	JMI	Render() now uses the hood passed to it to get the lighting
//							effects.  Render() now takes a CHood* as a parm.
//
//		02/13/97	JMI	Render3D() now uses textures and fog.
//
//		02/14/97	JMI	Render3D() now passes the CSprite3.m_psphere values to Jeff
//							who returns them and sizes an image.  Currently, for
//							clipping purposes, the dude is rendered in the image and
//							then rspBlitT'd to the screen (so, if his textures all map
//							to 0, he won't show up b/c 0 is the transparent color pass-
//							ed to rspBlitT).
//							Also, CSprite3 now contains an m_psphere ptr which must be
//							set to the bounding sphere data for the CSprite3 frame (x, y,
//							z, w).
//							Also, added TransformPts() function which will map an array
//							of pts through the optionally provided transform and then
//							through the scenes transforms.
//
//		02/15/97	JMI	Now there are 3 ways a 3D object is dealt with in Render3D:
//							1) Not transformed or rendered -- it is entirely off the
//							destination region.
//							2) Rendered into an intermediate buffer Jeff allocates and
//							then rspBlitT'ed to the destination buffer -- it is
//							partially off the destination region and, therefore,
//							requires clippage.
//							3) Rendered directly into the destination region -- it is
//							entirely within the region.
//
//		02/14/97	JMI	Added m_transNoZ which is a transform that is equivalent
//							to the scene's pipeline (m_pipeline), but does not scale
//							Z to the maximum value.  It instead scales Z the same
//							as X and Y.  This is now used by TransformPts().
//		02/16/97	JMI	Now the Render3D should clip to the bounding cube of the
//							points to efficientize the clippage further.
//							Need to clean up Render3D(). I feel like I'm doing a little
//							too much stuff.
//		02/17/97	JMI	Also, increased model diameter (MODEL_DAIMETER) to 30.0
//							since the dude was 10.0 off the origin (very close to
//							exceeding the Z buf height.
//							Also, Render3D() now uses brighter lighting for objects
//							that have the InHighIntensity flag set and also uses
//							the m_sBrightness flag as the center point for the fog.
//							Also, added TransformPtsToRealm() which translate points
//							through the specified transform, the scene's pipeline's
//							transforms, and finally maps them to Realm 3D coordinates.
//
//		02/17/97	JMI	Now the alpha X Ray effect auto centers on 3D dudes.
//
//		02/19/97	JMI	Now Alpha2d can handle sprites with no alpha.
//
//		02/19/97	JMI	Now does not affect the passed in brightness in Render3D.
//
//		02/21/97	JMI	Increased MODEL_DIAMETER to 50.0 to avoid problem with
//							children rigids exceeding the Z and clip buffers.
//
//		02/23/97	JMI	Had forgotten to Make1() the NoZ* transforms in
//							SetupPipeline().
//
//		02/24/97	JMI	Had forgotten to specify dest clipping rect for FSPR8 blit
//							of Alpha2d.
//
//		02/24/97	JMI	Collapsed CAlphaSprite2 into CSprite2 and added
//							m_sAlphaLevel so a constant alpha level can be specified.
//							Using a constant alpha level is faster than using a mask.
//							Also, made it so the m_type field of the various sprites
//							are set automatically on construction so YOU SHOULD NO
//							LONGER SET THE CSprite*::m_type!
//
//		02/24/97	JMI	Added homogeneous alpha blit but it is untested as far as
//							its use in this module.
//
//		02/25/97	JMI	PATCHED FOR DEMO:  Instead of ASSERTing in Render3D() when
//							a CSprite3 is hosed, it will try to detect and skip it
//							for now.
//
//		02/26/97	JMI	Took out above patch.
//
//		03/05/97	JMI	Added Render() function to render a 3D sprite into a
//							specified background.
//
//		03/21/97	JMI	Added InDeleteOnRender flag and added support for new
//							CSpriteLine2d CSprite derived class.
//
//		03/27/97	JMI	Added SCALE3D parameter to allow quick scaling.
//
//		03/28/97	JMI	Put in VERY TEMP clippage to keep until the rspLine can
//							clip to a specified area (now clips to the destination).
//
//		04/02/97	JMI	Now use slightly faster method to determine the blit
//							position for the sprite rendered into the clip buffer.
//							Changed MODEL_DIAMETER to 28.444444445 (from 50.0) so
//							we can divide it by 2 many times before getting errors
//							due to using integer division.  The idea of that
//							particular number is to get a pixel size of the 3d world
//							of 128 which, of course, is a power of 2.
//
//		04/02/97	JMI	Put MODEL_DIAMETER back to 50.0 b/c rocket man's rocket
//							is too far away from him when the animation starts.
//
//		04/02/97	JMI	Put MODEL_DIAMETER back to 30.0.  Now Render3D() checks
//							to make sure the item fits into the Z buffer and, instead
//							of ASSERTing like it used to, it just TRACEs and does not
//							do the render.
//
//		04/02/97	JMI	In last fix, I used ||'s to separate newly combined ASSERTs
//							into an if ().  It should've been &&'s.  Fixed.
//
//		04/02/97	JMI	Removed old, commented out ASSERT in Render3D().  Also,
//							made Render3D() aware of InHidden flag.  Although the
//							loop in Render() takes care of the top-level objects'
//							InHidden flag, Render3D() was not, so child items that
//							were hidden would have been drawn, I think.  Now they
//							definitely won't.
//
//		04/14/97	JMI	Added use of TEMP flag m_bDontBlit to allow us to speed up
//							the Snap() call by not blitting.
//
//		04/29/97	JMI	Made the child concept part of the base class sprite (was
//							part of the 3D sprite (CSprite3)) and added a parent
//							pointer.
//							Now that we have the parent sprite pointer, I was able to
//							make sure the sprites remove themselves from their parents
//							and remove their children when they are destroyed.
//							Also, added member functions to CScene to handle rendering
//							2D sprites, 3D sprites, and 2D lines.
//
//		05/02/97	JMI	If an alpha was 100 or over for a general alpha blit, it
//							was using the regular blit (opaque) instead of an rspBlitT
//							(transparent).
//
//		05/02/97	JMI	Added CSpriteCylinder3d, a cylinder primitive.
//
//		05/13/97	JMI	Now Render3D() tries to maximize the size of an object it
//							can render by centering top-level 3D objects in the 3D
//							screen based on their bounding sphere.
//							This change required additional fields in CSprite3.
//
//		05/13/97	JMI	Casted instances of warning C4018 (signed/unsigned mismatch)
//							to make MSVC 4.1(Alpha) happy (these seem to fall under
//							Warning Level 3 in 4.1(Alpha) but not in 4.2(Intel)).
//
//		05/15/97	JMI	I had thought that the max alpha (i.e., opaque) level was
//							100 and was, therefore, checking to see if the level was
//							less than 100 before calling the alpha blit.  As it turns
//							out, it is 255.  Fixed.
//
//		05/15/97	JMI	Now obeys g_GameSettings.m_sAlphaBlend and m_sXRayEffect.
//
//		05/16/97	JMI	Code in Render3D() is set up so that no more than 1 level
//							deep of child will currently work for CSprite3.  The
//							solution would be to add an m_transCumm to CSprite3 that
//							could accumulate the combined transforms as CSprite3's are
//							Render3D()'d.
//
//		05/19/97	JMI	Now, by pointing mysprite.m_pszText at some text, you can
//							display text at the sprites m_sX2, m_sY2.
//
//		05/20/97	JMI	Now DrawLine2D() uses rspLine() with a clip rect.
//
//		05/20/97	JMI	Text (CSprite::m_pszText) drawn via ms_print is now clipped
//							to a column that is guaranteed to be within the clip rect.
//
//		05/22/97	JMI	Now obeys g_GameSettings.m_s3dFog.
//
//		05/22/97	JMI	Now the collision detection for the alpha affect is done
//							using the width and height of the xray mask instead of the
//							dude's xrayee's width and height.
//
//		06/05/97	JMI	m_pipeline.m_pZB->Clear() was being called for top-level
//							3D items even if they were OFFSCREEN.  WHoopsee.  Fixed.
//
//		06/05/97	JMI	In Render3D(), the indirect render, when copying from the
//							temp (indirect) buf to the composite buffer, did not take
//							into account the fact that the center of the render may
//							not have been the center of the current renderee's bounding
//							sphere (which happens in the case of a child object (they
//							are blit relative to the center of the top-level parent's
//							bounding sphere) ).
//
//		06/10/97 JRD	Returned lighting to non-adjusting because the spheres weren't
//							accurate enough.  Luckily, the fix in the 3d export made the
//							non-adaptive lighting MUCH better.
//
//		06/10/97 JRD	Updated to centered based adaptive lighting - a good compromise!
//
//		06/14/97 MJR	Added g_bSceneDontBlit to replace the flag in gamesettings
//							that was being misused for that purpose.
//
//		06/16/97 JRD	Shrunk Z-buffer from 30 to 16 Randy Units
//
//		06/25/97	JMI	Added SetXRayAll() and m_bXRayAll which enable or disable
//							xraying of all 'alpha' _and_ 'opaque' layers.
//							Also, changed InXrayable to InAlpha and added InOpaque to
//							differentiate between these two types which are now, at
//							times, both xrayable.
//
//		06/26/97	JMI	Now uses phood->Map3Dto2D() (was using the global
//							version defined in reality.h which is no more).
//
//		06/29/97 MJR	Converted vector of layers into simple array of layers
//							(motivated by trying to get it to work on the mac).
//
//		07/01/97	JMI	Added m_transScene2Realm which is used in
//							TransformPtsToRealm() to convert from the scene to the
//							realm.  This value is set via the SetupPipeline() call.
//							Currently, the code exists but is commented out b/c it
//							does not seem to bridge the gap between scene and realm
//							coords correctly.
//
//		07/10/97	JMI	Now Render2D() can uses an alpha mask with an alpha level
//							via the new rspGeneralAlphaBlit() which accepts a level.
//
//		07/17/97	JRD	Moved the pipeline tranform so it would only be called
//							if a 3d object was on screen.  (watch for side effects)
//
//		07/20/97	JMI	Added some ASSERTs on m_sAlphaLevel in Render2D().
//
//		07/03/97 JRD	Altered the load to update the pipeline based on the
//							3d scale for that level.
//
//		08/04/97	JMI	Now Render3D() verifies that a sprite has a valid m_pthing
//							before using it to display info on a sprite that could not
//							be drawn.
//
//		08/07/97	JMI	Moved global single instance of gdSCALE3D defined in
//							scene.cpp into scene as an instantiable member, m_dScale3d.
//							Also, now SetupPipeline() does what UpdatePipeline() used
//							to (got rid of UpdatePipeline()).
//
//		08/18/97	JMI	Added ASSERTs to make sure inserted sprite's layer number
//							is greater than or equal to 0.
//
//		08/18/97	JMI	If the InDeleteOnRender flag was specified in a sprite,
//							it was removed from the scene list, deleted, and then its
//							flags were checked for the XRay bit which it usually had
//							because it was memset'ed to 0xdd on deletion.  This caused
//							it to be the XRayee.  Fixed.
//							Also, made the Render() call used for blitting things into
//							the background call the basic Render().
//							Also, increased MODEL_DIAMETER to 18.0 (was 16.0).
//
//		08/18/97	JMI	Changed Render() used by dead things to update themselves
//							into the background to be called DeadRender3D() to better
//							describe what it does and differentiate it from the other
//							more generic Render()ers.  Also, added two parameters so
//							it could call itself with 3D children.
//
//		08/23/97	JMI	Now offsets child light offset by the parent's.
//
//		10/03/99	JMI	Changed Render3D() to take a light scheme instead of a hood
//							to make it more general.
//
////////////////////////////////////////////////////////////////////////////////
#define SCENE_CPP

#include "RSPiX.h"
#include "scene.h"
#include "game.h"
#include "reality.h"

#include "pvr.h"

////////////////////////////////////////////////////////////////////////////////
// Macros/types/etc.
////////////////////////////////////////////////////////////////////////////////

#define SCREEN2MODEL_RATIO (4.5 * m_dScale3d)

// This represents the number of "Randy units" represented by
// SCREEN_DIAMETER_FOR_3D.
#define MODEL_DIAMETER 20.0 // In "Randy Units".
#define MODEL_ZSPAN 30.0	// must NEVER be exceeded or lighting is hosed

// Diameter in pixels for screen for 3D objects.
// Should be largest to hold largest 3D object.
#define SCREEN_DIAMETER_FOR_3D (MODEL_DIAMETER * SCREEN2MODEL_RATIO)

int16_t gsGlobalBrightnessPerLightAttribute = 5;
/* short gsGlobalLightingAdjustment = 128; /* neutral center */
// NOTE: This max value completely depends on the actual lighting effect curve:
// it should be set near the bright spot of the curve.
int16_t gsGlobalLightingAdjustment = 128; // NEUTRAL BABY!
//160 /* Max White */ - 6 * gsGlobalBrightnessPerLightAttribute;

// Used to control whether or not scene will actually blit.  When set to true,
// scene will execute quite a bit faster, except you won't see anything. :)
bool g_bSceneDontBlit;

// Font stuff.
#define FONT_CELL_HEIGHT 15
#define FONT_FORE_COLOR 250
#define FONT_BACK_COLOR 0
#define FONT_SHADOW_COLOR 0

const double c_dMaxScale = 10.0;
const double c_dMinScale = 0.2;

extern PVR::cRenderMgr *g_pvrRenderer;

////////////////////////////////////////////////////////////////////////////////
// Variables/data
////////////////////////////////////////////////////////////////////////////////
// Used to draw within the scene.
RPrint CScene::ms_print;

////////////////////////////////////////////////////////////////////////////////
// Default (and only) constructor
////////////////////////////////////////////////////////////////////////////////
CScene::CScene()
{
	// No array of layers yet
	m_pLayers = 0;
	m_sNumLayers = 0;

	// This can fail, so I had wanted to put it in SetupPipeline(), but there's
	// not much difference really since that function does not return an error
	// either.  Since this only needs to be done once, I decided to put it
	// here....let me know if you think it sucks.

	// Note that this function initializes m_dScale3d and other members.
	SetupPipeline();

	// If the static print has no font . . .
	if (ms_print.GetFont() == NULL)
	{
		ms_print.SetFont(FONT_CELL_HEIGHT, &g_fontBig);	
		ms_print.SetWordWrap(FALSE);
	}

	ms_print.SetEffectAbs(RPrint::SHADOW_X, 1);
	ms_print.SetEffectAbs(RPrint::SHADOW_Y, 1);
	ms_print.SetShadowColorRGB(0,0,0);

	// Default to something (anything)
	m_bXRayAll = false;
}

////////////////////////////////////////////////////////////////////////////////
// Destructor
////////////////////////////////////////////////////////////////////////////////
CScene::~CScene()
{
	Clear();
}

////////////////////////////////////////////////////////////////////////////////
// Clear the scene (clears out all layers and sprites)
////////////////////////////////////////////////////////////////////////////////
void CScene::Clear(void)
{
	// Clear all sprites from all layers
	RemoveAllSprites();

	// Delete the layers
	delete[] m_pLayers;
	m_pLayers = 0;
	m_sNumLayers = 0;
}

////////////////////////////////////////////////////////////////////////////////
// Remove all sprites (from all layers)
///////////////////////////////////////////////////////////////////////////////
void CScene::RemoveAllSprites(void)
{
	// Clear sprites from each layer
	for (int16_t s = 0; s < m_sNumLayers; s++)
		RemoveSprites(s);
}

////////////////////////////////////////////////////////////////////////////////
// Remove sprites from specified layer
////////////////////////////////////////////////////////////////////////////////
void CScene::RemoveSprites(
	int16_t sLayer) // In:  Layer number (0 to n-1)
{
	// Get pointer to layer
	Layer *pLayer = &(m_pLayers[sLayer]);

	// todo: clean up?
	// Go through layer's collection of 3D mesh sprites
	for (msetSprites::iterator i = pLayer->m_meshes.begin(); i != pLayer->m_meshes.end(); i++)
	{
		// Get pointer to sprite
		CSprite *pSprite = *i;

		// Clear inserted flag
		pSprite->m_sPrivFlags &= ~CSprite::PrivInserted;

		// Delete sprite if delete-on-clear flag is set
		if (pSprite->m_sInFlags & CSprite::InDeleteOnClear)
		{
			if (pSprite->m_type == CSprite::Standard2d)
			{
				CSprite2 *spr = ((CSprite2 *)(pSprite));
				if (spr->texture != nullptr && !spr->texture->m_bIsLocked)
				{
					delete spr->texture;
				}
			}
			delete pSprite;
		}
	}

	// Go through layer's collection of sprites
	for (msetSprites::iterator i = pLayer->m_sprites.begin(); i != pLayer->m_sprites.end(); i++)
	{
		// Get pointer to sprite
		CSprite *pSprite = *i;

		// Clear inserted flag
		pSprite->m_sPrivFlags &= ~CSprite::PrivInserted;

		// Delete sprite if delete-on-clear flag is set
		if (pSprite->m_sInFlags & CSprite::InDeleteOnClear)
		{
			if (pSprite->m_type == CSprite::Standard2d)
			{
				CSprite2 *spr = ((CSprite2 *)(pSprite));
				if (spr->texture != nullptr && !spr->texture->m_bIsLocked)
				{
					delete spr->texture;
				}
			}
			delete pSprite;
		}
	}

	// Clear the sprite container
#if _MSC_VER >= 1020
	pLayer->m_sprites.clear();
	pLayer->m_meshes.clear();
#else
	pLayer->m_sprites.erase(pLayer->m_sprites.begin(), pLayer->m_sprites.end());
	pLayer->m_sprites.clear();
	pLayer->m_meshes.erase(pLayer->m_meshes.begin(), pLayer->m_meshes.end());
	pLayer->m_meshes.clear();
#endif
}

////////////////////////////////////////////////////////////////////////////////
// Set number of layers (destroys any existing layers!)
////////////////////////////////////////////////////////////////////////////////
void CScene::SetLayers(
	int16_t sNumLayers) // In:  Number of layers
{
	// Validate parameters
	ASSERT(sNumLayers > 0);

	// Clear all layers (and sprites)
	Clear();

	// Create array of layers
	m_pLayers = new Layer[sNumLayers];
	m_sNumLayers = sNumLayers;
}

////////////////////////////////////////////////////////////////////////////////
// Update existing sprite or add new sprite
////////////////////////////////////////////////////////////////////////////////
void CScene::UpdateSprite(
	CSprite *pSprite) // In:  Sprite to add
{
	ASSERT(pSprite != NULL);

	// Check if it's already in the scene
	if (pSprite->m_sPrivFlags & CSprite::PrivInserted)
	{		
		// Check if layer has changed
		if (pSprite->m_sLayer != pSprite->m_sSavedLayer)
		{	
			// Erase from old layer
			switch (pSprite->m_type)
			{
			case CSprite::Standard3d:
				m_pLayers[pSprite->m_sSavedLayer].m_meshes.erase(pSprite->m_iter);
				break;
			default:
				m_pLayers[pSprite->m_sSavedLayer].m_sprites.erase(pSprite->m_iter);
				break;
			}

			// Add to specified layer and save iterator for fast access later on
			ASSERT(pSprite->m_sLayer < m_sNumLayers);
			ASSERT(pSprite->m_sLayer >= 0);

			switch (pSprite->m_type)
			{
			case CSprite::Standard3d:
				pSprite->m_iter = m_pLayers[pSprite->m_sLayer].m_meshes.insert(pSprite);
				break;
			default:
				pSprite->m_iter = m_pLayers[pSprite->m_sLayer].m_sprites.insert(pSprite);
				break;
			}

			pSprite->m_sSavedLayer = pSprite->m_sLayer;
		}

		// Check if priority has changed
		if (pSprite->m_sPriority != pSprite->m_sSavedPriority)
		{	
			// For now, just erase it and then re-insert it.  There is definitely
			// a faster way to do this, but since we'll likely be revamping the
			// entire draw-order logic, I'll leave it like this.
			switch (pSprite->m_type)
			{
			case CSprite::Standard3d:
				m_pLayers[pSprite->m_sSavedLayer].m_meshes.erase(pSprite->m_iter);
				pSprite->m_iter = m_pLayers[pSprite->m_sLayer].m_meshes.insert(pSprite);
				break;
			default:
				m_pLayers[pSprite->m_sSavedLayer].m_sprites.erase(pSprite->m_iter);
				pSprite->m_iter = m_pLayers[pSprite->m_sLayer].m_sprites.insert(pSprite);
				break;
			}

			pSprite->m_sSavedPriority = pSprite->m_sPriority;
		}
	}
	else
	{
		// Add to specified layer and save iterator for fast access later on
		ASSERT(pSprite->m_sLayer < m_sNumLayers);
		ASSERT(pSprite->m_sLayer >= 0);

		switch (pSprite->m_type)
		{
		case CSprite::Standard3d:
			pSprite->m_iter = m_pLayers[pSprite->m_sLayer].m_meshes.insert(pSprite);
			break;
		default:				
			pSprite->m_iter = m_pLayers[pSprite->m_sLayer].m_sprites.insert(pSprite);
			break;
		}

		// Save layer and priority so we can detect changes to them
		pSprite->m_sSavedLayer = pSprite->m_sLayer;
		pSprite->m_sSavedPriority = pSprite->m_sPriority;

		// Set inserted flag
		pSprite->m_sPrivFlags |= CSprite::PrivInserted;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Remove sprite (safe to call even if sprite isn't in scene).
////////////////////////////////////////////////////////////////////////////////
void CScene::RemoveSprite(
	CSprite *pSprite) // In:  Sprite to remove
{
	ASSERT(pSprite != NULL);

	// Make sure sprite is currently inserted -- if not, skip it.  This is
	// not treated as an error because there may be situations where the
	// caller doesn't know the sprite has already been removed from the layer,
	// such as when someone else has called Clear() or some similar function.
	if (pSprite->m_sPrivFlags & CSprite::PrivInserted)
	{
		// Erase sprite from layer.  Knowing the iterator makes this very fast.
		ASSERT(pSprite->m_sSavedLayer < m_sNumLayers);

		switch (pSprite->m_type)
		{
		case CSprite::Standard3d:
			m_pLayers[pSprite->m_sSavedLayer].m_meshes.erase(pSprite->m_iter);
			break;
		default:
			m_pLayers[pSprite->m_sSavedLayer].m_sprites.erase(pSprite->m_iter);
			break;
		}

		// Clear inserted flag
		pSprite->m_sPrivFlags &= ~CSprite::PrivInserted;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Render a 3D sprite.
//
// See the Render3D(..., RAlpha*, ...) for details.
//
////////////////////////////////////////////////////////////////////////////////
void // Returns nothing.
CScene::Render3D(
	int16_t sDstX,	   // Destination 2D x coord.
	int16_t sDstY,	   // Destination 2D y coord.
	CSprite3 *ps3Cur,  // 3D sprite to render.
	CHood *phood,	   // Da hood, homey.
	RRect *prcDstClip) // Dst clip rect.
{	
	pvr_poly_hdr_t *pPolyHdr;

	pPolyHdr = &phood->m_AmbientPolyHdr;	
	Render3D(		 // Returns happy thoughts.
		sDstX,		 // Destination 2D x coord.
		sDstY,		 // Destination 2D y coord.
		ps3Cur,		 // Tree of 3D sprites to render.
		pPolyHdr,
		prcDstClip); // Dst clip rect.
}

////////////////////////////////////////////////////////////////////////////////
// Render a 3D sprite.
//
// This function will, for a top level 3D sprite:
// 0) Clear the Z buffer.
//	1) Transform the bounding sphere to screen coordinates.
// 2) Ignore the sprite if the bounding sphere indicates the sprite is entirely
// offscreen.
// 3) Center the Render() on the bounding sphere to get maximum Z buffer room
// on X/Z plane.
// 4) Render into a clip image if the bounding sphere indicates the sprite is
// partially offscreen.  The clip image then gets rspBlitT'd to the composite
// buffer with clipping.
//
// For a non-top level 3D sprite:
// 1) Combine all parent transforms with this sprite's transform.
// 2) Transform the bounding sphere to screen coordinates.
// 3) Center the Render() on the top-level parent's bounding sphere so it matches
// the parents' position(s).
// 4) Render into a clip image if the bounding sphere indicates the sprite is
// partially offscreen.  The clip image then gets rspBlitT'd to the composite
// buffer with clipping.
//
////////////////////////////////////////////////////////////////////////////////
void // Returns nothing.
CScene::Render3D(
	int16_t sDstX,	   // Destination 2D x coord.
	int16_t sDstY,	   // Destination 2D y coord.
	CSprite3 *ps3Cur,  // 3D sprite to render.
	pvr_poly_hdr_t *pPolyHeader, // Light to render with.	
	RRect *prcDstClip) // Dst clip rect.
{
	// This transform is the product of the parent and the
	// child's transform used to determine the position of
	// a child object absolutely.
	RTransform transChildAbs;
	RTransform *ptransRender; // The transform used.

	int16_t sCurX;
	int16_t sCurY;
	RP3d pt3dSrcCenter, pt3dSrcRadius; // Center and point on outside of bounding
									   // sphere in "Randy" coords.
	int16_t sClipLeft;				   // Amount clipped off left edge of dest region.
	int16_t sClipTop;				   // Amount clipped off top edge of dest region.
	int16_t sClipRight;				   // Amount clipped off right edge of dest region.
	int16_t sClipBottom;			   // Amount clipped off bottom edge of dest region.
	int16_t sRadius;				   // Radius of bounding sphere in pixels.
	int16_t sDiameter;				   // Diameter of bounding sphere in pixels.
	int16_t sCenterX;				   // Center of bounding sphere as screen coord.
	int16_t sCenterY;				   // Center of bounding sphere as screen coord.
	//RImage *pimRender;				   // Image to render into.
	bool bIndirectRender;	  // Rendering to intermediate buffer.
	int16_t sIndirectRenderX; // Render coord when rendering to clip buffer.
	int16_t sIndirectRenderY; // Render coord when rendering to clip buffer.
	int16_t sDirectRenderZ;	  // Render Coord in either case
	int16_t sDirectRenderX;	  // Render coord when rendering to pimDst buffer.
	int16_t sDirectRenderY;	  // Render coord when rendering to pimDst buffer.
	int16_t sRenderX;		  // Render coord passed to Render().
	int16_t sRenderY;		  // Render coord passed to Render().
	int16_t sBlitX;			  // Blit pos for 3D model.
	int16_t sBlitY;			  // Biit pos for 3D model.
	int16_t sRenderOffX;	  // Offset to Render().
	int16_t sRenderOffY;	  // Offset to Render().

	ASSERT(ps3Cur->m_psop != NULL);
	ASSERT(ps3Cur->m_ptex != NULL);
	ASSERT(ps3Cur->m_pmesh != NULL);
	ASSERT(ps3Cur->m_ptrans != NULL);
	ASSERT(ps3Cur->m_psphere != NULL);
	
	// If there's a parent . . .
	if (ps3Cur->m_psprParent != NULL)
	{
		// NOTE: This does NOT work for more than 1 level of child depth.
		// To make that work, we must put a transform in the CSprite3.
		// Apply child and parent to transChildAbs.
		transChildAbs.Mul(((CSprite3 *)ps3Cur->m_psprParent)->m_ptrans->T, ps3Cur->m_ptrans->T);

		// Use transChildAbs.
		ptransRender = &transChildAbs;
	}
	else
	{
		// Use current top-level matrix.
		ptransRender = ps3Cur->m_ptrans;
	}

	// Here's the deal with m_pipeline. :
	// X == Origin.
	// @ == Center of sphere of points.
	//	 ________________m_pimClipBuf_________________________
	//	|			(m_sX, m_sY)	(m_sX + m_sW, m_sY)		 |
	//	|					 ___________					 |
	//	|					|    ooo    |					 |
	//	|					|   o\o/o   |					 |
	//	|					|    o*o    |					 |
	//	|					|     o     |					 |
	//	|					|ooooo@ooooo|					 |
	//	|					|     o     |					 |
	//	|					|    o o    |					 |
	//	|					|   o   o   |					 |
	//	|					|_oo__X__oo_|					 |
	//	|	(m_sX, m_sY + m_sH)	(m_sX + m_sW, m_sY + m_sH)	 |
	//	|													 |
	//	|													 |
	//	|													 |
	//	|													 |
	//	|													 |
	//	|													 |
	//	|													 |
	//	|													 |
	//	|													 |
	//	|____________________________________________________|

	// Get the bounding info.
	// Setup src pts.

	// Get current sphere.
	pt3dSrcCenter = *(ps3Cur->m_psphere);
	pt3dSrcRadius.x = pt3dSrcCenter.x + pt3dSrcCenter.w;
	pt3dSrcRadius.y = pt3dSrcCenter.y + pt3dSrcCenter.w;
	pt3dSrcRadius.z = pt3dSrcCenter.z + pt3dSrcCenter.w;
	pt3dSrcRadius.w = 1;
	pt3dSrcCenter.w = 1;

	// Let the pipeline know of the bounding sphere.
	m_pipeline.BoundingSphereToScreen(pt3dSrcCenter, pt3dSrcRadius, *ptransRender);

	// Check screen location:
	// Get radius of sphere of points (SphOP).
	sDiameter = MAX(m_pipeline.m_sW, m_pipeline.m_sH);
	sRadius = sDiameter * 0.5f;

	// ****TEMP****
	if (g_bSceneDontBlit == false)
	{
		// ****END TEMP****

		// Determine destination of hotspot on screen relative to parent, if any.
		sCurX = ps3Cur->m_sX2 + sDstX;
		sCurY = ps3Cur->m_sY2 + sDstY;

		// Determine center of sphere of points relative to origin.
		int16_t sOrgRelCenX = (m_pipeline.m_sCenX - (int16_t)(SCREEN_DIAMETER_FOR_3D * 0.5f));
		int16_t sOrgRelCenY = (m_pipeline.m_sCenY - (int16_t)(SCREEN_DIAMETER_FOR_3D * 0.5f));
		// Determine center of sphere of points on screen.
		sCenterX = sCurX + sOrgRelCenX;
		sCenterY = sCurY + sOrgRelCenY;
		// Determine amount that is off of the clip rect.
		sClipLeft = prcDstClip->sX - (sCenterX - sRadius);
		sClipTop = prcDstClip->sY - (sCenterY - sRadius);
		sClipRight = (sCenterX + sRadius) - (prcDstClip->sX + prcDstClip->sW);
		sClipBottom = (sCenterY + sRadius) - (prcDstClip->sY + prcDstClip->sH);
		// If on screen at all . . .
		if (sClipLeft < sDiameter && sClipTop < sDiameter && sClipRight < sDiameter && sClipBottom < sDiameter)
		{
			// Transform pts through *ptransRender, view, and finally screen transforms.
			m_pipeline.Transform(
				ps3Cur->m_psop, // Sea of 3D points to form
				// mesh around.
				*ptransRender); // The transformation.

			// todo?
			// If there's no parent . . .
			if (ps3Cur->m_psprParent == NULL)
			{
				// Clear Z buffer for new 3D tree.
				//	m_pipeline.m_pZB->Clear();
			}

			// Determine destination for sprite on screen.
			// Get upper left hand corner by subtracting the radius from the screen center of points.
			sBlitX = sCenterX - sRadius;
			sBlitY = sCenterY - sRadius;

			//////////////////////////////////////////////////////////////////////////////
			// GLOBAL LIGHTING FACTOR: (ADAPTIVE)
			//////////////////////////////////////////////////////////////////////////////
			// Here we do dynamic light adjustment based on the current sphere.
			// Stage two aligns front of sphere:
			// *** RESTORING DO NON-ADJUSTMENT! ***
			// re-attempting center adjustment level
			int16_t sLightOffset = 0; // Must be initialized here (the other assignments are +=).
			//	- (m_pipeline.m_sCenZ + m_pipeline.m_sZ);

			// If no parent . . .
			if (ps3Cur->m_psprParent == NULL)
			{
				// For indirect Render:
				// Render so center of sphere is at center of clip image.
				sIndirectRenderX = 0;
				sIndirectRenderY = 0;
				// For direct Render:
				// Determine destination for render on screen.
				// The position you supply to Render() is one 3D screen radius
				// up and to the left.
				sDirectRenderX = sCenterX - (int16_t)(SCREEN_DIAMETER_FOR_3D * 0.5f);
				sDirectRenderY = sCenterY - (int16_t)(SCREEN_DIAMETER_FOR_3D * 0.5f);
				sDirectRenderZ = m_pipeline.m_sCenZ; // don't need to know
				// Offset by the center relative to the origin.
				sRenderOffX = -sOrgRelCenX;
				sRenderOffY = -sOrgRelCenY;
			}
			else
			{
				// MUST BE 3D PARENT!
				ASSERT(ps3Cur->m_psprParent->m_type == CSprite::Standard3d);
				CSprite3 *ps3Parent = (CSprite3 *)(ps3Cur->m_psprParent);

				// Use the position the parent rendered at.
				sDirectRenderX = ps3Parent->m_sDirectRenderX;
				sDirectRenderY = ps3Parent->m_sDirectRenderY;
				sDirectRenderZ = ps3Parent->m_sDirectRenderZ;

				sIndirectRenderX = ps3Parent->m_sIndirectRenderX;
				sIndirectRenderY = ps3Parent->m_sIndirectRenderY;

				// Offset by the amount the parent was offset.
				sRenderOffX = ps3Parent->m_sRenderOffX;
				sRenderOffY = ps3Parent->m_sRenderOffY;

				// Offset lighting by parent's offset.
				sLightOffset += ps3Parent->m_sBrightness;

				ps3Cur->m_sLayer = ps3Parent->m_sLayer;
				ps3Cur->m_sPriority = ps3Parent->m_sPriority;
			}

			// If this sprite has children . . .
			if (ps3Cur->m_psprHeadChild != NULL)
			{
				// Store Render() positions for children to use.
				ps3Cur->m_sDirectRenderX = sDirectRenderX;
				ps3Cur->m_sDirectRenderY = sDirectRenderY;
				ps3Cur->m_sDirectRenderZ = sDirectRenderZ;
				ps3Cur->m_sIndirectRenderX = sIndirectRenderX;
				ps3Cur->m_sIndirectRenderY = sIndirectRenderY;
				// Store Render() offsets for children to use.
				ps3Cur->m_sRenderOffX = sRenderOffX;
				ps3Cur->m_sRenderOffY = sRenderOffY;
			}
												
			bIndirectRender = false;
			// Use appropriate position.
			sRenderX = sDirectRenderX;
			sRenderY = sDirectRenderY;
			
			// use either the current z center or parent to adjust:
			// Note that we've already offset this by the parent and that is why
			// we do a += (see above (search for sLightOffset) ).
			sLightOffset += ps3Cur->m_sBrightness + gsGlobalLightingAdjustment - sDirectRenderZ;
			
			if (m_pipeline.m_sCenX - sRadius >= -sRenderOffX && m_pipeline.m_sCenX + sRadius < SCREEN_DIAMETER_FOR_3D - sRenderOffX && m_pipeline.m_sCenY - sRadius >= -sRenderOffY && m_pipeline.m_sCenY + sRadius < SCREEN_DIAMETER_FOR_3D - sRenderOffY)
			{								
				// todo clean all of this up
				if (((sBlitX - sRenderOffX - (m_pipeline.m_sCenX)) + (m_pipeline.m_sW >> 1)) == sRenderX && ((sBlitY - sRenderOffY - (m_pipeline.m_sCenY)) + (m_pipeline.m_sH >> 1)) == sRenderY)
				{		
					bool bSpriteRemoved = false;
					int8_t cFrameTokenLevel = g_pvrRenderer->CurrentFrameTokenLevel();
					int8_t cFrameToken = g_pvrRenderer->CurrentFrameToken();
					bool bAllowFrameTokenBypass = (												
						ps3Cur->m_cFrameTokenLevel == -1
						|| (cFrameToken == ps3Cur->m_cFrameToken && ps3Cur->m_cFrameTokenLevel <= cFrameTokenLevel)
						|| (cFrameTokenLevel && ps3Cur->m_cFrameTokenLevel > cFrameTokenLevel)						
					);
			
					if (!ps3Cur->m_bTokenFrameExcluded && ps3Cur->m_cFrameTokenLevelExclude > 0 && ps3Cur->m_cFrameTokenLevelExclude == cFrameTokenLevel)
					{
						ps3Cur->m_bTokenFrameExcluded = true;
						RemoveSprite(ps3Cur);
					}
					
					if (bSpriteRemoved)
					{
						// Force skip.						
						bAllowFrameTokenBypass = false;
						cFrameToken = 1;						
					}

					if ((cFrameToken == 0 || bAllowFrameTokenBypass))
					{					
						bool sRes =	m_pipeline.RenderPVR(
							sRenderX,		  // 2D Dst coord.
							sRenderY,		  // 2D Dst coord.
							ps3Cur->m_pmesh,  // Src mesh.
							ps3Cur->m_sLayer, // Z buffer (use its own for now).
							ps3Cur->m_ptex,	  // Textures.
							sLightOffset,	  // Fog offset.  Fogool?
							pPolyHeader,	  // Ambient lighting schtuff.
							sRenderOffX,	  // Offset render/z-buffer to center of sphere of points.
							sRenderOffY,
							ps3Cur->m_sPriority); // Offset render/z-buffer to center of sphere of points.	
						
						if (sRes == false)
						{
							ps3Cur->m_cFrameToken = g_pvrRenderer->IssueFrameToken();
						}
					}						
				}			
			}			
		}

		// ****TEMP****
	}
	// ****END TEMP****

	// Store this location (it is used by Render() to do collision circle for XRay).
	ps3Cur->m_sCenX = ps3Cur->m_sX2 + m_pipeline.m_sCenX - (int16_t)(SCREEN_DIAMETER_FOR_3D * 0.5f);
	ps3Cur->m_sCenY = ps3Cur->m_sY2 + m_pipeline.m_sCenY - (int16_t)(SCREEN_DIAMETER_FOR_3D * 0.5f);
	ps3Cur->m_sRadius = sRadius;
}

////////////////////////////////////////////////////////////////////////////////
// Line function for 3D line.
////////////////////////////////////////////////////////////////////////////////
inline void DrawLine3d( // Returns nothing.
	U8 u8Color,			// Color to draw line with.
	int16_t sDstX1,		// Start pt.
	int16_t sDstY1,		// Start pt.
	int16_t sDstZ1,		// Start pt.
	int16_t sDstX2,		// End pt.
	int16_t sDstY2,		// End pt.
	int16_t sDstZ2,		// End pt.
	CHood *phood,		// Da hood, homey.
	RRect *prcDstClip)	// Dst clip rect.
{
	int16_t s2dX1, s2dY1;
	phood->Map3Dto2D(sDstX1, sDstY1, sDstZ1, &s2dX1, &s2dY1);
	int16_t s2dX2, s2dY2;
	phood->Map3Dto2D(sDstX2, sDstY2, sDstZ2, &s2dX2, &s2dY2);

	uint8_t *R = g_pvrRenderer->m_pPaletteDefault->Red(u8Color);
	uint8_t *G = g_pvrRenderer->m_pPaletteDefault->Green(u8Color);
	uint8_t *B = g_pvrRenderer->m_pPaletteDefault->Blue(u8Color);

	g_pvrRenderer->DrawLine2D(
		PACK_ARGB8888(255, *R, *G, *B),
		s2dX1,
		s2dY1,
		s2dX2,
		s2dY2,
		1,
		0.9f);
}

////////////////////////////////////////////////////////////////////////////////
// Draw a 2D line.
////////////////////////////////////////////////////////////////////////////////
void CScene::Line2D(	 // Returns nothing.
	int16_t sDstX,		 // Destination 2D x coord.
	int16_t sDstY,		 // Destination 2D y coord.
	CSpriteLine2d *psl2, // Tree of sprites to render.
	RRect *prcDstClip)	 // Dst clip rect.
{

// todo: the colour should be a sprite member - no need to calculate at draw time. What about global scaling?
	uint8_t index = psl2->m_u8Color;
	uint8_t *R = g_pvrRenderer->m_pPaletteDefault->Red(index);
	uint8_t *G = g_pvrRenderer->m_pPaletteDefault->Green(index);
	uint8_t *B = g_pvrRenderer->m_pPaletteDefault->Blue(index);
	uint8_t A = uint8_t(round(rspFastDiv(float(psl2->m_sAlphaLevel), 256.f)));

	g_pvrRenderer->DrawLine2DDirect(
		PACK_ARGB8888(150, *R, *G, *B),
		psl2->m_sX2 + sDstX,
		psl2->m_sY2 + sDstY,
		psl2->m_sX2End + sDstX,
		psl2->m_sY2End + sDstY,
		psl2->m_sLayer,
		1.f,
		psl2->m_thickness);

	// todo: clean up
		g_pvrRenderer->DrawLine2DDirect(
		PACK_ARGB8888(150, *R, *G, *B),
		psl2->m_sX2End + sDstX,
		psl2->m_sY2End + sDstY,
		psl2->m_sX2 + sDstX,
		psl2->m_sY2 + sDstY,		
		psl2->m_sLayer,
		1.f,
		psl2->m_thickness);


/*	g_pvrRenderer->DrawLine2D(
		PACK_ARGB8888(150, *R, *G, *B),
		psl2->m_sX2 + sDstX,
		psl2->m_sY2 + sDstY,
		psl2->m_sX2End + sDstX,
		psl2->m_sY2End + sDstY,
		psl2->m_sLayer,
		1.f,
		psl2->m_thickness);

		*/
}

////////////////////////////////////////////////////////////////////////////////
// Draw a 3D (as if there were another kind) cylinder.
// Actually, it draws a box from a cylinder description.
////////////////////////////////////////////////////////////////////////////////
void CScene::RenderCylinder3D( // Returns nothing.
	int16_t sDstX,			   // Destination 2D x coord.
	int16_t sDstY,			   // Destination 2D y coord.
	CSpriteCylinder3d *psc3,   // Cylinder sprite.
	CHood *phood,			   // Da hood, homey.
	RRect *prcDstClip)		   // Dst clip rect.
{
	int16_t sX = sDstX + psc3->m_sX2;
	int16_t sZ = sDstY + psc3->m_sY2;

	// Determine points.
	int16_t sX1 = sX - psc3->m_sRadius;
	int16_t sX2 = sX + psc3->m_sRadius;
	int16_t sY1 = 0;
	int16_t sY2 = psc3->m_sHeight;
	int16_t sZ1 = sZ - psc3->m_sRadius;
	int16_t sZ2 = sZ + psc3->m_sRadius;
	
	// Base box.
	DrawLine3d(
		psc3->m_u8Color,
		sX,
		sY1,
		sZ1,
		sX2,
		sY1,
		sZ,
		phood,
		prcDstClip);

	DrawLine3d(
		psc3->m_u8Color,
		sX2,
		sY1,
		sZ,
		sX,
		sY1,
		sZ2,
		phood,
		prcDstClip);

	DrawLine3d(
		psc3->m_u8Color,
		sX,
		sY1,
		sZ2,
		sX1,
		sY1,
		sZ,
		phood,
		prcDstClip);

	DrawLine3d(
		psc3->m_u8Color,
		sX1,
		sY1,
		sZ,
		sX,
		sY1,
		sZ1,
		phood,
		prcDstClip);

	// Top box.
	DrawLine3d(
		psc3->m_u8Color,
		sX,
		sY2,
		sZ1,
		sX2,
		sY2,
		sZ,
		phood,
		prcDstClip);

	DrawLine3d(
		psc3->m_u8Color,
		sX2,
		sY2,
		sZ,
		sX,
		sY2,
		sZ2,
		phood,
		prcDstClip);

	DrawLine3d(
		psc3->m_u8Color,
		sX,
		sY2,
		sZ2,
		sX1,
		sY2,
		sZ,
		phood,
		prcDstClip);

	DrawLine3d(
		psc3->m_u8Color,
		sX1,
		sY2,
		sZ,
		sX,
		sY2,
		sZ1,
		phood,
		prcDstClip);

	// Height edges.
	DrawLine3d(
		psc3->m_u8Color,
		sX,
		sY1,
		sZ1,
		sX,
		sY2,
		sZ1,
		phood,
		prcDstClip);

	DrawLine3d(
		psc3->m_u8Color,
		sX2,
		sY1,
		sZ,
		sX2,
		sY2,
		sZ,
		phood,
		prcDstClip);

	DrawLine3d(
		psc3->m_u8Color,
		sX,
		sY1,
		sZ2,
		sX,
		sY2,
		sZ2,
		phood,
		prcDstClip);

	DrawLine3d(
		psc3->m_u8Color,
		sX1,
		sY1,
		sZ,
		sX1,
		sY2,
		sZ,
		phood,
		prcDstClip);
}

////////////////////////////////////////////////////////////////////////////////
// Render a 2D sprite.
////////////////////////////////////////////////////////////////////////////////
void CScene::Render2D(	 // Returns nothing.
	int16_t sDstX,		 // Destination 2D x coord.
	int16_t sDstY,		 // Destination 2D y coord.
	CSprite2 *ps2Cur,	 // Tree of sprites to render.
	CHood *phood,		 // Da hood, homey.
	RRect *prcDstClip,	 // Dst clip rect.
	CSprite**	psXRayees,
	int8_t cNumXRayees)
{
	// Make sure the stuff we need is there.
	ASSERT(ps2Cur->m_pImage != NULL);

	ASSERT(ps2Cur->m_sAlphaLevel >= 0);
	ASSERT(ps2Cur->m_sAlphaLevel <= 255);

	if (ps2Cur->imageWidth == 0)
	{		
		return;
	}

	int x1 = ps2Cur->m_sX2 + sDstX;
	int x2 = ps2Cur->m_sX2 + sDstX + ps2Cur->imageWidth;
	int y1 = ps2Cur->m_sY2 + sDstY;
	int y2 = ps2Cur->m_sY2 + sDstY + ps2Cur->imageHeight;

	uint8_t r = uint8_t(255.f * g_pvrRenderer->m_fGlobalRScale);
	uint8_t g = uint8_t(255.f * g_pvrRenderer->m_fGlobalGScale);
	uint8_t b = uint8_t(255.f * g_pvrRenderer->m_fGlobalBScale);

	int16_t sX = (ps2Cur->m_sX2 + sDstX);
	int16_t sY = (ps2Cur->m_sY2 + sDstY);

// todo: cleanup?

	bool inClip = false;

	if (ps2Cur->m_sInFlags & CSprite::InClampToScreen)
	{
		inClip = true;

		int16_t minY = 40;
		int16_t minX = 20;
		int16_t maxY = 440;
		int16_t maxX = 610;
		
		if (sY <= minY)
			sY = minY;					
		if (sY >= maxY)
			sY = maxY;			
		if (sX <= minX)
			sX = minX;
		if(sX >= maxX)
			sX = maxX;									
	}
	else
	{
		inClip = (((x1 >= prcDstClip->sX || x2 >= prcDstClip->sX) && (x1 <= prcDstClip->sX + prcDstClip->sW || x2 <= prcDstClip->sX + prcDstClip->sW)) && ((y1 >= prcDstClip->sY || y2 >= prcDstClip->sY) && (y1 <= prcDstClip->sY + prcDstClip->sH || y2 <= prcDstClip->sY + prcDstClip->sH)));
	}
	
	if (!inClip)
	{
		ps2Cur->FlushXrayGrid();
		return;
	}

	// Transparent quad
	if (ps2Cur->m_sAlphaLevel < 255)
	{	
		bool bSpriteRemoved = false;
		int8_t cFrameTokenLevel = g_pvrRenderer->CurrentFrameTokenLevel();
		int8_t cFrameToken = g_pvrRenderer->CurrentFrameToken();
		
		bool bAllowFrameTokenBypass = (												
			ps2Cur->m_cFrameTokenLevel == -1
				|| (cFrameToken == ps2Cur->m_cFrameToken && ps2Cur->m_cFrameTokenLevel <= cFrameTokenLevel)
				|| (cFrameTokenLevel && ps2Cur->m_cFrameTokenLevel > cFrameTokenLevel)						
			);

		//bool bAllowFrameTokenBypass = true;			
		if (!ps2Cur->m_bTokenFrameExcluded && ps2Cur->m_cFrameTokenLevelExclude > 0 && ps2Cur->m_cFrameTokenLevelExclude == cFrameTokenLevel)
		{
			ps2Cur->m_bTokenFrameExcluded = true;
			bSpriteRemoved = true;
			RemoveSprite(ps2Cur);
		}

		if (bSpriteRemoved)
		{
			// Force skip.
			bAllowFrameTokenBypass = false;
			cFrameToken = 1;
		}

		if (!bAllowFrameTokenBypass)
		{
			ps2Cur->m_cFrameToken = g_pvrRenderer->IssueFrameToken();
		}


		if ((cFrameToken == 0 || bAllowFrameTokenBypass))
		{

			if (ps2Cur->texture != nullptr)
			{

				uint32_t colour;
				if (ps2Cur->m_cR > 0 || ps2Cur->m_cG > 0 || ps2Cur->m_cB > 0)
				{					
					colour = PACK_ARGB8888(
				ps2Cur->m_sAlphaLevel, 
				uint8_t(float(ps2Cur->m_cR) * g_pvrRenderer->m_fGlobalRScale),				
				uint8_t(float(ps2Cur->m_cG) * g_pvrRenderer->m_fGlobalGScale),
				uint8_t(float(ps2Cur->m_cB) * g_pvrRenderer->m_fGlobalBScale)
			);
				}
				else
				 colour = PACK_ARGB8888(ps2Cur->m_sAlphaLevel, r, g,b);

			g_pvrRenderer->DrawQuadDirect(
				ps2Cur->texture,
				ps2Cur->tileId,
				ps2Cur->startU,
				ps2Cur->startV,
				ps2Cur->endU,
				ps2Cur->endV,
				sX,
				sY,
				ps2Cur->imageWidth,
				ps2Cur->imageHeight,
				ps2Cur->m_sLayer,
				rspFastDiv(float(ps2Cur->m_sAlphaLevel), 255.0f),
				colour
			);
			}
			else
			{			
			uint32_t colour = PACK_ARGB8888(
				ps2Cur->m_sAlphaLevel, 
				uint8_t(float(ps2Cur->m_cR) * g_pvrRenderer->m_fGlobalRScale),				
				uint8_t(float(ps2Cur->m_cG) * g_pvrRenderer->m_fGlobalGScale),
				uint8_t(float(ps2Cur->m_cB) * g_pvrRenderer->m_fGlobalBScale)
			);
			
			g_pvrRenderer->DrawQuadDirect(
				colour,
				sX,
				sY,
				uint16_t(ps2Cur->imageWidth),
				uint16_t(ps2Cur->imageHeight),
				ps2Cur->m_sLayer			
			);
			}
		}

	}
	// Opaque quad
	else
	{
		if (ps2Cur->texture != nullptr && ps2Cur->imageWidth > 0)
		{
			// If this item is alpha OR this item is an opaque and we are
			// xraying opaques AND there is an xrayee AND the xray effect is enabled,
			// we need to see if the xray should be used via collision detection.
			int16_t sNormalBlit = 1;
									
			if ( ((ps2Cur->m_sInFlags & CSprite::InAlpha) || ((ps2Cur->m_sInFlags & CSprite::InOpaque) && m_bXRayAll == true)) && cNumXRayees && (g_GameSettings.m_sXRayEffect != FALSE || m_bXRayAll == true))
			{
				// Check if this sprite collides with the xrayee sprite.  If so,
				// we need to do draw this sprite with the xray effect so the
				// xrayee(s) can be seen through it.
				// todo: magic numbers
				int16_t sXRayeeW = 200;
				int16_t sXRayeeH = 200;
				float maxDistance = 100.f;				
				CSprite3* pLocalXRayees[4];
				int8_t cNumLocalXRayees = 0;

				for (int8_t index = 0; index < cNumXRayees; index++)
				{
					CSprite3 *ps3XRayee = (CSprite3 *)psXRayees[index];
					int16_t sXRayeeX = (ps3XRayee->m_sCenX - (100)) + sDstX;
					int16_t sXRayeeY = (ps3XRayee->m_sCenY - (100)) + sDstY;
					// Do an exteremely cheesy collision detecting by clipping the two rects!!!!
					RRect rect1(sXRayeeX, sXRayeeY, sXRayeeW, sXRayeeH);
					RRect rect2(ps2Cur->m_sX2 + sDstX, ps2Cur->m_sY2 + sDstY, ps2Cur->imageWidth, ps2Cur->imageHeight);
					if (rect2.ClipTo(&rect1) != -1 && ps3XRayee->m_sLayer <= ps2Cur->m_sLayer)
					{
						pLocalXRayees[cNumLocalXRayees++] = ps3XRayee;
					}
				}
																												
				if (cNumLocalXRayees)
				{
					// Clear flag so he doesn't get drawn by normal blit
					sNormalBlit = 0;

					if (!ps2Cur->m_bHasXrayGrid)											
					{						
						ps2Cur->InitXrayGrid((g_pvrRenderer->m_lNumVerts < 20000) ? 5 : 20);
					}
					else if (g_pvrRenderer->m_lNumVerts >= 20000 && !ps2Cur->m_bXrayDownGraded)
					{						
						ps2Cur->FlushXrayGrid();
						ps2Cur->InitXrayGrid(20);
						ps2Cur->m_bXrayDownGraded = true;
					}
					
					PVR::cTextureTile *pTile = ps2Cur->texture->m_pTextureTiles[0];						
					pvr_dr_state_t state;
					pvr_prim(pTile->GetPvrHdr(1), sizeof(pvr_poly_hdr_t));						
					pvr_dr_init(state);
					
					float fMinOpacity = 1.f;				
					int32_t lCell = 0;
					
					CXrayCell *pCell = ps2Cur->ms_listXrayCells[0];
					CXrayCell *pNext = nullptr;

					if (g_pvrRenderer->m_lNumVerts + (ps2Cur->m_lNumXrayCells*4) < 28000)
					{
						while (pCell)					
						{												
							if (++lCell < ps2Cur->m_lNumXrayCells)
							{
								pNext = ps2Cur->ms_listXrayCells[lCell];
  								__asm__ __volatile__ (
    								"fschg\n\t"
									"pref @%[cell]\n\t"
    								"fschg\n"
									:
    								: [cell] "r" (pNext)
    								: "memory" // clobbers
  								);
							}
							else
							{
								pNext = nullptr;
							}

							pCell->m_fOpacity = 1.f;
							fMinOpacity = 1.f;

							for (int index = 0; index < cNumLocalXRayees; index++)
							{
								CSprite3 *ps3XRayee = pLocalXRayees[index];
								int16_t sXRayeeX = (ps3XRayee->m_sCenX - (100)) + sDstX;
								int16_t sXRayeeY = (ps3XRayee->m_sCenY - (100)) + sDstY;
								int16_t dX = (ps2Cur->m_sX2 + sDstX) + (pCell->m_sCellX) - (sXRayeeX + (100));
								int16_t dY = (ps2Cur->m_sY2 + sDstY) + (pCell->m_sCellY) - (sXRayeeY + (100));
								int16_t dSquareDistance = rspSqrt((dX * dX) + (dY * dY));
							
								if (dSquareDistance <= maxDistance)
								{								
									float fOpacity = 1.f * rspFastDiv(float(dSquareDistance), float(maxDistance));
									if (fOpacity < fMinOpacity)
									{
										fMinOpacity = fOpacity;
									}
								}
							}

							pCell->m_fOpacity = fMinOpacity;
							if (pCell->m_fOpacity > 1.f)
							{
								pCell->m_fOpacity = 1.f;
							}									
			
							g_pvrRenderer->DrawTileDirectPrePrim(
								pTile,
								&state,
								pCell->m_fStartU,
								pCell->m_fStartV,
								pCell->m_fEndU,
								pCell->m_fEndV,
								sX + pCell->m_sCellX,
								sY + pCell->m_sCellY,
								pCell->m_cCellWidth,
								pCell->m_cCellHeight,								
								ps2Cur->m_sLayer, 								
								pCell->m_fOpacity,
								PACK_ARGB8888(uint8_t(pCell->m_fOpacity * 255.f), r, g,b)
							);
							
							// Next, please.
							pCell = pNext;							
						}										
					}
					else
					{
						/*if (ps2Cur->m_bHasXrayGrid)
						{
							for (int index = 0; index < cNumXRayees; index++)
							{
								CSprite3 *ps3XRayee = (CSprite3 *)psXRayees[index];						
								int16_t sXRayeeX = ps3XRayee->m_sX2; // Default to 2D blit location.
								int16_t sXRayeeY = ps3XRayee->m_sY2; // Default to 2D blit location.					
								sXRayeeX = (ps3XRayee->m_sCenX - (100)) + sDstX;
								sXRayeeY = (ps3XRayee->m_sCenY - (100)) + sDstY;

								int16_t dX = (ps2Cur->m_sX2 + sDstX) + (ps2Cur->imageWidth >> 1) - (sXRayeeX + (100));
								int16_t dY = (ps2Cur->m_sY2 + sDstY) + (ps2Cur->imageHeight >> 1) - (sXRayeeY + (100));
								int16_t dSquareDistance = rspSqrt((dX * dX) + (dY * dY));
					
								// Only clear the xray grid when significantly out of range
								if (dSquareDistance > 250)
								{
									ps2Cur->FlushXrayGrid();
								}							
							}					
						}*/

						g_pvrRenderer->DrawQuadDirect(
							ps2Cur->texture,
							sX,
							sY,
							ps2Cur->imageWidth,
							ps2Cur->imageHeight,
							ps2Cur->m_sLayer, 1.0f, PACK_ARGB8888(255, r, g,b));			
					}					
				}				
			}

			if (sNormalBlit)
			{
				if (ps2Cur->m_bHasXrayGrid)
				{
					ps2Cur->FlushXrayGrid();

					for (int index = 0; index < cNumXRayees; index++)
					{
						CSprite3 *ps3XRayee = (CSprite3 *)psXRayees[index];						
						int16_t sXRayeeX = ps3XRayee->m_sX2; // Default to 2D blit location.
						int16_t sXRayeeY = ps3XRayee->m_sY2; // Default to 2D blit location.					
						sXRayeeX = (ps3XRayee->m_sCenX - (100)) + sDstX;
						sXRayeeY = (ps3XRayee->m_sCenY - (100)) + sDstY;

						int16_t dX = (ps2Cur->m_sX2 + sDstX) + (ps2Cur->imageWidth >> 1) - (sXRayeeX + (100));
						int16_t dY = (ps2Cur->m_sY2 + sDstY) + (ps2Cur->imageHeight >> 1) - (sXRayeeY + (100));
						int16_t dSquareDistance = rspSqrt((dX * dX) + (dY * dY));
					
						// Only clear the xray grid when significantly out of range
						if (dSquareDistance > 250)
						{
							ps2Cur->FlushXrayGrid();
						}							
					}					
				}

				g_pvrRenderer->DrawQuadDirect(
					ps2Cur->texture,
					sX,
					sY,
					ps2Cur->imageWidth,
					ps2Cur->imageHeight,
					ps2Cur->m_sLayer, 1.0f, PACK_ARGB8888(255, r, g,b));					
			}
		}
		else if (inClip)
		{			
			//g_pvrRenderer->DrawDebugQuad(0, sX, sY, 20, 20, ps2Cur->m_sLayer);
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Render a sprite tree.
////////////////////////////////////////////////////////////////////////////////
void CScene::Render(	 // Returns nothing.
	int16_t sDstX,		 // Destination 2D x coord.
	int16_t sDstY,		 // Destination 2D y coord.
	CSprite *pSprite,	 // Tree of sprites to render.
	CHood *phood,		 // Da hood, homey.
	RRect *prcDstClip,	 // Dst clip rect.
	CSprite **psprXRayees,
	int8_t cNumXRayees)
{			
	float globalR = g_pvrRenderer->m_fGlobalRScale;
	float globalG = g_pvrRenderer->m_fGlobalGScale;
	float globalB = g_pvrRenderer->m_fGlobalBScale;

	while (pSprite != NULL)
	{
		// Make sure sprite isn't hidden (if it is, skip it)
		if (!(pSprite->m_sInFlags & CSprite::InHidden))
		{		
			// Transparent 2D sprites can be children of 3D sprites, which are all drawn in the opaque list.
			// So we detach them here and dispatch them to the transparent list to be drawn.
			if (pSprite->m_sInFlags & CSprite::InTransparentList && g_pvrRenderer->m_szDrawMode == PVR::cRenderMgr::DirectOpaque)
			{																										
				((CSprite2 *)pSprite)->sDstXOverride = sDstX;
				((CSprite2 *)pSprite)->sDstYOverride = sDstY;
				UpdateSprite(pSprite);

				// We detach the child after getting the parent's coords.
				if (pSprite->m_psprParent)
				{
					CSprite *tmp = pSprite->m_psprNext;
					pSprite->m_psprPrevParent = pSprite->m_psprParent;
					pSprite->m_psprParent->RemoveChild(pSprite);					
					pSprite = tmp;
				}			
				else
				{
					pSprite = pSprite->m_psprNext;
				}
								
				// todo: probab;ly not efficient?
				continue;																				
			}
			
			
			// Set flag to indicate sprite was rendered.  As of right now, the sprite WILL
			// be rendered if we get this far, but if the logic changes, the setting of
			// this flag might need to be moved to somewhere else.
			pSprite->m_sOutFlags |= CSprite::OutRendered;

			// Determine whether this is a 2d or 3d sprite
			switch (pSprite->m_type)
			{
			case CSprite::Standard2d:
			{
				// ****TEMP****
				if (g_bSceneDontBlit == false)
				{
					CSprite2 *sprite = (CSprite2 *)pSprite;
					if (sprite->sDstXOverride || sprite->sDstYOverride)
					{
						sDstX = sprite->sDstXOverride;
						sDstY = sprite->sDstYOverride;
						sprite->sDstXOverride = 0;
						sprite->sDstYOverride = 0;							
					}
		
					// ****END TEMP****
					Render2D(				 // Returns nothing.
						sDstX,				 // Destination 2D x coord.
						sDstY,				 // Destination 2D y coord.
						sprite,
						//(CSprite2 *)pSprite, // Tree of sprites to render.
						phood,				 // Da hood, homey.
						prcDstClip,			 // Dst clip rect.
						psprXRayees,
						cNumXRayees);		 // XRayee, if not NULL.
											 // ****TEMP****							
				}
				// ****END TEMP****

				break; // CSprite::Standard2d.
			}
			case CSprite::Standard3d:	 // *** g_bSceneDontBlit is checked inside Render3D() 10/03/99 ***
		
				if (((CSprite3 *)(pSprite))->m_fScaleG != 1.f)
				{
					g_pvrRenderer->m_fGlobalRScale = ((CSprite3 *)(pSprite))->m_fScaleR;
					g_pvrRenderer->m_fGlobalGScale = ((CSprite3 *)(pSprite))->m_fScaleG;
					g_pvrRenderer->m_fGlobalBScale = ((CSprite3 *)(pSprite))->m_fScaleB;
				}

				Render3D(				 // Returns happy thoughts.
					sDstX,				 // Destination 2D x coord.
					sDstY,				 // Destination 2D y coord.
					(CSprite3 *)pSprite, // Tree of 3D sprites to render.
					phood,				 // Da hood, homey.
					prcDstClip);		 // Dst clip rect.

				
				g_pvrRenderer->m_fGlobalRScale = globalR;
				g_pvrRenderer->m_fGlobalGScale = globalG;
				g_pvrRenderer->m_fGlobalBScale = globalB;					

				break; // CSprite::Standard3d.
			case CSprite::Line2d:
			{
				// ****TEMP****
				if (g_bSceneDontBlit == false)
				{
					//todo?
					// ****END TEMP****
					Line2D(						  // Returns nothing.
						sDstX,					  // Destination 2D x coord.
						sDstY,					  // Destination 2D y coord.
						(CSpriteLine2d *)pSprite, // Tree of sprites to render.
						prcDstClip);			  // Dst clip rect.

					// ****TEMP****
				}
				// ****END TEMP****

				break;
			}

			case CSprite::Cylinder3d:
			{				
				if (g_bSceneDontBlit == false)
				{					
					RenderCylinder3D(				  // Returns nothing.
						sDstX,						  // Destination 2D x coord.
						sDstY,						  // Destination 2D y coord.
						(CSpriteCylinder3d *)pSprite, // Cylinder sprite.
						phood,						  // Da hood, homey.
						prcDstClip);				  // Dst clip rect.					
				}

				break;
			}
			}
			
			if (g_bSceneDontBlit == false)
			{		
				// If there's some text . . .
				if (pSprite->m_pszText != NULL)
				{					
					// Bind it.
					int16_t sX = pSprite->m_sX2 + sDstX;
					int16_t sY = pSprite->m_sY2 + sDstY;						
					uint8_t r = 0, g = 255, b = 0;	

					if (pSprite->m_sInFlags & CSprite::InClampToScreen)
					{
						
						int16_t minY = 40;
						int16_t minX = 20;
						int16_t maxY = 440;
						int16_t maxX = 610;

						// Clamp to screen dimensions.
						/*switch (g_GameSettings.m_cHudMode)
						{
							case 0:
								minY = 40;
								maxY = 400;
								minX = 20;
								maxX = 610;
							break;
							case 1:
								minY = 20;
								maxY = 460;
								minX = 20;
								maxX = 610;
							break;
						}*/

						if (sY <= minY)
							sY = minY;					
						if (sY >= maxY)
							sY = maxY;			
						if (sX <= minX)
							sX = minX;
						if(sX >= maxX)
							sX = maxX;									
					}

					if (pSprite->m_bOverrideTextColour)
					{
						r = pSprite->m_ucFontR;
						g = pSprite->m_ucFontG;
						b = pSprite->m_ucFontB;						
						if (g_pvrRenderer->m_fGlobalRScale != 1.f || g_pvrRenderer->m_fGlobalGScale != 1.f || g_pvrRenderer->m_fGlobalBScale != 1.f)
						{						
							ms_print.SetShadowColorRGB(
								uint8_t(float(pSprite->m_ucFontShadowR) * g_pvrRenderer->m_fGlobalRScale), 
								uint8_t(float(pSprite->m_ucFontShadowG) * g_pvrRenderer->m_fGlobalGScale), 
								uint8_t(float(pSprite->m_ucFontShadowB) * g_pvrRenderer->m_fGlobalBScale)
							);
						}
						else
						{																						
							ms_print.SetShadowColorRGB(pSprite->m_ucFontShadowR, pSprite->m_ucFontShadowG, pSprite->m_ucFontShadowB);
						}
					}					

					if (g_pvrRenderer->m_fGlobalRScale != 1.f || g_pvrRenderer->m_fGlobalGScale != 1.f || g_pvrRenderer->m_fGlobalBScale != 1.f)
					{						
						ms_print.SetColorRGB(
							uint8_t(float(r) * g_pvrRenderer->m_fGlobalRScale), 
							uint8_t(float(g) * g_pvrRenderer->m_fGlobalGScale), 
							uint8_t(float(b) * g_pvrRenderer->m_fGlobalBScale)
						);
					}
					else
					{																						
						ms_print.SetColorRGB(r, g, b);
					}		

					ms_print.offsetX = sX + pSprite->m_sTextOffsetX;
					ms_print.offsetY = sY + pSprite->m_sTextOffsetY;
					ms_print.m_cLayerOffset = ((CSprite3*) pSprite)->m_cTextLayerOffset;
					ms_print.SetFont(pSprite->m_sFontSize, &g_fontBig);												
					ms_print.SetColumn(0, 0, 50, 100);										
					ms_print.printPVR(0, 0, "%s", pSprite->m_pszText);					
				}				
			}			

			// If this sprite has any children . . .
			if (pSprite->m_psprHeadChild != NULL)
			{
				Render(						  // Returns nothing.
					sDstX + pSprite->m_sX2,	  // Destination 2D x coord.
					sDstY + pSprite->m_sY2,	  // Destination 2D y coord.
					pSprite->m_psprHeadChild, // Tree of sprites to render.
					phood,					  // Da hood, homey.
					prcDstClip,				  // Dst clip rect.
					psprXRayees,
					cNumXRayees);			  // XRayee, if not NULL.
			}
		}

		// Get sibling.
		pSprite = pSprite->m_psprNext;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Render specified area of scene into specified image
///////////////////////////////////////////////////////////////////////////////
void CScene::Render(
	int16_t sSrcX, // In:  Source (scene) x coord
	int16_t sSrcY, // In:  Source (scene) y coord
	int16_t sW,	   // In:  Width
	int16_t sH,	   // In:  Height
	int16_t sDstX, // In:  Destination (image) x coord
	int16_t sDstY, // In:  Destination (image) y coord
	CHood *phood)  // In:  The hood involved.
{
	// Init dst clipping rect
	RRect rDstClip(sDstX, sDstY, sW, sH);

	// Clip the dst clipping rect to the image size (in case image is smaller)
//	RRect rImageSize(0, 40, 640, 400);

//	rDstClip.ClipTo(&rImageSize);

	// Calculate mapping from scene to image coords
	int16_t sMapX = sSrcX - sDstX;
	int16_t sMapY = sSrcY - sDstY;

	CSprite *psprXRayee = NULL; // XRayee when not NULL.

	int8_t cNumXRayees = 0;
	CSprite *psprXRayees[4] = {NULL}; // XRayee when not NULL.

	// Draw opaque background tiles & patch sprites
	CSprite2 *ps2Cur = phood->backgroundSprite;
	
	uint32_t colour = PACK_ARGB8888(
					255, 
					(uint8_t)(255.f * g_pvrRenderer->m_fGlobalRScale),
					(uint8_t)(255.f * g_pvrRenderer->m_fGlobalGScale),
					(uint8_t)(255.f * g_pvrRenderer->m_fGlobalBScale)
				);	

	if (ps2Cur->texture != nullptr && !g_bSceneDontBlit)
	{						
		// Only opaque VQ BG tiles
		g_pvrRenderer->DrawTiledQuadDirect(
			ps2Cur->texture, 
			-sMapX, -sMapY, 
			PVR::cRenderMgr::LayerBg, 
			1.f, colour);
	}

	CSprite2 *patchSprites[3];
	patchSprites[0] = phood->backgroundPatchSprite1;
	patchSprites[1] = phood->backgroundPatchSprite2;
	patchSprites[2] = phood->backgroundPatchSprite3;	
	for (int8_t index = 0; index < 3; index++)
	{		
		CSprite2 *patchSprite = patchSprites[index];
		if (patchSprite && patchSprite->texture != nullptr && !g_bSceneDontBlit)
		{					
			g_pvrRenderer->DrawQuadDirect(			
				patchSprite->texture,
				//-sMapX + 377, -sMapY + 482,
				-sMapX + patchSprite->m_sX2, -sMapY + patchSprite->m_sY2,
				patchSprite->texture->GetImageWidth(), patchSprite->texture->GetImageHeight(), 
				PVR::cRenderMgr::LayerBg,
				1.f, colour);			
		}
	}

	// Go through all the layers, back to front
	for (int16_t sLayer = 0; sLayer < m_sNumLayers; sLayer++)
	{
		// Get pointer to layer (more readable)
		Layer *pLayer = &m_pLayers[sLayer];

		// Make sure layer isn't hidden (if it is, skip it)
		if (!(pLayer->m_bHidden))
		{
			// Go through all the 3D sprites stored in this layer
			for (msetSprites::iterator iSprite = pLayer->m_meshes.begin(); iSprite != pLayer->m_meshes.end();)
			{
				// Get pointer to sprite (more readable than iterator dereference and may optimize better)
				CSprite *pSprite = *iSprite;
			
				Render(			 // Returns nothing.
					-sMapX,		 // Destination 2D x coord.
					-sMapY,		 // Destination 2D y coord.
					pSprite,	 // Tree of sprites to render.
					phood,		 // Da hood, homey.
					&rDstClip,	 // Dst clip rect.
					psprXRayees, cNumXRayees); // XRayee, if not NULL.

				// Move to next item before deleting this one.
				iSprite++;

				// Only hide the sprite if it's actually been drawn.
				if (!g_pvrRenderer->m_bFrameSkip && pSprite->m_sInFlags & CSprite::InHideOnRender)
				{					
					pSprite->m_sInFlags |= CSprite::InHidden;
				}

				// If this sprite wanted to be deleted after use . . .
				if (pSprite->m_sInFlags & CSprite::InDeleteOnRender)
				{
					RemoveSprite(pSprite);				
					// Be gone, vile weed.
					delete pSprite;
				}
				else
				{
					// If this sprite is an xrayee, add it to the container
					if (pSprite->m_sInFlags & CSprite::InXrayee && cNumXRayees < 4)
					{
						psprXRayees[cNumXRayees++] = pSprite;
					}
				}
			}
		}
	}

	// todo?
	if (!g_bSceneDontBlit)
	{
		// End opaque draw list
		g_pvrRenderer->EndDirectDrawMode();
		// Begin transparent draw list
		g_pvrRenderer->BeginDirectDrawMode(PVR::cRenderMgr::DirectTransparent);	
	}		
	
	// Go through all the layers, back to front
	for (int16_t sLayer = 0; sLayer < m_sNumLayers; sLayer++)
	{
		// Get pointer to layer (more readable)
		Layer *pLayer = &m_pLayers[sLayer];

		// Make sure layer isn't hidden (if it is, skip it)
		if (!(pLayer->m_bHidden))
		{
			// Go through all the sprites stored in this layer
			for (msetSprites::iterator iSprite = pLayer->m_sprites.begin(); iSprite != pLayer->m_sprites.end();)
			{
				// Get pointer to sprite (more readable than iterator dereference and may optimize better)
				CSprite *pSprite = *iSprite;
				Render(			 // Returns nothing.
					-sMapX,		 // Destination 2D x coord.
					-sMapY,		 // Destination 2D y coord.
					pSprite,	 // Tree of sprites to render.
					phood,		 // Da hood, homey.
					&rDstClip,	 // Dst clip rect.
					psprXRayees, cNumXRayees); // XRayee, if not NULL.

				// Move to next item before deleting this one.
				iSprite++;

				// Only hide the sprite if it's actually been drawn.
				if (!g_pvrRenderer->m_bFrameSkip && pSprite->m_sInFlags & CSprite::InHideOnRender)
				{					
					pSprite->m_sInFlags |= CSprite::InHidden;
				}

				// If this sprite wanted to be deleted after use . . .
				if (pSprite->m_sInFlags & CSprite::InDeleteOnRender)
				{
					RemoveSprite(pSprite);
					// Be gone, vile weed.
					delete pSprite;
				}
				/*else
				{
					// If this sprite is an xrayee, add it to the container
					if (pSprite->m_sInFlags & CSprite::InXrayee)
					{
						psprXRayee = pSprite;
					}
				}*/
			}
		}
	}
																					
}

////////////////////////////////////////////////////////////////////////////////
// Setup render pipeline.  Use this function to setup or alter the pipeline.
// This function DOES a Make1() and then multiplies by the supplied transform,
// if any.  Any transforms that need to be applied after this setup can be
// done following a call to this function.
// The only sux is you cannot insert yourself into the middle of this function.
// If that's what you need, you should probably just do the entire setup for
// the pipe yourself or add a similar function to this one.
////////////////////////////////////////////////////////////////////////////////
void CScene::SetupPipeline(					  // Returns nothing.
	RTransform *ptrans /*= NULL*/,			  // Transform to apply before doing defaults.
	RTransform *ptransScene2Realm /*= NULL*/, // Transform to convert from scene to realm.
	double dScale3d /*= 0.0*/)				  // In:  New scaling to apply to pipeline (see// m_dScale3d declaration).
{
	/////////////////////////////////////////////////////////////////////////////
	// This portion was previously done in UpdatePipeline().
	/////////////////////////////////////////////////////////////////////////////
	// Adjust the current pipeline to account for the case when we've
	// loaded a new hood which may have a different 3d scaling for
	// the 3d characters.  This effects both the pipeline and the
	// smashatorium!
	/////////////////////////////////////////////////////////////////////////////
	// Some extra safety
	if (dScale3d <= c_dMinScale)
		dScale3d = c_dMinScale;
	if (dScale3d > c_dMaxScale)
		dScale3d = c_dMaxScale;

	ASSERT((dScale3d >= c_dMinScale) && (dScale3d <= c_dMaxScale));

	// Strap onto the old methods:
	m_dScale3d = dScale3d;

	// Use the built in adjustment features of the pipeline:
	if (m_pipeline.Create(1000, SCREEN_DIAMETER_FOR_3D) != 0)
		TRACE("SetupPipeline(): FONGOOL!  m_pipeline.Create() failed!  No 3D for you!\n");

	/////////////////////////////////////////////////////////////////////////////
	// End previously done in UpdatePipeline().
	/////////////////////////////////////////////////////////////////////////////

	// Re-init (Renit) the pipeline.
	m_pipeline.m_tView.Make1();	  // Identity.
	m_pipeline.m_tScreen.Make1(); // Identity.
	m_transNoZView.Make1();		  // Identity.
	m_transNoZScreen.Make1();	  // Identity.

	// If there is a user transform . . .
	if (ptrans != NULL)
	{
		// Apply now. Ok. Close.
		m_pipeline.m_tView.PreMulBy(ptrans->T);
	}

	// If there is a conversion transform . . .
	if (ptransScene2Realm != NULL)
	{
		m_transScene2Realm = *ptransScene2Realm;
	}
	else
	{
		m_transScene2Realm.Make1();
	}

	////////////////////////////////////////////////////////
	// Stolen from the famous 3D e-letters by Jeff Diamond.
	// We use this to get from SoftImage to Postal coord
	// system.
	////////////////////////////////////////////////////////

	// OK, this is a bit more professional way to do it:
	int16_t sScreenSize = SCREEN_DIAMETER_FOR_3D; // diameter in pixels;
												  //	short	sScreenRadius	= sScreenSize>>1;

	double dModelSize = MODEL_DIAMETER; // diameter in Randy's coordinates
	double dModelRadius = 0.5 * dModelSize;

	// Flipping the image in the view was a bad hack, bacause it
	// changes the direction of rotation about the axis:
	//	m_pipe.m_tView.Scale(1.0,-1.0,1.0); // DON'T DO!

	// Just put the Randy origin in the center of the cube:
	m_pipeline.m_tView.Trans(dModelRadius, dModelRadius, 0.0);
	// Mimic this in transform that contains no Z scaling.
	m_transNoZView.Trans(dModelRadius, dModelRadius, 0.0);

	// Then, to project to the screen, need to FIRST scale it,
	// THEN flip it about y=0, and FINALLY slide it back down sp
	// we can see it.  (Luckily, when you're down it's still a single
	// matrix, so it's no slower than anything else you might do.

	// Also, stretch out the z-coordinate to take full advantage of the
	// 16-bit z-buffer
	//
	m_pipeline.m_tScreen.Scale(
		rspFastDiv(double(sScreenSize), dModelSize),
		rspFastDiv(double(sScreenSize), dModelSize),
		2184.5f /*REAL(65535.0 / MODEL_ZSPAN)*/); // Use MODEL_ZSPAN for consistent fog thickness
	// Mimic this in transform that contains no Z scaling.
	m_transNoZScreen.Scale(
		rspFastDiv(double(sScreenSize), dModelSize),
		rspFastDiv(double(sScreenSize), dModelSize),
		rspFastDiv(double(sScreenSize), dModelSize));

	// Now offset the image and flip the y-coordinate:
	m_pipeline.m_tScreen.Scale(1.0, -1.0, 1.0); // mirror vertically about (y=0)
	// Mimic this in transform that contains no Z scaling.
	m_transNoZScreen.Scale(1.0, -1.0, 1.0);

	m_pipeline.m_tScreen.Trans(0.0, sScreenSize, 0.0); // slide down the screen
	// Mimic this in transform that contains no Z scaling.
	m_transNoZScreen.Trans(0.0, sScreenSize, 0.0);

	////////////////////////////////////////////////////////
	// More transforms can be applied to either of the
	// pipeline's transforms after this function exits, if
	// so desired.
	////////////////////////////////////////////////////////
}

////////////////////////////////////////////////////////////////////////////////
// Transform the given points through the CScene's pipeline with the
// supplied transform.
////////////////////////////////////////////////////////////////////////////////
void CScene::TransformPts( // Returns nothing.
	RTransform *ptrans,	   // In:  Transformation to apply to CScene's before
						   // transforming pts.
	RP3d *p3dPtsSrc,	   // In:  Ptr to group of pts to transform from.
	RP3d *p3dPtsDst,	   // Out: Ptr to group of pts to transform into.
	int16_t sNum)		   // In:  The number of pts in p3dPtsSrc to transform.
{
	// Let's try to be nice and let the user call us all the time.
	// That is, check if there's anything to do before wasting any time . . .
	if (sNum > 0)
	{
		RTransform transApplied; // Auto-identitied.

		// If there is a user transformation . . .
		if (ptrans != NULL)
		{
			// Create scene-user transform:
			// Apply user to view.
			transApplied.Mul(m_transNoZView.T, ptrans->T);
			// Apply user-view to screen (store in user-view).
			transApplied.PreMulBy(m_transNoZScreen.T);
		}
		else
		{
			// Create plain scene transform:
			// Apply view to screen (store in scene transform).
			transApplied.Mul(m_transNoZScreen.T, m_transNoZView.T);
		}

		// todo??
		transApplied.UpdateMatr();

		// Translate points
		int16_t sCur;
		// Note that this could save a fraction of a miniscule(sp?) moment by being
		// a do { } while since we know sNum > 0, but fongooey.
		for (sCur = 0; sCur < sNum; sCur++)
		{
			transApplied.TransformInto(*p3dPtsSrc++, *p3dPtsDst++);
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Transform the given points through the CScene's pipeline with the
// supplied transform and then map them to Realm 3D coordinates.
////////////////////////////////////////////////////////////////////////////////
void CScene::TransformPtsToRealm( // Returns nothing.
	RTransform *ptrans,			  // In:  Transformation to apply to CScene's before
								  // transforming pts.
	RP3d *p3dPtsSrc,			  // In:  Ptr to group of pts to transform from.
	RP3d *p3dPtsDst,			  // Out: Ptr to group of pts to transform into.
	int16_t sNum)				  // In:  The number of pts in p3dPtsSrc to transform.
{
#if 0 // This doesn't work.
	RTransform	trans;
	// If there is a user transform . . .
	if (ptrans != NULL)
		{
		// Combine the caller's with the conversion.
		trans.Mul(m_transScene2Realm.T, ptrans->T);
		// Use new transform.
		ptrans	= &trans;
		}
	else
		{
		// Use the conversion transform.
		ptrans	= &m_transScene2Realm;
		}
#endif

	// Map to scene.
	TransformPts(ptrans, p3dPtsSrc, p3dPtsDst, sNum);

	// Translate to Postal Realm coords.
	int16_t i;
	for (i = 0; i < sNum; i++)
	{
		p3dPtsDst->x -= SCREEN_DIAMETER_FOR_3D * 0.5f;
		p3dPtsDst->y = -(p3dPtsDst->y - SCREEN_DIAMETER_FOR_3D * 0.5f);
		// No Z mapping.
		//		p3dPtsDst->z		= p3dPtsDst->z;

		p3dPtsDst++;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Render a 3D sprite tree into the specified image.
// Ignores non 3D sprites.
////////////////////////////////////////////////////////////////////////////////
void CScene::DeadRender3D(		  // Returns nothing.
	CSprite3 *ps3,				  // Tree of 3D sprites to render.
	CHood *phood,				  // Da hood, homey.
	int16_t sDstX /*= 0*/,		  // Destination 2D x coord.
	int16_t sDstY /*= 0*/,		  // Destination 2D y coord.
	RRect *prcDstClip /*= NULL*/) // Dst clip rect.
{
	// Make sure sprite isn't hidden (if it is, skip it)
	if (!(ps3->m_sInFlags & CSprite::InHidden))
	{
		ASSERT(ps3 != NULL);
		ASSERT(phood != NULL);

		RRect rcClip;
		// If no clipping rect specified . . .
		if (prcDstClip == NULL)
		{
			prcDstClip = &rcClip;

			rcClip.sX = 0;
			rcClip.sY = 0;
			// todo???
			rcClip.sW = 640; //pimDst->m_sWidth;
			rcClip.sH = 480; //pimDst->m_sHeight;
		}

		// Render.
		Render3D(
			sDstX,		 // Destination 2D x coord.
			sDstY,		 // Destination 2D y coord.
			ps3,		 // Tree of 3D sprites to render.
			phood,		 // Da hood, homey.
			prcDstClip); // Dst clip rect.

		// Call for all children . . .
		CSprite *psprite = ps3->m_psprHeadChild;
		while (psprite)
		{
			// If 3D child and visible . . .
			if (psprite->m_type == CSprite::Standard3d)
			{
				// Render 'em.
				DeadRender3D(
					(CSprite3 *)psprite,
					phood,
					sDstX + ps3->m_sX2,
					sDstY + ps3->m_sY2,
					prcDstClip);
			}

			// Next, please.
			psprite = psprite->m_psprNext;
		}

	}
}

void CScene::DeadRender3DPVR( // Returns nothing.

	CSprite3 *ps3,				  // Tree of 3D sprites to render.
	CHood *phood,				  // Da hood, homey.
	int16_t sDstX /*= 0*/,		  // Destination 2D x coord.
	int16_t sDstY /*= 0*/,		  // Destination 2D y coord.
	RRect *prcDstClip /*= NULL*/) // Dst clip rect.
{
	// Make sure sprite isn't hidden (if it is, skip it)
	/*if (!(ps3->m_sInFlags & CSprite::InHidden))
		{
		ASSERT(pimDst	!= NULL);
		ASSERT(ps3		!= NULL);
		ASSERT(phood	!= NULL);

		RRect	rcClip;
		// If no clipping rect specified . . .
		if (prcDstClip == NULL)
			{
			prcDstClip	= &rcClip;

			rcClip.sX	= 0;
			rcClip.sY	= 0;
			rcClip.sW	= pimDst->m_sWidth;
			rcClip.sH	= pimDst->m_sHeight;
			}

		// Render.
		Render3D(
			pimDst,			// Destination image.
			sDstX,			// Destination 2D x coord.
			sDstY,			// Destination 2D y coord.
			ps3,				// Tree of 3D sprites to render.
			phood,			// Da hood, homey.
			prcDstClip);	// Dst clip rect.

		// Call for all children . . .
		CSprite*	psprite	= ps3->m_psprHeadChild;
		while (psprite)
			{
			// If 3D child and visible . . .
			if (psprite->m_type == CSprite::Standard3d)
				{
				// Render 'em.
				DeadRender3D(
					pimDst, 
					(CSprite3*)psprite, 
					phood, 
					sDstX + ps3->m_sX2,
					sDstY + ps3->m_sY2,
					prcDstClip);
				}

			// Next, please.
			psprite	= psprite->m_psprNext;
			}
		}*/
}

////////////////////////////////////////////////////////////////////////////////
// Set all 'alpha' _and_ 'opaque' layers to xray.
////////////////////////////////////////////////////////////////////////////////
void CScene::SetXRayAll( // You see a door to the north.  Returns nothing.
	bool bXRayAll)		 // In:  true to X Ray all 'alpha' _and_ 'opaque' layers.
{
	m_bXRayAll = bXRayAll;
}

////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////
