/** 
 * @file llviewerdisplay.cpp
 * @brief LLViewerDisplay class implementation
 *
 * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
 * $License$
 */

#include "llviewerprecompiledheaders.h"

#include "llagent.h"
#include "llviewercontrol.h"
#include "llcoord.h"
#include "llcriticaldamp.h"
#include "lldir.h"
#include "lldynamictexture.h"
#include "lldrawpoolalpha.h"
#include "llframestats.h"
#include "llgl.h"
#include "llglheaders.h"
#include "llhudmanager.h"
#include "llimagebmp.h"
#include "llimagegl.h"
#include "llselectmgr.h"
#include "llsky.h"
#include "llstartup.h"
#include "lltoolfocus.h"
#include "lltoolmgr.h"
#include "lltooldraganddrop.h"
#include "lltoolpie.h"
#include "lltracker.h"
#include "llui.h"
#include "llviewercamera.h"
#include "llviewerobjectlist.h"
#include "llviewerparcelmgr.h"
#include "llviewerwindow.h"
#include "llvoavatar.h"
#include "llvograss.h"
#include "llworld.h"
#include "pipeline.h"
#include "viewer.h"
#include "llstartup.h"
#include "llfasttimer.h"
#include "llfloatertools.h"
#include "llviewerimagelist.h"
#include "llfocusmgr.h"

extern U32 gFrameCount;
extern LLPointer<LLImageGL> gStartImageGL;
extern LLPointer<LLImageGL> gDisconnectedImagep;
extern BOOL gLogoutRequestSent;
extern LLTimer gLogoutTimer;
extern BOOL gHaveSavedSnapshot;
extern BOOL gDisplaySwapBuffers;

// used to toggle renderer back on after teleport
const F32 TELEPORT_RENDER_DELAY = 20.f; // Max time a teleport is allowed to take before we raise the curtain
const F32 TELEPORT_ARRIVAL_DELAY = 2.f; // Time to preload the world before raising the curtain after we've actually already arrived.
BOOL		 gTeleportDisplay = FALSE;
LLFrameTimer gTeleportDisplayTimer;
LLFrameTimer gTeleportArrivalTimer;
const F32		RESTORE_GL_TIME = 5.f;	// Wait this long while reloading textures before we raise the curtain

BOOL gForceRenderLandFence = FALSE;
BOOL gDisplaySwapBuffers = FALSE;

// Rendering stuff
void pre_show_depth_buffer();
void post_show_depth_buffer();
void render_ui_and_swap();
void render_ui_3d();
void render_ui_2d();
void render_disconnected_background();

void process_keystrokes_async(); // in viewer.cpp

void display_startup()
{
	if (   !gViewerWindow->getActive()
		|| !gViewerWindow->mWindow->getVisible() 
		|| gViewerWindow->mWindow->getMinimized()
		|| gNoRender )
	{
		return; 
	}

	LLDynamicTexture::updateAllInstances();

	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
	LLGLSDefault gls_default;
	LLGLSUIDefault gls_ui;
	gPipeline.disableLights();

	gViewerWindow->setup2DRender();
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	gViewerWindow->draw();
	gViewerWindow->mWindow->swapBuffers();
}


void display_update_camera()
{
	// TODO: cut draw distance down if customizing avatar?
	// TODO: cut draw distance on per-parcel basis?

	// Cut draw distance in half when customizing avatar,
	// but on the viewer only.
	F32 final_far = gAgent.mDrawDistance;
	if (CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode())
	{
		final_far *= 0.5f;
	}
	gCamera->setFar(final_far);
	gViewerWindow->setup3DRender();

	// Update land visibility too
	if (gWorldp)
	{
		gWorldp->setLandFarClip(final_far);
	}
}


