summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autobuild.xml74
-rw-r--r--indra/cmake/Havok.cmake52
-rw-r--r--indra/cmake/LLConvexDecomposition.cmake10
-rw-r--r--indra/cmake/LLPathingLib.cmake16
-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/llrender/CMakeLists.txt2
-rw-r--r--indra/llrender/llrendernavprim.cpp86
-rw-r--r--indra/llrender/llrendernavprim.h54
-rw-r--r--indra/llrender/llvertexbuffer.cpp4509
-rw-r--r--indra/newview/CMakeLists.txt7
-rw-r--r--indra/newview/llnavmeshstation.cpp175
-rw-r--r--indra/newview/llnavmeshstation.h93
-rw-r--r--indra/newview/llspatialpartition.cpp9498
-rw-r--r--indra/newview/llviewermenufile.cpp2620
-rw-r--r--indra/newview/llviewerregion.cpp5
-rw-r--r--indra/newview/pipeline.cpp31
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml37
23 files changed, 8990 insertions, 8292 deletions
diff --git a/autobuild.xml b/autobuild.xml
index 9914be6867..4726b737a6 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -1098,7 +1098,7 @@
<key>llconvexdecomposition</key>
<map>
<key>license</key>
- <string>havok</string>
+ <string>llconvexdecomposition</string>
<key>license_file</key>
<string>on_file</string>
<key>name</key>
@@ -1759,6 +1759,78 @@
</map>
</map>
</map>
+ <key>havok</key>
+ <map>
+ <key>license</key>
+ <string>havok-ares</string>
+ <key>license_file</key>
+ <string>LICENSES/havok.txt</string>
+ <key>name</key>
+ <string>havok</string>
+ <key>platforms</key>
+ <map>
+ <key>darwin</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>hash</key>
+ <string>286741271be3dc3473af3044ad6d002e</string>
+ <key>url</key>
+ <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-havok/rev/224189/arch/Darwin/installer/havok-0.1-darwin-20110316.tar.bz2</string>
+ </map>
+ <key>name</key>
+ <string>darwin</string>
+ </map>
+ <key>linux</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>hash</key>
+ <string>1b6c85fc7705779cd2c2b02f7437fb24</string>
+ <key>url</key>
+ <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-havok/rev/224189/arch/Linux/installer/havok-0.1-linux-20110316.tar.bz2</string>
+ </map>
+ <key>name</key>
+ <string>linux</string>
+ </map>
+ <key>windows</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>hash</key>
+ <string>534c5217692bef44a60a3cf26ccb08d9</string>
+ <key>url</key>
+ <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-havok-source_Windows-vs2010/rev/245961/arch/CYGWIN/installer/havok_source-2011.2-windows-20111130.tar.bz2</string>
+ </map>
+ <key>name</key>
+ <string>windows</string>
+ </map>
+ </map>
+ </map>
+ <key>llpathinglib</key>
+ <map>
+ <key>license</key>
+ <string>llpathinglib</string>
+ <key>license_file</key>
+ <string>LICENSES/llpathinglib.txt</string>
+ <key>name</key>
+ <string>llpathinglib</string>
+ <key>platforms</key>
+ <map>
+ <key>windows</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>hash</key>
+ <string>ce6c326d37c99a10a13a4de806205466</string>
+ <key>url</key>
+ <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/stinson_llpathinglibrary/rev/246580/arch/CYGWIN/installer/llpathinglibrary-0.1-windows-20111212.tar.bz2</string>
+ </map>
+ <key>name</key>
+ <string>windows</string>
+ </map>
+ </map>
+ </map>
</map>
<key>package_description</key>
<map>
diff --git a/indra/cmake/Havok.cmake b/indra/cmake/Havok.cmake
new file mode 100644
index 0000000000..1b355792d0
--- /dev/null
+++ b/indra/cmake/Havok.cmake
@@ -0,0 +1,52 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+use_prebuilt_binary(havok)
+set(Havok_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/libraries/include/havok/Source)
+if (CMAKE_BUILD_TYPE MATCHES "Debug")
+ set(HAVOK_LIBRARY_PATH ${LIBS_PREBUILT_DIR}/libraries/i686-win32/lib/debug/havok)
+else (CMAKE_BUILD_TYPE MATCHES "Debug")
+ set(HAVOK_LIBRARY_PATH ${LIBS_PREBUILT_DIR}/libraries/i686-win32/lib/release/havok)
+endif (CMAKE_BUILD_TYPE MATCHES "Debug")
+
+find_library(HK_BASE_LIB hkBase PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_COMPAT_LIB hkCompat PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_GEOMETRY_UTILITIES_LIB hkGeometryUtilities PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_INTERNAL_LIB hkInternal PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_SERIALIZE_LIB hkSerialize PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_SCENEDATA_LIB hkSceneData PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_PHYS_COLLIDE_LIB hkpCollide PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_PHYS_UTILITIES_LIB hkpUtilities PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_PHYS_CONSTRAINTSOLVER_LIB hkpConstraintSolver PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_PHYS_DYNAMICS_LIB hkpDynamics PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_PHYS_INTERNAL_LIB hkpInternal PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_AI_INTERNAL_LIB hkaiInternal PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_AI_PATHFINDING_LIB hkaiPathfinding PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_AI_AIPHYSICSBRIDGE_LIB hkaiaiphysicsbridge PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_PHYS_UTILITIES_LIB hkputilities PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_CD_INTERNAL_LIB hkcdinternal PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_PHYS_VEHICLE_LIB hkpVehicle PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_VISUALIZE_LIB hkVisualize PATHS ${HAVOK_LIBRARY_PATH})
+find_library(HK_AI_VISUALIZE_LIB hkaiVisualize PATHS ${HAVOK_LIBRARY_PATH})
+
+set(HK_LIBRARIES
+ ${HK_BASE_LIB}
+ ${HK_COMPAT_LIB}
+ ${HK_GEOMETRY_UTILITIES_LIB}
+ ${HK_INTERNAL_LIB}
+ ${HK_SERIALIZE_LIB}
+ ${HK_SCENEDATA_LIB}
+ ${HK_PHYS_COLLIDE_LIB}
+ ${HK_PHYS_UTILITIES_LIB}
+ ${HK_PHYS_CONSTRAINTSOLVER_LIB}
+ ${HK_PHYS_DYNAMICS_LIB}
+ ${HK_PHYS_INTERNAL_LIB}
+ ${HK_AI_INTERNAL_LIB}
+ ${HK_AI_PATHFINDING_LIB}
+ ${HK_AI_AIPHYSICSBRIDGE_LIB}
+ ${HK_PHYS_UTILITIES_LIB}
+ ${HK_CD_INTERNAL_LIB}
+ ${HK_PHYS_VEHICLE_LIB}
+ ${HK_VISUALIZE_LIB}
+ ${HK_AI_VISUALIZE_LIB}
+)
diff --git a/indra/cmake/LLConvexDecomposition.cmake b/indra/cmake/LLConvexDecomposition.cmake
index 8e44504782..1731853300 100644
--- a/indra/cmake/LLConvexDecomposition.cmake
+++ b/indra/cmake/LLConvexDecomposition.cmake
@@ -3,10 +3,10 @@ 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)
+# 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)
+# endif (INSTALL_PROPRIETARY AND NOT STANDALONE)
diff --git a/indra/cmake/LLPathingLib.cmake b/indra/cmake/LLPathingLib.cmake
new file mode 100644
index 0000000000..b92190ad0a
--- /dev/null
+++ b/indra/cmake/LLPathingLib.cmake
@@ -0,0 +1,16 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+use_prebuilt_binary(llpathinglib)
+set(LLPATHING_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/libraries/include)
+if (CMAKE_BUILD_TYPE MATCHES "Debug")
+ set(LLPATHING_LIBRARY_PATH ${LIBS_PREBUILT_DIR}/lib/debug)
+else (CMAKE_BUILD_TYPE MATCHES "Debug")
+ set(LLPATHING_LIBRARY_PATH ${LIBS_PREBUILT_DIR}/lib/release)
+endif (CMAKE_BUILD_TYPE MATCHES "Debug")
+
+find_library(LL_PATHING_LIB llpathinglib PATHS ${LLPATHING_LIBRARY_PATH})
+
+set(LLPATHING_LIBRARIES
+ ${LL_PATHING_LIB}
+)
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/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..96027ba65b
--- /dev/null
+++ b/indra/llrender/llrendernavprim.cpp
@@ -0,0 +1,86 @@
+/**
+ * @file LLRenderNavPrim.cpp
+ * @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$
+ */
+
+
+#include "linden_common.h"
+#include "llrendernavprim.h"
+#include "llerror.h"
+#include "llglheaders.h"
+#include "llvertexbuffer.h"
+//=============================================================================
+LLRenderNavPrim gRenderNav;
+//=============================================================================
+void LLRenderNavPrim::renderSegment( const LLVector3& start, const LLVector3& end, int color ) const
+{
+
+ LLColor4 colorA( color );
+ glLineWidth(1.5f);
+ gGL.color3fv( colorA.mV );
+
+ gGL.begin(LLRender::LINES);
+ {
+ gGL.vertex3fv( start.mV );
+ gGL.vertex3fv( end.mV );
+ }
+ gGL.end();
+ glLineWidth(1.0f);
+}
+//=============================================================================
+void LLRenderNavPrim::renderTri( const LLVector3& a, const LLVector3& b, const LLVector3& c, int color ) const
+{
+ glPolygonMode(GL_NONE, GL_FILL);
+ LLGLDisable cull(GL_CULL_FACE);
+ //LLGLEnable lighting( GL_LIGHTING );
+ //glEnable(GL_POLYGON_STIPPLE);
+ glLineWidth(1.5f);
+ LLColor4 colorA( color );
+ colorA*=2.0f;
+ gGL.color4fv( colorA.mV );
+
+ gGL.begin(LLRender::TRIANGLES);
+ {
+ gGL.vertex3fv( a.mV );
+ gGL.vertex3fv( b.mV );
+ gGL.vertex3fv( c.mV );
+ }
+ gGL.end();
+ gGL.flush();
+}
+//=============================================================================
+void LLRenderNavPrim::renderNavMeshVB( LLVertexBuffer* pVBO, int vertCnt )
+{
+ glPolygonMode(GL_NONE, GL_FILL);
+ LLGLDisable cull(GL_CULL_FACE);
+ glColor3f ( 1.0f, 0.0f, 0.0f ) ;
+ glDisable( GL_COLOR_MATERIAL );
+
+ pVBO->setBuffer( LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR );
+ pVBO->drawArrays( LLRender::TRIANGLES, 0, vertCnt );
+ gGL.flush();
+ glEnable( GL_COLOR_MATERIAL );
+
+}
+//=============================================================================
diff --git a/indra/llrender/llrendernavprim.h b/indra/llrender/llrendernavprim.h
new file mode 100644
index 0000000000..cd57fd2223
--- /dev/null
+++ b/indra/llrender/llrendernavprim.h
@@ -0,0 +1,54 @@
+/**
+ * @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 ) const;
+ //Draw simple tri
+ void renderTri( const LLVector3& a, const LLVector3& b, const LLVector3& c, int color ) const;
+ //Draw the contents of vertex buffer
+ void renderNavMeshVB( LLVertexBuffer* pVBO, int vertCnt );
+
+private:
+};
+
+extern LLRenderNavPrim gRenderNav;
+
+#endif
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index 20a450fbfb..50b0c1a7dd 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -1,2254 +1,2255 @@
-/**
- * @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;
-}
-
-U8* LLVBOPool::allocate(U32& name, U32 size)
-{
- llassert(nhpo2(size) == size);
-
- U32 i = wpo2(size);
-
- if (mFreeList.size() <= i)
- {
- mFreeList.resize(i+1);
- }
-
- U8* ret = NULL;
-
- if (mFreeList[i].empty())
- {
- //make a new buffer
- glGenBuffersARB(1, &name);
- glBindBufferARB(mType, name);
- glBufferDataARB(mType, size, 0, mUsage);
- LLVertexBuffer::sAllocatedBytes += size;
-
- if (LLVertexBuffer::sDisableVBOMapping)
- {
- ret = (U8*) ll_aligned_malloc_16(size);
- }
- 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, 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;
-
- 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(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);
-
- 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);
-
- 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);
-
- 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
- mUsage = GL_DYNAMIC_DRAW_ARB;
- }
-
- //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, 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, 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;
-}
-
-// Map for data access
-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 (sDisableVBOMapping || 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, !sDisableVBOMapping && 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(sDisableVBOMapping)
- {
- map_range = false;
- }
- else
- {
- U8* src = NULL;
- waitFence();
- if (gGLManager.mHasMapBufferRange)
- {
- if (map_range)
- {
-#ifdef GL_ARB_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;
- }
- }
-
- 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<U8>(src);
- mAlignedOffset = mMappedData - src;
-
- stop_glerror();
- }
-
- if (!mMappedData)
- {
- log_glerror();
-
- //check the availability of memory
- LLMemory::logMemoryInfo(TRUE) ;
-
- if(!sDisableVBOMapping)
- {
- //--------------------
- //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 && !sDisableVBOMapping)
- {
- return mMappedData;
- }
- else
- {
- return mMappedData+mOffsets[type]+sTypeSize[type]*index;
- }
-}
-
-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 (sDisableVBOMapping || 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, !sDisableVBOMapping && 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(sDisableVBOMapping)
- {
- map_range = false;
- }
- else
- {
- U8* src = NULL;
- waitFence();
- if (gGLManager.mHasMapBufferRange)
- {
- if (map_range)
- {
-#ifdef GL_ARB_map_buffer_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
- 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
- {
- 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(!sDisableVBOMapping)
- {
- 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 && !sDisableVBOMapping)
- {
- return mMappedIndexData;
- }
- else
- {
- return mMappedIndexData + sizeof(U16)*index;
- }
-}
-
-void LLVertexBuffer::unmapBuffer()
-{
- LLMemType mt2(LLMemType::MTYPE_VERTEX_UNMAP_BUFFER);
- if (!useVBOs())
- {
- return ; //nothing to unmap
- }
-
- bool updated_all = false ;
-
- if (mMappedData && mVertexLocked)
- {
- bindGLBuffer(true);
- updated_all = mIndexLocked; //both vertex and index buffers done updating
-
- if(sDisableVBOMapping)
- {
- 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, mMappedData+offset);
- stop_glerror();
- }
-
- mMappedVertexRegions.clear();
- }
- else
- {
- stop_glerror();
- glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, getSize(), 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)
- {
-#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)
- {
- bindGLIndices();
- if(sDisableVBOMapping)
- {
- 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, mMappedIndexData+offset);
- stop_glerror();
- }
-
- mMappedIndexRegions.clear();
- }
- else
- {
- stop_glerror();
- glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, getIndicesSize(), 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)
- {
-#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)
- {
- 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];
-
- 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();
- 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;
+}
+
+U8* LLVBOPool::allocate(U32& name, U32 size)
+{
+ llassert(nhpo2(size) == size);
+
+ U32 i = wpo2(size);
+
+ if (mFreeList.size() <= i)
+ {
+ mFreeList.resize(i+1);
+ }
+
+ U8* ret = NULL;
+
+ if (mFreeList[i].empty())
+ {
+ //make a new buffer
+ glGenBuffersARB(1, &name);
+ glBindBufferARB(mType, name);
+ glBufferDataARB(mType, size, 0, mUsage);
+ LLVertexBuffer::sAllocatedBytes += size;
+
+ if (LLVertexBuffer::sDisableVBOMapping)
+ {
+ ret = (U8*) ll_aligned_malloc_16(size);
+ }
+ 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, 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;
+
+ 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(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);
+
+ 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);
+
+ 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);
+
+ 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
+ mUsage = GL_DYNAMIC_DRAW_ARB;
+ }
+
+ //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, 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, 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;
+}
+
+// Map for data access
+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 (sDisableVBOMapping || 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, !sDisableVBOMapping && 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(sDisableVBOMapping)
+ {
+ map_range = false;
+ }
+ else
+ {
+ U8* src = NULL;
+ waitFence();
+ if (gGLManager.mHasMapBufferRange)
+ {
+ if (map_range)
+ {
+#ifdef GL_ARB_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;
+ }
+ }
+
+ 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<U8>(src);
+ mAlignedOffset = mMappedData - src;
+
+ stop_glerror();
+ }
+
+ if (!mMappedData)
+ {
+ log_glerror();
+
+ //check the availability of memory
+ LLMemory::logMemoryInfo(TRUE) ;
+
+ if(!sDisableVBOMapping)
+ {
+ //--------------------
+ //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 && !sDisableVBOMapping)
+ {
+ return mMappedData;
+ }
+ else
+ {
+ return mMappedData+mOffsets[type]+sTypeSize[type]*index;
+ }
+}
+
+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 (sDisableVBOMapping || 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, !sDisableVBOMapping && 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(sDisableVBOMapping)
+ {
+ map_range = false;
+ }
+ else
+ {
+ U8* src = NULL;
+ waitFence();
+ if (gGLManager.mHasMapBufferRange)
+ {
+ if (map_range)
+ {
+#ifdef GL_ARB_map_buffer_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
+ 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
+ {
+ 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(!sDisableVBOMapping)
+ {
+ 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 && !sDisableVBOMapping)
+ {
+ return mMappedIndexData;
+ }
+ else
+ {
+ return mMappedIndexData + sizeof(U16)*index;
+ }
+}
+
+void LLVertexBuffer::unmapBuffer()
+{
+ LLMemType mt2(LLMemType::MTYPE_VERTEX_UNMAP_BUFFER);
+ if (!useVBOs())
+ {
+ return ; //nothing to unmap
+ }
+
+ bool updated_all = false ;
+
+ if (mMappedData && mVertexLocked)
+ {
+ bindGLBuffer(true);
+ updated_all = mIndexLocked; //both vertex and index buffers done updating
+
+ if(sDisableVBOMapping)
+ {
+ 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, mMappedData+offset);
+ stop_glerror();
+ }
+
+ mMappedVertexRegions.clear();
+ }
+ else
+ {
+ stop_glerror();
+ glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, getSize(), 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)
+ {
+#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)
+ {
+ bindGLIndices();
+ if(sDisableVBOMapping)
+ {
+ 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, mMappedIndexData+offset);
+ stop_glerror();
+ }
+
+ mMappedIndexRegions.clear();
+ }
+ else
+ {
+ stop_glerror();
+ glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, getIndicesSize(), 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)
+ {
+#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)
+ {
+ 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];
+
+ 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();
+ 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/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 6b2fe1e45a..ca6617a5ed 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -13,6 +13,7 @@ include(EXPAT)
include(FMOD)
include(OPENAL)
include(FindOpenGL)
+include(Havok)
include(JsonCpp)
include(LLAudio)
include(LLCharacter)
@@ -23,6 +24,7 @@ include(LLImageJ2COJ)
include(LLInventory)
include(LLMath)
include(LLMessage)
+include(LLPathingLib)
include(LLPlugin)
include(LLPrimitive)
include(LLRender)
@@ -52,6 +54,7 @@ include_directories(
${LLCHARACTER_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLCONVEXDECOMP_INCLUDE_DIRS}
+ ${LLPATHING_INCLUDE_DIRS}
${FMOD_INCLUDE_DIR}
${LLIMAGE_INCLUDE_DIRS}
${LLKDU_INCLUDE_DIRS}
@@ -326,6 +329,7 @@ set(viewer_SOURCE_FILES
llnameeditor.cpp
llnamelistctrl.cpp
llnavigationbar.cpp
+ llnavmeshstation.cpp
llnearbychat.cpp
llnearbychatbar.cpp
llnearbychathandler.cpp
@@ -880,6 +884,7 @@ set(viewer_HEADER_FILES
llnameeditor.h
llnamelistctrl.h
llnavigationbar.h
+ llnavmeshstation.h
llnearbychat.h
llnearbychatbar.h
llnearbychathandler.h
@@ -1725,6 +1730,8 @@ 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}
+ ${HK_LIBRARIES}
${UPDATER_LIBRARIES}
${GOOGLE_PERFTOOLS_LIBRARIES}
${LLAUDIO_LIBRARIES}
diff --git a/indra/newview/llnavmeshstation.cpp b/indra/newview/llnavmeshstation.cpp
new file mode 100644
index 0000000000..24c4979298
--- /dev/null
+++ b/indra/newview/llnavmeshstation.cpp
@@ -0,0 +1,175 @@
+/**
+ * @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"
+//===============================================================================
+LLNavMeshStation::LLNavMeshStation()
+{
+}
+//===============================================================================
+class LLNavMeshUploadResponder : public LLCurl::Responder
+{
+public:
+ LLNavMeshUploadResponder( const LLHandle<LLNavMeshObserver>& observer_handle )
+ : mObserverHandle( observer_handle )
+ {
+ }
+
+ void clearPendingRequests ( void )
+ {
+ }
+
+ void error( U32 statusNum, const std::string& reason )
+ {
+ statusNum;
+ llwarns << "Transport error "<<reason<<llendl;
+ }
+
+ void result( const LLSD& content )
+ {
+ llinfos<<"Content received"<<llendl;
+ //TODO# some sanity checking
+ if ( content.has("error") )
+ {
+ llwarns << "Error on fetched data"<< llendl;
+ }
+ else
+ {
+ LLNavMeshObserver* pObserver = mObserverHandle.get();
+ if ( pObserver )
+ {
+ llinfos<<"Do something immensely important w/content"<<llendl;
+ //pObserver->execute();
+ }
+ }
+ }
+
+private:
+ //Observer handle
+ LLHandle<LLNavMeshObserver> mObserverHandle;
+};
+//===============================================================================
+class LLNavMeshDownloadResponder : public LLCurl::Responder
+{
+public:
+ LLNavMeshDownloadResponder( const LLHandle<LLNavMeshDownloadObserver>& observer_handle )
+ : mObserverHandle( observer_handle )
+ {
+ }
+
+ void clearPendingRequests ( void )
+ {
+ }
+
+ void error( U32 statusNum, const std::string& reason )
+ {
+ statusNum;
+ llwarns << "Transport error "<<reason<<llendl;
+ }
+
+ void result( const LLSD& content )
+ {
+ llinfos<<"Content received"<<llendl;
+ //TODO# some sanity checking
+ 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") )
+ {
+ LLSD::Binary& stuff = content["navmesh_data"].asBinary();
+ LLPathingLib::getInstance()->extractNavMeshSrcFromLLSD( stuff );
+ }
+ else
+ {
+ llwarns<<"no mesh data "<<llendl;
+ }
+ }
+ }
+}
+
+private:
+ //Observer handle
+ LLHandle<LLNavMeshDownloadObserver> mObserverHandle;
+};
+
+//===============================================================================
+bool LLNavMeshStation::postNavMeshToServer( LLSD& data, const LLHandle<LLNavMeshObserver>& observerHandle )
+{
+ mCurlRequest = new LLCurlRequest();
+
+ if ( mNavMeshUploadURL.empty() )
+ {
+ llinfos << "Unable to upload navmesh because of missing URL" << llendl;
+ }
+ else
+ {
+ LLCurlRequest::headers_t headers;
+ mCurlRequest->post( mNavMeshUploadURL, headers, data,
+ new LLNavMeshUploadResponder(/*this, data,*/ observerHandle ) );
+ do
+ {
+ mCurlRequest->process();
+ //sleep for 10ms to prevent eating a whole core
+ apr_sleep(10000);
+ } while ( mCurlRequest->getQueued() > 0 );
+ }
+
+ delete mCurlRequest;
+
+ mCurlRequest = NULL;
+
+ return true;
+}
+//===============================================================================
+void LLNavMeshStation::downloadNavMeshSrc( const LLHandle<LLNavMeshDownloadObserver>& observerHandle )
+{
+ 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 ) );
+ }
+}
+//=============================================================================== \ No newline at end of file
diff --git a/indra/newview/llnavmeshstation.h b/indra/newview/llnavmeshstation.h
new file mode 100644
index 0000000000..5436694b41
--- /dev/null
+++ b/indra/newview/llnavmeshstation.h
@@ -0,0 +1,93 @@
+/**
+ * @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 LLMessageSystem;
+//===============================================================================
+class LLNavMeshObserver
+{
+public:
+ //Ctor
+ LLNavMeshObserver() { mObserverHandle.bind(this); }
+ //Dtor
+ virtual ~LLNavMeshObserver() {}
+ //Accessor for the observers handle
+ const LLHandle<LLNavMeshObserver>& getObserverHandle() const { return mObserverHandle; }
+
+protected:
+ LLRootHandle<LLNavMeshObserver> mObserverHandle;
+};
+//===============================================================================
+//prep#TODO# determine if a name change is needed?
+class LLNavMeshDownloadObserver
+{
+public:
+ //Ctor
+ LLNavMeshDownloadObserver() { mObserverHandle.bind(this); }
+ //Dtor
+ virtual ~LLNavMeshDownloadObserver() {}
+ //Accessor for the observers handle
+ const LLHandle<LLNavMeshDownloadObserver>& getObserverHandle() const { return mObserverHandle; }
+
+protected:
+ LLRootHandle<LLNavMeshDownloadObserver> mObserverHandle;
+};
+//===============================================================================
+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 );
+
+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 \ No newline at end of file
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 3e16ccf3da..99af27a63c 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -1,4748 +1,4750 @@
-/**
- * @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 "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->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),
- 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;
- 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);
- }
- }
-
- 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");
-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);
- }
- 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);
-
- {
- 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)
-{
- 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);
- if (!face.mOctree)
- {
- ((LLVolumeFace*) &face)->createOctree();
- }
-
- 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);
-
- F32 t = 1.f;
-
- LLRenderOctreeRaycast render(starta, dir, &t);
- 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);
- }
-
- 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 "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->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),
+ 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;
+ 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);
+ }
+ }
+
+ 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");
+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);
+ }
+ 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);
+
+ {
+ 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)
+{
+ 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);
+ if (!face.mOctree)
+ {
+ ((LLVolumeFace*) &face)->createOctree();
+ }
+
+ 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);
+
+ F32 t = 1.f;
+
+ LLRenderOctreeRaycast render(starta, dir, &t);
+ 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);
+ }
+
+ 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/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 7e830e14bf..850bf35c61 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -1,1272 +1,1348 @@
-/**
- * @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"
+#include "llpathinglib.h"
+#include "llnavmeshstation.h"
+
+// system libraries
+#include <boost/tokenizer.hpp>
+
+class LLBuildNavMesh : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ bool result = true;
+ return result;
+ }
+};
+class LLNavMeshRenderingToggle : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ if ( LLPathingLib::getInstance() )
+ {
+ LLPathingLib::getInstance()->toggleRenderNavMeshState( );
+ }
+ return true;
+ }
+};
+class LLNavMeshShapeRenderingToggle : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ if ( LLPathingLib::getInstance() )
+ {
+ LLPathingLib::getInstance()->toggleRenderShapeState( );
+ }
+ return true;
+ }
+};
+//prep#
+class LLPathingTools : public view_listener_t, LLNavMeshDownloadObserver
+{
+
+ bool handleEvent(const LLSD& userdata)
+ {
+ //make sure we have a pathing system
+ if ( !LLPathingLib::getInstance() )
+ {
+ LLPathingLib::initSystem();
+ }
+ //prep# test remove
+ //LLSD content;
+ //LLPathingLib::getInstance()->extractNavMeshSrcFromLLSD( content );
+ //return true;
+ //prep# end test
+ if ( LLPathingLib::getInstance() == NULL )
+ {
+ llinfos<<"No implementation of pathing library."<<llendl;
+ }
+ else
+ {
+ //make sure the region is essentially enabled for navmesh support
+ std::string capability = "RetrieveNavMeshSrc";
+ std::string url = gAgent.getRegion()->getCapability( capability );
+ if ( !url.empty() )
+ {
+ llinfos<<"Region has required caps of type ["<<capability<<"]"<<llendl;
+ LLNavMeshStation::getInstance()->setNavMeshDownloadURL( url );
+ LLNavMeshStation::getInstance()->downloadNavMeshSrc( getObserverHandle() );
+ }
+ else
+ {
+ llinfos<<"Region has does not required caps of type ["<<capability<<"]"<<llendl;
+ }
+ }
+ return true;
+ }
+};
+
+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");
+
+ //prep#
+ view_listener_t::addCommit(new LLPathingTools(), "PathingTools.RetrieveSrc");
+ view_listener_t::addCommit(new LLNavMeshRenderingToggle(), "PathingTools.ToggleNavMeshView");
+ view_listener_t::addCommit(new LLNavMeshShapeRenderingToggle(), "PathingTools.ToggleNavMeshShapeView");
+ // "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 ed943964f9..166890555f 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1518,7 +1518,8 @@ 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("ParcelPropertiesUpdate");
capabilityNames.append("ParcelMediaURLFilterList");
@@ -1529,6 +1530,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");
@@ -1554,7 +1556,6 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("ViewerMetrics");
capabilityNames.append("ViewerStartAuction");
capabilityNames.append("ViewerStats");
-
// Please add new capabilities alphabetically to reduce
// merge conflicts.
}
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 657cdc0e07..cc94882f8c 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
@@ -3789,13 +3789,37 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sDefaultImagep);
LLViewerFetchedTexture::sDefaultImagep->setAddressMode(LLTexUnit::TAM_WRAP);
+
+ {
+
+ if ( LLPathingLib::getInstance() )
+ {
+ //prep#
+ enableLightsFullbright(LLColor4(1,1,1,1));
+
+ bool exclusiveDraw = false;
+ if ( LLPathingLib::getInstance()->getRenderNavMeshState() )
+ {
+ LLPathingLib::getInstance()->renderNavMesh();
+ exclusiveDraw = true;
+ }
+ if ( LLPathingLib::getInstance()->getRenderShapeState() )
+ {
+ LLPathingLib::getInstance()->renderNavMeshShapesVBO();
+ exclusiveDraw = true;
+ }
+
+ if ( exclusiveDraw ) { return; }
+ }
+ }
+
//////////////////////////////////////////////
//
// Actually render all of the geometry
//
//
stop_glerror();
-
+
LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderDrawPools");
for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
@@ -3928,7 +3952,7 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
renderDebug();
LLVertexBuffer::unbind();
-
+
if (!LLPipeline::sReflectionRender && !LLPipeline::sRenderDeferred)
{
if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
@@ -8159,6 +8183,7 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in)
gGL.setColorMask(true, false);
renderGeom(camera);
+
}
LLPipeline::sUnderWaterRender = FALSE;
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 1834be2d48..28e9052ddb 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -691,6 +691,35 @@
<menu_item_check.on_enable
function="Build.Enabled" />
</menu_item_check>
+
+ <menu
+ create_jump_keys="true"
+ label="Pathing Tools"
+ name="Pathing Tools"
+ tear_off="true"
+ visible="true">
+ <menu_item_call
+ label="Retrieve Navigation Mesh Src"
+ name="Show Pathing Tools">
+ <menu_item_call.on_click
+ function="PathingTools.RetrieveSrc" />
+ </menu_item_call>
+
+ <menu_item_call
+ label="Toggle Navigation Mesh Display"
+ name="Toggle Navigation Mesh View">
+ <menu_item_call.on_click
+ function="PathingTools.ToggleNavMeshView" />
+ </menu_item_call>
+
+ <menu_item_call
+ label="Toggle Navigation Mesh Shape Display"
+ name="Toggle Navigation Mesh Shape View">
+ <menu_item_call.on_click
+ function="PathingTools.ToggleNavMeshShapeView" />
+ </menu_item_call>
+ </menu>
+
<menu
create_jump_keys="true"
label="Select Build Tool"
@@ -1133,6 +1162,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"