diff options
Diffstat (limited to 'indra/llmath')
| -rw-r--r-- | indra/llmath/CMakeLists.txt | 12 | ||||
| -rw-r--r-- | indra/llmath/camera.h | 27 | ||||
| -rw-r--r-- | indra/llmath/coordframe.h | 27 | ||||
| -rw-r--r-- | indra/llmath/llcalc.cpp | 11 | ||||
| -rw-r--r-- | indra/llmath/llcalc.h | 7 | ||||
| -rw-r--r-- | indra/llmath/llcalcparser.h | 12 | ||||
| -rw-r--r-- | indra/llmath/llcamera.h | 12 | ||||
| -rw-r--r-- | indra/llmath/llmatrix3a.cpp | 2 | ||||
| -rw-r--r-- | indra/llmath/llmatrix3a.h | 5 | ||||
| -rw-r--r-- | indra/llmath/llmatrix4a.h | 5 | ||||
| -rw-r--r-- | indra/llmath/llplane.h | 7 | ||||
| -rw-r--r-- | indra/llmath/llquantize.h | 8 | ||||
| -rw-r--r-- | indra/llmath/llquaternion2.h | 3 | ||||
| -rw-r--r-- | indra/llmath/llquaternion2.inl | 2 | ||||
| -rw-r--r-- | indra/llmath/llsimdtypes.inl | 4 | ||||
| -rw-r--r-- | indra/llmath/llsphere.cpp | 370 | ||||
| -rw-r--r-- | indra/llmath/llsphere.h | 77 | ||||
| -rw-r--r-- | indra/llmath/llvector4a.cpp | 4 | ||||
| -rw-r--r-- | indra/llmath/llvector4a.inl | 10 | ||||
| -rw-r--r-- | indra/llmath/llvector4logical.h | 4 | ||||
| -rw-r--r-- | indra/llmath/llvolume.cpp | 356 | ||||
| -rw-r--r-- | indra/llmath/llvolume.h | 9 | ||||
| -rw-r--r-- | indra/llmath/llvolumeoctree.h | 6 | ||||
| -rw-r--r-- | indra/llmath/tests/alignment_test.cpp | 5 | ||||
| -rw-r--r-- | indra/llmath/tests/mathmisc_test.cpp | 161 |
25 files changed, 406 insertions, 740 deletions
diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index e21b6fdb84..b54bfb7705 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -23,7 +23,6 @@ set(llmath_SOURCE_FILES llquaternion.cpp llrigginginfo.cpp llrect.cpp - llsphere.cpp llvector4a.cpp llvolume.cpp llvolumemgr.cpp @@ -45,8 +44,6 @@ set(llmath_SOURCE_FILES set(llmath_HEADER_FILES CMakeLists.txt - camera.h - coordframe.h llbbox.h llbboxlocal.h llcalc.h @@ -73,7 +70,6 @@ set(llmath_HEADER_FILES llsimdmath.h llsimdtypes.h llsimdtypes.inl - llsphere.h lltreenode.h llvector4a.h llvector4a.inl @@ -100,10 +96,14 @@ list(APPEND llmath_SOURCE_FILES ${llmath_HEADER_FILES}) include_directories(${LIBS_PREBUILT_DIR}/include) -add_library (llmath ${llmath_SOURCE_FILES}) +add_library(llmath ${llmath_SOURCE_FILES}) target_link_libraries(llmath llcommon llmeshoptimizer) -target_include_directories( llmath INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(llmath INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +if (USE_PRECOMPILED_HEADERS) + target_precompile_headers(llmath REUSE_FROM llprecompiled) +endif () if (CMAKE_CXX_COMPILER_ID MATCHES GNU) set_source_files_properties(llcalc.cpp PROPERTIES diff --git a/indra/llmath/camera.h b/indra/llmath/camera.h deleted file mode 100644 index 14a08c5985..0000000000 --- a/indra/llmath/camera.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file camera.h - * @brief Legacy wrapper header. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llcamera.h" diff --git a/indra/llmath/coordframe.h b/indra/llmath/coordframe.h deleted file mode 100644 index e7395b1212..0000000000 --- a/indra/llmath/coordframe.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file coordframe.h - * @brief Legacy wrapper header. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llcoordframe.h" diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp index e060b5df58..311e1e7059 100644 --- a/indra/llmath/llcalc.cpp +++ b/indra/llmath/llcalc.cpp @@ -116,17 +116,6 @@ void LLCalc::clearAllVariables() mVariables.clear(); } -/* -void LLCalc::updateVariables(LLSD& vars) -{ - LLSD::map_iterator cIt = vars.beginMap(); - for(; cIt != vars.endMap(); cIt++) - { - setVar(cIt->first, (F32)(LLSD::Real)cIt->second); - } -} -*/ - bool LLCalc::evalString(const std::string& expression, F32& result) { std::string expr_upper = expression; diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h index 09672eb13b..14797de996 100644 --- a/indra/llmath/llcalc.h +++ b/indra/llmath/llcalc.h @@ -73,10 +73,9 @@ public: void setVar(const std::string& name, const F32& value); void clearVar(const std::string& name); void clearAllVariables(); -// void updateVariables(LLSD& vars); bool evalString(const std::string& expression, F32& result); - std::string::size_type getLastErrorPos() { return mLastErrorPos; } + std::string::size_type getLastErrorPos() const { return mLastErrorPos; } static LLCalc* getInstance(); static void cleanUp(); @@ -89,10 +88,6 @@ private: calc_map_t mConstants; calc_map_t mVariables; - // *TODO: Add support for storing user defined variables, and stored functions. - // Will need UI work, and a means to save them between sessions. -// calc_map_t mUserVariables; - // "There shall be only one" static LLCalc* sInstance; }; diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index ec7f6a2cb6..fe4e6bb1a4 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -27,6 +27,13 @@ #ifndef LL_CALCPARSER_H #define LL_CALCPARSER_H +#include "llpreprocessor.h" + +#if defined(LL_GNUC) && GCC_VERSION >= 130000 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdangling-pointer" +#endif + #include <boost/spirit/include/classic_attribute.hpp> #include <boost/spirit/include/classic_core.hpp> #include <boost/spirit/include/classic_error_handling.hpp> @@ -35,6 +42,10 @@ #include <boost/spirit/include/classic_symbols.hpp> using namespace boost::spirit::classic; +#if defined(LL_GNUC) && GCC_VERSION >= 130000 +# pragma GCC diagnostic pop +#endif + #include "llcalc.h" #include "llmath.h" @@ -182,7 +193,6 @@ private: LLCalc::calc_map_t* mConstants; LLCalc::calc_map_t* mVariables; -// LLCalc::calc_map_t* mUserVariables; F32& mResult; }; diff --git a/indra/llmath/llcamera.h b/indra/llmath/llcamera.h index 6201761c46..3863588407 100644 --- a/indra/llmath/llcamera.h +++ b/indra/llmath/llcamera.h @@ -60,10 +60,10 @@ constexpr F32 MAX_FIELD_OF_VIEW = 175.f * DEG_TO_RAD; // roll(), pitch(), yaw() // etc... -LL_ALIGN_PREFIX(16) -class LLCamera +class alignas(16) LLCamera : public LLCoordFrame { + LL_ALIGN_NEW public: LLCamera(const LLCamera& rhs) { @@ -120,9 +120,9 @@ public: }; private: - LL_ALIGN_16(LLPlane mAgentPlanes[AGENT_PLANE_USER_CLIP_NUM]); //frustum planes in agent space a la gluUnproject (I'm a bastard, I know) - DaveP - LL_ALIGN_16(LLPlane mRegionPlanes[AGENT_PLANE_USER_CLIP_NUM]); //frustum planes in a local region space, derived from mAgentPlanes - LL_ALIGN_16(LLPlane mLastAgentPlanes[AGENT_PLANE_USER_CLIP_NUM]); + LLPlane mAgentPlanes[AGENT_PLANE_USER_CLIP_NUM]; //frustum planes in agent space a la gluUnproject (I'm a bastard, I know) - DaveP + LLPlane mRegionPlanes[AGENT_PLANE_USER_CLIP_NUM]; //frustum planes in a local region space, derived from mAgentPlanes + LLPlane mLastAgentPlanes[AGENT_PLANE_USER_CLIP_NUM]; U8 mPlaneMask[PLANE_MASK_NUM]; // 8 for alignment F32 mView; // angle between top and bottom frustum planes in radians. @@ -218,7 +218,7 @@ protected: void calculateFrustumPlanes(); void calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom); void calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2); -} LL_ALIGN_POSTFIX(16); +}; #endif diff --git a/indra/llmath/llmatrix3a.cpp b/indra/llmath/llmatrix3a.cpp index c0b00201cf..904c9de2a1 100644 --- a/indra/llmath/llmatrix3a.cpp +++ b/indra/llmath/llmatrix3a.cpp @@ -28,7 +28,7 @@ #include "llmath.h" -static LL_ALIGN_16(const F32 M_IDENT_3A[12]) = +alignas(16) static 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 diff --git a/indra/llmath/llmatrix3a.h b/indra/llmath/llmatrix3a.h index 9b173c22ed..742a2488fd 100644 --- a/indra/llmath/llmatrix3a.h +++ b/indra/llmath/llmatrix3a.h @@ -40,8 +40,9 @@ // LLMatrix3a is the base class for LLRotation, which should be used instead any time you're dealing with a // rotation matrix. -class LLMatrix3a +class alignas(16) LLMatrix3a { + LL_ALIGN_NEW public: // Utility function for quickly transforming an array of LLVector4a's @@ -111,7 +112,7 @@ public: protected: - LL_ALIGN_16(LLVector4a mColumns[3]); + LLVector4a mColumns[3]; }; diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h index 377203098e..2967949993 100644 --- a/indra/llmath/llmatrix4a.h +++ b/indra/llmath/llmatrix4a.h @@ -31,10 +31,11 @@ #include "m4math.h" #include "m3math.h" -class LLMatrix4a +class alignas(16) LLMatrix4a { + LL_ALIGN_NEW public: - LL_ALIGN_16(LLVector4a mMatrix[4]); + LLVector4a mMatrix[4]; LLMatrix4a() = default; diff --git a/indra/llmath/llplane.h b/indra/llmath/llplane.h index 832004bb64..822e6a7b6f 100644 --- a/indra/llmath/llplane.h +++ b/indra/llmath/llplane.h @@ -36,10 +36,9 @@ // The plane normal = [A, B, C] // The closest approach = D / sqrt(A*A + B*B + C*C) - -LL_ALIGN_PREFIX(16) -class LLPlane +class alignas(16) LLPlane { + LL_ALIGN_NEW public: // Constructors @@ -102,7 +101,7 @@ public: private: LLVector4a mV; -} LL_ALIGN_POSTFIX(16); +}; static_assert(std::is_trivial<LLPlane>::value, "LLPlane must be a trivial type"); diff --git a/indra/llmath/llquantize.h b/indra/llmath/llquantize.h index e8d880122e..2a53fdbf84 100644 --- a/indra/llmath/llquantize.h +++ b/indra/llmath/llquantize.h @@ -29,16 +29,16 @@ #define LL_LLQUANTIZE_H const U16 U16MAX = 65535; -LL_ALIGN_16( const F32 F_U16MAX_4A[4] ) = { 65535.f, 65535.f, 65535.f, 65535.f }; +alignas(16) const F32 F_U16MAX_4A[4] = { 65535.f, 65535.f, 65535.f, 65535.f }; const F32 OOU16MAX = 1.f/(F32)(U16MAX); -LL_ALIGN_16( const F32 F_OOU16MAX_4A[4] ) = { OOU16MAX, OOU16MAX, OOU16MAX, OOU16MAX }; +alignas(16) const F32 F_OOU16MAX_4A[4] = { OOU16MAX, OOU16MAX, OOU16MAX, OOU16MAX }; const U8 U8MAX = 255; -LL_ALIGN_16( const F32 F_U8MAX_4A[4] ) = { 255.f, 255.f, 255.f, 255.f }; +alignas(16) const F32 F_U8MAX_4A[4] = { 255.f, 255.f, 255.f, 255.f }; const F32 OOU8MAX = 1.f/(F32)(U8MAX); -LL_ALIGN_16( const F32 F_OOU8MAX_4A[4] ) = { OOU8MAX, OOU8MAX, OOU8MAX, OOU8MAX }; +alignas(16) const F32 F_OOU8MAX_4A[4] = { OOU8MAX, OOU8MAX, OOU8MAX, OOU8MAX }; const U8 FIRSTVALIDCHAR = 54; const U8 MAXSTRINGVAL = U8MAX - FIRSTVALIDCHAR; //we don't allow newline or null diff --git a/indra/llmath/llquaternion2.h b/indra/llmath/llquaternion2.h index c9dcc4573f..d73e1a0ca2 100644 --- a/indra/llmath/llquaternion2.h +++ b/indra/llmath/llquaternion2.h @@ -40,8 +40,9 @@ ///////////////////////////// #include "llquaternion.h" -class LLQuaternion2 +class alignas(16) LLQuaternion2 { + LL_ALIGN_NEW public: ////////////////////////// diff --git a/indra/llmath/llquaternion2.inl b/indra/llmath/llquaternion2.inl index b431d5766c..8cc5020bb8 100644 --- a/indra/llmath/llquaternion2.inl +++ b/indra/llmath/llquaternion2.inl @@ -62,7 +62,7 @@ inline LLVector4a& LLQuaternion2::getVector4aRw() // 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 }; + alignas(16) static 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)); } diff --git a/indra/llmath/llsimdtypes.inl b/indra/llmath/llsimdtypes.inl index 125f4b9df5..20ea7640b6 100644 --- a/indra/llmath/llsimdtypes.inl +++ b/indra/llmath/llsimdtypes.inl @@ -61,7 +61,7 @@ inline LLSimdScalar operator/(const LLSimdScalar& a, const LLSimdScalar& b) inline LLSimdScalar operator-(const LLSimdScalar& a) { - static LL_ALIGN_16(const U32 signMask[4]) = {0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + alignas(16) static const U32 signMask[4] = {0x80000000, 0x80000000, 0x80000000, 0x80000000 }; ll_assert_aligned(signMask,16); return _mm_xor_ps(*reinterpret_cast<const LLQuad*>(signMask), a); } @@ -146,7 +146,7 @@ inline LLSimdScalar& LLSimdScalar::operator/=(const LLSimdScalar& rhs) inline LLSimdScalar LLSimdScalar::getAbs() const { - static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }; + alignas(16) static const U32 F_ABS_MASK_4A[4] = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }; ll_assert_aligned(F_ABS_MASK_4A,16); return _mm_and_ps( mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A)); } diff --git a/indra/llmath/llsphere.cpp b/indra/llmath/llsphere.cpp deleted file mode 100644 index 5f48764455..0000000000 --- a/indra/llmath/llsphere.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/** - * @file llsphere.cpp - * @author Andrew Meadows - * @brief Simple line class that can compute nearest approach between two lines - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llsphere.h" - -LLSphere::LLSphere() -: mCenter(0.f, 0.f, 0.f), - mRadius(0.f) -{ } - -LLSphere::LLSphere( const LLVector3& center, F32 radius) -{ - set(center, radius); -} - -void LLSphere::set( const LLVector3& center, F32 radius ) -{ - mCenter = center; - setRadius(radius); -} - -void LLSphere::setCenter( const LLVector3& center) -{ - mCenter = center; -} - -void LLSphere::setRadius( F32 radius) -{ - if (radius < 0.f) - { - radius = -radius; - } - mRadius = radius; -} - -const LLVector3& LLSphere::getCenter() const -{ - return mCenter; -} - -F32 LLSphere::getRadius() const -{ - return mRadius; -} - -// returns 'true' if this sphere completely contains other_sphere -bool LLSphere::contains(const LLSphere& other_sphere) const -{ - F32 separation = (mCenter - other_sphere.mCenter).length(); - return mRadius >= separation + other_sphere.mRadius; -} - -// returns 'true' if this sphere completely contains other_sphere -bool LLSphere::overlaps(const LLSphere& other_sphere) const -{ - F32 separation = (mCenter - other_sphere.mCenter).length(); - return mRadius >= separation - other_sphere.mRadius; -} - -// returns overlap -// negative overlap is closest approach -F32 LLSphere::getOverlap(const LLSphere& other_sphere) const -{ - // separation is distance from other_sphere's edge and this center - return (mCenter - other_sphere.mCenter).length() - mRadius - other_sphere.mRadius; -} - -bool LLSphere::operator==(const LLSphere& rhs) const -{ - return fabs(mRadius - rhs.mRadius) <= FLT_EPSILON && - (mCenter - rhs.mCenter).length() <= FLT_EPSILON; -} - -std::ostream& operator<<( std::ostream& output_stream, const LLSphere& sphere) -{ - output_stream << "{center=" << sphere.mCenter << "," << "radius=" << sphere.mRadius << "}"; - return output_stream; -} - -// static -// removes any spheres that are contained in others -void LLSphere::collapse(std::vector<LLSphere>& sphere_list) -{ - std::vector<LLSphere>::iterator first_itr = sphere_list.begin(); - while (first_itr != sphere_list.end()) - { - bool delete_from_front = false; - - std::vector<LLSphere>::iterator second_itr = first_itr; - ++second_itr; - while (second_itr != sphere_list.end()) - { - if (second_itr->contains(*first_itr)) - { - delete_from_front = true; - break; - } - else if (first_itr->contains(*second_itr)) - { - sphere_list.erase(second_itr++); - } - else - { - ++second_itr; - } - } - - if (delete_from_front) - { - sphere_list.erase(first_itr++); - } - else - { - ++first_itr; - } - } -} - -// static -// returns the bounding sphere that contains both spheres -LLSphere LLSphere::getBoundingSphere(const LLSphere& first_sphere, const LLSphere& second_sphere) -{ - LLVector3 direction = second_sphere.mCenter - first_sphere.mCenter; - - // HACK -- it is possible to get enough floating point error in the - // other getBoundingSphere() method that we have to add some slop - // at the end. Unfortunately, this breaks the link-order invarience - // for the linkability tests... unless we also apply the same slop - // here. - F32 half_milimeter = 0.0005f; - - F32 distance = direction.length(); - if (0.f == distance) - { - direction.setVec(1.f, 0.f, 0.f); - } - else - { - direction.normVec(); - } - // the 'edge' is measured from the first_sphere's center - F32 max_edge = 0.f; - F32 min_edge = 0.f; - - max_edge = llmax(max_edge + first_sphere.getRadius(), max_edge + distance + second_sphere.getRadius() + half_milimeter); - min_edge = llmin(min_edge - first_sphere.getRadius(), min_edge + distance - second_sphere.getRadius() - half_milimeter); - F32 radius = 0.5f * (max_edge - min_edge); - LLVector3 center = first_sphere.mCenter + (0.5f * (max_edge + min_edge)) * direction; - return LLSphere(center, radius); -} - -// static -// returns the bounding sphere that contains an arbitrary set of spheres -LLSphere LLSphere::getBoundingSphere(const std::vector<LLSphere>& sphere_list) -{ - // this algorithm can get relatively inaccurate when the sphere - // collection is 'small' (contained within a bounding sphere of about - // 2 meters or less) - // TODO -- improve the accuracy for small collections of spheres - - LLSphere bounding_sphere( LLVector3(0.f, 0.f, 0.f), 0.f ); - auto sphere_count = sphere_list.size(); - if (1 == sphere_count) - { - // trivial case -- single sphere - std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin(); - bounding_sphere = *sphere_itr; - } - else if (2 == sphere_count) - { - // trivial case -- two spheres - std::vector<LLSphere>::const_iterator first_sphere = sphere_list.begin(); - std::vector<LLSphere>::const_iterator second_sphere = first_sphere; - ++second_sphere; - bounding_sphere = LLSphere::getBoundingSphere(*first_sphere, *second_sphere); - } - else if (sphere_count > 0) - { - // non-trivial case -- we will approximate the solution - // - // NOTE -- there is a fancy/fast way to do this for large - // numbers of arbirary N-dimensional spheres -- you can look it - // up on the net. We're dealing with 3D spheres at collection - // sizes of 256 spheres or smaller, so we just use this - // brute force method. - - // TODO -- perhaps would be worthwile to test for the solution where - // the largest spanning radius just happens to work. That is, where - // there are really two spheres that determine the bounding sphere, - // and all others are contained therein. - - // compute the AABB - std::vector<LLSphere>::const_iterator first_itr = sphere_list.begin(); - LLVector3 max_corner = first_itr->getCenter() + first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f); - LLVector3 min_corner = first_itr->getCenter() - first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f); - { - std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin(); - for (++sphere_itr; sphere_itr != sphere_list.end(); ++sphere_itr) - { - LLVector3 center = sphere_itr->getCenter(); - F32 radius = sphere_itr->getRadius(); - for (S32 i=0; i<3; ++i) - { - if (center.mV[i] + radius > max_corner.mV[i]) - { - max_corner.mV[i] = center.mV[i] + radius; - } - if (center.mV[i] - radius < min_corner.mV[i]) - { - min_corner.mV[i] = center.mV[i] - radius; - } - } - } - } - - // get the starting center and radius from the AABB - LLVector3 diagonal = max_corner - min_corner; - F32 bounding_radius = 0.5f * diagonal.length(); - LLVector3 bounding_center = 0.5f * (max_corner + min_corner); - - // compute the starting step-size - F32 minimum_radius = 0.5f * llmin(diagonal.mV[VX], llmin(diagonal.mV[VY], diagonal.mV[VZ])); - F32 step_length = bounding_radius - minimum_radius; - //S32 step_count = 0; - //S32 max_step_count = 12; - F32 half_milimeter = 0.0005f; - - // wander the center around in search of tighter solutions - S32 last_dx = 2; // 2 is out of bounds --> no match - S32 last_dy = 2; - S32 last_dz = 2; - - while (step_length > half_milimeter - /*&& step_count < max_step_count*/) - { - // the algorithm for testing the maximum radius could be expensive enough - // that it makes sense to NOT duplicate testing when possible, so we keep - // track of where we last tested, and only test the new points - - S32 best_dx = 0; - S32 best_dy = 0; - S32 best_dz = 0; - - // sample near the center of the box - bool found_better_center = false; - for (S32 dx = -1; dx < 2; ++dx) - { - for (S32 dy = -1; dy < 2; ++dy) - { - for (S32 dz = -1; dz < 2; ++dz) - { - if (dx == 0 && dy == 0 && dz == 0) - { - continue; - } - - // count the number of indecies that match the last_*'s - S32 match_count = 0; - if (last_dx == dx) ++match_count; - if (last_dy == dy) ++match_count; - if (last_dz == dz) ++match_count; - if (match_count == 2) - { - // we've already tested this point - continue; - } - - LLVector3 center = bounding_center; - center.mV[VX] += (F32) dx * step_length; - center.mV[VY] += (F32) dy * step_length; - center.mV[VZ] += (F32) dz * step_length; - - // compute the radius of the bounding sphere - F32 max_radius = 0.f; - std::vector<LLSphere>::const_iterator sphere_itr; - for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr) - { - F32 radius = (sphere_itr->getCenter() - center).length() + sphere_itr->getRadius(); - if (radius > max_radius) - { - max_radius = radius; - } - } - if (max_radius < bounding_radius) - { - best_dx = dx; - best_dy = dy; - best_dz = dz; - bounding_center = center; - bounding_radius = max_radius; - found_better_center = true; - } - } - } - } - if (found_better_center) - { - // remember where we came from so we can avoid retesting - last_dx = -best_dx; - last_dy = -best_dy; - last_dz = -best_dz; - } - else - { - // reduce the step size - step_length *= 0.5f; - //++step_count; - // reset the last_*'s - last_dx = 2; // 2 is out of bounds --> no match - last_dy = 2; - last_dz = 2; - } - } - - // HACK -- it is possible to get enough floating point error for the - // bounding sphere to too small on the order of 10e-6, but we only need - // it to be accurate to within about half a millimeter - bounding_radius += half_milimeter; - - // this algorithm can get relatively inaccurate when the sphere - // collection is 'small' (contained within a bounding sphere of about - // 2 meters or less) - // TODO -- fix this - /* debug code - { - std::vector<LLSphere>::const_iterator sphere_itr; - for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr) - { - F32 radius = (sphere_itr->getCenter() - bounding_center).length() + sphere_itr->getRadius(); - if (radius + 0.1f > bounding_radius) - { - std::cout << " rad = " << radius << " bounding - rad = " << (bounding_radius - radius) << std::endl; - } - } - std::cout << "\n" << std::endl; - } - */ - - bounding_sphere.set(bounding_center, bounding_radius); - } - return bounding_sphere; -} - - diff --git a/indra/llmath/llsphere.h b/indra/llmath/llsphere.h deleted file mode 100644 index cb923dcd3c..0000000000 --- a/indra/llmath/llsphere.h +++ /dev/null @@ -1,77 +0,0 @@ -// llsphere.h -/** - * @file llsphere.cpp - * @author Andrew Meadows - * @brief Simple sphere implementation for basic geometric operations - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_SPHERE_H -#define LL_SPHERE_H - -#include "stdtypes.h" -#include "v3math.h" -#include <iostream> -#include <vector> - -class LLSphere -{ -public: - LLSphere(); - LLSphere( const LLVector3& center, F32 radius ); - - void set( const LLVector3& center, F32 radius ); - void setCenter( const LLVector3& center ); - void setRadius( F32 radius ); - - const LLVector3& getCenter() const; - F32 getRadius() const; - - // returns true if this sphere completely contains other_sphere - bool contains(const LLSphere& other_sphere) const; - - // returns true if this sphere overlaps other_sphere - bool overlaps(const LLSphere& other_sphere) const; - - // returns overlap distance - // negative overlap is closest approach - F32 getOverlap(const LLSphere& other_sphere) const; - - // removes any spheres that are contained in others - static void collapse(std::vector<LLSphere>& sphere_list); - - // returns minimum sphere bounding sphere for a set of spheres - static LLSphere getBoundingSphere(const LLSphere& first_sphere, const LLSphere& second_sphere); - static LLSphere getBoundingSphere(const std::vector<LLSphere>& sphere_list); - - bool operator==(const LLSphere& rhs) const; - - friend std::ostream& operator<<( std::ostream& output_stream, const LLSphere& line ); - -protected: - LLVector3 mCenter; - F32 mRadius; -}; - - -#endif diff --git a/indra/llmath/llvector4a.cpp b/indra/llmath/llvector4a.cpp index df20585d16..c1845b0d94 100644 --- a/indra/llmath/llvector4a.cpp +++ b/indra/llmath/llvector4a.cpp @@ -111,7 +111,7 @@ void LLVector4a::quantize8( const LLVector4a& low, const LLVector4a& high ) // 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 }; +// alignas(16) static 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 @@ -156,7 +156,7 @@ void LLVector4a::quantize16( const LLVector4a& low, const LLVector4a& high ) // 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 }; + alignas(16) static const F32 F_TWO_4A[4] = { 2.f, 2.f, 2.f, 2.f }; ll_assert_aligned(F_TWO_4A,16); LLVector4a two; two.load4a( F_TWO_4A ); diff --git a/indra/llmath/llvector4a.inl b/indra/llmath/llvector4a.inl index 0f7c4123ac..e6153a27e6 100644 --- a/indra/llmath/llvector4a.inl +++ b/indra/llmath/llvector4a.inl @@ -196,7 +196,7 @@ inline void LLVector4a::setDiv(const LLVector4a& a, const LLVector4a& b) // 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 }; + alignas(16) static const U32 F_ABS_MASK_4A[4] = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }; mQ = _mm_and_ps(src.mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A)); } @@ -463,7 +463,7 @@ inline void LLVector4a::normalize3fast_checked(LLVector4a* d) // 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 }; + alignas(16) static 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 ); @@ -475,7 +475,7 @@ inline LLBool32 LLVector4a::isNormalized3( F32 tolerance ) const // 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 }; + alignas(16) static 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 ); @@ -522,7 +522,7 @@ inline void LLVector4a::setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F3 inline LLBool32 LLVector4a::isFinite3() const { - static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 }; + alignas(16) static const U32 nanOrInfMask[4] = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 }; ll_assert_aligned(nanOrInfMask,16); const __m128i nanOrInfMaskV = *reinterpret_cast<const __m128i*> (nanOrInfMask); const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV ); @@ -532,7 +532,7 @@ inline LLBool32 LLVector4a::isFinite3() const inline LLBool32 LLVector4a::isFinite4() const { - static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 }; + alignas(16) static 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 )); diff --git a/indra/llmath/llvector4logical.h b/indra/llmath/llvector4logical.h index 77cb5862e5..411971c42d 100644 --- a/indra/llmath/llvector4logical.h +++ b/indra/llmath/llvector4logical.h @@ -39,7 +39,7 @@ // contact someone with SSE experience (Falcon, Richard, Davep, e.g.) //////////////////////////// -static LL_ALIGN_16(const U32 S_V4LOGICAL_MASK_TABLE[4*4]) = +alignas(16) static const U32 S_V4LOGICAL_MASK_TABLE[4*4] = { 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, @@ -77,7 +77,7 @@ public: // Invert this mask inline LLVector4Logical& invert() { - static const LL_ALIGN_16(U32 allOnes[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + alignas(16) static const U32 allOnes[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; ll_assert_aligned(allOnes,16); mQ = _mm_andnot_ps( mQ, *(LLQuad*)(allOnes) ); return *this; diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 4660026d40..c49d5ff7d3 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -52,6 +52,7 @@ #include "llmeshoptimizer.h" #include "lltimer.h" #include "llvolumeoctree.h" +#include "workqueue.h" #include "mikktspace/mikktspace.hh" @@ -1971,6 +1972,8 @@ LLVolume::LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool ge mNumHullIndices = 0; // set defaults + mSculptValidationCache.fill(LLSculptValidationState::Unvalidated); + if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE) { mPathp = new LLDynamicPath(); @@ -1998,6 +2001,13 @@ void LLVolume::resizePath(S32 length) setDirty(); } +void LLVolume::setDirty() +{ + mPathp->setDirty(); + mProfilep->setDirty(); + mSculptValidationCache.fill(LLSculptValidationState::Unvalidated); +} + void LLVolume::regen() { generate(); @@ -2292,30 +2302,149 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + // Sanity-check before even trying to decompress + constexpr S32 MAX_MESH_COMPRESSED_SIZE = 128 * 1024 * 1024; // 128 MB + const LLUUID& mesh_id = getParams().getSculptID(); + + if (size <= 0 || size > MAX_MESH_COMPRESSED_SIZE) + { + LL_WARNS("MeshStreaming") << "Rejecting implausible compressed mesh size " << size + << " for mesh id " << mesh_id << LL_ENDL; + return false; + } + //input stream is now pointing at a zlib compressed block of LLSD //decompress block - LLSD mdl; - U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, is, size); - if (uzip_result != LLUZipHelper::ZR_OK) + try { - LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL; + LLSD mdl; + U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, is, size); + if (uzip_result != LLUZipHelper::ZR_OK) + { + LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL; + return false; + } + return unpackVolumeFacesInternal(mdl); + } + catch (const std::bad_alloc&) + { + constexpr S32 SMALL_MESH_THRESHOLD = 4000; + if (size < SMALL_MESH_THRESHOLD) + { + // showOutOfMemory and LL_ERRS must run on the main thread. + // Post to mainloop WorkQueue, mirroring LLThread::tryRun(). + LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop"); + bool done = false; + if (main_queue) + { + const LLUUID mesh_id_copy = mesh_id; // capture by value for the lambda + done = main_queue->post([mesh_id_copy, size]() + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS("MeshStreaming") << "Out of memory unpacking mesh id " << mesh_id_copy + << " of compressed size " << size << LL_ENDL; + }); + } + if (!done) + { + // No main queue available (e.g. during shutdown) + LL_WARNS("MeshStreaming") << "Out of memory unpacking mesh id " << mesh_id + << " of compressed size " << size << " (main queue unavailable)" << LL_ENDL; + } + } + else + { + LL_WARNS("MeshStreaming") << "Out of memory unpacking mesh id " << mesh_id + << " of compressed size " << size << LL_ENDL; + } + return false; + } + catch (const std::exception& e) + { + LL_WARNS("MeshStreaming") << "Exception unpacking mesh id " << mesh_id + << " of compressed size " << size << ": " << e.what() << LL_ENDL; + return false; + } + catch (...) + { + LL_WARNS("MeshStreaming") << "Unknown exception unpacking mesh id " << mesh_id + << " of compressed size " << size << LL_ENDL; return false; } - return unpackVolumeFacesInternal(mdl); } bool LLVolume::unpackVolumeFaces(U8* in_data, S32 size) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + constexpr S32 MAX_MESH_COMPRESSED_SIZE = 128 * 1024 * 1024; // 128 MB + const LLUUID& mesh_id = getParams().getSculptID(); + + if (!in_data || size <= 0 || size > MAX_MESH_COMPRESSED_SIZE) + { + LL_WARNS("MeshStreaming") << "Rejecting implausible compressed mesh size " << size + << " for mesh id " << mesh_id << LL_ENDL; + return false; + } + //input data is now pointing at a zlib compressed block of LLSD //decompress block - LLSD mdl; - U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, in_data, size); - if (uzip_result != LLUZipHelper::ZR_OK) + try { - LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL; + LLSD mdl; + U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, in_data, size); + if (uzip_result != LLUZipHelper::ZR_OK) + { + LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD, mesh id " << mesh_id + << ", code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL; + return false; + } + return unpackVolumeFacesInternal(mdl); + } + catch (const std::bad_alloc&) + { + constexpr S32 SMALL_MESH_THRESHOLD = 4000; + if (size < SMALL_MESH_THRESHOLD) + { + // showOutOfMemory and LL_ERRS must run on the main thread. + // Post to mainloop WorkQueue, mirroring LLThread::tryRun(). + LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop"); + bool done = false; + if (main_queue) + { + const LLUUID mesh_id_copy = mesh_id; // capture by value for the lambda + done = main_queue->post([mesh_id_copy, size]() + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS("MeshStreaming") << "Out of memory unpacking mesh id " << mesh_id_copy + << " of compressed size " << size << LL_ENDL; + }); + } + if (!done) + { + // No main queue available (e.g. during shutdown) + LL_WARNS("MeshStreaming") << "Out of memory unpacking mesh id " << mesh_id + << " of compressed size " << size << " (main queue unavailable)" << LL_ENDL; + } + } + else + { + LL_WARNS("MeshStreaming") << "Out of memory unpacking mesh id " << mesh_id + << " of compressed size " << size << LL_ENDL; + } + return false; + } + catch (const std::exception& e) + { + LL_WARNS("MeshStreaming") << "Exception unpacking mesh id " << mesh_id + << " of compressed size " << size << ": " << e.what() << LL_ENDL; + return false; + } + catch (...) + { + LL_WARNS("MeshStreaming") << "Unknown exception unpacking mesh id " << mesh_id + << " of compressed size " << size << LL_ENDL; return false; } - return unpackVolumeFacesInternal(mdl); } bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) @@ -2348,7 +2477,9 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) const LLSD::Binary& pos = mdl[i]["Position"].asBinary(); const LLSD::Binary& norm = mdl[i]["Normal"].asBinary(); +#if 0 // keep this code for now in case we decide to add support for on-the-wire tangents const LLSD::Binary& tangent = mdl[i]["Tangent"].asBinary(); +#endif const LLSD::Binary& tc = mdl[i]["TexCoord0"].asBinary(); const LLSD::Binary& idx = mdl[i]["TriangleList"].asBinary(); @@ -2383,11 +2514,31 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) //copy out vertices U32 num_verts = static_cast<U32>(pos.size())/(3*2); + if (num_verts == 0) + { + LL_WARNS() << "Zero vertices for face index: " << i << LL_ENDL; + face.resizeIndices(3); + face.resizeVertices(1); + face.mPositions->clear(); + face.mNormals->clear(); + face.mTexCoords->setZero(); + memset(face.mIndices, 0, sizeof(U16) * 3); + continue; + } + + if (num_verts > 65535) // U16 indices + { + LL_WARNS() << "Invalid vertex count " << num_verts << " exceeds maximum for face index: " << i << LL_ENDL; + mVolumeFaces.clear(); + return false; + } + face.resizeVertices(num_verts); if (num_verts > 0 && !face.mPositions) { LL_WARNS() << "Failed to allocate " << num_verts << " vertices for face index: " << i << " Total: " << face_count << LL_ENDL; + face.resizeVertices(0); face.resizeIndices(0); continue; } @@ -3138,10 +3289,100 @@ S32 sculpt_sides(F32 detail) } } +static bool validate_sculpt_geometry(const LLVolume* volume) +{ + if (!volume || volume->getNumVolumeFaces() == 0) + { + return true; + } + + // Validate each face in the sculpt + for (S32 face_idx = 0; face_idx < volume->getNumVolumeFaces(); ++face_idx) + { + const LLVolumeFace& face = volume->getVolumeFace(face_idx); + + // Skip validation for degenerate faces + if (face.mNumVertices < 3 || face.mNumIndices < 3) + { + continue; + } + + // Calculate 'bounding' surface + LLVector4a extent_size; + extent_size.setSub(face.mExtents[1], face.mExtents[0]); + + F32 width = extent_size[0]; + F32 height = extent_size[1]; + F32 depth = extent_size[2]; + + F32 bounding_surface = width * height + width * depth + height * depth; + bounding_surface *= 2; + + if (!llfinite(bounding_surface) || bounding_surface <= FLT_EPSILON) + { + return false; + } + + // Calculate total surface area of all triangles + F32 total_triangle_area = 0.0f; + S32 num_triangles = face.mNumIndices / 3; + + for (S32 i = 0; i < num_triangles; ++i) + { + S32 idx_base = i * 3; + U16 idx0 = face.mIndices[idx_base]; + U16 idx1 = face.mIndices[idx_base + 1]; + U16 idx2 = face.mIndices[idx_base + 2]; + + // Validate indices + if (idx0 >= face.mNumVertices || idx1 >= face.mNumVertices || idx2 >= face.mNumVertices) + { + continue; + } + const LLVector4a& v0 = face.mPositions[idx0]; + const LLVector4a& v1 = face.mPositions[idx1]; + const LLVector4a& v2 = face.mPositions[idx2]; + + // Calculate triangle area + LLVector4a edge1, edge2, cross; + edge1.setSub(v1, v0); + edge2.setSub(v2, v0); + cross.setCross3(edge1, edge2); + + F32 triangle_area = cross.getLength3().getF32() * 0.5f; + total_triangle_area += triangle_area; + } + + if (!llfinite(total_triangle_area)) + { + return false; + } + + // The idea here is that the total area of a sculpt is normally comparable + // to the area of a bounding box. But a random overlapping collection of + // triangles has a significantly larger area than a sculpt normally has. + F32 area_ratio = total_triangle_area / bounding_surface; + + constexpr F32 MAX_AREA_TO_AREA_RATIO = 16.0f; + if (area_ratio > MAX_AREA_TO_AREA_RATIO) + { + LL_DEBUGS("LLVOLUME") << "Sculpt rejected: excessive triangle area." + << " Face index: " << face_idx + << " Total triangle area: " << total_triangle_area << " sq m" + << " Bounding surface area: " << bounding_surface << " sq m" + << " Ratio: " << area_ratio + << " Triangle count: " << num_triangles + << " Bounds: [" << width << " x " << height << " x " << depth << "]" << LL_ENDL; + return false; + } + } + + return true; +} // determine the number of vertices in both s and t direction for this sculpt -void sculpt_calc_mesh_resolution(U16 width, U16 height, U8 type, F32 detail, S32& s, S32& t) +static void sculpt_calc_mesh_resolution(U16 width, U16 height, U8 type, F32 detail, S32& s, S32& t) { // this code has the following properties: // 1) the aspect ratio of the mesh is as close as possible to the ratio of the map @@ -3251,10 +3492,62 @@ void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, mSculptLevel = sculpt_level; - // Delete any existing faces so that they get regenerated - mVolumeFaces.clear(); + LLSculptValidationState cached_state = LLSculptValidationState::Valid; + if (!data_is_empty && (sculpt_level >= 0) && sculpt_level < SCULPT_CACHE_SIZE) + { + // validation might be expensive, so we do it only once per lod. + cached_state = mSculptValidationCache[sculpt_level]; + } - createVolumeFaces(); + switch (cached_state) + { + case LLSculptValidationState::Valid: + { + // Delete any existing faces, then regenerate + mVolumeFaces.clear(); + createVolumeFaces(); + break; + } + case LLSculptValidationState::Invalid: + { + // Invalid geometry + // Regenerate mesh with an empty placeholder + mVolumeFaces.clear(); + sculptGenerateEmptyPlaceholder(); + createVolumeFaces(); + break; + } + case LLSculptValidationState::Unvalidated: + { + // First time at this LOD + mVolumeFaces.clear(); + createVolumeFaces(); + + bool valid_geometry = true; + + // Todo: can this be backed into createVolumeFaces? + // Or calculated without having to call createVolumeFaces first? + // Todo 2: should the same be done for meshes? + valid_geometry = validate_sculpt_geometry(this); + mSculptValidationCache[sculpt_level] = valid_geometry ? LLSculptValidationState::Valid : LLSculptValidationState::Invalid; + + if (!valid_geometry) + { + LL_WARNS("LLVOLUME") << "Sculpt failed geometry validation - either invalid image or malicious. " + << "Replacing with placeholder. Image: " << mParams.getSculptID() << LL_ENDL; + + // Clear the malicious faces + mVolumeFaces.clear(); + + // Regenerate mesh with empty placeholder + sculptGenerateEmptyPlaceholder(); + + // Recreate faces with placeholder geometry + createVolumeFaces(); + } + break; + } + } //switch (cached_state) } @@ -5677,7 +5970,40 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) mOptimized = true; if (gen_tangents && mNormals && mTexCoords) - { // generate mikkt space tangents before cache optimizing since the index buffer may change + { + if (!mPositions || !mIndices || mNumVertices <= 0 || mNumIndices <= 0) + { + LL_WARNS_ONCE("LLVolume") << "Invalid volume face data for tangent generation: " + << "mPositions=" << (void*)mPositions + << ", mIndices=" << (void*)mIndices + << ", mNumVertices=" << mNumVertices + << ", mNumIndices=" << mNumIndices << LL_ENDL; + return false; + } + + if (mNumIndices % 3 != 0) + { + LL_WARNS_ONCE("LLVolume") << "Non-triangulated mesh, mNumIndices=" << mNumIndices << LL_ENDL; + return false; + } + + for (S32 i = 0; i < mNumIndices; ++i) + { + if (mIndices[i] >= mNumVertices) + { + LL_WARNS_ONCE("LLVolume") << "Out of bounds index detected: mIndices[" << i << "]=" + << mIndices[i] << " >= mNumVertices=" << mNumVertices << LL_ENDL; + return false; + } + } + + if (mNormalizedScale.mV[0] == 0.0f || mNormalizedScale.mV[1] == 0.0f || mNormalizedScale.mV[2] == 0.0f) + { + LL_WARNS_ONCE("LLVolume") << "Invalid normalized scale: " << mNormalizedScale << LL_ENDL; + return false; + } + + // generate mikkt space tangents before cache optimizing since the index buffer may change // a bit of a hack to do this here, but this function gets called exactly once for the lifetime of a mesh // and is executed on a background thread MikktData data(this); diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 3496928f7b..86a7bf0e6a 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -60,6 +60,8 @@ class LLVolumeOctree; #include "llalignedarray.h" #include "llrigginginfo.h" +#include <array> + //============================================================================ constexpr S32 MIN_DETAIL_FACES = 6; @@ -1040,7 +1042,7 @@ public: const LLVector4a& getMeshPt(const U32 i) const { return mMesh[i]; } - void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); } + void setDirty(); void regen(); void genTangents(S32 face); @@ -1131,6 +1133,11 @@ public: bool mIsMeshAssetLoaded; bool mIsMeshAssetUnavaliable; + // Cache for sculpt geometry validation results + static constexpr S32 SCULPT_CACHE_SIZE = 6; // 0 to 5 inclusive + enum class LLSculptValidationState : S8 { Unvalidated, Valid, Invalid }; + std::array<LLSculptValidationState, SCULPT_CACHE_SIZE> mSculptValidationCache; + const LLVolumeParams mParams; LLPath *mPathp; LLProfile *mProfilep; diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h index b6b7e22517..0250bf8bfb 100644 --- a/indra/llmath/llvolumeoctree.h +++ b/indra/llmath/llvolumeoctree.h @@ -53,7 +53,7 @@ public: } - LL_ALIGN_16(LLVector4a mPositionGroup); + LLVector4a mPositionGroup; const LLVector4a* mV[3]; U32 mIndex[3]; @@ -91,8 +91,8 @@ public: public: - LL_ALIGN_16(LLVector4a mBounds[2]); // bounding box (center, size) of this node and all its children (tight fit to objects) - LL_ALIGN_16(LLVector4a mExtents[2]); // extents (min, max) of this node and all its children + LLVector4a mBounds[2]; // bounding box (center, size) of this node and all its children (tight fit to objects) + LLVector4a mExtents[2]; // extents (min, max) of this node and all its children }; class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*> diff --git a/indra/llmath/tests/alignment_test.cpp b/indra/llmath/tests/alignment_test.cpp index eb6fa4a3b8..f41ebe82aa 100644 --- a/indra/llmath/tests/alignment_test.cpp +++ b/indra/llmath/tests/alignment_test.cpp @@ -46,8 +46,7 @@ typedef test_group<alignment_test> alignment_test_t; typedef alignment_test_t::object alignment_test_object_t; tut::alignment_test_t tut_alignment_test("LLAlignment"); -LL_ALIGN_PREFIX(16) -class MyVector4a +class alignas(16) MyVector4a { public: void* operator new(size_t size) @@ -71,7 +70,7 @@ public: } LLQuad mQ; -} LL_ALIGN_POSTFIX(16); +}; // Verify that aligned allocators perform as advertised. diff --git a/indra/llmath/tests/mathmisc_test.cpp b/indra/llmath/tests/mathmisc_test.cpp index ff0899e975..810b0d80da 100644 --- a/indra/llmath/tests/mathmisc_test.cpp +++ b/indra/llmath/tests/mathmisc_test.cpp @@ -35,7 +35,6 @@ #include "../llline.h" #include "../llmath.h" -#include "../llsphere.h" #include "../v3math.h" namespace tut @@ -280,166 +279,6 @@ namespace tut namespace tut { - struct sphere_data - { - }; - typedef test_group<sphere_data> sphere_test; - typedef sphere_test::object sphere_object; - tut::sphere_test tsphere("LLSphere"); - - template<> template<> - void sphere_object::test<1>() - { - // test LLSphere::contains() and ::overlaps() - S32 number_of_tests = 10; - for (S32 test = 0; test < number_of_tests; ++test) - { - LLVector3 first_center(1.f, 1.f, 1.f); - F32 first_radius = 3.f; - LLSphere first_sphere( first_center, first_radius ); - - F32 half_millimeter = 0.0005f; - LLVector3 direction( ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f); - direction.normalize(); - - F32 distance = ll_frand(first_radius - 2.f * half_millimeter); - LLVector3 second_center = first_center + distance * direction; - F32 second_radius = first_radius - distance - half_millimeter; - LLSphere second_sphere( second_center, second_radius ); - ensure("first sphere should contain the second", first_sphere.contains(second_sphere)); - ensure("first sphere should overlap the second", first_sphere.overlaps(second_sphere)); - - distance = first_radius + ll_frand(first_radius); - second_center = first_center + distance * direction; - second_radius = distance - first_radius + half_millimeter; - second_sphere.set( second_center, second_radius ); - ensure("first sphere should NOT contain the second", !first_sphere.contains(second_sphere)); - ensure("first sphere should overlap the second", first_sphere.overlaps(second_sphere)); - - distance = first_radius + ll_frand(first_radius) + half_millimeter; - second_center = first_center + distance * direction; - second_radius = distance - first_radius - half_millimeter; - second_sphere.set( second_center, second_radius ); - ensure("first sphere should NOT contain the second", !first_sphere.contains(second_sphere)); - ensure("first sphere should NOT overlap the second", !first_sphere.overlaps(second_sphere)); - } - } - - template<> template<> - void sphere_object::test<2>() - { - skip("See SNOW-620. Neither the test nor the code being tested seem good. Also sim-only."); - - // test LLSphere::getBoundingSphere() - S32 number_of_tests = 100; - S32 number_of_spheres = 10; - F32 sphere_center_range = 32.f; - F32 sphere_radius_range = 5.f; - - for (S32 test = 0; test < number_of_tests; ++test) - { - // gegnerate a bunch of random sphere - std::vector< LLSphere > sphere_list; - for (S32 sphere_count=0; sphere_count < number_of_spheres; ++sphere_count) - { - LLVector3 direction( ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f); - direction.normalize(); - F32 distance = ll_frand(sphere_center_range); - LLVector3 center = distance * direction; - F32 radius = ll_frand(sphere_radius_range); - LLSphere sphere( center, radius ); - sphere_list.push_back(sphere); - } - - // compute the bounding sphere - LLSphere bounding_sphere = LLSphere::getBoundingSphere(sphere_list); - - // make sure all spheres are inside the bounding sphere - { - std::vector< LLSphere >::const_iterator sphere_itr; - for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr) - { - ensure("sphere should be contained by the bounding sphere", bounding_sphere.contains(*sphere_itr)); - } - } - - // TODO -- improve LLSphere::getBoundingSphere() to the point where - // we can reduce the 'expansion' in the two tests below to about - // 2 mm or less - - F32 expansion = 0.005f; - // move all spheres out a little bit - // and count how many are NOT contained - { - std::vector< LLVector3 > uncontained_directions; - std::vector< LLSphere >::iterator sphere_itr; - for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr) - { - LLVector3 direction = sphere_itr->getCenter() - bounding_sphere.getCenter(); - direction.normalize(); - - sphere_itr->setCenter( sphere_itr->getCenter() + expansion * direction ); - if (! bounding_sphere.contains( *sphere_itr ) ) - { - uncontained_directions.push_back(direction); - } - } - ensure("when moving spheres out there should be at least two uncontained spheres", - uncontained_directions.size() > 1); - - /* TODO -- when the bounding sphere algorithm is improved we can open up this test - * at the moment it occasionally fails when the sphere collection is tight and small - * (2 meters or less) - if (2 == uncontained_directions.size() ) - { - // if there were only two uncontained spheres then - // the two directions should be nearly opposite - F32 dir_dot = uncontained_directions[0] * uncontained_directions[1]; - ensure("two uncontained spheres should lie opposite the bounding center", dir_dot < -0.95f); - } - */ - } - - // compute the new bounding sphere - bounding_sphere = LLSphere::getBoundingSphere(sphere_list); - - // increase the size of all spheres a little bit - // and count how many are NOT contained - { - std::vector< LLVector3 > uncontained_directions; - std::vector< LLSphere >::iterator sphere_itr; - for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr) - { - LLVector3 direction = sphere_itr->getCenter() - bounding_sphere.getCenter(); - direction.normalize(); - - sphere_itr->setRadius( sphere_itr->getRadius() + expansion ); - if (! bounding_sphere.contains( *sphere_itr ) ) - { - uncontained_directions.push_back(direction); - } - } - ensure("when boosting sphere radii there should be at least two uncontained spheres", - uncontained_directions.size() > 1); - - /* TODO -- when the bounding sphere algorithm is improved we can open up this test - * at the moment it occasionally fails when the sphere collection is tight and small - * (2 meters or less) - if (2 == uncontained_directions.size() ) - { - // if there were only two uncontained spheres then - // the two directions should be nearly opposite - F32 dir_dot = uncontained_directions[0] * uncontained_directions[1]; - ensure("two uncontained spheres should lie opposite the bounding center", dir_dot < -0.95f); - } - */ - } - } - } -} - -namespace tut -{ F32 SMALL_RADIUS = 1.0f; F32 MEDIUM_RADIUS = 5.0f; F32 LARGE_RADIUS = 10.0f; |
