summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorDave Parks <davep@lindenlab.com>2010-08-26 14:23:12 -0500
committerDave Parks <davep@lindenlab.com>2010-08-26 14:23:12 -0500
commit71de5f622a7917f78823a7e7840194e1b0f8f070 (patch)
tree1bfa580e5262ae6a8271ade6aee08f21a019c688 /indra
parentfc7a3f6daab94331aab52983caee5c68db8cd772 (diff)
Add missing files from viewer-experimental
Diffstat (limited to 'indra')
-rw-r--r--indra/cmake/GLOD.cmake9
-rw-r--r--indra/cmake/LLConvexDecomposition.cmake16
-rw-r--r--indra/llmath/llmatrix3a.cpp140
-rw-r--r--indra/llmath/llmatrix3a.h134
-rw-r--r--indra/llmath/llmatrix3a.inl125
-rw-r--r--indra/llmath/llmatrix4a.h149
-rw-r--r--indra/llmath/llquaternion2.h111
-rw-r--r--indra/llmath/llquaternion2.inl108
-rw-r--r--indra/llmath/llsimdmath.h95
-rw-r--r--indra/llmath/llsimdtypes.h130
-rw-r--r--indra/llmath/llsimdtypes.inl163
-rw-r--r--indra/llmath/llvector4a.cpp228
-rw-r--r--indra/llmath/llvector4a.h331
-rw-r--r--indra/llmath/llvector4a.inl599
-rw-r--r--indra/llmath/llvector4logical.h130
-rw-r--r--indra/llmath/llvolumeoctree.cpp208
-rw-r--r--indra/llmath/llvolumeoctree.h138
-rw-r--r--indra/llprimitive/llmodel.cpp1695
-rw-r--r--indra/llprimitive/llmodel.h179
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl30
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl75
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl16
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl25
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl37
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl33
-rw-r--r--indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl15
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl39
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl13
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl37
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl39
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl39
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl84
-rw-r--r--indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl29
-rw-r--r--indra/newview/llfloaterimportcollada.cpp1116
-rw-r--r--indra/newview/llfloaterimportcollada.h143
-rw-r--r--indra/newview/llfloatermodelpreview.cpp3382
-rw-r--r--indra/newview/llfloatermodelpreview.h275
-rw-r--r--indra/newview/llmeshreduction.cpp291
-rw-r--r--indra/newview/llmeshreduction.h59
-rw-r--r--indra/newview/llmeshrepository.cpp2676
-rw-r--r--indra/newview/llmeshrepository.h471
41 files changed, 13612 insertions, 0 deletions
diff --git a/indra/cmake/GLOD.cmake b/indra/cmake/GLOD.cmake
new file mode 100644
index 0000000000..77221d55ed
--- /dev/null
+++ b/indra/cmake/GLOD.cmake
@@ -0,0 +1,9 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+if (NOT STANDALONE)
+ use_prebuilt_binary(GLOD)
+endif (NOT STANDALONE)
+
+set(GLOD_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
+set(GLOD_LIBRARIES glod)
diff --git a/indra/cmake/LLConvexDecomposition.cmake b/indra/cmake/LLConvexDecomposition.cmake
new file mode 100644
index 0000000000..ae9dc3c17a
--- /dev/null
+++ b/indra/cmake/LLConvexDecomposition.cmake
@@ -0,0 +1,16 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+set(LLCONVEXDECOMP_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
+
+if (INSTALL_PROPRIETARY AND NOT STANDALONE)
+ use_prebuilt_binary(llconvexdecomposition)
+ if (WINDOWS)
+ set(LLCONVEXDECOMP_LIBRARY llconvexdecomposition)
+ else (WINDOWS)
+ set(LLCONVEXDECOMP_LIBRARY llconvexdecompositionstub)
+ endif (WINDOWS)
+else (INSTALL_PROPRIETARY AND NOT STANDALONE)
+ use_prebuilt_binary(llconvexdecompositionstub)
+ set(LLCONVEXDECOMP_LIBRARY llconvexdecompositionstub)
+endif (INSTALL_PROPRIETARY AND NOT STANDALONE)
diff --git a/indra/llmath/llmatrix3a.cpp b/indra/llmath/llmatrix3a.cpp
new file mode 100644
index 0000000000..b7468f4914
--- /dev/null
+++ b/indra/llmath/llmatrix3a.cpp
@@ -0,0 +1,140 @@
+/**
+ * @file llvector4a.cpp
+ * @brief SIMD vector implementation
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2007-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llmath.h"
+
+static LL_ALIGN_16(const F32 M_IDENT_3A[12]) =
+ { 1.f, 0.f, 0.f, 0.f, // Column 1
+ 0.f, 1.f, 0.f, 0.f, // Column 2
+ 0.f, 0.f, 1.f, 0.f }; // Column 3
+
+extern const LLMatrix3a LL_M3A_IDENTITY = *reinterpret_cast<const LLMatrix3a*> (M_IDENT_3A);
+
+void LLMatrix3a::setMul( const LLMatrix3a& lhs, const LLMatrix3a& rhs )
+{
+ const LLVector4a col0 = lhs.getColumn(0);
+ const LLVector4a col1 = lhs.getColumn(1);
+ const LLVector4a col2 = lhs.getColumn(2);
+
+ for ( int i = 0; i < 3; i++ )
+ {
+ LLVector4a xxxx = _mm_load_ss( rhs.mColumns[i].getF32ptr() );
+ xxxx.splat<0>( xxxx );
+ xxxx.mul( col0 );
+
+ {
+ LLVector4a yyyy = _mm_load_ss( rhs.mColumns[i].getF32ptr() + 1 );
+ yyyy.splat<0>( yyyy );
+ yyyy.mul( col1 );
+ xxxx.add( yyyy );
+ }
+
+ {
+ LLVector4a zzzz = _mm_load_ss( rhs.mColumns[i].getF32ptr() + 2 );
+ zzzz.splat<0>( zzzz );
+ zzzz.mul( col2 );
+ xxxx.add( zzzz );
+ }
+
+ xxxx.store4a( mColumns[i].getF32ptr() );
+ }
+
+}
+
+/*static */void LLMatrix3a::batchTransform( const LLMatrix3a& xform, const LLVector4a* src, int numVectors, LLVector4a* dst )
+{
+ const LLVector4a col0 = xform.getColumn(0);
+ const LLVector4a col1 = xform.getColumn(1);
+ const LLVector4a col2 = xform.getColumn(2);
+ const LLVector4a* maxAddr = src + numVectors;
+
+ if ( numVectors & 0x1 )
+ {
+ LLVector4a xxxx = _mm_load_ss( (const F32*)src );
+ LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 );
+ LLVector4a zzzz = _mm_load_ss( (const F32*)src + 2 );
+ xxxx.splat<0>( xxxx );
+ yyyy.splat<0>( yyyy );
+ zzzz.splat<0>( zzzz );
+ xxxx.mul( col0 );
+ yyyy.mul( col1 );
+ zzzz.mul( col2 );
+ xxxx.add( yyyy );
+ xxxx.add( zzzz );
+ xxxx.store4a( (F32*)dst );
+ src++;
+ dst++;
+ }
+
+
+ numVectors >>= 1;
+ while ( src < maxAddr )
+ {
+ _mm_prefetch( (const char*)(src + 32 ), _MM_HINT_NTA );
+ _mm_prefetch( (const char*)(dst + 32), _MM_HINT_NTA );
+ LLVector4a xxxx = _mm_load_ss( (const F32*)src );
+ LLVector4a xxxx1= _mm_load_ss( (const F32*)(src + 1) );
+
+ xxxx.splat<0>( xxxx );
+ xxxx1.splat<0>( xxxx1 );
+ xxxx.mul( col0 );
+ xxxx1.mul( col0 );
+
+ {
+ LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 );
+ LLVector4a yyyy1 = _mm_load_ss( (const F32*)(src + 1) + 1);
+ yyyy.splat<0>( yyyy );
+ yyyy1.splat<0>( yyyy1 );
+ yyyy.mul( col1 );
+ yyyy1.mul( col1 );
+ xxxx.add( yyyy );
+ xxxx1.add( yyyy1 );
+ }
+
+ {
+ LLVector4a zzzz = _mm_load_ss( (const F32*)(src) + 2 );
+ LLVector4a zzzz1 = _mm_load_ss( (const F32*)(++src) + 2 );
+ zzzz.splat<0>( zzzz );
+ zzzz1.splat<0>( zzzz1 );
+ zzzz.mul( col2 );
+ zzzz1.mul( col2 );
+ xxxx.add( zzzz );
+ xxxx1.add( zzzz1 );
+ }
+
+ xxxx.store4a(dst->getF32ptr());
+ src++;
+ dst++;
+
+ xxxx1.store4a((F32*)dst++);
+ }
+}
diff --git a/indra/llmath/llmatrix3a.h b/indra/llmath/llmatrix3a.h
new file mode 100644
index 0000000000..56327f9f6d
--- /dev/null
+++ b/indra/llmath/llmatrix3a.h
@@ -0,0 +1,134 @@
+/**
+ * @file llmatrix3a.h
+ * @brief LLMatrix3a class header file - memory aligned and vectorized 3x3 matrix
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMATRIX3A_H
+#define LL_LLMATRIX3A_H
+
+/////////////////////////////
+// LLMatrix3a, LLRotation
+/////////////////////////////
+// This class stores a 3x3 (technically 4x3) matrix in column-major order
+/////////////////////////////
+/////////////////////////////
+// These classes are intentionally minimal right now. If you need additional
+// functionality, please contact someone with SSE experience (e.g., Falcon or
+// Huseby).
+/////////////////////////////
+
+// LLMatrix3a is the base class for LLRotation, which should be used instead any time you're dealing with a
+// rotation matrix.
+class LLMatrix3a
+{
+public:
+
+ // Utility function for quickly transforming an array of LLVector4a's
+ // For transforming a single LLVector4a, see LLVector4a::setRotated
+ static void batchTransform( const LLMatrix3a& xform, const LLVector4a* src, int numVectors, LLVector4a* dst );
+
+ // Utility function to obtain the identity matrix
+ static inline const LLMatrix3a& getIdentity();
+
+ //////////////////////////
+ // Ctors
+ //////////////////////////
+
+ // Ctor
+ LLMatrix3a() {}
+
+ // Ctor for setting by columns
+ inline LLMatrix3a( const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2 );
+
+ //////////////////////////
+ // Get/Set
+ //////////////////////////
+
+ // Loads from an LLMatrix3
+ inline void loadu(const LLMatrix3& src);
+
+ // Set rows
+ inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2);
+
+ // Set columns
+ inline void setColumns(const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2);
+
+ // Get the read-only access to a specified column. Valid columns are 0-2, but the
+ // function is unchecked. You've been warned.
+ inline const LLVector4a& getColumn(const U32 column) const;
+
+ /////////////////////////
+ // Matrix modification
+ /////////////////////////
+
+ // Set this matrix to the product of lhs and rhs ( this = lhs * rhs )
+ void setMul( const LLMatrix3a& lhs, const LLMatrix3a& rhs );
+
+ // Set this matrix to the transpose of src
+ inline void setTranspose(const LLMatrix3a& src);
+
+ // Set this matrix to a*w + b*(1-w)
+ inline void setLerp(const LLMatrix3a& a, const LLMatrix3a& b, F32 w);
+
+ /////////////////////////
+ // Matrix inspection
+ /////////////////////////
+
+ // Sets all 4 elements in 'dest' to the determinant of this matrix.
+ // If you will be using the determinant in subsequent ops with LLVector4a, use this version
+ inline void getDeterminant( LLVector4a& dest ) const;
+
+ // Returns the determinant as an LLSimdScalar. Use this if you will be using the determinant
+ // primary for scalar operations.
+ inline LLSimdScalar getDeterminant() const;
+
+ // Returns nonzero if rows 0-2 and colums 0-2 contain no NaN or INF values. Row 3 is ignored
+ inline LLBool32 isFinite() const;
+
+ // Returns true if this matrix is equal to 'rhs' up to 'tolerance'
+ inline bool isApproximatelyEqual( const LLMatrix3a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;
+
+protected:
+
+ LLVector4a mColumns[3];
+
+};
+
+class LLRotation : public LLMatrix3a
+{
+public:
+
+ LLRotation() {}
+
+ // Returns true if this rotation is orthonormal with det ~= 1
+ inline bool isOkRotation() const;
+};
+
+#endif
diff --git a/indra/llmath/llmatrix3a.inl b/indra/llmath/llmatrix3a.inl
new file mode 100644
index 0000000000..65fd949f78
--- /dev/null
+++ b/indra/llmath/llmatrix3a.inl
@@ -0,0 +1,125 @@
+/**
+ * @file llmatrix3a.inl
+ * @brief LLMatrix3a inline definitions
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llmatrix3a.h"
+#include "m3math.h"
+
+inline LLMatrix3a::LLMatrix3a( const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2 )
+{
+ setColumns( c0, c1, c2 );
+}
+
+inline void LLMatrix3a::loadu(const LLMatrix3& src)
+{
+ mColumns[0].load3(src.mMatrix[0]);
+ mColumns[1].load3(src.mMatrix[1]);
+ mColumns[2].load3(src.mMatrix[2]);
+}
+
+inline void LLMatrix3a::setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2)
+{
+ mColumns[0] = r0;
+ mColumns[1] = r1;
+ mColumns[2] = r2;
+ setTranspose( *this );
+}
+
+inline void LLMatrix3a::setColumns(const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2)
+{
+ mColumns[0] = c0;
+ mColumns[1] = c1;
+ mColumns[2] = c2;
+}
+
+inline void LLMatrix3a::setTranspose(const LLMatrix3a& src)
+{
+ const LLQuad srcCol0 = src.mColumns[0];
+ const LLQuad srcCol1 = src.mColumns[1];
+ const LLQuad unpacklo = _mm_unpacklo_ps( srcCol0, srcCol1 );
+ mColumns[0] = _mm_movelh_ps( unpacklo, src.mColumns[2] );
+ mColumns[1] = _mm_shuffle_ps( _mm_movehl_ps( srcCol0, unpacklo ), src.mColumns[2], _MM_SHUFFLE(0, 1, 1, 0) );
+ mColumns[2] = _mm_shuffle_ps( _mm_unpackhi_ps( srcCol0, srcCol1 ), src.mColumns[2], _MM_SHUFFLE(0, 2, 1, 0) );
+}
+
+inline const LLVector4a& LLMatrix3a::getColumn(const U32 column) const
+{
+ llassert( column < 3 );
+ return mColumns[column];
+}
+
+inline void LLMatrix3a::setLerp(const LLMatrix3a& a, const LLMatrix3a& b, F32 w)
+{
+ mColumns[0].setLerp( a.mColumns[0], b.mColumns[0], w );
+ mColumns[1].setLerp( a.mColumns[1], b.mColumns[1], w );
+ mColumns[2].setLerp( a.mColumns[2], b.mColumns[2], w );
+}
+
+inline LLBool32 LLMatrix3a::isFinite() const
+{
+ return mColumns[0].isFinite3() && mColumns[1].isFinite3() && mColumns[2].isFinite3();
+}
+
+inline void LLMatrix3a::getDeterminant( LLVector4a& dest ) const
+{
+ LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] );
+ dest.setAllDot3( col1xcol2, mColumns[0] );
+}
+
+inline LLSimdScalar LLMatrix3a::getDeterminant() const
+{
+ LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] );
+ return col1xcol2.dot3( mColumns[0] );
+}
+
+inline bool LLMatrix3a::isApproximatelyEqual( const LLMatrix3a& rhs, F32 tolerance /*= F_APPROXIMATELY_ZERO*/ ) const
+{
+ return rhs.getColumn(0).equals3(mColumns[0], tolerance)
+ && rhs.getColumn(1).equals3(mColumns[1], tolerance)
+ && rhs.getColumn(2).equals3(mColumns[2], tolerance);
+}
+
+inline const LLMatrix3a& LLMatrix3a::getIdentity()
+{
+ extern const LLMatrix3a LL_M3A_IDENTITY;
+ return LL_M3A_IDENTITY;
+}
+
+inline bool LLRotation::isOkRotation() const
+{
+ LLMatrix3a transpose; transpose.setTranspose( *this );
+ LLMatrix3a product; product.setMul( *this, transpose );
+
+ LLSimdScalar detMinusOne = getDeterminant() - 1.f;
+
+ return product.isApproximatelyEqual( LLMatrix3a::getIdentity() ) && (detMinusOne.getAbs() < F_APPROXIMATELY_ZERO);
+}
+
diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h
new file mode 100644
index 0000000000..0ead045d04
--- /dev/null
+++ b/indra/llmath/llmatrix4a.h
@@ -0,0 +1,149 @@
+/**
+ * @file llmatrix4a.h
+ * @brief LLMatrix4a class header file - memory aligned and vectorized 4x4 matrix
+ *
+ * $LicenseInfo:firstyear=2007&license=viewergpl$
+ *
+ * Copyright (c) 2007-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMATRIX4A_H
+#define LL_LLMATRIX4A_H
+
+#include "llvector4a.h"
+#include "m4math.h"
+#include "m3math.h"
+
+class LLMatrix4a
+{
+public:
+ LLVector4a mMatrix[4];
+
+ inline void clear()
+ {
+ mMatrix[0].clear();
+ mMatrix[1].clear();
+ mMatrix[2].clear();
+ mMatrix[3].clear();
+ }
+
+ inline void loadu(const LLMatrix4& src)
+ {
+ mMatrix[0] = _mm_loadu_ps(src.mMatrix[0]);
+ mMatrix[1] = _mm_loadu_ps(src.mMatrix[1]);
+ mMatrix[2] = _mm_loadu_ps(src.mMatrix[2]);
+ mMatrix[3] = _mm_loadu_ps(src.mMatrix[3]);
+
+ }
+
+ inline void loadu(const LLMatrix3& src)
+ {
+ mMatrix[0].load3(src.mMatrix[0]);
+ mMatrix[1].load3(src.mMatrix[1]);
+ mMatrix[2].load3(src.mMatrix[2]);
+ mMatrix[3].set(0,0,0,1.f);
+ }
+
+ inline void add(const LLMatrix4a& rhs)
+ {
+ mMatrix[0].add(rhs.mMatrix[0]);
+ mMatrix[1].add(rhs.mMatrix[1]);
+ mMatrix[2].add(rhs.mMatrix[2]);
+ mMatrix[3].add(rhs.mMatrix[3]);
+ }
+
+ inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2)
+ {
+ mMatrix[0] = r0;
+ mMatrix[1] = r1;
+ mMatrix[2] = r2;
+ }
+
+ inline void setMul(const LLMatrix4a& m, const F32 s)
+ {
+ mMatrix[0].setMul(m.mMatrix[0], s);
+ mMatrix[1].setMul(m.mMatrix[1], s);
+ mMatrix[2].setMul(m.mMatrix[2], s);
+ mMatrix[3].setMul(m.mMatrix[3], s);
+ }
+
+ inline void setLerp(const LLMatrix4a& a, const LLMatrix4a& b, F32 w)
+ {
+ LLVector4a d0,d1,d2,d3;
+ d0.setSub(b.mMatrix[0], a.mMatrix[0]);
+ d1.setSub(b.mMatrix[1], a.mMatrix[1]);
+ d2.setSub(b.mMatrix[2], a.mMatrix[2]);
+ d3.setSub(b.mMatrix[3], a.mMatrix[3]);
+
+ // this = a + d*w
+
+ d0.mul(w);
+ d1.mul(w);
+ d2.mul(w);
+ d3.mul(w);
+
+ mMatrix[0].setAdd(a.mMatrix[0],d0);
+ mMatrix[1].setAdd(a.mMatrix[1],d1);
+ mMatrix[2].setAdd(a.mMatrix[2],d2);
+ mMatrix[3].setAdd(a.mMatrix[3],d3);
+ }
+
+ inline void rotate(const LLVector4a& v, LLVector4a& res)
+ {
+ res = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0));
+ res.mul(mMatrix[0]);
+
+ LLVector4a y;
+ y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1));
+ y.mul(mMatrix[1]);
+
+ LLVector4a z;
+ z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2));
+ z.mul(mMatrix[2]);
+
+ res.add(y);
+ res.add(z);
+ }
+
+ inline void affineTransform(const LLVector4a& v, LLVector4a& res)
+ {
+ LLVector4a x,y,z;
+
+ x = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0));
+ y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1));
+ z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2));
+
+ x.mul(mMatrix[0]);
+ y.mul(mMatrix[1]);
+ z.mul(mMatrix[2]);
+
+ x.add(y);
+ z.add(mMatrix[3]);
+ res.setAdd(x,z);
+ }
+};
+
+#endif
diff --git a/indra/llmath/llquaternion2.h b/indra/llmath/llquaternion2.h
new file mode 100644
index 0000000000..dbb4afe312
--- /dev/null
+++ b/indra/llmath/llquaternion2.h
@@ -0,0 +1,111 @@
+/**
+ * @file llquaternion2.h
+ * @brief LLQuaternion2 class header file - SIMD-enabled quaternion class
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_QUATERNION2_H
+#define LL_QUATERNION2_H
+
+/////////////////////////////
+// LLQuaternion2
+/////////////////////////////
+// This class stores a quaternion x*i + y*j + z*k + w in <x, y, z, w> order
+// (i.e., w in high order element of vector)
+/////////////////////////////
+/////////////////////////////
+// These classes are intentionally minimal right now. If you need additional
+// functionality, please contact someone with SSE experience (e.g., Falcon or
+// Huseby).
+/////////////////////////////
+#include "llquaternion.h"
+
+class LLQuaternion2
+{
+public:
+
+ //////////////////////////
+ // Ctors
+ //////////////////////////
+
+ // Ctor
+ LLQuaternion2() {}
+
+ // Ctor from LLQuaternion
+ explicit LLQuaternion2( const class LLQuaternion& quat );
+
+ //////////////////////////
+ // Get/Set
+ //////////////////////////
+
+ // Load from an LLQuaternion
+ inline void operator=( const LLQuaternion& quat )
+ {
+ mQ.loadua( quat.mQ );
+ }
+
+ // Return the internal LLVector4a representation of the quaternion
+ inline const LLVector4a& getVector4a() const;
+ inline LLVector4a& getVector4aRw();
+
+ /////////////////////////
+ // Quaternion modification
+ /////////////////////////
+
+ // Set this quaternion to the conjugate of src
+ inline void setConjugate(const LLQuaternion2& src);
+
+ // Renormalizes the quaternion. Assumes it has nonzero length.
+ inline void normalize();
+
+ // Quantize this quaternion to 8 bit precision
+ inline void quantize8();
+
+ // Quantize this quaternion to 16 bit precision
+ inline void quantize16();
+
+ /////////////////////////
+ // Quaternion inspection
+ /////////////////////////
+
+ // Return true if this quaternion is equal to 'rhs'.
+ // Note! Quaternions exhibit "double-cover", so any rotation has two equally valid
+ // quaternion representations and they will NOT compare equal.
+ inline bool equals(const LLQuaternion2& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;
+
+ // Return true if all components are finite and the quaternion is normalized
+ inline bool isOkRotation() const;
+
+protected:
+
+ LLVector4a mQ;
+
+};
+
+#endif
diff --git a/indra/llmath/llquaternion2.inl b/indra/llmath/llquaternion2.inl
new file mode 100644
index 0000000000..9a4274d6a4
--- /dev/null
+++ b/indra/llmath/llquaternion2.inl
@@ -0,0 +1,108 @@
+/**
+ * @file llquaternion2.inl
+ * @brief LLQuaternion2 inline definitions
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llquaternion2.h"
+
+static const LLQuad LL_V4A_PLUS_ONE = {1.f, 1.f, 1.f, 1.f};
+static const LLQuad LL_V4A_MINUS_ONE = {-1.f, -1.f, -1.f, -1.f};
+
+// Ctor from LLQuaternion
+inline LLQuaternion2::LLQuaternion2( const LLQuaternion& quat )
+{
+ mQ.set(quat.mQ[VX], quat.mQ[VY], quat.mQ[VZ], quat.mQ[VW]);
+}
+
+//////////////////////////
+// Get/Set
+//////////////////////////
+
+// Return the internal LLVector4a representation of the quaternion
+inline const LLVector4a& LLQuaternion2::getVector4a() const
+{
+ return mQ;
+}
+
+inline LLVector4a& LLQuaternion2::getVector4aRw()
+{
+ return mQ;
+}
+
+/////////////////////////
+// Quaternion modification
+/////////////////////////
+
+// Set this quaternion to the conjugate of src
+inline void LLQuaternion2::setConjugate(const LLQuaternion2& src)
+{
+ static LL_ALIGN_16( const U32 F_QUAT_INV_MASK_4A[4] ) = { 0x80000000, 0x80000000, 0x80000000, 0x00000000 };
+ mQ = _mm_xor_ps(src.mQ, *reinterpret_cast<const LLQuad*>(&F_QUAT_INV_MASK_4A));
+}
+
+// Renormalizes the quaternion. Assumes it has nonzero length.
+inline void LLQuaternion2::normalize()
+{
+ mQ.normalize4();
+}
+
+// Quantize this quaternion to 8 bit precision
+inline void LLQuaternion2::quantize8()
+{
+ mQ.quantize8( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE );
+ normalize();
+}
+
+// Quantize this quaternion to 16 bit precision
+inline void LLQuaternion2::quantize16()
+{
+ mQ.quantize16( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE );
+ normalize();
+}
+
+
+/////////////////////////
+// Quaternion inspection
+/////////////////////////
+
+// Return true if this quaternion is equal to 'rhs'.
+// Note! Quaternions exhibit "double-cover", so any rotation has two equally valid
+// quaternion representations and they will NOT compare equal.
+inline bool LLQuaternion2::equals(const LLQuaternion2 &rhs, F32 tolerance/* = F_APPROXIMATELY_ZERO*/) const
+{
+ return mQ.equals4(rhs.mQ, tolerance);
+}
+
+// Return true if all components are finite and the quaternion is normalized
+inline bool LLQuaternion2::isOkRotation() const
+{
+ return mQ.isFinite4() && mQ.isNormalized4();
+}
+
diff --git a/indra/llmath/llsimdmath.h b/indra/llmath/llsimdmath.h
new file mode 100644
index 0000000000..9377bfdb53
--- /dev/null
+++ b/indra/llmath/llsimdmath.h
@@ -0,0 +1,95 @@
+/**
+ * @file llsimdmath.h
+ * @brief Common header for SIMD-based math library (llvector4a, llmatrix3a, etc.)
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2007-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_SIMD_MATH_H
+#define LL_SIMD_MATH_H
+
+#ifndef LLMATH_H
+#error "Please include llmath.h before this file."
+#endif
+
+#if ( ( LL_DARWIN || LL_LINUX ) && !(__SSE2__) ) || ( LL_WINDOWS && ( _M_IX86_FP < 2 ) )
+#error SSE2 not enabled. LLVector4a and related class will not compile.
+#endif
+
+template <typename T> T* LL_NEXT_ALIGNED_ADDRESS(T* address)
+{
+ return reinterpret_cast<T*>(
+ (reinterpret_cast<U32>(address) + 0xF) & ~0xF);
+}
+
+template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
+{
+ return reinterpret_cast<T*>(
+ (reinterpret_cast<U32>(address) + 0x3F) & ~0x3F);
+}
+
+#if LL_LINUX || LL_DARWIN
+
+#define LL_ALIGN_PREFIX(x)
+#define LL_ALIGN_POSTFIX(x) __attribute__((aligned(x)))
+
+#elif LL_WINDOWS
+
+#define LL_ALIGN_PREFIX(x) __declspec(align(x))
+#define LL_ALIGN_POSTFIX(x)
+
+#else
+#error "LL_ALIGN_PREFIX and LL_ALIGN_POSTFIX undefined"
+#endif
+
+#define LL_ALIGN_16(var) LL_ALIGN_PREFIX(16) var LL_ALIGN_POSTFIX(16)
+
+
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+#include "llsimdtypes.h"
+#include "llsimdtypes.inl"
+
+class LLMatrix3a;
+class LLRotation;
+class LLMatrix3;
+
+#include "llquaternion.h"
+
+#include "llvector4logical.h"
+#include "llvector4a.h"
+#include "llmatrix3a.h"
+#include "llquaternion2.h"
+#include "llvector4a.inl"
+#include "llmatrix3a.inl"
+#include "llquaternion2.inl"
+
+
+#endif //LL_SIMD_MATH_H
diff --git a/indra/llmath/llsimdtypes.h b/indra/llmath/llsimdtypes.h
new file mode 100644
index 0000000000..82e318c8bf
--- /dev/null
+++ b/indra/llmath/llsimdtypes.h
@@ -0,0 +1,130 @@
+/**
+ * @file llsimdtypes.h
+ * @brief Declaration of basic SIMD math related types
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2007-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_SIMD_TYPES_H
+#define LL_SIMD_TYPES_H
+
+#ifndef LL_SIMD_MATH_H
+#error "Please include llmath.h before this file."
+#endif
+
+typedef __m128 LLQuad;
+
+
+#if LL_WINDOWS
+#pragma warning(push)
+#pragma warning( disable : 4800 3 ) // Disable warning about casting int to bool for this class.
+#if defined(_MSC_VER) && (_MSC_VER < 1500)
+// VC++ 2005 is missing these intrinsics
+// __forceinline is MSVC specific and attempts to override compiler inlining judgment. This is so
+// even in debug builds this call is a NOP.
+__forceinline const __m128 _mm_castsi128_ps( const __m128i a ) { return reinterpret_cast<const __m128&>(a); }
+__forceinline const __m128i _mm_castps_si128( const __m128 a ) { return reinterpret_cast<const __m128i&>(a); }
+#endif // _MSC_VER
+
+#endif // LL_WINDOWS
+
+class LLBool32
+{
+public:
+ inline LLBool32() {}
+ inline LLBool32(int rhs) : m_bool(rhs) {}
+ inline LLBool32(unsigned int rhs) : m_bool(rhs) {}
+ inline LLBool32(bool rhs) { m_bool = static_cast<const int>(rhs); }
+ inline LLBool32& operator= (bool rhs) { m_bool = (int)rhs; return *this; }
+ inline bool operator== (bool rhs) const { return static_cast<const bool&>(m_bool) == rhs; }
+ inline bool operator!= (bool rhs) const { return !operator==(rhs); }
+ inline operator bool() const { return static_cast<const bool&>(m_bool); }
+
+private:
+ int m_bool;
+};
+
+#if LL_WINDOWS
+#pragma warning(pop)
+#endif
+
+class LLSimdScalar
+{
+public:
+ inline LLSimdScalar() {}
+ inline LLSimdScalar(LLQuad q)
+ {
+ mQ = q;
+ }
+
+ inline LLSimdScalar(F32 f)
+ {
+ mQ = _mm_set_ss(f);
+ }
+
+ static inline const LLSimdScalar& getZero()
+ {
+ extern const LLQuad F_ZERO_4A;
+ return reinterpret_cast<const LLSimdScalar&>(F_ZERO_4A);
+ }
+
+ inline F32 getF32() const;
+
+ inline LLBool32 isApproximatelyEqual(const LLSimdScalar& rhs, F32 tolerance = F_APPROXIMATELY_ZERO) const;
+
+ inline LLSimdScalar getAbs() const;
+
+ inline void setMax( const LLSimdScalar& a, const LLSimdScalar& b );
+
+ inline void setMin( const LLSimdScalar& a, const LLSimdScalar& b );
+
+ inline LLSimdScalar& operator=(F32 rhs);
+
+ inline LLSimdScalar& operator+=(const LLSimdScalar& rhs);
+
+ inline LLSimdScalar& operator-=(const LLSimdScalar& rhs);
+
+ inline LLSimdScalar& operator*=(const LLSimdScalar& rhs);
+
+ inline LLSimdScalar& operator/=(const LLSimdScalar& rhs);
+
+ inline operator LLQuad() const
+ {
+ return mQ;
+ }
+
+ inline const LLQuad& getQuad() const
+ {
+ return mQ;
+ }
+
+private:
+ LLQuad mQ;
+};
+
+#endif //LL_SIMD_TYPES_H
diff --git a/indra/llmath/llsimdtypes.inl b/indra/llmath/llsimdtypes.inl
new file mode 100644
index 0000000000..69c858e310
--- /dev/null
+++ b/indra/llmath/llsimdtypes.inl
@@ -0,0 +1,163 @@
+/**
+ * @file llsimdtypes.inl
+ * @brief Inlined definitions of basic SIMD math related types
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2007-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+
+
+
+//////////////////
+// LLSimdScalar
+//////////////////
+
+inline LLSimdScalar operator+(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ LLSimdScalar t(a);
+ t += b;
+ return t;
+}
+
+inline LLSimdScalar operator-(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ LLSimdScalar t(a);
+ t -= b;
+ return t;
+}
+
+inline LLSimdScalar operator*(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ LLSimdScalar t(a);
+ t *= b;
+ return t;
+}
+
+inline LLSimdScalar operator/(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ LLSimdScalar t(a);
+ t /= b;
+ return t;
+}
+
+inline LLSimdScalar operator-(const LLSimdScalar& a)
+{
+ static LL_ALIGN_16(const U32 signMask[4]) = {0x80000000, 0x80000000, 0x80000000, 0x80000000 };
+ return _mm_xor_ps(*reinterpret_cast<const LLQuad*>(signMask), a);
+}
+
+inline LLBool32 operator==(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ return _mm_comieq_ss(a, b);
+}
+
+inline LLBool32 operator!=(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ return _mm_comineq_ss(a, b);
+}
+
+inline LLBool32 operator<(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ return _mm_comilt_ss(a, b);
+}
+
+inline LLBool32 operator<=(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ return _mm_comile_ss(a, b);
+}
+
+inline LLBool32 operator>(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ return _mm_comigt_ss(a, b);
+}
+
+inline LLBool32 operator>=(const LLSimdScalar& a, const LLSimdScalar& b)
+{
+ return _mm_comige_ss(a, b);
+}
+
+inline LLBool32 LLSimdScalar::isApproximatelyEqual(const LLSimdScalar& rhs, F32 tolerance /* = F_APPROXIMATELY_ZERO */) const
+{
+ const LLSimdScalar tol( tolerance );
+ const LLSimdScalar diff = _mm_sub_ss( mQ, rhs.mQ );
+ const LLSimdScalar absDiff = diff.getAbs();
+ return absDiff <= tol;
+}
+
+inline void LLSimdScalar::setMax( const LLSimdScalar& a, const LLSimdScalar& b )
+{
+ mQ = _mm_max_ss( a, b );
+}
+
+inline void LLSimdScalar::setMin( const LLSimdScalar& a, const LLSimdScalar& b )
+{
+ mQ = _mm_min_ss( a, b );
+}
+
+inline LLSimdScalar& LLSimdScalar::operator=(F32 rhs)
+{
+ mQ = _mm_set_ss(rhs);
+ return *this;
+}
+
+inline LLSimdScalar& LLSimdScalar::operator+=(const LLSimdScalar& rhs)
+{
+ mQ = _mm_add_ss( mQ, rhs );
+ return *this;
+}
+
+inline LLSimdScalar& LLSimdScalar::operator-=(const LLSimdScalar& rhs)
+{
+ mQ = _mm_sub_ss( mQ, rhs );
+ return *this;
+}
+
+inline LLSimdScalar& LLSimdScalar::operator*=(const LLSimdScalar& rhs)
+{
+ mQ = _mm_mul_ss( mQ, rhs );
+ return *this;
+}
+
+inline LLSimdScalar& LLSimdScalar::operator/=(const LLSimdScalar& rhs)
+{
+ mQ = _mm_div_ss( mQ, rhs );
+ return *this;
+}
+
+inline LLSimdScalar LLSimdScalar::getAbs() const
+{
+ static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF };
+ return _mm_and_ps( mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A));
+}
+
+inline F32 LLSimdScalar::getF32() const
+{
+ F32 ret;
+ _mm_store_ss(&ret, mQ);
+ return ret;
+}
diff --git a/indra/llmath/llvector4a.cpp b/indra/llmath/llvector4a.cpp
new file mode 100644
index 0000000000..b62c17302f
--- /dev/null
+++ b/indra/llmath/llvector4a.cpp
@@ -0,0 +1,228 @@
+/**
+ * @file llvector4a.cpp
+ * @brief SIMD vector implementation
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2007-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llmath.h"
+#include "llquantize.h"
+
+extern const LLQuad F_ZERO_4A = { 0, 0, 0, 0 };
+extern const LLQuad F_APPROXIMATELY_ZERO_4A = {
+ F_APPROXIMATELY_ZERO,
+ F_APPROXIMATELY_ZERO,
+ F_APPROXIMATELY_ZERO,
+ F_APPROXIMATELY_ZERO
+};
+
+extern const LLVector4a LL_V4A_ZERO = reinterpret_cast<const LLVector4a&> ( F_ZERO_4A );
+extern const LLVector4a LL_V4A_EPSILON = reinterpret_cast<const LLVector4a&> ( F_APPROXIMATELY_ZERO_4A );
+
+/*static */void LLVector4a::memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes)
+{
+ assert(src != NULL);
+ assert(dst != NULL);
+ assert(bytes > 0);
+ assert((bytes % sizeof(F32))== 0);
+
+ F32* end = dst + (bytes / sizeof(F32) );
+
+ if (bytes > 64)
+ {
+ F32* begin_64 = LL_NEXT_ALIGNED_ADDRESS_64(dst);
+
+ //at least 64 (16*4) bytes before the end of the destination, switch to 16 byte copies
+ F32* end_64 = end-16;
+
+ _mm_prefetch((char*)begin_64, _MM_HINT_NTA);
+ _mm_prefetch((char*)begin_64 + 64, _MM_HINT_NTA);
+ _mm_prefetch((char*)begin_64 + 128, _MM_HINT_NTA);
+ _mm_prefetch((char*)begin_64 + 192, _MM_HINT_NTA);
+
+ while (dst < begin_64)
+ {
+ copy4a(dst, src);
+ dst += 4;
+ src += 4;
+ }
+
+ while (dst < end_64)
+ {
+ _mm_prefetch((char*)src + 512, _MM_HINT_NTA);
+ _mm_prefetch((char*)dst + 512, _MM_HINT_NTA);
+ copy4a(dst, src);
+ copy4a(dst+4, src+4);
+ copy4a(dst+8, src+8);
+ copy4a(dst+12, src+12);
+
+ dst += 16;
+ src += 16;
+ }
+ }
+
+ while (dst < end)
+ {
+ copy4a(dst, src);
+ dst += 4;
+ src += 4;
+ }
+}
+
+void LLVector4a::setRotated( const LLRotation& rot, const LLVector4a& vec )
+{
+ const LLVector4a col0 = rot.getColumn(0);
+ const LLVector4a col1 = rot.getColumn(1);
+ const LLVector4a col2 = rot.getColumn(2);
+
+ LLVector4a result = _mm_load_ss( vec.getF32ptr() );
+ result.splat<0>( result );
+ result.mul( col0 );
+
+ {
+ LLVector4a yyyy = _mm_load_ss( vec.getF32ptr() + 1 );
+ yyyy.splat<0>( yyyy );
+ yyyy.mul( col1 );
+ result.add( yyyy );
+ }
+
+ {
+ LLVector4a zzzz = _mm_load_ss( vec.getF32ptr() + 2 );
+ zzzz.splat<0>( zzzz );
+ zzzz.mul( col2 );
+ result.add( zzzz );
+ }
+
+ *this = result;
+}
+
+void LLVector4a::setRotated( const LLQuaternion2& quat, const LLVector4a& vec )
+{
+ const LLVector4a& quatVec = quat.getVector4a();
+ LLVector4a temp; temp.setCross3(quatVec, vec);
+ temp.add( temp );
+
+ const LLVector4a realPart( quatVec.getScalarAt<3>() );
+ LLVector4a tempTimesReal; tempTimesReal.setMul( temp, realPart );
+
+ mQ = vec;
+ add( tempTimesReal );
+
+ LLVector4a imagCrossTemp; imagCrossTemp.setCross3( quatVec, temp );
+ add(imagCrossTemp);
+}
+
+void LLVector4a::quantize8( const LLVector4a& low, const LLVector4a& high )
+{
+ LLVector4a val(mQ);
+ LLVector4a delta; delta.setSub( high, low );
+
+ {
+ val.clamp(low, high);
+ val.sub(low);
+
+ // 8-bit quantization means we can do with just 12 bits of reciprocal accuracy
+ const LLVector4a oneOverDelta = _mm_rcp_ps(delta.mQ);
+// {
+// static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f };
+// LLVector4a two; two.load4a( F_TWO_4A );
+//
+// // Here we use _mm_rcp_ps plus one round of newton-raphson
+// // We wish to find 'x' such that x = 1/delta
+// // As a first approximation, we take x0 = _mm_rcp_ps(delta)
+// // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 )
+// // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf
+// const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ);
+// oneOverDelta.setMul( delta, recipApprox );
+// oneOverDelta.setSub( two, oneOverDelta );
+// oneOverDelta.mul( recipApprox );
+// }
+
+ val.mul(oneOverDelta);
+ val.mul(*reinterpret_cast<const LLVector4a*>(F_U8MAX_4A));
+ }
+
+ val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ ));
+
+ {
+ val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A));
+ val.mul(delta);
+ val.add(low);
+ }
+
+ {
+ LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A));
+ LLVector4a absVal; absVal.setAbs( val );
+ setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val );
+ }
+}
+
+void LLVector4a::quantize16( const LLVector4a& low, const LLVector4a& high )
+{
+ LLVector4a val(mQ);
+ LLVector4a delta; delta.setSub( high, low );
+
+ {
+ val.clamp(low, high);
+ val.sub(low);
+
+ // 16-bit quantization means we need a round of Newton-Raphson
+ LLVector4a oneOverDelta;
+ {
+ static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f };
+ LLVector4a two; two.load4a( F_TWO_4A );
+
+ // Here we use _mm_rcp_ps plus one round of newton-raphson
+ // We wish to find 'x' such that x = 1/delta
+ // As a first approximation, we take x0 = _mm_rcp_ps(delta)
+ // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 )
+ // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf
+ const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ);
+ oneOverDelta.setMul( delta, recipApprox );
+ oneOverDelta.setSub( two, oneOverDelta );
+ oneOverDelta.mul( recipApprox );
+ }
+
+ val.mul(oneOverDelta);
+ val.mul(*reinterpret_cast<const LLVector4a*>(F_U16MAX_4A));
+ }
+
+ val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ ));
+
+ {
+ val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A));
+ val.mul(delta);
+ val.add(low);
+ }
+
+ {
+ LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A));
+ LLVector4a absVal; absVal.setAbs( val );
+ setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val );
+ }
+}
diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h
new file mode 100644
index 0000000000..76a3e999ce
--- /dev/null
+++ b/indra/llmath/llvector4a.h
@@ -0,0 +1,331 @@
+/**
+ * @file llvector4a.h
+ * @brief LLVector4a class header file - memory aligned and vectorized 4 component vector
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2007-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLVECTOR4A_H
+#define LL_LLVECTOR4A_H
+
+
+class LLRotation;
+
+#include <assert.h>
+#include "llpreprocessor.h"
+
+///////////////////////////////////
+// FIRST TIME USERS PLEASE READ
+//////////////////////////////////
+// This is just the beginning of LLVector4a. There are many more useful functions
+// yet to be implemented. For example, setNeg to negate a vector, rotate() to apply
+// a matrix rotation, various functions to manipulate only the X, Y, and Z elements
+// and many others (including a whole variety of accessors). So if you don't see a
+// function here that you need, please contact Falcon or someone else with SSE
+// experience (Richard, I think, has some and davep has a little as of the time
+// of this writing, July 08, 2010) about getting it implemented before you resort to
+// LLVector3/LLVector4.
+/////////////////////////////////
+
+class LLVector4a
+{
+public:
+
+ ///////////////////////////////////
+ // STATIC METHODS
+ ///////////////////////////////////
+
+ // Call initClass() at startup to avoid 15,000+ cycle penalties from denormalized numbers
+ static void initClass()
+ {
+ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+ _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST);
+ }
+
+ // Return a vector of all zeros
+ static inline const LLVector4a& getZero()
+ {
+ extern const LLVector4a LL_V4A_ZERO;
+ return LL_V4A_ZERO;
+ }
+
+ // Return a vector of all epsilon, where epsilon is a small float suitable for approximate equality checks
+ static inline const LLVector4a& getEpsilon()
+ {
+ extern const LLVector4a LL_V4A_EPSILON;
+ return LL_V4A_EPSILON;
+ }
+
+ // Copy 16 bytes from src to dst. Source and destination must be 16-byte aligned
+ static inline void copy4a(F32* dst, const F32* src)
+ {
+ _mm_store_ps(dst, _mm_load_ps(src));
+ }
+
+ // Copy words 16-byte blocks from src to dst. Source and destination must not overlap.
+ static void memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes);
+
+ ////////////////////////////////////
+ // CONSTRUCTORS
+ ////////////////////////////////////
+
+ LLVector4a()
+ { //DO NOT INITIALIZE -- The overhead is completely unnecessary
+ }
+
+ LLVector4a(F32 x, F32 y, F32 z, F32 w = 0.f)
+ {
+ set(x,y,z,w);
+ }
+
+ LLVector4a(F32 x)
+ {
+ splat(x);
+ }
+
+ LLVector4a(const LLSimdScalar& x)
+ {
+ splat(x);
+ }
+
+ LLVector4a(LLQuad q)
+ {
+ mQ = q;
+ }
+
+ ////////////////////////////////////
+ // LOAD/STORE
+ ////////////////////////////////////
+
+ // Load from 16-byte aligned src array (preferred method of loading)
+ inline void load4a(const F32* src);
+
+ // Load from unaligned src array (NB: Significantly slower than load4a)
+ inline void loadua(const F32* src);
+
+ // Load only three floats beginning at address 'src'. Slowest method.
+ inline void load3(const F32* src);
+
+ // Store to a 16-byte aligned memory address
+ inline void store4a(F32* dst) const;
+
+ ////////////////////////////////////
+ // BASIC GET/SET
+ ////////////////////////////////////
+
+ // Return a "this" as an F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon)
+ inline F32* getF32ptr();
+
+ // Return a "this" as a const F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon)
+ inline const F32* const getF32ptr() const;
+
+ // Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates
+ // the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead
+ inline F32 operator[](const S32 idx) const;
+
+ // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
+ inline LLSimdScalar getScalarAt(const S32 idx) const;
+
+ // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
+ template <int N> LL_FORCE_INLINE LLSimdScalar getScalarAt() const;
+ template <> LL_FORCE_INLINE LLSimdScalar getScalarAt<0>() const;
+
+ // Set to an x, y, z and optional w provided
+ inline void set(F32 x, F32 y, F32 z, F32 w = 0.f);
+
+ // Set to all zeros. This is preferred to using ::getZero()
+ inline void clear();
+
+ // Set all elements to 'x'
+ inline void splat(const F32 x);
+
+ // Set all elements to 'x'
+ inline void splat(const LLSimdScalar& x);
+
+ // Set all 4 elements to element N of src, with N known at compile time
+ template <int N> void splat(const LLVector4a& src);
+
+ // Set all 4 elements to element i of v, with i NOT known at compile time
+ inline void splat(const LLVector4a& v, U32 i);
+
+ // Select bits from sourceIfTrue and sourceIfFalse according to bits in mask
+ inline void setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse );
+
+ ////////////////////////////////////
+ // ALGEBRAIC
+ ////////////////////////////////////
+
+ // Set this to the element-wise (a + b)
+ inline void setAdd(const LLVector4a& a, const LLVector4a& b);
+
+ // Set this to element-wise (a - b)
+ inline void setSub(const LLVector4a& a, const LLVector4a& b);
+
+ // Set this to element-wise multiply (a * b)
+ inline void setMul(const LLVector4a& a, const LLVector4a& b);
+
+ // Set this to element-wise quotient (a / b)
+ inline void setDiv(const LLVector4a& a, const LLVector4a& b);
+
+ // Set this to the element-wise absolute value of src
+ inline void setAbs(const LLVector4a& src);
+
+ // Add to each component in this vector the corresponding component in rhs
+ inline void add(const LLVector4a& rhs);
+
+ // Subtract from each component in this vector the corresponding component in rhs
+ inline void sub(const LLVector4a& rhs);
+
+ // Multiply each component in this vector by the corresponding component in rhs
+ inline void mul(const LLVector4a& rhs);
+
+ // Divide each component in this vector by the corresponding component in rhs
+ inline void div(const LLVector4a& rhs);
+
+ // Multiply this vector by x in a scalar fashion
+ inline void mul(const F32 x);
+
+ // Set this to (a x b) (geometric cross-product)
+ inline void setCross3(const LLVector4a& a, const LLVector4a& b);
+
+ // Set all elements to the dot product of the x, y, and z elements in a and b
+ inline void setAllDot3(const LLVector4a& a, const LLVector4a& b);
+
+ // Set all elements to the dot product of the x, y, z, and w elements in a and b
+ inline void setAllDot4(const LLVector4a& a, const LLVector4a& b);
+
+ // Return the 3D dot product of this vector and b
+ inline LLSimdScalar dot3(const LLVector4a& b) const;
+
+ // Return the 4D dot product of this vector and b
+ inline LLSimdScalar dot4(const LLVector4a& b) const;
+
+ // Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed
+ // Note that this does not consider zero length vectors!
+ inline void normalize3();
+
+ // Same as normalize3() but with respect to all 4 components
+ inline void normalize4();
+
+ // Same as normalize3(), but returns length as a SIMD scalar
+ inline LLSimdScalar normalize3withLength();
+
+ // Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed
+ // Note that this does not consider zero length vectors!
+ inline void normalize3fast();
+
+ // Return true if this vector is normalized with respect to x,y,z up to tolerance
+ inline LLBool32 isNormalized3( F32 tolerance = 1e-3 ) const;
+
+ // Return true if this vector is normalized with respect to all components up to tolerance
+ inline LLBool32 isNormalized4( F32 tolerance = 1e-3 ) const;
+
+ // Set all elements to the length of vector 'v'
+ inline void setAllLength3( const LLVector4a& v );
+
+ // Get this vector's length
+ inline LLSimdScalar getLength3() const;
+
+ // Set the components of this vector to the minimum of the corresponding components of lhs and rhs
+ inline void setMin(const LLVector4a& lhs, const LLVector4a& rhs);
+
+ // Set the components of this vector to the maximum of the corresponding components of lhs and rhs
+ inline void setMax(const LLVector4a& lhs, const LLVector4a& rhs);
+
+ // Clamps this vector to be within the component-wise range low to high (inclusive)
+ inline void clamp( const LLVector4a& low, const LLVector4a& high );
+
+ // Set this to (c * lhs) + rhs * ( 1 - c)
+ inline void setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c);
+
+ // Return true (nonzero) if x, y, z (and w for Finite4) are all finite floats
+ inline LLBool32 isFinite3() const;
+ inline LLBool32 isFinite4() const;
+
+ // Set this vector to 'vec' rotated by the LLRotation or LLQuaternion2 provided
+ void setRotated( const LLRotation& rot, const LLVector4a& vec );
+ void setRotated( const class LLQuaternion2& quat, const LLVector4a& vec );
+
+ // Set this vector to 'vec' rotated by the INVERSE of the LLRotation or LLQuaternion2 provided
+ inline void setRotatedInv( const LLRotation& rot, const LLVector4a& vec );
+ inline void setRotatedInv( const class LLQuaternion2& quat, const LLVector4a& vec );
+
+ // Quantize this vector to 8 or 16 bit precision
+ void quantize8( const LLVector4a& low, const LLVector4a& high );
+ void quantize16( const LLVector4a& low, const LLVector4a& high );
+
+ ////////////////////////////////////
+ // LOGICAL
+ ////////////////////////////////////
+ // The functions in this section will compare the elements in this vector
+ // to those in rhs and return an LLVector4Logical with all bits set in elements
+ // where the comparison was true and all bits unset in elements where the comparison
+ // was false. See llvector4logica.h
+ ////////////////////////////////////
+ // WARNING: Other than equals3 and equals4, these functions do NOT account
+ // for floating point tolerance. You should include the appropriate tolerance
+ // in the inputs.
+ ////////////////////////////////////
+
+ inline LLVector4Logical greaterThan(const LLVector4a& rhs) const;
+
+ inline LLVector4Logical lessThan(const LLVector4a& rhs) const;
+
+ inline LLVector4Logical greaterEqual(const LLVector4a& rhs) const;
+
+ inline LLVector4Logical lessEqual(const LLVector4a& rhs) const;
+
+ inline LLVector4Logical equal(const LLVector4a& rhs) const;
+
+ // Returns true if this and rhs are componentwise equal up to the specified absolute tolerance
+ inline bool equals4(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;
+
+ inline bool equals3(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;
+
+ ////////////////////////////////////
+ // OPERATORS
+ ////////////////////////////////////
+
+ // Do NOT add aditional operators without consulting someone with SSE experience
+ inline const LLVector4a& operator= ( const LLVector4a& rhs );
+
+ inline const LLVector4a& operator= ( const LLQuad& rhs );
+
+ inline operator LLQuad() const;
+
+private:
+ LLQuad mQ;
+};
+
+inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p)
+{
+ min.setMin(min, p);
+ max.setMax(max, p);
+}
+
+#endif
diff --git a/indra/llmath/llvector4a.inl b/indra/llmath/llvector4a.inl
new file mode 100644
index 0000000000..e52b550883
--- /dev/null
+++ b/indra/llmath/llvector4a.inl
@@ -0,0 +1,599 @@
+/**
+ * @file llvector4a.inl
+ * @brief LLVector4a inline function implementations
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2007-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+////////////////////////////////////
+// LOAD/STORE
+////////////////////////////////////
+
+// Load from 16-byte aligned src array (preferred method of loading)
+inline void LLVector4a::load4a(const F32* src)
+{
+ mQ = _mm_load_ps(src);
+}
+
+// Load from unaligned src array (NB: Significantly slower than load4a)
+inline void LLVector4a::loadua(const F32* src)
+{
+ mQ = _mm_loadu_ps(src);
+}
+
+// Load only three floats beginning at address 'src'. Slowest method.
+inline void LLVector4a::load3(const F32* src)
+{
+ // mQ = { 0.f, src[2], src[1], src[0] } = { W, Z, Y, X }
+ // NB: This differs from the convention of { Z, Y, X, W }
+ mQ = _mm_set_ps(0.f, src[2], src[1], src[0]);
+}
+
+// Store to a 16-byte aligned memory address
+inline void LLVector4a::store4a(F32* dst) const
+{
+ _mm_store_ps(dst, mQ);
+}
+
+////////////////////////////////////
+// BASIC GET/SET
+////////////////////////////////////
+
+// Return a "this" as an F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon)
+F32* LLVector4a::getF32ptr()
+{
+ return (F32*) &mQ;
+}
+
+// Return a "this" as a const F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon)
+const F32* const LLVector4a::getF32ptr() const
+{
+ return (const F32* const) &mQ;
+}
+
+// Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates
+// the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead
+inline F32 LLVector4a::operator[](const S32 idx) const
+{
+ return ((F32*)&mQ)[idx];
+}
+
+// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
+inline LLSimdScalar LLVector4a::getScalarAt(const S32 idx) const
+{
+ // Return appropriate LLQuad. It will be cast to LLSimdScalar automatically (should be effectively a nop)
+ switch (idx)
+ {
+ case 0:
+ return mQ;
+ case 1:
+ return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(1, 1, 1, 1));
+ case 2:
+ return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(2, 2, 2, 2));
+ case 3:
+ default:
+ return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(3, 3, 3, 3));
+ }
+}
+
+// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
+template <int N> LL_FORCE_INLINE LLSimdScalar LLVector4a::getScalarAt() const
+{
+ return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(N, N, N, N));
+}
+
+template<> LL_FORCE_INLINE LLSimdScalar LLVector4a::getScalarAt<0>() const
+{
+ return mQ;
+}
+
+// Set to an x, y, z and optional w provided
+inline void LLVector4a::set(F32 x, F32 y, F32 z, F32 w)
+{
+ mQ = _mm_set_ps(w, z, y, x);
+}
+
+// Set to all zeros
+inline void LLVector4a::clear()
+{
+ mQ = LLVector4a::getZero().mQ;
+}
+
+inline void LLVector4a::splat(const F32 x)
+{
+ mQ = _mm_set1_ps(x);
+}
+
+inline void LLVector4a::splat(const LLSimdScalar& x)
+{
+ mQ = _mm_shuffle_ps( x.getQuad(), x.getQuad(), _MM_SHUFFLE(0,0,0,0) );
+}
+
+// Set all 4 elements to element N of src, with N known at compile time
+template <int N> void LLVector4a::splat(const LLVector4a& src)
+{
+ mQ = _mm_shuffle_ps(src.mQ, src.mQ, _MM_SHUFFLE(N, N, N, N) );
+}
+
+// Set all 4 elements to element i of v, with i NOT known at compile time
+inline void LLVector4a::splat(const LLVector4a& v, U32 i)
+{
+ switch (i)
+ {
+ case 0:
+ mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(0, 0, 0, 0));
+ break;
+ case 1:
+ mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(1, 1, 1, 1));
+ break;
+ case 2:
+ mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(2, 2, 2, 2));
+ break;
+ case 3:
+ mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(3, 3, 3, 3));
+ break;
+ }
+}
+
+// Select bits from sourceIfTrue and sourceIfFalse according to bits in mask
+inline void LLVector4a::setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse )
+{
+ // ((( sourceIfTrue ^ sourceIfFalse ) & mask) ^ sourceIfFalse )
+ // E.g., sourceIfFalse = 1010b, sourceIfTrue = 0101b, mask = 1100b
+ // (sourceIfTrue ^ sourceIfFalse) = 1111b --> & mask = 1100b --> ^ sourceIfFalse = 0110b,
+ // as expected (01 from sourceIfTrue, 10 from sourceIfFalse)
+ // Courtesy of Mark++, http://markplusplus.wordpress.com/2007/03/14/fast-sse-select-operation/
+ mQ = _mm_xor_ps( sourceIfFalse, _mm_and_ps( mask, _mm_xor_ps( sourceIfTrue, sourceIfFalse ) ) );
+}
+
+////////////////////////////////////
+// ALGEBRAIC
+////////////////////////////////////
+
+// Set this to the element-wise (a + b)
+inline void LLVector4a::setAdd(const LLVector4a& a, const LLVector4a& b)
+{
+ mQ = _mm_add_ps(a.mQ, b.mQ);
+}
+
+// Set this to element-wise (a - b)
+inline void LLVector4a::setSub(const LLVector4a& a, const LLVector4a& b)
+{
+ mQ = _mm_sub_ps(a.mQ, b.mQ);
+}
+
+// Set this to element-wise multiply (a * b)
+inline void LLVector4a::setMul(const LLVector4a& a, const LLVector4a& b)
+{
+ mQ = _mm_mul_ps(a.mQ, b.mQ);
+}
+
+// Set this to element-wise quotient (a / b)
+inline void LLVector4a::setDiv(const LLVector4a& a, const LLVector4a& b)
+{
+ mQ = _mm_div_ps( a.mQ, b.mQ );
+}
+
+// Set this to the element-wise absolute value of src
+inline void LLVector4a::setAbs(const LLVector4a& src)
+{
+ static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF };
+ mQ = _mm_and_ps(src.mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A));
+}
+
+// Add to each component in this vector the corresponding component in rhs
+inline void LLVector4a::add(const LLVector4a& rhs)
+{
+ mQ = _mm_add_ps(mQ, rhs.mQ);
+}
+
+// Subtract from each component in this vector the corresponding component in rhs
+inline void LLVector4a::sub(const LLVector4a& rhs)
+{
+ mQ = _mm_sub_ps(mQ, rhs.mQ);
+}
+
+// Multiply each component in this vector by the corresponding component in rhs
+inline void LLVector4a::mul(const LLVector4a& rhs)
+{
+ mQ = _mm_mul_ps(mQ, rhs.mQ);
+}
+
+// Divide each component in this vector by the corresponding component in rhs
+inline void LLVector4a::div(const LLVector4a& rhs)
+{
+ // TODO: Check accuracy, maybe add divFast
+ mQ = _mm_div_ps(mQ, rhs.mQ);
+}
+
+// Multiply this vector by x in a scalar fashion
+inline void LLVector4a::mul(const F32 x)
+{
+ LLVector4a t;
+ t.splat(x);
+
+ mQ = _mm_mul_ps(mQ, t.mQ);
+}
+
+// Set this to (a x b) (geometric cross-product)
+inline void LLVector4a::setCross3(const LLVector4a& a, const LLVector4a& b)
+{
+ // Vectors are stored in memory in w, z, y, x order from high to low
+ // Set vector1 = { a[W], a[X], a[Z], a[Y] }
+ const LLQuad vector1 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 0, 2, 1 ));
+ // Set vector2 = { b[W], b[Y], b[X], b[Z] }
+ const LLQuad vector2 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 1, 0, 2 ));
+ // mQ = { a[W]*b[W], a[X]*b[Y], a[Z]*b[X], a[Y]*b[Z] }
+ mQ = _mm_mul_ps( vector1, vector2 );
+ // vector3 = { a[W], a[Y], a[X], a[Z] }
+ const LLQuad vector3 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 1, 0, 2 ));
+ // vector4 = { b[W], b[X], b[Z], b[Y] }
+ const LLQuad vector4 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 0, 2, 1 ));
+ // mQ = { 0, a[X]*b[Y] - a[Y]*b[X], a[Z]*b[X] - a[X]*b[Z], a[Y]*b[Z] - a[Z]*b[Y] }
+ mQ = _mm_sub_ps( mQ, _mm_mul_ps( vector3, vector4 ));
+}
+
+/* This function works, but may be slightly slower than the one below on older machines
+ inline void LLVector4a::setAllDot3(const LLVector4a& a, const LLVector4a& b)
+ {
+ // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] }
+ const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ );
+ // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] }
+ const LLQuad wzxy = _mm_shuffle_ps( ab, ab, _MM_SHUFFLE(3, 2, 0, 1 ));
+ // xPlusY = { 2*a[W]*b[W], 2 * a[Z] * b[Z], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] }
+ const LLQuad xPlusY = _mm_add_ps(ab, wzxy);
+ // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] }
+ const LLQuad xPlusYSplat = _mm_movelh_ps(xPlusY, xPlusY);
+ // zSplat = { a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z] }
+ const LLQuad zSplat = _mm_shuffle_ps( ab, ab, _MM_SHUFFLE( 2, 2, 2, 2 ));
+ // mQ = { a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same }
+ mQ = _mm_add_ps(zSplat, xPlusYSplat);
+ }*/
+
+// Set all elements to the dot product of the x, y, and z elements in a and b
+inline void LLVector4a::setAllDot3(const LLVector4a& a, const LLVector4a& b)
+{
+ // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] }
+ const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ );
+ // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] }
+ const __m128i wzxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(3, 2, 0, 1 ));
+ // xPlusY = { 2*a[W]*b[W], 2 * a[Z] * b[Z], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] }
+ const LLQuad xPlusY = _mm_add_ps(ab, _mm_castsi128_ps(wzxy));
+ // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] }
+ const LLQuad xPlusYSplat = _mm_movelh_ps(xPlusY, xPlusY);
+ // zSplat = { a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z] }
+ const __m128i zSplat = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE( 2, 2, 2, 2 ));
+ // mQ = { a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same }
+ mQ = _mm_add_ps(_mm_castsi128_ps(zSplat), xPlusYSplat);
+}
+
+// Set all elements to the dot product of the x, y, z, and w elements in a and b
+inline void LLVector4a::setAllDot4(const LLVector4a& a, const LLVector4a& b)
+{
+ // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] }
+ const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ );
+ // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] }
+ const __m128i zwxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(2, 3, 0, 1 ));
+ // zPlusWandXplusY = { a[W]*b[W] + a[Z]*b[Z], a[Z] * b[Z] + a[W]*b[W], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] }
+ const LLQuad zPlusWandXplusY = _mm_add_ps(ab, _mm_castsi128_ps(zwxy));
+ // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] }
+ const LLQuad xPlusYSplat = _mm_movelh_ps(zPlusWandXplusY, zPlusWandXplusY);
+ const LLQuad zPlusWSplat = _mm_movehl_ps(zPlusWandXplusY, zPlusWandXplusY);
+
+ // mQ = { a[W]*b[W] + a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same }
+ mQ = _mm_add_ps(xPlusYSplat, zPlusWSplat);
+}
+
+// Return the 3D dot product of this vector and b
+inline LLSimdScalar LLVector4a::dot3(const LLVector4a& b) const
+{
+ const LLQuad ab = _mm_mul_ps( mQ, b.mQ );
+ const LLQuad splatY = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(1, 1, 1, 1) ) );
+ const LLQuad splatZ = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(2, 2, 2, 2) ) );
+ const LLQuad xPlusY = _mm_add_ps( ab, splatY );
+ return _mm_add_ps( xPlusY, splatZ );
+}
+
+// Return the 4D dot product of this vector and b
+inline LLSimdScalar LLVector4a::dot4(const LLVector4a& b) const
+{
+ // ab = { w, z, y, x }
+ const LLQuad ab = _mm_mul_ps( mQ, b.mQ );
+ // upperProdsInLowerElems = { y, x, y, x }
+ const LLQuad upperProdsInLowerElems = _mm_movehl_ps( ab, ab );
+ // sumOfPairs = { w+y, z+x, 2y, 2x }
+ const LLQuad sumOfPairs = _mm_add_ps( upperProdsInLowerElems, ab );
+ // shuffled = { z+x, z+x, z+x, z+x }
+ const LLQuad shuffled = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( sumOfPairs ), _MM_SHUFFLE(1, 1, 1, 1) ) );
+ return _mm_add_ss( sumOfPairs, shuffled );
+}
+
+// Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed
+// Note that this does not consider zero length vectors!
+inline void LLVector4a::normalize3()
+{
+ // lenSqrd = a dot a
+ LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this );
+ // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 }
+ const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
+ static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f };
+ static const LLQuad three = {3.f, 3.f, 3.f, 3.f };
+ // Now we do one round of Newton-Raphson approximation to get full accuracy
+ // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a))
+ // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3))
+ // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3
+ // = 0.5 * w * (3 - a*w^2)
+ // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula
+ // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)]
+ const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt );
+ const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt );
+ const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt );
+ const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt));
+ mQ = _mm_mul_ps( mQ, nrApprox );
+}
+
+// Normalize this vector with respect to all components. Accurate to 22 bites of precision.
+// Note that this does not consider zero length vectors!
+inline void LLVector4a::normalize4()
+{
+ // lenSqrd = a dot a
+ LLVector4a lenSqrd; lenSqrd.setAllDot4( *this, *this );
+ // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 }
+ const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
+ static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f };
+ static const LLQuad three = {3.f, 3.f, 3.f, 3.f };
+ // Now we do one round of Newton-Raphson approximation to get full accuracy
+ // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a))
+ // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3))
+ // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3
+ // = 0.5 * w * (3 - a*w^2)
+ // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula
+ // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)]
+ const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt );
+ const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt );
+ const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt );
+ const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt));
+ mQ = _mm_mul_ps( mQ, nrApprox );
+}
+
+// Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed
+// Note that this does not consider zero length vectors!
+inline LLSimdScalar LLVector4a::normalize3withLength()
+{
+ // lenSqrd = a dot a
+ LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this );
+ // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 }
+ const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
+ static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f };
+ static const LLQuad three = {3.f, 3.f, 3.f, 3.f };
+ // Now we do one round of Newton-Raphson approximation to get full accuracy
+ // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a))
+ // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3))
+ // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3
+ // = 0.5 * w * (3 - a*w^2)
+ // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula
+ // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)]
+ const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt );
+ const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt );
+ const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt );
+ const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt));
+ mQ = _mm_mul_ps( mQ, nrApprox );
+ return _mm_sqrt_ss(lenSqrd);
+}
+
+// Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed
+// Note that this does not consider zero length vectors!
+inline void LLVector4a::normalize3fast()
+{
+ LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this );
+ const LLQuad approxRsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
+ mQ = _mm_mul_ps( mQ, approxRsqrt );
+}
+
+// Return true if this vector is normalized with respect to x,y,z up to tolerance
+inline LLBool32 LLVector4a::isNormalized3( F32 tolerance ) const
+{
+ static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 };
+ LLSimdScalar tol = _mm_load_ss( &tolerance );
+ tol = _mm_mul_ss( tol, tol );
+ LLVector4a lenSquared; lenSquared.setAllDot3( *this, *this );
+ lenSquared.sub( *reinterpret_cast<const LLVector4a*>(ones) );
+ lenSquared.setAbs(lenSquared);
+ return _mm_comile_ss( lenSquared, tol );
+}
+
+// Return true if this vector is normalized with respect to all components up to tolerance
+inline LLBool32 LLVector4a::isNormalized4( F32 tolerance ) const
+{
+ static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 };
+ LLSimdScalar tol = _mm_load_ss( &tolerance );
+ tol = _mm_mul_ss( tol, tol );
+ LLVector4a lenSquared; lenSquared.setAllDot4( *this, *this );
+ lenSquared.sub( *reinterpret_cast<const LLVector4a*>(ones) );
+ lenSquared.setAbs(lenSquared);
+ return _mm_comile_ss( lenSquared, tol );
+}
+
+// Set all elements to the length of vector 'v'
+inline void LLVector4a::setAllLength3( const LLVector4a& v )
+{
+ LLVector4a lenSqrd;
+ lenSqrd.setAllDot3(v, v);
+
+ mQ = _mm_sqrt_ps(lenSqrd.mQ);
+}
+
+// Get this vector's length
+inline LLSimdScalar LLVector4a::getLength3() const
+{
+ return _mm_sqrt_ss( dot3( (const LLVector4a)mQ ) );
+}
+
+// Set the components of this vector to the minimum of the corresponding components of lhs and rhs
+inline void LLVector4a::setMin(const LLVector4a& lhs, const LLVector4a& rhs)
+{
+ mQ = _mm_min_ps(lhs.mQ, rhs.mQ);
+}
+
+// Set the components of this vector to the maximum of the corresponding components of lhs and rhs
+inline void LLVector4a::setMax(const LLVector4a& lhs, const LLVector4a& rhs)
+{
+ mQ = _mm_max_ps(lhs.mQ, rhs.mQ);
+}
+
+// Set this to (c * lhs) + rhs * ( 1 - c)
+inline void LLVector4a::setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c)
+{
+ LLVector4a a = lhs;
+ a.mul(c);
+
+ LLVector4a b = rhs;
+ b.mul(1.f-c);
+
+ setAdd(a, b);
+}
+
+inline LLBool32 LLVector4a::isFinite3() const
+{
+ static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 };
+ const __m128i nanOrInfMaskV = *reinterpret_cast<const __m128i*> (nanOrInfMask);
+ const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV );
+ const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV ));
+ return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZ );
+}
+
+inline LLBool32 LLVector4a::isFinite4() const
+{
+ static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 };
+ const __m128i nanOrInfMaskV = *reinterpret_cast<const __m128i*> (nanOrInfMask);
+ const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV );
+ const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV ));
+ return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZW );
+}
+
+inline void LLVector4a::setRotatedInv( const LLRotation& rot, const LLVector4a& vec )
+{
+ LLRotation inv; inv.setTranspose( rot );
+ setRotated( inv, vec );
+}
+
+inline void LLVector4a::setRotatedInv( const LLQuaternion2& quat, const LLVector4a& vec )
+{
+ LLQuaternion2 invRot; invRot.setConjugate( quat );
+ setRotated(invRot, vec);
+}
+
+inline void LLVector4a::clamp( const LLVector4a& low, const LLVector4a& high )
+{
+ const LLVector4Logical highMask = greaterThan( high );
+ const LLVector4Logical lowMask = lessThan( low );
+
+ setSelectWithMask( highMask, high, *this );
+ setSelectWithMask( lowMask, low, *this );
+}
+
+
+////////////////////////////////////
+// LOGICAL
+////////////////////////////////////
+// The functions in this section will compare the elements in this vector
+// to those in rhs and return an LLVector4Logical with all bits set in elements
+// where the comparison was true and all bits unset in elements where the comparison
+// was false. See llvector4logica.h
+////////////////////////////////////
+// WARNING: Other than equals3 and equals4, these functions do NOT account
+// for floating point tolerance. You should include the appropriate tolerance
+// in the inputs.
+////////////////////////////////////
+
+inline LLVector4Logical LLVector4a::greaterThan(const LLVector4a& rhs) const
+{
+ return _mm_cmpgt_ps(mQ, rhs.mQ);
+}
+
+inline LLVector4Logical LLVector4a::lessThan(const LLVector4a& rhs) const
+{
+ return _mm_cmplt_ps(mQ, rhs.mQ);
+}
+
+inline LLVector4Logical LLVector4a::greaterEqual(const LLVector4a& rhs) const
+{
+ return _mm_cmpge_ps(mQ, rhs.mQ);
+}
+
+inline LLVector4Logical LLVector4a::lessEqual(const LLVector4a& rhs) const
+{
+ return _mm_cmple_ps(mQ, rhs.mQ);
+}
+
+inline LLVector4Logical LLVector4a::equal(const LLVector4a& rhs) const
+{
+ return _mm_cmpeq_ps(mQ, rhs.mQ);
+}
+
+// Returns true if this and rhs are componentwise equal up to the specified absolute tolerance
+inline bool LLVector4a::equals4(const LLVector4a& rhs, F32 tolerance ) const
+{
+ LLVector4a diff; diff.setSub( *this, rhs );
+ diff.setAbs( diff );
+ const LLQuad tol = _mm_set1_ps( tolerance );
+ const LLQuad cmp = _mm_cmplt_ps( diff, tol );
+ return (_mm_movemask_ps( cmp ) & LLVector4Logical::MASK_XYZW) == LLVector4Logical::MASK_XYZW;
+}
+
+inline bool LLVector4a::equals3(const LLVector4a& rhs, F32 tolerance ) const
+{
+ LLVector4a diff; diff.setSub( *this, rhs );
+ diff.setAbs( diff );
+ const LLQuad tol = _mm_set1_ps( tolerance );
+ const LLQuad t = _mm_cmplt_ps( diff, tol );
+ return (_mm_movemask_ps( t ) & LLVector4Logical::MASK_XYZ) == LLVector4Logical::MASK_XYZ;
+
+}
+
+////////////////////////////////////
+// OPERATORS
+////////////////////////////////////
+
+// Do NOT add aditional operators without consulting someone with SSE experience
+inline const LLVector4a& LLVector4a::operator= ( const LLVector4a& rhs )
+{
+ mQ = rhs.mQ;
+ return *this;
+}
+
+inline const LLVector4a& LLVector4a::operator= ( const LLQuad& rhs )
+{
+ mQ = rhs;
+ return *this;
+}
+
+inline LLVector4a::operator LLQuad() const
+{
+ return mQ;
+}
diff --git a/indra/llmath/llvector4logical.h b/indra/llmath/llvector4logical.h
new file mode 100644
index 0000000000..1c7ee1d79f
--- /dev/null
+++ b/indra/llmath/llvector4logical.h
@@ -0,0 +1,130 @@
+/**
+ * @file llvector4logical.h
+ * @brief LLVector4Logical class header file - Companion class to LLVector4a for logical and bit-twiddling operations
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2007-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_VECTOR4LOGICAL_H
+#define LL_VECTOR4LOGICAL_H
+
+
+////////////////////////////
+// LLVector4Logical
+////////////////////////////
+// This class is incomplete. If you need additional functionality,
+// for example setting/unsetting particular elements or performing
+// other boolean operations, feel free to implement. If you need
+// assistance in determining the most optimal implementation,
+// contact someone with SSE experience (Falcon, Richard, Davep, e.g.)
+////////////////////////////
+
+static LL_ALIGN_16(const U32 S_V4LOGICAL_MASK_TABLE[4*4]) =
+{
+ 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF
+};
+
+class LLVector4Logical
+{
+public:
+
+ enum {
+ MASK_X = 1,
+ MASK_Y = 1 << 1,
+ MASK_Z = 1 << 2,
+ MASK_W = 1 << 3,
+ MASK_XYZ = MASK_X | MASK_Y | MASK_Z,
+ MASK_XYZW = MASK_XYZ | MASK_W
+ };
+
+ // Empty default ctor
+ LLVector4Logical() {}
+
+ LLVector4Logical( const LLQuad& quad )
+ {
+ mQ = quad;
+ }
+
+ // Create and return a mask consisting of the lowest order bit of each element
+ inline U32 getGatheredBits() const
+ {
+ return _mm_movemask_ps(mQ);
+ };
+
+ // Invert this mask
+ inline LLVector4Logical& invert()
+ {
+ static const LL_ALIGN_16(U32 allOnes[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+ mQ = _mm_andnot_ps( mQ, *(LLQuad*)(allOnes) );
+ return *this;
+ }
+
+ inline LLBool32 areAllSet( U32 mask ) const
+ {
+ return ( getGatheredBits() & mask) == mask;
+ }
+
+ inline LLBool32 areAllSet() const
+ {
+ return areAllSet( MASK_XYZW );
+ }
+
+ inline LLBool32 areAnySet( U32 mask ) const
+ {
+ return getGatheredBits() & mask;
+ }
+
+ inline LLBool32 areAnySet() const
+ {
+ return areAnySet( MASK_XYZW );
+ }
+
+ inline operator LLQuad() const
+ {
+ return mQ;
+ }
+
+ inline void clear()
+ {
+ mQ = _mm_setzero_ps();
+ }
+
+ template<int N> void setElement()
+ {
+ mQ = _mm_or_ps( mQ, *reinterpret_cast<const LLQuad*>(S_V4LOGICAL_MASK_TABLE + 4*N) );
+ }
+
+private:
+
+ LLQuad mQ;
+};
+
+#endif //LL_VECTOR4ALOGICAL_H
diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp
new file mode 100644
index 0000000000..194b1faf81
--- /dev/null
+++ b/indra/llmath/llvolumeoctree.cpp
@@ -0,0 +1,208 @@
+/**
+
+ * @file llvolumeoctree.cpp
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llvolumeoctree.h"
+#include "llvector4a.h"
+
+BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size)
+{
+ LLVector4a fAWdU;
+ LLVector4a dir;
+ LLVector4a diff;
+
+ dir.setSub(end, start);
+ dir.mul(0.5f);
+
+ diff.setAdd(end,start);
+ diff.mul(0.5f);
+ diff.sub(center);
+ fAWdU.setAbs(dir);
+
+ LLVector4a rhs;
+ rhs.setAdd(size, fAWdU);
+
+ LLVector4a lhs;
+ lhs.setAbs(diff);
+
+ U32 grt = lhs.greaterThan(rhs).getGatheredBits();
+
+ if (grt & 0x7)
+ {
+ return false;
+ }
+
+ LLVector4a f;
+ f.setCross3(dir, diff);
+ f.setAbs(f);
+
+ LLVector4a v0, v1;
+
+ v0 = _mm_shuffle_ps(size, size,_MM_SHUFFLE(3,0,0,1));
+ v1 = _mm_shuffle_ps(fAWdU, fAWdU, _MM_SHUFFLE(3,1,2,2));
+ lhs.setMul(v0, v1);
+
+ v0 = _mm_shuffle_ps(size, size, _MM_SHUFFLE(3,1,2,2));
+ v1 = _mm_shuffle_ps(fAWdU, fAWdU, _MM_SHUFFLE(3,0,0,1));
+ rhs.setMul(v0, v1);
+ rhs.add(lhs);
+
+ grt = f.greaterThan(rhs).getGatheredBits();
+
+ return (grt & 0x7) ? false : true;
+}
+
+
+LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node)
+{
+ node->addListener(this);
+
+ mBounds = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*4);
+ mExtents = mBounds+2;
+}
+
+LLVolumeOctreeListener::~LLVolumeOctreeListener()
+{
+ ll_aligned_free_16(mBounds);
+}
+
+void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent,
+ LLOctreeNode<LLVolumeTriangle>* child)
+{
+ new LLVolumeOctreeListener(child);
+}
+
+
+LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir,
+ const LLVolumeFace* face, F32* closest_t,
+ LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+ : mFace(face),
+ mStart(start),
+ mDir(dir),
+ mIntersection(intersection),
+ mTexCoord(tex_coord),
+ mNormal(normal),
+ mBinormal(bi_normal),
+ mClosestT(closest_t),
+ mHitFace(false)
+{
+ mEnd.setAdd(mStart, mDir);
+}
+
+void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle>* node)
+{
+ LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0);
+
+ /*const F32* start = mStart.getF32();
+ const F32* end = mEnd.getF32();
+ const F32* center = vl->mBounds[0].getF32();
+ const F32* size = vl->mBounds[1].getF32();*/
+
+ //if (LLLineSegmentBoxIntersect(mStart.getF32(), mEnd.getF32(), vl->mBounds[0].getF32(), vl->mBounds[1].getF32()))
+ if (LLLineSegmentBoxIntersect(mStart, mEnd, vl->mBounds[0], vl->mBounds[1]))
+ {
+ node->accept(this);
+ for (S32 i = 0; i < node->getChildCount(); ++i)
+ {
+ traverse(node->getChild(i));
+ }
+ }
+}
+
+void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle>* node)
+{
+ for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter =
+ node->getData().begin(); iter != node->getData().end(); ++iter)
+ {
+ const LLVolumeTriangle* tri = *iter;
+
+ F32 a, b, t;
+
+ if (LLTriangleRayIntersect(*tri->mV[0], *tri->mV[1], *tri->mV[2],
+ mStart, mDir, a, b, t))
+ {
+ if ((t >= 0.f) && // if hit is after start
+ (t <= 1.f) && // and before end
+ (t < *mClosestT)) // and this hit is closer
+ {
+ *mClosestT = t;
+ mHitFace = true;
+
+ if (mIntersection != NULL)
+ {
+ LLVector4a intersect = mDir;
+ intersect.mul(*mClosestT);
+ intersect.add(mStart);
+ mIntersection->set(intersect.getF32ptr());
+ }
+
+
+ if (mTexCoord != NULL)
+ {
+ LLVector2* tc = (LLVector2*) mFace->mTexCoords;
+ *mTexCoord = ((1.f - a - b) * tc[tri->mIndex[0]] +
+ a * tc[tri->mIndex[1]] +
+ b * tc[tri->mIndex[2]]);
+
+ }
+
+ if (mNormal != NULL)
+ {
+ LLVector4* norm = (LLVector4*) mFace->mNormals;
+
+ *mNormal = ((1.f - a - b) * LLVector3(norm[tri->mIndex[0]]) +
+ a * LLVector3(norm[tri->mIndex[1]]) +
+ b * LLVector3(norm[tri->mIndex[2]]));
+ }
+
+ if (mBinormal != NULL)
+ {
+ LLVector4* binormal = (LLVector4*) mFace->mBinormals;
+ *mBinormal = ((1.f - a - b) * LLVector3(binormal[tri->mIndex[0]]) +
+ a * LLVector3(binormal[tri->mIndex[1]]) +
+ b * LLVector3(binormal[tri->mIndex[2]]));
+ }
+ }
+ }
+ }
+}
+
+const LLVector4a& LLVolumeTriangle::getPositionGroup() const
+{
+ return *mPositionGroup;
+}
+
+const F32& LLVolumeTriangle::getBinRadius() const
+{
+ return mRadius;
+}
+
+
diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h
new file mode 100644
index 0000000000..0031626498
--- /dev/null
+++ b/indra/llmath/llvolumeoctree.h
@@ -0,0 +1,138 @@
+/**
+ * @file llvolumeoctree.h
+ * @brief LLVolume octree classes.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLVOLUME_OCTREE_H
+#define LL_LLVOLUME_OCTREE_H
+
+#include "linden_common.h"
+#include "llmemory.h"
+
+#include "lloctree.h"
+#include "llvolume.h"
+#include "llvector4a.h"
+
+class LLVolumeOctreeListener : public LLOctreeListener<LLVolumeTriangle>
+{
+public:
+
+ LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node);
+ ~LLVolumeOctreeListener();
+
+ LLVolumeOctreeListener(const LLVolumeOctreeListener& rhs)
+ {
+ *this = rhs;
+ }
+
+ const LLVolumeOctreeListener& operator=(const LLVolumeOctreeListener& rhs)
+ {
+ llerrs << "Illegal operation!" << llendl;
+ return *this;
+ }
+
+ //LISTENER FUNCTIONS
+ virtual void handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent,
+ LLOctreeNode<LLVolumeTriangle>* child);
+ virtual void handleStateChange(const LLTreeNode<LLVolumeTriangle>* node) { }
+ virtual void handleChildRemoval(const LLOctreeNode<LLVolumeTriangle>* parent,
+ const LLOctreeNode<LLVolumeTriangle>* child) { }
+ virtual void handleInsertion(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
+ virtual void handleRemoval(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
+ virtual void handleDestruction(const LLTreeNode<LLVolumeTriangle>* node) { }
+
+
+public:
+ LLVector4a* mBounds; // bounding box (center, size) of this node and all its children (tight fit to objects)
+ LLVector4a* mExtents; // extents (min, max) of this node and all its children
+};
+
+class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle>
+{
+public:
+ const LLVolumeFace* mFace;
+ LLVector4a mStart;
+ LLVector4a mDir;
+ LLVector4a mEnd;
+ LLVector3* mIntersection;
+ LLVector2* mTexCoord;
+ LLVector3* mNormal;
+ LLVector3* mBinormal;
+ F32* mClosestT;
+ bool mHitFace;
+
+ LLOctreeTriangleRayIntersect() { };
+
+ LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir,
+ const LLVolumeFace* face, F32* closest_t,
+ LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal);
+
+ void traverse(const LLOctreeNode<LLVolumeTriangle>* node);
+
+ virtual void visit(const LLOctreeNode<LLVolumeTriangle>* node);
+};
+
+class LLVolumeTriangle : public LLRefCount
+{
+public:
+ LLVolumeTriangle()
+ {
+ mPositionGroup = (LLVector4a*) ll_aligned_malloc_16(16);
+ }
+
+ LLVolumeTriangle(const LLVolumeTriangle& rhs)
+ {
+ *this = rhs;
+ }
+
+ const LLVolumeTriangle& operator=(const LLVolumeTriangle& rhs)
+ {
+ llerrs << "Illegal operation!" << llendl;
+ return *this;
+ }
+
+ ~LLVolumeTriangle()
+ {
+ ll_aligned_free_16(mPositionGroup);
+ }
+
+ const LLVector4a* mV[3];
+ U16 mIndex[3];
+
+ LLVector4a* mPositionGroup;
+
+ F32 mRadius;
+
+ virtual const LLVector4a& getPositionGroup() const;
+ virtual const F32& getBinRadius() const;
+};
+
+
+#endif
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
new file mode 100644
index 0000000000..82765c740f
--- /dev/null
+++ b/indra/llprimitive/llmodel.cpp
@@ -0,0 +1,1695 @@
+/**
+ * @file llmodel.cpp
+ * @brief Model handling implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2007, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlife.com/developers/opensource/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlife.com/developers/opensource/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llmodel.h"
+#include "llsdserialize.h"
+#include "llvector4a.h"
+
+#include "dae.h"
+#include "dae/daeErrorHandler.h"
+#include "dom/domConstants.h"
+#include "dom/domMesh.h"
+#include "zlib/zlib.h"
+
+#if LL_MESH_ENABLED
+
+std::string model_names[] =
+{
+ "lowest_lod",
+ "low_lod",
+ "medium_lod",
+ "high_lod",
+ "physics_shape"
+};
+
+const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string);
+
+LLModel::LLModel(LLVolumeParams& params, F32 detail)
+ : LLVolume(params, detail), mNormalizedScale(1,1,1), mNormalizedTranslation(0,0,0)
+{
+
+}
+
+void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Array& inputs, U32 min_idx, U32 max_idx)
+{
+ for (U32 j = 0; j < inputs.getCount(); ++j)
+ {
+ if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0)
+ { //found vertex array
+ const domURIFragmentType& uri = inputs[j]->getSource();
+ daeElementRef elem = uri.getElement();
+ domVertices* vertices = (domVertices*) elem.cast();
+
+ domInputLocal_Array& v_inp = vertices->getInput_array();
+ if (inputs[j]->getOffset() != 0)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ for (U32 k = 0; k < v_inp.getCount(); ++k)
+ {
+ if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
+ {
+ const domURIFragmentType& uri = v_inp[k]->getSource();
+
+ daeElementRef elem = uri.getElement();
+ domSource* src = (domSource*) elem.cast();
+
+ if (src->getTechnique_common()->getAccessor()->getStride() != 3)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ domListOfFloats& v = src->getFloat_array()->getValue();
+
+ LLVector4a min;
+ min.set(v[min_idx], v[min_idx+1], v[min_idx+2]);
+ LLVector4a max = min;
+
+ for (U32 j = min_idx; j <= max_idx; ++j)
+ { //copy vertex array
+ face.mPositions[j-min_idx].set(v[j*3+0], v[j*3+1], v[j*3+2]);
+ update_min_max(min, max, face.mPositions[j-min_idx]);
+ }
+
+ face.mExtents[0] = min;
+ face.mExtents[1] = max;
+ }
+ }
+ }
+
+ if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0)
+ {
+ //found normal array for this triangle list
+ const domURIFragmentType& uri = inputs[j]->getSource();
+ daeElementRef elem = uri.getElement();
+ domSource* src = (domSource*) elem.cast();
+ domListOfFloats& n = src->getFloat_array()->getValue();
+
+ for (U32 j = min_idx; j <= max_idx; ++j)
+ {
+ LLVector4a* norm = (LLVector4a*) face.mNormals + (j-min_idx);
+ norm->set(n[j*3+0], n[j*3+1], n[j*3+2]);
+ norm->normalize3fast();
+ }
+ }
+ else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0)
+ { //found texCoords
+ const domURIFragmentType& uri = inputs[j]->getSource();
+ daeElementRef elem = uri.getElement();
+ domSource* src = (domSource*) elem.cast();
+ domListOfFloats& u = src->getFloat_array()->getValue();
+
+ for (U32 j = min_idx; j <= max_idx; ++j)
+ {
+ face.mTexCoords[j-min_idx].setVec(u[j*2+0], u[j*2+1]);
+ }
+ }
+ }
+}
+
+void get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride,
+ domSource* &pos_source, domSource* &tc_source, domSource* &norm_source)
+{
+ idx_stride = 0;
+
+ for (U32 j = 0; j < inputs.getCount(); ++j)
+ {
+ idx_stride = llmax((S32) inputs[j]->getOffset(), idx_stride);
+
+ if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0)
+ { //found vertex array
+ const domURIFragmentType& uri = inputs[j]->getSource();
+ daeElementRef elem = uri.getElement();
+ domVertices* vertices = (domVertices*) elem.cast();
+
+ domInputLocal_Array& v_inp = vertices->getInput_array();
+
+
+ for (U32 k = 0; k < v_inp.getCount(); ++k)
+ {
+ if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
+ {
+ pos_offset = inputs[j]->getOffset();
+
+ const domURIFragmentType& uri = v_inp[k]->getSource();
+ daeElementRef elem = uri.getElement();
+ pos_source = (domSource*) elem.cast();
+ }
+
+ if (strcmp(COMMON_PROFILE_INPUT_NORMAL, v_inp[k]->getSemantic()) == 0)
+ {
+ norm_offset = inputs[j]->getOffset();
+
+ const domURIFragmentType& uri = v_inp[k]->getSource();
+ daeElementRef elem = uri.getElement();
+ norm_source = (domSource*) elem.cast();
+ }
+ }
+ }
+
+ if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0)
+ {
+ //found normal array for this triangle list
+ norm_offset = inputs[j]->getOffset();
+ const domURIFragmentType& uri = inputs[j]->getSource();
+ daeElementRef elem = uri.getElement();
+ norm_source = (domSource*) elem.cast();
+ }
+ else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0)
+ { //found texCoords
+ tc_offset = inputs[j]->getOffset();
+ const domURIFragmentType& uri = inputs[j]->getSource();
+ daeElementRef elem = uri.getElement();
+ tc_source = (domSource*) elem.cast();
+ }
+ }
+
+ idx_stride += 1;
+}
+
+void load_face_from_dom_triangles(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domTrianglesRef& tri)
+{
+ LLVolumeFace face;
+ std::vector<LLVolumeFace::VertexData> verts;
+ std::vector<U16> indices;
+
+ const domInputLocalOffset_Array& inputs = tri->getInput_array();
+
+ S32 pos_offset = -1;
+ S32 tc_offset = -1;
+ S32 norm_offset = -1;
+
+ domSource* pos_source = NULL;
+ domSource* tc_source = NULL;
+ domSource* norm_source = NULL;
+
+ S32 idx_stride = 0;
+
+ get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source);
+
+ domPRef p = tri->getP();
+ domListOfUInts& idx = p->getValue();
+
+ domListOfFloats v;
+ domListOfFloats tc;
+ domListOfFloats n;
+
+ if (pos_source)
+ {
+ v = pos_source->getFloat_array()->getValue();
+ face.mExtents[0].set(v[0], v[1], v[2]);
+ face.mExtents[1].set(v[0], v[1], v[2]);
+ }
+
+ if (tc_source)
+ {
+ tc = tc_source->getFloat_array()->getValue();
+ }
+
+ if (norm_source)
+ {
+ n = norm_source->getFloat_array()->getValue();
+ }
+
+
+ LLVolumeFace::VertexMapData::PointMap point_map;
+
+ for (U32 i = 0; i < idx.getCount(); i += idx_stride)
+ {
+ LLVolumeFace::VertexData cv;
+ if (pos_source)
+ {
+ cv.setPosition(LLVector4a(v[idx[i+pos_offset]*3+0],
+ v[idx[i+pos_offset]*3+1],
+ v[idx[i+pos_offset]*3+2]));
+ }
+
+ if (tc_source)
+ {
+ cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0],
+ tc[idx[i+tc_offset]*2+1]);
+ }
+
+ if (norm_source)
+ {
+ cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0],
+ n[idx[i+norm_offset]*3+1],
+ n[idx[i+norm_offset]*3+2]));
+ }
+
+
+ BOOL found = FALSE;
+
+ LLVolumeFace::VertexMapData::PointMap::iterator point_iter;
+ point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr()));
+
+ if (point_iter != point_map.end())
+ {
+ for (U32 j = 0; j < point_iter->second.size(); ++j)
+ {
+ if ((point_iter->second)[j] == cv)
+ {
+ found = TRUE;
+ indices.push_back((point_iter->second)[j].mIndex);
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
+ verts.push_back(cv);
+ if (verts.size() >= 65535)
+ {
+ llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl;
+ }
+ U16 index = (U16) (verts.size()-1);
+ indices.push_back(index);
+
+ LLVolumeFace::VertexMapData d;
+ d.setPosition(cv.getPosition());
+ d.mTexCoord = cv.mTexCoord;
+ d.setNormal(cv.getNormal());
+ d.mIndex = index;
+ if (point_iter != point_map.end())
+ {
+ point_iter->second.push_back(d);
+ }
+ else
+ {
+ point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d);
+ }
+ }
+
+ if (indices.size()%3 == 0 && verts.size() >= 65532)
+ {
+ face_list.push_back(face);
+ face_list.rbegin()->fillFromLegacyData(verts, indices);
+ face = LLVolumeFace();
+ point_map.clear();
+ }
+
+ }
+
+ if (!verts.empty())
+ {
+ std::string material;
+
+ if (tri->getMaterial())
+ {
+ material = std::string(tri->getMaterial());
+ }
+
+ materials.push_back(material);
+ face_list.push_back(face);
+
+ face_list.rbegin()->fillFromLegacyData(verts, indices);
+ }
+
+}
+
+void load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly)
+{
+ domPRef p = poly->getP();
+ domListOfUInts& idx = p->getValue();
+
+ if (idx.getCount() == 0)
+ {
+ return;
+ }
+
+ const domInputLocalOffset_Array& inputs = poly->getInput_array();
+
+
+ domListOfUInts& vcount = poly->getVcount()->getValue();
+
+ S32 pos_offset = -1;
+ S32 tc_offset = -1;
+ S32 norm_offset = -1;
+
+ domSource* pos_source = NULL;
+ domSource* tc_source = NULL;
+ domSource* norm_source = NULL;
+
+ S32 idx_stride = 0;
+
+ get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source);
+
+ LLVolumeFace face;
+
+ std::vector<U16> indices;
+ std::vector<LLVolumeFace::VertexData> verts;
+
+ domListOfFloats v;
+ domListOfFloats tc;
+ domListOfFloats n;
+
+ if (pos_source)
+ {
+ v = pos_source->getFloat_array()->getValue();
+ face.mExtents[0].set(v[0], v[1], v[2]);
+ face.mExtents[1].set(v[0], v[1], v[2]);
+ }
+
+ if (tc_source)
+ {
+ tc = tc_source->getFloat_array()->getValue();
+ }
+
+ if (norm_source)
+ {
+ n = norm_source->getFloat_array()->getValue();
+ }
+
+ LLVolumeFace::VertexMapData::PointMap point_map;
+
+ U32 cur_idx = 0;
+ for (U32 i = 0; i < vcount.getCount(); ++i)
+ { //for each polygon
+ U32 first_index = 0;
+ U32 last_index = 0;
+ for (U32 j = 0; j < vcount[i]; ++j)
+ { //for each vertex
+
+ LLVolumeFace::VertexData cv;
+
+ if (pos_source)
+ {
+ cv.getPosition().set(v[idx[cur_idx+pos_offset]*3+0],
+ v[idx[cur_idx+pos_offset]*3+1],
+ v[idx[cur_idx+pos_offset]*3+2]);
+ }
+
+ if (tc_source)
+ {
+ cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0],
+ tc[idx[cur_idx+tc_offset]*2+1]);
+ }
+
+ if (norm_source)
+ {
+ cv.getNormal().set(n[idx[cur_idx+norm_offset]*3+0],
+ n[idx[cur_idx+norm_offset]*3+1],
+ n[idx[cur_idx+norm_offset]*3+2]);
+ }
+
+ cur_idx += idx_stride;
+
+ BOOL found = FALSE;
+
+ LLVolumeFace::VertexMapData::PointMap::iterator point_iter;
+ LLVector3 pos3(cv.getPosition().getF32ptr());
+ point_iter = point_map.find(pos3);
+
+ if (point_iter != point_map.end())
+ {
+ for (U32 k = 0; k < point_iter->second.size(); ++k)
+ {
+ if ((point_iter->second)[k] == cv)
+ {
+ found = TRUE;
+ U32 index = (point_iter->second)[k].mIndex;
+ if (j == 0)
+ {
+ first_index = index;
+ }
+ else if (j == 1)
+ {
+ last_index = index;
+ }
+ else
+ {
+ indices.push_back(first_index);
+ indices.push_back(last_index);
+ indices.push_back(index);
+ last_index = index;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
+ verts.push_back(cv);
+ if (verts.size() >= 65535)
+ {
+ llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl;
+ }
+ U16 index = (U16) (verts.size()-1);
+
+ if (j == 0)
+ {
+ first_index = index;
+ }
+ else if (j == 1)
+ {
+ last_index = index;
+ }
+ else
+ {
+ indices.push_back(first_index);
+ indices.push_back(last_index);
+ indices.push_back(index);
+ last_index = index;
+ }
+
+ LLVolumeFace::VertexMapData d;
+ d.setPosition(cv.getPosition());
+ d.mTexCoord = cv.mTexCoord;
+ d.setNormal(cv.getNormal());
+ d.mIndex = index;
+ if (point_iter != point_map.end())
+ {
+ point_iter->second.push_back(d);
+ }
+ else
+ {
+ point_map[pos3].push_back(d);
+ }
+ }
+
+ if (indices.size()%3 == 0 && indices.size() >= 65532)
+ {
+ face_list.push_back(face);
+ face_list.rbegin()->fillFromLegacyData(verts, indices);
+ face = LLVolumeFace();
+ verts.clear();
+ indices.clear();
+ point_map.clear();
+ }
+ }
+ }
+
+ if (!verts.empty())
+ {
+ std::string material;
+
+ if (poly->getMaterial())
+ {
+ material = std::string(poly->getMaterial());
+ }
+
+ materials.push_back(material);
+ face_list.push_back(face);
+ face_list.rbegin()->fillFromLegacyData(verts, indices);
+ }
+}
+
+void load_face_from_dom_polygons(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolygonsRef& poly)
+{
+ LLVolumeFace face;
+ std::vector<U16> indices;
+ std::vector<LLVolumeFace::VertexData> verts;
+
+ const domInputLocalOffset_Array& inputs = poly->getInput_array();
+
+
+ S32 v_offset = -1;
+ S32 n_offset = -1;
+ S32 t_offset = -1;
+
+ domListOfFloats* v = NULL;
+ domListOfFloats* n = NULL;
+ domListOfFloats* t = NULL;
+
+ U32 stride = 0;
+ for (U32 i = 0; i < inputs.getCount(); ++i)
+ {
+ stride = llmax((U32) inputs[i]->getOffset()+1, stride);
+
+ if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0)
+ { //found vertex array
+ v_offset = inputs[i]->getOffset();
+
+ const domURIFragmentType& uri = inputs[i]->getSource();
+ daeElementRef elem = uri.getElement();
+ domVertices* vertices = (domVertices*) elem.cast();
+
+ domInputLocal_Array& v_inp = vertices->getInput_array();
+
+ for (U32 k = 0; k < v_inp.getCount(); ++k)
+ {
+ if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
+ {
+ const domURIFragmentType& uri = v_inp[k]->getSource();
+ daeElementRef elem = uri.getElement();
+ domSource* src = (domSource*) elem.cast();
+ v = &(src->getFloat_array()->getValue());
+ }
+ }
+ }
+ else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[i]->getSemantic()) == 0)
+ {
+ n_offset = inputs[i]->getOffset();
+ //found normal array for this triangle list
+ const domURIFragmentType& uri = inputs[i]->getSource();
+ daeElementRef elem = uri.getElement();
+ domSource* src = (domSource*) elem.cast();
+ n = &(src->getFloat_array()->getValue());
+ }
+ else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[i]->getSemantic()) == 0 && inputs[i]->getSet() == 0)
+ { //found texCoords
+ t_offset = inputs[i]->getOffset();
+ const domURIFragmentType& uri = inputs[i]->getSource();
+ daeElementRef elem = uri.getElement();
+ domSource* src = (domSource*) elem.cast();
+ t = &(src->getFloat_array()->getValue());
+ }
+ }
+
+ domP_Array& ps = poly->getP_array();
+
+ //make a triangle list in <verts>
+ for (U32 i = 0; i < ps.getCount(); ++i)
+ { //for each polygon
+ domListOfUInts& idx = ps[i]->getValue();
+ for (U32 j = 0; j < idx.getCount()/stride; ++j)
+ { //for each vertex
+ if (j > 2)
+ {
+ U32 size = verts.size();
+ LLVolumeFace::VertexData v0 = verts[size-3];
+ LLVolumeFace::VertexData v1 = verts[size-1];
+
+ verts.push_back(v0);
+ verts.push_back(v1);
+ }
+
+ LLVolumeFace::VertexData vert;
+
+
+ if (v)
+ {
+ U32 v_idx = idx[j*stride+v_offset]*3;
+ vert.getPosition().set(v->get(v_idx),
+ v->get(v_idx+1),
+ v->get(v_idx+2));
+ }
+
+ if (n)
+ {
+ U32 n_idx = idx[j*stride+n_offset]*3;
+ vert.getNormal().set(n->get(n_idx),
+ n->get(n_idx+1),
+ n->get(n_idx+2));
+ }
+
+ if (t)
+ {
+ U32 t_idx = idx[j*stride+t_offset]*2;
+ vert.mTexCoord.setVec(t->get(t_idx),
+ t->get(t_idx+1));
+ }
+
+
+ verts.push_back(vert);
+ }
+ }
+
+ if (verts.empty())
+ {
+ return;
+ }
+
+ face.mExtents[0] = verts[0].getPosition();
+ face.mExtents[1] = verts[0].getPosition();
+
+ //create a map of unique vertices to indices
+ std::map<LLVolumeFace::VertexData, U32> vert_idx;
+
+ U32 cur_idx = 0;
+ for (U32 i = 0; i < verts.size(); ++i)
+ {
+ std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.find(verts[i]);
+ if (iter == vert_idx.end())
+ {
+ vert_idx[verts[i]] = cur_idx++;
+ }
+ }
+
+ //build vertex array from map
+ verts.resize(vert_idx.size());
+
+ for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter)
+ {
+ verts[iter->second] = iter->first;
+ update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition());
+ }
+
+ //build index array from map
+ indices.resize(verts.size());
+
+ for (U32 i = 0; i < verts.size(); ++i)
+ {
+ indices[i] = vert_idx[verts[i]];
+ }
+
+
+ if (!verts.empty())
+ {
+ std::string material;
+
+ if (poly->getMaterial())
+ {
+ material = std::string(poly->getMaterial());
+ }
+
+ materials.push_back(material);
+ face_list.push_back(face);
+ face_list.rbegin()->fillFromLegacyData(verts, indices);
+ }
+}
+
+void LLModel::addVolumeFacesFromDomMesh(domMesh* mesh)
+{
+ domTriangles_Array& tris = mesh->getTriangles_array();
+
+ for (U32 i = 0; i < tris.getCount(); ++i)
+ {
+ domTrianglesRef& tri = tris.get(i);
+
+ load_face_from_dom_triangles(mVolumeFaces, mMaterialList, tri);
+ }
+
+ domPolylist_Array& polys = mesh->getPolylist_array();
+ for (U32 i = 0; i < polys.getCount(); ++i)
+ {
+ domPolylistRef& poly = polys.get(i);
+
+ load_face_from_dom_polylist(mVolumeFaces, mMaterialList, poly);
+ }
+
+ domPolygons_Array& polygons = mesh->getPolygons_array();
+ for (U32 i = 0; i < polygons.getCount(); ++i)
+ {
+ domPolygonsRef& poly = polygons.get(i);
+
+ load_face_from_dom_polygons(mVolumeFaces, mMaterialList, poly);
+ }
+
+}
+
+BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh)
+{
+ if (mesh)
+ {
+ mVolumeFaces.clear();
+ mMaterialList.clear();
+
+ addVolumeFacesFromDomMesh(mesh);
+
+ if (getNumVolumeFaces() > 0)
+ {
+ optimizeVolumeFaces();
+ normalizeVolumeFaces();
+
+ if (getNumVolumeFaces() > 0)
+ {
+ return TRUE;
+ }
+ }
+ }
+ else
+ {
+ llwarns << "no mesh found" << llendl;
+ }
+
+ return FALSE;
+}
+
+
+BOOL LLModel::createVolumeFacesFromFile(const std::string& file_name)
+{
+ DAE dae;
+ domCOLLADA* dom = dae.open(file_name);
+ if (dom)
+ {
+ daeDatabase* db = dae.getDatabase();
+
+ daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
+
+ mVolumeFaces.clear();
+ mMaterialList.clear();
+
+ for (daeInt idx = 0; idx < count; ++idx)
+ {
+ domMesh* mesh = NULL;
+
+ db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH);
+
+ if (mesh)
+ {
+ addVolumeFacesFromDomMesh(mesh);
+ }
+ }
+
+ if (getNumVolumeFaces() > 0)
+ {
+ optimizeVolumeFaces();
+ normalizeVolumeFaces();
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+void LLModel::optimizeVolumeFaces()
+{
+#if 0 //VECTORIZE ?
+ for (std::vector<LLVolumeFace>::iterator iter = mVolumeFaces.begin(); iter != mVolumeFaces.end(); )
+ {
+ std::vector<LLVolumeFace>::iterator cur_iter = iter++;
+ LLVolumeFace& face = *cur_iter;
+
+ for (S32 i = 0; i < (S32) face.mNumIndices; i += 3)
+ { //remove zero area triangles
+ U16 i0 = face.mIndices[i+0];
+ U16 i1 = face.mIndices[i+1];
+ U16 i2 = face.mIndices[i+2];
+
+ if (i0 == i1 ||
+ i1 == i2 ||
+ i0 == i2)
+ { //duplicate index in triangle, remove triangle
+ face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3);
+ i -= 3;
+ }
+ else
+ {
+ LLVolumeFace::VertexData& v0 = face.mVertices[i0];
+ LLVolumeFace::VertexData& v1 = face.mVertices[i1];
+ LLVolumeFace::VertexData& v2 = face.mVertices[i2];
+
+ if (v0.mPosition == v1.mPosition ||
+ v1.mPosition == v2.mPosition ||
+ v2.mPosition == v0.mPosition)
+ { //zero area triangle, delete
+ face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3);
+ i-=3;
+ }
+ }
+ }
+
+ //remove unreference vertices
+ std::vector<bool> ref;
+ ref.resize(face.mNumVertices);
+
+ for (U32 i = 0; i < ref.size(); ++i)
+ {
+ ref[i] = false;
+ }
+
+ for (U32 i = 0; i < face.mNumIndices; ++i)
+ {
+ ref[face.mIndices[i]] = true;
+ }
+
+ U32 unref_count = 0;
+ for (U32 i = 0; i < ref.size(); ++i)
+ {
+ if (!ref[i])
+ {
+ //vertex is unreferenced
+ face.mVertices.erase(face.mVertices.begin()+(i-unref_count));
+ U16 idx = (U16) (i-unref_count);
+
+ for (U32 j = 0; j < face.mNumIndices; ++j)
+ { //decrement every index array value greater than idx
+ if (face.mIndices[j] > idx)
+ {
+ --face.mIndices[j];
+ }
+ }
+ ++unref_count;
+ }
+ }
+
+ if (face.mVertices.empty() || face.mIndices.empty())
+ { //face is empty, remove it
+ iter = mVolumeFaces.erase(cur_iter);
+ }
+ }
+#endif
+}
+
+void LLModel::normalizeVolumeFaces()
+{
+
+ // ensure we don't have too many faces
+ if (mVolumeFaces.size() > LL_SCULPT_MESH_MAX_FACES)
+ mVolumeFaces.resize(LL_SCULPT_MESH_MAX_FACES);
+
+ if (!mVolumeFaces.empty())
+ {
+ LLVector4a min, max;
+
+ if (mVolumeFaces[0].mNumVertices <= 0)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ min = mVolumeFaces[0].mExtents[0];
+ max = mVolumeFaces[0].mExtents[1];
+
+ for (U32 i = 1; i < mVolumeFaces.size(); ++i)
+ {
+ LLVolumeFace& face = mVolumeFaces[i];
+
+ if (face.mNumVertices <= 0)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ update_min_max(min, max, face.mExtents[0]);
+ update_min_max(min, max, face.mExtents[1]);
+ }
+
+ LLVector4a trans;
+ trans.setAdd(min, max);
+ trans.mul(-0.5f);
+ LLVector4a size;
+ size.setSub(max, min);
+
+ F32 scale = 1.f/llmax(llmax(size[0], size[1]), size[2]);
+
+ for (U32 i = 0; i < mVolumeFaces.size(); ++i)
+ {
+ LLVolumeFace& face = mVolumeFaces[i];
+
+ face.mExtents[0].add(trans);
+ face.mExtents[0].mul(scale);
+
+ face.mExtents[1].add(trans);
+ face.mExtents[1].mul(scale);
+
+ LLVector4a* pos = (LLVector4a*) face.mPositions;
+ for (U32 j = 0; j < face.mNumVertices; ++j)
+ {
+ pos[j].add(trans);
+ pos[j].mul(scale);
+ }
+ }
+
+ mNormalizedScale = LLVector3(1,1,1) / scale;
+ mNormalizedTranslation.set(trans.getF32ptr());
+ mNormalizedTranslation *= -1.f;
+ }
+}
+
+void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out)
+{
+ scale_out = mNormalizedScale;
+ translation_out = mNormalizedTranslation;
+}
+
+void LLModel::setNumVolumeFaces(S32 count)
+{
+ mVolumeFaces.resize(count);
+}
+
+void LLModel::setVolumeFaceData(S32 f,
+ LLStrider<LLVector3> pos,
+ LLStrider<LLVector3> norm,
+ LLStrider<LLVector2> tc,
+ LLStrider<U16> ind,
+ U32 num_verts,
+ U32 num_indices)
+{
+ LLVolumeFace& face = mVolumeFaces[f];
+
+ face.resizeVertices(num_verts);
+ face.resizeIndices(num_indices);
+
+ LLVector4a::memcpyNonAliased16((F32*) face.mPositions, (F32*) pos.get(), num_verts*4*sizeof(F32));
+ LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32));
+ LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), num_verts*2*sizeof(F32));
+ U32 size = (num_indices*2+0xF)&~0xF;
+ LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size);
+}
+
+void LLModel::appendFaces(LLModel *model, LLMatrix4 &transform, LLMatrix4& norm_mat)
+{
+ if (mVolumeFaces.empty())
+ {
+ setNumVolumeFaces(1);
+ }
+
+ LLVolumeFace& face = mVolumeFaces[mVolumeFaces.size()-1];
+
+
+ for (S32 i = 0; i < model->getNumFaces(); ++i)
+ {
+ face.appendFace(model->getVolumeFace(i), transform, norm_mat);
+ }
+
+}
+
+void LLModel::appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat)
+{
+ S32 rindex = getNumVolumeFaces()-1;
+ if (rindex == -1 ||
+ mVolumeFaces[rindex].mNumVertices + src_face.mNumVertices >= 65536)
+ { //empty or overflow will occur, append new face
+ LLVolumeFace cur_face;
+ cur_face.appendFace(src_face, mat, norm_mat);
+ addFace(cur_face);
+ mMaterialList.push_back(src_material);
+ }
+ else
+ { //append to existing end face
+ mVolumeFaces.rbegin()->appendFace(src_face, mat, norm_mat);
+ }
+}
+
+void LLModel::addFace(const LLVolumeFace& face)
+{
+ if (face.mNumVertices == 0)
+ {
+ llerrs << "Cannot add empty face." << llendl;
+ }
+
+ mVolumeFaces.push_back(face);
+
+ if (mVolumeFaces.size() > MAX_MODEL_FACES)
+ {
+ llerrs << "Model prims cannot have more than " << MAX_MODEL_FACES << " faces!" << llendl;
+ }
+}
+
+
+void LLModel::smoothNormals(F32 angle_cutoff)
+{
+ //smooth normals for all faces by:
+ // 1 - Create faceted copy of face with no texture coordinates
+ // 2 - Weld vertices in faceted copy that are shared between triangles with less than "angle_cutoff" difference between normals
+ // 3 - Generate smoothed set of normals based on welding results
+ // 4 - Create faceted copy of face with texture coordinates
+ // 5 - Copy smoothed normals to faceted copy, using closest normal to triangle normal where more than one normal exists for a given position
+ // 6 - Remove redundant vertices from new faceted (now smooth) copy
+
+ angle_cutoff = cosf(angle_cutoff);
+ for (U32 j = 0; j < mVolumeFaces.size(); ++j)
+ {
+ LLVolumeFace& vol_face = mVolumeFaces[j];
+
+ //create faceted copy of current face with no texture coordinates (step 1)
+ LLVolumeFace faceted;
+
+ LLVector4a* src_pos = (LLVector4a*) vol_face.mPositions;
+ //LLVector4a* src_norm = (LLVector4a*) vol_face.mNormals;
+
+
+ //bake out triangles into temporary face, clearing normals and texture coordinates
+ for (U32 i = 0; i < vol_face.mNumIndices; ++i)
+ {
+ U32 idx = vol_face.mIndices[i];
+ LLVolumeFace::VertexData v;
+ v.setPosition(src_pos[idx]);
+ v.getNormal().clear();
+ v.mTexCoord.clear();
+ faceted.pushVertex(v);
+ faceted.pushIndex(i);
+ }
+
+ //generate normals for temporary face
+ for (U32 i = 0; i < faceted.mNumIndices; i += 3)
+ { //for each triangle
+ U16 i0 = faceted.mIndices[i+0];
+ U16 i1 = faceted.mIndices[i+1];
+ U16 i2 = faceted.mIndices[i+2];
+
+ LLVector4a& p0 = faceted.mPositions[i0];
+ LLVector4a& p1 = faceted.mPositions[i1];
+ LLVector4a& p2 = faceted.mPositions[i2];
+
+ LLVector4a& n0 = faceted.mNormals[i0];
+ LLVector4a& n1 = faceted.mNormals[i1];
+ LLVector4a& n2 = faceted.mNormals[i2];
+
+ LLVector4a lhs, rhs;
+ lhs.setSub(p1, p0);
+ rhs.setSub(p2, p0);
+
+ n0.setCross3(lhs, rhs);
+ n0.normalize3fast();
+ n1 = n0;
+ n2 = n0;
+ }
+
+ //weld vertices in temporary face, respecting angle_cutoff (step 2)
+ faceted.optimize(angle_cutoff);
+
+ //generate normals for welded face based on new topology (step 3)
+
+ for (U32 i = 0; i < faceted.mNumVertices; i++)
+ {
+ faceted.mNormals[i].clear();
+ }
+
+ for (U32 i = 0; i < faceted.mNumIndices; i += 3)
+ { //for each triangle
+ U16 i0 = faceted.mIndices[i+0];
+ U16 i1 = faceted.mIndices[i+1];
+ U16 i2 = faceted.mIndices[i+2];
+
+ LLVector4a& p0 = faceted.mPositions[i0];
+ LLVector4a& p1 = faceted.mPositions[i1];
+ LLVector4a& p2 = faceted.mPositions[i2];
+
+ LLVector4a& n0 = faceted.mNormals[i0];
+ LLVector4a& n1 = faceted.mNormals[i1];
+ LLVector4a& n2 = faceted.mNormals[i2];
+
+ LLVector4a lhs, rhs;
+ lhs.setSub(p1, p0);
+ rhs.setSub(p2, p0);
+
+ LLVector4a n;
+ n.setCross3(lhs, rhs);
+
+ n0.add(n);
+ n1.add(n);
+ n2.add(n);
+ }
+
+ //normalize normals and build point map
+ LLVolumeFace::VertexMapData::PointMap point_map;
+
+ for (U32 i = 0; i < faceted.mNumVertices; ++i)
+ {
+ faceted.mNormals[i].normalize3fast();
+
+ LLVolumeFace::VertexMapData v;
+ v.setPosition(faceted.mPositions[i]);
+ v.setNormal(faceted.mNormals[i]);
+
+ point_map[LLVector3(v.getPosition().getF32ptr())].push_back(v);
+ }
+
+ //create faceted copy of current face with texture coordinates (step 4)
+ LLVolumeFace new_face;
+
+ //bake out triangles into new face
+ for (U32 i = 0; i < vol_face.mNumIndices; ++i)
+ {
+ U32 idx = vol_face.mIndices[i];
+ LLVolumeFace::VertexData v;
+ v.setPosition(vol_face.mPositions[idx]);
+ v.setNormal(vol_face.mNormals[idx]);
+ v.mTexCoord = vol_face.mTexCoords[idx];
+
+ new_face.pushVertex(v);
+ new_face.pushIndex(i);
+ }
+
+ //generate normals for new face
+ for (U32 i = 0; i < new_face.mNumIndices; i += 3)
+ { //for each triangle
+ U16 i0 = new_face.mIndices[i+0];
+ U16 i1 = new_face.mIndices[i+1];
+ U16 i2 = new_face.mIndices[i+2];
+
+ LLVector4a& p0 = new_face.mPositions[i0];
+ LLVector4a& p1 = new_face.mPositions[i1];
+ LLVector4a& p2 = new_face.mPositions[i2];
+
+ LLVector4a& n0 = new_face.mNormals[i0];
+ LLVector4a& n1 = new_face.mNormals[i1];
+ LLVector4a& n2 = new_face.mNormals[i2];
+
+ LLVector4a lhs, rhs;
+ lhs.setSub(p1, p0);
+ rhs.setSub(p2, p0);
+
+ n0.setCross3(lhs, rhs);
+ n0.normalize3fast();
+ n1 = n0;
+ n2 = n0;
+ }
+
+ //swap out normals in new_face with best match from point map (step 5)
+ for (U32 i = 0; i < new_face.mNumVertices; ++i)
+ {
+ //LLVolumeFace::VertexData v = new_face.mVertices[i];
+
+ LLVector4a ref_norm = new_face.mNormals[i];
+
+ LLVolumeFace::VertexMapData::PointMap::iterator iter = point_map.find(LLVector3(new_face.mPositions[i].getF32ptr()));
+
+ if (iter != point_map.end())
+ {
+ F32 best = -2.f;
+ for (U32 k = 0; k < iter->second.size(); ++k)
+ {
+ LLVector4a& n = iter->second[k].getNormal();
+
+ if (!iter->second[k].getPosition().equals3(new_face.mPositions[i]))
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ F32 cur = n.dot3(ref_norm).getF32();
+
+ if (cur > best)
+ {
+ best = cur;
+ new_face.mNormals[i] = n;
+ }
+ }
+ }
+ }
+
+ //remove redundant vertices from new face (step 6)
+ new_face.optimize();
+
+ mVolumeFaces[j] = new_face;
+ }
+}
+
+//static
+std::string LLModel::getElementLabel(daeElement *element)
+{ // try to get a decent label for this element
+ // if we have a name attribute, use it
+ std::string name = element->getAttribute("name");
+ if (name.length())
+ {
+ return name;
+ }
+
+ // if we have an ID attribute, use it
+ if (element->getID())
+ {
+ return std::string(element->getID());
+ }
+
+ // if we have a parent, use it
+ daeElement* parent = element->getParent();
+ if (parent)
+ {
+ // if parent has a name, use it
+ std::string name = parent->getAttribute("name");
+ if (name.length())
+ {
+ return name;
+ }
+
+ // if parent has an ID, use it
+ if (parent->getID())
+ {
+ return std::string(parent->getID());
+ }
+ }
+
+ // try to use our type
+ daeString element_name = element->getElementName();
+ if (element_name)
+ {
+ return std::string(element_name);
+ }
+
+ // if all else fails, use "object"
+ return std::string("object");
+}
+
+//static
+LLModel* LLModel::loadModelFromDae(std::string filename)
+{
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+ LLModel* ret = new LLModel(volume_params, 0.f);
+ ret->createVolumeFacesFromFile(filename);
+ return ret;
+}
+
+//static
+LLModel* LLModel::loadModelFromDomMesh(domMesh *mesh)
+{
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+ LLModel* ret = new LLModel(volume_params, 0.f);
+ ret->createVolumeFacesFromDomMesh(mesh);
+ ret->mLabel = getElementLabel(mesh);
+ return ret;
+}
+
+//static
+LLSD LLModel::writeModel(std::string filename, LLModel* physics, LLModel* high, LLModel* medium, LLModel* low, LLModel* impostor, LLModel::physics_shape& decomp, BOOL nowrite)
+{
+ std::ofstream os(filename.c_str(), std::ofstream::out | std::ofstream::binary);
+
+ LLSD header = writeModel(os, physics, high, medium, low, impostor, decomp, nowrite);
+
+ os.close();
+
+ return header;
+}
+
+//static
+LLSD LLModel::writeModel(std::ostream& ostr, LLModel* physics, LLModel* high, LLModel* medium, LLModel* low, LLModel* impostor, LLModel::physics_shape& decomp, BOOL nowrite)
+{
+ LLSD mdl;
+
+ LLModel* model[] =
+ {
+ impostor,
+ low,
+ medium,
+ high,
+ physics
+ };
+
+ bool skinning = high && !high->mSkinWeights.empty();
+
+ if (skinning)
+ { //write skinning block
+ if (high->mJointList.size() != high->mInvBindMatrix.size())
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ for (U32 i = 0; i < high->mJointList.size(); ++i)
+ {
+ mdl["skin"]["joint_names"][i] = high->mJointList[i];
+
+ for (U32 j = 0; j < 4; j++)
+ {
+ for (U32 k = 0; k < 4; k++)
+ {
+ mdl["skin"]["inverse_bind_matrix"][i][j*4+k] = high->mInvBindMatrix[i].mMatrix[j][k];
+ }
+ }
+ }
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ for (U32 j = 0; j < 4; j++)
+ {
+ mdl["skin"]["bind_shape_matrix"][i*4+j] = high->mBindShapeMatrix.mMatrix[i][j];
+ }
+ }
+ }
+
+ if (!decomp.empty())
+ {
+ //write decomposition block
+ // ["decomposition"]["HullList"] -- list of 8 bit integers, each entry represents a hull with specified number of points
+ // ["decomposition"]["PositionDomain"]["Min"/"Max"]
+ // ["decomposition"]["Position"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points
+
+ //get minimum and maximum
+ LLVector3 min = decomp[0][0];
+ LLVector3 max = min;
+
+ LLSD::Binary hulls(decomp.size());
+
+ U32 total = 0;
+
+ for (U32 i = 0; i < decomp.size(); ++i)
+ {
+ U32 size = decomp[i].size();
+ total += size;
+ hulls[i] = (U8) size;
+
+ for (U32 j = 0; j < decomp[i].size(); ++j)
+ {
+ update_min_max(min, max, decomp[i][j]);
+ }
+ }
+
+ mdl["decomposition"]["Min"] = min.getValue();
+ mdl["decomposition"]["Max"] = max.getValue();
+ mdl["decomposition"]["HullList"] = hulls;
+
+ LLSD::Binary p(total*3*2);
+
+ LLVector3 range = max-min;
+
+ U32 vert_idx = 0;
+ for (U32 i = 0; i < decomp.size(); ++i)
+ {
+ for (U32 j = 0; j < decomp[i].size(); ++j)
+ {
+ for (U32 k = 0; k < 3; k++)
+ {
+ //convert to 16-bit normalized across domain
+ U16 val = (U16) (((decomp[i][j].mV[k]-min.mV[k])/range.mV[k])*65535);
+
+ U8* buff = (U8*) &val;
+ //write to binary buffer
+ p[vert_idx++] = buff[0];
+ p[vert_idx++] = buff[1];
+
+ if (vert_idx > p.size())
+ {
+ llerrs << "WTF?" << llendl;
+ }
+ }
+ }
+ }
+
+ mdl["decomposition"]["Position"] = p;
+ }
+
+ for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx)
+ {
+ if (model[idx] && model[idx]->getNumVolumeFaces() > 0)
+ {
+ LLVector3 min_pos = LLVector3(model[idx]->getVolumeFace(0).mPositions[0].getF32ptr());
+ LLVector3 max_pos = min_pos;
+
+ //find position domain
+ for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i)
+ { //for each face
+ const LLVolumeFace& face = model[idx]->getVolumeFace(i);
+ for (U32 j = 0; j < face.mNumVertices; ++j)
+ {
+ update_min_max(min_pos, max_pos, face.mPositions[j].getF32ptr());
+ }
+ }
+
+ LLVector3 pos_range = max_pos - min_pos;
+
+ for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i)
+ { //for each face
+ const LLVolumeFace& face = model[idx]->getVolumeFace(i);
+ if (!face.mNumVertices)
+ { //don't export an empty face
+ continue;
+ }
+ LLSD::Binary verts(face.mNumVertices*3*2);
+ LLSD::Binary tc(face.mNumVertices*2*2);
+ LLSD::Binary normals(face.mNumVertices*3*2);
+ LLSD::Binary indices(face.mNumIndices*2);
+
+ U32 vert_idx = 0;
+ U32 norm_idx = 0;
+ U32 tc_idx = 0;
+
+ LLVector2* ftc = (LLVector2*) face.mTexCoords;
+ LLVector2 min_tc = ftc[0];
+ LLVector2 max_tc = min_tc;
+
+ //get texture coordinate domain
+ for (U32 j = 0; j < face.mNumVertices; ++j)
+ {
+ update_min_max(min_tc, max_tc, ftc[j]);
+ }
+
+ LLVector2 tc_range = max_tc - min_tc;
+
+ for (U32 j = 0; j < face.mNumVertices; ++j)
+ { //for each vert
+
+ F32* pos = face.mPositions[j].getF32ptr();
+ F32* norm = face.mNormals[j].getF32ptr();
+
+ //position + normal
+ for (U32 k = 0; k < 3; ++k)
+ { //for each component
+
+ //convert to 16-bit normalized across domain
+ U16 val = (U16) (((pos[k]-min_pos.mV[k])/pos_range.mV[k])*65535);
+
+ U8* buff = (U8*) &val;
+ //write to binary buffer
+ verts[vert_idx++] = buff[0];
+ verts[vert_idx++] = buff[1];
+
+ //convert to 16-bit normalized
+ val = (U16) ((norm[k]+1.f)*0.5f*65535);
+
+ //write to binary buffer
+ normals[norm_idx++] = buff[0];
+ normals[norm_idx++] = buff[1];
+ }
+
+ F32* src_tc = (F32*) face.mTexCoords[j].mV;
+
+ //texcoord
+ for (U32 k = 0; k < 2; ++k)
+ { //for each component
+ //convert to 16-bit normalized
+ U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535);
+
+ U8* buff = (U8*) &val;
+ //write to binary buffer
+ tc[tc_idx++] = buff[0];
+ tc[tc_idx++] = buff[1];
+ }
+
+ }
+
+ U32 idx_idx = 0;
+ for (U32 j = 0; j < face.mNumIndices; ++j)
+ {
+ U8* buff = (U8*) &(face.mIndices[j]);
+ indices[idx_idx++] = buff[0];
+ indices[idx_idx++] = buff[1];
+ }
+
+ //write out face data
+ mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue();
+ mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue();
+
+ mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue();
+ mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue();
+
+ mdl[model_names[idx]][i]["Position"] = verts;
+ mdl[model_names[idx]][i]["Normal"] = normals;
+ mdl[model_names[idx]][i]["TexCoord0"] = tc;
+ mdl[model_names[idx]][i]["TriangleList"] = indices;
+
+ if (skinning)
+ {
+ //write out skin weights
+
+ //each influence list entry is up to 4 24-bit values
+ // first 8 bits is bone index
+ // last 16 bits is bone influence weight
+ // a bone index of 0xFF signifies no more influences for this vertex
+
+ std::stringstream ostr;
+
+ for (U32 j = 0; j < face.mNumVertices; ++j)
+ {
+ LLVector3 pos(face.mPositions[j].getF32ptr());
+
+ weight_list& weights = high->getJointInfluences(pos);
+
+ if (weights.size() > 4)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ S32 count = 0;
+ for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter)
+ {
+ if (iter->mJointIdx < 255 && iter->mJointIdx >= 0)
+ {
+ U8 idx = (U8) iter->mJointIdx;
+ ostr.write((const char*) &idx, 1);
+
+ U16 influence = (U16) (iter->mWeight*65535);
+ ostr.write((const char*) &influence, 2);
+
+ ++count;
+ }
+ }
+ U8 end_list = 0xFF;
+ if (count < 4)
+ {
+ ostr.write((const char*) &end_list, 1);
+ }
+ }
+
+ //copy ostr to binary buffer
+ std::string data = ostr.str();
+ const U8* buff = (U8*) data.data();
+ U32 bytes = data.size();
+
+ LLSD::Binary w(bytes);
+ for (U32 j = 0; j < bytes; ++j)
+ {
+ w[j] = buff[j];
+ }
+
+ mdl[model_names[idx]][i]["Weights"] = w;
+ }
+ }
+ }
+ }
+
+ return writeModelToStream(ostr, mdl, nowrite);
+}
+
+LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite)
+{
+ U32 bytes = 0;
+
+ std::string::size_type cur_offset = 0;
+
+ LLSD header;
+
+ std::string skin;
+
+ if (mdl.has("skin"))
+ { //write out skin block
+ skin = zip_llsd(mdl["skin"]);
+
+ U32 size = skin.size();
+ if (size > 0)
+ {
+ header["skin"]["offset"] = (LLSD::Integer) cur_offset;
+ header["skin"]["size"] = (LLSD::Integer) size;
+ cur_offset += size;
+ bytes += size;
+ }
+ else
+ {
+ llerrs << "WTF?" << llendl;
+ }
+ }
+
+ std::string decomposition;
+
+ if (mdl.has("decomposition"))
+ { //write out convex decomposition
+ decomposition = zip_llsd(mdl["decomposition"]);
+
+ U32 size = decomposition.size();
+ if (size > 0)
+ {
+ header["decomposition"]["offset"] = (LLSD::Integer) cur_offset;
+ header["decomposition"]["size"] = (LLSD::Integer) size;
+ cur_offset += size;
+ bytes += size;
+ }
+ }
+
+ std::string out[MODEL_NAMES_LENGTH];
+
+ for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++)
+ {
+ if (mdl.has(model_names[i]))
+ {
+ out[i] = zip_llsd(mdl[model_names[i]]);
+
+ U32 size = out[i].size();
+
+ header[model_names[i]]["offset"] = (LLSD::Integer) cur_offset;
+ header[model_names[i]]["size"] = (LLSD::Integer) size;
+ cur_offset += size;
+ bytes += size;
+ }
+ else
+ {
+ header[model_names[i]]["offset"] = -1;
+ header[model_names[i]]["size"] = 0;
+ }
+ }
+
+ if (!nowrite)
+ {
+ LLSDSerialize::serialize(header, ostr, LLSDSerialize::LLSD_BINARY);
+
+ if (!skin.empty())
+ { //write skin block
+ ostr.write((const char*) skin.data(), header["skin"]["size"].asInteger());
+ }
+
+ if (!decomposition.empty())
+ { //write decomposition block
+ ostr.write((const char*) decomposition.data(), header["decomposition"]["size"].asInteger());
+ }
+
+ for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++)
+ {
+ if (!out[i].empty())
+ {
+ ostr.write((const char*) out[i].data(), header[model_names[i]]["size"].asInteger());
+ }
+ }
+ }
+
+ return header;
+}
+
+//static
+LLModel* LLModel::loadModelFromAsset(std::string filename, S32 lod)
+{
+ return NULL;
+}
+
+LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos)
+{
+ weight_map::iterator iter = mSkinWeights.find(pos);
+
+ if (iter != mSkinWeights.end())
+ {
+ if ((iter->first - pos).magVec() > 0.1f)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ return iter->second;
+ }
+ else
+ { //no exact match found, get closest point
+ iter = mSkinWeights.begin();
+ weight_map::iterator best = iter;
+
+ F32 min_dist = (iter->first - pos).magVecSquared();
+
+ while (++iter != mSkinWeights.end())
+ {
+ F32 dist = (iter->first - pos).magVecSquared();
+ if (dist < min_dist)
+ {
+ best = iter;
+ min_dist = dist;
+ }
+ }
+
+ return best->second;
+ }
+}
+
+#endif
+
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
new file mode 100644
index 0000000000..a91c80d5b7
--- /dev/null
+++ b/indra/llprimitive/llmodel.h
@@ -0,0 +1,179 @@
+/**
+ * @file llmodel.h
+ * @brief Model handling class definitions
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2007, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlife.com/developers/opensource/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlife.com/developers/opensource/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMODEL_H
+#define LL_LLMODEL_H
+
+#include "llvolume.h"
+#include "v4math.h"
+#include "m4math.h"
+
+#if LL_MESH_ENABLED
+
+class daeElement;
+class domMesh;
+
+#define MAX_MODEL_FACES 8
+
+class LLModel : public LLVolume
+{
+public:
+
+ enum
+ {
+ LOD_IMPOSTOR = 0,
+ LOD_LOW,
+ LOD_MEDIUM,
+ LOD_HIGH,
+ LOD_PHYSICS,
+ NUM_LODS
+ };
+
+ //physics shape is a vector of convex hulls
+ //each convex hull is a set of points
+ typedef std::vector<std::vector<LLVector3> > physics_shape;
+
+ LLModel(LLVolumeParams& params, F32 detail);
+ static LLSD writeModel(std::string filename, LLModel* physics, LLModel* high, LLModel* medium, LLModel* low, LLModel* imposotr, LLModel::physics_shape& physics_shape, BOOL nowrite = FALSE);
+ static LLSD writeModel(std::ostream& ostr, LLModel* physics, LLModel* high, LLModel* medium, LLModel* low, LLModel* imposotr, LLModel::physics_shape& physics_shape, BOOL nowrite = FALSE);
+ static LLSD writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite = FALSE);
+ static LLModel* loadModelFromAsset(std::string filename, S32 lod);
+ static LLModel* loadModelFromDae(std::string filename);
+ static LLModel* loadModelFromDomMesh(domMesh* mesh);
+ static std::string getElementLabel(daeElement* element);
+
+ void appendFaces(LLModel* model, LLMatrix4& transform, LLMatrix4& normal_transform);
+ void appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat);
+
+ void setNumVolumeFaces(S32 count);
+ void setVolumeFaceData(S32 f,
+ LLStrider<LLVector3> pos,
+ LLStrider<LLVector3> norm,
+ LLStrider<LLVector2> tc,
+ LLStrider<U16> ind,
+ U32 num_verts,
+ U32 num_indices);
+
+ void smoothNormals(F32 angle_cutoff);
+
+ void addFace(const LLVolumeFace& face);
+
+ void normalizeVolumeFaces();
+ void optimizeVolumeFaces();
+
+
+ U32 getResourceCost();
+ void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out);
+ std::vector<std::string> mMaterialList;
+
+ //data used for skin weights
+ class JointWeight
+ {
+ public:
+ S32 mJointIdx;
+ F32 mWeight;
+
+ JointWeight()
+ {
+ mJointIdx = 0;
+ mWeight = 0.f;
+ }
+
+ JointWeight(S32 idx, F32 weight)
+ : mJointIdx(idx), mWeight(weight)
+ {
+ }
+
+ bool operator<(const JointWeight& rhs) const
+ {
+ if (mWeight == rhs.mWeight)
+ {
+ return mJointIdx < rhs.mJointIdx;
+ }
+
+ return mWeight < rhs.mWeight;
+ }
+
+ };
+
+ struct CompareWeightGreater
+ {
+ bool operator()(const JointWeight& lhs, const JointWeight& rhs)
+ {
+ return rhs < lhs; // strongest = first
+ }
+ };
+
+ //copy of position array for this model -- mPosition[idx].mV[X,Y,Z]
+ std::vector<LLVector3> mPosition;
+
+ //map of positions to skin weights --- mSkinWeights[pos].mV[0..4] == <joint_index>.<weight>
+ //joint_index corresponds to mJointList
+ typedef std::vector<JointWeight> weight_list;
+ typedef std::map<LLVector3, weight_list > weight_map;
+ weight_map mSkinWeights;
+
+ //get list of weight influences closest to given position
+ weight_list& getJointInfluences(const LLVector3& pos);
+
+ //should always be true that mJointList[mJointMap["foo"]] == "foo"
+
+ //map of joint names to joint index
+ std::map<std::string, U32> mJointMap;
+
+ //list of joint names
+ std::vector<std::string> mJointList;
+
+ LLMatrix4 mBindShapeMatrix;
+ std::vector<LLMatrix4> mInvBindMatrix;
+
+ std::string mLabel;
+
+ LLVector3 mNormalizedScale;
+ LLVector3 mNormalizedTranslation;
+
+ //physics shape
+ physics_shape mPhysicsShape;
+
+ LLVector3 mPhysicsCenter;
+ std::vector<LLVector3> mHullCenter;
+ U32 mPhysicsPoints;
+
+protected:
+ void addVolumeFacesFromDomMesh(domMesh* mesh);
+ virtual BOOL createVolumeFacesFromFile(const std::string& file_name);
+ virtual BOOL createVolumeFacesFromDomMesh(domMesh *mesh);
+};
+
+
+#endif
+
+#endif //LL_LLMODEL_H
diff --git a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl
new file mode 100644
index 0000000000..eef6556fba
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl
@@ -0,0 +1,30 @@
+/**
+ * @file objectSkinV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+attribute vec4 object_weight;
+
+uniform mat4 matrixPalette[64];
+
+mat4 getObjectSkinnedTransform()
+{
+ int i;
+
+ vec4 w = fract(object_weight);
+ vec4 index = floor(object_weight);
+
+ float scale = 1.0/(w.x+w.y+w.z+w.w);
+ w *= scale;
+
+ mat4 mat = matrixPalette[int(index.x)]*w.x;
+ mat += matrixPalette[int(index.y)]*w.y;
+ mat += matrixPalette[int(index.z)]*w.z;
+ mat += matrixPalette[int(index.w)]*w.w;
+
+ return mat;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl
new file mode 100644
index 0000000000..fde0e97713
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl
@@ -0,0 +1,75 @@
+/**
+ * @file alphaSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+mat4 getObjectSkinnedTransform();
+void calcAtmospherics(vec3 inPositionEye);
+
+float calcDirectionalLight(vec3 n, vec3 l);
+float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight);
+
+vec3 atmosAmbient(vec3 light);
+vec3 atmosAffectDirectionalLight(float lightIntensity);
+vec3 scaleDownLight(vec3 light);
+vec3 scaleUpLight(vec3 light);
+
+varying vec3 vary_position;
+varying vec3 vary_ambient;
+varying vec3 vary_directional;
+varying vec3 vary_normal;
+varying vec3 vary_light;
+
+void main()
+{
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+
+ vec4 pos;
+ vec3 norm;
+
+ mat4 trans = getObjectSkinnedTransform();
+ trans = gl_ModelViewMatrix * trans;
+
+ pos = trans * gl_Vertex;
+
+ norm = gl_Vertex.xyz + gl_Normal.xyz;
+ norm = normalize(( trans*vec4(norm, 1.0) ).xyz-pos.xyz);
+
+ gl_Position = gl_ProjectionMatrix * pos;
+
+ vary_position = pos.xyz;
+ vary_normal = norm;
+
+ calcAtmospherics(pos.xyz);
+
+ vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a);
+
+ // Collect normal lights (need to be divided by two, as we later multiply by 2)
+ col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a);
+ col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a);
+ col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a);
+ col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a);
+ col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a);
+ col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a);
+ col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
+ col.rgb = scaleDownLight(col.rgb);
+
+ // Add windlight lights
+ col.rgb += atmosAmbient(vec3(0.));
+
+ vary_light = gl_LightSource[0].position.xyz;
+
+ vary_ambient = col.rgb*gl_Color.rgb;
+ vary_directional = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a)));
+
+ col.rgb = min(col.rgb*gl_Color.rgb, 1.0);
+
+ gl_FrontColor = col;
+
+ gl_FogFragCoord = pos.z;
+}
+
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl
new file mode 100644
index 0000000000..085ffddeec
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl
@@ -0,0 +1,16 @@
+/**
+ * @file avatarShadowF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+uniform sampler2D diffuseMap;
+
+
+void main()
+{
+ //gl_FragColor = vec4(1,1,1,gl_Color.a * texture2D(diffuseMap, gl_TexCoord[0].xy).a);
+ gl_FragColor = vec4(1,1,1,1);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl
new file mode 100644
index 0000000000..1626e21cd8
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl
@@ -0,0 +1,25 @@
+/**
+ * @file diffuseSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+mat4 getObjectSkinnedTransform();
+
+void main()
+{
+ //transform vertex
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ mat4 mat = getObjectSkinnedTransform();
+
+ mat = gl_ModelViewMatrix * mat;
+ vec3 pos = (mat*gl_Vertex).xyz;
+
+ gl_FrontColor = gl_Color;
+
+ gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
new file mode 100644
index 0000000000..d884f2e4a5
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
@@ -0,0 +1,37 @@
+/**
+ * @file bumpV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+varying vec3 vary_mat0;
+varying vec3 vary_mat1;
+varying vec3 vary_mat2;
+
+mat4 getObjectSkinnedTransform();
+
+void main()
+{
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ mat4 mat = getObjectSkinnedTransform();
+
+ mat = gl_ModelViewMatrix * mat;
+
+ vec3 pos = (mat*gl_Vertex).xyz;
+
+
+ vec3 n = normalize((mat * vec4(gl_Normal.xyz+gl_Vertex.xyz, 1.0)).xyz-pos.xyz);
+ vec3 b = normalize((mat * vec4(gl_MultiTexCoord2.xyz+gl_Vertex.xyz, 1.0)).xyz-pos.xyz);
+ vec3 t = cross(b, n);
+
+ vary_mat0 = vec3(t.x, b.x, n.x);
+ vary_mat1 = vec3(t.y, b.y, n.y);
+ vary_mat2 = vec3(t.z, b.z, n.z);
+
+ gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+ gl_FrontColor = gl_Color;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl
new file mode 100644
index 0000000000..9a45c03237
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl
@@ -0,0 +1,33 @@
+/**
+ * @file diffuseSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+varying vec3 vary_normal;
+
+mat4 getObjectSkinnedTransform();
+
+void main()
+{
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ mat4 mat = getObjectSkinnedTransform();
+
+ mat = gl_ModelViewMatrix * mat;
+ vec3 pos = (mat*gl_Vertex).xyz;
+
+ vec4 norm = gl_Vertex;
+ norm.xyz += gl_Normal.xyz;
+ norm.xyz = (mat*norm).xyz;
+ norm.xyz = normalize(norm.xyz-pos.xyz);
+
+ vary_normal = norm.xyz;
+
+ gl_FrontColor = gl_Color;
+
+ gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+}
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl
new file mode 100644
index 0000000000..4cde013eef
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl
@@ -0,0 +1,15 @@
+/**
+ * @file lightFullbrightShinyWaterF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+uniform sampler2D diffuseMap;
+uniform samplerCube environmentMap;
+
+void fullbright_shiny_lighting_water()
+{
+ gl_FragColor = texture2D(diffuseMap, gl_TexCoord[0].xy);
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl
new file mode 100644
index 0000000000..f0baeeeee5
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl
@@ -0,0 +1,39 @@
+/**
+ * @file shinySimpleSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+void calcAtmospherics(vec3 inPositionEye);
+mat4 getObjectSkinnedTransform();
+
+attribute vec4 object_weight;
+
+void main()
+{
+ mat4 mat = getObjectSkinnedTransform();
+
+ mat = gl_ModelViewMatrix * mat;
+ vec3 pos = (mat*gl_Vertex).xyz;
+
+ vec4 norm = gl_Vertex;
+ norm.xyz += gl_Normal.xyz;
+ norm.xyz = (mat*norm).xyz;
+ norm.xyz = normalize(norm.xyz-pos.xyz);
+
+ vec3 ref = reflect(pos.xyz, -norm.xyz);
+
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+ gl_TexCoord[1] = gl_TextureMatrix[1]*vec4(ref,1.0);
+
+ calcAtmospherics(pos.xyz);
+
+ gl_FrontColor = gl_Color;
+
+ gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+
+ gl_FogFragCoord = pos.z;
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl
new file mode 100644
index 0000000000..5e6e4c16b7
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl
@@ -0,0 +1,13 @@
+/**
+ * @file fullbrightShinyWaterF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+void fullbright_shiny_lighting_water();
+
+void main()
+{
+ fullbright_shiny_lighting_water();
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl
new file mode 100644
index 0000000000..02ff3cc2a9
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl
@@ -0,0 +1,37 @@
+/**
+ * @file fullbrightSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+void calcAtmospherics(vec3 inPositionEye);
+mat4 getObjectSkinnedTransform();
+
+attribute vec4 object_weight;
+
+void main()
+{
+ //transform vertex
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ mat4 mat = getObjectSkinnedTransform();
+
+ mat = gl_ModelViewMatrix * mat;
+ vec3 pos = (mat*gl_Vertex).xyz;
+
+ vec4 norm = gl_Vertex;
+ norm.xyz += gl_Normal.xyz;
+ norm.xyz = (mat*norm).xyz;
+ norm.xyz = normalize(norm.xyz-pos.xyz);
+
+ calcAtmospherics(pos.xyz);
+
+ gl_FrontColor = gl_Color;
+
+ gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+
+ gl_FogFragCoord = pos.z;
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl
new file mode 100644
index 0000000000..4146646058
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl
@@ -0,0 +1,39 @@
+/**
+ * @file shinySimpleSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void calcAtmospherics(vec3 inPositionEye);
+mat4 getObjectSkinnedTransform();
+
+attribute vec4 object_weight;
+
+void main()
+{
+ mat4 mat = getObjectSkinnedTransform();
+
+ mat = gl_ModelViewMatrix * mat;
+ vec3 pos = (mat*gl_Vertex).xyz;
+
+ vec4 norm = gl_Vertex;
+ norm.xyz += gl_Normal.xyz;
+ norm.xyz = (mat*norm).xyz;
+ norm.xyz = normalize(norm.xyz-pos.xyz);
+
+ vec3 ref = reflect(pos.xyz, -norm.xyz);
+
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+ gl_TexCoord[1] = gl_TextureMatrix[1]*vec4(ref,1.0);
+
+ calcAtmospherics(pos.xyz);
+
+ vec4 color = calcLighting(pos.xyz, norm.xyz, gl_Color, vec4(0.));
+ gl_FrontColor = color;
+
+ gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl
new file mode 100644
index 0000000000..59944b8861
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl
@@ -0,0 +1,39 @@
+/**
+ * @file simpleV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void calcAtmospherics(vec3 inPositionEye);
+mat4 getObjectSkinnedTransform();
+
+attribute vec4 object_weight;
+
+void main()
+{
+ //transform vertex
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ mat4 mat = getObjectSkinnedTransform();
+
+ mat = gl_ModelViewMatrix * mat;
+ vec3 pos = (mat*gl_Vertex).xyz;
+
+ vec4 norm = gl_Vertex;
+ norm.xyz += gl_Normal.xyz;
+ norm.xyz = (mat*norm).xyz;
+ norm.xyz = normalize(norm.xyz-pos.xyz);
+
+ calcAtmospherics(pos.xyz);
+
+ vec4 color = calcLighting(pos.xyz, norm.xyz, gl_Color, vec4(0.));
+ gl_FrontColor = color;
+
+ gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0);
+
+ gl_FogFragCoord = pos.z;
+}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl
new file mode 100644
index 0000000000..dc4663677b
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl
@@ -0,0 +1,84 @@
+/**
+ * @file alphaSkinnedV.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void calcAtmospherics(vec3 inPositionEye);
+
+float calcDirectionalLight(vec3 n, vec3 l);
+float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight);
+mat4 getObjectSkinnedTransform();
+vec3 atmosAmbient(vec3 light);
+vec3 atmosAffectDirectionalLight(float lightIntensity);
+vec3 scaleDownLight(vec3 light);
+vec3 scaleUpLight(vec3 light);
+
+varying vec3 vary_ambient;
+varying vec3 vary_directional;
+varying vec3 vary_fragcoord;
+varying vec3 vary_position;
+varying vec3 vary_light;
+
+uniform float near_clip;
+uniform float shadow_offset;
+uniform float shadow_bias;
+
+void main()
+{
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ mat4 mat = getObjectSkinnedTransform();
+
+ mat = gl_ModelViewMatrix * mat;
+
+ vec3 pos = (mat*gl_Vertex).xyz;
+
+ gl_Position = gl_ProjectionMatrix * vec4(pos, 1.0);
+
+ vec4 n = gl_Vertex;
+ n.xyz += gl_Normal.xyz;
+ n.xyz = (mat*n).xyz;
+ n.xyz = normalize(n.xyz-pos.xyz);
+
+ vec3 norm = n.xyz;
+
+ float dp_directional_light = max(0.0, dot(norm, gl_LightSource[0].position.xyz));
+ vary_position = pos.xyz + gl_LightSource[0].position.xyz * (1.0-dp_directional_light)*shadow_offset;
+
+ calcAtmospherics(pos.xyz);
+
+ //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.));
+ vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a);
+
+ // Collect normal lights (need to be divided by two, as we later multiply by 2)
+ col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a);
+ col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a);
+ col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a);
+ col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a);
+ col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a);
+ col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a);
+ col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
+ col.rgb = scaleDownLight(col.rgb);
+
+ // Add windlight lights
+ col.rgb += atmosAmbient(vec3(0.));
+
+ vary_light = gl_LightSource[0].position.xyz;
+
+ vary_ambient = col.rgb*gl_Color.rgb;
+ vary_directional.rgb = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a)));
+
+ col.rgb = min(col.rgb*gl_Color.rgb, 1.0);
+
+ gl_FrontColor = col;
+
+ gl_FogFragCoord = pos.z;
+
+ pos.xyz = (gl_ModelViewProjectionMatrix * gl_Vertex).xyz;
+ vary_fragcoord.xyz = pos.xyz + vec3(0,0,near_clip);
+
+}
+
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl
new file mode 100644
index 0000000000..eca9a567f6
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl
@@ -0,0 +1,29 @@
+/**
+ * @file lightFullbrightShinyWaterF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+uniform sampler2D diffuseMap;
+uniform samplerCube environmentMap;
+
+vec3 fullbrightShinyAtmosTransport(vec3 light);
+vec3 fullbrightScaleSoftClip(vec3 light);
+vec4 applyWaterFog(vec4 color);
+
+void fullbright_shiny_lighting_water()
+{
+ vec4 color = texture2D(diffuseMap, gl_TexCoord[0].xy);
+ color.rgb *= gl_Color.rgb;
+
+ vec3 envColor = textureCube(environmentMap, gl_TexCoord[1].xyz).rgb;
+ color.rgb = mix(color.rgb, envColor.rgb, gl_Color.a);
+
+ color.rgb = fullbrightShinyAtmosTransport(color.rgb);
+ color.rgb = fullbrightScaleSoftClip(color.rgb);
+ color.a = max(color.a, gl_Color.a);
+
+ gl_FragColor = applyWaterFog(color);
+}
+
diff --git a/indra/newview/llfloaterimportcollada.cpp b/indra/newview/llfloaterimportcollada.cpp
new file mode 100644
index 0000000000..476c02cd46
--- /dev/null
+++ b/indra/newview/llfloaterimportcollada.cpp
@@ -0,0 +1,1116 @@
+/**
+ * @file llfloaterimportcollada.cpp
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterimportcollada.h"
+
+#include "dae.h"
+//#include "dom.h"
+#include "dom/domAsset.h"
+#include "dom/domBind_material.h"
+#include "dom/domConstants.h"
+#include "dom/domEffect.h"
+#include "dom/domGeometry.h"
+#include "dom/domInstance_geometry.h"
+#include "dom/domInstance_material.h"
+#include "dom/domInstance_node.h"
+#include "dom/domInstance_effect.h"
+#include "dom/domMaterial.h"
+#include "dom/domMatrix.h"
+#include "dom/domNode.h"
+#include "dom/domProfile_COMMON.h"
+#include "dom/domRotate.h"
+#include "dom/domScale.h"
+#include "dom/domTranslate.h"
+#include "dom/domVisual_scene.h"
+
+#include "llagent.h"
+#include "llassetuploadresponders.h"
+#include "lleconomy.h"
+#include "llfloaterperms.h"
+#include "llfloaterreg.h"
+#include "llsdutil.h"
+#include "llsdutil_math.h"
+#include "llselectmgr.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "llviewermenufile.h"
+#include "llviewerregion.h"
+#include "llvolumemessage.h"
+#include "llmodel.h"
+#include "llmeshreduction.h"
+#include "material_codes.h"
+
+//
+// floater
+//
+
+#if LL_MESH_ENABLED
+
+LLFloaterImportCollada::LLFloaterImportCollada(const LLSD& key)
+ : LLFloater(key)
+{
+}
+
+
+BOOL LLFloaterImportCollada::postBuild()
+{
+ if (!LLFloater::postBuild())
+ {
+ return FALSE;
+ }
+
+ childSetCommitCallback("ok", LLImportCollada::onCommitOK, this);
+ childSetCommitCallback("cancel", LLImportCollada::onCommitCancel, this);
+
+ setStatusIdle();
+ setAssetCount(0,0);
+ enableOK(TRUE);
+
+ return TRUE;
+}
+
+
+void LLFloaterImportCollada::setAssetCount(S32 mesh_count, S32 texture_count)
+{
+ childSetTextArg("mesh count", "[COUNT]", llformat("%d", mesh_count));
+ childSetTextArg("texture count", "[COUNT]", llformat("%d", texture_count));
+}
+
+void LLFloaterImportCollada::setStatusAssetUploading(std::string asset_name)
+{
+ LLUIString uploading = getString("status_uploading");
+ uploading.setArg("[NAME]", asset_name);
+ childSetTextArg("status", "[STATUS]", uploading.getString());
+}
+
+void LLFloaterImportCollada::setStatusCreatingPrim(std::string prim_name)
+{
+ LLUIString creating = getString("status_creating");
+ creating.setArg("[NAME]", prim_name);
+ childSetTextArg("status", "[STATUS]", creating.getString());
+}
+
+void LLFloaterImportCollada::setStatusIdle()
+{
+ childSetTextArg("status", "[STATUS]", getString("status_idle"));
+}
+
+void LLFloaterImportCollada::enableOK(BOOL enable)
+{
+ childSetEnabled("ok", enable);
+}
+
+
+//
+// misc helpers
+//
+
+
+// why oh why do forbid matrix multiplication in our llmath library?
+LLMatrix4 matrix_multiply(LLMatrix4 a, LLMatrix4 b)
+{
+ a *= b;
+
+ return a;
+}
+
+// why oh why does colladadom not provide such things?
+daeElement* getFirstChild(daeElement* parent)
+{
+ daeTArray< daeSmartRef<daeElement> > children = parent->getChildren();
+
+ if (children.getCount() > 0)
+ {
+ return children[0];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// why oh why does colladadom not provide such things?
+daeElement* getNextSibling(daeElement* child)
+{
+ daeElement* parent = child->getParent();
+
+ if (parent == NULL)
+ {
+ // must be root, root has no siblings
+ return NULL;
+ }
+
+ daeElement* sibling = NULL;
+
+ daeTArray< daeSmartRef<daeElement> > children = parent->getChildren();
+ for (S32 i = 0; i < children.getCount(); i++)
+ {
+ if (child == children[i])
+ {
+ if ((i+1) < children.getCount())
+ {
+ sibling = children[i+1];
+ }
+ }
+ }
+
+ return sibling;
+}
+
+// try to get a decent label for this element
+std::string getElementLabel(daeElement *element)
+{
+ // if we have a name attribute, use it
+ std::string name = element->getAttribute("name");
+ if (name.length())
+ {
+ return name;
+ }
+
+ // if we have an ID attribute, use it
+ if (element->getID())
+ {
+ return std::string(element->getID());
+ }
+
+ // if we have a parent, use it
+ daeElement* parent = element->getParent();
+ if (parent)
+ {
+ // if parent has a name, use it
+ std::string name = parent->getAttribute("name");
+ if (name.length())
+ {
+ return name;
+ }
+
+ // if parent has an ID, use it
+ if (parent->getID())
+ {
+ return std::string(parent->getID());
+ }
+ }
+
+ // try to use our type
+ daeString element_name = element->getElementName();
+ if (element_name)
+ {
+ return std::string(element_name);
+ }
+
+ // if all else fails, use "object"
+ return std::string("object");
+}
+
+LLColor4 getDaeColor(daeElement* element)
+{
+ LLColor4 value;
+ domCommon_color_or_texture_type_complexType::domColor* color =
+ daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(element->getDescendant("color"));
+ if (color)
+ {
+ domFx_color_common domfx_color = color->getValue();
+ value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
+ }
+
+ return value;
+}
+
+LLTextureEntry profileToTextureEntry(domProfile_COMMON* material)
+{
+ LLTextureEntry te;
+
+ te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture
+ daeElement* diffuse = material->getDescendant("diffuse");
+ if (diffuse)
+ {
+ te.setColor(LLColor3(0.1f, 0.9f, 1.0f));
+
+ domCommon_color_or_texture_type_complexType::domTexture* texture =
+ daeSafeCast<domCommon_color_or_texture_type_complexType::domTexture>(diffuse->getDescendant("texture"));
+ if (texture)
+ {
+ domCommon_newparam_type_Array newparams = material->getNewparam_array();
+ for (S32 i = 0; i < newparams.getCount(); i++)
+ {
+ domFx_surface_common* surface = newparams[i]->getSurface();
+ if (surface)
+ {
+ domFx_surface_init_common* init = surface->getFx_surface_init_common();
+ if (init)
+ {
+ domFx_surface_init_from_common_Array init_from = init->getInit_from_array();
+
+ if (init_from.getCount() > 0)
+ {
+ daeElement* image = init_from[0]->getValue().getElement();
+ if (image)
+ {
+ LLUUID texture_asset = LLImportColladaAssetCache::getInstance()->getAssetForDaeElement(image);
+
+ if (texture_asset.notNull())
+ {
+ te.setID(texture_asset);
+ te.setColor(LLColor3(1,1,1));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ domCommon_color_or_texture_type_complexType::domColor* color =
+ daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(diffuse->getDescendant("color"));
+ if (color)
+ {
+ domFx_color_common domfx_color = color->getValue();
+ LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
+ te.setColor(value);
+ }
+ }
+
+ daeElement* emission = material->getDescendant("emission");
+ if (emission)
+ {
+ LLColor4 emission_color = getDaeColor(emission);
+ if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25)
+ {
+ te.setFullbright(TRUE);
+ }
+ }
+
+ return te;
+}
+
+std::vector<LLTextureEntry> getMaterials(LLModel* model, domInstance_geometry* instance_geo)
+{
+ std::vector<LLTextureEntry> texture_entries;
+ for (int i = 0; i < model->mMaterialList.size(); i++)
+ {
+ LLTextureEntry texture_entry;
+
+ domInstance_material* instance_mat = NULL;
+
+ domBind_material::domTechnique_common* technique =
+ daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID())));
+
+ if (technique)
+ {
+ daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>();
+ for (int j = 0; j < inst_materials.getCount(); j++)
+ {
+ std::string symbol(inst_materials[j]->getSymbol());
+
+ if (symbol == model->mMaterialList[i]) // found the binding
+ {
+ instance_mat = inst_materials[j];
+ }
+ }
+ }
+
+ if (instance_mat)
+ {
+ domMaterial* material = daeSafeCast<domMaterial>(instance_mat->getTarget().getElement());
+ if (material)
+ {
+ domInstance_effect* instance_effect =
+ daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID())));
+ if (instance_effect)
+ {
+ domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement());
+ if (effect)
+ {
+ domProfile_COMMON* profile =
+ daeSafeCast<domProfile_COMMON>(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID())));
+ if (profile)
+ {
+ texture_entry = profileToTextureEntry(profile);
+ }
+ }
+ }
+ }
+ }
+
+ texture_entries.push_back(texture_entry);
+ }
+
+ return texture_entries;
+}
+
+LLTextureEntry instanceGeoToTextureEntry(domInstance_geometry* instance_geo)
+{
+ LLTextureEntry te;
+ domInstance_material* instance_mat =
+ daeSafeCast<domInstance_material>(instance_geo->getDescendant(daeElement::matchType(domInstance_material::ID())));
+ if (instance_mat)
+ {
+ }
+
+ return te;
+}
+
+
+
+// responder for asset uploads
+// does all the normal stuff followed by a notification to continue importing
+// WARNING - currently unused - TODO
+class LLColladaNewAgentInventoryResponder : public LLNewAgentInventoryResponder
+{
+ LLColladaNewAgentInventoryResponder(const LLSD& post_data,
+ const LLUUID& vfile_id,
+ LLAssetType::EType asset_type)
+ : LLNewAgentInventoryResponder(post_data, vfile_id, asset_type)
+ {
+ }
+ LLColladaNewAgentInventoryResponder(const LLSD& post_data,
+ const std::string& file_name,
+ LLAssetType::EType asset_type)
+ : LLNewAgentInventoryResponder(post_data, file_name, asset_type)
+ {
+ }
+
+ virtual void uploadComplete(const LLSD& content)
+ {
+ LLNewAgentInventoryResponder::uploadComplete(content);
+ }
+
+};
+
+BOOL LLImportColladaAssetCache::uploadImageAsset(domImage* image)
+{
+ // we only support init_from now - embedded data will come later
+ domImage::domInit_from* init = image->getInit_from();
+ if (!init)
+ {
+ return FALSE;
+ }
+
+ std::string filename = cdom::uriToNativePath(init->getValue().str());
+
+ std::string name = getElementLabel(image);
+
+ LLUUID transaction_id = upload_new_resource(filename, name, std::string(),
+ 0, LLFolderType::FT_TEXTURE, LLInventoryType::IT_TEXTURE,
+ LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(),
+ LLFloaterPerms::getEveryonePerms(),
+ name, NULL,
+ LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(), NULL);
+
+ if (transaction_id.isNull())
+ {
+ llwarns << "cannot upload " << filename << llendl;
+
+ return FALSE;
+ }
+
+ mTransactionMap[transaction_id] = image;
+
+ LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->setStatusAssetUploading(name);
+
+ return TRUE;
+}
+
+
+
+//
+// asset cache -
+// uploads assets and provides a map from collada element to asset
+//
+
+
+
+BOOL LLImportColladaAssetCache::uploadMeshAsset(domMesh* mesh)
+{
+ LLPointer<LLModel> model = LLModel::loadModelFromDomMesh(mesh);
+
+ if (model->getNumVolumeFaces() == 0)
+ {
+ return FALSE;
+ }
+
+ // generate LODs
+
+
+ std::vector<LLPointer<LLModel> > lods;
+ lods.push_back(model);
+ S32 triangle_count = model->getNumTriangles();
+
+ for (S32 i = 0; i < 4; i++)
+ {
+ LLPointer<LLModel> last_model = lods.back();
+
+ S32 triangle_target = (S32)(triangle_count / pow(3.f, i + 1));
+ if (triangle_target > 16)
+ {
+ LLMeshReduction reduction;
+ LLPointer<LLModel> new_model = reduction.reduce(model, triangle_target, LLMeshReduction::TRIANGLE_BUDGET);
+ lods.push_back(new_model);
+ }
+ else
+ {
+ lods.push_back(last_model);
+ }
+ }
+
+ // write model to temp file
+
+ std::string filename = gDirUtilp->getTempFilename();
+ LLModel::writeModel(filename,
+ lods[4],
+ lods[0],
+ lods[1],
+ lods[2],
+ lods[3],
+ lods[4]->mPhysicsShape);
+
+
+ // copy file to VFS
+
+ LLTransactionID tid;
+ tid.generate();
+ LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID()); // create asset uuid
+
+ S32 file_size;
+ LLAPRFile infile ;
+ infile.open(filename, LL_APR_RB, NULL, &file_size);
+
+ if (infile.getFileHandle())
+ {
+ LLVFile file(gVFS, uuid, LLAssetType::AT_MESH, 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);
+ }
+ }
+
+
+ std::string name = getElementLabel(mesh);
+
+ upload_new_resource(tid, LLAssetType::AT_MESH, name, std::string(), 0,LLFolderType::FT_MESH, LLInventoryType::IT_MESH,
+ LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(),
+ name, NULL,
+ LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(), NULL);
+
+ LLFile::remove(filename);
+
+ mTransactionMap[uuid] = mesh;
+
+ LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->setStatusAssetUploading(name);
+
+ return TRUE;
+}
+
+
+// called by the mesh asset upload responder to indicate the mesh asset has been uploaded
+void LLImportColladaAssetCache::assetUploaded(LLUUID transaction_uuid, LLUUID asset_uuid, BOOL success)
+{
+ std::map<LLUUID, daeElement*>::iterator i = mTransactionMap.find(transaction_uuid);
+
+ if (i != mTransactionMap.end())
+ {
+ daeElement* element = i->second;
+
+
+ if (success)
+ {
+ mAssetMap[element] = asset_uuid;
+ }
+ else // failure
+ {
+ // if failed, put back on end of queue
+ mUploadsPending.push_back(element);
+ }
+
+ mUploads--;
+ uploadNextAsset();
+ }
+}
+
+const S32 MAX_CONCURRENT_UPLOADS = 5;
+
+void LLImportColladaAssetCache::uploadNextAsset()
+{
+ while ((mUploadsPending.size() > 0) && (mUploads < MAX_CONCURRENT_UPLOADS))
+ {
+ BOOL upload_started = FALSE;
+
+ daeElement* element = mUploadsPending.back();
+ mUploadsPending.pop_back();
+
+ domImage* image = daeSafeCast<domImage>(element);
+ if (image)
+ {
+ upload_started = uploadImageAsset(image);
+ }
+
+ domMesh* mesh = daeSafeCast<domMesh>(element);
+ if (mesh)
+ {
+ upload_started = uploadMeshAsset(mesh);
+ }
+
+ if (upload_started)
+ {
+ mUploads++;
+ }
+
+ }
+
+ if ((mUploadsPending.size() == 0) && (mUploads == 0))
+ {
+ // we're done! notify the importer
+ LLImportCollada::getInstance()->assetsUploaded();
+ }
+
+ updateCount();
+}
+
+
+void LLImportColladaAssetCache::clear()
+{
+ mDAE = NULL;
+ mTransactionMap.clear();
+ mAssetMap.clear();
+ mUploadsPending.clear();
+ mUploads = 0;
+}
+
+void LLImportColladaAssetCache::endImport()
+{
+ clear();
+}
+
+void LLImportColladaAssetCache::updateCount()
+{
+ S32 mesh_count = 0;
+ S32 image_count = 0;
+
+ for (S32 i = 0; i < mUploadsPending.size(); i++)
+ {
+ daeElement* element = mUploadsPending[i];
+
+ if (daeSafeCast<domMesh>(element))
+ {
+ mesh_count++;
+ }
+
+ if (daeSafeCast<domImage>(element))
+ {
+ image_count++;
+ }
+ }
+
+ LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->setAssetCount(mesh_count, image_count);
+}
+
+void LLImportColladaAssetCache::prepareForUpload(DAE* dae)
+{
+ clear();
+ mDAE = dae;
+
+ daeDatabase* db = mDAE->getDatabase();
+
+ S32 mesh_count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
+ for (S32 i = 0; i < mesh_count; i++)
+ {
+ domMesh* mesh = NULL;
+
+ db->getElement((daeElement**) &mesh, i, NULL, COLLADA_TYPE_MESH);
+
+ mUploadsPending.push_back(mesh);
+ }
+
+
+ S32 image_count = db->getElementCount(NULL, COLLADA_TYPE_IMAGE);
+ for (S32 i = 0; i < image_count; i++)
+ {
+ domImage* image = NULL;
+ db->getElement((daeElement**) &image, i, NULL, COLLADA_TYPE_IMAGE);
+
+ mUploadsPending.push_back(image);
+ }
+
+ updateCount();
+}
+
+
+void LLImportColladaAssetCache::uploadAssets()
+{
+ uploadNextAsset();
+}
+
+
+LLUUID LLImportColladaAssetCache::getAssetForDaeElement(daeElement* element)
+{
+ LLUUID id;
+
+ std::map<daeElement*, LLUUID>::iterator i = mAssetMap.find(element);
+ if (i != mAssetMap.end())
+ {
+ id = i->second;
+ }
+
+ return id;
+}
+
+
+//
+// importer
+//
+
+
+
+LLImportCollada::LLImportCollada()
+{
+ mIsImporting = FALSE;
+}
+
+
+
+
+void LLImportCollada::appendObjectAsset(domInstance_geometry* instance_geo)
+{
+ domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement());
+ if (!geo)
+ {
+ llwarns << "cannot find geometry" << llendl;
+ return;
+ }
+
+ domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID())));
+ if (!mesh)
+ {
+ llwarns << "could not find mesh" << llendl;
+ return;
+ }
+
+ LLUUID mesh_asset = LLImportColladaAssetCache::getInstance()->getAssetForDaeElement(mesh);
+ if (mesh_asset.isNull())
+ {
+ llwarns << "no mesh asset, skipping" << llendl;
+ return;
+ }
+
+ // load the model
+ LLModel* model = LLModel::loadModelFromDomMesh(mesh);
+
+
+
+
+ // get our local transformation
+ LLMatrix4 transformation = mStack.front().transformation;
+
+ // adjust the transformation to compensate for mesh normalization
+ LLVector3 mesh_scale_vector;
+ LLVector3 mesh_translation_vector;
+ model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+ LLMatrix4 mesh_translation;
+ mesh_translation.setTranslation(mesh_translation_vector);
+ transformation = matrix_multiply(mesh_translation, transformation);
+ LLMatrix4 mesh_scale;
+ mesh_scale.initScale(mesh_scale_vector);
+ transformation = matrix_multiply(mesh_scale, transformation);
+
+ // check for reflection
+ BOOL reflected = (transformation.determinant() < 0);
+
+ // compute position
+ LLVector3 position = LLVector3(0, 0, 0) * transformation;
+
+ // compute scale
+ LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
+ LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
+ LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
+ F32 x_length = x_transformed.normalize();
+ F32 y_length = y_transformed.normalize();
+ F32 z_length = z_transformed.normalize();
+ LLVector3 scale = LLVector3(x_length, y_length, z_length);
+
+ // adjust for "reflected" geometry
+ LLVector3 x_transformed_reflected = x_transformed;
+ if (reflected)
+ {
+ x_transformed_reflected *= -1.0;
+ }
+
+ // compute rotation
+ LLMatrix3 rotation_matrix;
+ rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed);
+ LLQuaternion quat_rotation = rotation_matrix.quaternion();
+ quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here.
+ LLVector3 euler_rotation;
+ quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]);
+
+
+ //
+ // build parameter block to construct this prim
+ //
+
+ LLSD object_params;
+
+ // create prim
+
+ // set volume params
+ LLVolumeParams volume_params;
+ volume_params.setType( LL_PCODE_PROFILE_SQUARE, 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 );
+ U8 sculpt_type = LL_SCULPT_TYPE_MESH;
+ if (reflected)
+ {
+ sculpt_type |= LL_SCULPT_FLAG_MIRROR;
+ }
+ volume_params.setSculptID(mesh_asset, sculpt_type);
+ object_params["shape"] = volume_params.asLLSD();
+
+ object_params["material"] = LL_MCODE_WOOD;
+
+ object_params["group-id"] = gAgent.getGroupID();
+ object_params["pos"] = ll_sd_from_vector3(position);
+ object_params["rotation"] = ll_sd_from_quaternion(quat_rotation);
+ object_params["scale"] = ll_sd_from_vector3(scale);
+ object_params["name"] = mStack.front().name;
+
+ // load material from dae file
+ std::vector<LLTextureEntry> texture_entries = getMaterials(model, instance_geo);
+ object_params["facelist"] = LLSD::emptyArray();
+ for (int i = 0; i < texture_entries.size(); i++)
+ {
+ object_params["facelist"][i] = texture_entries[i].asLLSD();
+ }
+
+ // set extra parameters
+ LLSculptParams sculpt_params;
+ sculpt_params.setSculptTexture(mesh_asset);
+ sculpt_params.setSculptType(sculpt_type);
+ U8 buffer[MAX_OBJECT_PARAMS_SIZE+1];
+ LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE);
+ sculpt_params.pack(dp);
+ std::vector<U8> v(dp.getCurrentSize());
+ memcpy(&v[0], buffer, dp.getCurrentSize());
+ LLSD extra_parameter;
+ extra_parameter["extra_parameter"] = sculpt_params.mType;
+ extra_parameter["param_data"] = v;
+ object_params["extra_parameters"].append(extra_parameter);
+
+ mObjectList.append(object_params);
+
+ delete model;
+
+ LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->setStatusCreatingPrim(mStack.front().name);
+
+ return;
+}
+
+void LLImportCollada::uploadObjectAsset()
+{
+ LLSD request;
+ request["objects"] = mObjectList;
+
+ std::string url = gAgent.getRegion()->getCapability("UploadObjectAsset");
+ LLHTTPClient::post(url, request, new LLHTTPClient::Responder());
+}
+
+
+
+void LLImportCollada::importFile(std::string filename)
+{
+ if (mIsImporting)
+ {
+ llwarns << "Importer already running, import command for " << filename << " ignored" << llendl;
+ return;
+ }
+
+ LLFloaterReg::showInstance("import_collada");
+ LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->enableOK(TRUE);
+
+
+ mIsImporting = TRUE;
+ mDAE = new DAE;
+ mImportOrigin = gAgent.getPositionAgent() + LLVector3(0, 0, 2);
+ mSceneTransformation = LLMatrix4(); // identity
+ mFilename = filename;
+ mCreates = 0;
+ mObjectList = LLSD::emptyArray();
+
+ if (mDAE->open(mFilename) == NULL)
+ {
+ llwarns << "cannot open file " << mFilename << llendl;
+ endImport();
+ return;
+ }
+
+ LLImportColladaAssetCache::getInstance()->prepareForUpload(mDAE);
+
+ return;
+}
+
+
+void LLImportCollada::assetsUploaded()
+{
+ if (!mIsImporting)
+ {
+ // weird, we got a callback while not importing.
+ return;
+ }
+
+ daeDocument* doc = mDAE->getDoc(mFilename);
+ if (!doc)
+ {
+ llwarns << "can't find internal doc" << llendl;
+ endImport();
+ }
+
+ daeElement* root = doc->getDomRoot();
+ if (!root)
+ {
+ llwarns << "document has no root" << llendl;
+ endImport();
+ }
+
+ domAsset::domUnit* unit = daeSafeCast<domAsset::domUnit>(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID())));
+ if (unit)
+ {
+ mSceneTransformation *= unit->getMeter();
+ }
+
+ domUpAxisType up = UPAXISTYPE_Y_UP; // default is Y_UP
+ domAsset::domUp_axis* up_axis =
+ daeSafeCast<domAsset::domUp_axis>(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID())));
+ if (up_axis)
+ {
+ up = up_axis->getValue();
+ }
+
+ if (up == UPAXISTYPE_X_UP)
+ {
+ LLMatrix4 rotation;
+ rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f);
+
+ mSceneTransformation = matrix_multiply(rotation, mSceneTransformation);
+ }
+ else if (up == UPAXISTYPE_Y_UP)
+ {
+ LLMatrix4 rotation;
+ rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f);
+
+ mSceneTransformation = matrix_multiply(rotation, mSceneTransformation);
+ }
+ // else Z_UP, which is our behavior
+
+
+
+ daeElement* scene = root->getDescendant("visual_scene");
+ if (!scene)
+ {
+ llwarns << "document has no visual_scene" << llendl;
+ endImport();
+ }
+
+ processElement(scene);
+ processNextElement();
+}
+
+void LLImportCollada::pushStack(daeElement* next_element, std::string name, LLMatrix4 transformation)
+{
+ struct StackState new_state;
+
+ new_state.next_element = next_element;
+ new_state.name = name;
+ new_state.transformation = transformation;
+
+ mStack.push_front(new_state);
+}
+
+
+
+void LLImportCollada::popStack()
+{
+ mStack.pop_front();
+}
+
+
+
+BOOL LLImportCollada::processElement(daeElement* element)
+{
+ if (mStack.size() > 0)
+ {
+ mStack.front().next_element = getNextSibling(element);
+ }
+
+ domTranslate* translate = daeSafeCast<domTranslate>(element);
+ if (translate)
+ {
+ domFloat3 dom_value = translate->getValue();
+
+ LLMatrix4 translation;
+ translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
+
+ mStack.front().transformation = matrix_multiply(translation, mStack.front().transformation);
+ }
+
+ domRotate* rotate = daeSafeCast<domRotate>(element);
+ if (rotate)
+ {
+ domFloat4 dom_value = rotate->getValue();
+
+ LLMatrix4 rotation;
+ rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0));
+
+ mStack.front().transformation = matrix_multiply(rotation, mStack.front().transformation);
+ }
+
+ domScale* scale = daeSafeCast<domScale>(element);
+ if (scale)
+ {
+ domFloat3 dom_value = scale->getValue();
+
+ LLMatrix4 scaling;
+ scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
+
+ mStack.front().transformation = matrix_multiply(scaling, mStack.front().transformation);
+ }
+
+ domMatrix* matrix = daeSafeCast<domMatrix>(element);
+ if (matrix)
+ {
+ domFloat4x4 dom_value = matrix->getValue();
+
+ LLMatrix4 matrix_transform;
+
+ for (int i = 0; i < 4; i++)
+ for(int j = 0; j < 4; j++)
+ {
+ matrix_transform.mMatrix[i][j] = dom_value[i + j*4];
+ }
+
+ mStack.front().transformation = matrix_multiply(matrix_transform, mStack.front().transformation);
+ }
+
+ domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element);
+ if (instance_geo)
+ {
+ appendObjectAsset(instance_geo);
+ }
+
+ domNode* node = daeSafeCast<domNode>(element);
+ if (node)
+ {
+ pushStack(getFirstChild(element), getElementLabel(element), mStack.front().transformation);
+ }
+
+ domInstance_node* instance_node = daeSafeCast<domInstance_node>(element);
+ if (instance_node)
+ {
+ daeElement* instance = instance_node->getUrl().getElement();
+ if (instance)
+ {
+ pushStack(getFirstChild(instance), getElementLabel(instance), mStack.front().transformation);
+ }
+ }
+
+ domVisual_scene* scene = daeSafeCast<domVisual_scene>(element);
+ if (scene)
+ {
+ pushStack(getFirstChild(element), std::string("scene"), mSceneTransformation);
+ }
+
+ return FALSE;
+}
+
+
+void LLImportCollada::processNextElement()
+{
+ while(1)
+ {
+ if (mStack.size() == 0)
+ {
+ uploadObjectAsset();
+ endImport();
+ return;
+ }
+
+ daeElement *element = mStack.front().next_element;
+
+ if (element == NULL)
+ {
+ popStack();
+ }
+ else
+ {
+ processElement(element);
+ }
+ }
+}
+
+
+void LLImportCollada::endImport()
+{
+ LLFloaterReg::hideInstance("import_collada");
+
+ LLImportColladaAssetCache::getInstance()->endImport();
+
+ if (mDAE)
+ {
+ delete mDAE;
+ mDAE = NULL;
+ }
+
+ mIsImporting = FALSE;
+}
+
+
+/* static */
+void LLImportCollada::onCommitOK(LLUICtrl*, void*)
+{
+ LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->enableOK(FALSE);
+
+ LLImportColladaAssetCache::getInstance()->uploadAssets();
+}
+
+
+/* static */
+void LLImportCollada::onCommitCancel(LLUICtrl*, void*)
+{
+ getInstance()->endImport();
+}
+
+#endif
diff --git a/indra/newview/llfloaterimportcollada.h b/indra/newview/llfloaterimportcollada.h
new file mode 100644
index 0000000000..818b19e403
--- /dev/null
+++ b/indra/newview/llfloaterimportcollada.h
@@ -0,0 +1,143 @@
+/**
+ * @file llfloaterimportcollada.h
+ * @brief The about box from Help -> About
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERIMPORTCOLLADA_H
+#define LL_LLFLOATERIMPORTCOLLADA_H
+
+#include "llfloater.h"
+#include "llvolume.h" //for LL_MESH_ENABLED
+
+#if LL_MESH_ENABLED
+
+class LLFloaterImportCollada : public LLFloater
+{
+public:
+ LLFloaterImportCollada(const LLSD& key);
+ /* virtual */ BOOL postBuild();
+
+ void setAssetCount(S32 mesh_count, S32 texture_count);
+ void setStatusAssetUploading(std::string asset_name);
+ void setStatusCreatingPrim(std::string prim_name);
+ void setStatusIdle();
+ void enableOK(BOOL enable);
+};
+
+class LLViewerObject;
+class DAE;
+class daeElement;
+class domMesh;
+class domImage;
+class domInstance_geometry;
+class LLModel;
+class LLImportCollada;
+
+class LLImportColladaAssetCache : public LLSingleton<LLImportColladaAssetCache>
+{
+public:
+ // called first to initialize
+ void prepareForUpload(DAE* dae);
+
+ // upload the assets in this collada file
+ void uploadAssets();
+
+ // get the uploaded assets which corresponds to this element
+ LLUUID getAssetForDaeElement(daeElement* element);
+
+ // stop the upload
+ void endImport();
+
+ // reset
+ void clear();
+
+ // callback for notification when an asset has been uploaded
+ void assetUploaded(LLUUID transaction_uuid, LLUUID asset_uuid, BOOL success);
+
+private:
+ void uploadNextAsset();
+ BOOL uploadMeshAsset(domMesh* mesh);
+ BOOL uploadImageAsset(domImage* image);
+ void updateCount();
+
+ std::vector<daeElement*> mUploadsPending;
+ std::map<LLUUID, daeElement*> mTransactionMap;
+ std::map<daeElement*, LLUUID> mAssetMap;
+
+ DAE* mDAE;
+ S32 mUploads;
+};
+
+
+class LLImportCollada
+: public LLSingleton<LLImportCollada>
+{
+public:
+ LLImportCollada();
+ void importFile(std::string filename);
+
+ // callback when all assets have been uploaded
+ void assetsUploaded();
+
+ // callback when buttons pressed
+ static void onCommitOK(LLUICtrl*, void*);
+ static void onCommitCancel(LLUICtrl*, void*);
+
+
+private:
+ void endImport();
+ void processNextElement();
+ BOOL processElement(daeElement* element);
+ void pushStack(daeElement* next_element, std::string name, LLMatrix4 transformation);
+ void popStack();
+ void appendObjectAsset(domInstance_geometry* instance_geo);
+ void uploadObjectAsset();
+
+ struct StackState
+ {
+ daeElement* next_element;
+ std::string name;
+ LLMatrix4 transformation;
+ };
+
+ std::list<struct StackState> mStack;
+ S32 mCreates;
+ LLVector3 mImportOrigin;
+ std::string mFilename;
+ BOOL mIsImporting;
+ DAE *mDAE;
+ LLSD mObjectList;
+
+ LLMatrix4 mSceneTransformation;
+};
+
+#endif
+
+#endif // LL_LLFLOATERIMPORTCOLLADA_H
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
new file mode 100644
index 0000000000..5dd983d818
--- /dev/null
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -0,0 +1,3382 @@
+/**
+ * @file llfloatermodelpreview.cpp
+ * @brief LLFloaterModelPreview class implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-2007, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlife.com/developers/opensource/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlife.com/developers/opensource/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "dae.h"
+//#include "dom.h"
+#include "dom/domAsset.h"
+#include "dom/domBind_material.h"
+#include "dom/domCOLLADA.h"
+#include "dom/domConstants.h"
+#include "dom/domController.h"
+#include "dom/domEffect.h"
+#include "dom/domGeometry.h"
+#include "dom/domInstance_geometry.h"
+#include "dom/domInstance_material.h"
+#include "dom/domInstance_node.h"
+#include "dom/domInstance_effect.h"
+#include "dom/domMaterial.h"
+#include "dom/domMatrix.h"
+#include "dom/domNode.h"
+#include "dom/domProfile_COMMON.h"
+#include "dom/domRotate.h"
+#include "dom/domScale.h"
+#include "dom/domTranslate.h"
+#include "dom/domVisual_scene.h"
+
+#include "llfloatermodelpreview.h"
+
+#include "llfilepicker.h"
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagejpeg.h"
+#include "llimagepng.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "lldatapacker.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llrender.h"
+#include "llface.h"
+#include "lleconomy.h"
+#include "llfocusmgr.h"
+#include "llfloaterperms.h"
+#include "llmatrix4a.h"
+#include "llmeshrepository.h"
+#include "llsdutil_math.h"
+#include "lltextbox.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llvector4a.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvoavatarself.h"
+#include "pipeline.h"
+#include "lluictrlfactory.h"
+#include "llviewermenufile.h"
+#include "llviewerregion.h"
+#include "llstring.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+
+#include "glod/glod.h"
+
+
+#if LL_MESH_ENABLED
+
+//static
+S32 LLFloaterModelPreview::sUploadAmount = 10;
+LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL;
+
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREF_BUTTON_HEIGHT = 16 + 7 + 16;
+const S32 PREVIEW_TEXTURE_HEIGHT = 300;
+
+void drawBoxOutline(const LLVector3& pos, const LLVector3& size);
+
+std::string limit_name[] =
+{
+ "lowest limit",
+ "low limit",
+ "medium limit",
+ "high limit",
+ "physics limit",
+
+ "I went off the end of the limit_name array. Me so smart."
+};
+
+std::string info_name[] =
+{
+ "lowest info",
+ "low info",
+ "medium info",
+ "high info",
+ "physics info",
+
+ "I went off the end of the info_name array. Me so smart."
+};
+
+bool validate_face(const LLVolumeFace& face)
+{
+ for (U32 i = 0; i < face.mNumIndices; ++i)
+ {
+ if (face.mIndices[i] >= face.mNumVertices)
+ {
+ llwarns << "Face has invalid index." << llendl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool validate_model(const LLModel* mdl)
+{
+ if (mdl->getNumVolumeFaces() == 0)
+ {
+ llwarns << "Model has no faces!" << llendl;
+ return false;
+ }
+
+ for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+ {
+ if (mdl->getVolumeFace(i).mNumVertices == 0)
+ {
+ llwarns << "Face has no vertices." << llendl;
+ return false;
+ }
+
+ if (mdl->getVolumeFace(i).mNumIndices == 0)
+ {
+ llwarns << "Face has no indices." << llendl;
+ return false;
+ }
+
+ if (!validate_face(mdl->getVolumeFace(i)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+BOOL stop_gloderror()
+{
+ GLuint error = glodGetError();
+
+ if (error != GLOD_NO_ERROR)
+ {
+ llwarns << "GLOD error detected, cannot generate LOD: " << std::hex << error << llendl;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+LLPhysicsDecompFloater::LLPhysicsDecompFloater(LLSD& key)
+: LLFloater(key)
+{
+
+}
+
+LLPhysicsDecompFloater::~LLPhysicsDecompFloater()
+{
+ if (LLFloaterModelPreview::sInstance && LLFloaterModelPreview::sInstance->mDecompFloater)
+ {
+ LLFloaterModelPreview::sInstance->mDecompFloater = NULL;
+ }
+}
+
+class LLMeshFilePicker : public LLFilePickerThread
+{
+public:
+ LLFloaterModelPreview* mFMP;
+ S32 mLOD;
+
+ LLMeshFilePicker(LLFloaterModelPreview* fmp, S32 lod)
+ : LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA)
+ {
+ mFMP = fmp;
+ mLOD = lod;
+ }
+
+ virtual void notify(const std::string& filename)
+ {
+ mFMP->mModelPreview->loadModel(mFile, mLOD);
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// LLFloaterModelPreview()
+//-----------------------------------------------------------------------------
+LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) :
+ LLFloater(key)
+{
+ sInstance = this;
+ mLastMouseX = 0;
+ mLastMouseY = 0;
+ mGLName = 0;
+ mLoading = FALSE;
+ mDecompFloater = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// postBuild()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::postBuild()
+{
+ if (!LLFloater::postBuild())
+ {
+ return FALSE;
+ }
+
+ childSetCommitCallback("high detail combo", onHighLODCommit, this);
+ childSetCommitCallback("medium detail combo", onMediumLODCommit, this);
+ childSetCommitCallback("low detail combo", onLowLODCommit, this);
+ childSetCommitCallback("lowest detail combo", onLowestLODCommit, this);
+ childSetCommitCallback("physics detail combo", onPhysicsLODCommit, this);
+
+
+ childSetCommitCallback("high limit", onHighLimitCommit, this);
+ childSetCommitCallback("medium limit", onMediumLimitCommit, this);
+ childSetCommitCallback("low limit", onLowLimitCommit, this);
+ childSetCommitCallback("lowest limit", onLowestLimitCommit, this);
+ childSetCommitCallback("physics limit", onPhysicsLimitCommit, this);
+
+ childSetCommitCallback("smooth normals", onSmoothNormalsCommit, this);
+
+ childSetCommitCallback("show edges", onShowEdgesCommit, this);
+ childSetCommitCallback("auto fill", onAutoFillCommit, this);
+
+ childSetCommitCallback("explode", onExplodeCommit, this);
+
+ childSetTextArg("status", "[STATUS]", getString("status_idle"));
+
+ for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+ {
+ if (lod == LLModel::LOD_PHYSICS)
+ {
+ childSetTextArg(info_name[lod], "[HULLS]", std::string("0"));
+ childSetTextArg(info_name[lod], "[POINTS]", std::string("0"));
+ }
+ else
+ {
+ childSetTextArg(info_name[lod], "[TRIANGLES]", std::string("0"));
+ childSetTextArg(info_name[lod], "[VERTICES]", std::string("0"));
+ childSetTextArg(info_name[lod], "[SUBMESHES]", std::string("0"));
+ std::string msg = getString("required");
+ childSetTextArg(info_name[lod], "[MESSAGE]", msg);
+ }
+
+ childSetVisible(limit_name[lod], FALSE);
+ }
+
+ //childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount));
+ childSetAction("ok_btn", onUpload, this);
+
+ childSetAction("consolidate", onConsolidate, this);
+ childSetAction("scrub materials", onScrubMaterials, this);
+
+ childSetAction("decompose_btn", onDecompose, this);
+
+ childSetCommitCallback("preview_lod_combo", onPreviewLODCommit, this);
+
+ const U32 width = 512;
+ const U32 height = 512;
+
+ mPreviewRect.set(getRect().getWidth()-PREVIEW_HPAD-width,
+ PREVIEW_HPAD+height,
+ getRect().getWidth()-PREVIEW_HPAD,
+ PREVIEW_HPAD);
+
+ mModelPreview = new LLModelPreview(512, 512, this);
+ mModelPreview->setPreviewTarget(16.f);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterModelPreview()
+//-----------------------------------------------------------------------------
+LLFloaterModelPreview::~LLFloaterModelPreview()
+{
+ sInstance = NULL;
+
+ delete mModelPreview;
+
+ if (mGLName)
+ {
+ LLImageGL::deleteTextures(1, &mGLName );
+ }
+
+ if (mDecompFloater)
+ {
+ mDecompFloater->closeFloater();
+ mDecompFloater = NULL;
+ }
+}
+
+void LLFloaterModelPreview::loadModel(S32 lod)
+{
+ mLoading = TRUE;
+
+ (new LLMeshFilePicker(this, lod))->getFile();
+}
+
+void LLFloaterModelPreview::setLODMode(S32 lod, S32 mode)
+{
+ if (mode == 0)
+ {
+ loadModel(lod);
+ }
+ else if (mode != mModelPreview->mLODMode[lod])
+ {
+ mModelPreview->mLODMode[lod] = mode;
+ mModelPreview->genLODs(lod);
+ }
+
+ mModelPreview->setPreviewLOD(lod);
+
+
+ LLSpinCtrl* lim = getChild<LLSpinCtrl>(limit_name[lod], TRUE);
+
+ if (mode == 1) //triangle count
+ {
+ U32 tri_count = 0;
+ for (LLModelLoader::model_list::iterator iter = mModelPreview->mBaseModel.begin();
+ iter != mModelPreview->mBaseModel.end(); ++iter)
+ {
+ tri_count += (*iter)->getNumTriangles();
+ }
+
+ lim->setMaxValue(tri_count);
+ lim->setVisible(TRUE);
+ }
+ else
+ {
+ lim->setVisible(FALSE);
+ }
+}
+
+void LLFloaterModelPreview::setLimit(S32 lod, S32 limit)
+{
+ if (limit != mModelPreview->mLimit[lod])
+ {
+ mModelPreview->mLimit[lod] = limit;
+ mModelPreview->genLODs(lod);
+ mModelPreview->setPreviewLOD(lod);
+ }
+}
+
+void LLFloaterModelPreview::onPreviewLODCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
+
+ if (!fp->mModelPreview)
+ {
+ return;
+ }
+
+ S32 which_mode = 0;
+
+ LLCtrlSelectionInterface* iface = fp->childGetSelectionInterface("preview_lod_combo");
+ if (iface)
+ {
+ which_mode = iface->getFirstSelectedIndex();
+ }
+ fp->mModelPreview->setPreviewLOD(which_mode);
+}
+
+//static
+void LLFloaterModelPreview::setLODMode(S32 lod, void* userdata)
+{
+ LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
+
+ if (!fp->mModelPreview)
+ {
+ return;
+ }
+
+ S32 which_mode = 0;
+
+ std::string combo_name[] =
+ {
+ "lowest detail combo",
+ "low detail combo",
+ "medium detail combo",
+ "high detail combo",
+ "physics detail combo",
+
+ "I went off the end of the combo_name array. Me so smart."
+ };
+
+ LLCtrlSelectionInterface* iface = fp->childGetSelectionInterface(combo_name[lod]);
+ if (iface)
+ {
+ which_mode = iface->getFirstSelectedIndex();
+ }
+
+ fp->setLODMode(lod, which_mode);
+}
+
+//static
+void LLFloaterModelPreview::onHighLODCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLODMode(3, userdata);
+}
+
+//static
+void LLFloaterModelPreview::onMediumLODCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLODMode(2, userdata);
+}
+
+//static
+void LLFloaterModelPreview::onLowLODCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLODMode(1, userdata);
+}
+
+//static
+void LLFloaterModelPreview::onLowestLODCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLODMode(0, userdata);
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsLODCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLODMode(4, userdata);
+}
+
+//static
+void LLFloaterModelPreview::setLimit(S32 lod, void* userdata)
+{
+ LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
+
+ if (!fp->mModelPreview)
+ {
+ return;
+ }
+
+ S32 limit = fp->childGetValue(limit_name[lod]).asInteger();
+
+
+ fp->setLimit(lod, limit);
+}
+
+//static
+void LLFloaterModelPreview::onHighLimitCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLimit(3, userdata);
+}
+
+//static
+void LLFloaterModelPreview::onMediumLimitCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLimit(2, userdata);
+}
+
+//static
+void LLFloaterModelPreview::onLowLimitCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLimit(1, userdata);
+}
+
+//static
+void LLFloaterModelPreview::onLowestLimitCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLimit(0, userdata);
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsLimitCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview::setLimit(4, userdata);
+}
+
+//static
+void LLFloaterModelPreview::onSmoothNormalsCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata;
+
+ fp->mModelPreview->smoothNormals();
+}
+
+//static
+void LLFloaterModelPreview::onShowEdgesCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata;
+
+ fp->mModelPreview->refresh();
+}
+
+//static
+void LLFloaterModelPreview::onExplodeCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata;
+
+ fp->mModelPreview->refresh();
+}
+
+//static
+void LLFloaterModelPreview::onAutoFillCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata;
+
+ fp->mModelPreview->genLODs();
+}
+
+
+//-----------------------------------------------------------------------------
+// draw()
+//-----------------------------------------------------------------------------
+void LLFloaterModelPreview::draw()
+{
+ LLFloater::draw();
+ LLRect r = getRect();
+
+ if (!mLoading)
+ {
+ childSetTextArg("status", "[STATUS]", getString("status_idle"));
+ }
+
+ childSetTextArg("description_label", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost));
+ childSetTextArg("description_label", "[TEXTURES]", llformat("%d", mModelPreview->mTextureSet.size()));
+
+ if (mDecompFloater)
+ {
+ mDecompFloater->childSetText("status", gMeshRepo.mDecompThread->mStatus);
+ }
+
+ U32 resource_cost = mModelPreview->mResourceCost*10;
+
+ if (childGetValue("upload_textures").asBoolean())
+ {
+ resource_cost += mModelPreview->mTextureSet.size()*10;
+ }
+
+ childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", resource_cost));
+
+ if (mModelPreview)
+ {
+ gGL.color3f(1.f, 1.f, 1.f);
+
+ gGL.getTexUnit(0)->bind(mModelPreview);
+
+ gGL.begin( LLRender::QUADS );
+ {
+ gGL.texCoord2f(0.f, 1.f);
+ gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop);
+ gGL.texCoord2f(0.f, 0.f);
+ gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom);
+ gGL.texCoord2f(1.f, 0.f);
+ gGL.vertex2i(mPreviewRect.mRight, mPreviewRect.mBottom);
+ gGL.texCoord2f(1.f, 1.f);
+ gGL.vertex2i(mPreviewRect.mRight, mPreviewRect.mTop);
+ }
+ gGL.end();
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseDown()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mPreviewRect.pointInRect(x, y))
+ {
+ bringToFront( x, y );
+ gFocusMgr.setMouseCapture(this);
+ gViewerWindow->hideCursor();
+ mLastMouseX = x;
+ mLastMouseY = y;
+ return TRUE;
+ }
+
+ return LLFloater::handleMouseDown(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseUp()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ gFocusMgr.setMouseCapture(FALSE);
+ gViewerWindow->showCursor();
+ return LLFloater::handleMouseUp(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleHover()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::handleHover (S32 x, S32 y, MASK mask)
+{
+ MASK local_mask = mask & ~MASK_ALT;
+
+ if (mModelPreview && hasMouseCapture())
+ {
+ if (local_mask == MASK_PAN)
+ {
+ // pan here
+ mModelPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f);
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f;
+
+ mModelPreview->rotate(yaw_radians, pitch_radians);
+ }
+ else
+ {
+
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f;
+
+ mModelPreview->rotate(yaw_radians, 0.f);
+ mModelPreview->zoom(zoom_amt);
+ }
+
+
+ mModelPreview->refresh();
+
+ LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY);
+ }
+
+ if (!mPreviewRect.pointInRect(x, y) || !mModelPreview)
+ {
+ return LLFloater::handleHover(x, y, mask);
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA);
+ }
+ else if (local_mask == MASK_PAN)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLPAN);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// handleScrollWheel()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterModelPreview::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (mPreviewRect.pointInRect(x, y) && mModelPreview)
+ {
+ mModelPreview->zoom((F32)clicks * -0.2f);
+ mModelPreview->refresh();
+ }
+
+ return TRUE;
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsParamCommit(LLUICtrl* ctrl, void* data)
+{
+ LLCDParam* param = (LLCDParam*) data;
+
+ LLCDResult ret = LLCD_OK;
+
+ if (LLConvexDecomposition::getInstance() == NULL)
+ {
+ llinfos << "convex decomposition tool is a stub on this platform. cannot get decomp." << llendl;
+ return;
+ }
+
+ if (param->mType == LLCDParam::LLCD_FLOAT)
+ {
+ ret = LLConvexDecomposition::getInstance()->setParam(param->mName, (F32) ctrl->getValue().asReal());
+ }
+ else if (param->mType == LLCDParam::LLCD_INTEGER ||
+ param->mType == LLCDParam::LLCD_ENUM)
+ {
+ ret = LLConvexDecomposition::getInstance()->setParam(param->mName, ctrl->getValue().asInteger());
+ }
+ else if (param->mType == LLCDParam::LLCD_BOOLEAN)
+ {
+ ret = LLConvexDecomposition::getInstance()->setParam(param->mName, ctrl->getValue().asBoolean());
+ }
+
+ if (ret)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data)
+{
+ LLCDStageData* stage = (LLCDStageData*) data;
+
+ LLModel* mdl = NULL;
+
+ if (sInstance)
+ {
+ if (sInstance->mModelPreview)
+ {
+ if (sInstance->mDecompFloater)
+ {
+ S32 idx = sInstance->mDecompFloater->childGetValue("model").asInteger();
+ if (idx >= 0 && idx < sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS].size())
+ {
+ mdl = sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS][idx];
+ }
+ }
+ }
+ }
+
+ if (mdl)
+ {
+ gMeshRepo.mDecompThread->execute(stage->mName, mdl);
+ }
+}
+
+//static
+void LLFloaterModelPreview::onPhysicsStageCancel(LLUICtrl* ctrl, void*data)
+{
+ gMeshRepo.mDecompThread->cancel();
+}
+
+void LLFloaterModelPreview::showDecompFloater()
+{
+ if (!mDecompFloater)
+ {
+ LLSD key;
+ mDecompFloater = new LLPhysicsDecompFloater(key);
+
+ S32 left = 20;
+ S32 right = 270;
+
+ S32 cur_y = 30;
+
+ {
+ //add status text
+ LLTextBox::Params p;
+ p.name("status");
+ p.rect(LLRect(left, cur_y, right-80, cur_y-20));
+ mDecompFloater->addChild(LLUICtrlFactory::create<LLTextBox>(p));
+ }
+
+
+ { //add cancel button
+ LLButton::Params p;
+ p.name("Cancel");
+ p.label("Cancel");
+ p.rect(LLRect(right-80, cur_y, right, cur_y-20));
+ LLButton* button = LLUICtrlFactory::create<LLButton>(p);
+ button->setCommitCallback(onPhysicsStageCancel, NULL);
+ mDecompFloater->addChild(button);
+ }
+
+ cur_y += 30;
+
+
+ const LLCDStageData* stage;
+ S32 stage_count = 0;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ stage_count = LLConvexDecomposition::getInstance()->getStages(&stage);
+ }
+
+ const LLCDParam* param;
+ S32 param_count = 0;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ param_count = LLConvexDecomposition::getInstance()->getParameters(&param);
+ }
+
+ for (S32 j = stage_count-1; j >= 0; --j)
+ {
+ LLButton::Params p;
+ p.name(stage[j].mName);
+ p.label(stage[j].mName);
+ p.rect(LLRect(left, cur_y, right, cur_y-20));
+ LLButton* button = LLUICtrlFactory::create<LLButton>(p);
+ button->setCommitCallback(onPhysicsStageExecute, (void*) &stage[j]);
+ mDecompFloater->addChild(button);
+ gMeshRepo.mDecompThread->mStageID[stage[j].mName] = j;
+ cur_y += 30;
+ // protected against stub by stage_count being 0 for stub above
+ LLConvexDecomposition::getInstance()->registerCallback(j, LLPhysicsDecomp::llcdCallback);
+
+ for (S32 i = 0; i < param_count; ++i)
+ {
+ if (param[i].mStage != j)
+ {
+ continue;
+ }
+
+ if (param[i].mType == LLCDParam::LLCD_FLOAT)
+ {
+ LLSliderCtrl::Params p;
+ p.name(param[i].mName);
+ p.label(param[i].mName);
+ p.rect(LLRect(left, cur_y, right, cur_y-20));
+ p.min_value(param[i].mDetails.mRange.mLow.mFloat);
+ p.max_value(param[i].mDetails.mRange.mHigh.mFloat);
+ p.increment(param[i].mDetails.mRange.mDelta.mFloat);
+ p.decimal_digits(3);
+ p.initial_value(param[i].mDefault.mFloat);
+ LLSliderCtrl* slider = LLUICtrlFactory::create<LLSliderCtrl>(p);
+ slider->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
+ mDecompFloater->addChild(slider);
+ cur_y += 30;
+ }
+ else if (param[i].mType == LLCDParam::LLCD_INTEGER)
+ {
+ LLSliderCtrl::Params p;
+ p.name(param[i].mName);
+ p.label(param[i].mName);
+ p.rect(LLRect(left, cur_y, right, cur_y-20));
+ p.min_value(param[i].mDetails.mRange.mLow.mIntOrEnumValue);
+ p.max_value(param[i].mDetails.mRange.mHigh.mIntOrEnumValue);
+ p.increment(param[i].mDetails.mRange.mDelta.mIntOrEnumValue);
+ p.initial_value(param[i].mDefault.mIntOrEnumValue);
+ LLSliderCtrl* slider = LLUICtrlFactory::create<LLSliderCtrl>(p);
+ slider->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
+ mDecompFloater->addChild(slider);
+ cur_y += 30;
+ }
+ else if (param[i].mType == LLCDParam::LLCD_BOOLEAN)
+ {
+ LLCheckBoxCtrl::Params p;
+ p.rect(LLRect(left, cur_y, right, cur_y-20));
+ p.name(param[i].mName);
+ p.label(param[i].mName);
+ p.initial_value(param[i].mDefault.mBool);
+ LLCheckBoxCtrl* check_box = LLUICtrlFactory::create<LLCheckBoxCtrl>(p);
+ check_box->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
+ mDecompFloater->addChild(check_box);
+ cur_y += 30;
+ }
+ else if (param[i].mType == LLCDParam::LLCD_ENUM)
+ {
+ LLComboBox::Params p;
+ p.rect(LLRect(left, cur_y, right/3, cur_y-20));
+ p.name(param[i].mName);
+ p.label(param[i].mName);
+ LLComboBox* combo_box = LLUICtrlFactory::create<LLComboBox>(p);
+ for (S32 k = 0; k < param[i].mDetails.mEnumValues.mNumEnums; ++k)
+ {
+ combo_box->add(param[i].mDetails.mEnumValues.mEnumsArray[k].mName,
+ LLSD::Integer(param[i].mDetails.mEnumValues.mEnumsArray[k].mValue));
+ }
+ combo_box->setValue(param[i].mDefault.mIntOrEnumValue);
+ combo_box->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
+ mDecompFloater->addChild(combo_box);
+ cur_y += 30;
+ }
+ }
+ }
+
+ //mesh render checkbox
+ {
+ LLCheckBoxCtrl::Params p;
+ p.label("Mesh: ");
+ p.name("render_mesh");
+ p.rect(LLRect(left, cur_y, right/4, cur_y-20));
+ LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(p);
+ check->setValue(true);
+ mDecompFloater->addChild(check);
+ }
+
+ //hull render checkbox
+ {
+ LLCheckBoxCtrl::Params p;
+ p.label("Hull: ");
+ p.name("render_hull");
+ p.rect(LLRect(right/4, cur_y, right/2, cur_y-20));
+ LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(p);
+ check->setValue(true);
+ mDecompFloater->addChild(check);
+ }
+
+ { //submesh combo box label
+ LLTextBox::Params p;
+ p.label("Model");
+ p.name("model label");
+ p.rect(LLRect(right/3, cur_y, right/2, cur_y-20));
+ LLTextBox* text_box = LLUICtrlFactory::create<LLTextBox>(p);
+ text_box->setValue("Model");
+ mDecompFloater->addChild(text_box);
+ }
+ {
+ //add submesh combo box
+ LLComboBox::Params p;
+ p.rect(LLRect(right/2, cur_y, right, cur_y-20));
+ p.name("model");
+ LLComboBox* combo_box = LLUICtrlFactory::create<LLComboBox>(p);
+ for (S32 i = 0; i < mModelPreview->mBaseModel.size(); ++i)
+ {
+ LLModel* mdl = mModelPreview->mBaseModel[i];
+ combo_box->add(mdl->mLabel, i);
+ }
+ combo_box->setValue(0);
+ mDecompFloater->addChild(combo_box);
+ cur_y += 30;
+ }
+
+ mDecompFloater->childSetCommitCallback("model", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance);
+ mDecompFloater->childSetCommitCallback("render_mesh", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance);
+ mDecompFloater->childSetCommitCallback("render_hull", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance);
+
+ mDecompFloater->setRect(LLRect(10, cur_y+20, right+20, 10));
+ }
+
+ mDecompFloater->openFloater();
+}
+
+//-----------------------------------------------------------------------------
+// onMouseCaptureLost()
+//-----------------------------------------------------------------------------
+// static
+void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handler)
+{
+ gViewerWindow->showCursor();
+}
+
+//-----------------------------------------------------------------------------
+// LLModelLoader
+//-----------------------------------------------------------------------------
+LLModelLoader::LLModelLoader(std::string filename, S32 lod, LLModelPreview* preview)
+: LLThread("Model Loader"), mFilename(filename), mLod(lod), mPreview(preview), mState(STARTING), mFirstTransform(TRUE)
+{
+ mJointMap["mPelvis"] = "mPelvis";
+ mJointMap["mTorso"] = "mTorso";
+ mJointMap["mChest"] = "mChest";
+ mJointMap["mNeck"] = "mNeck";
+ mJointMap["mHead"] = "mHead";
+ mJointMap["mSkull"] = "mSkull";
+ mJointMap["mEyeRight"] = "mEyeRight";
+ mJointMap["mEyeLeft"] = "mEyeLeft";
+ mJointMap["mCollarLeft"] = "mCollarLeft";
+ mJointMap["mShoulderLeft"] = "mShoulderLeft";
+ mJointMap["mElbowLeft"] = "mElbowLeft";
+ mJointMap["mWristLeft"] = "mWristLeft";
+ mJointMap["mCollarRight"] = "mCollarRight";
+ mJointMap["mShoulderRight"] = "mShoulderRight";
+ mJointMap["mElbowRight"] = "mElbowRight";
+ mJointMap["mWristRight"] = "mWristRight";
+ mJointMap["mHipRight"] = "mHipRight";
+ mJointMap["mKneeRight"] = "mKneeRight";
+ mJointMap["mAnkleRight"] = "mAnkleRight";
+ mJointMap["mFootRight"] = "mFootRight";
+ mJointMap["mToeRight"] = "mToeRight";
+ mJointMap["mHipLeft"] = "mHipLeft";
+ mJointMap["mKneeLeft"] = "mKneeLeft";
+ mJointMap["mAnkleLeft"] = "mAnkleLeft";
+ mJointMap["mFootLeft"] = "mFootLeft";
+ mJointMap["mToeLeft"] = "mToeLeft";
+
+ mJointMap["avatar_mPelvis"] = "mPelvis";
+ mJointMap["avatar_mTorso"] = "mTorso";
+ mJointMap["avatar_mChest"] = "mChest";
+ mJointMap["avatar_mNeck"] = "mNeck";
+ mJointMap["avatar_mHead"] = "mHead";
+ mJointMap["avatar_mSkull"] = "mSkull";
+ mJointMap["avatar_mEyeRight"] = "mEyeRight";
+ mJointMap["avatar_mEyeLeft"] = "mEyeLeft";
+ mJointMap["avatar_mCollarLeft"] = "mCollarLeft";
+ mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft";
+ mJointMap["avatar_mElbowLeft"] = "mElbowLeft";
+ mJointMap["avatar_mWristLeft"] = "mWristLeft";
+ mJointMap["avatar_mCollarRight"] = "mCollarRight";
+ mJointMap["avatar_mShoulderRight"] = "mShoulderRight";
+ mJointMap["avatar_mElbowRight"] = "mElbowRight";
+ mJointMap["avatar_mWristRight"] = "mWristRight";
+ mJointMap["avatar_mHipRight"] = "mHipRight";
+ mJointMap["avatar_mKneeRight"] = "mKneeRight";
+ mJointMap["avatar_mAnkleRight"] = "mAnkleRight";
+ mJointMap["avatar_mFootRight"] = "mFootRight";
+ mJointMap["avatar_mToeRight"] = "mToeRight";
+ mJointMap["avatar_mHipLeft"] = "mHipLeft";
+ mJointMap["avatar_mKneeLeft"] = "mKneeLeft";
+ mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft";
+ mJointMap["avatar_mFootLeft"] = "mFootLeft";
+ mJointMap["avatar_mToeLeft"] = "mToeLeft";
+
+
+ mJointMap["hip"] = "mPelvis";
+ mJointMap["abdomen"] = "mTorso";
+ mJointMap["chest"] = "mChest";
+ mJointMap["neck"] = "mNeck";
+ mJointMap["head"] = "mHead";
+ mJointMap["figureHair"] = "mSkull";
+ mJointMap["lCollar"] = "mCollarLeft";
+ mJointMap["lShldr"] = "mShoulderLeft";
+ mJointMap["lForeArm"] = "mElbowLeft";
+ mJointMap["lHand"] = "mWristLeft";
+ mJointMap["rCollar"] = "mCollarRight";
+ mJointMap["rShldr"] = "mShoulderRight";
+ mJointMap["rForeArm"] = "mElbowRight";
+ mJointMap["rHand"] = "mWristRight";
+ mJointMap["rThigh"] = "mHipRight";
+ mJointMap["rShin"] = "mKneeRight";
+ mJointMap["rFoot"] = "mFootRight";
+ mJointMap["lThigh"] = "mHipLeft";
+ mJointMap["lShin"] = "mKneeLeft";
+ mJointMap["lFoot"] = "mFootLeft";
+}
+
+void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform)
+{
+ LLVector4a box[] =
+ {
+ LLVector4a(-1, 1,-1),
+ LLVector4a(-1, 1, 1),
+ LLVector4a(-1,-1,-1),
+ LLVector4a(-1,-1, 1),
+ LLVector4a( 1, 1,-1),
+ LLVector4a( 1, 1, 1),
+ LLVector4a( 1,-1,-1),
+ LLVector4a( 1,-1, 1),
+ };
+
+ for (S32 j = 0; j < model->getNumVolumeFaces(); ++j)
+ {
+ const LLVolumeFace& face = model->getVolumeFace(j);
+
+ LLVector4a center;
+ center.setAdd(face.mExtents[0], face.mExtents[1]);
+ center.mul(0.5f);
+ LLVector4a size;
+ size.setSub(face.mExtents[1],face.mExtents[0]);
+ size.mul(0.5f);
+
+ for (U32 i = 0; i < 8; i++)
+ {
+ LLVector4a t;
+ t.setMul(size, box[i]);
+ t.add(center);
+
+ LLVector4a v;
+
+ mat.affineTransform(t, v);
+
+ if (first_transform)
+ {
+ first_transform = FALSE;
+ min = max = v;
+ }
+ else
+ {
+ update_min_max(min, max, v);
+ }
+ }
+ }
+}
+
+void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform)
+{
+ LLVector4a mina, maxa;
+ LLMatrix4a mata;
+
+ mata.loadu(mat);
+ mina.load3(min.mV);
+ maxa.load3(max.mV);
+
+ stretch_extents(model, mata, mina, maxa, first_transform);
+
+ min.set(mina.getF32ptr());
+ max.set(maxa.getF32ptr());
+}
+
+void LLModelLoader::run()
+{
+ DAE dae;
+ domCOLLADA* dom = dae.open(mFilename);
+
+ if (dom)
+ {
+ daeDatabase* db = dae.getDatabase();
+
+ daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
+
+ daeDocument* doc = dae.getDoc(mFilename);
+ if (!doc)
+ {
+ llwarns << "can't find internal doc" << llendl;
+ return;
+ }
+
+ daeElement* root = doc->getDomRoot();
+ if (!root)
+ {
+ llwarns << "document has no root" << llendl;
+ return;
+ }
+
+ //get unit scale
+ mTransform.setIdentity();
+
+ domAsset::domUnit* unit = daeSafeCast<domAsset::domUnit>(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID())));
+
+ if (unit)
+ {
+ F32 meter = unit->getMeter();
+ mTransform.mMatrix[0][0] = meter;
+ mTransform.mMatrix[1][1] = meter;
+ mTransform.mMatrix[2][2] = meter;
+ }
+
+ //get up axis rotation
+ LLMatrix4 rotation;
+
+ domUpAxisType up = UPAXISTYPE_Y_UP; // default is Y_UP
+ domAsset::domUp_axis* up_axis =
+ daeSafeCast<domAsset::domUp_axis>(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID())));
+
+ if (up_axis)
+ {
+ up = up_axis->getValue();
+ }
+
+ if (up == UPAXISTYPE_X_UP)
+ {
+ rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f);
+ }
+ else if (up == UPAXISTYPE_Y_UP)
+ {
+ rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f);
+ }
+
+ rotation *= mTransform;
+ mTransform = rotation;
+
+
+ for (daeInt idx = 0; idx < count; ++idx)
+ { //build map of domEntities to LLModel
+ domMesh* mesh = NULL;
+ db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH);
+
+ if (mesh)
+ {
+ LLPointer<LLModel> model = LLModel::loadModelFromDomMesh(mesh);
+
+ if (model.notNull() && validate_model(model))
+ {
+ mModelList.push_back(model);
+ mModel[mesh] = model;
+ }
+ }
+ }
+
+ count = db->getElementCount(NULL, COLLADA_TYPE_SKIN);
+ for (daeInt idx = 0; idx < count; ++idx)
+ { //add skinned meshes as instances
+ domSkin* skin = NULL;
+ db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN);
+
+ if (skin)
+ {
+ domGeometry* geom = daeSafeCast<domGeometry>(skin->getSource().getElement());
+
+ if (geom)
+ {
+ domMesh* mesh = geom->getMesh();
+ if (mesh)
+ {
+ LLModel* model = mModel[mesh];
+ if (model)
+ {
+ LLVector3 mesh_scale_vector;
+ LLVector3 mesh_translation_vector;
+ model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+
+ LLMatrix4 normalized_transformation;
+ normalized_transformation.setTranslation(mesh_translation_vector);
+
+ LLMatrix4 mesh_scale;
+ mesh_scale.initScale(mesh_scale_vector);
+ mesh_scale *= normalized_transformation;
+ normalized_transformation = mesh_scale;
+
+ glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix);
+ inv_mat = inv_mat.inverse();
+ LLMatrix4 inverse_normalized_transformation(inv_mat.m);
+
+ domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix();
+
+ if (bind_mat)
+ { //get bind shape matrix
+ domFloat4x4& dom_value = bind_mat->getValue();
+
+ for (int i = 0; i < 4; i++)
+ {
+ for(int j = 0; j < 4; j++)
+ {
+ model->mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4];
+ }
+ }
+
+ LLMatrix4 trans = normalized_transformation;
+ trans *= model->mBindShapeMatrix;
+ model->mBindShapeMatrix = trans;
+
+ }
+
+ /*{
+ LLMatrix4 rotation;
+ if (up == UPAXISTYPE_X_UP)
+ {
+ rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f);
+ }
+ else if (up == UPAXISTYPE_Z_UP)
+ {
+ rotation.initRotation(90.0f * DEG_TO_RAD, 90.0f * DEG_TO_RAD, 0.0f);
+ }
+
+ rotation *= model->mBindShapeMatrix;
+ model->mBindShapeMatrix = rotation;
+ }*/
+
+
+ domSkin::domJoints* joints = skin->getJoints();
+
+ domInputLocal_Array& joint_input = joints->getInput_array();
+
+ for (size_t i = 0; i < joint_input.getCount(); ++i)
+ {
+ domInputLocal* input = joint_input.get(i);
+ xsNMTOKEN semantic = input->getSemantic();
+
+ if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0)
+ { //found joint source, fill model->mJointMap and model->mJointList
+ daeElement* elem = input->getSource().getElement();
+
+ domSource* source = daeSafeCast<domSource>(elem);
+ if (source)
+ {
+
+
+ domName_array* names_source = source->getName_array();
+
+ if (names_source)
+ {
+ domListOfNames &names = names_source->getValue();
+
+ for (size_t j = 0; j < names.getCount(); ++j)
+ {
+ std::string name(names.get(j));
+ if (mJointMap.find(name) != mJointMap.end())
+ {
+ name = mJointMap[name];
+ }
+ model->mJointList.push_back(name);
+ model->mJointMap[name] = j;
+ }
+ }
+ else
+ {
+ domIDREF_array* names_source = source->getIDREF_array();
+ if (names_source)
+ {
+ xsIDREFS& names = names_source->getValue();
+
+ for (size_t j = 0; j < names.getCount(); ++j)
+ {
+ std::string name(names.get(j).getID());
+ if (mJointMap.find(name) != mJointMap.end())
+ {
+ name = mJointMap[name];
+ }
+ model->mJointList.push_back(name);
+ model->mJointMap[name] = j;
+ }
+ }
+ }
+ }
+ }
+ else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0)
+ { //found inv_bind_matrix array, fill model->mInvBindMatrix
+ domSource* source = daeSafeCast<domSource>(input->getSource().getElement());
+ if (source)
+ {
+ domFloat_array* t = source->getFloat_array();
+ if (t)
+ {
+ domListOfFloats& transform = t->getValue();
+ S32 count = transform.getCount()/16;
+
+ for (S32 k = 0; k < count; ++k)
+ {
+ LLMatrix4 mat;
+
+ for (int i = 0; i < 4; i++)
+ {
+ for(int j = 0; j < 4; j++)
+ {
+ mat.mMatrix[i][j] = transform[k*16 + i + j*4];
+ }
+ }
+
+ model->mInvBindMatrix.push_back(mat);
+ }
+ }
+ }
+ }
+ }
+
+
+ //grab raw position array
+
+ domVertices* verts = mesh->getVertices();
+ if (verts)
+ {
+ domInputLocal_Array& inputs = verts->getInput_array();
+ for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i)
+ {
+ if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0)
+ {
+ domSource* pos_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement());
+ if (pos_source)
+ {
+ domFloat_array* pos_array = pos_source->getFloat_array();
+ if (pos_array)
+ {
+ domListOfFloats& pos = pos_array->getValue();
+
+ for (size_t j = 0; j < pos.getCount(); j += 3)
+ {
+ if (pos.getCount() <= j+2)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ LLVector3 v(pos[j], pos[j+1], pos[j+2]);
+
+ //transform from COLLADA space to volume space
+ v = v * inverse_normalized_transformation;
+
+ model->mPosition.push_back(v);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //grab skin weights array
+ domSkin::domVertex_weights* weights = skin->getVertex_weights();
+ if (weights)
+ {
+ domInputLocalOffset_Array& inputs = weights->getInput_array();
+ domFloat_array* vertex_weights = NULL;
+ for (size_t i = 0; i < inputs.getCount(); ++i)
+ {
+ if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0)
+ {
+ domSource* weight_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement());
+ if (weight_source)
+ {
+ vertex_weights = weight_source->getFloat_array();
+ }
+ }
+ }
+
+ if (vertex_weights)
+ {
+ domListOfFloats& w = vertex_weights->getValue();
+ domListOfUInts& vcount = weights->getVcount()->getValue();
+ domListOfInts& v = weights->getV()->getValue();
+
+ U32 c_idx = 0;
+ for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx)
+ { //for each vertex
+ daeUInt count = vcount[vc_idx];
+
+ //create list of weights that influence this vertex
+ LLModel::weight_list weight_list;
+
+ for (daeUInt i = 0; i < count; ++i)
+ { //for each weight
+ daeInt joint_idx = v[c_idx++];
+ daeInt weight_idx = v[c_idx++];
+
+ if (joint_idx == -1)
+ {
+ //ignore bindings to bind_shape_matrix
+ continue;
+ }
+
+ F32 weight_value = w[weight_idx];
+
+ weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value));
+ }
+
+ //sort by joint weight
+ std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater());
+
+ std::vector<LLModel::JointWeight> wght;
+
+ F32 total = 0.f;
+
+ for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i)
+ { //take up to 4 most significant weights
+ if (weight_list[i].mWeight > 0.f)
+ {
+ wght.push_back( weight_list[i] );
+ total += weight_list[i].mWeight;
+ }
+ }
+
+ F32 scale = 1.f/total;
+ if (scale != 1.f)
+ { //normalize weights
+ for (U32 i = 0; i < wght.size(); ++i)
+ {
+ wght[i].mWeight *= scale;
+ }
+ }
+
+ model->mSkinWeights[model->mPosition[vc_idx]] = wght;
+ }
+
+ //add instance to scene for this model
+
+ LLMatrix4 transform;
+ std::vector<LLImportMaterial> materials;
+ materials.resize(model->getNumVolumeFaces());
+ mScene[transform].push_back(LLModelInstance(model, transform, materials));
+ stretch_extents(model, transform, mExtents[0], mExtents[1], mFirstTransform);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ daeElement* scene = root->getDescendant("visual_scene");
+ if (!scene)
+ {
+ llwarns << "document has no visual_scene" << llendl;
+ return;
+ }
+
+ processElement(scene);
+
+ mPreview->loadModelCallback(mLod);
+ }
+}
+
+void LLModelLoader::processElement(daeElement* element)
+{
+ LLMatrix4 saved_transform = mTransform;
+
+ domTranslate* translate = daeSafeCast<domTranslate>(element);
+ if (translate)
+ {
+ domFloat3 dom_value = translate->getValue();
+
+ LLMatrix4 translation;
+ translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
+
+ translation *= mTransform;
+ mTransform = translation;
+ }
+
+ domRotate* rotate = daeSafeCast<domRotate>(element);
+ if (rotate)
+ {
+ domFloat4 dom_value = rotate->getValue();
+
+ LLMatrix4 rotation;
+ rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0));
+
+ rotation *= mTransform;
+ mTransform = rotation;
+ }
+
+ domScale* scale = daeSafeCast<domScale>(element);
+ if (scale)
+ {
+ domFloat3 dom_value = scale->getValue();
+
+ LLMatrix4 scaling;
+ scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
+
+ scaling *= mTransform;
+ mTransform = scaling;
+ }
+
+ domMatrix* matrix = daeSafeCast<domMatrix>(element);
+ if (matrix)
+ {
+ domFloat4x4 dom_value = matrix->getValue();
+
+ LLMatrix4 matrix_transform;
+
+ for (int i = 0; i < 4; i++)
+ {
+ for(int j = 0; j < 4; j++)
+ {
+ matrix_transform.mMatrix[i][j] = dom_value[i + j*4];
+ }
+ }
+
+ matrix_transform *= mTransform;
+ mTransform = matrix_transform;
+ }
+
+ domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element);
+ if (instance_geo)
+ {
+ domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement());
+ if (geo)
+ {
+ domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID())));
+ if (mesh)
+ {
+ LLModel* model = mModel[mesh];
+ if (model)
+ {
+ LLMatrix4 transformation = mTransform;
+
+ std::vector<LLImportMaterial> materials = getMaterials(model, instance_geo);
+
+ // adjust the transformation to compensate for mesh normalization
+ LLVector3 mesh_scale_vector;
+ LLVector3 mesh_translation_vector;
+ model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+
+ LLMatrix4 mesh_translation;
+ mesh_translation.setTranslation(mesh_translation_vector);
+ mesh_translation *= transformation;
+ transformation = mesh_translation;
+
+ LLMatrix4 mesh_scale;
+ mesh_scale.initScale(mesh_scale_vector);
+ mesh_scale *= transformation;
+ transformation = mesh_scale;
+
+ mScene[transformation].push_back(LLModelInstance(model, transformation, materials));
+
+ stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
+ }
+ }
+ }
+ }
+
+ domInstance_node* instance_node = daeSafeCast<domInstance_node>(element);
+ if (instance_node)
+ {
+ daeElement* instance = instance_node->getUrl().getElement();
+ if (instance)
+ {
+ processElement(instance);
+ }
+ }
+
+ //process children
+ daeTArray< daeSmartRef<daeElement> > children = element->getChildren();
+ for (S32 i = 0; i < children.getCount(); i++)
+ {
+ processElement(children[i]);
+ }
+
+ domNode* node = daeSafeCast<domNode>(element);
+ if (node)
+ { //this element was a node, restore transform before processiing siblings
+ mTransform = saved_transform;
+ }
+}
+
+std::vector<LLImportMaterial> LLModelLoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo)
+{
+ std::vector<LLImportMaterial> materials;
+ for (int i = 0; i < model->mMaterialList.size(); i++)
+ {
+ LLImportMaterial import_material;
+
+ domInstance_material* instance_mat = NULL;
+
+ domBind_material::domTechnique_common* technique =
+ daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID())));
+
+ if (technique)
+ {
+ daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>();
+ for (int j = 0; j < inst_materials.getCount(); j++)
+ {
+ std::string symbol(inst_materials[j]->getSymbol());
+
+ if (symbol == model->mMaterialList[i]) // found the binding
+ {
+ instance_mat = inst_materials[j];
+ }
+ }
+ }
+
+ if (instance_mat)
+ {
+ domMaterial* material = daeSafeCast<domMaterial>(instance_mat->getTarget().getElement());
+ if (material)
+ {
+ domInstance_effect* instance_effect =
+ daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID())));
+ if (instance_effect)
+ {
+ domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement());
+ if (effect)
+ {
+ domProfile_COMMON* profile =
+ daeSafeCast<domProfile_COMMON>(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID())));
+ if (profile)
+ {
+ import_material = profileToMaterial(profile);
+ }
+ }
+ }
+ }
+ }
+
+ materials.push_back(import_material);
+ }
+
+ return materials;
+}
+
+LLImportMaterial LLModelLoader::profileToMaterial(domProfile_COMMON* material)
+{
+ LLImportMaterial mat;
+ mat.mFullbright = FALSE;
+
+ daeElement* diffuse = material->getDescendant("diffuse");
+ if (diffuse)
+ {
+ domCommon_color_or_texture_type_complexType::domTexture* texture =
+ daeSafeCast<domCommon_color_or_texture_type_complexType::domTexture>(diffuse->getDescendant("texture"));
+ if (texture)
+ {
+ domCommon_newparam_type_Array newparams = material->getNewparam_array();
+ for (S32 i = 0; i < newparams.getCount(); i++)
+ {
+ domFx_surface_common* surface = newparams[i]->getSurface();
+ if (surface)
+ {
+ domFx_surface_init_common* init = surface->getFx_surface_init_common();
+ if (init)
+ {
+ domFx_surface_init_from_common_Array init_from = init->getInit_from_array();
+
+ if (init_from.getCount() > i)
+ {
+ domImage* image = daeSafeCast<domImage>(init_from[i]->getValue().getElement());
+ if (image)
+ {
+ // we only support init_from now - embedded data will come later
+ domImage::domInit_from* init = image->getInit_from();
+ if (init)
+ {
+ std::string filename = cdom::uriToNativePath(init->getValue().str());
+
+ mat.mDiffuseMap = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + filename, TRUE, LLViewerTexture::BOOST_PREVIEW);
+ mat.mDiffuseMap->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, this->mPreview, NULL, NULL);
+
+ mat.mDiffuseMap->forceToSaveRawImage();
+ mat.mDiffuseMapFilename = filename;
+ mat.mDiffuseMapLabel = getElementLabel(material);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ domCommon_color_or_texture_type_complexType::domColor* color =
+ daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(diffuse->getDescendant("color"));
+ if (color)
+ {
+ domFx_color_common domfx_color = color->getValue();
+ LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
+ mat.mDiffuseColor = value;
+ }
+ }
+
+ daeElement* emission = material->getDescendant("emission");
+ if (emission)
+ {
+ LLColor4 emission_color = getDaeColor(emission);
+ if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25)
+ {
+ mat.mFullbright = TRUE;
+ }
+ }
+
+ return mat;
+}
+
+// try to get a decent label for this element
+std::string LLModelLoader::getElementLabel(daeElement *element)
+{
+ // if we have a name attribute, use it
+ std::string name = element->getAttribute("name");
+ if (name.length())
+ {
+ return name;
+ }
+
+ // if we have an ID attribute, use it
+ if (element->getID())
+ {
+ return std::string(element->getID());
+ }
+
+ // if we have a parent, use it
+ daeElement* parent = element->getParent();
+ if (parent)
+ {
+ // if parent has a name, use it
+ std::string name = parent->getAttribute("name");
+ if (name.length())
+ {
+ return name;
+ }
+
+ // if parent has an ID, use it
+ if (parent->getID())
+ {
+ return std::string(parent->getID());
+ }
+ }
+
+ // try to use our type
+ daeString element_name = element->getElementName();
+ if (element_name)
+ {
+ return std::string(element_name);
+ }
+
+ // if all else fails, use "object"
+ return std::string("object");
+}
+
+LLColor4 LLModelLoader::getDaeColor(daeElement* element)
+{
+ LLColor4 value;
+ domCommon_color_or_texture_type_complexType::domColor* color =
+ daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(element->getDescendant("color"));
+ if (color)
+ {
+ domFx_color_common domfx_color = color->getValue();
+ value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
+ }
+
+ return value;
+}
+
+//-----------------------------------------------------------------------------
+// LLModelPreview
+//-----------------------------------------------------------------------------
+
+LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloaterModelPreview* fmp)
+: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex(NULL)
+{
+ mNeedsUpdate = TRUE;
+ mCameraDistance = 0.f;
+ mCameraYaw = 0.f;
+ mCameraPitch = 0.f;
+ mCameraZoom = 1.f;
+ mTextureName = 0;
+ mPreviewLOD = 3;
+ mModelLoader = NULL;
+
+ mLODMode[0] = 0;
+
+ for (U32 i = 1; i < LLModel::NUM_LODS; i++)
+ {
+ mLODMode[i] = 1;
+ mLimit[i] = 0;
+ }
+
+ mFMP = fmp;
+
+ glodInit();
+}
+
+LLModelPreview::~LLModelPreview()
+{
+ if (mModelLoader)
+ {
+ delete mModelLoader;
+ mModelLoader = NULL;
+ }
+
+ //*HACK : *TODO : turn this back on when we understand why this crashes
+ //glodShutdown();
+}
+
+U32 LLModelPreview::calcResourceCost()
+{
+ rebuildUploadData();
+
+ U32 cost = 0;
+ std::set<LLModel*> accounted;
+ U32 num_points = 0;
+ U32 num_hulls = 0;
+
+ for (U32 i = 0; i < mUploadData.size(); ++i)
+ {
+ LLModelInstance& instance = mUploadData[i];
+
+ if (accounted.find(instance.mModel) == accounted.end())
+ {
+ accounted.insert(instance.mModel);
+
+ LLModel::physics_shape& physics_shape = instance.mLOD[LLModel::LOD_PHYSICS] ? instance.mLOD[LLModel::LOD_PHYSICS]->mPhysicsShape : instance.mModel->mPhysicsShape;
+
+ LLSD ret = LLModel::writeModel("",
+ instance.mLOD[4],
+ instance.mLOD[3],
+ instance.mLOD[2],
+ instance.mLOD[1],
+ instance.mLOD[0],
+ physics_shape,
+ TRUE);
+ cost += gMeshRepo.calcResourceCost(ret);
+
+
+ num_hulls += physics_shape.size();
+ for (U32 i = 0; i < physics_shape.size(); ++i)
+ {
+ num_points += physics_shape[i].size();
+ }
+ }
+ }
+
+ mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[HULLS]", llformat("%d",num_hulls));
+ mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[POINTS]", llformat("%d",num_points));
+
+ updateStatusMessages();
+
+ return cost;
+}
+
+void LLModelPreview::rebuildUploadData()
+{
+ mUploadData.clear();
+ mTextureSet.clear();
+
+ //fill uploaddata instance vectors from scene data
+
+ for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter)
+ { //for each transform in scene
+ for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+ { //for each instance with said transform applied
+ LLModelInstance& instance = *model_iter;
+
+ LLModel* base_model = instance.mModel;
+
+ S32 idx = 0;
+ for (idx = 0; idx < mBaseModel.size(); ++idx)
+ { //find reference instance for this model
+ if (mBaseModel[idx] == base_model)
+ {
+ break;
+ }
+ }
+
+ for (U32 i = 0; i < LLModel::NUM_LODS; i++)
+ { //fill LOD slots based on reference model index
+ if (!mModel[i].empty())
+ {
+ instance.mLOD[i] = mModel[i][idx];
+ }
+ else
+ {
+ instance.mLOD[i] = NULL;
+ }
+ }
+
+ mUploadData.push_back(instance);
+ }
+ }
+}
+
+
+void LLModelPreview::loadModel(std::string filename, S32 lod)
+{
+ LLMutexLock lock(this);
+
+ if (mModelLoader)
+ {
+ delete mModelLoader;
+ mModelLoader = NULL;
+ }
+
+ if (filename.empty() && mBaseModel.empty())
+ {
+ mFMP->closeFloater(false);
+ return;
+ }
+
+ if (lod == 3 && !mGroup.empty())
+ {
+ for (std::map<LLModel*, U32>::iterator iter = mGroup.begin(); iter != mGroup.end(); ++iter)
+ {
+ glodDeleteGroup(iter->second);
+ stop_gloderror();
+ }
+
+ for (std::map<LLModel*, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter)
+ {
+ glodDeleteObject(iter->second);
+ stop_gloderror();
+ }
+
+ mGroup.clear();
+ mObject.clear();
+ }
+
+ mModelLoader = new LLModelLoader(filename, lod, this);
+
+ mModelLoader->start();
+
+ mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file"));
+
+ if (mFMP->childGetValue("description_form").asString().empty())
+ {
+ std::string name = gDirUtilp->getBaseFileName(filename, true);
+ mFMP->childSetValue("description_form", name);
+ }
+
+ mFMP->openFloater();
+}
+
+void LLModelPreview::clearIncompatible(S32 lod)
+{
+ for (U32 i = 0; i <= LLModel::LOD_HIGH; i++)
+ { //clear out any entries that aren't compatible with this model
+ if (i != lod)
+ {
+ if (mModel[i].size() != mModel[lod].size())
+ {
+ mModel[i].clear();
+ mScene[i].clear();
+ mVertexBuffer[i].clear();
+
+ if (i == LLModel::LOD_HIGH)
+ {
+ mBaseModel = mModel[lod];
+ mBaseScene = mScene[lod];
+ mVertexBuffer[5].clear();
+ }
+ }
+ }
+ }
+}
+
+void LLModelPreview::loadModelCallback(S32 lod)
+{ //NOT the main thread
+ LLMutexLock lock(this);
+ if (!mModelLoader)
+ {
+ return;
+ }
+
+ mModel[lod] = mModelLoader->mModelList;
+ mScene[lod] = mModelLoader->mScene;
+ mVertexBuffer[lod].clear();
+
+ setPreviewLOD(lod);
+
+
+ if (lod == LLModel::LOD_HIGH)
+ { //save a copy of the highest LOD for automatic LOD manipulation
+ mBaseModel = mModel[lod];
+ mBaseScene = mScene[lod];
+ mVertexBuffer[5].clear();
+ //mModel[lod] = NULL;
+ }
+
+ clearIncompatible(lod);
+
+ mResourceCost = calcResourceCost();
+
+ mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f;
+ mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f;
+ setPreviewTarget(mPreviewScale.magVec()*2.f);
+
+ mFMP->mLoading = FALSE;
+ refresh();
+}
+
+void LLModelPreview::smoothNormals()
+{
+ S32 which_lod = mPreviewLOD;
+
+
+ if (which_lod > 4 || which_lod < 0 ||
+ mModel[which_lod].empty())
+ {
+ return;
+ }
+
+ F32 angle_cutoff = mFMP->childGetValue("edge threshold").asReal();
+
+ angle_cutoff *= DEG_TO_RAD;
+
+ if (which_lod == 3 && !mBaseModel.empty())
+ {
+ for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter)
+ {
+ (*iter)->smoothNormals(angle_cutoff);
+ }
+
+ mVertexBuffer[5].clear();
+ }
+
+ for (LLModelLoader::model_list::iterator iter = mModel[which_lod].begin(); iter != mModel[which_lod].end(); ++iter)
+ {
+ (*iter)->smoothNormals(angle_cutoff);
+ }
+
+ mVertexBuffer[which_lod].clear();
+ refresh();
+
+}
+
+void LLModelPreview::consolidate()
+{
+ std::map<LLImportMaterial, std::vector<LLModelInstance> > composite;
+
+ LLMatrix4 identity;
+
+ //bake out each node in current scene to composite
+ for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+ { //for each transform in current scene
+ LLMatrix4 mat = iter->first;
+ glh::matrix4f inv_trans = glh::matrix4f((F32*) mat.mMatrix).inverse().transpose();
+ LLMatrix4 norm_mat(inv_trans.m);
+
+ for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+ { //for each instance with that transform
+ LLModelInstance& source_instance = *model_iter;
+ LLModel* source = source_instance.mModel;
+
+ if (!validate_model(source))
+ {
+ llerrs << "Invalid model found!" << llendl;
+ }
+
+ for (S32 i = 0; i < source->getNumVolumeFaces(); ++i)
+ { //for each face in instance
+ const LLVolumeFace& src_face = source->getVolumeFace(i);
+ LLImportMaterial& source_material = source_instance.mMaterial[i];
+
+ //get model in composite that is composite for this material
+ LLModel* model = NULL;
+
+ if (composite.find(source_material) != composite.end())
+ {
+ model = composite[source_material].rbegin()->mModel;
+ if (model->getVolumeFace(0).mNumVertices + src_face.mNumVertices > 65535)
+ {
+ model = NULL;
+ }
+ }
+
+ if (model == NULL)
+ { //no model found, make new model
+ std::vector<LLImportMaterial> materials;
+ materials.push_back(source_material);
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+ model = new LLModel(volume_params, 0.f);
+ model->mLabel = source->mLabel;
+ model->setNumVolumeFaces(0);
+ composite[source_material].push_back(LLModelInstance(model, identity, materials));
+ }
+
+ model->appendFace(src_face, source->mMaterialList[i], mat, norm_mat);
+ }
+ }
+ }
+
+
+ //condense composite into as few LLModel instances as possible
+ LLModelLoader::model_list new_model;
+ std::vector<LLModelInstance> instance_list;
+
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+
+ std::vector<LLImportMaterial> empty_material;
+ LLModelInstance cur_instance(new LLModel(volume_params, 0.f), identity, empty_material);
+ cur_instance.mModel->setNumVolumeFaces(0);
+
+ BOOL first_transform = TRUE;
+
+ LLModelLoader::scene new_scene;
+ LLVector3 min,max;
+
+ for (std::map<LLImportMaterial, std::vector<LLModelInstance> >::iterator iter = composite.begin();
+ iter != composite.end();
+ ++iter)
+ {
+ std::map<LLImportMaterial, std::vector<LLModelInstance> >::iterator next_iter = iter; ++next_iter;
+
+ for (std::vector<LLModelInstance>::iterator instance_iter = iter->second.begin();
+ instance_iter != iter->second.end();
+ ++instance_iter)
+ {
+ LLModel* source = instance_iter->mModel;
+
+ if (instance_iter->mMaterial.size() != 1)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ if (source->getNumVolumeFaces() != 1)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ if (source->mMaterialList.size() != 1)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ cur_instance.mModel->addFace(source->getVolumeFace(0));
+ cur_instance.mMaterial.push_back(instance_iter->mMaterial[0]);
+ cur_instance.mModel->mMaterialList.push_back(source->mMaterialList[0]);
+
+ BOOL last_model = FALSE;
+
+ std::vector<LLModelInstance>::iterator next_instance = instance_iter; ++next_instance;
+
+ if (next_iter == composite.end() &&
+ next_instance == iter->second.end())
+ {
+ last_model = TRUE;
+ }
+
+ if (last_model || cur_instance.mModel->getNumVolumeFaces() >= MAX_MODEL_FACES)
+ {
+ cur_instance.mModel->mLabel = source->mLabel;
+
+ cur_instance.mModel->optimizeVolumeFaces();
+ cur_instance.mModel->normalizeVolumeFaces();
+
+ if (!validate_model(cur_instance.mModel))
+ {
+ llerrs << "Invalid model detected." << llendl;
+ }
+
+ new_model.push_back(cur_instance.mModel);
+
+ LLMatrix4 transformation = LLMatrix4();
+
+ // adjust the transformation to compensate for mesh normalization
+ LLVector3 mesh_scale_vector;
+ LLVector3 mesh_translation_vector;
+ cur_instance.mModel->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+
+ LLMatrix4 mesh_translation;
+ mesh_translation.setTranslation(mesh_translation_vector);
+ mesh_translation *= transformation;
+ transformation = mesh_translation;
+
+ LLMatrix4 mesh_scale;
+ mesh_scale.initScale(mesh_scale_vector);
+ mesh_scale *= transformation;
+ transformation = mesh_scale;
+
+ cur_instance.mTransform = transformation;
+
+ new_scene[transformation].push_back(cur_instance);
+ stretch_extents(cur_instance.mModel, transformation, min, max, first_transform);
+
+ if (!last_model)
+ {
+ cur_instance = LLModelInstance(new LLModel(volume_params, 0.f), identity, empty_material);
+ cur_instance.mModel->setNumVolumeFaces(0);
+ }
+ }
+ }
+ }
+
+ mScene[mPreviewLOD] = new_scene;
+ mModel[mPreviewLOD] = new_model;
+ mVertexBuffer[mPreviewLOD].clear();
+
+ if (mPreviewLOD == LLModel::LOD_HIGH)
+ {
+ mBaseScene = new_scene;
+ mBaseModel = new_model;
+ mVertexBuffer[5].clear();
+ }
+
+ mPreviewTarget = (min+max)*0.5f;
+ mPreviewScale = (max-min)*0.5f;
+ setPreviewTarget(mPreviewScale.magVec()*2.f);
+
+ clearIncompatible(mPreviewLOD);
+
+ mResourceCost = calcResourceCost();
+ refresh();
+}
+
+void LLModelPreview::scrubMaterials()
+{
+ for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+ { //for each transform in current scene
+ for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+ { //for each instance with that transform
+ LLModelInstance& source_instance = *model_iter;
+ LLModel* source = source_instance.mModel;
+
+ for (S32 i = 0; i < source->getNumVolumeFaces(); ++i)
+ { //for each face in instance
+ LLImportMaterial& source_material = source_instance.mMaterial[i];
+
+ //clear material info
+ source_material.mDiffuseColor = LLColor4(1,1,1,1);
+ source_material.mDiffuseMap = NULL;
+ source_material.mDiffuseMapFilename.clear();
+ source_material.mDiffuseMapLabel.clear();
+ source_material.mFullbright = false;
+ }
+ }
+ }
+
+
+ mVertexBuffer[mPreviewLOD].clear();
+
+ if (mPreviewLOD == LLModel::LOD_HIGH)
+ {
+ mBaseScene = mScene[mPreviewLOD];
+ mBaseModel = mModel[mPreviewLOD];
+ mVertexBuffer[5].clear();
+ }
+
+ mResourceCost = calcResourceCost();
+ refresh();
+}
+
+void LLModelPreview::genLODs(S32 which_lod)
+{
+ if (mBaseModel.empty())
+ {
+ return;
+ }
+
+ LLVertexBuffer::unbind();
+
+ stop_gloderror();
+ static U32 cur_name = 1;
+
+ S32 limit = -1;
+
+ if (which_lod != -1)
+ {
+ limit = mLimit[which_lod];
+ }
+
+ U32 triangle_count = 0;
+
+ for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter)
+ {
+ LLModel* mdl = *iter;
+ for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+ {
+ triangle_count += mdl->getVolumeFace(i).mNumIndices/3;
+ }
+ }
+
+ U32 base_triangle_count = triangle_count;
+
+ U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+
+ for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter)
+ { //build GLOD objects for each model in base model list
+ LLModel* mdl = *iter;
+ if (mGroup[mdl] == 0)
+ {
+ mGroup[mdl] = cur_name++;
+ mObject[mdl] = cur_name++;
+
+ glodNewGroup(mGroup[mdl]);
+ stop_gloderror();
+
+ glodGroupParameteri(mGroup[mdl], GLOD_ADAPT_MODE, GLOD_TRIANGLE_BUDGET);
+ stop_gloderror();
+
+ glodGroupParameteri(mGroup[mdl], GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR);
+ stop_gloderror();
+
+ glodGroupParameterf(mGroup[mdl], GLOD_OBJECT_SPACE_ERROR_THRESHOLD, 0.025f);
+ stop_gloderror();
+
+ glodNewObject(mObject[mdl], mGroup[mdl], GLOD_DISCRETE);
+ stop_gloderror();
+
+ if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty())
+ { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation
+ mVertexBuffer[5].clear();
+ }
+
+ if (mVertexBuffer[5].empty())
+ {
+ genBuffers(5);
+ }
+
+ U32 tri_count = 0;
+ for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i)
+ {
+ mVertexBuffer[5][mdl][i]->setBuffer(type_mask);
+ U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices();
+ if (num_indices > 2)
+ {
+ glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f);
+ }
+ tri_count += num_indices/3;
+ stop_gloderror();
+ }
+
+ //store what percentage of total model (in terms of triangle count) this model makes up
+ mPercentage[mdl] = (F32) tri_count / (F32) base_triangle_count;
+
+ //build glodobject
+ glodBuildObject(mObject[mdl]);
+ if (stop_gloderror())
+ {
+ glodDeleteGroup(mGroup[mdl]);
+ stop_gloderror();
+ glodDeleteObject(mObject[mdl]);
+ stop_gloderror();
+
+ mGroup[mdl] = 0;
+ mObject[mdl] = 0;
+
+ if (which_lod == -1)
+ {
+ mModel[LLModel::LOD_HIGH] = mBaseModel;
+ }
+
+ return;
+ }
+
+ }
+
+ if (which_lod == -1 || mLODMode[which_lod] == 1)
+ {
+ //generating LODs for all entries, or this entry has a triangle budget
+ glodGroupParameteri(mGroup[mdl], GLOD_ADAPT_MODE, GLOD_TRIANGLE_BUDGET);
+ stop_gloderror();
+ }
+ else
+ {
+ //this entry uses error mode
+ glodGroupParameteri(mGroup[mdl], GLOD_ADAPT_MODE, GLOD_OBJECT_SPACE_ERROR);
+ stop_gloderror();
+ }
+
+ if (which_lod != -1 && mLODMode[which_lod] == 2)
+ {
+ glodGroupParameterf(mGroup[mdl], GLOD_OBJECT_SPACE_ERROR_THRESHOLD, llmax(limit/100.f, 0.01f));
+ stop_gloderror();
+ }
+ else
+ {
+ glodGroupParameterf(mGroup[mdl], GLOD_OBJECT_SPACE_ERROR_THRESHOLD, 0.025f);
+ stop_gloderror();
+ }
+ }
+
+
+ S32 start = LLModel::LOD_HIGH;
+ S32 end = 0;
+
+ BOOL error_mode = FALSE;
+
+ if (which_lod != -1)
+ {
+ start = end = which_lod;
+
+ if (mLODMode[which_lod] == 2)
+ {
+ error_mode = TRUE;
+ }
+ }
+
+
+ for (S32 lod = start; lod >= end; --lod)
+ {
+ if (!error_mode)
+ {
+ if (which_lod == -1)
+ {
+ if (lod < start)
+ {
+ triangle_count /= 3;
+ }
+ }
+ else
+ {
+ triangle_count = limit;
+ }
+ }
+
+ mModel[lod].clear();
+ mModel[lod].resize(mBaseModel.size());
+ mVertexBuffer[lod].clear();
+
+ U32 actual_tris = 0;
+ U32 actual_verts = 0;
+ U32 submeshes = 0;
+
+ for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx)
+ {
+ LLModel* base = mBaseModel[mdl_idx];
+
+ U32 target_count = U32(mPercentage[base]*triangle_count);
+
+ if (error_mode)
+ {
+ target_count = base->getNumTriangles();
+ }
+
+ if (target_count < 4)
+ {
+ target_count = 4;
+ }
+
+ if (which_lod == -1 || mLODMode[which_lod] == 1)
+ {
+ glodGroupParameteri(mGroup[base], GLOD_MAX_TRIANGLES, target_count);
+ stop_gloderror();
+ }
+
+ glodAdaptGroup(mGroup[base]);
+ stop_gloderror();
+
+ GLint patch_count = 0;
+ glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count);
+ stop_gloderror();
+
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+ mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f);
+
+ GLint* sizes = new GLint[patch_count*2];
+ glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes);
+ stop_gloderror();
+
+ GLint* names = new GLint[patch_count];
+ glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names);
+ stop_gloderror();
+
+ mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count);
+
+ LLModel* target_model = mModel[lod][mdl_idx];
+
+ for (GLint i = 0; i < patch_count; ++i)
+ {
+ LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0);
+
+ if (sizes[i*2+1] > 0 && sizes[i*2] > 0)
+ {
+ buff->allocateBuffer(sizes[i*2+1], sizes[i*2], true);
+ buff->setBuffer(type_mask);
+ glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, buff->getIndicesPointer());
+ stop_gloderror();
+ }
+ else
+ { //this face was eliminated, create a dummy triangle (one vertex, 3 indices, all 0)
+ buff->allocateBuffer(1, 3, true);
+ memset(buff->getMappedData(), 0, buff->getSize());
+ memset(buff->getIndicesPointer(), 0, buff->getIndicesSize());
+ }
+
+ buff->validateRange(0, buff->getNumVerts()-1, buff->getNumIndices(), 0);
+
+ LLStrider<LLVector3> pos;
+ LLStrider<LLVector3> norm;
+ LLStrider<LLVector2> tc;
+ LLStrider<U16> index;
+
+ buff->getVertexStrider(pos);
+ buff->getNormalStrider(norm);
+ buff->getTexCoord0Strider(tc);
+ buff->getIndexStrider(index);
+
+
+ target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices());
+ actual_tris += buff->getNumIndices()/3;
+ actual_verts += buff->getNumVerts();
+ ++submeshes;
+
+ if (!validate_face(target_model->getVolumeFace(names[i])))
+ {
+ llerrs << "Invalid face generated during LOD generation." << llendl;
+ }
+ }
+
+ //blind copy skin weights and just take closest skin weight to point on
+ //decimated mesh for now (auto-generating LODs with skin weights is still a bit
+ //of an open problem).
+ target_model->mPosition = base->mPosition;
+ target_model->mSkinWeights = base->mSkinWeights;
+ target_model->mJointMap = base->mJointMap;
+ target_model->mJointList = base->mJointList;
+ target_model->mInvBindMatrix = base->mInvBindMatrix;
+ target_model->mBindShapeMatrix = base->mBindShapeMatrix;
+
+ if (!validate_model(target_model))
+ {
+ llerrs << "Invalid model generated when creating LODs" << llendl;
+ }
+
+ delete [] sizes;
+ delete [] names;
+ }
+
+ //rebuild scene based on mBaseScene
+ mScene[lod].clear();
+ mScene[lod] = mBaseScene;
+
+ for (U32 i = 0; i < mBaseModel.size(); ++i)
+ {
+ LLModel* mdl = mBaseModel[i];
+ LLModel* target = mModel[lod][i];
+ if (target)
+ {
+ for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter)
+ {
+ for (U32 j = 0; j < iter->second.size(); ++j)
+ {
+ if (iter->second[j].mModel == mdl)
+ {
+ iter->second[j].mModel = target;
+ }
+ }
+ }
+ }
+ }
+
+ mResourceCost = calcResourceCost();
+ }
+}
+
+void LLModelPreview::updateStatusMessages()
+{
+ //triangle/vertex/submesh count for each mesh asset for each lod
+ std::vector<S32> tris[LLModel::NUM_LODS];
+ std::vector<S32> verts[LLModel::NUM_LODS];
+ std::vector<S32> submeshes[LLModel::NUM_LODS];
+
+ //total triangle/vertex/submesh count for each lod
+ S32 total_tris[LLModel::NUM_LODS];
+ S32 total_verts[LLModel::NUM_LODS];
+ S32 total_submeshes[LLModel::NUM_LODS];
+
+ for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
+ {
+ //initialize total for this lod to 0
+ total_tris[lod] = total_verts[lod] = total_submeshes[lod] = 0;
+
+ for (U32 i = 0; i < mModel[lod].size(); ++i)
+ { //for each model in the lod
+ S32 cur_tris = 0;
+ S32 cur_verts = 0;
+ S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces();
+
+ for (S32 j = 0; j < cur_submeshes; ++j)
+ { //for each submesh (face), add triangles and vertices to current total
+ const LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j);
+ cur_tris += face.mNumIndices/3;
+ cur_verts += face.mNumVertices;
+ }
+
+ //add this model to the lod total
+ total_tris[lod] += cur_tris;
+ total_verts[lod] += cur_verts;
+ total_submeshes[lod] += cur_submeshes;
+
+ //store this model's counts to asset data
+ tris[lod].push_back(cur_tris);
+ verts[lod].push_back(cur_verts);
+ submeshes[lod].push_back(cur_submeshes);
+ }
+ }
+
+
+ std::string upload_message;
+
+ for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
+ {
+ mFMP->childSetTextArg(info_name[lod], "[TRIANGLES]", llformat("%d", total_tris[lod]));
+ mFMP->childSetTextArg(info_name[lod], "[VERTICES]", llformat("%d", total_verts[lod]));
+ mFMP->childSetTextArg(info_name[lod], "[SUBMESHES]", llformat("%d", total_submeshes[lod]));
+
+ std::string message = "good";
+
+ const U32 lod_high = LLModel::LOD_HIGH;
+
+ if (lod != lod_high)
+ {
+ if (total_submeshes[lod] == 0)
+ { //no model loaded for this lod, see if one is required
+ for (U32 i = 0; i < verts[lod_high].size(); ++i)
+ {
+ const F32 ratio = 0.5f;
+ const S32 required_verts = 128;
+
+ F32 scaler = powf(0.5f, lod_high-lod);
+ S32 max_verts = verts[lod_high][i]*scaler;
+
+ if (max_verts > required_verts)
+ { //some model in this slot might have more than 128 vertices
+
+ //if any model higher up the chain has more than 128 vertices,
+ // lod is required here
+ for (S32 j = lod+1; j <= LLModel::LOD_HIGH; ++j)
+ {
+ if (verts[j].size() > i && verts[j][i] > 128)
+ {
+ message = "required";
+ upload_message = "missing_lod";
+ }
+ }
+ }
+ }
+ }
+ else if (total_submeshes[lod] != total_submeshes[lod_high])
+ {
+ message = "mesh_mismatch";
+ upload_message = "bad_lod";
+ }
+ else if (tris[lod].size() != tris[lod_high].size())
+ {
+ message = "model_mismatch";
+ upload_message = "bad_lod";
+ }
+ else
+ {
+ for (U32 i = 0; i < verts[lod].size(); ++i)
+ {
+ const F32 ratio = 0.5f;
+
+ F32 scaler = powf(0.5f, lod_high-lod);
+ S32 max_verts = verts[lod_high][i]*scaler;
+
+ if (verts[lod][i] > max_verts)
+ {
+ message = "too_heavy";
+ upload_message = "bad_lod";
+ }
+ }
+ }
+ }
+
+ mFMP->childSetTextArg(info_name[lod], "[MESSAGE]", mFMP->getString(message));
+ }
+
+ if (upload_message.empty())
+ {
+ mFMP->childSetTextArg("upload_message", "[MESSAGE]", std::string(""));
+ mFMP->childEnable("ok_btn");
+ }
+ else
+ {
+ mFMP->childSetTextArg("upload_message", "[MESSAGE]", mFMP->getString(upload_message));
+ mFMP->childDisable("ok_btn");
+ }
+}
+
+void LLModelPreview::setPreviewTarget(F32 distance)
+{
+ mCameraDistance = distance;
+ mCameraZoom = 1.f;
+ mCameraPitch = 0.f;
+ mCameraYaw = 0.f;
+ mCameraOffset.clearVec();
+}
+
+void LLModelPreview::genBuffers(S32 lod)
+{
+ U32 tri_count = 0;
+ U32 vertex_count = 0;
+ U32 mesh_count = 0;
+
+ LLModelLoader::model_list* model = NULL;
+
+ if (lod < 0 || lod > 4)
+ {
+ model = &mBaseModel;
+ lod = 5;
+ }
+ else
+ {
+ model = &(mModel[lod]);
+ }
+
+ if (!mVertexBuffer[lod].empty())
+ {
+ mVertexBuffer[lod].clear();
+ }
+
+ mVertexBuffer[lod].clear();
+
+ LLModelLoader::model_list::iterator base_iter = mBaseModel.begin();
+
+ for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter)
+ {
+ LLModel* mdl = *iter;
+ if (!mdl)
+ {
+ continue;
+ }
+
+ LLModel* base_mdl = *base_iter;
+ base_iter++;
+
+ for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace &vf = mdl->getVolumeFace(i);
+ U32 num_vertices = vf.mNumVertices;
+ U32 num_indices = vf.mNumIndices;
+
+ if (!num_vertices || ! num_indices)
+ {
+ continue;
+ }
+
+ LLVertexBuffer* vb = NULL;
+
+ bool skinned = !mdl->mSkinWeights.empty();
+
+ U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+
+ if (skinned)
+ {
+ mask |= LLVertexBuffer::MAP_WEIGHT4;
+ }
+
+ vb = new LLVertexBuffer(mask, 0);
+
+ vb->allocateBuffer(num_vertices, num_indices, TRUE);
+
+ LLStrider<LLVector3> vertex_strider;
+ LLStrider<LLVector3> normal_strider;
+ LLStrider<LLVector2> tc_strider;
+ LLStrider<U16> index_strider;
+ LLStrider<LLVector4> weights_strider;
+
+ vb->getVertexStrider(vertex_strider);
+ vb->getNormalStrider(normal_strider);
+ vb->getTexCoord0Strider(tc_strider);
+ vb->getIndexStrider(index_strider);
+
+ if (skinned)
+ {
+ vb->getWeight4Strider(weights_strider);
+ }
+
+ LLVector4a::memcpyNonAliased16((F32*) vertex_strider.get(), (F32*) vf.mPositions, num_vertices*4*sizeof(F32));
+ LLVector4a::memcpyNonAliased16((F32*) tc_strider.get(), (F32*) vf.mTexCoords, num_vertices*2*sizeof(F32));
+ LLVector4a::memcpyNonAliased16((F32*) normal_strider.get(), (F32*) vf.mNormals, num_vertices*4*sizeof(F32));
+
+ if (skinned)
+ {
+ // build vertices and normals
+ for (U32 i = 0; i < num_vertices; i++)
+ {
+ //find closest weight to vf.mVertices[i].mPosition
+ LLVector3 pos(vf.mPositions[i].getF32ptr());
+
+ LLModel::weight_list weight_list = base_mdl->getJointInfluences(pos);
+
+ LLVector4 w(0,0,0,0);
+ if (weight_list.size() > 4)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ for (U32 i = 0; i < weight_list.size(); ++i)
+ {
+ F32 wght = llmin(weight_list[i].mWeight, 0.999999f);
+ F32 joint = (F32) weight_list[i].mJointIdx;
+ w.mV[i] = joint + wght;
+ }
+
+ *(weights_strider++) = w;
+ }
+ }
+
+ // build indices
+ for (U32 i = 0; i < num_indices; i++)
+ {
+ *(index_strider++) = vf.mIndices[i];
+ }
+
+ mVertexBuffer[lod][mdl].push_back(vb);
+
+ vertex_count += num_vertices;
+ tri_count += num_indices/3;
+ ++mesh_count;
+
+ }
+ }
+
+ if (lod == 4)
+ {
+ for (U32 i = 0; i < 4; i++)
+ {
+ LLSpinCtrl* lim = mFMP->getChild<LLSpinCtrl>(limit_name[i], TRUE);
+
+ lim->setMaxValue(tri_count);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+BOOL LLModelPreview::render()
+{
+ LLMutexLock lock(this);
+ mNeedsUpdate = FALSE;
+
+ S32 width = getWidth();
+ S32 height = getHeight();
+
+ LLGLSUIDefault def;
+ LLGLDisable no_blend(GL_BLEND);
+ LLGLEnable cull(GL_CULL_FACE);
+ LLGLDepthTest depth(GL_TRUE);
+ LLGLDisable fog(GL_FOG);
+
+ glMatrixMode(GL_PROJECTION);
+ gGL.pushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, width, 0.0f, height, -1.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ gGL.pushMatrix();
+ glLoadIdentity();
+
+ gGL.color4f(0.15f, 0.2f, 0.3f, 1.f);
+
+ gl_rect_2d_simple( width, height );
+
+ bool avatar_preview = false;
+ for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+ {
+ for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+ {
+ LLModelInstance& instance = *model_iter;
+ LLModel* model = instance.mModel;
+ if (!model->mSkinWeights.empty())
+ {
+ avatar_preview = true;
+ }
+ }
+ }
+
+ mFMP->childSetEnabled("consolidate", !avatar_preview);
+
+ F32 explode = mFMP->childGetValue("explode").asReal();
+
+ glMatrixMode(GL_PROJECTION);
+ gGL.popMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ gGL.popMatrix();
+
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ LLViewerCamera::getInstance()->setAspect((F32) width / height );
+ LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom);
+
+ LLVector3 target_pos = mPreviewTarget;
+ LLVector3 offset = mCameraOffset;
+
+ F32 z_near = llmax(mCameraDistance-mPreviewScale.magVec(), 0.001f);
+ F32 z_far = mCameraDistance+mPreviewScale.magVec();
+
+ if (avatar_preview)
+ {
+ target_pos = gAgentAvatarp->getPositionAgent();
+ z_near = 0.01f;
+ z_far = 1024.f;
+ mCameraDistance = 16.f;
+
+ //render avatar previews every frame
+ refresh();
+ }
+
+ LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) *
+ LLQuaternion(mCameraYaw, LLVector3::z_axis);
+
+ LLQuaternion av_rot = camera_rot;
+ LLViewerCamera::getInstance()->setOriginAndLookAt(
+ target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera
+ LLVector3::z_axis, // up
+ target_pos); // point of interest
+
+
+ LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far);
+
+ stop_glerror();
+
+ gPipeline.enableLightsAvatar();
+
+ gGL.pushMatrix();
+ const F32 BRIGHTNESS = 0.9f;
+ gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS);
+
+ LLGLEnable normalize(GL_NORMALIZE);
+
+ if (!mBaseModel.empty() && mVertexBuffer[5].empty())
+ {
+ genBuffers(-1);
+ genBuffers(3);
+ //genLODs();
+ }
+
+ bool physics = (mPreviewLOD == LLModel::LOD_PHYSICS);
+
+ S32 physics_idx = -1;
+
+ bool render_mesh = true;
+ bool render_hull = false;
+
+ if (physics && mFMP->mDecompFloater)
+ {
+ physics_idx = mFMP->mDecompFloater->childGetValue("model").asInteger();
+ render_mesh = mFMP->mDecompFloater->childGetValue("render_mesh").asBoolean();
+ render_hull = mFMP->mDecompFloater->childGetValue("render_hull").asBoolean();
+ }
+
+ if (!mModel[mPreviewLOD].empty())
+ {
+ if (mVertexBuffer[mPreviewLOD].empty())
+ {
+ genBuffers(mPreviewLOD);
+ }
+
+ if (!avatar_preview)
+ {
+ for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+ {
+ gGL.pushMatrix();
+ LLMatrix4 mat = iter->first;
+
+ glMultMatrixf((GLfloat*) mat.mMatrix);
+
+ for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+ {
+ LLModelInstance& instance = *model_iter;
+ LLModel* model = instance.mModel;
+
+ if (instance.mTransform != mat)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ if (render_mesh)
+ {
+ for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i)
+ {
+ LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
+
+ buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+ if (physics)
+ {
+ if (physics_idx > -1 && model == mModel[mPreviewLOD][physics_idx])
+ {
+ glColor4f(1,0,0,1);
+ }
+ else
+ {
+ glColor4f(0.75f, 0.75f, 0.75f, 1.f);
+ }
+ }
+ else
+ {
+ glColor4fv(instance.mMaterial[i].mDiffuseColor.mV);
+ if (i < instance.mMaterial.size() && instance.mMaterial[i].mDiffuseMap.notNull())
+ {
+ gGL.getTexUnit(0)->bind(instance.mMaterial[i].mDiffuseMap, true);
+ if (instance.mMaterial[i].mDiffuseMap->getDiscardLevel() > -1)
+ {
+ mTextureSet.insert(instance.mMaterial[i].mDiffuseMap);
+ }
+ }
+ }
+
+ buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ glColor3f(0.4f, 0.4f, 0.4f);
+
+ if (mFMP->childGetValue("show edges").asBoolean())
+ {
+ glLineWidth(3.f);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glLineWidth(1.f);
+ }
+ }
+ }
+
+ if (render_hull)
+ {
+ LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
+ if (decomp)
+ {
+ LLMutexLock(decomp->mMutex);
+
+ std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > >::iterator iter =
+ mPhysicsMesh.find(model);
+ if (iter != mPhysicsMesh.end())
+ {
+ for (U32 i = 0; i < iter->second.size(); ++i)
+ {
+ if (explode > 0.f)
+ {
+ gGL.pushMatrix();
+
+ LLVector3 offset = model->mHullCenter[i]-model->mPhysicsCenter;
+ offset *= explode;
+
+ gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
+ }
+
+ static std::vector<LLColor4U> hull_colors;
+
+ if (i+1 >= hull_colors.size())
+ {
+ hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 255));
+ }
+
+ LLVertexBuffer* buff = iter->second[i];
+ if (buff)
+ {
+ buff->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL);
+
+ glColor4ubv(hull_colors[i].mV);
+ buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts());
+
+ if (mFMP->childGetValue("show edges").asBoolean())
+ {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ glLineWidth(3.f);
+ glColor4ub(hull_colors[i].mV[0]/2, hull_colors[i].mV[1]/2, hull_colors[i].mV[2]/2, 255);
+ buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts());
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glLineWidth(1.f);
+ }
+ }
+
+ if (explode > 0.f)
+ {
+ gGL.popMatrix();
+ }
+ }
+ }
+ }
+
+ //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[HULLS]", llformat("%d",decomp->mHulls.size()));
+ //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[POINTS]", llformat("%d",decomp->mTotalPoints));
+ }
+ }
+
+ gGL.popMatrix();
+ }
+
+ if (physics)
+ {
+ mPreviewLOD = LLModel::LOD_PHYSICS;
+ }
+ }
+ else
+ {
+ LLVOAvatarSelf* avatar = gAgentAvatarp;
+ target_pos = avatar->getPositionAgent();
+
+ LLViewerCamera::getInstance()->setOriginAndLookAt(
+ target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera
+ LLVector3::z_axis, // up
+ target_pos); // point of interest
+
+ avatar->renderCollisionVolumes();
+
+ for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
+ {
+ for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
+ {
+ LLModelInstance& instance = *model_iter;
+ LLModel* model = instance.mModel;
+
+ if (!model->mSkinWeights.empty())
+ {
+ for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i)
+ {
+ LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
+
+ const LLVolumeFace& face = model->getVolumeFace(i);
+
+ LLStrider<LLVector3> position;
+ buffer->getVertexStrider(position);
+
+ LLStrider<LLVector4> weight;
+ buffer->getWeight4Strider(weight);
+
+ //quick 'n dirty software vertex skinning
+
+ //build matrix palette
+ LLMatrix4 mat[64];
+ for (U32 j = 0; j < model->mJointList.size(); ++j)
+ {
+ LLJoint* joint = avatar->getJoint(model->mJointList[j]);
+ if (joint)
+ {
+ mat[j] = model->mInvBindMatrix[j];
+ mat[j] *= joint->getWorldMatrix();
+ }
+ }
+
+ for (U32 j = 0; j < buffer->getRequestedVerts(); ++j)
+ {
+ LLMatrix4 final_mat;
+ final_mat.mMatrix[0][0] = final_mat.mMatrix[1][1] = final_mat.mMatrix[2][2] = final_mat.mMatrix[3][3] = 0.f;
+
+ LLVector4 wght;
+ S32 idx[4];
+
+ F32 scale = 0.f;
+ for (U32 k = 0; k < 4; k++)
+ {
+ F32 w = weight[j].mV[k];
+
+ idx[k] = (S32) floorf(w);
+ wght.mV[k] = w - floorf(w);
+ scale += wght.mV[k];
+ }
+
+ wght *= 1.f/scale;
+
+ for (U32 k = 0; k < 4; k++)
+ {
+ F32* src = (F32*) mat[idx[k]].mMatrix;
+ F32* dst = (F32*) final_mat.mMatrix;
+
+ F32 w = wght.mV[k];
+
+ for (U32 l = 0; l < 16; l++)
+ {
+ dst[l] += src[l]*w;
+ }
+ }
+
+ //VECTORIZE THIS
+ LLVector3 v(face.mPositions[j].getF32ptr());
+
+ v = v * model->mBindShapeMatrix;
+ v = v * final_mat;
+
+ position[j] = v;
+ }
+
+ buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+ glColor4fv(instance.mMaterial[i].mDiffuseColor.mV);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
+ glColor3f(0.4f, 0.4f, 0.4f);
+
+ if (mFMP->childGetValue("show edges").asBoolean())
+ {
+ glLineWidth(3.f);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glLineWidth(1.f);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ gGL.popMatrix();
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// refresh()
+//-----------------------------------------------------------------------------
+void LLModelPreview::refresh()
+{
+ mNeedsUpdate = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians)
+{
+ mCameraYaw = mCameraYaw + yaw_radians;
+
+ mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f);
+}
+
+//-----------------------------------------------------------------------------
+// zoom()
+//-----------------------------------------------------------------------------
+void LLModelPreview::zoom(F32 zoom_amt)
+{
+ F32 new_zoom = mCameraZoom+zoom_amt;
+
+ mCameraZoom = llclamp(new_zoom, 1.f, 10.f);
+}
+
+void LLModelPreview::pan(F32 right, F32 up)
+{
+ mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f);
+ mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f);
+}
+
+void LLModelPreview::setPreviewLOD(S32 lod)
+{
+ mPreviewLOD = llclamp(lod, 0, 4);
+ refresh();
+}
+
+//static
+void LLFloaterModelPreview::onBrowseHighLOD(void* data)
+{
+ LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data;
+ mp->loadModel(3);
+}
+
+//static
+void LLFloaterModelPreview::onBrowseMediumLOD(void* data)
+{
+ LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data;
+ mp->loadModel(2);
+}
+
+//static
+void LLFloaterModelPreview::onBrowseLowLOD(void* data)
+{
+ LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data;
+ mp->loadModel(1);
+}
+
+//static
+void LLFloaterModelPreview::onBrowseLowestLOD(void* data)
+{
+ LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data;
+ mp->loadModel(0);
+}
+
+//static
+void LLFloaterModelPreview::onUpload(void* user_data)
+{
+ LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
+
+ if (mp->mDecompFloater)
+ {
+ mp->mDecompFloater->closeFloater();
+ }
+
+ mp->mModelPreview->rebuildUploadData();
+
+ gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale, mp->childGetValue("upload_textures").asBoolean());
+
+ mp->closeFloater(false);
+}
+
+//static
+void LLFloaterModelPreview::onConsolidate(void* user_data)
+{
+ LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
+ mp->mModelPreview->consolidate();
+}
+
+//static
+void LLFloaterModelPreview::onScrubMaterials(void* user_data)
+{
+ LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
+ mp->mModelPreview->scrubMaterials();
+}
+
+//static
+void LLFloaterModelPreview::onDecompose(void* user_data)
+{
+ LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
+ mp->showDecompFloater();
+}
+
+//static
+void LLFloaterModelPreview::onModelDecompositionComplete(LLModel* model, std::vector<LLPointer<LLVertexBuffer> >& physics_mesh)
+{
+ if (sInstance && sInstance->mModelPreview)
+ {
+ sInstance->mModelPreview->mPhysicsMesh[model] = physics_mesh;
+
+ sInstance->mModelPreview->mResourceCost = sInstance->mModelPreview->calcResourceCost();
+ }
+}
+
+
+//static
+void LLFloaterModelPreview::refresh(LLUICtrl* ctrl, void* user_data)
+{
+ LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data;
+ mp->mModelPreview->refresh();
+}
+
+void LLFloaterModelPreview::updateResourceCost()
+{
+ U32 cost = mModelPreview->mResourceCost;
+ childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",cost));
+}
+
+//static
+void LLModelPreview::textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata )
+{
+ LLModelPreview* preview = (LLModelPreview*) userdata;
+ preview->refresh();
+}
+
+#endif
+
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
new file mode 100644
index 0000000000..1f9de2e2b9
--- /dev/null
+++ b/indra/newview/llfloatermodelpreview.h
@@ -0,0 +1,275 @@
+/**
+ * @file llfloatermodelpreview.h
+ * @brief LLFloaterModelPreview class definition
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-2007, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlife.com/developers/opensource/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlife.com/developers/opensource/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERMODELPREVIEW_H
+#define LL_LLFLOATERMODELPREVIEW_H
+
+#include "llfloaternamedesc.h"
+
+#include "lldynamictexture.h"
+#include "llquaternion.h"
+#include "llmeshrepository.h"
+#include "llmodel.h"
+#include "llthread.h"
+
+#if LL_MESH_ENABLED
+class LLComboBox;
+class LLJoint;
+class LLViewerJointMesh;
+class LLVOAvatar;
+class LLTextBox;
+class LLVertexBuffer;
+class LLModelPreview;
+class LLFloaterModelPreview;
+class daeElement;
+class domProfile_COMMON;
+class domInstance_geometry;
+
+class LLPhysicsDecompFloater : public LLFloater
+{
+public:
+
+ LLPhysicsDecompFloater(LLSD& key);
+ ~LLPhysicsDecompFloater();
+};
+
+class LLModelLoader : public LLThread
+{
+public:
+ typedef enum
+ {
+ STARTING = 0,
+ READING_FILE,
+ CREATING_FACES,
+ GENERATING_VERTEX_BUFFERS,
+ GENERATING_LOD,
+ DONE,
+ } eLoadState;
+
+ U32 mState;
+ std::string mFilename;
+ S32 mLod;
+ LLModelPreview* mPreview;
+ LLMatrix4 mTransform;
+ BOOL mFirstTransform;
+ LLVector3 mExtents[2];
+
+ std::map<daeElement*, LLPointer<LLModel> > mModel;
+
+ typedef std::vector<LLPointer<LLModel> > model_list;
+ model_list mModelList;
+
+ typedef std::vector<LLModelInstance> model_instance_list;
+
+ typedef std::map<LLMatrix4, model_instance_list > scene;
+
+ scene mScene;
+
+ LLModelLoader(std::string filename, S32 lod, LLModelPreview* preview);
+
+ virtual void run();
+
+ void processElement(daeElement* element);
+ std::vector<LLImportMaterial> getMaterials(LLModel* model, domInstance_geometry* instance_geo);
+ LLImportMaterial profileToMaterial(domProfile_COMMON* material);
+ std::string getElementLabel(daeElement *element);
+ LLColor4 getDaeColor(daeElement* element);
+
+ //map of avatar joints as named in COLLADA assets to internal joint names
+ std::map<std::string, std::string> mJointMap;
+};
+
+class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
+{
+ public:
+
+ LLModelPreview(S32 width, S32 height, LLFloaterModelPreview* fmp);
+ virtual ~LLModelPreview();
+
+ void setPreviewTarget(F32 distance);
+ void setTexture(U32 name) { mTextureName = name; }
+
+ BOOL render();
+ void genBuffers(S32 lod);
+ void refresh();
+ void rotate(F32 yaw_radians, F32 pitch_radians);
+ void zoom(F32 zoom_amt);
+ void pan(F32 right, F32 up);
+ virtual BOOL needsRender() { return mNeedsUpdate; }
+ void setPreviewLOD(S32 lod);
+ void loadModel(std::string filename, S32 lod);
+ void loadModelCallback(S32 lod);
+ void genLODs(S32 which_lod = -1);
+ void smoothNormals();
+ void consolidate();
+ void scrubMaterials();
+ U32 calcResourceCost();
+ void rebuildUploadData();
+ void clearIncompatible(S32 lod);
+ void updateStatusMessages();
+
+ static void textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata );
+
+ protected:
+ friend class LLFloaterModelPreview;
+ friend class LLPhysicsDecomp;
+
+ LLFloaterModelPreview* mFMP;
+
+ BOOL mNeedsUpdate;
+ U32 mTextureName;
+ F32 mCameraDistance;
+ F32 mCameraYaw;
+ F32 mCameraPitch;
+ F32 mCameraZoom;
+ LLVector3 mCameraOffset;
+ LLVector3 mPreviewTarget;
+ LLVector3 mPreviewScale;
+ S32 mPreviewLOD;
+ U32 mResourceCost;
+ S32 mLODMode[LLModel::NUM_LODS];
+ S32 mLimit[LLModel::NUM_LODS];
+
+ LLModelLoader* mModelLoader;
+
+
+ LLModelLoader::scene mScene[LLModel::NUM_LODS];
+ LLModelLoader::scene mBaseScene;
+
+ LLModelLoader::model_list mModel[LLModel::NUM_LODS];
+ LLModelLoader::model_list mBaseModel;
+
+ std::map<LLModel*, U32> mGroup;
+ std::map<LLModel*, U32> mObject;
+ std::map<LLModel*, std::vector<U32> > mPatch;
+
+ std::map<LLModel*, F32> mPercentage;
+ std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mPhysicsMesh;
+
+ LLMeshUploadThread::instance_list mUploadData;
+ std::set<LLPointer<LLViewerFetchedTexture> > mTextureSet;
+
+ //map of vertex buffers to models (one vertex buffer in vector per face in model
+ std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mVertexBuffer[6];
+};
+
+class LLFloaterModelPreview : public LLFloater
+{
+public:
+ static LLFloaterModelPreview* sInstance;
+
+ LLFloaterModelPreview(const LLSD& key);
+ virtual ~LLFloaterModelPreview();
+
+ virtual BOOL postBuild();
+
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ BOOL handleHover(S32 x, S32 y, MASK mask);
+ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+
+ static void onMouseCaptureLostModelPreview(LLMouseHandler*);
+ static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+
+ static void onBrowseHighLOD(void* data);
+ static void onBrowseMediumLOD(void* data);
+ static void onBrowseLowLOD(void* data);
+ static void onBrowseLowestLOD(void* data);
+
+ static void onUpload(void* data);
+
+ static void onConsolidate(void* data);
+ static void onScrubMaterials(void* data);
+ static void onDecompose(void* data);
+ static void onModelDecompositionComplete(LLModel* model, std::vector<LLPointer<LLVertexBuffer> >& physics_mesh);
+
+ static void refresh(LLUICtrl* ctrl, void* data);
+
+ void updateResourceCost();
+
+ void loadModel(S32 lod);
+
+protected:
+ friend class LLModelPreview;
+ friend class LLMeshFilePicker;
+ friend class LLPhysicsDecomp;
+ friend class LLPhysicsDecompFloater;
+
+ static void onPreviewLODCommit(LLUICtrl*,void*);
+
+ static void onHighLODCommit(LLUICtrl*,void*);
+ static void onMediumLODCommit(LLUICtrl*,void*);
+ static void onLowLODCommit(LLUICtrl*,void*);
+ static void onLowestLODCommit(LLUICtrl*,void*);
+ static void onPhysicsLODCommit(LLUICtrl*,void*);
+
+ static void onHighLimitCommit(LLUICtrl*,void*);
+ static void onMediumLimitCommit(LLUICtrl*,void*);
+ static void onLowLimitCommit(LLUICtrl*,void*);
+ static void onLowestLimitCommit(LLUICtrl*,void*);
+ static void onPhysicsLimitCommit(LLUICtrl*,void*);
+
+ static void onSmoothNormalsCommit(LLUICtrl*,void*);
+
+ static void onAutoFillCommit(LLUICtrl*,void*);
+ static void onShowEdgesCommit(LLUICtrl*,void*);
+
+ static void onExplodeCommit(LLUICtrl*, void*);
+
+ static void onPhysicsParamCommit(LLUICtrl* ctrl, void* userdata);
+ static void onPhysicsStageExecute(LLUICtrl* ctrl, void* userdata);
+ static void onPhysicsStageCancel(LLUICtrl* ctrl, void* userdata);
+ static void onClosePhysicsFloater(LLUICtrl* ctrl, void* userdata);
+
+ void draw();
+ static void setLODMode(S32 lod, void* userdata);
+ void setLODMode(S32 lod, S32 which_mode);
+
+ static void setLimit(S32 lod, void* userdata);
+ void setLimit(S32 lod, S32 limit);
+
+ void showDecompFloater();
+
+ LLModelPreview* mModelPreview;
+
+ LLFloater* mDecompFloater;
+
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ LLRect mPreviewRect;
+ U32 mGLName;
+ BOOL mLoading;
+ static S32 sUploadAmount;
+};
+
+#endif
+
+#endif // LL_LLFLOATERMODELPREVIEW_H
diff --git a/indra/newview/llmeshreduction.cpp b/indra/newview/llmeshreduction.cpp
new file mode 100644
index 0000000000..e785784a32
--- /dev/null
+++ b/indra/newview/llmeshreduction.cpp
@@ -0,0 +1,291 @@
+/**
+ * @file llmeshreduction.cpp
+ * @brief LLMeshReduction class implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlife.com/developers/opensource/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlife.com/developers/opensource/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmeshreduction.h"
+#include "llgl.h"
+#include "llvertexbuffer.h"
+
+#include "glod/glod.h"
+
+#if LL_MESH_ENABLED
+
+static BOOL stop_gloderror()
+{
+ GLuint error = glodGetError();
+
+ if (error != GLOD_NO_ERROR)
+ {
+ llwarns << "GLOD error detected: " << std::hex << error << llendl;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void create_vertex_buffers_from_model(LLModel* model, std::vector<LLPointer <LLVertexBuffer> >& vertex_buffers)
+{
+#if 0 //VECTORIZE THIS ?
+ vertex_buffers.clear();
+
+ for (S32 i = 0; i < model->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace &vf = model->getVolumeFace(i);
+ U32 num_vertices = vf.mNumVertices;
+ U32 num_indices = vf.mNumIndices;
+
+ if (!num_vertices || ! num_indices)
+ {
+ continue;
+ }
+
+ LLVertexBuffer* vb =
+ new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0, 0);
+
+ vb->allocateBuffer(num_vertices, num_indices, TRUE);
+
+ LLStrider<LLVector3> vertex_strider;
+ LLStrider<LLVector3> normal_strider;
+ LLStrider<LLVector2> tc_strider;
+ LLStrider<U16> index_strider;
+
+ vb->getVertexStrider(vertex_strider);
+ vb->getNormalStrider(normal_strider);
+ vb->getTexCoord0Strider(tc_strider);
+
+ vb->getIndexStrider(index_strider);
+
+ // build vertices and normals
+ for (U32 i = 0; (S32)i < num_vertices; i++)
+ {
+ *(vertex_strider++) = vf.mVertices[i].mPosition;
+ *(tc_strider++) = vf.mVertices[i].mTexCoord;
+ LLVector3 normal = vf.mVertices[i].mNormal;
+ normal.normalize();
+ *(normal_strider++) = normal;
+ }
+
+ // build indices
+ for (U32 i = 0; i < num_indices; i++)
+ {
+ *(index_strider++) = vf.mIndices[i];
+ }
+
+
+ vertex_buffers.push_back(vb);
+ }
+#endif
+}
+
+void create_glod_object_from_vertex_buffers(S32 object, S32 group, std::vector<LLPointer <LLVertexBuffer> >& vertex_buffers)
+{
+ glodNewGroup(group);
+ stop_gloderror();
+ glodNewObject(object, group, GLOD_DISCRETE);
+ stop_gloderror();
+
+ for (U32 i = 0; i < vertex_buffers.size(); ++i)
+ {
+ vertex_buffers[i]->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+
+ U32 num_indices = vertex_buffers[i]->getNumIndices();
+
+ if (num_indices > 2)
+ {
+ glodInsertElements(object, i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT,
+ vertex_buffers[i]->getIndicesPointer(), 0, 0.f);
+ }
+ stop_gloderror();
+ }
+
+ glodBuildObject(object);
+ stop_gloderror();
+}
+
+// extract the GLOD data into vertex buffers
+void create_vertex_buffers_from_glod_object(S32 object, S32 group, std::vector<LLPointer <LLVertexBuffer> >& vertex_buffers)
+{
+ vertex_buffers.clear();
+
+ GLint patch_count = 0;
+ glodGetObjectParameteriv(object, GLOD_NUM_PATCHES, &patch_count);
+ stop_gloderror();
+
+ GLint* sizes = new GLint[patch_count*2];
+ glodGetObjectParameteriv(object, GLOD_PATCH_SIZES, sizes);
+ stop_gloderror();
+
+ GLint* names = new GLint[patch_count];
+ glodGetObjectParameteriv(object, GLOD_PATCH_NAMES, names);
+ stop_gloderror();
+
+ for (S32 i = 0; i < patch_count; i++)
+ {
+ LLPointer<LLVertexBuffer> buff =
+ new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0, 0);
+
+ if (sizes[i*2+1] > 0 && sizes[i*2] > 0)
+ {
+ buff->allocateBuffer(sizes[i*2+1], sizes[i*2], true);
+ buff->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+ glodFillElements(object, names[i], GL_UNSIGNED_SHORT, buff->getIndicesPointer());
+ stop_gloderror();
+ }
+ else
+ {
+ // this face was eliminated, create a dummy triangle (one vertex, 3 indices, all 0)
+ buff->allocateBuffer(1, 3, true);
+ }
+
+ vertex_buffers.push_back(buff);
+ }
+
+ glodDeleteObject(object);
+ stop_gloderror();
+ glodDeleteGroup(group);
+ stop_gloderror();
+
+ delete [] sizes;
+ delete [] names;
+}
+
+
+LLPointer<LLModel> create_model_from_vertex_buffers(std::vector<LLPointer <LLVertexBuffer> >& vertex_buffers)
+{
+ // extract the newly reduced mesh
+
+ // create our output model
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+ LLPointer<LLModel> out_model = new LLModel(volume_params, 0.f);
+
+ out_model->setNumVolumeFaces(vertex_buffers.size());
+
+ // build new faces from each vertex buffer
+ for (GLint i = 0; i < vertex_buffers.size(); ++i)
+ {
+ LLStrider<LLVector3> pos;
+ LLStrider<LLVector3> norm;
+ LLStrider<LLVector2> tc;
+ LLStrider<U16> index;
+
+ vertex_buffers[i]->getVertexStrider(pos);
+ vertex_buffers[i]->getNormalStrider(norm);
+ vertex_buffers[i]->getTexCoord0Strider(tc);
+ vertex_buffers[i]->getIndexStrider(index);
+
+ out_model->setVolumeFaceData(i, pos, norm, tc, index,
+ vertex_buffers[i]->getNumVerts(), vertex_buffers[i]->getNumIndices());
+ }
+
+ return out_model;
+}
+
+
+
+LLMeshReduction::LLMeshReduction()
+{
+ mCounter = 1;
+
+ glodInit();
+}
+
+LLMeshReduction::~LLMeshReduction()
+{
+ glodShutdown();
+}
+
+
+LLPointer<LLModel> LLMeshReduction::reduce(LLModel* in_model, F32 limit, S32 mode)
+{
+ LLVertexBuffer::unbind();
+
+ // create vertex buffers from model
+ std::vector<LLPointer<LLVertexBuffer> > in_vertex_buffers;
+ create_vertex_buffers_from_model(in_model, in_vertex_buffers);
+
+ // create glod object from vertex buffers
+ stop_gloderror();
+ S32 glod_group = mCounter++;
+ S32 glod_object = mCounter++;
+ create_glod_object_from_vertex_buffers(glod_object, glod_group, in_vertex_buffers);
+
+
+ // set reduction parameters
+ stop_gloderror();
+
+ if (mode == TRIANGLE_BUDGET)
+ {
+ // triangle budget mode
+ glodGroupParameteri(glod_group, GLOD_ADAPT_MODE, GLOD_TRIANGLE_BUDGET);
+ stop_gloderror();
+ glodGroupParameteri(glod_group, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR);
+ stop_gloderror();
+ S32 triangle_count = (S32)limit;
+ glodGroupParameteri(glod_group, GLOD_MAX_TRIANGLES, triangle_count);
+ stop_gloderror();
+ }
+ else if (mode == ERROR_THRESHOLD)
+ {
+ // error threshold mode
+ glodGroupParameteri(glod_group, GLOD_ADAPT_MODE, GLOD_ERROR_THRESHOLD);
+ glodGroupParameteri(glod_group, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR);
+ F32 error_threshold = limit;
+ glodGroupParameterf(glod_group, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, error_threshold);
+ stop_gloderror();
+ }
+
+ else
+ {
+ // not a legal mode
+ return NULL;
+ }
+
+
+ // do the reduction
+ glodAdaptGroup(glod_group);
+ stop_gloderror();
+
+ // convert glod object into vertex buffers
+ std::vector<LLPointer<LLVertexBuffer> > out_vertex_buffers;
+ create_vertex_buffers_from_glod_object(glod_object, glod_group, out_vertex_buffers);
+
+ // convert vertex buffers into a model
+ LLPointer<LLModel> out_model = create_model_from_vertex_buffers(out_vertex_buffers);
+
+
+ return out_model;
+}
+
+#endif
+
diff --git a/indra/newview/llmeshreduction.h b/indra/newview/llmeshreduction.h
new file mode 100644
index 0000000000..d86696978d
--- /dev/null
+++ b/indra/newview/llmeshreduction.h
@@ -0,0 +1,59 @@
+/**
+ * @file llmeshreduction.h
+ * @brief LLMeshReduction class definition
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlife.com/developers/opensource/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlife.com/developers/opensource/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMESHREDUCTION_H
+#define LL_LLMESHREDUCTION_H
+
+#include "llmodel.h"
+
+#if LL_MESH_ENABLED
+
+class LLMeshReduction
+{
+ public:
+ enum EReductionMode
+ {
+ TRIANGLE_BUDGET,
+ ERROR_THRESHOLD
+ };
+
+ LLMeshReduction();
+ ~LLMeshReduction();
+
+ LLPointer<LLModel> reduce(LLModel* in_model, F32 limit, S32 mode);
+
+private:
+ U32 mCounter;
+};
+
+#endif
+
+#endif // LL_LLMESHREDUCTION_H
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
new file mode 100644
index 0000000000..96a170ef07
--- /dev/null
+++ b/indra/newview/llmeshrepository.cpp
@@ -0,0 +1,2676 @@
+/**
+ * @file llmeshrepository.cpp
+ * @brief Mesh repository implementation.
+ *
+ * $LicenseInfo:firstyear=2005&license=viewergpl$
+ *
+ * Copyright (c) 2005-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "apr_pools.h"
+#include "apr_dso.h"
+
+#include "llmeshrepository.h"
+
+#include "llagent.h"
+#include "llappviewer.h"
+#include "llbufferstream.h"
+#include "llcurl.h"
+#include "llfasttimer.h"
+#include "llfloatermodelpreview.h"
+#include "llfloaterperms.h"
+#include "lleconomy.h"
+#include "llimagej2c.h"
+#include "llhost.h"
+#include "llnotificationsutil.h"
+#include "llsd.h"
+#include "llsdutil_math.h"
+#include "llsdserialize.h"
+#include "llthread.h"
+#include "llvfile.h"
+#include "llviewercontrol.h"
+#include "llviewermenufile.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewertexturelist.h"
+#include "llvolume.h"
+#include "llvolumemgr.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "material_codes.h"
+#include "pipeline.h"
+
+
+#include <queue>
+
+#if LL_MESH_ENABLED
+
+LLFastTimer::DeclareTimer FTM_MESH_UPDATE("Mesh Update");
+LLFastTimer::DeclareTimer FTM_LOAD_MESH("Load Mesh");
+
+LLMeshRepository gMeshRepo;
+
+const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
+
+U32 LLMeshRepository::sBytesReceived = 0;
+U32 LLMeshRepository::sHTTPRequestCount = 0;
+U32 LLMeshRepository::sHTTPRetryCount = 0;
+U32 LLMeshRepository::sCacheBytesRead = 0;
+U32 LLMeshRepository::sCacheBytesWritten = 0;
+U32 LLMeshRepository::sPeakKbps = 0;
+
+
+std::string header_lod[] =
+{
+ "lowest_lod",
+ "low_lod",
+ "medium_lod",
+ "high_lod"
+};
+
+
+//get the number of bytes resident in memory for given volume
+U32 get_volume_memory_size(const LLVolume* volume)
+{
+ U32 indices = 0;
+ U32 vertices = 0;
+
+ for (U32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = volume->getVolumeFace(i);
+ indices += face.mNumIndices;
+ vertices += face.mNumVertices;
+ }
+
+
+ return indices*2+vertices*11+sizeof(LLVolume)+sizeof(LLVolumeFace)*volume->getNumVolumeFaces();
+}
+
+std::string scrub_host_name(std::string http_url, const LLHost& host)
+{ //curl loves to abuse the DNS cache, so scrub host names out of urls where trivial to prevent DNS timeouts
+ std::string ip_string = host.getIPString();
+ std::string host_string = host.getHostName();
+
+ std::string::size_type idx = http_url.find(host_string);
+
+ if (!ip_string.empty() && !host_string.empty() && idx != std::string::npos)
+ {
+ http_url.replace(idx, host_string.length(), ip_string);
+ }
+
+ return http_url;
+}
+
+LLVertexBuffer* get_vertex_buffer_from_mesh(LLCDMeshData& mesh, F32 scale = 1.f)
+{
+ LLVertexBuffer* buff = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL, 0);
+ buff->allocateBuffer(mesh.mNumTriangles*3, 0, true);
+
+ LLStrider<LLVector3> pos;
+ LLStrider<LLVector3> norm;
+
+ buff->getVertexStrider(pos);
+ buff->getNormalStrider(norm);
+
+ const F32* v = mesh.mVertexBase;
+
+ if (mesh.mIndexType == LLCDMeshData::INT_16)
+ {
+ U16* idx = (U16*) mesh.mIndexBase;
+ for (S32 j = 0; j < mesh.mNumTriangles; ++j)
+ {
+ F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes);
+ F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes);
+ F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes);
+
+ idx = (U16*) (((U8*)idx)+mesh.mIndexStrideBytes);
+
+ LLVector3 v0(mp0);
+ LLVector3 v1(mp1);
+ LLVector3 v2(mp2);
+
+ LLVector3 n = (v1-v0)%(v2-v0);
+ n.normalize();
+
+ *pos++ = v0*scale;
+ *pos++ = v1*scale;
+ *pos++ = v2*scale;
+
+ *norm++ = n;
+ *norm++ = n;
+ *norm++ = n;
+ }
+ }
+ else
+ {
+ U32* idx = (U32*) mesh.mIndexBase;
+ for (S32 j = 0; j < mesh.mNumTriangles; ++j)
+ {
+ F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes);
+ F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes);
+ F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes);
+
+ idx = (U32*) (((U8*)idx)+mesh.mIndexStrideBytes);
+
+ LLVector3 v0(mp0);
+ LLVector3 v1(mp1);
+ LLVector3 v2(mp2);
+
+ LLVector3 n = (v1-v0)%(v2-v0);
+ n.normalize();
+
+ *(pos++) = v0*scale;
+ *(pos++) = v1*scale;
+ *(pos++) = v2*scale;
+
+ *(norm++) = n;
+ *(norm++) = n;
+ *(norm++) = n;
+ }
+ }
+
+ return buff;
+}
+
+S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
+S32 LLMeshRepoThread::sActiveLODRequests = 0;
+U32 LLMeshRepoThread::sMaxConcurrentRequests = 1;
+
+
+class LLTextureCostResponder : public LLCurl::Responder
+{
+public:
+ LLTextureUploadData mData;
+ LLMeshUploadThread* mThread;
+
+ LLTextureCostResponder(LLTextureUploadData data, LLMeshUploadThread* thread)
+ : mData(data), mThread(thread)
+ {
+
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ mThread->mPendingConfirmations--;
+ if (isGoodStatus(status))
+ {
+ mThread->priceResult(mData, content);
+ }
+ else
+ {
+ llwarns << status << ": " << reason << llendl;
+ llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+ if (status == 499)
+ {
+ mThread->uploadTexture(mData);
+ }
+ else
+ {
+ llerrs << "Unhandled status " << status << llendl;
+ }
+ }
+ }
+};
+
+class LLTextureUploadResponder : public LLCurl::Responder
+{
+public:
+ LLTextureUploadData mData;
+ LLMeshUploadThread* mThread;
+
+ LLTextureUploadResponder(LLTextureUploadData data, LLMeshUploadThread* thread)
+ : mData(data), mThread(thread)
+ {
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ mThread->mPendingUploads--;
+ if (isGoodStatus(status))
+ {
+ mData.mUUID = content["new_asset"].asUUID();
+ gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content));
+ mThread->onTextureUploaded(mData);
+ }
+ else
+ {
+ llwarns << status << ": " << reason << llendl;
+ llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+ if (status == 404)
+ {
+ mThread->uploadTexture(mData);
+ }
+ else if (status == 499)
+ {
+ mThread->mConfirmedTextureQ.push(mData);
+ }
+ else
+ {
+ llerrs << "Unhandled status " << status << llendl;
+ }
+ }
+ }
+};
+
+class LLMeshCostResponder : public LLCurl::Responder
+{
+public:
+ LLMeshUploadData mData;
+ LLMeshUploadThread* mThread;
+
+ LLMeshCostResponder(LLMeshUploadData data, LLMeshUploadThread* thread)
+ : mData(data), mThread(thread)
+ {
+
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ mThread->mPendingConfirmations--;
+
+ if (isGoodStatus(status))
+ {
+ mThread->priceResult(mData, content);
+ }
+ else
+ {
+ llwarns << status << ": " << reason << llendl;
+ llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+ if (status == 499)
+ {
+ mThread->uploadModel(mData);
+ }
+ else if (status == 400)
+ {
+ llwarns << "Status 400 received from server, giving up." << llendl;
+ }
+ else
+ {
+ llerrs << "Unhandled status " << status << llendl;
+ }
+ }
+ }
+};
+
+class LLMeshUploadResponder : public LLCurl::Responder
+{
+public:
+ LLMeshUploadData mData;
+ LLMeshUploadThread* mThread;
+
+ LLMeshUploadResponder(LLMeshUploadData data, LLMeshUploadThread* thread)
+ : mData(data), mThread(thread)
+ {
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ mThread->mPendingUploads--;
+ if (isGoodStatus(status))
+ {
+ mData.mUUID = content["new_asset"].asUUID();
+ if (mData.mUUID.isNull())
+ {
+ LLSD args;
+ std::string message = content["error"]["message"];
+ std::string identifier = content["error"]["identifier"];
+ std::string invalidity_identifier = content["error"]["invalidity_identifier"];
+
+ args["MESSAGE"] = message;
+ args["IDENTIFIER"] = identifier;
+ args["INVALIDITY_IDENTIFIER"] = invalidity_identifier;
+ args["LABEL"] = mData.mBaseModel->mLabel;
+
+ gMeshRepo.uploadError(args);
+ }
+ else
+ {
+ gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content));
+ mThread->onModelUploaded(mData);
+ }
+ }
+ else
+ {
+ llwarns << status << ": " << reason << llendl;
+ llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+ if (status == 404)
+ {
+ mThread->uploadModel(mData);
+ }
+ else if (status == 499)
+ {
+ mThread->mConfirmedQ.push(mData);
+ }
+ else if (status != 500)
+ { //drop internal server errors on the floor, otherwise grab
+ llerrs << "Unhandled status " << status << llendl;
+ }
+ }
+ }
+};
+
+
+class LLMeshHeaderResponder : public LLCurl::Responder
+{
+public:
+ LLVolumeParams mMeshParams;
+
+ LLMeshHeaderResponder(const LLVolumeParams& mesh_params)
+ : mMeshParams(mesh_params)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshLODResponder : public LLCurl::Responder
+{
+public:
+ LLVolumeParams mMeshParams;
+ S32 mLOD;
+ U32 mRequestedBytes;
+ U32 mOffset;
+
+ LLMeshLODResponder(const LLVolumeParams& mesh_params, S32 lod, U32 offset, U32 requested_bytes)
+ : mMeshParams(mesh_params), mLOD(lod), mOffset(offset), mRequestedBytes(requested_bytes)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshSkinInfoResponder : public LLCurl::Responder
+{
+public:
+ LLUUID mMeshID;
+ U32 mRequestedBytes;
+ U32 mOffset;
+
+ LLMeshSkinInfoResponder(const LLUUID& id, U32 offset, U32 size)
+ : mMeshID(id), mRequestedBytes(size), mOffset(offset)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshDecompositionResponder : public LLCurl::Responder
+{
+public:
+ LLUUID mMeshID;
+ U32 mRequestedBytes;
+ U32 mOffset;
+
+ LLMeshDecompositionResponder(const LLUUID& id, U32 offset, U32 size)
+ : mMeshID(id), mRequestedBytes(size), mOffset(offset)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+
+LLMeshRepoThread::LLMeshRepoThread()
+: LLThread("mesh repo", NULL)
+{
+ mWaiting = false;
+ mMutex = new LLMutex(NULL);
+ mHeaderMutex = new LLMutex(NULL);
+ mSignal = new LLCondition(NULL);
+}
+
+LLMeshRepoThread::~LLMeshRepoThread()
+{
+
+}
+
+void LLMeshRepoThread::run()
+{
+ mCurlRequest = new LLCurlRequest();
+ LLCDResult res = LLConvexDecomposition::initThread();
+ if (res != LLCD_OK)
+ {
+ llwarns << "convex decomposition unable to be loaded" << llendl;
+ }
+
+ while (!LLApp::isQuitting())
+ {
+ mWaiting = true;
+ mSignal->wait();
+ mWaiting = false;
+
+ if (!LLApp::isQuitting())
+ {
+ static U32 count = 0;
+
+ static F32 last_hundred = gFrameTimeSeconds;
+
+ if (gFrameTimeSeconds - last_hundred > 1.f)
+ { //a second has gone by, clear count
+ last_hundred = gFrameTimeSeconds;
+ count = 0;
+ }
+
+ // NOTE: throttling intentionally favors LOD requests over header requests
+
+ while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests)
+ {
+ {
+ LLMutexLock lock(mMutex);
+ LODRequest req = mLODReqQ.front();
+ mLODReqQ.pop();
+ if (fetchMeshLOD(req.mMeshParams, req.mLOD))
+ {
+ count++;
+ }
+ }
+ }
+
+ while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests)
+ {
+ {
+ LLMutexLock lock(mMutex);
+ HeaderRequest req = mHeaderReqQ.front();
+ mHeaderReqQ.pop();
+ if (fetchMeshHeader(req.mMeshParams))
+ {
+ count++;
+ }
+ }
+ }
+
+ {
+ std::set<LLUUID> incomplete;
+ for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
+ {
+ LLUUID mesh_id = *iter;
+ if (!fetchMeshSkinInfo(mesh_id))
+ {
+ incomplete.insert(mesh_id);
+ }
+ }
+ mSkinRequests = incomplete;
+ }
+
+ {
+ std::set<LLUUID> incomplete;
+ for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
+ {
+ LLUUID mesh_id = *iter;
+ if (!fetchMeshDecomposition(mesh_id))
+ {
+ incomplete.insert(mesh_id);
+ }
+ }
+ mDecompositionRequests = incomplete;
+ }
+
+
+ }
+
+ mCurlRequest->process();
+ }
+
+ res = LLConvexDecomposition::quitThread();
+ if (res != LLCD_OK)
+ {
+ llwarns << "convex decomposition unable to be quit" << llendl;
+ }
+
+ delete mCurlRequest;
+ delete mMutex;
+}
+
+void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
+{ //protected by mSignal, no locking needed here
+ mSkinRequests.insert(mesh_id);
+}
+
+void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id)
+{ //protected by mSignal, no locking needed here
+ mDecompositionRequests.insert(mesh_id);
+}
+
+void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{ //protected by mSignal, no locking needed here
+
+ mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID());
+ if (iter != mMeshHeader.end())
+ { //if we have the header, request LOD byte range
+ LODRequest req(mesh_params, lod);
+ mLODReqQ.push(req);
+ }
+ else
+ {
+ HeaderRequest req(mesh_params);
+
+ pending_lod_map::iterator pending = mPendingLOD.find(mesh_params);
+
+ if (pending != mPendingLOD.end())
+ { //append this lod request to existing header request
+ pending->second.push_back(lod);
+ if (pending->second.size() > 4)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+ }
+ else
+ { //if no header request is pending, fetch header
+ mHeaderReqQ.push(req);
+ mPendingLOD[mesh_params].push_back(lod);
+ }
+ }
+}
+
+//static
+std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
+{
+ std::string http_url;
+
+ if (gAgent.getRegion())
+ {
+ http_url = gAgent.getRegion()->getCapability("GetMesh");
+ scrub_host_name(http_url, gAgent.getRegionHost());
+ }
+
+ if (!http_url.empty())
+ {
+ http_url += "/?mesh_id=";
+ http_url += mesh_id.asString().c_str();
+ }
+ else
+ {
+ llwarns << "Current region does not have GetMesh capability! Cannot load " << mesh_id << ".mesh" << llendl;
+ }
+
+ return http_url;
+}
+
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
+{ //protected by mMutex
+ mHeaderMutex->lock();
+
+ if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+ { //we have no header info for this mesh, do nothing
+ mHeaderMutex->unlock();
+ return false;
+ }
+
+ U32 header_size = mMeshHeaderSize[mesh_id];
+
+ if (header_size > 0)
+ {
+ S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger();
+ S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger();
+
+ mHeaderMutex->unlock();
+
+ if (offset >= 0 && size > 0)
+ {
+ //check VFS for mesh skin info
+ LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesRead += size;
+ file.seek(offset);
+ U8* buffer = new U8[size];
+ file.read(buffer, size);
+
+ //make sure buffer isn't all 0's (reserved block but not written)
+ bool zero = true;
+ for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+ {
+ zero = buffer[i] > 0 ? false : true;
+ }
+
+ if (!zero)
+ { //attempt to parse
+ if (skinInfoReceived(mesh_id, buffer, size))
+ {
+ delete[] buffer;
+ return true;
+ }
+ }
+
+ delete[] buffer;
+ }
+
+ //reading from VFS failed for whatever reason, fetch from sim
+ std::vector<std::string> headers;
+ headers.push_back("Accept: application/octet-stream");
+
+ std::string http_url = constructUrl(mesh_id);
+ if (!http_url.empty())
+ {
+ ++sActiveLODRequests;
+ LLMeshRepository::sHTTPRequestCount++;
+ mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
+ new LLMeshSkinInfoResponder(mesh_id, offset, size));
+ }
+ }
+ }
+ else
+ {
+ mHeaderMutex->unlock();
+ }
+
+ //early out was not hit, effectively fetched
+ return true;
+}
+
+bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
+{ //protected by mMutex
+ mHeaderMutex->lock();
+
+ if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+ { //we have no header info for this mesh, do nothing
+ mHeaderMutex->unlock();
+ return false;
+ }
+
+ U32 header_size = mMeshHeaderSize[mesh_id];
+
+ if (header_size > 0)
+ {
+ S32 offset = header_size + mMeshHeader[mesh_id]["decomposition"]["offset"].asInteger();
+ S32 size = mMeshHeader[mesh_id]["decomposition"]["size"].asInteger();
+
+ mHeaderMutex->unlock();
+
+ if (offset >= 0 && size > 0)
+ {
+ //check VFS for mesh skin info
+ LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesRead += size;
+ file.seek(offset);
+ U8* buffer = new U8[size];
+ file.read(buffer, size);
+
+ //make sure buffer isn't all 0's (reserved block but not written)
+ bool zero = true;
+ for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+ {
+ zero = buffer[i] > 0 ? false : true;
+ }
+
+ if (!zero)
+ { //attempt to parse
+ if (decompositionReceived(mesh_id, buffer, size))
+ {
+ delete[] buffer;
+ return true;
+ }
+ }
+
+ delete[] buffer;
+ }
+
+ //reading from VFS failed for whatever reason, fetch from sim
+ std::vector<std::string> headers;
+ headers.push_back("Accept: application/octet-stream");
+
+ std::string http_url = constructUrl(mesh_id);
+ if (!http_url.empty())
+ {
+ ++sActiveLODRequests;
+ LLMeshRepository::sHTTPRequestCount++;
+ mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
+ new LLMeshDecompositionResponder(mesh_id, offset, size));
+ }
+ }
+ }
+ else
+ {
+ mHeaderMutex->unlock();
+ }
+
+ //early out was not hit, effectively fetched
+ return true;
+}
+
+bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
+{
+ bool retval = false;
+
+ {
+ //look for mesh in asset in vfs
+ LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH);
+
+ S32 size = file.getSize();
+
+ if (size > 0)
+ {
+ U8 buffer[1024];
+ S32 bytes = llmin(size, 1024);
+ LLMeshRepository::sCacheBytesRead += bytes;
+ file.read(buffer, bytes);
+ if (headerReceived(mesh_params, buffer, bytes))
+ { //did not do an HTTP request, return false
+ return false;
+ }
+ }
+ }
+
+ //either cache entry doesn't exist or is corrupt, request header from simulator
+
+ std::vector<std::string> headers;
+ headers.push_back("Accept: application/octet-stream");
+
+ std::string http_url = constructUrl(mesh_params.getSculptID());
+ if (!http_url.empty())
+ {
+ ++sActiveHeaderRequests;
+ retval = true;
+ //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits
+ //within the first 4KB
+ LLMeshRepository::sHTTPRequestCount++;
+ mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params));
+ }
+
+ return retval;
+}
+
+bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{ //protected by mMutex
+ mHeaderMutex->lock();
+
+ bool retval = false;
+
+ LLUUID mesh_id = mesh_params.getSculptID();
+
+ U32 header_size = mMeshHeaderSize[mesh_id];
+
+ if (header_size > 0)
+ {
+ S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger();
+ S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger();
+ mHeaderMutex->unlock();
+ if (offset >= 0 && size > 0)
+ {
+
+ //check VFS for mesh asset
+ LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesRead += size;
+ file.seek(offset);
+ U8* buffer = new U8[size];
+ file.read(buffer, size);
+
+ //make sure buffer isn't all 0's (reserved block but not written)
+ bool zero = true;
+ for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+ {
+ zero = buffer[i] > 0 ? false : true;
+ }
+
+ if (!zero)
+ { //attempt to parse
+ if (lodReceived(mesh_params, lod, buffer, size))
+ {
+ delete[] buffer;
+ return false;
+ }
+ }
+
+ delete[] buffer;
+ }
+
+ //reading from VFS failed for whatever reason, fetch from sim
+ std::vector<std::string> headers;
+ headers.push_back("Accept: application/octet-stream");
+
+ std::string http_url = constructUrl(mesh_id);
+ if (!http_url.empty())
+ {
+ ++sActiveLODRequests;
+ retval = true;
+ LLMeshRepository::sHTTPRequestCount++;
+ mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
+ new LLMeshLODResponder(mesh_params, lod, offset, size));
+ }
+ else
+ {
+ mUnavailableQ.push(LODRequest(mesh_params, lod));
+ }
+ }
+ else
+ {
+ mUnavailableQ.push(LODRequest(mesh_params, lod));
+ }
+ }
+ else
+ {
+ mHeaderMutex->unlock();
+ }
+
+ return retval;
+}
+
+bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size)
+{
+ LLSD header;
+
+ U32 header_size = 0;
+ if (data_size > 0)
+ {
+ std::string res_str((char*) data, data_size);
+
+ std::istringstream stream(res_str);
+
+ if (!LLSDSerialize::deserialize(header, stream, data_size))
+ {
+ llwarns << "Mesh header parse error. Not a valid mesh asset!" << llendl;
+ return false;
+ }
+
+ header_size = stream.tellg();
+ }
+ else
+ {
+ header["404"] = 1;
+ }
+
+ {
+ U32 cost = gMeshRepo.calcResourceCost(header);
+
+ LLUUID mesh_id = mesh_params.getSculptID();
+
+ mHeaderMutex->lock();
+ mMeshHeaderSize[mesh_id] = header_size;
+ mMeshHeader[mesh_id] = header;
+ mMeshResourceCost[mesh_id] = cost;
+ mHeaderMutex->unlock();
+
+ //check for pending requests
+ pending_lod_map::iterator iter = mPendingLOD.find(mesh_params);
+ if (iter != mPendingLOD.end())
+ {
+ for (U32 i = 0; i < iter->second.size(); ++i)
+ {
+ LODRequest req(mesh_params, iter->second[i]);
+ mLODReqQ.push(req);
+ }
+ }
+ mPendingLOD.erase(iter);
+ }
+
+ return true;
+}
+
+bool LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size)
+{
+ LLVolume* volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod));
+ std::string mesh_string((char*) data, data_size);
+ std::istringstream stream(mesh_string);
+
+ if (volume->unpackVolumeFaces(stream, data_size))
+ {
+ LoadedMesh mesh(volume, mesh_params, lod);
+ if (volume->getNumFaces() > 0)
+ {
+ LLMutexLock lock(mMutex);
+ mLoadedQ.push(mesh);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+ LLSD skin;
+
+ if (data_size > 0)
+ {
+ std::string res_str((char*) data, data_size);
+
+ std::istringstream stream(res_str);
+
+ if (!unzip_llsd(skin, stream, data_size))
+ {
+ llwarns << "Mesh skin info parse error. Not a valid mesh asset!" << llendl;
+ return false;
+ }
+ }
+
+ {
+ LLMeshSkinInfo info;
+ info.mMeshID = mesh_id;
+
+ if (skin.has("joint_names"))
+ {
+ for (U32 i = 0; i < skin["joint_names"].size(); ++i)
+ {
+ info.mJointNames.push_back(skin["joint_names"][i]);
+ }
+ }
+
+ if (skin.has("inverse_bind_matrix"))
+ {
+ for (U32 i = 0; i < skin["inverse_bind_matrix"].size(); ++i)
+ {
+ LLMatrix4 mat;
+ for (U32 j = 0; j < 4; j++)
+ {
+ for (U32 k = 0; k < 4; k++)
+ {
+ mat.mMatrix[j][k] = skin["inverse_bind_matrix"][i][j*4+k].asReal();
+ }
+ }
+
+ info.mInvBindMatrix.push_back(mat);
+ }
+ }
+
+ if (skin.has("bind_shape_matrix"))
+ {
+ for (U32 j = 0; j < 4; j++)
+ {
+ for (U32 k = 0; k < 4; k++)
+ {
+ info.mBindShapeMatrix.mMatrix[j][k] = skin["bind_shape_matrix"][j*4+k].asReal();
+ }
+ }
+ }
+
+ mSkinInfoQ.push(info);
+ }
+
+ return true;
+}
+
+bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+ LLSD decomp;
+
+ if (data_size > 0)
+ {
+ std::string res_str((char*) data, data_size);
+
+ std::istringstream stream(res_str);
+
+ if (!unzip_llsd(decomp, stream, data_size))
+ {
+ llwarns << "Mesh decomposition parse error. Not a valid mesh asset!" << llendl;
+ return false;
+ }
+ }
+
+ {
+ LLMeshDecomposition* d = new LLMeshDecomposition();
+ d->mMeshID = mesh_id;
+
+ // updated for const-correctness. gcc is picky about this type of thing - Nyx
+ const LLSD::Binary& hulls = decomp["HullList"].asBinary();
+ const LLSD::Binary& position = decomp["Position"].asBinary();
+
+ U16* p = (U16*) &position[0];
+
+ d->mHull.resize(hulls.size());
+
+ LLVector3 min;
+ LLVector3 max;
+ LLVector3 range;
+
+ min.setValue(decomp["Min"]);
+ max.setValue(decomp["Max"]);
+ range = max-min;
+
+ for (U32 i = 0; i < hulls.size(); ++i)
+ {
+ U8 count = hulls[i];
+
+ for (U32 j = 0; j < count; ++j)
+ {
+ d->mHull[i].push_back(LLVector3(
+ (F32) p[0]/65535.f*range.mV[0]+min.mV[0],
+ (F32) p[1]/65535.f*range.mV[1]+min.mV[1],
+ (F32) p[2]/65535.f*range.mV[2]+min.mV[2]));
+ p += 3;
+ }
+
+ }
+
+ //get mesh for decomposition
+ for (U32 i = 0; i < d->mHull.size(); ++i)
+ {
+ LLCDHull hull;
+ hull.mNumVertices = d->mHull[i].size();
+ hull.mVertexBase = d->mHull[i][0].mV;
+ hull.mVertexStrideBytes = 12;
+
+ LLCDMeshData mesh;
+ LLCDResult res = LLCD_OK;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh);
+ }
+ if (res != LLCD_OK)
+ {
+ llwarns << "could not get mesh from hull from convex decomposition lib." << llendl;
+ return false;
+ }
+
+
+ d->mMesh.push_back(get_vertex_buffer_from_mesh(mesh));
+ }
+
+ mDecompositionQ.push(d);
+ }
+
+ return true;
+}
+
+LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures)
+: LLThread("mesh upload")
+{
+ mInstanceList = data;
+ mUploadTextures = upload_textures;
+ mMutex = new LLMutex(NULL);
+ mCurlRequest = NULL;
+ mPendingConfirmations = 0;
+ mPendingUploads = 0;
+ mPendingCost = 0;
+ mFinished = false;
+ mOrigin = gAgent.getPositionAgent();
+ mHost = gAgent.getRegionHost();
+ mUploadObjectAssetCapability = gAgent.getRegion()->getCapability("UploadObjectAsset");
+ mNewInventoryCapability = gAgent.getRegion()->getCapability("NewFileAgentInventoryVariablePrice");
+
+ mOrigin += gAgent.getAtAxis() * scale.magVec();
+
+ scrub_host_name(mUploadObjectAssetCapability, mHost);
+ scrub_host_name(mNewInventoryCapability, mHost);
+}
+
+LLMeshUploadThread::~LLMeshUploadThread()
+{
+
+}
+
+void LLMeshUploadThread::run()
+{
+ mCurlRequest = new LLCurlRequest();
+
+ //build map of LLModel refs to instances for callbacks
+ for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter)
+ {
+ mInstance[iter->mModel].push_back(*iter);
+ }
+
+ std::set<LLPointer<LLViewerTexture> > textures;
+
+ //populate upload queue with relevant models
+ for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ {
+ LLMeshUploadData data;
+ data.mBaseModel = iter->first;
+
+ LLModelInstance& instance = *(iter->second.begin());
+
+ for (S32 i = 0; i < 5; i++)
+ {
+ data.mModel[i] = instance.mLOD[i];
+ }
+
+ uploadModel(data);
+
+ if (mUploadTextures)
+ {
+ for (std::vector<LLImportMaterial>::iterator material_iter = instance.mMaterial.begin();
+ material_iter != instance.mMaterial.end(); ++material_iter)
+ {
+
+ if (textures.find(material_iter->mDiffuseMap) == textures.end())
+ {
+ textures.insert(material_iter->mDiffuseMap);
+
+ LLTextureUploadData data(material_iter->mDiffuseMap, material_iter->mDiffuseMapLabel);
+ uploadTexture(data);
+ }
+ }
+ }
+ }
+
+
+ //upload textures
+ bool done = false;
+ do
+ {
+ if (!mTextureQ.empty())
+ {
+ sendCostRequest(mTextureQ.front());
+ mTextureQ.pop();
+ }
+
+ if (!mConfirmedTextureQ.empty())
+ {
+ doUploadTexture(mConfirmedTextureQ.front());
+ mConfirmedTextureQ.pop();
+ }
+
+ mCurlRequest->process();
+
+ done = mTextureQ.empty() && mConfirmedTextureQ.empty();
+ }
+ while (!done || mCurlRequest->getQueued() > 0);
+
+ LLSD object_asset;
+ object_asset["objects"] = LLSD::emptyArray();
+
+ done = false;
+ do
+ {
+ static S32 count = 0;
+ static F32 last_hundred = gFrameTimeSeconds;
+ if (gFrameTimeSeconds - last_hundred > 1.f)
+ {
+ last_hundred = gFrameTimeSeconds;
+ count = 0;
+ }
+
+ //how many requests to push before calling process
+ const S32 PUSH_PER_PROCESS = 32;
+
+ S32 tcount = llmin(count+PUSH_PER_PROCESS, 100);
+
+ while (!mUploadQ.empty() && count < tcount)
+ { //send any pending upload requests
+ mMutex->lock();
+ LLMeshUploadData data = mUploadQ.front();
+ mUploadQ.pop();
+ mMutex->unlock();
+ sendCostRequest(data);
+ count++;
+ }
+
+ tcount = llmin(count+PUSH_PER_PROCESS, 100);
+
+ while (!mConfirmedQ.empty() && count < tcount)
+ { //process any meshes that have been confirmed for upload
+ LLMeshUploadData& data = mConfirmedQ.front();
+ doUploadModel(data);
+ mConfirmedQ.pop();
+ count++;
+ }
+
+ tcount = llmin(count+PUSH_PER_PROCESS, 100);
+
+ while (!mInstanceQ.empty() && count < tcount)
+ { //create any objects waiting for upload
+ count++;
+ object_asset["objects"].append(createObject(mInstanceQ.front()));
+ mInstanceQ.pop();
+ }
+
+ mCurlRequest->process();
+
+ done = mInstanceQ.empty() && mConfirmedQ.empty() && mUploadQ.empty();
+ }
+ while (!done || mCurlRequest->getQueued() > 0);
+
+ delete mCurlRequest;
+ mCurlRequest = NULL;
+
+ // now upload the object asset
+ std::string url = mUploadObjectAssetCapability;
+ LLHTTPClient::post(url, object_asset, new LLHTTPClient::Responder());
+
+ mFinished = true;
+}
+
+void LLMeshUploadThread::uploadModel(LLMeshUploadData& data)
+{ //called from arbitrary thread
+ {
+ LLMutexLock lock(mMutex);
+ mUploadQ.push(data);
+ }
+}
+
+void LLMeshUploadThread::uploadTexture(LLTextureUploadData& data)
+{ //called from mesh upload thread
+ mTextureQ.push(data);
+}
+
+
+static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_LOADED("Notify Loaded");
+static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_UNAVAILABLE("Notify Unavailable");
+
+void LLMeshRepoThread::notifyLoadedMeshes()
+{
+ while (!mLoadedQ.empty())
+ {
+ mMutex->lock();
+ LoadedMesh mesh = mLoadedQ.front();
+ mLoadedQ.pop();
+ mMutex->unlock();
+
+ if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0)
+ {
+ gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume);
+ }
+ else
+ {
+ gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams,
+ LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
+ }
+ }
+
+ while (!mUnavailableQ.empty())
+ {
+ mMutex->lock();
+ LODRequest req = mUnavailableQ.front();
+ mUnavailableQ.pop();
+ mMutex->unlock();
+
+ gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
+ }
+
+ while (!mSkinInfoQ.empty())
+ {
+ gMeshRepo.notifySkinInfoReceived(mSkinInfoQ.front());
+ mSkinInfoQ.pop();
+ }
+
+ while (!mDecompositionQ.empty())
+ {
+ gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front());
+ mDecompositionQ.pop();
+ }
+}
+
+S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{ //only ever called from main thread
+ lod = llclamp(lod, 0, 3);
+
+ LLMutexLock lock(mHeaderMutex);
+ mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID());
+
+ if (iter != mMeshHeader.end())
+ {
+ LLSD& header = iter->second;
+
+ if (header.has("404"))
+ {
+ return -1;
+ }
+
+ if (header[header_lod[lod]]["size"].asInteger() > 0)
+ {
+ return lod;
+ }
+
+ //search down to find the next available lower lod
+ for (S32 i = lod-1; i >= 0; --i)
+ {
+ if (header[header_lod[i]]["size"].asInteger() > 0)
+ {
+ return i;
+ }
+ }
+
+ //search up to find then ext available higher lod
+ for (S32 i = lod+1; i < 4; ++i)
+ {
+ if (header[header_lod[i]]["size"].asInteger() > 0)
+ {
+ return i;
+ }
+ }
+
+ //header exists and no good lod found, treat as 404
+ header["404"] = 1;
+ return -1;
+ }
+
+ return lod;
+}
+
+U32 LLMeshRepoThread::getResourceCost(const LLUUID& mesh_id)
+{
+ LLMutexLock lock(mHeaderMutex);
+
+ std::map<LLUUID, U32>::iterator iter = mMeshResourceCost.find(mesh_id);
+ if (iter != mMeshResourceCost.end())
+ {
+ return iter->second;
+ }
+
+ return 0;
+}
+
+void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header)
+{
+ mThread->mMeshHeader[data.mUUID] = header;
+
+ // we cache the mesh for default parameters
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+ volume_params.setSculptID(data.mUUID, LL_SCULPT_TYPE_MESH);
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ if (data.mModel[i].notNull())
+ {
+ LLPointer<LLVolume> volume = new LLVolume(volume_params, LLVolumeLODGroup::getVolumeScaleFromDetail(i));
+ volume->copyVolumeFaces(data.mModel[i]);
+ }
+ }
+
+}
+
+void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+{
+
+ LLMeshRepoThread::sActiveLODRequests--;
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ if (status < 200 || status > 400)
+ {
+ llwarns << status << ": " << reason << llendl;
+ }
+
+ if (data_size < mRequestedBytes)
+ {
+ if (status == 499 || status == 503)
+ { //timeout or service unavailable, try again
+ LLMeshRepository::sHTTPRetryCount++;
+ gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD);
+ }
+ else
+ {
+ llwarns << "Unhandled status " << status << llendl;
+ }
+ return;
+ }
+
+ LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+ U8* data = NULL;
+
+ if (data_size > 0)
+ {
+ data = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, data, data_size);
+ }
+
+ if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))
+ {
+ //good fetch from sim, write to VFS for caching
+ LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE);
+
+ S32 offset = mOffset;
+ S32 size = mRequestedBytes;
+
+ if (file.getSize() >= offset+size)
+ {
+ file.seek(offset);
+ file.write(data, size);
+ LLMeshRepository::sCacheBytesWritten += size;
+ }
+ }
+
+ delete [] data;
+}
+
+void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+{
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ if (status < 200 || status > 400)
+ {
+ llwarns << status << ": " << reason << llendl;
+ }
+
+ if (data_size < mRequestedBytes)
+ {
+ if (status == 499 || status == 503)
+ { //timeout or service unavailable, try again
+ LLMeshRepository::sHTTPRetryCount++;
+ gMeshRepo.mThread->loadMeshSkinInfo(mMeshID);
+ }
+ else
+ {
+ llwarns << "Unhandled status " << status << llendl;
+ }
+ return;
+ }
+
+ LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+ U8* data = NULL;
+
+ if (data_size > 0)
+ {
+ data = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, data, data_size);
+ }
+
+ if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
+ {
+ //good fetch from sim, write to VFS for caching
+ LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+ S32 offset = mOffset;
+ S32 size = mRequestedBytes;
+
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesWritten += size;
+ file.seek(offset);
+ file.write(data, size);
+ }
+ }
+
+ delete [] data;
+}
+
+void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+{
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ if (status < 200 || status > 400)
+ {
+ llwarns << status << ": " << reason << llendl;
+ }
+
+ if (data_size < mRequestedBytes)
+ {
+ if (status == 499 || status == 503)
+ { //timeout or service unavailable, try again
+ LLMeshRepository::sHTTPRetryCount++;
+ gMeshRepo.mThread->loadMeshDecomposition(mMeshID);
+ }
+ else
+ {
+ llwarns << "Unhandled status " << status << llendl;
+ }
+ return;
+ }
+
+ LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+ U8* data = NULL;
+
+ if (data_size > 0)
+ {
+ data = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, data, data_size);
+ }
+
+ if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))
+ {
+ //good fetch from sim, write to VFS for caching
+ LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+ S32 offset = mOffset;
+ S32 size = mRequestedBytes;
+
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesWritten += size;
+ file.seek(offset);
+ file.write(data, size);
+ }
+ }
+
+ delete [] data;
+}
+
+void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+{
+ LLMeshRepoThread::sActiveHeaderRequests--;
+ if (status < 200 || status > 400)
+ {
+ llwarns << status << ": " << reason << llendl;
+ }
+
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ U8* data = NULL;
+
+ if (data_size > 0)
+ {
+ data = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, data, data_size);
+ }
+
+ LLMeshRepository::sBytesReceived += llmin(data_size, 4096);
+
+ if (!gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size))
+ {
+ llwarns << "Header responder failed with status: " << status << ": " << reason << llendl;
+ if (status == 503 || status == 499)
+ { //retry
+ LLMeshRepository::sHTTPRetryCount++;
+ LLMeshRepoThread::HeaderRequest req(mMeshParams);
+ gMeshRepo.mThread->mHeaderReqQ.push(req);
+ }
+ }
+ else if (data && data_size > 0)
+ {
+ //header was successfully retrieved from sim, cache in vfs
+ LLUUID mesh_id = mMeshParams.getSculptID();
+ LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id];
+
+ std::stringstream str;
+
+ S32 lod_bytes = 0;
+
+ for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i)
+ { //figure out how many bytes we'll need to reserve in the file
+ std::string lod_name = header_lod[i];
+ lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger());
+ }
+
+ //just in case skin info or decomposition is at the end of the file (which it shouldn't be)
+ lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger());
+ lod_bytes = llmax(lod_bytes, header["decomposition"]["offset"].asInteger() + header["decomposition"]["size"].asInteger());
+
+ S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id];
+ S32 bytes = lod_bytes + header_bytes;
+
+
+ //it's possible for the remote asset to have more data than is needed for the local cache
+ //only allocate as much space in the VFS as is needed for the local cache
+ data_size = llmin(data_size, bytes);
+
+ LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE);
+ if (file.getMaxSize() >= bytes || file.setMaxSize(bytes))
+ {
+ LLMeshRepository::sCacheBytesWritten += data_size;
+
+ file.write((const U8*) data, data_size);
+
+ //zero out the rest of the file
+ U8 block[4096];
+ memset(block, 0, 4096);
+
+ while (bytes-file.tell() > 4096)
+ {
+ file.write(block, 4096);
+ }
+
+ S32 remaining = bytes-file.tell();
+
+ if (remaining < 0 || remaining > 4096)
+ {
+ llerrs << "Bad padding of mesh asset cache entry." << llendl;
+ }
+
+ if (remaining > 0)
+ {
+ file.write(block, remaining);
+ }
+ }
+ }
+
+ delete [] data;
+}
+
+
+LLMeshRepository::LLMeshRepository()
+: mMeshMutex(NULL),
+ mMeshThreadCount(0),
+ mThread(NULL)
+{
+
+}
+
+void LLMeshRepository::init()
+{
+ mMeshMutex = new LLMutex(NULL);
+
+ mDecompThread = new LLPhysicsDecomp();
+ mDecompThread->start();
+
+ while (!mDecompThread->mInited)
+ { //wait for physics decomp thread to init
+ apr_sleep(100);
+ }
+
+ mThread = new LLMeshRepoThread();
+ mThread->start();
+}
+
+void LLMeshRepository::shutdown()
+{
+ mThread->mSignal->signal();
+
+ delete mThread;
+ mThread = NULL;
+
+ for (U32 i = 0; i < mUploads.size(); ++i)
+ {
+ delete mUploads[i];
+ }
+
+ mUploads.clear();
+
+ delete mMeshMutex;
+ mMeshMutex = NULL;
+
+ if (mDecompThread)
+ {
+ mDecompThread->shutdown();
+ delete mDecompThread;
+ mDecompThread = NULL;
+ }
+}
+
+
+S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail)
+{
+ if (detail < 0 || detail > 4)
+ {
+ return detail;
+ }
+
+ LLFastTimer t(FTM_LOAD_MESH);
+
+ {
+ LLMutexLock lock(mMeshMutex);
+ //add volume to list of loading meshes
+ mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_params);
+ if (iter != mLoadingMeshes[detail].end())
+ { //request pending for this mesh, append volume id to list
+ iter->second.insert(vobj->getID());
+ }
+ else
+ {
+ //first request for this mesh
+ mLoadingMeshes[detail][mesh_params].insert(vobj->getID());
+ mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
+ }
+ }
+
+ //do a quick search to see if we can't display something while we wait for this mesh to load
+ LLVolume* volume = vobj->getVolume();
+
+ if (volume)
+ {
+ if (volume->getNumVolumeFaces() == 0 && !volume->isTetrahedron())
+ {
+ volume->makeTetrahedron();
+ }
+
+ LLVolumeParams params = volume->getParams();
+
+ LLVolumeLODGroup* group = LLPrimitive::getVolumeManager()->getGroup(params);
+
+ if (group)
+ {
+ //first see what the next lowest LOD available might be
+ for (S32 i = detail-1; i >= 0; --i)
+ {
+ LLVolume* lod = group->refLOD(i);
+ if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
+ {
+ group->derefLOD(lod);
+ return i;
+ }
+
+ group->derefLOD(lod);
+ }
+
+ //no lower LOD is a available, is a higher lod available?
+ for (S32 i = detail+1; i < 4; ++i)
+ {
+ LLVolume* lod = group->refLOD(i);
+ if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
+ {
+ group->derefLOD(lod);
+ return i;
+ }
+
+ group->derefLOD(lod);
+ }
+ }
+ else
+ {
+ llerrs << "WTF?" << llendl;
+ }
+ }
+
+ return detail;
+}
+
+static LLFastTimer::DeclareTimer FTM_START_MESH_THREAD("Start Thread");
+static LLFastTimer::DeclareTimer FTM_LOAD_MESH_LOD("Load LOD");
+static LLFastTimer::DeclareTimer FTM_MESH_LOCK1("Lock 1");
+static LLFastTimer::DeclareTimer FTM_MESH_LOCK2("Lock 2");
+
+void LLMeshRepository::notifyLoadedMeshes()
+{ //called from main thread
+
+ LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests");
+
+ //clean up completed upload threads
+ for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); )
+ {
+ LLMeshUploadThread* thread = *iter;
+
+ if (thread->isStopped() && thread->finished())
+ {
+ iter = mUploads.erase(iter);
+ delete thread;
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ //update inventory
+ if (!mInventoryQ.empty())
+ {
+ LLMutexLock lock(mMeshMutex);
+ while (!mInventoryQ.empty())
+ {
+ inventory_data& data = mInventoryQ.front();
+
+ LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString());
+ LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString());
+
+ on_new_single_inventory_upload_complete(
+ asset_type,
+ inventory_type,
+ data.mPostData["asset_type"].asString(),
+ data.mPostData["folder_id"].asUUID(),
+ data.mPostData["name"],
+ data.mPostData["description"],
+ data.mResponse,
+ 0);
+
+ mInventoryQ.pop();
+ }
+ }
+
+ if (!mThread->mWaiting)
+ { //curl thread is churning, wait for it to go idle
+ return;
+ }
+
+ LLFastTimer t(FTM_MESH_UPDATE);
+
+ {
+ LLFastTimer t(FTM_MESH_LOCK1);
+ mMeshMutex->lock();
+ }
+
+ {
+ LLFastTimer t(FTM_MESH_LOCK2);
+ mThread->mMutex->lock();
+ }
+
+ //popup queued error messages from background threads
+ while (!mUploadErrorQ.empty())
+ {
+ LLNotificationsUtil::add("MeshUploadError", mUploadErrorQ.front());
+ mUploadErrorQ.pop();
+ }
+
+ S32 push_count = LLMeshRepoThread::sMaxConcurrentRequests-(LLMeshRepoThread::sActiveHeaderRequests+LLMeshRepoThread::sActiveLODRequests);
+
+ if (push_count > 0)
+ {
+ //calculate "score" for pending requests
+
+ //create score map
+ std::map<LLUUID, F32> score_map;
+
+ for (U32 i = 0; i < 4; ++i)
+ {
+ for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter)
+ {
+ F32 max_score = 0.f;
+ for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
+ {
+ LLViewerObject* object = gObjectList.findObject(*obj_iter);
+
+ if (object)
+ {
+ LLDrawable* drawable = object->mDrawable;
+ if (drawable)
+ {
+ F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f);
+ max_score = llmax(max_score, cur_score);
+ }
+ }
+ }
+
+ score_map[iter->first.getSculptID()] = max_score;
+ }
+ }
+
+ //set "score" for pending requests
+ for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter)
+ {
+ iter->mScore = score_map[iter->mMeshParams.getSculptID()];
+ }
+
+ //sort by "score"
+ std::sort(mPendingRequests.begin(), mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater());
+
+ while (!mPendingRequests.empty() && push_count > 0)
+ {
+ LLFastTimer t(FTM_LOAD_MESH_LOD);
+ LLMeshRepoThread::LODRequest& request = mPendingRequests.front();
+ mThread->loadMeshLOD(request.mMeshParams, request.mLOD);
+ mPendingRequests.erase(mPendingRequests.begin());
+ push_count--;
+ }
+ }
+
+ //send skin info requests
+ while (!mPendingSkinRequests.empty())
+ {
+ mThread->loadMeshSkinInfo(mPendingSkinRequests.front());
+ mPendingSkinRequests.pop();
+ }
+
+ //send decomposition requests
+ while (!mPendingDecompositionRequests.empty())
+ {
+ mThread->loadMeshDecomposition(mPendingDecompositionRequests.front());
+ mPendingDecompositionRequests.pop();
+ }
+
+ mThread->notifyLoadedMeshes();
+
+ mThread->mMutex->unlock();
+ mMeshMutex->unlock();
+
+ mThread->mSignal->signal();
+}
+
+void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)
+{
+ mSkinMap[info.mMeshID] = info;
+ mLoadingSkins.erase(info.mMeshID);
+}
+
+void LLMeshRepository::notifyDecompositionReceived(LLMeshDecomposition* decomp)
+{
+ mDecompositionMap[decomp->mMeshID] = decomp;
+ mLoadingDecompositions.erase(decomp->mMeshID);
+}
+
+void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
+{
+ S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
+
+ //get list of objects waiting to be notified this mesh is loaded
+ mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_params);
+
+ if (volume && obj_iter != mLoadingMeshes[detail].end())
+ {
+ //make sure target volume is still valid
+ if (volume->getNumVolumeFaces() <= 0)
+ {
+ llwarns << "Mesh loading returned empty volume." << llendl;
+ volume->makeTetrahedron();
+ }
+
+ { //update system volume
+ LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail);
+ if (sys_volume)
+ {
+ sys_volume->copyVolumeFaces(volume);
+ LLPrimitive::getVolumeManager()->unrefVolume(sys_volume);
+ }
+ else
+ {
+ llwarns << "Couldn't find system volume for given mesh." << llendl;
+ }
+ }
+
+ //notify waiting LLVOVolume instances that their requested mesh is available
+ for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+ {
+ LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
+ if (vobj)
+ {
+ vobj->notifyMeshLoaded();
+ }
+ }
+
+ mLoadingMeshes[detail].erase(mesh_params);
+ }
+}
+
+void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)
+{
+ //get list of objects waiting to be notified this mesh is loaded
+ mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params);
+
+ F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
+
+ if (obj_iter != mLoadingMeshes[lod].end())
+ {
+ for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+ {
+ LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
+ if (vobj)
+ {
+ LLVolume* obj_volume = vobj->getVolume();
+
+ if (obj_volume &&
+ obj_volume->getDetail() == detail &&
+ obj_volume->getParams() == mesh_params)
+ { //should force volume to find most appropriate LOD
+ vobj->setVolume(obj_volume->getParams(), lod);
+ }
+ }
+ }
+
+ mLoadingMeshes[lod].erase(mesh_params);
+ }
+}
+
+S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{
+ return mThread->getActualMeshLOD(mesh_params, lod);
+}
+
+U32 LLMeshRepository::calcResourceCost(LLSD& header)
+{
+ U32 bytes = 0;
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ bytes += header[header_lod[i]]["size"].asInteger();
+ }
+
+ bytes += header["skin"]["size"].asInteger();
+
+ return bytes/4096 + 1;
+}
+
+U32 LLMeshRepository::getResourceCost(const LLUUID& mesh_id)
+{
+ return mThread->getResourceCost(mesh_id);
+}
+
+const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id)
+{
+ if (mesh_id.notNull())
+ {
+ skin_map::iterator iter = mSkinMap.find(mesh_id);
+ if (iter != mSkinMap.end())
+ {
+ return &(iter->second);
+ }
+
+ //no skin info known about given mesh, try to fetch it
+ {
+ LLMutexLock lock(mMeshMutex);
+ //add volume to list of loading meshes
+ std::set<LLUUID>::iterator iter = mLoadingSkins.find(mesh_id);
+ if (iter == mLoadingSkins.end())
+ { //no request pending for this skin info
+ mLoadingSkins.insert(mesh_id);
+ mPendingSkinRequests.push(mesh_id);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+const LLMeshDecomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id)
+{
+ if (mesh_id.notNull())
+ {
+ decomposition_map::iterator iter = mDecompositionMap.find(mesh_id);
+ if (iter != mDecompositionMap.end())
+ {
+ return iter->second;
+ }
+
+ //no skin info known about given mesh, try to fetch it
+ {
+ LLMutexLock lock(mMeshMutex);
+ //add volume to list of loading meshes
+ std::set<LLUUID>::iterator iter = mLoadingDecompositions.find(mesh_id);
+ if (iter == mLoadingDecompositions.end())
+ { //no request pending for this skin info
+ mLoadingDecompositions.insert(mesh_id);
+ mPendingDecompositionRequests.push(mesh_id);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures)
+{
+ LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures);
+ mUploads.push_back(thread);
+ thread->start();
+}
+
+void LLMeshUploadThread::sendCostRequest(LLMeshUploadData& data)
+{
+ //write model file to memory buffer
+ std::stringstream ostr;
+
+ LLModel::physics_shape& phys_shape = data.mModel[LLModel::LOD_PHYSICS].notNull() ?
+ data.mModel[LLModel::LOD_PHYSICS]->mPhysicsShape :
+ data.mBaseModel->mPhysicsShape;
+
+ LLSD header = LLModel::writeModel(ostr,
+ data.mModel[LLModel::LOD_PHYSICS],
+ data.mModel[LLModel::LOD_HIGH],
+ data.mModel[LLModel::LOD_MEDIUM],
+ data.mModel[LLModel::LOD_LOW],
+ data.mModel[LLModel::LOD_IMPOSTOR],
+ phys_shape,
+ true);
+
+ std::string desc = data.mBaseModel->mLabel;
+
+ // Grab the total vertex count of the model
+ // along with other information for the "asset_resources" map
+ // to send to the server.
+ LLSD asset_resources = LLSD::emptyMap();
+
+
+ std::string url = mNewInventoryCapability;
+
+ if (!url.empty())
+ {
+ LLSD body = generate_new_resource_upload_capability_body(
+ LLAssetType::AT_MESH,
+ desc,
+ desc,
+ LLFolderType::FT_MESH,
+ LLInventoryType::IT_MESH,
+ LLFloaterPerms::getNextOwnerPerms(),
+ LLFloaterPerms::getGroupPerms(),
+ LLFloaterPerms::getEveryonePerms());
+
+ body["asset_resources"] = asset_resources;
+
+ mPendingConfirmations++;
+ LLCurlRequest::headers_t headers;
+
+ data.mPostData = body;
+
+ mCurlRequest->post(url, headers, body, new LLMeshCostResponder(data, this));
+ }
+}
+
+void LLMeshUploadThread::sendCostRequest(LLTextureUploadData& data)
+{
+ if (data.mTexture.notNull() && data.mTexture->getDiscardLevel() >= 0)
+ {
+ LLSD asset_resources = LLSD::emptyMap();
+
+ std::string url = mNewInventoryCapability;
+
+ if (!url.empty())
+ {
+ LLSD body = generate_new_resource_upload_capability_body(
+ LLAssetType::AT_TEXTURE,
+ data.mLabel,
+ data.mLabel,
+ LLFolderType::FT_TEXTURE,
+ LLInventoryType::IT_TEXTURE,
+ LLFloaterPerms::getNextOwnerPerms(),
+ LLFloaterPerms::getGroupPerms(),
+ LLFloaterPerms::getEveryonePerms());
+
+ body["asset_resources"] = asset_resources;
+
+ mPendingConfirmations++;
+ LLCurlRequest::headers_t headers;
+
+ data.mPostData = body;
+ mCurlRequest->post(url, headers, body, new LLTextureCostResponder(data, this));
+ }
+ }
+}
+
+
+void LLMeshUploadThread::doUploadModel(LLMeshUploadData& data)
+{
+ if (!data.mRSVP.empty())
+ {
+ std::stringstream ostr;
+
+ LLModel::physics_shape& phys_shape = data.mModel[LLModel::LOD_PHYSICS].notNull() ?
+ data.mModel[LLModel::LOD_PHYSICS]->mPhysicsShape :
+ data.mBaseModel->mPhysicsShape;
+
+ LLModel::writeModel(ostr,
+ data.mModel[LLModel::LOD_PHYSICS],
+ data.mModel[LLModel::LOD_HIGH],
+ data.mModel[LLModel::LOD_MEDIUM],
+ data.mModel[LLModel::LOD_LOW],
+ data.mModel[LLModel::LOD_IMPOSTOR],
+ phys_shape);
+
+ data.mAssetData = ostr.str();
+
+ LLCurlRequest::headers_t headers;
+ mPendingUploads++;
+
+ mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLMeshUploadResponder(data, this));
+ }
+}
+
+void LLMeshUploadThread::doUploadTexture(LLTextureUploadData& data)
+{
+ if (!data.mRSVP.empty())
+ {
+ std::stringstream ostr;
+
+ if (!data.mTexture->isRawImageValid())
+ {
+ data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel());
+ }
+
+ LLPointer<LLImageJ2C> upload_file = LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage());
+
+ ostr.write((const char*) upload_file->getData(), upload_file->getDataSize());
+
+ data.mAssetData = ostr.str();
+
+ LLCurlRequest::headers_t headers;
+ mPendingUploads++;
+
+ mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLTextureUploadResponder(data, this));
+ }
+}
+
+
+void LLMeshUploadThread::onModelUploaded(LLMeshUploadData& data)
+{
+ createObjects(data);
+}
+
+void LLMeshUploadThread::onTextureUploaded(LLTextureUploadData& data)
+{
+ mTextureMap[data.mTexture] = data;
+}
+
+
+void LLMeshUploadThread::createObjects(LLMeshUploadData& data)
+{
+ instance_list& instances = mInstance[data.mBaseModel];
+
+ for (instance_list::iterator iter = instances.begin(); iter != instances.end(); ++iter)
+ { //create prims that reference given mesh
+ LLModelInstance& instance = *iter;
+ instance.mMeshID = data.mUUID;
+ mInstanceQ.push(instance);
+ }
+}
+
+LLSD LLMeshUploadThread::createObject(LLModelInstance& instance)
+{
+ LLMatrix4 transformation = instance.mTransform;
+
+ if (instance.mMeshID.isNull())
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ // check for reflection
+ BOOL reflected = (transformation.determinant() < 0);
+
+ // compute position
+ LLVector3 position = LLVector3(0, 0, 0) * transformation;
+
+ // compute scale
+ LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
+ LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
+ LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
+ F32 x_length = x_transformed.normalize();
+ F32 y_length = y_transformed.normalize();
+ F32 z_length = z_transformed.normalize();
+ LLVector3 scale = LLVector3(x_length, y_length, z_length);
+
+ // adjust for "reflected" geometry
+ LLVector3 x_transformed_reflected = x_transformed;
+ if (reflected)
+ {
+ x_transformed_reflected *= -1.0;
+ }
+
+ // compute rotation
+ LLMatrix3 rotation_matrix;
+ rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed);
+ LLQuaternion quat_rotation = rotation_matrix.quaternion();
+ quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here.
+ LLVector3 euler_rotation;
+ quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]);
+
+ //
+ // build parameter block to construct this prim
+ //
+
+ LLSD object_params;
+
+ // create prim
+
+ // set volume params
+ U8 sculpt_type = LL_SCULPT_TYPE_MESH;
+ if (reflected)
+ {
+ sculpt_type |= LL_SCULPT_FLAG_MIRROR;
+ }
+ LLVolumeParams volume_params;
+ volume_params.setType( LL_PCODE_PROFILE_SQUARE, 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 );
+ volume_params.setSculptID(instance.mMeshID, sculpt_type);
+ object_params["shape"] = volume_params.asLLSD();
+
+ object_params["material"] = LL_MCODE_WOOD;
+
+ object_params["group-id"] = gAgent.getGroupID();
+ object_params["pos"] = ll_sd_from_vector3(position + mOrigin);
+ object_params["rotation"] = ll_sd_from_quaternion(quat_rotation);
+ object_params["scale"] = ll_sd_from_vector3(scale);
+ object_params["name"] = instance.mModel->mLabel;
+
+ // load material from dae file
+ object_params["facelist"] = LLSD::emptyArray();
+ for (S32 i = 0; i < instance.mMaterial.size(); i++)
+ {
+ LLTextureEntry te;
+ LLImportMaterial& mat = instance.mMaterial[i];
+
+ te.setColor(mat.mDiffuseColor);
+
+ LLUUID diffuse_id = mTextureMap[mat.mDiffuseMap].mUUID;
+
+ if (diffuse_id.notNull())
+ {
+ te.setID(diffuse_id);
+ }
+ else
+ {
+ te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture
+ }
+
+ te.setFullbright(mat.mFullbright);
+
+ object_params["facelist"][i] = te.asLLSD();
+ }
+
+ // set extra parameters
+ LLSculptParams sculpt_params;
+ sculpt_params.setSculptTexture(instance.mMeshID);
+ sculpt_params.setSculptType(sculpt_type);
+ U8 buffer[MAX_OBJECT_PARAMS_SIZE+1];
+ LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE);
+ sculpt_params.pack(dp);
+ std::vector<U8> v(dp.getCurrentSize());
+ memcpy(&v[0], buffer, dp.getCurrentSize());
+ LLSD extra_parameter;
+ extra_parameter["extra_parameter"] = sculpt_params.mType;
+ extra_parameter["param_data"] = v;
+ object_params["extra_parameters"].append(extra_parameter);
+
+ return object_params;
+}
+
+void LLMeshUploadThread::priceResult(LLMeshUploadData& data, const LLSD& content)
+{
+ mPendingCost += content["upload_price"].asInteger();
+ data.mRSVP = content["rsvp"].asString();
+ data.mRSVP = scrub_host_name(data.mRSVP, mHost);
+
+ mConfirmedQ.push(data);
+}
+
+void LLMeshUploadThread::priceResult(LLTextureUploadData& data, const LLSD& content)
+{
+ mPendingCost += content["upload_price"].asInteger();
+ data.mRSVP = content["rsvp"].asString();
+ data.mRSVP = scrub_host_name(data.mRSVP, mHost);
+
+ mConfirmedTextureQ.push(data);
+}
+
+
+bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const
+{
+ if (mDiffuseMap != rhs.mDiffuseMap)
+ {
+ return mDiffuseMap < rhs.mDiffuseMap;
+ }
+
+ if (mDiffuseMapFilename != rhs.mDiffuseMapFilename)
+ {
+ return mDiffuseMapFilename < rhs.mDiffuseMapFilename;
+ }
+
+ if (mDiffuseMapLabel != rhs.mDiffuseMapLabel)
+ {
+ return mDiffuseMapLabel < rhs.mDiffuseMapLabel;
+ }
+
+ if (mDiffuseColor != rhs.mDiffuseColor)
+ {
+ return mDiffuseColor < rhs.mDiffuseColor;
+ }
+
+ return mFullbright < rhs.mFullbright;
+}
+
+
+void LLMeshRepository::updateInventory(inventory_data data)
+{
+ LLMutexLock lock(mMeshMutex);
+ mInventoryQ.push(data);
+}
+
+void LLMeshRepository::uploadError(LLSD& args)
+{
+ LLMutexLock lock(mMeshMutex);
+ mUploadErrorQ.push(args);
+}
+
+
+LLPhysicsDecomp::LLPhysicsDecomp()
+: LLThread("Physics Decomp")
+{
+ mInited = false;
+ mQuitting = false;
+ mDone = false;
+ mStage = -1;
+ mContinue = 1;
+
+ mSignal = new LLCondition(NULL);
+ mMutex = new LLMutex(NULL);
+
+ setStatusMessage("Idle");
+}
+
+LLPhysicsDecomp::~LLPhysicsDecomp()
+{
+ shutdown();
+}
+
+void LLPhysicsDecomp::shutdown()
+{
+ if (mSignal)
+ {
+ mQuitting = true;
+ mContinue = 0;
+ mSignal->signal();
+
+ while (!mDone)
+ {
+ apr_sleep(100);
+ }
+ }
+}
+
+void LLPhysicsDecomp::setStatusMessage(std::string msg)
+{
+ LLMutexLock lock(mMutex);
+ mStatus = msg;
+}
+
+void LLPhysicsDecomp::execute(const char* stage, LLModel* mdl)
+{
+ LLMutexLock lock(mMutex);
+
+ if (mModel.notNull())
+ {
+ llwarns << "Not done processing previous model." << llendl;
+ return;
+ }
+
+ mModel = mdl;
+ //load model into LLCD
+ if (mdl)
+ {
+ mStage = mStageID[stage];
+
+ U16 index_offset = 0;
+
+ if (mStage == 0)
+ {
+ mPositions.clear();
+ mIndices.clear();
+
+ //queue up vertex positions and indices
+ for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = mdl->getVolumeFace(i);
+ if (mPositions.size() + face.mNumVertices > 65535)
+ {
+ continue;
+ }
+
+ for (U32 j = 0; j < face.mNumVertices; ++j)
+ {
+ mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+ }
+
+ for (U32 j = 0; j < face.mNumIndices; ++j)
+ {
+ mIndices.push_back(face.mIndices[j]+index_offset);
+ }
+
+ index_offset += face.mNumVertices;
+ }
+ }
+
+ //signal decomposition thread
+ mSignal->signal();
+ }
+}
+
+//static
+S32 LLPhysicsDecomp::llcdCallback(const char* status, S32 p1, S32 p2)
+{
+ LLPhysicsDecomp* comp = gMeshRepo.mDecompThread;
+ comp->setStatusMessage(llformat("%s: %d/%d", status, p1, p2));
+ return comp->mContinue;
+}
+
+void LLPhysicsDecomp::cancel()
+{
+ mContinue = 0;
+}
+
+void LLPhysicsDecomp::run()
+{
+ LLConvexDecomposition::initSystem();
+ mInited = true;
+
+ while (!mQuitting)
+ {
+ mSignal->wait();
+ if (!mQuitting)
+ {
+ //load data intoLLCD
+ if (mStage == 0)
+ {
+ mMesh.mVertexBase = mPositions[0].mV;
+ mMesh.mVertexStrideBytes = 12;
+ mMesh.mNumVertices = mPositions.size();
+
+ mMesh.mIndexType = LLCDMeshData::INT_16;
+ mMesh.mIndexBase = &(mIndices[0]);
+ mMesh.mIndexStrideBytes = 6;
+
+ mMesh.mNumTriangles = mIndices.size()/3;
+
+ LLCDResult ret = LLCD_OK;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ ret = LLConvexDecomposition::getInstance()->setMeshData(&mMesh);
+ }
+
+ if (ret)
+ {
+ llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl;
+ }
+ }
+
+ setStatusMessage("Executing.");
+
+ mContinue = 1;
+ LLCDResult ret = LLCD_OK;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ ret = LLConvexDecomposition::getInstance()->executeStage(mStage);
+ }
+
+ mContinue = 0;
+ if (ret)
+ {
+ llerrs << "Convex Decomposition thread valid but could not execute stage " << mStage << llendl;
+ }
+
+
+ setStatusMessage("Reading results");
+
+ S32 num_hulls =0;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(mStage);
+ }
+
+ if (mModel.isNull())
+ {
+ llerrs << "mModel should never be null here!" << llendl;
+ }
+
+ mMutex->lock();
+ mModel->mPhysicsShape.clear();
+ mModel->mPhysicsShape.resize(num_hulls);
+ mModel->mHullCenter.clear();
+ mModel->mHullCenter.resize(num_hulls);
+ std::vector<LLPointer<LLVertexBuffer> > mesh_buffer;
+ mesh_buffer.resize(num_hulls);
+ mModel->mPhysicsCenter.clearVec();
+ mModel->mPhysicsPoints = 0;
+ mMutex->unlock();
+
+ for (S32 i = 0; i < num_hulls; ++i)
+ {
+ std::vector<LLVector3> p;
+ LLCDHull hull;
+ // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
+ LLConvexDecomposition::getInstance()->getHullFromStage(mStage, i, &hull);
+
+ const F32* v = hull.mVertexBase;
+
+ LLVector3 hull_center;
+
+ for (S32 j = 0; j < hull.mNumVertices; ++j)
+ {
+ LLVector3 vert(v[0], v[1], v[2]);
+ p.push_back(vert);
+ hull_center += vert;
+ v = (F32*) (((U8*) v) + hull.mVertexStrideBytes);
+ }
+
+
+ hull_center *= 1.f/hull.mNumVertices;
+
+ LLCDMeshData mesh;
+ // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
+ LLConvexDecomposition::getInstance()->getMeshFromStage(mStage, i, &mesh);
+
+ mesh_buffer[i] = get_vertex_buffer_from_mesh(mesh);
+
+ mMutex->lock();
+ mModel->mPhysicsShape[i] = p;
+ mModel->mHullCenter[i] = hull_center;
+ mModel->mPhysicsPoints += hull.mNumVertices;
+ mModel->mPhysicsCenter += hull_center;
+
+ mMutex->unlock();
+ }
+
+ {
+ LLMutexLock lock(mMutex);
+ mModel->mPhysicsCenter *= 1.f/mModel->mPhysicsPoints;
+
+ LLFloaterModelPreview::onModelDecompositionComplete(mModel, mesh_buffer);
+ //done updating model
+ mModel = NULL;
+ }
+
+ setStatusMessage("Done.");
+ LLFloaterModelPreview::sInstance->mModelPreview->refresh();
+ }
+ }
+
+ LLConvexDecomposition::quitSystem();
+
+ //delete mSignal;
+ delete mMutex;
+ mSignal = NULL;
+ mMutex = NULL;
+ mDone = true;
+}
+
+#endif
+
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
new file mode 100644
index 0000000000..d5e21c35cc
--- /dev/null
+++ b/indra/newview/llmeshrepository.h
@@ -0,0 +1,471 @@
+/**
+ * @file llmeshrepository.h
+ * @brief Client-side repository of mesh assets.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_MESH_REPOSITORY_H
+#define LL_MESH_REPOSITORY_H
+
+#include "llassettype.h"
+#include "llmodel.h"
+#include "lluuid.h"
+#include "llviewertexture.h"
+#include "llvolume.h"
+
+#if LL_MESH_ENABLED
+
+#define LLCONVEXDECOMPINTER_STATIC 1
+
+#include "llconvexdecomposition.h"
+
+class LLVOVolume;
+class LLMeshResponder;
+class LLCurlRequest;
+class LLMutex;
+class LLCondition;
+class LLVFS;
+class LLMeshRepository;
+
+class LLMeshUploadData
+{
+public:
+ LLPointer<LLModel> mBaseModel;
+ LLPointer<LLModel> mModel[5];
+ LLUUID mUUID;
+ U32 mRetries;
+ std::string mRSVP;
+ std::string mAssetData;
+ LLSD mPostData;
+
+ LLMeshUploadData()
+ {
+ mRetries = 0;
+ }
+};
+
+class LLTextureUploadData
+{
+public:
+ LLPointer<LLViewerFetchedTexture> mTexture;
+ LLUUID mUUID;
+ std::string mRSVP;
+ std::string mLabel;
+ U32 mRetries;
+ std::string mAssetData;
+ LLSD mPostData;
+
+ LLTextureUploadData()
+ {
+ mRetries = 0;
+ }
+
+ LLTextureUploadData(LLViewerFetchedTexture* texture, std::string& label)
+ : mTexture(texture), mLabel(label)
+ {
+ mRetries = 0;
+ }
+};
+
+class LLImportMaterial
+{
+public:
+ LLPointer<LLViewerFetchedTexture> mDiffuseMap;
+ std::string mDiffuseMapFilename;
+ std::string mDiffuseMapLabel;
+ LLColor4 mDiffuseColor;
+ bool mFullbright;
+
+ bool operator<(const LLImportMaterial &params) const;
+
+ LLImportMaterial()
+ : mFullbright(false)
+ {
+ mDiffuseColor.set(1,1,1,1);
+ }
+};
+
+class LLModelInstance
+{
+public:
+ LLPointer<LLModel> mModel;
+ LLPointer<LLModel> mLOD[5];
+
+ LLUUID mMeshID;
+
+ LLMatrix4 mTransform;
+ std::vector<LLImportMaterial> mMaterial;
+
+ LLModelInstance(LLModel* model, LLMatrix4& transform, std::vector<LLImportMaterial>& materials)
+ : mModel(model), mTransform(transform), mMaterial(materials)
+ {
+ }
+};
+
+class LLMeshSkinInfo
+{
+public:
+ LLUUID mMeshID;
+ std::vector<std::string> mJointNames;
+ std::vector<LLMatrix4> mInvBindMatrix;
+ LLMatrix4 mBindShapeMatrix;
+};
+
+class LLMeshDecomposition
+{
+public:
+ LLMeshDecomposition() { }
+
+ LLUUID mMeshID;
+ LLModel::physics_shape mHull;
+
+ std::vector<LLPointer<LLVertexBuffer> > mMesh;
+};
+
+class LLPhysicsDecomp : public LLThread
+{
+public:
+ LLCondition* mSignal;
+ LLMutex* mMutex;
+
+ LLCDMeshData mMesh;
+
+ bool mInited;
+ bool mQuitting;
+ bool mDone;
+
+ S32 mContinue;
+ std::string mStatus;
+
+ std::vector<LLVector3> mPositions;
+ std::vector<U16> mIndices;
+
+ S32 mStage;
+
+ LLPhysicsDecomp();
+ ~LLPhysicsDecomp();
+
+ void shutdown();
+ void setStatusMessage(std::string msg);
+
+ void execute(const char* stage, LLModel* mdl);
+ static S32 llcdCallback(const char*, S32, S32);
+ void cancel();
+
+ virtual void run();
+
+ std::map<std::string, S32> mStageID;
+ LLPointer<LLModel> mModel;
+};
+
+class LLMeshRepoThread : public LLThread
+{
+public:
+
+ static S32 sActiveHeaderRequests;
+ static S32 sActiveLODRequests;
+ static U32 sMaxConcurrentRequests;
+
+ LLCurlRequest* mCurlRequest;
+ LLMutex* mMutex;
+ LLMutex* mHeaderMutex;
+ LLCondition* mSignal;
+
+ bool mWaiting;
+
+ //map of known mesh headers
+ typedef std::map<LLUUID, LLSD> mesh_header_map;
+ mesh_header_map mMeshHeader;
+
+ std::map<LLUUID, U32> mMeshHeaderSize;
+ std::map<LLUUID, U32> mMeshResourceCost;
+
+ class HeaderRequest
+ {
+ public:
+ const LLVolumeParams mMeshParams;
+
+ HeaderRequest(const LLVolumeParams& mesh_params)
+ : mMeshParams(mesh_params)
+ {
+ }
+
+ bool operator<(const HeaderRequest& rhs) const
+ {
+ return mMeshParams < rhs.mMeshParams;
+ }
+ };
+
+ class LODRequest
+ {
+ public:
+ LLVolumeParams mMeshParams;
+ S32 mLOD;
+ F32 mScore;
+
+ LODRequest(const LLVolumeParams& mesh_params, S32 lod)
+ : mMeshParams(mesh_params), mLOD(lod), mScore(0.f)
+ {
+ }
+ };
+
+ struct CompareScoreGreater
+ {
+ bool operator()(const LODRequest& lhs, const LODRequest& rhs)
+ {
+ return lhs.mScore > rhs.mScore; // greatest = first
+ }
+ };
+
+
+ class LoadedMesh
+ {
+ public:
+ LLPointer<LLVolume> mVolume;
+ LLVolumeParams mMeshParams;
+ S32 mLOD;
+
+ LoadedMesh(LLVolume* volume, const LLVolumeParams& mesh_params, S32 lod)
+ : mVolume(volume), mMeshParams(mesh_params), mLOD(lod)
+ {
+ }
+
+ };
+
+ //set of requested skin info
+ std::set<LLUUID> mSkinRequests;
+
+ //queue of completed skin info requests
+ std::queue<LLMeshSkinInfo> mSkinInfoQ;
+
+ //set of requested decompositions
+ std::set<LLUUID> mDecompositionRequests;
+
+ //queue of completed Decomposition info requests
+ std::queue<LLMeshDecomposition*> mDecompositionQ;
+
+ //queue of requested headers
+ std::queue<HeaderRequest> mHeaderReqQ;
+
+ //queue of requested LODs
+ std::queue<LODRequest> mLODReqQ;
+
+ //queue of unavailable LODs (either asset doesn't exist or asset doesn't have desired LOD)
+ std::queue<LODRequest> mUnavailableQ;
+
+ //queue of successfully loaded meshes
+ std::queue<LoadedMesh> mLoadedQ;
+
+ //map of pending header requests and currently desired LODs
+ typedef std::map<LLVolumeParams, std::vector<S32> > pending_lod_map;
+ pending_lod_map mPendingLOD;
+
+ static std::string constructUrl(LLUUID mesh_id);
+
+ LLMeshRepoThread();
+ ~LLMeshRepoThread();
+
+ virtual void run();
+
+ void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
+ bool fetchMeshHeader(const LLVolumeParams& mesh_params);
+ bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
+ bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size);
+ bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);
+ bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
+ bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
+
+ void notifyLoadedMeshes();
+ S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
+ U32 getResourceCost(const LLUUID& mesh_params);
+
+ void loadMeshSkinInfo(const LLUUID& mesh_id);
+ void loadMeshDecomposition(const LLUUID& mesh_id);
+
+ //send request for skin info, returns true if header info exists
+ // (should hold onto mesh_id and try again later if header info does not exist)
+ bool fetchMeshSkinInfo(const LLUUID& mesh_id);
+
+ //send request for decomposition, returns true if header info exists
+ // (should hold onto mesh_id and try again later if header info does not exist)
+ bool fetchMeshDecomposition(const LLUUID& mesh_id);
+};
+
+class LLMeshUploadThread : public LLThread
+{
+public:
+ typedef std::vector<LLModelInstance> instance_list;
+ instance_list mInstanceList;
+
+ typedef std::map<LLPointer<LLModel>, instance_list> instance_map;
+ instance_map mInstance;
+
+ LLMutex* mMutex;
+ LLCurlRequest* mCurlRequest;
+ S32 mPendingConfirmations;
+ S32 mPendingUploads;
+ S32 mPendingCost;
+ bool mFinished;
+ LLVector3 mOrigin;
+ bool mUploadTextures;
+
+ LLHost mHost;
+ std::string mUploadObjectAssetCapability;
+ std::string mNewInventoryCapability;
+
+ std::queue<LLMeshUploadData> mUploadQ;
+ std::queue<LLMeshUploadData> mConfirmedQ;
+ std::queue<LLModelInstance> mInstanceQ;
+
+ std::queue<LLTextureUploadData> mTextureQ;
+ std::queue<LLTextureUploadData> mConfirmedTextureQ;
+
+ std::map<LLPointer<LLViewerFetchedTexture>, LLTextureUploadData> mTextureMap;
+
+ LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures);
+ ~LLMeshUploadThread();
+
+ void uploadTexture(LLTextureUploadData& data);
+ void doUploadTexture(LLTextureUploadData& data);
+ void sendCostRequest(LLTextureUploadData& data);
+ void priceResult(LLTextureUploadData& data, const LLSD& content);
+ void onTextureUploaded(LLTextureUploadData& data);
+
+ void uploadModel(LLMeshUploadData& data);
+ void sendCostRequest(LLMeshUploadData& data);
+ void doUploadModel(LLMeshUploadData& data);
+ void onModelUploaded(LLMeshUploadData& data);
+ void createObjects(LLMeshUploadData& data);
+ LLSD createObject(LLModelInstance& instance);
+ void priceResult(LLMeshUploadData& data, const LLSD& content);
+
+ bool finished() { return mFinished; }
+ virtual void run();
+
+};
+
+class LLMeshRepository
+{
+public:
+
+ //metrics
+ static U32 sBytesReceived;
+ static U32 sHTTPRequestCount;
+ static U32 sHTTPRetryCount;
+ static U32 sCacheBytesRead;
+ static U32 sCacheBytesWritten;
+ static U32 sPeakKbps;
+
+
+ LLMeshRepository();
+
+ void init();
+ void shutdown();
+
+ //mesh management functions
+ S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0);
+
+ void notifyLoadedMeshes();
+ void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume);
+ void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod);
+ void notifySkinInfoReceived(LLMeshSkinInfo& info);
+ void notifyDecompositionReceived(LLMeshDecomposition* info);
+
+ S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
+ U32 calcResourceCost(LLSD& header);
+ U32 getResourceCost(const LLUUID& mesh_params);
+ const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id);
+ const LLMeshDecomposition* getDecomposition(const LLUUID& mesh_id);
+
+ void uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures);
+
+
+
+
+ typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;
+ mesh_load_map mLoadingMeshes[4];
+
+ typedef std::map<LLUUID, LLMeshSkinInfo> skin_map;
+ skin_map mSkinMap;
+
+ typedef std::map<LLUUID, LLMeshDecomposition*> decomposition_map;
+ decomposition_map mDecompositionMap;
+
+ LLMutex* mMeshMutex;
+
+ std::vector<LLMeshRepoThread::LODRequest> mPendingRequests;
+
+ //list of mesh ids awaiting skin info
+ std::set<LLUUID> mLoadingSkins;
+
+ //list of mesh ids that need to send skin info fetch requests
+ std::queue<LLUUID> mPendingSkinRequests;
+
+ //list of mesh ids awaiting decompositions
+ std::set<LLUUID> mLoadingDecompositions;
+
+ //list of mesh ids that need to send decomposition fetch requests
+ std::queue<LLUUID> mPendingDecompositionRequests;
+
+ U32 mMeshThreadCount;
+
+ void cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header);
+
+ LLMeshRepoThread* mThread;
+ std::vector<LLMeshUploadThread*> mUploads;
+
+ LLPhysicsDecomp* mDecompThread;
+
+ class inventory_data
+ {
+ public:
+ LLSD mPostData;
+ LLSD mResponse;
+
+ inventory_data(const LLSD& data, const LLSD& content)
+ : mPostData(data), mResponse(content)
+ {
+ }
+ };
+
+ std::queue<inventory_data> mInventoryQ;
+
+ std::queue<LLSD> mUploadErrorQ;
+
+ void uploadError(LLSD& args);
+ void updateInventory(inventory_data data);
+
+};
+
+extern LLMeshRepository gMeshRepo;
+
+#endif
+
+#endif
+