// Paint the display!
void display(BOOL rebuild, F32 zoom_factor, int subfield)
{
	LLFastTimer t(LLFastTimer::FTM_RENDER);

	LLGLSDefault gls_default;
	LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE, GL_LEQUAL);

	// No clue where this is getting unset, but safe enough to reset it here.
	for (S32 j = 7; j >=0; j--)
	{
		glActiveTextureARB(GL_TEXTURE0_ARB+j);
		glClientActiveTextureARB(GL_TEXTURE0_ARB+j);
		j == 0 ? glEnable(GL_TEXTURE_2D) : glDisable(GL_TEXTURE_2D);
	}
	
#ifndef LL_RELEASE_FOR_DOWNLOAD
	LLGLState::checkStates();
	LLGLState::checkTextureChannels();
#endif
	
	gPipeline.disableLights();

	// Don't draw if the window is hidden or minimized.
	// In fact, must explicitly check the minimized state before drawing.
	// Attempting to draw into a minimized window causes a GL error. JC
	if (   !gViewerWindow->getActive()
		|| !gViewerWindow->mWindow->getVisible() 
		|| gViewerWindow->mWindow->getMinimized() )
	{
		// Clean up memory the pools may have allocated
		if (rebuild)
		{
			if (!gViewerWindow->renderingFastFrame())
			{
				gFrameStats.start(LLFrameStats::REBUILD);
				gPipeline.rebuildPools();
			}
		}
		return; 
	}

	gViewerWindow->checkSettings();
	gViewerWindow->performPick();

#ifndef LL_RELEASE_FOR_DOWNLOAD
	LLGLState::checkStates();
	LLGLState::checkTextureChannels();
