/** * @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 #include #include #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 // 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 > > 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_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 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 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 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 > >::iterator it = sTextureMaskMap.find(tex); if(it != sTextureMaskMap.end()) { std::list >::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 use_rmse_auto_mask("SHUseRMSEAutoMask",false); static const LLCachedControl 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 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 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;iisAvatar()) ++total_avatars; if(obj->isDead()) { ++dead_object_check; if(obj->isAvatar()) ++dead_avatar_check; } } } for(std::set::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::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 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 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 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 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() && gAgent.isInitialized()) { // 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(static_cast(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& 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::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(&LLNotification::getType, "alert")); LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy(&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 "<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(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("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 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)) { if (gSavedSettings.getBOOL("LiruUseAdvancedMenuShortcut")) 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- 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 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(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 > 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 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 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 picks_moving("PicksPerSecondMouseMoving",5.f); static const LLCachedControl 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 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(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 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 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(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::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::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 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 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 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 raw = new LLImageRaw; BOOL success = rawSnapshot(raw, image_width, image_height, (F32)image_width / image_height, show_ui, do_rebuild); if (success) { LLPointer 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 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 = mWindow->getMaximized(); 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 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 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 LLPickInfo::getObject() const { return gObjectList.findObject( mObjectID ); } void LLPickInfo::updateXYCoords() { if (mObjectFace > -1) { const LLTextureEntry* tep = getObject()->getTE(mObjectFace); LLPointer 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; }