6088 lines
174 KiB
C++
6088 lines
174 KiB
C++
/**
|
|
* @file llviewerwindow.cpp
|
|
* @brief Implementation of the LLViewerWindow class.
|
|
*
|
|
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2001-2009, Linden Research, Inc.
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
#include "llviewerwindow.h"
|
|
|
|
|
|
// system library includes
|
|
#include <stdio.h>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
|
|
#include "llagent.h"
|
|
#include "llagentcamera.h"
|
|
#include "llmeshrepository.h"
|
|
#include "llpanellogin.h"
|
|
#include "llviewerkeyboard.h"
|
|
|
|
|
|
#include "llviewquery.h"
|
|
#include "llxmltree.h"
|
|
#include "llrender.h"
|
|
|
|
#include "llvoiceclient.h" // for push-to-talk button handling
|
|
|
|
//
|
|
// TODO: Many of these includes are unnecessary. Remove them.
|
|
//
|
|
|
|
// linden library includes
|
|
#include "llaudioengine.h" // mute on minimize
|
|
#include "llassetstorage.h"
|
|
#include "llfontgl.h"
|
|
#include "llfontfreetype.h"
|
|
#include "llmousehandler.h"
|
|
#include "llrect.h"
|
|
#include "llsky.h"
|
|
#include "llstring.h"
|
|
#include "llui.h"
|
|
#include "lluuid.h"
|
|
#include "llview.h"
|
|
#include "llxfermanager.h"
|
|
#include "message.h"
|
|
#include "object_flags.h"
|
|
#include "lltimer.h"
|
|
#include "timing.h"
|
|
#include "llviewermenu.h"
|
|
#include "llmediaentry.h"
|
|
#include "raytrace.h"
|
|
|
|
// newview includes
|
|
#include "llbox.h"
|
|
#include "llchatbar.h"
|
|
#include "llconsole.h"
|
|
#include "lldebugview.h"
|
|
#include "lldir.h"
|
|
#include "lldrawable.h"
|
|
#include "lldrawpoolalpha.h"
|
|
#include "lldrawpoolbump.h"
|
|
#include "lldrawpoolwater.h"
|
|
#include "llmaniptranslate.h"
|
|
#include "llface.h"
|
|
#include "llfeaturemanager.h"
|
|
#include "statemachine/aifilepicker.h"
|
|
#include "llfloatercamera.h"
|
|
#include "llfloaterchat.h"
|
|
#include "llfloaterchatterbox.h"
|
|
#include "llfloatercustomize.h"
|
|
#include "llfloatereditui.h" // HACK JAMESDEBUG for ui editor
|
|
#include "llfloatersnapshot.h"
|
|
#include "llfloaterteleporthistory.h"
|
|
#include "llfloatertools.h"
|
|
#include "llfloaterworldmap.h"
|
|
#include "llfocusmgr.h"
|
|
#include "llframestatview.h"
|
|
#include "llgesturemgr.h"
|
|
#include "llglheaders.h"
|
|
#include "llhoverview.h"
|
|
#include "llhudmanager.h"
|
|
#include "llhudview.h"
|
|
#include "llimagebmp.h"
|
|
#include "llimagej2c.h"
|
|
#include "llimageworker.h"
|
|
#include "llkeyboard.h"
|
|
#include "lllineeditor.h"
|
|
#include "llmenugl.h"
|
|
#include "llmenuoptionpathfindingrebakenavmesh.h"
|
|
#include "llmodaldialog.h"
|
|
#include "llmorphview.h"
|
|
#include "llmoveview.h"
|
|
#include "llnotify.h"
|
|
#include "lloverlaybar.h"
|
|
#include "llpreviewtexture.h"
|
|
#include "llprogressview.h"
|
|
#include "llresmgr.h"
|
|
#include "llselectmgr.h"
|
|
#include "llrootview.h"
|
|
#include "llrendersphere.h"
|
|
#include "llstartup.h"
|
|
#include "llstatusbar.h"
|
|
#include "llstatview.h"
|
|
#include "llsurface.h"
|
|
#include "llsurfacepatch.h"
|
|
#include "llimview.h"
|
|
#include "lltexlayer.h"
|
|
#include "lltextbox.h"
|
|
#include "lltexturecache.h"
|
|
#include "lltexturefetch.h"
|
|
#include "lltextureview.h"
|
|
#include "lltool.h"
|
|
#include "lltoolbar.h"
|
|
#include "lltoolcomp.h"
|
|
#include "lltooldraganddrop.h"
|
|
#include "lltoolface.h"
|
|
#include "lltoolfocus.h"
|
|
#include "lltoolgrab.h"
|
|
#include "lltoolmgr.h"
|
|
#include "lltoolmorph.h"
|
|
#include "lltoolpie.h"
|
|
#include "lltoolplacer.h"
|
|
#include "lltoolselectland.h"
|
|
#include "lltoolview.h"
|
|
#include "lltrans.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "llurldispatcher.h" // SLURL from other app instance
|
|
#include "llvieweraudio.h"
|
|
#include "llviewercamera.h"
|
|
#include "llviewergesture.h"
|
|
#include "llviewertexturelist.h"
|
|
#include "llviewerinventory.h"
|
|
#include "llviewerkeyboard.h"
|
|
#include "llviewermedia.h"
|
|
#include "llviewermediafocus.h"
|
|
#include "llviewermenu.h"
|
|
#include "llviewermessage.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewerparcelmgr.h"
|
|
#include "llviewerregion.h"
|
|
#include "llviewershadermgr.h"
|
|
#include "llviewerstats.h"
|
|
#include "llvoavatarself.h"
|
|
#include "llvopartgroup.h"
|
|
#include "llvovolume.h"
|
|
#include "llworld.h"
|
|
#include "llworldmapview.h"
|
|
#include "pipeline.h"
|
|
#include "llappviewer.h"
|
|
#include "llviewerdisplay.h"
|
|
#include "llspatialpartition.h"
|
|
#include "llviewerjoystick.h"
|
|
#include "llviewernetwork.h"
|
|
#include "llpostprocess.h"
|
|
#include "llwearablelist.h"
|
|
|
|
#include "llnotifications.h"
|
|
#include "llnotificationsutil.h"
|
|
|
|
#include "llfloaternotificationsconsole.h"
|
|
|
|
#include "llpanelnearbymedia.h"
|
|
|
|
// [RLVa:KB]
|
|
#include "rlvhandler.h"
|
|
// [/RLVa:KB]
|
|
|
|
#if LL_WINDOWS
|
|
#include <tchar.h> // For Unicode conversion methods
|
|
#endif
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
void render_ui(F32 zoom_factor = 1.f, int subfield = 0, bool tiling = false);
|
|
LLBottomPanel* gBottomPanel = NULL;
|
|
|
|
extern BOOL gDebugClicks;
|
|
extern BOOL gDisplaySwapBuffers;
|
|
extern BOOL gDepthDirty;
|
|
extern BOOL gResizeScreenTexture;
|
|
|
|
LLViewerWindow *gViewerWindow = NULL;
|
|
LLVelocityBar *gVelocityBar = NULL;
|
|
|
|
LLFrameTimer gMouseIdleTimer;
|
|
LLFrameTimer gAwayTimer;
|
|
LLFrameTimer gAwayTriggerTimer;
|
|
LLFrameTimer gAlphaFadeTimer;
|
|
|
|
BOOL gShowOverlayTitle = FALSE;
|
|
BOOL gPickTransparent = TRUE;
|
|
|
|
LLViewerObject* gDebugRaycastObject = NULL;
|
|
LLVector4a gDebugRaycastIntersection;
|
|
LLVector2 gDebugRaycastTexCoord;
|
|
LLVector4a gDebugRaycastNormal;
|
|
LLVector4a gDebugRaycastTangent;
|
|
S32 gDebugRaycastFaceHit;
|
|
LLVector4a gDebugRaycastStart;
|
|
LLVector4a gDebugRaycastEnd;
|
|
|
|
// HUD display lines in lower right
|
|
BOOL gDisplayWindInfo = FALSE;
|
|
BOOL gDisplayCameraPos = FALSE;
|
|
BOOL gDisplayNearestWater = FALSE;
|
|
BOOL gDisplayFOV = FALSE;
|
|
|
|
S32 CHAT_BAR_HEIGHT = 28;
|
|
S32 OVERLAY_BAR_HEIGHT = 20;
|
|
|
|
const U8 NO_FACE = 255;
|
|
BOOL gQuietSnapshot = FALSE;
|
|
|
|
const F32 MIN_AFK_TIME = 2.f; // minimum time after setting away state before coming back
|
|
const F32 MAX_FAST_FRAME_TIME = 0.5f;
|
|
const F32 FAST_FRAME_INCREMENT = 0.1f;
|
|
|
|
const F32 MIN_DISPLAY_SCALE = 0.75f;
|
|
|
|
std::string LLViewerWindow::sSnapshotBaseName;
|
|
std::string LLViewerWindow::sSnapshotDir;
|
|
|
|
std::string LLViewerWindow::sMovieBaseName;
|
|
|
|
extern void toggle_debug_menus(void*);
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LLDebugText
|
|
//
|
|
|
|
//extern std::map<LLGLuint, std::list<std::pair<std::string,std::string> > > sTextureMaskMap;
|
|
|
|
class LLDebugText
|
|
{
|
|
private:
|
|
struct Line
|
|
{
|
|
Line(const std::string& in_text, S32 in_x, S32 in_y) : text(in_text), x(in_x), y(in_y) {}
|
|
std::string text;
|
|
S32 x,y;
|
|
};
|
|
|
|
LLViewerWindow *mWindow;
|
|
|
|
typedef std::vector<Line> line_list_t;
|
|
line_list_t mLineList;
|
|
LLColor4 mTextColor;
|
|
|
|
void addText(S32 x, S32 y, const std::string &text)
|
|
{
|
|
mLineList.push_back(Line(text, x, y));
|
|
}
|
|
|
|
public:
|
|
LLDebugText(LLViewerWindow* window) : mWindow(window) {}
|
|
|
|
void update()
|
|
{
|
|
std::string wind_vel_text;
|
|
std::string wind_vector_text;
|
|
std::string rwind_vel_text;
|
|
std::string rwind_vector_text;
|
|
std::string audio_text;
|
|
|
|
static const std::string beacon_particle = LLTrans::getString("BeaconParticle");
|
|
static const std::string beacon_physical = LLTrans::getString("BeaconPhysical");
|
|
static const std::string beacon_scripted = LLTrans::getString("BeaconScripted");
|
|
static const std::string beacon_scripted_touch = LLTrans::getString("BeaconScriptedTouch");
|
|
static const std::string beacon_sound = LLTrans::getString("BeaconSound");
|
|
static const std::string beacon_media = LLTrans::getString("BeaconMedia");
|
|
static const std::string particle_hiding = LLTrans::getString("ParticleHiding");
|
|
|
|
// Draw the statistics in a light gray
|
|
// and in a thin font
|
|
mTextColor = LLColor4( 0.86f, 0.86f, 0.86f, 1.f );
|
|
|
|
// Draw stuff growing up from right lower corner of screen
|
|
U32 xpos = mWindow->getWorldViewWidthScaled() - 350;
|
|
U32 ypos = 64;
|
|
const U32 y_inc = 20;
|
|
|
|
static const LLCachedControl<bool> slb_show_fps("SLBShowFPS");
|
|
if (slb_show_fps)
|
|
{
|
|
addText(xpos+280, ypos+5, llformat("FPS %3.1f", LLViewerStats::getInstance()->mFPSStat.getMeanPerSec()));
|
|
ypos += y_inc;
|
|
}
|
|
|
|
static const LLCachedControl<bool> debug_show_time("DebugShowTime");
|
|
if (debug_show_time)
|
|
{
|
|
{
|
|
const U32 y_inc2 = 15;
|
|
LLFrameTimer& timer = gTextureTimer;
|
|
F32 time = timer.getElapsedTimeF32();
|
|
S32 hours = (S32)(time / (60*60));
|
|
S32 mins = (S32)((time - hours*(60*60)) / 60);
|
|
S32 secs = (S32)((time - hours*(60*60) - mins*60));
|
|
addText(xpos, ypos, llformat("Texture: %d:%02d:%02d", hours,mins,secs)); ypos += y_inc2;
|
|
}
|
|
|
|
F32 time = gFrameTimeSeconds;
|
|
S32 hours = (S32)(time / (60*60));
|
|
S32 mins = (S32)((time - hours*(60*60)) / 60);
|
|
S32 secs = (S32)((time - hours*(60*60) - mins*60));
|
|
addText(xpos, ypos, llformat("Time: %d:%02d:%02d", hours,mins,secs)); ypos += y_inc;
|
|
}
|
|
static const LLCachedControl<bool> analyze_target_texture("AnalyzeTargetTexture", false);
|
|
if(analyze_target_texture)
|
|
{
|
|
LLSelectNode* nodep = LLSelectMgr::instance().getPrimaryHoverNode();
|
|
LLObjectSelectionHandle handle = LLSelectMgr::instance().getHoverObjects();
|
|
if(nodep || handle.notNull())
|
|
{
|
|
|
|
LLViewerObject* obj1 = nodep ? nodep->getObject() : NULL;
|
|
LLViewerObject* obj2 = handle ? handle->getPrimaryObject() : NULL;
|
|
LLViewerObject* obj = obj1 ? obj1 : obj2;
|
|
//llinfos << std::hex << obj1 << " " << obj2 << std::dec << llendl;
|
|
if(obj)
|
|
{
|
|
S32 te = nodep ? nodep->getLastSelectedTE() : -1;
|
|
if(te > 0)
|
|
{
|
|
LLViewerTexture* imagep = obj->getTEImage(te);
|
|
if(imagep && imagep != (LLViewerTexture*)LLViewerFetchedTexture::sDefaultImagep.get())
|
|
{
|
|
/*LLGLuint tex = imagep->getTexName();
|
|
std::map<LLGLuint, std::list<std::pair<std::string,std::string> > >::iterator it = sTextureMaskMap.find(tex);
|
|
if(it != sTextureMaskMap.end())
|
|
{
|
|
std::list<std::pair<std::string,std::string> >::reverse_iterator it2 = it->second.rbegin();
|
|
for(;it2!=it->second.rend();++it2)
|
|
{
|
|
addText(xpos, ypos, llformat(" %s: %s", it2->first.c_str(), it2->second.c_str())); ypos += y_inc;
|
|
}
|
|
}*/
|
|
static const LLCachedControl<bool> use_rmse_auto_mask("SHUseRMSEAutoMask",false);
|
|
static const LLCachedControl<F32> auto_mask_max_rmse("SHAutoMaskMaxRMSE",.09f);
|
|
addText(xpos, ypos, llformat("Mask: %s", imagep->getIsAlphaMask(use_rmse_auto_mask ? auto_mask_max_rmse : -1.f) ? "TRUE":"FALSE")); ypos += y_inc;
|
|
addText(xpos, ypos, llformat("ID: %s", imagep->getID().asString().c_str())); ypos += y_inc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if LL_WINDOWS
|
|
static const LLCachedControl<bool> debug_show_memory("DebugShowMemory");
|
|
if (debug_show_memory)
|
|
{
|
|
addText(xpos, ypos, llformat("Memory: %d (KB)", LLMemory::getWorkingSetSize() / 1024));
|
|
ypos += y_inc;
|
|
}
|
|
#endif
|
|
|
|
if (gDisplayCameraPos)
|
|
{
|
|
std::string camera_view_text;
|
|
std::string camera_center_text;
|
|
std::string agent_view_text;
|
|
std::string agent_left_text;
|
|
std::string agent_center_text;
|
|
std::string agent_root_center_text;
|
|
|
|
LLVector3d tvector; // Temporary vector to hold data for printing.
|
|
|
|
// Update camera center, camera view, wind info every other frame
|
|
tvector = gAgent.getPositionGlobal();
|
|
agent_center_text = llformat("AgentCenter %f %f %f",
|
|
(F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ]));
|
|
|
|
if (isAgentAvatarValid())
|
|
{
|
|
tvector = gAgent.getPosGlobalFromAgent(gAgentAvatarp->mRoot->getWorldPosition());
|
|
agent_root_center_text = llformat("AgentRootCenter %f %f %f",
|
|
(F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ]));
|
|
}
|
|
else
|
|
{
|
|
agent_root_center_text = "---";
|
|
}
|
|
|
|
|
|
tvector = LLVector4(gAgent.getFrameAgent().getAtAxis());
|
|
agent_view_text = llformat("AgentAtAxis %f %f %f",
|
|
(F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ]));
|
|
|
|
tvector = LLVector4(gAgent.getFrameAgent().getLeftAxis());
|
|
agent_left_text = llformat("AgentLeftAxis %f %f %f",
|
|
(F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ]));
|
|
|
|
tvector = gAgentCamera.getCameraPositionGlobal();
|
|
camera_center_text = llformat("CameraCenter %f %f %f",
|
|
(F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ]));
|
|
|
|
tvector = LLVector4(LLViewerCamera::getInstance()->getAtAxis());
|
|
camera_view_text = llformat("CameraAtAxis %f %f %f",
|
|
(F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ]));
|
|
|
|
addText(xpos, ypos, agent_center_text); ypos += y_inc;
|
|
addText(xpos, ypos, agent_root_center_text); ypos += y_inc;
|
|
addText(xpos, ypos, agent_view_text); ypos += y_inc;
|
|
addText(xpos, ypos, agent_left_text); ypos += y_inc;
|
|
addText(xpos, ypos, camera_center_text); ypos += y_inc;
|
|
addText(xpos, ypos, camera_view_text); ypos += y_inc;
|
|
}
|
|
|
|
if (gDisplayWindInfo)
|
|
{
|
|
wind_vel_text = llformat("Wind velocity %.2f m/s", gWindVec.magVec());
|
|
wind_vector_text = llformat("Wind vector %.2f %.2f %.2f", gWindVec.mV[0], gWindVec.mV[1], gWindVec.mV[2]);
|
|
rwind_vel_text = llformat("RWind vel %.2f m/s", gRelativeWindVec.magVec());
|
|
rwind_vector_text = llformat("RWind vec %.2f %.2f %.2f", gRelativeWindVec.mV[0], gRelativeWindVec.mV[1], gRelativeWindVec.mV[2]);
|
|
|
|
addText(xpos, ypos, wind_vel_text); ypos += y_inc;
|
|
addText(xpos, ypos, wind_vector_text); ypos += y_inc;
|
|
addText(xpos, ypos, rwind_vel_text); ypos += y_inc;
|
|
addText(xpos, ypos, rwind_vector_text); ypos += y_inc;
|
|
}
|
|
if (gDisplayWindInfo)
|
|
{
|
|
if (gAudiop)
|
|
{
|
|
audio_text= llformat("Audio for wind: %d", gAudiop->isWindEnabled());
|
|
}
|
|
addText(xpos, ypos, audio_text); ypos += y_inc;
|
|
}
|
|
if (gDisplayFOV)
|
|
{
|
|
addText(xpos, ypos, llformat("FOV: %2.1f deg", RAD_TO_DEG * LLViewerCamera::getInstance()->getView()));
|
|
ypos += y_inc;
|
|
}
|
|
|
|
/*if (LLViewerJoystick::getInstance()->getOverrideCamera())
|
|
{
|
|
addText(xpos + 200, ypos, llformat("Flycam"));
|
|
ypos += y_inc;
|
|
}*/
|
|
|
|
static const LLCachedControl<bool> debug_show_render_info("DebugShowRenderInfo");
|
|
if (debug_show_render_info)
|
|
{
|
|
if (gPipeline.getUseVertexShaders() == 0)
|
|
{
|
|
addText(xpos, ypos, "Shaders Disabled");
|
|
ypos += y_inc;
|
|
}
|
|
|
|
if (gGLManager.mHasATIMemInfo)
|
|
{
|
|
S32 meminfo[4];
|
|
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo);
|
|
|
|
addText(xpos, ypos, llformat("%.2f MB Texture Memory Free", meminfo[0]/1024.f));
|
|
ypos += y_inc;
|
|
|
|
if (gGLManager.mHasVertexBufferObject)
|
|
{
|
|
glGetIntegerv(GL_VBO_FREE_MEMORY_ATI, meminfo);
|
|
addText(xpos, ypos, llformat("%.2f MB VBO Memory Free", meminfo[0]/1024.f));
|
|
ypos += y_inc;
|
|
}
|
|
}
|
|
else if (gGLManager.mHasNVXMemInfo)
|
|
{
|
|
S32 free_memory;
|
|
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &free_memory);
|
|
addText(xpos, ypos, llformat("%.2f MB Video Memory Free", free_memory/1024.f));
|
|
ypos += y_inc;
|
|
}
|
|
|
|
//show streaming cost/triangle count of known prims in current region OR selection
|
|
//Note: This is SUPER slow
|
|
{
|
|
F32 cost = 0.f;
|
|
S32 count = 0;
|
|
S32 vcount = 0;
|
|
S32 object_count = 0;
|
|
S32 total_bytes = 0;
|
|
S32 visible_bytes = 0;
|
|
|
|
const char* label = "Region";
|
|
if (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 0)
|
|
{ //region
|
|
LLViewerRegion* region = gAgent.getRegion();
|
|
if (region)
|
|
{
|
|
for (U32 i = 0; i < (U32)gObjectList.getNumObjects(); ++i)
|
|
{
|
|
LLViewerObject* object = gObjectList.getObject(i);
|
|
if (object &&
|
|
object->getRegion() == region &&
|
|
object->getVolume())
|
|
{
|
|
object_count++;
|
|
S32 bytes = 0;
|
|
S32 visible = 0;
|
|
cost += object->getStreamingCost(&bytes, &visible);
|
|
S32 vt = 0;
|
|
count += object->getTriangleCount(&vt);
|
|
vcount += vt;
|
|
total_bytes += bytes;
|
|
visible_bytes += visible;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
label = "Selection";
|
|
cost = LLSelectMgr::getInstance()->getSelection()->getSelectedObjectStreamingCost(&total_bytes, &visible_bytes);
|
|
count = LLSelectMgr::getInstance()->getSelection()->getSelectedObjectTriangleCount(&vcount);
|
|
object_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
|
|
}
|
|
|
|
addText(xpos,ypos, llformat("%s streaming cost: %.1f", label, cost));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat(" %.3f KTris, %.3f KVerts, %.1f/%.1f KB, %d objects",
|
|
count/1000.f, vcount/1000.f, visible_bytes/1024.f, total_bytes/1024.f, object_count));
|
|
ypos += y_inc;
|
|
|
|
}
|
|
|
|
addText(xpos, ypos, llformat("%d MB Index Data (%d MB Pooled, %d KIndices)", LLVertexBuffer::sAllocatedIndexBytes/(1024*1024), LLVBOPool::sIndexBytesPooled/(1024*1024), LLVertexBuffer::sIndexCount/1024));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d MB Vertex Data (%d MB Pooled, %d KVerts)", LLVertexBuffer::sAllocatedBytes/(1024*1024), LLVBOPool::sBytesPooled/(1024*1024), LLVertexBuffer::sVertexCount/1024));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d Vertex Buffers", LLVertexBuffer::sGLCount));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d Mapped Buffers", LLVertexBuffer::sMappedCount));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d Vertex Buffer Binds", LLVertexBuffer::sBindCount));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d Vertex Buffer Sets", LLVertexBuffer::sSetCount));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d Texture Binds", LLImageGL::sBindCount));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d Unique Textures", LLImageGL::sUniqueCount));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d Render Calls", gPipeline.mBatchCount));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d Matrix Ops", gPipeline.mMatrixOpCount));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d Texture Matrix Ops", gPipeline.mTextureMatrixOps));
|
|
ypos += y_inc;
|
|
|
|
gPipeline.mTextureMatrixOps = 0;
|
|
gPipeline.mMatrixOpCount = 0;
|
|
|
|
if (gPipeline.mBatchCount > 0)
|
|
{
|
|
addText(xpos, ypos, llformat("Batch min/max/mean: %d/%d/%d", gPipeline.mMinBatchSize, gPipeline.mMaxBatchSize,
|
|
gPipeline.mTrianglesDrawn/gPipeline.mBatchCount));
|
|
|
|
gPipeline.mMinBatchSize = gPipeline.mMaxBatchSize;
|
|
gPipeline.mMaxBatchSize = 0;
|
|
gPipeline.mBatchCount = 0;
|
|
}
|
|
ypos += y_inc;
|
|
|
|
addText(xpos,ypos, llformat("%d/%d Nodes visible", gPipeline.mNumVisibleNodes, LLSpatialGroup::sNodeCount));
|
|
|
|
ypos += y_inc;
|
|
|
|
if (!LLSpatialGroup::sPendingQueries.empty())
|
|
{
|
|
addText(xpos,ypos, llformat("%d Queries pending", LLSpatialGroup::sPendingQueries.size()));
|
|
ypos += y_inc;
|
|
}
|
|
|
|
|
|
addText(xpos,ypos, llformat("%d Avatars visible", LLVOAvatar::sNumVisibleAvatars));
|
|
|
|
ypos += y_inc;
|
|
|
|
addText(xpos,ypos, llformat("%d Lights visible", LLPipeline::sVisibleLightCount));
|
|
|
|
ypos += y_inc;
|
|
|
|
S32 total_objects = gObjectList.getNumObjects();
|
|
S32 ID_objects = gObjectList.mUUIDObjectMap.size();
|
|
S32 dead_objects = gObjectList.mNumDeadObjects;
|
|
S32 dead_object_list = gObjectList.mDeadObjects.size();
|
|
S32 dead_object_check = 0;
|
|
S32 total_avatars = 0;
|
|
S32 ID_avatars = gObjectList.mUUIDAvatarMap.size();
|
|
S32 dead_avatar_list = 0;
|
|
S32 dead_avatar_check = 0;
|
|
|
|
S32 orphan_parents = gObjectList.getOrphanParentCount();
|
|
S32 orphan_parents_check = gObjectList.mOrphanParents.size();
|
|
S32 orphan_children = gObjectList.mOrphanChildren.size();
|
|
S32 orphan_total = gObjectList.getOrphanCount();
|
|
S32 orphan_child_attachments = 0;
|
|
|
|
for(U32 i = 0;i<gObjectList.mObjects.size();++i)
|
|
{
|
|
LLViewerObject *obj = gObjectList.mObjects[i];
|
|
if(obj)
|
|
{
|
|
if(obj->isAvatar())
|
|
++total_avatars;
|
|
if(obj->isDead())
|
|
{
|
|
++dead_object_check;
|
|
if(obj->isAvatar())
|
|
++dead_avatar_check;
|
|
}
|
|
}
|
|
}
|
|
for(std::set<LLUUID>::iterator it = gObjectList.mDeadObjects.begin();it!=gObjectList.mDeadObjects.end();++it)
|
|
{
|
|
LLViewerObject *obj = gObjectList.findObject(*it);
|
|
if(obj && obj->isAvatar())
|
|
++dead_avatar_list;
|
|
}
|
|
for(std::vector<LLViewerObjectList::OrphanInfo>::iterator it = gObjectList.mOrphanChildren.begin();it!=gObjectList.mOrphanChildren.end();++it)
|
|
{
|
|
LLViewerObject *obj = gObjectList.findObject(it->mChildInfo);
|
|
if(obj && obj->isAttachment())
|
|
++orphan_child_attachments;
|
|
}
|
|
addText(xpos,ypos, llformat("%d|%d (%d|%d|%d) Objects", total_objects, ID_objects, dead_objects, dead_object_list,dead_object_check));
|
|
ypos += y_inc;
|
|
addText(xpos,ypos, llformat("%d|%d (%d|%d) Avatars", total_avatars, ID_avatars, dead_avatar_list,dead_avatar_check));
|
|
ypos += y_inc;
|
|
addText(xpos,ypos, llformat("%d (%d|%d %d %d) Orphans", orphan_total, orphan_parents, orphan_parents_check,orphan_children, orphan_child_attachments));
|
|
|
|
ypos += y_inc;
|
|
|
|
if (gMeshRepo.meshRezEnabled())
|
|
{
|
|
addText(xpos, ypos, llformat("%.3f MB Mesh Data Received", LLMeshRepository::sBytesReceived/(1024.f*1024.f)));
|
|
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d/%d Mesh HTTP Requests/Retries", LLMeshRepository::sHTTPRequestCount,
|
|
LLMeshRepository::sHTTPRetryCount));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%d/%d Mesh LOD Pending/Processing", LLMeshRepository::sLODPending, LLMeshRepository::sLODProcessing));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%.3f/%.3f MB Mesh Cache Read/Write ", LLMeshRepository::sCacheBytesRead/(1024.f*1024.f), LLMeshRepository::sCacheBytesWritten/(1024.f*1024.f)));
|
|
|
|
ypos += y_inc;
|
|
}
|
|
|
|
LLVertexBuffer::sBindCount = LLImageGL::sBindCount =
|
|
LLVertexBuffer::sSetCount = LLImageGL::sUniqueCount =
|
|
gPipeline.mNumVisibleNodes = LLPipeline::sVisibleLightCount = 0;
|
|
}
|
|
static const LLCachedControl<bool> debug_show_render_matrices("DebugShowRenderMatrices");
|
|
if (debug_show_render_matrices)
|
|
{
|
|
F32* m = gGLProjection.getF32ptr();
|
|
|
|
addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", m[12], m[13], m[14], m[15]));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", m[8], m[9], m[10], m[11]));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", m[4], m[5], m[6], m[7]));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", m[0], m[1], m[2], m[3]));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, "Projection Matrix");
|
|
ypos += y_inc;
|
|
|
|
m = gGLModelView.getF32ptr();
|
|
|
|
addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", m[12], m[13], m[14], m[15]));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", m[8], m[9], m[10], m[11]));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", m[4], m[5], m[6], m[7]));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", m[0], m[1], m[2], m[3]));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, "View Matrix");
|
|
ypos += y_inc;
|
|
}
|
|
static const LLCachedControl<bool> debug_show_color("DebugShowColor");
|
|
if (debug_show_color)
|
|
{
|
|
U8 color[4];
|
|
LLCoordGL coord = gViewerWindow->getCurrentMouse();
|
|
glReadPixels(coord.mX, coord.mY, 1,1,GL_RGBA, GL_UNSIGNED_BYTE, color);
|
|
addText(xpos, ypos, llformat("%d %d %d %d", color[0], color[1], color[2], color[3]));
|
|
ypos += y_inc;
|
|
}
|
|
|
|
static const LLCachedControl<bool> DebugShowPrivateMem("DebugShowPrivateMem",false);
|
|
if (DebugShowPrivateMem)
|
|
{
|
|
LLPrivateMemoryPoolManager::getInstance()->updateStatistics() ;
|
|
addText(xpos, ypos, llformat("Total Reserved(KB): %d", LLPrivateMemoryPoolManager::getInstance()->mTotalReservedSize / 1024));
|
|
ypos += y_inc;
|
|
|
|
addText(xpos, ypos, llformat("Total Allocated(KB): %d", LLPrivateMemoryPoolManager::getInstance()->mTotalAllocatedSize / 1024));
|
|
ypos += y_inc;
|
|
}
|
|
|
|
// only display these messages if we are actually rendering beacons at this moment
|
|
static const LLCachedControl<bool> beacons_visible("BeaconsVisible",false);
|
|
if (LLPipeline::getRenderBeacons(NULL) && beacons_visible)
|
|
{
|
|
if (LLPipeline::getRenderMOAPBeacons(NULL))
|
|
{
|
|
addText(xpos, ypos, "Viewing media beacons (white)");
|
|
ypos += y_inc;
|
|
}
|
|
|
|
if (LLPipeline::toggleRenderTypeControlNegated((void*)LLPipeline::RENDER_TYPE_PARTICLES))
|
|
{
|
|
addText(xpos, ypos, particle_hiding);
|
|
ypos += y_inc;
|
|
}
|
|
|
|
if (LLPipeline::getRenderParticleBeacons(NULL))
|
|
{
|
|
addText(xpos, ypos, "Viewing particle beacons (blue)");
|
|
ypos += y_inc;
|
|
}
|
|
|
|
if (LLPipeline::getRenderSoundBeacons(NULL))
|
|
{
|
|
addText(xpos, ypos, "Viewing sound beacons (blue/cyan/green/yellow/red)");
|
|
ypos += y_inc;
|
|
}
|
|
|
|
if (LLPipeline::getRenderScriptedBeacons(NULL))
|
|
{
|
|
addText(xpos, ypos, beacon_scripted);
|
|
ypos += y_inc;
|
|
}
|
|
else
|
|
if (LLPipeline::getRenderScriptedTouchBeacons(NULL))
|
|
{
|
|
addText(xpos, ypos, beacon_scripted_touch);
|
|
ypos += y_inc;
|
|
}
|
|
|
|
if (LLPipeline::getRenderPhysicalBeacons(NULL))
|
|
{
|
|
addText(xpos, ypos, "Viewing physical object beacons (green)");
|
|
ypos += y_inc;
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw()
|
|
{
|
|
for (line_list_t::iterator iter = mLineList.begin();
|
|
iter != mLineList.end(); ++iter)
|
|
{
|
|
const Line& line = *iter;
|
|
LLFontGL::getFontMonospace()->renderUTF8(line.text, 0, (F32)line.x, (F32)line.y, mTextColor,
|
|
LLFontGL::LEFT, LLFontGL::TOP,
|
|
LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
|
|
}
|
|
mLineList.clear();
|
|
}
|
|
|
|
};
|
|
|
|
void LLViewerWindow::updateDebugText()
|
|
{
|
|
mDebugText->update();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LLViewerWindow
|
|
//
|
|
|
|
bool LLViewerWindow::shouldShowToolTipFor(LLMouseHandler *mh)
|
|
{
|
|
if (mToolTip && mh)
|
|
{
|
|
LLMouseHandler::EShowToolTip showlevel = mh->getShowToolTip();
|
|
|
|
return (
|
|
showlevel == LLMouseHandler::SHOW_ALWAYS ||
|
|
(showlevel == LLMouseHandler::SHOW_IF_NOT_BLOCKED &&
|
|
!mToolTipBlocked)
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down)
|
|
{
|
|
std::string buttonname;
|
|
std::string buttonstatestr;
|
|
BOOL handled = FALSE;
|
|
S32 x = pos.mX;
|
|
S32 y = pos.mY;
|
|
x = llround((F32)x / mDisplayScale.mV[VX]);
|
|
y = llround((F32)y / mDisplayScale.mV[VY]);
|
|
|
|
if (down)
|
|
{
|
|
buttonstatestr = "down";
|
|
}
|
|
else
|
|
{
|
|
buttonstatestr = "up";
|
|
}
|
|
|
|
switch (clicktype)
|
|
{
|
|
case LLMouseHandler::CLICK_LEFT:
|
|
mLeftMouseDown = down;
|
|
buttonname = "Left";
|
|
break;
|
|
case LLMouseHandler::CLICK_RIGHT:
|
|
mRightMouseDown = down;
|
|
buttonname = "Right";
|
|
break;
|
|
case LLMouseHandler::CLICK_MIDDLE:
|
|
mMiddleMouseDown = down;
|
|
buttonname = "Middle";
|
|
break;
|
|
case LLMouseHandler::CLICK_DOUBLELEFT:
|
|
mLeftMouseDown = down;
|
|
buttonname = "Left Double Click";
|
|
break;
|
|
}
|
|
|
|
LLView::sMouseHandlerMessage.clear();
|
|
|
|
if (gMenuBarView)
|
|
{
|
|
// stop ALT-key access to menu
|
|
gMenuBarView->resetMenuTrigger();
|
|
}
|
|
|
|
if (gDebugClicks)
|
|
{
|
|
llinfos << "ViewerWindow " << buttonname << " mouse " << buttonstatestr << " at " << x << "," << y << llendl;
|
|
}
|
|
|
|
// Make sure we get a corresponding mouseup event, even if the mouse leaves the window
|
|
if (down)
|
|
{
|
|
mWindow->captureMouse();
|
|
}
|
|
else
|
|
{
|
|
mWindow->releaseMouse();
|
|
}
|
|
|
|
// Indicate mouse was active
|
|
gMouseIdleTimer.reset();
|
|
|
|
// Hide tooltips on mousedown
|
|
if (down)
|
|
{
|
|
mToolTipBlocked = TRUE;
|
|
mToolTip->setVisible(FALSE);
|
|
}
|
|
|
|
// Also hide hover info on mousedown/mouseup
|
|
if (gHoverView)
|
|
{
|
|
gHoverView->cancelHover();
|
|
}
|
|
|
|
// Don't let the user move the mouse out of the window until mouse up.
|
|
if (LLToolMgr::getInstance()->getCurrentTool()->clipMouseWhenDown())
|
|
{
|
|
mWindow->setMouseClipping(down);
|
|
}
|
|
|
|
LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
|
|
if( mouse_captor )
|
|
{
|
|
S32 local_x;
|
|
S32 local_y;
|
|
mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
|
|
if (LLView::sDebugMouseHandling)
|
|
{
|
|
llinfos << buttonname << " Mouse " << buttonstatestr << " handled by captor " << mouse_captor->getName() << llendl;
|
|
}
|
|
return mouse_captor->handleAnyMouseClick(local_x, local_y, mask, clicktype, down);
|
|
}
|
|
|
|
// Topmost view gets a chance before the hierarchy
|
|
LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
|
|
if (top_ctrl)
|
|
{
|
|
S32 local_x, local_y;
|
|
top_ctrl->screenPointToLocal( x, y, &local_x, &local_y );
|
|
|
|
if (down)
|
|
{
|
|
if (top_ctrl->pointInView(local_x, local_y))
|
|
{
|
|
return top_ctrl->handleAnyMouseClick(local_x, local_y, mask, clicktype, down) ;
|
|
}
|
|
else
|
|
{
|
|
gFocusMgr.setTopCtrl(NULL);
|
|
}
|
|
}
|
|
else
|
|
handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleMouseUp(local_x, local_y, mask);
|
|
}
|
|
|
|
// Mark the click as handled and return if we aren't within the root view to avoid spurious bugs
|
|
if( !mRootView->pointInView(x, y) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Give the UI views a chance to process the click
|
|
if( mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) )
|
|
{
|
|
if (LLView::sDebugMouseHandling)
|
|
{
|
|
llinfos << buttonname << " Mouse " << buttonstatestr << " " << LLView::sMouseHandlerMessage << llendl;
|
|
}
|
|
return TRUE;
|
|
}
|
|
else if (LLView::sDebugMouseHandling)
|
|
{
|
|
llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl;
|
|
}
|
|
|
|
// Do not allow tool manager to handle mouseclicks if we have disconnected
|
|
if(!gDisconnected && LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// If we got this far on a down-click, it wasn't handled.
|
|
// Up-clicks, though, are always handled as far as the OS is concerned.
|
|
BOOL default_rtn = !down;
|
|
return default_rtn;
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask)
|
|
{
|
|
BOOL down = TRUE;
|
|
return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down);
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask)
|
|
{
|
|
// try handling as a double-click first, then a single-click if that
|
|
// wasn't handled.
|
|
BOOL down = TRUE;
|
|
if (handleAnyMouseClick(window, pos, mask,
|
|
LLMouseHandler::CLICK_DOUBLELEFT, down))
|
|
{
|
|
return TRUE;
|
|
}
|
|
return handleMouseDown(window, pos, mask);
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
|
|
{
|
|
BOOL down = FALSE;
|
|
return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down);
|
|
}
|
|
|
|
|
|
BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask)
|
|
{
|
|
S32 x = pos.mX;
|
|
S32 y = pos.mY;
|
|
x = llround((F32)x / mDisplayScale.mV[VX]);
|
|
y = llround((F32)y / mDisplayScale.mV[VY]);
|
|
|
|
LLView::sMouseHandlerMessage.clear();
|
|
|
|
BOOL down = TRUE;
|
|
BOOL handle = handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down);
|
|
if (handle)
|
|
return handle;
|
|
|
|
// *HACK: this should be rolled into the composite tool logic, not
|
|
// hardcoded at the top level.
|
|
if (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance())
|
|
{
|
|
// If the current tool didn't process the click, we should show
|
|
// the pie menu. This can be done by passing the event to the pie
|
|
// menu tool.
|
|
LLToolPie::getInstance()->handleRightMouseDown(x, y, mask);
|
|
// show_context_menu( x, y, mask );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
|
|
{
|
|
BOOL down = FALSE;
|
|
return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down);
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask)
|
|
{
|
|
BOOL down = TRUE;
|
|
LLVoiceClient::getInstance()->middleMouseState(true);
|
|
handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down);
|
|
|
|
// Always handled as far as the OS is concerned.
|
|
return TRUE;
|
|
}
|
|
|
|
LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data)
|
|
{
|
|
LLWindowCallbacks::DragNDropResult result = LLWindowCallbacks::DND_NONE;
|
|
|
|
const bool prim_media_dnd_enabled = gSavedSettings.getBOOL("PrimMediaDragNDrop");
|
|
const bool slurl_dnd_enabled = gSavedSettings.getBOOL("SLURLDragNDrop");
|
|
|
|
if ( prim_media_dnd_enabled || slurl_dnd_enabled )
|
|
{
|
|
switch(action)
|
|
{
|
|
// Much of the handling for these two cases is the same.
|
|
case LLWindowCallbacks::DNDA_TRACK:
|
|
case LLWindowCallbacks::DNDA_DROPPED:
|
|
case LLWindowCallbacks::DNDA_START_TRACKING:
|
|
{
|
|
bool drop = (LLWindowCallbacks::DNDA_DROPPED == action);
|
|
|
|
if (slurl_dnd_enabled)
|
|
{
|
|
LLSLURL dropped_slurl(data);
|
|
if(dropped_slurl.isSpatial())
|
|
{
|
|
if (drop)
|
|
{
|
|
LLURLDispatcher::dispatch( dropped_slurl.getSLURLString(), "clicked", NULL, true );
|
|
return LLWindowCallbacks::DND_MOVE;
|
|
}
|
|
return LLWindowCallbacks::DND_COPY;
|
|
}
|
|
}
|
|
|
|
if (prim_media_dnd_enabled)
|
|
{
|
|
LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY, TRUE /*BOOL pick_transparent*/ );
|
|
|
|
LLUUID object_id = pick_info.getObjectID();
|
|
S32 object_face = pick_info.mObjectFace;
|
|
std::string url = data;
|
|
|
|
lldebugs << "Object: picked at " << pos.mX << ", " << pos.mY << " - face = " << object_face << " - URL = " << url << llendl;
|
|
|
|
LLVOVolume *obj = dynamic_cast<LLVOVolume*>(static_cast<LLViewerObject*>(pick_info.getObject()));
|
|
|
|
if (obj && !obj->getRegion()->getCapability("ObjectMedia").empty())
|
|
{
|
|
LLTextureEntry *te = obj->getTE(object_face);
|
|
|
|
// can modify URL if we can modify the object or we have navigate permissions
|
|
bool allow_modify_url = obj->permModify() || obj->hasMediaPermission( te->getMediaData(), LLVOVolume::MEDIA_PERM_INTERACT );
|
|
|
|
if (te && allow_modify_url )
|
|
{
|
|
if (drop)
|
|
{
|
|
// object does NOT have media already
|
|
if ( ! te->hasMedia() )
|
|
{
|
|
// we are allowed to modify the object
|
|
if ( obj->permModify() )
|
|
{
|
|
// Create new media entry
|
|
LLSD media_data;
|
|
// XXX Should we really do Home URL too?
|
|
media_data[LLMediaEntry::HOME_URL_KEY] = url;
|
|
media_data[LLMediaEntry::CURRENT_URL_KEY] = url;
|
|
media_data[LLMediaEntry::AUTO_PLAY_KEY] = true;
|
|
obj->syncMediaData(object_face, media_data, true, true);
|
|
// XXX This shouldn't be necessary, should it ?!?
|
|
if (obj->getMediaImpl(object_face))
|
|
obj->getMediaImpl(object_face)->navigateReload();
|
|
obj->sendMediaDataUpdate();
|
|
|
|
result = LLWindowCallbacks::DND_COPY;
|
|
}
|
|
}
|
|
else
|
|
// object HAS media already
|
|
{
|
|
// URL passes the whitelist
|
|
if (te->getMediaData()->checkCandidateUrl( url ) )
|
|
{
|
|
// just navigate to the URL
|
|
if (obj->getMediaImpl(object_face))
|
|
{
|
|
obj->getMediaImpl(object_face)->navigateTo(url);
|
|
}
|
|
else
|
|
{
|
|
// This is very strange. Navigation should
|
|
// happen via the Impl, but we don't have one.
|
|
// This sends it to the server, which /should/
|
|
// trigger us getting it. Hopefully.
|
|
LLSD media_data;
|
|
media_data[LLMediaEntry::CURRENT_URL_KEY] = url;
|
|
obj->syncMediaData(object_face, media_data, true, true);
|
|
obj->sendMediaDataUpdate();
|
|
}
|
|
result = LLWindowCallbacks::DND_LINK;
|
|
|
|
}
|
|
}
|
|
LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
|
|
mDragHoveredObject = NULL;
|
|
|
|
}
|
|
else
|
|
{
|
|
// Check the whitelist, if there's media (otherwise just show it)
|
|
if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url))
|
|
{
|
|
if ( obj != mDragHoveredObject)
|
|
{
|
|
// Highlight the dragged object
|
|
LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
|
|
mDragHoveredObject = obj;
|
|
LLSelectMgr::getInstance()->highlightObjectOnly(mDragHoveredObject);
|
|
}
|
|
result = (! te->hasMedia()) ? LLWindowCallbacks::DND_COPY : LLWindowCallbacks::DND_LINK;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LLWindowCallbacks::DNDA_STOP_TRACKING:
|
|
// The cleanup case below will make sure things are unhilighted if necessary.
|
|
break;
|
|
}
|
|
|
|
if (prim_media_dnd_enabled &&
|
|
result == LLWindowCallbacks::DND_NONE && !mDragHoveredObject.isNull())
|
|
{
|
|
LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
|
|
mDragHoveredObject = NULL;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
|
|
{
|
|
BOOL down = FALSE;
|
|
LLVoiceClient::getInstance()->middleMouseState(false);
|
|
handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down);
|
|
|
|
// Always handled as far as the OS is concerned.
|
|
return TRUE;
|
|
}
|
|
|
|
// WARNING: this is potentially called multiple times per frame
|
|
void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask)
|
|
{
|
|
S32 x = pos.mX;
|
|
S32 y = pos.mY;
|
|
|
|
x = llround((F32)x / mDisplayScale.mV[VX]);
|
|
y = llround((F32)y / mDisplayScale.mV[VY]);
|
|
|
|
mMouseInWindow = TRUE;
|
|
|
|
// Save mouse point for access during idle() and display()
|
|
|
|
LLCoordGL prev_saved_mouse_point = mCurrentMousePoint;
|
|
LLCoordGL mouse_point(x, y);
|
|
|
|
if (mouse_point != mCurrentMousePoint)
|
|
{
|
|
gMouseIdleTimer.reset();
|
|
}
|
|
|
|
saveLastMouse(mouse_point);
|
|
BOOL mouse_actually_moved = !gFocusMgr.getMouseCapture() && // mouse is not currenty captured
|
|
((prev_saved_mouse_point.mX != mCurrentMousePoint.mX) || (prev_saved_mouse_point.mY != mCurrentMousePoint.mY)); // mouse moved from last recorded position
|
|
|
|
mWindow->showCursorFromMouseMove();
|
|
|
|
if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME
|
|
&& !gDisconnected)
|
|
{
|
|
gAgent.clearAFK();
|
|
}
|
|
|
|
if(mouse_actually_moved)
|
|
{
|
|
mToolTipBlocked = FALSE;
|
|
}
|
|
|
|
// Activate the hover picker on mouse move.
|
|
if (gHoverView)
|
|
{
|
|
gHoverView->setTyping(FALSE);
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::handleMouseLeave(LLWindow *window)
|
|
{
|
|
// Note: we won't get this if we have captured the mouse.
|
|
llassert( gFocusMgr.getMouseCapture() == NULL );
|
|
mMouseInWindow = FALSE;
|
|
if (mToolTip)
|
|
{
|
|
mToolTip->setVisible( FALSE );
|
|
}
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleCloseRequest(LLWindow *window)
|
|
{
|
|
// User has indicated they want to close, but we may need to ask
|
|
// about modified documents.
|
|
LLAppViewer::instance()->userQuit();
|
|
// Don't quit immediately
|
|
return FALSE;
|
|
}
|
|
|
|
void LLViewerWindow::handleQuit(LLWindow *window)
|
|
{
|
|
LLAppViewer::instance()->forceQuit();
|
|
}
|
|
|
|
void LLViewerWindow::handleResize(LLWindow *window, S32 width, S32 height)
|
|
{
|
|
reshape(width, height);
|
|
mResDirty = true;
|
|
}
|
|
|
|
// The top-level window has gained focus (e.g. via ALT-TAB)
|
|
void LLViewerWindow::handleFocus(LLWindow *window)
|
|
{
|
|
gFocusMgr.setAppHasFocus(TRUE);
|
|
LLModalDialog::onAppFocusGained();
|
|
|
|
gAgent.onAppFocusGained();
|
|
LLToolMgr::getInstance()->onAppFocusGained();
|
|
|
|
gShowTextEditCursor = TRUE;
|
|
|
|
// See if we're coming in with modifier keys held down
|
|
if (gKeyboard)
|
|
{
|
|
gKeyboard->resetMaskKeys();
|
|
}
|
|
|
|
// resume foreground running timer
|
|
// since we artifically limit framerate when not frontmost
|
|
gForegroundTime.unpause();
|
|
}
|
|
|
|
// The top-level window has lost focus (e.g. via ALT-TAB)
|
|
void LLViewerWindow::handleFocusLost(LLWindow *window)
|
|
{
|
|
gFocusMgr.setAppHasFocus(FALSE);
|
|
//LLModalDialog::onAppFocusLost();
|
|
LLToolMgr::getInstance()->onAppFocusLost();
|
|
gFocusMgr.setMouseCapture( NULL );
|
|
|
|
if (gMenuBarView)
|
|
{
|
|
// stop ALT-key access to menu
|
|
gMenuBarView->resetMenuTrigger();
|
|
}
|
|
|
|
// restore mouse cursor
|
|
showCursor();
|
|
getWindow()->setMouseClipping(FALSE);
|
|
|
|
// JC - Leave keyboard focus, so if you're popping in and out editing
|
|
// a script, you don't have to click in the editor again and again.
|
|
// gFocusMgr.setKeyboardFocus( NULL );
|
|
gShowTextEditCursor = FALSE;
|
|
|
|
// If losing focus while keys are down, reset them.
|
|
if (gKeyboard)
|
|
{
|
|
gKeyboard->resetKeys();
|
|
}
|
|
|
|
// pause timer that tracks total foreground running time
|
|
gForegroundTime.pause();
|
|
}
|
|
|
|
|
|
BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated)
|
|
{
|
|
// Let the voice chat code check for its PTT key. Note that this never affects event processing.
|
|
LLVoiceClient::getInstance()->keyDown(key, mask);
|
|
|
|
if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
|
|
{
|
|
gAgent.clearAFK();
|
|
}
|
|
|
|
// *NOTE: We want to interpret KEY_RETURN later when it arrives as
|
|
// a Unicode char, not as a keydown. Otherwise when client frame
|
|
// rate is really low, hitting return sends your chat text before
|
|
// it's all entered/processed.
|
|
if (key == KEY_RETURN && mask == MASK_NONE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return gViewerKeyboard.handleKey(key, mask, repeated);
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask)
|
|
{
|
|
// Let the voice chat code check for its PTT key. Note that this never affects event processing.
|
|
LLVoiceClient::getInstance()->keyUp(key, mask);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
|
|
{
|
|
LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true);
|
|
return gViewerKeyboard.scanKey(key, key_down, key_up, key_level);
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated)
|
|
{
|
|
if (activated)
|
|
{
|
|
mActive = true;
|
|
send_agent_resume();
|
|
gAgent.clearAFK();
|
|
if (mWindow->getFullscreen() && !mIgnoreActivate)
|
|
{
|
|
if (!LLApp::isExiting() )
|
|
{
|
|
if (LLStartUp::getStartupState() >= STATE_STARTED)
|
|
{
|
|
// if we're in world, show a progress bar to hide reloading of textures
|
|
llinfos << "Restoring GL during activate" << llendl;
|
|
restoreGL(LLTrans::getString("ProgressRestoring"));
|
|
}
|
|
else
|
|
{
|
|
// otherwise restore immediately
|
|
restoreGL();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Activating while quitting" << llendl;
|
|
}
|
|
}
|
|
|
|
// Unmute audio
|
|
audio_update_volume(false);
|
|
}
|
|
else
|
|
{
|
|
mActive = false;
|
|
|
|
if (gSavedSettings.getBOOL("AllowIdleAFK"))
|
|
{
|
|
gAgent.setAFK();
|
|
}
|
|
|
|
// SL-53351: Make sure we're not in mouselook when minimised, to prevent control issues
|
|
if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK)
|
|
{
|
|
gAgentCamera.changeCameraToDefault();
|
|
}
|
|
|
|
send_agent_pause();
|
|
|
|
if (mWindow->getFullscreen() && !mIgnoreActivate)
|
|
{
|
|
llinfos << "Stopping GL during deactivation" << llendl;
|
|
stopGL();
|
|
}
|
|
// Mute audio
|
|
audio_update_volume(false);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleActivateApp(LLWindow *window, BOOL activating)
|
|
{
|
|
//if (!activating) gAgentCamera.changeCameraToDefault();
|
|
|
|
LLViewerJoystick::getInstance()->setNeedsReset(true);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void LLViewerWindow::handleMenuSelect(LLWindow *window, S32 menu_item)
|
|
{
|
|
}
|
|
|
|
|
|
BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height)
|
|
{
|
|
#if LL_WINDOWS
|
|
if (gNoRender)
|
|
{
|
|
HWND window_handle = (HWND)window->getPlatformWindow();
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
|
|
RECT wnd_rect;
|
|
wnd_rect.left = 0;
|
|
wnd_rect.top = 0;
|
|
wnd_rect.bottom = 200;
|
|
wnd_rect.right = 500;
|
|
|
|
hdc = BeginPaint(window_handle, &ps);
|
|
//SetBKColor(hdc, RGB(255, 255, 255));
|
|
FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255)));
|
|
|
|
std::string name_str;
|
|
gAgent.getName(name_str);
|
|
|
|
std::string temp_str;
|
|
temp_str = llformat( "%s FPS %3.1f Phy FPS %2.1f Time Dil %1.3f", /* Flawfinder: ignore */
|
|
name_str.c_str(),
|
|
LLViewerStats::getInstance()->mFPSStat.getMeanPerSec(),
|
|
LLViewerStats::getInstance()->mSimPhysicsFPS.getPrev(0),
|
|
LLViewerStats::getInstance()->mSimTimeDilation.getPrev(0));
|
|
S32 len = temp_str.length();
|
|
TextOutA(hdc, 0, 0, temp_str.c_str(), len);
|
|
|
|
|
|
LLVector3d pos_global = gAgent.getPositionGlobal();
|
|
temp_str = llformat( "Avatar pos %6.1lf %6.1lf %6.1lf", pos_global.mdV[0], pos_global.mdV[1], pos_global.mdV[2]);
|
|
len = temp_str.length();
|
|
TextOutA(hdc, 0, 25, temp_str.c_str(), len);
|
|
|
|
TextOutA(hdc, 0, 50, "Set \"DisableRendering FALSE\" in settings.ini file to reenable", 61);
|
|
EndPaint(window_handle, &ps);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void LLViewerWindow::handleScrollWheel(LLWindow *window, S32 clicks)
|
|
{
|
|
handleScrollWheel( clicks );
|
|
}
|
|
|
|
void LLViewerWindow::handleWindowBlock(LLWindow *window)
|
|
{
|
|
send_agent_pause();
|
|
}
|
|
|
|
void LLViewerWindow::handleWindowUnblock(LLWindow *window)
|
|
{
|
|
send_agent_resume();
|
|
}
|
|
|
|
void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data)
|
|
{
|
|
const S32 SLURL_MESSAGE_TYPE = 0;
|
|
switch (data_type)
|
|
{
|
|
case SLURL_MESSAGE_TYPE:
|
|
// received URL
|
|
std::string url = (const char*)data;
|
|
LLMediaCtrl* web = NULL;
|
|
const bool trusted_browser = false;
|
|
// don't treat slapps coming from external browsers as "clicks" as this would bypass throttling
|
|
if (LLURLDispatcher::dispatch(url, "", web, trusted_browser))
|
|
{
|
|
// bring window to foreground, as it has just been "launched" from a URL
|
|
mWindow->bringToFront();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleTimerEvent(LLWindow *window)
|
|
{
|
|
if (LLViewerJoystick::getInstance()->getOverrideCamera())
|
|
{
|
|
LLViewerJoystick::getInstance()->updateStatus();
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL LLViewerWindow::handleDeviceChange(LLWindow *window)
|
|
{
|
|
// give a chance to use a joystick after startup (hot-plugging)
|
|
if (!LLViewerJoystick::getInstance()->isJoystickInitialized() )
|
|
{
|
|
LLViewerJoystick::getInstance()->init(true);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void LLViewerWindow::handlePingWatchdog(LLWindow *window, const char * msg)
|
|
{
|
|
LLAppViewer::instance()->pingMainloopTimeout(msg);
|
|
}
|
|
|
|
|
|
void LLViewerWindow::handleResumeWatchdog(LLWindow *window)
|
|
{
|
|
LLAppViewer::instance()->resumeMainloopTimeout();
|
|
}
|
|
|
|
void LLViewerWindow::handlePauseWatchdog(LLWindow *window)
|
|
{
|
|
LLAppViewer::instance()->pauseMainloopTimeout();
|
|
}
|
|
|
|
//virtual
|
|
std::string LLViewerWindow::translateString(const char* tag)
|
|
{
|
|
return LLTrans::getString( std::string(tag) );
|
|
}
|
|
|
|
//virtual
|
|
std::string LLViewerWindow::translateString(const char* tag,
|
|
const std::map<std::string, std::string>& args)
|
|
{
|
|
// LLTrans uses a special subclass of std::string for format maps,
|
|
// but we must use std::map<> in these callbacks, otherwise we create
|
|
// a dependency between LLWindow and LLFormatMapString. So copy the data.
|
|
LLStringUtil::format_map_t args_copy;
|
|
std::map<std::string,std::string>::const_iterator it = args.begin();
|
|
for ( ; it != args.end(); ++it)
|
|
{
|
|
args_copy[it->first] = it->second;
|
|
}
|
|
return LLTrans::getString( std::string(tag), args_copy);
|
|
}
|
|
|
|
//
|
|
// Classes
|
|
//
|
|
LLViewerWindow::LLViewerWindow(
|
|
const std::string& title, const std::string& name,
|
|
S32 x, S32 y,
|
|
S32 width, S32 height,
|
|
BOOL fullscreen, BOOL ignore_pixel_depth)
|
|
: mWindow(NULL),
|
|
mActive(true),
|
|
mWantFullscreen(fullscreen),
|
|
mShowFullscreenProgress(FALSE),
|
|
mWindowRectRaw(0, height, width, 0),
|
|
mWindowRectScaled(0, height, width, 0),
|
|
mLeftMouseDown(FALSE),
|
|
mMiddleMouseDown(FALSE),
|
|
mRightMouseDown(FALSE),
|
|
mToolTip(NULL),
|
|
mToolTipBlocked(FALSE),
|
|
mMouseInWindow( FALSE ),
|
|
mLastMask( MASK_NONE ),
|
|
mToolStored( NULL ),
|
|
mHideCursorPermanent( FALSE ),
|
|
mCursorHidden(FALSE),
|
|
mIgnoreActivate( FALSE ),
|
|
mHoverPick(),
|
|
mResDirty(false),
|
|
//mStatesDirty(false), //Singu Note: No longer needed. State update is now in restoreGL.
|
|
mIsFullscreenChecked(false),
|
|
mCurrResolutionIndex(0),
|
|
mProgressView(NULL)
|
|
{
|
|
LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert"));
|
|
LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal"));
|
|
|
|
LLNotifications::instance().getChannel("VW_alerts")->connectChanged(&LLViewerWindow::onAlert);
|
|
LLNotifications::instance().getChannel("VW_alertmodal")->connectChanged(&LLViewerWindow::onAlert);
|
|
|
|
// Default to application directory.
|
|
LLViewerWindow::sSnapshotBaseName = "Snapshot";
|
|
LLViewerWindow::sMovieBaseName = "SLmovie";
|
|
resetSnapshotLoc();
|
|
|
|
S32 vsync_mode = gSavedSettings.getS32("SHRenderVsyncMode");
|
|
|
|
// create window
|
|
mWindow = LLWindowManager::createWindow(this,
|
|
title, name, x, y, width, height, 0,
|
|
fullscreen,
|
|
gNoRender,
|
|
vsync_mode,
|
|
!gNoRender,
|
|
ignore_pixel_depth,
|
|
LLRenderTarget::sUseFBO ? 0 : gSavedSettings.getU32("RenderFSAASamples")); //don't use window level anti-aliasing if FBOs are enabled
|
|
|
|
if (!LLViewerShaderMgr::sInitialized)
|
|
{ //immediately initialize shaders
|
|
LLViewerShaderMgr::sInitialized = TRUE;
|
|
LLViewerShaderMgr::instance()->setShaders();
|
|
}
|
|
|
|
if (NULL == mWindow)
|
|
{
|
|
LLSplashScreen::update(LLTrans::getString("StartupRequireDriverUpdate"));
|
|
|
|
LL_WARNS("Window") << "Failed to create window, to be shutting Down, be sure your graphics driver is updated." << llendl ;
|
|
|
|
ms_sleep(5000) ; //wait for 5 seconds.
|
|
|
|
LLSplashScreen::update(LLTrans::getString("ShuttingDown"));
|
|
#if LL_LINUX || LL_SOLARIS
|
|
llwarns << "Unable to create window, be sure screen is set at 32-bit color and your graphics driver is configured correctly. See README-linux.txt or README-solaris.txt for further information."
|
|
<< llendl;
|
|
#else
|
|
LL_WARNS("Window") << "Unable to create window, be sure screen is set at 32-bit color in Control Panels->Display->Settings"
|
|
<< LL_ENDL;
|
|
#endif
|
|
LLAppViewer::instance()->fastQuit(1);
|
|
}
|
|
|
|
if (!LLAppViewer::instance()->restoreErrorTrap())
|
|
{
|
|
LL_WARNS("Window") << " Someone took over my signal/exception handler (post createWindow)!" << LL_ENDL;
|
|
}
|
|
|
|
const bool do_not_enforce = false;
|
|
mWindow->setMinSize(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, do_not_enforce); // root view not set
|
|
LLCoordScreen scr;
|
|
mWindow->getSize(&scr);
|
|
|
|
if(fullscreen && ( scr.mX!=width || scr.mY!=height))
|
|
{
|
|
llwarns << "Fullscreen has forced us in to a different resolution now using "<<scr.mX<<" x "<<scr.mY<<llendl;
|
|
gSavedSettings.setS32("FullScreenWidth",scr.mX);
|
|
gSavedSettings.setS32("FullScreenHeight",scr.mY);
|
|
}
|
|
|
|
// Get the real window rect the window was created with (since there are various OS-dependent reasons why
|
|
// the size of a window or fullscreen context may have been adjusted slightly...)
|
|
F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor");
|
|
|
|
mDisplayScale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f));
|
|
mDisplayScale *= ui_scale_factor;
|
|
LLUI::setScaleFactor(mDisplayScale);
|
|
|
|
{
|
|
LLCoordWindow size;
|
|
mWindow->getSize(&size);
|
|
mWindowRectRaw.set(0, size.mY, size.mX, 0);
|
|
mWindowRectScaled.set(0, llround((F32)size.mY / mDisplayScale.mV[VY]), llround((F32)size.mX / mDisplayScale.mV[VX]), 0);
|
|
}
|
|
|
|
LLFontManager::initClass();
|
|
|
|
//
|
|
// We want to set this stuff up BEFORE we initialize the pipeline, so we can turn off
|
|
// stuff like AGP if we think that it'll crash the viewer.
|
|
//
|
|
LL_DEBUGS("Window") << "Loading feature tables." << LL_ENDL;
|
|
|
|
LLFeatureManager::getInstance()->init();
|
|
|
|
// Initialize OpenGL Renderer
|
|
if (!LLFeatureManager::getInstance()->isFeatureAvailable("RenderVBOEnable") ||
|
|
!gGLManager.mHasVertexBufferObject)
|
|
{
|
|
gSavedSettings.setBOOL("RenderVBOEnable", FALSE);
|
|
}
|
|
LLVertexBuffer::initClass(gSavedSettings.getBOOL("RenderVBOEnable"), gSavedSettings.getBOOL("RenderVBOMappingDisable"));
|
|
LL_INFOS("RenderInit") << "LLVertexBuffer initialization done." << LL_ENDL ;
|
|
gGL.init() ;
|
|
LLImageGL::initClass(LLViewerTexture::MAX_GL_IMAGE_CATEGORY) ;
|
|
|
|
if (LLFeatureManager::getInstance()->isSafe()
|
|
|| (gSavedSettings.getS32("LastFeatureVersion") != LLFeatureManager::getInstance()->getVersion())
|
|
|| (gSavedSettings.getBOOL("ProbeHardwareOnStartup")))
|
|
{
|
|
LLFeatureManager::getInstance()->applyRecommendedSettings();
|
|
gSavedSettings.setBOOL("ProbeHardwareOnStartup", FALSE);
|
|
}
|
|
|
|
if (!gGLManager.mHasDepthClamp)
|
|
{
|
|
LL_INFOS("RenderInit") << "Missing feature GL_ARB_depth_clamp. Void water might disappear in rare cases." << LL_ENDL;
|
|
}
|
|
|
|
// If we crashed while initializng GL stuff last time, disable certain features
|
|
if (gSavedSettings.getBOOL("RenderInitError"))
|
|
{
|
|
mInitAlert = "DisplaySettingsNoShaders";
|
|
LLFeatureManager::getInstance()->setGraphicsLevel(0, false);
|
|
gSavedSettings.setU32("RenderQualityPerformance", 0);
|
|
}
|
|
|
|
// Init the image list. Must happen after GL is initialized and before the images that
|
|
// LLViewerWindow needs are requested.
|
|
gTextureList.init();
|
|
LLViewerTextureManager::init() ;
|
|
gBumpImageList.init();
|
|
|
|
// Init font system, but don't actually load the fonts yet
|
|
// because our window isn't onscreen and they take several
|
|
// seconds to parse.
|
|
if (!gNoRender)
|
|
{
|
|
LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"),
|
|
mDisplayScale.mV[VX],
|
|
mDisplayScale.mV[VY],
|
|
gDirUtilp->getAppRODataDir(),
|
|
LLUICtrlFactory::getXUIPaths());
|
|
}
|
|
// Create container for all sub-views
|
|
LLView::Params rvp;
|
|
rvp.name("root");
|
|
rvp.rect(mWindowRectScaled);
|
|
rvp.mouse_opaque(false);
|
|
rvp.follows.flags(FOLLOWS_NONE);
|
|
mRootView = LLUICtrlFactory::create<LLRootView>(rvp);
|
|
LLUI::setRootView(mRootView);
|
|
|
|
// Make avatar head look forward at start
|
|
mCurrentMousePoint.mX = getWindowWidthScaled() / 2;
|
|
mCurrentMousePoint.mY = getWindowHeightScaled() / 2;
|
|
|
|
gShowOverlayTitle = gSavedSettings.getBOOL("ShowOverlayTitle");
|
|
mOverlayTitle = gSavedSettings.getString("OverlayTitle");
|
|
// Can't have spaces in settings.ini strings, so use underscores instead and convert them.
|
|
LLStringUtil::replaceChar(mOverlayTitle, '_', ' ');
|
|
|
|
// sync the keyboard's setting with the saved setting
|
|
gSavedSettings.getControl("NumpadControl")->firePropertyChanged();
|
|
|
|
mDebugText = new LLDebugText(this);
|
|
|
|
}
|
|
|
|
void LLViewerWindow::initGLDefaults()
|
|
{
|
|
gGL.setSceneBlendType(LLRender::BT_ALPHA);
|
|
|
|
if (!LLGLSLShader::sNoFixedFunction)
|
|
{ //initialize fixed function state
|
|
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
|
|
|
|
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,LLColor4::black.mV);
|
|
glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,LLColor4::white.mV);
|
|
|
|
// lights for objects
|
|
glShadeModel( GL_SMOOTH );
|
|
|
|
gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
|
|
gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
|
|
}
|
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT,1);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
|
|
|
|
gGL.setAmbientLightColor(LLColor4::black);
|
|
|
|
glCullFace(GL_BACK);
|
|
|
|
// RN: Need this for translation and stretch manip.
|
|
gBox.prerender();
|
|
}
|
|
|
|
void LLViewerWindow::initBase()
|
|
{
|
|
S32 height = getWindowHeightScaled();
|
|
S32 width = getWindowWidthScaled();
|
|
|
|
LLRect full_window(0, height, width, 0);
|
|
|
|
adjustRectanglesForFirstUse(full_window);
|
|
|
|
////////////////////
|
|
//
|
|
// Set the gamma
|
|
//
|
|
|
|
F32 gamma = gSavedSettings.getF32("RenderGamma");
|
|
if (gamma != 0.0f)
|
|
{
|
|
getWindow()->setGamma(gamma);
|
|
}
|
|
|
|
// Create global views
|
|
|
|
// Create the floater view at the start so that other views can add children to it.
|
|
// (But wait to add it as a child of the root view so that it will be in front of the
|
|
// other views.)
|
|
|
|
// Constrain floaters to inside the menu and status bar regions.
|
|
LLRect floater_view_rect = full_window;
|
|
// make space for menu bar if we have one
|
|
floater_view_rect.mTop -= MENU_BAR_HEIGHT;
|
|
|
|
// TODO: Eliminate magic constants - please used named constants if changing this
|
|
floater_view_rect.mBottom += STATUS_BAR_HEIGHT + 12 + 16 + 2;
|
|
|
|
// Check for non-first startup
|
|
S32 floater_view_bottom = gSavedSettings.getS32("FloaterViewBottom");
|
|
if (floater_view_bottom >= 0)
|
|
{
|
|
floater_view_rect.mBottom = floater_view_bottom;
|
|
}
|
|
gFloaterView = new LLFloaterView("Floater View", floater_view_rect );
|
|
gFloaterView->setVisible(TRUE);
|
|
|
|
gSnapshotFloaterView = new LLSnapshotFloaterView("Snapshot Floater View", full_window);
|
|
// Snapshot floater must start invisible otherwise it eats all
|
|
// the tooltips. JC
|
|
gSnapshotFloaterView->setVisible(FALSE);
|
|
|
|
// Console
|
|
llassert( !gConsole );
|
|
gConsole = new LLConsole(
|
|
"console",
|
|
getChatConsoleRect(),
|
|
gSavedSettings.getS32("ChatFontSize"),
|
|
gSavedSettings.getF32("ChatPersistTime") );
|
|
gConsole->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
|
|
getRootView()->addChild(gConsole);
|
|
|
|
// Debug view over the console
|
|
gDebugView = new LLDebugView("gDebugView", full_window);
|
|
gDebugView->setFollowsAll();
|
|
gDebugView->setVisible(TRUE);
|
|
mRootView->addChild(gDebugView);
|
|
|
|
// Add floater view at the end so it will be on top, and give it tab priority over others
|
|
mRootView->addChild(gFloaterView, -1);
|
|
mRootView->addChild(gSnapshotFloaterView);
|
|
|
|
// notify above floaters!
|
|
LLRect notify_rect = full_window;
|
|
//notify_rect.mTop -= 24;
|
|
notify_rect.mBottom += STATUS_BAR_HEIGHT;
|
|
gNotifyBoxView = new LLNotifyBoxView("notify_container", notify_rect, FALSE, FOLLOWS_ALL);
|
|
mRootView->addChild(gNotifyBoxView, -2);
|
|
|
|
// Tooltips go above floaters
|
|
mToolTip = new LLTextBox( std::string("tool tip"), LLRect(0, 1, 1, 0 ) );
|
|
mToolTip->setHPad( 4 );
|
|
mToolTip->setVPad( 2 );
|
|
mToolTip->setColor( gColors.getColor( "ToolTipTextColor" ) );
|
|
mToolTip->setBorderColor( gColors.getColor( "ToolTipBorderColor" ) );
|
|
mToolTip->setBorderVisible( FALSE );
|
|
mToolTip->setBackgroundColor( gColors.getColor( "ToolTipBgColor" ) );
|
|
mToolTip->setBackgroundVisible( TRUE );
|
|
mToolTip->setFontStyle(LLFontGL::NORMAL);
|
|
mToolTip->setBorderDropshadowVisible( TRUE );
|
|
mToolTip->setVisible( FALSE );
|
|
|
|
// Add the progress bar view (startup view), which overrides everything
|
|
mProgressView = new LLProgressView(std::string("ProgressView"), full_window);
|
|
mRootView->addChild(mProgressView);
|
|
setShowProgress(FALSE);
|
|
setProgressCancelButtonVisible(FALSE);
|
|
}
|
|
|
|
|
|
void adjust_rect_top_left(const std::string& control, const LLRect& window)
|
|
{
|
|
LLRect r = gSavedSettings.getRect(control);
|
|
if (r.mLeft == 0 && r.mBottom == 0)
|
|
{
|
|
r.setLeftTopAndSize(0, window.getHeight(), r.getWidth(), r.getHeight());
|
|
gSavedSettings.setRect(control, r);
|
|
}
|
|
}
|
|
|
|
void adjust_rect_top_center(const std::string& control, const LLRect& window)
|
|
{
|
|
LLRect r = gSavedSettings.getRect(control);
|
|
if (r.mLeft == 0 && r.mBottom == 0)
|
|
{
|
|
r.setLeftTopAndSize( window.getWidth()/2 - r.getWidth()/2,
|
|
window.getHeight(),
|
|
r.getWidth(),
|
|
r.getHeight() );
|
|
gSavedSettings.setRect(control, r);
|
|
}
|
|
}
|
|
|
|
void adjust_rect_top_right(const std::string& control, const LLRect& window)
|
|
{
|
|
LLRect r = gSavedSettings.getRect(control);
|
|
if (r.mLeft == 0 && r.mBottom == 0)
|
|
{
|
|
r.setLeftTopAndSize(window.getWidth() - r.getWidth(),
|
|
window.getHeight(),
|
|
r.getWidth(),
|
|
r.getHeight());
|
|
gSavedSettings.setRect(control, r);
|
|
}
|
|
}
|
|
|
|
// *TODO: Adjust based on XUI XML
|
|
const S32 TOOLBAR_HEIGHT = 64;
|
|
|
|
void adjust_rect_bottom_left(const std::string& control, const LLRect& window)
|
|
{
|
|
LLRect r = gSavedSettings.getRect(control);
|
|
if (r.mLeft == 0 && r.mBottom == 0)
|
|
{
|
|
r.setOriginAndSize(0, TOOLBAR_HEIGHT, r.getWidth(), r.getHeight());
|
|
gSavedSettings.setRect(control, r);
|
|
}
|
|
}
|
|
|
|
void adjust_rect_bottom_center(const std::string& control, const LLRect& window)
|
|
{
|
|
LLRect r = gSavedSettings.getRect(control);
|
|
if (r.mLeft == 0 && r.mBottom == 0)
|
|
{
|
|
r.setOriginAndSize(
|
|
window.getWidth()/2 - r.getWidth()/2,
|
|
TOOLBAR_HEIGHT,
|
|
r.getWidth(),
|
|
r.getHeight());
|
|
gSavedSettings.setRect(control, r);
|
|
}
|
|
}
|
|
|
|
void adjust_rect_centered_partial_zoom(const std::string& control,
|
|
const LLRect& window)
|
|
{
|
|
LLRect rect = gSavedSettings.getRect(control);
|
|
// Only adjust on first use
|
|
if (rect.mLeft == 0 && rect.mBottom == 0)
|
|
{
|
|
S32 width = window.getWidth();
|
|
S32 height = window.getHeight();
|
|
rect.set(0, height-STATUS_BAR_HEIGHT, width, TOOL_BAR_HEIGHT);
|
|
// Make floater fill 80% of window, leaving 20% padding on
|
|
// the sides.
|
|
const F32 ZOOM_FRACTION = 0.8f;
|
|
S32 dx = (S32)(width * (1.f - ZOOM_FRACTION));
|
|
S32 dy = (S32)(height * (1.f - ZOOM_FRACTION));
|
|
rect.stretch(-dx/2, -dy/2);
|
|
gSavedSettings.setRect(control, rect);
|
|
}
|
|
}
|
|
|
|
|
|
// Many rectangles can't be placed until we know the screen size.
|
|
// These rectangles have their bottom-left corner as 0,0
|
|
void LLViewerWindow::adjustRectanglesForFirstUse(const LLRect& window)
|
|
{
|
|
LLRect r;
|
|
|
|
// *NOTE: The width and height of these floaters must be
|
|
// identical in settings.xml and their relevant floater.xml
|
|
// files, otherwise the window construction will get
|
|
// confused. JC
|
|
adjust_rect_bottom_center("FloaterMoveRect2", window);
|
|
|
|
adjust_rect_top_center("FloaterCameraRect3", window);
|
|
|
|
adjust_rect_top_left("FloaterCustomizeAppearanceRect", window);
|
|
|
|
adjust_rect_top_left("FloaterLandRect5", window);
|
|
|
|
adjust_rect_top_left("FloaterFindRect2", window);
|
|
|
|
adjust_rect_top_left("FloaterGestureRect2", window);
|
|
|
|
adjust_rect_top_right("FloaterMiniMapRect", window);
|
|
|
|
adjust_rect_top_right("FloaterLagMeter", window);
|
|
|
|
adjust_rect_top_left("FloaterBuildOptionsRect", window);
|
|
|
|
adjust_rect_bottom_left("FloaterActiveSpeakersRect", window);
|
|
|
|
adjust_rect_bottom_left("FloaterBumpRect", window);
|
|
|
|
adjust_rect_bottom_left("FloaterRegionInfo", window);
|
|
|
|
adjust_rect_bottom_left("FloaterEnvRect", window);
|
|
|
|
adjust_rect_bottom_left("FloaterAdvancedSkyRect", window);
|
|
|
|
adjust_rect_bottom_left("FloaterAdvancedWaterRect", window);
|
|
|
|
adjust_rect_bottom_left("FloaterDayCycleRect", window);
|
|
|
|
adjust_rect_top_right("FloaterStatisticsRect", window);
|
|
|
|
|
|
// bottom-right
|
|
r = gSavedSettings.getRect("FloaterInventoryRect");
|
|
if (r.mLeft == 0 && r.mBottom == 0)
|
|
{
|
|
r.setOriginAndSize(
|
|
window.getWidth() - r.getWidth(),
|
|
0,
|
|
r.getWidth(),
|
|
r.getHeight());
|
|
gSavedSettings.setRect("FloaterInventoryRect", r);
|
|
}
|
|
|
|
// adjust_rect_top_left("FloaterHUDRect2", window);
|
|
|
|
// slightly off center to be left of the avatar.
|
|
r = gSavedSettings.getRect("FloaterHUDRect2");
|
|
if (r.mLeft == 0 && r.mBottom == 0)
|
|
{
|
|
r.setOriginAndSize(
|
|
window.getWidth()/4 - r.getWidth()/2,
|
|
2*window.getHeight()/3 - r.getHeight()/2,
|
|
r.getWidth(),
|
|
r.getHeight());
|
|
gSavedSettings.setRect("FloaterHUDRect2", r);
|
|
}
|
|
}
|
|
|
|
//Rectangles need to be adjusted after the window is constructed
|
|
//in order for proper centering to take place
|
|
void LLViewerWindow::adjustControlRectanglesForFirstUse(const LLRect& window)
|
|
{
|
|
adjust_rect_bottom_center("FloaterMoveRect2", window);
|
|
adjust_rect_top_center("FloaterCameraRect3", window);
|
|
}
|
|
|
|
void LLViewerWindow::initWorldUI()
|
|
{
|
|
pre_init_menus();
|
|
if(!gMenuHolder)
|
|
{
|
|
//
|
|
// Tools for building
|
|
//
|
|
init_menus();
|
|
}
|
|
}
|
|
|
|
// initWorldUI that wasn't before logging in. Some of this may require the access the 'LindenUserDir'.
|
|
void LLViewerWindow::initWorldUI_postLogin()
|
|
{
|
|
S32 height = mRootView->getRect().getHeight();
|
|
S32 width = mRootView->getRect().getWidth();
|
|
LLRect full_window(0, height, width, 0);
|
|
|
|
//============================================
|
|
//Begin LLViewerWindow::initWorlUI
|
|
// Don't re-enter if objects are alreay created
|
|
if (gBottomPanel == NULL)
|
|
{
|
|
// panel containing chatbar, toolbar, and overlay, over floaters
|
|
gBottomPanel = new LLBottomPanel(mRootView->getRect());
|
|
mRootView->addChild(gBottomPanel);
|
|
|
|
LLFloaterNearbyMedia::updateClass(); //Dependent on the overlay panel being fully initialized.
|
|
|
|
// View for hover information
|
|
gHoverView = new LLHoverView(std::string("gHoverView"), full_window);
|
|
gHoverView->setVisible(TRUE);
|
|
mRootView->addChild(gHoverView);
|
|
|
|
gIMMgr = LLIMMgr::getInstance();
|
|
gIMMgr->loadIgnoreGroup();
|
|
|
|
// Toolbox floater
|
|
gFloaterTools = new LLFloaterTools();
|
|
gFloaterTools->setVisible(FALSE);
|
|
}
|
|
|
|
if ( gHUDView == NULL )
|
|
{
|
|
LLRect hud_rect = full_window;
|
|
hud_rect.mBottom += 50;
|
|
if (gMenuBarView)
|
|
{
|
|
hud_rect.mTop -= gMenuBarView->getRect().getHeight();
|
|
}
|
|
gHUDView = new LLHUDView(hud_rect);
|
|
// put behind everything else in the UI
|
|
mRootView->addChildInBack(gHUDView);
|
|
}
|
|
//End LLViewerWindow::initWorlUI
|
|
//============================================
|
|
|
|
|
|
LLPanel* panel_ssf_container = getRootView()->getChild<LLPanel>("state_management_buttons_container");
|
|
panel_ssf_container->setVisible(TRUE);
|
|
|
|
LLMenuOptionPathfindingRebakeNavmesh::getInstance()->initialize();
|
|
|
|
|
|
|
|
// Don't re-enter if objects are alreay created.
|
|
if (!gStatusBar)
|
|
{
|
|
// Status bar
|
|
S32 menu_bar_height = gMenuBarView->getRect().getHeight();
|
|
LLRect root_rect = getRootView()->getRect();
|
|
LLRect status_rect(0, root_rect.getHeight(), root_rect.getWidth(), root_rect.getHeight() - menu_bar_height);
|
|
gStatusBar = new LLStatusBar(std::string("status"), status_rect);
|
|
gStatusBar->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
|
|
|
|
gStatusBar->reshape(root_rect.getWidth(), gStatusBar->getRect().getHeight(), TRUE);
|
|
gStatusBar->translate(0, root_rect.getHeight() - gStatusBar->getRect().getHeight());
|
|
// sync bg color with menu bar
|
|
gStatusBar->setBackgroundColor( gMenuBarView->getBackgroundColor() );
|
|
getRootView()->addChild(gStatusBar);
|
|
|
|
// Menu holder appears on top to get first pass at all mouse events
|
|
getRootView()->sendChildToFront(gMenuHolder);
|
|
|
|
if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
|
|
{
|
|
LLFloaterChat::getInstance(LLSD())->loadHistory();
|
|
}
|
|
|
|
LLRect morph_view_rect = full_window;
|
|
morph_view_rect.stretch( -STATUS_BAR_HEIGHT );
|
|
morph_view_rect.mTop = full_window.mTop - 32;
|
|
gMorphView = new LLMorphView(std::string("gMorphView"), morph_view_rect );
|
|
mRootView->addChild(gMorphView);
|
|
gMorphView->setVisible(FALSE);
|
|
|
|
// *Note: this is where gFloaterMute used to be initialized.
|
|
|
|
LLWorldMapView::initClass();
|
|
|
|
adjust_rect_centered_partial_zoom("FloaterWorldMapRect2", full_window);
|
|
|
|
gFloaterWorldMap = new LLFloaterWorldMap();
|
|
gFloaterWorldMap->setVisible(FALSE);
|
|
|
|
// open teleport history floater and hide it initially
|
|
LLFloaterTeleportHistory::getInstance()->setVisible(FALSE);
|
|
LLFloaterTeleportHistory::loadFile("teleport_history.xml");
|
|
|
|
LLFloaterChatterBox::createInstance(LLSD());
|
|
}
|
|
mRootView->sendChildToFront(mProgressView);
|
|
}
|
|
|
|
// Destroy the UI
|
|
void LLViewerWindow::shutdownViews()
|
|
{
|
|
delete mDebugText;
|
|
mDebugText = NULL;
|
|
|
|
gSavedSettings.setS32("FloaterViewBottom", gFloaterView->getRect().mBottom);
|
|
|
|
// Cleanup global views
|
|
if (gMorphView)
|
|
{
|
|
gMorphView->setVisible(FALSE);
|
|
}
|
|
llinfos << "Global views cleaned." << llendl ;
|
|
|
|
// DEV-40930: Clear sModalStack. Otherwise, any LLModalDialog left open
|
|
// will crump with LL_ERRS.
|
|
LLModalDialog::shutdownModals();
|
|
llinfos << "LLModalDialog shut down." << llendl;
|
|
|
|
// Delete all child views.
|
|
delete mRootView;
|
|
mRootView = NULL;
|
|
llinfos << "RootView deleted." << llendl ;
|
|
|
|
if(LLMenuOptionPathfindingRebakeNavmesh::instanceExists())
|
|
LLMenuOptionPathfindingRebakeNavmesh::getInstance()->quit();
|
|
|
|
// Automatically deleted as children of mRootView. Fix the globals.
|
|
gFloaterTools = NULL;
|
|
gStatusBar = NULL;
|
|
gIMMgr = NULL;
|
|
gHoverView = NULL;
|
|
|
|
gFloaterView = NULL;
|
|
gMorphView = NULL;
|
|
|
|
gHUDView = NULL;
|
|
|
|
gNotifyBoxView = NULL;
|
|
|
|
delete mToolTip;
|
|
mToolTip = NULL;
|
|
}
|
|
|
|
void LLViewerWindow::shutdownGL()
|
|
{
|
|
//--------------------------------------------------------
|
|
// Shutdown GL cleanly. Order is very important here.
|
|
//--------------------------------------------------------
|
|
LLFontGL::destroyDefaultFonts();
|
|
LLFontManager::cleanupClass();
|
|
stop_glerror();
|
|
|
|
gSky.cleanup();
|
|
stop_glerror();
|
|
|
|
llinfos << "Cleaning up pipeline" << llendl;
|
|
gPipeline.cleanup();
|
|
stop_glerror();
|
|
|
|
//MUST clean up pipeline before cleaning up wearables
|
|
llinfos << "Cleaning up wearables" << llendl;
|
|
LLWearableList::instance().cleanup() ;
|
|
|
|
gTextureList.shutdown();
|
|
stop_glerror();
|
|
|
|
gBumpImageList.shutdown();
|
|
stop_glerror();
|
|
|
|
LLWorldMapView::cleanupTextures();
|
|
|
|
LLViewerTextureManager::cleanup() ;
|
|
LLImageGL::cleanupClass() ;
|
|
|
|
llinfos << "All textures and llimagegl images are destroyed!" << llendl ;
|
|
|
|
llinfos << "Cleaning up select manager" << llendl;
|
|
LLSelectMgr::getInstance()->cleanup();
|
|
|
|
llinfos << "Stopping GL during shutdown" << llendl;
|
|
if (!gNoRender)
|
|
{
|
|
stopGL(FALSE);
|
|
stop_glerror();
|
|
}
|
|
|
|
gGL.shutdown();
|
|
|
|
LLVertexBuffer::cleanupClass();
|
|
|
|
llinfos << "LLVertexBuffer cleaned." << llendl ;
|
|
}
|
|
|
|
// shutdownViews() and shutdownGL() need to be called first
|
|
LLViewerWindow::~LLViewerWindow()
|
|
{
|
|
llinfos << "Destroying Window" << llendl;
|
|
destroyWindow();
|
|
|
|
delete mDebugText;
|
|
mDebugText = NULL;
|
|
}
|
|
|
|
|
|
void LLViewerWindow::setCursor( ECursorType c )
|
|
{
|
|
mWindow->setCursor( c );
|
|
}
|
|
|
|
void LLViewerWindow::showCursor()
|
|
{
|
|
mWindow->showCursor();
|
|
|
|
mCursorHidden = FALSE;
|
|
}
|
|
|
|
void LLViewerWindow::hideCursor()
|
|
{
|
|
// Hide tooltips
|
|
if(mToolTip ) mToolTip->setVisible( FALSE );
|
|
|
|
// Also hide hover info
|
|
if (gHoverView) gHoverView->cancelHover();
|
|
|
|
// And hide the cursor
|
|
mWindow->hideCursor();
|
|
|
|
mCursorHidden = TRUE;
|
|
}
|
|
|
|
void LLViewerWindow::sendShapeToSim()
|
|
{
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
if(!msg) return;
|
|
msg->newMessageFast(_PREHASH_AgentHeightWidth);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode);
|
|
msg->nextBlockFast(_PREHASH_HeightWidthBlock);
|
|
msg->addU32Fast(_PREHASH_GenCounter, 0);
|
|
U16 height16 = (U16) mWindowRectRaw.getHeight();
|
|
U16 width16 = (U16) mWindowRectRaw.getWidth();
|
|
msg->addU16Fast(_PREHASH_Height, height16);
|
|
msg->addU16Fast(_PREHASH_Width, width16);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
// Must be called after window is created to set up agent
|
|
// camera variables and UI variables.
|
|
void LLViewerWindow::reshape(S32 width, S32 height)
|
|
{
|
|
// Destroying the window at quit time generates spurious
|
|
// reshape messages. We don't care about these, and we
|
|
// don't want to send messages because the message system
|
|
// may have been destructed.
|
|
if (!LLApp::isExiting())
|
|
{
|
|
if (gNoRender)
|
|
{
|
|
return;
|
|
}
|
|
|
|
gWindowResized = TRUE;
|
|
glViewport(0, 0, width, height );
|
|
|
|
if (height > 0)
|
|
{
|
|
LLViewerCamera::getInstance()->setViewHeightInPixels( height );
|
|
if (mWindow->getFullscreen())
|
|
{
|
|
// force to 4:3 aspect for odd resolutions
|
|
LLViewerCamera::getInstance()->setAspect( getDisplayAspectRatio() );
|
|
}
|
|
else
|
|
{
|
|
LLViewerCamera::getInstance()->setAspect( width / (F32) height);
|
|
}
|
|
}
|
|
|
|
// update our window rectangle
|
|
mWindowRectRaw.mRight = mWindowRectRaw.mLeft + width;
|
|
mWindowRectRaw.mTop = mWindowRectRaw.mBottom + height;
|
|
calcDisplayScale();
|
|
|
|
BOOL display_scale_changed = mDisplayScale != LLUI::getScaleFactor();
|
|
LLUI::setScaleFactor(mDisplayScale);
|
|
|
|
// update our window rectangle
|
|
mWindowRectScaled.mRight = mWindowRectScaled.mLeft + llround((F32)width / mDisplayScale.mV[VX]);
|
|
mWindowRectScaled.mTop = mWindowRectScaled.mBottom + llround((F32)height / mDisplayScale.mV[VY]);
|
|
|
|
setup2DViewport();
|
|
|
|
// Inform lower views of the change
|
|
// round up when converting coordinates to make sure there are no gaps at edge of window
|
|
LLView::sForceReshape = display_scale_changed;
|
|
mRootView->reshape(llceil((F32)width / mDisplayScale.mV[VX]), llceil((F32)height / mDisplayScale.mV[VY]));
|
|
LLView::sForceReshape = FALSE;
|
|
|
|
// clear font width caches
|
|
if (display_scale_changed)
|
|
{
|
|
LLHUDObject::reshapeAll();
|
|
}
|
|
|
|
sendShapeToSim();
|
|
|
|
|
|
// store the mode the user wants (even if not there yet)
|
|
gSavedSettings.setBOOL("FullScreen", mWantFullscreen);
|
|
|
|
// store new settings for the mode we are in, regardless
|
|
if (!mWindow->getFullscreen())
|
|
{
|
|
// Only save size if not maximized
|
|
BOOL maximized = mWindow->getMaximized();
|
|
gSavedSettings.setBOOL("WindowMaximized", maximized);
|
|
|
|
LLCoordScreen window_size;
|
|
if (!maximized
|
|
&& mWindow->getSize(&window_size))
|
|
{
|
|
gSavedSettings.setS32("WindowWidth", window_size.mX);
|
|
gSavedSettings.setS32("WindowHeight", window_size.mY);
|
|
}
|
|
}
|
|
|
|
LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width);
|
|
LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height);
|
|
gResizeScreenTexture = TRUE;
|
|
LLLayoutStack::updateClass();
|
|
}
|
|
}
|
|
|
|
|
|
// Hide normal UI when a logon fails
|
|
void LLViewerWindow::setNormalControlsVisible( BOOL visible )
|
|
{
|
|
if (gBottomPanel)
|
|
{
|
|
gBottomPanel->setVisible(visible);
|
|
gBottomPanel->setEnabled(visible);
|
|
}
|
|
|
|
if ( gMenuBarView )
|
|
{
|
|
gMenuBarView->setVisible( visible );
|
|
gMenuBarView->setEnabled( visible );
|
|
|
|
// ...and set the menu color appropriately.
|
|
setMenuBackgroundColor(gAgent.getGodLevel() > GOD_NOT,
|
|
LLViewerLogin::getInstance()->isInProductionGrid());
|
|
}
|
|
|
|
if ( gStatusBar )
|
|
{
|
|
gStatusBar->setVisible( visible );
|
|
gStatusBar->setEnabled( visible );
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::setMenuBackgroundColor(bool god_mode, bool dev_grid)
|
|
{
|
|
LLSD args;
|
|
LLColor4 new_bg_color;
|
|
|
|
if(god_mode && LLViewerLogin::getInstance()->isInProductionGrid())
|
|
{
|
|
new_bg_color = gColors.getColor( "MenuBarGodBgColor" );
|
|
}
|
|
else if(god_mode && !LLViewerLogin::getInstance()->isInProductionGrid())
|
|
{
|
|
new_bg_color = gColors.getColor( "MenuNonProductionGodBgColor" );
|
|
}
|
|
else if(!god_mode && !LLViewerLogin::getInstance()->isInProductionGrid())
|
|
{
|
|
new_bg_color = gColors.getColor( "MenuNonProductionBgColor" );
|
|
}
|
|
else
|
|
{
|
|
new_bg_color = gColors.getColor( "MenuBarBgColor" );
|
|
}
|
|
|
|
if(gMenuBarView)
|
|
{
|
|
gMenuBarView->setBackgroundColor( new_bg_color );
|
|
}
|
|
|
|
if(gStatusBar)
|
|
{
|
|
gStatusBar->setBackgroundColor( new_bg_color );
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::drawDebugText()
|
|
{
|
|
gGL.color4f(1,1,1,1);
|
|
gGL.pushMatrix();
|
|
gGL.pushUIMatrix();
|
|
if (LLGLSLShader::sNoFixedFunction)
|
|
{
|
|
gUIProgram.bind();
|
|
}
|
|
{
|
|
// scale view by UI global scale factor and aspect ratio correction factor
|
|
gGL.scaleUI(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f);
|
|
mDebugText->draw();
|
|
}
|
|
gGL.popUIMatrix();
|
|
gGL.popMatrix();
|
|
|
|
gGL.flush();
|
|
if (LLGLSLShader::sNoFixedFunction)
|
|
{
|
|
gUIProgram.unbind();
|
|
}
|
|
}
|
|
|
|
extern void check_blend_funcs();
|
|
void LLViewerWindow::draw()
|
|
{
|
|
|
|
#if LL_DEBUG
|
|
LLView::sIsDrawing = TRUE;
|
|
#endif
|
|
stop_glerror();
|
|
|
|
LLUI::setLineWidth(1.f);
|
|
|
|
LLUI::setLineWidth(1.f);
|
|
// Reset any left-over transforms
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
|
|
gGL.loadIdentity();
|
|
|
|
//S32 screen_x, screen_y;
|
|
|
|
// HACK for timecode debugging
|
|
static const LLCachedControl<bool> display_timecode("DisplayTimecode",false);
|
|
if (display_timecode)
|
|
{
|
|
// draw timecode block
|
|
std::string text;
|
|
|
|
gGL.loadIdentity();
|
|
|
|
microsecondsToTimecodeString(gFrameTime,text);
|
|
const LLFontGL* font = LLFontGL::getFontSansSerif();
|
|
font->renderUTF8(text, 0,
|
|
llround((getWindowWidthScaled()/2)-100.f),
|
|
llround((getWindowHeightScaled()-60.f)),
|
|
LLColor4( 1.f, 1.f, 1.f, 1.f ),
|
|
LLFontGL::LEFT, LLFontGL::TOP);
|
|
}
|
|
|
|
// Draw all nested UI views.
|
|
// No translation needed, this view is glued to 0,0
|
|
|
|
if (LLGLSLShader::sNoFixedFunction)
|
|
{
|
|
gUIProgram.bind();
|
|
}
|
|
|
|
gGL.pushMatrix();
|
|
LLUI::pushMatrix();
|
|
{
|
|
|
|
// scale view by UI global scale factor and aspect ratio correction factor
|
|
gGL.scaleUI(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f);
|
|
|
|
LLVector2 old_scale_factor = LLUI::getScaleFactor();
|
|
// apply camera zoom transform (for high res screenshots)
|
|
F32 zoom_factor = LLViewerCamera::getInstance()->getZoomFactor();
|
|
S16 sub_region = LLViewerCamera::getInstance()->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
|
|
gGL.translatef((F32)getWindowWidthScaled() * -(F32)pos_x,
|
|
(F32)getWindowHeightScaled() * -(F32)pos_y,
|
|
0.f);
|
|
gGL.scalef(zoom_factor, zoom_factor, 1.f);
|
|
LLUI::getScaleFactor() *= zoom_factor;
|
|
}
|
|
|
|
// Draw tool specific overlay on world
|
|
LLToolMgr::getInstance()->getCurrentTool()->draw();
|
|
|
|
if( gAgentCamera.cameraMouselook() )
|
|
{
|
|
drawMouselookInstructions();
|
|
stop_glerror();
|
|
}
|
|
|
|
// Draw all nested UI views.
|
|
// No translation needed, this view is glued to 0,0
|
|
if(gDebugGL)check_blend_funcs();
|
|
mRootView->draw();
|
|
if(gDebugGL)check_blend_funcs();
|
|
|
|
// Draw optional on-top-of-everyone view
|
|
LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
|
|
if (top_ctrl && top_ctrl->getVisible())
|
|
{
|
|
S32 screen_x, screen_y;
|
|
top_ctrl->localPointToScreen(0, 0, &screen_x, &screen_y);
|
|
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
LLUI::pushMatrix();
|
|
LLUI::translate( (F32) screen_x, (F32) screen_y);
|
|
if(gDebugGL)check_blend_funcs();
|
|
top_ctrl->draw();
|
|
if(gDebugGL)check_blend_funcs();
|
|
LLUI::popMatrix();
|
|
}
|
|
|
|
// Draw tooltips
|
|
// Adjust their rectangle so they don't go off the top or bottom
|
|
// of the screen.
|
|
if( mToolTip && mToolTip->getVisible() && !mToolTipBlocked )
|
|
{
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
LLUI::pushMatrix();
|
|
{
|
|
S32 tip_height = mToolTip->getRect().getHeight();
|
|
|
|
S32 screen_x, screen_y;
|
|
mToolTip->localPointToScreen(0, -24 - tip_height,
|
|
&screen_x, &screen_y);
|
|
|
|
// If tooltip would draw off the bottom of the screen,
|
|
// show it from the cursor tip position.
|
|
if (screen_y < tip_height)
|
|
{
|
|
mToolTip->localPointToScreen(0, 0, &screen_x, &screen_y);
|
|
}
|
|
LLUI::translate( (F32) screen_x, (F32) screen_y, 0);
|
|
mToolTip->draw();
|
|
}
|
|
LLUI::popMatrix();
|
|
}
|
|
|
|
if( gShowOverlayTitle && !mOverlayTitle.empty() )
|
|
{
|
|
// Used for special titles such as "Second Life - Special E3 2003 Beta"
|
|
const S32 DIST_FROM_TOP = 20;
|
|
LLFontGL::getFontSansSerifBig()->renderUTF8(
|
|
mOverlayTitle, 0,
|
|
llround( getWindowWidthScaled() * 0.5f),
|
|
getWindowHeightScaled() - DIST_FROM_TOP,
|
|
LLColor4(1, 1, 1, 0.4f),
|
|
LLFontGL::HCENTER, LLFontGL::TOP);
|
|
}
|
|
|
|
LLUI::setScaleFactor(old_scale_factor);
|
|
}
|
|
LLUI::popMatrix();
|
|
gGL.popMatrix();
|
|
|
|
if (LLGLSLShader::sNoFixedFunction)
|
|
{
|
|
gUIProgram.unbind();
|
|
}
|
|
#if LL_DEBUG
|
|
LLView::sIsDrawing = FALSE;
|
|
#endif
|
|
}
|
|
|
|
// Takes a single keydown event, usually when UI is visible
|
|
BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
|
|
{
|
|
// Hide tooltips on keypress
|
|
mToolTipBlocked = TRUE; // block until next time mouse is moved
|
|
|
|
// Also hide hover info on keypress
|
|
if (gHoverView)
|
|
{
|
|
gHoverView->cancelHover();
|
|
gHoverView->setTyping(TRUE);
|
|
}
|
|
|
|
if (gFocusMgr.getKeyboardFocus()
|
|
&& !(mask & (MASK_CONTROL | MASK_ALT))
|
|
&& !gFocusMgr.getKeystrokesOnly())
|
|
{
|
|
// We have keyboard focus, and it's not an accelerator
|
|
if (key < 0x80)
|
|
{
|
|
// Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first.
|
|
return (gFocusMgr.getKeyboardFocus() != NULL);
|
|
}
|
|
}
|
|
|
|
// HACK look for UI editing keys
|
|
if (LLView::sEditingUI && LLFloaterEditUI::processKeystroke(key, mask))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Explicit hack for debug menu.
|
|
if ((MASK_ALT & mask) &&
|
|
(MASK_CONTROL & mask) &&
|
|
('D' == key || 'd' == key))
|
|
{
|
|
toggle_debug_menus(NULL);
|
|
}
|
|
|
|
// handle shift-escape key (reset camera view)
|
|
if (key == KEY_ESCAPE && mask == MASK_SHIFT)
|
|
{
|
|
handle_reset_view();
|
|
return TRUE;
|
|
}
|
|
|
|
// let menus handle navigation keys for navigation
|
|
if ((gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE))
|
|
|| (gLoginMenuBarView && gLoginMenuBarView->handleKey(key, mask, TRUE))
|
|
|| (gMenuHolder && gMenuHolder->handleKey(key, mask, TRUE)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus();
|
|
|
|
// give menus a chance to handle modified (Ctrl, Alt) shortcut keys before current focus
|
|
// as long as focus isn't locked
|
|
if (mask & (MASK_CONTROL | MASK_ALT) && !gFocusMgr.focusLocked())
|
|
{
|
|
// Check the current floater's menu first, if it has one.
|
|
if (gFocusMgr.keyboardFocusHasAccelerators()
|
|
&& keyboard_focus
|
|
&& keyboard_focus->handleKey(key,mask,FALSE))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* Singu Note: This caused a bug where the menu ate keys before parents of keyboard_focus for some reason, breaking multifloaters usage of ctrl-w to close their selected child floater
|
|
if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask))
|
|
|| (gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
*/
|
|
}
|
|
|
|
// give floaters first chance to handle TAB key
|
|
// so frontmost floater gets focus
|
|
// if nothing has focus, go to first or last UI element as appropriate
|
|
if (key == KEY_TAB && (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL))
|
|
{
|
|
if (gMenuHolder) gMenuHolder->hideMenus();
|
|
|
|
// if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode
|
|
gFloaterView->setCycleMode((mask & MASK_CONTROL) != 0);
|
|
|
|
// do CTRL-TAB and CTRL-SHIFT-TAB logic
|
|
if (mask & MASK_SHIFT)
|
|
{
|
|
mRootView->focusPrevRoot();
|
|
}
|
|
else
|
|
{
|
|
mRootView->focusNextRoot();
|
|
}
|
|
return TRUE;
|
|
}
|
|
/* Singu TODO: gEditMenu?
|
|
// hidden edit menu for cut/copy/paste
|
|
if (gEditMenu && gEditMenu->handleAcceleratorKey(key, mask))
|
|
{
|
|
return TRUE;
|
|
}
|
|
*/
|
|
|
|
// Traverses up the hierarchy
|
|
if( keyboard_focus )
|
|
{
|
|
// arrow keys move avatar while chatting hack
|
|
if (gChatBar && gChatBar->inputEditorHasFocus())
|
|
{
|
|
// If text field is empty, there's no point in trying to move
|
|
// cursor with arrow keys, so allow movement
|
|
if (gChatBar->getCurrentChat().empty()
|
|
|| gSavedSettings.getBOOL("ArrowKeysMoveAvatar"))
|
|
{
|
|
/* Singu Note: We do this differently from LL to preserve the Ctrl-<Any ArrowKey> behavior in the chatbar, and we don't need alt because we're not CHUI
|
|
// let Control-Up and Control-Down through for chat line history,
|
|
if (!(key == KEY_UP && mask == MASK_CONTROL)
|
|
&& !(key == KEY_DOWN && mask == MASK_CONTROL)
|
|
&& !(key == KEY_UP && mask == MASK_ALT)
|
|
&& !(key == KEY_DOWN && mask == MASK_ALT))
|
|
*/
|
|
{
|
|
switch(key)
|
|
{
|
|
case KEY_LEFT:
|
|
case KEY_RIGHT:
|
|
case KEY_UP:
|
|
case KEY_DOWN:
|
|
if (mask == MASK_CONTROL)
|
|
break;
|
|
case KEY_PAGE_UP:
|
|
case KEY_PAGE_DOWN:
|
|
case KEY_HOME:
|
|
// when chatbar is empty or ArrowKeysMoveAvatar set,
|
|
//pass arrow keys on to avatar...
|
|
return FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (keyboard_focus->handleKey(key, mask, FALSE))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if( LLToolMgr::getInstance()->getCurrentTool()->handleKey(key, mask) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Try for a new-format gesture
|
|
if (LLGestureMgr::instance().triggerGesture(key, mask))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// See if this is a gesture trigger. If so, eat the key and
|
|
// don't pass it down to the menus.
|
|
if (gGestureList.trigger(key, mask))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// give menus a chance to handle unmodified accelerator keys
|
|
if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask))
|
|
||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// don't pass keys on to world when something in ui has focus
|
|
return gFocusMgr.childHasKeyboardFocus(mRootView)
|
|
|| LLMenuGL::getKeyboardMode()
|
|
|| (gMenuBarView && gMenuBarView->getHighlightedItem() && gMenuBarView->getHighlightedItem()->isActive());
|
|
}
|
|
|
|
|
|
BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
|
|
{
|
|
// HACK: We delay processing of return keys until they arrive as a Unicode char,
|
|
// so that if you're typing chat text at low frame rate, we don't send the chat
|
|
// until all keystrokes have been entered. JC
|
|
// HACK: Numeric keypad <enter> on Mac is Unicode 3
|
|
// HACK: Control-M on Windows is Unicode 13
|
|
if ((uni_char == 13 && mask != MASK_CONTROL)
|
|
|| (uni_char == 3 && mask == MASK_NONE))
|
|
{
|
|
return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN));
|
|
}
|
|
|
|
// let menus handle navigation (jump) keys
|
|
if (gMenuBarView && gMenuBarView->handleUnicodeChar(uni_char, TRUE))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Traverses up the hierarchy
|
|
LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus();
|
|
if( keyboard_focus )
|
|
{
|
|
if (keyboard_focus->handleUnicodeChar(uni_char, FALSE))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//// Topmost view gets a chance before the hierarchy
|
|
//LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
|
|
//if (top_ctrl && top_ctrl->handleUnicodeChar( uni_char, FALSE ) )
|
|
//{
|
|
// return TRUE;
|
|
//}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void LLViewerWindow::handleScrollWheel(S32 clicks)
|
|
{
|
|
LLView::sMouseHandlerMessage.clear();
|
|
|
|
gMouseIdleTimer.reset();
|
|
|
|
// Hide tooltips
|
|
if( mToolTip )
|
|
{
|
|
mToolTip->setVisible( FALSE );
|
|
}
|
|
|
|
LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
|
|
if( mouse_captor )
|
|
{
|
|
S32 local_x;
|
|
S32 local_y;
|
|
mouse_captor->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y );
|
|
mouse_captor->handleScrollWheel(local_x, local_y, clicks);
|
|
if (LLView::sDebugMouseHandling)
|
|
{
|
|
llinfos << "Scroll Wheel handled by captor " << mouse_captor->getName() << llendl;
|
|
}
|
|
return;
|
|
}
|
|
|
|
LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
|
|
if (top_ctrl)
|
|
{
|
|
S32 local_x;
|
|
S32 local_y;
|
|
top_ctrl->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y );
|
|
if (top_ctrl->handleScrollWheel(local_x, local_y, clicks)) return;
|
|
}
|
|
|
|
if (mRootView->handleScrollWheel(mCurrentMousePoint.mX, mCurrentMousePoint.mY, clicks) )
|
|
{
|
|
if (LLView::sDebugMouseHandling)
|
|
{
|
|
llinfos << "Scroll Wheel" << LLView::sMouseHandlerMessage << llendl;
|
|
}
|
|
return;
|
|
}
|
|
else if (LLView::sDebugMouseHandling)
|
|
{
|
|
llinfos << "Scroll Wheel not handled by view" << llendl;
|
|
}
|
|
|
|
// Zoom the camera in and out behavior
|
|
|
|
if(top_ctrl == 0
|
|
&& getWorldViewRectScaled().pointInRect(mCurrentMousePoint.mX, mCurrentMousePoint.mY)
|
|
&& gAgentCamera.isInitialized())
|
|
gAgentCamera.handleScrollWheel(clicks);
|
|
|
|
return;
|
|
}
|
|
|
|
void LLViewerWindow::moveCursorToCenter()
|
|
{
|
|
if (gSavedSettings.getBOOL("SGAbsolutePointer")) {
|
|
return;
|
|
}
|
|
|
|
S32 x = getWorldViewWidthScaled() / 2;
|
|
S32 y = getWorldViewHeightScaled() / 2;
|
|
|
|
//on a forced move, all deltas get zeroed out to prevent jumping
|
|
mCurrentMousePoint.set(x,y);
|
|
mLastMousePoint.set(x,y);
|
|
mCurrentMouseDelta.set(0,0);
|
|
|
|
LLUI::setMousePositionScreen(x, y);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Hover handlers
|
|
//
|
|
|
|
// Update UI based on stored mouse position from mouse-move
|
|
// event processing.
|
|
void LLViewerWindow::updateUI()
|
|
{
|
|
static LLFastTimer::DeclareTimer ftm("Update UI");
|
|
LLFastTimer t(ftm);
|
|
|
|
static std::string last_handle_msg;
|
|
// animate layout stacks so we have up to date rect for world view
|
|
LLLayoutStack::updateClass();
|
|
|
|
LLView::sMouseHandlerMessage.clear();
|
|
|
|
S32 x = mCurrentMousePoint.mX;
|
|
S32 y = mCurrentMousePoint.mY;
|
|
|
|
MASK mask = gKeyboard->currentMask(TRUE);
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
|
|
{
|
|
gDebugRaycastFaceHit = -1;
|
|
gDebugRaycastObject = cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE,
|
|
&gDebugRaycastFaceHit,
|
|
&gDebugRaycastIntersection,
|
|
&gDebugRaycastTexCoord,
|
|
&gDebugRaycastNormal,
|
|
&gDebugRaycastTangent,
|
|
&gDebugRaycastStart,
|
|
&gDebugRaycastEnd);
|
|
}
|
|
|
|
updateMouseDelta();
|
|
|
|
if (gNoRender)
|
|
{
|
|
return;
|
|
}
|
|
|
|
updateKeyboardFocus();
|
|
|
|
BOOL handled = FALSE;
|
|
|
|
LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
|
|
LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
|
|
LLView* captor_view = dynamic_cast<LLView*>(mouse_captor);
|
|
|
|
//FIXME: only include captor and captor's ancestors if mouse is truly over them --RN
|
|
|
|
//build set of views containing mouse cursor by traversing UI hierarchy and testing
|
|
//screen rect against mouse cursor
|
|
view_handle_set_t mouse_hover_set;
|
|
|
|
// constraint mouse enter events to children of mouse captor
|
|
LLView* root_view = captor_view;
|
|
|
|
// if mouse captor doesn't exist or isn't a LLView
|
|
// then allow mouse enter events on entire UI hierarchy
|
|
if (!root_view)
|
|
{
|
|
root_view = mRootView;
|
|
}
|
|
|
|
// only update mouse hover set when UI is visible (since we shouldn't send hover events to invisible UI
|
|
// if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
|
|
{
|
|
// include all ancestors of captor_view as automatically having mouse
|
|
if (captor_view)
|
|
{
|
|
LLView* captor_parent_view = captor_view->getParent();
|
|
while(captor_parent_view)
|
|
{
|
|
mouse_hover_set.insert(captor_parent_view->getHandle());
|
|
captor_parent_view = captor_parent_view->getParent();
|
|
}
|
|
}
|
|
|
|
// while the top_ctrl contains the mouse cursor, only it and its descendants will receive onMouseEnter events
|
|
if (top_ctrl && top_ctrl->calcScreenBoundingRect().pointInRect(x, y))
|
|
{
|
|
// iterator over contents of top_ctrl, and throw into mouse_hover_set
|
|
for (LLView::tree_iterator_t it = top_ctrl->beginTreeDFS();
|
|
it != top_ctrl->endTreeDFS();
|
|
++it)
|
|
{
|
|
LLView* viewp = *it;
|
|
if (viewp->getVisible()
|
|
&& viewp->calcScreenBoundingRect().pointInRect(x, y))
|
|
{
|
|
// we have a view that contains the mouse, add it to the set
|
|
mouse_hover_set.insert(viewp->getHandle());
|
|
}
|
|
else
|
|
{
|
|
// skip this view and all of its children
|
|
it.skipDescendants();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// walk UI tree in depth-first order
|
|
for (LLView::tree_iterator_t it = root_view->beginTreeDFS();
|
|
it != root_view->endTreeDFS();
|
|
++it)
|
|
{
|
|
LLView* viewp = *it;
|
|
// calculating the screen rect involves traversing the parent, so this is less than optimal
|
|
if (viewp->getVisible()
|
|
&& viewp->calcScreenBoundingRect().pointInRect(x, y))
|
|
{
|
|
|
|
// if this view is mouse opaque, nothing behind it should be in mouse_hover_set
|
|
if (viewp->getMouseOpaque())
|
|
{
|
|
// constrain further iteration to children of this widget
|
|
it = viewp->beginTreeDFS();
|
|
}
|
|
|
|
// we have a view that contains the mouse, add it to the set
|
|
mouse_hover_set.insert(viewp->getHandle());
|
|
}
|
|
else
|
|
{
|
|
// skip this view and all of its children
|
|
it.skipDescendants();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef std::vector<LLHandle<LLView> > view_handle_list_t;
|
|
|
|
// call onMouseEnter() on all views which contain the mouse cursor but did not before
|
|
view_handle_list_t mouse_enter_views;
|
|
std::set_difference(mouse_hover_set.begin(), mouse_hover_set.end(),
|
|
mMouseHoverViews.begin(), mMouseHoverViews.end(),
|
|
std::back_inserter(mouse_enter_views));
|
|
for (view_handle_list_t::iterator it = mouse_enter_views.begin();
|
|
it != mouse_enter_views.end();
|
|
++it)
|
|
{
|
|
LLView* viewp = it->get();
|
|
if (viewp)
|
|
{
|
|
LLRect view_screen_rect = viewp->calcScreenRect();
|
|
viewp->onMouseEnter(x - view_screen_rect.mLeft, y - view_screen_rect.mBottom, mask);
|
|
}
|
|
}
|
|
|
|
// call onMouseLeave() on all views which no longer contain the mouse cursor
|
|
view_handle_list_t mouse_leave_views;
|
|
std::set_difference(mMouseHoverViews.begin(), mMouseHoverViews.end(),
|
|
mouse_hover_set.begin(), mouse_hover_set.end(),
|
|
std::back_inserter(mouse_leave_views));
|
|
for (view_handle_list_t::iterator it = mouse_leave_views.begin();
|
|
it != mouse_leave_views.end();
|
|
++it)
|
|
{
|
|
LLView* viewp = it->get();
|
|
if (viewp)
|
|
{
|
|
LLRect view_screen_rect = viewp->calcScreenRect();
|
|
viewp->onMouseLeave(x - view_screen_rect.mLeft, y - view_screen_rect.mBottom, mask);
|
|
}
|
|
}
|
|
|
|
// store resulting hover set for next frame
|
|
swap(mMouseHoverViews, mouse_hover_set);
|
|
|
|
// only handle hover events when UI is enabled
|
|
// if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
|
|
{
|
|
|
|
if( mouse_captor )
|
|
{
|
|
// Pass hover events to object capturing mouse events.
|
|
S32 local_x;
|
|
S32 local_y;
|
|
mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
|
|
handled = mouse_captor->handleHover(local_x, local_y, mask);
|
|
if (LLView::sDebugMouseHandling)
|
|
{
|
|
llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl;
|
|
}
|
|
|
|
if( !handled )
|
|
{
|
|
lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (top_ctrl)
|
|
{
|
|
S32 local_x, local_y;
|
|
top_ctrl->screenPointToLocal( x, y, &local_x, &local_y );
|
|
handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleHover(local_x, local_y, mask);
|
|
}
|
|
|
|
if ( !handled )
|
|
{
|
|
// x and y are from last time mouse was in window
|
|
// mMouseInWindow tracks *actual* mouse location
|
|
if (mMouseInWindow && mRootView->handleHover(x, y, mask) )
|
|
{
|
|
if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg)
|
|
{
|
|
last_handle_msg = LLView::sMouseHandlerMessage;
|
|
llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl;
|
|
}
|
|
handled = TRUE;
|
|
}
|
|
else if (LLView::sDebugMouseHandling)
|
|
{
|
|
if (last_handle_msg != LLStringUtil::null)
|
|
{
|
|
last_handle_msg.clear();
|
|
llinfos << "Hover not handled by view" << llendl;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !handled )
|
|
{
|
|
lldebugst(LLERR_USER_INPUT) << "hover not handled by top view or root" << llendl;
|
|
}
|
|
}
|
|
|
|
// *NOTE: sometimes tools handle the mouse as a captor, so this
|
|
// logic is a little confusing
|
|
LLTool *tool = NULL;
|
|
if (gHoverView)
|
|
{
|
|
tool = LLToolMgr::getInstance()->getCurrentTool();
|
|
|
|
if(!handled && tool)
|
|
{
|
|
handled = tool->handleHover(x, y, mask);
|
|
|
|
if (!mWindow->isCursorHidden())
|
|
{
|
|
gHoverView->updateHover(tool);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Cancel hovering if any UI element handled the event.
|
|
gHoverView->cancelHover();
|
|
}
|
|
|
|
}
|
|
|
|
// Show a new tool tip (or update one that is already shown)
|
|
BOOL tool_tip_handled = FALSE;
|
|
std::string tool_tip_msg;
|
|
static const LLCachedControl<F32> tool_tip_delay("ToolTipDelay",.7f);
|
|
F32 tooltip_delay = tool_tip_delay;
|
|
//HACK: hack for tool-based tooltips which need to pop up more quickly
|
|
//Also for show xui names as tooltips debug mode
|
|
if ((mouse_captor && !mouse_captor->isView()) || LLUI::sShowXUINames)
|
|
{
|
|
static const LLCachedControl<F32> drag_and_drop_tool_tip_delay("DragAndDropToolTipDelay",.1f);
|
|
tooltip_delay = drag_and_drop_tool_tip_delay;
|
|
}
|
|
if( handled &&
|
|
gMouseIdleTimer.getElapsedTimeF32() > tooltip_delay &&
|
|
!mWindow->isCursorHidden() )
|
|
{
|
|
LLRect screen_sticky_rect;
|
|
LLMouseHandler *mh;
|
|
S32 local_x, local_y;
|
|
if (mouse_captor)
|
|
{
|
|
mouse_captor->screenPointToLocal(x, y, &local_x, &local_y);
|
|
mh = mouse_captor;
|
|
}
|
|
else if (top_ctrl)
|
|
{
|
|
top_ctrl->screenPointToLocal(x, y, &local_x, &local_y);
|
|
mh = top_ctrl;
|
|
}
|
|
else
|
|
{
|
|
local_x = x; local_y = y;
|
|
mh = mRootView;
|
|
}
|
|
|
|
BOOL tooltip_vis = FALSE;
|
|
if (shouldShowToolTipFor(mh))
|
|
{
|
|
tool_tip_handled = mh->handleToolTip(local_x, local_y, tool_tip_msg, &screen_sticky_rect );
|
|
|
|
if( tool_tip_handled && !tool_tip_msg.empty() )
|
|
{
|
|
mToolTipStickyRect = screen_sticky_rect;
|
|
mToolTip->setWrappedText( tool_tip_msg, 200 );
|
|
mToolTip->reshapeToFitText();
|
|
mToolTip->setOrigin( x, y );
|
|
LLRect virtual_window_rect(0, getWindowHeight(), getWindowWidth(), 0);
|
|
mToolTip->translateIntoRect( virtual_window_rect, FALSE );
|
|
tooltip_vis = TRUE;
|
|
}
|
|
}
|
|
|
|
if (mToolTip)
|
|
{
|
|
mToolTip->setVisible( tooltip_vis );
|
|
}
|
|
}
|
|
}
|
|
|
|
updateLayout();
|
|
|
|
mLastMousePoint = mCurrentMousePoint;
|
|
|
|
// cleanup unused selections when no modal dialogs are open
|
|
if (LLModalDialog::activeCount() == 0)
|
|
{
|
|
LLViewerParcelMgr::getInstance()->deselectUnused();
|
|
}
|
|
|
|
if (LLModalDialog::activeCount() == 0)
|
|
{
|
|
LLSelectMgr::getInstance()->deselectUnused();
|
|
}
|
|
|
|
// per frame picking - for tooltips and changing cursor over interactive objects
|
|
static S32 previous_x = -1;
|
|
static S32 previous_y = -1;
|
|
static BOOL mouse_moved_since_pick = FALSE;
|
|
|
|
if ((previous_x != x) || (previous_y != y))
|
|
mouse_moved_since_pick = TRUE;
|
|
|
|
static const LLCachedControl<F32> picks_moving("PicksPerSecondMouseMoving",5.f);
|
|
static const LLCachedControl<F32> picks_stationary("PicksPerSecondMouseStationary",0.f);
|
|
if( !getCursorHidden()
|
|
// When in-world media is in focus, pick every frame so that browser mouse-overs, dragging scrollbars, etc. work properly.
|
|
&& (LLViewerMediaFocus::getInstance()->getFocus()
|
|
|| ((mouse_moved_since_pick) && (picks_moving > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_moving))
|
|
|| ((!mouse_moved_since_pick) && (picks_stationary > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_stationary))))
|
|
{
|
|
mouse_moved_since_pick = FALSE;
|
|
mPickTimer.reset();
|
|
pickAsync(getCurrentMouseX(), getCurrentMouseY(), mask, hoverPickCallback, TRUE, TRUE);
|
|
}
|
|
|
|
previous_x = x;
|
|
previous_y = y;
|
|
|
|
return;
|
|
}
|
|
|
|
/* static */
|
|
void LLViewerWindow::hoverPickCallback(const LLPickInfo& pick_info)
|
|
{
|
|
gViewerWindow->mHoverPick = pick_info;
|
|
}
|
|
|
|
void LLViewerWindow::updateLayout()
|
|
{
|
|
static const LLCachedControl<bool> freeze_time("FreezeTime",0);
|
|
LLTool* tool = LLToolMgr::getInstance()->getCurrentTool();
|
|
if (gHoverView != NULL &&
|
|
gFloaterTools != NULL
|
|
&& tool != NULL
|
|
&& tool != gToolNull
|
|
&& tool != LLToolCompInspect::getInstance()
|
|
&& tool != LLToolDragAndDrop::getInstance()
|
|
&& !freeze_time)
|
|
{
|
|
// Suppress the toolbox view if our source tool was the pie tool,
|
|
// and we've overridden to something else.
|
|
bool suppress_toolbox =
|
|
(LLToolMgr::getInstance()->getBaseTool() == LLToolPie::getInstance()) &&
|
|
(LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance());
|
|
|
|
LLMouseHandler *captor = gFocusMgr.getMouseCapture();
|
|
// With the null, inspect, or drag and drop tool, don't muck
|
|
// with visibility.
|
|
|
|
if (gFloaterTools->isMinimized()
|
|
|| (tool != LLToolPie::getInstance() // not default tool
|
|
&& tool != LLToolCompGun::getInstance() // not coming out of mouselook
|
|
&& !suppress_toolbox // not override in third person
|
|
&& LLToolMgr::getInstance()->getCurrentToolset() != gFaceEditToolset // not special mode
|
|
&& LLToolMgr::getInstance()->getCurrentToolset() != gMouselookToolset
|
|
&& (!captor || dynamic_cast<LLView*>(captor) != NULL))) // not dragging
|
|
|
|
{
|
|
// Force floater tools to be visible (unless minimized)
|
|
if (!gFloaterTools->getVisible())
|
|
{
|
|
gFloaterTools->open(); /* Flawfinder: ignore */
|
|
}
|
|
// Update the location of the blue box tool popup
|
|
LLCoordGL select_center_screen;
|
|
MASK mask = gKeyboard->currentMask(TRUE);
|
|
gFloaterTools->updatePopup( select_center_screen, mask );
|
|
}
|
|
else
|
|
{
|
|
gFloaterTools->setVisible(FALSE);
|
|
}
|
|
// In the future we may wish to hide the tools menu unless you
|
|
// are building. JC
|
|
//gMenuBarView->setItemVisible("Tools", gFloaterTools->getVisible());
|
|
//gMenuBarView->arrange();
|
|
}
|
|
if (gToolBar)
|
|
{
|
|
gToolBar->refresh();
|
|
}
|
|
|
|
if (gChatBar)
|
|
{
|
|
gChatBar->refresh();
|
|
}
|
|
|
|
if (gOverlayBar)
|
|
{
|
|
gOverlayBar->refresh();
|
|
}
|
|
|
|
// Update rectangles for the various toolbars
|
|
if (gOverlayBar && gNotifyBoxView && gConsole && gToolBar && gHUDView)
|
|
{
|
|
LLRect bar_rect(-1, STATUS_BAR_HEIGHT, getWindowWidth()+1, -1);
|
|
|
|
LLRect notify_box_rect = gNotifyBoxView->getRect();
|
|
notify_box_rect.mBottom = bar_rect.mBottom;
|
|
gNotifyBoxView->reshape(notify_box_rect.getWidth(), notify_box_rect.getHeight());
|
|
gNotifyBoxView->setShape(notify_box_rect);
|
|
|
|
// make sure floaters snap to visible rect by adjusting floater view rect
|
|
LLRect floater_rect = gFloaterView->getRect();
|
|
if (floater_rect.mBottom != bar_rect.mBottom+1)
|
|
{
|
|
floater_rect.mBottom = bar_rect.mBottom+1;
|
|
// Don't bounce the floaters up and down.
|
|
gFloaterView->reshapeFloater(floater_rect.getWidth(), floater_rect.getHeight(),
|
|
TRUE, ADJUST_VERTICAL_NO);
|
|
gFloaterView->setShape(floater_rect);
|
|
}
|
|
|
|
// snap floaters to top of chat bar/button strip
|
|
LLView* chatbar_and_buttons = gOverlayBar->getChatbarAndButtons();
|
|
// find top of chatbar and state buttons, if either are visible
|
|
if (chatbar_and_buttons && chatbar_and_buttons->getLocalBoundingRect().notEmpty())
|
|
{
|
|
// convert top/left corner of chatbar/buttons container to gFloaterView-relative coordinates
|
|
S32 top, left;
|
|
chatbar_and_buttons->localPointToOtherView(
|
|
chatbar_and_buttons->getLocalBoundingRect().mLeft,
|
|
chatbar_and_buttons->getLocalBoundingRect().mTop,
|
|
&left,
|
|
&top,
|
|
gFloaterView);
|
|
gFloaterView->setSnapOffsetBottom(top);
|
|
}
|
|
else if (gToolBar->getVisible())
|
|
{
|
|
S32 top, left;
|
|
gToolBar->localPointToOtherView(
|
|
gToolBar->getLocalBoundingRect().mLeft,
|
|
gToolBar->getLocalBoundingRect().mTop,
|
|
&left,
|
|
&top,
|
|
gFloaterView);
|
|
gFloaterView->setSnapOffsetBottom(top);
|
|
}
|
|
else
|
|
{
|
|
gFloaterView->setSnapOffsetBottom(0);
|
|
}
|
|
|
|
// Always update console
|
|
LLRect console_rect = getChatConsoleRect();
|
|
console_rect.mBottom = gHUDView->getRect().mBottom + getChatConsoleBottomPad();
|
|
gConsole->reshape(console_rect.getWidth(), console_rect.getHeight());
|
|
gConsole->setRect(console_rect);
|
|
}
|
|
|
|
static const LLCachedControl<bool> chat_bar_steals_focus("ChatBarStealsFocus",true);
|
|
if (chat_bar_steals_focus
|
|
&& gChatBar
|
|
&& gFocusMgr.getKeyboardFocus() == NULL
|
|
&& gChatBar->isInVisibleChain())
|
|
{
|
|
gChatBar->startChat(NULL);
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::updateMouseDelta()
|
|
{
|
|
S32 dx = lltrunc((F32) (mCurrentMousePoint.mX - mLastMousePoint.mX) * LLUI::getScaleFactor().mV[VX]);
|
|
S32 dy = lltrunc((F32) (mCurrentMousePoint.mY - mLastMousePoint.mY) * LLUI::getScaleFactor().mV[VY]);
|
|
|
|
//RN: fix for asynchronous notification of mouse leaving window not working
|
|
LLCoordWindow mouse_pos;
|
|
mWindow->getCursorPosition(&mouse_pos);
|
|
if (mouse_pos.mX < 0 ||
|
|
mouse_pos.mY < 0 ||
|
|
mouse_pos.mX > mWindowRectRaw.getWidth() ||
|
|
mouse_pos.mY > mWindowRectRaw.getHeight())
|
|
{
|
|
mMouseInWindow = FALSE;
|
|
}
|
|
else
|
|
{
|
|
mMouseInWindow = TRUE;
|
|
}
|
|
|
|
LLVector2 mouse_vel;
|
|
|
|
static const LLCachedControl<bool> mouse_smooth("MouseSmooth",false);
|
|
if (mouse_smooth)
|
|
{
|
|
static F32 fdx = 0.f;
|
|
static F32 fdy = 0.f;
|
|
|
|
F32 amount = 16.f;
|
|
fdx = fdx + ((F32) dx - fdx) * llmin(gFrameIntervalSeconds*amount,1.f);
|
|
fdy = fdy + ((F32) dy - fdy) * llmin(gFrameIntervalSeconds*amount,1.f);
|
|
|
|
mCurrentMouseDelta.set(llround(fdx), llround(fdy));
|
|
mouse_vel.setVec(fdx,fdy);
|
|
}
|
|
else
|
|
{
|
|
mCurrentMouseDelta.set(dx, dy);
|
|
mouse_vel.setVec((F32) dx, (F32) dy);
|
|
}
|
|
|
|
mMouseVelocityStat.addValue(mouse_vel.magVec());
|
|
}
|
|
|
|
void LLViewerWindow::updateKeyboardFocus()
|
|
{
|
|
if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
|
|
{
|
|
gFocusMgr.setKeyboardFocus(NULL);
|
|
}
|
|
|
|
// clean up current focus
|
|
LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus());
|
|
if (cur_focus)
|
|
{
|
|
if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain())
|
|
{
|
|
gFocusMgr.releaseFocusIfNeeded(cur_focus);
|
|
|
|
LLUICtrl* parent = cur_focus->getParentUICtrl();
|
|
const LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot();
|
|
while(parent)
|
|
{
|
|
if (parent->isCtrl()
|
|
&& (parent->hasTabStop() || parent == focus_root)
|
|
&& !parent->getIsChrome()
|
|
&& parent->isInVisibleChain()
|
|
&& parent->isInEnabledChain())
|
|
{
|
|
if (!parent->focusFirstItem())
|
|
{
|
|
parent->setFocus(TRUE);
|
|
}
|
|
break;
|
|
}
|
|
parent = parent->getParentUICtrl();
|
|
}
|
|
}
|
|
else if (cur_focus->isFocusRoot())
|
|
{
|
|
// focus roots keep trying to delegate focus to their first valid descendant
|
|
// this assumes that focus roots are not valid focus holders on their own
|
|
cur_focus->focusFirstItem();
|
|
}
|
|
}
|
|
// last ditch force of edit menu to selection manager
|
|
if (LLEditMenuHandler::gEditMenuHandler == NULL && LLSelectMgr::getInstance()->getSelection()->getObjectCount())
|
|
{
|
|
LLEditMenuHandler::gEditMenuHandler = LLSelectMgr::getInstance();
|
|
}
|
|
|
|
if (gFloaterView->getCycleMode())
|
|
{
|
|
// sync all floaters with their focus state
|
|
gFloaterView->highlightFocusedFloater();
|
|
gSnapshotFloaterView->highlightFocusedFloater();
|
|
MASK mask = gKeyboard->currentMask(TRUE);
|
|
if ((mask & MASK_CONTROL) == 0)
|
|
{
|
|
// control key no longer held down, finish cycle mode
|
|
gFloaterView->setCycleMode(FALSE);
|
|
|
|
gFloaterView->syncFloaterTabOrder();
|
|
}
|
|
else
|
|
{
|
|
// user holding down CTRL, don't update tab order of floaters
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// update focused floater
|
|
gFloaterView->highlightFocusedFloater();
|
|
gSnapshotFloaterView->highlightFocusedFloater();
|
|
// make sure floater visible order is in sync with tab order
|
|
gFloaterView->syncFloaterTabOrder();
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::saveLastMouse(const LLCoordGL &point)
|
|
{
|
|
// Store last mouse location.
|
|
// If mouse leaves window, pretend last point was on edge of window
|
|
if (point.mX < 0)
|
|
{
|
|
mCurrentMousePoint.mX = 0;
|
|
}
|
|
else if (point.mX > getWindowWidthScaled())
|
|
{
|
|
mCurrentMousePoint.mX = getWindowWidthScaled();
|
|
}
|
|
else
|
|
{
|
|
mCurrentMousePoint.mX = point.mX;
|
|
}
|
|
|
|
if (point.mY < 0)
|
|
{
|
|
mCurrentMousePoint.mY = 0;
|
|
}
|
|
else if (point.mY > getWindowHeightScaled() )
|
|
{
|
|
mCurrentMousePoint.mY = getWindowHeightScaled();
|
|
}
|
|
else
|
|
{
|
|
mCurrentMousePoint.mY = point.mY;
|
|
}
|
|
}
|
|
|
|
|
|
// Draws the selection outlines for the currently selected objects
|
|
// Must be called after displayObjects is called, which sets the mGLName parameter
|
|
// NOTE: This function gets called 3 times:
|
|
// render_ui_3d: FALSE, FALSE, TRUE
|
|
// renderObjectsForSelect: TRUE, pick_parcel_wall, FALSE
|
|
// render_hud_elements: FALSE, FALSE, FALSE
|
|
void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud )
|
|
{
|
|
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
|
|
|
|
if (!for_hud && !for_gl_pick)
|
|
{
|
|
// Call this once and only once
|
|
LLSelectMgr::getInstance()->updateSilhouettes();
|
|
}
|
|
|
|
// Draw fence around land selections
|
|
if (for_gl_pick)
|
|
{
|
|
if (pick_parcel_walls)
|
|
{
|
|
LLViewerParcelMgr::getInstance()->renderParcelCollision();
|
|
}
|
|
}
|
|
else if (( for_hud && selection->getSelectType() == SELECT_TYPE_HUD) ||
|
|
(!for_hud && selection->getSelectType() != SELECT_TYPE_HUD))
|
|
{
|
|
LLSelectMgr::getInstance()->renderSilhouettes(for_hud);
|
|
|
|
stop_glerror();
|
|
|
|
// setup HUD render
|
|
if (selection->getSelectType() == SELECT_TYPE_HUD && LLSelectMgr::getInstance()->getSelection()->getObjectCount())
|
|
{
|
|
LLBBox hud_bbox = gAgentAvatarp->getHUDBBox();
|
|
|
|
// set up transform to encompass bounding box of HUD
|
|
gGL.matrixMode(LLRender::MM_PROJECTION);
|
|
gGL.pushMatrix();
|
|
gGL.loadIdentity();
|
|
F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f);
|
|
gGL.ortho(-0.5f * LLViewerCamera::getInstance()->getAspect(), 0.5f * LLViewerCamera::getInstance()->getAspect(), -0.5f, 0.5f, 0.f, depth);
|
|
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
gGL.pushMatrix();
|
|
gGL.loadIdentity();
|
|
gGL.loadMatrix(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
|
|
gGL.translatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f);
|
|
}
|
|
|
|
// Render light for editing
|
|
if (LLSelectMgr::sRenderLightRadius && LLToolMgr::getInstance()->inEdit())
|
|
{
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
LLGLEnable gls_blend(GL_BLEND);
|
|
LLGLEnable gls_cull(GL_CULL_FACE);
|
|
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
gGL.pushMatrix();
|
|
if (selection->getSelectType() == SELECT_TYPE_HUD)
|
|
{
|
|
F32 zoom = gAgentCamera.mHUDCurZoom;
|
|
gGL.scalef(zoom, zoom, zoom);
|
|
}
|
|
|
|
struct f : public LLSelectedObjectFunctor
|
|
{
|
|
virtual bool apply(LLViewerObject* object)
|
|
{
|
|
LLDrawable* drawable = object->mDrawable;
|
|
if (drawable && drawable->isLight())
|
|
{
|
|
LLVOVolume* vovolume = drawable->getVOVolume();
|
|
gGL.pushMatrix();
|
|
|
|
LLVector3 center = drawable->getPositionAgent();
|
|
gGL.translatef(center[0], center[1], center[2]);
|
|
F32 scale = vovolume->getLightRadius();
|
|
gGL.scalef(scale, scale, scale);
|
|
|
|
LLColor4 color(vovolume->getLightColor(), .5f);
|
|
gGL.color4fv(color.mV);
|
|
|
|
//F32 pixel_area = 100000.f;
|
|
// Render Outside
|
|
gSphere.render();
|
|
|
|
// Render Inside
|
|
glCullFace(GL_FRONT);
|
|
gSphere.render();
|
|
glCullFace(GL_BACK);
|
|
|
|
gGL.popMatrix();
|
|
}
|
|
return true;
|
|
}
|
|
} func;
|
|
LLSelectMgr::getInstance()->getSelection()->applyToObjects(&func);
|
|
|
|
gGL.popMatrix();
|
|
}
|
|
|
|
// NOTE: The average position for the axis arrows of the selected objects should
|
|
// not be recalculated at this time. If they are, then group rotations will break.
|
|
|
|
// Draw arrows at average center of all selected objects
|
|
LLTool* tool = LLToolMgr::getInstance()->getCurrentTool();
|
|
if (tool)
|
|
{
|
|
if(tool->isAlwaysRendered())
|
|
{
|
|
tool->render();
|
|
}
|
|
else
|
|
{
|
|
if( !LLSelectMgr::getInstance()->getSelection()->isEmpty() )
|
|
{
|
|
BOOL moveable_object_selected = FALSE;
|
|
BOOL all_selected_objects_move = TRUE;
|
|
BOOL all_selected_objects_modify = TRUE;
|
|
BOOL selecting_linked_set = !gSavedSettings.getBOOL("EditLinkedParts");
|
|
|
|
for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin();
|
|
iter != LLSelectMgr::getInstance()->getSelection()->end(); iter++)
|
|
{
|
|
LLSelectNode* nodep = *iter;
|
|
LLViewerObject* object = nodep->getObject();
|
|
LLViewerObject *root_object = (object == NULL) ? NULL : object->getRootEdit();
|
|
BOOL this_object_movable = FALSE;
|
|
if (object->permMove() && !object->isPermanentEnforced() &&
|
|
((root_object == NULL) || !root_object->isPermanentEnforced()) &&
|
|
(object->permModify() || selecting_linked_set))
|
|
{
|
|
moveable_object_selected = TRUE;
|
|
this_object_movable = TRUE;
|
|
|
|
// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) | Modified: RLVa-0.2.0g
|
|
if ( (rlv_handler_t::isEnabled()) &&
|
|
((gRlvHandler.hasBehaviour(RLV_BHVR_UNSIT)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SITTP))) )
|
|
{
|
|
LLVOAvatar* pAvatar = gAgentAvatarp;
|
|
if ( (pAvatar) && (pAvatar->isSitting()) && (pAvatar->getRoot() == object->getRootEdit()) )
|
|
moveable_object_selected = this_object_movable = FALSE;
|
|
}
|
|
// [/RLVa:KB]
|
|
}
|
|
all_selected_objects_move = all_selected_objects_move && this_object_movable;
|
|
all_selected_objects_modify = all_selected_objects_modify && object->permModify();
|
|
}
|
|
|
|
BOOL draw_handles = TRUE;
|
|
|
|
if (tool == LLToolCompTranslate::getInstance() && (!moveable_object_selected || !all_selected_objects_move))
|
|
{
|
|
draw_handles = FALSE;
|
|
}
|
|
|
|
if (tool == LLToolCompRotate::getInstance() && (!moveable_object_selected || !all_selected_objects_move))
|
|
{
|
|
draw_handles = FALSE;
|
|
}
|
|
|
|
if ( !all_selected_objects_modify && tool == LLToolCompScale::getInstance() )
|
|
{
|
|
draw_handles = FALSE;
|
|
}
|
|
|
|
if( draw_handles )
|
|
{
|
|
tool->render();
|
|
}
|
|
}
|
|
}
|
|
if (selection->getSelectType() == SELECT_TYPE_HUD && selection->getObjectCount())
|
|
{
|
|
gGL.matrixMode(LLRender::MM_PROJECTION);
|
|
gGL.popMatrix();
|
|
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
gGL.popMatrix();
|
|
stop_glerror();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return a point near the clicked object representative of the place the object was clicked.
|
|
LLVector3d LLViewerWindow::clickPointInWorldGlobal(S32 x, S32 y_from_bot, LLViewerObject* clicked_object) const
|
|
{
|
|
// create a normalized vector pointing from the camera center into the
|
|
// world at the location of the mouse click
|
|
LLVector3 mouse_direction_global = mouseDirectionGlobal( x, y_from_bot );
|
|
|
|
LLVector3d relative_object = clicked_object->getPositionGlobal() - gAgentCamera.getCameraPositionGlobal();
|
|
|
|
// make mouse vector as long as object vector, so it touchs a point near
|
|
// where the user clicked on the object
|
|
mouse_direction_global *= (F32) relative_object.magVec();
|
|
|
|
LLVector3d new_pos;
|
|
new_pos.setVec(mouse_direction_global);
|
|
// transform mouse vector back to world coords
|
|
new_pos += gAgentCamera.getCameraPositionGlobal();
|
|
|
|
return new_pos;
|
|
}
|
|
|
|
|
|
BOOL LLViewerWindow::clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewerObject *objectp, LLVector3d &point_global) const
|
|
{
|
|
BOOL intersect = FALSE;
|
|
|
|
// U8 shape = objectp->mPrimitiveCode & LL_PCODE_BASE_MASK;
|
|
if (!intersect)
|
|
{
|
|
point_global = clickPointInWorldGlobal(x, y, objectp);
|
|
llinfos << "approx intersection at " << (objectp->getPositionGlobal() - point_global) << llendl;
|
|
}
|
|
else
|
|
{
|
|
llinfos << "good intersection at " << (objectp->getPositionGlobal() - point_global) << llendl;
|
|
}
|
|
|
|
return intersect;
|
|
}
|
|
|
|
void LLViewerWindow::pickAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(const LLPickInfo& info), BOOL pick_transparent, BOOL get_surface_info)
|
|
{
|
|
if (gNoRender)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// push back pick info object
|
|
BOOL in_build_mode = gFloaterTools && gFloaterTools->getVisible();
|
|
if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha)
|
|
{
|
|
// build mode allows interaction with all transparent objects
|
|
// "Show Debug Alpha" means no object actually transparent
|
|
pick_transparent = TRUE;
|
|
}
|
|
|
|
LLPickInfo pick_info(LLCoordGL(x, y_from_bot), mask, pick_transparent, get_surface_info, callback);
|
|
schedulePick(pick_info);
|
|
}
|
|
|
|
void LLViewerWindow::schedulePick(LLPickInfo& pick_info)
|
|
{
|
|
if (mPicks.size() >= 1024 || mWindow->getMinimized())
|
|
{ //something went wrong, picks are being scheduled but not processed
|
|
|
|
if (pick_info.mPickCallback)
|
|
{
|
|
pick_info.mPickCallback(pick_info);
|
|
}
|
|
|
|
return;
|
|
}
|
|
mPicks.push_back(pick_info);
|
|
|
|
// delay further event processing until we receive results of pick
|
|
mWindow->delayInputProcessing();
|
|
}
|
|
|
|
|
|
void LLViewerWindow::performPick()
|
|
{
|
|
if (gNoRender)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!mPicks.empty())
|
|
{
|
|
std::vector<LLPickInfo>::iterator pick_it;
|
|
for (pick_it = mPicks.begin(); pick_it != mPicks.end(); ++pick_it)
|
|
{
|
|
pick_it->fetchResults();
|
|
}
|
|
|
|
mLastPick = mPicks.back();
|
|
mPicks.clear();
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::returnEmptyPicks()
|
|
{
|
|
std::vector<LLPickInfo>::iterator pick_it;
|
|
for (pick_it = mPicks.begin(); pick_it != mPicks.end(); ++pick_it)
|
|
{
|
|
mLastPick = *pick_it;
|
|
// just trigger callback with empty results
|
|
if (pick_it->mPickCallback)
|
|
{
|
|
pick_it->mPickCallback(*pick_it);
|
|
}
|
|
}
|
|
mPicks.clear();
|
|
}
|
|
|
|
// Performs the GL object/land pick.
|
|
LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_transparent)
|
|
{
|
|
if (gNoRender)
|
|
{
|
|
return LLPickInfo();
|
|
}
|
|
|
|
// push back pick info object
|
|
BOOL in_build_mode = gFloaterTools && gFloaterTools->getVisible();
|
|
if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha)
|
|
{
|
|
// build mode allows interaction with all transparent objects
|
|
// "Show Debug Alpha" means no object actually transparent
|
|
pick_transparent = TRUE;
|
|
}
|
|
|
|
// shortcut queueing in mPicks and just update mLastPick in place
|
|
MASK key_mask = gKeyboard->currentMask(TRUE);
|
|
mLastPick = LLPickInfo(LLCoordGL(x, y_from_bot), key_mask, pick_transparent, TRUE, NULL);
|
|
mLastPick.fetchResults();
|
|
|
|
return mLastPick;
|
|
}
|
|
|
|
LLHUDIcon* LLViewerWindow::cursorIntersectIcon(S32 mouse_x, S32 mouse_y, F32 depth,
|
|
LLVector4a* intersection)
|
|
{
|
|
S32 x = mouse_x;
|
|
S32 y = mouse_y;
|
|
|
|
if ((mouse_x == -1) && (mouse_y == -1)) // use current mouse position
|
|
{
|
|
x = getCurrentMouseX();
|
|
y = getCurrentMouseY();
|
|
}
|
|
|
|
// world coordinates of mouse
|
|
LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
|
|
LLVector3 mouse_point_global = LLViewerCamera::getInstance()->getOrigin();
|
|
LLVector3 mouse_world_start = mouse_point_global;
|
|
LLVector3 mouse_world_end = mouse_point_global + mouse_direction_global * depth;
|
|
|
|
LLVector4a start, end;
|
|
start.load3(mouse_world_start.mV);
|
|
end.load3(mouse_world_end.mV);
|
|
|
|
return LLHUDIcon::lineSegmentIntersectAll(start, end, intersection);
|
|
}
|
|
|
|
LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 depth,
|
|
LLViewerObject *this_object,
|
|
S32 this_face,
|
|
BOOL pick_transparent,
|
|
S32* face_hit,
|
|
LLVector4a *intersection,
|
|
LLVector2 *uv,
|
|
LLVector4a *normal,
|
|
LLVector4a *tangent,
|
|
LLVector4a* start,
|
|
LLVector4a* end)
|
|
{
|
|
S32 x = mouse_x;
|
|
S32 y = mouse_y;
|
|
|
|
if ((mouse_x == -1) && (mouse_y == -1)) // use current mouse position
|
|
{
|
|
x = getCurrentMouseX();
|
|
y = getCurrentMouseY();
|
|
}
|
|
|
|
// HUD coordinates of mouse
|
|
LLVector3 mouse_point_hud = mousePointHUD(x, y);
|
|
LLVector3 mouse_hud_start = mouse_point_hud - LLVector3(depth, 0, 0);
|
|
LLVector3 mouse_hud_end = mouse_point_hud + LLVector3(depth, 0, 0);
|
|
|
|
// world coordinates of mouse
|
|
LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
|
|
LLVector3 mouse_point_global = LLViewerCamera::getInstance()->getOrigin();
|
|
|
|
//get near clip plane
|
|
LLVector3 n = LLViewerCamera::getInstance()->getAtAxis();
|
|
LLVector3 p = mouse_point_global + n * LLViewerCamera::getInstance()->getNear();
|
|
|
|
//project mouse point onto plane
|
|
LLVector3 pos;
|
|
line_plane(mouse_point_global, mouse_direction_global, p, n, pos);
|
|
mouse_point_global = pos;
|
|
|
|
LLVector3 mouse_world_start = mouse_point_global;
|
|
LLVector3 mouse_world_end = mouse_point_global + mouse_direction_global * depth;
|
|
|
|
if (!LLViewerJoystick::getInstance()->getOverrideCamera())
|
|
{ //always set raycast intersection to mouse_world_end unless
|
|
//flycam is on (for DoF effect)
|
|
gDebugRaycastIntersection.load3(mouse_world_end.mV);
|
|
}
|
|
|
|
LLVector4a mw_start;
|
|
mw_start.load3(mouse_world_start.mV);
|
|
LLVector4a mw_end;
|
|
mw_end.load3(mouse_world_end.mV);
|
|
|
|
LLVector4a mh_start;
|
|
mh_start.load3(mouse_hud_start.mV);
|
|
LLVector4a mh_end;
|
|
mh_end.load3(mouse_hud_end.mV);
|
|
|
|
if (start)
|
|
{
|
|
*start = mw_start;
|
|
}
|
|
|
|
if (end)
|
|
{
|
|
*end = mw_end;
|
|
}
|
|
|
|
LLViewerObject* found = NULL;
|
|
|
|
if (this_object) // check only this object
|
|
{
|
|
if (this_object->isHUDAttachment()) // is a HUD object?
|
|
{
|
|
if (this_object->lineSegmentIntersect(mh_start, mh_end, this_face, pick_transparent,
|
|
face_hit, intersection, uv, normal, tangent))
|
|
{
|
|
found = this_object;
|
|
}
|
|
}
|
|
else // is a world object
|
|
{
|
|
if (this_object->lineSegmentIntersect(mw_start, mw_end, this_face, pick_transparent,
|
|
face_hit, intersection, uv, normal, tangent))
|
|
{
|
|
found = this_object;
|
|
}
|
|
}
|
|
}
|
|
else // check ALL objects
|
|
{
|
|
found = gPipeline.lineSegmentIntersectInHUD(mh_start, mh_end, pick_transparent,
|
|
face_hit, intersection, uv, normal, tangent);
|
|
|
|
// [RLVa:KB] - Checked: 2009-12-28 (RLVa-1.1.0k) | Modified: RLVa-1.1.0k
|
|
if ( (rlv_handler_t::isEnabled()) && (LLToolCamera::getInstance()->hasMouseCapture()) && (gKeyboard->currentMask(TRUE) & MASK_ALT) )
|
|
{
|
|
found = NULL;
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
if (!found) // if not found in HUD, look in world:
|
|
{
|
|
found = gPipeline.lineSegmentIntersectInWorld(mw_start, mw_end, pick_transparent,
|
|
face_hit, intersection, uv, normal, tangent);
|
|
|
|
if (found && !pick_transparent)
|
|
{
|
|
gDebugRaycastIntersection = *intersection;
|
|
}
|
|
|
|
// [RLVa:KB] - Checked: 2010-01-02 (RLVa-1.1.0l) | Added: RLVa-1.1.0l
|
|
#ifdef RLV_EXTENSION_CMD_INTERACT
|
|
if ( (rlv_handler_t::isEnabled()) && (found) && (gRlvHandler.hasBehaviour(RLV_BHVR_INTERACT)) )
|
|
{
|
|
// Allow picking if:
|
|
// - the drag-and-drop tool is active (allows inventory offers)
|
|
// - the camera tool is active
|
|
// - the pie tool is active *and* we picked our own avie (allows "mouse steering" and the self pie menu)
|
|
LLTool* pCurTool = LLToolMgr::getInstance()->getCurrentTool();
|
|
if ( (LLToolDragAndDrop::getInstance() != pCurTool) &&
|
|
(!LLToolCamera::getInstance()->hasMouseCapture()) &&
|
|
((LLToolPie::getInstance() != pCurTool) || (gAgent.getID() != found->getID())) )
|
|
{
|
|
found = NULL;
|
|
}
|
|
}
|
|
#endif // RLV_EXTENSION_CMD_INTERACT
|
|
// [/RLVa:KB]
|
|
if (found && !pick_transparent)
|
|
{
|
|
gDebugRaycastIntersection = *intersection;
|
|
}
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
// Returns unit vector relative to camera
|
|
// indicating direction of point on screen x,y
|
|
LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const
|
|
{
|
|
// find vertical field of view
|
|
F32 fov = LLViewerCamera::getInstance()->getView();
|
|
|
|
// find world view center in scaled ui coordinates
|
|
F32 center_x = getWorldViewRectScaled().getCenterX();
|
|
F32 center_y = getWorldViewRectScaled().getCenterY();
|
|
|
|
// calculate pixel distance to screen
|
|
F32 distance = ((F32)getWorldViewHeightScaled() * 0.5f) / (tan(fov / 2.f));
|
|
|
|
// calculate click point relative to middle of screen
|
|
F32 click_x = x - center_x;
|
|
F32 click_y = y - center_y;
|
|
|
|
// compute mouse vector
|
|
LLVector3 mouse_vector = distance * LLViewerCamera::getInstance()->getAtAxis()
|
|
- click_x * LLViewerCamera::getInstance()->getLeftAxis()
|
|
+ click_y * LLViewerCamera::getInstance()->getUpAxis();
|
|
|
|
mouse_vector.normVec();
|
|
|
|
return mouse_vector;
|
|
}
|
|
|
|
LLVector3 LLViewerWindow::mousePointHUD(const S32 x, const S32 y) const
|
|
{
|
|
// find screen resolution
|
|
S32 height = getWorldViewHeightScaled();
|
|
|
|
// find world view center
|
|
F32 center_x = getWorldViewRectScaled().getCenterX();
|
|
F32 center_y = getWorldViewRectScaled().getCenterY();
|
|
|
|
// remap with uniform scale (1/height) so that top is -0.5, bottom is +0.5
|
|
F32 hud_x = -((F32)x - center_x) / height;
|
|
F32 hud_y = ((F32)y - center_y) / height;
|
|
|
|
return LLVector3(0.f, hud_x/gAgentCamera.mHUDCurZoom, hud_y/gAgentCamera.mHUDCurZoom);
|
|
}
|
|
|
|
// Returns unit vector relative to camera in camera space
|
|
// indicating direction of point on screen x,y
|
|
LLVector3 LLViewerWindow::mouseDirectionCamera(const S32 x, const S32 y) const
|
|
{
|
|
// find vertical field of view
|
|
F32 fov_height = LLViewerCamera::getInstance()->getView();
|
|
F32 fov_width = fov_height * LLViewerCamera::getInstance()->getAspect();
|
|
|
|
// find screen resolution
|
|
S32 height = getWorldViewHeightScaled();
|
|
S32 width = getWorldViewWidthScaled();
|
|
|
|
// find world view center
|
|
F32 center_x = getWorldViewRectScaled().getCenterX();
|
|
F32 center_y = getWorldViewRectScaled().getCenterY();
|
|
|
|
// calculate click point relative to middle of screen
|
|
F32 click_x = (((F32)x - center_x) / (F32)width) * fov_width * -1.f;
|
|
F32 click_y = (((F32)y - center_y) / (F32)height) * fov_height;
|
|
|
|
// compute mouse vector
|
|
LLVector3 mouse_vector = LLVector3(0.f, 0.f, -1.f);
|
|
LLQuaternion mouse_rotate;
|
|
mouse_rotate.setQuat(click_y, click_x, 0.f);
|
|
|
|
mouse_vector = mouse_vector * mouse_rotate;
|
|
// project to z = -1 plane;
|
|
mouse_vector = mouse_vector * (-1.f / mouse_vector.mV[VZ]);
|
|
|
|
return mouse_vector;
|
|
}
|
|
|
|
|
|
|
|
BOOL LLViewerWindow::mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, const S32 y,
|
|
const LLVector3d &plane_point_global,
|
|
const LLVector3 &plane_normal_global)
|
|
{
|
|
LLVector3d mouse_direction_global_d;
|
|
|
|
mouse_direction_global_d.setVec(mouseDirectionGlobal(x,y));
|
|
LLVector3d plane_normal_global_d;
|
|
plane_normal_global_d.setVec(plane_normal_global);
|
|
F64 plane_mouse_dot = (plane_normal_global_d * mouse_direction_global_d);
|
|
LLVector3d plane_origin_camera_rel = plane_point_global - gAgentCamera.getCameraPositionGlobal();
|
|
F64 mouse_look_at_scale = (plane_normal_global_d * plane_origin_camera_rel)
|
|
/ plane_mouse_dot;
|
|
if (llabs(plane_mouse_dot) < 0.00001)
|
|
{
|
|
// if mouse is parallel to plane, return closest point on line through plane origin
|
|
// that is parallel to camera plane by scaling mouse direction vector
|
|
// by distance to plane origin, modulated by deviation of mouse direction from plane origin
|
|
LLVector3d plane_origin_dir = plane_origin_camera_rel;
|
|
plane_origin_dir.normVec();
|
|
|
|
mouse_look_at_scale = plane_origin_camera_rel.magVec() / (plane_origin_dir * mouse_direction_global_d);
|
|
}
|
|
|
|
point = gAgentCamera.getCameraPositionGlobal() + mouse_look_at_scale * mouse_direction_global_d;
|
|
|
|
return mouse_look_at_scale > 0.0;
|
|
}
|
|
|
|
|
|
// Returns global position
|
|
BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_position_global)
|
|
{
|
|
LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
|
|
F32 mouse_dir_scale;
|
|
BOOL hit_land = FALSE;
|
|
LLViewerRegion *regionp;
|
|
F32 land_z;
|
|
const F32 FIRST_PASS_STEP = 1.0f; // meters
|
|
const F32 SECOND_PASS_STEP = 0.1f; // meters
|
|
LLVector3d camera_pos_global;
|
|
|
|
camera_pos_global = gAgentCamera.getCameraPositionGlobal();
|
|
LLVector3d probe_point_global;
|
|
LLVector3 probe_point_region;
|
|
|
|
// walk forwards to find the point
|
|
for (mouse_dir_scale = FIRST_PASS_STEP; mouse_dir_scale < gAgentCamera.mDrawDistance * 4; mouse_dir_scale += FIRST_PASS_STEP)
|
|
{
|
|
LLVector3d mouse_direction_global_d;
|
|
mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale);
|
|
probe_point_global = camera_pos_global + mouse_direction_global_d;
|
|
|
|
regionp = LLWorld::getInstance()->resolveRegionGlobal(probe_point_region, probe_point_global);
|
|
|
|
if (!regionp)
|
|
{
|
|
// ...we're outside the world somehow
|
|
continue;
|
|
}
|
|
|
|
S32 i = (S32) (probe_point_region.mV[VX]/regionp->getLand().getMetersPerGrid());
|
|
S32 j = (S32) (probe_point_region.mV[VY]/regionp->getLand().getMetersPerGrid());
|
|
S32 grids_per_edge = (S32) regionp->getLand().mGridsPerEdge;
|
|
if ((i >= grids_per_edge) || (j >= grids_per_edge))
|
|
{
|
|
//llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl;
|
|
continue;
|
|
}
|
|
|
|
land_z = regionp->getLand().resolveHeightRegion(probe_point_region);
|
|
|
|
//llinfos << "mousePointOnLand initial z " << land_z << llendl;
|
|
|
|
if (probe_point_region.mV[VZ] < land_z)
|
|
{
|
|
// ...just went under land
|
|
|
|
// cout << "under land at " << probe_point << " scale " << mouse_vec_scale << endl;
|
|
|
|
hit_land = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (hit_land)
|
|
{
|
|
// Don't go more than one step beyond where we stopped above.
|
|
// This can't just be "mouse_vec_scale" because floating point error
|
|
// will stop the loop before the last increment.... X - 1.0 + 0.1 + 0.1 + ... + 0.1 != X
|
|
F32 stop_mouse_dir_scale = mouse_dir_scale + FIRST_PASS_STEP;
|
|
|
|
// take a step backwards, then walk forwards again to refine position
|
|
for ( mouse_dir_scale -= FIRST_PASS_STEP; mouse_dir_scale <= stop_mouse_dir_scale; mouse_dir_scale += SECOND_PASS_STEP)
|
|
{
|
|
LLVector3d mouse_direction_global_d;
|
|
mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale);
|
|
probe_point_global = camera_pos_global + mouse_direction_global_d;
|
|
|
|
regionp = LLWorld::getInstance()->resolveRegionGlobal(probe_point_region, probe_point_global);
|
|
|
|
if (!regionp)
|
|
{
|
|
// ...we're outside the world somehow
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
i = (S32) (local_probe_point.mV[VX]/regionp->getLand().getMetersPerGrid());
|
|
j = (S32) (local_probe_point.mV[VY]/regionp->getLand().getMetersPerGrid());
|
|
if ((i >= regionp->getLand().mGridsPerEdge) || (j >= regionp->getLand().mGridsPerEdge))
|
|
{
|
|
// llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl;
|
|
continue;
|
|
}
|
|
land_z = regionp->getLand().mSurfaceZ[ i + j * (regionp->getLand().mGridsPerEdge) ];
|
|
*/
|
|
|
|
land_z = regionp->getLand().resolveHeightRegion(probe_point_region);
|
|
|
|
//llinfos << "mousePointOnLand refine z " << land_z << llendl;
|
|
|
|
if (probe_point_region.mV[VZ] < land_z)
|
|
{
|
|
// ...just went under land again
|
|
|
|
*land_position_global = probe_point_global;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Saves an image to the harddrive as "SnapshotX" where X >= 1.
|
|
void LLViewerWindow::saveImageNumbered(LLPointer<LLImageFormatted> image, int index)
|
|
{
|
|
if (!image)
|
|
{
|
|
LLFloaterSnapshot::saveLocalDone(false, index);
|
|
return;
|
|
}
|
|
|
|
ESaveFilter pick_type;
|
|
std::string extension("." + image->getExtension());
|
|
if (extension == ".j2c")
|
|
pick_type = FFSAVE_J2C;
|
|
else if (extension == ".bmp")
|
|
pick_type = FFSAVE_BMP;
|
|
else if (extension == ".jpg")
|
|
pick_type = FFSAVE_JPEG;
|
|
else if (extension == ".png")
|
|
pick_type = FFSAVE_PNG;
|
|
else if (extension == ".tga")
|
|
pick_type = FFSAVE_TGA;
|
|
else
|
|
pick_type = FFSAVE_ALL; // ???
|
|
|
|
// Get a base file location if needed.
|
|
if (!isSnapshotLocSet())
|
|
{
|
|
std::string proposed_name( sSnapshotBaseName );
|
|
|
|
// AIFilePicker will append an appropriate extension to the proposed name, based on the ESaveFilter constant passed in.
|
|
|
|
// pick a directory in which to save
|
|
AIFilePicker* filepicker = AIFilePicker::create(); // Deleted in LLViewerWindow::saveImageNumbered_continued1
|
|
filepicker->open(proposed_name, pick_type, "", "snapshot");
|
|
filepicker->run(boost::bind(&LLViewerWindow::saveImageNumbered_continued1, this, image, extension, filepicker, index));
|
|
return;
|
|
}
|
|
|
|
// LLViewerWindow::sSnapshotBaseName and LLViewerWindow::sSnapshotDir already known. Go straight to saveImageNumbered_continued2.
|
|
saveImageNumbered_continued2(image, extension, index);
|
|
}
|
|
|
|
void LLViewerWindow::saveImageNumbered_continued1(LLPointer<LLImageFormatted> image, std::string const& extension, AIFilePicker* filepicker, int index)
|
|
{
|
|
if (filepicker->hasFilename())
|
|
{
|
|
// Copy the directory + file name
|
|
std::string filepath = filepicker->getFilename();
|
|
|
|
LLViewerWindow::sSnapshotBaseName = gDirUtilp->getBaseFileName(filepath, true);
|
|
LLViewerWindow::sSnapshotDir = gDirUtilp->getDirName(filepath);
|
|
|
|
saveImageNumbered_continued2(image, extension, index);
|
|
}
|
|
else
|
|
{
|
|
LLFloaterSnapshot::saveLocalDone(false, index);
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::saveImageNumbered_continued2(LLPointer<LLImageFormatted> image, std::string const& extension, int index)
|
|
{
|
|
// Look for an unused file name
|
|
std::string filepath;
|
|
S32 i = 1;
|
|
S32 err = 0;
|
|
|
|
do
|
|
{
|
|
filepath = sSnapshotDir;
|
|
filepath += gDirUtilp->getDirDelimiter();
|
|
filepath += sSnapshotBaseName;
|
|
filepath += llformat("_%.3d",i);
|
|
filepath += extension;
|
|
|
|
llstat stat_info;
|
|
err = LLFile::stat( filepath, &stat_info );
|
|
i++;
|
|
}
|
|
while( -1 != err ); // search until the file is not found (i.e., stat() gives an error).
|
|
|
|
if (image->save(filepath))
|
|
{
|
|
playSnapshotAnimAndSound();
|
|
LLFloaterSnapshot::saveLocalDone(true, index);
|
|
}
|
|
else
|
|
{
|
|
LLFloaterSnapshot::saveLocalDone(false, index);
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::resetSnapshotLoc()
|
|
{
|
|
sSnapshotDir.clear();
|
|
}
|
|
|
|
static S32 BORDERHEIGHT = 0;
|
|
static S32 BORDERWIDTH = 0;
|
|
|
|
// static
|
|
void LLViewerWindow::movieSize(S32 new_width, S32 new_height)
|
|
{
|
|
LLCoordScreen size;
|
|
gViewerWindow->getWindow()->getSize(&size);
|
|
if ( (size.mX != new_width + BORDERWIDTH)
|
|
||(size.mY != new_height + BORDERHEIGHT))
|
|
{
|
|
// use actual display dimensions, not virtual UI dimensions
|
|
S32 x = gViewerWindow->getWindowWidthRaw();
|
|
S32 y = gViewerWindow->getWindowHeightRaw();
|
|
BORDERWIDTH = size.mX - x;
|
|
BORDERHEIGHT = size.mY- y;
|
|
LLCoordScreen new_size(new_width + BORDERWIDTH,
|
|
new_height + BORDERHEIGHT);
|
|
|
|
S32 vsync_mode = gSavedSettings.getS32("SHRenderVsyncMode");
|
|
if(vsync_mode == -1 && !gGLManager.mHasAdaptiveVsync)
|
|
{
|
|
vsync_mode = 0; //Disable vsync if adaptive is desired yet isn't supported.
|
|
}
|
|
|
|
if (gViewerWindow->getWindow()->getFullscreen())
|
|
{
|
|
LLGLState::checkStates();
|
|
LLGLState::checkTextureChannels();
|
|
gViewerWindow->changeDisplaySettings(FALSE,
|
|
new_size,
|
|
vsync_mode,
|
|
TRUE);
|
|
LLGLState::checkStates();
|
|
LLGLState::checkTextureChannels();
|
|
}
|
|
else
|
|
{
|
|
gViewerWindow->getWindow()->setSize(new_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL LLViewerWindow::saveSnapshot( const std::string& filepath, S32 image_width, S32 image_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type)
|
|
{
|
|
llinfos << "Saving snapshot to: " << filepath << llendl;
|
|
|
|
LLPointer<LLImageRaw> raw = new LLImageRaw;
|
|
BOOL success = rawSnapshot(raw, image_width, image_height, (F32)image_width / image_height, show_ui, do_rebuild);
|
|
|
|
if (success)
|
|
{
|
|
LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
|
|
success = bmp_image->encode(raw, 0.0f);
|
|
if( success )
|
|
{
|
|
success = bmp_image->save(filepath);
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Unable to encode bmp snapshot" << llendl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Unable to capture raw snapshot" << llendl;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
void LLViewerWindow::playSnapshotAnimAndSound()
|
|
{
|
|
if (gSavedSettings.getBOOL("QuietSnapshotsToDisk"))
|
|
{
|
|
return;
|
|
}
|
|
gAgent.sendAnimationRequest(ANIM_AGENT_SNAPSHOT, ANIM_REQUEST_START);
|
|
send_sound_trigger(LLUUID(gSavedSettings.getString("UISndSnapshot")), 1.0f);
|
|
}
|
|
|
|
BOOL LLViewerWindow::thumbnailSnapshot(LLImageRaw *raw, S32 preview_width, S32 preview_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type)
|
|
{
|
|
return rawSnapshot(raw, preview_width, preview_height, (F32)gViewerWindow->getWindowWidthRaw() / gViewerWindow->getWindowHeightRaw(), show_ui, do_rebuild, type);
|
|
|
|
// *TODO below code was broken in deferred pipeline
|
|
/*
|
|
if ((!raw) || preview_width < 10 || preview_height < 10)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(gResizeScreenTexture) //the window is resizing
|
|
{
|
|
return FALSE ;
|
|
}
|
|
|
|
setCursor(UI_CURSOR_WAIT);
|
|
|
|
// Hide all the UI widgets first and draw a frame
|
|
BOOL prev_draw_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI);
|
|
|
|
if ( prev_draw_ui != show_ui)
|
|
{
|
|
LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
|
|
}
|
|
|
|
BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && LLPipeline::sShowHUDAttachments;
|
|
if (hide_hud)
|
|
{
|
|
LLPipeline::sShowHUDAttachments = FALSE;
|
|
}
|
|
|
|
S32 render_name = gSavedSettings.getS32("RenderName");
|
|
gSavedSettings.setS32("RenderName", 0);
|
|
LLVOAvatar::updateFreezeCounter(1) ; //pause avatar updating for one frame
|
|
|
|
S32 w = preview_width ;
|
|
S32 h = preview_height ;
|
|
LLVector2 display_scale = mDisplayScale ;
|
|
mDisplayScale.setVec((F32)w / mWindowRectRaw.getWidth(), (F32)h / mWindowRectRaw.getHeight()) ;
|
|
LLRect window_rect = mWindowRect;
|
|
mWindowRectRaw.set(0, h, w, 0);
|
|
|
|
gDisplaySwapBuffers = FALSE;
|
|
gDepthDirty = TRUE;
|
|
glClearColor(0.f, 0.f, 0.f, 0.f);
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
setup3DRender();
|
|
setupViewport();
|
|
|
|
LLFontGL::setFontDisplay(FALSE) ;
|
|
LLHUDText::setDisplayText(FALSE) ;
|
|
if (type == SNAPSHOT_TYPE_OBJECT_ID)
|
|
{
|
|
gObjectList.renderPickList(gViewerWindow->getVirtualWindowRect(), FALSE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
display(do_rebuild, 1.0f, 0, TRUE);
|
|
render_ui();
|
|
}
|
|
|
|
S32 glformat, gltype, glpixel_length ;
|
|
if(SNAPSHOT_TYPE_DEPTH == type)
|
|
{
|
|
glpixel_length = 4 ;
|
|
glformat = GL_DEPTH_COMPONENT ;
|
|
gltype = GL_FLOAT ;
|
|
}
|
|
else
|
|
{
|
|
glpixel_length = 3 ;
|
|
glformat = GL_RGB ;
|
|
gltype = GL_UNSIGNED_BYTE ;
|
|
}
|
|
|
|
raw->resize(w, h, glpixel_length);
|
|
glReadPixels(0, 0, w, h, glformat, gltype, raw->getData());
|
|
|
|
if(SNAPSHOT_TYPE_DEPTH == type)
|
|
{
|
|
LLViewerCamera* camerap = LLViewerCamera::getInstance();
|
|
F32 depth_conversion_factor_1 = (camerap->getFar() + camerap->getNear()) / (2.f * camerap->getFar() * camerap->getNear());
|
|
F32 depth_conversion_factor_2 = (camerap->getFar() - camerap->getNear()) / (2.f * camerap->getFar() * camerap->getNear());
|
|
|
|
//calculate the depth
|
|
for (S32 y = 0 ; y < h ; y++)
|
|
{
|
|
for(S32 x = 0 ; x < w ; x++)
|
|
{
|
|
S32 i = (w * y + x) << 2 ;
|
|
|
|
F32 depth_float_i = *(F32*)(raw->getData() + i);
|
|
|
|
F32 linear_depth_float = 1.f / (depth_conversion_factor_1 - (depth_float_i * depth_conversion_factor_2));
|
|
U8 depth_byte = F32_to_U8(linear_depth_float, camerap->getNear(), camerap->getFar());
|
|
*(raw->getData() + i + 0) = depth_byte;
|
|
*(raw->getData() + i + 1) = depth_byte;
|
|
*(raw->getData() + i + 2) = depth_byte;
|
|
*(raw->getData() + i + 3) = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
LLFontGL::setFontDisplay(TRUE) ;
|
|
LLHUDText::setDisplayText(TRUE) ;
|
|
mDisplayScale.setVec(display_scale) ;
|
|
mWindowRect = window_rect;
|
|
setup3DRender();
|
|
setupViewport();
|
|
gDisplaySwapBuffers = FALSE;
|
|
gDepthDirty = TRUE;
|
|
|
|
// POST SNAPSHOT
|
|
if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
|
|
{
|
|
LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
|
|
}
|
|
|
|
if (hide_hud)
|
|
{
|
|
LLPipeline::sShowHUDAttachments = TRUE;
|
|
}
|
|
|
|
setCursor(UI_CURSOR_ARROW);
|
|
|
|
if (do_rebuild)
|
|
{
|
|
// If we had to do a rebuild, that means that the lists of drawables to be rendered
|
|
// was empty before we started.
|
|
// Need to reset these, otherwise we call state sort on it again when render gets called the next time
|
|
// and we stand a good chance of crashing on rebuild because the render drawable arrays have multiple copies of
|
|
// objects on them.
|
|
gPipeline.resetDrawOrders();
|
|
}
|
|
|
|
gSavedSettings.setS32("RenderName", render_name);
|
|
|
|
return TRUE;*/
|
|
}
|
|
|
|
// Saves the image from the screen to the image pointed to by raw.
|
|
// This function does NOT yet scale the snapshot down to the requested size
|
|
// if that is smaller than the current window (scale_factor < 1) or if
|
|
// the aspect of the snapshot is unequal to the aspect of requested image.
|
|
bool LLViewerWindow::rawRawSnapshot(LLImageRaw *raw,
|
|
S32 image_width, S32 image_height, F32 snapshot_aspect, BOOL show_ui,
|
|
BOOL do_rebuild, ESnapshotType type, S32 max_size, F32 supersample, bool uncrop)
|
|
{
|
|
if (!raw)
|
|
{
|
|
return false;
|
|
}
|
|
//check if there is enough memory for the snapshot image
|
|
if(LLPipeline::sMemAllocationThrottled)
|
|
{
|
|
return false; //snapshot taking is disabled due to memory restriction.
|
|
}
|
|
if(image_width * image_height > (1 << 22)) //if snapshot image is larger than 2K by 2K
|
|
{
|
|
if(!LLMemory::tryToAlloc(NULL, image_width * image_height * 3))
|
|
{
|
|
llwarns << "No enough memory to take the snapshot with size (w : h): " << image_width << " : " << image_height << llendl ;
|
|
return false; //there is no enough memory for taking this snapshot.
|
|
}
|
|
}
|
|
|
|
// PRE SNAPSHOT
|
|
gDisplaySwapBuffers = FALSE;
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
setCursor(UI_CURSOR_WAIT);
|
|
|
|
// Hide all the UI widgets first and draw a frame
|
|
BOOL prev_draw_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI);
|
|
|
|
if ( prev_draw_ui != show_ui)
|
|
{
|
|
LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
|
|
}
|
|
|
|
BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && LLPipeline::sShowHUDAttachments;
|
|
if (hide_hud)
|
|
{
|
|
LLPipeline::sShowHUDAttachments = FALSE;
|
|
}
|
|
|
|
// Copy screen to a buffer
|
|
|
|
LLRect const window_rect = show_ui ? getWindowRectRaw() : getWorldViewRectRaw();
|
|
S32 window_width = window_rect.getWidth();
|
|
S32 window_height = window_rect.getHeight();
|
|
|
|
// SNAPSHOT
|
|
|
|
#if 1//SHY_MOD // screenshot improvement
|
|
F32 internal_scale = llmin(llmax(supersample,1.f),3.f);
|
|
// render at specified internal resolution. >1 results in supersampling.
|
|
image_height *= internal_scale;
|
|
image_width *= internal_scale;
|
|
#endif //shy_mod
|
|
|
|
//Hack until hud ui works in high-res shots again (nameplates and hud attachments are buggered).
|
|
if ((image_width > window_width || image_height > window_height))
|
|
{
|
|
if(LLPipeline::sShowHUDAttachments)
|
|
{
|
|
hide_hud=true;
|
|
LLPipeline::sShowHUDAttachments = FALSE;
|
|
}
|
|
if(show_ui)
|
|
{
|
|
show_ui=false;
|
|
LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
|
|
}
|
|
}
|
|
|
|
S32 buffer_x_offset = 0;
|
|
S32 buffer_y_offset = 0;
|
|
F32 scale_factor = 1.0f;
|
|
S32 image_buffer_x;
|
|
S32 image_buffer_y;
|
|
|
|
F32 const window_aspect = (F32)window_width / window_height;
|
|
// snapshot fits precisely inside window, it is the portion of the window with the correct aspect.
|
|
F32 snapshot_width = (snapshot_aspect > window_aspect) ? (F32)window_width : window_height * snapshot_aspect;
|
|
F32 snapshot_height = (snapshot_aspect < window_aspect) ? (F32)window_height : window_width / snapshot_aspect;
|
|
|
|
//This is what I would classify as a hack. If in deferred then do not tile (window_*=snapshot_*=image_*, ratio=scale_factor=1.f)
|
|
S32 original_width = 0;
|
|
S32 original_height = 0;
|
|
bool reset_deferred = false;
|
|
LLRenderTarget scratch_space;
|
|
if ((image_width > window_width || image_height > window_height) && LLPipeline::sRenderDeferred && !show_ui)
|
|
{
|
|
if (scratch_space.allocate(image_width, image_height, GL_RGBA, true, true))
|
|
{
|
|
original_width = gPipeline.mDeferredScreen.getWidth();
|
|
original_height = gPipeline.mDeferredScreen.getHeight();
|
|
|
|
if (gPipeline.allocateScreenBuffer(image_width, image_height))
|
|
{
|
|
image_width = snapshot_width = window_width = scratch_space.getWidth();
|
|
image_height = snapshot_height = window_height = scratch_space.getHeight();
|
|
reset_deferred = true;
|
|
mWindowRectRaw.set(0, image_height, image_width, 0);
|
|
scratch_space.bindTarget();
|
|
}
|
|
else
|
|
{
|
|
scratch_space.release();
|
|
gPipeline.allocateScreenBuffer(original_width, original_height);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ratio is the ratio snapshot/image', where image' is a rectangle with aspect snapshot_aspect that precisely contains image.
|
|
// Thus image_width' / image_height' == aspect ==> snapshot_width / image_width' == snapshot_height / image_height'.
|
|
// Since image' precisely contains image, one of them is equal (ie, image_height' = image_height) and the other is larger
|
|
// (or equal) (ie, image_width' >= image_width), and therefore one of snapshot_width / image_width and
|
|
// snapshot_height / image_height is correct, and the other is larger. Therefore, the smallest value of the
|
|
// following equals the ratio we're looking for.
|
|
F32 ratio = llmin(snapshot_width / image_width, snapshot_height / image_height);
|
|
// buffer equals the largest of image' and snapshot. This is because in the first case we need the higher
|
|
// resolution because of the size of the target image, and in the second case there is no reason to go
|
|
// smaller because it takes the same amount of time (and a slightly better quality should result after
|
|
// the final scaling). Thus, if ratio < 1 then buffer equals image', otherwise it equals snapshot.
|
|
// scale_factor is the ratio buffer/snapshot, and is initiallly equal to the ratio between buffer
|
|
// and snapshot (which have the same aspect).
|
|
S32 unscaled_image_buffer_x = snapshot_width;
|
|
S32 unscaled_image_buffer_y = snapshot_height;
|
|
if (uncrop) // Cropping will happen later.
|
|
{
|
|
// Stretch the requested snapshot to fill the entire (scaled) window, so that any aspect ratio,
|
|
// that requires cropping either horizontally or vertically, will always work.
|
|
unscaled_image_buffer_x = window_width;
|
|
unscaled_image_buffer_y = window_height;
|
|
}
|
|
for(scale_factor = llmax(1.0f, 1.0f / ratio);; // Initial attempt.
|
|
// However, if the buffer turns out to be too large, then clamp it to max_size.
|
|
scale_factor = llmin(max_size / snapshot_width, max_size / snapshot_height)) // Clamp
|
|
{
|
|
image_buffer_x = llround(unscaled_image_buffer_x * scale_factor);
|
|
image_buffer_y = llround(unscaled_image_buffer_y * scale_factor);
|
|
S32 image_size_x = llround(snapshot_width * scale_factor);
|
|
S32 image_size_y = llround(snapshot_width * scale_factor);
|
|
if (llmax(image_size_x, image_size_y) > max_size && // Boundary check to avoid memory overflow.
|
|
internal_scale <= 1.f && !reset_deferred) // SHY_MOD: If supersampling... Don't care about max_size.
|
|
{
|
|
// Too big, clamp.
|
|
continue;
|
|
}
|
|
// Done.
|
|
break;
|
|
}
|
|
|
|
// Center the buffer.
|
|
buffer_x_offset = llfloor(((window_width - unscaled_image_buffer_x) * scale_factor) / 2.f);
|
|
buffer_y_offset = llfloor(((window_height - unscaled_image_buffer_y) * scale_factor) / 2.f);
|
|
Dout(dc::snapshot, "rawRawSnapshot(" << image_width << ", " << image_height << ", " << snapshot_aspect << "): image_buffer_x = " << image_buffer_x << "; image_buffer_y = " << image_buffer_y);
|
|
|
|
bool error = !(image_buffer_x > 0 && image_buffer_y > 0);
|
|
if (!error)
|
|
{
|
|
raw->resize(image_buffer_x, image_buffer_y, 3);
|
|
error = raw->isBufferInvalid();
|
|
}
|
|
if (error)
|
|
{
|
|
if (prev_draw_ui != gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
|
|
{
|
|
LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
|
|
}
|
|
if (hide_hud)
|
|
{
|
|
LLPipeline::sShowHUDAttachments = TRUE;
|
|
}
|
|
setCursor(UI_CURSOR_ARROW);
|
|
return false;
|
|
}
|
|
|
|
BOOL is_tiling = scale_factor > 1.f;
|
|
if (is_tiling)
|
|
{
|
|
Dout(dc::warning, "USING TILING FOR SNAPSHOT!");
|
|
send_agent_pause();
|
|
if (show_ui || !hide_hud)
|
|
{
|
|
//rescale fonts
|
|
initFonts(scale_factor);
|
|
LLHUDObject::reshapeAll();
|
|
}
|
|
}
|
|
|
|
S32 output_buffer_offset_y = 0;
|
|
|
|
F32 depth_conversion_factor_1 = (LLViewerCamera::getInstance()->getFar() + LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear());
|
|
F32 depth_conversion_factor_2 = (LLViewerCamera::getInstance()->getFar() - LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear());
|
|
|
|
gObjectList.generatePickList(*LLViewerCamera::getInstance());
|
|
|
|
for (int subimage_y = 0; subimage_y < scale_factor; ++subimage_y)
|
|
{
|
|
S32 subimage_y_offset = llclamp(buffer_y_offset - (subimage_y * window_height), 0, window_height);
|
|
// handle fractional columns
|
|
U32 read_height = llmax(0, (window_height - subimage_y_offset) -
|
|
llmax(0, (window_height * (subimage_y + 1)) - (buffer_y_offset + raw->getHeight())));
|
|
|
|
S32 output_buffer_offset_x = 0;
|
|
for (int subimage_x = 0; subimage_x < scale_factor; ++subimage_x)
|
|
{
|
|
gDisplaySwapBuffers = FALSE;
|
|
gDepthDirty = TRUE;
|
|
|
|
S32 subimage_x_offset = llclamp(buffer_x_offset - (subimage_x * window_width), 0, window_width);
|
|
// handle fractional rows
|
|
U32 read_width = llmax(0, (window_width - subimage_x_offset) -
|
|
llmax(0, (window_width * (subimage_x + 1)) - (buffer_x_offset + raw->getWidth())));
|
|
|
|
// Skip rendering and sampling altogether if either width or height is degenerated to 0 (common in cropping cases)
|
|
if (read_width && read_height)
|
|
{
|
|
const U32 subfield = subimage_x+(subimage_y*llceil(scale_factor));
|
|
display(do_rebuild, scale_factor, subfield, TRUE, is_tiling);
|
|
|
|
if (!LLPipeline::sRenderDeferred)
|
|
{
|
|
// Required for showing the GUI in snapshots and performing bloom composite overlay
|
|
// Call even if show_ui is FALSE
|
|
render_ui(scale_factor, subfield);
|
|
}
|
|
|
|
for (U32 out_y = 0; out_y < read_height ; out_y++)
|
|
{
|
|
S32 output_buffer_offset = (
|
|
(out_y * (raw->getWidth())) // ...plus iterated y...
|
|
+ (window_width * subimage_x) // ...plus subimage start in x...
|
|
+ (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y...
|
|
- output_buffer_offset_x // ...minus buffer padding x...
|
|
- (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y...
|
|
) * raw->getComponents();
|
|
|
|
// Ping the watchdog thread every 100 lines to keep us alive (arbitrary number, feel free to change)
|
|
if (out_y % 100 == 0)
|
|
{
|
|
LLAppViewer::instance()->pingMainloopTimeout("LLViewerWindow::rawRawSnapshot");
|
|
}
|
|
|
|
if (type == SNAPSHOT_TYPE_COLOR)
|
|
{
|
|
glReadPixels(
|
|
subimage_x_offset, out_y + subimage_y_offset,
|
|
read_width, 1,
|
|
GL_RGB, GL_UNSIGNED_BYTE,
|
|
raw->getData() + output_buffer_offset
|
|
);
|
|
}
|
|
else // SNAPSHOT_TYPE_DEPTH
|
|
{
|
|
LLPointer<LLImageRaw> depth_line_buffer = new LLImageRaw(read_width, 1, sizeof(GL_FLOAT)); // need to store floating point values
|
|
glReadPixels(
|
|
subimage_x_offset, out_y + subimage_y_offset,
|
|
read_width, 1,
|
|
GL_DEPTH_COMPONENT, GL_FLOAT,
|
|
depth_line_buffer->getData()// current output pixel is beginning of buffer...
|
|
);
|
|
|
|
for (S32 i = 0; i < (S32)read_width; i++)
|
|
{
|
|
F32 depth_float = *(F32*)(depth_line_buffer->getData() + (i * sizeof(F32)));
|
|
|
|
F32 linear_depth_float = 1.f / (depth_conversion_factor_1 - (depth_float * depth_conversion_factor_2));
|
|
U8 depth_byte = F32_to_U8(linear_depth_float, LLViewerCamera::getInstance()->getNear(), LLViewerCamera::getInstance()->getFar());
|
|
// write converted scanline out to result image
|
|
for (S32 j = 0; j < raw->getComponents(); j++)
|
|
{
|
|
*(raw->getData() + output_buffer_offset + (i * raw->getComponents()) + j) = depth_byte;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
output_buffer_offset_x += subimage_x_offset;
|
|
stop_glerror();
|
|
}
|
|
output_buffer_offset_y += subimage_y_offset;
|
|
}
|
|
|
|
gDisplaySwapBuffers = FALSE;
|
|
gDepthDirty = TRUE;
|
|
|
|
// POST SNAPSHOT
|
|
if (prev_draw_ui != gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
|
|
{
|
|
LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
|
|
}
|
|
|
|
if (hide_hud)
|
|
{
|
|
LLPipeline::sShowHUDAttachments = TRUE;
|
|
}
|
|
|
|
if (is_tiling && (show_ui || !hide_hud))
|
|
{
|
|
initFonts(1.f);
|
|
LLHUDObject::reshapeAll();
|
|
}
|
|
|
|
setCursor(UI_CURSOR_ARROW);
|
|
|
|
if (do_rebuild)
|
|
{
|
|
// If we had to do a rebuild, that means that the lists of drawables to be rendered
|
|
// was empty before we started.
|
|
// Need to reset these, otherwise we call state sort on it again when render gets called the next time
|
|
// and we stand a good chance of crashing on rebuild because the render drawable arrays have multiple copies of
|
|
// objects on them.
|
|
gPipeline.resetDrawOrders();
|
|
}
|
|
|
|
if (reset_deferred)
|
|
{
|
|
mWindowRectRaw = window_rect;
|
|
scratch_space.flush();
|
|
scratch_space.release();
|
|
gPipeline.allocateScreenBuffer(original_width, original_height);
|
|
|
|
}
|
|
|
|
if (is_tiling)
|
|
{
|
|
send_agent_resume();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Same as the above, but does the resizing.
|
|
bool LLViewerWindow::rawSnapshot(LLImageRaw *raw,
|
|
S32 image_width, S32 image_height, F32 snapshot_aspect, BOOL show_ui,
|
|
BOOL do_rebuild, ESnapshotType type, S32 max_size, F32 supersample)
|
|
{
|
|
bool ret = rawRawSnapshot(raw, image_width, image_height, snapshot_aspect, show_ui, do_rebuild, type, max_size, supersample);
|
|
|
|
#if 1
|
|
|
|
if (ret && !raw->scale(image_width, image_height))
|
|
{
|
|
ret = false; // Failure.
|
|
}
|
|
|
|
#else // This was the old behavior.. but I don't think this is needed here.
|
|
|
|
if (ret)
|
|
{
|
|
// Pad image width such that the line length is a multiple of 4 bytes (for BMP encoding).
|
|
int n = 4;
|
|
for (int c = raw->getComponents(); c % 2 == 0 && n > 1; c /= 2) { n /= 2; } // n /= gcd(n, components)
|
|
image_width += (image_width * (n - 1)) % n; // Now n divides image_width, and thus four divides image_width * components, the line length.
|
|
|
|
// Resize image
|
|
if (llabs(image_width - image_buffer_x) > 4 || llabs(image_height - image_buffer_y) > 4)
|
|
{
|
|
ret = raw->scale( image_width, image_height );
|
|
}
|
|
else if (image_width != image_buffer_x || image_height != image_buffer_y)
|
|
{
|
|
ret = raw->scale( image_width, image_height, FALSE );
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
void LLViewerWindow::destroyWindow()
|
|
{
|
|
if (mWindow)
|
|
{
|
|
LLWindowManager::destroyWindow(mWindow);
|
|
}
|
|
mWindow = NULL;
|
|
}
|
|
|
|
|
|
void LLViewerWindow::drawMouselookInstructions()
|
|
{
|
|
static const F32 INSTRUCTIONS_OPAQUE_TIME = 10.f;
|
|
static const F32 INSTRUCTIONS_FADE_TIME = 5.f;
|
|
|
|
F32 mouselook_duration = gAgentCamera.getMouseLookDuration();
|
|
if( mouselook_duration >= (INSTRUCTIONS_OPAQUE_TIME+INSTRUCTIONS_OPAQUE_TIME) )
|
|
return;
|
|
|
|
F32 alpha = 1.f;
|
|
|
|
if( mouselook_duration > INSTRUCTIONS_OPAQUE_TIME) //instructions are fading
|
|
{
|
|
alpha = (F32) sqrt(1.f-pow(((mouselook_duration-INSTRUCTIONS_OPAQUE_TIME)/INSTRUCTIONS_FADE_TIME),2.f));
|
|
}
|
|
|
|
// Draw instructions for mouselook ("Press ESC to leave Mouselook" in a box at the top of the screen.)
|
|
const std::string instructions = LLTrans::getString("LeaveMouselook");
|
|
const LLFontGL* font = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF );
|
|
|
|
const S32 INSTRUCTIONS_PAD = 5;
|
|
LLRect instructions_rect;
|
|
instructions_rect.setLeftTopAndSize(
|
|
INSTRUCTIONS_PAD,
|
|
getWindowHeight() - INSTRUCTIONS_PAD,
|
|
font->getWidth( instructions ) + 2 * INSTRUCTIONS_PAD,
|
|
llround(font->getLineHeight() + 2 * INSTRUCTIONS_PAD));
|
|
|
|
{
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.color4f( 0.9f, 0.9f, 0.9f, alpha );
|
|
gl_rect_2d( instructions_rect );
|
|
}
|
|
|
|
font->renderUTF8(
|
|
instructions, 0,
|
|
instructions_rect.mLeft + INSTRUCTIONS_PAD,
|
|
instructions_rect.mTop - INSTRUCTIONS_PAD,
|
|
LLColor4( 0.0f, 0.0f, 0.0f, alpha ),
|
|
LLFontGL::LEFT, LLFontGL::TOP);
|
|
}
|
|
|
|
void* LLViewerWindow::getPlatformWindow() const
|
|
{
|
|
return mWindow->getPlatformWindow();
|
|
}
|
|
|
|
void* LLViewerWindow::getMediaWindow() const
|
|
{
|
|
return mWindow->getMediaWindow();
|
|
}
|
|
|
|
void LLViewerWindow::focusClient() const
|
|
{
|
|
return mWindow->focusClient();
|
|
}
|
|
S32 LLViewerWindow::getWindowHeight() const
|
|
{
|
|
return mWindowRectScaled.getHeight();
|
|
}
|
|
|
|
S32 LLViewerWindow::getWindowWidth() const
|
|
{
|
|
return mWindowRectScaled.getWidth();
|
|
}
|
|
|
|
S32 LLViewerWindow::getWindowDisplayHeight() const
|
|
{
|
|
return mWindowRectRaw.getHeight();
|
|
}
|
|
|
|
S32 LLViewerWindow::getWindowDisplayWidth() const
|
|
{
|
|
return mWindowRectRaw.getWidth();
|
|
}
|
|
|
|
void LLViewerWindow::setup2DRender()
|
|
{
|
|
// setup ortho camera
|
|
gl_state_for_2d(mWindowRectRaw.getWidth(), mWindowRectRaw.getHeight());
|
|
setup2DViewport();
|
|
}
|
|
|
|
void LLViewerWindow::setup2DViewport(S32 x_offset, S32 y_offset)
|
|
{
|
|
gGLViewport[0] = mWindowRectRaw.mLeft + x_offset;
|
|
gGLViewport[1] = mWindowRectRaw.mBottom + y_offset;
|
|
gGLViewport[2] = mWindowRectRaw.getWidth();
|
|
gGLViewport[3] = mWindowRectRaw.getHeight();
|
|
glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]);
|
|
}
|
|
|
|
|
|
void LLViewerWindow::setup3DRender()
|
|
{
|
|
// setup perspective camera
|
|
LLViewerCamera::getInstance()->setPerspective(NOT_FOR_SELECTION, getWindowRectRaw().mLeft, getWindowRectRaw().mBottom, getWindowRectRaw().getWidth(), getWindowRectRaw().getHeight(), FALSE, LLViewerCamera::getInstance()->getNear(), MAX_FAR_CLIP*2.f);
|
|
setup3DViewport();
|
|
}
|
|
|
|
void LLViewerWindow::setup3DViewport(S32 x_offset, S32 y_offset)
|
|
{
|
|
gGLViewport[0] = getWindowRectRaw().mLeft + x_offset;
|
|
gGLViewport[1] = getWindowRectRaw().mBottom + y_offset;
|
|
gGLViewport[2] = getWindowRectRaw().getWidth();
|
|
gGLViewport[3] = getWindowRectRaw().getHeight();
|
|
glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]);
|
|
}
|
|
|
|
void LLViewerWindow::revealIntroPanel()
|
|
{
|
|
if (mProgressView)
|
|
{
|
|
mProgressView->revealIntroPanel();
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::setShowProgress(const BOOL show)
|
|
{
|
|
if (mProgressView)
|
|
{
|
|
mProgressView->setVisible(show);
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::setStartupComplete()
|
|
{
|
|
if (mProgressView)
|
|
{
|
|
mProgressView->setStartupComplete();
|
|
}
|
|
}
|
|
|
|
BOOL LLViewerWindow::getShowProgress() const
|
|
{
|
|
return (mProgressView && mProgressView->getVisible());
|
|
}
|
|
|
|
void LLViewerWindow::setProgressString(const std::string& string)
|
|
{
|
|
if (mProgressView)
|
|
{
|
|
mProgressView->setText(string);
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::setProgressMessage(const std::string& msg)
|
|
{
|
|
if(mProgressView)
|
|
{
|
|
mProgressView->setMessage(msg);
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::setProgressPercent(const F32 percent)
|
|
{
|
|
if (mProgressView)
|
|
{
|
|
mProgressView->setPercent(percent);
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::setProgressCancelButtonVisible( BOOL b, const std::string& label )
|
|
{
|
|
if (mProgressView)
|
|
{
|
|
mProgressView->setCancelButtonVisible( b, label );
|
|
}
|
|
}
|
|
|
|
|
|
LLProgressView *LLViewerWindow::getProgressView() const
|
|
{
|
|
return mProgressView;
|
|
}
|
|
|
|
void LLViewerWindow::dumpState()
|
|
{
|
|
llinfos << "LLViewerWindow Active " << S32(mActive) << llendl;
|
|
llinfos << "mWindow visible " << S32(mWindow->getVisible())
|
|
<< " minimized " << S32(mWindow->getMinimized())
|
|
<< llendl;
|
|
}
|
|
|
|
void LLViewerWindow::stopGL(BOOL save_state)
|
|
{
|
|
//Note: --bao
|
|
//if not necessary, do not change the order of the function calls in this function.
|
|
//if change something, make sure it will not break anything.
|
|
//especially be careful to put anything behind gTextureList.destroyGL(save_state);
|
|
if (!gGLManager.mIsDisabled)
|
|
{
|
|
llinfos << "Shutting down GL..." << llendl;
|
|
|
|
// Pause texture decode threads (will get unpaused during main loop)
|
|
LLAppViewer::getTextureCache()->pause();
|
|
LLAppViewer::getImageDecodeThread()->pause();
|
|
LLAppViewer::getTextureFetch()->pause();
|
|
|
|
gSky.destroyGL();
|
|
stop_glerror();
|
|
|
|
LLManipTranslate::destroyGL() ;
|
|
stop_glerror();
|
|
|
|
gBumpImageList.destroyGL();
|
|
stop_glerror();
|
|
|
|
LLFontGL::destroyAllGL();
|
|
stop_glerror();
|
|
|
|
LLVOAvatar::destroyGL();
|
|
stop_glerror();
|
|
|
|
LLVOPartGroup::destroyGL();
|
|
|
|
LLViewerDynamicTexture::destroyGL();
|
|
stop_glerror();
|
|
|
|
if (gPipeline.isInit())
|
|
{
|
|
gPipeline.destroyGL();
|
|
}
|
|
|
|
gBox.cleanupGL();
|
|
|
|
if(LLPostProcess::instanceExists())
|
|
{
|
|
LLPostProcess::getInstance()->destroyGL();
|
|
}
|
|
|
|
gTextureList.destroyGL(save_state);
|
|
stop_glerror();
|
|
|
|
gGLManager.mIsDisabled = TRUE;
|
|
stop_glerror();
|
|
|
|
llinfos << "Remaining allocated texture memory: " << LLImageGL::sGlobalTextureMemoryInBytes << " bytes" << llendl;
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::restoreGL(const std::string& progress_message)
|
|
{
|
|
//Note: --bao
|
|
//if not necessary, do not change the order of the function calls in this function.
|
|
//if change something, make sure it will not break anything.
|
|
//especially, be careful to put something before gTextureList.restoreGL();
|
|
if (gGLManager.mIsDisabled)
|
|
{
|
|
llinfos << "Restoring GL..." << llendl;
|
|
gGLManager.mIsDisabled = FALSE;
|
|
|
|
initGLDefaults();
|
|
gGL.refreshState(); //Singu Note: Call immediately. Cached states may have prevented initGLDefaults from actually applying changes.
|
|
LLGLState::restoreGL();
|
|
gTextureList.restoreGL();
|
|
|
|
// for future support of non-square pixels, and fonts that are properly stretched
|
|
//LLFontGL::destroyDefaultFonts();
|
|
initFonts();
|
|
|
|
gSky.restoreGL();
|
|
gPipeline.restoreGL();
|
|
LLDrawPoolWater::restoreGL();
|
|
LLManipTranslate::restoreGL();
|
|
|
|
gBumpImageList.restoreGL();
|
|
LLViewerDynamicTexture::restoreGL();
|
|
LLVOAvatar::restoreGL();
|
|
LLVOPartGroup::restoreGL();
|
|
|
|
gResizeScreenTexture = TRUE;
|
|
gWindowResized = TRUE;
|
|
|
|
if (isAgentAvatarValid() && gAgentAvatarp->isEditingAppearance())
|
|
{
|
|
LLVisualParamHint::requestHintUpdates();
|
|
}
|
|
|
|
if (!progress_message.empty())
|
|
{
|
|
gRestoreGLTimer.reset();
|
|
gRestoreGL = TRUE;
|
|
setShowProgress(TRUE);
|
|
setProgressString(progress_message);
|
|
}
|
|
llinfos << "...Restoring GL done" << llendl;
|
|
if(!LLAppViewer::instance()->restoreErrorTrap())
|
|
{
|
|
llwarns << " Someone took over my signal/exception handler (post restoreGL)!" << llendl;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::initFonts(F32 zoom_factor)
|
|
{
|
|
if(gGLManager.mIsDisabled)
|
|
return;
|
|
LLFontGL::destroyAllGL();
|
|
// Initialize with possibly different zoom factor
|
|
|
|
LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"),
|
|
mDisplayScale.mV[VX] * zoom_factor,
|
|
mDisplayScale.mV[VY] * zoom_factor,
|
|
gDirUtilp->getAppRODataDir(),
|
|
LLUICtrlFactory::getXUIPaths());
|
|
LLFontGL::loadDefaultFonts();
|
|
}
|
|
void LLViewerWindow::toggleFullscreen(BOOL show_progress)
|
|
{
|
|
if (mWindow)
|
|
{
|
|
mWantFullscreen = mWindow->getFullscreen() ? FALSE : TRUE;
|
|
mIsFullscreenChecked = mWindow->getFullscreen() ? FALSE : TRUE;
|
|
mShowFullscreenProgress = show_progress;
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::getTargetWindow(BOOL& fullscreen, S32& width, S32& height) const
|
|
{
|
|
fullscreen = mWantFullscreen;
|
|
|
|
if (mWindow
|
|
&& mWindow->getFullscreen() == mWantFullscreen)
|
|
{
|
|
width = getWindowDisplayWidth();
|
|
height = getWindowDisplayHeight();
|
|
}
|
|
else if (mWantFullscreen)
|
|
{
|
|
width = gSavedSettings.getS32("FullScreenWidth");
|
|
height = gSavedSettings.getS32("FullScreenHeight");
|
|
}
|
|
else
|
|
{
|
|
width = gSavedSettings.getS32("WindowWidth");
|
|
height = gSavedSettings.getS32("WindowHeight");
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::requestResolutionUpdate(bool fullscreen_checked)
|
|
{
|
|
mResDirty = true;
|
|
mWantFullscreen = fullscreen_checked;
|
|
mIsFullscreenChecked = fullscreen_checked;
|
|
}
|
|
|
|
BOOL LLViewerWindow::checkSettings()
|
|
{
|
|
//Singu Note: Don't do the following.
|
|
//setShaders is already called in restoreGL(), and gGL.refreshState() is too as to maintain blend states.
|
|
//This maintaining of blend states is needed for LLGLState::checkStates() to not error out.
|
|
/*if (mStatesDirty)
|
|
{
|
|
gGL.refreshState();
|
|
LLViewerShaderMgr::instance()->setShaders();
|
|
mStatesDirty = false;
|
|
}*/
|
|
|
|
// We want to update the resolution AFTER the states getting refreshed not before.
|
|
if (mResDirty)
|
|
{
|
|
if (gSavedSettings.getBOOL("FullScreenAutoDetectAspectRatio"))
|
|
{
|
|
getWindow()->setNativeAspectRatio(0.f);
|
|
}
|
|
else
|
|
{
|
|
getWindow()->setNativeAspectRatio(gSavedSettings.getF32("FullScreenAspectRatio"));
|
|
}
|
|
|
|
reshape(getWindowWidthRaw(), getWindowHeightRaw());
|
|
|
|
// force aspect ratio
|
|
if (mIsFullscreenChecked)
|
|
{
|
|
LLViewerCamera::getInstance()->setAspect( getDisplayAspectRatio() );
|
|
}
|
|
|
|
mResDirty = false;
|
|
}
|
|
|
|
BOOL is_fullscreen = mWindow->getFullscreen();
|
|
if(mWantFullscreen)
|
|
{
|
|
LLCoordScreen screen_size;
|
|
LLCoordScreen desired_screen_size(gSavedSettings.getS32("FullScreenWidth"),
|
|
gSavedSettings.getS32("FullScreenHeight"));
|
|
getWindow()->getSize(&screen_size);
|
|
if(!is_fullscreen ||
|
|
screen_size.mX != desired_screen_size.mX
|
|
|| screen_size.mY != desired_screen_size.mY)
|
|
{
|
|
if (!LLStartUp::canGoFullscreen())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
LLGLState::checkStates();
|
|
LLGLState::checkTextureChannels();
|
|
|
|
S32 vsync_mode = gSavedSettings.getS32("SHRenderVsyncMode");
|
|
if(vsync_mode == -1 && !gGLManager.mHasAdaptiveVsync)
|
|
{
|
|
vsync_mode = 0; //Disable vsync if adaptive is desired yet isn't supported.
|
|
}
|
|
|
|
changeDisplaySettings(TRUE,
|
|
desired_screen_size,
|
|
vsync_mode,
|
|
mShowFullscreenProgress);
|
|
LLGLState::checkStates();
|
|
LLGLState::checkTextureChannels();
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(is_fullscreen)
|
|
{
|
|
// Changing to windowed mode.
|
|
LLGLState::checkStates();
|
|
LLGLState::checkTextureChannels();
|
|
changeDisplaySettings(FALSE,
|
|
LLCoordScreen(gSavedSettings.getS32("WindowWidth"),
|
|
gSavedSettings.getS32("WindowHeight")),
|
|
TRUE,
|
|
mShowFullscreenProgress);
|
|
LLGLState::checkStates();
|
|
LLGLState::checkTextureChannels();
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void LLViewerWindow::restartDisplay(BOOL show_progress_bar)
|
|
{
|
|
llinfos << "Restaring GL" << llendl;
|
|
stopGL();
|
|
if (show_progress_bar)
|
|
{
|
|
restoreGL(LLTrans::getString("ProgressChangingResolution"));
|
|
}
|
|
else
|
|
{
|
|
restoreGL();
|
|
}
|
|
}
|
|
|
|
BOOL LLViewerWindow::changeDisplaySettings(BOOL fullscreen, LLCoordScreen size, const S32 vsync_mode, BOOL show_progress_bar)
|
|
{
|
|
BOOL was_maximized = gSavedSettings.getBOOL("WindowMaximized");
|
|
mWantFullscreen = fullscreen;
|
|
mShowFullscreenProgress = show_progress_bar;
|
|
gSavedSettings.setBOOL("FullScreen", mWantFullscreen);
|
|
|
|
gResizeScreenTexture = TRUE;
|
|
|
|
BOOL old_fullscreen = mWindow->getFullscreen();
|
|
if (!old_fullscreen && fullscreen && !LLStartUp::canGoFullscreen())
|
|
{
|
|
// Not allowed to switch to fullscreen now, so exit early.
|
|
// *NOTE: This case should never be reached, but just-in-case.
|
|
return TRUE;
|
|
}
|
|
|
|
U32 fsaa = LLRenderTarget::sUseFBO ? 0 : gSavedSettings.getU32("RenderFSAASamples"); //don't use window level anti-aliasing if FBOs are enabled
|
|
U32 old_fsaa = mWindow->getFSAASamples();
|
|
|
|
// going from windowed to windowed
|
|
if (!old_fullscreen && !fullscreen)
|
|
{
|
|
// if not maximized, use the request size
|
|
if (!mWindow->getMaximized())
|
|
{
|
|
mWindow->setSize(size);
|
|
}
|
|
|
|
if (fsaa == old_fsaa && vsync_mode == mWindow->getVsyncMode())
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// Close floaters that don't handle settings change
|
|
LLFloaterSnapshot::hide(0);
|
|
|
|
BOOL result_first_try = FALSE;
|
|
BOOL result_second_try = FALSE;
|
|
|
|
LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus();
|
|
send_agent_pause();
|
|
llinfos << "Stopping GL during changeDisplaySettings" << llendl;
|
|
stopGL();
|
|
mIgnoreActivate = TRUE;
|
|
LLCoordScreen old_size;
|
|
LLCoordScreen old_pos;
|
|
LLCoordScreen new_pos;
|
|
mWindow->getSize(&old_size);
|
|
BOOL got_position = mWindow->getPosition(&old_pos);
|
|
|
|
//Singu Note: ALWAYS Save old values if we can.
|
|
if(!old_fullscreen && !mWindow->getMaximized() && got_position)
|
|
{
|
|
//Always save the current position if we can
|
|
gSavedSettings.setS32("WindowX", old_pos.mX);
|
|
gSavedSettings.setS32("WindowY", old_pos.mY);
|
|
}
|
|
|
|
//Singu Note: Try to feed switchcontext a posp pointer right off the bat. Looks less clunky on systems that implemented it.
|
|
if (!fullscreen && !mWindow->getMaximized())
|
|
{
|
|
new_pos.mX = gSavedSettings.getS32("WindowX");
|
|
new_pos.mY = gSavedSettings.getS32("WindowY");
|
|
}
|
|
|
|
mWindow->setFSAASamples(fsaa);
|
|
mWindow->setVsyncMode(vsync_mode);
|
|
|
|
result_first_try = mWindow->switchContext(fullscreen, size, vsync_mode, &new_pos);
|
|
if (!result_first_try)
|
|
{
|
|
// try to switch back
|
|
mWindow->setFSAASamples(old_fsaa);
|
|
result_second_try = mWindow->switchContext(old_fullscreen, old_size, vsync_mode, &new_pos);
|
|
|
|
if (!result_second_try)
|
|
{
|
|
// we are stuck...try once again with a minimal resolution?
|
|
send_agent_resume();
|
|
mIgnoreActivate = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
send_agent_resume();
|
|
|
|
llinfos << "Restoring GL during resolution change" << llendl;
|
|
if (show_progress_bar)
|
|
{
|
|
restoreGL(LLTrans::getString("ProgressChangingResolution"));
|
|
}
|
|
else
|
|
{
|
|
restoreGL();
|
|
}
|
|
|
|
if (!result_first_try)
|
|
{
|
|
LLSD args;
|
|
args["RESX"] = llformat("%d",size.mX);
|
|
args["RESY"] = llformat("%d",size.mY);
|
|
LLNotificationsUtil::add("ResolutionSwitchFail", args);
|
|
size = old_size; // for reshape below
|
|
}
|
|
|
|
BOOL success = result_first_try || result_second_try;
|
|
|
|
if (success)
|
|
{
|
|
#if LL_WINDOWS
|
|
// Only trigger a reshape after switching to fullscreen; otherwise rely on the windows callback
|
|
// (otherwise size is wrong; this is the entire window size, reshape wants the visible window size)
|
|
if (fullscreen && result_first_try)
|
|
#endif
|
|
{
|
|
reshape(size.mX, size.mY);
|
|
}
|
|
}
|
|
|
|
if (!mWindow->getFullscreen() && success)
|
|
{
|
|
// maximize window if was maximized, else reposition
|
|
if (was_maximized)
|
|
{
|
|
mWindow->maximize();
|
|
}
|
|
else
|
|
{
|
|
mWindow->setPosition(new_pos);
|
|
}
|
|
}
|
|
|
|
mIgnoreActivate = FALSE;
|
|
gFocusMgr.setKeyboardFocus(keyboard_focus);
|
|
mWantFullscreen = mWindow->getFullscreen();
|
|
mShowFullscreenProgress = FALSE;
|
|
|
|
//mStatesDirty = true; //Singu Note: No longer needed. State update is now in restoreGL.
|
|
return success;
|
|
}
|
|
|
|
|
|
F32 LLViewerWindow::getDisplayAspectRatio() const
|
|
{
|
|
if (mWindow->getFullscreen())
|
|
{
|
|
if (gSavedSettings.getBOOL("FullScreenAutoDetectAspectRatio"))
|
|
{
|
|
return mWindow->getNativeAspectRatio();
|
|
}
|
|
else
|
|
{
|
|
return gSavedSettings.getF32("FullScreenAspectRatio");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return mWindow->getNativeAspectRatio();
|
|
}
|
|
}
|
|
|
|
void LLViewerWindow::calcDisplayScale()
|
|
{
|
|
F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor");
|
|
LLVector2 display_scale;
|
|
display_scale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f));
|
|
if(mWindow->getFullscreen())
|
|
{
|
|
F32 height_normalization = gSavedSettings.getBOOL("UIAutoScale") ? ((F32)mWindowRectRaw.getHeight() / display_scale.mV[VY]) / 768.f : 1.f;
|
|
display_scale *= (ui_scale_factor * height_normalization);
|
|
}
|
|
else
|
|
{
|
|
display_scale *= ui_scale_factor;
|
|
}
|
|
|
|
// limit minimum display scale
|
|
if (display_scale.mV[VX] < MIN_DISPLAY_SCALE || display_scale.mV[VY] < MIN_DISPLAY_SCALE)
|
|
{
|
|
display_scale *= MIN_DISPLAY_SCALE / llmin(display_scale.mV[VX], display_scale.mV[VY]);
|
|
}
|
|
|
|
if (mWindow->getFullscreen())
|
|
{
|
|
display_scale.mV[0] = llround(display_scale.mV[0], 2.0f/(F32) mWindowRectRaw.getWidth());
|
|
display_scale.mV[1] = llround(display_scale.mV[1], 2.0f/(F32) mWindowRectRaw.getHeight());
|
|
}
|
|
|
|
if (display_scale != mDisplayScale)
|
|
{
|
|
llinfos << "Setting display scale to " << display_scale << llendl;
|
|
|
|
mDisplayScale = display_scale;
|
|
// Init default fonts
|
|
initFonts();
|
|
}
|
|
}
|
|
|
|
S32 LLViewerWindow::getChatConsoleBottomPad()
|
|
{
|
|
static const LLCachedControl<S32> user_offset("ConsoleBottomOffset");
|
|
S32 offset = user_offset;
|
|
|
|
if(gToolBar && gToolBar->getVisible())
|
|
offset += TOOL_BAR_HEIGHT;
|
|
|
|
return offset;
|
|
}
|
|
|
|
LLRect LLViewerWindow::getChatConsoleRect()
|
|
{
|
|
LLRect full_window(0, getWindowHeightScaled(), getWindowWidthScaled(), 0);
|
|
LLRect console_rect = full_window;
|
|
|
|
const S32 CONSOLE_PADDING_TOP = 24;
|
|
const S32 CONSOLE_PADDING_LEFT = 24;
|
|
const S32 CONSOLE_PADDING_RIGHT = 10;
|
|
|
|
console_rect.mTop -= CONSOLE_PADDING_TOP;
|
|
console_rect.mBottom += getChatConsoleBottomPad();
|
|
|
|
console_rect.mLeft += CONSOLE_PADDING_LEFT;
|
|
|
|
static const LLCachedControl<bool> CHAT_FULL_WIDTH("ChatFullWidth",true);
|
|
|
|
if (CHAT_FULL_WIDTH)
|
|
{
|
|
console_rect.mRight -= CONSOLE_PADDING_RIGHT;
|
|
}
|
|
else
|
|
{
|
|
// Make console rect somewhat narrow so having inventory open is
|
|
// less of a problem.
|
|
console_rect.mRight = console_rect.mLeft + 2 * getWindowWidthScaled() / 3;
|
|
}
|
|
|
|
return console_rect;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
//static
|
|
bool LLViewerWindow::onAlert(const LLSD& notify)
|
|
{
|
|
LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID());
|
|
|
|
if (gNoRender)
|
|
{
|
|
llinfos << "Alert: " << notification->getName() << llendl;
|
|
notification->respond(LLSD::emptyMap());
|
|
LLNotifications::instance().cancel(notification);
|
|
return false;
|
|
}
|
|
|
|
// If we're in mouselook, the mouse is hidden and so the user can't click
|
|
// the dialog buttons. In that case, change to First Person instead.
|
|
if( gAgentCamera.cameraMouselook() )
|
|
{
|
|
gAgentCamera.changeCameraToDefault();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLBottomPanel::LLBottomPanel(const LLRect &rect) :
|
|
LLPanel(LLStringUtil::null, rect, FALSE),
|
|
mIndicator(NULL)
|
|
{
|
|
// bottom panel is focus root, so Tab moves through the toolbar and button bar, and overlay
|
|
setFocusRoot(TRUE);
|
|
// flag this panel as chrome so buttons don't grab keyboard focus
|
|
setIsChrome(TRUE);
|
|
|
|
mFactoryMap["toolbar"] = LLCallbackMap(createToolBar, NULL);
|
|
mFactoryMap["overlay"] = LLCallbackMap(createOverlayBar, NULL);
|
|
LLUICtrlFactory::getInstance()->buildPanel(this, "panel_bars.xml", &getFactoryMap());
|
|
|
|
setOrigin(rect.mLeft, rect.mBottom);
|
|
reshape(rect.getWidth(), rect.getHeight());
|
|
}
|
|
|
|
void LLBottomPanel::setFocusIndicator(LLView * indicator)
|
|
{
|
|
mIndicator = indicator;
|
|
}
|
|
|
|
void LLBottomPanel::draw()
|
|
{
|
|
if(mIndicator)
|
|
{
|
|
BOOL hasFocus = gFocusMgr.childHasKeyboardFocus(this);
|
|
mIndicator->setVisible(hasFocus);
|
|
mIndicator->setEnabled(hasFocus);
|
|
}
|
|
LLPanel::draw();
|
|
}
|
|
|
|
void* LLBottomPanel::createOverlayBar(void* data)
|
|
{
|
|
delete gOverlayBar;
|
|
gOverlayBar = new LLOverlayBar();
|
|
return gOverlayBar;
|
|
}
|
|
|
|
void* LLBottomPanel::createToolBar(void* data)
|
|
{
|
|
delete gToolBar;
|
|
gToolBar = new LLToolBar();
|
|
return gToolBar;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LLPickInfo
|
|
//
|
|
LLPickInfo::LLPickInfo()
|
|
: mKeyMask(MASK_NONE),
|
|
mPickCallback(NULL),
|
|
mPickType(PICK_INVALID),
|
|
mWantSurfaceInfo(FALSE),
|
|
mObjectFace(-1),
|
|
mUVCoords(-1.f, -1.f),
|
|
mSTCoords(-1.f, -1.f),
|
|
mXYCoords(-1, -1),
|
|
mIntersection(),
|
|
mNormal(),
|
|
mTangent(),
|
|
mBinormal(),
|
|
mHUDIcon(NULL),
|
|
mPickTransparent(FALSE)
|
|
{
|
|
}
|
|
|
|
LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos,
|
|
MASK keyboard_mask,
|
|
BOOL pick_transparent,
|
|
BOOL pick_uv_coords,
|
|
void (*pick_callback)(const LLPickInfo& pick_info))
|
|
: mMousePt(mouse_pos),
|
|
mKeyMask(keyboard_mask),
|
|
mPickCallback(pick_callback),
|
|
mPickType(PICK_INVALID),
|
|
mWantSurfaceInfo(pick_uv_coords),
|
|
mObjectFace(-1),
|
|
mUVCoords(-1.f, -1.f),
|
|
mSTCoords(-1.f, -1.f),
|
|
mXYCoords(-1, -1),
|
|
mNormal(),
|
|
mTangent(),
|
|
mBinormal(),
|
|
mHUDIcon(NULL),
|
|
mPickTransparent(pick_transparent)
|
|
{
|
|
}
|
|
|
|
void LLPickInfo::fetchResults()
|
|
{
|
|
|
|
S32 face_hit = -1;
|
|
LLVector4a intersection, normal;
|
|
LLVector4a tangent;
|
|
|
|
LLVector2 uv;
|
|
|
|
LLHUDIcon* hit_icon = gViewerWindow->cursorIntersectIcon(mMousePt.mX, mMousePt.mY, 512.f, &intersection);
|
|
|
|
LLVector4a origin;
|
|
origin.load3(LLViewerCamera::getInstance()->getOrigin().mV);
|
|
F32 icon_dist = 0.f;
|
|
if (hit_icon)
|
|
{
|
|
LLVector4a delta;
|
|
delta.setSub(intersection, origin);
|
|
icon_dist = delta.getLength3().getF32();
|
|
}
|
|
|
|
LLViewerObject* hit_object = gViewerWindow->cursorIntersect(mMousePt.mX, mMousePt.mY, 512.f,
|
|
NULL, -1, mPickTransparent, &face_hit,
|
|
&intersection, &uv, &normal, &tangent);
|
|
|
|
mPickPt = mMousePt;
|
|
|
|
U32 te_offset = face_hit > -1 ? face_hit : 0;
|
|
|
|
//unproject relative clicked coordinate from window coordinate using GL
|
|
|
|
LLViewerObject* objectp = hit_object;
|
|
|
|
|
|
LLVector4a delta;
|
|
delta.setSub(origin, intersection);
|
|
|
|
if (hit_icon &&
|
|
(!objectp ||
|
|
icon_dist < delta.getLength3().getF32()))
|
|
{
|
|
// was this name referring to a hud icon?
|
|
mHUDIcon = hit_icon;
|
|
mPickType = PICK_ICON;
|
|
mPosGlobal = mHUDIcon->getPositionGlobal();
|
|
}
|
|
else if (objectp)
|
|
{
|
|
if( objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH )
|
|
{
|
|
// Hit land
|
|
mPickType = PICK_LAND;
|
|
mObjectID.setNull(); // land has no id
|
|
|
|
// put global position into land_pos
|
|
LLVector3d land_pos;
|
|
if (!gViewerWindow->mousePointOnLandGlobal(mPickPt.mX, mPickPt.mY, &land_pos))
|
|
{
|
|
// The selected point is beyond the draw distance or is otherwise
|
|
// not selectable. Return before calling mPickCallback().
|
|
return;
|
|
}
|
|
|
|
// Fudge the land focus a little bit above ground.
|
|
mPosGlobal = land_pos + LLVector3d::z_axis * 0.1f;
|
|
}
|
|
else
|
|
{
|
|
if(isFlora(objectp))
|
|
{
|
|
mPickType = PICK_FLORA;
|
|
}
|
|
else
|
|
{
|
|
mPickType = PICK_OBJECT;
|
|
}
|
|
|
|
LLVector3 v_intersection(intersection.getF32ptr());
|
|
|
|
mObjectOffset = gAgentCamera.calcFocusOffset(objectp, v_intersection, mPickPt.mX, mPickPt.mY);
|
|
mObjectID = objectp->mID;
|
|
mObjectFace = (te_offset == NO_FACE) ? -1 : (S32)te_offset;
|
|
|
|
|
|
|
|
mPosGlobal = gAgent.getPosGlobalFromAgent(v_intersection);
|
|
|
|
if (mWantSurfaceInfo)
|
|
{
|
|
getSurfaceInfo();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mPickCallback)
|
|
{
|
|
mPickCallback(*this);
|
|
}
|
|
}
|
|
|
|
LLPointer<LLViewerObject> LLPickInfo::getObject() const
|
|
{
|
|
return gObjectList.findObject( mObjectID );
|
|
}
|
|
|
|
void LLPickInfo::updateXYCoords()
|
|
{
|
|
if (mObjectFace > -1)
|
|
{
|
|
const LLTextureEntry* tep = getObject()->getTE(mObjectFace);
|
|
LLPointer<LLViewerTexture> imagep = LLViewerTextureManager::getFetchedTexture(tep->getID());
|
|
if(mUVCoords.mV[VX] >= 0.f && mUVCoords.mV[VY] >= 0.f && imagep.notNull())
|
|
{
|
|
mXYCoords.mX = llround(mUVCoords.mV[VX] * (F32)imagep->getWidth());
|
|
mXYCoords.mY = llround((1.f - mUVCoords.mV[VY]) * (F32)imagep->getHeight());
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLPickInfo::getSurfaceInfo()
|
|
{
|
|
// set values to uninitialized - this is what we return if no intersection is found
|
|
mObjectFace = -1;
|
|
mUVCoords = LLVector2(-1, -1);
|
|
mSTCoords = LLVector2(-1, -1);
|
|
mXYCoords = LLCoordScreen(-1, -1);
|
|
mIntersection = LLVector3(0,0,0);
|
|
mNormal = LLVector3(0,0,0);
|
|
mBinormal = LLVector3(0,0,0);
|
|
mTangent = LLVector4(0,0,0,0);
|
|
|
|
LLVector4a tangent;
|
|
LLVector4a intersection;
|
|
LLVector4a normal;
|
|
|
|
tangent.clear();
|
|
normal.clear();
|
|
intersection.clear();
|
|
|
|
LLViewerObject* objectp = getObject();
|
|
|
|
if (objectp)
|
|
{
|
|
if (gViewerWindow->cursorIntersect(llround((F32)mMousePt.mX), llround((F32)mMousePt.mY), 1024.f,
|
|
objectp, -1, mPickTransparent,
|
|
&mObjectFace,
|
|
&intersection,
|
|
&mSTCoords,
|
|
&normal,
|
|
&tangent))
|
|
{
|
|
// if we succeeded with the intersect above, compute the texture coordinates:
|
|
|
|
if (objectp->mDrawable.notNull() && mObjectFace > -1)
|
|
{
|
|
LLFace* facep = objectp->mDrawable->getFace(mObjectFace);
|
|
if (facep)
|
|
{
|
|
mUVCoords = facep->surfaceToTexture(mSTCoords, intersection, normal);
|
|
}
|
|
}
|
|
|
|
mIntersection.set(intersection.getF32ptr());
|
|
mNormal.set(normal.getF32ptr());
|
|
mTangent.set(tangent.getF32ptr());
|
|
|
|
//extrapoloate binormal from normal and tangent
|
|
|
|
LLVector4a binormal;
|
|
binormal.setCross3(normal, tangent);
|
|
binormal.mul(tangent.getF32ptr()[3]);
|
|
|
|
mBinormal.set(binormal.getF32ptr());
|
|
|
|
mBinormal.normalize();
|
|
mNormal.normalize();
|
|
mTangent.normalize();
|
|
|
|
// and XY coords:
|
|
updateXYCoords();
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* code to get UV via a special UV render - removed in lieu of raycast method
|
|
LLVector2 LLPickInfo::pickUV()
|
|
{
|
|
LLVector2 result(-1.f, -1.f);
|
|
|
|
LLViewerObject* objectp = getObject();
|
|
if (!objectp)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (mObjectFace > -1 &&
|
|
objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME &&
|
|
mObjectFace < objectp->mDrawable->getNumFaces())
|
|
{
|
|
S32 scaled_x = llround((F32)mPickPt.mX * gViewerWindow->getDisplayScale().mV[VX]);
|
|
S32 scaled_y = llround((F32)mPickPt.mY * gViewerWindow->getDisplayScale().mV[VY]);
|
|
const S32 UV_PICK_WIDTH = 5;
|
|
const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2;
|
|
U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4];
|
|
LLFace* facep = objectp->mDrawable->getFace(mObjectFace);
|
|
if (facep)
|
|
{
|
|
LLGLState scissor_state(GL_SCISSOR_TEST);
|
|
scissor_state.enable();
|
|
LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE);
|
|
//glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH);
|
|
glScissor(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH);
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
facep->renderSelectedUV();
|
|
|
|
glReadPixels(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, GL_RGBA, GL_UNSIGNED_BYTE, uv_pick_buffer);
|
|
U8* center_pixel = &uv_pick_buffer[4 * ((UV_PICK_WIDTH * UV_PICK_HALF_WIDTH) + UV_PICK_HALF_WIDTH + 1)];
|
|
|
|
result.mV[VX] = (F32)((center_pixel[VGREEN] & 0xf) + (16.f * center_pixel[VRED])) / 4095.f;
|
|
result.mV[VY] = (F32)((center_pixel[VGREEN] >> 4) + (16.f * center_pixel[VBLUE])) / 4095.f;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
} */
|
|
|
|
|
|
//static
|
|
bool LLPickInfo::isFlora(LLViewerObject* object)
|
|
{
|
|
if (!object) return false;
|
|
|
|
LLPCode pcode = object->getPCode();
|
|
|
|
if( (LL_PCODE_LEGACY_GRASS == pcode)
|
|
|| (LL_PCODE_LEGACY_TREE == pcode)
|
|
|| (LL_PCODE_TREE_NEW == pcode))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|