#endif
	
	//////////////////////////////////////////////////////////
	//
	// Logic for forcing window updates if we're in drone mode.
	//

	if (gNoRender) 
	{
#if LL_WINDOWS
		static F32 last_update_time = 0.f;
		if ((gFrameTimeSeconds - last_update_time) > 1.f)
		{
			InvalidateRect(llwindow_get_hwnd(gViewerWindow->getWindow()), NULL, FALSE);
			last_update_time = gFrameTimeSeconds;
		}
#elif LL_DARWIN
		// MBW -- Do something clever here.
#endif
		// Not actually rendering, don't bother.
		return;
	}


	//
	// Bail out if we're in the startup state and don't want to try to
	// render the world.
	//
	if (gStartupState < STATE_STARTED)
	{
		display_startup();
		return;
	}

	//LLGLState::verify(FALSE);

	/////////////////////////////////////////////////
	//
	// Update GL Texture statistics (used for discard logic?)
	//

	gFrameStats.start(LLFrameStats::UPDATE_TEX_STATS);
	stop_glerror();

	LLImageGL::updateStats(gFrameTimeSeconds);

	LLVOAvatar::sRenderName = gSavedSettings.getS32("RenderName");
	gPipeline.mBackfaceCull = TRUE;
	gFrameCount++;

	//////////////////////////////////////////////////////////
	//
	// Display start screen if we're teleporting, and skip render
	//

	if (gTeleportDisplay)
	{
		const F32 TELEPORT_ARRIVAL_DELAY = 2.f; // Time to preload the world before raising the curtain after we've actually already arrived.

		S32 attach_count = 0;
		if (gAgent.getAvatarObject())
		{
			attach_count = gAgent.getAvatarObject()->getAttachmentCount();
		}
		F32 teleport_save_time = TELEPORT_EXPIRY + TELEPORT_EXPIRY_PER_ATTACHMENT * attach_count;
		F32 teleport_elapsed = gTeleportDisplayTimer.getElapsedTimeF32();
		F32 teleport_percent = teleport_elapsed * (100.f / teleport_save_time);
		if( (gAgent.getTeleportState() != LLAgent::TELEPORT_START) && (teleport_percent > 100.f) )
		{
			// Give up.  Don't keep the UI locked forever.
			gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
			gAgent.setTeleportMessage("");
		}

		const LLString& message = gAgent.getTeleportMessage();
		switch( gAgent.getTeleportState() )
		{
		case LLAgent::TELEPORT_START:
			// Transition to REQUESTED.  Viewer has sent some kind
			// of TeleportRequest to the source simulator
			gTeleportDisplayTimer.reset();
			gViewerWindow->setShowProgress(TRUE);
			gViewerWindow->setProgressPercent(0);
			gAgent.setTeleportState( LLAgent::TELEPORT_REQUESTED );
			gAgent.setTeleportMessage("Requesting Teleport...");
			break;

		case LLAgent::TELEPORT_REQUESTED:
			// Waiting for source simulator to respond
			gViewerWindow->setProgressPercent( llmin(teleport_percent, 37.5f) );
			gViewerWindow->setProgressString(message);
			break;

		case LLAgent::TELEPORT_MOVING:
			// Viewer has received destination location from source simulator
			gViewerWindow->setProgressPercent( llmin(teleport_percent, 75.f) );
			gViewerWindow->setProgressString(message);
			break;

		case LLAgent::TELEPORT_START_ARRIVAL:
			// Transition to ARRIVING.  Viewer has received avatar update, etc., from destination simulator
			gTeleportArrivalTimer.reset();
			gViewerWindow->setProgressCancelButtonVisible(FALSE, "Cancel");
			gViewerWindow->setProgressPercent(75.f);
			gAgent.setTeleportState( LLAgent::TELEPORT_ARRIVING );
			gAgent.setTeleportMessage("Arriving...");
			gImageList.mForceResetTextureStats = TRUE;
			break;

		case LLAgent::TELEPORT_ARRIVING:
			// Make the user wait while content "pre-caches"
			{
				F32 arrival_fraction = (gTeleportArrivalTimer.getElapsedTimeF32() / TELEPORT_ARRIVAL_DELAY);
				if( arrival_fraction > 1.f )
				{
					arrival_fraction = 1.f;
					gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
				}
				gViewerWindow->setProgressCancelButtonVisible(FALSE, "Cancel");
				gViewerWindow->setProgressPercent(  arrival_fraction * 25.f + 75.f);
				gViewerWindow->setProgressString(message);
			}
			break;

		case LLAgent::TELEPORT_CANCELLING:
			gViewerWindow->setProgressCancelButtonVisible(FALSE, "Cancel");
			gViewerWindow->setProgressPercent(  100.f );
			gViewerWindow->setProgressString("Canceling...");
			break;

		case LLAgent::TELEPORT_NONE:
			// No teleport in progress
			gViewerWindow->setShowProgress(FALSE);
			gTeleportDisplay = FALSE;
			break;
		}
	}
	else if(gLogoutRequestSent)
	{
		F32 percent_done = gLogoutTimer.getElapsedTimeF32() * 100.f / gLogoutMaxTime;
		if (percent_done > 100.f)
		{
			percent_done = 100.f;
		}

		if( gQuit )
		{
			percent_done = 100.f;
		}
		
		gViewerWindow->setProgressPercent( percent_done );
	}
	else
	if (gRestoreGL)
	{
		F32 percent_done = gRestoreGLTimer.getElapsedTimeF32() * 100.f / RESTORE_GL_TIME;
		if( percent_done > 100.f )
		{
			gViewerWindow->setShowProgress(FALSE);
			gRestoreGL = FALSE;
		}
		else
		{

			if( gQuit )
			{
				percent_done = 100.f;
			}
			
			gViewerWindow->setProgressPercent( percent_done );
		}
	}

	//////////////////////////
	//
	// Prepare for the next frame
	//

	// Hmm...  Should this be moved elsewhere? - djs 09/09/02
	// do render-to-texture stuff here
	if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES))
	{
// 		LLFastTimer t(LLFastTimer::FTM_UPDATE_TEXTURES);
		if (LLDynamicTexture::updateAllInstances())
		{
			glClear(GL_COLOR_BUFFER_BIT);
		}
	}

	if (rebuild)
	{
		if (gViewerWindow->renderingFastFrame())
		{
			gFrameStats.start(LLFrameStats::STATE_SORT);
			gFrameStats.start(LLFrameStats::REBUILD);
		}
	}

	/////////////////////////////
	//
	// Update the camera
	//
	//

	gCamera->setZoomParameters(zoom_factor, subfield);

	//////////////////////////
	//
	// clear the next buffer
	// (must follow dynamic texture writing since that uses the frame buffer)
	//

	if (gDisconnected)
	{
		glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
		render_disconnected_background();
	}
	else if (!gViewerWindow->isPickPending())
	{
		glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
	}
	gViewerWindow->setupViewport();


	//////////////////////////
	//
	// Set rendering options
	//
	//
	stop_glerror();
	if (gSavedSettings.getBOOL("ShowDepthBuffer"))
	{
		pre_show_depth_buffer();
	}

	if(gUseWireframe)//gSavedSettings.getBOOL("UseWireframe"))
	{
		glClearColor(0.5f, 0.5f, 0.5f, 0.f);
		glClear(GL_COLOR_BUFFER_BIT);
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	}
	stop_glerror();

	///////////////////////////////////////
	//
	// Slam lighting parameters back to our defaults.
	// Note that these are not the same as GL defaults...

	stop_glerror();
	F32 one[4] =	{1.f, 1.f, 1.f, 1.f};
	glLightModelfv (GL_LIGHT_MODEL_AMBIENT,one);
	stop_glerror();
	
	//LLGLState::verify();

	/////////////////////////////////////
	//
	// Render
	//
	// Actually push all of our triangles to the screen.
	//
	if (!gDisconnected)
	{
		LLFastTimer t(LLFastTimer::FTM_WORLD_UPDATE);
		stop_glerror();
		display_update_camera();
		stop_glerror();
		
		//FIXME: merge these two methods
		gHUDManager->updateEffects();
		LLHUDObject::updateAll();
		stop_glerror();
		
		gFrameStats.start(LLFrameStats::UPDATE_GEOM);
		const F32 max_geom_update_time = 0.005f; // 5 ms update time
		gPipeline.updateGeom(max_geom_update_time);
		stop_glerror();
		
		gFrameStats.start(LLFrameStats::UPDATE_CULL);
		gPipeline.updateCull();
		stop_glerror();
		
		if (rebuild && !gViewerWindow->renderingFastFrame())
		{
			LLFastTimer t(LLFastTimer::FTM_REBUILD);

			///////////////////////////////////
			//
			// StateSort
			//
			// Responsible for taking visible objects, and adding them to the appropriate draw orders.
			// In the case of alpha objects, z-sorts them first.
			// Also creates special lists for outlines and selected face rendering.
			//
			gFrameStats.start(LLFrameStats::STATE_SORT);
			gPipeline.stateSort();
			stop_glerror();
			
			//////////////////////////////////////
			//
			// rebuildPools
			//
			//
			gFrameStats.start(LLFrameStats::REBUILD);
			gPipeline.rebuildPools();
			stop_glerror();
		}
	}

	//// render frontmost floater opaque for occlusion culling purposes
	//LLFloater* frontmost_floaterp = gFloaterView->getFrontmost();
	//// assumes frontmost floater with focus is opaque
	//if (frontmost_floaterp && gFocusMgr.childHasKeyboardFocus(frontmost_floaterp))
	//{
	//	glMatrixMode(GL_MODELVIEW);
	//	glPushMatrix();
	//	{
	//		LLGLSNoTexture gls_no_texture;

	//		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
	//		glLoadIdentity();

	//		LLRect floater_rect = frontmost_floaterp->getScreenRect();
	//		// deflate by one pixel so rounding errors don't occlude outside of floater extents
	//		floater_rect.stretch(-1);
	//		LLRectf floater_3d_rect((F32)floater_rect.mLeft / (F32)gViewerWindow->getWindowWidth(), 
	//								(F32)floater_rect.mTop / (F32)gViewerWindow->getWindowHeight(),
	//								(F32)floater_rect.mRight / (F32)gViewerWindow->getWindowWidth(),
	//								(F32)floater_rect.mBottom / (F32)gViewerWindow->getWindowHeight());
	//		floater_3d_rect.translate(-0.5f, -0.5f);
	//		glTranslatef(0.f, 0.f, -gCamera->getNear());
	//		glScalef(gCamera->getNear() * gCamera->getAspect() / sinf(gCamera->getView()), gCamera->getNear() / sinf(gCamera->getView()), 1.f);
	//		glColor4fv(LLColor4::white.mV);
	//		glBegin(GL_QUADS);
	//		{
	//			glVertex3f(floater_3d_rect.mLeft, floater_3d_rect.mBottom, 0.f);
	//			glVertex3f(floater_3d_rect.mLeft, floater_3d_rect.mTop, 0.f);
	//			glVertex3f(floater_3d_rect.mRight, floater_3d_rect.mTop, 0.f);
	//			glVertex3f(floater_3d_rect.mRight, floater_3d_rect.mBottom, 0.f);
	//		}
	//		glEnd();
	//		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	//	}
	//	glPopMatrix();
	//}

	if (gViewerWindow->renderingFastFrame())
	{
		gFrameStats.start(LLFrameStats::RENDER_SYNC);
		gFrameStats.start(LLFrameStats::RENDER_GEOM);
	}
	else if (!(gLogoutRequestSent && gHaveSavedSnapshot) 
			&& !gRestoreGL
			&& !gDisconnected)
	{
		gPipeline.renderGeom();
		stop_glerror();
	}

	gFrameStats.start(LLFrameStats::RENDER_UI);

	if (gHandleKeysAsync)
	{
		process_keystrokes_async();
		stop_glerror();
	}

	
