summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autobuild.xml76
-rw-r--r--indra/cmake/CMakeLists.txt2
-rw-r--r--indra/cmake/LLConvexDecomposition.cmake12
-rw-r--r--indra/cmake/LLPhysicsExtensions.cmake29
-rw-r--r--indra/llcommon/llpointer.h6
-rw-r--r--indra/llmath/llmatrix3a.inl1
-rw-r--r--indra/llmath/m3math.h2
-rw-r--r--indra/llmath/v3color.h2
-rw-r--r--indra/llmath/v3dmath.h2
-rw-r--r--indra/llmath/v3math.h2
-rw-r--r--indra/llmath/v4color.h2
-rw-r--r--indra/llmath/v4math.h2
-rw-r--r--indra/llprimitive/llmodel.cpp2
-rw-r--r--indra/llrender/CMakeLists.txt2
-rw-r--r--indra/llrender/llrendernavprim.cpp116
-rw-r--r--indra/llrender/llrendernavprim.h56
-rw-r--r--indra/llrender/llvertexbuffer.cpp4613
-rw-r--r--indra/llui/llscrolllistctrl.cpp16
-rw-r--r--indra/llui/llscrolllistctrl.h1
-rw-r--r--indra/newview/CMakeLists.txt25
-rw-r--r--indra/newview/app_settings/commands.xml30
-rw-r--r--indra/newview/llappviewer.cpp4
-rw-r--r--[-rwxr-xr-x]indra/newview/llavatariconctrl.cpp0
-rw-r--r--indra/newview/llfilteredpathfindinglinksets.cpp313
-rw-r--r--indra/newview/llfilteredpathfindinglinksets.h111
-rw-r--r--indra/newview/llfloaterpathfindingcharacters.cpp491
-rw-r--r--indra/newview/llfloaterpathfindingcharacters.h124
-rw-r--r--indra/newview/llfloaterpathfindinglinksets.cpp825
-rw-r--r--indra/newview/llfloaterpathfindinglinksets.h147
-rw-r--r--indra/newview/llfloaterpathfindingsetup.cpp653
-rw-r--r--indra/newview/llfloaterpathfindingsetup.h151
-rw-r--r--indra/newview/llmeshrepository.h2
-rw-r--r--indra/newview/llnavmeshstation.cpp105
-rw-r--r--indra/newview/llnavmeshstation.h101
-rw-r--r--indra/newview/llpanelvolume.cpp24
-rw-r--r--indra/newview/llpanelvolume.h5
-rw-r--r--indra/newview/llpathfindingcharacter.cpp98
-rw-r--r--indra/newview/llpathfindingcharacter.h63
-rw-r--r--indra/newview/llpathfindinglinkset.cpp356
-rw-r--r--indra/newview/llpathfindinglinkset.h108
-rw-r--r--indra/newview/llspatialpartition.cpp9592
-rw-r--r--indra/newview/llsurface.cpp13
-rw-r--r--indra/newview/llsurface.h1
-rw-r--r--indra/newview/llviewerdisplay.cpp40
-rw-r--r--indra/newview/llviewerfloaterreg.cpp6
-rw-r--r--indra/newview/llviewermenufile.cpp2544
-rw-r--r--indra/newview/llviewerregion.cpp11
-rw-r--r--indra/newview/llviewerregion.h1
-rw-r--r--indra/newview/llviewerwindow.cpp26
-rw-r--r--indra/newview/pipeline.cpp16
-rw-r--r--indra/newview/skins/default/textures/textures.xml3
-rw-r--r--indra/newview/skins/default/xui/en/floater_pathfinding_characters.xml173
-rw-r--r--indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml419
-rw-r--r--indra/newview/skins/default/xui/en/floater_pathfinding_setup.xml466
-rw-r--r--indra/newview/skins/default/xui/en/floater_tools.xml40
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml40
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml6
-rwxr-xr-xpackage_physicsextensions.sh46
-rw-r--r--package_physicsextensions.xml302
59 files changed, 13944 insertions, 8481 deletions
diff --git a/autobuild.xml b/autobuild.xml
index 9914be6867..8dcd6e8e7d 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -1095,84 +1095,24 @@
</map>
</map>
</map>
- <key>llconvexdecomposition</key>
+ <key>llphysicsextensions</key>
<map>
<key>license</key>
- <string>havok</string>
+ <string>llphysicsextensions</string>
<key>license_file</key>
- <string>on_file</string>
+ <string>LICENSES/llphysicsextensions.txt</string>
<key>name</key>
- <string>llconvexdecomposition</string>
+ <string>llphysicsextensions</string>
<key>platforms</key>
<map>
- <key>darwin</key>
- <map>
- <key>archive</key>
- <map>
- <key>hash</key>
- <string>362654a472ef7368d4c803ae3fb89d95</string>
- <key>url</key>
- <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-llconvexdecomposition/rev/238959/arch/Darwin/installer/llconvexdecomposition-0.1-darwin-20110819.tar.bz2</string>
- </map>
- <key>name</key>
- <string>darwin</string>
- </map>
- <key>linux</key>
- <map>
- <key>archive</key>
- <map>
- <key>hash</key>
- <string>c7801d899daec5338fbe95053255b7e7</string>
- <key>url</key>
- <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-llconvexdecomposition/rev/238959/arch/Linux/installer/llconvexdecomposition-0.1-linux-20110819.tar.bz2</string>
- </map>
- <key>name</key>
- <string>linux</string>
- </map>
- <key>windows</key>
- <map>
- <key>archive</key>
- <map>
- <key>hash</key>
- <string>6ecf2f85f03c5ae87fe45769566a5660</string>
- <key>url</key>
- <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-llconvexdecomposition/rev/238959/arch/CYGWIN/installer/llconvexdecomposition-0.1-windows-20110819.tar.bz2</string>
- </map>
- <key>name</key>
- <string>windows</string>
- </map>
- </map>
- </map>
- <key>llconvexdecompositionstub</key>
- <map>
- <key>license</key>
- <string>lgpl</string>
- <key>license_file</key>
- <string>LICENSES/LLConvexDecompositionStubLicense.txt</string>
- <key>name</key>
- <string>llconvexdecompositionstub</string>
- <key>platforms</key>
- <map>
- <key>darwin</key>
- <map>
- <key>archive</key>
- <map>
- <key>hash</key>
- <string>a5f53e09f67271fd50f1131ffdda9d27</string>
- <key>url</key>
- <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llconvexdecompositionstub/rev/238958/arch/Darwin/installer/llconvexdecompositionstub-0.3-darwin-20110819.tar.bz2</string>
- </map>
- <key>name</key>
- <string>darwin</string>
- </map>
<key>linux</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
- <string>0006a964f1497f55a5f181b7042d2d22</string>
+ <string>06f6a620f3c8348de4f32dd903031ead</string>
<key>url</key>
- <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llconvexdecompositionstub/rev/238958/arch/Linux/installer/llconvexdecompositionstub-0.3-linux-20110819.tar.bz2</string>
+ <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/stinson_llpathinglibrary/rev/248802/arch/Linux/installer/llphysicsextensions-0.1-linux-20120202.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
@@ -1182,9 +1122,9 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>b859e7e3bb03ebb467f0309f46422995</string>
+ <string>46f53bf24bce79c2424d576f7c1dd09c</string>
<key>url</key>
- <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llconvexdecompositionstub/rev/238958/arch/CYGWIN/installer/llconvexdecompositionstub-0.3-windows-20110819.tar.bz2</string>
+ <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/stinson_llpathinglibrary/rev/248892/arch/CYGWIN/installer/llphysicsextensions-0.1-windows-20120203.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 279d577a27..7fccf3327f 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -42,7 +42,6 @@ set(cmake_SOURCE_FILES
LLAudio.cmake
LLCharacter.cmake
LLCommon.cmake
- LLConvexDecomposition.cmake
LLCrashLogger.cmake
LLDatabase.cmake
LLImage.cmake
@@ -53,6 +52,7 @@ set(cmake_SOURCE_FILES
LLMessage.cmake
LLPlugin.cmake
LLPrimitive.cmake
+ LLPhysicsExtensions.cmake
LLRender.cmake
LLScene.cmake
LLTestCommand.cmake
diff --git a/indra/cmake/LLConvexDecomposition.cmake b/indra/cmake/LLConvexDecomposition.cmake
deleted file mode 100644
index 8e44504782..0000000000
--- a/indra/cmake/LLConvexDecomposition.cmake
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- cmake -*-
-include(Prebuilt)
-
-set(LLCONVEXDECOMP_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
-
-if (INSTALL_PROPRIETARY AND NOT STANDALONE)
- use_prebuilt_binary(llconvexdecomposition)
- set(LLCONVEXDECOMP_LIBRARY llconvexdecomposition)
-else (INSTALL_PROPRIETARY AND NOT STANDALONE)
- use_prebuilt_binary(llconvexdecompositionstub)
- set(LLCONVEXDECOMP_LIBRARY llconvexdecompositionstub)
-endif (INSTALL_PROPRIETARY AND NOT STANDALONE)
diff --git a/indra/cmake/LLPhysicsExtensions.cmake b/indra/cmake/LLPhysicsExtensions.cmake
new file mode 100644
index 0000000000..73d5dd59cf
--- /dev/null
+++ b/indra/cmake/LLPhysicsExtensions.cmake
@@ -0,0 +1,29 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+if (INSTALL_PROPRIETARY AND NOT STANDALONE)
+ use_prebuilt_binary(llphysicsextensions)
+ set(LLPHYSICS_EXTENSIONS_LIB_NAME llphysicsextensions)
+else (INSTALL_PROPRIETARY AND NOT STANDALONE)
+ use_prebuilt_binary(llphysicsextensionsstub)
+ set(LLPHYSICS_EXTENSIONS_LIB_NAME llphysicsextensionsstub)
+endif (INSTALL_PROPRIETARY AND NOT STANDALONE)
+
+set(LLPHYSICS_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/lib/include)
+
+set(LLPHYSICS_DEBUG_LIBRARY_PATH ${LIBS_PREBUILT_DIR}/lib/debug)
+set(LLPHYSICS_RELEASE_LIBRARY_PATH ${LIBS_PREBUILT_DIR}/lib/release)
+
+find_library(LL_PHYSICS_DEBUG_LIB ${LLPHYSICS_EXTENSIONS_LIB_NAME} PATHS ${LLPHYSICS_DEBUG_LIBRARY_PATH})
+find_library(LL_PHYSICS_RELEASE_LIB ${LLPHYSICS_EXTENSIONS_LIB_NAME} PATHS ${LLPHYSICS_RELEASE_LIBRARY_PATH})
+
+set(LLPHYSICS_LIBRARIES
+
+ debug ${LL_PHYSICS_DEBUG_LIB}
+ optimized ${LL_PHYSICS_RELEASE_LIB}
+)
+
+if (LINUX)
+ list(INSERT LLPHYSICS_LIBRARIES 0 -Wl,--start-group)
+ list(APPEND LLPHYSICS_LIBRARIES -Wl,--end-group)
+endif (LINUX)
diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h
index affa040602..88c09c8dca 100644
--- a/indra/llcommon/llpointer.h
+++ b/indra/llcommon/llpointer.h
@@ -140,6 +140,10 @@ public:
}
protected:
+#ifdef LL_LIBRARY_INCLUDE
+ void ref();
+ void unref();
+#else
void ref()
{
if (mPointer)
@@ -162,7 +166,7 @@ protected:
}
}
}
-
+#endif
protected:
Type* mPointer;
};
diff --git a/indra/llmath/llmatrix3a.inl b/indra/llmath/llmatrix3a.inl
index 37819fea3c..270f1e905b 100644
--- a/indra/llmath/llmatrix3a.inl
+++ b/indra/llmath/llmatrix3a.inl
@@ -66,7 +66,6 @@ inline void LLMatrix3a::setTranspose(const LLMatrix3a& src)
inline const LLVector4a& LLMatrix3a::getColumn(const U32 column) const
{
- llassert( column < 3 );
return mColumns[column];
}
diff --git a/indra/llmath/m3math.h b/indra/llmath/m3math.h
index 2be5452f8d..3970a5de31 100644
--- a/indra/llmath/m3math.h
+++ b/indra/llmath/m3math.h
@@ -27,7 +27,7 @@
#ifndef LL_M3MATH_H
#define LL_M3MATH_H
-#include "llerror.h"
+//#include "llerror.h"
#include "stdtypes.h"
class LLVector4;
diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h
index 56cb2ae73e..4e77d9e5ff 100644
--- a/indra/llmath/v3color.h
+++ b/indra/llmath/v3color.h
@@ -30,7 +30,7 @@
class LLColor4;
class LLVector4;
-#include "llerror.h"
+//#include "llerror.h"
#include "llmath.h"
#include "llsd.h"
diff --git a/indra/llmath/v3dmath.h b/indra/llmath/v3dmath.h
index 578dcdc8ea..fba549a3b7 100644
--- a/indra/llmath/v3dmath.h
+++ b/indra/llmath/v3dmath.h
@@ -27,7 +27,7 @@
#ifndef LL_V3DMATH_H
#define LL_V3DMATH_H
-#include "llerror.h"
+//#include "llerror.h"
#include "v3math.h"
class LLVector3d
diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h
index 0432aeba4c..206a99b567 100644
--- a/indra/llmath/v3math.h
+++ b/indra/llmath/v3math.h
@@ -27,7 +27,7 @@
#ifndef LL_V3MATH_H
#define LL_V3MATH_H
-#include "llerror.h"
+//#include "llerror.h"
#include "llmath.h"
#include "llsd.h"
diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h
index b047f86e6e..6946b570ab 100644
--- a/indra/llmath/v4color.h
+++ b/indra/llmath/v4color.h
@@ -27,7 +27,7 @@
#ifndef LL_V4COLOR_H
#define LL_V4COLOR_H
-#include "llerror.h"
+//#include "llerror.h"
//#include "vmath.h"
#include "llmath.h"
#include "llsd.h"
diff --git a/indra/llmath/v4math.h b/indra/llmath/v4math.h
index 623c8b2003..f999e0533e 100644
--- a/indra/llmath/v4math.h
+++ b/indra/llmath/v4math.h
@@ -27,7 +27,7 @@
#ifndef LL_V4MATH_H
#define LL_V4MATH_H
-#include "llerror.h"
+//#include "llerror.h"
#include "llmath.h"
#include "v3math.h"
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index cb32a510b8..768d3a6ae8 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -28,7 +28,7 @@
#include "llmodel.h"
#include "llmemory.h"
-#include "llconvexdecomposition.h"
+#include "LLConvexDecomposition.h"
#include "llsdserialize.h"
#include "llvector4a.h"
#if LL_MSVC
diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt
index 5c13df9f81..516af93316 100644
--- a/indra/llrender/CMakeLists.txt
+++ b/indra/llrender/CMakeLists.txt
@@ -36,6 +36,7 @@ set(llrender_SOURCE_FILES
llglslshader.cpp
llimagegl.cpp
llpostprocess.cpp
+ llrendernavprim.cpp
llrendersphere.cpp
llshadermgr.cpp
lltexture.cpp
@@ -59,6 +60,7 @@ set(llrender_HEADER_FILES
llimagegl.h
llpostprocess.h
llrender.h
+ llrendernavprim.h
llrendersphere.h
llshadermgr.h
lltexture.h
diff --git a/indra/llrender/llrendernavprim.cpp b/indra/llrender/llrendernavprim.cpp
new file mode 100644
index 0000000000..71e808d04e
--- /dev/null
+++ b/indra/llrender/llrendernavprim.cpp
@@ -0,0 +1,116 @@
+/**
+ * @file LLRenderNavPrim.cpp
+ * @brief Renderable primitives used by the pathing library
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "linden_common.h"
+#include "llrendernavprim.h"
+#include "llerror.h"
+#include "llglheaders.h"
+#include "llvertexbuffer.h"
+#include "llglslshader.h"
+
+//=============================================================================
+LLRenderNavPrim gRenderNav;
+//=============================================================================
+void LLRenderNavPrim::renderSegment( const LLVector3& start, const LLVector3& end, int color, bool overlayMode ) const
+{
+ LLGLSLShader::sNoFixedFunction = false;
+ LLColor4 colorA( color );
+ glLineWidth(1.5f);
+ gGL.color3fv( colorA.mV );
+
+ gGL.begin(LLRender::LINES);
+ {
+ gGL.vertex3fv( start.mV );
+ gGL.vertex3fv( end.mV );
+ }
+ gGL.end();
+
+ gGL.flush();
+ LLGLSLShader::sNoFixedFunction = true;
+ glLineWidth(1.0f);
+}
+//=============================================================================
+void LLRenderNavPrim::renderTri( const LLVector3& a, const LLVector3& b, const LLVector3& c, int color, bool overlayMode ) const
+{
+ glLineWidth(1.5f);
+ if ( overlayMode )
+ {
+ glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+ }
+ else
+ {
+ glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
+ }
+ LLGLDisable cull(GL_CULL_FACE);
+ LLColor4 colorA( color );
+ colorA*=1.25f;
+ gGL.color4fv( colorA.mV );
+ LLGLSLShader::sNoFixedFunction = false;
+ gGL.begin(LLRender::TRIANGLES);
+ {
+ gGL.vertex3fv( a.mV );
+ gGL.vertex3fv( b.mV );
+ gGL.vertex3fv( c.mV );
+ }
+ gGL.end();
+ gGL.flush();
+ glLineWidth(1.0f);
+ glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
+ LLGLSLShader::sNoFixedFunction = true;
+}
+//=============================================================================
+void LLRenderNavPrim::renderNavMeshVB( LLVertexBuffer* pVBO, int vertCnt )
+{
+ LLGLSUIDefault gls_ui;
+ glEnable( GL_DEPTH_TEST );
+ LLGLEnable cull( GL_CULL_FACE );
+ glLineWidth(1.5f);
+ LLGLSLShader::sNoFixedFunction = false;
+ //pass 1 filled
+ pVBO->setBuffer( LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_NORMAL );
+ pVBO->drawArrays( LLRender::TRIANGLES, 0, vertCnt );
+ glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+ //pass 2 outlined
+ pVBO->drawArrays( LLRender::TRIANGLES, 0, vertCnt );
+ LLGLSLShader::sNoFixedFunction = true;
+ glLineWidth(1.0f);
+ glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
+}
+//=============================================================================
+void LLRenderNavPrim::renderStar( const LLVector3& center, const float scale, int color ) const
+{
+ for (int k=0; k<3; k++)
+ {
+ LLVector3 star, pt1, pt2;
+ star = LLVector3( 0.0f,0.0f,0.0f);
+ star[k] = 0.5f;
+ pt1 = center + star;
+ pt2 = center - star;
+ renderSegment( pt1, pt2, color, false );
+ }
+}
+//=============================================================================
diff --git a/indra/llrender/llrendernavprim.h b/indra/llrender/llrendernavprim.h
new file mode 100644
index 0000000000..d88fe656b7
--- /dev/null
+++ b/indra/llrender/llrendernavprim.h
@@ -0,0 +1,56 @@
+/**
+ * @file LLRenderNavPrim.h
+ * @brief
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_RENDER_NAVPRIM_H
+#define LL_RENDER_NAVPRIM_H
+
+#include "llmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "v4color.h"
+#include "llgl.h"
+
+
+class LLRenderNavPrim
+{
+public:
+ //Draw a line
+ void renderSegment( const LLVector3& start, const LLVector3& end, int color, bool overlayMode ) const;
+ //Draw simple tri
+ void renderTri( const LLVector3& a, const LLVector3& b, const LLVector3& c, int color, bool overlayMode ) const;
+ //Draw the contents of vertex buffer
+ void renderNavMeshVB( LLVertexBuffer* pVBO, int vertCnt );
+ //Draw a star
+ void renderStar( const LLVector3& center, const float scale, int color ) const;
+
+private:
+};
+
+extern LLRenderNavPrim gRenderNav;
+
+#endif
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index eb302392bb..79d1beb33a 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -1,2306 +1,2307 @@
-/**
- * @file llvertexbuffer.cpp
- * @brief LLVertexBuffer implementation
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include <boost/static_assert.hpp>
-#include "llsys.h"
-#include "llvertexbuffer.h"
-// #include "llrender.h"
-#include "llglheaders.h"
-#include "llmemtype.h"
-#include "llrender.h"
-#include "llvector4a.h"
-#include "llshadermgr.h"
-#include "llglslshader.h"
-#include "llmemory.h"
-
-//Next Highest Power Of Two
-//helper function, returns first number > v that is a power of 2, or v if v is already a power of 2
-U32 nhpo2(U32 v)
-{
- U32 r = 1;
- while (r < v) {
- r *= 2;
- }
- return r;
-}
-
-
-//============================================================================
-
-//static
-LLVBOPool LLVertexBuffer::sStreamVBOPool;
-LLVBOPool LLVertexBuffer::sDynamicVBOPool;
-LLVBOPool LLVertexBuffer::sStreamIBOPool;
-LLVBOPool LLVertexBuffer::sDynamicIBOPool;
-U32 LLVBOPool::sBytesPooled = 0;
-
-LLPrivateMemoryPool* LLVertexBuffer::sPrivatePoolp = NULL ;
-U32 LLVertexBuffer::sBindCount = 0;
-U32 LLVertexBuffer::sSetCount = 0;
-S32 LLVertexBuffer::sCount = 0;
-S32 LLVertexBuffer::sGLCount = 0;
-S32 LLVertexBuffer::sMappedCount = 0;
-BOOL LLVertexBuffer::sDisableVBOMapping = FALSE ;
-BOOL LLVertexBuffer::sEnableVBOs = TRUE;
-U32 LLVertexBuffer::sGLRenderBuffer = 0;
-U32 LLVertexBuffer::sGLRenderArray = 0;
-U32 LLVertexBuffer::sGLRenderIndices = 0;
-U32 LLVertexBuffer::sLastMask = 0;
-BOOL LLVertexBuffer::sVBOActive = FALSE;
-BOOL LLVertexBuffer::sIBOActive = FALSE;
-U32 LLVertexBuffer::sAllocatedBytes = 0;
-BOOL LLVertexBuffer::sMapped = FALSE;
-BOOL LLVertexBuffer::sUseStreamDraw = TRUE;
-BOOL LLVertexBuffer::sUseVAO = FALSE;
-BOOL LLVertexBuffer::sPreferStreamDraw = FALSE;
-
-const U32 FENCE_WAIT_TIME_NANOSECONDS = 10000; //1 ms
-
-class LLGLSyncFence : public LLGLFence
-{
-public:
-#ifdef GL_ARB_sync
- GLsync mSync;
-#endif
-
- LLGLSyncFence()
- {
-#ifdef GL_ARB_sync
- mSync = 0;
-#endif
- }
-
- virtual ~LLGLSyncFence()
- {
-#ifdef GL_ARB_sync
- if (mSync)
- {
- glDeleteSync(mSync);
- }
-#endif
- }
-
- void placeFence()
- {
-#ifdef GL_ARB_sync
- if (mSync)
- {
- glDeleteSync(mSync);
- }
- mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
-#endif
- }
-
- void wait()
- {
-#ifdef GL_ARB_sync
- if (mSync)
- {
- while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
- { //track the number of times we've waited here
- static S32 waits = 0;
- waits++;
- }
- }
-#endif
- }
-
-
-};
-
-
-//which power of 2 is i?
-//assumes i is a power of 2 > 0
-U32 wpo2(U32 i)
-{
- llassert(i > 0);
- llassert(nhpo2(i) == i);
-
- U32 r = 0;
-
- while (i >>= 1) ++r;
-
- return r;
-}
-
-volatile U8* LLVBOPool::allocate(U32& name, U32 size)
-{
- llassert(nhpo2(size) == size);
-
- U32 i = wpo2(size);
-
- if (mFreeList.size() <= i)
- {
- mFreeList.resize(i+1);
- }
-
- volatile U8* ret = NULL;
-
- if (mFreeList[i].empty())
- {
- //make a new buffer
- glGenBuffersARB(1, &name);
- glBindBufferARB(mType, name);
- LLVertexBuffer::sAllocatedBytes += size;
-
- if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB)
- {
- glBufferDataARB(mType, size, 0, mUsage);
- ret = (U8*) ll_aligned_malloc_16(size);
- }
- else
- { //always use a true hint of static draw when allocating non-client-backed buffers
- glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB);
- }
-
- glBindBufferARB(mType, 0);
- }
- else
- {
- name = mFreeList[i].front().mGLName;
- ret = mFreeList[i].front().mClientData;
-
- sBytesPooled -= size;
-
- mFreeList[i].pop_front();
- }
-
- return ret;
-}
-
-void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
-{
- llassert(nhpo2(size) == size);
-
- U32 i = wpo2(size);
-
- llassert(mFreeList.size() > i);
-
- Record rec;
- rec.mGLName = name;
- rec.mClientData = buffer;
-
- sBytesPooled += size;
-
- if (!LLVertexBuffer::sDisableVBOMapping && mUsage == GL_DYNAMIC_DRAW_ARB)
- {
- glDeleteBuffersARB(1, &rec.mGLName);
- }
- else
- {
- mFreeList[i].push_back(rec);
- }
-}
-
-void LLVBOPool::cleanup()
-{
- U32 size = 1;
-
- for (U32 i = 0; i < mFreeList.size(); ++i)
- {
- record_list_t& l = mFreeList[i];
-
- while (!l.empty())
- {
- Record& r = l.front();
-
- glDeleteBuffersARB(1, &r.mGLName);
-
- if (r.mClientData)
- {
- ll_aligned_free_16((void*) r.mClientData);
- }
-
- l.pop_front();
-
- LLVertexBuffer::sAllocatedBytes -= size;
- sBytesPooled -= size;
- }
-
- size *= 2;
- }
-}
-
-
-//NOTE: each component must be AT LEAST 4 bytes in size to avoid a performance penalty on AMD hardware
-S32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
-{
- sizeof(LLVector4), // TYPE_VERTEX,
- sizeof(LLVector4), // TYPE_NORMAL,
- sizeof(LLVector2), // TYPE_TEXCOORD0,
- sizeof(LLVector2), // TYPE_TEXCOORD1,
- sizeof(LLVector2), // TYPE_TEXCOORD2,
- sizeof(LLVector2), // TYPE_TEXCOORD3,
- sizeof(LLColor4U), // TYPE_COLOR,
- sizeof(LLColor4U), // TYPE_EMISSIVE, only alpha is used currently
- sizeof(LLVector4), // TYPE_BINORMAL,
- sizeof(F32), // TYPE_WEIGHT,
- sizeof(LLVector4), // TYPE_WEIGHT4,
- sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
- sizeof(LLVector4), // TYPE_TEXTURE_INDEX (actually exists as position.w), no extra data, but stride is 16 bytes
-};
-
-U32 LLVertexBuffer::sGLMode[LLRender::NUM_MODES] =
-{
- GL_TRIANGLES,
- GL_TRIANGLE_STRIP,
- GL_TRIANGLE_FAN,
- GL_POINTS,
- GL_LINES,
- GL_LINE_STRIP,
- GL_QUADS,
- GL_LINE_LOOP,
-};
-
-
-//static
-void LLVertexBuffer::setupClientArrays(U32 data_mask)
-{
- if (sLastMask != data_mask)
- {
- BOOL error = FALSE;
-
- if (LLGLSLShader::sNoFixedFunction)
- {
- for (U32 i = 0; i < TYPE_MAX; ++i)
- {
- S32 loc = i;
-
- U32 mask = 1 << i;
-
- if (sLastMask & (1 << i))
- { //was enabled
- if (!(data_mask & mask))
- { //needs to be disabled
- glDisableVertexAttribArrayARB(loc);
- }
- }
- else
- { //was disabled
- if (data_mask & mask)
- { //needs to be enabled
- glEnableVertexAttribArrayARB(loc);
- }
- }
- }
- }
- else
- {
-
- GLenum array[] =
- {
- GL_VERTEX_ARRAY,
- GL_NORMAL_ARRAY,
- GL_TEXTURE_COORD_ARRAY,
- GL_COLOR_ARRAY,
- };
-
- GLenum mask[] =
- {
- MAP_VERTEX,
- MAP_NORMAL,
- MAP_TEXCOORD0,
- MAP_COLOR
- };
-
-
-
- for (U32 i = 0; i < 4; ++i)
- {
- if (sLastMask & mask[i])
- { //was enabled
- if (!(data_mask & mask[i]))
- { //needs to be disabled
- glDisableClientState(array[i]);
- }
- else if (gDebugGL)
- { //needs to be enabled, make sure it was (DEBUG)
- if (!glIsEnabled(array[i]))
- {
- if (gDebugSession)
- {
- error = TRUE;
- gFailLog << "Bad client state! " << array[i] << " disabled." << std::endl;
- }
- else
- {
- llerrs << "Bad client state! " << array[i] << " disabled." << llendl;
- }
- }
- }
- }
- else
- { //was disabled
- if (data_mask & mask[i])
- { //needs to be enabled
- glEnableClientState(array[i]);
- }
- else if (gDebugGL && glIsEnabled(array[i]))
- { //needs to be disabled, make sure it was (DEBUG TEMPORARY)
- if (gDebugSession)
- {
- error = TRUE;
- gFailLog << "Bad client state! " << array[i] << " enabled." << std::endl;
- }
- else
- {
- llerrs << "Bad client state! " << array[i] << " enabled." << llendl;
- }
- }
- }
- }
-
- U32 map_tc[] =
- {
- MAP_TEXCOORD1,
- MAP_TEXCOORD2,
- MAP_TEXCOORD3
- };
-
- for (U32 i = 0; i < 3; i++)
- {
- if (sLastMask & map_tc[i])
- {
- if (!(data_mask & map_tc[i]))
- { //disable
- glClientActiveTextureARB(GL_TEXTURE1_ARB+i);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glClientActiveTextureARB(GL_TEXTURE0_ARB);
- }
- }
- else if (data_mask & map_tc[i])
- {
- glClientActiveTextureARB(GL_TEXTURE1_ARB+i);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glClientActiveTextureARB(GL_TEXTURE0_ARB);
- }
- }
-
- if (sLastMask & MAP_BINORMAL)
- {
- if (!(data_mask & MAP_BINORMAL))
- {
- glClientActiveTextureARB(GL_TEXTURE2_ARB);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glClientActiveTextureARB(GL_TEXTURE0_ARB);
- }
- }
- else if (data_mask & MAP_BINORMAL)
- {
- glClientActiveTextureARB(GL_TEXTURE2_ARB);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glClientActiveTextureARB(GL_TEXTURE0_ARB);
- }
- }
-
- sLastMask = data_mask;
- }
-}
-
-//static
-void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm)
-{
- llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
- gGL.syncMatrices();
-
- U32 count = pos.size();
- llassert_always(norm.size() >= pos.size());
- llassert_always(count > 0) ;
-
- unbind();
-
- setupClientArrays(MAP_VERTEX | MAP_NORMAL);
-
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
-
- if (shader)
- {
- S32 loc = LLVertexBuffer::TYPE_VERTEX;
- if (loc > -1)
- {
- glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, pos[0].mV);
- }
- loc = LLVertexBuffer::TYPE_NORMAL;
- if (loc > -1)
- {
- glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, norm[0].mV);
- }
- }
- else
- {
- glVertexPointer(3, GL_FLOAT, 0, pos[0].mV);
- glNormalPointer(GL_FLOAT, 0, norm[0].mV);
- }
-
- glDrawArrays(sGLMode[mode], 0, count);
-}
-
-//static
-void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp)
-{
- llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
-
- gGL.syncMatrices();
-
- U32 mask = LLVertexBuffer::MAP_VERTEX;
- if (tc)
- {
- mask = mask | LLVertexBuffer::MAP_TEXCOORD0;
- }
-
- unbind();
-
- setupClientArrays(mask);
-
- if (LLGLSLShader::sNoFixedFunction)
- {
- S32 loc = LLVertexBuffer::TYPE_VERTEX;
- glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 16, pos);
-
- if (tc)
- {
- loc = LLVertexBuffer::TYPE_TEXCOORD0;
- glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, 0, tc);
- }
- }
- else
- {
- glTexCoordPointer(2, GL_FLOAT, 0, tc);
- glVertexPointer(3, GL_FLOAT, 16, pos);
- }
-
- glDrawElements(sGLMode[mode], num_indices, GL_UNSIGNED_SHORT, indicesp);
-}
-
-void LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_offset) const
-{
- if (start >= (U32) mNumVerts ||
- end >= (U32) mNumVerts)
- {
- llerrs << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mNumVerts << llendl;
- }
-
- llassert(mNumIndices >= 0);
-
- if (indices_offset >= (U32) mNumIndices ||
- indices_offset + count > (U32) mNumIndices)
- {
- llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
- }
-
- if (gDebugGL && !useVBOs())
- {
- U16* idx = ((U16*) getIndicesPointer())+indices_offset;
- for (U32 i = 0; i < count; ++i)
- {
- if (idx[i] < start || idx[i] > end)
- {
- llerrs << "Index out of range: " << idx[i] << " not in [" << start << ", " << end << "]" << llendl;
- }
- }
-
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
-
- if (shader && shader->mFeatures.mIndexedTextureChannels > 1)
- {
- LLStrider<LLVector4a> v;
- //hack to get non-const reference
- LLVertexBuffer* vb = (LLVertexBuffer*) this;
- vb->getVertexStrider(v);
-
- for (U32 i = start; i < end; i++)
- {
- S32 idx = (S32) (v[i][3]+0.25f);
- if (idx < 0 || idx >= shader->mFeatures.mIndexedTextureChannels)
- {
- llerrs << "Bad texture index found in vertex data stream." << llendl;
- }
- }
- }
- }
-}
-
-void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const
-{
- validateRange(start, end, count, indices_offset);
- mMappable = FALSE;
- gGL.syncMatrices();
-
- llassert(mNumVerts >= 0);
- llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
-
- if (mGLArray)
- {
- if (mGLArray != sGLRenderArray)
- {
- llerrs << "Wrong vertex array bound." << llendl;
- }
- }
- else
- {
- if (mGLIndices != sGLRenderIndices)
- {
- llerrs << "Wrong index buffer bound." << llendl;
- }
-
- if (mGLBuffer != sGLRenderBuffer)
- {
- llerrs << "Wrong vertex buffer bound." << llendl;
- }
- }
-
- if (gDebugGL && !mGLArray && useVBOs())
- {
- GLint elem = 0;
- glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &elem);
-
- if (elem != mGLIndices)
- {
- llerrs << "Wrong index buffer bound!" << llendl;
- }
- }
-
- if (mode >= LLRender::NUM_MODES)
- {
- llerrs << "Invalid draw mode: " << mode << llendl;
- return;
- }
-
- U16* idx = ((U16*) getIndicesPointer())+indices_offset;
-
- stop_glerror();
- glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT,
- idx);
- stop_glerror();
- placeFence();
-}
-
-void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
-{
- llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
- mMappable = FALSE;
- gGL.syncMatrices();
-
- llassert(mNumIndices >= 0);
- if (indices_offset >= (U32) mNumIndices ||
- indices_offset + count > (U32) mNumIndices)
- {
- llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
- }
-
- if (mGLArray)
- {
- if (mGLArray != sGLRenderArray)
- {
- llerrs << "Wrong vertex array bound." << llendl;
- }
- }
- else
- {
- if (mGLIndices != sGLRenderIndices)
- {
- llerrs << "Wrong index buffer bound." << llendl;
- }
-
- if (mGLBuffer != sGLRenderBuffer)
- {
- llerrs << "Wrong vertex buffer bound." << llendl;
- }
- }
-
- if (mode >= LLRender::NUM_MODES)
- {
- llerrs << "Invalid draw mode: " << mode << llendl;
- return;
- }
-
- stop_glerror();
- glDrawElements(sGLMode[mode], count, GL_UNSIGNED_SHORT,
- ((U16*) getIndicesPointer()) + indices_offset);
- stop_glerror();
- placeFence();
-}
-
-void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
-{
- llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
- mMappable = FALSE;
- gGL.syncMatrices();
-
- llassert(mNumVerts >= 0);
- if (first >= (U32) mNumVerts ||
- first + count > (U32) mNumVerts)
- {
- llerrs << "Bad vertex buffer draw range: [" << first << ", " << first+count << "]" << llendl;
- }
-
- if (mGLArray)
- {
- if (mGLArray != sGLRenderArray)
- {
- llerrs << "Wrong vertex array bound." << llendl;
- }
- }
- else
- {
- if (mGLBuffer != sGLRenderBuffer || useVBOs() != sVBOActive)
- {
- llerrs << "Wrong vertex buffer bound." << llendl;
- }
- }
-
- if (mode >= LLRender::NUM_MODES)
- {
- llerrs << "Invalid draw mode: " << mode << llendl;
- return;
- }
-
- stop_glerror();
- glDrawArrays(sGLMode[mode], first, count);
- stop_glerror();
- placeFence();
-}
-
-//static
-void LLVertexBuffer::initClass(bool use_vbo, bool no_vbo_mapping)
-{
- sEnableVBOs = use_vbo && gGLManager.mHasVertexBufferObject ;
- sDisableVBOMapping = sEnableVBOs && no_vbo_mapping ;
-
- if(!sPrivatePoolp)
- {
- sPrivatePoolp = LLPrivateMemoryPoolManager::getInstance()->newPool(LLPrivateMemoryPool::STATIC) ;
- }
-
- sStreamVBOPool.mType = GL_ARRAY_BUFFER_ARB;
- sStreamVBOPool.mUsage= GL_STREAM_DRAW_ARB;
- sStreamIBOPool.mType = GL_ELEMENT_ARRAY_BUFFER_ARB;
- sStreamIBOPool.mUsage= GL_STREAM_DRAW_ARB;
-
- sDynamicVBOPool.mType = GL_ARRAY_BUFFER_ARB;
- sDynamicVBOPool.mUsage= GL_DYNAMIC_DRAW_ARB;
- sDynamicIBOPool.mType = GL_ELEMENT_ARRAY_BUFFER_ARB;
- sDynamicIBOPool.mUsage= GL_DYNAMIC_DRAW_ARB;
-}
-
-//static
-void LLVertexBuffer::unbind()
-{
- if (sGLRenderArray)
- {
-#if GL_ARB_vertex_array_object
- glBindVertexArray(0);
-#endif
- sGLRenderArray = 0;
- sGLRenderIndices = 0;
- sIBOActive = FALSE;
- }
-
- if (sVBOActive)
- {
- glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
- sVBOActive = FALSE;
- }
- if (sIBOActive)
- {
- glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
- sIBOActive = FALSE;
- }
-
- sGLRenderBuffer = 0;
- sGLRenderIndices = 0;
-
- setupClientArrays(0);
-}
-
-//static
-void LLVertexBuffer::cleanupClass()
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_CLEANUP_CLASS);
- unbind();
-
- sStreamIBOPool.cleanup();
- sDynamicIBOPool.cleanup();
- sStreamVBOPool.cleanup();
- sDynamicVBOPool.cleanup();
-
- if(sPrivatePoolp)
- {
- LLPrivateMemoryPoolManager::getInstance()->deletePool(sPrivatePoolp) ;
- sPrivatePoolp = NULL ;
- }
-}
-
-//----------------------------------------------------------------------------
-
-LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
- LLRefCount(),
-
- mNumVerts(0),
- mNumIndices(0),
- mUsage(usage),
- mGLBuffer(0),
- mGLArray(0),
- mGLIndices(0),
- mMappedData(NULL),
- mMappedIndexData(NULL),
- mVertexLocked(FALSE),
- mIndexLocked(FALSE),
- mFinal(FALSE),
- mEmpty(TRUE),
- mFence(NULL)
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_CONSTRUCTOR);
- mFence = NULL;
- if (!sEnableVBOs)
- {
- mUsage = 0 ;
- }
-
- if (mUsage == GL_STREAM_DRAW_ARB && !sUseStreamDraw)
- {
- mUsage = 0;
- }
-
- if (mUsage == GL_DYNAMIC_DRAW_ARB && sPreferStreamDraw)
- {
- mUsage = GL_STREAM_DRAW_ARB;
- }
-
- if (mUsage == 0 && LLRender::sGLCoreProfile)
- { //MUST use VBOs for all rendering
- mUsage = GL_STREAM_DRAW_ARB;
- }
-
- if (mUsage && mUsage != GL_STREAM_DRAW_ARB)
- { //only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default
- if (sDisableVBOMapping)
- { //always use stream draw if VBO mapping is disabled
- mUsage = GL_STREAM_DRAW_ARB;
- }
- else
- {
- mUsage = GL_DYNAMIC_DRAW_ARB;
- }
- }
-
-
- if (mUsage == GL_DYNAMIC_DRAW_ARB && !sDisableVBOMapping)
- {
- mMappable = TRUE;
- }
- else
- {
- mMappable = FALSE;
- }
-
- //zero out offsets
- for (U32 i = 0; i < TYPE_MAX; i++)
- {
- mOffsets[i] = 0;
- }
-
- mTypeMask = typemask;
- mSize = 0;
- mIndicesSize = 0;
- mAlignedOffset = 0;
- mAlignedIndexOffset = 0;
-
- sCount++;
-}
-
-//static
-S32 LLVertexBuffer::calcOffsets(const U32& typemask, S32* offsets, S32 num_vertices)
-{
- S32 offset = 0;
- for (S32 i=0; i<TYPE_TEXTURE_INDEX; i++)
- {
- U32 mask = 1<<i;
- if (typemask & mask)
- {
- if (offsets && LLVertexBuffer::sTypeSize[i])
- {
- offsets[i] = offset;
- offset += LLVertexBuffer::sTypeSize[i]*num_vertices;
- offset = (offset + 0xF) & ~0xF;
- }
- }
- }
-
- offsets[TYPE_TEXTURE_INDEX] = offsets[TYPE_VERTEX] + 12;
-
- return offset+16;
-}
-
-//static
-S32 LLVertexBuffer::calcVertexSize(const U32& typemask)
-{
- S32 size = 0;
- for (S32 i = 0; i < TYPE_TEXTURE_INDEX; i++)
- {
- U32 mask = 1<<i;
- if (typemask & mask)
- {
- size += LLVertexBuffer::sTypeSize[i];
- }
- }
-
- return size;
-}
-
-S32 LLVertexBuffer::getSize() const
-{
- return mSize;
-}
-
-// protected, use unref()
-//virtual
-LLVertexBuffer::~LLVertexBuffer()
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTRUCTOR);
- destroyGLBuffer();
- destroyGLIndices();
-
- if (mGLArray)
- {
-#if GL_ARB_vertex_array_object
- glDeleteVertexArrays(1, &mGLArray);
-#endif
- }
-
- sCount--;
-
- if (mFence)
- {
- delete mFence;
- }
-
- mFence = NULL;
-
- llassert_always(!mMappedData && !mMappedIndexData) ;
-};
-
-void LLVertexBuffer::placeFence() const
-{
- /*if (!mFence && useVBOs())
- {
- if (gGLManager.mHasSync)
- {
- mFence = new LLGLSyncFence();
- }
- }
-
- if (mFence)
- {
- mFence->placeFence();
- }*/
-}
-
-void LLVertexBuffer::waitFence() const
-{
- /*if (mFence)
- {
- mFence->wait();
- }*/
-}
-
-//----------------------------------------------------------------------------
-
-void LLVertexBuffer::genBuffer(U32 size)
-{
- mSize = nhpo2(size);
-
- if (mUsage == GL_STREAM_DRAW_ARB)
- {
- mMappedData = sStreamVBOPool.allocate(mGLBuffer, mSize);
- }
- else
- {
- mMappedData = sDynamicVBOPool.allocate(mGLBuffer, mSize);
- }
-
- sGLCount++;
-}
-
-void LLVertexBuffer::genIndices(U32 size)
-{
- mIndicesSize = nhpo2(size);
-
- if (mUsage == GL_STREAM_DRAW_ARB)
- {
- mMappedIndexData = sStreamIBOPool.allocate(mGLIndices, mIndicesSize);
- }
- else
- {
- mMappedIndexData = sDynamicIBOPool.allocate(mGLIndices, mIndicesSize);
- }
-
- sGLCount++;
-}
-
-void LLVertexBuffer::releaseBuffer()
-{
- if (mUsage == GL_STREAM_DRAW_ARB)
- {
- sStreamVBOPool.release(mGLBuffer, mMappedData, mSize);
- }
- else
- {
- sDynamicVBOPool.release(mGLBuffer, mMappedData, mSize);
- }
-
- mGLBuffer = 0;
- mMappedData = NULL;
-
- sGLCount--;
-}
-
-void LLVertexBuffer::releaseIndices()
-{
- if (mUsage == GL_STREAM_DRAW_ARB)
- {
- sStreamIBOPool.release(mGLIndices, mMappedIndexData, mIndicesSize);
- }
- else
- {
- sDynamicIBOPool.release(mGLIndices, mMappedIndexData, mIndicesSize);
- }
-
- mGLIndices = 0;
- mMappedIndexData = NULL;
-
- sGLCount--;
-}
-
-void LLVertexBuffer::createGLBuffer(U32 size)
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_VERTICES);
-
- if (mGLBuffer)
- {
- destroyGLBuffer();
- }
-
- if (size == 0)
- {
- return;
- }
-
- mEmpty = TRUE;
-
- if (useVBOs())
- {
- genBuffer(size);
- }
- else
- {
- static int gl_buffer_idx = 0;
- mGLBuffer = ++gl_buffer_idx;
- mMappedData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
- mSize = size;
- }
-}
-
-void LLVertexBuffer::createGLIndices(U32 size)
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_INDICES);
-
- if (mGLIndices)
- {
- destroyGLIndices();
- }
-
- if (size == 0)
- {
- return;
- }
-
- mEmpty = TRUE;
-
- //pad by 16 bytes for aligned copies
- size += 16;
-
- if (useVBOs())
- {
- //pad by another 16 bytes for VBO pointer adjustment
- size += 16;
- genIndices(size);
- }
- else
- {
- mMappedIndexData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
- static int gl_buffer_idx = 0;
- mGLIndices = ++gl_buffer_idx;
- mIndicesSize = size;
- }
-}
-
-void LLVertexBuffer::destroyGLBuffer()
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_BUFFER);
- if (mGLBuffer)
- {
- if (useVBOs())
- {
- releaseBuffer();
- }
- else
- {
- FREE_MEM(sPrivatePoolp, (void*) mMappedData) ;
- mMappedData = NULL;
- mEmpty = TRUE;
- }
- }
-
- mGLBuffer = 0;
- //unbind();
-}
-
-void LLVertexBuffer::destroyGLIndices()
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_INDICES);
- if (mGLIndices)
- {
- if (useVBOs())
- {
- releaseIndices();
- }
- else
- {
- FREE_MEM(sPrivatePoolp, (void*) mMappedIndexData) ;
- mMappedIndexData = NULL;
- mEmpty = TRUE;
- }
- }
-
- mGLIndices = 0;
- //unbind();
-}
-
-void LLVertexBuffer::updateNumVerts(S32 nverts)
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_VERTS);
-
- llassert(nverts >= 0);
-
- if (nverts >= 65535)
- {
- llwarns << "Vertex buffer overflow!" << llendl;
- nverts = 65535;
- }
-
- U32 needed_size = calcOffsets(mTypeMask, mOffsets, nverts);
-
- if (needed_size > mSize || needed_size <= mSize/2)
- {
- createGLBuffer(needed_size);
- }
-
- mNumVerts = nverts;
-}
-
-void LLVertexBuffer::updateNumIndices(S32 nindices)
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_INDICES);
-
- llassert(nindices >= 0);
-
- U32 needed_size = sizeof(U16) * nindices;
-
- if (needed_size > mIndicesSize || needed_size <= mIndicesSize/2)
- {
- createGLIndices(needed_size);
- }
-
- mNumIndices = nindices;
-}
-
-void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER);
-
- stop_glerror();
-
- if (nverts < 0 || nindices < 0 ||
- nverts > 65536)
- {
- llerrs << "Bad vertex buffer allocation: " << nverts << " : " << nindices << llendl;
- }
-
- updateNumVerts(nverts);
- updateNumIndices(nindices);
-
- if (create && (nverts || nindices))
- {
- //actually allocate space for the vertex buffer if using VBO mapping
- flush();
-
- if (gGLManager.mHasVertexArrayObject && useVBOs() && (LLRender::sGLCoreProfile || sUseVAO))
- {
-#if GL_ARB_vertex_array_object
- glGenVertexArrays(1, &mGLArray);
-#endif
- setupVertexArray();
- }
- }
-}
-
-static LLFastTimer::DeclareTimer FTM_SETUP_VERTEX_ARRAY("Setup VAO");
-
-void LLVertexBuffer::setupVertexArray()
-{
- if (!mGLArray)
- {
- return;
- }
-
- LLFastTimer t(FTM_SETUP_VERTEX_ARRAY);
-#if GL_ARB_vertex_array_object
- glBindVertexArray(mGLArray);
-#endif
- sGLRenderArray = mGLArray;
-
- U32 attrib_size[] =
- {
- 3, //TYPE_VERTEX,
- 3, //TYPE_NORMAL,
- 2, //TYPE_TEXCOORD0,
- 2, //TYPE_TEXCOORD1,
- 2, //TYPE_TEXCOORD2,
- 2, //TYPE_TEXCOORD3,
- 4, //TYPE_COLOR,
- 4, //TYPE_EMISSIVE,
- 3, //TYPE_BINORMAL,
- 1, //TYPE_WEIGHT,
- 4, //TYPE_WEIGHT4,
- 4, //TYPE_CLOTHWEIGHT,
- 1, //TYPE_TEXTURE_INDEX
- };
-
- U32 attrib_type[] =
- {
- GL_FLOAT, //TYPE_VERTEX,
- GL_FLOAT, //TYPE_NORMAL,
- GL_FLOAT, //TYPE_TEXCOORD0,
- GL_FLOAT, //TYPE_TEXCOORD1,
- GL_FLOAT, //TYPE_TEXCOORD2,
- GL_FLOAT, //TYPE_TEXCOORD3,
- GL_UNSIGNED_BYTE, //TYPE_COLOR,
- GL_UNSIGNED_BYTE, //TYPE_EMISSIVE,
- GL_FLOAT, //TYPE_BINORMAL,
- GL_FLOAT, //TYPE_WEIGHT,
- GL_FLOAT, //TYPE_WEIGHT4,
- GL_FLOAT, //TYPE_CLOTHWEIGHT,
- GL_FLOAT, //TYPE_TEXTURE_INDEX
- };
-
- U32 attrib_normalized[] =
- {
- GL_FALSE, //TYPE_VERTEX,
- GL_FALSE, //TYPE_NORMAL,
- GL_FALSE, //TYPE_TEXCOORD0,
- GL_FALSE, //TYPE_TEXCOORD1,
- GL_FALSE, //TYPE_TEXCOORD2,
- GL_FALSE, //TYPE_TEXCOORD3,
- GL_TRUE, //TYPE_COLOR,
- GL_TRUE, //TYPE_EMISSIVE,
- GL_FALSE, //TYPE_BINORMAL,
- GL_FALSE, //TYPE_WEIGHT,
- GL_FALSE, //TYPE_WEIGHT4,
- GL_FALSE, //TYPE_CLOTHWEIGHT,
- GL_FALSE, //TYPE_TEXTURE_INDEX
- };
-
- bindGLBuffer(true);
- bindGLIndices(true);
-
- for (U32 i = 0; i < TYPE_MAX; ++i)
- {
- if (mTypeMask & (1 << i))
- {
- glEnableVertexAttribArrayARB(i);
- glVertexAttribPointerARB(i, attrib_size[i], attrib_type[i], attrib_normalized[i], sTypeSize[i], (void*) mOffsets[i]);
- }
- else
- {
- glDisableVertexAttribArrayARB(i);
- }
- }
-
- //draw a dummy triangle to set index array pointer
- //glDrawElements(GL_TRIANGLES, 0, GL_UNSIGNED_SHORT, NULL);
-
- unbind();
-}
-
-void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
-{
- llassert(newnverts >= 0);
- llassert(newnindices >= 0);
-
- LLMemType mt2(LLMemType::MTYPE_VERTEX_RESIZE_BUFFER);
-
- updateNumVerts(newnverts);
- updateNumIndices(newnindices);
-
- if (useVBOs())
- {
- flush();
-
- if (mGLArray)
- { //if size changed, offsets changed
- setupVertexArray();
- }
- }
-}
-
-BOOL LLVertexBuffer::useVBOs() const
-{
- //it's generally ineffective to use VBO for things that are streaming on apple
-
- if (!mUsage)
- {
- return FALSE;
- }
-
- return TRUE;
-}
-
-//----------------------------------------------------------------------------
-
-bool expand_region(LLVertexBuffer::MappedRegion& region, S32 index, S32 count)
-{
- S32 end = index+count;
- S32 region_end = region.mIndex+region.mCount;
-
- if (end < region.mIndex ||
- index > region_end)
- { //gap exists, do not merge
- return false;
- }
-
- S32 new_end = llmax(end, region_end);
- S32 new_index = llmin(index, region.mIndex);
- region.mIndex = new_index;
- region.mCount = new_end-new_index;
- return true;
-}
-
-static LLFastTimer::DeclareTimer FTM_VBO_MAP_BUFFER_RANGE("VBO Map Range");
-static LLFastTimer::DeclareTimer FTM_VBO_MAP_BUFFER("VBO Map");
-
-// Map for data access
-volatile U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range)
-{
- bindGLBuffer(true);
- LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
- if (mFinal)
- {
- llerrs << "LLVertexBuffer::mapVeretxBuffer() called on a finalized buffer." << llendl;
- }
- if (!useVBOs() && !mMappedData && !mMappedIndexData)
- {
- llerrs << "LLVertexBuffer::mapVertexBuffer() called on unallocated buffer." << llendl;
- }
-
- if (useVBOs())
- {
- if (!mMappable || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
- {
- if (count == -1)
- {
- count = mNumVerts-index;
- }
-
- bool mapped = false;
- //see if range is already mapped
- for (U32 i = 0; i < mMappedVertexRegions.size(); ++i)
- {
- MappedRegion& region = mMappedVertexRegions[i];
- if (region.mType == type)
- {
- if (expand_region(region, index, count))
- {
- mapped = true;
- break;
- }
- }
- }
-
- if (!mapped)
- {
- //not already mapped, map new region
- MappedRegion region(type, mMappable && map_range ? -1 : index, count);
- mMappedVertexRegions.push_back(region);
- }
- }
-
- if (mVertexLocked && map_range)
- {
- llerrs << "Attempted to map a specific range of a buffer that was already mapped." << llendl;
- }
-
- if (!mVertexLocked)
- {
- LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES);
- mVertexLocked = TRUE;
- sMappedCount++;
- stop_glerror();
-
- if(!mMappable)
- {
- map_range = false;
- }
- else
- {
- volatile U8* src = NULL;
- waitFence();
- if (gGLManager.mHasMapBufferRange)
- {
- if (map_range)
- {
-#ifdef GL_ARB_map_buffer_range
- LLFastTimer t(FTM_VBO_MAP_BUFFER_RANGE);
- S32 offset = mOffsets[type] + sTypeSize[type]*index;
- S32 length = (sTypeSize[type]*count+0xF) & ~0xF;
- src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER_ARB, offset, length,
- GL_MAP_WRITE_BIT |
- GL_MAP_FLUSH_EXPLICIT_BIT |
- GL_MAP_INVALIDATE_RANGE_BIT);
-#endif
- }
- else
- {
-#ifdef GL_ARB_map_buffer_range
-
- if (gDebugGL)
- {
- GLint size = 0;
- glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size);
-
- if (size < mSize)
- {
- llerrs << "Invalid buffer size." << llendl;
- }
- }
-
- LLFastTimer t(FTM_VBO_MAP_BUFFER);
- src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER_ARB, 0, mSize,
- GL_MAP_WRITE_BIT |
- GL_MAP_FLUSH_EXPLICIT_BIT);
-#endif
- }
- }
- else if (gGLManager.mHasFlushBufferRange)
- {
- if (map_range)
- {
- glBufferParameteriAPPLE(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE);
- glBufferParameteriAPPLE(GL_ARRAY_BUFFER_ARB, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
- src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
- }
- else
- {
- src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
- }
- }
- else
- {
- map_range = false;
- src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
- }
-
- llassert(src != NULL);
-
- mMappedData = LL_NEXT_ALIGNED_ADDRESS<volatile U8>(src);
- mAlignedOffset = mMappedData - src;
-
- stop_glerror();
- }
-
- if (!mMappedData)
- {
- log_glerror();
-
- //check the availability of memory
- LLMemory::logMemoryInfo(TRUE) ;
-
- if(mMappable)
- {
- //--------------------
- //print out more debug info before crash
- llinfos << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << llendl ;
- GLint size ;
- glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size) ;
- llinfos << "GL_ARRAY_BUFFER_ARB size is " << size << llendl ;
- //--------------------
-
- GLint buff;
- glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff);
- if ((GLuint)buff != mGLBuffer)
- {
- llerrs << "Invalid GL vertex buffer bound: " << buff << llendl;
- }
-
-
- llerrs << "glMapBuffer returned NULL (no vertex data)" << llendl;
- }
- else
- {
- llerrs << "memory allocation for vertex data failed." << llendl ;
- }
- }
- }
- }
- else
- {
- map_range = false;
- }
-
- if (map_range && gGLManager.mHasMapBufferRange && mMappable)
- {
- return mMappedData;
- }
- else
- {
- return mMappedData+mOffsets[type]+sTypeSize[type]*index;
- }
-}
-
-
-static LLFastTimer::DeclareTimer FTM_VBO_MAP_INDEX_RANGE("IBO Map Range");
-static LLFastTimer::DeclareTimer FTM_VBO_MAP_INDEX("IBO Map");
-
-volatile U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range)
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
- bindGLIndices(true);
- if (mFinal)
- {
- llerrs << "LLVertexBuffer::mapIndexBuffer() called on a finalized buffer." << llendl;
- }
- if (!useVBOs() && !mMappedData && !mMappedIndexData)
- {
- llerrs << "LLVertexBuffer::mapIndexBuffer() called on unallocated buffer." << llendl;
- }
-
- if (useVBOs())
- {
- if (!mMappable || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
- {
- if (count == -1)
- {
- count = mNumIndices-index;
- }
-
- bool mapped = false;
- //see if range is already mapped
- for (U32 i = 0; i < mMappedIndexRegions.size(); ++i)
- {
- MappedRegion& region = mMappedIndexRegions[i];
- if (expand_region(region, index, count))
- {
- mapped = true;
- break;
- }
- }
-
- if (!mapped)
- {
- //not already mapped, map new region
- MappedRegion region(TYPE_INDEX, mMappable && map_range ? -1 : index, count);
- mMappedIndexRegions.push_back(region);
- }
- }
-
- if (mIndexLocked && map_range)
- {
- llerrs << "Attempted to map a specific range of a buffer that was already mapped." << llendl;
- }
-
- if (!mIndexLocked)
- {
- LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES);
-
- mIndexLocked = TRUE;
- sMappedCount++;
- stop_glerror();
-
- if (gDebugGL && useVBOs())
- {
- GLint elem = 0;
- glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &elem);
-
- if (elem != mGLIndices)
- {
- llerrs << "Wrong index buffer bound!" << llendl;
- }
- }
-
- if(!mMappable)
- {
- map_range = false;
- }
- else
- {
- volatile U8* src = NULL;
- waitFence();
- if (gGLManager.mHasMapBufferRange)
- {
- if (map_range)
- {
-#ifdef GL_ARB_map_buffer_range
- LLFastTimer t(FTM_VBO_MAP_INDEX_RANGE);
- S32 offset = sizeof(U16)*index;
- S32 length = sizeof(U16)*count;
- src = (U8*) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length,
- GL_MAP_WRITE_BIT |
- GL_MAP_FLUSH_EXPLICIT_BIT |
- GL_MAP_INVALIDATE_RANGE_BIT);
-#endif
- }
- else
- {
-#ifdef GL_ARB_map_buffer_range
- LLFastTimer t(FTM_VBO_MAP_INDEX);
- src = (U8*) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, sizeof(U16)*mNumIndices,
- GL_MAP_WRITE_BIT |
- GL_MAP_FLUSH_EXPLICIT_BIT);
-#endif
- }
- }
- else if (gGLManager.mHasFlushBufferRange)
- {
- if (map_range)
- {
- glBufferParameteriAPPLE(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE);
- glBufferParameteriAPPLE(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
- src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
- }
- else
- {
- src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
- }
- }
- else
- {
- LLFastTimer t(FTM_VBO_MAP_INDEX);
- map_range = false;
- src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
- }
-
- llassert(src != NULL);
-
-
- mMappedIndexData = src; //LL_NEXT_ALIGNED_ADDRESS<U8>(src);
- mAlignedIndexOffset = mMappedIndexData - src;
- stop_glerror();
- }
- }
-
- if (!mMappedIndexData)
- {
- log_glerror();
- LLMemory::logMemoryInfo(TRUE) ;
-
- if(mMappable)
- {
- GLint buff;
- glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff);
- if ((GLuint)buff != mGLIndices)
- {
- llerrs << "Invalid GL index buffer bound: " << buff << llendl;
- }
-
- llerrs << "glMapBuffer returned NULL (no index data)" << llendl;
- }
- else
- {
- llerrs << "memory allocation for Index data failed. " << llendl ;
- }
- }
- }
- else
- {
- map_range = false;
- }
-
- if (map_range && gGLManager.mHasMapBufferRange && mMappable)
- {
- return mMappedIndexData;
- }
- else
- {
- return mMappedIndexData + sizeof(U16)*index;
- }
-}
-
-static LLFastTimer::DeclareTimer FTM_VBO_UNMAP("VBO Unmap");
-static LLFastTimer::DeclareTimer FTM_VBO_FLUSH_RANGE("Flush VBO Range");
-
-
-static LLFastTimer::DeclareTimer FTM_IBO_UNMAP("IBO Unmap");
-static LLFastTimer::DeclareTimer FTM_IBO_FLUSH_RANGE("Flush IBO Range");
-
-void LLVertexBuffer::unmapBuffer()
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_UNMAP_BUFFER);
- if (!useVBOs())
- {
- return ; //nothing to unmap
- }
-
- bool updated_all = false ;
-
- if (mMappedData && mVertexLocked)
- {
- LLFastTimer t(FTM_VBO_UNMAP);
- bindGLBuffer(true);
- updated_all = mIndexLocked; //both vertex and index buffers done updating
-
- if(!mMappable)
- {
- if (!mMappedVertexRegions.empty())
- {
- stop_glerror();
- for (U32 i = 0; i < mMappedVertexRegions.size(); ++i)
- {
- const MappedRegion& region = mMappedVertexRegions[i];
- S32 offset = region.mIndex >= 0 ? mOffsets[region.mType]+sTypeSize[region.mType]*region.mIndex : 0;
- S32 length = sTypeSize[region.mType]*region.mCount;
- glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, offset, length, (U8*) mMappedData+offset);
- stop_glerror();
- }
-
- mMappedVertexRegions.clear();
- }
- else
- {
- stop_glerror();
- glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, getSize(), (U8*) mMappedData);
- stop_glerror();
- }
- }
- else
- {
- if (gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
- {
- if (!mMappedVertexRegions.empty())
- {
- stop_glerror();
- for (U32 i = 0; i < mMappedVertexRegions.size(); ++i)
- {
- const MappedRegion& region = mMappedVertexRegions[i];
- S32 offset = region.mIndex >= 0 ? mOffsets[region.mType]+sTypeSize[region.mType]*region.mIndex : 0;
- S32 length = sTypeSize[region.mType]*region.mCount;
- if (gGLManager.mHasMapBufferRange)
- {
- LLFastTimer t(FTM_VBO_FLUSH_RANGE);
-#ifdef GL_ARB_map_buffer_range
- glFlushMappedBufferRange(GL_ARRAY_BUFFER_ARB, offset, length);
-#endif
- }
- else if (gGLManager.mHasFlushBufferRange)
- {
- glFlushMappedBufferRangeAPPLE(GL_ARRAY_BUFFER_ARB, offset, length);
- }
- stop_glerror();
- }
-
- mMappedVertexRegions.clear();
- }
- }
- stop_glerror();
- glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
- stop_glerror();
-
- mMappedData = NULL;
- }
-
- mVertexLocked = FALSE ;
- sMappedCount--;
- }
-
- if (mMappedIndexData && mIndexLocked)
- {
- LLFastTimer t(FTM_IBO_UNMAP);
- bindGLIndices();
- if(!mMappable)
- {
- if (!mMappedIndexRegions.empty())
- {
- for (U32 i = 0; i < mMappedIndexRegions.size(); ++i)
- {
- const MappedRegion& region = mMappedIndexRegions[i];
- S32 offset = region.mIndex >= 0 ? sizeof(U16)*region.mIndex : 0;
- S32 length = sizeof(U16)*region.mCount;
- glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length, (U8*) mMappedIndexData+offset);
- stop_glerror();
- }
-
- mMappedIndexRegions.clear();
- }
- else
- {
- stop_glerror();
- glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, getIndicesSize(), (U8*) mMappedIndexData);
- stop_glerror();
- }
- }
- else
- {
- if (gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
- {
- if (!mMappedIndexRegions.empty())
- {
- for (U32 i = 0; i < mMappedIndexRegions.size(); ++i)
- {
- const MappedRegion& region = mMappedIndexRegions[i];
- S32 offset = region.mIndex >= 0 ? sizeof(U16)*region.mIndex : 0;
- S32 length = sizeof(U16)*region.mCount;
- if (gGLManager.mHasMapBufferRange)
- {
- LLFastTimer t(FTM_IBO_FLUSH_RANGE);
-#ifdef GL_ARB_map_buffer_range
- glFlushMappedBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length);
-#endif
- }
- else if (gGLManager.mHasFlushBufferRange)
- {
-#ifdef GL_APPLE_flush_buffer_range
- glFlushMappedBufferRangeAPPLE(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length);
-#endif
- }
- stop_glerror();
- }
-
- mMappedIndexRegions.clear();
- }
- }
- stop_glerror();
- glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
- stop_glerror();
-
- mMappedIndexData = NULL ;
- }
-
- mIndexLocked = FALSE ;
- sMappedCount--;
- }
-
- if(updated_all)
- {
- mEmpty = FALSE;
- }
-}
-
-//----------------------------------------------------------------------------
-
-template <class T,S32 type> struct VertexBufferStrider
-{
- typedef LLStrider<T> strider_t;
- static bool get(LLVertexBuffer& vbo,
- strider_t& strider,
- S32 index, S32 count, bool map_range)
- {
- if (type == LLVertexBuffer::TYPE_INDEX)
- {
- volatile U8* ptr = vbo.mapIndexBuffer(index, count, map_range);
-
- if (ptr == NULL)
- {
- llwarns << "mapIndexBuffer failed!" << llendl;
- return FALSE;
- }
-
- strider = (T*)ptr;
- strider.setStride(0);
- return TRUE;
- }
- else if (vbo.hasDataType(type))
- {
- S32 stride = LLVertexBuffer::sTypeSize[type];
-
- volatile U8* ptr = vbo.mapVertexBuffer(type, index, count, map_range);
-
- if (ptr == NULL)
- {
- llwarns << "mapVertexBuffer failed!" << llendl;
- return FALSE;
- }
-
- strider = (T*)ptr;
- strider.setStride(stride);
- return TRUE;
- }
- else
- {
- llerrs << "VertexBufferStrider could not find valid vertex data." << llendl;
- }
- return FALSE;
- }
-};
-
-bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLVector3,TYPE_VERTEX>::get(*this, strider, index, count, map_range);
-}
-bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector4a>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLVector4a,TYPE_VERTEX>::get(*this, strider, index, count, map_range);
-}
-bool LLVertexBuffer::getIndexStrider(LLStrider<U16>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<U16,TYPE_INDEX>::get(*this, strider, index, count, map_range);
-}
-bool LLVertexBuffer::getTexCoord0Strider(LLStrider<LLVector2>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLVector2,TYPE_TEXCOORD0>::get(*this, strider, index, count, map_range);
-}
-bool LLVertexBuffer::getTexCoord1Strider(LLStrider<LLVector2>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLVector2,TYPE_TEXCOORD1>::get(*this, strider, index, count, map_range);
-}
-
-bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLVector3,TYPE_NORMAL>::get(*this, strider, index, count, map_range);
-}
-bool LLVertexBuffer::getBinormalStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLVector3,TYPE_BINORMAL>::get(*this, strider, index, count, map_range);
-}
-bool LLVertexBuffer::getColorStrider(LLStrider<LLColor4U>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLColor4U,TYPE_COLOR>::get(*this, strider, index, count, map_range);
-}
-bool LLVertexBuffer::getEmissiveStrider(LLStrider<LLColor4U>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLColor4U,TYPE_EMISSIVE>::get(*this, strider, index, count, map_range);
-}
-bool LLVertexBuffer::getWeightStrider(LLStrider<F32>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<F32,TYPE_WEIGHT>::get(*this, strider, index, count, map_range);
-}
-
-bool LLVertexBuffer::getWeight4Strider(LLStrider<LLVector4>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLVector4,TYPE_WEIGHT4>::get(*this, strider, index, count, map_range);
-}
-
-bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index, S32 count, bool map_range)
-{
- return VertexBufferStrider<LLVector4,TYPE_CLOTHWEIGHT>::get(*this, strider, index, count, map_range);
-}
-
-//----------------------------------------------------------------------------
-
-static LLFastTimer::DeclareTimer FTM_BIND_GL_ARRAY("Bind Array");
-bool LLVertexBuffer::bindGLArray()
-{
- if (mGLArray && sGLRenderArray != mGLArray)
- {
- {
- LLFastTimer t(FTM_BIND_GL_ARRAY);
-#if GL_ARB_vertex_array_object
- glBindVertexArray(mGLArray);
-#endif
- sGLRenderArray = mGLArray;
- }
-
- //really shouldn't be necessary, but some drivers don't properly restore the
- //state of GL_ELEMENT_ARRAY_BUFFER_BINDING
- bindGLIndices();
-
- return true;
- }
-
- return false;
-}
-
-static LLFastTimer::DeclareTimer FTM_BIND_GL_BUFFER("Bind Buffer");
-
-bool LLVertexBuffer::bindGLBuffer(bool force_bind)
-{
- bindGLArray();
-
- bool ret = false;
-
- if (useVBOs() && (force_bind || (mGLBuffer && (mGLBuffer != sGLRenderBuffer || !sVBOActive))))
- {
- LLFastTimer t(FTM_BIND_GL_BUFFER);
- /*if (sMapped)
- {
- llerrs << "VBO bound while another VBO mapped!" << llendl;
- }*/
- glBindBufferARB(GL_ARRAY_BUFFER_ARB, mGLBuffer);
- sGLRenderBuffer = mGLBuffer;
- sBindCount++;
- sVBOActive = TRUE;
-
- if (mGLArray)
- {
- llassert(sGLRenderArray == mGLArray);
- //mCachedRenderBuffer = mGLBuffer;
- }
-
- ret = true;
- }
-
- return ret;
-}
-
-static LLFastTimer::DeclareTimer FTM_BIND_GL_INDICES("Bind Indices");
-
-bool LLVertexBuffer::bindGLIndices(bool force_bind)
-{
- bindGLArray();
-
- bool ret = false;
- if (useVBOs() && (force_bind || (mGLIndices && (mGLIndices != sGLRenderIndices || !sIBOActive))))
- {
- LLFastTimer t(FTM_BIND_GL_INDICES);
- /*if (sMapped)
- {
- llerrs << "VBO bound while another VBO mapped!" << llendl;
- }*/
- glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mGLIndices);
- sGLRenderIndices = mGLIndices;
- stop_glerror();
- sBindCount++;
- sIBOActive = TRUE;
- ret = true;
- }
-
- return ret;
-}
-
-void LLVertexBuffer::flush()
-{
- if (useVBOs())
- {
- unmapBuffer();
- }
-}
-
-// Set for rendering
-void LLVertexBuffer::setBuffer(U32 data_mask)
-{
- flush();
-
- LLMemType mt2(LLMemType::MTYPE_VERTEX_SET_BUFFER);
- //set up pointers if the data mask is different ...
- BOOL setup = (sLastMask != data_mask);
-
- if (gDebugGL && data_mask != 0)
- { //make sure data requirements are fulfilled
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
- if (shader)
- {
- U32 required_mask = 0;
- for (U32 i = 0; i < LLVertexBuffer::TYPE_TEXTURE_INDEX; ++i)
- {
- if (shader->getAttribLocation(i) > -1)
- {
- U32 required = 1 << i;
- if ((data_mask & required) == 0)
- {
- llwarns << "Missing attribute: " << LLShaderMgr::instance()->mReservedAttribs[i] << llendl;
- }
-
- required_mask |= required;
- }
- }
-
- if ((data_mask & required_mask) != required_mask)
- {
- llerrs << "Shader consumption mismatches data provision." << llendl;
- }
- }
- }
-
- if (useVBOs())
- {
- if (mGLArray)
- {
- bindGLArray();
- setup = FALSE; //do NOT perform pointer setup if using VAO
- }
- else
- {
- if (bindGLBuffer())
- {
- setup = TRUE;
- }
- if (bindGLIndices())
- {
- setup = TRUE;
- }
- }
-
- BOOL error = FALSE;
- if (gDebugGL && !mGLArray)
- {
- GLint buff;
- glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff);
- if ((GLuint)buff != mGLBuffer)
- {
- if (gDebugSession)
- {
- error = TRUE;
- gFailLog << "Invalid GL vertex buffer bound: " << buff << std::endl;
- }
- else
- {
- llerrs << "Invalid GL vertex buffer bound: " << buff << llendl;
- }
- }
-
- if (mGLIndices)
- {
- glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff);
- if ((GLuint)buff != mGLIndices)
- {
- if (gDebugSession)
- {
- error = TRUE;
- gFailLog << "Invalid GL index buffer bound: " << buff << std::endl;
- }
- else
- {
- llerrs << "Invalid GL index buffer bound: " << buff << llendl;
- }
- }
- }
- }
-
-
- }
- else
- {
- if (sGLRenderArray)
- {
-#if GL_ARB_vertex_array_object
- glBindVertexArray(0);
-#endif
- sGLRenderArray = 0;
- sGLRenderIndices = 0;
- sIBOActive = FALSE;
- }
-
- if (mGLBuffer)
- {
- if (sVBOActive)
- {
- glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
- sBindCount++;
- sVBOActive = FALSE;
- setup = TRUE; // ... or a VBO is deactivated
- }
- if (sGLRenderBuffer != mGLBuffer)
- {
- sGLRenderBuffer = mGLBuffer;
- setup = TRUE; // ... or a client memory pointer changed
- }
- }
- if (mGLIndices)
- {
- if (sIBOActive)
- {
- glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
- sBindCount++;
- sIBOActive = FALSE;
- }
-
- sGLRenderIndices = mGLIndices;
- }
- }
-
- if (!mGLArray)
- {
- setupClientArrays(data_mask);
- }
-
- if (mGLBuffer)
- {
- if (data_mask && setup)
- {
- setupVertexBuffer(data_mask); // subclass specific setup (virtual function)
- sSetCount++;
- }
- }
-}
-
-// virtual (default)
-void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER);
- stop_glerror();
- volatile U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
-
- /*if ((data_mask & mTypeMask) != data_mask)
- {
- llerrs << "LLVertexBuffer::setupVertexBuffer missing required components for supplied data mask." << llendl;
- }*/
-
- if (LLGLSLShader::sNoFixedFunction)
- {
- if (data_mask & MAP_NORMAL)
- {
- S32 loc = TYPE_NORMAL;
- void* ptr = (void*)(base + mOffsets[TYPE_NORMAL]);
- glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_NORMAL], ptr);
- }
- if (data_mask & MAP_TEXCOORD3)
- {
- S32 loc = TYPE_TEXCOORD3;
- void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD3]);
- glVertexAttribPointerARB(loc,2,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD3], ptr);
- }
- if (data_mask & MAP_TEXCOORD2)
- {
- S32 loc = TYPE_TEXCOORD2;
- void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD2]);
- glVertexAttribPointerARB(loc,2,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD2], ptr);
- }
- if (data_mask & MAP_TEXCOORD1)
- {
- S32 loc = TYPE_TEXCOORD1;
- void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD1]);
- glVertexAttribPointerARB(loc,2,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], ptr);
- }
- if (data_mask & MAP_BINORMAL)
- {
- S32 loc = TYPE_BINORMAL;
- void* ptr = (void*)(base + mOffsets[TYPE_BINORMAL]);
- glVertexAttribPointerARB(loc, 3,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_BINORMAL], ptr);
- }
- if (data_mask & MAP_TEXCOORD0)
- {
- S32 loc = TYPE_TEXCOORD0;
- void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD0]);
- glVertexAttribPointerARB(loc,2,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], ptr);
- }
- if (data_mask & MAP_COLOR)
- {
- S32 loc = TYPE_COLOR;
- void* ptr = (void*)(base + mOffsets[TYPE_COLOR]);
- glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_COLOR], ptr);
- }
- if (data_mask & MAP_EMISSIVE)
- {
- S32 loc = TYPE_EMISSIVE;
- void* ptr = (void*)(base + mOffsets[TYPE_EMISSIVE]);
- glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_EMISSIVE], ptr);
- }
- if (data_mask & MAP_WEIGHT)
- {
- S32 loc = TYPE_WEIGHT;
- void* ptr = (void*)(base + mOffsets[TYPE_WEIGHT]);
- glVertexAttribPointerARB(loc, 1, GL_FLOAT, FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT], ptr);
- }
- if (data_mask & MAP_WEIGHT4)
- {
- S32 loc = TYPE_WEIGHT4;
- void* ptr = (void*)(base+mOffsets[TYPE_WEIGHT4]);
- glVertexAttribPointerARB(loc, 4, GL_FLOAT, FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT4], ptr);
- }
- if (data_mask & MAP_CLOTHWEIGHT)
- {
- S32 loc = TYPE_CLOTHWEIGHT;
- void* ptr = (void*)(base + mOffsets[TYPE_CLOTHWEIGHT]);
- glVertexAttribPointerARB(loc, 4, GL_FLOAT, TRUE, LLVertexBuffer::sTypeSize[TYPE_CLOTHWEIGHT], ptr);
- }
- if (data_mask & MAP_TEXTURE_INDEX)
- {
- S32 loc = TYPE_TEXTURE_INDEX;
- void *ptr = (void*) (base + mOffsets[TYPE_VERTEX] + 12);
- glVertexAttribPointerARB(loc, 1, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
- }
- if (data_mask & MAP_VERTEX)
- {
- S32 loc = TYPE_VERTEX;
- void* ptr = (void*)(base + mOffsets[TYPE_VERTEX]);
- glVertexAttribPointerARB(loc, 3,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
- }
- }
- else
- {
- if (data_mask & MAP_NORMAL)
- {
- glNormalPointer(GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_NORMAL], (void*)(base + mOffsets[TYPE_NORMAL]));
- }
- if (data_mask & MAP_TEXCOORD3)
- {
- glClientActiveTextureARB(GL_TEXTURE3_ARB);
- glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD3], (void*)(base + mOffsets[TYPE_TEXCOORD3]));
- glClientActiveTextureARB(GL_TEXTURE0_ARB);
- }
- if (data_mask & MAP_TEXCOORD2)
- {
- glClientActiveTextureARB(GL_TEXTURE2_ARB);
- glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD2], (void*)(base + mOffsets[TYPE_TEXCOORD2]));
- glClientActiveTextureARB(GL_TEXTURE0_ARB);
- }
- if (data_mask & MAP_TEXCOORD1)
- {
- glClientActiveTextureARB(GL_TEXTURE1_ARB);
- glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], (void*)(base + mOffsets[TYPE_TEXCOORD1]));
- glClientActiveTextureARB(GL_TEXTURE0_ARB);
- }
- if (data_mask & MAP_BINORMAL)
- {
- glClientActiveTextureARB(GL_TEXTURE2_ARB);
- glTexCoordPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_BINORMAL], (void*)(base + mOffsets[TYPE_BINORMAL]));
- glClientActiveTextureARB(GL_TEXTURE0_ARB);
- }
- if (data_mask & MAP_TEXCOORD0)
- {
- glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0]));
- }
- if (data_mask & MAP_COLOR)
- {
- glColorPointer(4, GL_UNSIGNED_BYTE, LLVertexBuffer::sTypeSize[TYPE_COLOR], (void*)(base + mOffsets[TYPE_COLOR]));
- }
- if (data_mask & MAP_VERTEX)
- {
- glVertexPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_VERTEX], (void*)(base + 0));
- }
- }
-
- llglassertok();
-}
-
-LLVertexBuffer::MappedRegion::MappedRegion(S32 type, S32 index, S32 count)
-: mType(type), mIndex(index), mCount(count)
-{
- llassert(mType == LLVertexBuffer::TYPE_INDEX ||
- mType < LLVertexBuffer::TYPE_TEXTURE_INDEX);
-}
-
-
+/**
+ * @file llvertexbuffer.cpp
+ * @brief LLVertexBuffer implementation
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include <boost/static_assert.hpp>
+#include "llsys.h"
+#include "llvertexbuffer.h"
+// #include "llrender.h"
+#include "llglheaders.h"
+#include "llmemtype.h"
+#include "llrender.h"
+#include "llvector4a.h"
+#include "llshadermgr.h"
+#include "llglslshader.h"
+#include "llmemory.h"
+
+//Next Highest Power Of Two
+//helper function, returns first number > v that is a power of 2, or v if v is already a power of 2
+U32 nhpo2(U32 v)
+{
+ U32 r = 1;
+ while (r < v) {
+ r *= 2;
+ }
+ return r;
+}
+
+
+//============================================================================
+
+//static
+LLVBOPool LLVertexBuffer::sStreamVBOPool;
+LLVBOPool LLVertexBuffer::sDynamicVBOPool;
+LLVBOPool LLVertexBuffer::sStreamIBOPool;
+LLVBOPool LLVertexBuffer::sDynamicIBOPool;
+U32 LLVBOPool::sBytesPooled = 0;
+
+LLPrivateMemoryPool* LLVertexBuffer::sPrivatePoolp = NULL ;
+U32 LLVertexBuffer::sBindCount = 0;
+U32 LLVertexBuffer::sSetCount = 0;
+S32 LLVertexBuffer::sCount = 0;
+S32 LLVertexBuffer::sGLCount = 0;
+S32 LLVertexBuffer::sMappedCount = 0;
+BOOL LLVertexBuffer::sDisableVBOMapping = FALSE ;
+BOOL LLVertexBuffer::sEnableVBOs = TRUE;
+U32 LLVertexBuffer::sGLRenderBuffer = 0;
+U32 LLVertexBuffer::sGLRenderArray = 0;
+U32 LLVertexBuffer::sGLRenderIndices = 0;
+U32 LLVertexBuffer::sLastMask = 0;
+BOOL LLVertexBuffer::sVBOActive = FALSE;
+BOOL LLVertexBuffer::sIBOActive = FALSE;
+U32 LLVertexBuffer::sAllocatedBytes = 0;
+BOOL LLVertexBuffer::sMapped = FALSE;
+BOOL LLVertexBuffer::sUseStreamDraw = TRUE;
+BOOL LLVertexBuffer::sUseVAO = FALSE;
+BOOL LLVertexBuffer::sPreferStreamDraw = FALSE;
+
+const U32 FENCE_WAIT_TIME_NANOSECONDS = 10000; //1 ms
+
+class LLGLSyncFence : public LLGLFence
+{
+public:
+#ifdef GL_ARB_sync
+ GLsync mSync;
+#endif
+
+ LLGLSyncFence()
+ {
+#ifdef GL_ARB_sync
+ mSync = 0;
+#endif
+ }
+
+ virtual ~LLGLSyncFence()
+ {
+#ifdef GL_ARB_sync
+ if (mSync)
+ {
+ glDeleteSync(mSync);
+ }
+#endif
+ }
+
+ void placeFence()
+ {
+#ifdef GL_ARB_sync
+ if (mSync)
+ {
+ glDeleteSync(mSync);
+ }
+ mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+#endif
+ }
+
+ void wait()
+ {
+#ifdef GL_ARB_sync
+ if (mSync)
+ {
+ while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
+ { //track the number of times we've waited here
+ static S32 waits = 0;
+ waits++;
+ }
+ }
+#endif
+ }
+
+
+};
+
+
+//which power of 2 is i?
+//assumes i is a power of 2 > 0
+U32 wpo2(U32 i)
+{
+ llassert(i > 0);
+ llassert(nhpo2(i) == i);
+
+ U32 r = 0;
+
+ while (i >>= 1) ++r;
+
+ return r;
+}
+
+volatile U8* LLVBOPool::allocate(U32& name, U32 size)
+{
+ llassert(nhpo2(size) == size);
+
+ U32 i = wpo2(size);
+
+ if (mFreeList.size() <= i)
+ {
+ mFreeList.resize(i+1);
+ }
+
+ volatile U8* ret = NULL;
+
+ if (mFreeList[i].empty())
+ {
+ //make a new buffer
+ glGenBuffersARB(1, &name);
+ glBindBufferARB(mType, name);
+ LLVertexBuffer::sAllocatedBytes += size;
+
+ if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB)
+ {
+ glBufferDataARB(mType, size, 0, mUsage);
+ ret = (U8*) ll_aligned_malloc_16(size);
+ }
+ else
+ { //always use a true hint of static draw when allocating non-client-backed buffers
+ glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB);
+ }
+
+ glBindBufferARB(mType, 0);
+ }
+ else
+ {
+ name = mFreeList[i].front().mGLName;
+ ret = mFreeList[i].front().mClientData;
+
+ sBytesPooled -= size;
+
+ mFreeList[i].pop_front();
+ }
+
+ return ret;
+}
+
+void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
+{
+ llassert(nhpo2(size) == size);
+
+ U32 i = wpo2(size);
+
+ llassert(mFreeList.size() > i);
+
+ Record rec;
+ rec.mGLName = name;
+ rec.mClientData = buffer;
+
+ sBytesPooled += size;
+
+ if (!LLVertexBuffer::sDisableVBOMapping && mUsage == GL_DYNAMIC_DRAW_ARB)
+ {
+ glDeleteBuffersARB(1, &rec.mGLName);
+ }
+ else
+ {
+ mFreeList[i].push_back(rec);
+}
+}
+
+void LLVBOPool::cleanup()
+{
+ U32 size = 1;
+
+ for (U32 i = 0; i < mFreeList.size(); ++i)
+ {
+ record_list_t& l = mFreeList[i];
+
+ while (!l.empty())
+ {
+ Record& r = l.front();
+
+ glDeleteBuffersARB(1, &r.mGLName);
+
+ if (r.mClientData)
+ {
+ ll_aligned_free_16((void*) r.mClientData);
+ }
+
+ l.pop_front();
+
+ LLVertexBuffer::sAllocatedBytes -= size;
+ sBytesPooled -= size;
+ }
+
+ size *= 2;
+ }
+}
+
+
+//NOTE: each component must be AT LEAST 4 bytes in size to avoid a performance penalty on AMD hardware
+S32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
+{
+ sizeof(LLVector4), // TYPE_VERTEX,
+ sizeof(LLVector4), // TYPE_NORMAL,
+ sizeof(LLVector2), // TYPE_TEXCOORD0,
+ sizeof(LLVector2), // TYPE_TEXCOORD1,
+ sizeof(LLVector2), // TYPE_TEXCOORD2,
+ sizeof(LLVector2), // TYPE_TEXCOORD3,
+ sizeof(LLColor4U), // TYPE_COLOR,
+ sizeof(LLColor4U), // TYPE_EMISSIVE, only alpha is used currently
+ sizeof(LLVector4), // TYPE_BINORMAL,
+ sizeof(F32), // TYPE_WEIGHT,
+ sizeof(LLVector4), // TYPE_WEIGHT4,
+ sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
+ sizeof(LLVector4), // TYPE_TEXTURE_INDEX (actually exists as position.w), no extra data, but stride is 16 bytes
+};
+
+U32 LLVertexBuffer::sGLMode[LLRender::NUM_MODES] =
+{
+ GL_TRIANGLES,
+ GL_TRIANGLE_STRIP,
+ GL_TRIANGLE_FAN,
+ GL_POINTS,
+ GL_LINES,
+ GL_LINE_STRIP,
+ GL_QUADS,
+ GL_LINE_LOOP,
+};
+
+
+//static
+void LLVertexBuffer::setupClientArrays(U32 data_mask)
+{
+ if (sLastMask != data_mask)
+ {
+ BOOL error = FALSE;
+
+ if (LLGLSLShader::sNoFixedFunction)
+ {
+ for (U32 i = 0; i < TYPE_MAX; ++i)
+ {
+ S32 loc = i;
+
+ U32 mask = 1 << i;
+
+ if (sLastMask & (1 << i))
+ { //was enabled
+ if (!(data_mask & mask))
+ { //needs to be disabled
+ glDisableVertexAttribArrayARB(loc);
+ }
+ }
+ else
+ { //was disabled
+ if (data_mask & mask)
+ { //needs to be enabled
+ glEnableVertexAttribArrayARB(loc);
+ }
+ }
+ }
+ }
+ else
+ {
+
+ GLenum array[] =
+ {
+ GL_VERTEX_ARRAY,
+ GL_NORMAL_ARRAY,
+ GL_TEXTURE_COORD_ARRAY,
+ GL_COLOR_ARRAY,
+ };
+
+ GLenum mask[] =
+ {
+ MAP_VERTEX,
+ MAP_NORMAL,
+ MAP_TEXCOORD0,
+ MAP_COLOR
+ };
+
+
+
+ for (U32 i = 0; i < 4; ++i)
+ {
+ if (sLastMask & mask[i])
+ { //was enabled
+ if (!(data_mask & mask[i]))
+ { //needs to be disabled
+ glDisableClientState(array[i]);
+ }
+ else if (gDebugGL)
+ { //needs to be enabled, make sure it was (DEBUG)
+ if (!glIsEnabled(array[i]))
+ {
+ if (gDebugSession)
+ {
+ error = TRUE;
+ gFailLog << "Bad client state! " << array[i] << " disabled." << std::endl;
+ }
+ else
+ {
+ llerrs << "Bad client state! " << array[i] << " disabled." << llendl;
+ }
+ }
+ }
+ }
+ else
+ { //was disabled
+ if (data_mask & mask[i])
+ { //needs to be enabled
+ glEnableClientState(array[i]);
+ }
+ else if (gDebugGL && glIsEnabled(array[i]))
+ { //needs to be disabled, make sure it was (DEBUG TEMPORARY)
+ if (gDebugSession)
+ {
+ error = TRUE;
+ gFailLog << "Bad client state! " << array[i] << " enabled." << std::endl;
+ }
+ else
+ {
+ llerrs << "Bad client state! " << array[i] << " enabled." << llendl;
+ }
+ }
+ }
+ }
+
+ U32 map_tc[] =
+ {
+ MAP_TEXCOORD1,
+ MAP_TEXCOORD2,
+ MAP_TEXCOORD3
+ };
+
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (sLastMask & map_tc[i])
+ {
+ if (!(data_mask & map_tc[i]))
+ { //disable
+ glClientActiveTextureARB(GL_TEXTURE1_ARB+i);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+ }
+ else if (data_mask & map_tc[i])
+ {
+ glClientActiveTextureARB(GL_TEXTURE1_ARB+i);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+ }
+
+ if (sLastMask & MAP_BINORMAL)
+ {
+ if (!(data_mask & MAP_BINORMAL))
+ {
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+ }
+ else if (data_mask & MAP_BINORMAL)
+ {
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+ }
+
+ sLastMask = data_mask;
+ }
+}
+
+//static
+void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm)
+{
+ llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
+ gGL.syncMatrices();
+
+ U32 count = pos.size();
+ llassert_always(norm.size() >= pos.size());
+ llassert_always(count > 0) ;
+
+ unbind();
+
+ setupClientArrays(MAP_VERTEX | MAP_NORMAL);
+
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+
+ if (shader)
+ {
+ S32 loc = LLVertexBuffer::TYPE_VERTEX;
+ if (loc > -1)
+ {
+ glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, pos[0].mV);
+ }
+ loc = LLVertexBuffer::TYPE_NORMAL;
+ if (loc > -1)
+ {
+ glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, norm[0].mV);
+ }
+ }
+ else
+ {
+ glVertexPointer(3, GL_FLOAT, 0, pos[0].mV);
+ glNormalPointer(GL_FLOAT, 0, norm[0].mV);
+ }
+
+ glDrawArrays(sGLMode[mode], 0, count);
+}
+
+//static
+void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp)
+{
+ llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
+
+ gGL.syncMatrices();
+
+ U32 mask = LLVertexBuffer::MAP_VERTEX;
+ if (tc)
+ {
+ mask = mask | LLVertexBuffer::MAP_TEXCOORD0;
+ }
+
+ unbind();
+
+ setupClientArrays(mask);
+
+ if (LLGLSLShader::sNoFixedFunction)
+ {
+ S32 loc = LLVertexBuffer::TYPE_VERTEX;
+ glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 16, pos);
+
+ if (tc)
+ {
+ loc = LLVertexBuffer::TYPE_TEXCOORD0;
+ glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, 0, tc);
+ }
+ }
+ else
+ {
+ glTexCoordPointer(2, GL_FLOAT, 0, tc);
+ glVertexPointer(3, GL_FLOAT, 16, pos);
+ }
+
+ glDrawElements(sGLMode[mode], num_indices, GL_UNSIGNED_SHORT, indicesp);
+}
+
+void LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_offset) const
+{
+ if (start >= (U32) mNumVerts ||
+ end >= (U32) mNumVerts)
+ {
+ llerrs << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mNumVerts << llendl;
+ }
+
+ llassert(mNumIndices >= 0);
+
+ if (indices_offset >= (U32) mNumIndices ||
+ indices_offset + count > (U32) mNumIndices)
+ {
+ llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
+ }
+
+ if (gDebugGL && !useVBOs())
+ {
+ U16* idx = ((U16*) getIndicesPointer())+indices_offset;
+ for (U32 i = 0; i < count; ++i)
+ {
+ if (idx[i] < start || idx[i] > end)
+ {
+ llerrs << "Index out of range: " << idx[i] << " not in [" << start << ", " << end << "]" << llendl;
+ }
+ }
+
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+
+ if (shader && shader->mFeatures.mIndexedTextureChannels > 1)
+ {
+ LLStrider<LLVector4a> v;
+ //hack to get non-const reference
+ LLVertexBuffer* vb = (LLVertexBuffer*) this;
+ vb->getVertexStrider(v);
+
+ for (U32 i = start; i < end; i++)
+ {
+ S32 idx = (S32) (v[i][3]+0.25f);
+ if (idx < 0 || idx >= shader->mFeatures.mIndexedTextureChannels)
+ {
+ llerrs << "Bad texture index found in vertex data stream." << llendl;
+ }
+ }
+ }
+ }
+}
+
+void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const
+{
+ validateRange(start, end, count, indices_offset);
+ mMappable = FALSE;
+ gGL.syncMatrices();
+
+ llassert(mNumVerts >= 0);
+ llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
+
+ if (mGLArray)
+ {
+ if (mGLArray != sGLRenderArray)
+ {
+ llerrs << "Wrong vertex array bound." << llendl;
+ }
+ }
+ else
+ {
+ if (mGLIndices != sGLRenderIndices)
+ {
+ llerrs << "Wrong index buffer bound." << llendl;
+ }
+
+ if (mGLBuffer != sGLRenderBuffer)
+ {
+ llerrs << "Wrong vertex buffer bound." << llendl;
+ }
+ }
+
+ if (gDebugGL && !mGLArray && useVBOs())
+ {
+ GLint elem = 0;
+ glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &elem);
+
+ if (elem != mGLIndices)
+ {
+ llerrs << "Wrong index buffer bound!" << llendl;
+ }
+ }
+
+ if (mode >= LLRender::NUM_MODES)
+ {
+ llerrs << "Invalid draw mode: " << mode << llendl;
+ return;
+ }
+
+ U16* idx = ((U16*) getIndicesPointer())+indices_offset;
+
+ stop_glerror();
+ glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT,
+ idx);
+ stop_glerror();
+ placeFence();
+}
+
+void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
+{
+ llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
+ mMappable = FALSE;
+ gGL.syncMatrices();
+
+ llassert(mNumIndices >= 0);
+ if (indices_offset >= (U32) mNumIndices ||
+ indices_offset + count > (U32) mNumIndices)
+ {
+ llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
+ }
+
+ if (mGLArray)
+ {
+ if (mGLArray != sGLRenderArray)
+ {
+ llerrs << "Wrong vertex array bound." << llendl;
+ }
+ }
+ else
+ {
+ if (mGLIndices != sGLRenderIndices)
+ {
+ llerrs << "Wrong index buffer bound." << llendl;
+ }
+
+ if (mGLBuffer != sGLRenderBuffer)
+ {
+ llerrs << "Wrong vertex buffer bound." << llendl;
+ }
+ }
+
+ if (mode >= LLRender::NUM_MODES)
+ {
+ llerrs << "Invalid draw mode: " << mode << llendl;
+ return;
+ }
+
+ stop_glerror();
+ glDrawElements(sGLMode[mode], count, GL_UNSIGNED_SHORT,
+ ((U16*) getIndicesPointer()) + indices_offset);
+ stop_glerror();
+ placeFence();
+}
+
+void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
+{
+ llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
+ mMappable = FALSE;
+ gGL.syncMatrices();
+
+ llassert(mNumVerts >= 0);
+ if (first >= (U32) mNumVerts ||
+ first + count > (U32) mNumVerts)
+ {
+ llerrs << "Bad vertex buffer draw range: [" << first << ", " << first+count << "]" << llendl;
+ }
+
+ if (mGLArray)
+ {
+ if (mGLArray != sGLRenderArray)
+ {
+ llerrs << "Wrong vertex array bound." << llendl;
+ }
+ }
+ else
+ {
+ BOOL uvb = useVBOs();
+ if (mGLBuffer != sGLRenderBuffer || uvb != sVBOActive)
+ {
+ llerrs << "Wrong vertex buffer bound." << llendl;
+ }
+ }
+
+ if (mode >= LLRender::NUM_MODES)
+ {
+ llerrs << "Invalid draw mode: " << mode << llendl;
+ return;
+ }
+
+ stop_glerror();
+ glDrawArrays(sGLMode[mode], first, count);
+ stop_glerror();
+ placeFence();
+}
+
+//static
+void LLVertexBuffer::initClass(bool use_vbo, bool no_vbo_mapping)
+{
+ sEnableVBOs = use_vbo && gGLManager.mHasVertexBufferObject ;
+ sDisableVBOMapping = sEnableVBOs && no_vbo_mapping ;
+
+ if(!sPrivatePoolp)
+ {
+ sPrivatePoolp = LLPrivateMemoryPoolManager::getInstance()->newPool(LLPrivateMemoryPool::STATIC) ;
+ }
+
+ sStreamVBOPool.mType = GL_ARRAY_BUFFER_ARB;
+ sStreamVBOPool.mUsage= GL_STREAM_DRAW_ARB;
+ sStreamIBOPool.mType = GL_ELEMENT_ARRAY_BUFFER_ARB;
+ sStreamIBOPool.mUsage= GL_STREAM_DRAW_ARB;
+
+ sDynamicVBOPool.mType = GL_ARRAY_BUFFER_ARB;
+ sDynamicVBOPool.mUsage= GL_DYNAMIC_DRAW_ARB;
+ sDynamicIBOPool.mType = GL_ELEMENT_ARRAY_BUFFER_ARB;
+ sDynamicIBOPool.mUsage= GL_DYNAMIC_DRAW_ARB;
+}
+
+//static
+void LLVertexBuffer::unbind()
+{
+ if (sGLRenderArray)
+ {
+#if GL_ARB_vertex_array_object
+ glBindVertexArray(0);
+#endif
+ sGLRenderArray = 0;
+ sGLRenderIndices = 0;
+ sIBOActive = FALSE;
+ }
+
+ if (sVBOActive)
+ {
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+ sVBOActive = FALSE;
+ }
+ if (sIBOActive)
+ {
+ glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+ sIBOActive = FALSE;
+ }
+
+ sGLRenderBuffer = 0;
+ sGLRenderIndices = 0;
+
+ setupClientArrays(0);
+}
+
+//static
+void LLVertexBuffer::cleanupClass()
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_CLEANUP_CLASS);
+ unbind();
+
+ sStreamIBOPool.cleanup();
+ sDynamicIBOPool.cleanup();
+ sStreamVBOPool.cleanup();
+ sDynamicVBOPool.cleanup();
+
+ if(sPrivatePoolp)
+ {
+ LLPrivateMemoryPoolManager::getInstance()->deletePool(sPrivatePoolp) ;
+ sPrivatePoolp = NULL ;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
+ LLRefCount(),
+
+ mNumVerts(0),
+ mNumIndices(0),
+ mUsage(usage),
+ mGLBuffer(0),
+ mGLArray(0),
+ mGLIndices(0),
+ mMappedData(NULL),
+ mMappedIndexData(NULL),
+ mVertexLocked(FALSE),
+ mIndexLocked(FALSE),
+ mFinal(FALSE),
+ mEmpty(TRUE),
+ mFence(NULL)
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_CONSTRUCTOR);
+ mFence = NULL;
+ if (!sEnableVBOs)
+ {
+ mUsage = 0 ;
+ }
+
+ if (mUsage == GL_STREAM_DRAW_ARB && !sUseStreamDraw)
+ {
+ mUsage = 0;
+ }
+
+ if (mUsage == GL_DYNAMIC_DRAW_ARB && sPreferStreamDraw)
+ {
+ mUsage = GL_STREAM_DRAW_ARB;
+ }
+
+ if (mUsage == 0 && LLRender::sGLCoreProfile)
+ { //MUST use VBOs for all rendering
+ mUsage = GL_STREAM_DRAW_ARB;
+ }
+
+ if (mUsage && mUsage != GL_STREAM_DRAW_ARB)
+ { //only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default
+ if (sDisableVBOMapping)
+ { //always use stream draw if VBO mapping is disabled
+ mUsage = GL_STREAM_DRAW_ARB;
+ }
+ else
+ {
+ mUsage = GL_DYNAMIC_DRAW_ARB;
+ }
+ }
+
+
+ if (mUsage == GL_DYNAMIC_DRAW_ARB && !sDisableVBOMapping)
+ {
+ mMappable = TRUE;
+ }
+ else
+ {
+ mMappable = FALSE;
+ }
+
+ //zero out offsets
+ for (U32 i = 0; i < TYPE_MAX; i++)
+ {
+ mOffsets[i] = 0;
+ }
+
+ mTypeMask = typemask;
+ mSize = 0;
+ mIndicesSize = 0;
+ mAlignedOffset = 0;
+ mAlignedIndexOffset = 0;
+
+ sCount++;
+}
+
+//static
+S32 LLVertexBuffer::calcOffsets(const U32& typemask, S32* offsets, S32 num_vertices)
+{
+ S32 offset = 0;
+ for (S32 i=0; i<TYPE_TEXTURE_INDEX; i++)
+ {
+ U32 mask = 1<<i;
+ if (typemask & mask)
+ {
+ if (offsets && LLVertexBuffer::sTypeSize[i])
+ {
+ offsets[i] = offset;
+ offset += LLVertexBuffer::sTypeSize[i]*num_vertices;
+ offset = (offset + 0xF) & ~0xF;
+ }
+ }
+ }
+
+ offsets[TYPE_TEXTURE_INDEX] = offsets[TYPE_VERTEX] + 12;
+
+ return offset+16;
+}
+
+//static
+S32 LLVertexBuffer::calcVertexSize(const U32& typemask)
+{
+ S32 size = 0;
+ for (S32 i = 0; i < TYPE_TEXTURE_INDEX; i++)
+ {
+ U32 mask = 1<<i;
+ if (typemask & mask)
+ {
+ size += LLVertexBuffer::sTypeSize[i];
+ }
+ }
+
+ return size;
+}
+
+S32 LLVertexBuffer::getSize() const
+{
+ return mSize;
+}
+
+// protected, use unref()
+//virtual
+LLVertexBuffer::~LLVertexBuffer()
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTRUCTOR);
+ destroyGLBuffer();
+ destroyGLIndices();
+
+ if (mGLArray)
+ {
+#if GL_ARB_vertex_array_object
+ glDeleteVertexArrays(1, &mGLArray);
+#endif
+ }
+
+ sCount--;
+
+ if (mFence)
+ {
+ delete mFence;
+ }
+
+ mFence = NULL;
+
+ llassert_always(!mMappedData && !mMappedIndexData) ;
+};
+
+void LLVertexBuffer::placeFence() const
+{
+ /*if (!mFence && useVBOs())
+ {
+ if (gGLManager.mHasSync)
+ {
+ mFence = new LLGLSyncFence();
+ }
+ }
+
+ if (mFence)
+ {
+ mFence->placeFence();
+ }*/
+}
+
+void LLVertexBuffer::waitFence() const
+{
+ /*if (mFence)
+ {
+ mFence->wait();
+ }*/
+}
+
+//----------------------------------------------------------------------------
+
+void LLVertexBuffer::genBuffer(U32 size)
+{
+ mSize = nhpo2(size);
+
+ if (mUsage == GL_STREAM_DRAW_ARB)
+ {
+ mMappedData = sStreamVBOPool.allocate(mGLBuffer, mSize);
+ }
+ else
+ {
+ mMappedData = sDynamicVBOPool.allocate(mGLBuffer, mSize);
+ }
+
+ sGLCount++;
+}
+
+void LLVertexBuffer::genIndices(U32 size)
+{
+ mIndicesSize = nhpo2(size);
+
+ if (mUsage == GL_STREAM_DRAW_ARB)
+ {
+ mMappedIndexData = sStreamIBOPool.allocate(mGLIndices, mIndicesSize);
+ }
+ else
+ {
+ mMappedIndexData = sDynamicIBOPool.allocate(mGLIndices, mIndicesSize);
+ }
+
+ sGLCount++;
+}
+
+void LLVertexBuffer::releaseBuffer()
+{
+ if (mUsage == GL_STREAM_DRAW_ARB)
+ {
+ sStreamVBOPool.release(mGLBuffer, mMappedData, mSize);
+ }
+ else
+ {
+ sDynamicVBOPool.release(mGLBuffer, mMappedData, mSize);
+ }
+
+ mGLBuffer = 0;
+ mMappedData = NULL;
+
+ sGLCount--;
+}
+
+void LLVertexBuffer::releaseIndices()
+{
+ if (mUsage == GL_STREAM_DRAW_ARB)
+ {
+ sStreamIBOPool.release(mGLIndices, mMappedIndexData, mIndicesSize);
+ }
+ else
+ {
+ sDynamicIBOPool.release(mGLIndices, mMappedIndexData, mIndicesSize);
+ }
+
+ mGLIndices = 0;
+ mMappedIndexData = NULL;
+
+ sGLCount--;
+}
+
+void LLVertexBuffer::createGLBuffer(U32 size)
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_VERTICES);
+
+ if (mGLBuffer)
+ {
+ destroyGLBuffer();
+ }
+
+ if (size == 0)
+ {
+ return;
+ }
+
+ mEmpty = TRUE;
+
+ if (useVBOs())
+ {
+ genBuffer(size);
+ }
+ else
+ {
+ static int gl_buffer_idx = 0;
+ mGLBuffer = ++gl_buffer_idx;
+ mMappedData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
+ mSize = size;
+ }
+}
+
+void LLVertexBuffer::createGLIndices(U32 size)
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_INDICES);
+
+ if (mGLIndices)
+ {
+ destroyGLIndices();
+ }
+
+ if (size == 0)
+ {
+ return;
+ }
+
+ mEmpty = TRUE;
+
+ //pad by 16 bytes for aligned copies
+ size += 16;
+
+ if (useVBOs())
+ {
+ //pad by another 16 bytes for VBO pointer adjustment
+ size += 16;
+ genIndices(size);
+ }
+ else
+ {
+ mMappedIndexData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
+ static int gl_buffer_idx = 0;
+ mGLIndices = ++gl_buffer_idx;
+ mIndicesSize = size;
+ }
+}
+
+void LLVertexBuffer::destroyGLBuffer()
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_BUFFER);
+ if (mGLBuffer)
+ {
+ if (useVBOs())
+ {
+ releaseBuffer();
+ }
+ else
+ {
+ FREE_MEM(sPrivatePoolp, (void*) mMappedData) ;
+ mMappedData = NULL;
+ mEmpty = TRUE;
+ }
+ }
+
+ mGLBuffer = 0;
+ //unbind();
+}
+
+void LLVertexBuffer::destroyGLIndices()
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_INDICES);
+ if (mGLIndices)
+ {
+ if (useVBOs())
+ {
+ releaseIndices();
+ }
+ else
+ {
+ FREE_MEM(sPrivatePoolp, (void*) mMappedIndexData) ;
+ mMappedIndexData = NULL;
+ mEmpty = TRUE;
+ }
+ }
+
+ mGLIndices = 0;
+ //unbind();
+}
+
+void LLVertexBuffer::updateNumVerts(S32 nverts)
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_VERTS);
+
+ llassert(nverts >= 0);
+
+ if (nverts >= 65535)
+ {
+ llwarns << "Vertex buffer overflow!" << llendl;
+ nverts = 65535;
+ }
+
+ U32 needed_size = calcOffsets(mTypeMask, mOffsets, nverts);
+
+ if (needed_size > mSize || needed_size <= mSize/2)
+ {
+ createGLBuffer(needed_size);
+ }
+
+ mNumVerts = nverts;
+}
+
+void LLVertexBuffer::updateNumIndices(S32 nindices)
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_INDICES);
+
+ llassert(nindices >= 0);
+
+ U32 needed_size = sizeof(U16) * nindices;
+
+ if (needed_size > mIndicesSize || needed_size <= mIndicesSize/2)
+ {
+ createGLIndices(needed_size);
+ }
+
+ mNumIndices = nindices;
+}
+
+void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER);
+
+ stop_glerror();
+
+ if (nverts < 0 || nindices < 0 ||
+ nverts > 65536)
+ {
+ llerrs << "Bad vertex buffer allocation: " << nverts << " : " << nindices << llendl;
+ }
+
+ updateNumVerts(nverts);
+ updateNumIndices(nindices);
+
+ if (create && (nverts || nindices))
+ {
+ //actually allocate space for the vertex buffer if using VBO mapping
+ flush();
+
+ if (gGLManager.mHasVertexArrayObject && useVBOs() && (LLRender::sGLCoreProfile || sUseVAO))
+ {
+#if GL_ARB_vertex_array_object
+ glGenVertexArrays(1, &mGLArray);
+#endif
+ setupVertexArray();
+ }
+ }
+}
+
+static LLFastTimer::DeclareTimer FTM_SETUP_VERTEX_ARRAY("Setup VAO");
+
+void LLVertexBuffer::setupVertexArray()
+{
+ if (!mGLArray)
+ {
+ return;
+ }
+
+ LLFastTimer t(FTM_SETUP_VERTEX_ARRAY);
+#if GL_ARB_vertex_array_object
+ glBindVertexArray(mGLArray);
+#endif
+ sGLRenderArray = mGLArray;
+
+ U32 attrib_size[] =
+ {
+ 3, //TYPE_VERTEX,
+ 3, //TYPE_NORMAL,
+ 2, //TYPE_TEXCOORD0,
+ 2, //TYPE_TEXCOORD1,
+ 2, //TYPE_TEXCOORD2,
+ 2, //TYPE_TEXCOORD3,
+ 4, //TYPE_COLOR,
+ 4, //TYPE_EMISSIVE,
+ 3, //TYPE_BINORMAL,
+ 1, //TYPE_WEIGHT,
+ 4, //TYPE_WEIGHT4,
+ 4, //TYPE_CLOTHWEIGHT,
+ 1, //TYPE_TEXTURE_INDEX
+ };
+
+ U32 attrib_type[] =
+ {
+ GL_FLOAT, //TYPE_VERTEX,
+ GL_FLOAT, //TYPE_NORMAL,
+ GL_FLOAT, //TYPE_TEXCOORD0,
+ GL_FLOAT, //TYPE_TEXCOORD1,
+ GL_FLOAT, //TYPE_TEXCOORD2,
+ GL_FLOAT, //TYPE_TEXCOORD3,
+ GL_UNSIGNED_BYTE, //TYPE_COLOR,
+ GL_UNSIGNED_BYTE, //TYPE_EMISSIVE,
+ GL_FLOAT, //TYPE_BINORMAL,
+ GL_FLOAT, //TYPE_WEIGHT,
+ GL_FLOAT, //TYPE_WEIGHT4,
+ GL_FLOAT, //TYPE_CLOTHWEIGHT,
+ GL_FLOAT, //TYPE_TEXTURE_INDEX
+ };
+
+ U32 attrib_normalized[] =
+ {
+ GL_FALSE, //TYPE_VERTEX,
+ GL_FALSE, //TYPE_NORMAL,
+ GL_FALSE, //TYPE_TEXCOORD0,
+ GL_FALSE, //TYPE_TEXCOORD1,
+ GL_FALSE, //TYPE_TEXCOORD2,
+ GL_FALSE, //TYPE_TEXCOORD3,
+ GL_TRUE, //TYPE_COLOR,
+ GL_TRUE, //TYPE_EMISSIVE,
+ GL_FALSE, //TYPE_BINORMAL,
+ GL_FALSE, //TYPE_WEIGHT,
+ GL_FALSE, //TYPE_WEIGHT4,
+ GL_FALSE, //TYPE_CLOTHWEIGHT,
+ GL_FALSE, //TYPE_TEXTURE_INDEX
+ };
+
+ bindGLBuffer(true);
+ bindGLIndices(true);
+
+ for (U32 i = 0; i < TYPE_MAX; ++i)
+ {
+ if (mTypeMask & (1 << i))
+ {
+ glEnableVertexAttribArrayARB(i);
+ glVertexAttribPointerARB(i, attrib_size[i], attrib_type[i], attrib_normalized[i], sTypeSize[i], (void*) mOffsets[i]);
+ }
+ else
+ {
+ glDisableVertexAttribArrayARB(i);
+ }
+ }
+
+ //draw a dummy triangle to set index array pointer
+ //glDrawElements(GL_TRIANGLES, 0, GL_UNSIGNED_SHORT, NULL);
+
+ unbind();
+}
+
+void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
+{
+ llassert(newnverts >= 0);
+ llassert(newnindices >= 0);
+
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_RESIZE_BUFFER);
+
+ updateNumVerts(newnverts);
+ updateNumIndices(newnindices);
+
+ if (useVBOs())
+ {
+ flush();
+
+ if (mGLArray)
+ { //if size changed, offsets changed
+ setupVertexArray();
+ }
+ }
+}
+
+BOOL LLVertexBuffer::useVBOs() const
+{
+ //it's generally ineffective to use VBO for things that are streaming on apple
+
+ if (!mUsage)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+//----------------------------------------------------------------------------
+
+bool expand_region(LLVertexBuffer::MappedRegion& region, S32 index, S32 count)
+{
+ S32 end = index+count;
+ S32 region_end = region.mIndex+region.mCount;
+
+ if (end < region.mIndex ||
+ index > region_end)
+ { //gap exists, do not merge
+ return false;
+ }
+
+ S32 new_end = llmax(end, region_end);
+ S32 new_index = llmin(index, region.mIndex);
+ region.mIndex = new_index;
+ region.mCount = new_end-new_index;
+ return true;
+}
+
+static LLFastTimer::DeclareTimer FTM_VBO_MAP_BUFFER_RANGE("VBO Map Range");
+static LLFastTimer::DeclareTimer FTM_VBO_MAP_BUFFER("VBO Map");
+
+// Map for data access
+volatile U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range)
+{
+ bindGLBuffer(true);
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
+ if (mFinal)
+ {
+ llerrs << "LLVertexBuffer::mapVeretxBuffer() called on a finalized buffer." << llendl;
+ }
+ if (!useVBOs() && !mMappedData && !mMappedIndexData)
+ {
+ llerrs << "LLVertexBuffer::mapVertexBuffer() called on unallocated buffer." << llendl;
+ }
+
+ if (useVBOs())
+ {
+ if (!mMappable || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
+ {
+ if (count == -1)
+ {
+ count = mNumVerts-index;
+ }
+
+ bool mapped = false;
+ //see if range is already mapped
+ for (U32 i = 0; i < mMappedVertexRegions.size(); ++i)
+ {
+ MappedRegion& region = mMappedVertexRegions[i];
+ if (region.mType == type)
+ {
+ if (expand_region(region, index, count))
+ {
+ mapped = true;
+ break;
+ }
+ }
+ }
+
+ if (!mapped)
+ {
+ //not already mapped, map new region
+ MappedRegion region(type, mMappable && map_range ? -1 : index, count);
+ mMappedVertexRegions.push_back(region);
+ }
+ }
+
+ if (mVertexLocked && map_range)
+ {
+ llerrs << "Attempted to map a specific range of a buffer that was already mapped." << llendl;
+ }
+
+ if (!mVertexLocked)
+ {
+ LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES);
+ mVertexLocked = TRUE;
+ sMappedCount++;
+ stop_glerror();
+
+ if(!mMappable)
+ {
+ map_range = false;
+ }
+ else
+ {
+ volatile U8* src = NULL;
+ waitFence();
+ if (gGLManager.mHasMapBufferRange)
+ {
+ if (map_range)
+ {
+#ifdef GL_ARB_map_buffer_range
+ LLFastTimer t(FTM_VBO_MAP_BUFFER_RANGE);
+ S32 offset = mOffsets[type] + sTypeSize[type]*index;
+ S32 length = (sTypeSize[type]*count+0xF) & ~0xF;
+ src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER_ARB, offset, length,
+ GL_MAP_WRITE_BIT |
+ GL_MAP_FLUSH_EXPLICIT_BIT |
+ GL_MAP_INVALIDATE_RANGE_BIT);
+#endif
+ }
+ else
+ {
+#ifdef GL_ARB_map_buffer_range
+
+ if (gDebugGL)
+ {
+ GLint size = 0;
+ glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size);
+
+ if (size < mSize)
+ {
+ llerrs << "Invalid buffer size." << llendl;
+ }
+ }
+
+ LLFastTimer t(FTM_VBO_MAP_BUFFER);
+ src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER_ARB, 0, mSize,
+ GL_MAP_WRITE_BIT |
+ GL_MAP_FLUSH_EXPLICIT_BIT);
+#endif
+ }
+ }
+ else if (gGLManager.mHasFlushBufferRange)
+ {
+ if (map_range)
+ {
+ glBufferParameteriAPPLE(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE);
+ glBufferParameteriAPPLE(GL_ARRAY_BUFFER_ARB, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
+ src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+ }
+ else
+ {
+ src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+ }
+ }
+ else
+ {
+ map_range = false;
+ src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+ }
+
+ llassert(src != NULL);
+
+ mMappedData = LL_NEXT_ALIGNED_ADDRESS<volatile U8>(src);
+ mAlignedOffset = mMappedData - src;
+
+ stop_glerror();
+ }
+
+ if (!mMappedData)
+ {
+ log_glerror();
+
+ //check the availability of memory
+ LLMemory::logMemoryInfo(TRUE) ;
+
+ if(mMappable)
+ {
+ //--------------------
+ //print out more debug info before crash
+ llinfos << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << llendl ;
+ GLint size ;
+ glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size) ;
+ llinfos << "GL_ARRAY_BUFFER_ARB size is " << size << llendl ;
+ //--------------------
+
+ GLint buff;
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff);
+ if ((GLuint)buff != mGLBuffer)
+ {
+ llerrs << "Invalid GL vertex buffer bound: " << buff << llendl;
+ }
+
+
+ llerrs << "glMapBuffer returned NULL (no vertex data)" << llendl;
+ }
+ else
+ {
+ llerrs << "memory allocation for vertex data failed." << llendl ;
+ }
+ }
+ }
+ }
+ else
+ {
+ map_range = false;
+ }
+
+ if (map_range && gGLManager.mHasMapBufferRange && mMappable)
+ {
+ return mMappedData;
+ }
+ else
+ {
+ return mMappedData+mOffsets[type]+sTypeSize[type]*index;
+ }
+}
+
+
+static LLFastTimer::DeclareTimer FTM_VBO_MAP_INDEX_RANGE("IBO Map Range");
+static LLFastTimer::DeclareTimer FTM_VBO_MAP_INDEX("IBO Map");
+
+volatile U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range)
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
+ bindGLIndices(true);
+ if (mFinal)
+ {
+ llerrs << "LLVertexBuffer::mapIndexBuffer() called on a finalized buffer." << llendl;
+ }
+ if (!useVBOs() && !mMappedData && !mMappedIndexData)
+ {
+ llerrs << "LLVertexBuffer::mapIndexBuffer() called on unallocated buffer." << llendl;
+ }
+
+ if (useVBOs())
+ {
+ if (!mMappable || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
+ {
+ if (count == -1)
+ {
+ count = mNumIndices-index;
+ }
+
+ bool mapped = false;
+ //see if range is already mapped
+ for (U32 i = 0; i < mMappedIndexRegions.size(); ++i)
+ {
+ MappedRegion& region = mMappedIndexRegions[i];
+ if (expand_region(region, index, count))
+ {
+ mapped = true;
+ break;
+ }
+ }
+
+ if (!mapped)
+ {
+ //not already mapped, map new region
+ MappedRegion region(TYPE_INDEX, mMappable && map_range ? -1 : index, count);
+ mMappedIndexRegions.push_back(region);
+ }
+ }
+
+ if (mIndexLocked && map_range)
+ {
+ llerrs << "Attempted to map a specific range of a buffer that was already mapped." << llendl;
+ }
+
+ if (!mIndexLocked)
+ {
+ LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES);
+
+ mIndexLocked = TRUE;
+ sMappedCount++;
+ stop_glerror();
+
+ if (gDebugGL && useVBOs())
+ {
+ GLint elem = 0;
+ glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &elem);
+
+ if (elem != mGLIndices)
+ {
+ llerrs << "Wrong index buffer bound!" << llendl;
+ }
+ }
+
+ if(!mMappable)
+ {
+ map_range = false;
+ }
+ else
+ {
+ volatile U8* src = NULL;
+ waitFence();
+ if (gGLManager.mHasMapBufferRange)
+ {
+ if (map_range)
+ {
+#ifdef GL_ARB_map_buffer_range
+ LLFastTimer t(FTM_VBO_MAP_INDEX_RANGE);
+ S32 offset = sizeof(U16)*index;
+ S32 length = sizeof(U16)*count;
+ src = (U8*) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length,
+ GL_MAP_WRITE_BIT |
+ GL_MAP_FLUSH_EXPLICIT_BIT |
+ GL_MAP_INVALIDATE_RANGE_BIT);
+#endif
+ }
+ else
+ {
+#ifdef GL_ARB_map_buffer_range
+ LLFastTimer t(FTM_VBO_MAP_INDEX);
+ src = (U8*) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, sizeof(U16)*mNumIndices,
+ GL_MAP_WRITE_BIT |
+ GL_MAP_FLUSH_EXPLICIT_BIT);
+#endif
+ }
+ }
+ else if (gGLManager.mHasFlushBufferRange)
+ {
+ if (map_range)
+ {
+ glBufferParameteriAPPLE(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE);
+ glBufferParameteriAPPLE(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
+ src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+ }
+ else
+ {
+ src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+ }
+ }
+ else
+ {
+ LLFastTimer t(FTM_VBO_MAP_INDEX);
+ map_range = false;
+ src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+ }
+
+ llassert(src != NULL);
+
+
+ mMappedIndexData = src; //LL_NEXT_ALIGNED_ADDRESS<U8>(src);
+ mAlignedIndexOffset = mMappedIndexData - src;
+ stop_glerror();
+ }
+ }
+
+ if (!mMappedIndexData)
+ {
+ log_glerror();
+ LLMemory::logMemoryInfo(TRUE) ;
+
+ if(mMappable)
+ {
+ GLint buff;
+ glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff);
+ if ((GLuint)buff != mGLIndices)
+ {
+ llerrs << "Invalid GL index buffer bound: " << buff << llendl;
+ }
+
+ llerrs << "glMapBuffer returned NULL (no index data)" << llendl;
+ }
+ else
+ {
+ llerrs << "memory allocation for Index data failed. " << llendl ;
+ }
+ }
+ }
+ else
+ {
+ map_range = false;
+ }
+
+ if (map_range && gGLManager.mHasMapBufferRange && mMappable)
+ {
+ return mMappedIndexData;
+ }
+ else
+ {
+ return mMappedIndexData + sizeof(U16)*index;
+ }
+}
+
+static LLFastTimer::DeclareTimer FTM_VBO_UNMAP("VBO Unmap");
+static LLFastTimer::DeclareTimer FTM_VBO_FLUSH_RANGE("Flush VBO Range");
+
+
+static LLFastTimer::DeclareTimer FTM_IBO_UNMAP("IBO Unmap");
+static LLFastTimer::DeclareTimer FTM_IBO_FLUSH_RANGE("Flush IBO Range");
+
+void LLVertexBuffer::unmapBuffer()
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_UNMAP_BUFFER);
+ if (!useVBOs())
+ {
+ return ; //nothing to unmap
+ }
+
+ bool updated_all = false ;
+
+ if (mMappedData && mVertexLocked)
+ {
+ LLFastTimer t(FTM_VBO_UNMAP);
+ bindGLBuffer(true);
+ updated_all = mIndexLocked; //both vertex and index buffers done updating
+
+ if(!mMappable)
+ {
+ if (!mMappedVertexRegions.empty())
+ {
+ stop_glerror();
+ for (U32 i = 0; i < mMappedVertexRegions.size(); ++i)
+ {
+ const MappedRegion& region = mMappedVertexRegions[i];
+ S32 offset = region.mIndex >= 0 ? mOffsets[region.mType]+sTypeSize[region.mType]*region.mIndex : 0;
+ S32 length = sTypeSize[region.mType]*region.mCount;
+ glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, offset, length, (U8*) mMappedData+offset);
+ stop_glerror();
+ }
+
+ mMappedVertexRegions.clear();
+ }
+ else
+ {
+ stop_glerror();
+ glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, getSize(), (U8*) mMappedData);
+ stop_glerror();
+ }
+ }
+ else
+ {
+ if (gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
+ {
+ if (!mMappedVertexRegions.empty())
+ {
+ stop_glerror();
+ for (U32 i = 0; i < mMappedVertexRegions.size(); ++i)
+ {
+ const MappedRegion& region = mMappedVertexRegions[i];
+ S32 offset = region.mIndex >= 0 ? mOffsets[region.mType]+sTypeSize[region.mType]*region.mIndex : 0;
+ S32 length = sTypeSize[region.mType]*region.mCount;
+ if (gGLManager.mHasMapBufferRange)
+ {
+ LLFastTimer t(FTM_VBO_FLUSH_RANGE);
+#ifdef GL_ARB_map_buffer_range
+ glFlushMappedBufferRange(GL_ARRAY_BUFFER_ARB, offset, length);
+#endif
+ }
+ else if (gGLManager.mHasFlushBufferRange)
+ {
+ glFlushMappedBufferRangeAPPLE(GL_ARRAY_BUFFER_ARB, offset, length);
+ }
+ stop_glerror();
+ }
+
+ mMappedVertexRegions.clear();
+ }
+ }
+ stop_glerror();
+ glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
+ stop_glerror();
+
+ mMappedData = NULL;
+ }
+
+ mVertexLocked = FALSE ;
+ sMappedCount--;
+ }
+
+ if (mMappedIndexData && mIndexLocked)
+ {
+ LLFastTimer t(FTM_IBO_UNMAP);
+ bindGLIndices();
+ if(!mMappable)
+ {
+ if (!mMappedIndexRegions.empty())
+ {
+ for (U32 i = 0; i < mMappedIndexRegions.size(); ++i)
+ {
+ const MappedRegion& region = mMappedIndexRegions[i];
+ S32 offset = region.mIndex >= 0 ? sizeof(U16)*region.mIndex : 0;
+ S32 length = sizeof(U16)*region.mCount;
+ glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length, (U8*) mMappedIndexData+offset);
+ stop_glerror();
+ }
+
+ mMappedIndexRegions.clear();
+ }
+ else
+ {
+ stop_glerror();
+ glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, getIndicesSize(), (U8*) mMappedIndexData);
+ stop_glerror();
+ }
+ }
+ else
+ {
+ if (gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
+ {
+ if (!mMappedIndexRegions.empty())
+ {
+ for (U32 i = 0; i < mMappedIndexRegions.size(); ++i)
+ {
+ const MappedRegion& region = mMappedIndexRegions[i];
+ S32 offset = region.mIndex >= 0 ? sizeof(U16)*region.mIndex : 0;
+ S32 length = sizeof(U16)*region.mCount;
+ if (gGLManager.mHasMapBufferRange)
+ {
+ LLFastTimer t(FTM_IBO_FLUSH_RANGE);
+#ifdef GL_ARB_map_buffer_range
+ glFlushMappedBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length);
+#endif
+ }
+ else if (gGLManager.mHasFlushBufferRange)
+ {
+#ifdef GL_APPLE_flush_buffer_range
+ glFlushMappedBufferRangeAPPLE(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length);
+#endif
+ }
+ stop_glerror();
+ }
+
+ mMappedIndexRegions.clear();
+ }
+ }
+ stop_glerror();
+ glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
+ stop_glerror();
+
+ mMappedIndexData = NULL ;
+ }
+
+ mIndexLocked = FALSE ;
+ sMappedCount--;
+ }
+
+ if(updated_all)
+ {
+ mEmpty = FALSE;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+template <class T,S32 type> struct VertexBufferStrider
+{
+ typedef LLStrider<T> strider_t;
+ static bool get(LLVertexBuffer& vbo,
+ strider_t& strider,
+ S32 index, S32 count, bool map_range)
+ {
+ if (type == LLVertexBuffer::TYPE_INDEX)
+ {
+ volatile U8* ptr = vbo.mapIndexBuffer(index, count, map_range);
+
+ if (ptr == NULL)
+ {
+ llwarns << "mapIndexBuffer failed!" << llendl;
+ return FALSE;
+ }
+
+ strider = (T*)ptr;
+ strider.setStride(0);
+ return TRUE;
+ }
+ else if (vbo.hasDataType(type))
+ {
+ S32 stride = LLVertexBuffer::sTypeSize[type];
+
+ volatile U8* ptr = vbo.mapVertexBuffer(type, index, count, map_range);
+
+ if (ptr == NULL)
+ {
+ llwarns << "mapVertexBuffer failed!" << llendl;
+ return FALSE;
+ }
+
+ strider = (T*)ptr;
+ strider.setStride(stride);
+ return TRUE;
+ }
+ else
+ {
+ llerrs << "VertexBufferStrider could not find valid vertex data." << llendl;
+ }
+ return FALSE;
+ }
+};
+
+bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLVector3,TYPE_VERTEX>::get(*this, strider, index, count, map_range);
+}
+bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector4a>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLVector4a,TYPE_VERTEX>::get(*this, strider, index, count, map_range);
+}
+bool LLVertexBuffer::getIndexStrider(LLStrider<U16>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<U16,TYPE_INDEX>::get(*this, strider, index, count, map_range);
+}
+bool LLVertexBuffer::getTexCoord0Strider(LLStrider<LLVector2>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLVector2,TYPE_TEXCOORD0>::get(*this, strider, index, count, map_range);
+}
+bool LLVertexBuffer::getTexCoord1Strider(LLStrider<LLVector2>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLVector2,TYPE_TEXCOORD1>::get(*this, strider, index, count, map_range);
+}
+
+bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLVector3,TYPE_NORMAL>::get(*this, strider, index, count, map_range);
+}
+bool LLVertexBuffer::getBinormalStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLVector3,TYPE_BINORMAL>::get(*this, strider, index, count, map_range);
+}
+bool LLVertexBuffer::getColorStrider(LLStrider<LLColor4U>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLColor4U,TYPE_COLOR>::get(*this, strider, index, count, map_range);
+}
+bool LLVertexBuffer::getEmissiveStrider(LLStrider<LLColor4U>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLColor4U,TYPE_EMISSIVE>::get(*this, strider, index, count, map_range);
+}
+bool LLVertexBuffer::getWeightStrider(LLStrider<F32>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<F32,TYPE_WEIGHT>::get(*this, strider, index, count, map_range);
+}
+
+bool LLVertexBuffer::getWeight4Strider(LLStrider<LLVector4>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLVector4,TYPE_WEIGHT4>::get(*this, strider, index, count, map_range);
+}
+
+bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index, S32 count, bool map_range)
+{
+ return VertexBufferStrider<LLVector4,TYPE_CLOTHWEIGHT>::get(*this, strider, index, count, map_range);
+}
+
+//----------------------------------------------------------------------------
+
+static LLFastTimer::DeclareTimer FTM_BIND_GL_ARRAY("Bind Array");
+bool LLVertexBuffer::bindGLArray()
+{
+ if (mGLArray && sGLRenderArray != mGLArray)
+ {
+ {
+ LLFastTimer t(FTM_BIND_GL_ARRAY);
+#if GL_ARB_vertex_array_object
+ glBindVertexArray(mGLArray);
+#endif
+ sGLRenderArray = mGLArray;
+ }
+
+ //really shouldn't be necessary, but some drivers don't properly restore the
+ //state of GL_ELEMENT_ARRAY_BUFFER_BINDING
+ bindGLIndices();
+
+ return true;
+ }
+
+ return false;
+}
+
+static LLFastTimer::DeclareTimer FTM_BIND_GL_BUFFER("Bind Buffer");
+
+bool LLVertexBuffer::bindGLBuffer(bool force_bind)
+{
+ bindGLArray();
+
+ bool ret = false;
+
+ if (useVBOs() && (force_bind || (mGLBuffer && (mGLBuffer != sGLRenderBuffer || !sVBOActive))))
+ {
+ LLFastTimer t(FTM_BIND_GL_BUFFER);
+ /*if (sMapped)
+ {
+ llerrs << "VBO bound while another VBO mapped!" << llendl;
+ }*/
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, mGLBuffer);
+ sGLRenderBuffer = mGLBuffer;
+ sBindCount++;
+ sVBOActive = TRUE;
+
+ if (mGLArray)
+ {
+ llassert(sGLRenderArray == mGLArray);
+ //mCachedRenderBuffer = mGLBuffer;
+ }
+
+ ret = true;
+ }
+
+ return ret;
+}
+
+static LLFastTimer::DeclareTimer FTM_BIND_GL_INDICES("Bind Indices");
+
+bool LLVertexBuffer::bindGLIndices(bool force_bind)
+{
+ bindGLArray();
+
+ bool ret = false;
+ if (useVBOs() && (force_bind || (mGLIndices && (mGLIndices != sGLRenderIndices || !sIBOActive))))
+ {
+ LLFastTimer t(FTM_BIND_GL_INDICES);
+ /*if (sMapped)
+ {
+ llerrs << "VBO bound while another VBO mapped!" << llendl;
+ }*/
+ glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mGLIndices);
+ sGLRenderIndices = mGLIndices;
+ stop_glerror();
+ sBindCount++;
+ sIBOActive = TRUE;
+ ret = true;
+ }
+
+ return ret;
+}
+
+void LLVertexBuffer::flush()
+{
+ if (useVBOs())
+ {
+ unmapBuffer();
+ }
+}
+
+// Set for rendering
+void LLVertexBuffer::setBuffer(U32 data_mask)
+{
+ flush();
+
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_SET_BUFFER);
+ //set up pointers if the data mask is different ...
+ BOOL setup = (sLastMask != data_mask);
+
+ if (gDebugGL && data_mask != 0)
+ { //make sure data requirements are fulfilled
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+ if (shader)
+ {
+ U32 required_mask = 0;
+ for (U32 i = 0; i < LLVertexBuffer::TYPE_TEXTURE_INDEX; ++i)
+ {
+ if (shader->getAttribLocation(i) > -1)
+ {
+ U32 required = 1 << i;
+ if ((data_mask & required) == 0)
+ {
+ llwarns << "Missing attribute: " << LLShaderMgr::instance()->mReservedAttribs[i] << llendl;
+ }
+
+ required_mask |= required;
+ }
+ }
+
+ if ((data_mask & required_mask) != required_mask)
+ {
+ llerrs << "Shader consumption mismatches data provision." << llendl;
+ }
+ }
+ }
+
+ if (useVBOs())
+ {
+ if (mGLArray)
+ {
+ bindGLArray();
+ setup = FALSE; //do NOT perform pointer setup if using VAO
+ }
+ else
+ {
+ if (bindGLBuffer())
+ {
+ setup = TRUE;
+ }
+ if (bindGLIndices())
+ {
+ setup = TRUE;
+ }
+ }
+
+ BOOL error = FALSE;
+ if (gDebugGL && !mGLArray)
+ {
+ GLint buff;
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff);
+ if ((GLuint)buff != mGLBuffer)
+ {
+ if (gDebugSession)
+ {
+ error = TRUE;
+ gFailLog << "Invalid GL vertex buffer bound: " << buff << std::endl;
+ }
+ else
+ {
+ llerrs << "Invalid GL vertex buffer bound: " << buff << llendl;
+ }
+ }
+
+ if (mGLIndices)
+ {
+ glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff);
+ if ((GLuint)buff != mGLIndices)
+ {
+ if (gDebugSession)
+ {
+ error = TRUE;
+ gFailLog << "Invalid GL index buffer bound: " << buff << std::endl;
+ }
+ else
+ {
+ llerrs << "Invalid GL index buffer bound: " << buff << llendl;
+ }
+ }
+ }
+ }
+
+
+ }
+ else
+ {
+ if (sGLRenderArray)
+ {
+#if GL_ARB_vertex_array_object
+ glBindVertexArray(0);
+#endif
+ sGLRenderArray = 0;
+ sGLRenderIndices = 0;
+ sIBOActive = FALSE;
+ }
+
+ if (mGLBuffer)
+ {
+ if (sVBOActive)
+ {
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+ sBindCount++;
+ sVBOActive = FALSE;
+ setup = TRUE; // ... or a VBO is deactivated
+ }
+ if (sGLRenderBuffer != mGLBuffer)
+ {
+ sGLRenderBuffer = mGLBuffer;
+ setup = TRUE; // ... or a client memory pointer changed
+ }
+ }
+ if (mGLIndices)
+ {
+ if (sIBOActive)
+ {
+ glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+ sBindCount++;
+ sIBOActive = FALSE;
+ }
+
+ sGLRenderIndices = mGLIndices;
+ }
+ }
+
+ if (!mGLArray)
+ {
+ setupClientArrays(data_mask);
+ }
+
+ if (mGLBuffer)
+ {
+ if (data_mask && setup)
+ {
+ setupVertexBuffer(data_mask); // subclass specific setup (virtual function)
+ sSetCount++;
+ }
+ }
+}
+
+// virtual (default)
+void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER);
+ stop_glerror();
+ volatile U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
+
+ /*if ((data_mask & mTypeMask) != data_mask)
+ {
+ llerrs << "LLVertexBuffer::setupVertexBuffer missing required components for supplied data mask." << llendl;
+ }*/
+
+ if (LLGLSLShader::sNoFixedFunction)
+ {
+ if (data_mask & MAP_NORMAL)
+ {
+ S32 loc = TYPE_NORMAL;
+ void* ptr = (void*)(base + mOffsets[TYPE_NORMAL]);
+ glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_NORMAL], ptr);
+ }
+ if (data_mask & MAP_TEXCOORD3)
+ {
+ S32 loc = TYPE_TEXCOORD3;
+ void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD3]);
+ glVertexAttribPointerARB(loc,2,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD3], ptr);
+ }
+ if (data_mask & MAP_TEXCOORD2)
+ {
+ S32 loc = TYPE_TEXCOORD2;
+ void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD2]);
+ glVertexAttribPointerARB(loc,2,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD2], ptr);
+ }
+ if (data_mask & MAP_TEXCOORD1)
+ {
+ S32 loc = TYPE_TEXCOORD1;
+ void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD1]);
+ glVertexAttribPointerARB(loc,2,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], ptr);
+ }
+ if (data_mask & MAP_BINORMAL)
+ {
+ S32 loc = TYPE_BINORMAL;
+ void* ptr = (void*)(base + mOffsets[TYPE_BINORMAL]);
+ glVertexAttribPointerARB(loc, 3,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_BINORMAL], ptr);
+ }
+ if (data_mask & MAP_TEXCOORD0)
+ {
+ S32 loc = TYPE_TEXCOORD0;
+ void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD0]);
+ glVertexAttribPointerARB(loc,2,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], ptr);
+ }
+ if (data_mask & MAP_COLOR)
+ {
+ S32 loc = TYPE_COLOR;
+ void* ptr = (void*)(base + mOffsets[TYPE_COLOR]);
+ glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_COLOR], ptr);
+ }
+ if (data_mask & MAP_EMISSIVE)
+ {
+ S32 loc = TYPE_EMISSIVE;
+ void* ptr = (void*)(base + mOffsets[TYPE_EMISSIVE]);
+ glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_EMISSIVE], ptr);
+ }
+ if (data_mask & MAP_WEIGHT)
+ {
+ S32 loc = TYPE_WEIGHT;
+ void* ptr = (void*)(base + mOffsets[TYPE_WEIGHT]);
+ glVertexAttribPointerARB(loc, 1, GL_FLOAT, FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT], ptr);
+ }
+ if (data_mask & MAP_WEIGHT4)
+ {
+ S32 loc = TYPE_WEIGHT4;
+ void* ptr = (void*)(base+mOffsets[TYPE_WEIGHT4]);
+ glVertexAttribPointerARB(loc, 4, GL_FLOAT, FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT4], ptr);
+ }
+ if (data_mask & MAP_CLOTHWEIGHT)
+ {
+ S32 loc = TYPE_CLOTHWEIGHT;
+ void* ptr = (void*)(base + mOffsets[TYPE_CLOTHWEIGHT]);
+ glVertexAttribPointerARB(loc, 4, GL_FLOAT, TRUE, LLVertexBuffer::sTypeSize[TYPE_CLOTHWEIGHT], ptr);
+ }
+ if (data_mask & MAP_TEXTURE_INDEX)
+ {
+ S32 loc = TYPE_TEXTURE_INDEX;
+ void *ptr = (void*) (base + mOffsets[TYPE_VERTEX] + 12);
+ glVertexAttribPointerARB(loc, 1, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
+ }
+ if (data_mask & MAP_VERTEX)
+ {
+ S32 loc = TYPE_VERTEX;
+ void* ptr = (void*)(base + mOffsets[TYPE_VERTEX]);
+ glVertexAttribPointerARB(loc, 3,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
+ }
+ }
+ else
+ {
+ if (data_mask & MAP_NORMAL)
+ {
+ glNormalPointer(GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_NORMAL], (void*)(base + mOffsets[TYPE_NORMAL]));
+ }
+ if (data_mask & MAP_TEXCOORD3)
+ {
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+ glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD3], (void*)(base + mOffsets[TYPE_TEXCOORD3]));
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+ if (data_mask & MAP_TEXCOORD2)
+ {
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD2], (void*)(base + mOffsets[TYPE_TEXCOORD2]));
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+ if (data_mask & MAP_TEXCOORD1)
+ {
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], (void*)(base + mOffsets[TYPE_TEXCOORD1]));
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+ if (data_mask & MAP_BINORMAL)
+ {
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glTexCoordPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_BINORMAL], (void*)(base + mOffsets[TYPE_BINORMAL]));
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+ if (data_mask & MAP_TEXCOORD0)
+ {
+ glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0]));
+ }
+ if (data_mask & MAP_COLOR)
+ {
+ glColorPointer(4, GL_UNSIGNED_BYTE, LLVertexBuffer::sTypeSize[TYPE_COLOR], (void*)(base + mOffsets[TYPE_COLOR]));
+ }
+ if (data_mask & MAP_VERTEX)
+ {
+ glVertexPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_VERTEX], (void*)(base + 0));
+ }
+ }
+
+ llglassertok();
+}
+
+LLVertexBuffer::MappedRegion::MappedRegion(S32 type, S32 index, S32 count)
+: mType(type), mIndex(index), mCount(count)
+{
+ llassert(mType == LLVertexBuffer::TYPE_INDEX ||
+ mType < LLVertexBuffer::TYPE_TEXTURE_INDEX);
+}
+
+
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 466fac33ea..f7017064d7 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -389,6 +389,22 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
return ret;
}
+S32 LLScrollListCtrl::getNumSelected() const
+{
+ S32 numSelected = 0;
+
+ for(item_list::const_iterator iter = mItemList.begin(); iter != mItemList.end(); ++iter)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ ++numSelected;
+ }
+ }
+
+ return numSelected;
+}
+
S32 LLScrollListCtrl::getFirstSelectedIndex() const
{
S32 CurSelectedIndex = 0;
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index ae8aea9245..44d9635838 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -257,6 +257,7 @@ public:
LLScrollListItem* getFirstSelected() const;
virtual S32 getFirstSelectedIndex() const;
std::vector<LLScrollListItem*> getAllSelected() const;
+ S32 getNumSelected() const;
LLScrollListItem* getLastSelectedItem() const { return mLastSelected; }
// iterate over all items
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index f85b943c70..76aa982f1d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -17,12 +17,12 @@ include(JsonCpp)
include(LLAudio)
include(LLCharacter)
include(LLCommon)
-include(LLConvexDecomposition)
include(LLImage)
include(LLImageJ2COJ)
include(LLInventory)
include(LLMath)
include(LLMessage)
+include(LLPhysicsExtensions)
include(LLPlugin)
include(LLPrimitive)
include(LLRender)
@@ -51,7 +51,7 @@ include_directories(
${LLAUDIO_INCLUDE_DIRS}
${LLCHARACTER_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
- ${LLCONVEXDECOMP_INCLUDE_DIRS}
+ ${LLPHYSICS_INCLUDE_DIRS}
${FMOD_INCLUDE_DIR}
${LLIMAGE_INCLUDE_DIRS}
${LLKDU_INCLUDE_DIRS}
@@ -162,6 +162,7 @@ set(viewer_SOURCE_FILES
llfavoritesbar.cpp
llfeaturemanager.cpp
llfilepicker.cpp
+ llfilteredpathfindinglinksets.cpp
llfilteredwearablelist.cpp
llfirstuse.cpp
llflexibleobject.cpp
@@ -217,6 +218,9 @@ set(viewer_SOURCE_FILES
llfloaterobjectweights.cpp
llfloateropenobject.cpp
llfloateroutbox.cpp
+ llfloaterpathfindingcharacters.cpp
+ llfloaterpathfindinglinksets.cpp
+ llfloaterpathfindingsetup.cpp
llfloaterpay.cpp
llfloaterperms.cpp
llfloaterpostprocess.cpp
@@ -329,6 +333,7 @@ set(viewer_SOURCE_FILES
llnameeditor.cpp
llnamelistctrl.cpp
llnavigationbar.cpp
+ llnavmeshstation.cpp
llnearbychat.cpp
llnearbychatbar.cpp
llnearbychathandler.cpp
@@ -412,6 +417,8 @@ set(viewer_SOURCE_FILES
llparcelselection.cpp
llparticipantlist.cpp
llpatchvertexarray.cpp
+ llpathfindingcharacter.cpp
+ llpathfindinglinkset.cpp
llphysicsmotion.cpp
llphysicsshapebuilderutil.cpp
llplacesinventorybridge.cpp
@@ -718,6 +725,7 @@ set(viewer_HEADER_FILES
llfavoritesbar.h
llfeaturemanager.h
llfilepicker.h
+ llfilteredpathfindinglinksets.h
llfilteredwearablelist.h
llfirstuse.h
llflexibleobject.h
@@ -773,6 +781,9 @@ set(viewer_HEADER_FILES
llfloaterobjectweights.h
llfloateropenobject.h
llfloateroutbox.h
+ llfloaterpathfindingcharacters.h
+ llfloaterpathfindinglinksets.h
+ llfloaterpathfindingsetup.h
llfloaterpay.h
llfloaterperms.h
llfloaterpostprocess.h
@@ -885,6 +896,7 @@ set(viewer_HEADER_FILES
llnameeditor.h
llnamelistctrl.h
llnavigationbar.h
+ llnavmeshstation.h
llnearbychat.h
llnearbychatbar.h
llnearbychathandler.h
@@ -957,6 +969,8 @@ set(viewer_HEADER_FILES
llparcelselection.h
llparticipantlist.h
llpatchvertexarray.h
+ llpathfindingcharacter.h
+ llpathfindinglinkset.h
llphysicsmotion.h
llphysicsshapebuilderutil.h
llplacesinventorybridge.h
@@ -1513,9 +1527,9 @@ if (WINDOWS)
PROPERTIES
# *TODO -reenable this once we get server usage sorted out
#LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:\"__tcmalloc\""
- LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:__tcmalloc"
+ LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:__tcmalloc "
LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO"
- LINK_FLAGS_RELEASE ""
+ LINK_FLAGS_RELEASE "/FORCE:MULTIPLE"
)
if(USE_PRECOMPILED_HEADERS)
set_target_properties(
@@ -1729,6 +1743,7 @@ endif (WINDOWS)
# To work around this, higher level modules should be listed before the modules
# that they depend upon. -brad
target_link_libraries(${VIEWER_BINARY_NAME}
+ ${LLPATHING_LIBRARIES}
${UPDATER_LIBRARIES}
${GOOGLE_PERFTOOLS_LIBRARIES}
${LLAUDIO_LIBRARIES}
@@ -1767,7 +1782,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${LLLOGIN_LIBRARIES}
- ${LLCONVEXDECOMP_LIBRARY}
+ ${LLPHYSICS_LIBRARIES}
${TCMALLOC_LIBRARIES}
)
diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml
index 1d1d39c786..4ff6b6f37d 100644
--- a/indra/newview/app_settings/commands.xml
+++ b/indra/newview/app_settings/commands.xml
@@ -155,6 +155,36 @@
is_running_function="Floater.IsOpen"
is_running_parameters="people"
/>
+ <command name="pathfinding_setup"
+ available_in_toybox="false"
+ icon="Command_Pathfinding_Icon"
+ label_ref="Command_Pathfinding_Label"
+ tooltip_ref="Command_Pathfinding_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="pathfinding_setup"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="pathfinding_setup"
+ />
+ <command name="pathfinding_characters"
+ available_in_toybox="false"
+ icon="Command_PF_Characters_Icon"
+ label_ref="Command_PF_Characters_Label"
+ tooltip_ref="Command_PF_Characters_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="pathfinding_characters"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="pathfinding_characters"
+ />
+ <command name="pathfinding_linksets"
+ available_in_toybox="false"
+ icon="Command_PF_Linksets_Icon"
+ label_ref="Command_PF_Linksets_Label"
+ tooltip_ref="Command_PF_Linksets_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="pathfinding_linksets"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="pathfinding_linksets"
+ />
<command name="picks"
available_in_toybox="true"
icon="Command_Picks_Icon"
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 49fbdbf1df..261553599b 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -106,6 +106,7 @@
#include "llvfsthread.h"
#include "llvolumemgr.h"
#include "llxfermanager.h"
+#include "LLPhysicsExtensions.h"
#include "llnotificationmanager.h"
#include "llnotifications.h"
@@ -1545,6 +1546,9 @@ bool LLAppViewer::cleanup()
// shut down mesh streamer
gMeshRepo.shutdown();
+ // shut down Havok
+ LLPhysicsExtensions::quitSystem();
+
// Must clean up texture references before viewer window is destroyed.
if(LLHUDManager::instanceExists())
{
diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp
index b539ac38ed..b539ac38ed 100755..100644
--- a/indra/newview/llavatariconctrl.cpp
+++ b/indra/newview/llavatariconctrl.cpp
diff --git a/indra/newview/llfilteredpathfindinglinksets.cpp b/indra/newview/llfilteredpathfindinglinksets.cpp
new file mode 100644
index 0000000000..aaff2bcc68
--- /dev/null
+++ b/indra/newview/llfilteredpathfindinglinksets.cpp
@@ -0,0 +1,313 @@
+/**
+ * @file llfilteredpathfindinglinksets.h
+ * @author William Todd Stinson
+ * @brief Class to implement the filtering of a set of pathfinding linksets
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <string>
+#include <map>
+
+#include "llsd.h"
+#include "lluuid.h"
+#include "llpathfindinglinkset.h"
+#include "llfilteredpathfindinglinksets.h"
+
+//---------------------------------------------------------------------------
+// FilterString
+//---------------------------------------------------------------------------
+
+FilterString::FilterString()
+ : mFilter(),
+ mUpperFilter()
+{
+}
+
+FilterString::FilterString(const std::string& pFilter)
+ : mFilter(pFilter),
+ mUpperFilter()
+{
+ LLStringUtil::trim(mFilter);
+ mUpperFilter = mFilter;
+ if (!mUpperFilter.empty())
+ {
+ LLStringUtil::toUpper(mUpperFilter);
+ }
+}
+
+FilterString::~FilterString()
+{
+}
+
+const std::string& FilterString::get() const
+{
+ return mFilter;
+}
+
+bool FilterString::set(const std::string& pFilter)
+{
+ std::string newFilter(pFilter);
+ LLStringUtil::trim(newFilter);
+ bool didFilterChange = (mFilter.compare(newFilter) != 0);
+ if (didFilterChange)
+ {
+ mFilter = newFilter;
+ mUpperFilter = newFilter;
+ LLStringUtil::toUpper(mUpperFilter);
+ }
+
+ return didFilterChange;
+}
+
+void FilterString::clear()
+{
+ mFilter.clear();
+ mUpperFilter.clear();
+}
+
+bool FilterString::isActive() const
+{
+ return !mFilter.empty();
+}
+
+bool FilterString::doesMatch(const std::string& pTestString) const
+{
+ bool doesMatch = true;
+
+ if (isActive())
+ {
+ std::string upperTestString(pTestString);
+ LLStringUtil::toUpper(upperTestString);
+ doesMatch = (upperTestString.find(mUpperFilter) != std::string::npos);
+ }
+
+ return doesMatch;
+}
+
+//---------------------------------------------------------------------------
+// LLFilteredPathfindingLinksets
+//---------------------------------------------------------------------------
+
+LLFilteredPathfindingLinksets::LLFilteredPathfindingLinksets()
+ : mAllLinksets(),
+ mFilteredLinksets(),
+ mIsFiltersDirty(false),
+ mNameFilter(),
+ mDescriptionFilter(),
+ mIsWalkableFilter(true),
+ mIsObstacleFilter(true),
+ mIsIgnoredFilter(true)
+{
+}
+
+LLFilteredPathfindingLinksets::LLFilteredPathfindingLinksets(const LLSD& pLinksetItems)
+ : mAllLinksets(),
+ mFilteredLinksets(),
+ mIsFiltersDirty(false),
+ mNameFilter(),
+ mDescriptionFilter(),
+ mIsWalkableFilter(true),
+ mIsObstacleFilter(true),
+ mIsIgnoredFilter(true)
+{
+ setPathfindingLinksets(pLinksetItems);
+}
+
+LLFilteredPathfindingLinksets::LLFilteredPathfindingLinksets(const LLFilteredPathfindingLinksets& pOther)
+ : mAllLinksets(pOther.mAllLinksets),
+ mFilteredLinksets(pOther.mFilteredLinksets),
+ mIsFiltersDirty(pOther.mIsFiltersDirty),
+ mNameFilter(pOther.mNameFilter),
+ mDescriptionFilter(pOther.mDescriptionFilter),
+ mIsWalkableFilter(pOther.mIsWalkableFilter),
+ mIsObstacleFilter(pOther.mIsObstacleFilter),
+ mIsIgnoredFilter(pOther.mIsIgnoredFilter)
+{
+}
+
+LLFilteredPathfindingLinksets::~LLFilteredPathfindingLinksets()
+{
+ clearPathfindingLinksets();
+}
+
+void LLFilteredPathfindingLinksets::setPathfindingLinksets(const LLSD& pLinksetItems)
+{
+ clearPathfindingLinksets();
+
+ for (LLSD::map_const_iterator linksetItemIter = pLinksetItems.beginMap();
+ linksetItemIter != pLinksetItems.endMap(); ++linksetItemIter)
+ {
+ const std::string& uuid(linksetItemIter->first);
+ const LLSD& linksetData = linksetItemIter->second;
+ LLPathfindingLinkset linkset(uuid, linksetData);
+
+ mAllLinksets.insert(std::pair<std::string, LLPathfindingLinkset>(uuid, linkset));
+ }
+
+ mIsFiltersDirty = true;
+}
+
+void LLFilteredPathfindingLinksets::updatePathfindingLinksets(const LLSD& pLinksetItems)
+{
+ for (LLSD::map_const_iterator linksetItemIter = pLinksetItems.beginMap();
+ linksetItemIter != pLinksetItems.endMap(); ++linksetItemIter)
+ {
+ const std::string& uuid(linksetItemIter->first);
+ const LLSD& linksetData = linksetItemIter->second;
+ LLPathfindingLinkset linkset(uuid, linksetData);
+
+ PathfindingLinksetMap::iterator linksetIter = mAllLinksets.find(uuid);
+ if (linksetIter == mAllLinksets.end())
+ {
+ mAllLinksets.insert(std::pair<std::string, LLPathfindingLinkset>(uuid, linkset));
+ }
+ else
+ {
+ linksetIter->second = linkset;
+ }
+ }
+
+ mIsFiltersDirty = true;
+}
+
+void LLFilteredPathfindingLinksets::clearPathfindingLinksets()
+{
+ mAllLinksets.clear();
+ mFilteredLinksets.clear();
+ mIsFiltersDirty = false;
+}
+
+const LLFilteredPathfindingLinksets::PathfindingLinksetMap& LLFilteredPathfindingLinksets::getAllLinksets() const
+{
+ return mAllLinksets;
+}
+
+const LLFilteredPathfindingLinksets::PathfindingLinksetMap& LLFilteredPathfindingLinksets::getFilteredLinksets()
+{
+ if (!isFiltersActive())
+ {
+ return mAllLinksets;
+ }
+ else
+ {
+ applyFilters();
+ return mFilteredLinksets;
+ }
+}
+
+BOOL LLFilteredPathfindingLinksets::isFiltersActive() const
+{
+ return (mNameFilter.isActive() || mDescriptionFilter.isActive() || !mIsWalkableFilter || !mIsObstacleFilter || !mIsIgnoredFilter);
+}
+
+void LLFilteredPathfindingLinksets::setNameFilter(const std::string& pNameFilter)
+{
+ mIsFiltersDirty = (mNameFilter.set(pNameFilter) || mIsFiltersDirty);
+}
+
+const std::string& LLFilteredPathfindingLinksets::getNameFilter() const
+{
+ return mNameFilter.get();
+}
+
+void LLFilteredPathfindingLinksets::setDescriptionFilter(const std::string& pDescriptionFilter)
+{
+ mIsFiltersDirty = (mDescriptionFilter.set(pDescriptionFilter) || mIsFiltersDirty);
+}
+
+const std::string& LLFilteredPathfindingLinksets::getDescriptionFilter() const
+{
+ return mDescriptionFilter.get();
+}
+
+void LLFilteredPathfindingLinksets::setWalkableFilter(BOOL pWalkableFilter)
+{
+ mIsFiltersDirty = (mIsFiltersDirty || (mIsWalkableFilter == pWalkableFilter));
+ mIsWalkableFilter = pWalkableFilter;
+}
+
+BOOL LLFilteredPathfindingLinksets::isWalkableFilter() const
+{
+ return mIsWalkableFilter;
+}
+
+void LLFilteredPathfindingLinksets::setObstacleFilter(BOOL pObstacleFilter)
+{
+ mIsFiltersDirty = (mIsFiltersDirty || (mIsObstacleFilter == pObstacleFilter));
+ mIsObstacleFilter = pObstacleFilter;
+}
+
+BOOL LLFilteredPathfindingLinksets::isObstacleFilter() const
+{
+ return mIsObstacleFilter;
+}
+
+void LLFilteredPathfindingLinksets::setIgnoredFilter(BOOL pIgnoredFilter)
+{
+ mIsFiltersDirty = (mIsFiltersDirty || (mIsIgnoredFilter == pIgnoredFilter));
+ mIsIgnoredFilter = pIgnoredFilter;
+}
+
+BOOL LLFilteredPathfindingLinksets::isIgnoredFilter() const
+{
+ return mIsIgnoredFilter;
+}
+
+void LLFilteredPathfindingLinksets::clearFilters()
+{
+ mNameFilter.clear();
+ mDescriptionFilter.clear();
+ mIsWalkableFilter = true;
+ mIsObstacleFilter = true;
+ mIsIgnoredFilter = true;
+ mIsFiltersDirty = false;
+}
+
+void LLFilteredPathfindingLinksets::applyFilters()
+{
+ mFilteredLinksets.clear();
+
+ for (PathfindingLinksetMap::const_iterator linksetIter = mAllLinksets.begin();
+ linksetIter != mAllLinksets.end(); ++linksetIter)
+ {
+ const std::string& uuid(linksetIter->first);
+ const LLPathfindingLinkset& linkset(linksetIter->second);
+ if (doesMatchFilters(linkset))
+ {
+ mFilteredLinksets.insert(std::pair<std::string, LLPathfindingLinkset>(uuid, linkset));
+ }
+ }
+
+ mIsFiltersDirty = false;
+}
+
+BOOL LLFilteredPathfindingLinksets::doesMatchFilters(const LLPathfindingLinkset& pLinkset) const
+{
+ return (((mIsWalkableFilter && (pLinkset.getPathState() == LLPathfindingLinkset::kWalkable)) ||
+ (mIsObstacleFilter && (pLinkset.getPathState() == LLPathfindingLinkset::kObstacle)) ||
+ (mIsIgnoredFilter && (pLinkset.getPathState() == LLPathfindingLinkset::kIgnored))) &&
+ (!mNameFilter.isActive() || mNameFilter.doesMatch(pLinkset.getName())) &&
+ (!mDescriptionFilter.isActive() || mDescriptionFilter.doesMatch(pLinkset.getDescription())));
+}
diff --git a/indra/newview/llfilteredpathfindinglinksets.h b/indra/newview/llfilteredpathfindinglinksets.h
new file mode 100644
index 0000000000..de9b3a5e15
--- /dev/null
+++ b/indra/newview/llfilteredpathfindinglinksets.h
@@ -0,0 +1,111 @@
+/**
+ * @file llfilteredpathfindinglinksets.h
+ * @author William Todd Stinson
+ * @brief Class to implement the filtering of a set of pathfinding linksets
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_FILTEREDPATHFINDINGLINKSETS_H
+#define LL_FILTEREDPATHFINDINGLINKSETS_H
+
+#include <string>
+#include <map>
+
+class LLSD;
+class LLPathfindingLinkset;
+
+class FilterString
+{
+public:
+ FilterString();
+ FilterString(const std::string& pFilter);
+ virtual ~FilterString();
+
+ const std::string& get() const;
+ bool set(const std::string& pFilter);
+ void clear();
+
+ bool isActive() const;
+ bool doesMatch(const std::string& pTestString) const;
+
+protected:
+
+private:
+ std::string mFilter;
+ std::string mUpperFilter;
+};
+
+class LLFilteredPathfindingLinksets
+{
+public:
+ typedef std::map<std::string, LLPathfindingLinkset> PathfindingLinksetMap;
+
+ LLFilteredPathfindingLinksets();
+ LLFilteredPathfindingLinksets(const LLSD& pLinksetItems);
+ LLFilteredPathfindingLinksets(const LLFilteredPathfindingLinksets& pOther);
+ virtual ~LLFilteredPathfindingLinksets();
+
+ void setPathfindingLinksets(const LLSD& pLinksetItems);
+ void updatePathfindingLinksets(const LLSD& pLinksetItems);
+ void clearPathfindingLinksets();
+
+ const PathfindingLinksetMap& getAllLinksets() const;
+ const PathfindingLinksetMap& getFilteredLinksets();
+
+ BOOL isFiltersActive() const;
+
+ void setNameFilter(const std::string& pNameFilter);
+ const std::string& getNameFilter() const;
+
+ void setDescriptionFilter(const std::string& pDescriptionFilter);
+ const std::string& getDescriptionFilter() const;
+
+ void setWalkableFilter(BOOL pWalkableFilter);
+ BOOL isWalkableFilter() const;
+
+ void setObstacleFilter(BOOL pObstacleFilter);
+ BOOL isObstacleFilter() const;
+
+ void setIgnoredFilter(BOOL pIgnoredFilter);
+ BOOL isIgnoredFilter() const;
+
+ void clearFilters();
+
+protected:
+
+private:
+ PathfindingLinksetMap mAllLinksets;
+ PathfindingLinksetMap mFilteredLinksets;
+
+ bool mIsFiltersDirty;
+ FilterString mNameFilter;
+ FilterString mDescriptionFilter;
+ BOOL mIsWalkableFilter;
+ BOOL mIsObstacleFilter;
+ BOOL mIsIgnoredFilter;
+
+ void applyFilters();
+ BOOL doesMatchFilters(const LLPathfindingLinkset& pLinkset) const;
+};
+
+#endif // LL_FILTEREDPATHFINDINGLINKSETS_H
diff --git a/indra/newview/llfloaterpathfindingcharacters.cpp b/indra/newview/llfloaterpathfindingcharacters.cpp
new file mode 100644
index 0000000000..021b48f0a0
--- /dev/null
+++ b/indra/newview/llfloaterpathfindingcharacters.cpp
@@ -0,0 +1,491 @@
+/**
+ * @file llfloaterpathfindingcharacters.cpp
+ * @author William Todd Stinson
+ * @brief "Pathfinding linksets" floater, allowing manipulation of the Havok AI pathfinding settings.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloater.h"
+#include "llfloaterpathfindingcharacters.h"
+#include "llpathfindingcharacter.h"
+#include "llsd.h"
+#include "llagent.h"
+#include "llhandle.h"
+#include "llfloaterreg.h"
+#include "lltextbase.h"
+#include "llscrolllistitem.h"
+#include "llscrolllistctrl.h"
+#include "llcheckboxctrl.h"
+#include "llradiogroup.h"
+#include "llbutton.h"
+#include "llresmgr.h"
+#include "llviewerregion.h"
+#include "llhttpclient.h"
+#include "lluuid.h"
+
+//---------------------------------------------------------------------------
+// CharactersGetResponder
+//---------------------------------------------------------------------------
+
+class CharactersGetResponder : public LLHTTPClient::Responder
+{
+public:
+ CharactersGetResponder(const std::string& pCharactersDataGetURL,
+ const LLHandle<LLFloaterPathfindingCharacters> &pCharactersFloaterHandle);
+ virtual ~CharactersGetResponder();
+
+ virtual void result(const LLSD& pContent);
+ virtual void error(U32 pStatus, const std::string& pReason);
+
+private:
+ CharactersGetResponder(const CharactersGetResponder& pOther);
+
+ std::string mCharactersDataGetURL;
+ LLHandle<LLFloaterPathfindingCharacters> mCharactersFloaterHandle;
+};
+
+//---------------------------------------------------------------------------
+// LLFloaterPathfindingCharacters
+//---------------------------------------------------------------------------
+
+BOOL LLFloaterPathfindingCharacters::postBuild()
+{
+ childSetAction("refresh_characters_list", boost::bind(&LLFloaterPathfindingCharacters::onRefreshCharactersClicked, this));
+ childSetAction("select_all_characters", boost::bind(&LLFloaterPathfindingCharacters::onSelectAllCharactersClicked, this));
+ childSetAction("select_none_characters", boost::bind(&LLFloaterPathfindingCharacters::onSelectNoneCharactersClicked, this));
+
+ mCharactersScrollList = findChild<LLScrollListCtrl>("pathfinding_characters");
+ llassert(mCharactersScrollList != NULL);
+ mCharactersScrollList->setCommitCallback(boost::bind(&LLFloaterPathfindingCharacters::onCharactersSelectionChange, this));
+ mCharactersScrollList->setCommitOnSelectionChange(true);
+ mCharactersScrollList->sortByColumnIndex(0, true);
+
+ mCharactersStatus = findChild<LLTextBase>("characters_status");
+ llassert(mCharactersStatus != NULL);
+
+ mLabelActions = findChild<LLTextBase>("actions_label");
+ llassert(mLabelActions != NULL);
+
+ mShowBeaconCheckBox = findChild<LLCheckBoxCtrl>("show_beacon");
+ llassert(mShowBeaconCheckBox != NULL);
+ mShowBeaconCheckBox->setCommitCallback(boost::bind(&LLFloaterPathfindingCharacters::onShowBeaconToggled, this));
+
+ mTakeBtn = findChild<LLButton>("take_characters");
+ llassert(mTakeBtn != NULL)
+ mTakeBtn->setCommitCallback(boost::bind(&LLFloaterPathfindingCharacters::onTakeCharactersClicked, this));
+
+ mTakeCopyBtn = findChild<LLButton>("take_copy_characters");
+ llassert(mTakeCopyBtn != NULL)
+ mTakeCopyBtn->setCommitCallback(boost::bind(&LLFloaterPathfindingCharacters::onTakeCopyCharactersClicked, this));
+
+ mReturnBtn = findChild<LLButton>("return_characters");
+ llassert(mReturnBtn != NULL)
+ mReturnBtn->setCommitCallback(boost::bind(&LLFloaterPathfindingCharacters::onReturnCharactersClicked, this));
+
+ mDeleteBtn = findChild<LLButton>("delete_characters");
+ llassert(mDeleteBtn != NULL)
+ mDeleteBtn->setCommitCallback(boost::bind(&LLFloaterPathfindingCharacters::onDeleteCharactersClicked, this));
+
+ mTeleportBtn = findChild<LLButton>("teleport_to_character");
+ llassert(mTeleportBtn != NULL)
+ mTeleportBtn->setCommitCallback(boost::bind(&LLFloaterPathfindingCharacters::onTeleportCharacterToMeClicked, this));
+
+ setEnableActionFields(false);
+ setMessagingState(kMessagingInitial);
+
+ return LLFloater::postBuild();
+}
+
+void LLFloaterPathfindingCharacters::onOpen(const LLSD& pKey)
+{
+ sendCharactersDataGetRequest();
+}
+
+LLFloaterPathfindingCharacters::EMessagingState LLFloaterPathfindingCharacters::getMessagingState() const
+{
+ return mMessagingState;
+}
+
+BOOL LLFloaterPathfindingCharacters::isMessagingInProgress() const
+{
+ BOOL retVal;
+ switch (getMessagingState())
+ {
+ case kMessagingFetchStarting :
+ case kMessagingFetchRequestSent :
+ case kMessagingFetchRequestSent_MultiRequested :
+ case kMessagingFetchReceived :
+ retVal = true;
+ break;
+ default :
+ retVal = false;
+ break;
+ }
+
+ return retVal;
+}
+
+LLFloaterPathfindingCharacters::LLFloaterPathfindingCharacters(const LLSD& pSeed)
+ : LLFloater(pSeed),
+ mSelfHandle(),
+ mPathfindingCharacters(),
+ mMessagingState(kMessagingInitial),
+ mCharactersScrollList(NULL),
+ mCharactersStatus(NULL),
+ mLabelActions(NULL),
+ mShowBeaconCheckBox(NULL),
+ mTakeBtn(NULL),
+ mTakeCopyBtn(NULL),
+ mReturnBtn(NULL),
+ mDeleteBtn(NULL),
+ mTeleportBtn(NULL)
+{
+ mSelfHandle.bind(this);
+}
+
+LLFloaterPathfindingCharacters::~LLFloaterPathfindingCharacters()
+{
+ mPathfindingCharacters.clear();
+}
+
+void LLFloaterPathfindingCharacters::sendCharactersDataGetRequest()
+{
+ if (isMessagingInProgress())
+ {
+ if (getMessagingState() == kMessagingFetchRequestSent)
+ {
+ setMessagingState(kMessagingFetchRequestSent_MultiRequested);
+ }
+ }
+ else
+ {
+ setMessagingState(kMessagingFetchStarting);
+ mPathfindingCharacters.clear();
+ updateCharactersList();
+
+ std::string charactersDataURL = getCapabilityURL();
+ if (charactersDataURL.empty())
+ {
+ setMessagingState(kMessagingComplete);
+ llwarns << "cannot query pathfinding characters from current region '" << getRegionName() << "'" << llendl;
+ }
+ else
+ {
+ setMessagingState(kMessagingFetchRequestSent);
+ LLHTTPClient::get(charactersDataURL, new CharactersGetResponder(charactersDataURL, mSelfHandle));
+ }
+ }
+}
+
+void LLFloaterPathfindingCharacters::handleCharactersDataGetReply(const LLSD& pCharactersData)
+{
+ setMessagingState(kMessagingFetchReceived);
+ mPathfindingCharacters.clear();
+ parseCharactersData(pCharactersData);
+ updateCharactersList();
+ setMessagingState(kMessagingComplete);
+}
+
+void LLFloaterPathfindingCharacters::handleCharactersDataGetError(const std::string& pURL, const std::string& pErrorReason)
+{
+ setMessagingState(kMessagingFetchError);
+ mPathfindingCharacters.clear();
+ updateCharactersList();
+ llwarns << "Error fetching pathfinding characters from URL '" << pURL << "' because " << pErrorReason << llendl;
+}
+
+std::string LLFloaterPathfindingCharacters::getRegionName() const
+{
+ std::string regionName("");
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region != NULL)
+ {
+ regionName = region->getName();
+ }
+
+ return regionName;
+}
+
+std::string LLFloaterPathfindingCharacters::getCapabilityURL() const
+{
+ std::string charactersDataURL("");
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region != NULL)
+ {
+ charactersDataURL = region->getCapability("ObjectNavMeshProperties");
+ }
+
+ return charactersDataURL;
+}
+
+void LLFloaterPathfindingCharacters::parseCharactersData(const LLSD &pCharactersData)
+{
+ for (LLSD::map_const_iterator characterItemIter = pCharactersData.beginMap();
+ characterItemIter != pCharactersData.endMap(); ++characterItemIter)
+ {
+ const std::string &uuid(characterItemIter->first);
+ const LLSD &characterData(characterItemIter->second);
+ LLPathfindingCharacter character(uuid, characterData);
+
+ mPathfindingCharacters.insert(std::pair<std::string, LLPathfindingCharacter>(uuid, character));
+ }
+}
+
+void LLFloaterPathfindingCharacters::setMessagingState(EMessagingState pMessagingState)
+{
+ mMessagingState = pMessagingState;
+ updateCharactersList();
+ updateActionFields();
+}
+
+void LLFloaterPathfindingCharacters::onCharactersSelectionChange()
+{
+ updateCharactersStatusMessage();
+ updateActionFields();
+}
+
+void LLFloaterPathfindingCharacters::onRefreshCharactersClicked()
+{
+ sendCharactersDataGetRequest();
+}
+
+void LLFloaterPathfindingCharacters::onSelectAllCharactersClicked()
+{
+ selectAllCharacters();
+}
+
+void LLFloaterPathfindingCharacters::onSelectNoneCharactersClicked()
+{
+ selectNoneCharacters();
+}
+
+void LLFloaterPathfindingCharacters::onShowBeaconToggled()
+{
+ llwarns << "functionality has not yet been implemented to toggle show beacon" << llendl;
+}
+
+void LLFloaterPathfindingCharacters::onTakeCharactersClicked()
+{
+ llwarns << "functionality has not yet been implemented to take characters" << llendl;
+}
+
+void LLFloaterPathfindingCharacters::onTakeCopyCharactersClicked()
+{
+ llwarns << "functionality has not yet been implemented to take a copy of characters" << llendl;
+}
+
+void LLFloaterPathfindingCharacters::onReturnCharactersClicked()
+{
+ llwarns << "functionality has not yet been implemented to return characters" << llendl;
+}
+
+void LLFloaterPathfindingCharacters::onDeleteCharactersClicked()
+{
+ llwarns << "functionality has not yet been implemented to delete characters" << llendl;
+}
+
+void LLFloaterPathfindingCharacters::onTeleportCharacterToMeClicked()
+{
+ llwarns << "functionality has not yet been implemented to teleport me tothe character" << llendl;
+}
+
+void LLFloaterPathfindingCharacters::updateCharactersList()
+{
+ std::vector<LLScrollListItem*> selectedItems = mCharactersScrollList->getAllSelected();
+ int numSelectedItems = selectedItems.size();
+ uuid_vec_t selectedUUIDs;
+ if (numSelectedItems > 0)
+ {
+ selectedUUIDs.reserve(selectedItems.size());
+ for (std::vector<LLScrollListItem*>::const_iterator itemIter = selectedItems.begin();
+ itemIter != selectedItems.end(); ++itemIter)
+ {
+ const LLScrollListItem *listItem = *itemIter;
+ selectedUUIDs.push_back(listItem->getUUID());
+ }
+ }
+
+ mCharactersScrollList->deleteAllItems();
+ updateCharactersStatusMessage();
+
+ for (PathfindingCharacterMap::const_iterator characterIter = mPathfindingCharacters.begin();
+ characterIter != mPathfindingCharacters.end(); ++characterIter)
+ {
+ const LLPathfindingCharacter& character(characterIter->second);
+
+ LLSD columns;
+
+ columns[0]["column"] = "name";
+ columns[0]["value"] = character.getName();
+ columns[0]["font"] = "SANSSERIF";
+
+ columns[1]["column"] = "description";
+ columns[1]["value"] = character.getDescription();
+ columns[1]["font"] = "SANSSERIF";
+
+ columns[2]["column"] = "owner";
+ columns[2]["value"] = character.getOwner();
+ columns[2]["font"] = "SANSSERIF";
+
+ columns[3]["column"] = "cpu_time";
+ columns[3]["value"] = llformat("%3d ms", character.getCPUTime());
+ columns[3]["font"] = "SANSSERIF";
+
+ columns[4]["column"] = "altitude";
+ columns[4]["value"] = llformat("%1.0f m", character.getLocation()[2]);
+ columns[4]["font"] = "SANSSERIF";
+
+ LLSD element;
+ element["id"] = character.getUUID().asString();
+ element["column"] = columns;
+
+ mCharactersScrollList->addElement(element);
+ }
+
+ mCharactersScrollList->selectMultiple(selectedUUIDs);
+ updateCharactersStatusMessage();
+ updateActionFields();
+}
+
+void LLFloaterPathfindingCharacters::selectAllCharacters()
+{
+ mCharactersScrollList->selectAll();
+}
+
+void LLFloaterPathfindingCharacters::selectNoneCharacters()
+{
+ mCharactersScrollList->deselectAllItems();
+}
+
+void LLFloaterPathfindingCharacters::updateCharactersStatusMessage()
+{
+ static const LLColor4 warningColor = LLUIColorTable::instance().getColor("DrYellow");
+
+ std::string statusText("");
+ LLStyle::Params styleParams;
+
+ switch (getMessagingState())
+ {
+ case kMessagingInitial:
+ statusText = getString("characters_messaging_initial");
+ break;
+ case kMessagingFetchStarting :
+ statusText = getString("characters_messaging_fetch_starting");
+ break;
+ case kMessagingFetchRequestSent :
+ statusText = getString("characters_messaging_fetch_inprogress");
+ break;
+ case kMessagingFetchRequestSent_MultiRequested :
+ statusText = getString("characters_messaging_fetch_inprogress_multi_request");
+ break;
+ case kMessagingFetchReceived :
+ statusText = getString("characters_messaging_fetch_received");
+ break;
+ case kMessagingFetchError :
+ statusText = getString("characters_messaging_fetch_error");
+ styleParams.color = warningColor;
+ break;
+ case kMessagingComplete :
+ if (mCharactersScrollList->isEmpty())
+ {
+ statusText = getString("characters_messaging_complete_none_found");
+ }
+ else
+ {
+ S32 numItems = mCharactersScrollList->getItemCount();
+ S32 numSelectedItems = mCharactersScrollList->getNumSelected();
+
+ LLLocale locale(LLStringUtil::getLocale());
+ std::string numItemsString;
+ LLResMgr::getInstance()->getIntegerString(numItemsString, numItems);
+
+ std::string numSelectedItemsString;
+ LLResMgr::getInstance()->getIntegerString(numSelectedItemsString, numSelectedItems);
+
+ LLStringUtil::format_map_t string_args;
+ string_args["[NUM_SELECTED]"] = numSelectedItemsString;
+ string_args["[NUM_TOTAL]"] = numItemsString;
+ statusText = getString("characters_messaging_complete_available", string_args);
+ }
+ break;
+ default:
+ statusText = getString("characters_messaging_initial");
+ llassert(0);
+ break;
+ }
+
+ mCharactersStatus->setText((LLStringExplicit)statusText, styleParams);
+}
+
+void LLFloaterPathfindingCharacters::updateActionFields()
+{
+ std::vector<LLScrollListItem*> selectedItems = mCharactersScrollList->getAllSelected();
+ setEnableActionFields(!selectedItems.empty());
+}
+
+void LLFloaterPathfindingCharacters::setEnableActionFields(BOOL pEnabled)
+{
+ mLabelActions->setEnabled(pEnabled);
+ mShowBeaconCheckBox->setEnabled(pEnabled);
+ mTakeBtn->setEnabled(pEnabled);
+ mTakeCopyBtn->setEnabled(pEnabled);
+ mReturnBtn->setEnabled(pEnabled);
+ mDeleteBtn->setEnabled(pEnabled);
+ mTeleportBtn->setEnabled(pEnabled && (mCharactersScrollList->getNumSelected() == 1));
+}
+
+//---------------------------------------------------------------------------
+// CharactersGetResponder
+//---------------------------------------------------------------------------
+
+CharactersGetResponder::CharactersGetResponder(const std::string& pCharactersDataGetURL,
+ const LLHandle<LLFloaterPathfindingCharacters> &pCharactersFloaterHandle)
+ : mCharactersDataGetURL(pCharactersDataGetURL),
+ mCharactersFloaterHandle(pCharactersFloaterHandle)
+{
+}
+
+CharactersGetResponder::~CharactersGetResponder()
+{
+}
+
+void CharactersGetResponder::result(const LLSD& pContent)
+{
+ LLFloaterPathfindingCharacters *charactersFloater = mCharactersFloaterHandle.get();
+ if (charactersFloater != NULL)
+ {
+ charactersFloater->handleCharactersDataGetReply(pContent);
+ }
+}
+
+void CharactersGetResponder::error(U32 status, const std::string& reason)
+{
+ LLFloaterPathfindingCharacters *charactersFloater = mCharactersFloaterHandle.get();
+ if (charactersFloater != NULL)
+ {
+ charactersFloater->handleCharactersDataGetError(mCharactersDataGetURL, reason);
+ }
+}
diff --git a/indra/newview/llfloaterpathfindingcharacters.h b/indra/newview/llfloaterpathfindingcharacters.h
new file mode 100644
index 0000000000..6264c5d27a
--- /dev/null
+++ b/indra/newview/llfloaterpathfindingcharacters.h
@@ -0,0 +1,124 @@
+/**
+ * @file llfloaterpathfindingcharacters.h
+ * @author William Todd Stinson
+ * @brief "Pathfinding linksets" floater, allowing manipulation of the Havok AI pathfinding settings.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERPATHFINDINGCHARACTERS_H
+#define LL_LLFLOATERPATHFINDINGCHARACTERS_H
+
+#include <string>
+#include <map>
+
+#include "llhandle.h"
+#include "llfloater.h"
+#include "llpathfindingcharacter.h"
+
+class LLSD;
+class LLTextBase;
+class LLScrollListCtrl;
+class LLCheckBoxCtrl;
+class LLRadioGroup;
+class LLButton;
+
+class LLFloaterPathfindingCharacters
+: public LLFloater
+{
+ friend class LLFloaterReg;
+ friend class CharactersGetResponder;
+
+public:
+ typedef enum
+ {
+ kMessagingInitial,
+ kMessagingFetchStarting,
+ kMessagingFetchRequestSent,
+ kMessagingFetchRequestSent_MultiRequested,
+ kMessagingFetchReceived,
+ kMessagingFetchError,
+ kMessagingComplete
+ } EMessagingState;
+
+ virtual BOOL postBuild();
+ virtual void onOpen(const LLSD& pKey);
+
+ EMessagingState getMessagingState() const;
+ BOOL isMessagingInProgress() const;
+
+protected:
+
+private:
+ typedef std::map<std::string, LLPathfindingCharacter> PathfindingCharacterMap;
+
+ LLRootHandle<LLFloaterPathfindingCharacters> mSelfHandle;
+ PathfindingCharacterMap mPathfindingCharacters;
+ EMessagingState mMessagingState;
+ LLScrollListCtrl *mCharactersScrollList;
+ LLTextBase *mCharactersStatus;
+ LLTextBase *mLabelActions;
+ LLCheckBoxCtrl *mShowBeaconCheckBox;
+ LLButton *mTakeBtn;
+ LLButton *mTakeCopyBtn;
+ LLButton *mReturnBtn;
+ LLButton *mDeleteBtn;
+ LLButton *mTeleportBtn;
+
+ // Does its own instance management, so clients not allowed
+ // to allocate or destroy.
+ LLFloaterPathfindingCharacters(const LLSD& pSeed);
+ virtual ~LLFloaterPathfindingCharacters();
+
+ void sendCharactersDataGetRequest();
+ void handleCharactersDataGetReply(const LLSD& pCharactersData);
+ void handleCharactersDataGetError(const std::string& pURL, const std::string& pErrorReason);
+
+ std::string getRegionName() const;
+ std::string getCapabilityURL() const;
+
+ void parseCharactersData(const LLSD &pCharactersData);
+
+ void setMessagingState(EMessagingState pMessagingState);
+
+ void onCharactersSelectionChange();
+ void onRefreshCharactersClicked();
+ void onSelectAllCharactersClicked();
+ void onSelectNoneCharactersClicked();
+ void onShowBeaconToggled();
+ void onTakeCharactersClicked();
+ void onTakeCopyCharactersClicked();
+ void onReturnCharactersClicked();
+ void onDeleteCharactersClicked();
+ void onTeleportCharacterToMeClicked();
+
+ void updateCharactersList();
+ void selectAllCharacters();
+ void selectNoneCharacters();
+
+ void updateCharactersStatusMessage();
+
+ void updateActionFields();
+ void setEnableActionFields(BOOL pEnabled);
+};
+
+#endif // LL_LLFLOATERPATHFINDINGCHARACTERS_H
diff --git a/indra/newview/llfloaterpathfindinglinksets.cpp b/indra/newview/llfloaterpathfindinglinksets.cpp
new file mode 100644
index 0000000000..4d3581fc60
--- /dev/null
+++ b/indra/newview/llfloaterpathfindinglinksets.cpp
@@ -0,0 +1,825 @@
+/**
+ * @file llfloaterpathfindinglinksets.cpp
+ * @author William Todd Stinson
+ * @brief "Pathfinding linksets" floater, allowing manipulation of the Havok AI pathfinding settings.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloater.h"
+#include "llfloaterpathfindinglinksets.h"
+#include "llsd.h"
+#include "v3math.h"
+#include "lltextvalidate.h"
+#include "llagent.h"
+#include "llhandle.h"
+#include "llfloaterreg.h"
+#include "lltextbase.h"
+#include "lllineeditor.h"
+#include "llscrolllistitem.h"
+#include "llscrolllistctrl.h"
+#include "llcheckboxctrl.h"
+#include "llradiogroup.h"
+#include "llbutton.h"
+#include "llresmgr.h"
+#include "llviewerregion.h"
+#include "llhttpclient.h"
+#include "lluuid.h"
+#include "llpathfindinglinkset.h"
+#include "llfilteredpathfindinglinksets.h"
+
+#define XUI_PATH_STATE_WALKABLE 1
+#define XUI_PATH_STATE_OBSTACLE 2
+#define XUI_PATH_STATE_IGNORED 3
+
+//---------------------------------------------------------------------------
+// NavMeshDataGetResponder
+//---------------------------------------------------------------------------
+
+class NavMeshDataGetResponder : public LLHTTPClient::Responder
+{
+public:
+ NavMeshDataGetResponder(const std::string& pNavMeshDataGetURL,
+ const LLHandle<LLFloaterPathfindingLinksets> &pLinksetsHandle);
+ virtual ~NavMeshDataGetResponder();
+
+ virtual void result(const LLSD& pContent);
+ virtual void error(U32 pStatus, const std::string& pReason);
+
+private:
+ NavMeshDataGetResponder(const NavMeshDataGetResponder& pOther);
+
+ std::string mNavMeshDataGetURL;
+ LLHandle<LLFloaterPathfindingLinksets> mLinksetsFloaterHandle;
+};
+
+//---------------------------------------------------------------------------
+// NavMeshDataPutResponder
+//---------------------------------------------------------------------------
+
+class NavMeshDataPutResponder : public LLHTTPClient::Responder
+{
+public:
+ NavMeshDataPutResponder(const std::string& pNavMeshDataPutURL,
+ const LLHandle<LLFloaterPathfindingLinksets> &pLinksetsHandle);
+ virtual ~NavMeshDataPutResponder();
+
+ virtual void result(const LLSD& pContent);
+ virtual void error(U32 pStatus, const std::string& pReason);
+
+private:
+ NavMeshDataPutResponder(const NavMeshDataPutResponder& pOther);
+
+ std::string mNavMeshDataPutURL;
+ LLHandle<LLFloaterPathfindingLinksets> mLinksetsFloaterHandle;
+};
+
+//---------------------------------------------------------------------------
+// LLFloaterPathfindingLinksets
+//---------------------------------------------------------------------------
+
+BOOL LLFloaterPathfindingLinksets::postBuild()
+{
+ childSetAction("apply_filters", boost::bind(&LLFloaterPathfindingLinksets::onApplyFiltersClicked, this));
+ childSetAction("clear_filters", boost::bind(&LLFloaterPathfindingLinksets::onClearFiltersClicked, this));
+ childSetAction("refresh_linksets_list", boost::bind(&LLFloaterPathfindingLinksets::onRefreshLinksetsClicked, this));
+ childSetAction("select_all_linksets", boost::bind(&LLFloaterPathfindingLinksets::onSelectAllLinksetsClicked, this));
+ childSetAction("select_none_linksets", boost::bind(&LLFloaterPathfindingLinksets::onSelectNoneLinksetsClicked, this));
+
+ mLinksetsScrollList = findChild<LLScrollListCtrl>("pathfinding_linksets");
+ llassert(mLinksetsScrollList != NULL);
+ mLinksetsScrollList->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onLinksetsSelectionChange, this));
+ mLinksetsScrollList->setCommitOnSelectionChange(true);
+ mLinksetsScrollList->sortByColumnIndex(0, true);
+
+ mLinksetsStatus = findChild<LLTextBase>("linksets_status");
+ llassert(mLinksetsStatus != NULL);
+
+ mFilterByName = findChild<LLLineEditor>("filter_by_name");
+ llassert(mFilterByName != NULL);
+ mFilterByName->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyFiltersClicked, this));
+ mFilterByName->setSelectAllonFocusReceived(true);
+ mFilterByName->setCommitOnFocusLost(true);
+
+ mFilterByDescription = findChild<LLLineEditor>("filter_by_description");
+ llassert(mFilterByDescription != NULL);
+ mFilterByDescription->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyFiltersClicked, this));
+ mFilterByDescription->setSelectAllonFocusReceived(true);
+ mFilterByDescription->setCommitOnFocusLost(true);
+
+ mFilterByWalkable = findChild<LLCheckBoxCtrl>("filter_by_walkable");
+ llassert(mFilterByWalkable != NULL);
+ mFilterByWalkable->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyFiltersClicked, this));
+
+ mFilterByObstacle = findChild<LLCheckBoxCtrl>("filter_by_obstacle");
+ llassert(mFilterByObstacle != NULL);
+ mFilterByObstacle->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyFiltersClicked, this));
+
+ mFilterByIgnored = findChild<LLCheckBoxCtrl>("filter_by_ignored");
+ llassert(mFilterByIgnored != NULL);
+ mFilterByIgnored->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyFiltersClicked, this));
+
+ mEditPathState = findChild<LLRadioGroup>("edit_path_state");
+ llassert(mEditPathState != NULL);
+
+ mEditPathStateWalkable = findChild<LLUICtrl>("edit_pathing_state_walkable");
+ llassert(mEditPathStateWalkable != NULL);
+
+ mEditPathStateObstacle = findChild<LLUICtrl>("edit_pathing_state_obstacle");
+ llassert(mEditPathStateObstacle != NULL);
+
+ mEditPathStateIgnored = findChild<LLUICtrl>("edit_pathing_state_ignored");
+ llassert(mEditPathStateIgnored != NULL);
+
+ mLabelWalkabilityCoefficients = findChild<LLTextBase>("walkability_coefficients_label");
+ llassert(mLabelWalkabilityCoefficients != NULL);
+
+ mLabelEditA = findChild<LLTextBase>("edit_a_label");
+ llassert(mLabelEditA != NULL);
+
+ mLabelEditB = findChild<LLTextBase>("edit_b_label");
+ llassert(mLabelEditB != NULL);
+
+ mLabelEditC = findChild<LLTextBase>("edit_c_label");
+ llassert(mLabelEditC != NULL);
+
+ mLabelEditD = findChild<LLTextBase>("edit_d_label");
+ llassert(mLabelEditD != NULL);
+
+ mEditA = findChild<LLLineEditor>("edit_a_value");
+ llassert(mEditA != NULL);
+ mEditA->setPrevalidate(LLTextValidate::validatePositiveS32);
+
+ mEditB = findChild<LLLineEditor>("edit_b_value");
+ llassert(mEditB != NULL);
+ mEditB->setPrevalidate(LLTextValidate::validatePositiveS32);
+
+ mEditC = findChild<LLLineEditor>("edit_c_value");
+ llassert(mEditC != NULL);
+ mEditC->setPrevalidate(LLTextValidate::validatePositiveS32);
+
+ mEditD = findChild<LLLineEditor>("edit_d_value");
+ llassert(mEditD != NULL);
+ mEditD->setPrevalidate(LLTextValidate::validatePositiveS32);
+
+ mEditPhantom = findChild<LLCheckBoxCtrl>("edit_phantom_value");
+ llassert(mEditPhantom != NULL);
+
+ mApplyEdits = findChild<LLButton>("apply_edit_values");
+ llassert(mApplyEdits != NULL);
+ mApplyEdits->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyChangesClicked, this));
+
+ setEnableEditFields(false);
+ setMessagingState(kMessagingInitial);
+
+ return LLFloater::postBuild();
+}
+
+void LLFloaterPathfindingLinksets::onOpen(const LLSD& pKey)
+{
+ sendNavMeshDataGetRequest();
+}
+
+void LLFloaterPathfindingLinksets::openLinksetsEditor()
+{
+ LLFloaterReg::toggleInstanceOrBringToFront("pathfinding_linksets");
+}
+
+LLFloaterPathfindingLinksets::EMessagingState LLFloaterPathfindingLinksets::getMessagingState() const
+{
+ return mMessagingState;
+}
+
+BOOL LLFloaterPathfindingLinksets::isMessagingInProgress() const
+{
+ BOOL retVal;
+ switch (getMessagingState())
+ {
+ case kMessagingFetchStarting :
+ case kMessagingFetchRequestSent :
+ case kMessagingFetchRequestSent_MultiRequested :
+ case kMessagingFetchReceived :
+ case kMessagingModifyStarting :
+ case kMessagingModifyRequestSent :
+ case kMessagingModifyReceived :
+ retVal = true;
+ break;
+ default :
+ retVal = false;
+ break;
+ }
+
+ return retVal;
+}
+
+LLFloaterPathfindingLinksets::LLFloaterPathfindingLinksets(const LLSD& pSeed)
+ : LLFloater(pSeed),
+ mSelfHandle(),
+ mPathfindingLinksets(),
+ mMessagingState(kMessagingInitial),
+ mLinksetsScrollList(NULL),
+ mLinksetsStatus(NULL),
+ mFilterByName(NULL),
+ mFilterByDescription(NULL),
+ mFilterByWalkable(NULL),
+ mFilterByObstacle(NULL),
+ mFilterByIgnored(NULL),
+ mEditPathState(NULL),
+ mEditPathStateWalkable(NULL),
+ mEditPathStateObstacle(NULL),
+ mEditPathStateIgnored(NULL),
+ mLabelWalkabilityCoefficients(NULL),
+ mLabelEditA(NULL),
+ mLabelEditB(NULL),
+ mLabelEditC(NULL),
+ mLabelEditD(NULL),
+ mEditA(NULL),
+ mEditB(NULL),
+ mEditC(NULL),
+ mEditD(NULL),
+ mEditPhantom(NULL),
+ mApplyEdits(NULL)
+{
+ mSelfHandle.bind(this);
+}
+
+LLFloaterPathfindingLinksets::~LLFloaterPathfindingLinksets()
+{
+}
+
+void LLFloaterPathfindingLinksets::sendNavMeshDataGetRequest()
+{
+ if (isMessagingInProgress())
+ {
+ if (getMessagingState() == kMessagingFetchRequestSent)
+ {
+ setMessagingState(kMessagingFetchRequestSent_MultiRequested);
+ }
+ }
+ else
+ {
+ setMessagingState(kMessagingFetchStarting);
+ mPathfindingLinksets.clearPathfindingLinksets();
+ updateLinksetsList();
+
+ std::string navMeshDataURL = getCapabilityURL();
+ if (navMeshDataURL.empty())
+ {
+ setMessagingState(kMessagingComplete);
+ llwarns << "cannot query object navmesh properties from current region '" << getRegionName() << "'" << llendl;
+ }
+ else
+ {
+ setMessagingState(kMessagingFetchRequestSent);
+ LLHTTPClient::get(navMeshDataURL, new NavMeshDataGetResponder(navMeshDataURL, mSelfHandle));
+ }
+ }
+}
+
+void LLFloaterPathfindingLinksets::sendNavMeshDataPutRequest(const LLSD& pPostData)
+{
+ if (!isMessagingInProgress())
+ {
+ std::string navMeshDataURL = getCapabilityURL();
+ if (navMeshDataURL.empty())
+ {
+ llwarns << "cannot put object navmesh properties for current region '" << getRegionName() << "'" << llendl;
+ }
+ else
+ {
+ LLHTTPClient::put(navMeshDataURL, pPostData, new NavMeshDataPutResponder(navMeshDataURL, mSelfHandle));
+ }
+ }
+}
+
+void LLFloaterPathfindingLinksets::handleNavMeshDataGetReply(const LLSD& pNavMeshData)
+{
+ setMessagingState(kMessagingFetchReceived);
+ mPathfindingLinksets.setPathfindingLinksets(pNavMeshData);
+ updateLinksetsList();
+ setMessagingState(kMessagingComplete);
+}
+
+void LLFloaterPathfindingLinksets::handleNavMeshDataGetError(const std::string& pURL, const std::string& pErrorReason)
+{
+ setMessagingState(kMessagingFetchError);
+ mPathfindingLinksets.clearPathfindingLinksets();
+ updateLinksetsList();
+ llwarns << "Error fetching object navmesh properties from URL '" << pURL << "' because " << pErrorReason << llendl;
+}
+
+void LLFloaterPathfindingLinksets::handleNavMeshDataPutReply(const LLSD& pModifiedData)
+{
+ setMessagingState(kMessagingModifyReceived);
+ mPathfindingLinksets.updatePathfindingLinksets(pModifiedData);
+ updateLinksetsList();
+ setMessagingState(kMessagingComplete);
+}
+
+void LLFloaterPathfindingLinksets::handleNavMeshDataPutError(const std::string& pURL, const std::string& pErrorReason)
+{
+ setMessagingState(kMessagingModifyError);
+ llwarns << "Error putting object navmesh properties to URL '" << pURL << "' because " << pErrorReason << llendl;
+}
+
+std::string LLFloaterPathfindingLinksets::getRegionName() const
+{
+ std::string regionName("");
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region != NULL)
+ {
+ regionName = region->getName();
+ }
+
+ return regionName;
+}
+
+std::string LLFloaterPathfindingLinksets::getCapabilityURL() const
+{
+ std::string navMeshDataURL("");
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region != NULL)
+ {
+ navMeshDataURL = region->getCapability("ObjectNavMeshProperties");
+ }
+
+ return navMeshDataURL;
+}
+
+void LLFloaterPathfindingLinksets::setMessagingState(EMessagingState pMessagingState)
+{
+ mMessagingState = pMessagingState;
+ updateLinksetsStatusMessage();
+ updateEditFields();
+}
+
+void LLFloaterPathfindingLinksets::onApplyFiltersClicked()
+{
+ applyFilters();
+}
+
+void LLFloaterPathfindingLinksets::onClearFiltersClicked()
+{
+ clearFilters();
+}
+
+void LLFloaterPathfindingLinksets::onLinksetsSelectionChange()
+{
+ updateLinksetsStatusMessage();
+ updateEditFields();
+}
+
+void LLFloaterPathfindingLinksets::onRefreshLinksetsClicked()
+{
+ sendNavMeshDataGetRequest();
+}
+
+void LLFloaterPathfindingLinksets::onSelectAllLinksetsClicked()
+{
+ selectAllLinksets();
+}
+
+void LLFloaterPathfindingLinksets::onSelectNoneLinksetsClicked()
+{
+ selectNoneLinksets();
+}
+
+void LLFloaterPathfindingLinksets::onApplyChangesClicked()
+{
+ applyEditFields();
+}
+
+void LLFloaterPathfindingLinksets::applyFilters()
+{
+ mPathfindingLinksets.setNameFilter(mFilterByName->getText());
+ mPathfindingLinksets.setDescriptionFilter(mFilterByDescription->getText());
+ mPathfindingLinksets.setWalkableFilter(mFilterByWalkable->get());
+ mPathfindingLinksets.setObstacleFilter(mFilterByObstacle->get());
+ mPathfindingLinksets.setIgnoredFilter(mFilterByIgnored->get());
+ updateLinksetsList();
+}
+
+void LLFloaterPathfindingLinksets::clearFilters()
+{
+ mPathfindingLinksets.clearFilters();
+ mFilterByName->setText(LLStringExplicit(mPathfindingLinksets.getNameFilter()));
+ mFilterByDescription->setText(LLStringExplicit(mPathfindingLinksets.getDescriptionFilter()));
+ mFilterByWalkable->set(mPathfindingLinksets.isWalkableFilter());
+ mFilterByObstacle->set(mPathfindingLinksets.isObstacleFilter());
+ mFilterByIgnored->set(mPathfindingLinksets.isIgnoredFilter());
+ updateLinksetsList();
+}
+
+void LLFloaterPathfindingLinksets::updateLinksetsList()
+{
+ std::vector<LLScrollListItem*> selectedItems = mLinksetsScrollList->getAllSelected();
+ int numSelectedItems = selectedItems.size();
+ uuid_vec_t selectedUUIDs;
+ if (numSelectedItems > 0)
+ {
+ selectedUUIDs.reserve(selectedItems.size());
+ for (std::vector<LLScrollListItem*>::const_iterator itemIter = selectedItems.begin();
+ itemIter != selectedItems.end(); ++itemIter)
+ {
+ const LLScrollListItem *listItem = *itemIter;
+ selectedUUIDs.push_back(listItem->getUUID());
+ }
+ }
+
+ mLinksetsScrollList->deleteAllItems();
+ updateLinksetsStatusMessage();
+
+ const LLVector3& avatarPosition = gAgent.getPositionAgent();
+ const LLFilteredPathfindingLinksets::PathfindingLinksetMap& linksetMap = mPathfindingLinksets.getFilteredLinksets();
+
+ for (LLFilteredPathfindingLinksets::PathfindingLinksetMap::const_iterator linksetIter = linksetMap.begin();
+ linksetIter != linksetMap.end(); ++linksetIter)
+ {
+ const LLPathfindingLinkset& linkset(linksetIter->second);
+
+ LLSD columns;
+
+ columns[0]["column"] = "name";
+ columns[0]["value"] = linkset.getName();
+ columns[0]["font"] = "SANSSERIF";
+
+ columns[1]["column"] = "description";
+ columns[1]["value"] = linkset.getDescription();
+ columns[1]["font"] = "SANSSERIF";
+
+ columns[2]["column"] = "land_impact";
+ columns[2]["value"] = llformat("%1d", linkset.getLandImpact());
+ columns[2]["font"] = "SANSSERIF";
+
+ columns[3]["column"] = "dist_from_you";
+ columns[3]["value"] = llformat("%1.0f m", dist_vec(avatarPosition, linkset.getLocation()));
+ columns[3]["font"] = "SANSSERIF";
+
+ columns[4]["column"] = "path_state";
+ switch (linkset.getPathState())
+ {
+ case LLPathfindingLinkset::kWalkable :
+ columns[4]["value"] = getString("linkset_path_state_walkable");
+ break;
+ case LLPathfindingLinkset::kObstacle :
+ columns[4]["value"] = getString("linkset_path_state_obstacle");
+ break;
+ case LLPathfindingLinkset::kIgnored :
+ columns[4]["value"] = getString("linkset_path_state_ignored");
+ break;
+ default :
+ columns[4]["value"] = getString("linkset_path_state_ignored");
+ llassert(0);
+ break;
+ }
+ columns[4]["font"] = "SANSSERIF";
+
+ columns[5]["column"] = "is_phantom";
+ columns[5]["value"] = getString(linkset.isPhantom() ? "linkset_is_phantom" : "linkset_is_not_phantom");
+ columns[5]["font"] = "SANSSERIF";
+
+ columns[6]["column"] = "a_percent";
+ columns[6]["value"] = llformat("%3d", linkset.getWalkabilityCoefficientA());
+ columns[6]["font"] = "SANSSERIF";
+
+ columns[7]["column"] = "b_percent";
+ columns[7]["value"] = llformat("%3d", linkset.getWalkabilityCoefficientB());
+ columns[7]["font"] = "SANSSERIF";
+
+ columns[8]["column"] = "c_percent";
+ columns[8]["value"] = llformat("%3d", linkset.getWalkabilityCoefficientC());
+ columns[8]["font"] = "SANSSERIF";
+
+ columns[9]["column"] = "d_percent";
+ columns[9]["value"] = llformat("%3d", linkset.getWalkabilityCoefficientD());
+ columns[9]["font"] = "SANSSERIF";
+
+ LLSD element;
+ element["id"] = linkset.getUUID().asString();
+ element["column"] = columns;
+
+ mLinksetsScrollList->addElement(element);
+ }
+
+ mLinksetsScrollList->selectMultiple(selectedUUIDs);
+ updateLinksetsStatusMessage();
+ updateEditFields();
+}
+
+void LLFloaterPathfindingLinksets::selectAllLinksets()
+{
+ mLinksetsScrollList->selectAll();
+}
+
+void LLFloaterPathfindingLinksets::selectNoneLinksets()
+{
+ mLinksetsScrollList->deselectAllItems();
+}
+
+void LLFloaterPathfindingLinksets::updateLinksetsStatusMessage()
+{
+ static const LLColor4 warningColor = LLUIColorTable::instance().getColor("DrYellow");
+
+ std::string statusText("");
+ LLStyle::Params styleParams;
+
+ switch (getMessagingState())
+ {
+ case kMessagingInitial:
+ statusText = getString("linksets_messaging_initial");
+ break;
+ case kMessagingFetchStarting :
+ statusText = getString("linksets_messaging_fetch_starting");
+ break;
+ case kMessagingFetchRequestSent :
+ statusText = getString("linksets_messaging_fetch_inprogress");
+ break;
+ case kMessagingFetchRequestSent_MultiRequested :
+ statusText = getString("linksets_messaging_fetch_inprogress_multi_request");
+ break;
+ case kMessagingFetchReceived :
+ statusText = getString("linksets_messaging_fetch_received");
+ break;
+ case kMessagingFetchError :
+ statusText = getString("linksets_messaging_fetch_error");
+ styleParams.color = warningColor;
+ break;
+ case kMessagingModifyStarting :
+ statusText = getString("linksets_messaging_modify_starting");
+ break;
+ case kMessagingModifyRequestSent :
+ statusText = getString("linksets_messaging_modify_inprogress");
+ break;
+ case kMessagingModifyReceived :
+ statusText = getString("linksets_messaging_modify_received");
+ break;
+ case kMessagingModifyError :
+ statusText = getString("linksets_messaging_modify_error");
+ styleParams.color = warningColor;
+ break;
+ case kMessagingComplete :
+ if (mLinksetsScrollList->isEmpty())
+ {
+ statusText = getString("linksets_messaging_complete_none_found");
+ }
+ else
+ {
+ S32 numItems = mLinksetsScrollList->getItemCount();
+ S32 numSelectedItems = mLinksetsScrollList->getNumSelected();
+
+ LLLocale locale(LLStringUtil::getLocale());
+ std::string numItemsString;
+ LLResMgr::getInstance()->getIntegerString(numItemsString, numItems);
+
+ std::string numSelectedItemsString;
+ LLResMgr::getInstance()->getIntegerString(numSelectedItemsString, numSelectedItems);
+
+ LLStringUtil::format_map_t string_args;
+ string_args["[NUM_SELECTED]"] = numSelectedItemsString;
+ string_args["[NUM_TOTAL]"] = numItemsString;
+ statusText = getString("linksets_messaging_complete_available", string_args);
+ }
+ break;
+ default:
+ statusText = getString("linksets_messaging_initial");
+ llassert(0);
+ break;
+ }
+
+ mLinksetsStatus->setText((LLStringExplicit)statusText, styleParams);
+}
+
+void LLFloaterPathfindingLinksets::updateEditFields()
+{
+ std::vector<LLScrollListItem*> selectedItems = mLinksetsScrollList->getAllSelected();
+ if (selectedItems.empty())
+ {
+ mEditPathState->clear();
+ mEditA->clear();
+ mEditB->clear();
+ mEditC->clear();
+ mEditD->clear();
+ mEditPhantom->clear();
+
+ setEnableEditFields(false);
+ }
+ else
+ {
+ LLScrollListItem *firstItem = selectedItems.front();
+
+ const LLFilteredPathfindingLinksets::PathfindingLinksetMap &linksetsMap = mPathfindingLinksets.getAllLinksets();
+ LLFilteredPathfindingLinksets::PathfindingLinksetMap::const_iterator linksetIter = linksetsMap.find(firstItem->getUUID().asString());
+ const LLPathfindingLinkset &linkset(linksetIter->second);
+
+ setPathState(linkset.getPathState());
+ mEditA->setValue(LLSD(linkset.getWalkabilityCoefficientA()));
+ mEditB->setValue(LLSD(linkset.getWalkabilityCoefficientB()));
+ mEditC->setValue(LLSD(linkset.getWalkabilityCoefficientC()));
+ mEditD->setValue(LLSD(linkset.getWalkabilityCoefficientD()));
+ mEditPhantom->set(linkset.isPhantom());
+
+ setEnableEditFields(true);
+ }
+}
+
+void LLFloaterPathfindingLinksets::applyEditFields()
+{
+ std::vector<LLScrollListItem*> selectedItems = mLinksetsScrollList->getAllSelected();
+ if (!selectedItems.empty())
+ {
+ LLPathfindingLinkset::EPathState pathState = getPathState();
+ const std::string &aString = mEditA->getText();
+ const std::string &bString = mEditB->getText();
+ const std::string &cString = mEditC->getText();
+ const std::string &dString = mEditD->getText();
+ S32 aValue = static_cast<S32>(atoi(aString.c_str()));
+ S32 bValue = static_cast<S32>(atoi(bString.c_str()));
+ S32 cValue = static_cast<S32>(atoi(cString.c_str()));
+ S32 dValue = static_cast<S32>(atoi(dString.c_str()));
+ BOOL isPhantom = mEditPhantom->getValue();
+
+ const LLFilteredPathfindingLinksets::PathfindingLinksetMap &linksetsMap = mPathfindingLinksets.getAllLinksets();
+
+ LLSD editData;
+ for (std::vector<LLScrollListItem*>::const_iterator itemIter = selectedItems.begin();
+ itemIter != selectedItems.end(); ++itemIter)
+ {
+ const LLScrollListItem *listItem = *itemIter;
+ LLUUID uuid = listItem->getUUID();
+
+ const LLFilteredPathfindingLinksets::PathfindingLinksetMap::const_iterator linksetIter = linksetsMap.find(uuid.asString());
+ const LLPathfindingLinkset &linkset = linksetIter->second;
+
+ LLSD itemData = linkset.encodeAlteredFields(pathState, aValue, bValue, cValue, dValue, isPhantom);
+
+ if (!itemData.isUndefined())
+ {
+ editData[uuid.asString()] = itemData;
+ }
+ }
+
+ if (editData.isUndefined())
+ {
+ llwarns << "No PUT data specified" << llendl;
+ }
+ else
+ {
+ sendNavMeshDataPutRequest(editData);
+ }
+ }
+}
+
+void LLFloaterPathfindingLinksets::setEnableEditFields(BOOL pEnabled)
+{
+ mEditPathState->setEnabled(pEnabled);
+ mEditPathStateWalkable->setEnabled(pEnabled);
+ mEditPathStateObstacle->setEnabled(pEnabled);
+ mEditPathStateIgnored->setEnabled(pEnabled);
+ mLabelWalkabilityCoefficients->setEnabled(pEnabled);
+ mLabelEditA->setEnabled(pEnabled);
+ mLabelEditB->setEnabled(pEnabled);
+ mLabelEditC->setEnabled(pEnabled);
+ mLabelEditD->setEnabled(pEnabled);
+ mEditA->setEnabled(pEnabled);
+ mEditB->setEnabled(pEnabled);
+ mEditC->setEnabled(pEnabled);
+ mEditD->setEnabled(pEnabled);
+ mEditPhantom->setEnabled(pEnabled);
+ mApplyEdits->setEnabled(pEnabled);
+}
+
+LLPathfindingLinkset::EPathState LLFloaterPathfindingLinksets::getPathState() const
+{
+ LLPathfindingLinkset::EPathState pathState;
+
+ switch (mEditPathState->getValue().asInteger())
+ {
+ case XUI_PATH_STATE_WALKABLE :
+ pathState = LLPathfindingLinkset::kWalkable;
+ break;
+ case XUI_PATH_STATE_OBSTACLE :
+ pathState = LLPathfindingLinkset::kObstacle;
+ break;
+ case XUI_PATH_STATE_IGNORED :
+ pathState = LLPathfindingLinkset::kIgnored;
+ break;
+ default :
+ pathState = LLPathfindingLinkset::kIgnored;
+ llassert(0);
+ break;
+ }
+
+ return pathState;
+}
+
+void LLFloaterPathfindingLinksets::setPathState(LLPathfindingLinkset::EPathState pPathState)
+{
+ LLSD radioGroupValue;
+
+ switch (pPathState)
+ {
+ case LLPathfindingLinkset::kWalkable :
+ radioGroupValue = XUI_PATH_STATE_WALKABLE;
+ break;
+ case LLPathfindingLinkset::kObstacle :
+ radioGroupValue = XUI_PATH_STATE_OBSTACLE;
+ break;
+ case LLPathfindingLinkset::kIgnored :
+ radioGroupValue = XUI_PATH_STATE_IGNORED;
+ break;
+ default :
+ radioGroupValue = XUI_PATH_STATE_IGNORED;
+ llassert(0);
+ break;
+ }
+
+ mEditPathState->setValue(radioGroupValue);
+}
+
+//---------------------------------------------------------------------------
+// NavMeshDataGetResponder
+//---------------------------------------------------------------------------
+
+NavMeshDataGetResponder::NavMeshDataGetResponder(const std::string& pNavMeshDataGetURL,
+ const LLHandle<LLFloaterPathfindingLinksets> &pLinksetsHandle)
+ : mNavMeshDataGetURL(pNavMeshDataGetURL),
+ mLinksetsFloaterHandle(pLinksetsHandle)
+{
+}
+
+NavMeshDataGetResponder::~NavMeshDataGetResponder()
+{
+}
+
+void NavMeshDataGetResponder::result(const LLSD& pContent)
+{
+ LLFloaterPathfindingLinksets *linksetsFloater = mLinksetsFloaterHandle.get();
+ if (linksetsFloater != NULL)
+ {
+ linksetsFloater->handleNavMeshDataGetReply(pContent);
+ }
+}
+
+void NavMeshDataGetResponder::error(U32 status, const std::string& reason)
+{
+ LLFloaterPathfindingLinksets *linksetsFloater = mLinksetsFloaterHandle.get();
+ if (linksetsFloater != NULL)
+ {
+ linksetsFloater->handleNavMeshDataGetError(mNavMeshDataGetURL, reason);
+ }
+}
+
+//---------------------------------------------------------------------------
+// NavMeshDataPutResponder
+//---------------------------------------------------------------------------
+
+NavMeshDataPutResponder::NavMeshDataPutResponder(const std::string& pNavMeshDataPutURL,
+ const LLHandle<LLFloaterPathfindingLinksets> &pLinksetsHandle)
+ : mNavMeshDataPutURL(pNavMeshDataPutURL),
+ mLinksetsFloaterHandle(pLinksetsHandle)
+{
+}
+
+NavMeshDataPutResponder::~NavMeshDataPutResponder()
+{
+}
+
+void NavMeshDataPutResponder::result(const LLSD& pContent)
+{
+ LLFloaterPathfindingLinksets *linksetsFloater = mLinksetsFloaterHandle.get();
+ if (linksetsFloater != NULL)
+ {
+ linksetsFloater->handleNavMeshDataPutReply(pContent);
+ }
+}
+
+void NavMeshDataPutResponder::error(U32 status, const std::string& reason)
+{
+ LLFloaterPathfindingLinksets *linksetsFloater = mLinksetsFloaterHandle.get();
+ if (linksetsFloater != NULL)
+ {
+ linksetsFloater->handleNavMeshDataPutError(mNavMeshDataPutURL, reason);
+ }
+}
diff --git a/indra/newview/llfloaterpathfindinglinksets.h b/indra/newview/llfloaterpathfindinglinksets.h
new file mode 100644
index 0000000000..250625c72a
--- /dev/null
+++ b/indra/newview/llfloaterpathfindinglinksets.h
@@ -0,0 +1,147 @@
+/**
+ * @file llfloaterpathfindinglinksets.h
+ * @author William Todd Stinson
+ * @brief "Pathfinding linksets" floater, allowing manipulation of the Havok AI pathfinding settings.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERPATHFINDINGLINKSETS_H
+#define LL_LLFLOATERPATHFINDINGLINKSETS_H
+
+#include "llhandle.h"
+#include "llfloater.h"
+#include "lluuid.h"
+#include "llpathfindinglinkset.h"
+#include "llfilteredpathfindinglinksets.h"
+
+class LLSD;
+class LLTextBase;
+class LLScrollListCtrl;
+class LLLineEditor;
+class LLCheckBoxCtrl;
+class LLRadioGroup;
+class LLButton;
+
+class LLFloaterPathfindingLinksets
+: public LLFloater
+{
+ friend class LLFloaterReg;
+ friend class NavMeshDataGetResponder;
+ friend class NavMeshDataPutResponder;
+
+public:
+ typedef enum
+ {
+ kMessagingInitial,
+ kMessagingFetchStarting,
+ kMessagingFetchRequestSent,
+ kMessagingFetchRequestSent_MultiRequested,
+ kMessagingFetchReceived,
+ kMessagingFetchError,
+ kMessagingModifyStarting,
+ kMessagingModifyRequestSent,
+ kMessagingModifyReceived,
+ kMessagingModifyError,
+ kMessagingComplete
+ } EMessagingState;
+
+ virtual BOOL postBuild();
+ virtual void onOpen(const LLSD& pKey);
+
+ static void openLinksetsEditor();
+
+ EMessagingState getMessagingState() const;
+ BOOL isMessagingInProgress() const;
+
+protected:
+
+private:
+ LLRootHandle<LLFloaterPathfindingLinksets> mSelfHandle;
+ LLFilteredPathfindingLinksets mPathfindingLinksets;
+ EMessagingState mMessagingState;
+ LLScrollListCtrl *mLinksetsScrollList;
+ LLTextBase *mLinksetsStatus;
+ LLLineEditor *mFilterByName;
+ LLLineEditor *mFilterByDescription;
+ LLCheckBoxCtrl *mFilterByWalkable;
+ LLCheckBoxCtrl *mFilterByObstacle;
+ LLCheckBoxCtrl *mFilterByIgnored;
+ LLRadioGroup *mEditPathState;
+ LLUICtrl *mEditPathStateWalkable;
+ LLUICtrl *mEditPathStateObstacle;
+ LLUICtrl *mEditPathStateIgnored;
+ LLTextBase *mLabelWalkabilityCoefficients;
+ LLTextBase *mLabelEditA;
+ LLTextBase *mLabelEditB;
+ LLTextBase *mLabelEditC;
+ LLTextBase *mLabelEditD;
+ LLLineEditor *mEditA;
+ LLLineEditor *mEditB;
+ LLLineEditor *mEditC;
+ LLLineEditor *mEditD;
+ LLCheckBoxCtrl *mEditPhantom;
+ LLButton *mApplyEdits;
+
+ // Does its own instance management, so clients not allowed
+ // to allocate or destroy.
+ LLFloaterPathfindingLinksets(const LLSD& pSeed);
+ virtual ~LLFloaterPathfindingLinksets();
+
+ void sendNavMeshDataGetRequest();
+ void sendNavMeshDataPutRequest(const LLSD& pPostData);
+ void handleNavMeshDataGetReply(const LLSD& pNavMeshData);
+ void handleNavMeshDataGetError(const std::string& pURL, const std::string& pErrorReason);
+ void handleNavMeshDataPutReply(const LLSD& pModifiedData);
+ void handleNavMeshDataPutError(const std::string& pURL, const std::string& pErrorReason);
+
+ std::string getRegionName() const;
+ std::string getCapabilityURL() const;
+
+ void setMessagingState(EMessagingState pMessagingState);
+
+ void onApplyFiltersClicked();
+ void onClearFiltersClicked();
+ void onLinksetsSelectionChange();
+ void onRefreshLinksetsClicked();
+ void onSelectAllLinksetsClicked();
+ void onSelectNoneLinksetsClicked();
+ void onApplyChangesClicked();
+
+ void applyFilters();
+ void clearFilters();
+
+ void updateLinksetsList();
+ void selectAllLinksets();
+ void selectNoneLinksets();
+
+ void updateLinksetsStatusMessage();
+
+ void updateEditFields();
+ void applyEditFields();
+ void setEnableEditFields(BOOL pEnabled);
+
+ LLPathfindingLinkset::EPathState getPathState() const;
+ void setPathState(LLPathfindingLinkset::EPathState pPathState);
+};
+
+#endif // LL_LLFLOATERPATHFINDINGLINKSETS_H
diff --git a/indra/newview/llfloaterpathfindingsetup.cpp b/indra/newview/llfloaterpathfindingsetup.cpp
new file mode 100644
index 0000000000..dc8e96eb53
--- /dev/null
+++ b/indra/newview/llfloaterpathfindingsetup.cpp
@@ -0,0 +1,653 @@
+/**
+* @file llfloaterpathfindingsetup.cpp
+* @author William Todd Stinson
+* @brief "Pathfinding console" floater, allowing manipulation of the Havok AI pathfinding settings.
+*
+* $LicenseInfo:firstyear=2002&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloaterpathfindingsetup.h"
+#include "llfloaterpathfindinglinksets.h"
+
+#include "llsd.h"
+#include "llagent.h"
+#include "llbutton.h"
+#include "llradiogroup.h"
+#include "llsliderctrl.h"
+#include "lllineeditor.h"
+#include "lltextbase.h"
+#include "lltextvalidate.h"
+#include "llnavmeshstation.h"
+#include "llviewerregion.h"
+
+#include "LLPathingLib.h"
+
+#define XUI_RENDER_OVERLAY_ON_FIXED_PHYSICS_GEOMETRY 1
+#define XUI_RENDER_OVERLAY_ON_ALL_RENDERABLE_GEOMETRY 2
+
+#define XUI_PATH_SELECT_NONE 0
+#define XUI_PATH_SELECT_START_POINT 1
+#define XUI_PATH_SELECT_END_POINT 2
+
+#define XUI_CHARACTER_TYPE_A 1
+#define XUI_CHARACTER_TYPE_B 2
+#define XUI_CHARACTER_TYPE_C 3
+#define XUI_CHARACTER_TYPE_D 4
+
+const int CURRENT_REGION = 99;
+const int MAX_OBSERVERS = 10;
+//---------------------------------------------------------------------------
+// LLFloaterPathfindingSetup
+//---------------------------------------------------------------------------
+
+BOOL LLFloaterPathfindingSetup::postBuild()
+{
+ childSetAction("view_and_edit_linksets", boost::bind(&LLFloaterPathfindingSetup::onViewEditLinksetClicked, this));
+ childSetAction("rebuild_navmesh", boost::bind(&LLFloaterPathfindingSetup::onRebuildNavMeshClicked, this));
+ childSetAction("refresh_navmesh", boost::bind(&LLFloaterPathfindingSetup::onRefreshNavMeshClicked, this));
+
+ mShowNavMeshCheckBox = findChild<LLCheckBoxCtrl>("show_navmesh_overlay");
+ llassert(mShowNavMeshCheckBox != NULL);
+ mShowNavMeshCheckBox->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onShowNavMeshToggle, this));
+
+ mShowExcludeVolumesCheckBox = findChild<LLCheckBoxCtrl>("show_exclusion_volumes");
+ llassert(mShowExcludeVolumesCheckBox != NULL);
+ mShowExcludeVolumesCheckBox->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onShowExcludeVolumesToggle, this));
+
+ mShowPathCheckBox = findChild<LLCheckBoxCtrl>("show_path");
+ llassert(mShowPathCheckBox != NULL);
+ mShowPathCheckBox->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onShowPathToggle, this));
+
+ mShowWaterPlaneCheckBox = findChild<LLCheckBoxCtrl>("show_water_plane");
+ llassert(mShowWaterPlaneCheckBox != NULL);
+ mShowWaterPlaneCheckBox->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onShowWaterPlaneToggle, this));
+
+ mRegionOverlayDisplayRadioGroup = findChild<LLRadioGroup>("region_overlay_display");
+ llassert(mRegionOverlayDisplayRadioGroup != NULL);
+ mRegionOverlayDisplayRadioGroup->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onRegionOverlayDisplaySwitch, this));
+
+ mPathSelectionRadioGroup = findChild<LLRadioGroup>("path_selection");
+ llassert(mPathSelectionRadioGroup != NULL);
+ mPathSelectionRadioGroup ->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onPathSelectionSwitch, this));
+
+ mCharacterWidthSlider = findChild<LLSliderCtrl>("character_width");
+ llassert(mCharacterWidthSlider != NULL);
+ mCharacterWidthSlider->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onCharacterWidthSet, this));
+
+ mCharacterTypeRadioGroup = findChild<LLRadioGroup>("character_type");
+ llassert(mCharacterTypeRadioGroup != NULL);
+ mCharacterTypeRadioGroup->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onCharacterTypeSwitch, this));
+
+ mPathfindingStatus = findChild<LLTextBase>("pathfinding_status");
+ llassert(mPathfindingStatus != NULL);
+
+ mTerrainMaterialA = findChild<LLLineEditor>("terrain_material_a");
+ llassert(mTerrainMaterialA != NULL);
+ mTerrainMaterialA->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onTerrainMaterialASet, this));
+ mTerrainMaterialA->setPrevalidate(LLTextValidate::validateFloat);
+
+ mTerrainMaterialB = findChild<LLLineEditor>("terrain_material_b");
+ llassert(mTerrainMaterialB != NULL);
+ mTerrainMaterialB->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onTerrainMaterialBSet, this));
+ mTerrainMaterialB->setPrevalidate(LLTextValidate::validateFloat);
+
+ mTerrainMaterialC = findChild<LLLineEditor>("terrain_material_c");
+ llassert(mTerrainMaterialC != NULL);
+ mTerrainMaterialC->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onTerrainMaterialCSet, this));
+ mTerrainMaterialC->setPrevalidate(LLTextValidate::validateFloat);
+
+ mTerrainMaterialD = findChild<LLLineEditor>("terrain_material_d");
+ llassert(mTerrainMaterialD != NULL);
+ mTerrainMaterialD->setCommitCallback(boost::bind(&LLFloaterPathfindingSetup::onTerrainMaterialDSet, this));
+ mTerrainMaterialD->setPrevalidate(LLTextValidate::validateFloat);
+
+ return LLFloater::postBuild();
+}
+
+LLFloaterPathfindingSetup::ERegionOverlayDisplay LLFloaterPathfindingSetup::getRegionOverlayDisplay() const
+{
+ ERegionOverlayDisplay regionOverlayDisplay;
+ switch (mRegionOverlayDisplayRadioGroup->getValue().asInteger())
+ {
+ case XUI_RENDER_OVERLAY_ON_FIXED_PHYSICS_GEOMETRY :
+ regionOverlayDisplay = kRenderOverlayOnFixedPhysicsGeometry;
+ break;
+ case XUI_RENDER_OVERLAY_ON_ALL_RENDERABLE_GEOMETRY :
+ regionOverlayDisplay = kRenderOverlayOnAllRenderableGeometry;
+ break;
+ default :
+ regionOverlayDisplay = kRenderOverlayOnFixedPhysicsGeometry;
+ llassert(0);
+ break;
+ }
+
+ return regionOverlayDisplay;
+}
+
+void LLFloaterPathfindingSetup::setRegionOverlayDisplay(ERegionOverlayDisplay pRegionOverlayDisplay)
+{
+ LLSD radioGroupValue;
+
+ switch (pRegionOverlayDisplay)
+ {
+ case kRenderOverlayOnFixedPhysicsGeometry :
+ radioGroupValue = XUI_RENDER_OVERLAY_ON_FIXED_PHYSICS_GEOMETRY;
+ break;
+ case kRenderOverlayOnAllRenderableGeometry :
+ radioGroupValue = XUI_RENDER_OVERLAY_ON_ALL_RENDERABLE_GEOMETRY;
+ break;
+ default :
+ radioGroupValue = XUI_RENDER_OVERLAY_ON_FIXED_PHYSICS_GEOMETRY;
+ llassert(0);
+ break;
+ }
+
+ mRegionOverlayDisplayRadioGroup->setValue(radioGroupValue);
+}
+
+LLFloaterPathfindingSetup::EPathSelectionState LLFloaterPathfindingSetup::getPathSelectionState() const
+{
+ EPathSelectionState pathSelectionState;
+
+ switch (mPathSelectionRadioGroup->getValue().asInteger())
+ {
+ case XUI_PATH_SELECT_START_POINT :
+ pathSelectionState = kPathSelectStartPoint;
+ break;
+ case XUI_PATH_SELECT_END_POINT :
+ pathSelectionState = kPathSelectEndPoint;
+ break;
+ default :
+ pathSelectionState = kPathSelectNone;
+ break;
+ }
+
+ return pathSelectionState;
+}
+
+void LLFloaterPathfindingSetup::setPathSelectionState(EPathSelectionState pPathSelectionState)
+{
+ LLSD radioGroupValue;
+
+ switch (pPathSelectionState)
+ {
+ case kPathSelectStartPoint :
+ radioGroupValue = XUI_PATH_SELECT_START_POINT;
+ break;
+ case kPathSelectEndPoint :
+ radioGroupValue = XUI_PATH_SELECT_END_POINT;
+ break;
+ default :
+ radioGroupValue = XUI_PATH_SELECT_NONE;
+ break;
+ }
+
+ mPathSelectionRadioGroup->setValue(radioGroupValue);
+}
+
+F32 LLFloaterPathfindingSetup::getCharacterWidth() const
+{
+ return mCharacterWidthSlider->getValueF32();
+}
+
+void LLFloaterPathfindingSetup::setCharacterWidth(F32 pCharacterWidth)
+{
+ mCharacterWidthSlider->setValue(LLSD(pCharacterWidth));
+}
+
+LLFloaterPathfindingSetup::ECharacterType LLFloaterPathfindingSetup::getCharacterType() const
+{
+ ECharacterType characterType;
+
+ switch (mCharacterTypeRadioGroup->getValue().asInteger())
+ {
+ case XUI_CHARACTER_TYPE_A :
+ characterType = kCharacterTypeA;
+ break;
+ case XUI_CHARACTER_TYPE_B :
+ characterType = kCharacterTypeB;
+ break;
+ case XUI_CHARACTER_TYPE_C :
+ characterType = kCharacterTypeC;
+ break;
+ case XUI_CHARACTER_TYPE_D :
+ characterType = kCharacterTypeD;
+ break;
+ default :
+ characterType = kCharacterTypeA;
+ llassert(0);
+ break;
+ }
+
+ return characterType;
+}
+
+void LLFloaterPathfindingSetup::setCharacterType(ECharacterType pCharacterType)
+{
+ LLSD radioGroupValue;
+
+ switch (pCharacterType)
+ {
+ case kCharacterTypeA :
+ radioGroupValue = XUI_CHARACTER_TYPE_A;
+ break;
+ case kCharacterTypeB :
+ radioGroupValue = XUI_CHARACTER_TYPE_B;
+ break;
+ case kCharacterTypeC :
+ radioGroupValue = XUI_CHARACTER_TYPE_C;
+ break;
+ case kCharacterTypeD :
+ radioGroupValue = XUI_CHARACTER_TYPE_D;
+ break;
+ default :
+ radioGroupValue = XUI_CHARACTER_TYPE_A;
+ llassert(0);
+ break;
+ }
+
+ mCharacterTypeRadioGroup->setValue(radioGroupValue);
+}
+
+F32 LLFloaterPathfindingSetup::getTerrainMaterialA() const
+{
+ return mTerrainMaterialA->getValue().asReal();
+}
+
+void LLFloaterPathfindingSetup::setTerrainMaterialA(F32 pTerrainMaterial)
+{
+ mTerrainMaterialA->setValue(LLSD(pTerrainMaterial));
+}
+
+F32 LLFloaterPathfindingSetup::getTerrainMaterialB() const
+{
+ return mTerrainMaterialB->getValue().asReal();
+}
+
+void LLFloaterPathfindingSetup::setTerrainMaterialB(F32 pTerrainMaterial)
+{
+ mTerrainMaterialB->setValue(LLSD(pTerrainMaterial));
+}
+
+F32 LLFloaterPathfindingSetup::getTerrainMaterialC() const
+{
+ return mTerrainMaterialC->getValue().asReal();
+}
+
+void LLFloaterPathfindingSetup::setTerrainMaterialC(F32 pTerrainMaterial)
+{
+ mTerrainMaterialC->setValue(LLSD(pTerrainMaterial));
+}
+
+F32 LLFloaterPathfindingSetup::getTerrainMaterialD() const
+{
+ return mTerrainMaterialD->getValue().asReal();
+}
+
+void LLFloaterPathfindingSetup::setTerrainMaterialD(F32 pTerrainMaterial)
+{
+ mTerrainMaterialD->setValue(LLSD(pTerrainMaterial));
+}
+
+void LLFloaterPathfindingSetup::setHasNavMeshReceived()
+{
+ std::string str = getString("navmesh_fetch_complete_available");
+ mPathfindingStatus->setText((LLStringExplicit)str);
+ //check to see if all regions are done loading and they are then stitch the navmeshes together
+ --mNavMeshCnt;
+ if ( mNavMeshCnt == 0 )
+ {
+ LLPathingLib::getInstance()->stitchNavMeshes();
+ }
+}
+
+void LLFloaterPathfindingSetup::setHasNoNavMesh()
+{
+ std::string str = getString("navmesh_fetch_complete_none");
+ mPathfindingStatus->setText((LLStringExplicit)str);
+}
+
+LLFloaterPathfindingSetup::LLFloaterPathfindingSetup(const LLSD& pSeed)
+ : LLFloater(pSeed),
+ mShowNavMeshCheckBox(NULL),
+ mShowExcludeVolumesCheckBox(NULL),
+ mShowPathCheckBox(NULL),
+ mShowWaterPlaneCheckBox(NULL),
+ mRegionOverlayDisplayRadioGroup(NULL),
+ mPathSelectionRadioGroup(NULL),
+ mCharacterWidthSlider(NULL),
+ mCharacterTypeRadioGroup(NULL),
+ mPathfindingStatus(NULL),
+ mTerrainMaterialA(NULL),
+ mTerrainMaterialB(NULL),
+ mTerrainMaterialC(NULL),
+ mTerrainMaterialD(NULL),
+ mNavMeshCnt(0),
+ mHasStartPoint(false),
+ mHasEndPoint(false)
+{
+ for (int i=0;i<MAX_OBSERVERS;++i)
+ {
+ mNavMeshDownloadObserver[i].setPathfindingConsole(this);
+ }
+}
+
+LLFloaterPathfindingSetup::~LLFloaterPathfindingSetup()
+{
+}
+
+void LLFloaterPathfindingSetup::onOpen(const LLSD& pKey)
+{
+ //make sure we have a pathing system
+ if ( !LLPathingLib::getInstance() )
+ {
+ LLPathingLib::initSystem();
+ }
+ if ( LLPathingLib::getInstance() == NULL )
+ {
+ std::string str = getString("navmesh_library_not_implemented");
+ LLStyle::Params styleParams;
+ styleParams.color = LLUIColorTable::instance().getColor("DrYellow");
+ mPathfindingStatus->setText((LLStringExplicit)str, styleParams);
+ llwarns <<"Errror: cannout find pathing library implementation."<<llendl;
+ }
+ else
+ {
+ LLPathingLib::getInstance()->cleanupResidual();
+
+ mCurrentMDO = 0;
+ mNavMeshCnt = 0;
+
+ //make sure the region is essentially enabled for navmesh support
+ std::string capability = "RetrieveNavMeshSrc";
+
+ //prep# neighboring navmesh support proto
+ LLViewerRegion* pCurrentRegion = gAgent.getRegion();
+ std::vector<LLViewerRegion*> regions;
+ regions.push_back( pCurrentRegion );
+ pCurrentRegion->getNeighboringRegions( regions );
+
+ std::vector<int> shift;
+ shift.push_back( CURRENT_REGION );
+ pCurrentRegion->getNeighboringRegionsStatus( shift );
+
+ //If the navmesh shift ops and the total region counts do not match - use the current region, only.
+ if ( shift.size() != regions.size() )
+ {
+ shift.clear();regions.clear();
+ regions.push_back( pCurrentRegion );
+ shift.push_back( CURRENT_REGION );
+ }
+ int regionCnt = regions.size();
+ mNavMeshCnt = regionCnt;
+ for ( int i=0; i<regionCnt; ++i )
+ {
+ std::string url = regions[i]->getCapability( capability );
+
+ if ( !url.empty() )
+ {
+ std::string str = getString("navmesh_fetch_inprogress");
+ mPathfindingStatus->setText((LLStringExplicit)str);
+ LLNavMeshStation::getInstance()->setNavMeshDownloadURL( url );
+ int dir = shift[i];
+ LLNavMeshStation::getInstance()->downloadNavMeshSrc( mNavMeshDownloadObserver[mCurrentMDO].getObserverHandle(), dir );
+ ++mCurrentMDO;
+ }
+ else
+ {
+ --mNavMeshCnt;
+ std::string str = getString("navmesh_region_not_enabled");
+ LLStyle::Params styleParams;
+ styleParams.color = LLUIColorTable::instance().getColor("DrYellow");
+ mPathfindingStatus->setText((LLStringExplicit)str, styleParams);
+ llinfos<<"Region has does not required caps of type ["<<capability<<"]"<<llendl;
+ }
+ }
+ }
+}
+
+void LLFloaterPathfindingSetup::onShowNavMeshToggle()
+{
+ BOOL checkBoxValue = mShowNavMeshCheckBox->get();
+
+ LLPathingLib *llPathingLibInstance = LLPathingLib::getInstance();
+ if (llPathingLibInstance != NULL)
+ {
+ llPathingLibInstance->setRenderNavMesh(checkBoxValue);
+ }
+ else
+ {
+ mShowNavMeshCheckBox->set(FALSE);
+ llwarns << "cannot find LLPathingLib instance" << llendl;
+ }
+}
+
+void LLFloaterPathfindingSetup::onShowExcludeVolumesToggle()
+{
+ BOOL checkBoxValue = mShowExcludeVolumesCheckBox->get();
+
+ LLPathingLib *llPathingLibInstance = LLPathingLib::getInstance();
+ if (llPathingLibInstance != NULL)
+ {
+ llPathingLibInstance->setRenderShapes(checkBoxValue);
+ }
+ else
+ {
+ mShowExcludeVolumesCheckBox->set(FALSE);
+ llwarns << "cannot find LLPathingLib instance" << llendl;
+ }
+}
+
+void LLFloaterPathfindingSetup::onShowPathToggle()
+{
+ BOOL checkBoxValue = mShowPathCheckBox->get();
+
+ LLPathingLib *llPathingLibInstance = LLPathingLib::getInstance();
+ if (llPathingLibInstance != NULL)
+ {
+ llPathingLibInstance->setRenderPath(checkBoxValue);
+ }
+ else
+ {
+ mShowPathCheckBox->set(FALSE);
+ llwarns << "cannot find LLPathingLib instance" << llendl;
+ }
+}
+
+void LLFloaterPathfindingSetup::onShowWaterPlaneToggle()
+{
+ BOOL checkBoxValue = mShowWaterPlaneCheckBox->get();
+
+ LLPathingLib *llPathingLibInstance = LLPathingLib::getInstance();
+ if (llPathingLibInstance != NULL)
+ {
+ llPathingLibInstance->setRenderWaterPlane(checkBoxValue);
+ }
+ else
+ {
+ mShowWaterPlaneCheckBox->set(FALSE);
+ llwarns << "cannot find LLPathingLib instance" << llendl;
+ }
+
+ llwarns << "functionality has not yet been implemented to toggle '"
+ << mShowWaterPlaneCheckBox->getLabel() << "' to "
+ << (checkBoxValue ? "ON" : "OFF") << llendl;
+}
+
+void LLFloaterPathfindingSetup::onRegionOverlayDisplaySwitch()
+{
+ LLPathingLib *llPathingLibInstance = LLPathingLib::getInstance();
+ if (llPathingLibInstance != NULL)
+ {
+ switch (getRegionOverlayDisplay())
+ {
+ case kRenderOverlayOnFixedPhysicsGeometry :
+ llPathingLibInstance->setRenderOverlayMode(false);
+ break;
+ case kRenderOverlayOnAllRenderableGeometry :
+ llPathingLibInstance->setRenderOverlayMode(true);
+ break;
+ default :
+ llPathingLibInstance->setRenderOverlayMode(false);
+ llassert(0);
+ break;
+ }
+ }
+ else
+ {
+ this->setRegionOverlayDisplay(kRenderOverlayOnFixedPhysicsGeometry);
+ llwarns << "cannot find LLPathingLib instance" << llendl;
+ }
+}
+
+void LLFloaterPathfindingSetup::onPathSelectionSwitch()
+{
+ switch (getPathSelectionState())
+ {
+ case kPathSelectNone :
+ break;
+ case kPathSelectStartPoint :
+ break;
+ case kPathSelectEndPoint :
+ break;
+ default :
+ llassert(0);
+ break;
+ }
+}
+
+void LLFloaterPathfindingSetup::onCharacterWidthSet()
+{
+ generatePath();
+}
+
+void LLFloaterPathfindingSetup::onCharacterTypeSwitch()
+{
+ switch (getCharacterType())
+ {
+ case kCharacterTypeA :
+ llwarns << "functionality has not yet been implemented to toggle '"
+ << mCharacterTypeRadioGroup->getName() << "' to CharacterTypeA"
+ << llendl;
+ break;
+ case kCharacterTypeB :
+ llwarns << "functionality has not yet been implemented to toggle '"
+ << mCharacterTypeRadioGroup->getName() << "' to CharacterTypeB"
+ << llendl;
+ break;
+ case kCharacterTypeC :
+ llwarns << "functionality has not yet been implemented to toggle '"
+ << mCharacterTypeRadioGroup->getName() << "' to CharacterTypeC"
+ << llendl;
+ break;
+ case kCharacterTypeD :
+ llwarns << "functionality has not yet been implemented to toggle '"
+ << mCharacterTypeRadioGroup->getName() << "' to CharacterTypeD"
+ << llendl;
+ break;
+ default :
+ llassert(0);
+ break;
+ }
+
+}
+
+void LLFloaterPathfindingSetup::onViewEditLinksetClicked()
+{
+ LLFloaterPathfindingLinksets::openLinksetsEditor();
+}
+
+void LLFloaterPathfindingSetup::onRebuildNavMeshClicked()
+{
+ llwarns << "functionality has not yet been implemented to handle rebuilding of the navmesh" << llendl;
+}
+
+void LLFloaterPathfindingSetup::onRefreshNavMeshClicked()
+{
+ llwarns << "functionality has not yet been implemented to handle refreshing of the navmesh" << llendl;
+}
+
+void LLFloaterPathfindingSetup::onTerrainMaterialASet()
+{
+ F32 terrainMaterial = getTerrainMaterialA();
+ llwarns << "functionality has not yet been implemented to setting '" << mTerrainMaterialA->getName()
+ << "' to value (" << terrainMaterial << ")" << llendl;
+}
+
+void LLFloaterPathfindingSetup::onTerrainMaterialBSet()
+{
+ F32 terrainMaterial = getTerrainMaterialB();
+ llwarns << "functionality has not yet been implemented to setting '" << mTerrainMaterialB->getName()
+ << "' to value (" << terrainMaterial << ")" << llendl;
+}
+
+void LLFloaterPathfindingSetup::onTerrainMaterialCSet()
+{
+ F32 terrainMaterial = getTerrainMaterialC();
+ llwarns << "functionality has not yet been implemented to setting '" << mTerrainMaterialC->getName()
+ << "' to value (" << terrainMaterial << ")" << llendl;
+}
+
+void LLFloaterPathfindingSetup::onTerrainMaterialDSet()
+{
+ F32 terrainMaterial = getTerrainMaterialD();
+ llwarns << "functionality has not yet been implemented to setting '" << mTerrainMaterialD->getName()
+ << "' to value (" << terrainMaterial << ")" << llendl;
+}
+
+
+void LLFloaterPathfindingSetup::providePathingData( const LLVector3& point1, const LLVector3& point2 )
+{
+ switch (getPathSelectionState())
+ {
+ case kPathSelectNone :
+ break;
+
+ case kPathSelectStartPoint :
+ mPathData.mStartPointA = point1;
+ mPathData.mEndPointA = point2;
+ mHasStartPoint = true;
+ break;
+
+ case kPathSelectEndPoint :
+ mPathData.mStartPointB = point1;
+ mPathData.mEndPointB = point2;
+ mHasEndPoint = true;
+ break;
+
+ default :
+ llassert(0);
+ break;
+ }
+
+ generatePath();
+}
+
+void LLFloaterPathfindingSetup::generatePath()
+{
+ if (mHasStartPoint && mHasEndPoint)
+ {
+ mPathData.mCharacterWidth = getCharacterWidth();
+ LLPathingLib::getInstance()->generatePath(mPathData);
+ }
+}
diff --git a/indra/newview/llfloaterpathfindingsetup.h b/indra/newview/llfloaterpathfindingsetup.h
new file mode 100644
index 0000000000..edb7072b28
--- /dev/null
+++ b/indra/newview/llfloaterpathfindingsetup.h
@@ -0,0 +1,151 @@
+/**
+ * @file llfloaterpathfindingsetup.h
+ * @author William Todd Stinson
+ * @brief "Pathfinding console" floater, allowing manipulation of the Havok AI pathfinding settings.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERPATHFINDINGSETUP_H
+#define LL_LLFLOATERPATHFINDINGSETUP_H
+
+#include "llfloater.h"
+#include "llnavmeshstation.h"
+#include "LLPathingLib.h"
+
+class LLSD;
+class LLRadioGroup;
+class LLSliderCtrl;
+class LLLineEditor;
+class LLTextBase;
+class LLCheckBoxCtrl;
+
+class LLFloaterPathfindingSetup
+: public LLFloater
+{
+ friend class LLFloaterReg;
+
+public:
+ typedef enum
+ {
+ kRenderOverlayOnFixedPhysicsGeometry = 0,
+ kRenderOverlayOnAllRenderableGeometry = 1
+ } ERegionOverlayDisplay;
+
+ typedef enum
+ {
+ kPathSelectNone = 0,
+ kPathSelectStartPoint = 1,
+ kPathSelectEndPoint = 2
+ } EPathSelectionState;
+
+ typedef enum
+ {
+ kCharacterTypeA = 0,
+ kCharacterTypeB = 1,
+ kCharacterTypeC = 2,
+ kCharacterTypeD = 3
+ } ECharacterType;
+
+ virtual BOOL postBuild();
+ //Populates a data packet that is forwarded onto the LLPathingSystem
+ void providePathingData( const LLVector3& point1, const LLVector3& point2 );
+
+ ERegionOverlayDisplay getRegionOverlayDisplay() const;
+ void setRegionOverlayDisplay(ERegionOverlayDisplay pRegionOverlayDisplay);
+
+ EPathSelectionState getPathSelectionState() const;
+ void setPathSelectionState(EPathSelectionState pPathSelectionState);
+
+ F32 getCharacterWidth() const;
+ void setCharacterWidth(F32 pCharacterWidth);
+
+ ECharacterType getCharacterType() const;
+ void setCharacterType(ECharacterType pCharacterType);
+
+ F32 getTerrainMaterialA() const;
+ void setTerrainMaterialA(F32 pTerrainMaterial);
+
+ F32 getTerrainMaterialB() const;
+ void setTerrainMaterialB(F32 pTerrainMaterial);
+
+ F32 getTerrainMaterialC() const;
+ void setTerrainMaterialC(F32 pTerrainMaterial);
+
+ F32 getTerrainMaterialD() const;
+ void setTerrainMaterialD(F32 pTerrainMaterial);
+
+ void setHasNavMeshReceived();
+ void setHasNoNavMesh();
+
+protected:
+
+private:
+ // Does its own instance management, so clients not allowed
+ // to allocate or destroy.
+ LLFloaterPathfindingSetup(const LLSD& pSeed);
+ virtual ~LLFloaterPathfindingSetup();
+
+ virtual void onOpen(const LLSD& pKey);
+
+ void onShowNavMeshToggle();
+ void onShowExcludeVolumesToggle();
+ void onShowPathToggle();
+ void onShowWaterPlaneToggle();
+ void onRegionOverlayDisplaySwitch();
+ void onPathSelectionSwitch();
+ void onCharacterWidthSet();
+ void onCharacterTypeSwitch();
+ void onViewEditLinksetClicked();
+ void onRebuildNavMeshClicked();
+ void onRefreshNavMeshClicked();
+ void onTerrainMaterialASet();
+ void onTerrainMaterialBSet();
+ void onTerrainMaterialCSet();
+ void onTerrainMaterialDSet();
+ void generatePath();
+
+ LLCheckBoxCtrl *mShowNavMeshCheckBox;
+ LLCheckBoxCtrl *mShowExcludeVolumesCheckBox;
+ LLCheckBoxCtrl *mShowPathCheckBox;
+ LLCheckBoxCtrl *mShowWaterPlaneCheckBox;
+ LLRadioGroup *mRegionOverlayDisplayRadioGroup;
+ LLRadioGroup *mPathSelectionRadioGroup;
+ LLSliderCtrl *mCharacterWidthSlider;
+ LLRadioGroup *mCharacterTypeRadioGroup;
+ LLTextBase *mPathfindingStatus;
+ LLLineEditor *mTerrainMaterialA;
+ LLLineEditor *mTerrainMaterialB;
+ LLLineEditor *mTerrainMaterialC;
+ LLLineEditor *mTerrainMaterialD;
+
+ LLNavMeshDownloadObserver mNavMeshDownloadObserver[10];
+ int mCurrentMDO;
+ int mNavMeshCnt;
+
+ //Container that is populated and subsequently submitted to the LLPathingSystem for processing
+ LLPathingLib::PathingPacket mPathData;
+ bool mHasStartPoint;
+ bool mHasEndPoint;
+};
+
+#endif // LL_LLFLOATERPATHFINDINGSETUP_H
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index da81bb057b..4139b9725b 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -35,7 +35,7 @@
#define LLCONVEXDECOMPINTER_STATIC 1
-#include "llconvexdecomposition.h"
+#include "LLConvexDecomposition.h"
#include "lluploadfloaterobservers.h"
class LLVOVolume;
diff --git a/indra/newview/llnavmeshstation.cpp b/indra/newview/llnavmeshstation.cpp
new file mode 100644
index 0000000000..16f459ff84
--- /dev/null
+++ b/indra/newview/llnavmeshstation.cpp
@@ -0,0 +1,105 @@
+/**
+ * @file llnavmeshstation.cpp
+ * @brief
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llnavmeshstation.h"
+#include "llcurl.h"
+#include "LLPathingLib.h"
+#include "llagent.h"
+#include "llviewerregion.h"
+#include "llsdutil.h"
+#include "llfloaterpathfindingsetup.h"
+
+//===============================================================================
+LLNavMeshStation::LLNavMeshStation()
+{
+}
+//===============================================================================
+class LLNavMeshDownloadResponder : public LLCurl::Responder
+{
+public:
+ LLNavMeshDownloadResponder( const LLHandle<LLNavMeshDownloadObserver>& observer_handle, int dir )
+ : mObserverHandle( observer_handle )
+ , mDir( dir )
+ {
+ }
+
+ void error( U32 statusNum, const std::string& reason )
+ {
+ //statusNum;
+ llwarns << "Transport error "<<reason<<llendl;
+ }
+
+ void result( const LLSD& content )
+ {
+ llinfos<<"Content received"<<llendl;
+ if ( content.has("error") )
+ {
+ llwarns << "Error on fetched data"<< llendl;
+ //llinfos<<"LLsd buffer on error"<<ll_pretty_print_sd(content)<<llendl;
+ }
+ else
+ {
+ LLNavMeshDownloadObserver* pObserver = mObserverHandle.get();
+ if ( pObserver )
+ {
+
+ if ( content.has("navmesh_data") )
+ {
+ const LLSD::Binary& stuff = content["navmesh_data"].asBinary();
+ LLPathingLib::getInstance()->extractNavMeshSrcFromLLSD( stuff, mDir );
+ pObserver->getPathfindingConsole()->setHasNavMeshReceived();
+ }
+ else
+ {
+ llwarns<<"no mesh data "<<llendl;
+ pObserver->getPathfindingConsole()->setHasNoNavMesh();
+ }
+ }
+ }
+}
+
+private:
+ //Observer handle
+ LLHandle<LLNavMeshDownloadObserver> mObserverHandle;
+ int mDir;
+};
+//===============================================================================
+void LLNavMeshStation::downloadNavMeshSrc( const LLHandle<LLNavMeshDownloadObserver>& observerHandle, int dir )
+{
+ if ( mNavMeshDownloadURL.empty() )
+ {
+ llinfos << "Unable to upload navmesh because of missing URL" << llendl;
+ }
+ else
+ {
+ LLSD data;
+ data["agent_id"] = gAgent.getID();
+ data["region_id"] = gAgent.getRegion()->getRegionID();
+ LLHTTPClient::post(mNavMeshDownloadURL, data, new LLNavMeshDownloadResponder( observerHandle, dir ) );
+ }
+}
+//===============================================================================
diff --git a/indra/newview/llnavmeshstation.h b/indra/newview/llnavmeshstation.h
new file mode 100644
index 0000000000..0aa714dac8
--- /dev/null
+++ b/indra/newview/llnavmeshstation.h
@@ -0,0 +1,101 @@
+/**
+ * @file llnavmeshstation.h
+ * @brief Client-side navmesh support
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_NAV_MESH_STATION_H
+#define LL_NAV_MESH_STATION_H
+
+//===============================================================================
+#include "llhandle.h"
+//===============================================================================
+class LLCurlRequest;
+class LLFloaterPathfindingSetup;
+//===============================================================================
+class LLNavMeshObserver
+{
+public:
+ //Ctor
+ LLNavMeshObserver()
+ : mPathfindingConsole(NULL)
+ { mObserverHandle.bind(this); }
+ //Dtor
+ virtual ~LLNavMeshObserver() {}
+ //Accessor for the observers handle
+ const LLHandle<LLNavMeshObserver>& getObserverHandle() const { return mObserverHandle; }
+ LLFloaterPathfindingSetup *getPathfindingConsole() {return mPathfindingConsole;}
+ void setPathfindingConsole(LLFloaterPathfindingSetup *pPathfindingConsole) {mPathfindingConsole = pPathfindingConsole;}
+
+protected:
+ LLRootHandle<LLNavMeshObserver> mObserverHandle;
+ LLFloaterPathfindingSetup *mPathfindingConsole;
+};
+//===============================================================================
+class LLNavMeshDownloadObserver
+{
+public:
+ //Ctor
+ LLNavMeshDownloadObserver() { mObserverHandle.bind(this); }
+ //Dtor
+ virtual ~LLNavMeshDownloadObserver() {}
+ //Accessor for the observers handle
+ const LLHandle<LLNavMeshDownloadObserver>& getObserverHandle() const { return mObserverHandle; }
+ LLFloaterPathfindingSetup *getPathfindingConsole() {return mPathfindingConsole;}
+ void setPathfindingConsole(LLFloaterPathfindingSetup *pPathfindingConsole) {mPathfindingConsole = pPathfindingConsole;}
+
+protected:
+ LLRootHandle<LLNavMeshDownloadObserver> mObserverHandle;
+ LLFloaterPathfindingSetup *mPathfindingConsole;
+};
+//===============================================================================
+class LLNavMeshStation : public LLSingleton<LLNavMeshStation>
+{
+public:
+ //Ctor
+ LLNavMeshStation();
+ //Facilitates the posting of a prepopulated llsd block to an existing url
+ bool postNavMeshToServer( LLSD& data, const LLHandle<LLNavMeshObserver>& observerHandle );
+ //Setter for the navmesh upload url
+ void setNavMeshUploadURL( std::string& url ) { mNavMeshUploadURL = url; }
+ //Setter for the navmesh download url
+ void setNavMeshDownloadURL( std::string& url ) { mNavMeshDownloadURL = url; }
+ //Callback to handle the requested src data for this regions navmesh src
+ static void processNavMeshSrc( LLMessageSystem* msg, void** );
+ //Initiate download of the navmesh source from the server
+ void downloadNavMeshSrc( const LLHandle<LLNavMeshDownloadObserver>& observerHandle, int dir );
+
+protected:
+ //Curl object to facilitate posts to server
+ LLCurlRequest* mCurlRequest;
+ //Maximum time in seconds to execute an uploading request.
+ S32 mMeshUploadTimeOut ;
+ //URL used for uploading viewer generated navmesh
+ std::string mNavMeshUploadURL;
+ //URL used for download the src data for a navmesh
+ std::string mNavMeshDownloadURL;
+
+};
+//===============================================================================
+#endif //LL_NAV_MESH_STATION_H
+
diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp
index 12eea7844d..1b629f515b 100644
--- a/indra/newview/llpanelvolume.cpp
+++ b/indra/newview/llpanelvolume.cpp
@@ -68,6 +68,7 @@
#include "llworld.h"
#include "pipeline.h"
#include "llviewershadermgr.h"
+#include "llradiogroup.h"
#include "lldrawpool.h"
#include "lluictrlfactory.h"
@@ -158,6 +159,14 @@ BOOL LLPanelVolume::postBuild()
mSpinPhysicsRestitution->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsRestitution, this, _1, mSpinPhysicsRestitution));
}
+ // Pathfinding Parameters
+ {
+ // Pathfinding state
+ mPathfindingType = findChild<LLRadioGroup>("edit_pathfinding_state");
+ llassert(mPathfindingType != NULL);
+ mPathfindingType->setCommitCallback(boost::bind(&LLPanelVolume::sendPathfindingType, this));
+ }
+
std::map<std::string, std::string> material_name_map;
material_name_map["Stone"]= LLTrans::getString("Stone");
material_name_map["Metal"]= LLTrans::getString("Metal");
@@ -194,7 +203,8 @@ BOOL LLPanelVolume::postBuild()
LLPanelVolume::LLPanelVolume()
: LLPanel(),
- mComboMaterialItemCount(0)
+ mComboMaterialItemCount(0),
+ mPathfindingType(NULL)
{
setMouseOpaque(FALSE);
@@ -463,6 +473,8 @@ void LLPanelVolume::getState( )
mSpinPhysicsRestitution->set(objectp->getPhysicsRestitution());
mSpinPhysicsRestitution->setEnabled(editable);
+ mPathfindingType->setEnabled(editable);
+
// update the physics shape combo to include allowed physics shapes
mComboPhysicsShapeType->removeall();
mComboPhysicsShapeType->add(getString("None"), LLSD(1));
@@ -546,6 +558,8 @@ void LLPanelVolume::refresh()
getChildView("Physics Friction")->setVisible(enable_mesh);
getChildView("Physics Density")->setVisible(enable_mesh);
getChildView("Physics Restitution")->setVisible(enable_mesh);
+
+ mPathfindingType->setVisible(enable_mesh);
/* TODO: add/remove individual physics shape types as per the PhysicsShapeTypes simulator features */
}
@@ -600,6 +614,8 @@ void LLPanelVolume::clearCtrls()
mSpinPhysicsDensity->setEnabled(FALSE);
mSpinPhysicsRestitution->setEnabled(FALSE);
+ mPathfindingType->setEnabled(FALSE);
+
mComboMaterial->setEnabled( FALSE );
}
@@ -685,6 +701,12 @@ void LLPanelVolume::sendPhysicsDensity(LLUICtrl* ctrl, void* userdata)
LLSelectMgr::getInstance()->selectionSetDensity(val);
}
+void LLPanelVolume::sendPathfindingType()
+{
+ S32 val = mPathfindingType->getValue().asInteger();
+ llwarns << "functionality to set '" << mPathfindingType->getName() << "' to value " << val << " has not been implemented." << llendl;
+}
+
void LLPanelVolume::refreshCost()
{
LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h
index 0ef47db0d9..ae66414b0f 100644
--- a/indra/newview/llpanelvolume.h
+++ b/indra/newview/llpanelvolume.h
@@ -40,6 +40,7 @@ class LLButton;
class LLViewerObject;
class LLComboBox;
class LLColorSwatchCtrl;
+class LLRadioGroup;
class LLPanelVolume : public LLPanel
{
@@ -84,6 +85,8 @@ protected:
void sendPhysicsRestitution(LLUICtrl* ctrl, void* userdata);
void sendPhysicsDensity(LLUICtrl* ctrl, void* userdata);
+ void sendPathfindingType();
+
/*
LLTextBox* mLabelSelectSingleMessage;
// Light
@@ -118,6 +121,8 @@ protected:
LLSpinCtrl* mSpinPhysicsFriction;
LLSpinCtrl* mSpinPhysicsDensity;
LLSpinCtrl* mSpinPhysicsRestitution;
+
+ LLRadioGroup* mPathfindingType;
};
#endif
diff --git a/indra/newview/llpathfindingcharacter.cpp b/indra/newview/llpathfindingcharacter.cpp
new file mode 100644
index 0000000000..efbb3606e1
--- /dev/null
+++ b/indra/newview/llpathfindingcharacter.cpp
@@ -0,0 +1,98 @@
+/**
+ * @file llpathfindinglinksets.cpp
+ * @author William Todd Stinson
+ * @brief Definition of a pathfinding character that contains various properties required for havok pathfinding.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llpathfindingcharacter.h"
+#include "llsd.h"
+#include "v3math.h"
+#include "lluuid.h"
+
+#define CHARACTER_NAME_FIELD "name"
+#define CHARACTER_DESCRIPTION_FIELD "description"
+#define CHARACTER_OWNER_FIELD "description"
+#define CHARACTER_CPU_TIME_FIELD "landimpact"
+#define CHARACTER_POSITION_FIELD "position"
+
+//---------------------------------------------------------------------------
+// LLPathfindingCharacter
+//---------------------------------------------------------------------------
+
+LLPathfindingCharacter::LLPathfindingCharacter(const std::string &pUUID, const LLSD& pCharacterItem)
+ : mUUID(pUUID),
+ mName(),
+ mDescription(),
+ mOwner(),
+ mCPUTime(0U),
+ mLocation()
+{
+ llassert(pCharacterItem.has(CHARACTER_NAME_FIELD));
+ llassert(pCharacterItem.get(CHARACTER_NAME_FIELD).isString());
+ mName = pCharacterItem.get(CHARACTER_NAME_FIELD).asString();
+
+ llassert(pCharacterItem.has(CHARACTER_DESCRIPTION_FIELD));
+ llassert(pCharacterItem.get(CHARACTER_DESCRIPTION_FIELD).isString());
+ mDescription = pCharacterItem.get(CHARACTER_DESCRIPTION_FIELD).asString();
+
+ llassert(pCharacterItem.has(CHARACTER_OWNER_FIELD));
+ llassert(pCharacterItem.get(CHARACTER_OWNER_FIELD).isString());
+ mOwner = pCharacterItem.get(CHARACTER_OWNER_FIELD).asString();
+
+ llassert(pCharacterItem.has(CHARACTER_CPU_TIME_FIELD));
+ llassert(pCharacterItem.get(CHARACTER_CPU_TIME_FIELD).isInteger());
+ llassert(pCharacterItem.get(CHARACTER_CPU_TIME_FIELD).asInteger() >= 0);
+ mCPUTime = pCharacterItem.get(CHARACTER_CPU_TIME_FIELD).asInteger();
+
+ llassert(pCharacterItem.has(CHARACTER_POSITION_FIELD));
+ llassert(pCharacterItem.get(CHARACTER_POSITION_FIELD).isArray());
+ mLocation.setValue(pCharacterItem.get(CHARACTER_POSITION_FIELD));
+}
+
+LLPathfindingCharacter::LLPathfindingCharacter(const LLPathfindingCharacter& pOther)
+ : mUUID(pOther.mUUID),
+ mName(pOther.mName),
+ mDescription(pOther.mDescription),
+ mOwner(pOther.mOwner),
+ mCPUTime(pOther.mCPUTime),
+ mLocation(pOther.mLocation)
+{
+}
+
+LLPathfindingCharacter::~LLPathfindingCharacter()
+{
+}
+
+LLPathfindingCharacter& LLPathfindingCharacter::operator =(const LLPathfindingCharacter& pOther)
+{
+ mUUID = pOther.mUUID;
+ mName = pOther.mName;
+ mDescription = pOther.mDescription;
+ mOwner = pOther.mOwner;
+ mCPUTime = pOther.mCPUTime;
+ mLocation = pOther.mLocation;
+
+ return *this;
+}
diff --git a/indra/newview/llpathfindingcharacter.h b/indra/newview/llpathfindingcharacter.h
new file mode 100644
index 0000000000..c64d88892a
--- /dev/null
+++ b/indra/newview/llpathfindingcharacter.h
@@ -0,0 +1,63 @@
+/**
+ * @file llpathfindingcharacter.h
+ * @author William Todd Stinson
+ * @brief Definition of a pathfinding character that contains various properties required for havok pathfinding.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPATHFINDINGCHARACTER_H
+#define LL_LLPATHFINDINGCHARACTER_H
+
+#include "v3math.h"
+#include "lluuid.h"
+
+class LLSD;
+
+class LLPathfindingCharacter
+{
+public:
+ LLPathfindingCharacter(const std::string &pUUID, const LLSD &pCharacterItem);
+ LLPathfindingCharacter(const LLPathfindingCharacter& pOther);
+ virtual ~LLPathfindingCharacter();
+
+ LLPathfindingCharacter& operator = (const LLPathfindingCharacter& pOther);
+
+ inline const LLUUID& getUUID() const {return mUUID;};
+ inline const std::string& getName() const {return mName;};
+ inline const std::string& getDescription() const {return mDescription;};
+ inline const std::string& getOwner() const {return mOwner;};
+ inline U32 getCPUTime() const {return mCPUTime;};
+ inline const LLVector3& getLocation() const {return mLocation;};
+
+protected:
+
+private:
+ LLUUID mUUID;
+ std::string mName;
+ std::string mDescription;
+ std::string mOwner;
+ U32 mCPUTime;
+ LLVector3 mLocation;
+};
+
+#endif // LL_LLPATHFINDINGCHARACTER_H
diff --git a/indra/newview/llpathfindinglinkset.cpp b/indra/newview/llpathfindinglinkset.cpp
new file mode 100644
index 0000000000..daa308f862
--- /dev/null
+++ b/indra/newview/llpathfindinglinkset.cpp
@@ -0,0 +1,356 @@
+/**
+ * @file llpathfindinglinksets.cpp
+ * @author William Todd Stinson
+ * @brief Definition of a pathfinding linkset that contains various properties required for havok pathfinding.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llpathfindinglinkset.h"
+#include "llsd.h"
+#include "v3math.h"
+#include "lluuid.h"
+
+#define LINKSET_NAME_FIELD "name"
+#define LINKSET_DESCRIPTION_FIELD "description"
+#define LINKSET_LAND_IMPACT_FIELD "landimpact"
+#define LINKSET_PERMANENT_FIELD "permanent"
+#define LINKSET_WALKABLE_FIELD "walkable"
+#define LINKSET_PHANTOM_FIELD "phantom"
+#define LINKSET_WALKABILITY_A_FIELD "A"
+#define LINKSET_WALKABILITY_B_FIELD "B"
+#define LINKSET_WALKABILITY_C_FIELD "C"
+#define LINKSET_WALKABILITY_D_FIELD "D"
+#define LINKSET_POSITION_FIELD "position"
+
+//---------------------------------------------------------------------------
+// LLPathfindingLinkset
+//---------------------------------------------------------------------------
+
+const S32 LLPathfindingLinkset::MIN_WALKABILITY_VALUE(0);
+const S32 LLPathfindingLinkset::MAX_WALKABILITY_VALUE(100);
+
+LLPathfindingLinkset::LLPathfindingLinkset(const std::string &pUUID, const LLSD& pLinksetItem)
+ : mUUID(pUUID),
+ mName(),
+ mDescription(),
+ mLandImpact(0U),
+ mLocation(),
+ mPathState(kIgnored),
+ mIsPhantom(false),
+#ifdef XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ mIsWalkabilityCoefficientsF32(false),
+#endif // XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ mWalkabilityCoefficientA(MIN_WALKABILITY_VALUE),
+ mWalkabilityCoefficientB(MIN_WALKABILITY_VALUE),
+ mWalkabilityCoefficientC(MIN_WALKABILITY_VALUE),
+ mWalkabilityCoefficientD(MIN_WALKABILITY_VALUE)
+{
+ llassert(pLinksetItem.has(LINKSET_NAME_FIELD));
+ llassert(pLinksetItem.get(LINKSET_NAME_FIELD).isString());
+ mName = pLinksetItem.get(LINKSET_NAME_FIELD).asString();
+
+ llassert(pLinksetItem.has(LINKSET_DESCRIPTION_FIELD));
+ llassert(pLinksetItem.get(LINKSET_DESCRIPTION_FIELD).isString());
+ mDescription = pLinksetItem.get(LINKSET_DESCRIPTION_FIELD).asString();
+
+ llassert(pLinksetItem.has(LINKSET_LAND_IMPACT_FIELD));
+ llassert(pLinksetItem.get(LINKSET_LAND_IMPACT_FIELD).isInteger());
+ llassert(pLinksetItem.get(LINKSET_LAND_IMPACT_FIELD).asInteger() >= 0);
+ mLandImpact = pLinksetItem.get(LINKSET_LAND_IMPACT_FIELD).asInteger();
+
+ llassert(pLinksetItem.has(LINKSET_PERMANENT_FIELD));
+ llassert(pLinksetItem.get(LINKSET_PERMANENT_FIELD).isBoolean());
+ bool isPermanent = pLinksetItem.get(LINKSET_PERMANENT_FIELD).asBoolean();
+
+ llassert(pLinksetItem.has(LINKSET_WALKABLE_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABLE_FIELD).isBoolean());
+ bool isWalkable = pLinksetItem.get(LINKSET_WALKABLE_FIELD).asBoolean();
+
+ mPathState = getPathState(isPermanent, isWalkable);
+
+ llassert(pLinksetItem.has(LINKSET_PHANTOM_FIELD));
+ llassert(pLinksetItem.get(LINKSET_PHANTOM_FIELD).isBoolean());
+ mIsPhantom = pLinksetItem.get(LINKSET_PHANTOM_FIELD).asBoolean();
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_A_FIELD));
+#ifdef XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ mIsWalkabilityCoefficientsF32 = pLinksetItem.get(LINKSET_WALKABILITY_A_FIELD).isReal();
+ if (mIsWalkabilityCoefficientsF32)
+ {
+ // Old server-side storage was real
+ mWalkabilityCoefficientA = llround(pLinksetItem.get(LINKSET_WALKABILITY_A_FIELD).asReal() * 100.0f);
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_B_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_B_FIELD).isReal());
+ mWalkabilityCoefficientB = llround(pLinksetItem.get(LINKSET_WALKABILITY_B_FIELD).asReal() * 100.0f);
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_C_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_C_FIELD).isReal());
+ mWalkabilityCoefficientC = llround(pLinksetItem.get(LINKSET_WALKABILITY_C_FIELD).asReal() * 100.0f);
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_D_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_D_FIELD).isReal());
+ mWalkabilityCoefficientD = llround(pLinksetItem.get(LINKSET_WALKABILITY_D_FIELD).asReal() * 100.0f);
+ }
+ else
+ {
+ // New server-side storage will be integer
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_A_FIELD).isInteger());
+ mWalkabilityCoefficientA = pLinksetItem.get(LINKSET_WALKABILITY_A_FIELD).asInteger();
+ llassert(mWalkabilityCoefficientA >= MIN_WALKABILITY_VALUE);
+ llassert(mWalkabilityCoefficientA <= MAX_WALKABILITY_VALUE);
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_B_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_B_FIELD).isInteger());
+ mWalkabilityCoefficientB = pLinksetItem.get(LINKSET_WALKABILITY_B_FIELD).asInteger();
+ llassert(mWalkabilityCoefficientB >= MIN_WALKABILITY_VALUE);
+ llassert(mWalkabilityCoefficientB <= MAX_WALKABILITY_VALUE);
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_C_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_C_FIELD).isInteger());
+ mWalkabilityCoefficientC = pLinksetItem.get(LINKSET_WALKABILITY_C_FIELD).asInteger();
+ llassert(mWalkabilityCoefficientC >= MIN_WALKABILITY_VALUE);
+ llassert(mWalkabilityCoefficientC <= MAX_WALKABILITY_VALUE);
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_D_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_D_FIELD).isInteger());
+ mWalkabilityCoefficientD = pLinksetItem.get(LINKSET_WALKABILITY_D_FIELD).asInteger();
+ llassert(mWalkabilityCoefficientD >= MIN_WALKABILITY_VALUE);
+ llassert(mWalkabilityCoefficientD <= MAX_WALKABILITY_VALUE);
+ }
+#else // XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_A_FIELD).isInteger());
+ mWalkabilityCoefficientA = pLinksetItem.get(LINKSET_WALKABILITY_A_FIELD).asInteger();
+ llassert(mWalkabilityCoefficientA >= MIN_WALKABILITY_VALUE);
+ llassert(mWalkabilityCoefficientA <= MAX_WALKABILITY_VALUE);
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_B_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_B_FIELD).isInteger());
+ mWalkabilityCoefficientB = pLinksetItem.get(LINKSET_WALKABILITY_B_FIELD).asInteger();
+ llassert(mWalkabilityCoefficientB >= MIN_WALKABILITY_VALUE);
+ llassert(mWalkabilityCoefficientB <= MAX_WALKABILITY_VALUE);
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_C_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_C_FIELD).isInteger());
+ mWalkabilityCoefficientC = pLinksetItem.get(LINKSET_WALKABILITY_C_FIELD).asInteger();
+ llassert(mWalkabilityCoefficientC >= MIN_WALKABILITY_VALUE);
+ llassert(mWalkabilityCoefficientC <= MAX_WALKABILITY_VALUE);
+
+ llassert(pLinksetItem.has(LINKSET_WALKABILITY_D_FIELD));
+ llassert(pLinksetItem.get(LINKSET_WALKABILITY_D_FIELD).isInteger());
+ mWalkabilityCoefficientD = pLinksetItem.get(LINKSET_WALKABILITY_D_FIELD).asInteger();
+ llassert(mWalkabilityCoefficientD >= MIN_WALKABILITY_VALUE);
+ llassert(mWalkabilityCoefficientD <= MAX_WALKABILITY_VALUE);
+#endif // XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+
+ llassert(pLinksetItem.has(LINKSET_POSITION_FIELD));
+ llassert(pLinksetItem.get(LINKSET_POSITION_FIELD).isArray());
+ mLocation.setValue(pLinksetItem.get(LINKSET_POSITION_FIELD));
+}
+
+LLPathfindingLinkset::LLPathfindingLinkset(const LLPathfindingLinkset& pOther)
+ : mUUID(pOther.mUUID),
+ mName(pOther.mName),
+ mDescription(pOther.mDescription),
+ mLandImpact(pOther.mLandImpact),
+ mLocation(pOther.mLocation),
+ mPathState(pOther.mPathState),
+ mIsPhantom(pOther.mIsPhantom),
+#ifdef XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ mIsWalkabilityCoefficientsF32(pOther.mIsWalkabilityCoefficientsF32),
+#endif // XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ mWalkabilityCoefficientA(pOther.mWalkabilityCoefficientA),
+ mWalkabilityCoefficientB(pOther.mWalkabilityCoefficientB),
+ mWalkabilityCoefficientC(pOther.mWalkabilityCoefficientC),
+ mWalkabilityCoefficientD(pOther.mWalkabilityCoefficientD)
+{
+}
+
+LLPathfindingLinkset::~LLPathfindingLinkset()
+{
+}
+
+LLPathfindingLinkset& LLPathfindingLinkset::operator =(const LLPathfindingLinkset& pOther)
+{
+ mUUID = pOther.mUUID;
+ mName = pOther.mName;
+ mDescription = pOther.mDescription;
+ mLandImpact = pOther.mLandImpact;
+ mLocation = pOther.mLocation;
+ mPathState = pOther.mPathState;
+ mIsPhantom = pOther.mIsPhantom;
+#ifdef XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ mIsWalkabilityCoefficientsF32 = pOther.mIsWalkabilityCoefficientsF32;
+#endif // XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ mWalkabilityCoefficientA = pOther.mWalkabilityCoefficientA;
+ mWalkabilityCoefficientB = pOther.mWalkabilityCoefficientB;
+ mWalkabilityCoefficientC = pOther.mWalkabilityCoefficientC;
+ mWalkabilityCoefficientD = pOther.mWalkabilityCoefficientD;
+
+ return *this;
+}
+
+
+LLPathfindingLinkset::EPathState LLPathfindingLinkset::getPathState(bool pIsPermanent, bool pIsWalkable)
+{
+ return (pIsPermanent ? (pIsWalkable ? kWalkable : kObstacle) : kIgnored);
+}
+
+BOOL LLPathfindingLinkset::isPermanent(EPathState pPathState)
+{
+ BOOL retVal;
+
+ switch (pPathState)
+ {
+ case kWalkable :
+ case kObstacle :
+ retVal = true;
+ break;
+ case kIgnored :
+ retVal = false;
+ break;
+ default :
+ retVal = false;
+ llassert(0);
+ break;
+ }
+
+ return retVal;
+}
+
+BOOL LLPathfindingLinkset::isWalkable(EPathState pPathState)
+{
+ BOOL retVal;
+
+ switch (pPathState)
+ {
+ case kWalkable :
+ retVal = true;
+ break;
+ case kObstacle :
+ case kIgnored :
+ retVal = false;
+ break;
+ default :
+ retVal = false;
+ llassert(0);
+ break;
+ }
+
+ return retVal;
+}
+
+void LLPathfindingLinkset::setWalkabilityCoefficientA(S32 pA)
+{
+ mWalkabilityCoefficientA = llclamp(pA, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+}
+
+void LLPathfindingLinkset::setWalkabilityCoefficientB(S32 pB)
+{
+ mWalkabilityCoefficientB = llclamp(pB, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+}
+
+void LLPathfindingLinkset::setWalkabilityCoefficientC(S32 pC)
+{
+ mWalkabilityCoefficientC = llclamp(pC, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+}
+
+void LLPathfindingLinkset::setWalkabilityCoefficientD(S32 pD)
+{
+ mWalkabilityCoefficientD = llclamp(pD, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+}
+
+LLSD LLPathfindingLinkset::encodeAlteredFields(EPathState pPathState, S32 pA, S32 pB, S32 pC, S32 pD, BOOL pIsPhantom) const
+{
+ LLSD itemData;
+
+ if (mPathState != pPathState)
+ {
+ itemData[LINKSET_PERMANENT_FIELD] = static_cast<bool>(LLPathfindingLinkset::isPermanent(pPathState));
+ itemData[LINKSET_WALKABLE_FIELD] = static_cast<bool>(LLPathfindingLinkset::isWalkable(pPathState));
+ }
+#ifdef XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ if (mIsWalkabilityCoefficientsF32)
+ {
+ if (mWalkabilityCoefficientA != pA)
+ {
+ itemData[LINKSET_WALKABILITY_A_FIELD] = llclamp(static_cast<F32>(pA) / 100.0f, 0.0f, 1.0f);
+ }
+ if (mWalkabilityCoefficientB != pB)
+ {
+ itemData[LINKSET_WALKABILITY_B_FIELD] = llclamp(static_cast<F32>(pB) / 100.0f, 0.0f, 1.0f);
+ }
+ if (mWalkabilityCoefficientC != pC)
+ {
+ itemData[LINKSET_WALKABILITY_C_FIELD] = llclamp(static_cast<F32>(pC) / 100.0f, 0.0f, 1.0f);
+ }
+ if (mWalkabilityCoefficientD != pD)
+ {
+ itemData[LINKSET_WALKABILITY_D_FIELD] = llclamp(static_cast<F32>(pD) / 100.0f, 0.0f, 1.0f);
+ }
+ }
+ else
+ {
+ if (mWalkabilityCoefficientA != pA)
+ {
+ itemData[LINKSET_WALKABILITY_A_FIELD] = llclamp(pA, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+ }
+ if (mWalkabilityCoefficientB != pB)
+ {
+ itemData[LINKSET_WALKABILITY_B_FIELD] = llclamp(pB, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+ }
+ if (mWalkabilityCoefficientC != pC)
+ {
+ itemData[LINKSET_WALKABILITY_C_FIELD] = llclamp(pC, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+ }
+ if (mWalkabilityCoefficientD != pD)
+ {
+ itemData[LINKSET_WALKABILITY_D_FIELD] = llclamp(pD, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+ }
+ }
+#else // XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ if (mWalkabilityCoefficientA != pA)
+ {
+ itemData[LINKSET_WALKABILITY_A_FIELD] = llclamp(pA, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+ }
+ if (mWalkabilityCoefficientB != pB)
+ {
+ itemData[LINKSET_WALKABILITY_B_FIELD] = llclamp(pB, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+ }
+ if (mWalkabilityCoefficientC != pC)
+ {
+ itemData[LINKSET_WALKABILITY_C_FIELD] = llclamp(pC, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+ }
+ if (mWalkabilityCoefficientD != pD)
+ {
+ itemData[LINKSET_WALKABILITY_D_FIELD] = llclamp(pD, MIN_WALKABILITY_VALUE, MAX_WALKABILITY_VALUE);
+ }
+#endif // XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ if (mIsPhantom != pIsPhantom)
+ {
+ itemData[LINKSET_PHANTOM_FIELD] = static_cast<bool>(pIsPhantom);
+ }
+
+ return itemData;
+}
diff --git a/indra/newview/llpathfindinglinkset.h b/indra/newview/llpathfindinglinkset.h
new file mode 100644
index 0000000000..d4e58874eb
--- /dev/null
+++ b/indra/newview/llpathfindinglinkset.h
@@ -0,0 +1,108 @@
+/**
+ * @file llpathfindinglinkset.h
+ * @author William Todd Stinson
+ * @brief Definition of a pathfinding linkset that contains various properties required for havok pathfinding.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPATHFINDINGLINKSET_H
+#define LL_LLPATHFINDINGLINKSET_H
+
+#include "v3math.h"
+#include "lluuid.h"
+
+// This is a reminder to remove the code regarding the changing of the data type for the
+// walkability coefficients from F32 to S32 representing the percentage from 0-100.
+#define XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+
+class LLSD;
+
+class LLPathfindingLinkset
+{
+public:
+ typedef enum
+ {
+ kWalkable,
+ kObstacle,
+ kIgnored
+ } EPathState;
+
+ LLPathfindingLinkset(const std::string &pUUID, const LLSD &pLinksetItem);
+ LLPathfindingLinkset(const LLPathfindingLinkset& pOther);
+ virtual ~LLPathfindingLinkset();
+
+ LLPathfindingLinkset& operator = (const LLPathfindingLinkset& pOther);
+
+ inline const LLUUID& getUUID() const {return mUUID;};
+ inline const std::string& getName() const {return mName;};
+ inline const std::string& getDescription() const {return mDescription;};
+ inline U32 getLandImpact() const {return mLandImpact;};
+ inline const LLVector3& getLocation() const {return mLocation;};
+
+ inline EPathState getPathState() const {return mPathState;};
+ inline void setPathState(EPathState pPathState) {mPathState = pPathState;};
+
+ static EPathState getPathState(bool pIsPermanent, bool pIsWalkable);
+ static BOOL isPermanent(EPathState pPathState);
+ static BOOL isWalkable(EPathState pPathState);
+
+ inline BOOL isPhantom() const {return mIsPhantom;};
+ inline void setPhantom(BOOL pIsPhantom) {mIsPhantom = pIsPhantom;};
+
+ inline S32 getWalkabilityCoefficientA() const {return mWalkabilityCoefficientA;};
+ void setWalkabilityCoefficientA(S32 pA);
+
+ inline S32 getWalkabilityCoefficientB() const {return mWalkabilityCoefficientB;};
+ void setWalkabilityCoefficientB(S32 pB);
+
+ inline S32 getWalkabilityCoefficientC() const {return mWalkabilityCoefficientC;};
+ void setWalkabilityCoefficientC(S32 pC);
+
+ inline S32 getWalkabilityCoefficientD() const {return mWalkabilityCoefficientD;};
+ void setWalkabilityCoefficientD(S32 pD);
+
+ LLSD encodeAlteredFields(EPathState pPathState, S32 pA, S32 pB, S32 pC, S32 pD, BOOL pIsPhantom) const;
+
+protected:
+
+private:
+ static const S32 MIN_WALKABILITY_VALUE;
+ static const S32 MAX_WALKABILITY_VALUE;
+
+ LLUUID mUUID;
+ std::string mName;
+ std::string mDescription;
+ U32 mLandImpact;
+ LLVector3 mLocation;
+ EPathState mPathState;
+ BOOL mIsPhantom;
+#ifdef XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ BOOL mIsWalkabilityCoefficientsF32;
+#endif // XXX_STINSON_WALKABILITY_COEFFICIENTS_TYPE_CHANGE
+ S32 mWalkabilityCoefficientA;
+ S32 mWalkabilityCoefficientB;
+ S32 mWalkabilityCoefficientC;
+ S32 mWalkabilityCoefficientD;
+};
+
+#endif // LL_LLPATHFINDINGLINKSET_H
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 5d196a465f..746320f887 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -1,4795 +1,4797 @@
-/**
- * @file llspatialpartition.cpp
- * @brief LLSpatialGroup class implementation and supporting functions
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llspatialpartition.h"
-
-#include "llappviewer.h"
-#include "lltexturecache.h"
-#include "lltexturefetch.h"
-#include "llimageworker.h"
-#include "llviewerwindow.h"
-#include "llviewerobjectlist.h"
-#include "llvovolume.h"
-#include "llvolume.h"
-#include "llvolumeoctree.h"
-#include "llviewercamera.h"
-#include "llface.h"
-#include "llfloatertools.h"
-#include "llviewercontrol.h"
-#include "llviewerregion.h"
-#include "llcamera.h"
-#include "pipeline.h"
-#include "llmeshrepository.h"
-#include "llrender.h"
-#include "lloctree.h"
-#include "llphysicsshapebuilderutil.h"
-#include "llvoavatar.h"
-#include "llvolumemgr.h"
-#include "lltextureatlas.h"
-#include "llglslshader.h"
-#include "llviewershadermgr.h"
-
-static LLFastTimer::DeclareTimer FTM_FRUSTUM_CULL("Frustum Culling");
-static LLFastTimer::DeclareTimer FTM_CULL_REBOUND("Cull Rebound");
-
-const F32 SG_OCCLUSION_FUDGE = 0.25f;
-#define SG_DISCARD_TOLERANCE 0.01f
-
-#if LL_OCTREE_PARANOIA_CHECK
-#define assert_octree_valid(x) x->validate()
-#define assert_states_valid(x) ((LLSpatialGroup*) x->mSpatialPartition->mOctree->getListener(0))->checkStates()
-#else
-#define assert_octree_valid(x)
-#define assert_states_valid(x)
-#endif
-
-
-static U32 sZombieGroups = 0;
-U32 LLSpatialGroup::sNodeCount = 0;
-
-#define LL_TRACK_PENDING_OCCLUSION_QUERIES 0
-
-std::set<GLuint> LLSpatialGroup::sPendingQueries;
-
-U32 gOctreeMaxCapacity;
-
-BOOL LLSpatialGroup::sNoDelete = FALSE;
-
-static F32 sLastMaxTexPriority = 1.f;
-static F32 sCurMaxTexPriority = 1.f;
-
-class LLOcclusionQueryPool : public LLGLNamePool
-{
-protected:
- virtual GLuint allocateName()
- {
- GLuint name;
- glGenQueriesARB(1, &name);
- return name;
- }
-
- virtual void releaseName(GLuint name)
- {
-#if LL_TRACK_PENDING_OCCLUSION_QUERIES
- LLSpatialGroup::sPendingQueries.erase(name);
-#endif
- glDeleteQueriesARB(1, &name);
- }
-};
-
-static LLOcclusionQueryPool sQueryPool;
-
-//static counter for frame to switch LOD on
-
-void sg_assert(BOOL expr)
-{
-#if LL_OCTREE_PARANOIA_CHECK
- if (!expr)
- {
- llerrs << "Octree invalid!" << llendl;
- }
-#endif
-}
-
-S32 AABBSphereIntersect(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &rad)
-{
- return AABBSphereIntersectR2(min, max, origin, rad*rad);
-}
-
-S32 AABBSphereIntersectR2(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &r)
-{
- F32 d = 0.f;
- F32 t;
-
- if ((min-origin).magVecSquared() < r &&
- (max-origin).magVecSquared() < r)
- {
- return 2;
- }
-
- for (U32 i = 0; i < 3; i++)
- {
- if (origin.mV[i] < min.mV[i])
- {
- t = min.mV[i] - origin.mV[i];
- d += t*t;
- }
- else if (origin.mV[i] > max.mV[i])
- {
- t = origin.mV[i] - max.mV[i];
- d += t*t;
- }
-
- if (d > r)
- {
- return 0;
- }
- }
-
- return 1;
-}
-
-
-S32 AABBSphereIntersect(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &rad)
-{
- return AABBSphereIntersectR2(min, max, origin, rad*rad);
-}
-
-S32 AABBSphereIntersectR2(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &r)
-{
- F32 d = 0.f;
- F32 t;
-
- LLVector4a origina;
- origina.load3(origin.mV);
-
- LLVector4a v;
- v.setSub(min, origina);
-
- if (v.dot3(v) < r)
- {
- v.setSub(max, origina);
- if (v.dot3(v) < r)
- {
- return 2;
- }
- }
-
-
- for (U32 i = 0; i < 3; i++)
- {
- if (origin.mV[i] < min[i])
- {
- t = min[i] - origin.mV[i];
- d += t*t;
- }
- else if (origin.mV[i] > max[i])
- {
- t = origin.mV[i] - max[i];
- d += t*t;
- }
-
- if (d > r)
- {
- return 0;
- }
- }
-
- return 1;
-}
-
-
-typedef enum
-{
- b000 = 0x00,
- b001 = 0x01,
- b010 = 0x02,
- b011 = 0x03,
- b100 = 0x04,
- b101 = 0x05,
- b110 = 0x06,
- b111 = 0x07,
-} eLoveTheBits;
-
-//contact Runitai Linden for a copy of the SL object used to write this table
-//basically, you give the table a bitmask of the look-at vector to a node and it
-//gives you a triangle fan index array
-static U16 sOcclusionIndices[] =
-{
- //000
- b111, b110, b010, b011, b001, b101, b100, b110,
- //001
- b011, b010, b000, b001, b101, b111, b110, b010,
- //010
- b101, b100, b110, b111, b011, b001, b000, b100,
- //011
- b001, b000, b100, b101, b111, b011, b010, b000,
- //100
- b110, b000, b010, b011, b111, b101, b100, b000,
- //101
- b010, b100, b000, b001, b011, b111, b110, b100,
- //110
- b100, b010, b110, b111, b101, b001, b000, b010,
- //111
- b000, b110, b100, b101, b001, b011, b010, b110,
-};
-
-U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center)
-{
- LLVector4a origin;
- origin.load3(camera->getOrigin().mV);
-
- S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
-
- return cypher*8;
-}
-
-U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center)
-{
- LLVector4a origin;
- origin.load3(camera->getOrigin().mV);
-
- S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
-
- return (U8*) (sOcclusionIndices+cypher*8);
-}
-
-
-static LLFastTimer::DeclareTimer FTM_BUILD_OCCLUSION("Build Occlusion");
-
-void LLSpatialGroup::buildOcclusion()
-{
- //if (mOcclusionVerts.isNull())
- {
- mOcclusionVerts = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX,
- LLVertexBuffer::sUseStreamDraw ? mBufferUsage : 0); //if GL has a hard time with VBOs, don't use them for occlusion culling.
- mOcclusionVerts->allocateBuffer(8, 64, true);
-
- LLStrider<U16> idx;
- mOcclusionVerts->getIndexStrider(idx);
- for (U32 i = 0; i < 64; i++)
- {
- *idx++ = sOcclusionIndices[i];
- }
- }
-
- LLVector4a fudge;
- fudge.splat(SG_OCCLUSION_FUDGE);
-
- LLVector4a r;
- r.setAdd(mBounds[1], fudge);
-
- LLStrider<LLVector3> pos;
-
- {
- LLFastTimer t(FTM_BUILD_OCCLUSION);
- mOcclusionVerts->getVertexStrider(pos);
- }
-
- {
- LLVector4a* v = (LLVector4a*) pos.get();
-
- const LLVector4a& c = mBounds[0];
- const LLVector4a& s = r;
-
- static const LLVector4a octant[] =
- {
- LLVector4a(-1.f, -1.f, -1.f),
- LLVector4a(-1.f, -1.f, 1.f),
- LLVector4a(-1.f, 1.f, -1.f),
- LLVector4a(-1.f, 1.f, 1.f),
-
- LLVector4a(1.f, -1.f, -1.f),
- LLVector4a(1.f, -1.f, 1.f),
- LLVector4a(1.f, 1.f, -1.f),
- LLVector4a(1.f, 1.f, 1.f),
- };
-
- //vertex positions are encoded so the 3 bits of their vertex index
- //correspond to their axis facing, with bit position 3,2,1 matching
- //axis facing x,y,z, bit set meaning positive facing, bit clear
- //meaning negative facing
-
- for (S32 i = 0; i < 8; ++i)
- {
- LLVector4a p;
- p.setMul(s, octant[i]);
- p.add(c);
- v[i] = p;
- }
- }
-
- {
- mOcclusionVerts->flush();
- LLVertexBuffer::unbind();
- }
-
- clearState(LLSpatialGroup::OCCLUSION_DIRTY);
-}
-
-
-BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group);
-
-//returns:
-// 0 if sphere and AABB are not intersecting
-// 1 if they are
-// 2 if AABB is entirely inside sphere
-
-S32 LLSphereAABB(const LLVector3& center, const LLVector3& size, const LLVector3& pos, const F32 &rad)
-{
- S32 ret = 2;
-
- LLVector3 min = center - size;
- LLVector3 max = center + size;
- for (U32 i = 0; i < 3; i++)
- {
- if (min.mV[i] > pos.mV[i] + rad ||
- max.mV[i] < pos.mV[i] - rad)
- { //totally outside
- return 0;
- }
-
- if (min.mV[i] < pos.mV[i] - rad ||
- max.mV[i] > pos.mV[i] + rad)
- { //intersecting
- ret = 1;
- }
- }
-
- return ret;
-}
-
-LLSpatialGroup::~LLSpatialGroup()
-{
- /*if (sNoDelete)
- {
- llerrs << "Illegal deletion of LLSpatialGroup!" << llendl;
- }*/
-
- if (gDebugGL)
- {
- gPipeline.checkReferences(this);
- }
-
- if (isState(DEAD))
- {
- sZombieGroups--;
- }
-
- sNodeCount--;
-
- if (gGLManager.mHasOcclusionQuery)
- {
- for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; ++i)
- {
- if (mOcclusionQuery[i])
- {
- sQueryPool.release(mOcclusionQuery[i]);
- }
- }
- }
-
- mOcclusionVerts = NULL;
-
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- clearDrawMap();
- clearAtlasList() ;
-}
-
-BOOL LLSpatialGroup::hasAtlas(LLTextureAtlas* atlasp)
-{
- S8 type = atlasp->getComponents() - 1 ;
- for(std::list<LLTextureAtlas*>::iterator iter = mAtlasList[type].begin(); iter != mAtlasList[type].end() ; ++iter)
- {
- if(atlasp == *iter)
- {
- return TRUE ;
- }
- }
- return FALSE ;
-}
-
-void LLSpatialGroup::addAtlas(LLTextureAtlas* atlasp, S8 recursive_level)
-{
- if(!hasAtlas(atlasp))
- {
- mAtlasList[atlasp->getComponents() - 1].push_back(atlasp) ;
- atlasp->addSpatialGroup(this) ;
- }
-
- --recursive_level;
- if(recursive_level)//levels propagating up.
- {
- LLSpatialGroup* parent = getParent() ;
- if(parent)
- {
- parent->addAtlas(atlasp, recursive_level) ;
- }
- }
-}
-
-void LLSpatialGroup::removeAtlas(LLTextureAtlas* atlasp, BOOL remove_group, S8 recursive_level)
-{
- mAtlasList[atlasp->getComponents() - 1].remove(atlasp) ;
- if(remove_group)
- {
- atlasp->removeSpatialGroup(this) ;
- }
-
- --recursive_level;
- if(recursive_level)//levels propagating up.
- {
- LLSpatialGroup* parent = getParent() ;
- if(parent)
- {
- parent->removeAtlas(atlasp, recursive_level) ;
- }
- }
-}
-
-void LLSpatialGroup::clearAtlasList()
-{
- std::list<LLTextureAtlas*>::iterator iter ;
- for(S8 i = 0 ; i < 4 ; i++)
- {
- if(mAtlasList[i].size() > 0)
- {
- for(iter = mAtlasList[i].begin(); iter != mAtlasList[i].end() ; ++iter)
- {
- ((LLTextureAtlas*)*iter)->removeSpatialGroup(this) ;
- }
- mAtlasList[i].clear() ;
- }
- }
-}
-
-LLTextureAtlas* LLSpatialGroup::getAtlas(S8 ncomponents, S8 to_be_reserved, S8 recursive_level)
-{
- S8 type = ncomponents - 1 ;
- if(mAtlasList[type].size() > 0)
- {
- for(std::list<LLTextureAtlas*>::iterator iter = mAtlasList[type].begin(); iter != mAtlasList[type].end() ; ++iter)
- {
- if(!((LLTextureAtlas*)*iter)->isFull(to_be_reserved))
- {
- return *iter ;
- }
- }
- }
-
- --recursive_level;
- if(recursive_level)
- {
- LLSpatialGroup* parent = getParent() ;
- if(parent)
- {
- return parent->getAtlas(ncomponents, to_be_reserved, recursive_level) ;
- }
- }
- return NULL ;
-}
-
-void LLSpatialGroup::setCurUpdatingSlot(LLTextureAtlasSlot* slotp)
-{
- mCurUpdatingSlotp = slotp;
-
- //if(!hasAtlas(mCurUpdatingSlotp->getAtlas()))
- //{
- // addAtlas(mCurUpdatingSlotp->getAtlas()) ;
- //}
-}
-
-LLTextureAtlasSlot* LLSpatialGroup::getCurUpdatingSlot(LLViewerTexture* imagep, S8 recursive_level)
-{
- if(gFrameCount && mCurUpdatingTime == gFrameCount && mCurUpdatingTexture == imagep)
- {
- return mCurUpdatingSlotp ;
- }
-
- //--recursive_level ;
- //if(recursive_level)
- //{
- // LLSpatialGroup* parent = getParent() ;
- // if(parent)
- // {
- // return parent->getCurUpdatingSlot(imagep, recursive_level) ;
- // }
- //}
- return NULL ;
-}
-
-void LLSpatialGroup::clearDrawMap()
-{
- mDrawMap.clear();
-}
-
-BOOL LLSpatialGroup::isHUDGroup()
-{
- return mSpatialPartition && mSpatialPartition->isHUDPartition() ;
-}
-
-BOOL LLSpatialGroup::isRecentlyVisible() const
-{
- return (LLDrawable::getCurrentFrame() - mVisible[LLViewerCamera::sCurCameraID]) < LLDrawable::getMinVisFrameRange() ;
-}
-
-BOOL LLSpatialGroup::isVisible() const
-{
- return mVisible[LLViewerCamera::sCurCameraID] >= LLDrawable::getCurrentFrame() ? TRUE : FALSE;
-}
-
-void LLSpatialGroup::setVisible()
-{
- mVisible[LLViewerCamera::sCurCameraID] = LLDrawable::getCurrentFrame();
-}
-
-void LLSpatialGroup::validate()
-{
-#if LL_OCTREE_PARANOIA_CHECK
-
- sg_assert(!isState(DIRTY));
- sg_assert(!isDead());
-
- LLVector4a myMin;
- myMin.setSub(mBounds[0], mBounds[1]);
- LLVector4a myMax;
- myMax.setAdd(mBounds[0], mBounds[1]);
-
- validateDrawMap();
-
- for (element_iter i = getData().begin(); i != getData().end(); ++i)
- {
- LLDrawable* drawable = *i;
- sg_assert(drawable->getSpatialGroup() == this);
- if (drawable->getSpatialBridge())
- {
- sg_assert(drawable->getSpatialBridge() == mSpatialPartition->asBridge());
- }
-
- /*if (drawable->isSpatialBridge())
- {
- LLSpatialPartition* part = drawable->asPartition();
- if (!part)
- {
- llerrs << "Drawable reports it is a spatial bridge but not a partition." << llendl;
- }
- LLSpatialGroup* group = (LLSpatialGroup*) part->mOctree->getListener(0);
- group->validate();
- }*/
- }
-
- for (U32 i = 0; i < mOctreeNode->getChildCount(); ++i)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
-
- group->validate();
-
- //ensure all children are enclosed in this node
- LLVector4a center = group->mBounds[0];
- LLVector4a size = group->mBounds[1];
-
- LLVector4a min;
- min.setSub(center, size);
- LLVector4a max;
- max.setAdd(center, size);
-
- for (U32 j = 0; j < 3; j++)
- {
- sg_assert(min[j] >= myMin[j]-0.02f);
- sg_assert(max[j] <= myMax[j]+0.02f);
- }
- }
-
-#endif
-}
-
-void LLSpatialGroup::checkStates()
-{
-#if LL_OCTREE_PARANOIA_CHECK
- //LLOctreeStateCheck checker;
- //checker.traverse(mOctreeNode);
-#endif
-}
-
-void LLSpatialGroup::validateDrawMap()
-{
-#if LL_OCTREE_PARANOIA_CHECK
- for (draw_map_t::iterator i = mDrawMap.begin(); i != mDrawMap.end(); ++i)
- {
- LLSpatialGroup::drawmap_elem_t& draw_vec = i->second;
- for (drawmap_elem_t::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
- {
- LLDrawInfo& params = **j;
-
- params.validate();
- }
- }
-#endif
-}
-
-BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- drawablep->updateSpatialExtents();
-
- OctreeNode* parent = mOctreeNode->getOctParent();
-
- if (mOctreeNode->isInside(drawablep->getPositionGroup()) &&
- (mOctreeNode->contains(drawablep) ||
- (drawablep->getBinRadius() > mOctreeNode->getSize()[0] &&
- parent && parent->getElementCount() >= gOctreeMaxCapacity)))
- {
- unbound();
- setState(OBJECT_DIRTY);
- //setState(GEOM_DIRTY);
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-BOOL LLSpatialGroup::addObject(LLDrawable *drawablep, BOOL add_all, BOOL from_octree)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- if (!from_octree)
- {
- mOctreeNode->insert(drawablep);
- }
- else
- {
- drawablep->setSpatialGroup(this);
- setState(OBJECT_DIRTY | GEOM_DIRTY);
- setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS);
- gPipeline.markRebuild(this, TRUE);
- if (drawablep->isSpatialBridge())
- {
- mBridgeList.push_back((LLSpatialBridge*) drawablep);
- }
- if (drawablep->getRadius() > 1.f)
- {
- setState(IMAGE_DIRTY);
- }
- }
-
- return TRUE;
-}
-
-void LLSpatialGroup::rebuildGeom()
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- if (!isDead())
- {
- mSpatialPartition->rebuildGeom(this);
- }
-}
-
-void LLSpatialGroup::rebuildMesh()
-{
- if (!isDead())
- {
- mSpatialPartition->rebuildMesh(this);
- }
-}
-
-static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt");
-
-void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group)
-{
- if (group->isDead() || !group->isState(LLSpatialGroup::GEOM_DIRTY))
- {
- return;
- }
-
- if (group->changeLOD())
- {
- group->mLastUpdateDistance = group->mDistance;
- group->mLastUpdateViewAngle = group->mViewAngle;
- }
-
- LLFastTimer ftm(FTM_REBUILD_VBO);
-
- group->clearDrawMap();
-
- //get geometry count
- U32 index_count = 0;
- U32 vertex_count = 0;
-
- addGeometryCount(group, vertex_count, index_count);
-
- if (vertex_count > 0 && index_count > 0)
- { //create vertex buffer containing volume geometry for this node
- group->mBuilt = 1.f;
- if (group->mVertexBuffer.isNull() ||
- !group->mVertexBuffer->isWriteable() ||
- (group->mBufferUsage != group->mVertexBuffer->getUsage() && LLVertexBuffer::sEnableVBOs))
- {
- group->mVertexBuffer = createVertexBuffer(mVertexDataMask, group->mBufferUsage);
- group->mVertexBuffer->allocateBuffer(vertex_count, index_count, true);
- stop_glerror();
- }
- else
- {
- group->mVertexBuffer->resizeBuffer(vertex_count, index_count);
- stop_glerror();
- }
-
- getGeometry(group);
- }
- else
- {
- group->mVertexBuffer = NULL;
- group->mBufferMap.clear();
- }
-
- group->mLastUpdateTime = gFrameTimeSeconds;
- group->clearState(LLSpatialGroup::GEOM_DIRTY);
-}
-
-
-void LLSpatialPartition::rebuildMesh(LLSpatialGroup* group)
-{
-
-}
-
-BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector4a& minOut, LLVector4a& maxOut)
-{
- const OctreeNode* node = mOctreeNode;
-
- if (node->getData().empty())
- { //don't do anything if there are no objects
- if (empty && mOctreeNode->getParent())
- { //only root is allowed to be empty
- OCT_ERRS << "Empty leaf found in octree." << llendl;
- }
- return FALSE;
- }
-
- LLVector4a& newMin = mObjectExtents[0];
- LLVector4a& newMax = mObjectExtents[1];
-
- if (isState(OBJECT_DIRTY))
- { //calculate new bounding box
- clearState(OBJECT_DIRTY);
-
- //initialize bounding box to first element
- OctreeNode::const_element_iter i = node->getData().begin();
- LLDrawable* drawablep = *i;
- const LLVector4a* minMax = drawablep->getSpatialExtents();
-
- newMin = minMax[0];
- newMax = minMax[1];
-
- for (++i; i != node->getData().end(); ++i)
- {
- drawablep = *i;
- minMax = drawablep->getSpatialExtents();
-
- update_min_max(newMin, newMax, minMax[0]);
- update_min_max(newMin, newMax, minMax[1]);
-
- //bin up the object
- /*for (U32 i = 0; i < 3; i++)
- {
- if (minMax[0].mV[i] < newMin.mV[i])
- {
- newMin.mV[i] = minMax[0].mV[i];
- }
- if (minMax[1].mV[i] > newMax.mV[i])
- {
- newMax.mV[i] = minMax[1].mV[i];
- }
- }*/
- }
-
- mObjectBounds[0].setAdd(newMin, newMax);
- mObjectBounds[0].mul(0.5f);
- mObjectBounds[1].setSub(newMax, newMin);
- mObjectBounds[1].mul(0.5f);
- }
-
- if (empty)
- {
- minOut = newMin;
- maxOut = newMax;
- }
- else
- {
- minOut.setMin(minOut, newMin);
- maxOut.setMax(maxOut, newMax);
- }
-
- return TRUE;
-}
-
-void LLSpatialGroup::unbound()
-{
- if (isState(DIRTY))
- {
- return;
- }
-
- setState(DIRTY);
-
- //all the parent nodes need to rebound this child
- if (mOctreeNode)
- {
- OctreeNode* parent = (OctreeNode*) mOctreeNode->getParent();
- while (parent != NULL)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) parent->getListener(0);
- if (group->isState(DIRTY))
- {
- return;
- }
-
- group->setState(DIRTY);
- parent = (OctreeNode*) parent->getParent();
- }
- }
-}
-
-LLSpatialGroup* LLSpatialGroup::getParent()
-{
- if (isDead())
- {
- return NULL;
- }
-
- if(!mOctreeNode)
- {
- return NULL;
- }
- OctreeNode* parent = mOctreeNode->getOctParent();
-
- if (parent)
- {
- return (LLSpatialGroup*) parent->getListener(0);
- }
-
- return NULL;
-}
-
-BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- unbound();
- if (mOctreeNode && !from_octree)
- {
- if (!mOctreeNode->remove(drawablep))
- {
- OCT_ERRS << "Could not remove drawable from spatial group" << llendl;
- }
- }
- else
- {
- drawablep->setSpatialGroup(NULL);
- setState(GEOM_DIRTY);
- gPipeline.markRebuild(this, TRUE);
-
- if (drawablep->isSpatialBridge())
- {
- for (bridge_list_t::iterator i = mBridgeList.begin(); i != mBridgeList.end(); ++i)
- {
- if (*i == drawablep)
- {
- mBridgeList.erase(i);
- break;
- }
- }
- }
-
- if (getElementCount() == 0)
- { //delete draw map on last element removal since a rebuild might never happen
- clearDrawMap();
- }
- }
- return TRUE;
-}
-
-void LLSpatialGroup::shift(const LLVector4a &offset)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- LLVector4a t = mOctreeNode->getCenter();
- t.add(offset);
- mOctreeNode->setCenter(t);
- mOctreeNode->updateMinMax();
- mBounds[0].add(offset);
- mExtents[0].add(offset);
- mExtents[1].add(offset);
- mObjectBounds[0].add(offset);
- mObjectExtents[0].add(offset);
- mObjectExtents[1].add(offset);
-
- //if (!mSpatialPartition->mRenderByGroup)
- {
- setState(GEOM_DIRTY);
- gPipeline.markRebuild(this, TRUE);
- }
-
- if (mOcclusionVerts.notNull())
- {
- setState(OCCLUSION_DIRTY);
- }
-}
-
-class LLSpatialSetState : public LLSpatialGroup::OctreeTraveler
-{
-public:
- U32 mState;
- LLSpatialSetState(U32 state) : mState(state) { }
- virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setState(mState); }
-};
-
-class LLSpatialSetStateDiff : public LLSpatialSetState
-{
-public:
- LLSpatialSetStateDiff(U32 state) : LLSpatialSetState(state) { }
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* n)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
-
- if (!group->isState(mState))
- {
- LLSpatialGroup::OctreeTraveler::traverse(n);
- }
- }
-};
-
-void LLSpatialGroup::setState(U32 state)
-{
- mState |= state;
-
- llassert(state <= LLSpatialGroup::STATE_MASK);
-}
-
-void LLSpatialGroup::setState(U32 state, S32 mode)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- llassert(state <= LLSpatialGroup::STATE_MASK);
-
- if (mode > STATE_MODE_SINGLE)
- {
- if (mode == STATE_MODE_DIFF)
- {
- LLSpatialSetStateDiff setter(state);
- setter.traverse(mOctreeNode);
- }
- else
- {
- LLSpatialSetState setter(state);
- setter.traverse(mOctreeNode);
- }
- }
- else
- {
- mState |= state;
- }
-}
-
-class LLSpatialClearState : public LLSpatialGroup::OctreeTraveler
-{
-public:
- U32 mState;
- LLSpatialClearState(U32 state) : mState(state) { }
- virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearState(mState); }
-};
-
-class LLSpatialClearStateDiff : public LLSpatialClearState
-{
-public:
- LLSpatialClearStateDiff(U32 state) : LLSpatialClearState(state) { }
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* n)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
-
- if (group->isState(mState))
- {
- LLSpatialGroup::OctreeTraveler::traverse(n);
- }
- }
-};
-
-void LLSpatialGroup::clearState(U32 state)
-{
- llassert(state <= LLSpatialGroup::STATE_MASK);
-
- mState &= ~state;
-}
-
-void LLSpatialGroup::clearState(U32 state, S32 mode)
-{
- llassert(state <= LLSpatialGroup::STATE_MASK);
-
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- if (mode > STATE_MODE_SINGLE)
- {
- if (mode == STATE_MODE_DIFF)
- {
- LLSpatialClearStateDiff clearer(state);
- clearer.traverse(mOctreeNode);
- }
- else
- {
- LLSpatialClearState clearer(state);
- clearer.traverse(mOctreeNode);
- }
- }
- else
- {
- mState &= ~state;
- }
-}
-
-BOOL LLSpatialGroup::isState(U32 state) const
-{
- llassert(state <= LLSpatialGroup::STATE_MASK);
-
- return mState & state ? TRUE : FALSE;
-}
-
-//=====================================
-// Occlusion State Set/Clear
-//=====================================
-class LLSpatialSetOcclusionState : public LLSpatialGroup::OctreeTraveler
-{
-public:
- U32 mState;
- LLSpatialSetOcclusionState(U32 state) : mState(state) { }
- virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setOcclusionState(mState); }
-};
-
-class LLSpatialSetOcclusionStateDiff : public LLSpatialSetOcclusionState
-{
-public:
- LLSpatialSetOcclusionStateDiff(U32 state) : LLSpatialSetOcclusionState(state) { }
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* n)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
-
- if (!group->isOcclusionState(mState))
- {
- LLSpatialGroup::OctreeTraveler::traverse(n);
- }
- }
-};
-
-
-void LLSpatialGroup::setOcclusionState(U32 state, S32 mode)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- if (mode > STATE_MODE_SINGLE)
- {
- if (mode == STATE_MODE_DIFF)
- {
- LLSpatialSetOcclusionStateDiff setter(state);
- setter.traverse(mOctreeNode);
- }
- else if (mode == STATE_MODE_BRANCH)
- {
- LLSpatialSetOcclusionState setter(state);
- setter.traverse(mOctreeNode);
- }
- else
- {
- for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
- {
- mOcclusionState[i] |= state;
-
- if ((state & DISCARD_QUERY) && mOcclusionQuery[i])
- {
- sQueryPool.release(mOcclusionQuery[i]);
- mOcclusionQuery[i] = 0;
- }
- }
- }
- }
- else
- {
- mOcclusionState[LLViewerCamera::sCurCameraID] |= state;
- if ((state & DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
- {
- sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
- mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
- }
- }
-}
-
-class LLSpatialClearOcclusionState : public LLSpatialGroup::OctreeTraveler
-{
-public:
- U32 mState;
-
- LLSpatialClearOcclusionState(U32 state) : mState(state) { }
- virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearOcclusionState(mState); }
-};
-
-class LLSpatialClearOcclusionStateDiff : public LLSpatialClearOcclusionState
-{
-public:
- LLSpatialClearOcclusionStateDiff(U32 state) : LLSpatialClearOcclusionState(state) { }
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* n)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
-
- if (group->isOcclusionState(mState))
- {
- LLSpatialGroup::OctreeTraveler::traverse(n);
- }
- }
-};
-
-void LLSpatialGroup::clearOcclusionState(U32 state, S32 mode)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- if (mode > STATE_MODE_SINGLE)
- {
- if (mode == STATE_MODE_DIFF)
- {
- LLSpatialClearOcclusionStateDiff clearer(state);
- clearer.traverse(mOctreeNode);
- }
- else if (mode == STATE_MODE_BRANCH)
- {
- LLSpatialClearOcclusionState clearer(state);
- clearer.traverse(mOctreeNode);
- }
- else
- {
- for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
- {
- mOcclusionState[i] &= ~state;
- }
- }
- }
- else
- {
- mOcclusionState[LLViewerCamera::sCurCameraID] &= ~state;
- }
-}
-//======================================
-// Octree Listener Implementation
-//======================================
-
-LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) :
- mState(0),
- mGeometryBytes(0),
- mSurfaceArea(0.f),
- mBuilt(0.f),
- mOctreeNode(node),
- mSpatialPartition(part),
- mVertexBuffer(NULL),
- mBufferUsage(part->mBufferUsage),
- mDistance(0.f),
- mDepth(0.f),
- mLastUpdateDistance(-1.f),
- mLastUpdateTime(gFrameTimeSeconds),
- mAtlasList(4),
- mCurUpdatingTime(0),
- mCurUpdatingSlotp(NULL),
- mCurUpdatingTexture (NULL)
-{
- sNodeCount++;
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- mViewAngle.splat(0.f);
- mLastUpdateViewAngle.splat(-1.f);
- mExtents[0] = mExtents[1] = mObjectBounds[0] = mObjectBounds[0] = mObjectBounds[1] =
- mObjectExtents[0] = mObjectExtents[1] = mViewAngle;
-
- sg_assert(mOctreeNode->getListenerCount() == 0);
- mOctreeNode->addListener(this);
- setState(SG_INITIAL_STATE_MASK);
- gPipeline.markRebuild(this, TRUE);
-
- mBounds[0] = node->getCenter();
- mBounds[1] = node->getSize();
-
- part->mLODSeed = (part->mLODSeed+1)%part->mLODPeriod;
- mLODHash = part->mLODSeed;
-
- OctreeNode* oct_parent = node->getOctParent();
-
- LLSpatialGroup* parent = oct_parent ? (LLSpatialGroup*) oct_parent->getListener(0) : NULL;
-
- for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
- {
- mOcclusionQuery[i] = 0;
- mOcclusionIssued[i] = 0;
- mOcclusionState[i] = parent ? SG_STATE_INHERIT_MASK & parent->mOcclusionState[i] : 0;
- mVisible[i] = 0;
- }
-
- mOcclusionVerts = NULL;
-
- mRadius = 1;
- mPixelArea = 1024.f;
-}
-
-void LLSpatialGroup::updateDistance(LLCamera &camera)
-{
- if (LLViewerCamera::sCurCameraID != LLViewerCamera::CAMERA_WORLD)
- {
- llwarns << "Attempted to update distance for camera other than world camera!" << llendl;
- return;
- }
-
-#if !LL_RELEASE_FOR_DOWNLOAD
- if (isState(LLSpatialGroup::OBJECT_DIRTY))
- {
- llerrs << "Spatial group dirty on distance update." << llendl;
- }
-#endif
- if (!getData().empty())
- {
- mRadius = mSpatialPartition->mRenderByGroup ? mObjectBounds[1].getLength3().getF32() :
- (F32) mOctreeNode->getSize().getLength3().getF32();
- mDistance = mSpatialPartition->calcDistance(this, camera);
- mPixelArea = mSpatialPartition->calcPixelArea(this, camera);
- }
-}
-
-F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
-{
- LLVector4a eye;
- LLVector4a origin;
- origin.load3(camera.getOrigin().mV);
-
- eye.setSub(group->mObjectBounds[0], origin);
-
- F32 dist = 0.f;
-
- if (group->mDrawMap.find(LLRenderPass::PASS_ALPHA) != group->mDrawMap.end())
- {
- LLVector4a v = eye;
-
- dist = eye.getLength3().getF32();
- eye.normalize3fast();
-
- if (!group->isState(LLSpatialGroup::ALPHA_DIRTY))
- {
- if (!group->mSpatialPartition->isBridge())
- {
- LLVector4a view_angle = eye;
-
- LLVector4a diff;
- diff.setSub(view_angle, group->mLastUpdateViewAngle);
-
- if (diff.getLength3().getF32() > 0.64f)
- {
- group->mViewAngle = view_angle;
- group->mLastUpdateViewAngle = view_angle;
- //for occasional alpha sorting within the group
- //NOTE: If there is a trivial way to detect that alpha sorting here would not change the render order,
- //not setting this node to dirty would be a very good thing
- group->setState(LLSpatialGroup::ALPHA_DIRTY);
- gPipeline.markRebuild(group, FALSE);
- }
- }
- }
-
- //calculate depth of node for alpha sorting
-
- LLVector3 at = camera.getAtAxis();
-
- LLVector4a ata;
- ata.load3(at.mV);
-
- LLVector4a t = ata;
- //front of bounding box
- t.mul(0.25f);
- t.mul(group->mObjectBounds[1]);
- v.sub(t);
-
- group->mDepth = v.dot3(ata).getF32();
- }
- else
- {
- dist = eye.getLength3().getF32();
- }
-
- if (dist < 16.f)
- {
- dist /= 16.f;
- dist *= dist;
- dist *= 16.f;
- }
-
- return dist;
-}
-
-F32 LLSpatialPartition::calcPixelArea(LLSpatialGroup* group, LLCamera& camera)
-{
- return LLPipeline::calcPixelArea(group->mObjectBounds[0], group->mObjectBounds[1], camera);
-}
-
-F32 LLSpatialGroup::getUpdateUrgency() const
-{
- if (!isVisible())
- {
- return 0.f;
- }
- else
- {
- F32 time = gFrameTimeSeconds-mLastUpdateTime+4.f;
- return time + (mObjectBounds[1].dot3(mObjectBounds[1]).getF32()+1.f)/mDistance;
- }
-}
-
-BOOL LLSpatialGroup::needsUpdate()
-{
- return (LLDrawable::getCurrentFrame()%mSpatialPartition->mLODPeriod == mLODHash) ? TRUE : FALSE;
-}
-
-BOOL LLSpatialGroup::changeLOD()
-{
- if (isState(ALPHA_DIRTY | OBJECT_DIRTY))
- { ///a rebuild is going to happen, update distance and LoD
- return TRUE;
- }
-
- if (mSpatialPartition->mSlopRatio > 0.f)
- {
- F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius));
-
- if (fabsf(ratio) >= mSpatialPartition->mSlopRatio)
- {
- return TRUE;
- }
-
- if (mDistance > mRadius*2.f)
- {
- return FALSE;
- }
- }
-
- if (needsUpdate())
- {
- return TRUE;
- }
-
- return FALSE;
-}
-
-void LLSpatialGroup::handleInsertion(const TreeNode* node, LLDrawable* drawablep)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- addObject(drawablep, FALSE, TRUE);
- unbound();
- setState(OBJECT_DIRTY);
-}
-
-void LLSpatialGroup::handleRemoval(const TreeNode* node, LLDrawable* drawable)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- removeObject(drawable, TRUE);
- setState(OBJECT_DIRTY);
-}
-
-void LLSpatialGroup::handleDestruction(const TreeNode* node)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- setState(DEAD);
-
- for (element_iter i = getData().begin(); i != getData().end(); ++i)
- {
- LLDrawable* drawable = *i;
- if (drawable->getSpatialGroup() == this)
- {
- drawable->setSpatialGroup(NULL);
- }
- }
-
- //clean up avatar attachment stats
- LLSpatialBridge* bridge = mSpatialPartition->asBridge();
- if (bridge)
- {
- if (bridge->mAvatar.notNull())
- {
- bridge->mAvatar->mAttachmentGeometryBytes -= mGeometryBytes;
- bridge->mAvatar->mAttachmentSurfaceArea -= mSurfaceArea;
- }
- }
-
- clearDrawMap();
- mVertexBuffer = NULL;
- mBufferMap.clear();
- sZombieGroups++;
- mOctreeNode = NULL;
-}
-
-void LLSpatialGroup::handleStateChange(const TreeNode* node)
-{
- //drop bounding box upon state change
- if (mOctreeNode != node)
- {
- mOctreeNode = (OctreeNode*) node;
- }
- unbound();
-}
-
-void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- if (child->getListenerCount() == 0)
- {
- new LLSpatialGroup(child, mSpatialPartition);
- }
- else
- {
- OCT_ERRS << "LLSpatialGroup redundancy detected." << llendl;
- }
-
- unbound();
-
- assert_states_valid(this);
-}
-
-void LLSpatialGroup::handleChildRemoval(const OctreeNode* parent, const OctreeNode* child)
-{
- unbound();
-}
-
-void LLSpatialGroup::destroyGL()
-{
- setState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::IMAGE_DIRTY);
- gPipeline.markRebuild(this, TRUE);
-
- mLastUpdateTime = gFrameTimeSeconds;
- mVertexBuffer = NULL;
- mBufferMap.clear();
-
- clearDrawMap();
-
- for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
- {
- if (mOcclusionQuery[i])
- {
- sQueryPool.release(mOcclusionQuery[i]);
- mOcclusionQuery[i] = 0;
- }
- }
-
- mOcclusionVerts = NULL;
-
- for (LLSpatialGroup::element_iter i = getData().begin(); i != getData().end(); ++i)
- {
- LLDrawable* drawable = *i;
- for (S32 j = 0; j < drawable->getNumFaces(); j++)
- {
- LLFace* facep = drawable->getFace(j);
- facep->clearVertexBuffer();
- }
- }
-}
-
-BOOL LLSpatialGroup::rebound()
-{
- if (!isState(DIRTY))
- { //return TRUE if we're not empty
- return TRUE;
- }
-
- if (mOctreeNode->getChildCount() == 1 && mOctreeNode->getElementCount() == 0)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
- group->rebound();
-
- //copy single child's bounding box
- mBounds[0] = group->mBounds[0];
- mBounds[1] = group->mBounds[1];
- mExtents[0] = group->mExtents[0];
- mExtents[1] = group->mExtents[1];
-
- group->setState(SKIP_FRUSTUM_CHECK);
- }
- else if (mOctreeNode->isLeaf())
- { //copy object bounding box if this is a leaf
- boundObjects(TRUE, mExtents[0], mExtents[1]);
- mBounds[0] = mObjectBounds[0];
- mBounds[1] = mObjectBounds[1];
- }
- else
- {
- LLVector4a& newMin = mExtents[0];
- LLVector4a& newMax = mExtents[1];
- LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
- group->clearState(SKIP_FRUSTUM_CHECK);
- group->rebound();
- //initialize to first child
- newMin = group->mExtents[0];
- newMax = group->mExtents[1];
-
- //first, rebound children
- for (U32 i = 1; i < mOctreeNode->getChildCount(); i++)
- {
- group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
- group->clearState(SKIP_FRUSTUM_CHECK);
- group->rebound();
- const LLVector4a& max = group->mExtents[1];
- const LLVector4a& min = group->mExtents[0];
-
- newMax.setMax(newMax, max);
- newMin.setMin(newMin, min);
- }
-
- boundObjects(FALSE, newMin, newMax);
-
- mBounds[0].setAdd(newMin, newMax);
- mBounds[0].mul(0.5f);
- mBounds[1].setSub(newMax, newMin);
- mBounds[1].mul(0.5f);
- }
-
- setState(OCCLUSION_DIRTY);
-
- clearState(DIRTY);
-
- return TRUE;
-}
-
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_READBACK("Readback Occlusion");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_WAIT("Wait");
-
-void LLSpatialGroup::checkOcclusion()
-{
- if (LLPipeline::sUseOcclusion > 1)
- {
- LLFastTimer t(FTM_OCCLUSION_READBACK);
- LLSpatialGroup* parent = getParent();
- if (parent && parent->isOcclusionState(LLSpatialGroup::OCCLUDED))
- { //if the parent has been marked as occluded, the child is implicitly occluded
- clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
- }
- else if (isOcclusionState(QUERY_PENDING))
- { //otherwise, if a query is pending, read it back
-
- GLuint available = 0;
- if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
- {
- glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
-
- if (mOcclusionIssued[LLViewerCamera::sCurCameraID] < gFrameCount)
- { //query was issued last frame, wait until it's available
- S32 max_loop = 1024;
- LLFastTimer t(FTM_OCCLUSION_WAIT);
- while (!available && max_loop-- > 0)
- {
- F32 max_time = llmin(gFrameIntervalSeconds*10.f, 1.f);
- //do some usefu work while we wait
- LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread
- LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread
- LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread
-
- glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
- }
- }
- }
- else
- {
- available = 1;
- }
-
- if (available)
- { //result is available, read it back, otherwise wait until next frame
- GLuint res = 1;
- if (!isOcclusionState(DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
- {
- glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_ARB, &res);
-#if LL_TRACK_PENDING_OCCLUSION_QUERIES
- sPendingQueries.erase(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
-#endif
- }
- else if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
- { //delete the query to avoid holding onto hundreds of pending queries
- sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
- mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
- }
-
- if (isOcclusionState(DISCARD_QUERY))
- {
- res = 2;
- }
-
- if (res > 0)
- {
- assert_states_valid(this);
- clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
- assert_states_valid(this);
- }
- else
- {
- assert_states_valid(this);
- setOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
- assert_states_valid(this);
- }
-
- clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
- }
- }
- else if (mSpatialPartition->isOcclusionEnabled() && isOcclusionState(LLSpatialGroup::OCCLUDED))
- { //check occlusion has been issued for occluded node that has not had a query issued
- assert_states_valid(this);
- clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
- assert_states_valid(this);
- }
- }
-}
-
-static LLFastTimer::DeclareTimer FTM_PUSH_OCCLUSION_VERTS("Push Occlusion");
-static LLFastTimer::DeclareTimer FTM_SET_OCCLUSION_STATE("Occlusion State");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_EARLY_FAIL("Occlusion Early Fail");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_ALLOCATE("Allocate");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_BUILD("Build");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_BEGIN_QUERY("Begin Query");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_END_QUERY("End Query");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_SET_BUFFER("Set Buffer");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW_WATER("Draw Water");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW("Draw");
-
-
-
-void LLSpatialGroup::doOcclusion(LLCamera* camera)
-{
- if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1)
- {
- // Don't cull hole/edge water, unless we have the GL_ARB_depth_clamp extension
- if (earlyFail(camera, this))
- {
- LLFastTimer t(FTM_OCCLUSION_EARLY_FAIL);
- setOcclusionState(LLSpatialGroup::DISCARD_QUERY);
- assert_states_valid(this);
- clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
- assert_states_valid(this);
- }
- else
- {
- if (!isOcclusionState(QUERY_PENDING) || isOcclusionState(DISCARD_QUERY))
- {
- { //no query pending, or previous query to be discarded
- LLFastTimer t(FTM_RENDER_OCCLUSION);
-
- if (!mOcclusionQuery[LLViewerCamera::sCurCameraID])
- {
- LLFastTimer t(FTM_OCCLUSION_ALLOCATE);
- mOcclusionQuery[LLViewerCamera::sCurCameraID] = sQueryPool.allocate();
- }
-
- if (mOcclusionVerts.isNull() || isState(LLSpatialGroup::OCCLUSION_DIRTY))
- {
- LLFastTimer t(FTM_OCCLUSION_BUILD);
- buildOcclusion();
- }
-
- // Depth clamp all water to avoid it being culled as a result of being
- // behind the far clip plane, and in the case of edge water to avoid
- // it being culled while still visible.
- bool const use_depth_clamp = gGLManager.mHasDepthClamp &&
- (mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER ||
- mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER);
-
- LLGLEnable clamp(use_depth_clamp ? GL_DEPTH_CLAMP : 0);
-
-#if !LL_DARWIN
- U32 mode = gGLManager.mHasOcclusionQuery2 ? GL_ANY_SAMPLES_PASSED : GL_SAMPLES_PASSED_ARB;
-#else
- U32 mode = GL_SAMPLES_PASSED_ARB;
-#endif
-
-#if LL_TRACK_PENDING_OCCLUSION_QUERIES
- sPendingQueries.insert(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
-#endif
-
- {
- LLFastTimer t(FTM_PUSH_OCCLUSION_VERTS);
-
- //store which frame this query was issued on
- mOcclusionIssued[LLViewerCamera::sCurCameraID] = gFrameCount;
-
- {
- LLFastTimer t(FTM_OCCLUSION_BEGIN_QUERY);
- glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]);
- }
-
- {
- LLFastTimer t(FTM_OCCLUSION_SET_BUFFER);
- mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
- }
-
- if (!use_depth_clamp && mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER)
- {
- LLFastTimer t(FTM_OCCLUSION_DRAW_WATER);
-
- LLGLSquashToFarClip squash(glh_get_current_projection(), 1);
- if (camera->getOrigin().isExactlyZero())
- { //origin is invalid, draw entire box
- mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
- mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);
- }
- else
- {
- mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
- }
- }
- else
- {
- LLFastTimer t(FTM_OCCLUSION_DRAW);
- if (camera->getOrigin().isExactlyZero())
- { //origin is invalid, draw entire box
- mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
- mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);
- }
- else
- {
- mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
- }
- }
-
-
- {
- LLFastTimer t(FTM_OCCLUSION_END_QUERY);
- glEndQueryARB(mode);
- }
- }
- }
-
- {
- LLFastTimer t(FTM_SET_OCCLUSION_STATE);
- setOcclusionState(LLSpatialGroup::QUERY_PENDING);
- clearOcclusionState(LLSpatialGroup::DISCARD_QUERY);
- }
- }
- }
- }
-}
-
-//==============================================
-
-LLSpatialPartition::LLSpatialPartition(U32 data_mask, BOOL render_by_group, U32 buffer_usage)
-: mRenderByGroup(render_by_group), mBridge(NULL)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- mOcclusionEnabled = TRUE;
- mDrawableType = 0;
- mPartitionType = LLViewerRegion::PARTITION_NONE;
- mLODSeed = 0;
- mLODPeriod = 1;
- mVertexDataMask = data_mask;
- mBufferUsage = buffer_usage;
- mDepthMask = FALSE;
- mSlopRatio = 0.25f;
- mInfiniteFarClip = FALSE;
-
- LLVector4a center, size;
- center.splat(0.f);
- size.splat(1.f);
-
- mOctree = new LLSpatialGroup::OctreeRoot(center,size,
- NULL);
- new LLSpatialGroup(mOctree, this);
-}
-
-
-LLSpatialPartition::~LLSpatialPartition()
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- delete mOctree;
- mOctree = NULL;
-}
-
-
-LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- drawablep->updateSpatialExtents();
-
- //keep drawable from being garbage collected
- LLPointer<LLDrawable> ptr = drawablep;
-
- assert_octree_valid(mOctree);
- mOctree->insert(drawablep);
- assert_octree_valid(mOctree);
-
- LLSpatialGroup* group = drawablep->getSpatialGroup();
-
- if (group && was_visible && group->isOcclusionState(LLSpatialGroup::QUERY_PENDING))
- {
- group->setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS);
- }
-
- return group;
-}
-
-BOOL LLSpatialPartition::remove(LLDrawable *drawablep, LLSpatialGroup *curp)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- drawablep->setSpatialGroup(NULL);
-
- if (!curp->removeObject(drawablep))
- {
- OCT_ERRS << "Failed to remove drawable from octree!" << llendl;
- }
-
- assert_octree_valid(mOctree);
-
- return TRUE;
-}
-
-void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- // sanity check submitted by open source user bushing Spatula
- // who was seeing crashing here. (See VWR-424 reported by Bunny Mayne)
- if (!drawablep)
- {
- OCT_ERRS << "LLSpatialPartition::move was passed a bad drawable." << llendl;
- return;
- }
-
- BOOL was_visible = curp ? curp->isVisible() : FALSE;
-
- if (curp && curp->mSpatialPartition != this)
- {
- //keep drawable from being garbage collected
- LLPointer<LLDrawable> ptr = drawablep;
- if (curp->mSpatialPartition->remove(drawablep, curp))
- {
- put(drawablep, was_visible);
- return;
- }
- else
- {
- OCT_ERRS << "Drawable lost between spatial partitions on outbound transition." << llendl;
- }
- }
-
- if (curp && curp->updateInGroup(drawablep, immediate))
- {
- // Already updated, don't need to do anything
- assert_octree_valid(mOctree);
- return;
- }
-
- //keep drawable from being garbage collected
- LLPointer<LLDrawable> ptr = drawablep;
- if (curp && !remove(drawablep, curp))
- {
- OCT_ERRS << "Move couldn't find existing spatial group!" << llendl;
- }
-
- put(drawablep, was_visible);
-}
-
-class LLSpatialShift : public LLSpatialGroup::OctreeTraveler
-{
-public:
- const LLVector4a& mOffset;
-
- LLSpatialShift(const LLVector4a& offset) : mOffset(offset) { }
- virtual void visit(const LLSpatialGroup::OctreeNode* branch)
- {
- ((LLSpatialGroup*) branch->getListener(0))->shift(mOffset);
- }
-};
-
-void LLSpatialPartition::shift(const LLVector4a &offset)
-{ //shift octree node bounding boxes by offset
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- LLSpatialShift shifter(offset);
- shifter.traverse(mOctree);
-}
-
-class LLOctreeCull : public LLSpatialGroup::OctreeTraveler
-{
-public:
- LLOctreeCull(LLCamera* camera)
- : mCamera(camera), mRes(0) { }
-
- virtual bool earlyFail(LLSpatialGroup* group)
- {
- group->checkOcclusion();
-
- if (group->mOctreeNode->getParent() && //never occlusion cull the root node
- LLPipeline::sUseOcclusion && //ignore occlusion if disabled
- group->isOcclusionState(LLSpatialGroup::OCCLUDED))
- {
- gPipeline.markOccluder(group);
- return true;
- }
-
- return false;
- }
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* n)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
-
- if (earlyFail(group))
- {
- return;
- }
-
- if (mRes == 2 ||
- (mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)))
- { //fully in, just add everything
- LLSpatialGroup::OctreeTraveler::traverse(n);
- }
- else
- {
- mRes = frustumCheck(group);
-
- if (mRes)
- { //at least partially in, run on down
- LLSpatialGroup::OctreeTraveler::traverse(n);
- }
-
- mRes = 0;
- }
- }
-
- virtual S32 frustumCheck(const LLSpatialGroup* group)
- {
- S32 res = mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
- if (res != 0)
- {
- res = llmin(res, AABBSphereIntersect(group->mExtents[0], group->mExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist));
- }
- return res;
- }
-
- virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
- {
- S32 res = mCamera->AABBInFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
- if (res != 0)
- {
- res = llmin(res, AABBSphereIntersect(group->mObjectExtents[0], group->mObjectExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist));
- }
- return res;
- }
-
- virtual bool checkObjects(const LLSpatialGroup::OctreeNode* branch, const LLSpatialGroup* group)
- {
- if (branch->getElementCount() == 0) //no elements
- {
- return false;
- }
- else if (branch->getChildCount() == 0) //leaf state, already checked tightest bounding box
- {
- return true;
- }
- else if (mRes == 1 && !frustumCheckObjects(group)) //no objects in frustum
- {
- return false;
- }
-
- return true;
- }
-
- virtual void preprocess(LLSpatialGroup* group)
- {
-
- }
-
- virtual void processGroup(LLSpatialGroup* group)
- {
- if (group->needsUpdate() ||
- group->mVisible[LLViewerCamera::sCurCameraID] < LLDrawable::getCurrentFrame() - 1)
- {
- group->doOcclusion(mCamera);
- }
- gPipeline.markNotCulled(group, *mCamera);
- }
-
- virtual void visit(const LLSpatialGroup::OctreeNode* branch)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
-
- preprocess(group);
-
- if (checkObjects(branch, group))
- {
- processGroup(group);
- }
- }
-
- LLCamera *mCamera;
- S32 mRes;
-};
-
-class LLOctreeCullNoFarClip : public LLOctreeCull
-{
-public:
- LLOctreeCullNoFarClip(LLCamera* camera)
- : LLOctreeCull(camera) { }
-
- virtual S32 frustumCheck(const LLSpatialGroup* group)
- {
- return mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
- }
-
- virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
- {
- S32 res = mCamera->AABBInFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
- return res;
- }
-};
-
-class LLOctreeCullShadow : public LLOctreeCull
-{
-public:
- LLOctreeCullShadow(LLCamera* camera)
- : LLOctreeCull(camera) { }
-
- virtual S32 frustumCheck(const LLSpatialGroup* group)
- {
- return mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]);
- }
-
- virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
- {
- return mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]);
- }
-};
-
-class LLOctreeCullVisExtents: public LLOctreeCullShadow
-{
-public:
- LLOctreeCullVisExtents(LLCamera* camera, LLVector4a& min, LLVector4a& max)
- : LLOctreeCullShadow(camera), mMin(min), mMax(max), mEmpty(TRUE) { }
-
- virtual bool earlyFail(LLSpatialGroup* group)
- {
- if (group->mOctreeNode->getParent() && //never occlusion cull the root node
- LLPipeline::sUseOcclusion && //ignore occlusion if disabled
- group->isOcclusionState(LLSpatialGroup::OCCLUDED))
- {
- return true;
- }
-
- return false;
- }
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* n)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
-
- if (earlyFail(group))
- {
- return;
- }
-
- if ((mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)) ||
- mRes == 2)
- { //don't need to do frustum check
- LLSpatialGroup::OctreeTraveler::traverse(n);
- }
- else
- {
- mRes = frustumCheck(group);
-
- if (mRes)
- { //at least partially in, run on down
- LLSpatialGroup::OctreeTraveler::traverse(n);
- }
-
- mRes = 0;
- }
- }
-
- virtual void processGroup(LLSpatialGroup* group)
- {
- llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->getData().empty())
-
- if (mRes < 2)
- {
- if (mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]) > 0)
- {
- mEmpty = FALSE;
- update_min_max(mMin, mMax, group->mObjectExtents[0]);
- update_min_max(mMin, mMax, group->mObjectExtents[1]);
- }
- }
- else
- {
- mEmpty = FALSE;
- update_min_max(mMin, mMax, group->mExtents[0]);
- update_min_max(mMin, mMax, group->mExtents[1]);
- }
- }
-
- BOOL mEmpty;
- LLVector4a& mMin;
- LLVector4a& mMax;
-};
-
-class LLOctreeCullDetectVisible: public LLOctreeCullShadow
-{
-public:
- LLOctreeCullDetectVisible(LLCamera* camera)
- : LLOctreeCullShadow(camera), mResult(FALSE) { }
-
- virtual bool earlyFail(LLSpatialGroup* group)
- {
- if (mResult || //already found a node, don't check any more
- (group->mOctreeNode->getParent() && //never occlusion cull the root node
- LLPipeline::sUseOcclusion && //ignore occlusion if disabled
- group->isOcclusionState(LLSpatialGroup::OCCLUDED)))
- {
- return true;
- }
-
- return false;
- }
-
- virtual void processGroup(LLSpatialGroup* group)
- {
- if (group->isVisible())
- {
- mResult = TRUE;
- }
- }
-
- BOOL mResult;
-};
-
-class LLOctreeSelect : public LLOctreeCull
-{
-public:
- LLOctreeSelect(LLCamera* camera, std::vector<LLDrawable*>* results)
- : LLOctreeCull(camera), mResults(results) { }
-
- virtual bool earlyFail(LLSpatialGroup* group) { return false; }
- virtual void preprocess(LLSpatialGroup* group) { }
-
- virtual void processGroup(LLSpatialGroup* group)
- {
- LLSpatialGroup::OctreeNode* branch = group->mOctreeNode;
-
- for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
- {
- LLDrawable* drawable = *i;
-
- if (!drawable->isDead())
- {
- if (drawable->isSpatialBridge())
- {
- drawable->setVisible(*mCamera, mResults, TRUE);
- }
- else
- {
- mResults->push_back(drawable);
- }
- }
- }
- }
-
- std::vector<LLDrawable*>* mResults;
-};
-
-void drawBox(const LLVector3& c, const LLVector3& r)
-{
- LLVertexBuffer::unbind();
-
- gGL.begin(LLRender::TRIANGLE_STRIP);
- //left front
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
- //right front
- gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
- //right back
- gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
- //left back
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
- //left front
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
- gGL.end();
-
- //bottom
- gGL.begin(LLRender::TRIANGLE_STRIP);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
- gGL.end();
-
- //top
- gGL.begin(LLRender::TRIANGLE_STRIP);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
- gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
- gGL.end();
-}
-
-void drawBox(const LLVector4a& c, const LLVector4a& r)
-{
- drawBox(reinterpret_cast<const LLVector3&>(c), reinterpret_cast<const LLVector3&>(r));
-}
-
-void drawBoxOutline(const LLVector3& pos, const LLVector3& size)
-{
- LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
- LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
- LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
- LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
-
- gGL.begin(LLRender::LINES);
-
- //top
- gGL.vertex3fv((pos+v1).mV);
- gGL.vertex3fv((pos+v2).mV);
- gGL.vertex3fv((pos+v2).mV);
- gGL.vertex3fv((pos+v3).mV);
- gGL.vertex3fv((pos+v3).mV);
- gGL.vertex3fv((pos+v4).mV);
- gGL.vertex3fv((pos+v4).mV);
- gGL.vertex3fv((pos+v1).mV);
-
- //bottom
- gGL.vertex3fv((pos-v1).mV);
- gGL.vertex3fv((pos-v2).mV);
- gGL.vertex3fv((pos-v2).mV);
- gGL.vertex3fv((pos-v3).mV);
- gGL.vertex3fv((pos-v3).mV);
- gGL.vertex3fv((pos-v4).mV);
- gGL.vertex3fv((pos-v4).mV);
- gGL.vertex3fv((pos-v1).mV);
-
- //right
- gGL.vertex3fv((pos+v1).mV);
- gGL.vertex3fv((pos-v3).mV);
-
- gGL.vertex3fv((pos+v4).mV);
- gGL.vertex3fv((pos-v2).mV);
-
- //left
- gGL.vertex3fv((pos+v2).mV);
- gGL.vertex3fv((pos-v4).mV);
-
- gGL.vertex3fv((pos+v3).mV);
- gGL.vertex3fv((pos-v1).mV);
-
- gGL.end();
-}
-
-void drawBoxOutline(const LLVector4a& pos, const LLVector4a& size)
-{
- drawBoxOutline(reinterpret_cast<const LLVector3&>(pos), reinterpret_cast<const LLVector3&>(size));
-}
-
-class LLOctreeDirty : public LLOctreeTraveler<LLDrawable>
-{
-public:
- virtual void visit(const LLOctreeNode<LLDrawable>* state)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) state->getListener(0);
- group->destroyGL();
-
- for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
- {
- LLDrawable* drawable = *i;
- if (drawable->getVObj().notNull() && !group->mSpatialPartition->mRenderByGroup)
- {
- gPipeline.markRebuild(drawable, LLDrawable::REBUILD_ALL, TRUE);
- }
- }
-
- for (LLSpatialGroup::bridge_list_t::iterator i = group->mBridgeList.begin(); i != group->mBridgeList.end(); ++i)
- {
- LLSpatialBridge* bridge = *i;
- traverse(bridge->mOctree);
- }
- }
-};
-
-void LLSpatialPartition::restoreGL()
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-}
-
-void LLSpatialPartition::resetVertexBuffers()
-{
- LLOctreeDirty dirty;
- dirty.traverse(mOctree);
-}
-
-BOOL LLSpatialPartition::isOcclusionEnabled()
-{
- return mOcclusionEnabled || LLPipeline::sUseOcclusion > 2;
-}
-
-BOOL LLSpatialPartition::getVisibleExtents(LLCamera& camera, LLVector3& visMin, LLVector3& visMax)
-{
- LLVector4a visMina, visMaxa;
- visMina.load3(visMin.mV);
- visMaxa.load3(visMax.mV);
-
- {
- LLFastTimer ftm(FTM_CULL_REBOUND);
- LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
- group->rebound();
- }
-
- LLOctreeCullVisExtents vis(&camera, visMina, visMaxa);
- vis.traverse(mOctree);
-
- visMin.set(visMina.getF32ptr());
- visMax.set(visMaxa.getF32ptr());
- return vis.mEmpty;
-}
-
-BOOL LLSpatialPartition::visibleObjectsInFrustum(LLCamera& camera)
-{
- LLOctreeCullDetectVisible vis(&camera);
- vis.traverse(mOctree);
- return vis.mResult;
-}
-
-S32 LLSpatialPartition::cull(LLCamera &camera, std::vector<LLDrawable *>* results, BOOL for_select)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-#if LL_OCTREE_PARANOIA_CHECK
- ((LLSpatialGroup*)mOctree->getListener(0))->checkStates();
-#endif
- {
- LLFastTimer ftm(FTM_CULL_REBOUND);
- LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
- group->rebound();
- }
-
-#if LL_OCTREE_PARANOIA_CHECK
- ((LLSpatialGroup*)mOctree->getListener(0))->validate();
-#endif
-
-
- if (for_select)
- {
- LLOctreeSelect selecter(&camera, results);
- selecter.traverse(mOctree);
- }
- else if (LLPipeline::sShadowRender)
- {
- LLFastTimer ftm(FTM_FRUSTUM_CULL);
- LLOctreeCullShadow culler(&camera);
- culler.traverse(mOctree);
- }
- else if (mInfiniteFarClip || !LLPipeline::sUseFarClip)
- {
- LLFastTimer ftm(FTM_FRUSTUM_CULL);
- LLOctreeCullNoFarClip culler(&camera);
- culler.traverse(mOctree);
- }
- else
- {
- LLFastTimer ftm(FTM_FRUSTUM_CULL);
- LLOctreeCull culler(&camera);
- culler.traverse(mOctree);
- }
-
- return 0;
-}
-
-BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group)
-{
- if (camera->getOrigin().isExactlyZero())
- {
- return FALSE;
- }
-
- const F32 vel = SG_OCCLUSION_FUDGE*2.f;
- LLVector4a fudge;
- fudge.splat(vel);
-
- const LLVector4a& c = group->mBounds[0];
- LLVector4a r;
- r.setAdd(group->mBounds[1], fudge);
-
- /*if (r.magVecSquared() > 1024.0*1024.0)
- {
- return TRUE;
- }*/
-
- LLVector4a e;
- e.load3(camera->getOrigin().mV);
-
- LLVector4a min;
- min.setSub(c,r);
- LLVector4a max;
- max.setAdd(c,r);
-
- S32 lt = e.lessThan(min).getGatheredBits() & 0x7;
- if (lt)
- {
- return FALSE;
- }
-
- S32 gt = e.greaterThan(max).getGatheredBits() & 0x7;
- if (gt)
- {
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-void pushVerts(LLDrawInfo* params, U32 mask)
-{
- LLRenderPass::applyModelMatrix(*params);
- params->mVertexBuffer->setBuffer(mask);
- params->mVertexBuffer->drawRange(params->mParticle ? LLRender::POINTS : LLRender::TRIANGLES,
- params->mStart, params->mEnd, params->mCount, params->mOffset);
-}
-
-void pushVerts(LLSpatialGroup* group, U32 mask)
-{
- LLDrawInfo* params = NULL;
-
- for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
- {
- for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
- {
- params = *j;
- pushVerts(params, mask);
- }
- }
-}
-
-void pushVerts(LLFace* face, U32 mask)
-{
- llassert(face->verify());
-
- LLVertexBuffer* buffer = face->getVertexBuffer();
-
- if (buffer && (face->getGeomCount() >= 3))
- {
- buffer->setBuffer(mask);
- U16 start = face->getGeomStart();
- U16 end = start + face->getGeomCount()-1;
- U32 count = face->getIndicesCount();
- U16 offset = face->getIndicesStart();
- buffer->drawRange(LLRender::TRIANGLES, start, end, count, offset);
- }
-}
-
-void pushVerts(LLDrawable* drawable, U32 mask)
-{
- for (S32 i = 0; i < drawable->getNumFaces(); ++i)
- {
- pushVerts(drawable->getFace(i), mask);
- }
-}
-
-void pushVerts(LLVolume* volume)
-{
- LLVertexBuffer::unbind();
- for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = volume->getVolumeFace(i);
- LLVertexBuffer::drawElements(LLRender::TRIANGLES, face.mPositions, NULL, face.mNumIndices, face.mIndices);
- }
-}
-
-void pushBufferVerts(LLVertexBuffer* buffer, U32 mask)
-{
- if (buffer)
- {
- buffer->setBuffer(mask);
- buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
- }
-}
-
-void pushBufferVerts(LLSpatialGroup* group, U32 mask)
-{
- if (group->mSpatialPartition->mRenderByGroup)
- {
- if (!group->mDrawMap.empty())
- {
- LLDrawInfo* params = *(group->mDrawMap.begin()->second.begin());
- LLRenderPass::applyModelMatrix(*params);
-
- pushBufferVerts(group->mVertexBuffer, mask);
-
- for (LLSpatialGroup::buffer_map_t::iterator i = group->mBufferMap.begin(); i != group->mBufferMap.end(); ++i)
- {
- for (LLSpatialGroup::buffer_texture_map_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
- {
- for (LLSpatialGroup::buffer_list_t::iterator k = j->second.begin(); k != j->second.end(); ++k)
- {
- pushBufferVerts(*k, mask);
- }
- }
- }
- }
- }
- else
- {
- drawBox(group->mBounds[0], group->mBounds[1]);
- }
-}
-
-void pushVertsColorCoded(LLSpatialGroup* group, U32 mask)
-{
- LLDrawInfo* params = NULL;
-
- LLColor4 colors[] = {
- LLColor4::green,
- LLColor4::green1,
- LLColor4::green2,
- LLColor4::green3,
- LLColor4::green4,
- LLColor4::green5,
- LLColor4::green6
- };
-
- static const U32 col_count = LL_ARRAY_SIZE(colors);
-
- U32 col = 0;
-
- for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
- {
- for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
- {
- params = *j;
- LLRenderPass::applyModelMatrix(*params);
- gGL.diffuseColor4f(colors[col].mV[0], colors[col].mV[1], colors[col].mV[2], 0.5f);
- params->mVertexBuffer->setBuffer(mask);
- params->mVertexBuffer->drawRange(params->mParticle ? LLRender::POINTS : LLRender::TRIANGLES,
- params->mStart, params->mEnd, params->mCount, params->mOffset);
- col = (col+1)%col_count;
- }
- }
-}
-
-void renderOctree(LLSpatialGroup* group)
-{
- //render solid object bounding box, color
- //coded by buffer usage and activity
- gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
- LLVector4 col;
- if (group->mBuilt > 0.f)
- {
- group->mBuilt -= 2.f * gFrameIntervalSeconds;
- if (group->mBufferUsage == GL_STATIC_DRAW_ARB)
- {
- col.setVec(1.0f, 0, 0, group->mBuilt*0.5f);
- }
- else
- {
- col.setVec(0.1f,0.1f,1,0.1f);
- //col.setVec(1.0f, 1.0f, 0, sinf(group->mBuilt*3.14159f)*0.5f);
- }
-
- if (group->mBufferUsage != GL_STATIC_DRAW_ARB)
- {
- LLGLDepthTest gl_depth(FALSE, FALSE);
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-
- gGL.diffuseColor4f(1,0,0,group->mBuilt);
- gGL.flush();
- glLineWidth(5.f);
- drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]);
- gGL.flush();
- glLineWidth(1.f);
- gGL.flush();
- for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
- {
- LLDrawable* drawable = *i;
- if (!group->mSpatialPartition->isBridge())
- {
- gGL.pushMatrix();
- LLVector3 trans = drawable->getRegion()->getOriginAgent();
- gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);
- }
-
- for (S32 j = 0; j < drawable->getNumFaces(); j++)
- {
- LLFace* face = drawable->getFace(j);
- if (face->getVertexBuffer())
- {
- if (gFrameTimeSeconds - face->mLastUpdateTime < 0.5f)
- {
- gGL.diffuseColor4f(0, 1, 0, group->mBuilt);
- }
- else if (gFrameTimeSeconds - face->mLastMoveTime < 0.5f)
- {
- gGL.diffuseColor4f(1, 0, 0, group->mBuilt);
- }
- else
- {
- continue;
- }
-
- face->getVertexBuffer()->setBuffer(LLVertexBuffer::MAP_VERTEX);
- //drawBox((face->mExtents[0] + face->mExtents[1])*0.5f,
- // (face->mExtents[1]-face->mExtents[0])*0.5f);
- face->getVertexBuffer()->draw(LLRender::TRIANGLES, face->getIndicesCount(), face->getIndicesStart());
- }
- }
-
- if (!group->mSpatialPartition->isBridge())
- {
- gGL.popMatrix();
- }
- }
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- gGL.diffuseColor4f(1,1,1,1);
- }
- }
- else
- {
- if (group->mBufferUsage == GL_STATIC_DRAW_ARB && !group->getData().empty()
- && group->mSpatialPartition->mRenderByGroup)
- {
- col.setVec(0.8f, 0.4f, 0.1f, 0.1f);
- }
- else
- {
- col.setVec(0.1f, 0.1f, 1.f, 0.1f);
- }
- }
-
- gGL.diffuseColor4fv(col.mV);
- LLVector4a fudge;
- fudge.splat(0.001f);
- LLVector4a size = group->mObjectBounds[1];
- size.mul(1.01f);
- size.add(fudge);
-
- //{
- // LLGLDepthTest depth(GL_TRUE, GL_FALSE);
- // drawBox(group->mObjectBounds[0], fudge);
- //}
-
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
-
- //if (group->mBuilt <= 0.f)
- {
- //draw opaque outline
- //gGL.diffuseColor4f(col.mV[0], col.mV[1], col.mV[2], 1.f);
- //drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]);
-
- gGL.diffuseColor4f(0,1,1,1);
- drawBoxOutline(group->mBounds[0],group->mBounds[1]);
-
- //draw bounding box for draw info
- /*if (group->mSpatialPartition->mRenderByGroup)
- {
- gGL.diffuseColor4f(1.0f, 0.75f, 0.25f, 0.6f);
- for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
- {
- for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
- {
- LLDrawInfo* draw_info = *j;
- LLVector4a center;
- center.setAdd(draw_info->mExtents[1], draw_info->mExtents[0]);
- center.mul(0.5f);
- LLVector4a size;
- size.setSub(draw_info->mExtents[1], draw_info->mExtents[0]);
- size.mul(0.5f);
- drawBoxOutline(center, size);
- }
- }
- }*/
- }
-
-// LLSpatialGroup::OctreeNode* node = group->mOctreeNode;
-// gGL.diffuseColor4f(0,1,0,1);
-// drawBoxOutline(LLVector3(node->getCenter()), LLVector3(node->getSize()));
-}
-
-void renderVisibility(LLSpatialGroup* group, LLCamera* camera)
-{
- LLGLEnable blend(GL_BLEND);
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
- LLGLEnable cull(GL_CULL_FACE);
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-
- BOOL render_objects = (!LLPipeline::sUseOcclusion || !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) && group->isVisible() &&
- !group->getData().empty();
-
- if (render_objects)
- {
- LLGLDepthTest depth_under(GL_TRUE, GL_FALSE, GL_GREATER);
- gGL.diffuseColor4f(0, 0.5f, 0, 0.5f);
- gGL.diffuseColor4f(0, 0.5f, 0, 0.5f);
- pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
- }
-
- {
- LLGLDepthTest depth_over(GL_TRUE, GL_FALSE, GL_LEQUAL);
-
- if (render_objects)
- {
- gGL.diffuseColor4f(0.f, 0.5f, 0.f,1.f);
- gGL.diffuseColor4f(0.f, 0.5f, 0.f, 1.f);
- pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
- }
-
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-
- if (render_objects)
- {
- gGL.diffuseColor4f(0.f, 0.75f, 0.f,0.5f);
- gGL.diffuseColor4f(0.f, 0.75f, 0.f, 0.5f);
- pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
- }
- /*else if (camera && group->mOcclusionVerts.notNull())
- {
- LLVertexBuffer::unbind();
- group->mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
-
- gGL.diffuseColor4f(1.0f, 0.f, 0.f, 0.5f);
- group->mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, group->mBounds[0]));
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-
- gGL.diffuseColor4f(1.0f, 1.f, 1.f, 1.0f);
- group->mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, group->mBounds[0]));
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- }*/
- }
-}
-
-void renderCrossHairs(LLVector3 position, F32 size, LLColor4 color)
-{
- gGL.diffuseColor4fv(color.mV);
- gGL.begin(LLRender::LINES);
- {
- gGL.vertex3fv((position - LLVector3(size, 0.f, 0.f)).mV);
- gGL.vertex3fv((position + LLVector3(size, 0.f, 0.f)).mV);
- gGL.vertex3fv((position - LLVector3(0.f, size, 0.f)).mV);
- gGL.vertex3fv((position + LLVector3(0.f, size, 0.f)).mV);
- gGL.vertex3fv((position - LLVector3(0.f, 0.f, size)).mV);
- gGL.vertex3fv((position + LLVector3(0.f, 0.f, size)).mV);
- }
- gGL.end();
-}
-
-void renderUpdateType(LLDrawable* drawablep)
-{
- LLViewerObject* vobj = drawablep->getVObj();
- if (!vobj || OUT_UNKNOWN == vobj->getLastUpdateType())
- {
- return;
- }
- LLGLEnable blend(GL_BLEND);
- switch (vobj->getLastUpdateType())
- {
- case OUT_FULL:
- gGL.diffuseColor4f(0,1,0,0.5f);
- break;
- case OUT_TERSE_IMPROVED:
- gGL.diffuseColor4f(0,1,1,0.5f);
- break;
- case OUT_FULL_COMPRESSED:
- if (vobj->getLastUpdateCached())
- {
- gGL.diffuseColor4f(1,0,0,0.5f);
- }
- else
- {
- gGL.diffuseColor4f(1,1,0,0.5f);
- }
- break;
- case OUT_FULL_CACHED:
- gGL.diffuseColor4f(0,0,1,0.5f);
- break;
- default:
- llwarns << "Unknown update_type " << vobj->getLastUpdateType() << llendl;
- break;
- };
- S32 num_faces = drawablep->getNumFaces();
- if (num_faces)
- {
- for (S32 i = 0; i < num_faces; ++i)
- {
- pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
- }
- }
-}
-
-void renderComplexityDisplay(LLDrawable* drawablep)
-{
- LLViewerObject* vobj = drawablep->getVObj();
- if (!vobj)
- {
- return;
- }
-
- LLVOVolume *voVol = dynamic_cast<LLVOVolume*>(vobj);
-
- if (!voVol)
- {
- return;
- }
-
- if (!voVol->isRoot())
- {
- return;
- }
-
- LLVOVolume::texture_cost_t textures;
- F32 cost = (F32) voVol->getRenderCost(textures);
-
- // add any child volumes
- LLViewerObject::const_child_list_t children = voVol->getChildren();
- for (LLViewerObject::const_child_list_t::const_iterator iter = children.begin(); iter != children.end(); ++iter)
- {
- const LLViewerObject *child = *iter;
- const LLVOVolume *child_volume = dynamic_cast<const LLVOVolume*>(child);
- if (child_volume)
- {
- cost += child_volume->getRenderCost(textures);
- }
- }
-
- // add texture cost
- for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
- {
- // add the cost of each individual texture in the linkset
- cost += iter->second;
- }
-
- F32 cost_max = (F32) LLVOVolume::getRenderComplexityMax();
-
-
-
- // allow user to set a static color scale
- if (gSavedSettings.getS32("RenderComplexityStaticMax") > 0)
- {
- cost_max = gSavedSettings.getS32("RenderComplexityStaticMax");
- }
-
- F32 cost_ratio = cost / cost_max;
-
- // cap cost ratio at 1.0f in case cost_max is at a low threshold
- cost_ratio = cost_ratio > 1.0f ? 1.0f : cost_ratio;
-
- LLGLEnable blend(GL_BLEND);
-
- LLColor4 color;
- const LLColor4 color_min = gSavedSettings.getColor4("RenderComplexityColorMin");
- const LLColor4 color_mid = gSavedSettings.getColor4("RenderComplexityColorMid");
- const LLColor4 color_max = gSavedSettings.getColor4("RenderComplexityColorMax");
-
- if (cost_ratio < 0.5f)
- {
- color = color_min * (1 - cost_ratio * 2) + color_mid * (cost_ratio * 2);
- }
- else
- {
- color = color_mid * (1 - (cost_ratio - 0.5) * 2) + color_max * ((cost_ratio - 0.5) * 2);
- }
-
- LLSD color_val = color.getValue();
-
- // don't highlight objects below the threshold
- if (cost > gSavedSettings.getS32("RenderComplexityThreshold"))
- {
- glColor4f(color[0],color[1],color[2],0.5f);
-
-
- S32 num_faces = drawablep->getNumFaces();
- if (num_faces)
- {
- for (S32 i = 0; i < num_faces; ++i)
- {
- pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
- }
- }
- LLViewerObject::const_child_list_t children = voVol->getChildren();
- for (LLViewerObject::const_child_list_t::const_iterator iter = children.begin(); iter != children.end(); ++iter)
- {
- const LLViewerObject *child = *iter;
- if (child)
- {
- num_faces = child->getNumFaces();
- if (num_faces)
- {
- for (S32 i = 0; i < num_faces; ++i)
- {
- pushVerts(child->mDrawable->getFace(i), LLVertexBuffer::MAP_VERTEX);
- }
- }
- }
- }
- }
-
- voVol->setDebugText(llformat("%4.0f", cost));
-}
-
-void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
-{
- if (set_color)
- {
- if (drawable->isSpatialBridge())
- {
- gGL.diffuseColor4f(1,0.5f,0,1);
- }
- else if (drawable->getVOVolume())
- {
- if (drawable->isRoot())
- {
- gGL.diffuseColor4f(1,1,0,1);
- }
- else
- {
- gGL.diffuseColor4f(0,1,0,1);
- }
- }
- else if (drawable->getVObj())
- {
- switch (drawable->getVObj()->getPCode())
- {
- case LLViewerObject::LL_VO_SURFACE_PATCH:
- gGL.diffuseColor4f(0,1,1,1);
- break;
- case LLViewerObject::LL_VO_CLOUDS:
- // no longer used
- break;
- case LLViewerObject::LL_VO_PART_GROUP:
- case LLViewerObject::LL_VO_HUD_PART_GROUP:
- gGL.diffuseColor4f(0,0,1,1);
- break;
- case LLViewerObject::LL_VO_VOID_WATER:
- case LLViewerObject::LL_VO_WATER:
- gGL.diffuseColor4f(0,0.5f,1,1);
- break;
- case LL_PCODE_LEGACY_TREE:
- gGL.diffuseColor4f(0,0.5f,0,1);
- break;
- default:
- gGL.diffuseColor4f(1,0,1,1);
- break;
- }
- }
- else
- {
- gGL.diffuseColor4f(1,0,0,1);
- }
- }
-
- const LLVector4a* ext;
- LLVector4a pos, size;
-
- //render face bounding boxes
- for (S32 i = 0; i < drawable->getNumFaces(); i++)
- {
- LLFace* facep = drawable->getFace(i);
-
- ext = facep->mExtents;
-
- pos.setAdd(ext[0], ext[1]);
- pos.mul(0.5f);
- size.setSub(ext[1], ext[0]);
- size.mul(0.5f);
-
- drawBoxOutline(pos,size);
- }
-
- //render drawable bounding box
- ext = drawable->getSpatialExtents();
-
- pos.setAdd(ext[0], ext[1]);
- pos.mul(0.5f);
- size.setSub(ext[1], ext[0]);
- size.mul(0.5f);
-
- LLViewerObject* vobj = drawable->getVObj();
- if (vobj && vobj->onActiveList())
- {
- gGL.flush();
- glLineWidth(llmax(4.f*sinf(gFrameTimeSeconds*2.f)+1.f, 1.f));
- //glLineWidth(4.f*(sinf(gFrameTimeSeconds*2.f)*0.25f+0.75f));
- stop_glerror();
- drawBoxOutline(pos,size);
- gGL.flush();
- glLineWidth(1.f);
- }
- else
- {
- drawBoxOutline(pos,size);
- }
-}
-
-void renderNormals(LLDrawable* drawablep)
-{
- LLVertexBuffer::unbind();
-
- LLVOVolume* vol = drawablep->getVOVolume();
- if (vol)
- {
- LLVolume* volume = vol->getVolume();
- gGL.pushMatrix();
- gGL.multMatrix((F32*) vol->getRelativeXform().mMatrix);
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- LLVector4a scale(gSavedSettings.getF32("RenderDebugNormalScale"));
-
- for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = volume->getVolumeFace(i);
-
- for (S32 j = 0; j < face.mNumVertices; ++j)
- {
- gGL.begin(LLRender::LINES);
- LLVector4a n,p;
-
- n.setMul(face.mNormals[j], scale);
- p.setAdd(face.mPositions[j], n);
-
- gGL.diffuseColor4f(1,1,1,1);
- gGL.vertex3fv(face.mPositions[j].getF32ptr());
- gGL.vertex3fv(p.getF32ptr());
-
- if (face.mBinormals)
- {
- n.setMul(face.mBinormals[j], scale);
- p.setAdd(face.mPositions[j], n);
-
- gGL.diffuseColor4f(0,1,1,1);
- gGL.vertex3fv(face.mPositions[j].getF32ptr());
- gGL.vertex3fv(p.getF32ptr());
- }
- gGL.end();
- }
- }
-
- gGL.popMatrix();
- }
-}
-
-S32 get_physics_detail(const LLVolumeParams& volume_params, const LLVector3& scale)
-{
- const S32 DEFAULT_DETAIL = 1;
- const F32 LARGE_THRESHOLD = 5.f;
- const F32 MEGA_THRESHOLD = 25.f;
-
- S32 detail = DEFAULT_DETAIL;
- F32 avg_scale = (scale[0]+scale[1]+scale[2])/3.f;
-
- if (avg_scale > LARGE_THRESHOLD)
- {
- detail += 1;
- if (avg_scale > MEGA_THRESHOLD)
- {
- detail += 1;
- }
- }
-
- return detail;
-}
-
-void renderMeshBaseHull(LLVOVolume* volume, U32 data_mask, LLColor4& color, LLColor4& line_color)
-{
- LLUUID mesh_id = volume->getVolume()->getParams().getSculptID();
- LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id);
-
- const LLVector3 center(0,0,0);
- const LLVector3 size(0.25f,0.25f,0.25f);
-
- if (decomp)
- {
- if (!decomp->mBaseHullMesh.empty())
- {
- gGL.diffuseColor4fv(color.mV);
- LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mBaseHullMesh.mPositions, decomp->mBaseHullMesh.mNormals);
- }
- else
- {
- gMeshRepo.buildPhysicsMesh(*decomp);
- gGL.diffuseColor4f(0,1,1,1);
- drawBoxOutline(center, size);
- }
-
- }
- else
- {
- gGL.diffuseColor3f(1,0,1);
- drawBoxOutline(center, size);
- }
-}
-
-void render_hull(LLModel::PhysicsMesh& mesh, const LLColor4& color, const LLColor4& line_color)
-{
- gGL.diffuseColor4fv(color.mV);
- LLVertexBuffer::drawArrays(LLRender::TRIANGLES, mesh.mPositions, mesh.mNormals);
- LLGLEnable offset(GL_POLYGON_OFFSET_LINE);
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- glPolygonOffset(3.f, 3.f);
- glLineWidth(3.f);
- gGL.diffuseColor4fv(line_color.mV);
- LLVertexBuffer::drawArrays(LLRender::TRIANGLES, mesh.mPositions, mesh.mNormals);
- glLineWidth(1.f);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-}
-
-void renderPhysicsShape(LLDrawable* drawable, LLVOVolume* volume)
-{
- U8 physics_type = volume->getPhysicsShapeType();
-
- if (physics_type == LLViewerObject::PHYSICS_SHAPE_NONE || volume->isFlexible())
- {
- return;
- }
-
- //not allowed to return at this point without rendering *something*
-
- F32 threshold = gSavedSettings.getF32("ObjectCostHighThreshold");
- F32 cost = volume->getObjectCost();
-
- LLColor4 low = gSavedSettings.getColor4("ObjectCostLowColor");
- LLColor4 mid = gSavedSettings.getColor4("ObjectCostMidColor");
- LLColor4 high = gSavedSettings.getColor4("ObjectCostHighColor");
-
- F32 normalizedCost = 1.f - exp( -(cost / threshold) );
-
- LLColor4 color;
- if ( normalizedCost <= 0.5f )
- {
- color = lerp( low, mid, 2.f * normalizedCost );
- }
- else
- {
- color = lerp( mid, high, 2.f * ( normalizedCost - 0.5f ) );
- }
-
- LLColor4 line_color = color*0.5f;
-
- U32 data_mask = LLVertexBuffer::MAP_VERTEX;
-
- LLVolumeParams volume_params = volume->getVolume()->getParams();
-
- LLPhysicsVolumeParams physics_params(volume_params,
- physics_type == LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
-
- LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification physics_spec;
- LLPhysicsShapeBuilderUtil::determinePhysicsShape(physics_params, volume->getScale(), physics_spec);
-
- U32 type = physics_spec.getType();
-
- LLVector3 center(0,0,0);
- LLVector3 size(0.25f,0.25f,0.25f);
-
- gGL.pushMatrix();
- gGL.multMatrix((F32*) volume->getRelativeXform().mMatrix);
-
- if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::USER_MESH)
- {
- LLUUID mesh_id = volume->getVolume()->getParams().getSculptID();
- LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id);
-
- if (decomp)
- { //render a physics based mesh
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- if (!decomp->mHull.empty())
- { //decomposition exists, use that
-
- if (decomp->mMesh.empty())
- {
- gMeshRepo.buildPhysicsMesh(*decomp);
- }
-
- for (U32 i = 0; i < decomp->mMesh.size(); ++i)
- {
- render_hull(decomp->mMesh[i], color, line_color);
- }
- }
- else if (!decomp->mPhysicsShapeMesh.empty())
- {
- //decomp has physics mesh, render that mesh
- gGL.diffuseColor4fv(color.mV);
- LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mPhysicsShapeMesh.mPositions, decomp->mPhysicsShapeMesh.mNormals);
-
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- gGL.diffuseColor4fv(line_color.mV);
- LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mPhysicsShapeMesh.mPositions, decomp->mPhysicsShapeMesh.mNormals);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- }
- else
- { //no mesh or decomposition, render base hull
- renderMeshBaseHull(volume, data_mask, color, line_color);
-
- if (decomp->mPhysicsShapeMesh.empty())
- {
- //attempt to fetch physics shape mesh if available
- gMeshRepo.fetchPhysicsShape(mesh_id);
- }
- }
- }
- else
- {
- gGL.diffuseColor3f(1,1,0);
- drawBoxOutline(center, size);
- }
- }
- else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::USER_CONVEX ||
- type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_CONVEX)
- {
- if (volume->isMesh())
- {
- renderMeshBaseHull(volume, data_mask, color, line_color);
- }
- else
- {
- LLVolumeParams volume_params = volume->getVolume()->getParams();
- S32 detail = get_physics_detail(volume_params, volume->getScale());
- LLVolume* phys_volume = LLPrimitive::sVolumeManager->refVolume(volume_params, detail);
-
- if (!phys_volume->mHullPoints)
- { //build convex hull
- std::vector<LLVector3> pos;
- std::vector<U16> index;
-
- S32 index_offset = 0;
-
- for (S32 i = 0; i < phys_volume->getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = phys_volume->getVolumeFace(i);
- if (index_offset + face.mNumVertices > 65535)
- {
- continue;
- }
-
- for (S32 j = 0; j < face.mNumVertices; ++j)
- {
- pos.push_back(LLVector3(face.mPositions[j].getF32ptr()));
- }
-
- for (S32 j = 0; j < face.mNumIndices; ++j)
- {
- index.push_back(face.mIndices[j]+index_offset);
- }
-
- index_offset += face.mNumVertices;
- }
-
- if (!pos.empty() && !index.empty())
- {
- LLCDMeshData mesh;
- mesh.mIndexBase = &index[0];
- mesh.mVertexBase = pos[0].mV;
- mesh.mNumVertices = pos.size();
- mesh.mVertexStrideBytes = 12;
- mesh.mIndexStrideBytes = 6;
- mesh.mIndexType = LLCDMeshData::INT_16;
-
- mesh.mNumTriangles = index.size()/3;
-
- LLCDMeshData res;
-
- LLConvexDecomposition::getInstance()->generateSingleHullMeshFromMesh( &mesh, &res );
-
- //copy res into phys_volume
- phys_volume->mHullPoints = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*res.mNumVertices);
- phys_volume->mNumHullPoints = res.mNumVertices;
-
- S32 idx_size = (res.mNumTriangles*3*2+0xF) & ~0xF;
- phys_volume->mHullIndices = (U16*) ll_aligned_malloc_16(idx_size);
- phys_volume->mNumHullIndices = res.mNumTriangles*3;
-
- const F32* v = res.mVertexBase;
-
- for (S32 i = 0; i < res.mNumVertices; ++i)
- {
- F32* p = (F32*) ((U8*)v+i*res.mVertexStrideBytes);
- phys_volume->mHullPoints[i].load3(p);
- }
-
- if (res.mIndexType == LLCDMeshData::INT_16)
- {
- for (S32 i = 0; i < res.mNumTriangles; ++i)
- {
- U16* idx = (U16*) (((U8*)res.mIndexBase)+i*res.mIndexStrideBytes);
-
- phys_volume->mHullIndices[i*3+0] = idx[0];
- phys_volume->mHullIndices[i*3+1] = idx[1];
- phys_volume->mHullIndices[i*3+2] = idx[2];
- }
- }
- else
- {
- for (S32 i = 0; i < res.mNumTriangles; ++i)
- {
- U32* idx = (U32*) (((U8*)res.mIndexBase)+i*res.mIndexStrideBytes);
-
- phys_volume->mHullIndices[i*3+0] = (U16) idx[0];
- phys_volume->mHullIndices[i*3+1] = (U16) idx[1];
- phys_volume->mHullIndices[i*3+2] = (U16) idx[2];
- }
- }
- }
- }
-
- if (phys_volume->mHullPoints)
- {
- //render hull
-
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-
- gGL.diffuseColor4fv(line_color.mV);
- LLVertexBuffer::unbind();
-
- llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShader != 0);
-
- LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
-
- gGL.diffuseColor4fv(color.mV);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
-
- }
- else
- {
- gGL.diffuseColor4f(1,0,1,1);
- drawBoxOutline(center, size);
- }
-
- LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
- }
- }
- else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::BOX)
- {
- LLVector3 center = physics_spec.getCenter();
- LLVector3 scale = physics_spec.getScale();
- LLVector3 vscale = volume->getScale()*2.f;
- scale.set(scale[0]/vscale[0], scale[1]/vscale[1], scale[2]/vscale[2]);
-
- gGL.diffuseColor4fv(color.mV);
- drawBox(center, scale);
- }
- else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::SPHERE)
- {
- /*LLVolumeParams volume_params;
- volume_params.setType( LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE );
- volume_params.setBeginAndEndS( 0.f, 1.f );
- volume_params.setBeginAndEndT( 0.f, 1.f );
- volume_params.setRatio ( 1, 1 );
- volume_params.setShear ( 0, 0 );
- LLVolume* sphere = LLPrimitive::sVolumeManager->refVolume(volume_params, 3);
-
- gGL.diffuseColor4fv(color.mV);
- pushVerts(sphere);
- LLPrimitive::sVolumeManager->unrefVolume(sphere);*/
- }
- else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::CYLINDER)
- {
- LLVolumeParams volume_params;
- volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
- volume_params.setBeginAndEndS( 0.f, 1.f );
- volume_params.setBeginAndEndT( 0.f, 1.f );
- volume_params.setRatio ( 1, 1 );
- volume_params.setShear ( 0, 0 );
- LLVolume* cylinder = LLPrimitive::sVolumeManager->refVolume(volume_params, 3);
-
- gGL.diffuseColor4fv(color.mV);
- pushVerts(cylinder);
- LLPrimitive::sVolumeManager->unrefVolume(cylinder);
- }
- else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_MESH)
- {
- LLVolumeParams volume_params = volume->getVolume()->getParams();
- S32 detail = get_physics_detail(volume_params, volume->getScale());
-
- LLVolume* phys_volume = LLPrimitive::sVolumeManager->refVolume(volume_params, detail);
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-
- gGL.diffuseColor4fv(line_color.mV);
- pushVerts(phys_volume);
-
- gGL.diffuseColor4fv(color.mV);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- pushVerts(phys_volume);
- LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
- }
- else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_CONVEX)
- {
- LLVolumeParams volume_params = volume->getVolume()->getParams();
- S32 detail = get_physics_detail(volume_params, volume->getScale());
-
- LLVolume* phys_volume = LLPrimitive::sVolumeManager->refVolume(volume_params, detail);
-
- if (phys_volume->mHullPoints && phys_volume->mHullIndices)
- {
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShader != 0);
- LLVertexBuffer::unbind();
- glVertexPointer(3, GL_FLOAT, 16, phys_volume->mHullPoints);
- gGL.diffuseColor4fv(line_color.mV);
- gGL.syncMatrices();
- glDrawElements(GL_TRIANGLES, phys_volume->mNumHullIndices, GL_UNSIGNED_SHORT, phys_volume->mHullIndices);
-
- gGL.diffuseColor4fv(color.mV);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- glDrawElements(GL_TRIANGLES, phys_volume->mNumHullIndices, GL_UNSIGNED_SHORT, phys_volume->mHullIndices);
- }
- else
- {
- gGL.diffuseColor3f(1,0,1);
- drawBoxOutline(center, size);
- gMeshRepo.buildHull(volume_params, detail);
- }
- LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
- }
- else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::SCULPT)
- {
- //TODO: implement sculpted prim physics display
- }
- else
- {
- llerrs << "Unhandled type" << llendl;
- }
-
- gGL.popMatrix();
-}
-
-void renderPhysicsShapes(LLSpatialGroup* group)
-{
- for (LLSpatialGroup::OctreeNode::const_element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
- {
- LLDrawable* drawable = *i;
- LLVOVolume* volume = drawable->getVOVolume();
- if (volume && !volume->isAttachment() && volume->getPhysicsShapeType() != LLViewerObject::PHYSICS_SHAPE_NONE )
- {
- if (!group->mSpatialPartition->isBridge())
- {
- gGL.pushMatrix();
- LLVector3 trans = drawable->getRegion()->getOriginAgent();
- gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);
- renderPhysicsShape(drawable, volume);
- gGL.popMatrix();
- }
- else
- {
- renderPhysicsShape(drawable, volume);
- }
- }
- else
- {
- LLViewerObject* object = drawable->getVObj();
- if (object && object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
- {
- //push face vertices for terrain
- for (S32 i = 0; i < drawable->getNumFaces(); ++i)
- {
- LLFace* face = drawable->getFace(i);
- LLVertexBuffer* buff = face->getVertexBuffer();
- if (buff)
- {
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-
- buff->setBuffer(LLVertexBuffer::MAP_VERTEX);
- gGL.diffuseColor3f(0.2f, 0.5f, 0.3f);
- buff->draw(LLRender::TRIANGLES, buff->getNumIndices(), 0);
-
- gGL.diffuseColor3f(0.2f, 1.f, 0.3f);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- buff->draw(LLRender::TRIANGLES, buff->getNumIndices(), 0);
- }
- }
- }
- }
- }
-}
-
-void renderTexturePriority(LLDrawable* drawable)
-{
- for (int face=0; face<drawable->getNumFaces(); ++face)
- {
- LLFace *facep = drawable->getFace(face);
-
- LLVector4 cold(0,0,0.25f);
- LLVector4 hot(1,0.25f,0.25f);
-
- LLVector4 boost_cold(0,0,0,0);
- LLVector4 boost_hot(0,1,0,1);
-
- LLGLDisable blend(GL_BLEND);
-
- //LLViewerTexture* imagep = facep->getTexture();
- //if (imagep)
- {
-
- //F32 vsize = imagep->mMaxVirtualSize;
- F32 vsize = facep->getPixelArea();
-
- if (vsize > sCurMaxTexPriority)
- {
- sCurMaxTexPriority = vsize;
- }
-
- F32 t = vsize/sLastMaxTexPriority;
-
- LLVector4 col = lerp(cold, hot, t);
- gGL.diffuseColor4fv(col.mV);
- }
- //else
- //{
- // gGL.diffuseColor4f(1,0,1,1);
- //}
-
- LLVector4a center;
- center.setAdd(facep->mExtents[1],facep->mExtents[0]);
- center.mul(0.5f);
- LLVector4a size;
- size.setSub(facep->mExtents[1],facep->mExtents[0]);
- size.mul(0.5f);
- size.add(LLVector4a(0.01f));
- drawBox(center, size);
-
- /*S32 boost = imagep->getBoostLevel();
- if (boost>LLViewerTexture::BOOST_NONE)
- {
- F32 t = (F32) boost / (F32) (LLViewerTexture::BOOST_MAX_LEVEL-1);
- LLVector4 col = lerp(boost_cold, boost_hot, t);
- LLGLEnable blend_on(GL_BLEND);
- gGL.blendFunc(GL_SRC_ALPHA, GL_ONE);
- gGL.diffuseColor4fv(col.mV);
- drawBox(center, size);
- gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }*/
- }
-}
-
-void renderPoints(LLDrawable* drawablep)
-{
- LLGLDepthTest depth(GL_FALSE, GL_FALSE);
- if (drawablep->getNumFaces())
- {
- gGL.begin(LLRender::POINTS);
- gGL.diffuseColor3f(1,1,1);
- for (S32 i = 0; i < drawablep->getNumFaces(); i++)
- {
- gGL.vertex3fv(drawablep->getFace(i)->mCenterLocal.mV);
- }
- gGL.end();
- }
-}
-
-void renderTextureAnim(LLDrawInfo* params)
-{
- if (!params->mTextureMatrix)
- {
- return;
- }
-
- LLGLEnable blend(GL_BLEND);
- gGL.diffuseColor4f(1,1,0,0.5f);
- pushVerts(params, LLVertexBuffer::MAP_VERTEX);
-}
-
-void renderBatchSize(LLDrawInfo* params)
-{
- LLGLEnable offset(GL_POLYGON_OFFSET_FILL);
- glPolygonOffset(-1.f, 1.f);
- gGL.diffuseColor4ubv((GLubyte*) &(params->mDebugColor));
- pushVerts(params, LLVertexBuffer::MAP_VERTEX);
-}
-
-void renderShadowFrusta(LLDrawInfo* params)
-{
- LLGLEnable blend(GL_BLEND);
- gGL.setSceneBlendType(LLRender::BT_ADD);
-
- LLVector4a center;
- center.setAdd(params->mExtents[1], params->mExtents[0]);
- center.mul(0.5f);
- LLVector4a size;
- size.setSub(params->mExtents[1],params->mExtents[0]);
- size.mul(0.5f);
-
- if (gPipeline.mShadowCamera[4].AABBInFrustum(center, size))
- {
- gGL.diffuseColor3f(1,0,0);
- pushVerts(params, LLVertexBuffer::MAP_VERTEX);
- }
- if (gPipeline.mShadowCamera[5].AABBInFrustum(center, size))
- {
- gGL.diffuseColor3f(0,1,0);
- pushVerts(params, LLVertexBuffer::MAP_VERTEX);
- }
- if (gPipeline.mShadowCamera[6].AABBInFrustum(center, size))
- {
- gGL.diffuseColor3f(0,0,1);
- pushVerts(params, LLVertexBuffer::MAP_VERTEX);
- }
- if (gPipeline.mShadowCamera[7].AABBInFrustum(center, size))
- {
- gGL.diffuseColor3f(1,0,1);
- pushVerts(params, LLVertexBuffer::MAP_VERTEX);
- }
-
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
-}
-
-
-void renderLights(LLDrawable* drawablep)
-{
- if (!drawablep->isLight())
- {
- return;
- }
-
- if (drawablep->getNumFaces())
- {
- LLGLEnable blend(GL_BLEND);
- gGL.diffuseColor4f(0,1,1,0.5f);
-
- for (S32 i = 0; i < drawablep->getNumFaces(); i++)
- {
- pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
- }
-
- const LLVector4a* ext = drawablep->getSpatialExtents();
-
- LLVector4a pos;
- pos.setAdd(ext[0], ext[1]);
- pos.mul(0.5f);
- LLVector4a size;
- size.setSub(ext[1], ext[0]);
- size.mul(0.5f);
-
- {
- LLGLDepthTest depth(GL_FALSE, GL_TRUE);
- gGL.diffuseColor4f(1,1,1,1);
- drawBoxOutline(pos, size);
- }
-
- gGL.diffuseColor4f(1,1,0,1);
- F32 rad = drawablep->getVOVolume()->getLightRadius();
- drawBoxOutline(pos, LLVector4a(rad));
- }
-}
-
-class LLRenderOctreeRaycast : public LLOctreeTriangleRayIntersect
-{
-public:
-
-
- LLRenderOctreeRaycast(const LLVector4a& start, const LLVector4a& dir, F32* closest_t)
- : LLOctreeTriangleRayIntersect(start, dir, NULL, closest_t, NULL, NULL, NULL, NULL)
- {
-
- }
-
- void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
- {
- LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) branch->getListener(0);
-
- LLVector3 center, size;
-
- if (branch->getData().empty())
- {
- gGL.diffuseColor3f(1.f,0.2f,0.f);
- center.set(branch->getCenter().getF32ptr());
- size.set(branch->getSize().getF32ptr());
- }
- else
- {
- gGL.diffuseColor3f(0.75f, 1.f, 0.f);
- center.set(vl->mBounds[0].getF32ptr());
- size.set(vl->mBounds[1].getF32ptr());
- }
-
- drawBoxOutline(center, size);
-
- for (U32 i = 0; i < 2; i++)
- {
- LLGLDepthTest depth(GL_TRUE, GL_FALSE, i == 1 ? GL_LEQUAL : GL_GREATER);
-
- if (i == 1)
- {
- gGL.diffuseColor4f(0,1,1,0.5f);
- }
- else
- {
- gGL.diffuseColor4f(0,0.5f,0.5f, 0.25f);
- drawBoxOutline(center, size);
- }
-
- if (i == 1)
- {
- gGL.flush();
- glLineWidth(3.f);
- }
-
- gGL.begin(LLRender::TRIANGLES);
- for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getData().begin();
- iter != branch->getData().end();
- ++iter)
- {
- const LLVolumeTriangle* tri = *iter;
-
- gGL.vertex3fv(tri->mV[0]->getF32ptr());
- gGL.vertex3fv(tri->mV[1]->getF32ptr());
- gGL.vertex3fv(tri->mV[2]->getF32ptr());
- }
- gGL.end();
-
- if (i == 1)
- {
- gGL.flush();
- glLineWidth(1.f);
- }
- }
- }
-};
-
-void renderRaycast(LLDrawable* drawablep)
-{
- if (drawablep->getNumFaces())
- {
- LLGLEnable blend(GL_BLEND);
- gGL.diffuseColor4f(0,1,1,0.5f);
-
- if (drawablep->getVOVolume())
- {
- //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- //pushVerts(drawablep->getFace(gDebugRaycastFaceHit), LLVertexBuffer::MAP_VERTEX);
- //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-
- LLVOVolume* vobj = drawablep->getVOVolume();
- LLVolume* volume = vobj->getVolume();
-
- bool transform = true;
- if (drawablep->isState(LLDrawable::RIGGED))
- {
- volume = vobj->getRiggedVolume();
- transform = false;
- }
-
- if (volume)
- {
- LLVector3 trans = drawablep->getRegion()->getOriginAgent();
-
- for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = volume->getVolumeFace(i);
-
- gGL.pushMatrix();
- gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);
- gGL.multMatrix((F32*) vobj->getRelativeXform().mMatrix);
-
- LLVector3 start, end;
- if (transform)
- {
- start = vobj->agentPositionToVolume(gDebugRaycastStart);
- end = vobj->agentPositionToVolume(gDebugRaycastEnd);
- }
- else
- {
- start = gDebugRaycastStart;
- end = gDebugRaycastEnd;
- }
-
- LLVector4a starta, enda;
- starta.load3(start.mV);
- enda.load3(end.mV);
- LLVector4a dir;
- dir.setSub(enda, starta);
-
- gGL.flush();
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-
- {
- //render face positions
- LLVertexBuffer::unbind();
- gGL.diffuseColor4f(0,1,1,0.5f);
- glVertexPointer(3, GL_FLOAT, sizeof(LLVector4a), face.mPositions);
- gGL.syncMatrices();
- glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices);
- }
-
- if (!volume->isUnique())
- {
- F32 t = 1.f;
-
- if (!face.mOctree)
- {
- ((LLVolumeFace*) &face)->createOctree();
- }
-
- LLRenderOctreeRaycast render(starta, dir, &t);
-
- render.traverse(face.mOctree);
- }
-
- gGL.popMatrix();
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- }
- }
- }
- else if (drawablep->isAvatar())
- {
- if (drawablep->getVObj() == gDebugRaycastObject)
- {
- LLGLDepthTest depth(GL_FALSE);
- LLVOAvatar* av = (LLVOAvatar*) drawablep->getVObj().get();
- av->renderCollisionVolumes();
- }
- }
-
- if (drawablep->getVObj() == gDebugRaycastObject)
- {
- // draw intersection point
- gGL.pushMatrix();
- gGL.loadMatrix(gGLModelView);
- LLVector3 translate = gDebugRaycastIntersection;
- gGL.translatef(translate.mV[0], translate.mV[1], translate.mV[2]);
- LLCoordFrame orient;
- orient.lookDir(gDebugRaycastNormal, gDebugRaycastBinormal);
- LLMatrix4 rotation;
- orient.getRotMatrixToParent(rotation);
- gGL.multMatrix((float*)rotation.mMatrix);
-
- gGL.diffuseColor4f(1,0,0,0.5f);
- drawBox(LLVector3(0, 0, 0), LLVector3(0.1f, 0.022f, 0.022f));
- gGL.diffuseColor4f(0,1,0,0.5f);
- drawBox(LLVector3(0, 0, 0), LLVector3(0.021f, 0.1f, 0.021f));
- gGL.diffuseColor4f(0,0,1,0.5f);
- drawBox(LLVector3(0, 0, 0), LLVector3(0.02f, 0.02f, 0.1f));
- gGL.popMatrix();
-
- // draw bounding box of prim
- const LLVector4a* ext = drawablep->getSpatialExtents();
-
- LLVector4a pos;
- pos.setAdd(ext[0], ext[1]);
- pos.mul(0.5f);
- LLVector4a size;
- size.setSub(ext[1], ext[0]);
- size.mul(0.5f);
-
- LLGLDepthTest depth(GL_FALSE, GL_TRUE);
- gGL.diffuseColor4f(0,0.5f,0.5f,1);
- drawBoxOutline(pos, size);
- }
- }
-}
-
-
-void renderAvatarCollisionVolumes(LLVOAvatar* avatar)
-{
- avatar->renderCollisionVolumes();
-}
-
-void renderAgentTarget(LLVOAvatar* avatar)
-{
- // render these for self only (why, i don't know)
- if (avatar->isSelf())
- {
- renderCrossHairs(avatar->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f));
- renderCrossHairs(avatar->mDrawable->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f));
- renderCrossHairs(avatar->mRoot.getWorldPosition(), 0.2f, LLColor4(1, 1, 1, 0.8f));
- renderCrossHairs(avatar->mPelvisp->getWorldPosition(), 0.2f, LLColor4(0, 0, 1, 0.8f));
- }
-}
-
-class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
-{
-public:
- LLCamera* mCamera;
- LLOctreeRenderNonOccluded(LLCamera* camera): mCamera(camera) {}
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* node)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
-
- if (!mCamera || mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]))
- {
- node->accept(this);
- stop_glerror();
-
- for (U32 i = 0; i < node->getChildCount(); i++)
- {
- traverse(node->getChild(i));
- stop_glerror();
- }
-
- //draw tight fit bounding boxes for spatial group
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
- {
- group->rebuildGeom();
- group->rebuildMesh();
-
- renderOctree(group);
- stop_glerror();
- }
-
- //render visibility wireframe
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
- {
- group->rebuildGeom();
- group->rebuildMesh();
-
- gGL.flush();
- gGL.pushMatrix();
- gGLLastMatrix = NULL;
- gGL.loadMatrix(gGLModelView);
- renderVisibility(group, mCamera);
- stop_glerror();
- gGLLastMatrix = NULL;
- gGL.popMatrix();
- gGL.diffuseColor4f(1,1,1,1);
- }
- }
- }
-
- virtual void visit(const LLSpatialGroup::OctreeNode* branch)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
-
- if (group->isState(LLSpatialGroup::GEOM_DIRTY) || (mCamera && !mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1])))
- {
- return;
- }
-
- LLVector4a nodeCenter = group->mBounds[0];
- LLVector4a octCenter = group->mOctreeNode->getCenter();
-
- group->rebuildGeom();
- group->rebuildMesh();
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
- {
- if (!group->getData().empty())
- {
- gGL.diffuseColor3f(0,0,1);
- drawBoxOutline(group->mObjectBounds[0],
- group->mObjectBounds[1]);
- }
- }
-
- for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
- {
- LLDrawable* drawable = *i;
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
- {
- renderBoundingBox(drawable);
- }
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_NORMALS))
- {
- renderNormals(drawable);
- }
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BUILD_QUEUE))
- {
- if (drawable->isState(LLDrawable::IN_REBUILD_Q2))
- {
- gGL.diffuseColor4f(0.6f, 0.6f, 0.1f, 1.f);
- const LLVector4a* ext = drawable->getSpatialExtents();
- LLVector4a center;
- center.setAdd(ext[0], ext[1]);
- center.mul(0.5f);
- LLVector4a size;
- size.setSub(ext[1], ext[0]);
- size.mul(0.5f);
- drawBoxOutline(center, size);
- }
- }
-
- if (drawable->getVOVolume() && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
- {
- renderTexturePriority(drawable);
- }
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_POINTS))
- {
- renderPoints(drawable);
- }
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LIGHTS))
- {
- renderLights(drawable);
- }
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
- {
- renderRaycast(drawable);
- }
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_UPDATE_TYPE))
- {
- renderUpdateType(drawable);
- }
- if(gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY))
- {
- renderComplexityDisplay(drawable);
- }
-
- LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(drawable->getVObj().get());
-
- if (avatar && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_AVATAR_VOLUME))
- {
- renderAvatarCollisionVolumes(avatar);
- }
-
- if (avatar && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_AGENT_TARGET))
- {
- renderAgentTarget(avatar);
- }
-
- if (gDebugGL)
- {
- for (U32 i = 0; i < drawable->getNumFaces(); ++i)
- {
- LLFace* facep = drawable->getFace(i);
- U8 index = facep->getTextureIndex();
- if (facep->mDrawInfo)
- {
- if (index < 255)
- {
- if (facep->mDrawInfo->mTextureList.size() <= index)
- {
- llerrs << "Face texture index out of bounds." << llendl;
- }
- else if (facep->mDrawInfo->mTextureList[index] != facep->getTexture())
- {
- llerrs << "Face texture index incorrect." << llendl;
- }
- }
- }
- }
- }
- }
-
- for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
- {
- LLSpatialGroup::drawmap_elem_t& draw_vec = i->second;
- for (LLSpatialGroup::drawmap_elem_t::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
- {
- LLDrawInfo* draw_info = *j;
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_ANIM))
- {
- renderTextureAnim(draw_info);
- }
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BATCH_SIZE))
- {
- renderBatchSize(draw_info);
- }
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA))
- {
- renderShadowFrusta(draw_info);
- }
- }
- }
- }
-};
-
-
-class LLOctreeRenderPhysicsShapes : public LLOctreeTraveler<LLDrawable>
-{
-public:
- LLCamera* mCamera;
- LLOctreeRenderPhysicsShapes(LLCamera* camera): mCamera(camera) {}
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* node)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
-
- if (!mCamera || mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]))
- {
- node->accept(this);
- stop_glerror();
-
- for (U32 i = 0; i < node->getChildCount(); i++)
- {
- traverse(node->getChild(i));
- stop_glerror();
- }
-
- group->rebuildGeom();
- group->rebuildMesh();
-
- renderPhysicsShapes(group);
- }
- }
-
- virtual void visit(const LLSpatialGroup::OctreeNode* branch)
- {
-
- }
-};
-
-class LLOctreePushBBoxVerts : public LLOctreeTraveler<LLDrawable>
-{
-public:
- LLCamera* mCamera;
- LLOctreePushBBoxVerts(LLCamera* camera): mCamera(camera) {}
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* node)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
-
- if (!mCamera || mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]))
- {
- node->accept(this);
-
- for (U32 i = 0; i < node->getChildCount(); i++)
- {
- traverse(node->getChild(i));
- }
- }
- }
-
- virtual void visit(const LLSpatialGroup::OctreeNode* branch)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
-
- if (group->isState(LLSpatialGroup::GEOM_DIRTY) || (mCamera && !mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1])))
- {
- return;
- }
-
- for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
- {
- LLDrawable* drawable = *i;
-
- renderBoundingBox(drawable, FALSE);
- }
- }
-};
-
-void LLSpatialPartition::renderIntersectingBBoxes(LLCamera* camera)
-{
- LLOctreePushBBoxVerts pusher(camera);
- pusher.traverse(mOctree);
-}
-
-class LLOctreeStateCheck : public LLOctreeTraveler<LLDrawable>
-{
-public:
- U32 mInheritedMask[LLViewerCamera::NUM_CAMERAS];
-
- LLOctreeStateCheck()
- {
- for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
- {
- mInheritedMask[i] = 0;
- }
- }
-
- virtual void traverse(const LLSpatialGroup::OctreeNode* node)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
-
- node->accept(this);
-
-
- U32 temp[LLViewerCamera::NUM_CAMERAS];
-
- for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
- {
- temp[i] = mInheritedMask[i];
- mInheritedMask[i] |= group->mOcclusionState[i] & LLSpatialGroup::OCCLUDED;
- }
-
- for (U32 i = 0; i < node->getChildCount(); i++)
- {
- traverse(node->getChild(i));
- }
-
- for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
- {
- mInheritedMask[i] = temp[i];
- }
- }
-
-
- virtual void visit(const LLOctreeNode<LLDrawable>* state)
- {
- LLSpatialGroup* group = (LLSpatialGroup*) state->getListener(0);
-
- for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
- {
- if (mInheritedMask[i] && !(group->mOcclusionState[i] & mInheritedMask[i]))
- {
- llerrs << "Spatial group failed inherited mask test." << llendl;
- }
- }
-
- if (group->isState(LLSpatialGroup::DIRTY))
- {
- assert_parent_state(group, LLSpatialGroup::DIRTY);
- }
- }
-
- void assert_parent_state(LLSpatialGroup* group, U32 state)
- {
- LLSpatialGroup* parent = group->getParent();
- while (parent)
- {
- if (!parent->isState(state))
- {
- llerrs << "Spatial group failed parent state check." << llendl;
- }
- parent = parent->getParent();
- }
- }
-};
-
-
-void LLSpatialPartition::renderPhysicsShapes()
-{
- LLSpatialBridge* bridge = asBridge();
- LLCamera* camera = LLViewerCamera::getInstance();
-
- if (bridge)
- {
- camera = NULL;
- }
-
- gGL.flush();
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- glLineWidth(3.f);
- LLOctreeRenderPhysicsShapes render_physics(camera);
- render_physics.traverse(mOctree);
- gGL.flush();
- glLineWidth(1.f);
-}
-
-void LLSpatialPartition::renderDebug()
-{
- if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE |
- LLPipeline::RENDER_DEBUG_OCCLUSION |
- LLPipeline::RENDER_DEBUG_LIGHTS |
- LLPipeline::RENDER_DEBUG_BATCH_SIZE |
- LLPipeline::RENDER_DEBUG_UPDATE_TYPE |
- LLPipeline::RENDER_DEBUG_BBOXES |
- LLPipeline::RENDER_DEBUG_NORMALS |
- LLPipeline::RENDER_DEBUG_POINTS |
- LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY |
- LLPipeline::RENDER_DEBUG_TEXTURE_ANIM |
- LLPipeline::RENDER_DEBUG_RAYCAST |
- LLPipeline::RENDER_DEBUG_AVATAR_VOLUME |
- LLPipeline::RENDER_DEBUG_AGENT_TARGET |
- //LLPipeline::RENDER_DEBUG_BUILD_QUEUE |
- LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA |
- LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY))
- {
- return;
- }
-
- if (LLGLSLShader::sNoFixedFunction)
- {
- gDebugProgram.bind();
- }
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
- {
- //sLastMaxTexPriority = lerp(sLastMaxTexPriority, sCurMaxTexPriority, gFrameIntervalSeconds);
- sLastMaxTexPriority = (F32) LLViewerCamera::getInstance()->getScreenPixelArea();
- sCurMaxTexPriority = 0.f;
- }
-
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- LLGLDisable cullface(GL_CULL_FACE);
- LLGLEnable blend(GL_BLEND);
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gPipeline.disableLights();
-
- LLSpatialBridge* bridge = asBridge();
- LLCamera* camera = LLViewerCamera::getInstance();
-
- if (bridge)
- {
- camera = NULL;
- }
-
- LLOctreeStateCheck checker;
- checker.traverse(mOctree);
-
- LLOctreeRenderNonOccluded render_debug(camera);
- render_debug.traverse(mOctree);
-
- if (LLGLSLShader::sNoFixedFunction)
- {
- gDebugProgram.unbind();
- }
-}
-
-void LLSpatialGroup::drawObjectBox(LLColor4 col)
-{
- gGL.diffuseColor4fv(col.mV);
- LLVector4a size;
- size = mObjectBounds[1];
- size.mul(1.01f);
- size.add(LLVector4a(0.001f));
- drawBox(mObjectBounds[0], size);
-}
-
-bool LLSpatialPartition::isHUDPartition()
-{
- return mPartitionType == LLViewerRegion::PARTITION_HUD ;
-}
-
-BOOL LLSpatialPartition::isVisible(const LLVector3& v)
-{
- if (!LLViewerCamera::getInstance()->sphereInFrustum(v, 4.0f))
- {
- return FALSE;
- }
-
- return TRUE;
-}
-
-class LLOctreeIntersect : public LLSpatialGroup::OctreeTraveler
-{
-public:
- LLVector3 mStart;
- LLVector3 mEnd;
- S32 *mFaceHit;
- LLVector3 *mIntersection;
- LLVector2 *mTexCoord;
- LLVector3 *mNormal;
- LLVector3 *mBinormal;
- LLDrawable* mHit;
- BOOL mPickTransparent;
-
- LLOctreeIntersect(LLVector3 start, LLVector3 end, BOOL pick_transparent,
- S32* face_hit, LLVector3* intersection, LLVector2* tex_coord, LLVector3* normal, LLVector3* binormal)
- : mStart(start),
- mEnd(end),
- mFaceHit(face_hit),
- mIntersection(intersection),
- mTexCoord(tex_coord),
- mNormal(normal),
- mBinormal(binormal),
- mHit(NULL),
- mPickTransparent(pick_transparent)
- {
- }
-
- virtual void visit(const LLSpatialGroup::OctreeNode* branch)
- {
- for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
- {
- check(*i);
- }
- }
-
- virtual LLDrawable* check(const LLSpatialGroup::OctreeNode* node)
- {
- node->accept(this);
-
- for (U32 i = 0; i < node->getChildCount(); i++)
- {
- const LLSpatialGroup::OctreeNode* child = node->getChild(i);
- LLVector3 res;
-
- LLSpatialGroup* group = (LLSpatialGroup*) child->getListener(0);
-
- LLVector4a size;
- LLVector4a center;
-
- size = group->mBounds[1];
- center = group->mBounds[0];
-
- LLVector3 local_start = mStart;
- LLVector3 local_end = mEnd;
-
- if (group->mSpatialPartition->isBridge())
- {
- LLMatrix4 local_matrix = group->mSpatialPartition->asBridge()->mDrawable->getRenderMatrix();
- local_matrix.invert();
-
- local_start = mStart * local_matrix;
- local_end = mEnd * local_matrix;
- }
-
- LLVector4a start, end;
- start.load3(local_start.mV);
- end.load3(local_end.mV);
-
- if (LLLineSegmentBoxIntersect(start, end, center, size))
- {
- check(child);
- }
- }
-
- return mHit;
- }
-
- virtual bool check(LLDrawable* drawable)
- {
- LLVector3 local_start = mStart;
- LLVector3 local_end = mEnd;
-
- if (!drawable || !gPipeline.hasRenderType(drawable->getRenderType()) || !drawable->isVisible())
- {
- return false;
- }
-
- if (drawable->isSpatialBridge())
- {
- LLSpatialPartition *part = drawable->asPartition();
- LLSpatialBridge* bridge = part->asBridge();
- if (bridge && gPipeline.hasRenderType(bridge->mDrawableType))
- {
- check(part->mOctree);
- }
- }
- else
- {
- LLViewerObject* vobj = drawable->getVObj();
-
- if (vobj)
- {
- LLVector3 intersection;
- bool skip_check = false;
- if (vobj->isAvatar())
- {
- LLVOAvatar* avatar = (LLVOAvatar*) vobj;
- if (avatar->isSelf() && LLFloater::isVisible(gFloaterTools))
- {
- LLViewerObject* hit = avatar->lineSegmentIntersectRiggedAttachments(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal);
- if (hit)
- {
- mEnd = intersection;
- if (mIntersection)
- {
- *mIntersection = intersection;
- }
-
- mHit = hit->mDrawable;
- skip_check = true;
- }
-
- }
- }
-
- if (!skip_check && vobj->lineSegmentIntersect(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal))
- {
- mEnd = intersection; // shorten ray so we only find CLOSER hits
- if (mIntersection)
- {
- *mIntersection = intersection;
- }
-
- mHit = vobj->mDrawable;
- }
- }
- }
-
- return false;
- }
-};
-
-LLDrawable* LLSpatialPartition::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
- BOOL pick_transparent,
- S32* face_hit, // return the face hit
- LLVector3* intersection, // return the intersection point
- LLVector2* tex_coord, // return the texture coordinates of the intersection point
- LLVector3* normal, // return the surface normal at the intersection point
- LLVector3* bi_normal // return the surface bi-normal at the intersection point
- )
-
-{
- LLOctreeIntersect intersect(start, end, pick_transparent, face_hit, intersection, tex_coord, normal, bi_normal);
- LLDrawable* drawable = intersect.check(mOctree);
-
- return drawable;
-}
-
-LLDrawInfo::LLDrawInfo(U16 start, U16 end, U32 count, U32 offset,
- LLViewerTexture* texture, LLVertexBuffer* buffer,
- BOOL fullbright, U8 bump, BOOL particle, F32 part_size)
-:
- mVertexBuffer(buffer),
- mTexture(texture),
- mTextureMatrix(NULL),
- mModelMatrix(NULL),
- mStart(start),
- mEnd(end),
- mCount(count),
- mOffset(offset),
- mFullbright(fullbright),
- mBump(bump),
- mParticle(particle),
- mPartSize(part_size),
- mVSize(0.f),
- mGroup(NULL),
- mFace(NULL),
- mDistance(0.f),
- mDrawMode(LLRender::TRIANGLES)
-{
- mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
-
- mDebugColor = (rand() << 16) + rand();
-}
-
-LLDrawInfo::~LLDrawInfo()
-{
- /*if (LLSpatialGroup::sNoDelete)
- {
- llerrs << "LLDrawInfo deleted illegally!" << llendl;
- }*/
-
- if (mFace)
- {
- mFace->setDrawInfo(NULL);
- }
-
- if (gDebugGL)
- {
- gPipeline.checkReferences(this);
- }
-}
-
-void LLDrawInfo::validate()
-{
- mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
-}
-
-LLVertexBuffer* LLGeometryManager::createVertexBuffer(U32 type_mask, U32 usage)
-{
- return new LLVertexBuffer(type_mask, usage);
-}
-
-LLCullResult::LLCullResult()
-{
- clear();
-}
-
-void LLCullResult::clear()
-{
- mVisibleGroupsSize = 0;
- mVisibleGroupsEnd = mVisibleGroups.begin();
-
- mAlphaGroupsSize = 0;
- mAlphaGroupsEnd = mAlphaGroups.begin();
-
- mOcclusionGroupsSize = 0;
- mOcclusionGroupsEnd = mOcclusionGroups.begin();
-
- mDrawableGroupsSize = 0;
- mDrawableGroupsEnd = mDrawableGroups.begin();
-
- mVisibleListSize = 0;
- mVisibleListEnd = mVisibleList.begin();
-
- mVisibleBridgeSize = 0;
- mVisibleBridgeEnd = mVisibleBridge.begin();
-
-
- for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; i++)
- {
- for (U32 j = 0; j < mRenderMapSize[i]; j++)
- {
- mRenderMap[i][j] = 0;
- }
- mRenderMapSize[i] = 0;
- mRenderMapEnd[i] = mRenderMap[i].begin();
- }
-}
-
-LLCullResult::sg_list_t::iterator LLCullResult::beginVisibleGroups()
-{
- return mVisibleGroups.begin();
-}
-
-LLCullResult::sg_list_t::iterator LLCullResult::endVisibleGroups()
-{
- return mVisibleGroupsEnd;
-}
-
-LLCullResult::sg_list_t::iterator LLCullResult::beginAlphaGroups()
-{
- return mAlphaGroups.begin();
-}
-
-LLCullResult::sg_list_t::iterator LLCullResult::endAlphaGroups()
-{
- return mAlphaGroupsEnd;
-}
-
-LLCullResult::sg_list_t::iterator LLCullResult::beginOcclusionGroups()
-{
- return mOcclusionGroups.begin();
-}
-
-LLCullResult::sg_list_t::iterator LLCullResult::endOcclusionGroups()
-{
- return mOcclusionGroupsEnd;
-}
-
-LLCullResult::sg_list_t::iterator LLCullResult::beginDrawableGroups()
-{
- return mDrawableGroups.begin();
-}
-
-LLCullResult::sg_list_t::iterator LLCullResult::endDrawableGroups()
-{
- return mDrawableGroupsEnd;
-}
-
-LLCullResult::drawable_list_t::iterator LLCullResult::beginVisibleList()
-{
- return mVisibleList.begin();
-}
-
-LLCullResult::drawable_list_t::iterator LLCullResult::endVisibleList()
-{
- return mVisibleListEnd;
-}
-
-LLCullResult::bridge_list_t::iterator LLCullResult::beginVisibleBridge()
-{
- return mVisibleBridge.begin();
-}
-
-LLCullResult::bridge_list_t::iterator LLCullResult::endVisibleBridge()
-{
- return mVisibleBridgeEnd;
-}
-
-LLCullResult::drawinfo_list_t::iterator LLCullResult::beginRenderMap(U32 type)
-{
- return mRenderMap[type].begin();
-}
-
-LLCullResult::drawinfo_list_t::iterator LLCullResult::endRenderMap(U32 type)
-{
- return mRenderMapEnd[type];
-}
-
-void LLCullResult::pushVisibleGroup(LLSpatialGroup* group)
-{
- if (mVisibleGroupsSize < mVisibleGroups.size())
- {
- mVisibleGroups[mVisibleGroupsSize] = group;
- }
- else
- {
- mVisibleGroups.push_back(group);
- }
- ++mVisibleGroupsSize;
- mVisibleGroupsEnd = mVisibleGroups.begin()+mVisibleGroupsSize;
-}
-
-void LLCullResult::pushAlphaGroup(LLSpatialGroup* group)
-{
- if (mAlphaGroupsSize < mAlphaGroups.size())
- {
- mAlphaGroups[mAlphaGroupsSize] = group;
- }
- else
- {
- mAlphaGroups.push_back(group);
- }
- ++mAlphaGroupsSize;
- mAlphaGroupsEnd = mAlphaGroups.begin()+mAlphaGroupsSize;
-}
-
-void LLCullResult::pushOcclusionGroup(LLSpatialGroup* group)
-{
- if (mOcclusionGroupsSize < mOcclusionGroups.size())
- {
- mOcclusionGroups[mOcclusionGroupsSize] = group;
- }
- else
- {
- mOcclusionGroups.push_back(group);
- }
- ++mOcclusionGroupsSize;
- mOcclusionGroupsEnd = mOcclusionGroups.begin()+mOcclusionGroupsSize;
-}
-
-void LLCullResult::pushDrawableGroup(LLSpatialGroup* group)
-{
- if (mDrawableGroupsSize < mDrawableGroups.size())
- {
- mDrawableGroups[mDrawableGroupsSize] = group;
- }
- else
- {
- mDrawableGroups.push_back(group);
- }
- ++mDrawableGroupsSize;
- mDrawableGroupsEnd = mDrawableGroups.begin()+mDrawableGroupsSize;
-}
-
-void LLCullResult::pushDrawable(LLDrawable* drawable)
-{
- if (mVisibleListSize < mVisibleList.size())
- {
- mVisibleList[mVisibleListSize] = drawable;
- }
- else
- {
- mVisibleList.push_back(drawable);
- }
- ++mVisibleListSize;
- mVisibleListEnd = mVisibleList.begin()+mVisibleListSize;
-}
-
-void LLCullResult::pushBridge(LLSpatialBridge* bridge)
-{
- if (mVisibleBridgeSize < mVisibleBridge.size())
- {
- mVisibleBridge[mVisibleBridgeSize] = bridge;
- }
- else
- {
- mVisibleBridge.push_back(bridge);
- }
- ++mVisibleBridgeSize;
- mVisibleBridgeEnd = mVisibleBridge.begin()+mVisibleBridgeSize;
-}
-
-void LLCullResult::pushDrawInfo(U32 type, LLDrawInfo* draw_info)
-{
- if (mRenderMapSize[type] < mRenderMap[type].size())
- {
- mRenderMap[type][mRenderMapSize[type]] = draw_info;
- }
- else
- {
- mRenderMap[type].push_back(draw_info);
- }
- ++mRenderMapSize[type];
- mRenderMapEnd[type] = mRenderMap[type].begin() + mRenderMapSize[type];
-}
-
-
-void LLCullResult::assertDrawMapsEmpty()
-{
- for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; i++)
- {
- if (mRenderMapSize[i] != 0)
- {
- llerrs << "Stale LLDrawInfo's in LLCullResult!" << llendl;
- }
- }
-}
-
-
-
+/**
+ * @file llspatialpartition.cpp
+ * @brief LLSpatialGroup class implementation and supporting functions
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llspatialpartition.h"
+
+#include "llappviewer.h"
+#include "lltexturecache.h"
+#include "lltexturefetch.h"
+#include "llimageworker.h"
+#include "llviewerwindow.h"
+#include "llviewerobjectlist.h"
+#include "llvovolume.h"
+#include "llvolume.h"
+#include "llvolumeoctree.h"
+#include "llviewercamera.h"
+#include "llface.h"
+#include "llfloatertools.h"
+#include "llviewercontrol.h"
+#include "llviewerregion.h"
+#include "llcamera.h"
+#include "pipeline.h"
+#include "llmeshrepository.h"
+#include "llrender.h"
+#include "lloctree.h"
+#include "llphysicsshapebuilderutil.h"
+#include "llvoavatar.h"
+#include "llvolumemgr.h"
+#include "lltextureatlas.h"
+#include "llglslshader.h"
+#include "llagent.h"
+#include "llviewershadermgr.h"
+
+static LLFastTimer::DeclareTimer FTM_FRUSTUM_CULL("Frustum Culling");
+static LLFastTimer::DeclareTimer FTM_CULL_REBOUND("Cull Rebound");
+
+const F32 SG_OCCLUSION_FUDGE = 0.25f;
+#define SG_DISCARD_TOLERANCE 0.01f
+
+#if LL_OCTREE_PARANOIA_CHECK
+#define assert_octree_valid(x) x->validate()
+#define assert_states_valid(x) ((LLSpatialGroup*) x->mSpatialPartition->mOctree->getListener(0))->checkStates()
+#else
+#define assert_octree_valid(x)
+#define assert_states_valid(x)
+#endif
+
+
+static U32 sZombieGroups = 0;
+U32 LLSpatialGroup::sNodeCount = 0;
+
+#define LL_TRACK_PENDING_OCCLUSION_QUERIES 0
+
+std::set<GLuint> LLSpatialGroup::sPendingQueries;
+
+U32 gOctreeMaxCapacity;
+
+BOOL LLSpatialGroup::sNoDelete = FALSE;
+
+static F32 sLastMaxTexPriority = 1.f;
+static F32 sCurMaxTexPriority = 1.f;
+
+class LLOcclusionQueryPool : public LLGLNamePool
+{
+protected:
+ virtual GLuint allocateName()
+ {
+ GLuint name;
+ glGenQueriesARB(1, &name);
+ return name;
+ }
+
+ virtual void releaseName(GLuint name)
+ {
+#if LL_TRACK_PENDING_OCCLUSION_QUERIES
+ LLSpatialGroup::sPendingQueries.erase(name);
+#endif
+ glDeleteQueriesARB(1, &name);
+ }
+};
+
+static LLOcclusionQueryPool sQueryPool;
+
+//static counter for frame to switch LOD on
+
+void sg_assert(BOOL expr)
+{
+#if LL_OCTREE_PARANOIA_CHECK
+ if (!expr)
+ {
+ llerrs << "Octree invalid!" << llendl;
+ }
+#endif
+}
+
+S32 AABBSphereIntersect(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &rad)
+{
+ return AABBSphereIntersectR2(min, max, origin, rad*rad);
+}
+
+S32 AABBSphereIntersectR2(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &r)
+{
+ F32 d = 0.f;
+ F32 t;
+
+ if ((min-origin).magVecSquared() < r &&
+ (max-origin).magVecSquared() < r)
+ {
+ return 2;
+ }
+
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (origin.mV[i] < min.mV[i])
+ {
+ t = min.mV[i] - origin.mV[i];
+ d += t*t;
+ }
+ else if (origin.mV[i] > max.mV[i])
+ {
+ t = origin.mV[i] - max.mV[i];
+ d += t*t;
+ }
+
+ if (d > r)
+ {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+S32 AABBSphereIntersect(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &rad)
+{
+ return AABBSphereIntersectR2(min, max, origin, rad*rad);
+}
+
+S32 AABBSphereIntersectR2(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &r)
+{
+ F32 d = 0.f;
+ F32 t;
+
+ LLVector4a origina;
+ origina.load3(origin.mV);
+
+ LLVector4a v;
+ v.setSub(min, origina);
+
+ if (v.dot3(v) < r)
+ {
+ v.setSub(max, origina);
+ if (v.dot3(v) < r)
+ {
+ return 2;
+ }
+ }
+
+
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (origin.mV[i] < min[i])
+ {
+ t = min[i] - origin.mV[i];
+ d += t*t;
+ }
+ else if (origin.mV[i] > max[i])
+ {
+ t = origin.mV[i] - max[i];
+ d += t*t;
+ }
+
+ if (d > r)
+ {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+typedef enum
+{
+ b000 = 0x00,
+ b001 = 0x01,
+ b010 = 0x02,
+ b011 = 0x03,
+ b100 = 0x04,
+ b101 = 0x05,
+ b110 = 0x06,
+ b111 = 0x07,
+} eLoveTheBits;
+
+//contact Runitai Linden for a copy of the SL object used to write this table
+//basically, you give the table a bitmask of the look-at vector to a node and it
+//gives you a triangle fan index array
+static U16 sOcclusionIndices[] =
+{
+ //000
+ b111, b110, b010, b011, b001, b101, b100, b110,
+ //001
+ b011, b010, b000, b001, b101, b111, b110, b010,
+ //010
+ b101, b100, b110, b111, b011, b001, b000, b100,
+ //011
+ b001, b000, b100, b101, b111, b011, b010, b000,
+ //100
+ b110, b000, b010, b011, b111, b101, b100, b000,
+ //101
+ b010, b100, b000, b001, b011, b111, b110, b100,
+ //110
+ b100, b010, b110, b111, b101, b001, b000, b010,
+ //111
+ b000, b110, b100, b101, b001, b011, b010, b110,
+};
+
+U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center)
+{
+ LLVector4a origin;
+ origin.load3(camera->getOrigin().mV);
+
+ S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
+
+ return cypher*8;
+}
+
+U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center)
+{
+ LLVector4a origin;
+ origin.load3(camera->getOrigin().mV);
+
+ S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
+
+ return (U8*) (sOcclusionIndices+cypher*8);
+}
+
+
+static LLFastTimer::DeclareTimer FTM_BUILD_OCCLUSION("Build Occlusion");
+
+void LLSpatialGroup::buildOcclusion()
+{
+ //if (mOcclusionVerts.isNull())
+ {
+ mOcclusionVerts = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX,
+ LLVertexBuffer::sUseStreamDraw ? mBufferUsage : 0); //if GL has a hard time with VBOs, don't use them for occlusion culling.
+ mOcclusionVerts->allocateBuffer(8, 64, true);
+
+ LLStrider<U16> idx;
+ mOcclusionVerts->getIndexStrider(idx);
+ for (U32 i = 0; i < 64; i++)
+ {
+ *idx++ = sOcclusionIndices[i];
+ }
+ }
+
+ LLVector4a fudge;
+ fudge.splat(SG_OCCLUSION_FUDGE);
+
+ LLVector4a r;
+ r.setAdd(mBounds[1], fudge);
+
+ LLStrider<LLVector3> pos;
+
+ {
+ LLFastTimer t(FTM_BUILD_OCCLUSION);
+ mOcclusionVerts->getVertexStrider(pos);
+ }
+
+ {
+ LLVector4a* v = (LLVector4a*) pos.get();
+
+ const LLVector4a& c = mBounds[0];
+ const LLVector4a& s = r;
+
+ static const LLVector4a octant[] =
+ {
+ LLVector4a(-1.f, -1.f, -1.f),
+ LLVector4a(-1.f, -1.f, 1.f),
+ LLVector4a(-1.f, 1.f, -1.f),
+ LLVector4a(-1.f, 1.f, 1.f),
+
+ LLVector4a(1.f, -1.f, -1.f),
+ LLVector4a(1.f, -1.f, 1.f),
+ LLVector4a(1.f, 1.f, -1.f),
+ LLVector4a(1.f, 1.f, 1.f),
+ };
+
+ //vertex positions are encoded so the 3 bits of their vertex index
+ //correspond to their axis facing, with bit position 3,2,1 matching
+ //axis facing x,y,z, bit set meaning positive facing, bit clear
+ //meaning negative facing
+
+ for (S32 i = 0; i < 8; ++i)
+ {
+ LLVector4a p;
+ p.setMul(s, octant[i]);
+ p.add(c);
+ v[i] = p;
+ }
+ }
+
+ {
+ mOcclusionVerts->flush();
+ LLVertexBuffer::unbind();
+ }
+
+ clearState(LLSpatialGroup::OCCLUSION_DIRTY);
+}
+
+
+BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group);
+
+//returns:
+// 0 if sphere and AABB are not intersecting
+// 1 if they are
+// 2 if AABB is entirely inside sphere
+
+S32 LLSphereAABB(const LLVector3& center, const LLVector3& size, const LLVector3& pos, const F32 &rad)
+{
+ S32 ret = 2;
+
+ LLVector3 min = center - size;
+ LLVector3 max = center + size;
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (min.mV[i] > pos.mV[i] + rad ||
+ max.mV[i] < pos.mV[i] - rad)
+ { //totally outside
+ return 0;
+ }
+
+ if (min.mV[i] < pos.mV[i] - rad ||
+ max.mV[i] > pos.mV[i] + rad)
+ { //intersecting
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+LLSpatialGroup::~LLSpatialGroup()
+{
+ /*if (sNoDelete)
+ {
+ llerrs << "Illegal deletion of LLSpatialGroup!" << llendl;
+ }*/
+
+ if (gDebugGL)
+ {
+ gPipeline.checkReferences(this);
+ }
+
+ if (isState(DEAD))
+ {
+ sZombieGroups--;
+ }
+
+ sNodeCount--;
+
+ if (gGLManager.mHasOcclusionQuery)
+ {
+ for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; ++i)
+ {
+ if (mOcclusionQuery[i])
+ {
+ sQueryPool.release(mOcclusionQuery[i]);
+ }
+ }
+ }
+
+ mOcclusionVerts = NULL;
+
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ clearDrawMap();
+ clearAtlasList() ;
+}
+
+BOOL LLSpatialGroup::hasAtlas(LLTextureAtlas* atlasp)
+{
+ S8 type = atlasp->getComponents() - 1 ;
+ for(std::list<LLTextureAtlas*>::iterator iter = mAtlasList[type].begin(); iter != mAtlasList[type].end() ; ++iter)
+ {
+ if(atlasp == *iter)
+ {
+ return TRUE ;
+ }
+ }
+ return FALSE ;
+}
+
+void LLSpatialGroup::addAtlas(LLTextureAtlas* atlasp, S8 recursive_level)
+{
+ if(!hasAtlas(atlasp))
+ {
+ mAtlasList[atlasp->getComponents() - 1].push_back(atlasp) ;
+ atlasp->addSpatialGroup(this) ;
+ }
+
+ --recursive_level;
+ if(recursive_level)//levels propagating up.
+ {
+ LLSpatialGroup* parent = getParent() ;
+ if(parent)
+ {
+ parent->addAtlas(atlasp, recursive_level) ;
+ }
+ }
+}
+
+void LLSpatialGroup::removeAtlas(LLTextureAtlas* atlasp, BOOL remove_group, S8 recursive_level)
+{
+ mAtlasList[atlasp->getComponents() - 1].remove(atlasp) ;
+ if(remove_group)
+ {
+ atlasp->removeSpatialGroup(this) ;
+ }
+
+ --recursive_level;
+ if(recursive_level)//levels propagating up.
+ {
+ LLSpatialGroup* parent = getParent() ;
+ if(parent)
+ {
+ parent->removeAtlas(atlasp, recursive_level) ;
+ }
+ }
+}
+
+void LLSpatialGroup::clearAtlasList()
+{
+ std::list<LLTextureAtlas*>::iterator iter ;
+ for(S8 i = 0 ; i < 4 ; i++)
+ {
+ if(mAtlasList[i].size() > 0)
+ {
+ for(iter = mAtlasList[i].begin(); iter != mAtlasList[i].end() ; ++iter)
+ {
+ ((LLTextureAtlas*)*iter)->removeSpatialGroup(this) ;
+ }
+ mAtlasList[i].clear() ;
+ }
+ }
+}
+
+LLTextureAtlas* LLSpatialGroup::getAtlas(S8 ncomponents, S8 to_be_reserved, S8 recursive_level)
+{
+ S8 type = ncomponents - 1 ;
+ if(mAtlasList[type].size() > 0)
+ {
+ for(std::list<LLTextureAtlas*>::iterator iter = mAtlasList[type].begin(); iter != mAtlasList[type].end() ; ++iter)
+ {
+ if(!((LLTextureAtlas*)*iter)->isFull(to_be_reserved))
+ {
+ return *iter ;
+ }
+ }
+ }
+
+ --recursive_level;
+ if(recursive_level)
+ {
+ LLSpatialGroup* parent = getParent() ;
+ if(parent)
+ {
+ return parent->getAtlas(ncomponents, to_be_reserved, recursive_level) ;
+ }
+ }
+ return NULL ;
+}
+
+void LLSpatialGroup::setCurUpdatingSlot(LLTextureAtlasSlot* slotp)
+{
+ mCurUpdatingSlotp = slotp;
+
+ //if(!hasAtlas(mCurUpdatingSlotp->getAtlas()))
+ //{
+ // addAtlas(mCurUpdatingSlotp->getAtlas()) ;
+ //}
+}
+
+LLTextureAtlasSlot* LLSpatialGroup::getCurUpdatingSlot(LLViewerTexture* imagep, S8 recursive_level)
+{
+ if(gFrameCount && mCurUpdatingTime == gFrameCount && mCurUpdatingTexture == imagep)
+ {
+ return mCurUpdatingSlotp ;
+ }
+
+ //--recursive_level ;
+ //if(recursive_level)
+ //{
+ // LLSpatialGroup* parent = getParent() ;
+ // if(parent)
+ // {
+ // return parent->getCurUpdatingSlot(imagep, recursive_level) ;
+ // }
+ //}
+ return NULL ;
+}
+
+void LLSpatialGroup::clearDrawMap()
+{
+ mDrawMap.clear();
+}
+
+BOOL LLSpatialGroup::isHUDGroup()
+{
+ return mSpatialPartition && mSpatialPartition->isHUDPartition() ;
+}
+
+BOOL LLSpatialGroup::isRecentlyVisible() const
+{
+ return (LLDrawable::getCurrentFrame() - mVisible[LLViewerCamera::sCurCameraID]) < LLDrawable::getMinVisFrameRange() ;
+}
+
+BOOL LLSpatialGroup::isVisible() const
+{
+ return mVisible[LLViewerCamera::sCurCameraID] >= LLDrawable::getCurrentFrame() ? TRUE : FALSE;
+}
+
+void LLSpatialGroup::setVisible()
+{
+ mVisible[LLViewerCamera::sCurCameraID] = LLDrawable::getCurrentFrame();
+}
+
+void LLSpatialGroup::validate()
+{
+#if LL_OCTREE_PARANOIA_CHECK
+
+ sg_assert(!isState(DIRTY));
+ sg_assert(!isDead());
+
+ LLVector4a myMin;
+ myMin.setSub(mBounds[0], mBounds[1]);
+ LLVector4a myMax;
+ myMax.setAdd(mBounds[0], mBounds[1]);
+
+ validateDrawMap();
+
+ for (element_iter i = getData().begin(); i != getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ sg_assert(drawable->getSpatialGroup() == this);
+ if (drawable->getSpatialBridge())
+ {
+ sg_assert(drawable->getSpatialBridge() == mSpatialPartition->asBridge());
+ }
+
+ /*if (drawable->isSpatialBridge())
+ {
+ LLSpatialPartition* part = drawable->asPartition();
+ if (!part)
+ {
+ llerrs << "Drawable reports it is a spatial bridge but not a partition." << llendl;
+ }
+ LLSpatialGroup* group = (LLSpatialGroup*) part->mOctree->getListener(0);
+ group->validate();
+ }*/
+ }
+
+ for (U32 i = 0; i < mOctreeNode->getChildCount(); ++i)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
+
+ group->validate();
+
+ //ensure all children are enclosed in this node
+ LLVector4a center = group->mBounds[0];
+ LLVector4a size = group->mBounds[1];
+
+ LLVector4a min;
+ min.setSub(center, size);
+ LLVector4a max;
+ max.setAdd(center, size);
+
+ for (U32 j = 0; j < 3; j++)
+ {
+ sg_assert(min[j] >= myMin[j]-0.02f);
+ sg_assert(max[j] <= myMax[j]+0.02f);
+ }
+ }
+
+#endif
+}
+
+void LLSpatialGroup::checkStates()
+{
+#if LL_OCTREE_PARANOIA_CHECK
+ //LLOctreeStateCheck checker;
+ //checker.traverse(mOctreeNode);
+#endif
+}
+
+void LLSpatialGroup::validateDrawMap()
+{
+#if LL_OCTREE_PARANOIA_CHECK
+ for (draw_map_t::iterator i = mDrawMap.begin(); i != mDrawMap.end(); ++i)
+ {
+ LLSpatialGroup::drawmap_elem_t& draw_vec = i->second;
+ for (drawmap_elem_t::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
+ {
+ LLDrawInfo& params = **j;
+
+ params.validate();
+ }
+ }
+#endif
+}
+
+BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ drawablep->updateSpatialExtents();
+
+ OctreeNode* parent = mOctreeNode->getOctParent();
+
+ if (mOctreeNode->isInside(drawablep->getPositionGroup()) &&
+ (mOctreeNode->contains(drawablep) ||
+ (drawablep->getBinRadius() > mOctreeNode->getSize()[0] &&
+ parent && parent->getElementCount() >= gOctreeMaxCapacity)))
+ {
+ unbound();
+ setState(OBJECT_DIRTY);
+ //setState(GEOM_DIRTY);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+BOOL LLSpatialGroup::addObject(LLDrawable *drawablep, BOOL add_all, BOOL from_octree)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (!from_octree)
+ {
+ mOctreeNode->insert(drawablep);
+ }
+ else
+ {
+ drawablep->setSpatialGroup(this);
+ setState(OBJECT_DIRTY | GEOM_DIRTY);
+ setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS);
+ gPipeline.markRebuild(this, TRUE);
+ if (drawablep->isSpatialBridge())
+ {
+ mBridgeList.push_back((LLSpatialBridge*) drawablep);
+ }
+ if (drawablep->getRadius() > 1.f)
+ {
+ setState(IMAGE_DIRTY);
+ }
+ }
+
+ return TRUE;
+}
+
+void LLSpatialGroup::rebuildGeom()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (!isDead())
+ {
+ mSpatialPartition->rebuildGeom(this);
+ }
+}
+
+void LLSpatialGroup::rebuildMesh()
+{
+ if (!isDead())
+ {
+ mSpatialPartition->rebuildMesh(this);
+ }
+}
+
+static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt");
+
+void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group)
+{
+ if (group->isDead() || !group->isState(LLSpatialGroup::GEOM_DIRTY))
+ {
+ return;
+ }
+
+ if (group->changeLOD())
+ {
+ group->mLastUpdateDistance = group->mDistance;
+ group->mLastUpdateViewAngle = group->mViewAngle;
+ }
+
+ LLFastTimer ftm(FTM_REBUILD_VBO);
+
+ group->clearDrawMap();
+
+ //get geometry count
+ U32 index_count = 0;
+ U32 vertex_count = 0;
+
+ addGeometryCount(group, vertex_count, index_count);
+
+ if (vertex_count > 0 && index_count > 0)
+ { //create vertex buffer containing volume geometry for this node
+ group->mBuilt = 1.f;
+ if (group->mVertexBuffer.isNull() ||
+ !group->mVertexBuffer->isWriteable() ||
+ (group->mBufferUsage != group->mVertexBuffer->getUsage() && LLVertexBuffer::sEnableVBOs))
+ {
+ group->mVertexBuffer = createVertexBuffer(mVertexDataMask, group->mBufferUsage);
+ group->mVertexBuffer->allocateBuffer(vertex_count, index_count, true);
+ stop_glerror();
+ }
+ else
+ {
+ group->mVertexBuffer->resizeBuffer(vertex_count, index_count);
+ stop_glerror();
+ }
+
+ getGeometry(group);
+ }
+ else
+ {
+ group->mVertexBuffer = NULL;
+ group->mBufferMap.clear();
+ }
+
+ group->mLastUpdateTime = gFrameTimeSeconds;
+ group->clearState(LLSpatialGroup::GEOM_DIRTY);
+}
+
+
+void LLSpatialPartition::rebuildMesh(LLSpatialGroup* group)
+{
+
+}
+
+BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector4a& minOut, LLVector4a& maxOut)
+{
+ const OctreeNode* node = mOctreeNode;
+
+ if (node->getData().empty())
+ { //don't do anything if there are no objects
+ if (empty && mOctreeNode->getParent())
+ { //only root is allowed to be empty
+ OCT_ERRS << "Empty leaf found in octree." << llendl;
+ }
+ return FALSE;
+ }
+
+ LLVector4a& newMin = mObjectExtents[0];
+ LLVector4a& newMax = mObjectExtents[1];
+
+ if (isState(OBJECT_DIRTY))
+ { //calculate new bounding box
+ clearState(OBJECT_DIRTY);
+
+ //initialize bounding box to first element
+ OctreeNode::const_element_iter i = node->getData().begin();
+ LLDrawable* drawablep = *i;
+ const LLVector4a* minMax = drawablep->getSpatialExtents();
+
+ newMin = minMax[0];
+ newMax = minMax[1];
+
+ for (++i; i != node->getData().end(); ++i)
+ {
+ drawablep = *i;
+ minMax = drawablep->getSpatialExtents();
+
+ update_min_max(newMin, newMax, minMax[0]);
+ update_min_max(newMin, newMax, minMax[1]);
+
+ //bin up the object
+ /*for (U32 i = 0; i < 3; i++)
+ {
+ if (minMax[0].mV[i] < newMin.mV[i])
+ {
+ newMin.mV[i] = minMax[0].mV[i];
+ }
+ if (minMax[1].mV[i] > newMax.mV[i])
+ {
+ newMax.mV[i] = minMax[1].mV[i];
+ }
+ }*/
+ }
+
+ mObjectBounds[0].setAdd(newMin, newMax);
+ mObjectBounds[0].mul(0.5f);
+ mObjectBounds[1].setSub(newMax, newMin);
+ mObjectBounds[1].mul(0.5f);
+ }
+
+ if (empty)
+ {
+ minOut = newMin;
+ maxOut = newMax;
+ }
+ else
+ {
+ minOut.setMin(minOut, newMin);
+ maxOut.setMax(maxOut, newMax);
+ }
+
+ return TRUE;
+}
+
+void LLSpatialGroup::unbound()
+{
+ if (isState(DIRTY))
+ {
+ return;
+ }
+
+ setState(DIRTY);
+
+ //all the parent nodes need to rebound this child
+ if (mOctreeNode)
+ {
+ OctreeNode* parent = (OctreeNode*) mOctreeNode->getParent();
+ while (parent != NULL)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) parent->getListener(0);
+ if (group->isState(DIRTY))
+ {
+ return;
+ }
+
+ group->setState(DIRTY);
+ parent = (OctreeNode*) parent->getParent();
+ }
+ }
+}
+
+LLSpatialGroup* LLSpatialGroup::getParent()
+{
+ if (isDead())
+ {
+ return NULL;
+ }
+
+ if(!mOctreeNode)
+ {
+ return NULL;
+ }
+ OctreeNode* parent = mOctreeNode->getOctParent();
+
+ if (parent)
+ {
+ return (LLSpatialGroup*) parent->getListener(0);
+ }
+
+ return NULL;
+}
+
+BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ unbound();
+ if (mOctreeNode && !from_octree)
+ {
+ if (!mOctreeNode->remove(drawablep))
+ {
+ OCT_ERRS << "Could not remove drawable from spatial group" << llendl;
+ }
+ }
+ else
+ {
+ drawablep->setSpatialGroup(NULL);
+ setState(GEOM_DIRTY);
+ gPipeline.markRebuild(this, TRUE);
+
+ if (drawablep->isSpatialBridge())
+ {
+ for (bridge_list_t::iterator i = mBridgeList.begin(); i != mBridgeList.end(); ++i)
+ {
+ if (*i == drawablep)
+ {
+ mBridgeList.erase(i);
+ break;
+ }
+ }
+ }
+
+ if (getElementCount() == 0)
+ { //delete draw map on last element removal since a rebuild might never happen
+ clearDrawMap();
+ }
+ }
+ return TRUE;
+}
+
+void LLSpatialGroup::shift(const LLVector4a &offset)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ LLVector4a t = mOctreeNode->getCenter();
+ t.add(offset);
+ mOctreeNode->setCenter(t);
+ mOctreeNode->updateMinMax();
+ mBounds[0].add(offset);
+ mExtents[0].add(offset);
+ mExtents[1].add(offset);
+ mObjectBounds[0].add(offset);
+ mObjectExtents[0].add(offset);
+ mObjectExtents[1].add(offset);
+
+ //if (!mSpatialPartition->mRenderByGroup)
+ {
+ setState(GEOM_DIRTY);
+ gPipeline.markRebuild(this, TRUE);
+ }
+
+ if (mOcclusionVerts.notNull())
+ {
+ setState(OCCLUSION_DIRTY);
+ }
+}
+
+class LLSpatialSetState : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ U32 mState;
+ LLSpatialSetState(U32 state) : mState(state) { }
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setState(mState); }
+};
+
+class LLSpatialSetStateDiff : public LLSpatialSetState
+{
+public:
+ LLSpatialSetStateDiff(U32 state) : LLSpatialSetState(state) { }
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (!group->isState(mState))
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ }
+};
+
+void LLSpatialGroup::setState(U32 state)
+{
+ mState |= state;
+
+ llassert(state <= LLSpatialGroup::STATE_MASK);
+}
+
+void LLSpatialGroup::setState(U32 state, S32 mode)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ llassert(state <= LLSpatialGroup::STATE_MASK);
+
+ if (mode > STATE_MODE_SINGLE)
+ {
+ if (mode == STATE_MODE_DIFF)
+ {
+ LLSpatialSetStateDiff setter(state);
+ setter.traverse(mOctreeNode);
+ }
+ else
+ {
+ LLSpatialSetState setter(state);
+ setter.traverse(mOctreeNode);
+ }
+ }
+ else
+ {
+ mState |= state;
+ }
+}
+
+class LLSpatialClearState : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ U32 mState;
+ LLSpatialClearState(U32 state) : mState(state) { }
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearState(mState); }
+};
+
+class LLSpatialClearStateDiff : public LLSpatialClearState
+{
+public:
+ LLSpatialClearStateDiff(U32 state) : LLSpatialClearState(state) { }
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (group->isState(mState))
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ }
+};
+
+void LLSpatialGroup::clearState(U32 state)
+{
+ llassert(state <= LLSpatialGroup::STATE_MASK);
+
+ mState &= ~state;
+}
+
+void LLSpatialGroup::clearState(U32 state, S32 mode)
+{
+ llassert(state <= LLSpatialGroup::STATE_MASK);
+
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ if (mode > STATE_MODE_SINGLE)
+ {
+ if (mode == STATE_MODE_DIFF)
+ {
+ LLSpatialClearStateDiff clearer(state);
+ clearer.traverse(mOctreeNode);
+ }
+ else
+ {
+ LLSpatialClearState clearer(state);
+ clearer.traverse(mOctreeNode);
+ }
+ }
+ else
+ {
+ mState &= ~state;
+ }
+}
+
+BOOL LLSpatialGroup::isState(U32 state) const
+{
+ llassert(state <= LLSpatialGroup::STATE_MASK);
+
+ return mState & state ? TRUE : FALSE;
+}
+
+//=====================================
+// Occlusion State Set/Clear
+//=====================================
+class LLSpatialSetOcclusionState : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ U32 mState;
+ LLSpatialSetOcclusionState(U32 state) : mState(state) { }
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setOcclusionState(mState); }
+};
+
+class LLSpatialSetOcclusionStateDiff : public LLSpatialSetOcclusionState
+{
+public:
+ LLSpatialSetOcclusionStateDiff(U32 state) : LLSpatialSetOcclusionState(state) { }
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (!group->isOcclusionState(mState))
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ }
+};
+
+
+void LLSpatialGroup::setOcclusionState(U32 state, S32 mode)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ if (mode > STATE_MODE_SINGLE)
+ {
+ if (mode == STATE_MODE_DIFF)
+ {
+ LLSpatialSetOcclusionStateDiff setter(state);
+ setter.traverse(mOctreeNode);
+ }
+ else if (mode == STATE_MODE_BRANCH)
+ {
+ LLSpatialSetOcclusionState setter(state);
+ setter.traverse(mOctreeNode);
+ }
+ else
+ {
+ for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+ {
+ mOcclusionState[i] |= state;
+
+ if ((state & DISCARD_QUERY) && mOcclusionQuery[i])
+ {
+ sQueryPool.release(mOcclusionQuery[i]);
+ mOcclusionQuery[i] = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ mOcclusionState[LLViewerCamera::sCurCameraID] |= state;
+ if ((state & DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
+ {
+ sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+ mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
+ }
+ }
+}
+
+class LLSpatialClearOcclusionState : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ U32 mState;
+
+ LLSpatialClearOcclusionState(U32 state) : mState(state) { }
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearOcclusionState(mState); }
+};
+
+class LLSpatialClearOcclusionStateDiff : public LLSpatialClearOcclusionState
+{
+public:
+ LLSpatialClearOcclusionStateDiff(U32 state) : LLSpatialClearOcclusionState(state) { }
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (group->isOcclusionState(mState))
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ }
+};
+
+void LLSpatialGroup::clearOcclusionState(U32 state, S32 mode)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ if (mode > STATE_MODE_SINGLE)
+ {
+ if (mode == STATE_MODE_DIFF)
+ {
+ LLSpatialClearOcclusionStateDiff clearer(state);
+ clearer.traverse(mOctreeNode);
+ }
+ else if (mode == STATE_MODE_BRANCH)
+ {
+ LLSpatialClearOcclusionState clearer(state);
+ clearer.traverse(mOctreeNode);
+ }
+ else
+ {
+ for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+ {
+ mOcclusionState[i] &= ~state;
+ }
+ }
+ }
+ else
+ {
+ mOcclusionState[LLViewerCamera::sCurCameraID] &= ~state;
+ }
+}
+//======================================
+// Octree Listener Implementation
+//======================================
+
+LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) :
+ mState(0),
+ mGeometryBytes(0),
+ mSurfaceArea(0.f),
+ mBuilt(0.f),
+ mOctreeNode(node),
+ mSpatialPartition(part),
+ mVertexBuffer(NULL),
+ mBufferUsage(part->mBufferUsage),
+ mDistance(0.f),
+ mDepth(0.f),
+ mLastUpdateDistance(-1.f),
+ mLastUpdateTime(gFrameTimeSeconds),
+ mAtlasList(4),
+ mCurUpdatingTime(0),
+ mCurUpdatingSlotp(NULL),
+ mCurUpdatingTexture (NULL)
+{
+ sNodeCount++;
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ mViewAngle.splat(0.f);
+ mLastUpdateViewAngle.splat(-1.f);
+ mExtents[0] = mExtents[1] = mObjectBounds[0] = mObjectBounds[0] = mObjectBounds[1] =
+ mObjectExtents[0] = mObjectExtents[1] = mViewAngle;
+
+ sg_assert(mOctreeNode->getListenerCount() == 0);
+ mOctreeNode->addListener(this);
+ setState(SG_INITIAL_STATE_MASK);
+ gPipeline.markRebuild(this, TRUE);
+
+ mBounds[0] = node->getCenter();
+ mBounds[1] = node->getSize();
+
+ part->mLODSeed = (part->mLODSeed+1)%part->mLODPeriod;
+ mLODHash = part->mLODSeed;
+
+ OctreeNode* oct_parent = node->getOctParent();
+
+ LLSpatialGroup* parent = oct_parent ? (LLSpatialGroup*) oct_parent->getListener(0) : NULL;
+
+ for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+ {
+ mOcclusionQuery[i] = 0;
+ mOcclusionIssued[i] = 0;
+ mOcclusionState[i] = parent ? SG_STATE_INHERIT_MASK & parent->mOcclusionState[i] : 0;
+ mVisible[i] = 0;
+ }
+
+ mOcclusionVerts = NULL;
+
+ mRadius = 1;
+ mPixelArea = 1024.f;
+}
+
+void LLSpatialGroup::updateDistance(LLCamera &camera)
+{
+ if (LLViewerCamera::sCurCameraID != LLViewerCamera::CAMERA_WORLD)
+ {
+ llwarns << "Attempted to update distance for camera other than world camera!" << llendl;
+ return;
+ }
+
+#if !LL_RELEASE_FOR_DOWNLOAD
+ if (isState(LLSpatialGroup::OBJECT_DIRTY))
+ {
+ llerrs << "Spatial group dirty on distance update." << llendl;
+ }
+#endif
+ if (!getData().empty())
+ {
+ mRadius = mSpatialPartition->mRenderByGroup ? mObjectBounds[1].getLength3().getF32() :
+ (F32) mOctreeNode->getSize().getLength3().getF32();
+ mDistance = mSpatialPartition->calcDistance(this, camera);
+ mPixelArea = mSpatialPartition->calcPixelArea(this, camera);
+ }
+}
+
+F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
+{
+ LLVector4a eye;
+ LLVector4a origin;
+ origin.load3(camera.getOrigin().mV);
+
+ eye.setSub(group->mObjectBounds[0], origin);
+
+ F32 dist = 0.f;
+
+ if (group->mDrawMap.find(LLRenderPass::PASS_ALPHA) != group->mDrawMap.end())
+ {
+ LLVector4a v = eye;
+
+ dist = eye.getLength3().getF32();
+ eye.normalize3fast();
+
+ if (!group->isState(LLSpatialGroup::ALPHA_DIRTY))
+ {
+ if (!group->mSpatialPartition->isBridge())
+ {
+ LLVector4a view_angle = eye;
+
+ LLVector4a diff;
+ diff.setSub(view_angle, group->mLastUpdateViewAngle);
+
+ if (diff.getLength3().getF32() > 0.64f)
+ {
+ group->mViewAngle = view_angle;
+ group->mLastUpdateViewAngle = view_angle;
+ //for occasional alpha sorting within the group
+ //NOTE: If there is a trivial way to detect that alpha sorting here would not change the render order,
+ //not setting this node to dirty would be a very good thing
+ group->setState(LLSpatialGroup::ALPHA_DIRTY);
+ gPipeline.markRebuild(group, FALSE);
+ }
+ }
+ }
+
+ //calculate depth of node for alpha sorting
+
+ LLVector3 at = camera.getAtAxis();
+
+ LLVector4a ata;
+ ata.load3(at.mV);
+
+ LLVector4a t = ata;
+ //front of bounding box
+ t.mul(0.25f);
+ t.mul(group->mObjectBounds[1]);
+ v.sub(t);
+
+ group->mDepth = v.dot3(ata).getF32();
+ }
+ else
+ {
+ dist = eye.getLength3().getF32();
+ }
+
+ if (dist < 16.f)
+ {
+ dist /= 16.f;
+ dist *= dist;
+ dist *= 16.f;
+ }
+
+ return dist;
+}
+
+F32 LLSpatialPartition::calcPixelArea(LLSpatialGroup* group, LLCamera& camera)
+{
+ return LLPipeline::calcPixelArea(group->mObjectBounds[0], group->mObjectBounds[1], camera);
+}
+
+F32 LLSpatialGroup::getUpdateUrgency() const
+{
+ if (!isVisible())
+ {
+ return 0.f;
+ }
+ else
+ {
+ F32 time = gFrameTimeSeconds-mLastUpdateTime+4.f;
+ return time + (mObjectBounds[1].dot3(mObjectBounds[1]).getF32()+1.f)/mDistance;
+ }
+}
+
+BOOL LLSpatialGroup::needsUpdate()
+{
+ return (LLDrawable::getCurrentFrame()%mSpatialPartition->mLODPeriod == mLODHash) ? TRUE : FALSE;
+}
+
+BOOL LLSpatialGroup::changeLOD()
+{
+ if (isState(ALPHA_DIRTY | OBJECT_DIRTY))
+ { ///a rebuild is going to happen, update distance and LoD
+ return TRUE;
+ }
+
+ if (mSpatialPartition->mSlopRatio > 0.f)
+ {
+ F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius));
+
+ if (fabsf(ratio) >= mSpatialPartition->mSlopRatio)
+ {
+ return TRUE;
+ }
+
+ if (mDistance > mRadius*2.f)
+ {
+ return FALSE;
+ }
+ }
+
+ if (needsUpdate())
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLSpatialGroup::handleInsertion(const TreeNode* node, LLDrawable* drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ addObject(drawablep, FALSE, TRUE);
+ unbound();
+ setState(OBJECT_DIRTY);
+}
+
+void LLSpatialGroup::handleRemoval(const TreeNode* node, LLDrawable* drawable)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ removeObject(drawable, TRUE);
+ setState(OBJECT_DIRTY);
+}
+
+void LLSpatialGroup::handleDestruction(const TreeNode* node)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ setState(DEAD);
+
+ for (element_iter i = getData().begin(); i != getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ if (drawable->getSpatialGroup() == this)
+ {
+ drawable->setSpatialGroup(NULL);
+ }
+ }
+
+ //clean up avatar attachment stats
+ LLSpatialBridge* bridge = mSpatialPartition->asBridge();
+ if (bridge)
+ {
+ if (bridge->mAvatar.notNull())
+ {
+ bridge->mAvatar->mAttachmentGeometryBytes -= mGeometryBytes;
+ bridge->mAvatar->mAttachmentSurfaceArea -= mSurfaceArea;
+ }
+ }
+
+ clearDrawMap();
+ mVertexBuffer = NULL;
+ mBufferMap.clear();
+ sZombieGroups++;
+ mOctreeNode = NULL;
+}
+
+void LLSpatialGroup::handleStateChange(const TreeNode* node)
+{
+ //drop bounding box upon state change
+ if (mOctreeNode != node)
+ {
+ mOctreeNode = (OctreeNode*) node;
+ }
+ unbound();
+}
+
+void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (child->getListenerCount() == 0)
+ {
+ new LLSpatialGroup(child, mSpatialPartition);
+ }
+ else
+ {
+ OCT_ERRS << "LLSpatialGroup redundancy detected." << llendl;
+ }
+
+ unbound();
+
+ assert_states_valid(this);
+}
+
+void LLSpatialGroup::handleChildRemoval(const OctreeNode* parent, const OctreeNode* child)
+{
+ unbound();
+}
+
+void LLSpatialGroup::destroyGL()
+{
+ setState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::IMAGE_DIRTY);
+ gPipeline.markRebuild(this, TRUE);
+
+ mLastUpdateTime = gFrameTimeSeconds;
+ mVertexBuffer = NULL;
+ mBufferMap.clear();
+
+ clearDrawMap();
+
+ for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+ {
+ if (mOcclusionQuery[i])
+ {
+ sQueryPool.release(mOcclusionQuery[i]);
+ mOcclusionQuery[i] = 0;
+ }
+ }
+
+ mOcclusionVerts = NULL;
+
+ for (LLSpatialGroup::element_iter i = getData().begin(); i != getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ for (S32 j = 0; j < drawable->getNumFaces(); j++)
+ {
+ LLFace* facep = drawable->getFace(j);
+ facep->clearVertexBuffer();
+ }
+ }
+}
+
+BOOL LLSpatialGroup::rebound()
+{
+ if (!isState(DIRTY))
+ { //return TRUE if we're not empty
+ return TRUE;
+ }
+
+ if (mOctreeNode->getChildCount() == 1 && mOctreeNode->getElementCount() == 0)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
+ group->rebound();
+
+ //copy single child's bounding box
+ mBounds[0] = group->mBounds[0];
+ mBounds[1] = group->mBounds[1];
+ mExtents[0] = group->mExtents[0];
+ mExtents[1] = group->mExtents[1];
+
+ group->setState(SKIP_FRUSTUM_CHECK);
+ }
+ else if (mOctreeNode->isLeaf())
+ { //copy object bounding box if this is a leaf
+ boundObjects(TRUE, mExtents[0], mExtents[1]);
+ mBounds[0] = mObjectBounds[0];
+ mBounds[1] = mObjectBounds[1];
+ }
+ else
+ {
+ LLVector4a& newMin = mExtents[0];
+ LLVector4a& newMax = mExtents[1];
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
+ group->clearState(SKIP_FRUSTUM_CHECK);
+ group->rebound();
+ //initialize to first child
+ newMin = group->mExtents[0];
+ newMax = group->mExtents[1];
+
+ //first, rebound children
+ for (U32 i = 1; i < mOctreeNode->getChildCount(); i++)
+ {
+ group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
+ group->clearState(SKIP_FRUSTUM_CHECK);
+ group->rebound();
+ const LLVector4a& max = group->mExtents[1];
+ const LLVector4a& min = group->mExtents[0];
+
+ newMax.setMax(newMax, max);
+ newMin.setMin(newMin, min);
+ }
+
+ boundObjects(FALSE, newMin, newMax);
+
+ mBounds[0].setAdd(newMin, newMax);
+ mBounds[0].mul(0.5f);
+ mBounds[1].setSub(newMax, newMin);
+ mBounds[1].mul(0.5f);
+ }
+
+ setState(OCCLUSION_DIRTY);
+
+ clearState(DIRTY);
+
+ return TRUE;
+}
+
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_READBACK("Readback Occlusion");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_WAIT("Wait");
+
+void LLSpatialGroup::checkOcclusion()
+{
+ if (LLPipeline::sUseOcclusion > 1)
+ {
+ LLFastTimer t(FTM_OCCLUSION_READBACK);
+ LLSpatialGroup* parent = getParent();
+ if (parent && parent->isOcclusionState(LLSpatialGroup::OCCLUDED))
+ { //if the parent has been marked as occluded, the child is implicitly occluded
+ clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
+ }
+ else if (isOcclusionState(QUERY_PENDING))
+ { //otherwise, if a query is pending, read it back
+
+ GLuint available = 0;
+ if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
+ {
+ glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
+
+ if (mOcclusionIssued[LLViewerCamera::sCurCameraID] < gFrameCount)
+ { //query was issued last frame, wait until it's available
+ S32 max_loop = 1024;
+ LLFastTimer t(FTM_OCCLUSION_WAIT);
+ while (!available && max_loop-- > 0)
+ {
+ F32 max_time = llmin(gFrameIntervalSeconds*10.f, 1.f);
+ //do some usefu work while we wait
+ LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread
+ LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread
+ LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread
+
+ glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
+ }
+ }
+ }
+ else
+ {
+ available = 1;
+ }
+
+ if (available)
+ { //result is available, read it back, otherwise wait until next frame
+ GLuint res = 1;
+ if (!isOcclusionState(DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
+ {
+ glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_ARB, &res);
+#if LL_TRACK_PENDING_OCCLUSION_QUERIES
+ sPendingQueries.erase(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+#endif
+ }
+ else if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
+ { //delete the query to avoid holding onto hundreds of pending queries
+ sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+ mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
+ }
+
+ if (isOcclusionState(DISCARD_QUERY))
+ {
+ res = 2;
+ }
+
+ if (res > 0)
+ {
+ assert_states_valid(this);
+ clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
+ assert_states_valid(this);
+ }
+ else
+ {
+ assert_states_valid(this);
+ setOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
+ assert_states_valid(this);
+ }
+
+ clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
+ }
+ }
+ else if (mSpatialPartition->isOcclusionEnabled() && isOcclusionState(LLSpatialGroup::OCCLUDED))
+ { //check occlusion has been issued for occluded node that has not had a query issued
+ assert_states_valid(this);
+ clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
+ assert_states_valid(this);
+ }
+ }
+}
+
+static LLFastTimer::DeclareTimer FTM_PUSH_OCCLUSION_VERTS("Push Occlusion");
+static LLFastTimer::DeclareTimer FTM_SET_OCCLUSION_STATE("Occlusion State");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_EARLY_FAIL("Occlusion Early Fail");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_ALLOCATE("Allocate");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_BUILD("Build");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_BEGIN_QUERY("Begin Query");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_END_QUERY("End Query");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_SET_BUFFER("Set Buffer");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW_WATER("Draw Water");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW("Draw");
+
+
+
+void LLSpatialGroup::doOcclusion(LLCamera* camera)
+{
+ if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1)
+ {
+ // Don't cull hole/edge water, unless we have the GL_ARB_depth_clamp extension
+ if (earlyFail(camera, this))
+ {
+ LLFastTimer t(FTM_OCCLUSION_EARLY_FAIL);
+ setOcclusionState(LLSpatialGroup::DISCARD_QUERY);
+ assert_states_valid(this);
+ clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
+ assert_states_valid(this);
+ }
+ else
+ {
+ if (!isOcclusionState(QUERY_PENDING) || isOcclusionState(DISCARD_QUERY))
+ {
+ { //no query pending, or previous query to be discarded
+ LLFastTimer t(FTM_RENDER_OCCLUSION);
+
+ if (!mOcclusionQuery[LLViewerCamera::sCurCameraID])
+ {
+ LLFastTimer t(FTM_OCCLUSION_ALLOCATE);
+ mOcclusionQuery[LLViewerCamera::sCurCameraID] = sQueryPool.allocate();
+ }
+
+ if (mOcclusionVerts.isNull() || isState(LLSpatialGroup::OCCLUSION_DIRTY))
+ {
+ LLFastTimer t(FTM_OCCLUSION_BUILD);
+ buildOcclusion();
+ }
+
+ // Depth clamp all water to avoid it being culled as a result of being
+ // behind the far clip plane, and in the case of edge water to avoid
+ // it being culled while still visible.
+ bool const use_depth_clamp = gGLManager.mHasDepthClamp &&
+ (mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER ||
+ mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER);
+
+ LLGLEnable clamp(use_depth_clamp ? GL_DEPTH_CLAMP : 0);
+
+#if !LL_DARWIN
+ U32 mode = gGLManager.mHasOcclusionQuery2 ? GL_ANY_SAMPLES_PASSED : GL_SAMPLES_PASSED_ARB;
+#else
+ U32 mode = GL_SAMPLES_PASSED_ARB;
+#endif
+
+#if LL_TRACK_PENDING_OCCLUSION_QUERIES
+ sPendingQueries.insert(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+#endif
+
+ {
+ LLFastTimer t(FTM_PUSH_OCCLUSION_VERTS);
+
+ //store which frame this query was issued on
+ mOcclusionIssued[LLViewerCamera::sCurCameraID] = gFrameCount;
+
+ {
+ LLFastTimer t(FTM_OCCLUSION_BEGIN_QUERY);
+ glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+ }
+
+ {
+ LLFastTimer t(FTM_OCCLUSION_SET_BUFFER);
+ mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
+ }
+
+ if (!use_depth_clamp && mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER)
+ {
+ LLFastTimer t(FTM_OCCLUSION_DRAW_WATER);
+
+ LLGLSquashToFarClip squash(glh_get_current_projection(), 1);
+ if (camera->getOrigin().isExactlyZero())
+ { //origin is invalid, draw entire box
+ mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
+ mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);
+ }
+ else
+ {
+ mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
+ }
+ }
+ else
+ {
+ LLFastTimer t(FTM_OCCLUSION_DRAW);
+ if (camera->getOrigin().isExactlyZero())
+ { //origin is invalid, draw entire box
+ mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
+ mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);
+ }
+ else
+ {
+ mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
+ }
+ }
+
+
+ {
+ LLFastTimer t(FTM_OCCLUSION_END_QUERY);
+ glEndQueryARB(mode);
+ }
+ }
+ }
+
+ {
+ LLFastTimer t(FTM_SET_OCCLUSION_STATE);
+ setOcclusionState(LLSpatialGroup::QUERY_PENDING);
+ clearOcclusionState(LLSpatialGroup::DISCARD_QUERY);
+ }
+ }
+ }
+ }
+}
+
+//==============================================
+
+LLSpatialPartition::LLSpatialPartition(U32 data_mask, BOOL render_by_group, U32 buffer_usage)
+: mRenderByGroup(render_by_group), mBridge(NULL)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ mOcclusionEnabled = TRUE;
+ mDrawableType = 0;
+ mPartitionType = LLViewerRegion::PARTITION_NONE;
+ mLODSeed = 0;
+ mLODPeriod = 1;
+ mVertexDataMask = data_mask;
+ mBufferUsage = buffer_usage;
+ mDepthMask = FALSE;
+ mSlopRatio = 0.25f;
+ mInfiniteFarClip = FALSE;
+
+ LLVector4a center, size;
+ center.splat(0.f);
+ size.splat(1.f);
+
+ mOctree = new LLSpatialGroup::OctreeRoot(center,size,
+ NULL);
+ new LLSpatialGroup(mOctree, this);
+}
+
+
+LLSpatialPartition::~LLSpatialPartition()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ delete mOctree;
+ mOctree = NULL;
+}
+
+
+LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ drawablep->updateSpatialExtents();
+
+ //keep drawable from being garbage collected
+ LLPointer<LLDrawable> ptr = drawablep;
+
+ assert_octree_valid(mOctree);
+ mOctree->insert(drawablep);
+ assert_octree_valid(mOctree);
+
+ LLSpatialGroup* group = drawablep->getSpatialGroup();
+
+ if (group && was_visible && group->isOcclusionState(LLSpatialGroup::QUERY_PENDING))
+ {
+ group->setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS);
+ }
+
+ return group;
+}
+
+BOOL LLSpatialPartition::remove(LLDrawable *drawablep, LLSpatialGroup *curp)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ drawablep->setSpatialGroup(NULL);
+
+ if (!curp->removeObject(drawablep))
+ {
+ OCT_ERRS << "Failed to remove drawable from octree!" << llendl;
+ }
+
+ assert_octree_valid(mOctree);
+
+ return TRUE;
+}
+
+void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ // sanity check submitted by open source user bushing Spatula
+ // who was seeing crashing here. (See VWR-424 reported by Bunny Mayne)
+ if (!drawablep)
+ {
+ OCT_ERRS << "LLSpatialPartition::move was passed a bad drawable." << llendl;
+ return;
+ }
+
+ BOOL was_visible = curp ? curp->isVisible() : FALSE;
+
+ if (curp && curp->mSpatialPartition != this)
+ {
+ //keep drawable from being garbage collected
+ LLPointer<LLDrawable> ptr = drawablep;
+ if (curp->mSpatialPartition->remove(drawablep, curp))
+ {
+ put(drawablep, was_visible);
+ return;
+ }
+ else
+ {
+ OCT_ERRS << "Drawable lost between spatial partitions on outbound transition." << llendl;
+ }
+ }
+
+ if (curp && curp->updateInGroup(drawablep, immediate))
+ {
+ // Already updated, don't need to do anything
+ assert_octree_valid(mOctree);
+ return;
+ }
+
+ //keep drawable from being garbage collected
+ LLPointer<LLDrawable> ptr = drawablep;
+ if (curp && !remove(drawablep, curp))
+ {
+ OCT_ERRS << "Move couldn't find existing spatial group!" << llendl;
+ }
+
+ put(drawablep, was_visible);
+}
+
+class LLSpatialShift : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ const LLVector4a& mOffset;
+
+ LLSpatialShift(const LLVector4a& offset) : mOffset(offset) { }
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch)
+ {
+ ((LLSpatialGroup*) branch->getListener(0))->shift(mOffset);
+ }
+};
+
+void LLSpatialPartition::shift(const LLVector4a &offset)
+{ //shift octree node bounding boxes by offset
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ LLSpatialShift shifter(offset);
+ shifter.traverse(mOctree);
+}
+
+class LLOctreeCull : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLOctreeCull(LLCamera* camera)
+ : mCamera(camera), mRes(0) { }
+
+ virtual bool earlyFail(LLSpatialGroup* group)
+ {
+ group->checkOcclusion();
+
+ if (group->mOctreeNode->getParent() && //never occlusion cull the root node
+ LLPipeline::sUseOcclusion && //ignore occlusion if disabled
+ group->isOcclusionState(LLSpatialGroup::OCCLUDED))
+ {
+ gPipeline.markOccluder(group);
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (earlyFail(group))
+ {
+ return;
+ }
+
+ if (mRes == 2 ||
+ (mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)))
+ { //fully in, just add everything
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ else
+ {
+ mRes = frustumCheck(group);
+
+ if (mRes)
+ { //at least partially in, run on down
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+
+ mRes = 0;
+ }
+ }
+
+ virtual S32 frustumCheck(const LLSpatialGroup* group)
+ {
+ S32 res = mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
+ if (res != 0)
+ {
+ res = llmin(res, AABBSphereIntersect(group->mExtents[0], group->mExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist));
+ }
+ return res;
+ }
+
+ virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
+ {
+ S32 res = mCamera->AABBInFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
+ if (res != 0)
+ {
+ res = llmin(res, AABBSphereIntersect(group->mObjectExtents[0], group->mObjectExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist));
+ }
+ return res;
+ }
+
+ virtual bool checkObjects(const LLSpatialGroup::OctreeNode* branch, const LLSpatialGroup* group)
+ {
+ if (branch->getElementCount() == 0) //no elements
+ {
+ return false;
+ }
+ else if (branch->getChildCount() == 0) //leaf state, already checked tightest bounding box
+ {
+ return true;
+ }
+ else if (mRes == 1 && !frustumCheckObjects(group)) //no objects in frustum
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ virtual void preprocess(LLSpatialGroup* group)
+ {
+
+ }
+
+ virtual void processGroup(LLSpatialGroup* group)
+ {
+ if (group->needsUpdate() ||
+ group->mVisible[LLViewerCamera::sCurCameraID] < LLDrawable::getCurrentFrame() - 1)
+ {
+ group->doOcclusion(mCamera);
+ }
+ gPipeline.markNotCulled(group, *mCamera);
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
+
+ preprocess(group);
+
+ if (checkObjects(branch, group))
+ {
+ processGroup(group);
+ }
+ }
+
+ LLCamera *mCamera;
+ S32 mRes;
+};
+
+class LLOctreeCullNoFarClip : public LLOctreeCull
+{
+public:
+ LLOctreeCullNoFarClip(LLCamera* camera)
+ : LLOctreeCull(camera) { }
+
+ virtual S32 frustumCheck(const LLSpatialGroup* group)
+ {
+ return mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
+ }
+
+ virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
+ {
+ S32 res = mCamera->AABBInFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
+ return res;
+ }
+};
+
+class LLOctreeCullShadow : public LLOctreeCull
+{
+public:
+ LLOctreeCullShadow(LLCamera* camera)
+ : LLOctreeCull(camera) { }
+
+ virtual S32 frustumCheck(const LLSpatialGroup* group)
+ {
+ return mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]);
+ }
+
+ virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
+ {
+ return mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]);
+ }
+};
+
+class LLOctreeCullVisExtents: public LLOctreeCullShadow
+{
+public:
+ LLOctreeCullVisExtents(LLCamera* camera, LLVector4a& min, LLVector4a& max)
+ : LLOctreeCullShadow(camera), mMin(min), mMax(max), mEmpty(TRUE) { }
+
+ virtual bool earlyFail(LLSpatialGroup* group)
+ {
+ if (group->mOctreeNode->getParent() && //never occlusion cull the root node
+ LLPipeline::sUseOcclusion && //ignore occlusion if disabled
+ group->isOcclusionState(LLSpatialGroup::OCCLUDED))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (earlyFail(group))
+ {
+ return;
+ }
+
+ if ((mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)) ||
+ mRes == 2)
+ { //don't need to do frustum check
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ else
+ {
+ mRes = frustumCheck(group);
+
+ if (mRes)
+ { //at least partially in, run on down
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+
+ mRes = 0;
+ }
+ }
+
+ virtual void processGroup(LLSpatialGroup* group)
+ {
+ llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->getData().empty())
+
+ if (mRes < 2)
+ {
+ if (mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]) > 0)
+ {
+ mEmpty = FALSE;
+ update_min_max(mMin, mMax, group->mObjectExtents[0]);
+ update_min_max(mMin, mMax, group->mObjectExtents[1]);
+ }
+ }
+ else
+ {
+ mEmpty = FALSE;
+ update_min_max(mMin, mMax, group->mExtents[0]);
+ update_min_max(mMin, mMax, group->mExtents[1]);
+ }
+ }
+
+ BOOL mEmpty;
+ LLVector4a& mMin;
+ LLVector4a& mMax;
+};
+
+class LLOctreeCullDetectVisible: public LLOctreeCullShadow
+{
+public:
+ LLOctreeCullDetectVisible(LLCamera* camera)
+ : LLOctreeCullShadow(camera), mResult(FALSE) { }
+
+ virtual bool earlyFail(LLSpatialGroup* group)
+ {
+ if (mResult || //already found a node, don't check any more
+ (group->mOctreeNode->getParent() && //never occlusion cull the root node
+ LLPipeline::sUseOcclusion && //ignore occlusion if disabled
+ group->isOcclusionState(LLSpatialGroup::OCCLUDED)))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual void processGroup(LLSpatialGroup* group)
+ {
+ if (group->isVisible())
+ {
+ mResult = TRUE;
+ }
+ }
+
+ BOOL mResult;
+};
+
+class LLOctreeSelect : public LLOctreeCull
+{
+public:
+ LLOctreeSelect(LLCamera* camera, std::vector<LLDrawable*>* results)
+ : LLOctreeCull(camera), mResults(results) { }
+
+ virtual bool earlyFail(LLSpatialGroup* group) { return false; }
+ virtual void preprocess(LLSpatialGroup* group) { }
+
+ virtual void processGroup(LLSpatialGroup* group)
+ {
+ LLSpatialGroup::OctreeNode* branch = group->mOctreeNode;
+
+ for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+
+ if (!drawable->isDead())
+ {
+ if (drawable->isSpatialBridge())
+ {
+ drawable->setVisible(*mCamera, mResults, TRUE);
+ }
+ else
+ {
+ mResults->push_back(drawable);
+ }
+ }
+ }
+ }
+
+ std::vector<LLDrawable*>* mResults;
+};
+
+void drawBox(const LLVector3& c, const LLVector3& r)
+{
+ LLVertexBuffer::unbind();
+
+ gGL.begin(LLRender::TRIANGLE_STRIP);
+ //left front
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
+ //right front
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
+ //right back
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
+ //left back
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
+ //left front
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
+ gGL.end();
+
+ //bottom
+ gGL.begin(LLRender::TRIANGLE_STRIP);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
+ gGL.end();
+
+ //top
+ gGL.begin(LLRender::TRIANGLE_STRIP);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
+ gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
+ gGL.end();
+}
+
+void drawBox(const LLVector4a& c, const LLVector4a& r)
+{
+ drawBox(reinterpret_cast<const LLVector3&>(c), reinterpret_cast<const LLVector3&>(r));
+}
+
+void drawBoxOutline(const LLVector3& pos, const LLVector3& size)
+{
+ LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
+ LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
+ LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
+ LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
+
+ gGL.begin(LLRender::LINES);
+
+ //top
+ gGL.vertex3fv((pos+v1).mV);
+ gGL.vertex3fv((pos+v2).mV);
+ gGL.vertex3fv((pos+v2).mV);
+ gGL.vertex3fv((pos+v3).mV);
+ gGL.vertex3fv((pos+v3).mV);
+ gGL.vertex3fv((pos+v4).mV);
+ gGL.vertex3fv((pos+v4).mV);
+ gGL.vertex3fv((pos+v1).mV);
+
+ //bottom
+ gGL.vertex3fv((pos-v1).mV);
+ gGL.vertex3fv((pos-v2).mV);
+ gGL.vertex3fv((pos-v2).mV);
+ gGL.vertex3fv((pos-v3).mV);
+ gGL.vertex3fv((pos-v3).mV);
+ gGL.vertex3fv((pos-v4).mV);
+ gGL.vertex3fv((pos-v4).mV);
+ gGL.vertex3fv((pos-v1).mV);
+
+ //right
+ gGL.vertex3fv((pos+v1).mV);
+ gGL.vertex3fv((pos-v3).mV);
+
+ gGL.vertex3fv((pos+v4).mV);
+ gGL.vertex3fv((pos-v2).mV);
+
+ //left
+ gGL.vertex3fv((pos+v2).mV);
+ gGL.vertex3fv((pos-v4).mV);
+
+ gGL.vertex3fv((pos+v3).mV);
+ gGL.vertex3fv((pos-v1).mV);
+
+ gGL.end();
+}
+
+void drawBoxOutline(const LLVector4a& pos, const LLVector4a& size)
+{
+ drawBoxOutline(reinterpret_cast<const LLVector3&>(pos), reinterpret_cast<const LLVector3&>(size));
+}
+
+class LLOctreeDirty : public LLOctreeTraveler<LLDrawable>
+{
+public:
+ virtual void visit(const LLOctreeNode<LLDrawable>* state)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) state->getListener(0);
+ group->destroyGL();
+
+ for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ if (drawable->getVObj().notNull() && !group->mSpatialPartition->mRenderByGroup)
+ {
+ gPipeline.markRebuild(drawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ }
+
+ for (LLSpatialGroup::bridge_list_t::iterator i = group->mBridgeList.begin(); i != group->mBridgeList.end(); ++i)
+ {
+ LLSpatialBridge* bridge = *i;
+ traverse(bridge->mOctree);
+ }
+ }
+};
+
+void LLSpatialPartition::restoreGL()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+}
+
+void LLSpatialPartition::resetVertexBuffers()
+{
+ LLOctreeDirty dirty;
+ dirty.traverse(mOctree);
+}
+
+BOOL LLSpatialPartition::isOcclusionEnabled()
+{
+ return mOcclusionEnabled || LLPipeline::sUseOcclusion > 2;
+}
+
+BOOL LLSpatialPartition::getVisibleExtents(LLCamera& camera, LLVector3& visMin, LLVector3& visMax)
+{
+ LLVector4a visMina, visMaxa;
+ visMina.load3(visMin.mV);
+ visMaxa.load3(visMax.mV);
+
+ {
+ LLFastTimer ftm(FTM_CULL_REBOUND);
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
+ group->rebound();
+ }
+
+ LLOctreeCullVisExtents vis(&camera, visMina, visMaxa);
+ vis.traverse(mOctree);
+
+ visMin.set(visMina.getF32ptr());
+ visMax.set(visMaxa.getF32ptr());
+ return vis.mEmpty;
+}
+
+BOOL LLSpatialPartition::visibleObjectsInFrustum(LLCamera& camera)
+{
+ LLOctreeCullDetectVisible vis(&camera);
+ vis.traverse(mOctree);
+ return vis.mResult;
+}
+
+S32 LLSpatialPartition::cull(LLCamera &camera, std::vector<LLDrawable *>* results, BOOL for_select)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+#if LL_OCTREE_PARANOIA_CHECK
+ ((LLSpatialGroup*)mOctree->getListener(0))->checkStates();
+#endif
+ {
+ LLFastTimer ftm(FTM_CULL_REBOUND);
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
+ group->rebound();
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ ((LLSpatialGroup*)mOctree->getListener(0))->validate();
+#endif
+
+
+ if (for_select)
+ {
+ LLOctreeSelect selecter(&camera, results);
+ selecter.traverse(mOctree);
+ }
+ else if (LLPipeline::sShadowRender)
+ {
+ LLFastTimer ftm(FTM_FRUSTUM_CULL);
+ LLOctreeCullShadow culler(&camera);
+ culler.traverse(mOctree);
+ }
+ else if (mInfiniteFarClip || !LLPipeline::sUseFarClip)
+ {
+ LLFastTimer ftm(FTM_FRUSTUM_CULL);
+ LLOctreeCullNoFarClip culler(&camera);
+ culler.traverse(mOctree);
+ }
+ else
+ {
+ LLFastTimer ftm(FTM_FRUSTUM_CULL);
+ LLOctreeCull culler(&camera);
+ culler.traverse(mOctree);
+ }
+
+ return 0;
+}
+
+BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group)
+{
+ if (camera->getOrigin().isExactlyZero())
+ {
+ return FALSE;
+ }
+
+ const F32 vel = SG_OCCLUSION_FUDGE*2.f;
+ LLVector4a fudge;
+ fudge.splat(vel);
+
+ const LLVector4a& c = group->mBounds[0];
+ LLVector4a r;
+ r.setAdd(group->mBounds[1], fudge);
+
+ /*if (r.magVecSquared() > 1024.0*1024.0)
+ {
+ return TRUE;
+ }*/
+
+ LLVector4a e;
+ e.load3(camera->getOrigin().mV);
+
+ LLVector4a min;
+ min.setSub(c,r);
+ LLVector4a max;
+ max.setAdd(c,r);
+
+ S32 lt = e.lessThan(min).getGatheredBits() & 0x7;
+ if (lt)
+ {
+ return FALSE;
+ }
+
+ S32 gt = e.greaterThan(max).getGatheredBits() & 0x7;
+ if (gt)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void pushVerts(LLDrawInfo* params, U32 mask)
+{
+ LLRenderPass::applyModelMatrix(*params);
+ params->mVertexBuffer->setBuffer(mask);
+ params->mVertexBuffer->drawRange(params->mParticle ? LLRender::POINTS : LLRender::TRIANGLES,
+ params->mStart, params->mEnd, params->mCount, params->mOffset);
+}
+
+void pushVerts(LLSpatialGroup* group, U32 mask)
+{
+ LLDrawInfo* params = NULL;
+
+ for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
+ {
+ for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
+ {
+ params = *j;
+ pushVerts(params, mask);
+ }
+ }
+}
+
+void pushVerts(LLFace* face, U32 mask)
+{
+ llassert(face->verify());
+
+ LLVertexBuffer* buffer = face->getVertexBuffer();
+
+ if (buffer && (face->getGeomCount() >= 3))
+ {
+ buffer->setBuffer(mask);
+ U16 start = face->getGeomStart();
+ U16 end = start + face->getGeomCount()-1;
+ U32 count = face->getIndicesCount();
+ U16 offset = face->getIndicesStart();
+ buffer->drawRange(LLRender::TRIANGLES, start, end, count, offset);
+ }
+}
+
+void pushVerts(LLDrawable* drawable, U32 mask)
+{
+ for (S32 i = 0; i < drawable->getNumFaces(); ++i)
+ {
+ pushVerts(drawable->getFace(i), mask);
+ }
+}
+
+void pushVerts(LLVolume* volume)
+{
+ LLVertexBuffer::unbind();
+ for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = volume->getVolumeFace(i);
+ LLVertexBuffer::drawElements(LLRender::TRIANGLES, face.mPositions, NULL, face.mNumIndices, face.mIndices);
+ }
+}
+
+void pushBufferVerts(LLVertexBuffer* buffer, U32 mask)
+{
+ if (buffer)
+ {
+ buffer->setBuffer(mask);
+ buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+ }
+}
+
+void pushBufferVerts(LLSpatialGroup* group, U32 mask)
+{
+ if (group->mSpatialPartition->mRenderByGroup)
+ {
+ if (!group->mDrawMap.empty())
+ {
+ LLDrawInfo* params = *(group->mDrawMap.begin()->second.begin());
+ LLRenderPass::applyModelMatrix(*params);
+
+ pushBufferVerts(group->mVertexBuffer, mask);
+
+ for (LLSpatialGroup::buffer_map_t::iterator i = group->mBufferMap.begin(); i != group->mBufferMap.end(); ++i)
+ {
+ for (LLSpatialGroup::buffer_texture_map_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
+ {
+ for (LLSpatialGroup::buffer_list_t::iterator k = j->second.begin(); k != j->second.end(); ++k)
+ {
+ pushBufferVerts(*k, mask);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ drawBox(group->mBounds[0], group->mBounds[1]);
+ }
+}
+
+void pushVertsColorCoded(LLSpatialGroup* group, U32 mask)
+{
+ LLDrawInfo* params = NULL;
+
+ LLColor4 colors[] = {
+ LLColor4::green,
+ LLColor4::green1,
+ LLColor4::green2,
+ LLColor4::green3,
+ LLColor4::green4,
+ LLColor4::green5,
+ LLColor4::green6
+ };
+
+ static const U32 col_count = LL_ARRAY_SIZE(colors);
+
+ U32 col = 0;
+
+ for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
+ {
+ for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
+ {
+ params = *j;
+ LLRenderPass::applyModelMatrix(*params);
+ gGL.diffuseColor4f(colors[col].mV[0], colors[col].mV[1], colors[col].mV[2], 0.5f);
+ params->mVertexBuffer->setBuffer(mask);
+ params->mVertexBuffer->drawRange(params->mParticle ? LLRender::POINTS : LLRender::TRIANGLES,
+ params->mStart, params->mEnd, params->mCount, params->mOffset);
+ col = (col+1)%col_count;
+ }
+ }
+}
+
+void renderOctree(LLSpatialGroup* group)
+{
+ //render solid object bounding box, color
+ //coded by buffer usage and activity
+ gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
+ LLVector4 col;
+ if (group->mBuilt > 0.f)
+ {
+ group->mBuilt -= 2.f * gFrameIntervalSeconds;
+ if (group->mBufferUsage == GL_STATIC_DRAW_ARB)
+ {
+ col.setVec(1.0f, 0, 0, group->mBuilt*0.5f);
+ }
+ else
+ {
+ col.setVec(0.1f,0.1f,1,0.1f);
+ //col.setVec(1.0f, 1.0f, 0, sinf(group->mBuilt*3.14159f)*0.5f);
+ }
+
+ if (group->mBufferUsage != GL_STATIC_DRAW_ARB)
+ {
+ LLGLDepthTest gl_depth(FALSE, FALSE);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ gGL.diffuseColor4f(1,0,0,group->mBuilt);
+ gGL.flush();
+ glLineWidth(5.f);
+ drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]);
+ gGL.flush();
+ glLineWidth(1.f);
+ gGL.flush();
+ for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ if (!group->mSpatialPartition->isBridge())
+ {
+ gGL.pushMatrix();
+ LLVector3 trans = drawable->getRegion()->getOriginAgent();
+ gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);
+ }
+
+ for (S32 j = 0; j < drawable->getNumFaces(); j++)
+ {
+ LLFace* face = drawable->getFace(j);
+ if (face->getVertexBuffer())
+ {
+ if (gFrameTimeSeconds - face->mLastUpdateTime < 0.5f)
+ {
+ gGL.diffuseColor4f(0, 1, 0, group->mBuilt);
+ }
+ else if (gFrameTimeSeconds - face->mLastMoveTime < 0.5f)
+ {
+ gGL.diffuseColor4f(1, 0, 0, group->mBuilt);
+ }
+ else
+ {
+ continue;
+ }
+
+ face->getVertexBuffer()->setBuffer(LLVertexBuffer::MAP_VERTEX);
+ //drawBox((face->mExtents[0] + face->mExtents[1])*0.5f,
+ // (face->mExtents[1]-face->mExtents[0])*0.5f);
+ face->getVertexBuffer()->draw(LLRender::TRIANGLES, face->getIndicesCount(), face->getIndicesStart());
+ }
+ }
+
+ if (!group->mSpatialPartition->isBridge())
+ {
+ gGL.popMatrix();
+ }
+ }
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ gGL.diffuseColor4f(1,1,1,1);
+ }
+ }
+ else
+ {
+ if (group->mBufferUsage == GL_STATIC_DRAW_ARB && !group->getData().empty()
+ && group->mSpatialPartition->mRenderByGroup)
+ {
+ col.setVec(0.8f, 0.4f, 0.1f, 0.1f);
+ }
+ else
+ {
+ col.setVec(0.1f, 0.1f, 1.f, 0.1f);
+ }
+ }
+
+ gGL.diffuseColor4fv(col.mV);
+ LLVector4a fudge;
+ fudge.splat(0.001f);
+ LLVector4a size = group->mObjectBounds[1];
+ size.mul(1.01f);
+ size.add(fudge);
+
+ //{
+ // LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+ // drawBox(group->mObjectBounds[0], fudge);
+ //}
+
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+ //if (group->mBuilt <= 0.f)
+ {
+ //draw opaque outline
+ //gGL.diffuseColor4f(col.mV[0], col.mV[1], col.mV[2], 1.f);
+ //drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]);
+
+ gGL.diffuseColor4f(0,1,1,1);
+ drawBoxOutline(group->mBounds[0],group->mBounds[1]);
+
+ //draw bounding box for draw info
+ /*if (group->mSpatialPartition->mRenderByGroup)
+ {
+ gGL.diffuseColor4f(1.0f, 0.75f, 0.25f, 0.6f);
+ for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
+ {
+ for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
+ {
+ LLDrawInfo* draw_info = *j;
+ LLVector4a center;
+ center.setAdd(draw_info->mExtents[1], draw_info->mExtents[0]);
+ center.mul(0.5f);
+ LLVector4a size;
+ size.setSub(draw_info->mExtents[1], draw_info->mExtents[0]);
+ size.mul(0.5f);
+ drawBoxOutline(center, size);
+ }
+ }
+ }*/
+ }
+
+// LLSpatialGroup::OctreeNode* node = group->mOctreeNode;
+// gGL.diffuseColor4f(0,1,0,1);
+// drawBoxOutline(LLVector3(node->getCenter()), LLVector3(node->getSize()));
+}
+
+void renderVisibility(LLSpatialGroup* group, LLCamera* camera)
+{
+ LLGLEnable blend(GL_BLEND);
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ LLGLEnable cull(GL_CULL_FACE);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ BOOL render_objects = (!LLPipeline::sUseOcclusion || !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) && group->isVisible() &&
+ !group->getData().empty();
+
+ if (render_objects)
+ {
+ LLGLDepthTest depth_under(GL_TRUE, GL_FALSE, GL_GREATER);
+ gGL.diffuseColor4f(0, 0.5f, 0, 0.5f);
+ gGL.diffuseColor4f(0, 0.5f, 0, 0.5f);
+ pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
+ }
+
+ {
+ LLGLDepthTest depth_over(GL_TRUE, GL_FALSE, GL_LEQUAL);
+
+ if (render_objects)
+ {
+ gGL.diffuseColor4f(0.f, 0.5f, 0.f,1.f);
+ gGL.diffuseColor4f(0.f, 0.5f, 0.f, 1.f);
+ pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
+ }
+
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+ if (render_objects)
+ {
+ gGL.diffuseColor4f(0.f, 0.75f, 0.f,0.5f);
+ gGL.diffuseColor4f(0.f, 0.75f, 0.f, 0.5f);
+ pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
+ }
+ /*else if (camera && group->mOcclusionVerts.notNull())
+ {
+ LLVertexBuffer::unbind();
+ group->mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
+
+ gGL.diffuseColor4f(1.0f, 0.f, 0.f, 0.5f);
+ group->mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, group->mBounds[0]));
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ gGL.diffuseColor4f(1.0f, 1.f, 1.f, 1.0f);
+ group->mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, group->mBounds[0]));
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }*/
+ }
+}
+
+void renderCrossHairs(LLVector3 position, F32 size, LLColor4 color)
+{
+ gGL.diffuseColor4fv(color.mV);
+ gGL.begin(LLRender::LINES);
+ {
+ gGL.vertex3fv((position - LLVector3(size, 0.f, 0.f)).mV);
+ gGL.vertex3fv((position + LLVector3(size, 0.f, 0.f)).mV);
+ gGL.vertex3fv((position - LLVector3(0.f, size, 0.f)).mV);
+ gGL.vertex3fv((position + LLVector3(0.f, size, 0.f)).mV);
+ gGL.vertex3fv((position - LLVector3(0.f, 0.f, size)).mV);
+ gGL.vertex3fv((position + LLVector3(0.f, 0.f, size)).mV);
+ }
+ gGL.end();
+}
+
+void renderUpdateType(LLDrawable* drawablep)
+{
+ LLViewerObject* vobj = drawablep->getVObj();
+ if (!vobj || OUT_UNKNOWN == vobj->getLastUpdateType())
+ {
+ return;
+ }
+ LLGLEnable blend(GL_BLEND);
+ switch (vobj->getLastUpdateType())
+ {
+ case OUT_FULL:
+ gGL.diffuseColor4f(0,1,0,0.5f);
+ break;
+ case OUT_TERSE_IMPROVED:
+ gGL.diffuseColor4f(0,1,1,0.5f);
+ break;
+ case OUT_FULL_COMPRESSED:
+ if (vobj->getLastUpdateCached())
+ {
+ gGL.diffuseColor4f(1,0,0,0.5f);
+ }
+ else
+ {
+ gGL.diffuseColor4f(1,1,0,0.5f);
+ }
+ break;
+ case OUT_FULL_CACHED:
+ gGL.diffuseColor4f(0,0,1,0.5f);
+ break;
+ default:
+ llwarns << "Unknown update_type " << vobj->getLastUpdateType() << llendl;
+ break;
+ };
+ S32 num_faces = drawablep->getNumFaces();
+ if (num_faces)
+ {
+ for (S32 i = 0; i < num_faces; ++i)
+ {
+ pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
+ }
+ }
+}
+
+void renderComplexityDisplay(LLDrawable* drawablep)
+{
+ LLViewerObject* vobj = drawablep->getVObj();
+ if (!vobj)
+ {
+ return;
+ }
+
+ LLVOVolume *voVol = dynamic_cast<LLVOVolume*>(vobj);
+
+ if (!voVol)
+ {
+ return;
+ }
+
+ if (!voVol->isRoot())
+ {
+ return;
+ }
+
+ LLVOVolume::texture_cost_t textures;
+ F32 cost = (F32) voVol->getRenderCost(textures);
+
+ // add any child volumes
+ LLViewerObject::const_child_list_t children = voVol->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator iter = children.begin(); iter != children.end(); ++iter)
+ {
+ const LLViewerObject *child = *iter;
+ const LLVOVolume *child_volume = dynamic_cast<const LLVOVolume*>(child);
+ if (child_volume)
+ {
+ cost += child_volume->getRenderCost(textures);
+ }
+ }
+
+ // add texture cost
+ for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+ {
+ // add the cost of each individual texture in the linkset
+ cost += iter->second;
+ }
+
+ F32 cost_max = (F32) LLVOVolume::getRenderComplexityMax();
+
+
+
+ // allow user to set a static color scale
+ if (gSavedSettings.getS32("RenderComplexityStaticMax") > 0)
+ {
+ cost_max = gSavedSettings.getS32("RenderComplexityStaticMax");
+ }
+
+ F32 cost_ratio = cost / cost_max;
+
+ // cap cost ratio at 1.0f in case cost_max is at a low threshold
+ cost_ratio = cost_ratio > 1.0f ? 1.0f : cost_ratio;
+
+ LLGLEnable blend(GL_BLEND);
+
+ LLColor4 color;
+ const LLColor4 color_min = gSavedSettings.getColor4("RenderComplexityColorMin");
+ const LLColor4 color_mid = gSavedSettings.getColor4("RenderComplexityColorMid");
+ const LLColor4 color_max = gSavedSettings.getColor4("RenderComplexityColorMax");
+
+ if (cost_ratio < 0.5f)
+ {
+ color = color_min * (1 - cost_ratio * 2) + color_mid * (cost_ratio * 2);
+ }
+ else
+ {
+ color = color_mid * (1 - (cost_ratio - 0.5) * 2) + color_max * ((cost_ratio - 0.5) * 2);
+ }
+
+ LLSD color_val = color.getValue();
+
+ // don't highlight objects below the threshold
+ if (cost > gSavedSettings.getS32("RenderComplexityThreshold"))
+ {
+ glColor4f(color[0],color[1],color[2],0.5f);
+
+
+ S32 num_faces = drawablep->getNumFaces();
+ if (num_faces)
+ {
+ for (S32 i = 0; i < num_faces; ++i)
+ {
+ pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
+ }
+ }
+ LLViewerObject::const_child_list_t children = voVol->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator iter = children.begin(); iter != children.end(); ++iter)
+ {
+ const LLViewerObject *child = *iter;
+ if (child)
+ {
+ num_faces = child->getNumFaces();
+ if (num_faces)
+ {
+ for (S32 i = 0; i < num_faces; ++i)
+ {
+ pushVerts(child->mDrawable->getFace(i), LLVertexBuffer::MAP_VERTEX);
+ }
+ }
+ }
+ }
+ }
+
+ voVol->setDebugText(llformat("%4.0f", cost));
+}
+
+void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
+{
+ if (set_color)
+ {
+ if (drawable->isSpatialBridge())
+ {
+ gGL.diffuseColor4f(1,0.5f,0,1);
+ }
+ else if (drawable->getVOVolume())
+ {
+ if (drawable->isRoot())
+ {
+ gGL.diffuseColor4f(1,1,0,1);
+ }
+ else
+ {
+ gGL.diffuseColor4f(0,1,0,1);
+ }
+ }
+ else if (drawable->getVObj())
+ {
+ switch (drawable->getVObj()->getPCode())
+ {
+ case LLViewerObject::LL_VO_SURFACE_PATCH:
+ gGL.diffuseColor4f(0,1,1,1);
+ break;
+ case LLViewerObject::LL_VO_CLOUDS:
+ // no longer used
+ break;
+ case LLViewerObject::LL_VO_PART_GROUP:
+ case LLViewerObject::LL_VO_HUD_PART_GROUP:
+ gGL.diffuseColor4f(0,0,1,1);
+ break;
+ case LLViewerObject::LL_VO_VOID_WATER:
+ case LLViewerObject::LL_VO_WATER:
+ gGL.diffuseColor4f(0,0.5f,1,1);
+ break;
+ case LL_PCODE_LEGACY_TREE:
+ gGL.diffuseColor4f(0,0.5f,0,1);
+ break;
+ default:
+ gGL.diffuseColor4f(1,0,1,1);
+ break;
+ }
+ }
+ else
+ {
+ gGL.diffuseColor4f(1,0,0,1);
+ }
+ }
+
+ const LLVector4a* ext;
+ LLVector4a pos, size;
+
+ //render face bounding boxes
+ for (S32 i = 0; i < drawable->getNumFaces(); i++)
+ {
+ LLFace* facep = drawable->getFace(i);
+
+ ext = facep->mExtents;
+
+ pos.setAdd(ext[0], ext[1]);
+ pos.mul(0.5f);
+ size.setSub(ext[1], ext[0]);
+ size.mul(0.5f);
+
+ drawBoxOutline(pos,size);
+ }
+
+ //render drawable bounding box
+ ext = drawable->getSpatialExtents();
+
+ pos.setAdd(ext[0], ext[1]);
+ pos.mul(0.5f);
+ size.setSub(ext[1], ext[0]);
+ size.mul(0.5f);
+
+ LLViewerObject* vobj = drawable->getVObj();
+ if (vobj && vobj->onActiveList())
+ {
+ gGL.flush();
+ glLineWidth(llmax(4.f*sinf(gFrameTimeSeconds*2.f)+1.f, 1.f));
+ //glLineWidth(4.f*(sinf(gFrameTimeSeconds*2.f)*0.25f+0.75f));
+ stop_glerror();
+ drawBoxOutline(pos,size);
+ gGL.flush();
+ glLineWidth(1.f);
+ }
+ else
+ {
+ drawBoxOutline(pos,size);
+ }
+}
+
+void renderNormals(LLDrawable* drawablep)
+{
+ LLVertexBuffer::unbind();
+
+ LLVOVolume* vol = drawablep->getVOVolume();
+ if (vol)
+ {
+ LLVolume* volume = vol->getVolume();
+ gGL.pushMatrix();
+ gGL.multMatrix((F32*) vol->getRelativeXform().mMatrix);
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ LLVector4a scale(gSavedSettings.getF32("RenderDebugNormalScale"));
+
+ for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = volume->getVolumeFace(i);
+
+ for (S32 j = 0; j < face.mNumVertices; ++j)
+ {
+ gGL.begin(LLRender::LINES);
+ LLVector4a n,p;
+
+ n.setMul(face.mNormals[j], scale);
+ p.setAdd(face.mPositions[j], n);
+
+ gGL.diffuseColor4f(1,1,1,1);
+ gGL.vertex3fv(face.mPositions[j].getF32ptr());
+ gGL.vertex3fv(p.getF32ptr());
+
+ if (face.mBinormals)
+ {
+ n.setMul(face.mBinormals[j], scale);
+ p.setAdd(face.mPositions[j], n);
+
+ gGL.diffuseColor4f(0,1,1,1);
+ gGL.vertex3fv(face.mPositions[j].getF32ptr());
+ gGL.vertex3fv(p.getF32ptr());
+ }
+ gGL.end();
+ }
+ }
+
+ gGL.popMatrix();
+ }
+}
+
+S32 get_physics_detail(const LLVolumeParams& volume_params, const LLVector3& scale)
+{
+ const S32 DEFAULT_DETAIL = 1;
+ const F32 LARGE_THRESHOLD = 5.f;
+ const F32 MEGA_THRESHOLD = 25.f;
+
+ S32 detail = DEFAULT_DETAIL;
+ F32 avg_scale = (scale[0]+scale[1]+scale[2])/3.f;
+
+ if (avg_scale > LARGE_THRESHOLD)
+ {
+ detail += 1;
+ if (avg_scale > MEGA_THRESHOLD)
+ {
+ detail += 1;
+ }
+ }
+
+ return detail;
+}
+
+void renderMeshBaseHull(LLVOVolume* volume, U32 data_mask, LLColor4& color, LLColor4& line_color)
+{
+ LLUUID mesh_id = volume->getVolume()->getParams().getSculptID();
+ LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id);
+
+ const LLVector3 center(0,0,0);
+ const LLVector3 size(0.25f,0.25f,0.25f);
+
+ if (decomp)
+ {
+ if (!decomp->mBaseHullMesh.empty())
+ {
+ gGL.diffuseColor4fv(color.mV);
+ LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mBaseHullMesh.mPositions, decomp->mBaseHullMesh.mNormals);
+ }
+ else
+ {
+ gMeshRepo.buildPhysicsMesh(*decomp);
+ gGL.diffuseColor4f(0,1,1,1);
+ drawBoxOutline(center, size);
+ }
+
+ }
+ else
+ {
+ gGL.diffuseColor3f(1,0,1);
+ drawBoxOutline(center, size);
+ }
+}
+
+void render_hull(LLModel::PhysicsMesh& mesh, const LLColor4& color, const LLColor4& line_color)
+{
+ gGL.diffuseColor4fv(color.mV);
+ LLVertexBuffer::drawArrays(LLRender::TRIANGLES, mesh.mPositions, mesh.mNormals);
+ LLGLEnable offset(GL_POLYGON_OFFSET_LINE);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ glPolygonOffset(3.f, 3.f);
+ glLineWidth(3.f);
+ gGL.diffuseColor4fv(line_color.mV);
+ LLVertexBuffer::drawArrays(LLRender::TRIANGLES, mesh.mPositions, mesh.mNormals);
+ glLineWidth(1.f);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+}
+
+void renderPhysicsShape(LLDrawable* drawable, LLVOVolume* volume)
+{
+ U8 physics_type = volume->getPhysicsShapeType();
+
+ if (physics_type == LLViewerObject::PHYSICS_SHAPE_NONE || volume->isFlexible())
+ {
+ return;
+ }
+
+ //not allowed to return at this point without rendering *something*
+
+ F32 threshold = gSavedSettings.getF32("ObjectCostHighThreshold");
+ F32 cost = volume->getObjectCost();
+
+ LLColor4 low = gSavedSettings.getColor4("ObjectCostLowColor");
+ LLColor4 mid = gSavedSettings.getColor4("ObjectCostMidColor");
+ LLColor4 high = gSavedSettings.getColor4("ObjectCostHighColor");
+
+ F32 normalizedCost = 1.f - exp( -(cost / threshold) );
+
+ LLColor4 color;
+ if ( normalizedCost <= 0.5f )
+ {
+ color = lerp( low, mid, 2.f * normalizedCost );
+ }
+ else
+ {
+ color = lerp( mid, high, 2.f * ( normalizedCost - 0.5f ) );
+ }
+
+ LLColor4 line_color = color*0.5f;
+
+ U32 data_mask = LLVertexBuffer::MAP_VERTEX;
+
+ LLVolumeParams volume_params = volume->getVolume()->getParams();
+
+ LLPhysicsVolumeParams physics_params(volume_params,
+ physics_type == LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
+
+ LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification physics_spec;
+ LLPhysicsShapeBuilderUtil::determinePhysicsShape(physics_params, volume->getScale(), physics_spec);
+
+ U32 type = physics_spec.getType();
+
+ LLVector3 center(0,0,0);
+ LLVector3 size(0.25f,0.25f,0.25f);
+
+ gGL.pushMatrix();
+ gGL.multMatrix((F32*) volume->getRelativeXform().mMatrix);
+
+ if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::USER_MESH)
+ {
+ LLUUID mesh_id = volume->getVolume()->getParams().getSculptID();
+ LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id);
+
+ if (decomp)
+ { //render a physics based mesh
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ if (!decomp->mHull.empty())
+ { //decomposition exists, use that
+
+ if (decomp->mMesh.empty())
+ {
+ gMeshRepo.buildPhysicsMesh(*decomp);
+ }
+
+ for (U32 i = 0; i < decomp->mMesh.size(); ++i)
+ {
+ render_hull(decomp->mMesh[i], color, line_color);
+ }
+ }
+ else if (!decomp->mPhysicsShapeMesh.empty())
+ {
+ //decomp has physics mesh, render that mesh
+ gGL.diffuseColor4fv(color.mV);
+ LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mPhysicsShapeMesh.mPositions, decomp->mPhysicsShapeMesh.mNormals);
+
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ gGL.diffuseColor4fv(line_color.mV);
+ LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mPhysicsShapeMesh.mPositions, decomp->mPhysicsShapeMesh.mNormals);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+ else
+ { //no mesh or decomposition, render base hull
+ renderMeshBaseHull(volume, data_mask, color, line_color);
+
+ if (decomp->mPhysicsShapeMesh.empty())
+ {
+ //attempt to fetch physics shape mesh if available
+ gMeshRepo.fetchPhysicsShape(mesh_id);
+ }
+ }
+ }
+ else
+ {
+ gGL.diffuseColor3f(1,1,0);
+ drawBoxOutline(center, size);
+ }
+ }
+ else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::USER_CONVEX ||
+ type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_CONVEX)
+ {
+ if (volume->isMesh())
+ {
+ renderMeshBaseHull(volume, data_mask, color, line_color);
+ }
+ else
+ {
+ LLVolumeParams volume_params = volume->getVolume()->getParams();
+ S32 detail = get_physics_detail(volume_params, volume->getScale());
+ LLVolume* phys_volume = LLPrimitive::sVolumeManager->refVolume(volume_params, detail);
+
+ if (!phys_volume->mHullPoints)
+ { //build convex hull
+ std::vector<LLVector3> pos;
+ std::vector<U16> index;
+
+ S32 index_offset = 0;
+
+ for (S32 i = 0; i < phys_volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = phys_volume->getVolumeFace(i);
+ if (index_offset + face.mNumVertices > 65535)
+ {
+ continue;
+ }
+
+ for (S32 j = 0; j < face.mNumVertices; ++j)
+ {
+ pos.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+ }
+
+ for (S32 j = 0; j < face.mNumIndices; ++j)
+ {
+ index.push_back(face.mIndices[j]+index_offset);
+ }
+
+ index_offset += face.mNumVertices;
+ }
+
+ if (!pos.empty() && !index.empty())
+ {
+ LLCDMeshData mesh;
+ mesh.mIndexBase = &index[0];
+ mesh.mVertexBase = pos[0].mV;
+ mesh.mNumVertices = pos.size();
+ mesh.mVertexStrideBytes = 12;
+ mesh.mIndexStrideBytes = 6;
+ mesh.mIndexType = LLCDMeshData::INT_16;
+
+ mesh.mNumTriangles = index.size()/3;
+
+ LLCDMeshData res;
+
+ LLConvexDecomposition::getInstance()->generateSingleHullMeshFromMesh( &mesh, &res );
+
+ //copy res into phys_volume
+ phys_volume->mHullPoints = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*res.mNumVertices);
+ phys_volume->mNumHullPoints = res.mNumVertices;
+
+ S32 idx_size = (res.mNumTriangles*3*2+0xF) & ~0xF;
+ phys_volume->mHullIndices = (U16*) ll_aligned_malloc_16(idx_size);
+ phys_volume->mNumHullIndices = res.mNumTriangles*3;
+
+ const F32* v = res.mVertexBase;
+
+ for (S32 i = 0; i < res.mNumVertices; ++i)
+ {
+ F32* p = (F32*) ((U8*)v+i*res.mVertexStrideBytes);
+ phys_volume->mHullPoints[i].load3(p);
+ }
+
+ if (res.mIndexType == LLCDMeshData::INT_16)
+ {
+ for (S32 i = 0; i < res.mNumTriangles; ++i)
+ {
+ U16* idx = (U16*) (((U8*)res.mIndexBase)+i*res.mIndexStrideBytes);
+
+ phys_volume->mHullIndices[i*3+0] = idx[0];
+ phys_volume->mHullIndices[i*3+1] = idx[1];
+ phys_volume->mHullIndices[i*3+2] = idx[2];
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < res.mNumTriangles; ++i)
+ {
+ U32* idx = (U32*) (((U8*)res.mIndexBase)+i*res.mIndexStrideBytes);
+
+ phys_volume->mHullIndices[i*3+0] = (U16) idx[0];
+ phys_volume->mHullIndices[i*3+1] = (U16) idx[1];
+ phys_volume->mHullIndices[i*3+2] = (U16) idx[2];
+ }
+ }
+ }
+ }
+
+ if (phys_volume->mHullPoints)
+ {
+ //render hull
+
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ gGL.diffuseColor4fv(line_color.mV);
+ LLVertexBuffer::unbind();
+
+ llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShader != 0);
+
+ LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
+
+ gGL.diffuseColor4fv(color.mV);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
+
+ }
+ else
+ {
+ gGL.diffuseColor4f(1,0,1,1);
+ drawBoxOutline(center, size);
+ }
+
+ LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
+ }
+ }
+ else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::BOX)
+ {
+ LLVector3 center = physics_spec.getCenter();
+ LLVector3 scale = physics_spec.getScale();
+ LLVector3 vscale = volume->getScale()*2.f;
+ scale.set(scale[0]/vscale[0], scale[1]/vscale[1], scale[2]/vscale[2]);
+
+ gGL.diffuseColor4fv(color.mV);
+ drawBox(center, scale);
+ }
+ else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::SPHERE)
+ {
+ /*LLVolumeParams volume_params;
+ volume_params.setType( LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE );
+ volume_params.setBeginAndEndS( 0.f, 1.f );
+ volume_params.setBeginAndEndT( 0.f, 1.f );
+ volume_params.setRatio ( 1, 1 );
+ volume_params.setShear ( 0, 0 );
+ LLVolume* sphere = LLPrimitive::sVolumeManager->refVolume(volume_params, 3);
+
+ gGL.diffuseColor4fv(color.mV);
+ pushVerts(sphere);
+ LLPrimitive::sVolumeManager->unrefVolume(sphere);*/
+ }
+ else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::CYLINDER)
+ {
+ LLVolumeParams volume_params;
+ volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
+ volume_params.setBeginAndEndS( 0.f, 1.f );
+ volume_params.setBeginAndEndT( 0.f, 1.f );
+ volume_params.setRatio ( 1, 1 );
+ volume_params.setShear ( 0, 0 );
+ LLVolume* cylinder = LLPrimitive::sVolumeManager->refVolume(volume_params, 3);
+
+ gGL.diffuseColor4fv(color.mV);
+ pushVerts(cylinder);
+ LLPrimitive::sVolumeManager->unrefVolume(cylinder);
+ }
+ else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_MESH)
+ {
+ LLVolumeParams volume_params = volume->getVolume()->getParams();
+ S32 detail = get_physics_detail(volume_params, volume->getScale());
+
+ LLVolume* phys_volume = LLPrimitive::sVolumeManager->refVolume(volume_params, detail);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ gGL.diffuseColor4fv(line_color.mV);
+ pushVerts(phys_volume);
+
+ gGL.diffuseColor4fv(color.mV);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ pushVerts(phys_volume);
+ LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
+ }
+ else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_CONVEX)
+ {
+ LLVolumeParams volume_params = volume->getVolume()->getParams();
+ S32 detail = get_physics_detail(volume_params, volume->getScale());
+
+ LLVolume* phys_volume = LLPrimitive::sVolumeManager->refVolume(volume_params, detail);
+
+ if (phys_volume->mHullPoints && phys_volume->mHullIndices)
+ {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShader != 0);
+ LLVertexBuffer::unbind();
+ glVertexPointer(3, GL_FLOAT, 16, phys_volume->mHullPoints);
+ gGL.diffuseColor4fv(line_color.mV);
+ gGL.syncMatrices();
+ glDrawElements(GL_TRIANGLES, phys_volume->mNumHullIndices, GL_UNSIGNED_SHORT, phys_volume->mHullIndices);
+
+ gGL.diffuseColor4fv(color.mV);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glDrawElements(GL_TRIANGLES, phys_volume->mNumHullIndices, GL_UNSIGNED_SHORT, phys_volume->mHullIndices);
+ }
+ else
+ {
+ gGL.diffuseColor3f(1,0,1);
+ drawBoxOutline(center, size);
+ gMeshRepo.buildHull(volume_params, detail);
+ }
+ LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
+ }
+ else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::SCULPT)
+ {
+ //TODO: implement sculpted prim physics display
+ }
+ else
+ {
+ llerrs << "Unhandled type" << llendl;
+ }
+
+ gGL.popMatrix();
+}
+
+void renderPhysicsShapes(LLSpatialGroup* group)
+{
+ for (LLSpatialGroup::OctreeNode::const_element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ LLVOVolume* volume = drawable->getVOVolume();
+ if (volume && !volume->isAttachment() && volume->getPhysicsShapeType() != LLViewerObject::PHYSICS_SHAPE_NONE )
+ {
+ if (!group->mSpatialPartition->isBridge())
+ {
+ gGL.pushMatrix();
+ LLVector3 trans = drawable->getRegion()->getOriginAgent();
+ gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);
+ renderPhysicsShape(drawable, volume);
+ gGL.popMatrix();
+ }
+ else
+ {
+ renderPhysicsShape(drawable, volume);
+ }
+ }
+ else
+ {
+ LLViewerObject* object = drawable->getVObj();
+ if (object && object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
+ {
+ //push face vertices for terrain
+ for (S32 i = 0; i < drawable->getNumFaces(); ++i)
+ {
+ LLFace* face = drawable->getFace(i);
+ LLVertexBuffer* buff = face->getVertexBuffer();
+ if (buff)
+ {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ buff->setBuffer(LLVertexBuffer::MAP_VERTEX);
+ gGL.diffuseColor3f(0.2f, 0.5f, 0.3f);
+ buff->draw(LLRender::TRIANGLES, buff->getNumIndices(), 0);
+
+ gGL.diffuseColor3f(0.2f, 1.f, 0.3f);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ buff->draw(LLRender::TRIANGLES, buff->getNumIndices(), 0);
+ }
+ }
+ }
+ }
+ }
+}
+
+void renderTexturePriority(LLDrawable* drawable)
+{
+ for (int face=0; face<drawable->getNumFaces(); ++face)
+ {
+ LLFace *facep = drawable->getFace(face);
+
+ LLVector4 cold(0,0,0.25f);
+ LLVector4 hot(1,0.25f,0.25f);
+
+ LLVector4 boost_cold(0,0,0,0);
+ LLVector4 boost_hot(0,1,0,1);
+
+ LLGLDisable blend(GL_BLEND);
+
+ //LLViewerTexture* imagep = facep->getTexture();
+ //if (imagep)
+ {
+
+ //F32 vsize = imagep->mMaxVirtualSize;
+ F32 vsize = facep->getPixelArea();
+
+ if (vsize > sCurMaxTexPriority)
+ {
+ sCurMaxTexPriority = vsize;
+ }
+
+ F32 t = vsize/sLastMaxTexPriority;
+
+ LLVector4 col = lerp(cold, hot, t);
+ gGL.diffuseColor4fv(col.mV);
+ }
+ //else
+ //{
+ // gGL.diffuseColor4f(1,0,1,1);
+ //}
+
+ LLVector4a center;
+ center.setAdd(facep->mExtents[1],facep->mExtents[0]);
+ center.mul(0.5f);
+ LLVector4a size;
+ size.setSub(facep->mExtents[1],facep->mExtents[0]);
+ size.mul(0.5f);
+ size.add(LLVector4a(0.01f));
+ drawBox(center, size);
+
+ /*S32 boost = imagep->getBoostLevel();
+ if (boost>LLViewerTexture::BOOST_NONE)
+ {
+ F32 t = (F32) boost / (F32) (LLViewerTexture::BOOST_MAX_LEVEL-1);
+ LLVector4 col = lerp(boost_cold, boost_hot, t);
+ LLGLEnable blend_on(GL_BLEND);
+ gGL.blendFunc(GL_SRC_ALPHA, GL_ONE);
+ gGL.diffuseColor4fv(col.mV);
+ drawBox(center, size);
+ gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }*/
+ }
+}
+
+void renderPoints(LLDrawable* drawablep)
+{
+ LLGLDepthTest depth(GL_FALSE, GL_FALSE);
+ if (drawablep->getNumFaces())
+ {
+ gGL.begin(LLRender::POINTS);
+ gGL.diffuseColor3f(1,1,1);
+ for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+ {
+ gGL.vertex3fv(drawablep->getFace(i)->mCenterLocal.mV);
+ }
+ gGL.end();
+ }
+}
+
+void renderTextureAnim(LLDrawInfo* params)
+{
+ if (!params->mTextureMatrix)
+ {
+ return;
+ }
+
+ LLGLEnable blend(GL_BLEND);
+ gGL.diffuseColor4f(1,1,0,0.5f);
+ pushVerts(params, LLVertexBuffer::MAP_VERTEX);
+}
+
+void renderBatchSize(LLDrawInfo* params)
+{
+ LLGLEnable offset(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(-1.f, 1.f);
+ gGL.diffuseColor4ubv((GLubyte*) &(params->mDebugColor));
+ pushVerts(params, LLVertexBuffer::MAP_VERTEX);
+}
+
+void renderShadowFrusta(LLDrawInfo* params)
+{
+ LLGLEnable blend(GL_BLEND);
+ gGL.setSceneBlendType(LLRender::BT_ADD);
+
+ LLVector4a center;
+ center.setAdd(params->mExtents[1], params->mExtents[0]);
+ center.mul(0.5f);
+ LLVector4a size;
+ size.setSub(params->mExtents[1],params->mExtents[0]);
+ size.mul(0.5f);
+
+ if (gPipeline.mShadowCamera[4].AABBInFrustum(center, size))
+ {
+ gGL.diffuseColor3f(1,0,0);
+ pushVerts(params, LLVertexBuffer::MAP_VERTEX);
+ }
+ if (gPipeline.mShadowCamera[5].AABBInFrustum(center, size))
+ {
+ gGL.diffuseColor3f(0,1,0);
+ pushVerts(params, LLVertexBuffer::MAP_VERTEX);
+ }
+ if (gPipeline.mShadowCamera[6].AABBInFrustum(center, size))
+ {
+ gGL.diffuseColor3f(0,0,1);
+ pushVerts(params, LLVertexBuffer::MAP_VERTEX);
+ }
+ if (gPipeline.mShadowCamera[7].AABBInFrustum(center, size))
+ {
+ gGL.diffuseColor3f(1,0,1);
+ pushVerts(params, LLVertexBuffer::MAP_VERTEX);
+ }
+
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+}
+
+
+void renderLights(LLDrawable* drawablep)
+{
+ if (!drawablep->isLight())
+ {
+ return;
+ }
+
+ if (drawablep->getNumFaces())
+ {
+ LLGLEnable blend(GL_BLEND);
+ gGL.diffuseColor4f(0,1,1,0.5f);
+
+ for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+ {
+ pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
+ }
+
+ const LLVector4a* ext = drawablep->getSpatialExtents();
+
+ LLVector4a pos;
+ pos.setAdd(ext[0], ext[1]);
+ pos.mul(0.5f);
+ LLVector4a size;
+ size.setSub(ext[1], ext[0]);
+ size.mul(0.5f);
+
+ {
+ LLGLDepthTest depth(GL_FALSE, GL_TRUE);
+ gGL.diffuseColor4f(1,1,1,1);
+ drawBoxOutline(pos, size);
+ }
+
+ gGL.diffuseColor4f(1,1,0,1);
+ F32 rad = drawablep->getVOVolume()->getLightRadius();
+ drawBoxOutline(pos, LLVector4a(rad));
+ }
+}
+
+class LLRenderOctreeRaycast : public LLOctreeTriangleRayIntersect
+{
+public:
+
+
+ LLRenderOctreeRaycast(const LLVector4a& start, const LLVector4a& dir, F32* closest_t)
+ : LLOctreeTriangleRayIntersect(start, dir, NULL, closest_t, NULL, NULL, NULL, NULL)
+ {
+
+ }
+
+ void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+ {
+ LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) branch->getListener(0);
+
+ LLVector3 center, size;
+
+ if (branch->getData().empty())
+ {
+ gGL.diffuseColor3f(1.f,0.2f,0.f);
+ center.set(branch->getCenter().getF32ptr());
+ size.set(branch->getSize().getF32ptr());
+ }
+ else
+ {
+ gGL.diffuseColor3f(0.75f, 1.f, 0.f);
+ center.set(vl->mBounds[0].getF32ptr());
+ size.set(vl->mBounds[1].getF32ptr());
+ }
+
+ drawBoxOutline(center, size);
+
+ for (U32 i = 0; i < 2; i++)
+ {
+ LLGLDepthTest depth(GL_TRUE, GL_FALSE, i == 1 ? GL_LEQUAL : GL_GREATER);
+
+ if (i == 1)
+ {
+ gGL.diffuseColor4f(0,1,1,0.5f);
+ }
+ else
+ {
+ gGL.diffuseColor4f(0,0.5f,0.5f, 0.25f);
+ drawBoxOutline(center, size);
+ }
+
+ if (i == 1)
+ {
+ gGL.flush();
+ glLineWidth(3.f);
+ }
+
+ gGL.begin(LLRender::TRIANGLES);
+ for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getData().begin();
+ iter != branch->getData().end();
+ ++iter)
+ {
+ const LLVolumeTriangle* tri = *iter;
+
+ gGL.vertex3fv(tri->mV[0]->getF32ptr());
+ gGL.vertex3fv(tri->mV[1]->getF32ptr());
+ gGL.vertex3fv(tri->mV[2]->getF32ptr());
+ }
+ gGL.end();
+
+ if (i == 1)
+ {
+ gGL.flush();
+ glLineWidth(1.f);
+ }
+ }
+ }
+};
+
+void renderRaycast(LLDrawable* drawablep)
+{
+ if (drawablep->getNumFaces())
+ {
+ LLGLEnable blend(GL_BLEND);
+ gGL.diffuseColor4f(0,1,1,0.5f);
+
+ if (drawablep->getVOVolume())
+ {
+ //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ //pushVerts(drawablep->getFace(gDebugRaycastFaceHit), LLVertexBuffer::MAP_VERTEX);
+ //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+ LLVOVolume* vobj = drawablep->getVOVolume();
+ LLVolume* volume = vobj->getVolume();
+
+ bool transform = true;
+ if (drawablep->isState(LLDrawable::RIGGED))
+ {
+ volume = vobj->getRiggedVolume();
+ transform = false;
+ }
+
+ if (volume)
+ {
+ LLVector3 trans = drawablep->getRegion()->getOriginAgent();
+
+ for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = volume->getVolumeFace(i);
+
+ gGL.pushMatrix();
+ gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);
+ gGL.multMatrix((F32*) vobj->getRelativeXform().mMatrix);
+
+ LLVector3 start, end;
+ if (transform)
+ {
+ start = vobj->agentPositionToVolume(gDebugRaycastStart);
+ end = vobj->agentPositionToVolume(gDebugRaycastEnd);
+ }
+ else
+ {
+ start = gDebugRaycastStart;
+ end = gDebugRaycastEnd;
+ }
+
+ LLVector4a starta, enda;
+ starta.load3(start.mV);
+ enda.load3(end.mV);
+ LLVector4a dir;
+ dir.setSub(enda, starta);
+
+ gGL.flush();
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ {
+ //render face positions
+ LLVertexBuffer::unbind();
+ gGL.diffuseColor4f(0,1,1,0.5f);
+ glVertexPointer(3, GL_FLOAT, sizeof(LLVector4a), face.mPositions);
+ gGL.syncMatrices();
+ glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices);
+ }
+
+ if (!volume->isUnique())
+ {
+ F32 t = 1.f;
+
+ if (!face.mOctree)
+ {
+ ((LLVolumeFace*) &face)->createOctree();
+ }
+
+ LLRenderOctreeRaycast render(starta, dir, &t);
+
+ render.traverse(face.mOctree);
+ }
+
+ gGL.popMatrix();
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+ }
+ }
+ else if (drawablep->isAvatar())
+ {
+ if (drawablep->getVObj() == gDebugRaycastObject)
+ {
+ LLGLDepthTest depth(GL_FALSE);
+ LLVOAvatar* av = (LLVOAvatar*) drawablep->getVObj().get();
+ av->renderCollisionVolumes();
+ }
+ }
+
+ if (drawablep->getVObj() == gDebugRaycastObject)
+ {
+ // draw intersection point
+ gGL.pushMatrix();
+ gGL.loadMatrix(gGLModelView);
+ LLVector3 translate = gDebugRaycastIntersection;
+ gGL.translatef(translate.mV[0], translate.mV[1], translate.mV[2]);
+ LLCoordFrame orient;
+ orient.lookDir(gDebugRaycastNormal, gDebugRaycastBinormal);
+ LLMatrix4 rotation;
+ orient.getRotMatrixToParent(rotation);
+ gGL.multMatrix((float*)rotation.mMatrix);
+
+ gGL.diffuseColor4f(1,0,0,0.5f);
+ drawBox(LLVector3(0, 0, 0), LLVector3(0.1f, 0.022f, 0.022f));
+ gGL.diffuseColor4f(0,1,0,0.5f);
+ drawBox(LLVector3(0, 0, 0), LLVector3(0.021f, 0.1f, 0.021f));
+ gGL.diffuseColor4f(0,0,1,0.5f);
+ drawBox(LLVector3(0, 0, 0), LLVector3(0.02f, 0.02f, 0.1f));
+ gGL.popMatrix();
+
+ // draw bounding box of prim
+ const LLVector4a* ext = drawablep->getSpatialExtents();
+
+ LLVector4a pos;
+ pos.setAdd(ext[0], ext[1]);
+ pos.mul(0.5f);
+ LLVector4a size;
+ size.setSub(ext[1], ext[0]);
+ size.mul(0.5f);
+
+ LLGLDepthTest depth(GL_FALSE, GL_TRUE);
+ gGL.diffuseColor4f(0,0.5f,0.5f,1);
+ drawBoxOutline(pos, size);
+ }
+ }
+}
+
+
+void renderAvatarCollisionVolumes(LLVOAvatar* avatar)
+{
+ avatar->renderCollisionVolumes();
+}
+
+void renderAgentTarget(LLVOAvatar* avatar)
+{
+ // render these for self only (why, i don't know)
+ if (avatar->isSelf())
+ {
+ renderCrossHairs(avatar->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f));
+ renderCrossHairs(avatar->mDrawable->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f));
+ renderCrossHairs(avatar->mRoot.getWorldPosition(), 0.2f, LLColor4(1, 1, 1, 0.8f));
+ renderCrossHairs(avatar->mPelvisp->getWorldPosition(), 0.2f, LLColor4(0, 0, 1, 0.8f));
+ }
+}
+
+class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
+{
+public:
+ LLCamera* mCamera;
+ LLOctreeRenderNonOccluded(LLCamera* camera): mCamera(camera) {}
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* node)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+
+ if (!mCamera || mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]))
+ {
+ node->accept(this);
+ stop_glerror();
+
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ traverse(node->getChild(i));
+ stop_glerror();
+ }
+
+ //draw tight fit bounding boxes for spatial group
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
+ {
+ group->rebuildGeom();
+ group->rebuildMesh();
+
+ renderOctree(group);
+ stop_glerror();
+ }
+
+ //render visibility wireframe
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
+ {
+ group->rebuildGeom();
+ group->rebuildMesh();
+
+ gGL.flush();
+ gGL.pushMatrix();
+ gGLLastMatrix = NULL;
+ gGL.loadMatrix(gGLModelView);
+ renderVisibility(group, mCamera);
+ stop_glerror();
+ gGLLastMatrix = NULL;
+ gGL.popMatrix();
+ gGL.diffuseColor4f(1,1,1,1);
+ }
+ }
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
+
+ if (group->isState(LLSpatialGroup::GEOM_DIRTY) || (mCamera && !mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1])))
+ {
+ return;
+ }
+
+ LLVector4a nodeCenter = group->mBounds[0];
+ LLVector4a octCenter = group->mOctreeNode->getCenter();
+
+ group->rebuildGeom();
+ group->rebuildMesh();
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
+ {
+ if (!group->getData().empty())
+ {
+ gGL.diffuseColor3f(0,0,1);
+ drawBoxOutline(group->mObjectBounds[0],
+ group->mObjectBounds[1]);
+ }
+ }
+
+ for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
+ {
+ renderBoundingBox(drawable);
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_NORMALS))
+ {
+ renderNormals(drawable);
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BUILD_QUEUE))
+ {
+ if (drawable->isState(LLDrawable::IN_REBUILD_Q2))
+ {
+ gGL.diffuseColor4f(0.6f, 0.6f, 0.1f, 1.f);
+ const LLVector4a* ext = drawable->getSpatialExtents();
+ LLVector4a center;
+ center.setAdd(ext[0], ext[1]);
+ center.mul(0.5f);
+ LLVector4a size;
+ size.setSub(ext[1], ext[0]);
+ size.mul(0.5f);
+ drawBoxOutline(center, size);
+ }
+ }
+
+ if (drawable->getVOVolume() && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ renderTexturePriority(drawable);
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_POINTS))
+ {
+ renderPoints(drawable);
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LIGHTS))
+ {
+ renderLights(drawable);
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
+ {
+ renderRaycast(drawable);
+ }
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_UPDATE_TYPE))
+ {
+ renderUpdateType(drawable);
+ }
+ if(gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY))
+ {
+ renderComplexityDisplay(drawable);
+ }
+
+ LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(drawable->getVObj().get());
+
+ if (avatar && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_AVATAR_VOLUME))
+ {
+ renderAvatarCollisionVolumes(avatar);
+ }
+
+ if (avatar && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_AGENT_TARGET))
+ {
+ renderAgentTarget(avatar);
+ }
+
+
+ if (gDebugGL)
+ {
+ for (U32 i = 0; i < drawable->getNumFaces(); ++i)
+ {
+ LLFace* facep = drawable->getFace(i);
+ U8 index = facep->getTextureIndex();
+ if (facep->mDrawInfo)
+ {
+ if (index < 255)
+ {
+ if (facep->mDrawInfo->mTextureList.size() <= index)
+ {
+ llerrs << "Face texture index out of bounds." << llendl;
+ }
+ else if (facep->mDrawInfo->mTextureList[index] != facep->getTexture())
+ {
+ llerrs << "Face texture index incorrect." << llendl;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
+ {
+ LLSpatialGroup::drawmap_elem_t& draw_vec = i->second;
+ for (LLSpatialGroup::drawmap_elem_t::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
+ {
+ LLDrawInfo* draw_info = *j;
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_ANIM))
+ {
+ renderTextureAnim(draw_info);
+ }
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BATCH_SIZE))
+ {
+ renderBatchSize(draw_info);
+ }
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA))
+ {
+ renderShadowFrusta(draw_info);
+ }
+ }
+ }
+ }
+};
+
+
+class LLOctreeRenderPhysicsShapes : public LLOctreeTraveler<LLDrawable>
+{
+public:
+ LLCamera* mCamera;
+ LLOctreeRenderPhysicsShapes(LLCamera* camera): mCamera(camera) {}
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* node)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+
+ if (!mCamera || mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]))
+ {
+ node->accept(this);
+ stop_glerror();
+
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ traverse(node->getChild(i));
+ stop_glerror();
+ }
+
+ group->rebuildGeom();
+ group->rebuildMesh();
+
+ renderPhysicsShapes(group);
+ }
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch)
+ {
+
+ }
+};
+
+class LLOctreePushBBoxVerts : public LLOctreeTraveler<LLDrawable>
+{
+public:
+ LLCamera* mCamera;
+ LLOctreePushBBoxVerts(LLCamera* camera): mCamera(camera) {}
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* node)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+
+ if (!mCamera || mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]))
+ {
+ node->accept(this);
+
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ traverse(node->getChild(i));
+ }
+ }
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
+
+ if (group->isState(LLSpatialGroup::GEOM_DIRTY) || (mCamera && !mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1])))
+ {
+ return;
+ }
+
+ for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+
+ renderBoundingBox(drawable, FALSE);
+ }
+ }
+};
+
+void LLSpatialPartition::renderIntersectingBBoxes(LLCamera* camera)
+{
+ LLOctreePushBBoxVerts pusher(camera);
+ pusher.traverse(mOctree);
+}
+
+class LLOctreeStateCheck : public LLOctreeTraveler<LLDrawable>
+{
+public:
+ U32 mInheritedMask[LLViewerCamera::NUM_CAMERAS];
+
+ LLOctreeStateCheck()
+ {
+ for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+ {
+ mInheritedMask[i] = 0;
+ }
+ }
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* node)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+
+ node->accept(this);
+
+
+ U32 temp[LLViewerCamera::NUM_CAMERAS];
+
+ for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+ {
+ temp[i] = mInheritedMask[i];
+ mInheritedMask[i] |= group->mOcclusionState[i] & LLSpatialGroup::OCCLUDED;
+ }
+
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ traverse(node->getChild(i));
+ }
+
+ for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+ {
+ mInheritedMask[i] = temp[i];
+ }
+ }
+
+
+ virtual void visit(const LLOctreeNode<LLDrawable>* state)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) state->getListener(0);
+
+ for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+ {
+ if (mInheritedMask[i] && !(group->mOcclusionState[i] & mInheritedMask[i]))
+ {
+ llerrs << "Spatial group failed inherited mask test." << llendl;
+ }
+ }
+
+ if (group->isState(LLSpatialGroup::DIRTY))
+ {
+ assert_parent_state(group, LLSpatialGroup::DIRTY);
+ }
+ }
+
+ void assert_parent_state(LLSpatialGroup* group, U32 state)
+ {
+ LLSpatialGroup* parent = group->getParent();
+ while (parent)
+ {
+ if (!parent->isState(state))
+ {
+ llerrs << "Spatial group failed parent state check." << llendl;
+ }
+ parent = parent->getParent();
+ }
+ }
+};
+
+
+void LLSpatialPartition::renderPhysicsShapes()
+{
+ LLSpatialBridge* bridge = asBridge();
+ LLCamera* camera = LLViewerCamera::getInstance();
+
+ if (bridge)
+ {
+ camera = NULL;
+ }
+
+ gGL.flush();
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ glLineWidth(3.f);
+ LLOctreeRenderPhysicsShapes render_physics(camera);
+ render_physics.traverse(mOctree);
+ gGL.flush();
+ glLineWidth(1.f);
+}
+
+void LLSpatialPartition::renderDebug()
+{
+ if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE |
+ LLPipeline::RENDER_DEBUG_OCCLUSION |
+ LLPipeline::RENDER_DEBUG_LIGHTS |
+ LLPipeline::RENDER_DEBUG_BATCH_SIZE |
+ LLPipeline::RENDER_DEBUG_UPDATE_TYPE |
+ LLPipeline::RENDER_DEBUG_BBOXES |
+ LLPipeline::RENDER_DEBUG_NORMALS |
+ LLPipeline::RENDER_DEBUG_POINTS |
+ LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY |
+ LLPipeline::RENDER_DEBUG_TEXTURE_ANIM |
+ LLPipeline::RENDER_DEBUG_RAYCAST |
+ LLPipeline::RENDER_DEBUG_AVATAR_VOLUME |
+ LLPipeline::RENDER_DEBUG_AGENT_TARGET |
+ //LLPipeline::RENDER_DEBUG_BUILD_QUEUE |
+ LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA |
+ LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY))
+ {
+ return;
+ }
+
+ if (LLGLSLShader::sNoFixedFunction)
+ {
+ gDebugProgram.bind();
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ //sLastMaxTexPriority = lerp(sLastMaxTexPriority, sCurMaxTexPriority, gFrameIntervalSeconds);
+ sLastMaxTexPriority = (F32) LLViewerCamera::getInstance()->getScreenPixelArea();
+ sCurMaxTexPriority = 0.f;
+ }
+
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ LLGLDisable cullface(GL_CULL_FACE);
+ LLGLEnable blend(GL_BLEND);
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gPipeline.disableLights();
+
+ LLSpatialBridge* bridge = asBridge();
+ LLCamera* camera = LLViewerCamera::getInstance();
+
+ if (bridge)
+ {
+ camera = NULL;
+ }
+
+ LLOctreeStateCheck checker;
+ checker.traverse(mOctree);
+
+ LLOctreeRenderNonOccluded render_debug(camera);
+ render_debug.traverse(mOctree);
+
+ if (LLGLSLShader::sNoFixedFunction)
+ {
+ gDebugProgram.unbind();
+ }
+}
+
+void LLSpatialGroup::drawObjectBox(LLColor4 col)
+{
+ gGL.diffuseColor4fv(col.mV);
+ LLVector4a size;
+ size = mObjectBounds[1];
+ size.mul(1.01f);
+ size.add(LLVector4a(0.001f));
+ drawBox(mObjectBounds[0], size);
+}
+
+bool LLSpatialPartition::isHUDPartition()
+{
+ return mPartitionType == LLViewerRegion::PARTITION_HUD ;
+}
+
+BOOL LLSpatialPartition::isVisible(const LLVector3& v)
+{
+ if (!LLViewerCamera::getInstance()->sphereInFrustum(v, 4.0f))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+class LLOctreeIntersect : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLVector3 mStart;
+ LLVector3 mEnd;
+ S32 *mFaceHit;
+ LLVector3 *mIntersection;
+ LLVector2 *mTexCoord;
+ LLVector3 *mNormal;
+ LLVector3 *mBinormal;
+ LLDrawable* mHit;
+ BOOL mPickTransparent;
+
+ LLOctreeIntersect(LLVector3 start, LLVector3 end, BOOL pick_transparent,
+ S32* face_hit, LLVector3* intersection, LLVector2* tex_coord, LLVector3* normal, LLVector3* binormal)
+ : mStart(start),
+ mEnd(end),
+ mFaceHit(face_hit),
+ mIntersection(intersection),
+ mTexCoord(tex_coord),
+ mNormal(normal),
+ mBinormal(binormal),
+ mHit(NULL),
+ mPickTransparent(pick_transparent)
+ {
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeNode* branch)
+ {
+ for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ check(*i);
+ }
+ }
+
+ virtual LLDrawable* check(const LLSpatialGroup::OctreeNode* node)
+ {
+ node->accept(this);
+
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ const LLSpatialGroup::OctreeNode* child = node->getChild(i);
+ LLVector3 res;
+
+ LLSpatialGroup* group = (LLSpatialGroup*) child->getListener(0);
+
+ LLVector4a size;
+ LLVector4a center;
+
+ size = group->mBounds[1];
+ center = group->mBounds[0];
+
+ LLVector3 local_start = mStart;
+ LLVector3 local_end = mEnd;
+
+ if (group->mSpatialPartition->isBridge())
+ {
+ LLMatrix4 local_matrix = group->mSpatialPartition->asBridge()->mDrawable->getRenderMatrix();
+ local_matrix.invert();
+
+ local_start = mStart * local_matrix;
+ local_end = mEnd * local_matrix;
+ }
+
+ LLVector4a start, end;
+ start.load3(local_start.mV);
+ end.load3(local_end.mV);
+
+ if (LLLineSegmentBoxIntersect(start, end, center, size))
+ {
+ check(child);
+ }
+ }
+
+ return mHit;
+ }
+
+ virtual bool check(LLDrawable* drawable)
+ {
+ LLVector3 local_start = mStart;
+ LLVector3 local_end = mEnd;
+
+ if (!drawable || !gPipeline.hasRenderType(drawable->getRenderType()) || !drawable->isVisible())
+ {
+ return false;
+ }
+
+ if (drawable->isSpatialBridge())
+ {
+ LLSpatialPartition *part = drawable->asPartition();
+ LLSpatialBridge* bridge = part->asBridge();
+ if (bridge && gPipeline.hasRenderType(bridge->mDrawableType))
+ {
+ check(part->mOctree);
+ }
+ }
+ else
+ {
+ LLViewerObject* vobj = drawable->getVObj();
+
+ if (vobj)
+ {
+ LLVector3 intersection;
+ bool skip_check = false;
+ if (vobj->isAvatar())
+ {
+ LLVOAvatar* avatar = (LLVOAvatar*) vobj;
+ if (avatar->isSelf() && LLFloater::isVisible(gFloaterTools))
+ {
+ LLViewerObject* hit = avatar->lineSegmentIntersectRiggedAttachments(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal);
+ if (hit)
+ {
+ mEnd = intersection;
+ if (mIntersection)
+ {
+ *mIntersection = intersection;
+ }
+
+ mHit = hit->mDrawable;
+ skip_check = true;
+ }
+
+ }
+ }
+
+ if (!skip_check && vobj->lineSegmentIntersect(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal))
+ {
+ mEnd = intersection; // shorten ray so we only find CLOSER hits
+ if (mIntersection)
+ {
+ *mIntersection = intersection;
+ }
+
+ mHit = vobj->mDrawable;
+ }
+ }
+ }
+
+ return false;
+ }
+};
+
+LLDrawable* LLSpatialPartition::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+ BOOL pick_transparent,
+ S32* face_hit, // return the face hit
+ LLVector3* intersection, // return the intersection point
+ LLVector2* tex_coord, // return the texture coordinates of the intersection point
+ LLVector3* normal, // return the surface normal at the intersection point
+ LLVector3* bi_normal // return the surface bi-normal at the intersection point
+ )
+
+{
+ LLOctreeIntersect intersect(start, end, pick_transparent, face_hit, intersection, tex_coord, normal, bi_normal);
+ LLDrawable* drawable = intersect.check(mOctree);
+
+ return drawable;
+}
+
+LLDrawInfo::LLDrawInfo(U16 start, U16 end, U32 count, U32 offset,
+ LLViewerTexture* texture, LLVertexBuffer* buffer,
+ BOOL fullbright, U8 bump, BOOL particle, F32 part_size)
+:
+ mVertexBuffer(buffer),
+ mTexture(texture),
+ mTextureMatrix(NULL),
+ mModelMatrix(NULL),
+ mStart(start),
+ mEnd(end),
+ mCount(count),
+ mOffset(offset),
+ mFullbright(fullbright),
+ mBump(bump),
+ mParticle(particle),
+ mPartSize(part_size),
+ mVSize(0.f),
+ mGroup(NULL),
+ mFace(NULL),
+ mDistance(0.f),
+ mDrawMode(LLRender::TRIANGLES)
+{
+ mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
+
+ mDebugColor = (rand() << 16) + rand();
+}
+
+LLDrawInfo::~LLDrawInfo()
+{
+ /*if (LLSpatialGroup::sNoDelete)
+ {
+ llerrs << "LLDrawInfo deleted illegally!" << llendl;
+ }*/
+
+ if (mFace)
+ {
+ mFace->setDrawInfo(NULL);
+ }
+
+ if (gDebugGL)
+ {
+ gPipeline.checkReferences(this);
+ }
+}
+
+void LLDrawInfo::validate()
+{
+ mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
+}
+
+LLVertexBuffer* LLGeometryManager::createVertexBuffer(U32 type_mask, U32 usage)
+{
+ return new LLVertexBuffer(type_mask, usage);
+}
+
+LLCullResult::LLCullResult()
+{
+ clear();
+}
+
+void LLCullResult::clear()
+{
+ mVisibleGroupsSize = 0;
+ mVisibleGroupsEnd = mVisibleGroups.begin();
+
+ mAlphaGroupsSize = 0;
+ mAlphaGroupsEnd = mAlphaGroups.begin();
+
+ mOcclusionGroupsSize = 0;
+ mOcclusionGroupsEnd = mOcclusionGroups.begin();
+
+ mDrawableGroupsSize = 0;
+ mDrawableGroupsEnd = mDrawableGroups.begin();
+
+ mVisibleListSize = 0;
+ mVisibleListEnd = mVisibleList.begin();
+
+ mVisibleBridgeSize = 0;
+ mVisibleBridgeEnd = mVisibleBridge.begin();
+
+
+ for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; i++)
+ {
+ for (U32 j = 0; j < mRenderMapSize[i]; j++)
+ {
+ mRenderMap[i][j] = 0;
+ }
+ mRenderMapSize[i] = 0;
+ mRenderMapEnd[i] = mRenderMap[i].begin();
+ }
+}
+
+LLCullResult::sg_list_t::iterator LLCullResult::beginVisibleGroups()
+{
+ return mVisibleGroups.begin();
+}
+
+LLCullResult::sg_list_t::iterator LLCullResult::endVisibleGroups()
+{
+ return mVisibleGroupsEnd;
+}
+
+LLCullResult::sg_list_t::iterator LLCullResult::beginAlphaGroups()
+{
+ return mAlphaGroups.begin();
+}
+
+LLCullResult::sg_list_t::iterator LLCullResult::endAlphaGroups()
+{
+ return mAlphaGroupsEnd;
+}
+
+LLCullResult::sg_list_t::iterator LLCullResult::beginOcclusionGroups()
+{
+ return mOcclusionGroups.begin();
+}
+
+LLCullResult::sg_list_t::iterator LLCullResult::endOcclusionGroups()
+{
+ return mOcclusionGroupsEnd;
+}
+
+LLCullResult::sg_list_t::iterator LLCullResult::beginDrawableGroups()
+{
+ return mDrawableGroups.begin();
+}
+
+LLCullResult::sg_list_t::iterator LLCullResult::endDrawableGroups()
+{
+ return mDrawableGroupsEnd;
+}
+
+LLCullResult::drawable_list_t::iterator LLCullResult::beginVisibleList()
+{
+ return mVisibleList.begin();
+}
+
+LLCullResult::drawable_list_t::iterator LLCullResult::endVisibleList()
+{
+ return mVisibleListEnd;
+}
+
+LLCullResult::bridge_list_t::iterator LLCullResult::beginVisibleBridge()
+{
+ return mVisibleBridge.begin();
+}
+
+LLCullResult::bridge_list_t::iterator LLCullResult::endVisibleBridge()
+{
+ return mVisibleBridgeEnd;
+}
+
+LLCullResult::drawinfo_list_t::iterator LLCullResult::beginRenderMap(U32 type)
+{
+ return mRenderMap[type].begin();
+}
+
+LLCullResult::drawinfo_list_t::iterator LLCullResult::endRenderMap(U32 type)
+{
+ return mRenderMapEnd[type];
+}
+
+void LLCullResult::pushVisibleGroup(LLSpatialGroup* group)
+{
+ if (mVisibleGroupsSize < mVisibleGroups.size())
+ {
+ mVisibleGroups[mVisibleGroupsSize] = group;
+ }
+ else
+ {
+ mVisibleGroups.push_back(group);
+ }
+ ++mVisibleGroupsSize;
+ mVisibleGroupsEnd = mVisibleGroups.begin()+mVisibleGroupsSize;
+}
+
+void LLCullResult::pushAlphaGroup(LLSpatialGroup* group)
+{
+ if (mAlphaGroupsSize < mAlphaGroups.size())
+ {
+ mAlphaGroups[mAlphaGroupsSize] = group;
+ }
+ else
+ {
+ mAlphaGroups.push_back(group);
+ }
+ ++mAlphaGroupsSize;
+ mAlphaGroupsEnd = mAlphaGroups.begin()+mAlphaGroupsSize;
+}
+
+void LLCullResult::pushOcclusionGroup(LLSpatialGroup* group)
+{
+ if (mOcclusionGroupsSize < mOcclusionGroups.size())
+ {
+ mOcclusionGroups[mOcclusionGroupsSize] = group;
+ }
+ else
+ {
+ mOcclusionGroups.push_back(group);
+ }
+ ++mOcclusionGroupsSize;
+ mOcclusionGroupsEnd = mOcclusionGroups.begin()+mOcclusionGroupsSize;
+}
+
+void LLCullResult::pushDrawableGroup(LLSpatialGroup* group)
+{
+ if (mDrawableGroupsSize < mDrawableGroups.size())
+ {
+ mDrawableGroups[mDrawableGroupsSize] = group;
+ }
+ else
+ {
+ mDrawableGroups.push_back(group);
+ }
+ ++mDrawableGroupsSize;
+ mDrawableGroupsEnd = mDrawableGroups.begin()+mDrawableGroupsSize;
+}
+
+void LLCullResult::pushDrawable(LLDrawable* drawable)
+{
+ if (mVisibleListSize < mVisibleList.size())
+ {
+ mVisibleList[mVisibleListSize] = drawable;
+ }
+ else
+ {
+ mVisibleList.push_back(drawable);
+ }
+ ++mVisibleListSize;
+ mVisibleListEnd = mVisibleList.begin()+mVisibleListSize;
+}
+
+void LLCullResult::pushBridge(LLSpatialBridge* bridge)
+{
+ if (mVisibleBridgeSize < mVisibleBridge.size())
+ {
+ mVisibleBridge[mVisibleBridgeSize] = bridge;
+ }
+ else
+ {
+ mVisibleBridge.push_back(bridge);
+ }
+ ++mVisibleBridgeSize;
+ mVisibleBridgeEnd = mVisibleBridge.begin()+mVisibleBridgeSize;
+}
+
+void LLCullResult::pushDrawInfo(U32 type, LLDrawInfo* draw_info)
+{
+ if (mRenderMapSize[type] < mRenderMap[type].size())
+ {
+ mRenderMap[type][mRenderMapSize[type]] = draw_info;
+ }
+ else
+ {
+ mRenderMap[type].push_back(draw_info);
+ }
+ ++mRenderMapSize[type];
+ mRenderMapEnd[type] = mRenderMap[type].begin() + mRenderMapSize[type];
+}
+
+
+void LLCullResult::assertDrawMapsEmpty()
+{
+ for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; i++)
+ {
+ if (mRenderMapSize[i] != 0)
+ {
+ llerrs << "Stale LLDrawInfo's in LLCullResult!" << llendl;
+ }
+ }
+}
+
+
+
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
index 66df7dae3e..65393cc168 100644
--- a/indra/newview/llsurface.cpp
+++ b/indra/newview/llsurface.cpp
@@ -346,6 +346,19 @@ void LLSurface::getNeighboringRegions( std::vector<LLViewerRegion*>& uniqueRegio
}
}
+
+void LLSurface::getNeighboringRegionsStatus( std::vector<S32>& regions )
+{
+ S32 i;
+ for (i = 0; i < 8; i++)
+ {
+ if ( mNeighbors[i] != NULL )
+ {
+ regions.push_back( i );
+ }
+ }
+}
+
void LLSurface::connectNeighbor(LLSurface *neighborp, U32 direction)
{
S32 i;
diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h
index a4ef4fe2de..8052fb0d18 100644
--- a/indra/newview/llsurface.h
+++ b/indra/newview/llsurface.h
@@ -142,6 +142,7 @@ public:
friend std::ostream& operator<<(std::ostream &s, const LLSurface &S);
void getNeighboringRegions( std::vector<LLViewerRegion*>& uniqueRegions );
+ void getNeighboringRegionsStatus( std::vector<S32>& regions );
public:
// Number of grid points on one side of a region, including +1 buffer for
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index cb40af7061..719f560101 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -77,6 +77,9 @@
#include "llwlparammanager.h"
#include "llwaterparammanager.h"
#include "llpostprocess.h"
+#include "LLPathingLib.h"
+#include "llfloaterpathfindingsetup.h"
+#include "llfloaterreg.h"
extern LLPointer<LLViewerTexture> gStartTexture;
@@ -880,7 +883,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
}
LLAppViewer::instance()->pingMainloopTimeout("Display:RenderGeom");
-
+ bool exclusiveDraw = false;
+ BOOL allowRenderables = false;
if (!(LLAppViewer::instance()->logoutRequestSent() && LLAppViewer::instance()->hasSavedFinalSnapshot())
&& !gRestoreGL)
{
@@ -893,9 +897,41 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
}
else
{
- gPipeline.renderGeom(*LLViewerCamera::getInstance(), TRUE);
+ //Render any navmesh geometry
+ LLPathingLib *llPathingLibInstance = LLPathingLib::getInstance();
+ if ( llPathingLibInstance != NULL )
+ {
+ //Determine if we can should overlay the navmesh ontop of the scenes typical renderables
+ allowRenderables = llPathingLibInstance->getRenderOverlayMode();
+
+ //NavMesh
+ if ( llPathingLibInstance->getRenderNavMeshState() )
+ {
+ glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
+ glEnable(GL_DEPTH_TEST);
+ gGL.setAmbientLightColor( LLColor4::white );
+ llPathingLibInstance->renderNavMesh();
+ exclusiveDraw = true;
+ }
+ //physics/exclusion shapes
+ if ( llPathingLibInstance->getRenderShapesState() )
+ {
+ llPathingLibInstance->renderNavMeshShapesVBO();
+ exclusiveDraw = true;
+ }
+ //User designated path
+ if ( llPathingLibInstance->getRenderPathState() )
+ {
+ llPathingLibInstance->renderPath();
+ }
+ }
}
+ if ( !exclusiveDraw || allowRenderables )
+ {
+ gPipeline.renderGeom(*LLViewerCamera::getInstance(), TRUE);
+ }
+
gGL.setColorMask(true, true);
//store this frame's modelview matrix for use
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index bb870f7651..84a0a8c6c4 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -84,6 +84,9 @@
#include "llfloaterobjectweights.h"
#include "llfloateropenobject.h"
#include "llfloateroutbox.h"
+#include "llfloaterpathfindingcharacters.h"
+#include "llfloaterpathfindinglinksets.h"
+#include "llfloaterpathfindingsetup.h"
#include "llfloaterpay.h"
#include "llfloaterperms.h"
#include "llfloaterpostprocess.h"
@@ -243,6 +246,9 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("outgoing_call", "floater_outgoing_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLOutgoingCallDialog>);
LLFloaterPayUtil::registerFloater();
+ LLFloaterReg::add("pathfinding_characters", "floater_pathfinding_characters.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPathfindingCharacters>);
+ LLFloaterReg::add("pathfinding_linksets", "floater_pathfinding_linksets.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPathfindingLinksets>);
+ LLFloaterReg::add("pathfinding_setup", "floater_pathfinding_setup.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPathfindingSetup>);
LLFloaterReg::add("people", "floater_people.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
LLFloaterReg::add("places", "floater_places.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreference>);
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 7e830e14bf..99d08087d4 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -1,1272 +1,1272 @@
-/**
- * @file llviewermenufile.cpp
- * @brief "File" menu in the main menu bar.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llviewermenufile.h"
-
-// project includes
-#include "llagent.h"
-#include "llagentcamera.h"
-#include "llfilepicker.h"
-#include "llfloaterreg.h"
-#include "llbuycurrencyhtml.h"
-#include "llfloatermodelpreview.h"
-#include "llfloatersnapshot.h"
-#include "llimage.h"
-#include "llimagebmp.h"
-#include "llimagepng.h"
-#include "llimagej2c.h"
-#include "llimagejpeg.h"
-#include "llimagetga.h"
-#include "llinventorymodel.h" // gInventory
-#include "llresourcedata.h"
-#include "llfloaterperms.h"
-#include "llstatusbar.h"
-#include "llviewercontrol.h" // gSavedSettings
-#include "llviewertexturelist.h"
-#include "lluictrlfactory.h"
-#include "llvfile.h"
-#include "llvfs.h"
-#include "llviewerinventory.h"
-#include "llviewermenu.h" // gMenuHolder
-#include "llviewerparcelmgr.h"
-#include "llviewerregion.h"
-#include "llviewerstats.h"
-#include "llviewerwindow.h"
-#include "llappviewer.h"
-#include "lluploaddialog.h"
-#include "lltrans.h"
-#include "llfloaterbuycurrency.h"
-
-// linden libraries
-#include "llassetuploadresponders.h"
-#include "lleconomy.h"
-#include "llhttpclient.h"
-#include "llnotificationsutil.h"
-#include "llsdserialize.h"
-#include "llsdutil.h"
-#include "llstring.h"
-#include "lltransactiontypes.h"
-#include "lluuid.h"
-#include "llvorbisencode.h"
-#include "message.h"
-
-// system libraries
-#include <boost/tokenizer.hpp>
-
-class LLFileEnableUpload : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- bool new_value = gStatusBar && LLGlobalEconomy::Singleton::getInstance() && (gStatusBar->getBalance() >= LLGlobalEconomy::Singleton::getInstance()->getPriceUpload());
- return new_value;
- }
-};
-
-class LLFileEnableUploadModel : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- return true;
- }
-};
-
-class LLMeshEnabled : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- return gSavedSettings.getBOOL("MeshEnabled");
- }
-};
-
-class LLMeshUploadVisible : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- return gMeshRepo.meshUploadEnabled();
- }
-};
-
-LLMutex* LLFilePickerThread::sMutex = NULL;
-std::queue<LLFilePickerThread*> LLFilePickerThread::sDeadQ;
-
-void LLFilePickerThread::getFile()
-{
-#if LL_WINDOWS
- start();
-#else
- run();
-#endif
-}
-
-//virtual
-void LLFilePickerThread::run()
-{
- LLFilePicker picker;
-#if LL_WINDOWS
- if (picker.getOpenFile(mFilter, false))
- {
- mFile = picker.getFirstFile();
- }
-#else
- if (picker.getOpenFile(mFilter, true))
- {
- mFile = picker.getFirstFile();
- }
-#endif
-
- {
- LLMutexLock lock(sMutex);
- sDeadQ.push(this);
- }
-
-}
-
-//static
-void LLFilePickerThread::initClass()
-{
- sMutex = new LLMutex(NULL);
-}
-
-//static
-void LLFilePickerThread::cleanupClass()
-{
- clearDead();
-
- delete sMutex;
- sMutex = NULL;
-}
-
-//static
-void LLFilePickerThread::clearDead()
-{
- if (!sDeadQ.empty())
- {
- LLMutexLock lock(sMutex);
- while (!sDeadQ.empty())
- {
- LLFilePickerThread* thread = sDeadQ.front();
- thread->notify(thread->mFile);
- delete thread;
- sDeadQ.pop();
- }
- }
-}
-
-
-//============================================================================
-
-#if LL_WINDOWS
-static std::string SOUND_EXTENSIONS = "wav";
-static std::string IMAGE_EXTENSIONS = "tga bmp jpg jpeg png";
-static std::string ANIM_EXTENSIONS = "bvh";
-#ifdef _CORY_TESTING
-static std::string GEOMETRY_EXTENSIONS = "slg";
-#endif
-static std::string XML_EXTENSIONS = "xml";
-static std::string SLOBJECT_EXTENSIONS = "slobject";
-#endif
-static std::string ALL_FILE_EXTENSIONS = "*.*";
-static std::string MODEL_EXTENSIONS = "dae";
-
-std::string build_extensions_string(LLFilePicker::ELoadFilter filter)
-{
- switch(filter)
- {
-#if LL_WINDOWS
- case LLFilePicker::FFLOAD_IMAGE:
- return IMAGE_EXTENSIONS;
- case LLFilePicker::FFLOAD_WAV:
- return SOUND_EXTENSIONS;
- case LLFilePicker::FFLOAD_ANIM:
- return ANIM_EXTENSIONS;
- case LLFilePicker::FFLOAD_SLOBJECT:
- return SLOBJECT_EXTENSIONS;
- case LLFilePicker::FFLOAD_MODEL:
- return MODEL_EXTENSIONS;
-#ifdef _CORY_TESTING
- case LLFilePicker::FFLOAD_GEOMETRY:
- return GEOMETRY_EXTENSIONS;
-#endif
- case LLFilePicker::FFLOAD_XML:
- return XML_EXTENSIONS;
- case LLFilePicker::FFLOAD_ALL:
- return ALL_FILE_EXTENSIONS;
-#endif
- default:
- return ALL_FILE_EXTENSIONS;
- }
-}
-
-/**
- char* upload_pick(void* data)
-
- If applicable, brings up a file chooser in which the user selects a file
- to upload for a particular task. If the file is valid for the given action,
- returns the string to the full path filename, else returns NULL.
- Data is the load filter for the type of file as defined in LLFilePicker.
-**/
-const std::string upload_pick(void* data)
-{
- if( gAgentCamera.cameraMouselook() )
- {
- gAgentCamera.changeCameraToDefault();
- // This doesn't seem necessary. JC
- // display();
- }
-
- LLFilePicker::ELoadFilter type;
- if(data)
- {
- type = (LLFilePicker::ELoadFilter)((intptr_t)data);
- }
- else
- {
- type = LLFilePicker::FFLOAD_ALL;
- }
-
- LLFilePicker& picker = LLFilePicker::instance();
- if (!picker.getOpenFile(type))
- {
- llinfos << "Couldn't import objects from file" << llendl;
- return std::string();
- }
-
-
- const std::string& filename = picker.getFirstFile();
- std::string ext = gDirUtilp->getExtension(filename);
-
- //strincmp doesn't like NULL pointers
- if (ext.empty())
- {
- std::string short_name = gDirUtilp->getBaseFileName(filename);
-
- // No extension
- LLSD args;
- args["FILE"] = short_name;
- LLNotificationsUtil::add("NoFileExtension", args);
- return std::string();
- }
- else
- {
- //so there is an extension
- //loop over the valid extensions and compare to see
- //if the extension is valid
-
- //now grab the set of valid file extensions
- std::string valid_extensions = build_extensions_string(type);
-
- BOOL ext_valid = FALSE;
-
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep(" ");
- tokenizer tokens(valid_extensions, sep);
- tokenizer::iterator token_iter;
-
- //now loop over all valid file extensions
- //and compare them to the extension of the file
- //to be uploaded
- for( token_iter = tokens.begin();
- token_iter != tokens.end() && ext_valid != TRUE;
- ++token_iter)
- {
- const std::string& cur_token = *token_iter;
-
- if (cur_token == ext || cur_token == "*.*")
- {
- //valid extension
- //or the acceptable extension is any
- ext_valid = TRUE;
- }
- }//end for (loop over all tokens)
-
- if (ext_valid == FALSE)
- {
- //should only get here if the extension exists
- //but is invalid
- LLSD args;
- args["EXTENSION"] = ext;
- args["VALIDS"] = valid_extensions;
- LLNotificationsUtil::add("InvalidFileExtension", args);
- return std::string();
- }
- }//end else (non-null extension)
-
- //valid file extension
-
- //now we check to see
- //if the file is actually a valid image/sound/etc.
- if (type == LLFilePicker::FFLOAD_WAV)
- {
- // pre-qualify wavs to make sure the format is acceptable
- std::string error_msg;
- if (check_for_invalid_wav_formats(filename,error_msg))
- {
- llinfos << error_msg << ": " << filename << llendl;
- LLSD args;
- args["FILE"] = filename;
- LLNotificationsUtil::add( error_msg, args );
- return std::string();
- }
- }//end if a wave/sound file
-
-
- return filename;
-}
-
-class LLFileUploadImage : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- std::string filename = upload_pick((void *)LLFilePicker::FFLOAD_IMAGE);
- if (!filename.empty())
- {
- LLFloaterReg::showInstance("upload_image", LLSD(filename));
- }
- return TRUE;
- }
-};
-
-class LLFileUploadModel : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::getInstance("upload_model");
- if (fmp)
- {
- fmp->loadModel(3);
- }
-
- return TRUE;
- }
-};
-
-class LLFileUploadSound : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- std::string filename = upload_pick((void*)LLFilePicker::FFLOAD_WAV);
- if (!filename.empty())
- {
- LLFloaterReg::showInstance("upload_sound", LLSD(filename));
- }
- return true;
- }
-};
-
-class LLFileUploadAnim : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- const std::string filename = upload_pick((void*)LLFilePicker::FFLOAD_ANIM);
- if (!filename.empty())
- {
- LLFloaterReg::showInstance("upload_anim", LLSD(filename));
- }
- return true;
- }
-};
-
-class LLFileUploadBulk : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- if( gAgentCamera.cameraMouselook() )
- {
- gAgentCamera.changeCameraToDefault();
- }
-
- // TODO:
- // Iterate over all files
- // Check extensions for uploadability, cost
- // Check user balance for entire cost
- // Charge user entire cost
- // Loop, uploading
- // If an upload fails, refund the user for that one
- //
- // Also fix single upload to charge first, then refund
-
- LLFilePicker& picker = LLFilePicker::instance();
- if (picker.getMultipleOpenFiles())
- {
- const std::string& filename = picker.getFirstFile();
- std::string name = gDirUtilp->getBaseFileName(filename, true);
-
- std::string asset_name = name;
- LLStringUtil::replaceNonstandardASCII( asset_name, '?' );
- LLStringUtil::replaceChar(asset_name, '|', '?');
- LLStringUtil::stripNonprintable(asset_name);
- LLStringUtil::trim(asset_name);
-
- std::string display_name = LLStringUtil::null;
- LLAssetStorage::LLStoreAssetCallback callback = NULL;
- S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
- void *userdata = NULL;
-
- upload_new_resource(
- filename,
- asset_name,
- asset_name,
- 0,
- LLFolderType::FT_NONE,
- LLInventoryType::IT_NONE,
- LLFloaterPerms::getNextOwnerPerms(),
- LLFloaterPerms::getGroupPerms(),
- LLFloaterPerms::getEveryonePerms(),
- display_name,
- callback,
- expected_upload_cost,
- userdata);
-
- // *NOTE: Ew, we don't iterate over the file list here,
- // we handle the next files in upload_done_callback()
- }
- else
- {
- llinfos << "Couldn't import objects from file" << llendl;
- }
- return true;
- }
-};
-
-void upload_error(const std::string& error_message, const std::string& label, const std::string& filename, const LLSD& args)
-{
- llwarns << error_message << llendl;
- LLNotificationsUtil::add(label, args);
- if(LLFile::remove(filename) == -1)
- {
- lldebugs << "unable to remove temp file" << llendl;
- }
- LLFilePicker::instance().reset();
-}
-
-class LLFileEnableCloseWindow : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- bool new_value = NULL != LLFloater::getClosableFloaterFromFocus();
- return new_value;
- }
-};
-
-class LLFileCloseWindow : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- LLFloater::closeFocusedFloater();
-
- return true;
- }
-};
-
-class LLFileEnableCloseAllWindows : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- bool open_children = gFloaterView->allChildrenClosed();
- return !open_children;
- }
-};
-
-class LLFileCloseAllWindows : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- bool app_quitting = false;
- gFloaterView->closeAllChildren(app_quitting);
-
- return true;
- }
-};
-
-class LLFileTakeSnapshotToDisk : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- LLPointer<LLImageRaw> raw = new LLImageRaw;
-
- S32 width = gViewerWindow->getWindowWidthRaw();
- S32 height = gViewerWindow->getWindowHeightRaw();
-
- if (gSavedSettings.getBOOL("HighResSnapshot"))
- {
- width *= 2;
- height *= 2;
- }
-
- if (gViewerWindow->rawSnapshot(raw,
- width,
- height,
- TRUE,
- FALSE,
- gSavedSettings.getBOOL("RenderUIInSnapshot"),
- FALSE))
- {
- gViewerWindow->playSnapshotAnimAndSound();
-
- LLPointer<LLImageFormatted> formatted = new LLImagePNG;
- formatted->enableOverSize() ;
- formatted->encode(raw, 0);
- formatted->disableOverSize() ;
- gViewerWindow->saveImageNumbered(formatted);
- }
- return true;
- }
-};
-
-class LLFileQuit : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- LLAppViewer::instance()->userQuit();
- return true;
- }
-};
-
-
-void handle_compress_image(void*)
-{
- LLFilePicker& picker = LLFilePicker::instance();
- if (picker.getMultipleOpenFiles(LLFilePicker::FFLOAD_IMAGE))
- {
- std::string infile = picker.getFirstFile();
- while (!infile.empty())
- {
- std::string outfile = infile + ".j2c";
-
- llinfos << "Input: " << infile << llendl;
- llinfos << "Output: " << outfile << llendl;
-
- BOOL success;
-
- success = LLViewerTextureList::createUploadFile(infile, outfile, IMG_CODEC_TGA);
-
- if (success)
- {
- llinfos << "Compression complete" << llendl;
- }
- else
- {
- llinfos << "Compression failed: " << LLImage::getLastError() << llendl;
- }
-
- infile = picker.getNextFile();
- }
- }
-}
-
-LLUUID upload_new_resource(
- const std::string& src_filename,
- std::string name,
- std::string desc,
- S32 compression_info,
- LLFolderType::EType destination_folder_type,
- LLInventoryType::EType inv_type,
- U32 next_owner_perms,
- U32 group_perms,
- U32 everyone_perms,
- const std::string& display_name,
- LLAssetStorage::LLStoreAssetCallback callback,
- S32 expected_upload_cost,
- void *userdata)
-{
- // Generate the temporary UUID.
- std::string filename = gDirUtilp->getTempFilename();
- LLTransactionID tid;
- LLAssetID uuid;
-
- LLSD args;
-
- std::string exten = gDirUtilp->getExtension(src_filename);
- U32 codec = LLImageBase::getCodecFromExtension(exten);
- LLAssetType::EType asset_type = LLAssetType::AT_NONE;
- std::string error_message;
-
- BOOL error = FALSE;
-
- if (exten.empty())
- {
- std::string short_name = gDirUtilp->getBaseFileName(filename);
-
- // No extension
- error_message = llformat(
- "No file extension for the file: '%s'\nPlease make sure the file has a correct file extension",
- short_name.c_str());
- args["FILE"] = short_name;
- upload_error(error_message, "NoFileExtension", filename, args);
- return LLUUID();
- }
- else if (codec != IMG_CODEC_INVALID)
- {
- // It's an image file, the upload procedure is the same for all
- asset_type = LLAssetType::AT_TEXTURE;
- if (!LLViewerTextureList::createUploadFile(src_filename, filename, codec ))
- {
- error_message = llformat( "Problem with file %s:\n\n%s\n",
- src_filename.c_str(), LLImage::getLastError().c_str());
- args["FILE"] = src_filename;
- args["ERROR"] = LLImage::getLastError();
- upload_error(error_message, "ProblemWithFile", filename, args);
- return LLUUID();
- }
- }
- else if(exten == "wav")
- {
- asset_type = LLAssetType::AT_SOUND; // tag it as audio
- S32 encode_result = 0;
-
- llinfos << "Attempting to encode wav as an ogg file" << llendl;
-
- encode_result = encode_vorbis_file(src_filename, filename);
-
- if (LLVORBISENC_NOERR != encode_result)
- {
- switch(encode_result)
- {
- case LLVORBISENC_DEST_OPEN_ERR:
- error_message = llformat( "Couldn't open temporary compressed sound file for writing: %s\n", filename.c_str());
- args["FILE"] = filename;
- upload_error(error_message, "CannotOpenTemporarySoundFile", filename, args);
- break;
-
- default:
- error_message = llformat("Unknown vorbis encode failure on: %s\n", src_filename.c_str());
- args["FILE"] = src_filename;
- upload_error(error_message, "UnknownVorbisEncodeFailure", filename, args);
- break;
- }
- return LLUUID();
- }
- }
- else if(exten == "tmp")
- {
- // This is a generic .lin resource file
- asset_type = LLAssetType::AT_OBJECT;
- LLFILE* in = LLFile::fopen(src_filename, "rb"); /* Flawfinder: ignore */
- if (in)
- {
- // read in the file header
- char buf[16384]; /* Flawfinder: ignore */
- size_t readbytes;
- S32 version;
- if (fscanf(in, "LindenResource\nversion %d\n", &version))
- {
- if (2 == version)
- {
- // *NOTE: This buffer size is hard coded into scanf() below.
- char label[MAX_STRING]; /* Flawfinder: ignore */
- char value[MAX_STRING]; /* Flawfinder: ignore */
- S32 tokens_read;
- while (fgets(buf, 1024, in))
- {
- label[0] = '\0';
- value[0] = '\0';
- tokens_read = sscanf( /* Flawfinder: ignore */
- buf,
- "%254s %254s\n",
- label, value);
-
- llinfos << "got: " << label << " = " << value
- << llendl;
-
- if (EOF == tokens_read)
- {
- fclose(in);
- error_message = llformat("corrupt resource file: %s", src_filename.c_str());
- args["FILE"] = src_filename;
- upload_error(error_message, "CorruptResourceFile", filename, args);
- return LLUUID();
- }
-
- if (2 == tokens_read)
- {
- if (! strcmp("type", label))
- {
- asset_type = (LLAssetType::EType)(atoi(value));
- }
- }
- else
- {
- if (! strcmp("_DATA_", label))
- {
- // below is the data section
- break;
- }
- }
- // other values are currently discarded
- }
-
- }
- else
- {
- fclose(in);
- error_message = llformat("unknown linden resource file version in file: %s", src_filename.c_str());
- args["FILE"] = src_filename;
- upload_error(error_message, "UnknownResourceFileVersion", filename, args);
- return LLUUID();
- }
- }
- else
- {
- // this is an original binary formatted .lin file
- // start over at the beginning of the file
- fseek(in, 0, SEEK_SET);
-
- const S32 MAX_ASSET_DESCRIPTION_LENGTH = 256;
- const S32 MAX_ASSET_NAME_LENGTH = 64;
- S32 header_size = 34 + MAX_ASSET_DESCRIPTION_LENGTH + MAX_ASSET_NAME_LENGTH;
- S16 type_num;
-
- // read in and throw out most of the header except for the type
- if (fread(buf, header_size, 1, in) != 1)
- {
- llwarns << "Short read" << llendl;
- }
- memcpy(&type_num, buf + 16, sizeof(S16)); /* Flawfinder: ignore */
- asset_type = (LLAssetType::EType)type_num;
- }
-
- // copy the file's data segment into another file for uploading
- LLFILE* out = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */
- if (out)
- {
- while((readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
- {
- if (fwrite(buf, 1, readbytes, out) != readbytes)
- {
- llwarns << "Short write" << llendl;
- }
- }
- fclose(out);
- }
- else
- {
- fclose(in);
- error_message = llformat( "Unable to create output file: %s", filename.c_str());
- args["FILE"] = filename;
- upload_error(error_message, "UnableToCreateOutputFile", filename, args);
- return LLUUID();
- }
-
- fclose(in);
- }
- else
- {
- llinfos << "Couldn't open .lin file " << src_filename << llendl;
- }
- }
- else if (exten == "bvh")
- {
- error_message = llformat("We do not currently support bulk upload of animation files\n");
- upload_error(error_message, "DoNotSupportBulkAnimationUpload", filename, args);
- return LLUUID();
- }
- else
- {
- // Unknown extension
- error_message = llformat(LLTrans::getString("UnknownFileExtension").c_str(), exten.c_str());
- error = TRUE;;
- }
-
- // gen a new transaction ID for this asset
- tid.generate();
-
- if (!error)
- {
- uuid = tid.makeAssetID(gAgent.getSecureSessionID());
- // copy this file into the vfs for upload
- S32 file_size;
- LLAPRFile infile ;
- infile.open(filename, LL_APR_RB, NULL, &file_size);
- if (infile.getFileHandle())
- {
- LLVFile file(gVFS, uuid, asset_type, LLVFile::WRITE);
-
- file.setMaxSize(file_size);
-
- const S32 buf_size = 65536;
- U8 copy_buf[buf_size];
- while ((file_size = infile.read(copy_buf, buf_size)))
- {
- file.write(copy_buf, file_size);
- }
- }
- else
- {
- error_message = llformat( "Unable to access output file: %s", filename.c_str());
- error = TRUE;
- }
- }
-
- if (!error)
- {
- std::string t_disp_name = display_name;
- if (t_disp_name.empty())
- {
- t_disp_name = src_filename;
- }
- upload_new_resource(
- tid,
- asset_type,
- name,
- desc,
- compression_info, // tid
- destination_folder_type,
- inv_type,
- next_owner_perms,
- group_perms,
- everyone_perms,
- display_name,
- callback,
- expected_upload_cost,
- userdata);
- }
- else
- {
- llwarns << error_message << llendl;
- LLSD args;
- args["ERROR_MESSAGE"] = error_message;
- LLNotificationsUtil::add("ErrorMessage", args);
- if(LLFile::remove(filename) == -1)
- {
- lldebugs << "unable to remove temp file" << llendl;
- }
- LLFilePicker::instance().reset();
- }
-
- return uuid;
-}
-
-void upload_done_callback(
- const LLUUID& uuid,
- void* user_data,
- S32 result,
- LLExtStat ext_status) // StoreAssetData callback (fixed)
-{
- LLResourceData* data = (LLResourceData*)user_data;
- S32 expected_upload_cost = data ? data->mExpectedUploadCost : 0;
- //LLAssetType::EType pref_loc = data->mPreferredLocation;
- BOOL is_balance_sufficient = TRUE;
-
- if(data)
- {
- if (result >= 0)
- {
- LLFolderType::EType dest_loc = (data->mPreferredLocation == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(data->mAssetInfo.mType) : data->mPreferredLocation;
-
- if (LLAssetType::AT_SOUND == data->mAssetInfo.mType ||
- LLAssetType::AT_TEXTURE == data->mAssetInfo.mType ||
- LLAssetType::AT_ANIMATION == data->mAssetInfo.mType)
- {
- // Charge the user for the upload.
- LLViewerRegion* region = gAgent.getRegion();
-
- if(!(can_afford_transaction(expected_upload_cost)))
- {
- LLStringUtil::format_map_t args;
- args["NAME"] = data->mAssetInfo.getName();
- args["AMOUNT"] = llformat("%d", expected_upload_cost);
- LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("UploadingCosts", args), expected_upload_cost );
- is_balance_sufficient = FALSE;
- }
- else if(region)
- {
- // Charge user for upload
- gStatusBar->debitBalance(expected_upload_cost);
-
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_MoneyTransferRequest);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_MoneyData);
- msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_DestID, LLUUID::null);
- msg->addU8("Flags", 0);
- // we tell the sim how much we were expecting to pay so it
- // can respond to any discrepancy
- msg->addS32Fast(_PREHASH_Amount, expected_upload_cost);
- msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY);
- msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY);
- msg->addS32Fast(_PREHASH_TransactionType, TRANS_UPLOAD_CHARGE);
- msg->addStringFast(_PREHASH_Description, NULL);
- msg->sendReliable(region->getHost());
- }
- }
-
- if(is_balance_sufficient)
- {
- // Actually add the upload to inventory
- llinfos << "Adding " << uuid << " to inventory." << llendl;
- const LLUUID folder_id = gInventory.findCategoryUUIDForType(dest_loc);
- if(folder_id.notNull())
- {
- U32 next_owner_perms = data->mNextOwnerPerm;
- if(PERM_NONE == next_owner_perms)
- {
- next_owner_perms = PERM_MOVE | PERM_TRANSFER;
- }
- create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
- folder_id, data->mAssetInfo.mTransactionID, data->mAssetInfo.getName(),
- data->mAssetInfo.getDescription(), data->mAssetInfo.mType,
- data->mInventoryType, NOT_WEARABLE, next_owner_perms,
- LLPointer<LLInventoryCallback>(NULL));
- }
- else
- {
- llwarns << "Can't find a folder to put it in" << llendl;
- }
- }
- }
- else // if(result >= 0)
- {
- LLSD args;
- args["FILE"] = LLInventoryType::lookupHumanReadable(data->mInventoryType);
- args["REASON"] = std::string(LLAssetStorage::getErrorString(result));
- LLNotificationsUtil::add("CannotUploadReason", args);
- }
- }
-
- LLUploadDialog::modalUploadFinished();
- delete data;
- data = NULL;
-
- // *NOTE: This is a pretty big hack. What this does is check the
- // file picker if there are any more pending uploads. If so,
- // upload that file.
- const std::string& next_file = LLFilePicker::instance().getNextFile();
- if(is_balance_sufficient && !next_file.empty())
- {
- std::string asset_name = gDirUtilp->getBaseFileName(next_file, true);
- LLStringUtil::replaceNonstandardASCII( asset_name, '?' );
- LLStringUtil::replaceChar(asset_name, '|', '?');
- LLStringUtil::stripNonprintable(asset_name);
- LLStringUtil::trim(asset_name);
-
- std::string display_name = LLStringUtil::null;
- LLAssetStorage::LLStoreAssetCallback callback = NULL;
- void *userdata = NULL;
- upload_new_resource(
- next_file,
- asset_name,
- asset_name, // file
- 0,
- LLFolderType::FT_NONE,
- LLInventoryType::IT_NONE,
- PERM_NONE,
- PERM_NONE,
- PERM_NONE,
- display_name,
- callback,
- expected_upload_cost, // assuming next in a group of uploads is of roughly the same type, i.e. same upload cost
- userdata);
- }
-}
-
-static LLAssetID upload_new_resource_prep(
- const LLTransactionID& tid,
- LLAssetType::EType asset_type,
- LLInventoryType::EType& inventory_type,
- std::string& name,
- const std::string& display_name,
- std::string& description)
-{
- LLAssetID uuid = generate_asset_id_for_new_upload(tid);
-
- increase_new_upload_stats(asset_type);
-
- assign_defaults_and_show_upload_message(
- asset_type,
- inventory_type,
- name,
- display_name,
- description);
-
- return uuid;
-}
-
-LLSD generate_new_resource_upload_capability_body(
- LLAssetType::EType asset_type,
- const std::string& name,
- const std::string& desc,
- LLFolderType::EType destination_folder_type,
- LLInventoryType::EType inv_type,
- U32 next_owner_perms,
- U32 group_perms,
- U32 everyone_perms)
-{
- LLSD body;
-
- body["folder_id"] = gInventory.findCategoryUUIDForType(
- (destination_folder_type == LLFolderType::FT_NONE) ?
- (LLFolderType::EType) asset_type :
- destination_folder_type);
-
- body["asset_type"] = LLAssetType::lookup(asset_type);
- body["inventory_type"] = LLInventoryType::lookup(inv_type);
- body["name"] = name;
- body["description"] = desc;
- body["next_owner_mask"] = LLSD::Integer(next_owner_perms);
- body["group_mask"] = LLSD::Integer(group_perms);
- body["everyone_mask"] = LLSD::Integer(everyone_perms);
-
- return body;
-}
-
-void upload_new_resource(
- const LLTransactionID &tid,
- LLAssetType::EType asset_type,
- std::string name,
- std::string desc,
- S32 compression_info,
- LLFolderType::EType destination_folder_type,
- LLInventoryType::EType inv_type,
- U32 next_owner_perms,
- U32 group_perms,
- U32 everyone_perms,
- const std::string& display_name,
- LLAssetStorage::LLStoreAssetCallback callback,
- S32 expected_upload_cost,
- void *userdata)
-{
- if(gDisconnected)
- {
- return ;
- }
-
- LLAssetID uuid =
- upload_new_resource_prep(
- tid,
- asset_type,
- inv_type,
- name,
- display_name,
- desc);
-
- if( LLAssetType::AT_SOUND == asset_type )
- {
- LLViewerStats::getInstance()->incStat(LLViewerStats::ST_UPLOAD_SOUND_COUNT );
- }
- else
- if( LLAssetType::AT_TEXTURE == asset_type )
- {
- LLViewerStats::getInstance()->incStat(LLViewerStats::ST_UPLOAD_TEXTURE_COUNT );
- }
- else
- if( LLAssetType::AT_ANIMATION == asset_type)
- {
- LLViewerStats::getInstance()->incStat(LLViewerStats::ST_UPLOAD_ANIM_COUNT );
- }
-
- if(LLInventoryType::IT_NONE == inv_type)
- {
- inv_type = LLInventoryType::defaultForAssetType(asset_type);
- }
- LLStringUtil::stripNonprintable(name);
- LLStringUtil::stripNonprintable(desc);
- if(name.empty())
- {
- name = "(No Name)";
- }
- if(desc.empty())
- {
- desc = "(No Description)";
- }
-
- // At this point, we're ready for the upload.
- std::string upload_message = "Uploading...\n\n";
- upload_message.append(display_name);
- LLUploadDialog::modalUploadDialog(upload_message);
-
- llinfos << "*** Uploading: " << llendl;
- llinfos << "Type: " << LLAssetType::lookup(asset_type) << llendl;
- llinfos << "UUID: " << uuid << llendl;
- llinfos << "Name: " << name << llendl;
- llinfos << "Desc: " << desc << llendl;
- llinfos << "Expected Upload Cost: " << expected_upload_cost << llendl;
- lldebugs << "Folder: " << gInventory.findCategoryUUIDForType((destination_folder_type == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(asset_type) : destination_folder_type) << llendl;
- lldebugs << "Asset Type: " << LLAssetType::lookup(asset_type) << llendl;
-
- std::string url = gAgent.getRegion()->getCapability(
- "NewFileAgentInventory");
-
- if ( !url.empty() )
- {
- llinfos << "New Agent Inventory via capability" << llendl;
-
- LLSD body;
- body = generate_new_resource_upload_capability_body(
- asset_type,
- name,
- desc,
- destination_folder_type,
- inv_type,
- next_owner_perms,
- group_perms,
- everyone_perms);
-
- LLHTTPClient::post(
- url,
- body,
- new LLNewAgentInventoryResponder(
- body,
- uuid,
- asset_type));
- }
- else
- {
- llinfos << "NewAgentInventory capability not found, new agent inventory via asset system." << llendl;
- // check for adequate funds
- // TODO: do this check on the sim
- if (LLAssetType::AT_SOUND == asset_type ||
- LLAssetType::AT_TEXTURE == asset_type ||
- LLAssetType::AT_ANIMATION == asset_type)
- {
- S32 balance = gStatusBar->getBalance();
- if (balance < expected_upload_cost)
- {
- // insufficient funds, bail on this upload
- LLStringUtil::format_map_t args;
- args["NAME"] = name;
- args["AMOUNT"] = llformat("%d", expected_upload_cost);
- LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("UploadingCosts", args), expected_upload_cost );
- return;
- }
- }
-
- LLResourceData* data = new LLResourceData;
- data->mAssetInfo.mTransactionID = tid;
- data->mAssetInfo.mUuid = uuid;
- data->mAssetInfo.mType = asset_type;
- data->mAssetInfo.mCreatorID = gAgentID;
- data->mInventoryType = inv_type;
- data->mNextOwnerPerm = next_owner_perms;
- data->mExpectedUploadCost = expected_upload_cost;
- data->mUserData = userdata;
- data->mAssetInfo.setName(name);
- data->mAssetInfo.setDescription(desc);
- data->mPreferredLocation = destination_folder_type;
-
- LLAssetStorage::LLStoreAssetCallback asset_callback = &upload_done_callback;
- if (callback)
- {
- asset_callback = callback;
- }
- gAssetStorage->storeAssetData(
- data->mAssetInfo.mTransactionID,
- data->mAssetInfo.mType,
- asset_callback,
- (void*)data,
- FALSE);
- }
-}
-
-LLAssetID generate_asset_id_for_new_upload(const LLTransactionID& tid)
-{
- if ( gDisconnected )
- {
- LLAssetID rv;
-
- rv.setNull();
- return rv;
- }
-
- LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID());
-
- return uuid;
-}
-
-void increase_new_upload_stats(LLAssetType::EType asset_type)
-{
- if ( LLAssetType::AT_SOUND == asset_type )
- {
- LLViewerStats::getInstance()->incStat(
- LLViewerStats::ST_UPLOAD_SOUND_COUNT );
- }
- else if ( LLAssetType::AT_TEXTURE == asset_type )
- {
- LLViewerStats::getInstance()->incStat(
- LLViewerStats::ST_UPLOAD_TEXTURE_COUNT );
- }
- else if ( LLAssetType::AT_ANIMATION == asset_type )
- {
- LLViewerStats::getInstance()->incStat(
- LLViewerStats::ST_UPLOAD_ANIM_COUNT );
- }
-}
-
-void assign_defaults_and_show_upload_message(
- LLAssetType::EType asset_type,
- LLInventoryType::EType& inventory_type,
- std::string& name,
- const std::string& display_name,
- std::string& description)
-{
- if ( LLInventoryType::IT_NONE == inventory_type )
- {
- inventory_type = LLInventoryType::defaultForAssetType(asset_type);
- }
- LLStringUtil::stripNonprintable(name);
- LLStringUtil::stripNonprintable(description);
-
- if ( name.empty() )
- {
- name = "(No Name)";
- }
- if ( description.empty() )
- {
- description = "(No Description)";
- }
-
- // At this point, we're ready for the upload.
- std::string upload_message = "Uploading...\n\n";
- upload_message.append(display_name);
- LLUploadDialog::modalUploadDialog(upload_message);
-}
-
-
-void init_menu_file()
-{
- view_listener_t::addCommit(new LLFileUploadImage(), "File.UploadImage");
- view_listener_t::addCommit(new LLFileUploadSound(), "File.UploadSound");
- view_listener_t::addCommit(new LLFileUploadAnim(), "File.UploadAnim");
- view_listener_t::addCommit(new LLFileUploadModel(), "File.UploadModel");
- view_listener_t::addCommit(new LLFileUploadBulk(), "File.UploadBulk");
- view_listener_t::addCommit(new LLFileCloseWindow(), "File.CloseWindow");
- view_listener_t::addCommit(new LLFileCloseAllWindows(), "File.CloseAllWindows");
- view_listener_t::addEnable(new LLFileEnableCloseWindow(), "File.EnableCloseWindow");
- view_listener_t::addEnable(new LLFileEnableCloseAllWindows(), "File.EnableCloseAllWindows");
- view_listener_t::addCommit(new LLFileTakeSnapshotToDisk(), "File.TakeSnapshotToDisk");
- view_listener_t::addCommit(new LLFileQuit(), "File.Quit");
-
- view_listener_t::addEnable(new LLFileEnableUpload(), "File.EnableUpload");
- view_listener_t::addEnable(new LLFileEnableUploadModel(), "File.EnableUploadModel");
- view_listener_t::addMenu(new LLMeshEnabled(), "File.MeshEnabled");
- view_listener_t::addMenu(new LLMeshUploadVisible(), "File.VisibleUploadModel");
-
- // "File.SaveTexture" moved to llpanelmaininventory so that it can be properly handled.
-}
+/**
+ * @file llviewermenufile.cpp
+ * @brief "File" menu in the main menu bar.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewermenufile.h"
+
+// project includes
+#include "llagent.h"
+#include "llagentcamera.h"
+#include "llfilepicker.h"
+#include "llfloaterreg.h"
+#include "llbuycurrencyhtml.h"
+#include "llfloatermodelpreview.h"
+#include "llfloatersnapshot.h"
+#include "llimage.h"
+#include "llimagebmp.h"
+#include "llimagepng.h"
+#include "llimagej2c.h"
+#include "llimagejpeg.h"
+#include "llimagetga.h"
+#include "llinventorymodel.h" // gInventory
+#include "llresourcedata.h"
+#include "llfloaterperms.h"
+#include "llstatusbar.h"
+#include "llviewercontrol.h" // gSavedSettings
+#include "llviewertexturelist.h"
+#include "lluictrlfactory.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h" // gMenuHolder
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llappviewer.h"
+#include "lluploaddialog.h"
+#include "lltrans.h"
+#include "llfloaterbuycurrency.h"
+
+// linden libraries
+#include "llassetuploadresponders.h"
+#include "lleconomy.h"
+#include "llhttpclient.h"
+#include "llnotificationsutil.h"
+#include "llsdserialize.h"
+#include "llsdutil.h"
+#include "llstring.h"
+#include "lltransactiontypes.h"
+#include "lluuid.h"
+#include "llvorbisencode.h"
+#include "message.h"
+
+// system libraries
+#include <boost/tokenizer.hpp>
+
+class LLFileEnableUpload : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ bool new_value = gStatusBar && LLGlobalEconomy::Singleton::getInstance() && (gStatusBar->getBalance() >= LLGlobalEconomy::Singleton::getInstance()->getPriceUpload());
+ return new_value;
+ }
+};
+
+class LLFileEnableUploadModel : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ return true;
+ }
+};
+
+class LLMeshEnabled : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ return gSavedSettings.getBOOL("MeshEnabled");
+ }
+};
+
+class LLMeshUploadVisible : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ return gMeshRepo.meshUploadEnabled();
+ }
+};
+
+LLMutex* LLFilePickerThread::sMutex = NULL;
+std::queue<LLFilePickerThread*> LLFilePickerThread::sDeadQ;
+
+void LLFilePickerThread::getFile()
+{
+#if LL_WINDOWS
+ start();
+#else
+ run();
+#endif
+}
+
+//virtual
+void LLFilePickerThread::run()
+{
+ LLFilePicker picker;
+#if LL_WINDOWS
+ if (picker.getOpenFile(mFilter, false))
+ {
+ mFile = picker.getFirstFile();
+ }
+#else
+ if (picker.getOpenFile(mFilter, true))
+ {
+ mFile = picker.getFirstFile();
+ }
+#endif
+
+ {
+ LLMutexLock lock(sMutex);
+ sDeadQ.push(this);
+ }
+
+}
+
+//static
+void LLFilePickerThread::initClass()
+{
+ sMutex = new LLMutex(NULL);
+}
+
+//static
+void LLFilePickerThread::cleanupClass()
+{
+ clearDead();
+
+ delete sMutex;
+ sMutex = NULL;
+}
+
+//static
+void LLFilePickerThread::clearDead()
+{
+ if (!sDeadQ.empty())
+ {
+ LLMutexLock lock(sMutex);
+ while (!sDeadQ.empty())
+ {
+ LLFilePickerThread* thread = sDeadQ.front();
+ thread->notify(thread->mFile);
+ delete thread;
+ sDeadQ.pop();
+ }
+ }
+}
+
+
+//============================================================================
+
+#if LL_WINDOWS
+static std::string SOUND_EXTENSIONS = "wav";
+static std::string IMAGE_EXTENSIONS = "tga bmp jpg jpeg png";
+static std::string ANIM_EXTENSIONS = "bvh";
+#ifdef _CORY_TESTING
+static std::string GEOMETRY_EXTENSIONS = "slg";
+#endif
+static std::string XML_EXTENSIONS = "xml";
+static std::string SLOBJECT_EXTENSIONS = "slobject";
+#endif
+static std::string ALL_FILE_EXTENSIONS = "*.*";
+static std::string MODEL_EXTENSIONS = "dae";
+
+std::string build_extensions_string(LLFilePicker::ELoadFilter filter)
+{
+ switch(filter)
+ {
+#if LL_WINDOWS
+ case LLFilePicker::FFLOAD_IMAGE:
+ return IMAGE_EXTENSIONS;
+ case LLFilePicker::FFLOAD_WAV:
+ return SOUND_EXTENSIONS;
+ case LLFilePicker::FFLOAD_ANIM:
+ return ANIM_EXTENSIONS;
+ case LLFilePicker::FFLOAD_SLOBJECT:
+ return SLOBJECT_EXTENSIONS;
+ case LLFilePicker::FFLOAD_MODEL:
+ return MODEL_EXTENSIONS;
+#ifdef _CORY_TESTING
+ case LLFilePicker::FFLOAD_GEOMETRY:
+ return GEOMETRY_EXTENSIONS;
+#endif
+ case LLFilePicker::FFLOAD_XML:
+ return XML_EXTENSIONS;
+ case LLFilePicker::FFLOAD_ALL:
+ return ALL_FILE_EXTENSIONS;
+#endif
+ default:
+ return ALL_FILE_EXTENSIONS;
+ }
+}
+
+/**
+ char* upload_pick(void* data)
+
+ If applicable, brings up a file chooser in which the user selects a file
+ to upload for a particular task. If the file is valid for the given action,
+ returns the string to the full path filename, else returns NULL.
+ Data is the load filter for the type of file as defined in LLFilePicker.
+**/
+const std::string upload_pick(void* data)
+{
+ if( gAgentCamera.cameraMouselook() )
+ {
+ gAgentCamera.changeCameraToDefault();
+ // This doesn't seem necessary. JC
+ // display();
+ }
+
+ LLFilePicker::ELoadFilter type;
+ if(data)
+ {
+ type = (LLFilePicker::ELoadFilter)((intptr_t)data);
+ }
+ else
+ {
+ type = LLFilePicker::FFLOAD_ALL;
+ }
+
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getOpenFile(type))
+ {
+ llinfos << "Couldn't import objects from file" << llendl;
+ return std::string();
+ }
+
+
+ const std::string& filename = picker.getFirstFile();
+ std::string ext = gDirUtilp->getExtension(filename);
+
+ //strincmp doesn't like NULL pointers
+ if (ext.empty())
+ {
+ std::string short_name = gDirUtilp->getBaseFileName(filename);
+
+ // No extension
+ LLSD args;
+ args["FILE"] = short_name;
+ LLNotificationsUtil::add("NoFileExtension", args);
+ return std::string();
+ }
+ else
+ {
+ //so there is an extension
+ //loop over the valid extensions and compare to see
+ //if the extension is valid
+
+ //now grab the set of valid file extensions
+ std::string valid_extensions = build_extensions_string(type);
+
+ BOOL ext_valid = FALSE;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(valid_extensions, sep);
+ tokenizer::iterator token_iter;
+
+ //now loop over all valid file extensions
+ //and compare them to the extension of the file
+ //to be uploaded
+ for( token_iter = tokens.begin();
+ token_iter != tokens.end() && ext_valid != TRUE;
+ ++token_iter)
+ {
+ const std::string& cur_token = *token_iter;
+
+ if (cur_token == ext || cur_token == "*.*")
+ {
+ //valid extension
+ //or the acceptable extension is any
+ ext_valid = TRUE;
+ }
+ }//end for (loop over all tokens)
+
+ if (ext_valid == FALSE)
+ {
+ //should only get here if the extension exists
+ //but is invalid
+ LLSD args;
+ args["EXTENSION"] = ext;
+ args["VALIDS"] = valid_extensions;
+ LLNotificationsUtil::add("InvalidFileExtension", args);
+ return std::string();
+ }
+ }//end else (non-null extension)
+
+ //valid file extension
+
+ //now we check to see
+ //if the file is actually a valid image/sound/etc.
+ if (type == LLFilePicker::FFLOAD_WAV)
+ {
+ // pre-qualify wavs to make sure the format is acceptable
+ std::string error_msg;
+ if (check_for_invalid_wav_formats(filename,error_msg))
+ {
+ llinfos << error_msg << ": " << filename << llendl;
+ LLSD args;
+ args["FILE"] = filename;
+ LLNotificationsUtil::add( error_msg, args );
+ return std::string();
+ }
+ }//end if a wave/sound file
+
+
+ return filename;
+}
+
+class LLFileUploadImage : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ std::string filename = upload_pick((void *)LLFilePicker::FFLOAD_IMAGE);
+ if (!filename.empty())
+ {
+ LLFloaterReg::showInstance("upload_image", LLSD(filename));
+ }
+ return TRUE;
+ }
+};
+
+class LLFileUploadModel : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::getInstance("upload_model");
+ if (fmp)
+ {
+ fmp->loadModel(3);
+ }
+
+ return TRUE;
+ }
+};
+
+class LLFileUploadSound : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ std::string filename = upload_pick((void*)LLFilePicker::FFLOAD_WAV);
+ if (!filename.empty())
+ {
+ LLFloaterReg::showInstance("upload_sound", LLSD(filename));
+ }
+ return true;
+ }
+};
+
+class LLFileUploadAnim : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ const std::string filename = upload_pick((void*)LLFilePicker::FFLOAD_ANIM);
+ if (!filename.empty())
+ {
+ LLFloaterReg::showInstance("upload_anim", LLSD(filename));
+ }
+ return true;
+ }
+};
+
+class LLFileUploadBulk : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ if( gAgentCamera.cameraMouselook() )
+ {
+ gAgentCamera.changeCameraToDefault();
+ }
+
+ // TODO:
+ // Iterate over all files
+ // Check extensions for uploadability, cost
+ // Check user balance for entire cost
+ // Charge user entire cost
+ // Loop, uploading
+ // If an upload fails, refund the user for that one
+ //
+ // Also fix single upload to charge first, then refund
+
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getMultipleOpenFiles())
+ {
+ const std::string& filename = picker.getFirstFile();
+ std::string name = gDirUtilp->getBaseFileName(filename, true);
+
+ std::string asset_name = name;
+ LLStringUtil::replaceNonstandardASCII( asset_name, '?' );
+ LLStringUtil::replaceChar(asset_name, '|', '?');
+ LLStringUtil::stripNonprintable(asset_name);
+ LLStringUtil::trim(asset_name);
+
+ std::string display_name = LLStringUtil::null;
+ LLAssetStorage::LLStoreAssetCallback callback = NULL;
+ S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
+ void *userdata = NULL;
+
+ upload_new_resource(
+ filename,
+ asset_name,
+ asset_name,
+ 0,
+ LLFolderType::FT_NONE,
+ LLInventoryType::IT_NONE,
+ LLFloaterPerms::getNextOwnerPerms(),
+ LLFloaterPerms::getGroupPerms(),
+ LLFloaterPerms::getEveryonePerms(),
+ display_name,
+ callback,
+ expected_upload_cost,
+ userdata);
+
+ // *NOTE: Ew, we don't iterate over the file list here,
+ // we handle the next files in upload_done_callback()
+ }
+ else
+ {
+ llinfos << "Couldn't import objects from file" << llendl;
+ }
+ return true;
+ }
+};
+
+void upload_error(const std::string& error_message, const std::string& label, const std::string& filename, const LLSD& args)
+{
+ llwarns << error_message << llendl;
+ LLNotificationsUtil::add(label, args);
+ if(LLFile::remove(filename) == -1)
+ {
+ lldebugs << "unable to remove temp file" << llendl;
+ }
+ LLFilePicker::instance().reset();
+}
+
+class LLFileEnableCloseWindow : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ bool new_value = NULL != LLFloater::getClosableFloaterFromFocus();
+ return new_value;
+ }
+};
+
+class LLFileCloseWindow : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LLFloater::closeFocusedFloater();
+
+ return true;
+ }
+};
+
+class LLFileEnableCloseAllWindows : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ bool open_children = gFloaterView->allChildrenClosed();
+ return !open_children;
+ }
+};
+
+class LLFileCloseAllWindows : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ bool app_quitting = false;
+ gFloaterView->closeAllChildren(app_quitting);
+
+ return true;
+ }
+};
+
+class LLFileTakeSnapshotToDisk : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+
+ S32 width = gViewerWindow->getWindowWidthRaw();
+ S32 height = gViewerWindow->getWindowHeightRaw();
+
+ if (gSavedSettings.getBOOL("HighResSnapshot"))
+ {
+ width *= 2;
+ height *= 2;
+ }
+
+ if (gViewerWindow->rawSnapshot(raw,
+ width,
+ height,
+ TRUE,
+ FALSE,
+ gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE))
+ {
+ gViewerWindow->playSnapshotAnimAndSound();
+
+ LLPointer<LLImageFormatted> formatted = new LLImagePNG;
+ formatted->enableOverSize() ;
+ formatted->encode(raw, 0);
+ formatted->disableOverSize() ;
+ gViewerWindow->saveImageNumbered(formatted);
+ }
+ return true;
+ }
+};
+
+class LLFileQuit : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LLAppViewer::instance()->userQuit();
+ return true;
+ }
+};
+
+
+void handle_compress_image(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getMultipleOpenFiles(LLFilePicker::FFLOAD_IMAGE))
+ {
+ std::string infile = picker.getFirstFile();
+ while (!infile.empty())
+ {
+ std::string outfile = infile + ".j2c";
+
+ llinfos << "Input: " << infile << llendl;
+ llinfos << "Output: " << outfile << llendl;
+
+ BOOL success;
+
+ success = LLViewerTextureList::createUploadFile(infile, outfile, IMG_CODEC_TGA);
+
+ if (success)
+ {
+ llinfos << "Compression complete" << llendl;
+ }
+ else
+ {
+ llinfos << "Compression failed: " << LLImage::getLastError() << llendl;
+ }
+
+ infile = picker.getNextFile();
+ }
+ }
+}
+
+LLUUID upload_new_resource(
+ const std::string& src_filename,
+ std::string name,
+ std::string desc,
+ S32 compression_info,
+ LLFolderType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perms,
+ U32 group_perms,
+ U32 everyone_perms,
+ const std::string& display_name,
+ LLAssetStorage::LLStoreAssetCallback callback,
+ S32 expected_upload_cost,
+ void *userdata)
+{
+ // Generate the temporary UUID.
+ std::string filename = gDirUtilp->getTempFilename();
+ LLTransactionID tid;
+ LLAssetID uuid;
+
+ LLSD args;
+
+ std::string exten = gDirUtilp->getExtension(src_filename);
+ U32 codec = LLImageBase::getCodecFromExtension(exten);
+ LLAssetType::EType asset_type = LLAssetType::AT_NONE;
+ std::string error_message;
+
+ BOOL error = FALSE;
+
+ if (exten.empty())
+ {
+ std::string short_name = gDirUtilp->getBaseFileName(filename);
+
+ // No extension
+ error_message = llformat(
+ "No file extension for the file: '%s'\nPlease make sure the file has a correct file extension",
+ short_name.c_str());
+ args["FILE"] = short_name;
+ upload_error(error_message, "NoFileExtension", filename, args);
+ return LLUUID();
+ }
+ else if (codec != IMG_CODEC_INVALID)
+ {
+ // It's an image file, the upload procedure is the same for all
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerTextureList::createUploadFile(src_filename, filename, codec ))
+ {
+ error_message = llformat( "Problem with file %s:\n\n%s\n",
+ src_filename.c_str(), LLImage::getLastError().c_str());
+ args["FILE"] = src_filename;
+ args["ERROR"] = LLImage::getLastError();
+ upload_error(error_message, "ProblemWithFile", filename, args);
+ return LLUUID();
+ }
+ }
+ else if(exten == "wav")
+ {
+ asset_type = LLAssetType::AT_SOUND; // tag it as audio
+ S32 encode_result = 0;
+
+ llinfos << "Attempting to encode wav as an ogg file" << llendl;
+
+ encode_result = encode_vorbis_file(src_filename, filename);
+
+ if (LLVORBISENC_NOERR != encode_result)
+ {
+ switch(encode_result)
+ {
+ case LLVORBISENC_DEST_OPEN_ERR:
+ error_message = llformat( "Couldn't open temporary compressed sound file for writing: %s\n", filename.c_str());
+ args["FILE"] = filename;
+ upload_error(error_message, "CannotOpenTemporarySoundFile", filename, args);
+ break;
+
+ default:
+ error_message = llformat("Unknown vorbis encode failure on: %s\n", src_filename.c_str());
+ args["FILE"] = src_filename;
+ upload_error(error_message, "UnknownVorbisEncodeFailure", filename, args);
+ break;
+ }
+ return LLUUID();
+ }
+ }
+ else if(exten == "tmp")
+ {
+ // This is a generic .lin resource file
+ asset_type = LLAssetType::AT_OBJECT;
+ LLFILE* in = LLFile::fopen(src_filename, "rb"); /* Flawfinder: ignore */
+ if (in)
+ {
+ // read in the file header
+ char buf[16384]; /* Flawfinder: ignore */
+ size_t readbytes;
+ S32 version;
+ if (fscanf(in, "LindenResource\nversion %d\n", &version))
+ {
+ if (2 == version)
+ {
+ // *NOTE: This buffer size is hard coded into scanf() below.
+ char label[MAX_STRING]; /* Flawfinder: ignore */
+ char value[MAX_STRING]; /* Flawfinder: ignore */
+ S32 tokens_read;
+ while (fgets(buf, 1024, in))
+ {
+ label[0] = '\0';
+ value[0] = '\0';
+ tokens_read = sscanf( /* Flawfinder: ignore */
+ buf,
+ "%254s %254s\n",
+ label, value);
+
+ llinfos << "got: " << label << " = " << value
+ << llendl;
+
+ if (EOF == tokens_read)
+ {
+ fclose(in);
+ error_message = llformat("corrupt resource file: %s", src_filename.c_str());
+ args["FILE"] = src_filename;
+ upload_error(error_message, "CorruptResourceFile", filename, args);
+ return LLUUID();
+ }
+
+ if (2 == tokens_read)
+ {
+ if (! strcmp("type", label))
+ {
+ asset_type = (LLAssetType::EType)(atoi(value));
+ }
+ }
+ else
+ {
+ if (! strcmp("_DATA_", label))
+ {
+ // below is the data section
+ break;
+ }
+ }
+ // other values are currently discarded
+ }
+
+ }
+ else
+ {
+ fclose(in);
+ error_message = llformat("unknown linden resource file version in file: %s", src_filename.c_str());
+ args["FILE"] = src_filename;
+ upload_error(error_message, "UnknownResourceFileVersion", filename, args);
+ return LLUUID();
+ }
+ }
+ else
+ {
+ // this is an original binary formatted .lin file
+ // start over at the beginning of the file
+ fseek(in, 0, SEEK_SET);
+
+ const S32 MAX_ASSET_DESCRIPTION_LENGTH = 256;
+ const S32 MAX_ASSET_NAME_LENGTH = 64;
+ S32 header_size = 34 + MAX_ASSET_DESCRIPTION_LENGTH + MAX_ASSET_NAME_LENGTH;
+ S16 type_num;
+
+ // read in and throw out most of the header except for the type
+ if (fread(buf, header_size, 1, in) != 1)
+ {
+ llwarns << "Short read" << llendl;
+ }
+ memcpy(&type_num, buf + 16, sizeof(S16)); /* Flawfinder: ignore */
+ asset_type = (LLAssetType::EType)type_num;
+ }
+
+ // copy the file's data segment into another file for uploading
+ LLFILE* out = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */
+ if (out)
+ {
+ while((readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
+ {
+ if (fwrite(buf, 1, readbytes, out) != readbytes)
+ {
+ llwarns << "Short write" << llendl;
+ }
+ }
+ fclose(out);
+ }
+ else
+ {
+ fclose(in);
+ error_message = llformat( "Unable to create output file: %s", filename.c_str());
+ args["FILE"] = filename;
+ upload_error(error_message, "UnableToCreateOutputFile", filename, args);
+ return LLUUID();
+ }
+
+ fclose(in);
+ }
+ else
+ {
+ llinfos << "Couldn't open .lin file " << src_filename << llendl;
+ }
+ }
+ else if (exten == "bvh")
+ {
+ error_message = llformat("We do not currently support bulk upload of animation files\n");
+ upload_error(error_message, "DoNotSupportBulkAnimationUpload", filename, args);
+ return LLUUID();
+ }
+ else
+ {
+ // Unknown extension
+ error_message = llformat(LLTrans::getString("UnknownFileExtension").c_str(), exten.c_str());
+ error = TRUE;;
+ }
+
+ // gen a new transaction ID for this asset
+ tid.generate();
+
+ if (!error)
+ {
+ uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+ // copy this file into the vfs for upload
+ S32 file_size;
+ LLAPRFile infile ;
+ infile.open(filename, LL_APR_RB, NULL, &file_size);
+ if (infile.getFileHandle())
+ {
+ LLVFile file(gVFS, uuid, asset_type, LLVFile::WRITE);
+
+ file.setMaxSize(file_size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((file_size = infile.read(copy_buf, buf_size)))
+ {
+ file.write(copy_buf, file_size);
+ }
+ }
+ else
+ {
+ error_message = llformat( "Unable to access output file: %s", filename.c_str());
+ error = TRUE;
+ }
+ }
+
+ if (!error)
+ {
+ std::string t_disp_name = display_name;
+ if (t_disp_name.empty())
+ {
+ t_disp_name = src_filename;
+ }
+ upload_new_resource(
+ tid,
+ asset_type,
+ name,
+ desc,
+ compression_info, // tid
+ destination_folder_type,
+ inv_type,
+ next_owner_perms,
+ group_perms,
+ everyone_perms,
+ display_name,
+ callback,
+ expected_upload_cost,
+ userdata);
+ }
+ else
+ {
+ llwarns << error_message << llendl;
+ LLSD args;
+ args["ERROR_MESSAGE"] = error_message;
+ LLNotificationsUtil::add("ErrorMessage", args);
+ if(LLFile::remove(filename) == -1)
+ {
+ lldebugs << "unable to remove temp file" << llendl;
+ }
+ LLFilePicker::instance().reset();
+ }
+
+ return uuid;
+}
+
+void upload_done_callback(
+ const LLUUID& uuid,
+ void* user_data,
+ S32 result,
+ LLExtStat ext_status) // StoreAssetData callback (fixed)
+{
+ LLResourceData* data = (LLResourceData*)user_data;
+ S32 expected_upload_cost = data ? data->mExpectedUploadCost : 0;
+ //LLAssetType::EType pref_loc = data->mPreferredLocation;
+ BOOL is_balance_sufficient = TRUE;
+
+ if(data)
+ {
+ if (result >= 0)
+ {
+ LLFolderType::EType dest_loc = (data->mPreferredLocation == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(data->mAssetInfo.mType) : data->mPreferredLocation;
+
+ if (LLAssetType::AT_SOUND == data->mAssetInfo.mType ||
+ LLAssetType::AT_TEXTURE == data->mAssetInfo.mType ||
+ LLAssetType::AT_ANIMATION == data->mAssetInfo.mType)
+ {
+ // Charge the user for the upload.
+ LLViewerRegion* region = gAgent.getRegion();
+
+ if(!(can_afford_transaction(expected_upload_cost)))
+ {
+ LLStringUtil::format_map_t args;
+ args["NAME"] = data->mAssetInfo.getName();
+ args["AMOUNT"] = llformat("%d", expected_upload_cost);
+ LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("UploadingCosts", args), expected_upload_cost );
+ is_balance_sufficient = FALSE;
+ }
+ else if(region)
+ {
+ // Charge user for upload
+ gStatusBar->debitBalance(expected_upload_cost);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoneyTransferRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_DestID, LLUUID::null);
+ msg->addU8("Flags", 0);
+ // we tell the sim how much we were expecting to pay so it
+ // can respond to any discrepancy
+ msg->addS32Fast(_PREHASH_Amount, expected_upload_cost);
+ msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addS32Fast(_PREHASH_TransactionType, TRANS_UPLOAD_CHARGE);
+ msg->addStringFast(_PREHASH_Description, NULL);
+ msg->sendReliable(region->getHost());
+ }
+ }
+
+ if(is_balance_sufficient)
+ {
+ // Actually add the upload to inventory
+ llinfos << "Adding " << uuid << " to inventory." << llendl;
+ const LLUUID folder_id = gInventory.findCategoryUUIDForType(dest_loc);
+ if(folder_id.notNull())
+ {
+ U32 next_owner_perms = data->mNextOwnerPerm;
+ if(PERM_NONE == next_owner_perms)
+ {
+ next_owner_perms = PERM_MOVE | PERM_TRANSFER;
+ }
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ folder_id, data->mAssetInfo.mTransactionID, data->mAssetInfo.getName(),
+ data->mAssetInfo.getDescription(), data->mAssetInfo.mType,
+ data->mInventoryType, NOT_WEARABLE, next_owner_perms,
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ else
+ {
+ llwarns << "Can't find a folder to put it in" << llendl;
+ }
+ }
+ }
+ else // if(result >= 0)
+ {
+ LLSD args;
+ args["FILE"] = LLInventoryType::lookupHumanReadable(data->mInventoryType);
+ args["REASON"] = std::string(LLAssetStorage::getErrorString(result));
+ LLNotificationsUtil::add("CannotUploadReason", args);
+ }
+ }
+
+ LLUploadDialog::modalUploadFinished();
+ delete data;
+ data = NULL;
+
+ // *NOTE: This is a pretty big hack. What this does is check the
+ // file picker if there are any more pending uploads. If so,
+ // upload that file.
+ const std::string& next_file = LLFilePicker::instance().getNextFile();
+ if(is_balance_sufficient && !next_file.empty())
+ {
+ std::string asset_name = gDirUtilp->getBaseFileName(next_file, true);
+ LLStringUtil::replaceNonstandardASCII( asset_name, '?' );
+ LLStringUtil::replaceChar(asset_name, '|', '?');
+ LLStringUtil::stripNonprintable(asset_name);
+ LLStringUtil::trim(asset_name);
+
+ std::string display_name = LLStringUtil::null;
+ LLAssetStorage::LLStoreAssetCallback callback = NULL;
+ void *userdata = NULL;
+ upload_new_resource(
+ next_file,
+ asset_name,
+ asset_name, // file
+ 0,
+ LLFolderType::FT_NONE,
+ LLInventoryType::IT_NONE,
+ PERM_NONE,
+ PERM_NONE,
+ PERM_NONE,
+ display_name,
+ callback,
+ expected_upload_cost, // assuming next in a group of uploads is of roughly the same type, i.e. same upload cost
+ userdata);
+ }
+}
+
+static LLAssetID upload_new_resource_prep(
+ const LLTransactionID& tid,
+ LLAssetType::EType asset_type,
+ LLInventoryType::EType& inventory_type,
+ std::string& name,
+ const std::string& display_name,
+ std::string& description)
+{
+ LLAssetID uuid = generate_asset_id_for_new_upload(tid);
+
+ increase_new_upload_stats(asset_type);
+
+ assign_defaults_and_show_upload_message(
+ asset_type,
+ inventory_type,
+ name,
+ display_name,
+ description);
+
+ return uuid;
+}
+
+LLSD generate_new_resource_upload_capability_body(
+ LLAssetType::EType asset_type,
+ const std::string& name,
+ const std::string& desc,
+ LLFolderType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perms,
+ U32 group_perms,
+ U32 everyone_perms)
+{
+ LLSD body;
+
+ body["folder_id"] = gInventory.findCategoryUUIDForType(
+ (destination_folder_type == LLFolderType::FT_NONE) ?
+ (LLFolderType::EType) asset_type :
+ destination_folder_type);
+
+ body["asset_type"] = LLAssetType::lookup(asset_type);
+ body["inventory_type"] = LLInventoryType::lookup(inv_type);
+ body["name"] = name;
+ body["description"] = desc;
+ body["next_owner_mask"] = LLSD::Integer(next_owner_perms);
+ body["group_mask"] = LLSD::Integer(group_perms);
+ body["everyone_mask"] = LLSD::Integer(everyone_perms);
+
+ return body;
+}
+
+void upload_new_resource(
+ const LLTransactionID &tid,
+ LLAssetType::EType asset_type,
+ std::string name,
+ std::string desc,
+ S32 compression_info,
+ LLFolderType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perms,
+ U32 group_perms,
+ U32 everyone_perms,
+ const std::string& display_name,
+ LLAssetStorage::LLStoreAssetCallback callback,
+ S32 expected_upload_cost,
+ void *userdata)
+{
+ if(gDisconnected)
+ {
+ return ;
+ }
+
+ LLAssetID uuid =
+ upload_new_resource_prep(
+ tid,
+ asset_type,
+ inv_type,
+ name,
+ display_name,
+ desc);
+
+ if( LLAssetType::AT_SOUND == asset_type )
+ {
+ LLViewerStats::getInstance()->incStat(LLViewerStats::ST_UPLOAD_SOUND_COUNT );
+ }
+ else
+ if( LLAssetType::AT_TEXTURE == asset_type )
+ {
+ LLViewerStats::getInstance()->incStat(LLViewerStats::ST_UPLOAD_TEXTURE_COUNT );
+ }
+ else
+ if( LLAssetType::AT_ANIMATION == asset_type)
+ {
+ LLViewerStats::getInstance()->incStat(LLViewerStats::ST_UPLOAD_ANIM_COUNT );
+ }
+
+ if(LLInventoryType::IT_NONE == inv_type)
+ {
+ inv_type = LLInventoryType::defaultForAssetType(asset_type);
+ }
+ LLStringUtil::stripNonprintable(name);
+ LLStringUtil::stripNonprintable(desc);
+ if(name.empty())
+ {
+ name = "(No Name)";
+ }
+ if(desc.empty())
+ {
+ desc = "(No Description)";
+ }
+
+ // At this point, we're ready for the upload.
+ std::string upload_message = "Uploading...\n\n";
+ upload_message.append(display_name);
+ LLUploadDialog::modalUploadDialog(upload_message);
+
+ llinfos << "*** Uploading: " << llendl;
+ llinfos << "Type: " << LLAssetType::lookup(asset_type) << llendl;
+ llinfos << "UUID: " << uuid << llendl;
+ llinfos << "Name: " << name << llendl;
+ llinfos << "Desc: " << desc << llendl;
+ llinfos << "Expected Upload Cost: " << expected_upload_cost << llendl;
+ lldebugs << "Folder: " << gInventory.findCategoryUUIDForType((destination_folder_type == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(asset_type) : destination_folder_type) << llendl;
+ lldebugs << "Asset Type: " << LLAssetType::lookup(asset_type) << llendl;
+
+ std::string url = gAgent.getRegion()->getCapability(
+ "NewFileAgentInventory");
+
+ if ( !url.empty() )
+ {
+ llinfos << "New Agent Inventory via capability" << llendl;
+
+ LLSD body;
+ body = generate_new_resource_upload_capability_body(
+ asset_type,
+ name,
+ desc,
+ destination_folder_type,
+ inv_type,
+ next_owner_perms,
+ group_perms,
+ everyone_perms);
+
+ LLHTTPClient::post(
+ url,
+ body,
+ new LLNewAgentInventoryResponder(
+ body,
+ uuid,
+ asset_type));
+ }
+ else
+ {
+ llinfos << "NewAgentInventory capability not found, new agent inventory via asset system." << llendl;
+ // check for adequate funds
+ // TODO: do this check on the sim
+ if (LLAssetType::AT_SOUND == asset_type ||
+ LLAssetType::AT_TEXTURE == asset_type ||
+ LLAssetType::AT_ANIMATION == asset_type)
+ {
+ S32 balance = gStatusBar->getBalance();
+ if (balance < expected_upload_cost)
+ {
+ // insufficient funds, bail on this upload
+ LLStringUtil::format_map_t args;
+ args["NAME"] = name;
+ args["AMOUNT"] = llformat("%d", expected_upload_cost);
+ LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("UploadingCosts", args), expected_upload_cost );
+ return;
+ }
+ }
+
+ LLResourceData* data = new LLResourceData;
+ data->mAssetInfo.mTransactionID = tid;
+ data->mAssetInfo.mUuid = uuid;
+ data->mAssetInfo.mType = asset_type;
+ data->mAssetInfo.mCreatorID = gAgentID;
+ data->mInventoryType = inv_type;
+ data->mNextOwnerPerm = next_owner_perms;
+ data->mExpectedUploadCost = expected_upload_cost;
+ data->mUserData = userdata;
+ data->mAssetInfo.setName(name);
+ data->mAssetInfo.setDescription(desc);
+ data->mPreferredLocation = destination_folder_type;
+
+ LLAssetStorage::LLStoreAssetCallback asset_callback = &upload_done_callback;
+ if (callback)
+ {
+ asset_callback = callback;
+ }
+ gAssetStorage->storeAssetData(
+ data->mAssetInfo.mTransactionID,
+ data->mAssetInfo.mType,
+ asset_callback,
+ (void*)data,
+ FALSE);
+ }
+}
+
+LLAssetID generate_asset_id_for_new_upload(const LLTransactionID& tid)
+{
+ if ( gDisconnected )
+ {
+ LLAssetID rv;
+
+ rv.setNull();
+ return rv;
+ }
+
+ LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ return uuid;
+}
+
+void increase_new_upload_stats(LLAssetType::EType asset_type)
+{
+ if ( LLAssetType::AT_SOUND == asset_type )
+ {
+ LLViewerStats::getInstance()->incStat(
+ LLViewerStats::ST_UPLOAD_SOUND_COUNT );
+ }
+ else if ( LLAssetType::AT_TEXTURE == asset_type )
+ {
+ LLViewerStats::getInstance()->incStat(
+ LLViewerStats::ST_UPLOAD_TEXTURE_COUNT );
+ }
+ else if ( LLAssetType::AT_ANIMATION == asset_type )
+ {
+ LLViewerStats::getInstance()->incStat(
+ LLViewerStats::ST_UPLOAD_ANIM_COUNT );
+ }
+}
+
+void assign_defaults_and_show_upload_message(
+ LLAssetType::EType asset_type,
+ LLInventoryType::EType& inventory_type,
+ std::string& name,
+ const std::string& display_name,
+ std::string& description)
+{
+ if ( LLInventoryType::IT_NONE == inventory_type )
+ {
+ inventory_type = LLInventoryType::defaultForAssetType(asset_type);
+ }
+ LLStringUtil::stripNonprintable(name);
+ LLStringUtil::stripNonprintable(description);
+
+ if ( name.empty() )
+ {
+ name = "(No Name)";
+ }
+ if ( description.empty() )
+ {
+ description = "(No Description)";
+ }
+
+ // At this point, we're ready for the upload.
+ std::string upload_message = "Uploading...\n\n";
+ upload_message.append(display_name);
+ LLUploadDialog::modalUploadDialog(upload_message);
+}
+
+
+void init_menu_file()
+{
+ view_listener_t::addCommit(new LLFileUploadImage(), "File.UploadImage");
+ view_listener_t::addCommit(new LLFileUploadSound(), "File.UploadSound");
+ view_listener_t::addCommit(new LLFileUploadAnim(), "File.UploadAnim");
+ view_listener_t::addCommit(new LLFileUploadModel(), "File.UploadModel");
+ view_listener_t::addCommit(new LLFileUploadBulk(), "File.UploadBulk");
+ view_listener_t::addCommit(new LLFileCloseWindow(), "File.CloseWindow");
+ view_listener_t::addCommit(new LLFileCloseAllWindows(), "File.CloseAllWindows");
+ view_listener_t::addEnable(new LLFileEnableCloseWindow(), "File.EnableCloseWindow");
+ view_listener_t::addEnable(new LLFileEnableCloseAllWindows(), "File.EnableCloseAllWindows");
+ view_listener_t::addCommit(new LLFileTakeSnapshotToDisk(), "File.TakeSnapshotToDisk");
+ view_listener_t::addCommit(new LLFileQuit(), "File.Quit");
+
+ view_listener_t::addEnable(new LLFileEnableUpload(), "File.EnableUpload");
+ view_listener_t::addEnable(new LLFileEnableUploadModel(), "File.EnableUploadModel");
+ view_listener_t::addMenu(new LLMeshEnabled(), "File.MeshEnabled");
+ view_listener_t::addMenu(new LLMeshUploadVisible(), "File.VisibleUploadModel");
+
+ // "File.SaveTexture" moved to llpanelmaininventory so that it can be properly handled.
+}
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index e3cb985ddb..cc4cac4202 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1519,8 +1519,10 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("LandResources");
capabilityNames.append("MapLayer");
capabilityNames.append("MapLayerGod");
- capabilityNames.append("MeshUploadFlag");
+ capabilityNames.append("MeshUploadFlag");
+ capabilityNames.append("NavMeshUpload");
capabilityNames.append("NewFileAgentInventory");
+ capabilityNames.append("ObjectNavMeshProperties");
capabilityNames.append("ParcelPropertiesUpdate");
capabilityNames.append("ParcelMediaURLFilterList");
capabilityNames.append("ParcelNavigateMedia");
@@ -1530,6 +1532,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("RemoteParcelRequest");
capabilityNames.append("RequestTextureDownload");
capabilityNames.append("ResourceCostSelected");
+ capabilityNames.append("RetrieveNavMeshSrc");
capabilityNames.append("SearchStatRequest");
capabilityNames.append("SearchStatTracking");
capabilityNames.append("SendPostcard");
@@ -1555,7 +1558,6 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("ViewerMetrics");
capabilityNames.append("ViewerStartAuction");
capabilityNames.append("ViewerStats");
-
// Please add new capabilities alphabetically to reduce
// merge conflicts.
}
@@ -1794,7 +1796,10 @@ void LLViewerRegion::getNeighboringRegions( std::vector<LLViewerRegion*>& unique
{
mImpl->mLandp->getNeighboringRegions( uniqueRegions );
}
-
+void LLViewerRegion::getNeighboringRegionsStatus( std::vector<S32>& regions )
+{
+ mImpl->mLandp->getNeighboringRegionsStatus( regions );
+}
void LLViewerRegion::showReleaseNotes()
{
std::string url = this->getCapability("ServerReleaseNotes");
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index c483c6ef52..6004165b0e 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -325,6 +325,7 @@ public:
bool objectsCrossParcel(const std::vector<LLBBox>& boxes) const;
void getNeighboringRegions( std::vector<LLViewerRegion*>& uniqueRegions );
+ void getNeighboringRegionsStatus( std::vector<S32>& regions );
public:
struct CompareDistance
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 8a713ae22c..04b687b42f 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -203,6 +203,8 @@
#include "llwindowlistener.h"
#include "llviewerwindowlistener.h"
#include "llpaneltopinfobar.h"
+#include "LLPathingLib.h"
+#include "llfloaterpathfindingsetup.h"
#if LL_WINDOWS
#include <tchar.h> // For Unicode conversion methods
@@ -836,6 +838,7 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK
x = llround((F32)x / mDisplayScale.mV[VX]);
y = llround((F32)y / mDisplayScale.mV[VY]);
+
// only send mouse clicks to UI if UI is visible
if(gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
{
@@ -949,6 +952,29 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK
}
}
+
+ //Determine if we have a pathing system and subsequently provide any mouse input
+ if ( LLPathingLib::getInstance() && mLeftMouseDown == down )
+ {
+ LLVector3 dv = mouseDirectionGlobal(x,y);
+ LLVector3 mousePos = LLViewerCamera::getInstance()->getOrigin();
+ LLVector3 rayStart = mousePos;
+ LLVector3 rayEnd = mousePos + dv * 150;
+
+ //Determine if alt is being held in conjunction with a lmb click, if alt is being held
+ //then do not provide any input to the pathingLib console
+ MASK currentKeyMask = gKeyboard->currentMask(TRUE);
+ if ( !(currentKeyMask & MASK_ALT) )
+ {
+ LLFloaterPathfindingSetup* pFloater = LLFloaterReg::getTypedInstance<LLFloaterPathfindingSetup>("pathfinding_setup");
+ if ( pFloater )
+ {
+ //The floater takes care of determining what stage - essentially where the data goes into the pathing packet(start or end)
+ pFloater->providePathingData( rayStart, rayEnd );
+ }
+ }
+ }
+
// Do not allow tool manager to handle mouseclicks if we have disconnected
if(!gDisconnected && LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) )
{
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index c523a78b22..1f777032e6 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -104,7 +104,7 @@
#include "lltoolpie.h"
#include "llcurl.h"
#include "llnotifications.h"
-
+#include "LLPathingLib.h"
#ifdef _DEBUG
// Debug indices is disabled for now for debug performance - djs 4/24/02
@@ -3792,6 +3792,7 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sDefaultImagep);
LLViewerFetchedTexture::sDefaultImagep->setAddressMode(LLTexUnit::TAM_WRAP);
+
//////////////////////////////////////////////
//
// Actually render all of the geometry
@@ -6217,6 +6218,10 @@ void LLPipeline::resetVertexBuffers()
gSky.resetVertexBuffers();
+ if ( LLPathingLib::getInstance() )
+ {
+ LLPathingLib::getInstance()->cleanupVBOManger();
+ }
LLVertexBuffer::cleanupClass();
//delete all name pool caches
@@ -6805,7 +6810,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
mFXAABuffer.bindTexture(0, channel);
gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
}
-
+
gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft;
gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom;
gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth();
@@ -7457,7 +7462,7 @@ void LLPipeline::renderDeferredLighting()
F32 s = volume->getLightRadius()*1.5f;
LLColor3 col = volume->getLightColor();
-
+
if (col.magVecSquared() < 0.001f)
{
continue;
@@ -7570,7 +7575,7 @@ void LLPipeline::renderDeferredLighting()
setupSpotLight(gDeferredSpotLightProgram, drawablep);
LLColor3 col = volume->getLightColor();
-
+
//vertex positions are encoded so the 3 bits of their vertex index
//correspond to their axis facing, with bit position 3,2,1 matching
//axis facing x,y,z, bit set meaning positive facing, bit clear
@@ -7679,7 +7684,7 @@ void LLPipeline::renderDeferredLighting()
setupSpotLight(gDeferredMultiSpotLightProgram, drawablep);
LLColor3 col = volume->getLightColor();
-
+
gDeferredMultiSpotLightProgram.uniform3fv(LLShaderMgr::LIGHT_CENTER, 1, tc.v);
gDeferredMultiSpotLightProgram.uniform1f(LLShaderMgr::LIGHT_SIZE, s*s);
gDeferredMultiSpotLightProgram.uniform3fv(LLShaderMgr::DIFFUSE_COLOR, 1, col.mV);
@@ -8155,6 +8160,7 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
gGL.setColorMask(true, false);
renderGeom(camera);
+
}
LLPipeline::sUnderWaterRender = FALSE;
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index e4a8622a4b..5eea581987 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -138,7 +138,10 @@ with the same filename but different name
<texture name="Command_MiniMap_Icon" file_name="toolbar_icons/mini_map.png" preload="true" />
<texture name="Command_Move_Icon" file_name="toolbar_icons/move.png" preload="true" />
<texture name="Command_Outbox_Icon" file_name="toolbar_icons/outbox.png" preload="true" />
+ <texture name="Command_Pathfinding_Icon" file_name="toolbar_icons/land.png" preload="true" />
<texture name="Command_People_Icon" file_name="toolbar_icons/people.png" preload="true" />
+ <texture name="Command_PF_Characters_Icon" file_name="toolbar_icons/land.png" preload="true" />
+ <texture name="Command_PF_Linksets_Icon" file_name="toolbar_icons/land.png" preload="true" />
<texture name="Command_Picks_Icon" file_name="toolbar_icons/picks.png" preload="true" />
<texture name="Command_Places_Icon" file_name="toolbar_icons/places.png" preload="true" />
<texture name="Command_Preferences_Icon" file_name="toolbar_icons/preferences.png" preload="true" />
diff --git a/indra/newview/skins/default/xui/en/floater_pathfinding_characters.xml b/indra/newview/skins/default/xui/en/floater_pathfinding_characters.xml
new file mode 100644
index 0000000000..9ae28d6a15
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_pathfinding_characters.xml
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ open_positioning="cascading"
+ can_tear_off="false"
+ height="273"
+ layout="topleft"
+ name="floater_pathfinding_characters"
+ help_topic="floater_pathfinding_characters"
+ reuse_instance="true"
+ save_rect="true"
+ single_instance="true"
+ title="Pathfinding characters"
+ width="635">
+ <floater.string name="characters_messaging_initial"></floater.string>
+ <floater.string name="characters_messaging_fetch_starting">Building query for pathfinding characters ...</floater.string>
+ <floater.string name="characters_messaging_fetch_inprogress">Querying for pathfinding characters ...</floater.string>
+ <floater.string name="characters_messaging_fetch_inprogress_multi_request">Querying for pathfinding characters (already in progress) ...</floater.string>
+ <floater.string name="characters_messaging_fetch_received">Loading pathfinding characters data from response ...</floater.string>
+ <floater.string name="characters_messaging_fetch_error">Error detected while querying for pathfinding characters</floater.string>
+ <floater.string name="characters_messaging_complete_none_found">No pathfinding characters</floater.string>
+ <floater.string name="characters_messaging_complete_available">[NUM_SELECTED] characters selected out of [NUM_TOTAL]</floater.string>
+ <scroll_list
+ column_padding="0"
+ draw_heading="true"
+ follows="all"
+ height="135"
+ layout="topleft"
+ left="18"
+ top="10"
+ multi_select="true"
+ name="pathfinding_characters"
+ width="600">
+ <scroll_list.columns
+ label="Name"
+ name="name"
+ width="137" />
+ <scroll_list.columns
+ label="Description"
+ name="description"
+ width="172" />
+ <scroll_list.columns
+ label="Owner"
+ name="owner"
+ width="141" />
+ <scroll_list.columns
+ label="CPU"
+ name="cpu_time"
+ width="60" />
+ <scroll_list.columns
+ label="Altitude"
+ name="altitude"
+ width="64" />
+ </scroll_list>
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ name="characters_status"
+ top="161"
+ width="500">
+ Characters:
+ </text>
+ <button
+ follows="right|top"
+ height="21"
+ label="Refresh list"
+ layout="topleft"
+ name="refresh_characters_list"
+ top="161"
+ left="257"
+ width="115"/>
+ <button
+ follows="right|top"
+ height="21"
+ label="Select all"
+ layout="topleft"
+ name="select_all_characters"
+ top="161"
+ left="378"
+ width="115"/>
+ <button
+ follows="right|top"
+ height="21"
+ label="Select none"
+ layout="topleft"
+ name="select_none_characters"
+ top="161"
+ left="502"
+ width="115"/>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ name="horiz_separator"
+ top="196"
+ left="20"
+ width="600"/>
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ text_readonly_color="LabelDisabledColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ name="actions_label"
+ top_pad="12"
+ width="910">
+ Actions on selected characters:
+ </text>
+ <check_box
+ height="19"
+ label="Show beacon"
+ layout="topleft"
+ name="show_beacon"
+ top="208"
+ left="259"
+ width="90" />
+ <button
+ follows="right|top"
+ height="22"
+ label="Take"
+ layout="topleft"
+ name="take_characters"
+ top="232"
+ left="17"
+ width="94"/>
+ <button
+ follows="right|top"
+ height="22"
+ label="Take copy"
+ layout="topleft"
+ name="take_copy_characters"
+ top="232"
+ left="119"
+ width="94"/>
+ <button
+ follows="right|top"
+ height="22"
+ label="Return"
+ layout="topleft"
+ name="return_characters"
+ top="233"
+ left="220"
+ width="94"/>
+ <button
+ follows="right|top"
+ height="22"
+ label="Delete"
+ layout="topleft"
+ name="delete_characters"
+ top="233"
+ left="322"
+ width="94"/>
+ <button
+ follows="right|top"
+ height="22"
+ label="Teleport me to it"
+ layout="topleft"
+ name="teleport_to_character"
+ tool_tip="Enabled only when one character is selected."
+ top="233"
+ left="420"
+ width="159"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml
new file mode 100644
index 0000000000..65c0bf3cca
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ open_positioning="cascading"
+ can_tear_off="false"
+ height="330"
+ layout="topleft"
+ name="floater_pathfinding_linksets"
+ help_topic="floater_pathfinding_linksets"
+ reuse_instance="true"
+ save_rect="true"
+ single_instance="true"
+ title="Pathfinding linksets"
+ width="950">
+ <floater.string name="linksets_messaging_initial"></floater.string>
+ <floater.string name="linksets_messaging_fetch_starting">Building query for pathfinding linksets ...</floater.string>
+ <floater.string name="linksets_messaging_fetch_inprogress">Querying for pathfinding linksets ...</floater.string>
+ <floater.string name="linksets_messaging_fetch_inprogress_multi_request">Querying for pathfinding linksets (already in progress) ...</floater.string>
+ <floater.string name="linksets_messaging_fetch_received">Loading pathfinding linksets data from response ...</floater.string>
+ <floater.string name="linksets_messaging_fetch_error">Error detected while querying for pathfinding linksets</floater.string>
+ <floater.string name="linksets_messaging_modify_starting">Building modify message for selected pathfinding linksets ...</floater.string>
+ <floater.string name="linksets_messaging_modify_inprogress">Modifying selected pathfinding linksets ...</floater.string>
+ <floater.string name="linksets_messaging_modify_received">Loading modified pathfinding linksets data from response ...</floater.string>
+ <floater.string name="linksets_messaging_modify_error">Error detected while modifying for pathfinding linksets</floater.string>
+ <floater.string name="linksets_messaging_complete_none_found">No pathfinding linksets</floater.string>
+ <floater.string name="linksets_messaging_complete_available">[NUM_SELECTED] linksets selected out of [NUM_TOTAL]</floater.string>
+ <floater.string name="linkset_path_state_walkable">Walkable</floater.string>
+ <floater.string name="linkset_path_state_obstacle">Obstacle</floater.string>
+ <floater.string name="linkset_path_state_ignored">Ignored</floater.string>
+ <floater.string name="linkset_is_phantom">Phantom</floater.string>
+ <floater.string name="linkset_is_not_phantom">--</floater.string>
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="20"
+ top="16"
+ width="67">
+ Filter by:
+ </text>
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="87"
+ top="16"
+ width="62">
+ Name
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="20"
+ layout="topleft"
+ left_delta="62"
+ top="11"
+ max_length_bytes="10"
+ name="filter_by_name"
+ width="105" />
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="273"
+ top="16"
+ width="88">
+ Description
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="20"
+ layout="topleft"
+ left_delta="88"
+ top="11"
+ max_length_bytes="10"
+ name="filter_by_description"
+ width="106" />
+ <check_box
+ height="19"
+ initial_value="true"
+ label="Walkable"
+ layout="topleft"
+ left="481"
+ top="14"
+ name="filter_by_walkable"
+ width="90" />
+ <check_box
+ height="19"
+ initial_value="true"
+ label="Obstacle"
+ layout="topleft"
+ left="577"
+ top="14"
+ name="filter_by_obstacle"
+ width="90" />
+ <check_box
+ height="19"
+ initial_value="true"
+ label="Ignored"
+ layout="topleft"
+ left="674"
+ top="14"
+ name="filter_by_ignored"
+ width="90" />
+ <button
+ follows="right|top"
+ height="21"
+ label="Apply"
+ layout="topleft"
+ name="apply_filters"
+ top="11"
+ left="769"
+ width="73"/>
+ <button
+ follows="right|top"
+ height="21"
+ label="Clear"
+ layout="topleft"
+ name="clear_filters"
+ top="11"
+ left="851"
+ width="73"/>
+ <scroll_list
+ column_padding="0"
+ draw_heading="true"
+ follows="all"
+ height="135"
+ layout="topleft"
+ left="18"
+ top="48"
+ multi_select="true"
+ name="pathfinding_linksets"
+ width="910">
+ <scroll_list.columns
+ label="Name (root prim)"
+ name="name"
+ width="173" />
+ <scroll_list.columns
+ label="Description (root prim)"
+ name="description"
+ width="212" />
+ <scroll_list.columns
+ label="Land impact"
+ name="land_impact"
+ width="95" />
+ <scroll_list.columns
+ label="Dist from you"
+ name="dist_from_you"
+ width="97" />
+ <scroll_list.columns
+ label="State"
+ name="path_state"
+ width="74" />
+ <scroll_list.columns
+ label="Phantom"
+ name="is_phantom"
+ width="74" />
+ <scroll_list.columns
+ label="A %"
+ name="a_percent"
+ width="41" />
+ <scroll_list.columns
+ label="B %"
+ name="b_percent"
+ width="41" />
+ <scroll_list.columns
+ label="C %"
+ name="c_percent"
+ width="41" />
+ <scroll_list.columns
+ label="D %"
+ name="d_percent"
+ width="41" />
+ </scroll_list>
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ name="linksets_status"
+ top="200"
+ width="500">
+ Linksets:
+ </text>
+ <button
+ follows="right|top"
+ height="21"
+ label="Refresh list"
+ layout="topleft"
+ name="refresh_linksets_list"
+ top="200"
+ left="568"
+ width="115"/>
+ <button
+ follows="right|top"
+ height="21"
+ label="Select all"
+ layout="topleft"
+ name="select_all_linksets"
+ top="200"
+ left="690"
+ width="115"/>
+ <button
+ follows="right|top"
+ height="21"
+ label="Select none"
+ layout="topleft"
+ name="select_none_linksets"
+ top="200"
+ left="812"
+ width="115"/>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ name="horiz_separator"
+ top="230"
+ left="20"
+ width="912"/>
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ top_pad="12"
+ width="910">
+ Select row(s) to edit attributes of linkset(s). If a linkset is deleted or returned to inventory, attributes assigned to it will be lost.
+ </text>
+ <radio_group
+ follows="top|left"
+ height="55"
+ value="1"
+ layout="topleft"
+ left_delta="0"
+ name="edit_path_state"
+ top_delta="21"
+ width="138">
+ <radio_item
+ label="Walkable"
+ layout="topleft"
+ height="13"
+ name="edit_pathing_state_walkable"
+ value="1"/>
+ <radio_item
+ label="Obstacle"
+ layout="topleft"
+ height="13"
+ name="edit_pathing_state_obstacle"
+ value="2"/>
+ <radio_item
+ label="Ignored"
+ layout="topleft"
+ height="13"
+ name="edit_pathing_state_ignored"
+ value="3"/>
+ </radio_group>
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ text_readonly_color="LabelDisabledColor"
+ name="walkability_coefficients_label"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="159"
+ top="271"
+ width="200">
+ Walkability coefficients
+ </text>
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ text_readonly_color="LabelDisabledColor"
+ name="edit_a_label"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ top_pad="12"
+ width="90">
+ A
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="20"
+ layout="topleft"
+ left_delta="14"
+ max_length_chars="3"
+ name="edit_a_value"
+ width="45" />
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ text_readonly_color="LabelDisabledColor"
+ name="edit_b_label"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="248"
+ top_pad="-13"
+ width="90">
+ B
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="20"
+ layout="topleft"
+ left_delta="14"
+ max_length_chars="3"
+ name="edit_b_value"
+ width="45" />
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ text_readonly_color="LabelDisabledColor"
+ name="edit_c_label"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="337"
+ top_pad="-13"
+ width="90">
+ C
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="20"
+ layout="topleft"
+ left_delta="14"
+ max_length_chars="3"
+ name="edit_c_value"
+ width="45" />
+ <text
+ height="13"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ text_readonly_color="LabelDisabledColor"
+ name="edit_d_label"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="426"
+ top_pad="-13"
+ width="90">
+ D
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="20"
+ layout="topleft"
+ left_delta="14"
+ max_length_chars="3"
+ name="edit_d_value"
+ width="45" />
+ <check_box
+ height="19"
+ label="Phantom"
+ layout="topleft"
+ name="edit_phantom_value"
+ top="271"
+ left="559"
+ width="90" />
+ <button
+ follows="right|top"
+ height="21"
+ label="Apply changes"
+ layout="topleft"
+ name="apply_edit_values"
+ top="270"
+ left="735"
+ width="134"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_pathfinding_setup.xml b/indra/newview/skins/default/xui/en/floater_pathfinding_setup.xml
new file mode 100644
index 0000000000..37555eea3a
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_pathfinding_setup.xml
@@ -0,0 +1,466 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ open_positioning="cascading"
+ can_tear_off="false"
+ height="228"
+ layout="topleft"
+ name="floater_pathfinding"
+ help_topic="floater_pathfinding"
+ reuse_instance="true"
+ save_rect="true"
+ single_instance="true"
+ title="Pathfinding setup"
+ width="833">
+ <floater.string name="navmesh_fetch_initial"></floater.string>
+ <floater.string name="navmesh_fetch_inprogress">Downloading the navmesh ...</floater.string>
+ <floater.string name="navmesh_fetch_complete_available">Navmesh received.</floater.string>
+ <floater.string name="navmesh_fetch_complete_none">No navmesh for region.</floater.string>
+ <floater.string name="navmesh_region_not_enabled">Pathfinding is not enabled for this region.</floater.string>
+ <floater.string name="navmesh_library_not_implemented">Cannot find pathing library implementation.</floater.string>
+ <text
+ height="13"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="12"
+ top="20"
+ width="208">
+ Show overlays:
+ </text>
+ <check_box
+ height="19"
+ label="Navmesh"
+ layout="topleft"
+ left="20"
+ name="show_navmesh_overlay"
+ top_pad="5"
+ width="90" />
+ <check_box
+ height="19"
+ label="Exclusion volumes"
+ layout="topleft"
+ left="20"
+ name="show_exclusion_volumes"
+ top_pad="0"
+ width="90" />
+ <check_box
+ height="19"
+ label="Path"
+ layout="topleft"
+ left="20"
+ name="show_path"
+ top_pad="0"
+ width="90" />
+ <check_box
+ height="19"
+ label="Water plane"
+ layout="topleft"
+ left="20"
+ name="show_water_plane"
+ top_pad="0"
+ width="90" />
+ <text
+ height="13"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ layout="topleft"
+ left="12"
+ top_pad="10"
+ width="208">
+ Overlay on:
+ </text>
+ <radio_group
+ follows="top|left"
+ height="45"
+ value="1"
+ layout="topleft"
+ left_delta="8"
+ name="region_overlay_display"
+ top_delta="17"
+ width="200">
+ <radio_item
+ label="Fixed physics geometry"
+ layout="topleft"
+ height="14"
+ name="display_overlay_on_fixed"
+ value="1"/>
+ <radio_item
+ label="All renderable geometry"
+ layout="topleft"
+ height="14"
+ name="display_overlay_on_all"
+ value="2"/>
+ </radio_group>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="203"
+ layout="topleft"
+ name="horiz_separator_1"
+ top="15"
+ left="228"
+ width="0"/>
+ <text
+ height="24"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ line_spacing.pixels="2"
+ follows="left|top"
+ layout="topleft"
+ left="240"
+ top="18"
+ width="208">
+Click on two points
+to see the path between them.
+ </text>
+ <radio_group
+ allow_deselect="true"
+ follows="top|left"
+ height="45"
+ layout="topleft"
+ left_delta="0"
+ name="path_selection"
+ top_delta="33"
+ value="0"
+ width="200">
+ <radio_item
+ label="Choose start point"
+ layout="topleft"
+ height="14"
+ name="choose_path_start_point"
+ value="1"/>
+ <radio_item
+ label="Choose end point"
+ layout="topleft"
+ height="14"
+ name="choose_path_end_point"
+ value="2"/>
+ </radio_group>
+ <text
+ height="14"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="240"
+ width="208">
+ Character width
+ </text>
+ <slider
+ decimal_digits="1"
+ height="14"
+ increment="0.1"
+ layout="topleft"
+ max_val="2"
+ min_val="0.2"
+ name="character_width"
+ top_pad="7"
+ value="1"
+ width="145" />
+ <text
+ height="14"
+ word_wrap="false"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ top_pad="-14"
+ left="382"
+ width="208">
+ m
+ </text>
+ <text
+ height="14"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="240"
+ top_pad="10"
+ width="208">
+ Character type
+ </text>
+ <radio_group
+ follows="top|left"
+ height="20"
+ layout="topleft"
+ left_delta="0"
+ name="character_type"
+ top_delta="20"
+ value="1"
+ width="200">
+ <radio_item
+ label="A"
+ height="14"
+ width="30"
+ value="1"
+ name="character_type_a"/>
+ <radio_item
+ label="B"
+ height="14"
+ width="30"
+ layout="topleft"
+ top="4"
+ left="50"
+ value="2"
+ name="character_type_b"/>
+ <radio_item
+ label="C"
+ height="14"
+ width="30"
+ layout="topleft"
+ top="4"
+ left="100"
+ value="3"
+ name="character_type_c"/>
+ <radio_item
+ label="D"
+ height="14"
+ width="30"
+ layout="topleft"
+ top="4"
+ left="150"
+ value="4"
+ name="character_type_d"/>
+ </radio_group>
+ <text
+ height="14"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="240"
+ name="pathfinding_status"
+ top_pad="10"
+ width="208">
+ </text>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="203"
+ layout="topleft"
+ name="horiz_separator_2"
+ top="15"
+ left="456"
+ width="0"/>
+ <text
+ height="13"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="470"
+ top="18"
+ width="180">
+ View / edit linkset attributes:
+ </text>
+ <button
+ follows="left|top"
+ height="21"
+ label="Linksets..."
+ layout="topleft"
+ name="view_and_edit_linksets"
+ width="96"/>
+ <text
+ height="25"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ line_spacing.pixels="4"
+ follows="left|top"
+ layout="topleft"
+ top_pad="22"
+ width="180">
+If you have made changes
+to objects or terrain:
+ </text>
+ <button
+ follows="left|top"
+ height="22"
+ label="Rebuild navmesh"
+ layout="topleft"
+ name="rebuild_navmesh"
+ top_pad="14"
+ width="149"/>
+ <text
+ height="25"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ line_spacing.pixels="4"
+ follows="left|top"
+ layout="topleft"
+ top_pad="14"
+ width="180">
+To load the current state
+of the mesh:
+ </text>
+ <button
+ follows="left|top"
+ height="21"
+ label="Refresh"
+ layout="topleft"
+ name="refresh_navmesh"
+ top_pad="9"
+ width="95"/>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="203"
+ layout="topleft"
+ name="horiz_separator_3"
+ top="15"
+ left="667"
+ width="0"/>
+ <text
+ height="13"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="689"
+ top="18"
+ width="208">
+ Terrain materials
+ </text>
+ <text
+ height="13"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ top_pad="17"
+ width="208">
+ A
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="21"
+ layout="topleft"
+ left_delta="22"
+ max_length_bytes="10"
+ name="terrain_material_a"
+ width="46" />
+ <text
+ height="13"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ layout="topleft"
+ left_pad="-68"
+ top_pad="17"
+ width="208">
+ B
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="21"
+ layout="topleft"
+ left_delta="22"
+ max_length_bytes="10"
+ name="terrain_material_b"
+ width="46" />
+ <text
+ height="13"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ layout="topleft"
+ left_pad="-68"
+ top_pad="17"
+ width="208">
+ C
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="21"
+ layout="topleft"
+ left_delta="22"
+ max_length_bytes="10"
+ name="terrain_material_c"
+ width="46" />
+ <text
+ height="13"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ layout="topleft"
+ left_pad="-68"
+ top_pad="17"
+ width="208">
+ D
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="top|left"
+ height="21"
+ layout="topleft"
+ left_delta="22"
+ max_length_bytes="10"
+ name="terrain_material_d"
+ width="46" />
+ <text
+ height="38"
+ word_wrap="true"
+ use_ellipses="false"
+ type="string"
+ text_color="LabelTextColor"
+ length="1"
+ line_spacing.pixels="4"
+ follows="left|top"
+ layout="topleft"
+ left_pad="-68"
+ top_pad="14"
+ width="180">
+Rebuild the navmesh
+after making any
+changes.
+ </text>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index f9147ea650..b8ed10b339 100644
--- a/indra/newview/skins/default/xui/en/floater_tools.xml
+++ b/indra/newview/skins/default/xui/en/floater_tools.xml
@@ -2401,6 +2401,46 @@ even though the user gets a free copy.
name="Physics Restitution"
top_pad="8"
width="132" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="0"
+ top_delta="45"
+ name="pathfinding_type_label"
+ width="121">
+ Pathfinding Type:
+ </text>
+ <radio_group
+ follows="top|left"
+ height="55"
+ layout="topleft"
+ name="edit_pathfinding_state"
+ left_delta="0"
+ top_delta="15"
+ value="1"
+ width="75">
+ <radio_item
+ label="Walkable"
+ layout="topleft"
+ height="13"
+ name="edit_pathfinding_state_walkable"
+ value="1"/>
+ <radio_item
+ label="Obstacle"
+ layout="topleft"
+ height="13"
+ name="edit_pathfinding_state_obstacle"
+ value="2"/>
+ <radio_item
+ label="Ignored"
+ layout="topleft"
+ height="13"
+ name="edit_pathfinding_state_ignored"
+ value="3"/>
+ </radio_group>
</panel>
<panel
border="false"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index cd8550b00d..d8f9453b09 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -674,6 +674,7 @@
<menu_item_check.on_enable
function="Build.Enabled" />
</menu_item_check>
+
<menu
create_jump_keys="true"
label="Select Build Tool"
@@ -930,7 +931,36 @@
</menu_item_call>
</menu>
- <menu_item_separator/>
+ <menu
+ create_jump_keys="true"
+ label="Pathfinding"
+ name="Pathfindubg"
+ tear_off="false">
+ <menu_item_call
+ label="Setup..."
+ name="pathfinding_setup_menu_item">
+ <menu_item_call.on_click
+ function="Floater.ToggleOrBringToFront"
+ parameter="pathfinding_setup" />
+ </menu_item_call>
+ <menu_item_call
+ label="Linksets..."
+ name="pathfinding_linksets_menu_item">
+ <menu_item_call.on_click
+ function="Floater.ToggleOrBringToFront"
+ parameter="pathfinding_linksets" />
+ </menu_item_call>
+ <menu_item_call
+ label="Characters..."
+ name="pathfinding_characters_menu_item">
+ <menu_item_call.on_click
+ function="Floater.ToggleOrBringToFront"
+ parameter="pathfinding_characters" />
+ </menu_item_call>
+ </menu>
+
+
+ <menu_item_separator/>
<menu
create_jump_keys="true"
@@ -1116,6 +1146,14 @@
<menu_item_call.on_visible
function="File.VisibleUploadModel"/>
</menu_item_call>
+ <menu_item_call
+ label="BuildNavMeshTest"
+ layout="topleft"
+ name="BuildNavMesh">
+ <menu_item_call.on_click
+ function="File.UploadNavMesh"
+ parameter="" />
+ </menu_item_call>
<menu_item_call
label="Bulk (L$[COST] per file)..."
layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 3351ffe00f..793893f540 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3694,7 +3694,10 @@ Try enclosing path to the editor with double quotes.
<string name="Command_MiniMap_Label">Mini-map</string>
<string name="Command_Move_Label">Walk / run / fly</string>
<string name="Command_Outbox_Label">Merchant outbox</string>
+ <string name="Command_Pathfinding_Label">Pathfinding</string>
<string name="Command_People_Label">People</string>
+ <string name="Command_PF_Characters_Label">Pathfinding Characters</string>
+ <string name="Command_PF_Linksets_Label">Pathfinding Linksets</string>
<string name="Command_Picks_Label">Picks</string>
<string name="Command_Places_Label">Places</string>
<string name="Command_Preferences_Label">Preferences</string>
@@ -3720,7 +3723,10 @@ Try enclosing path to the editor with double quotes.
<string name="Command_MiniMap_Tooltip">Show nearby people</string>
<string name="Command_Move_Tooltip">Moving your avatar</string>
<string name="Command_Outbox_Tooltip">Transfer items to your marketplace for sale</string>
+ <string name="Command_Pathfinding_Tooltip">Information about pathfinding</string>
<string name="Command_People_Tooltip">Friends, groups, and nearby people</string>
+ <string name="Command_PF_Characters_Tooltip">Manipulation of pathfinding characters</string>
+ <string name="Command_PF_Linksets_Tooltip">Manipulation of pathfinding linksets</string>
<string name="Command_Picks_Tooltip">Places to show as favorites in your profile</string>
<string name="Command_Places_Tooltip">Places you've saved</string>
<string name="Command_Preferences_Tooltip">Preferences</string>
diff --git a/package_physicsextensions.sh b/package_physicsextensions.sh
new file mode 100755
index 0000000000..9260dd03ce
--- /dev/null
+++ b/package_physicsextensions.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+cd "$(dirname "$0")"
+
+# turn on verbose debugging output for parabuild logs.
+set -x
+# make errors fatal
+set -e
+
+if [ -z "$AUTOBUILD" ] ; then
+ fail
+fi
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ export AUTOBUILD="$(cygpath -u $AUTOBUILD)"
+fi
+
+# load autbuild provided shell functions and variables
+eval "$("$AUTOBUILD" source_environment)"
+
+projectDir="$(pwd)"
+
+#directories we need the headers from
+projects="llcommon llimage llmath llrender"
+
+source="$projectDir/indra"
+
+stage="$projectDir/stage/include"
+mkdir -p $stage
+
+for project in $projects
+do
+ dstIncludeDir="$stage/$project"
+ mkdir -p $dstIncludeDir
+ headers="$source/$project/*.h"
+ cp $headers "$dstIncludeDir"
+done
+
+# Copy the license files into place for packaging
+srcLicenseDir="$projectDir/doc"
+dstLicenseDir="$projectDir/stage/LICENSES"
+
+mkdir -p "$dstLicenseDir"
+cp "$srcLicenseDir/LGPL-licence.txt" "$dstLicenseDir/LGPL-licence.txt"
+
+pass
diff --git a/package_physicsextensions.xml b/package_physicsextensions.xml
new file mode 100644
index 0000000000..896e08f01f
--- /dev/null
+++ b/package_physicsextensions.xml
@@ -0,0 +1,302 @@
+<?xml version="1.0" ?>
+<llsd>
+<map>
+ <key>package_description</key>
+ <map>
+ <key>description</key>
+ <string>include files referenced in the llphysicsextensions</string>
+ <key>license</key>
+ <string>LGPL</string>
+ <key>license_file</key>
+ <string>LICENSES/LGPL-licence.txt</string>
+ <key>name</key>
+ <string>pathfinding_includes</string>
+ <key>platforms</key>
+ <map>
+ <key>common</key>
+ <map>
+ <key>build_directory</key>
+ <string>stage</string>
+ <key>configurations</key>
+ <map>
+ <key>Debug</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>True</string>
+ <key>name</key>
+ <string>Debug</string>
+ </map>
+ <key>Release</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>True</string>
+ <key>name</key>
+ <string>Release</string>
+ </map>
+ <key>RelWithDebInfo</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>True</string>
+ <key>name</key>
+ <string>RelWithDebInfo</string>
+ </map>
+ </map>
+ <key>name</key>
+ <string>common</string>
+ </map>
+ <key>darwin</key>
+ <map>
+ <key>build_directory</key>
+ <string>stage</string>
+ <key>configurations</key>
+ <map>
+ <key>Debug</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>true</string>
+ <key>manifest</key>
+ <array>
+ <string>include/</string>
+ <string>LICENSES</string>
+ </array>
+ <key>name</key>
+ <string>Debug</string>
+ </map>
+ <key>Release</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>true</string>
+ <key>manifest</key>
+ <array>
+ <string>include/</string>
+ <string>LICENSES</string>
+ </array>
+ <key>name</key>
+ <string>Release</string>
+ </map>
+ <key>RelWithDebInfo</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>true</string>
+ <key>manifest</key>
+ <array>
+ <string>include/</string>
+ <string>LICENSES</string>
+ </array>
+ <key>name</key>
+ <string>RelWithDebInfo</string>
+ </map>
+ </map>
+ <key>name</key>
+ <string>darwin</string>
+ </map>
+ <key>linux</key>
+ <map>
+ <key>build_directory</key>
+ <string>stage</string>
+ <key>configurations</key>
+ <map>
+ <key>Debug</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>true</string>
+ <key>manifest</key>
+ <array>
+ <string>include/</string>
+ <string>LICENSES</string>
+ </array>
+ <key>name</key>
+ <string>Debug</string>
+ </map>
+ <key>Release</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>true</string>
+ <key>manifest</key>
+ <array>
+ <string>include/</string>
+ <string>LICENSES</string>
+ </array>
+ <key>name</key>
+ <string>Release</string>
+ </map>
+ <key>RelWithDebInfo</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>true</string>
+ <key>manifest</key>
+ <array>
+ <string>include/</string>
+ <string>LICENSES</string>
+ </array>
+ <key>name</key>
+ <string>RelWithDebInfo</string>
+ </map>
+ </map>
+ <key>name</key>
+ <string>linux</string>
+ </map>
+ <key>windows</key>
+ <map>
+ <key>build_directory</key>
+ <string>stage</string>
+ <key>configurations</key>
+ <map>
+ <key>Debug</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>true</string>
+ <key>manifest</key>
+ <array>
+ <string>include/</string>
+ <string>LICENSES</string>
+ </array>
+ <key>name</key>
+ <string>Debug</string>
+ </map>
+ <key>Release</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>true</string>
+ <key>manifest</key>
+ <array>
+ <string>include/</string>
+ <string>LICENSES</string>
+ </array>
+ <key>name</key>
+ <string>Release</string>
+ </map>
+ <key>RelWithDebInfo</key>
+ <map>
+ <key>build</key>
+ <map>
+ <key>command</key>
+ <string>bash</string>
+ <key>options</key>
+ <array>
+ <string>-c ../package_physicsextensions.sh</string>
+ </array>
+ </map>
+ <key>default</key>
+ <string>true</string>
+ <key>manifest</key>
+ <array>
+ <string>include/</string>
+ <string>LICENSES</string>
+ </array>
+ <key>name</key>
+ <string>RelWithDebInfo</string>
+ </map>
+ </map>
+ <key>name</key>
+ <string>windows</string>
+ </map>
+ </map>
+ <key>version</key>
+ <string>1.0</string>
+ </map>
+ <key>type</key>
+ <string>autobuild</string>
+ <key>version</key>
+ <string>1.2</string>
+ </map>
+</llsd>