diff options
author | AndreyL ProductEngine <alihatskiy@productengine.com> | 2018-11-14 22:47:31 +0200 |
---|---|---|
committer | AndreyL ProductEngine <alihatskiy@productengine.com> | 2018-11-14 22:47:31 +0200 |
commit | c21396181b090b626d7a9f989bcead2e517bb75f (patch) | |
tree | c543941ee817a5b5952ff670d8ae03cd4663bd88 /indra/llmath | |
parent | 62085f9f7904aa20406aa4f0122065aec3a786ee (diff) | |
parent | 8558ce5c600b810356010ba3cd6d534ef22f4081 (diff) |
Merged in lindenlab/viewer-release
Diffstat (limited to 'indra/llmath')
-rw-r--r-- | indra/llmath/CMakeLists.txt | 3 | ||||
-rw-r--r-- | indra/llmath/llmatrix4a.cpp | 80 | ||||
-rw-r--r-- | indra/llmath/llmatrix4a.h | 14 | ||||
-rw-r--r-- | indra/llmath/llrigginginfo.cpp | 159 | ||||
-rw-r--r-- | indra/llmath/llrigginginfo.h | 100 | ||||
-rw-r--r-- | indra/llmath/llvector4a.h | 7 | ||||
-rw-r--r-- | indra/llmath/llvolume.cpp | 104 | ||||
-rw-r--r-- | indra/llmath/llvolume.h | 7 | ||||
-rw-r--r-- | indra/llmath/v3math.cpp | 36 | ||||
-rw-r--r-- | indra/llmath/v3math.h | 2 |
10 files changed, 411 insertions, 101 deletions
diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 4c8bcdac91..379c3ee9ea 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -20,10 +20,12 @@ set(llmath_SOURCE_FILES llcoordframe.cpp llline.cpp llmatrix3a.cpp + llmatrix4a.cpp llmodularmath.cpp lloctree.cpp llperlin.cpp llquaternion.cpp + llrigginginfo.cpp llrect.cpp llsphere.cpp llvector4a.cpp @@ -70,6 +72,7 @@ set(llmath_HEADER_FILES llquaternion2.h llquaternion2.inl llrect.h + llrigginginfo.h llsimdmath.h llsimdtypes.h llsimdtypes.inl diff --git a/indra/llmath/llmatrix4a.cpp b/indra/llmath/llmatrix4a.cpp new file mode 100644 index 0000000000..fe8f0b98f3 --- /dev/null +++ b/indra/llmath/llmatrix4a.cpp @@ -0,0 +1,80 @@ +/** +* @file llmatrix4a.cpp +* @brief Functions for vectorized matrix/vector operations +* +* $LicenseInfo:firstyear=2018&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, 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 "llmath.h" +#include "llmatrix4a.h" + +// Convert a bounding box into other coordinate system. Should give +// the same results as transforming every corner of the bounding box +// and extracting the bounding box of that, although that's not +// necessarily the fastest way to implement. +void matMulBoundBox(const LLMatrix4a &mat, const LLVector4a *in_extents, LLVector4a *out_extents) +{ + //get 8 corners of bounding box + LLVector4Logical mask[6]; + + for (U32 i = 0; i < 6; ++i) + { + mask[i].clear(); + } + + mask[0].setElement<2>(); //001 + mask[1].setElement<1>(); //010 + mask[2].setElement<1>(); //011 + mask[2].setElement<2>(); + mask[3].setElement<0>(); //100 + mask[4].setElement<0>(); //101 + mask[4].setElement<2>(); + mask[5].setElement<0>(); //110 + mask[5].setElement<1>(); + + LLVector4a v[8]; + + v[6] = in_extents[0]; + v[7] = in_extents[1]; + + for (U32 i = 0; i < 6; ++i) + { + v[i].setSelectWithMask(mask[i], in_extents[0], in_extents[1]); + } + + LLVector4a tv[8]; + + //transform bounding box into drawable space + for (U32 i = 0; i < 8; ++i) + { + mat.affineTransform(v[i], tv[i]); + } + + //find bounding box + out_extents[0] = out_extents[1] = tv[0]; + + for (U32 i = 1; i < 8; ++i) + { + out_extents[0].setMin(out_extents[0], tv[i]); + out_extents[1].setMax(out_extents[1], tv[i]); + } +} diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h index 216334752a..7ba347062f 100644 --- a/indra/llmath/llmatrix4a.h +++ b/indra/llmath/llmatrix4a.h @@ -121,7 +121,7 @@ public: res.add(z); } - inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res) + inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res) const { LLVector4a x,y,z; @@ -138,7 +138,7 @@ public: res.setAdd(x,z); } - inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res) + inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res) const { F32 x = v[0] * mMatrix[0][0] + v[1] * mMatrix[1][0] + v[2] * mMatrix[2][0] + mMatrix[3][0]; F32 y = v[0] * mMatrix[0][1] + v[1] * mMatrix[1][1] + v[2] * mMatrix[2][1] + mMatrix[3][1]; @@ -147,7 +147,7 @@ public: res.set(x,y,z,w); } - inline void affineTransform(const LLVector4a& v, LLVector4a& res) + inline void affineTransform(const LLVector4a& v, LLVector4a& res) const { affineTransformSSE(v,res); } @@ -176,4 +176,12 @@ inline void matMul(const LLMatrix4a &a, const LLMatrix4a &b, LLMatrix4a &res) res.mMatrix[3] = row3; } +inline std::ostream& operator<<(std::ostream& s, const LLMatrix4a& m) +{ + s << "[" << m.mMatrix[0] << ", " << m.mMatrix[1] << ", " << m.mMatrix[2] << ", " << m.mMatrix[3] << "]"; + return s; +} + +void matMulBoundBox(const LLMatrix4a &a, const LLVector4a *in_extents, LLVector4a *out_extents); + #endif diff --git a/indra/llmath/llrigginginfo.cpp b/indra/llmath/llrigginginfo.cpp new file mode 100644 index 0000000000..0de07950c1 --- /dev/null +++ b/indra/llmath/llrigginginfo.cpp @@ -0,0 +1,159 @@ +/** +* @file llrigginginfo.cpp +* @brief Functions for tracking rigged box extents +* +* $LicenseInfo:firstyear=2018&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, 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 "llmath.h" +#include "llrigginginfo.h" + +//----------------------------------------------------------------------------- +// LLJointRiggingInfo +//----------------------------------------------------------------------------- +LLJointRiggingInfo::LLJointRiggingInfo() +{ + mRiggedExtents[0].clear(); + mRiggedExtents[1].clear(); + mIsRiggedTo = false; +} + +bool LLJointRiggingInfo::isRiggedTo() const +{ + return mIsRiggedTo; +} + +void LLJointRiggingInfo::setIsRiggedTo(bool val) +{ + mIsRiggedTo = val; +} + +LLVector4a *LLJointRiggingInfo::getRiggedExtents() +{ + return mRiggedExtents; +} + +const LLVector4a *LLJointRiggingInfo::getRiggedExtents() const +{ + return mRiggedExtents; +} + +// Combine two rigging info states. +// - isRiggedTo if either of the source infos are rigged to +// - box is union of the two sources +void LLJointRiggingInfo::merge(const LLJointRiggingInfo& other) +{ + if (other.mIsRiggedTo) + { + if (mIsRiggedTo) + { + // Combine existing boxes + update_min_max(mRiggedExtents[0], mRiggedExtents[1], other.mRiggedExtents[0]); + update_min_max(mRiggedExtents[0], mRiggedExtents[1], other.mRiggedExtents[1]); + } + else + { + // Initialize box + mIsRiggedTo = true; + mRiggedExtents[0] = other.mRiggedExtents[0]; + mRiggedExtents[1] = other.mRiggedExtents[1]; + } + } +} + +LLJointRiggingInfoTab::LLJointRiggingInfoTab(): + mRigInfoPtr(NULL), + mSize(0), + mNeedsUpdate(true) +{ +} + +LLJointRiggingInfoTab::~LLJointRiggingInfoTab() +{ + clear(); +} + +// This doesn't preserve data if the size changes. In practice +// this doesn't matter because the size is always either +// LL_CHARACTER_MAX_ANIMATED_JOINTS or 0. +void LLJointRiggingInfoTab::resize(S32 size) +{ + if (size != mSize) + { + clear(); + if (size > 0) + { + mRigInfoPtr = new LLJointRiggingInfo[size]; + mSize = size; + } + } +} + +void LLJointRiggingInfoTab::clear() +{ + if (mRigInfoPtr) + { + delete[](mRigInfoPtr); + mRigInfoPtr = NULL; + mSize = 0; + } +} + +void showDetails(const LLJointRiggingInfoTab& src, const std::string& str) +{ + S32 count_rigged = 0; + S32 count_box = 0; + LLVector4a zero_vec; + zero_vec.clear(); + for (S32 i=0; i<src.size(); i++) + { + if (src[i].isRiggedTo()) + { + count_rigged++; + if ((!src[i].getRiggedExtents()[0].equals3(zero_vec)) || + (!src[i].getRiggedExtents()[1].equals3(zero_vec))) + { + count_box++; + } + } + } + LL_DEBUGS("RigSpammish") << "details: " << str << " has " << count_rigged << " rigged joints, of which " << count_box << " are non-empty" << LL_ENDL; +} + +void LLJointRiggingInfoTab::merge(const LLJointRiggingInfoTab& src) +{ + //showDetails(*this, "input this"); + // Size should be either LL_CHARACTER_MAX_ANIMATED_JOINTS, or 0 if + // no data. Not necessarily the same for both inputs. + if (src.size() > size()) + { + resize(src.size()); + } + S32 min_size = llmin(size(), src.size()); + for (S32 i=0; i<min_size; i++) + { + (*this)[i].merge(src[i]); + } + //showDetails(src, "input src"); + //showDetails(*this, "output this"); + +} diff --git a/indra/llmath/llrigginginfo.h b/indra/llmath/llrigginginfo.h new file mode 100644 index 0000000000..b3d6bc2d19 --- /dev/null +++ b/indra/llmath/llrigginginfo.h @@ -0,0 +1,100 @@ +/** +* @file llrigginginfo.h +* @brief Functions for tracking rigged box extents +* +* $LicenseInfo:firstyear=2018&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, 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$ +*/ + +// Stores information related to associated rigged mesh vertices +// This lives in llmath because llvolume lives in llmath. + +#ifndef LL_LLRIGGINGINFO_H +#define LL_LLRIGGINGINFO_H + +#include "llvector4a.h" + +// Extents are in joint space +// isRiggedTo is based on the state of all currently associated rigged meshes +LL_ALIGN_PREFIX(16) +class LLJointRiggingInfo +{ +public: + LLJointRiggingInfo(); + bool isRiggedTo() const; + void setIsRiggedTo(bool val); + LLVector4a *getRiggedExtents(); + const LLVector4a *getRiggedExtents() const; + void merge(const LLJointRiggingInfo& other); + + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + + void* operator new[](size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete[](void* ptr) + { + ll_aligned_free_16(ptr); + } + + +private: + LL_ALIGN_16(LLVector4a mRiggedExtents[2]); + bool mIsRiggedTo; +} LL_ALIGN_POSTFIX(16); + +// For storing all the rigging info associated with a given avatar or +// object, keyed by joint_num. +// Using direct memory management instead of std::vector<> to avoid alignment issues. +class LLJointRiggingInfoTab +{ +public: + LLJointRiggingInfoTab(); + ~LLJointRiggingInfoTab(); + void resize(S32 size); + void clear(); + S32 size() const { return mSize; } + void merge(const LLJointRiggingInfoTab& src); + LLJointRiggingInfo& operator[](S32 i) { return mRigInfoPtr[i]; } + const LLJointRiggingInfo& operator[](S32 i) const { return mRigInfoPtr[i]; }; + bool needsUpdate() { return mNeedsUpdate; } + void setNeedsUpdate(bool val) { mNeedsUpdate = val; } +private: + // Not implemented + LLJointRiggingInfoTab& operator=(const LLJointRiggingInfoTab& src); + LLJointRiggingInfoTab(const LLJointRiggingInfoTab& src); + + LLJointRiggingInfo *mRigInfoPtr; + S32 mSize; + bool mNeedsUpdate; +}; + +#endif diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h index 79d0a44551..222f3cf235 100644 --- a/indra/llmath/llvector4a.h +++ b/indra/llmath/llvector4a.h @@ -320,7 +320,7 @@ public: inline const LLVector4a& operator= ( const LLQuad& rhs ); inline operator LLQuad() const; - + private: LLQuad mQ; } LL_ALIGN_POSTFIX(16); @@ -331,4 +331,9 @@ inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p max.setMax(max, p); } +inline std::ostream& operator<<(std::ostream& s, const LLVector4a& v) +{ + s << "(" << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << ")"; + return s; +} #endif diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index a34999b6fd..e7a8ca2f9d 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1,5 +1,4 @@ /** - * @file llvolume.cpp * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ @@ -2646,6 +2645,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) } //calculate bounding box + // VFExtents change LLVector4a& min = face.mExtents[0]; LLVector4a& max = face.mExtents[1]; @@ -4775,6 +4775,7 @@ LLVolumeFace::~LLVolumeFace() { ll_aligned_free_16(mExtents); mExtents = NULL; + mCenter = NULL; freeData(); } @@ -4956,7 +4957,7 @@ void LLVolumeFace::optimize(F32 angle_cutoff) // if (new_face.mNumVertices <= mNumVertices) { - llassert(new_face.mNumIndices == mNumIndices); + llassert(new_face.mNumIndices == mNumIndices); swapData(new_face); } @@ -5577,7 +5578,7 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build) // S32 i; S32 grid_size = (profile.size()-1)/4; - + // VFExtents change LLVector4a& min = mExtents[0]; LLVector4a& max = mExtents[1]; @@ -5854,7 +5855,7 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) LLVector2 cuv; LLVector2 min_uv, max_uv; - + // VFExtents change LLVector4a& min = mExtents[0]; LLVector4a& max = mExtents[1]; @@ -6293,6 +6294,9 @@ void LLVolumeFace::resizeVertices(S32 num_verts) mNumVertices = num_verts; mNumAllocatedVertices = num_verts; + + // Force update + mJointRiggingInfoTab.clear(); } void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv) @@ -6414,96 +6418,6 @@ void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, } } -void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMatrix4& norm_mat_in) -{ - U16 offset = mNumVertices; - - S32 new_count = face.mNumVertices + mNumVertices; - - if (new_count > 65536) - { - LL_ERRS() << "Cannot append face -- 16-bit overflow will occur." << LL_ENDL; - } - - if (face.mNumVertices == 0) - { - LL_ERRS() << "Cannot append empty face." << LL_ENDL; - } - - U32 old_vsize = mNumVertices*16; - U32 new_vsize = new_count * 16; - U32 old_tcsize = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF; - U32 new_tcsize = (new_count*sizeof(LLVector2)+0xF) & ~0xF; - U32 new_size = new_vsize * 2 + new_tcsize; - - //allocate new buffer space - LLVector4a* old_buf = mPositions; - mPositions = (LLVector4a*) ll_aligned_malloc<64>(new_size); - mNormals = mPositions + new_count; - mTexCoords = (LLVector2*) (mNormals+new_count); - - mNumAllocatedVertices = new_count; - - LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) old_buf, old_vsize); - LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) (old_buf+mNumVertices), old_vsize); - LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) (old_buf+mNumVertices*2), old_tcsize); - - mNumVertices = new_count; - - //get destination address of appended face - LLVector4a* dst_pos = mPositions+offset; - LLVector2* dst_tc = mTexCoords+offset; - LLVector4a* dst_norm = mNormals+offset; - - //get source addresses of appended face - const LLVector4a* src_pos = face.mPositions; - const LLVector2* src_tc = face.mTexCoords; - const LLVector4a* src_norm = face.mNormals; - - //load aligned matrices - LLMatrix4a mat, norm_mat; - mat.loadu(mat_in); - norm_mat.loadu(norm_mat_in); - - for (U32 i = 0; i < face.mNumVertices; ++i) - { - //transform appended face position and store - mat.affineTransform(src_pos[i], dst_pos[i]); - - //transform appended face normal and store - norm_mat.rotate(src_norm[i], dst_norm[i]); - dst_norm[i].normalize3fast(); - - //copy appended face texture coordinate - dst_tc[i] = src_tc[i]; - - if (offset == 0 && i == 0) - { //initialize bounding box - mExtents[0] = mExtents[1] = dst_pos[i]; - } - else - { - //stretch bounding box - update_min_max(mExtents[0], mExtents[1], dst_pos[i]); - } - } - - - new_count = mNumIndices + face.mNumIndices; - - //allocate new index buffer - mIndices = (U16*) ll_aligned_realloc_16(mIndices, (new_count*sizeof(U16)+0xF) & ~0xF, (mNumIndices*sizeof(U16)+0xF) & ~0xF); - - //get destination address into new index buffer - U16* dst_idx = mIndices+mNumIndices; - mNumIndices = new_count; - - for (U32 i = 0; i < face.mNumIndices; ++i) - { //copy indices, offsetting by old vertex count - dst_idx[i] = face.mIndices[i]+offset; - } -} - BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) { LL_CHECK_MEMORY @@ -6649,7 +6563,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) { update_min_max(face_min, face_max, *cur_pos++); } - + // VFExtents change mExtents[0] = face_min; mExtents[1] = face_max; diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index d3c1ac46fe..1d6d35c432 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -57,6 +57,7 @@ class LLVolumeTriangle; #include "llpointer.h" #include "llfile.h" #include "llalignedarray.h" +#include "llrigginginfo.h" //============================================================================ @@ -871,8 +872,6 @@ public: BOOL create(LLVolume* volume, BOOL partial_build = FALSE); void createTangents(); - void appendFace(const LLVolumeFace& face, LLMatrix4& transform, LLMatrix4& normal_tranform); - void resizeVertices(S32 num_verts); void allocateTangents(S32 num_verts); void allocateWeights(S32 num_verts); @@ -958,6 +957,10 @@ public: LLVector4a* mWeights; mutable BOOL mWeightsScrubbed; + + // Which joints are rigged to, and the bounding box of any rigged + // vertices per joint. + LLJointRiggingInfoTab mJointRiggingInfoTab; LLOctreeNode<LLVolumeTriangle>* mOctree; diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp index e7107dee16..b04c67d926 100644 --- a/indra/llmath/v3math.cpp +++ b/indra/llmath/v3math.cpp @@ -369,3 +369,39 @@ BOOL LLVector3::parseVector3(const std::string& buf, LLVector3* value) return FALSE; } + +// Displacement from query point to nearest neighbor point on bounding box. +// Returns zero vector for points within or on the box. +LLVector3 point_to_box_offset(LLVector3& pos, const LLVector3* box) +{ + LLVector3 offset; + for (S32 k=0; k<3; k++) + { + offset[k] = 0; + if (pos[k] < box[0][k]) + { + offset[k] = pos[k] - box[0][k]; + } + else if (pos[k] > box[1][k]) + { + offset[k] = pos[k] - box[1][k]; + } + } + return offset; +} + +bool box_valid_and_non_zero(const LLVector3* box) +{ + if (!box[0].isFinite() || !box[1].isFinite()) + { + return false; + } + LLVector3 zero_vec; + zero_vec.clear(); + if ((box[0] != zero_vec) || (box[1] != zero_vec)) + { + return true; + } + return false; +} + diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h index f3fbce4843..6f857d7061 100644 --- a/indra/llmath/v3math.h +++ b/indra/llmath/v3math.h @@ -163,6 +163,8 @@ LLVector3 inverse_projected_vec(const LLVector3 &a, const LLVector3 &b); // Retu LLVector3 parallel_component(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b (same as projected_vec) LLVector3 orthogonal_component(const LLVector3 &a, const LLVector3 &b); // Returns component of vector a not parallel to vector b (same as projected_vec) LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u); // Returns a vector that is a linear interpolation between a and b +LLVector3 point_to_box_offset(LLVector3& pos, const LLVector3* box); // Displacement from query point to nearest point on bounding box. +bool box_valid_and_non_zero(const LLVector3* box); inline LLVector3::LLVector3(void) { |