#ifndef LL_RELEASE_FOR_DOWNLOAD
	LLGLState::checkStates();
#endif
	render_ui_and_swap();
#ifndef LL_RELEASE_FOR_DOWNLOAD
	LLGLState::checkStates();
#endif

	gFrameStats.start(LLFrameStats::MISC_END);
	stop_glerror();

}


void render_ui_and_swap()
{
#ifndef LL_RELEASE_FOR_DOWNLOAD
	LLGLState::checkStates();
#endif

	LLGLSDefault gls_default;
	{
		LLGLSUIDefault gls_ui;
		gPipeline.disableLights();
		
		if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
		{
			LLFastTimer t(LLFastTimer::FTM_RENDER_UI);
			if (!gViewerWindow->renderingFastFrame() && !gDisconnected)
			{
				render_ui_3d();
#ifndef LL_RELEASE_FOR_DOWNLOAD
				LLGLState::checkStates();
#endif
			}

			render_ui_2d();
#ifndef LL_RELEASE_FOR_DOWNLOAD
			LLGLState::checkStates();
#endif
		}

		// now do the swap buffer
		if (gDisplaySwapBuffers)
		{
			LLFastTimer t(LLFastTimer::FTM_SWAP);
			gViewerWindow->mWindow->swapBuffers();
		}
	}

	gViewerWindow->finishFirstFastFrame();
}

