diff options
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(¶m); +		} + +		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*) ¶m[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*) ¶m[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*) ¶m[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*) ¶m[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 ¶ms) 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 +  | 