void render_ui_3d()
{
	LLGLSPipeline gls_pipeline;

	//////////////////////////////////////
	//
	// Render 3D UI elements
	// NOTE: zbuffer is cleared before we get here by LLDrawPoolHUD,
	//		 so 3d elements requiring Z buffer are moved to LLDrawPoolHUD
	//

	// Render selections

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);

	/////////////////////////////////////////////////////////////
	//
	// Render 2.5D elements (2D elements in the world)
	// Stuff without z writes
	//

	// Debugging stuff goes before the UI.

	if (gSavedSettings.getBOOL("ShowDepthBuffer"))
	{
		post_show_depth_buffer();
	}

	// Coordinate axes
	if (gSavedSettings.getBOOL("ShowAxes"))
	{
		draw_axes();
	}

	stop_glerror();
		
	gViewerWindow->renderSelections(FALSE, FALSE, TRUE); // Non HUD call in render_hud_elements
	stop_glerror();
}

void render_ui_2d()
{
	LLGLSUIDefault gls_ui;

	/////////////////////////////////////////////////////////////
	//
	// Render 2D UI elements that overlay the world (no z compare)

	//  Disable wireframe mode below here, as this is HUD/menus
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	//  Menu overlays, HUD, etc
	gViewerWindow->setup2DRender();

	F32 zoom_factor = gCamera->getZoomFactor();
	S16 sub_region = gCamera->getZoomSubRegion();

	if (zoom_factor > 1.f)
	{
		//decompose subregion number to x and y values
		int pos_y = sub_region / llceil(zoom_factor);
		int pos_x = sub_region - (pos_y*llceil(zoom_factor));
		// offset for this tile
		LLFontGL::sCurOrigin.mX -= llround((F32)gViewerWindow->getWindowWidth() * (F32)pos_x / zoom_factor);
		LLFontGL::sCurOrigin.mY -= llround((F32)gViewerWindow->getWindowHeight() * (F32)pos_y / zoom_factor);
	}


	stop_glerror();
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	// render outline for HUD
	if (gAgent.getAvatarObject() && gAgent.getAvatarObject()->mHUDCurZoom < 0.98f)
	{
		glPushMatrix();
		S32 half_width = (gViewerWindow->getWindowWidth() / 2);
		S32 half_height = (gViewerWindow->getWindowHeight() / 2);
		glTranslatef((F32)half_width, (F32)half_height, 0.f);
		glScalef(gAgent.getAvatarObject()->mHUDCurZoom, gAgent.getAvatarObject()->mHUDCurZoom, gAgent.getAvatarObject()->mHUDCurZoom);
		glColor4fv(LLColor4::white.mV);
		gl_rect_2d(-half_width, half_height, half_width, -half_height, FALSE);
		glPopMatrix();
		stop_glerror();
	}
	gViewerWindow->draw();
	if (gDebugSelect)
	{
		gViewerWindow->drawPickBuffer();
	}

	// reset current origin for font rendering, in case of tiling render
	LLFontGL::sCurOrigin.set(0, 0);
}

void renderCoordinateAxes()
{
	LLGLSNoTexture gls_no_texture;
	glBegin(GL_LINES);
		glColor3f(1.0f, 0.0f, 0.0f);   // i direction = X-Axis = red
		glVertex3f(0.0f, 0.0f, 0.0f);
		glVertex3f(2.0f, 0.0f, 0.0f);
		glVertex3f(3.0f, 0.0f, 0.0f);
		glVertex3f(5.0f, 0.0f, 0.0f);
		glVertex3f(6.0f, 0.0f, 0.0f);
		glVertex3f(8.0f, 0.0f, 0.0f);
		// Make an X
		glVertex3f(11.0f, 1.0f, 1.0f);
		glVertex3f(11.0f, -1.0f, -1.0f);
		glVertex3f(11.0f, 1.0f, -1.0f);
		glVertex3f(11.0f, -1.0f, 1.0f);

		glColor3f(0.0f, 1.0f, 0.0f);   // j direction = Y-Axis = green
		glVertex3f(0.0f, 0.0f, 0.0f);
		glVertex3f(0.0f, 2.0f, 0.0f);
		glVertex3f(0.0f, 3.0f, 0.0f);
		glVertex3f(0.0f, 5.0f, 0.0f);
		glVertex3f(0.0f, 6.0f, 0.0f);
		glVertex3f(0.0f, 8.0f, 0.0f);
		// Make a Y
		glVertex3f(1.0f, 11.0f, 1.0f);
		glVertex3f(0.0f, 11.0f, 0.0f);
		glVertex3f(-1.0f, 11.0f, 1.0f);
		glVertex3f(0.0f, 11.0f, 0.0f);
		glVertex3f(0.0f, 11.0f, 0.0f);
		glVertex3f(0.0f, 11.0f, -1.0f);

		glColor3f(0.0f, 0.0f, 1.0f);   // Z-Axis = blue
		glVertex3f(0.0f, 0.0f, 0.0f);
		glVertex3f(0.0f, 0.0f, 2.0f);
		glVertex3f(0.0f, 0.0f, 3.0f);
		glVertex3f(0.0f, 0.0f, 5.0f);
		glVertex3f(0.0f, 0.0f, 6.0f);
		glVertex3f(0.0f, 0.0f, 8.0f);
		// Make a Z
		glVertex3f(-1.0f, 1.0f, 11.0f);
		glVertex3f(1.0f, 1.0f, 11.0f);
		glVertex3f(1.0f, 1.0f, 11.0f);
		glVertex3f(-1.0f, -1.0f, 11.0f);
		glVertex3f(-1.0f, -1.0f, 11.0f);
		glVertex3f(1.0f, -1.0f, 11.0f);
	glEnd();
}

void draw_axes() 
{
	LLGLSUIDefault gls_ui;
	LLGLSNoTexture gls_no_texture;
	// A vertical white line at origin
	LLVector3 v = gAgent.getPositionAgent();
	glBegin(GL_LINES);
		glColor3f(1.0f, 1.0f, 1.0f); 
		glVertex3f(0.0f, 0.0f, 0.0f);
		glVertex3f(0.0f, 0.0f, 40.0f);
	glEnd();
	// Some coordinate axes
	glPushMatrix();
		glTranslatef( v.mV[VX], v.mV[VY], v.mV[VZ] );
		renderCoordinateAxes();
	glPopMatrix();
}


void render_disconnected_background()
{
	if (!gDisconnectedImagep && gDisconnected)
	{
		llinfos << "Loading last bitmap..." << llendl;

		char temp_str[MAX_PATH];
		strcpy(temp_str, gDirUtilp->getLindenUserDir().c_str());
		strcat(temp_str, gDirUtilp->getDirDelimiter().c_str());

		strcat(temp_str, SCREEN_LAST_FILENAME);

		LLPointer<LLImageBMP> image_bmp = new LLImageBMP;
		if( !image_bmp->load(temp_str) )
		{
			//llinfos << "Bitmap load failed" << llendl;
			return;
		}

		gDisconnectedImagep = new LLImageGL( FALSE );
		LLPointer<LLImageRaw> raw = new LLImageRaw;
		if (!image_bmp->decode(raw))
		{
			llinfos << "Bitmap decode failed" << llendl;
			gDisconnectedImagep = NULL;
			return;
		}

		U8 *rawp = raw->getData();
		S32 npixels = (S32)image_bmp->getWidth()*(S32)image_bmp->getHeight();
		for (S32 i = 0; i < npixels; i++)
		{
			S32 sum = 0;
			sum = *rawp + *(rawp+1) + *(rawp+2);
			sum /= 3;
			*rawp = ((S32)sum*6 + *rawp)/7;
			rawp++;
			*rawp = ((S32)sum*6 + *rawp)/7;
			rawp++;
			*rawp = ((S32)sum*6 + *rawp)/7;
			rawp++;
		}


		raw->expandToPowerOfTwo();
		gDisconnectedImagep->createGLTexture(0, raw);
		gStartImageGL = gDisconnectedImagep;
		LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
	}

	// Make sure the progress view always fills the entire window.
	S32 width = gViewerWindow->getWindowWidth();
	S32 height = gViewerWindow->getWindowHeight();

	if (gDisconnectedImagep)
	{
		LLGLSUIDefault gls_ui;
		gViewerWindow->setup2DRender();
		glPushMatrix();
		{
			// scale ui to reflect UIScaleFactor
			// this can't be done in setup2DRender because it requires a
			// pushMatrix/popMatrix pair
			const LLVector2& display_scale = gViewerWindow->getDisplayScale();
			glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f);

			LLViewerImage::bindTexture(gDisconnectedImagep);
			glColor4f(1.f, 1.f, 1.f, 1.f);
			gl_rect_2d_simple_tex(width, height);
			LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
		}
		glPopMatrix();
	}
}
