diff options
Diffstat (limited to 'indra/llmath')
45 files changed, 26973 insertions, 26973 deletions
diff --git a/indra/llmath/llbbox.cpp b/indra/llmath/llbbox.cpp index 728b222d09..fa1253e421 100644 --- a/indra/llmath/llbbox.cpp +++ b/indra/llmath/llbbox.cpp @@ -1,178 +1,178 @@ -/**
- * @file llbbox.cpp
- * @brief General purpose bounding box class (Not axis aligned)
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-// self include
-#include "llbbox.h"
-
-// library includes
-#include "m4math.h"
-
-void LLBBox::addPointLocal(const LLVector3& p)
-{
- if (mEmpty)
- {
- mMinLocal = p;
- mMaxLocal = p;
- mEmpty = false;
- }
- else
- {
- mMinLocal.mV[VX] = llmin( p.mV[VX], mMinLocal.mV[VX] );
- mMinLocal.mV[VY] = llmin( p.mV[VY], mMinLocal.mV[VY] );
- mMinLocal.mV[VZ] = llmin( p.mV[VZ], mMinLocal.mV[VZ] );
- mMaxLocal.mV[VX] = llmax( p.mV[VX], mMaxLocal.mV[VX] );
- mMaxLocal.mV[VY] = llmax( p.mV[VY], mMaxLocal.mV[VY] );
- mMaxLocal.mV[VZ] = llmax( p.mV[VZ], mMaxLocal.mV[VZ] );
- }
-}
-
-void LLBBox::addPointAgent( LLVector3 p)
-{
- p -= mPosAgent;
- p.rotVec( ~mRotation );
- addPointLocal( p );
-}
-
-
-void LLBBox::addBBoxAgent(const LLBBox& b)
-{
- if (mEmpty)
- {
- mPosAgent = b.mPosAgent;
- mRotation = b.mRotation;
- mMinLocal.clearVec();
- mMaxLocal.clearVec();
- }
- LLVector3 vertex[8];
- vertex[0].setVec( b.mMinLocal.mV[VX], b.mMinLocal.mV[VY], b.mMinLocal.mV[VZ] );
- vertex[1].setVec( b.mMinLocal.mV[VX], b.mMinLocal.mV[VY], b.mMaxLocal.mV[VZ] );
- vertex[2].setVec( b.mMinLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMinLocal.mV[VZ] );
- vertex[3].setVec( b.mMinLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMaxLocal.mV[VZ] );
- vertex[4].setVec( b.mMaxLocal.mV[VX], b.mMinLocal.mV[VY], b.mMinLocal.mV[VZ] );
- vertex[5].setVec( b.mMaxLocal.mV[VX], b.mMinLocal.mV[VY], b.mMaxLocal.mV[VZ] );
- vertex[6].setVec( b.mMaxLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMinLocal.mV[VZ] );
- vertex[7].setVec( b.mMaxLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMaxLocal.mV[VZ] );
-
- LLMatrix4 m( b.mRotation );
- m.translate( b.mPosAgent );
- m.translate( -mPosAgent );
- m.rotate( ~mRotation );
-
- for( S32 i=0; i<8; i++ )
- {
- addPointLocal( vertex[i] * m );
- }
-}
-
-LLBBox LLBBox::getAxisAligned() const
-{
- // no rotation = axis aligned rotation
- LLBBox aligned(mPosAgent, LLQuaternion(), LLVector3(), LLVector3());
-
- // add the center point so that it's not empty
- aligned.addPointAgent(mPosAgent);
-
- // add our BBox
- aligned.addBBoxAgent(*this);
-
- return aligned;
-}
-
-void LLBBox::expand( F32 delta )
-{
- mMinLocal.mV[VX] -= delta;
- mMinLocal.mV[VY] -= delta;
- mMinLocal.mV[VZ] -= delta;
- mMaxLocal.mV[VX] += delta;
- mMaxLocal.mV[VY] += delta;
- mMaxLocal.mV[VZ] += delta;
-}
-
-LLVector3 LLBBox::localToAgent(const LLVector3& v) const
-{
- LLMatrix4 m( mRotation );
- m.translate( mPosAgent );
- return v * m;
-}
-
-LLVector3 LLBBox::agentToLocal(const LLVector3& v) const
-{
- LLMatrix4 m;
- m.translate( -mPosAgent );
- m.rotate( ~mRotation ); // inverse rotation
- return v * m;
-}
-
-LLVector3 LLBBox::localToAgentBasis(const LLVector3& v) const
-{
- LLMatrix4 m( mRotation );
- return v * m;
-}
-
-LLVector3 LLBBox::agentToLocalBasis(const LLVector3& v) const
-{
- LLMatrix4 m( ~mRotation ); // inverse rotation
- return v * m;
-}
-
-bool LLBBox::containsPointLocal(const LLVector3& p) const
-{
- if ( (p.mV[VX] < mMinLocal.mV[VX])
- ||(p.mV[VX] > mMaxLocal.mV[VX])
- ||(p.mV[VY] < mMinLocal.mV[VY])
- ||(p.mV[VY] > mMaxLocal.mV[VY])
- ||(p.mV[VZ] < mMinLocal.mV[VZ])
- ||(p.mV[VZ] > mMaxLocal.mV[VZ]))
- {
- return false;
- }
- return true;
-}
-
-bool LLBBox::containsPointAgent(const LLVector3& p) const
-{
- LLVector3 point_local = agentToLocal(p);
- return containsPointLocal(point_local);
-}
-
-LLVector3 LLBBox::getMinAgent() const
-{
- return localToAgent(mMinLocal);
-}
-
-LLVector3 LLBBox::getMaxAgent() const
-{
- return localToAgent(mMaxLocal);
-}
-
-/*
-LLBBox operator*(const LLBBox &a, const LLMatrix4 &b)
-{
- return LLBBox( a.mMin * b, a.mMax * b );
-}
-*/
+/** + * @file llbbox.cpp + * @brief General purpose bounding box class (Not axis aligned) + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +// self include +#include "llbbox.h" + +// library includes +#include "m4math.h" + +void LLBBox::addPointLocal(const LLVector3& p) +{ + if (mEmpty) + { + mMinLocal = p; + mMaxLocal = p; + mEmpty = false; + } + else + { + mMinLocal.mV[VX] = llmin( p.mV[VX], mMinLocal.mV[VX] ); + mMinLocal.mV[VY] = llmin( p.mV[VY], mMinLocal.mV[VY] ); + mMinLocal.mV[VZ] = llmin( p.mV[VZ], mMinLocal.mV[VZ] ); + mMaxLocal.mV[VX] = llmax( p.mV[VX], mMaxLocal.mV[VX] ); + mMaxLocal.mV[VY] = llmax( p.mV[VY], mMaxLocal.mV[VY] ); + mMaxLocal.mV[VZ] = llmax( p.mV[VZ], mMaxLocal.mV[VZ] ); + } +} + +void LLBBox::addPointAgent( LLVector3 p) +{ + p -= mPosAgent; + p.rotVec( ~mRotation ); + addPointLocal( p ); +} + + +void LLBBox::addBBoxAgent(const LLBBox& b) +{ + if (mEmpty) + { + mPosAgent = b.mPosAgent; + mRotation = b.mRotation; + mMinLocal.clearVec(); + mMaxLocal.clearVec(); + } + LLVector3 vertex[8]; + vertex[0].setVec( b.mMinLocal.mV[VX], b.mMinLocal.mV[VY], b.mMinLocal.mV[VZ] ); + vertex[1].setVec( b.mMinLocal.mV[VX], b.mMinLocal.mV[VY], b.mMaxLocal.mV[VZ] ); + vertex[2].setVec( b.mMinLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMinLocal.mV[VZ] ); + vertex[3].setVec( b.mMinLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMaxLocal.mV[VZ] ); + vertex[4].setVec( b.mMaxLocal.mV[VX], b.mMinLocal.mV[VY], b.mMinLocal.mV[VZ] ); + vertex[5].setVec( b.mMaxLocal.mV[VX], b.mMinLocal.mV[VY], b.mMaxLocal.mV[VZ] ); + vertex[6].setVec( b.mMaxLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMinLocal.mV[VZ] ); + vertex[7].setVec( b.mMaxLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMaxLocal.mV[VZ] ); + + LLMatrix4 m( b.mRotation ); + m.translate( b.mPosAgent ); + m.translate( -mPosAgent ); + m.rotate( ~mRotation ); + + for( S32 i=0; i<8; i++ ) + { + addPointLocal( vertex[i] * m ); + } +} + +LLBBox LLBBox::getAxisAligned() const +{ + // no rotation = axis aligned rotation + LLBBox aligned(mPosAgent, LLQuaternion(), LLVector3(), LLVector3()); + + // add the center point so that it's not empty + aligned.addPointAgent(mPosAgent); + + // add our BBox + aligned.addBBoxAgent(*this); + + return aligned; +} + +void LLBBox::expand( F32 delta ) +{ + mMinLocal.mV[VX] -= delta; + mMinLocal.mV[VY] -= delta; + mMinLocal.mV[VZ] -= delta; + mMaxLocal.mV[VX] += delta; + mMaxLocal.mV[VY] += delta; + mMaxLocal.mV[VZ] += delta; +} + +LLVector3 LLBBox::localToAgent(const LLVector3& v) const +{ + LLMatrix4 m( mRotation ); + m.translate( mPosAgent ); + return v * m; +} + +LLVector3 LLBBox::agentToLocal(const LLVector3& v) const +{ + LLMatrix4 m; + m.translate( -mPosAgent ); + m.rotate( ~mRotation ); // inverse rotation + return v * m; +} + +LLVector3 LLBBox::localToAgentBasis(const LLVector3& v) const +{ + LLMatrix4 m( mRotation ); + return v * m; +} + +LLVector3 LLBBox::agentToLocalBasis(const LLVector3& v) const +{ + LLMatrix4 m( ~mRotation ); // inverse rotation + return v * m; +} + +bool LLBBox::containsPointLocal(const LLVector3& p) const +{ + if ( (p.mV[VX] < mMinLocal.mV[VX]) + ||(p.mV[VX] > mMaxLocal.mV[VX]) + ||(p.mV[VY] < mMinLocal.mV[VY]) + ||(p.mV[VY] > mMaxLocal.mV[VY]) + ||(p.mV[VZ] < mMinLocal.mV[VZ]) + ||(p.mV[VZ] > mMaxLocal.mV[VZ])) + { + return false; + } + return true; +} + +bool LLBBox::containsPointAgent(const LLVector3& p) const +{ + LLVector3 point_local = agentToLocal(p); + return containsPointLocal(point_local); +} + +LLVector3 LLBBox::getMinAgent() const +{ + return localToAgent(mMinLocal); +} + +LLVector3 LLBBox::getMaxAgent() const +{ + return localToAgent(mMaxLocal); +} + +/* +LLBBox operator*(const LLBBox &a, const LLMatrix4 &b) +{ + return LLBBox( a.mMin * b, a.mMax * b ); +} +*/ diff --git a/indra/llmath/llbbox.h b/indra/llmath/llbbox.h index 988b95c54d..5617eaebde 100644 --- a/indra/llmath/llbbox.h +++ b/indra/llmath/llbbox.h @@ -1,101 +1,101 @@ -/**
- * @file llbbox.h
- * @brief General purpose bounding box class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_BBOX_H
-#define LL_BBOX_H
-
-#include "v3math.h"
-#include "llquaternion.h"
-
-// Note: "local space" for an LLBBox is defined relative to agent space in terms of
-// a translation followed by a rotation. There is no scale term since the LLBBox's min and
-// max are not necessarily symetrical and define their own extents.
-
-class LLBBox
-{
-public:
- LLBBox() {mEmpty = true;}
- LLBBox( const LLVector3& pos_agent,
- const LLQuaternion& rot,
- const LLVector3& min_local,
- const LLVector3& max_local )
- :
- mMinLocal( min_local ), mMaxLocal( max_local ), mPosAgent(pos_agent), mRotation( rot), mEmpty( true )
- {}
-
- // Default copy constructor is OK.
-
- const LLVector3& getPositionAgent() const { return mPosAgent; }
- const LLQuaternion& getRotation() const { return mRotation; }
-
- LLVector3 getMinAgent() const;
- const LLVector3& getMinLocal() const { return mMinLocal; }
- void setMinLocal( const LLVector3& min ) { mMinLocal = min; }
-
- LLVector3 getMaxAgent() const;
- const LLVector3& getMaxLocal() const { return mMaxLocal; }
- void setMaxLocal( const LLVector3& max ) { mMaxLocal = max; }
-
- LLVector3 getCenterLocal() const { return (mMaxLocal - mMinLocal) * 0.5f + mMinLocal; }
- LLVector3 getCenterAgent() const { return localToAgent( getCenterLocal() ); }
-
- LLVector3 getExtentLocal() const { return mMaxLocal - mMinLocal; }
-
- bool containsPointLocal(const LLVector3& p) const;
- bool containsPointAgent(const LLVector3& p) const;
-
- void addPointAgent(LLVector3 p);
- void addBBoxAgent(const LLBBox& b);
-
- void addPointLocal(const LLVector3& p);
- void addBBoxLocal(const LLBBox& b) { addPointLocal( b.mMinLocal ); addPointLocal( b.mMaxLocal ); }
-
- void expand( F32 delta );
-
- LLVector3 localToAgent( const LLVector3& v ) const;
- LLVector3 agentToLocal( const LLVector3& v ) const;
-
- // Changes rotation but not position
- LLVector3 localToAgentBasis(const LLVector3& v) const;
- LLVector3 agentToLocalBasis(const LLVector3& v) const;
-
- // Get the smallest possible axis aligned bbox that contains this bbox
- LLBBox getAxisAligned() const;
-
-// friend LLBBox operator*(const LLBBox& a, const LLMatrix4& b);
-
-private:
- LLVector3 mMinLocal;
- LLVector3 mMaxLocal;
- LLVector3 mPosAgent; // Position relative to Agent's Region
- LLQuaternion mRotation;
- bool mEmpty; // Nothing has been added to this bbox yet
-};
-
-//LLBBox operator*(const LLBBox &a, const LLMatrix4 &b);
-
-
-#endif // LL_BBOX_H
+/** + * @file llbbox.h + * @brief General purpose bounding box class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_BBOX_H +#define LL_BBOX_H + +#include "v3math.h" +#include "llquaternion.h" + +// Note: "local space" for an LLBBox is defined relative to agent space in terms of +// a translation followed by a rotation. There is no scale term since the LLBBox's min and +// max are not necessarily symetrical and define their own extents. + +class LLBBox +{ +public: + LLBBox() {mEmpty = true;} + LLBBox( const LLVector3& pos_agent, + const LLQuaternion& rot, + const LLVector3& min_local, + const LLVector3& max_local ) + : + mMinLocal( min_local ), mMaxLocal( max_local ), mPosAgent(pos_agent), mRotation( rot), mEmpty( true ) + {} + + // Default copy constructor is OK. + + const LLVector3& getPositionAgent() const { return mPosAgent; } + const LLQuaternion& getRotation() const { return mRotation; } + + LLVector3 getMinAgent() const; + const LLVector3& getMinLocal() const { return mMinLocal; } + void setMinLocal( const LLVector3& min ) { mMinLocal = min; } + + LLVector3 getMaxAgent() const; + const LLVector3& getMaxLocal() const { return mMaxLocal; } + void setMaxLocal( const LLVector3& max ) { mMaxLocal = max; } + + LLVector3 getCenterLocal() const { return (mMaxLocal - mMinLocal) * 0.5f + mMinLocal; } + LLVector3 getCenterAgent() const { return localToAgent( getCenterLocal() ); } + + LLVector3 getExtentLocal() const { return mMaxLocal - mMinLocal; } + + bool containsPointLocal(const LLVector3& p) const; + bool containsPointAgent(const LLVector3& p) const; + + void addPointAgent(LLVector3 p); + void addBBoxAgent(const LLBBox& b); + + void addPointLocal(const LLVector3& p); + void addBBoxLocal(const LLBBox& b) { addPointLocal( b.mMinLocal ); addPointLocal( b.mMaxLocal ); } + + void expand( F32 delta ); + + LLVector3 localToAgent( const LLVector3& v ) const; + LLVector3 agentToLocal( const LLVector3& v ) const; + + // Changes rotation but not position + LLVector3 localToAgentBasis(const LLVector3& v) const; + LLVector3 agentToLocalBasis(const LLVector3& v) const; + + // Get the smallest possible axis aligned bbox that contains this bbox + LLBBox getAxisAligned() const; + +// friend LLBBox operator*(const LLBBox& a, const LLMatrix4& b); + +private: + LLVector3 mMinLocal; + LLVector3 mMaxLocal; + LLVector3 mPosAgent; // Position relative to Agent's Region + LLQuaternion mRotation; + bool mEmpty; // Nothing has been added to this bbox yet +}; + +//LLBBox operator*(const LLBBox &a, const LLMatrix4 &b); + + +#endif // LL_BBOX_H diff --git a/indra/llmath/llcoordframe.h b/indra/llmath/llcoordframe.h index a21d39bbc0..aaa701f792 100644 --- a/indra/llmath/llcoordframe.h +++ b/indra/llmath/llcoordframe.h @@ -1,174 +1,174 @@ -/**
- * @file llcoordframe.h
- * @brief LLCoordFrame class header file.
- *
- * $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$
- */
-
-#ifndef LL_COORDFRAME_H
-#define LL_COORDFRAME_H
-
-#include "v3math.h"
-#include "v4math.h"
-#include "llerror.h"
-
-// XXX : The constructors of the LLCoordFrame class assume that all vectors
-// and quaternion being passed as arguments are normalized, and all matrix
-// arguments are unitary. VERY BAD things will happen if these assumptions fail.
-// Also, segfault hazzards exist in methods that accept F32* arguments.
-
-
-class LLCoordFrame
-{
-public:
- LLCoordFrame(); // Inits at zero with identity rotation
- explicit LLCoordFrame(const LLVector3 &origin); // Sets origin, and inits rotation = Identity
- LLCoordFrame(const LLVector3 &x_axis,
- const LLVector3 &y_axis,
- const LLVector3 &z_axis); // Sets coordinate axes and inits origin at zero
- LLCoordFrame(const LLVector3 &origin,
- const LLVector3 &x_axis,
- const LLVector3 &y_axis,
- const LLVector3 &z_axis); // Sets the origin and coordinate axes
- LLCoordFrame(const LLVector3 &origin,
- const LLMatrix3 &rotation); // Sets axes to 3x3 matrix
- LLCoordFrame(const LLVector3 &origin,
- const LLVector3 &direction); // Sets origin and calls lookDir(direction)
- explicit LLCoordFrame(const LLQuaternion &q); // Sets axes using q and inits mOrigin to zero
- LLCoordFrame(const LLVector3 &origin,
- const LLQuaternion &q); // Uses quaternion to init axes
- explicit LLCoordFrame(const LLMatrix4 &mat); // Extracts frame from a 4x4 matrix
- // The folowing two constructors are dangerous due to implicit casting and have been disabled - SJB
- //LLCoordFrame(const F32 *origin, const F32 *rotation); // Assumes "origin" is 1x3 and "rotation" is 1x9 array
- //LLCoordFrame(const F32 *origin_and_rotation); // Assumes "origin_and_rotation" is 1x12 array
-
- bool isFinite() { return mOrigin.isFinite() && mXAxis.isFinite() && mYAxis.isFinite() && mZAxis.isFinite(); }
-
- void reset();
- void resetAxes();
-
- void setOrigin(F32 x, F32 y, F32 z); // Set mOrigin
- void setOrigin(const LLVector3 &origin);
- void setOrigin(const F32 *origin);
- void setOrigin(const LLCoordFrame &frame);
-
- inline void setOriginX(F32 x) { mOrigin.mV[VX] = x; }
- inline void setOriginY(F32 y) { mOrigin.mV[VY] = y; }
- inline void setOriginZ(F32 z) { mOrigin.mV[VZ] = z; }
-
- void setAxes(const LLVector3 &x_axis, // Set axes
- const LLVector3 &y_axis,
- const LLVector3 &z_axis);
- void setAxes(const LLMatrix3 &rotation_matrix);
- void setAxes(const LLQuaternion &q);
- void setAxes(const F32 *rotation_matrix);
- void setAxes(const LLCoordFrame &frame);
-
- void translate(F32 x, F32 y, F32 z); // Move mOrgin
- void translate(const LLVector3 &v);
- void translate(const F32 *origin);
-
- void rotate(F32 angle, F32 x, F32 y, F32 z); // Move axes
- void rotate(F32 angle, const LLVector3 &rotation_axis);
- void rotate(const LLQuaternion &q);
- void rotate(const LLMatrix3 &m);
-
- void orthonormalize(); // Makes sure axes are unitary and orthogonal.
-
- // These methods allow rotations in the LLCoordFrame's frame
- void roll(F32 angle); // RH rotation about mXAxis, radians
- void pitch(F32 angle); // RH rotation about mYAxis, radians
- void yaw(F32 angle); // RH rotation about mZAxis, radians
-
- inline const LLVector3 &getOrigin() const { return mOrigin; }
-
- inline const LLVector3 &getXAxis() const { return mXAxis; }
- inline const LLVector3 &getYAxis() const { return mYAxis; }
- inline const LLVector3 &getZAxis() const { return mZAxis; }
-
- inline const LLVector3 &getAtAxis() const { return mXAxis; }
- inline const LLVector3 &getLeftAxis() const { return mYAxis; }
- inline const LLVector3 &getUpAxis() const { return mZAxis; }
-
- // These return representations of the rotation or orientation of the LLFrame
- // it its absolute frame. That is, these rotations acting on the X-axis {1,0,0}
- // will produce the mXAxis.
- // LLMatrix3 getMatrix3() const; // Returns axes in 3x3 matrix
- LLQuaternion getQuaternion() const; // Returns axes in quaternion form
-
- // Same as above, except it also includes the translation of the LLFrame
- // LLMatrix4 getMatrix4() const; // Returns position and axes in 4x4 matrix
-
- // Returns matrix which expresses point in local frame in the parent frame
- void getMatrixToParent(LLMatrix4 &mat) const;
- // Returns matrix which expresses point in parent frame in the local frame
- void getMatrixToLocal(LLMatrix4 &mat) const; // Returns matrix which expresses point in parent frame in the local frame
-
- void getRotMatrixToParent(LLMatrix4 &mat) const;
-
- // Copies mOrigin, then the three axes to buffer, returns number of bytes copied.
- size_t writeOrientation(char *buffer) const;
-
- // Copies mOrigin, then the three axes from buffer, returns the number of bytes copied.
- // Assumes the data in buffer is correct.
- size_t readOrientation(const char *buffer);
-
- LLVector3 rotateToLocal(const LLVector3 &v) const; // Returns v' rotated to local
- LLVector4 rotateToLocal(const LLVector4 &v) const; // Returns v' rotated to local
- LLVector3 rotateToAbsolute(const LLVector3 &v) const; // Returns v' rotated to absolute
- LLVector4 rotateToAbsolute(const LLVector4 &v) const; // Returns v' rotated to absolute
-
- LLVector3 transformToLocal(const LLVector3 &v) const; // Returns v' in local coord
- LLVector4 transformToLocal(const LLVector4 &v) const; // Returns v' in local coord
- LLVector3 transformToAbsolute(const LLVector3 &v) const; // Returns v' in absolute coord
- LLVector4 transformToAbsolute(const LLVector4 &v) const; // Returns v' in absolute coord
-
- // Write coord frame orientation into provided array in OpenGL matrix format.
- void getOpenGLTranslation(F32 *ogl_matrix) const;
- void getOpenGLRotation(F32 *ogl_matrix) const;
- void getOpenGLTransform(F32 *ogl_matrix) const;
-
- // lookDir orients to (xuv, presumed normalized) and does not affect origin
- void lookDir(const LLVector3 &xuv, const LLVector3 &up);
- void lookDir(const LLVector3 &xuv); // up = 0,0,1
- // lookAt orients to (point_of_interest - origin) and sets origin
- void lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest, const LLVector3 &up);
- void lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest); // up = 0,0,1
-
- // deprecated
- void setOriginAndLookAt(const LLVector3 &origin, const LLVector3 &up, const LLVector3 &point_of_interest)
- {
- lookAt(origin, point_of_interest, up);
- }
-
- friend std::ostream& operator<<(std::ostream &s, const LLCoordFrame &C);
-
- // These vectors are in absolute frame
- LLVector3 mOrigin;
- LLVector3 mXAxis;
- LLVector3 mYAxis;
- LLVector3 mZAxis;
-};
-
-
-#endif
-
+/** + * @file llcoordframe.h + * @brief LLCoordFrame class header file. + * + * $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$ + */ + +#ifndef LL_COORDFRAME_H +#define LL_COORDFRAME_H + +#include "v3math.h" +#include "v4math.h" +#include "llerror.h" + +// XXX : The constructors of the LLCoordFrame class assume that all vectors +// and quaternion being passed as arguments are normalized, and all matrix +// arguments are unitary. VERY BAD things will happen if these assumptions fail. +// Also, segfault hazzards exist in methods that accept F32* arguments. + + +class LLCoordFrame +{ +public: + LLCoordFrame(); // Inits at zero with identity rotation + explicit LLCoordFrame(const LLVector3 &origin); // Sets origin, and inits rotation = Identity + LLCoordFrame(const LLVector3 &x_axis, + const LLVector3 &y_axis, + const LLVector3 &z_axis); // Sets coordinate axes and inits origin at zero + LLCoordFrame(const LLVector3 &origin, + const LLVector3 &x_axis, + const LLVector3 &y_axis, + const LLVector3 &z_axis); // Sets the origin and coordinate axes + LLCoordFrame(const LLVector3 &origin, + const LLMatrix3 &rotation); // Sets axes to 3x3 matrix + LLCoordFrame(const LLVector3 &origin, + const LLVector3 &direction); // Sets origin and calls lookDir(direction) + explicit LLCoordFrame(const LLQuaternion &q); // Sets axes using q and inits mOrigin to zero + LLCoordFrame(const LLVector3 &origin, + const LLQuaternion &q); // Uses quaternion to init axes + explicit LLCoordFrame(const LLMatrix4 &mat); // Extracts frame from a 4x4 matrix + // The folowing two constructors are dangerous due to implicit casting and have been disabled - SJB + //LLCoordFrame(const F32 *origin, const F32 *rotation); // Assumes "origin" is 1x3 and "rotation" is 1x9 array + //LLCoordFrame(const F32 *origin_and_rotation); // Assumes "origin_and_rotation" is 1x12 array + + bool isFinite() { return mOrigin.isFinite() && mXAxis.isFinite() && mYAxis.isFinite() && mZAxis.isFinite(); } + + void reset(); + void resetAxes(); + + void setOrigin(F32 x, F32 y, F32 z); // Set mOrigin + void setOrigin(const LLVector3 &origin); + void setOrigin(const F32 *origin); + void setOrigin(const LLCoordFrame &frame); + + inline void setOriginX(F32 x) { mOrigin.mV[VX] = x; } + inline void setOriginY(F32 y) { mOrigin.mV[VY] = y; } + inline void setOriginZ(F32 z) { mOrigin.mV[VZ] = z; } + + void setAxes(const LLVector3 &x_axis, // Set axes + const LLVector3 &y_axis, + const LLVector3 &z_axis); + void setAxes(const LLMatrix3 &rotation_matrix); + void setAxes(const LLQuaternion &q); + void setAxes(const F32 *rotation_matrix); + void setAxes(const LLCoordFrame &frame); + + void translate(F32 x, F32 y, F32 z); // Move mOrgin + void translate(const LLVector3 &v); + void translate(const F32 *origin); + + void rotate(F32 angle, F32 x, F32 y, F32 z); // Move axes + void rotate(F32 angle, const LLVector3 &rotation_axis); + void rotate(const LLQuaternion &q); + void rotate(const LLMatrix3 &m); + + void orthonormalize(); // Makes sure axes are unitary and orthogonal. + + // These methods allow rotations in the LLCoordFrame's frame + void roll(F32 angle); // RH rotation about mXAxis, radians + void pitch(F32 angle); // RH rotation about mYAxis, radians + void yaw(F32 angle); // RH rotation about mZAxis, radians + + inline const LLVector3 &getOrigin() const { return mOrigin; } + + inline const LLVector3 &getXAxis() const { return mXAxis; } + inline const LLVector3 &getYAxis() const { return mYAxis; } + inline const LLVector3 &getZAxis() const { return mZAxis; } + + inline const LLVector3 &getAtAxis() const { return mXAxis; } + inline const LLVector3 &getLeftAxis() const { return mYAxis; } + inline const LLVector3 &getUpAxis() const { return mZAxis; } + + // These return representations of the rotation or orientation of the LLFrame + // it its absolute frame. That is, these rotations acting on the X-axis {1,0,0} + // will produce the mXAxis. + // LLMatrix3 getMatrix3() const; // Returns axes in 3x3 matrix + LLQuaternion getQuaternion() const; // Returns axes in quaternion form + + // Same as above, except it also includes the translation of the LLFrame + // LLMatrix4 getMatrix4() const; // Returns position and axes in 4x4 matrix + + // Returns matrix which expresses point in local frame in the parent frame + void getMatrixToParent(LLMatrix4 &mat) const; + // Returns matrix which expresses point in parent frame in the local frame + void getMatrixToLocal(LLMatrix4 &mat) const; // Returns matrix which expresses point in parent frame in the local frame + + void getRotMatrixToParent(LLMatrix4 &mat) const; + + // Copies mOrigin, then the three axes to buffer, returns number of bytes copied. + size_t writeOrientation(char *buffer) const; + + // Copies mOrigin, then the three axes from buffer, returns the number of bytes copied. + // Assumes the data in buffer is correct. + size_t readOrientation(const char *buffer); + + LLVector3 rotateToLocal(const LLVector3 &v) const; // Returns v' rotated to local + LLVector4 rotateToLocal(const LLVector4 &v) const; // Returns v' rotated to local + LLVector3 rotateToAbsolute(const LLVector3 &v) const; // Returns v' rotated to absolute + LLVector4 rotateToAbsolute(const LLVector4 &v) const; // Returns v' rotated to absolute + + LLVector3 transformToLocal(const LLVector3 &v) const; // Returns v' in local coord + LLVector4 transformToLocal(const LLVector4 &v) const; // Returns v' in local coord + LLVector3 transformToAbsolute(const LLVector3 &v) const; // Returns v' in absolute coord + LLVector4 transformToAbsolute(const LLVector4 &v) const; // Returns v' in absolute coord + + // Write coord frame orientation into provided array in OpenGL matrix format. + void getOpenGLTranslation(F32 *ogl_matrix) const; + void getOpenGLRotation(F32 *ogl_matrix) const; + void getOpenGLTransform(F32 *ogl_matrix) const; + + // lookDir orients to (xuv, presumed normalized) and does not affect origin + void lookDir(const LLVector3 &xuv, const LLVector3 &up); + void lookDir(const LLVector3 &xuv); // up = 0,0,1 + // lookAt orients to (point_of_interest - origin) and sets origin + void lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest, const LLVector3 &up); + void lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest); // up = 0,0,1 + + // deprecated + void setOriginAndLookAt(const LLVector3 &origin, const LLVector3 &up, const LLVector3 &point_of_interest) + { + lookAt(origin, point_of_interest, up); + } + + friend std::ostream& operator<<(std::ostream &s, const LLCoordFrame &C); + + // These vectors are in absolute frame + LLVector3 mOrigin; + LLVector3 mXAxis; + LLVector3 mYAxis; + LLVector3 mZAxis; +}; + + +#endif + diff --git a/indra/llmath/llinterp.h b/indra/llmath/llinterp.h index 2df3ae8561..f4faa82a82 100644 --- a/indra/llmath/llinterp.h +++ b/indra/llmath/llinterp.h @@ -1,425 +1,425 @@ -/**
- * @file llinterp.h
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLINTERP_H
-#define LL_LLINTERP_H
-
-#if defined(LL_WINDOWS)
-// macro definitions for common math constants (e.g. M_PI) are declared under the _USE_MATH_DEFINES
-// on Windows system.
-// So, let's define _USE_MATH_DEFINES before including math.h
- #define _USE_MATH_DEFINES
-#endif
-
-#include "math.h"
-
-// Class from which different types of interpolators can be derived
-
-class LLInterpVal
-{
-public:
- virtual ~LLInterpVal() {}
-};
-
-template <typename Type>
-class LLInterp
-{
-public:
- LLInterp();
- virtual ~LLInterp() {}
-
- virtual void start();
- virtual void update(const F32 time) = 0;
- const Type &getCurVal() const;
-
- void setStartVal(const Type &start_val);
- const Type &getStartVal() const;
-
- void setEndVal(const Type &target_val);
- const Type &getEndVal() const;
-
- void setStartTime(const F32 time);
- F32 getStartTime() const;
-
- void setEndTime(const F32 time);
- F32 getEndTime() const;
-
- bool isActive() const;
- bool isDone() const;
-
-protected:
- F32 mStartTime;
- F32 mEndTime;
- F32 mDuration;
- bool mActive;
- bool mDone;
-
- Type mStartVal;
- Type mEndVal;
-
- F32 mCurTime;
- Type mCurVal;
-};
-
-template <typename Type>
-class LLInterpLinear : public LLInterp<Type>
-{
-public:
- void start() override;
- void update(const F32 time) override;
- F32 getCurFrac() const;
-protected:
- F32 mCurFrac;
-};
-
-template <typename Type>
-class LLInterpExp : public LLInterpLinear<Type>
-{
-public:
- void update(const F32 time);
-protected:
-};
-
-template <typename Type>
-class LLInterpAttractor : public LLInterp<Type>
-{
-public:
- LLInterpAttractor();
- void start() override;
- void setStartVel(const Type &vel);
- void setForce(const F32 force);
- void update(const F32 time) override;
-protected:
- F32 mForce;
- Type mStartVel;
- Type mVelocity;
-};
-
-template <typename Type>
-class LLInterpFunc : public LLInterp<Type>
-{
-public:
- LLInterpFunc();
- void update(const F32 time) override;
-
- void setFunc(Type (*)(const F32, void *data), void *data);
-protected:
- Type (*mFunc)(const F32 time, void *data);
- void *mData;
-};
-
-
-///////////////////////////////////
-//
-// Implementation
-//
-//
-
-/////////////////////////////////
-//
-// LLInterp base class implementation
-//
-
-template <typename Type>
-LLInterp<Type>::LLInterp()
-: mStartVal(Type()), mEndVal(Type()), mCurVal(Type())
-{
- mStartTime = 0.f;
- mEndTime = 1.f;
- mDuration = 1.f;
- mCurTime = 0.f;
- mDone = false;
- mActive = false;
-}
-
-template <class Type>
-void LLInterp<Type>::setStartVal(const Type &start_val)
-{
- mStartVal = start_val;
-}
-
-template <class Type>
-void LLInterp<Type>::start()
-{
- mCurVal = mStartVal;
- mCurTime = mStartTime;
- mDone = false;
- mActive = false;
-}
-
-template <class Type>
-const Type &LLInterp<Type>::getStartVal() const
-{
- return mStartVal;
-}
-
-template <class Type>
-void LLInterp<Type>::setEndVal(const Type &end_val)
-{
- mEndVal = end_val;
-}
-
-template <class Type>
-const Type &LLInterp<Type>::getEndVal() const
-{
- return mEndVal;
-}
-
-template <class Type>
-const Type &LLInterp<Type>::getCurVal() const
-{
- return mCurVal;
-}
-
-
-template <class Type>
-void LLInterp<Type>::setStartTime(const F32 start_time)
-{
- mStartTime = start_time;
- mDuration = mEndTime - mStartTime;
-}
-
-template <class Type>
-F32 LLInterp<Type>::getStartTime() const
-{
- return mStartTime;
-}
-
-
-template <class Type>
-void LLInterp<Type>::setEndTime(const F32 end_time)
-{
- mEndTime = end_time;
- mDuration = mEndTime - mStartTime;
-}
-
-
-template <class Type>
-F32 LLInterp<Type>::getEndTime() const
-{
- return mEndTime;
-}
-
-
-template <class Type>
-bool LLInterp<Type>::isDone() const
-{
- return mDone;
-}
-
-template <class Type>
-bool LLInterp<Type>::isActive() const
-{
- return mActive;
-}
-
-//////////////////////////////
-//
-// LLInterpLinear derived class implementation.
-//
-template <typename Type>
-void LLInterpLinear<Type>::start()
-{
- LLInterp<Type>::start();
- mCurFrac = 0.f;
-}
-
-template <typename Type>
-void LLInterpLinear<Type>::update(const F32 time)
-{
- F32 target_frac = (time - this->mStartTime) / this->mDuration;
- F32 dfrac = target_frac - this->mCurFrac;
- if (target_frac >= 0.f)
- {
- this->mActive = true;
- }
-
- if (target_frac > 1.f)
- {
- this->mCurVal = this->mEndVal;
- this->mCurFrac = 1.f;
- this->mCurTime = time;
- this->mDone = true;
- return;
- }
-
- target_frac = llmin(1.f, target_frac);
- target_frac = llmax(0.f, target_frac);
-
- if (dfrac >= 0.f)
- {
- F32 total_frac = 1.f - this->mCurFrac;
- F32 inc_frac = dfrac / total_frac;
- this->mCurVal = inc_frac * this->mEndVal + (1.f - inc_frac) * this->mCurVal;
- this->mCurTime = time;
- }
- else
- {
- F32 total_frac = this->mCurFrac - 1.f;
- F32 inc_frac = dfrac / total_frac;
- this->mCurVal = inc_frac * this->mStartVal + (1.f - inc_frac) * this->mCurVal;
- this->mCurTime = time;
- }
- mCurFrac = target_frac;
-}
-
-template <class Type>
-F32 LLInterpLinear<Type>::getCurFrac() const
-{
- return mCurFrac;
-}
-
-
-//////////////////////////////
-//
-// LLInterpAttractor derived class implementation.
-//
-
-
-template <class Type>
-LLInterpAttractor<Type>::LLInterpAttractor() : LLInterp<Type>()
-{
- mForce = 0.1f;
- mVelocity *= 0.f;
- mStartVel *= 0.f;
-}
-
-template <class Type>
-void LLInterpAttractor<Type>::start()
-{
- LLInterp<Type>::start();
- mVelocity = mStartVel;
-}
-
-
-template <class Type>
-void LLInterpAttractor<Type>::setStartVel(const Type &vel)
-{
- mStartVel = vel;
-}
-
-template <class Type>
-void LLInterpAttractor<Type>::setForce(const F32 force)
-{
- mForce = force;
-}
-
-template <class Type>
-void LLInterpAttractor<Type>::update(const F32 time)
-{
- if (time > this->mStartTime)
- {
- this->mActive = true;
- }
- else
- {
- return;
- }
- if (time > this->mEndTime)
- {
- this->mDone = true;
- return;
- }
-
- F32 dt = time - this->mCurTime;
- Type dist_val = this->mEndVal - this->mCurVal;
- Type dv = 0.5*dt*dt*this->mForce*dist_val;
- this->mVelocity += dv;
- this->mCurVal += this->mVelocity * dt;
- this->mCurTime = time;
-}
-
-
-//////////////////////////////
-//
-// LLInterpFucn derived class implementation.
-//
-
-
-template <class Type>
-LLInterpFunc<Type>::LLInterpFunc() : LLInterp<Type>()
-{
- mFunc = nullptr;
- mData = nullptr;
-}
-
-template <class Type>
-void LLInterpFunc<Type>::setFunc(Type (*func)(const F32, void *data), void *data)
-{
- mFunc = func;
- mData = data;
-}
-
-template <class Type>
-void LLInterpFunc<Type>::update(const F32 time)
-{
- if (time > this->mStartTime)
- {
- this->mActive = true;
- }
- else
- {
- return;
- }
- if (time > this->mEndTime)
- {
- this->mDone = true;
- return;
- }
-
- this->mCurVal = (*mFunc)(time - this->mStartTime, mData);
- this->mCurTime = time;
-}
-
-//////////////////////////////
-//
-// LLInterpExp derived class implementation.
-//
-
-template <class Type>
-void LLInterpExp<Type>::update(const F32 time)
-{
- F32 target_frac = (time - this->mStartTime) / this->mDuration;
- if (target_frac >= 0.f)
- {
- this->mActive = true;
- }
-
- if (target_frac > 1.f)
- {
- this->mCurVal = this->mEndVal;
- this->mCurFrac = 1.f;
- this->mCurTime = time;
- this->mDone = true;
- return;
- }
-
- this->mCurFrac = 1.f - (F32)(exp(-2.f*target_frac));
- this->mCurVal = this->mStartVal + this->mCurFrac * (this->mEndVal - this->mStartVal);
- this->mCurTime = time;
-}
-
-#endif // LL_LLINTERP_H
-
+/** + * @file llinterp.h + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLINTERP_H +#define LL_LLINTERP_H + +#if defined(LL_WINDOWS) +// macro definitions for common math constants (e.g. M_PI) are declared under the _USE_MATH_DEFINES +// on Windows system. +// So, let's define _USE_MATH_DEFINES before including math.h + #define _USE_MATH_DEFINES +#endif + +#include "math.h" + +// Class from which different types of interpolators can be derived + +class LLInterpVal +{ +public: + virtual ~LLInterpVal() {} +}; + +template <typename Type> +class LLInterp +{ +public: + LLInterp(); + virtual ~LLInterp() {} + + virtual void start(); + virtual void update(const F32 time) = 0; + const Type &getCurVal() const; + + void setStartVal(const Type &start_val); + const Type &getStartVal() const; + + void setEndVal(const Type &target_val); + const Type &getEndVal() const; + + void setStartTime(const F32 time); + F32 getStartTime() const; + + void setEndTime(const F32 time); + F32 getEndTime() const; + + bool isActive() const; + bool isDone() const; + +protected: + F32 mStartTime; + F32 mEndTime; + F32 mDuration; + bool mActive; + bool mDone; + + Type mStartVal; + Type mEndVal; + + F32 mCurTime; + Type mCurVal; +}; + +template <typename Type> +class LLInterpLinear : public LLInterp<Type> +{ +public: + void start() override; + void update(const F32 time) override; + F32 getCurFrac() const; +protected: + F32 mCurFrac; +}; + +template <typename Type> +class LLInterpExp : public LLInterpLinear<Type> +{ +public: + void update(const F32 time); +protected: +}; + +template <typename Type> +class LLInterpAttractor : public LLInterp<Type> +{ +public: + LLInterpAttractor(); + void start() override; + void setStartVel(const Type &vel); + void setForce(const F32 force); + void update(const F32 time) override; +protected: + F32 mForce; + Type mStartVel; + Type mVelocity; +}; + +template <typename Type> +class LLInterpFunc : public LLInterp<Type> +{ +public: + LLInterpFunc(); + void update(const F32 time) override; + + void setFunc(Type (*)(const F32, void *data), void *data); +protected: + Type (*mFunc)(const F32 time, void *data); + void *mData; +}; + + +/////////////////////////////////// +// +// Implementation +// +// + +///////////////////////////////// +// +// LLInterp base class implementation +// + +template <typename Type> +LLInterp<Type>::LLInterp() +: mStartVal(Type()), mEndVal(Type()), mCurVal(Type()) +{ + mStartTime = 0.f; + mEndTime = 1.f; + mDuration = 1.f; + mCurTime = 0.f; + mDone = false; + mActive = false; +} + +template <class Type> +void LLInterp<Type>::setStartVal(const Type &start_val) +{ + mStartVal = start_val; +} + +template <class Type> +void LLInterp<Type>::start() +{ + mCurVal = mStartVal; + mCurTime = mStartTime; + mDone = false; + mActive = false; +} + +template <class Type> +const Type &LLInterp<Type>::getStartVal() const +{ + return mStartVal; +} + +template <class Type> +void LLInterp<Type>::setEndVal(const Type &end_val) +{ + mEndVal = end_val; +} + +template <class Type> +const Type &LLInterp<Type>::getEndVal() const +{ + return mEndVal; +} + +template <class Type> +const Type &LLInterp<Type>::getCurVal() const +{ + return mCurVal; +} + + +template <class Type> +void LLInterp<Type>::setStartTime(const F32 start_time) +{ + mStartTime = start_time; + mDuration = mEndTime - mStartTime; +} + +template <class Type> +F32 LLInterp<Type>::getStartTime() const +{ + return mStartTime; +} + + +template <class Type> +void LLInterp<Type>::setEndTime(const F32 end_time) +{ + mEndTime = end_time; + mDuration = mEndTime - mStartTime; +} + + +template <class Type> +F32 LLInterp<Type>::getEndTime() const +{ + return mEndTime; +} + + +template <class Type> +bool LLInterp<Type>::isDone() const +{ + return mDone; +} + +template <class Type> +bool LLInterp<Type>::isActive() const +{ + return mActive; +} + +////////////////////////////// +// +// LLInterpLinear derived class implementation. +// +template <typename Type> +void LLInterpLinear<Type>::start() +{ + LLInterp<Type>::start(); + mCurFrac = 0.f; +} + +template <typename Type> +void LLInterpLinear<Type>::update(const F32 time) +{ + F32 target_frac = (time - this->mStartTime) / this->mDuration; + F32 dfrac = target_frac - this->mCurFrac; + if (target_frac >= 0.f) + { + this->mActive = true; + } + + if (target_frac > 1.f) + { + this->mCurVal = this->mEndVal; + this->mCurFrac = 1.f; + this->mCurTime = time; + this->mDone = true; + return; + } + + target_frac = llmin(1.f, target_frac); + target_frac = llmax(0.f, target_frac); + + if (dfrac >= 0.f) + { + F32 total_frac = 1.f - this->mCurFrac; + F32 inc_frac = dfrac / total_frac; + this->mCurVal = inc_frac * this->mEndVal + (1.f - inc_frac) * this->mCurVal; + this->mCurTime = time; + } + else + { + F32 total_frac = this->mCurFrac - 1.f; + F32 inc_frac = dfrac / total_frac; + this->mCurVal = inc_frac * this->mStartVal + (1.f - inc_frac) * this->mCurVal; + this->mCurTime = time; + } + mCurFrac = target_frac; +} + +template <class Type> +F32 LLInterpLinear<Type>::getCurFrac() const +{ + return mCurFrac; +} + + +////////////////////////////// +// +// LLInterpAttractor derived class implementation. +// + + +template <class Type> +LLInterpAttractor<Type>::LLInterpAttractor() : LLInterp<Type>() +{ + mForce = 0.1f; + mVelocity *= 0.f; + mStartVel *= 0.f; +} + +template <class Type> +void LLInterpAttractor<Type>::start() +{ + LLInterp<Type>::start(); + mVelocity = mStartVel; +} + + +template <class Type> +void LLInterpAttractor<Type>::setStartVel(const Type &vel) +{ + mStartVel = vel; +} + +template <class Type> +void LLInterpAttractor<Type>::setForce(const F32 force) +{ + mForce = force; +} + +template <class Type> +void LLInterpAttractor<Type>::update(const F32 time) +{ + if (time > this->mStartTime) + { + this->mActive = true; + } + else + { + return; + } + if (time > this->mEndTime) + { + this->mDone = true; + return; + } + + F32 dt = time - this->mCurTime; + Type dist_val = this->mEndVal - this->mCurVal; + Type dv = 0.5*dt*dt*this->mForce*dist_val; + this->mVelocity += dv; + this->mCurVal += this->mVelocity * dt; + this->mCurTime = time; +} + + +////////////////////////////// +// +// LLInterpFucn derived class implementation. +// + + +template <class Type> +LLInterpFunc<Type>::LLInterpFunc() : LLInterp<Type>() +{ + mFunc = nullptr; + mData = nullptr; +} + +template <class Type> +void LLInterpFunc<Type>::setFunc(Type (*func)(const F32, void *data), void *data) +{ + mFunc = func; + mData = data; +} + +template <class Type> +void LLInterpFunc<Type>::update(const F32 time) +{ + if (time > this->mStartTime) + { + this->mActive = true; + } + else + { + return; + } + if (time > this->mEndTime) + { + this->mDone = true; + return; + } + + this->mCurVal = (*mFunc)(time - this->mStartTime, mData); + this->mCurTime = time; +} + +////////////////////////////// +// +// LLInterpExp derived class implementation. +// + +template <class Type> +void LLInterpExp<Type>::update(const F32 time) +{ + F32 target_frac = (time - this->mStartTime) / this->mDuration; + if (target_frac >= 0.f) + { + this->mActive = true; + } + + if (target_frac > 1.f) + { + this->mCurVal = this->mEndVal; + this->mCurFrac = 1.f; + this->mCurTime = time; + this->mDone = true; + return; + } + + this->mCurFrac = 1.f - (F32)(exp(-2.f*target_frac)); + this->mCurVal = this->mStartVal + this->mCurFrac * (this->mEndVal - this->mStartVal); + this->mCurTime = time; +} + +#endif // LL_LLINTERP_H + diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index c60b90f301..4e8fc56de0 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -1,569 +1,569 @@ -/**
- * @file llmath.h
- * @brief Useful math constants and macros.
- *
- * $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$
- */
-
-#ifndef LLMATH_H
-#define LLMATH_H
-
-#include <cmath>
-#include <cstdlib>
-#include <vector>
-#include <limits>
-#include "lldefs.h"
-//#include "llstl.h" // *TODO: Remove when LLString is gone
-//#include "llstring.h" // *TODO: Remove when LLString is gone
-// lltut.h uses is_approx_equal_fraction(). This was moved to its own header
-// file in llcommon so we can use lltut.h for llcommon tests without making
-// llcommon depend on llmath.
-#include "is_approx_equal_fraction.h"
-
-// work around for Windows & older gcc non-standard function names.
-#if LL_WINDOWS
-#include <float.h>
-#define llisnan(val) _isnan(val)
-#define llfinite(val) _finite(val)
-#elif (LL_LINUX && __GNUC__ <= 2)
-#define llisnan(val) isnan(val)
-#define llfinite(val) isfinite(val)
-#else
-#define llisnan(val) std::isnan(val)
-#define llfinite(val) std::isfinite(val)
-#endif
-
-// Single Precision Floating Point Routines
-// (There used to be more defined here, but they appeared to be redundant and
-// were breaking some other includes. Removed by Falcon, reviewed by Andrew, 11/25/09)
-/*#ifndef tanf
-#define tanf(x) ((F32)tan((F64)(x)))
-#endif*/
-
-constexpr F32 GRAVITY = -9.8f;
-
-// mathematical constants
-constexpr F32 F_PI = 3.1415926535897932384626433832795f;
-constexpr F32 F_TWO_PI = 6.283185307179586476925286766559f;
-constexpr F32 F_PI_BY_TWO = 1.5707963267948966192313216916398f;
-constexpr F32 F_SQRT_TWO_PI = 2.506628274631000502415765284811f;
-constexpr F32 F_E = 2.71828182845904523536f;
-constexpr F32 F_SQRT2 = 1.4142135623730950488016887242097f;
-constexpr F32 F_SQRT3 = 1.73205080756888288657986402541f;
-constexpr F32 OO_SQRT2 = 0.7071067811865475244008443621049f;
-constexpr F32 OO_SQRT3 = 0.577350269189625764509f;
-constexpr F32 DEG_TO_RAD = 0.017453292519943295769236907684886f;
-constexpr F32 RAD_TO_DEG = 57.295779513082320876798154814105f;
-constexpr F32 F_APPROXIMATELY_ZERO = 0.00001f;
-constexpr F32 F_LN10 = 2.3025850929940456840179914546844f;
-constexpr F32 OO_LN10 = 0.43429448190325182765112891891661;
-constexpr F32 F_LN2 = 0.69314718056f;
-constexpr F32 OO_LN2 = 1.4426950408889634073599246810019f;
-
-constexpr F32 F_ALMOST_ZERO = 0.0001f;
-constexpr F32 F_ALMOST_ONE = 1.0f - F_ALMOST_ZERO;
-
-constexpr F32 GIMBAL_THRESHOLD = 0.000436f; // sets the gimballock threshold 0.025 away from +/-90 degrees
-// formula: GIMBAL_THRESHOLD = sin(DEG_TO_RAD * gimbal_threshold_angle);
-
-// BUG: Eliminate in favor of F_APPROXIMATELY_ZERO above?
-constexpr F32 FP_MAG_THRESHOLD = 0.0000001f;
-
-// TODO: Replace with logic like is_approx_equal
-inline bool is_approx_zero( F32 f ) { return (-F_APPROXIMATELY_ZERO < f) && (f < F_APPROXIMATELY_ZERO); }
-
-// These functions work by interpreting sign+exp+mantissa as an unsigned
-// integer.
-// For example:
-// x = <sign>1 <exponent>00000010 <mantissa>00000000000000000000000
-// y = <sign>1 <exponent>00000001 <mantissa>11111111111111111111111
-//
-// interpreted as ints =
-// x = 10000001000000000000000000000000
-// y = 10000000111111111111111111111111
-// which is clearly a different of 1 in the least significant bit
-// Values with the same exponent can be trivially shown to work.
-//
-// WARNING: Denormals of opposite sign do not work
-// x = <sign>1 <exponent>00000000 <mantissa>00000000000000000000001
-// y = <sign>0 <exponent>00000000 <mantissa>00000000000000000000001
-// Although these values differ by 2 in the LSB, the sign bit makes
-// the int comparison fail.
-//
-// WARNING: NaNs can compare equal
-// There is no special treatment of exceptional values like NaNs
-//
-// WARNING: Infinity is comparable with F32_MAX and negative
-// infinity is comparable with F32_MIN
-
-// handles negative and positive zeros
-inline bool is_zero(F32 x)
-{
- return (*(U32*)(&x) & 0x7fffffff) == 0;
-}
-
-inline bool is_approx_equal(F32 x, F32 y)
-{
- constexpr S32 COMPARE_MANTISSA_UP_TO_BIT = 0x02;
- return (std::abs((S32) ((U32&)x - (U32&)y) ) < COMPARE_MANTISSA_UP_TO_BIT);
-}
-
-inline bool is_approx_equal(F64 x, F64 y)
-{
- constexpr S64 COMPARE_MANTISSA_UP_TO_BIT = 0x02;
- return (std::abs((S32) ((U64&)x - (U64&)y) ) < COMPARE_MANTISSA_UP_TO_BIT);
-}
-
-inline S32 llabs(const S32 a)
-{
- return S32(std::labs(a));
-}
-
-inline F32 llabs(const F32 a)
-{
- return F32(std::fabs(a));
-}
-
-inline F64 llabs(const F64 a)
-{
- return F64(std::fabs(a));
-}
-
-inline S32 lltrunc( F32 f )
-{
-#if LL_WINDOWS && !defined( __INTEL_COMPILER ) && (ADDRESS_SIZE == 32)
- // Avoids changing the floating point control word.
- // Add or subtract 0.5 - epsilon and then round
- const static U32 zpfp[] = { 0xBEFFFFFF, 0x3EFFFFFF };
- S32 result;
- __asm {
- fld f
- mov eax, f
- shr eax, 29
- and eax, 4
- fadd dword ptr [zpfp + eax]
- fistp result
- }
- return result;
-#else
- return (S32)f;
-#endif
-}
-
-inline S32 lltrunc( F64 f )
-{
- return (S32)f;
-}
-
-inline S32 llfloor( F32 f )
-{
-#if LL_WINDOWS && !defined( __INTEL_COMPILER ) && (ADDRESS_SIZE == 32)
- // Avoids changing the floating point control word.
- // Accurate (unlike Stereopsis version) for all values between S32_MIN and S32_MAX and slightly faster than Stereopsis version.
- // Add -(0.5 - epsilon) and then round
- const U32 zpfp = 0xBEFFFFFF;
- S32 result;
- __asm {
- fld f
- fadd dword ptr [zpfp]
- fistp result
- }
- return result;
-#else
- return (S32)floor(f);
-#endif
-}
-
-
-inline S32 llceil( F32 f )
-{
- // This could probably be optimized, but this works.
- return (S32)ceil(f);
-}
-
-
-#ifndef BOGUS_ROUND
-// Use this round. Does an arithmetic round (0.5 always rounds up)
-inline S32 ll_round(const F32 val)
-{
- return llfloor(val + 0.5f);
-}
-
-#else // BOGUS_ROUND
-// Old ll_round implementation - does banker's round (toward nearest even in the case of a 0.5.
-// Not using this because we don't have a consistent implementation on both platforms, use
-// llfloor(val + 0.5f), which is consistent on all platforms.
-inline S32 ll_round(const F32 val)
-{
- #if LL_WINDOWS
- // Note: assumes that the floating point control word is set to rounding mode (the default)
- S32 ret_val;
- _asm fld val
- _asm fistp ret_val;
- return ret_val;
- #elif LL_LINUX
- // Note: assumes that the floating point control word is set
- // to rounding mode (the default)
- S32 ret_val;
- __asm__ __volatile__( "flds %1 \n\t"
- "fistpl %0 \n\t"
- : "=m" (ret_val)
- : "m" (val) );
- return ret_val;
- #else
- return llfloor(val + 0.5f);
- #endif
-}
-
-// A fast arithmentic round on intel, from Laurent de Soras http://ldesoras.free.fr
-inline int round_int(double x)
-{
- const float round_to_nearest = 0.5f;
- int i;
- __asm
- {
- fld x
- fadd st, st (0)
- fadd round_to_nearest
- fistp i
- sar i, 1
- }
- return (i);
-}
-#endif // BOGUS_ROUND
-
-inline F64 ll_round(const F64 val)
-{
- return F64(floor(val + 0.5f));
-}
-
-inline F32 ll_round( F32 val, F32 nearest )
-{
- return F32(floor(val * (1.0f / nearest) + 0.5f)) * nearest;
-}
-
-inline F64 ll_round( F64 val, F64 nearest )
-{
- return F64(floor(val * (1.0 / nearest) + 0.5)) * nearest;
-}
-
-// these provide minimum peak error
-//
-// avg error = -0.013049
-// peak error = -31.4 dB
-// RMS error = -28.1 dB
-
-constexpr F32 FAST_MAG_ALPHA = 0.960433870103f;
-constexpr F32 FAST_MAG_BETA = 0.397824734759f;
-
-// these provide minimum RMS error
-//
-// avg error = 0.000003
-// peak error = -32.6 dB
-// RMS error = -25.7 dB
-//
-//constexpr F32 FAST_MAG_ALPHA = 0.948059448969f;
-//constexpr F32 FAST_MAG_BETA = 0.392699081699f;
-
-inline F32 fastMagnitude(F32 a, F32 b)
-{
- a = (a > 0) ? a : -a;
- b = (b > 0) ? b : -b;
- return(FAST_MAG_ALPHA * llmax(a,b) + FAST_MAG_BETA * llmin(a,b));
-}
-
-
-
-////////////////////
-//
-// Fast F32/S32 conversions
-//
-// Culled from www.stereopsis.com/FPU.html
-
-constexpr F64 LL_DOUBLE_TO_FIX_MAGIC = 68719476736.0*1.5; //2^36 * 1.5, (52-_shiftamt=36) uses limited precisicion to floor
-constexpr S32 LL_SHIFT_AMOUNT = 16; //16.16 fixed point representation,
-
-// Endian dependent code
-#ifdef LL_LITTLE_ENDIAN
- #define LL_EXP_INDEX 1
- #define LL_MAN_INDEX 0
-#else
- #define LL_EXP_INDEX 0
- #define LL_MAN_INDEX 1
-#endif
-
-////////////////////////////////////////////////
-//
-// Fast exp and log
-//
-
-// Implementation of fast exp() approximation (from a paper by Nicol N. Schraudolph
-// http://www.inf.ethz.ch/~schraudo/pubs/exp.pdf
-static union
-{
- double d;
- struct
- {
-#ifdef LL_LITTLE_ENDIAN
- S32 j, i;
-#else
- S32 i, j;
-#endif
- } n;
-} LLECO; // not sure what the name means
-
-#define LL_EXP_A (1048576 * OO_LN2) // use 1512775 for integer
-#define LL_EXP_C (60801) // this value of C good for -4 < y < 4
-
-#define LL_FAST_EXP(y) (LLECO.n.i = ll_round(F32(LL_EXP_A*(y))) + (1072693248 - LL_EXP_C), LLECO.d)
-
-inline F32 llfastpow(const F32 x, const F32 y)
-{
- return (F32)(LL_FAST_EXP(y * log(x)));
-}
-
-
-inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs)
-{
- // compute the power of ten
- F32 bar = 1.f;
- for (S32 i = 0; i < sig_figs; i++)
- {
- bar *= 10.f;
- }
-
- F32 sign = (foo > 0.f) ? 1.f : -1.f;
- F32 new_foo = F32( S64(foo * bar + sign * 0.5f));
- new_foo /= bar;
-
- return new_foo;
-}
-
-inline F32 lerp(F32 a, F32 b, F32 u)
-{
- return a + ((b - a) * u);
-}
-
-inline F32 lerp2d(F32 x00, F32 x01, F32 x10, F32 x11, F32 u, F32 v)
-{
- F32 a = x00 + (x01-x00)*u;
- F32 b = x10 + (x11-x10)*u;
- F32 r = a + (b-a)*v;
- return r;
-}
-
-inline F32 ramp(F32 x, F32 a, F32 b)
-{
- return (a == b) ? 0.0f : ((a - x) / (a - b));
-}
-
-inline F32 rescale(F32 x, F32 x1, F32 x2, F32 y1, F32 y2)
-{
- return lerp(y1, y2, ramp(x, x1, x2));
-}
-
-inline F32 clamp_rescale(F32 x, F32 x1, F32 x2, F32 y1, F32 y2)
-{
- if (y1 < y2)
- {
- return llclamp(rescale(x,x1,x2,y1,y2),y1,y2);
- }
- else
- {
- return llclamp(rescale(x,x1,x2,y1,y2),y2,y1);
- }
-}
-
-
-inline F32 cubic_step( F32 x, F32 x0, F32 x1, F32 s0, F32 s1 )
-{
- if (x <= x0)
- return s0;
-
- if (x >= x1)
- return s1;
-
- F32 f = (x - x0) / (x1 - x0);
-
- return s0 + (s1 - s0) * (f * f) * (3.0f - 2.0f * f);
-}
-
-inline F32 cubic_step( F32 x )
-{
- x = llclampf(x);
-
- return (x * x) * (3.0f - 2.0f * x);
-}
-
-inline F32 quadratic_step( F32 x, F32 x0, F32 x1, F32 s0, F32 s1 )
-{
- if (x <= x0)
- return s0;
-
- if (x >= x1)
- return s1;
-
- F32 f = (x - x0) / (x1 - x0);
- F32 f_squared = f * f;
-
- return (s0 * (1.f - f_squared)) + ((s1 - s0) * f_squared);
-}
-
-inline F32 llsimple_angle(F32 angle)
-{
- while(angle <= -F_PI)
- angle += F_TWO_PI;
- while(angle > F_PI)
- angle -= F_TWO_PI;
- return angle;
-}
-
-//SDK - Renamed this to get_lower_power_two, since this is what this actually does.
-inline U32 get_lower_power_two(U32 val, U32 max_power_two)
-{
- if(!max_power_two)
- {
- max_power_two = 1 << 31 ;
- }
- if(max_power_two & (max_power_two - 1))
- {
- return 0 ;
- }
-
- for(; val < max_power_two ; max_power_two >>= 1) ;
-
- return max_power_two ;
-}
-
-// calculate next highest power of two, limited by max_power_two
-// This is taken from a brilliant little code snipped on http://acius2.blogspot.com/2007/11/calculating-next-power-of-2.html
-// Basically we convert the binary to a solid string of 1's with the same
-// number of digits, then add one. We subtract 1 initially to handle
-// the case where the number passed in is actually a power of two.
-// WARNING: this only works with 32 bit ints.
-inline U32 get_next_power_two(U32 val, U32 max_power_two)
-{
- if(!max_power_two)
- {
- max_power_two = 1 << 31 ;
- }
-
- if(val >= max_power_two)
- {
- return max_power_two;
- }
-
- val--;
- val = (val >> 1) | val;
- val = (val >> 2) | val;
- val = (val >> 4) | val;
- val = (val >> 8) | val;
- val = (val >> 16) | val;
- val++;
-
- return val;
-}
-
-//get the gaussian value given the linear distance from axis x and guassian value o
-inline F32 llgaussian(F32 x, F32 o)
-{
- return 1.f/(F_SQRT_TWO_PI*o)*powf(F_E, -(x*x)/(2*o*o));
-}
-
-//helper function for removing outliers
-template <class VEC_TYPE>
-inline void ll_remove_outliers(std::vector<VEC_TYPE>& data, F32 k)
-{
- if (data.size() < 100)
- { //not enough samples
- return;
- }
-
- VEC_TYPE Q1 = data[data.size()/4];
- VEC_TYPE Q3 = data[data.size()-data.size()/4-1];
-
- if ((F32)(Q3-Q1) < 1.f)
- {
- // not enough variation to detect outliers
- return;
- }
-
-
- VEC_TYPE min = (VEC_TYPE) ((F32) Q1-k * (F32) (Q3-Q1));
- VEC_TYPE max = (VEC_TYPE) ((F32) Q3+k * (F32) (Q3-Q1));
-
- U32 i = 0;
- while (i < data.size() && data[i] < min)
- {
- i++;
- }
-
- S32 j = data.size()-1;
- while (j > 0 && data[j] > max)
- {
- j--;
- }
-
- if (j < data.size()-1)
- {
- data.erase(data.begin()+j, data.end());
- }
-
- if (i > 0)
- {
- data.erase(data.begin(), data.begin()+i);
- }
-}
-
-// Converts given value from a linear RGB floating point value (0..1) to a gamma corrected (sRGB) value.
-// Some shaders require color values in linear space, while others require color values in gamma corrected (sRGB) space.
-// Note: in our code, values labeled as sRGB are ALWAYS gamma corrected linear values, NOT linear values with monitor gamma applied
-// Note: stored color values should always be gamma corrected linear (i.e. the values returned from an on-screen color swatch)
-// Note: DO NOT cache the conversion. This leads to error prone synchronization and is actually slower in the typical case due to cache misses
-inline float linearTosRGB(const float val) {
- if (val < 0.0031308f) {
- return val * 12.92f;
- }
- else {
- return 1.055f * pow(val, 1.0f / 2.4f) - 0.055f;
- }
-}
-
-// Converts given value from a gamma corrected (sRGB) floating point value (0..1) to a linear color value.
-// Some shaders require color values in linear space, while others require color values in gamma corrected (sRGB) space.
-// Note: In our code, values labeled as sRGB are gamma corrected linear values, NOT linear values with monitor gamma applied
-// Note: Stored color values should generally be gamma corrected sRGB.
-// If you're serializing the return value of this function, you're probably doing it wrong.
-// Note: DO NOT cache the conversion. This leads to error prone synchronization and is actually slower in the typical case due to cache misses.
-inline float sRGBtoLinear(const float val) {
- if (val < 0.04045f) {
- return val / 12.92f;
- }
- else {
- return pow((val + 0.055f) / 1.055f, 2.4f);
- }
-}
-
-// Include simd math header
-#include "llsimdmath.h"
-
-#endif
+/** + * @file llmath.h + * @brief Useful math constants and macros. + * + * $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$ + */ + +#ifndef LLMATH_H +#define LLMATH_H + +#include <cmath> +#include <cstdlib> +#include <vector> +#include <limits> +#include "lldefs.h" +//#include "llstl.h" // *TODO: Remove when LLString is gone +//#include "llstring.h" // *TODO: Remove when LLString is gone +// lltut.h uses is_approx_equal_fraction(). This was moved to its own header +// file in llcommon so we can use lltut.h for llcommon tests without making +// llcommon depend on llmath. +#include "is_approx_equal_fraction.h" + +// work around for Windows & older gcc non-standard function names. +#if LL_WINDOWS +#include <float.h> +#define llisnan(val) _isnan(val) +#define llfinite(val) _finite(val) +#elif (LL_LINUX && __GNUC__ <= 2) +#define llisnan(val) isnan(val) +#define llfinite(val) isfinite(val) +#else +#define llisnan(val) std::isnan(val) +#define llfinite(val) std::isfinite(val) +#endif + +// Single Precision Floating Point Routines +// (There used to be more defined here, but they appeared to be redundant and +// were breaking some other includes. Removed by Falcon, reviewed by Andrew, 11/25/09) +/*#ifndef tanf +#define tanf(x) ((F32)tan((F64)(x))) +#endif*/ + +constexpr F32 GRAVITY = -9.8f; + +// mathematical constants +constexpr F32 F_PI = 3.1415926535897932384626433832795f; +constexpr F32 F_TWO_PI = 6.283185307179586476925286766559f; +constexpr F32 F_PI_BY_TWO = 1.5707963267948966192313216916398f; +constexpr F32 F_SQRT_TWO_PI = 2.506628274631000502415765284811f; +constexpr F32 F_E = 2.71828182845904523536f; +constexpr F32 F_SQRT2 = 1.4142135623730950488016887242097f; +constexpr F32 F_SQRT3 = 1.73205080756888288657986402541f; +constexpr F32 OO_SQRT2 = 0.7071067811865475244008443621049f; +constexpr F32 OO_SQRT3 = 0.577350269189625764509f; +constexpr F32 DEG_TO_RAD = 0.017453292519943295769236907684886f; +constexpr F32 RAD_TO_DEG = 57.295779513082320876798154814105f; +constexpr F32 F_APPROXIMATELY_ZERO = 0.00001f; +constexpr F32 F_LN10 = 2.3025850929940456840179914546844f; +constexpr F32 OO_LN10 = 0.43429448190325182765112891891661; +constexpr F32 F_LN2 = 0.69314718056f; +constexpr F32 OO_LN2 = 1.4426950408889634073599246810019f; + +constexpr F32 F_ALMOST_ZERO = 0.0001f; +constexpr F32 F_ALMOST_ONE = 1.0f - F_ALMOST_ZERO; + +constexpr F32 GIMBAL_THRESHOLD = 0.000436f; // sets the gimballock threshold 0.025 away from +/-90 degrees +// formula: GIMBAL_THRESHOLD = sin(DEG_TO_RAD * gimbal_threshold_angle); + +// BUG: Eliminate in favor of F_APPROXIMATELY_ZERO above? +constexpr F32 FP_MAG_THRESHOLD = 0.0000001f; + +// TODO: Replace with logic like is_approx_equal +inline bool is_approx_zero( F32 f ) { return (-F_APPROXIMATELY_ZERO < f) && (f < F_APPROXIMATELY_ZERO); } + +// These functions work by interpreting sign+exp+mantissa as an unsigned +// integer. +// For example: +// x = <sign>1 <exponent>00000010 <mantissa>00000000000000000000000 +// y = <sign>1 <exponent>00000001 <mantissa>11111111111111111111111 +// +// interpreted as ints = +// x = 10000001000000000000000000000000 +// y = 10000000111111111111111111111111 +// which is clearly a different of 1 in the least significant bit +// Values with the same exponent can be trivially shown to work. +// +// WARNING: Denormals of opposite sign do not work +// x = <sign>1 <exponent>00000000 <mantissa>00000000000000000000001 +// y = <sign>0 <exponent>00000000 <mantissa>00000000000000000000001 +// Although these values differ by 2 in the LSB, the sign bit makes +// the int comparison fail. +// +// WARNING: NaNs can compare equal +// There is no special treatment of exceptional values like NaNs +// +// WARNING: Infinity is comparable with F32_MAX and negative +// infinity is comparable with F32_MIN + +// handles negative and positive zeros +inline bool is_zero(F32 x) +{ + return (*(U32*)(&x) & 0x7fffffff) == 0; +} + +inline bool is_approx_equal(F32 x, F32 y) +{ + constexpr S32 COMPARE_MANTISSA_UP_TO_BIT = 0x02; + return (std::abs((S32) ((U32&)x - (U32&)y) ) < COMPARE_MANTISSA_UP_TO_BIT); +} + +inline bool is_approx_equal(F64 x, F64 y) +{ + constexpr S64 COMPARE_MANTISSA_UP_TO_BIT = 0x02; + return (std::abs((S32) ((U64&)x - (U64&)y) ) < COMPARE_MANTISSA_UP_TO_BIT); +} + +inline S32 llabs(const S32 a) +{ + return S32(std::labs(a)); +} + +inline F32 llabs(const F32 a) +{ + return F32(std::fabs(a)); +} + +inline F64 llabs(const F64 a) +{ + return F64(std::fabs(a)); +} + +inline S32 lltrunc( F32 f ) +{ +#if LL_WINDOWS && !defined( __INTEL_COMPILER ) && (ADDRESS_SIZE == 32) + // Avoids changing the floating point control word. + // Add or subtract 0.5 - epsilon and then round + const static U32 zpfp[] = { 0xBEFFFFFF, 0x3EFFFFFF }; + S32 result; + __asm { + fld f + mov eax, f + shr eax, 29 + and eax, 4 + fadd dword ptr [zpfp + eax] + fistp result + } + return result; +#else + return (S32)f; +#endif +} + +inline S32 lltrunc( F64 f ) +{ + return (S32)f; +} + +inline S32 llfloor( F32 f ) +{ +#if LL_WINDOWS && !defined( __INTEL_COMPILER ) && (ADDRESS_SIZE == 32) + // Avoids changing the floating point control word. + // Accurate (unlike Stereopsis version) for all values between S32_MIN and S32_MAX and slightly faster than Stereopsis version. + // Add -(0.5 - epsilon) and then round + const U32 zpfp = 0xBEFFFFFF; + S32 result; + __asm { + fld f + fadd dword ptr [zpfp] + fistp result + } + return result; +#else + return (S32)floor(f); +#endif +} + + +inline S32 llceil( F32 f ) +{ + // This could probably be optimized, but this works. + return (S32)ceil(f); +} + + +#ifndef BOGUS_ROUND +// Use this round. Does an arithmetic round (0.5 always rounds up) +inline S32 ll_round(const F32 val) +{ + return llfloor(val + 0.5f); +} + +#else // BOGUS_ROUND +// Old ll_round implementation - does banker's round (toward nearest even in the case of a 0.5. +// Not using this because we don't have a consistent implementation on both platforms, use +// llfloor(val + 0.5f), which is consistent on all platforms. +inline S32 ll_round(const F32 val) +{ + #if LL_WINDOWS + // Note: assumes that the floating point control word is set to rounding mode (the default) + S32 ret_val; + _asm fld val + _asm fistp ret_val; + return ret_val; + #elif LL_LINUX + // Note: assumes that the floating point control word is set + // to rounding mode (the default) + S32 ret_val; + __asm__ __volatile__( "flds %1 \n\t" + "fistpl %0 \n\t" + : "=m" (ret_val) + : "m" (val) ); + return ret_val; + #else + return llfloor(val + 0.5f); + #endif +} + +// A fast arithmentic round on intel, from Laurent de Soras http://ldesoras.free.fr +inline int round_int(double x) +{ + const float round_to_nearest = 0.5f; + int i; + __asm + { + fld x + fadd st, st (0) + fadd round_to_nearest + fistp i + sar i, 1 + } + return (i); +} +#endif // BOGUS_ROUND + +inline F64 ll_round(const F64 val) +{ + return F64(floor(val + 0.5f)); +} + +inline F32 ll_round( F32 val, F32 nearest ) +{ + return F32(floor(val * (1.0f / nearest) + 0.5f)) * nearest; +} + +inline F64 ll_round( F64 val, F64 nearest ) +{ + return F64(floor(val * (1.0 / nearest) + 0.5)) * nearest; +} + +// these provide minimum peak error +// +// avg error = -0.013049 +// peak error = -31.4 dB +// RMS error = -28.1 dB + +constexpr F32 FAST_MAG_ALPHA = 0.960433870103f; +constexpr F32 FAST_MAG_BETA = 0.397824734759f; + +// these provide minimum RMS error +// +// avg error = 0.000003 +// peak error = -32.6 dB +// RMS error = -25.7 dB +// +//constexpr F32 FAST_MAG_ALPHA = 0.948059448969f; +//constexpr F32 FAST_MAG_BETA = 0.392699081699f; + +inline F32 fastMagnitude(F32 a, F32 b) +{ + a = (a > 0) ? a : -a; + b = (b > 0) ? b : -b; + return(FAST_MAG_ALPHA * llmax(a,b) + FAST_MAG_BETA * llmin(a,b)); +} + + + +//////////////////// +// +// Fast F32/S32 conversions +// +// Culled from www.stereopsis.com/FPU.html + +constexpr F64 LL_DOUBLE_TO_FIX_MAGIC = 68719476736.0*1.5; //2^36 * 1.5, (52-_shiftamt=36) uses limited precisicion to floor +constexpr S32 LL_SHIFT_AMOUNT = 16; //16.16 fixed point representation, + +// Endian dependent code +#ifdef LL_LITTLE_ENDIAN + #define LL_EXP_INDEX 1 + #define LL_MAN_INDEX 0 +#else + #define LL_EXP_INDEX 0 + #define LL_MAN_INDEX 1 +#endif + +//////////////////////////////////////////////// +// +// Fast exp and log +// + +// Implementation of fast exp() approximation (from a paper by Nicol N. Schraudolph +// http://www.inf.ethz.ch/~schraudo/pubs/exp.pdf +static union +{ + double d; + struct + { +#ifdef LL_LITTLE_ENDIAN + S32 j, i; +#else + S32 i, j; +#endif + } n; +} LLECO; // not sure what the name means + +#define LL_EXP_A (1048576 * OO_LN2) // use 1512775 for integer +#define LL_EXP_C (60801) // this value of C good for -4 < y < 4 + +#define LL_FAST_EXP(y) (LLECO.n.i = ll_round(F32(LL_EXP_A*(y))) + (1072693248 - LL_EXP_C), LLECO.d) + +inline F32 llfastpow(const F32 x, const F32 y) +{ + return (F32)(LL_FAST_EXP(y * log(x))); +} + + +inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs) +{ + // compute the power of ten + F32 bar = 1.f; + for (S32 i = 0; i < sig_figs; i++) + { + bar *= 10.f; + } + + F32 sign = (foo > 0.f) ? 1.f : -1.f; + F32 new_foo = F32( S64(foo * bar + sign * 0.5f)); + new_foo /= bar; + + return new_foo; +} + +inline F32 lerp(F32 a, F32 b, F32 u) +{ + return a + ((b - a) * u); +} + +inline F32 lerp2d(F32 x00, F32 x01, F32 x10, F32 x11, F32 u, F32 v) +{ + F32 a = x00 + (x01-x00)*u; + F32 b = x10 + (x11-x10)*u; + F32 r = a + (b-a)*v; + return r; +} + +inline F32 ramp(F32 x, F32 a, F32 b) +{ + return (a == b) ? 0.0f : ((a - x) / (a - b)); +} + +inline F32 rescale(F32 x, F32 x1, F32 x2, F32 y1, F32 y2) +{ + return lerp(y1, y2, ramp(x, x1, x2)); +} + +inline F32 clamp_rescale(F32 x, F32 x1, F32 x2, F32 y1, F32 y2) +{ + if (y1 < y2) + { + return llclamp(rescale(x,x1,x2,y1,y2),y1,y2); + } + else + { + return llclamp(rescale(x,x1,x2,y1,y2),y2,y1); + } +} + + +inline F32 cubic_step( F32 x, F32 x0, F32 x1, F32 s0, F32 s1 ) +{ + if (x <= x0) + return s0; + + if (x >= x1) + return s1; + + F32 f = (x - x0) / (x1 - x0); + + return s0 + (s1 - s0) * (f * f) * (3.0f - 2.0f * f); +} + +inline F32 cubic_step( F32 x ) +{ + x = llclampf(x); + + return (x * x) * (3.0f - 2.0f * x); +} + +inline F32 quadratic_step( F32 x, F32 x0, F32 x1, F32 s0, F32 s1 ) +{ + if (x <= x0) + return s0; + + if (x >= x1) + return s1; + + F32 f = (x - x0) / (x1 - x0); + F32 f_squared = f * f; + + return (s0 * (1.f - f_squared)) + ((s1 - s0) * f_squared); +} + +inline F32 llsimple_angle(F32 angle) +{ + while(angle <= -F_PI) + angle += F_TWO_PI; + while(angle > F_PI) + angle -= F_TWO_PI; + return angle; +} + +//SDK - Renamed this to get_lower_power_two, since this is what this actually does. +inline U32 get_lower_power_two(U32 val, U32 max_power_two) +{ + if(!max_power_two) + { + max_power_two = 1 << 31 ; + } + if(max_power_two & (max_power_two - 1)) + { + return 0 ; + } + + for(; val < max_power_two ; max_power_two >>= 1) ; + + return max_power_two ; +} + +// calculate next highest power of two, limited by max_power_two +// This is taken from a brilliant little code snipped on http://acius2.blogspot.com/2007/11/calculating-next-power-of-2.html +// Basically we convert the binary to a solid string of 1's with the same +// number of digits, then add one. We subtract 1 initially to handle +// the case where the number passed in is actually a power of two. +// WARNING: this only works with 32 bit ints. +inline U32 get_next_power_two(U32 val, U32 max_power_two) +{ + if(!max_power_two) + { + max_power_two = 1 << 31 ; + } + + if(val >= max_power_two) + { + return max_power_two; + } + + val--; + val = (val >> 1) | val; + val = (val >> 2) | val; + val = (val >> 4) | val; + val = (val >> 8) | val; + val = (val >> 16) | val; + val++; + + return val; +} + +//get the gaussian value given the linear distance from axis x and guassian value o +inline F32 llgaussian(F32 x, F32 o) +{ + return 1.f/(F_SQRT_TWO_PI*o)*powf(F_E, -(x*x)/(2*o*o)); +} + +//helper function for removing outliers +template <class VEC_TYPE> +inline void ll_remove_outliers(std::vector<VEC_TYPE>& data, F32 k) +{ + if (data.size() < 100) + { //not enough samples + return; + } + + VEC_TYPE Q1 = data[data.size()/4]; + VEC_TYPE Q3 = data[data.size()-data.size()/4-1]; + + if ((F32)(Q3-Q1) < 1.f) + { + // not enough variation to detect outliers + return; + } + + + VEC_TYPE min = (VEC_TYPE) ((F32) Q1-k * (F32) (Q3-Q1)); + VEC_TYPE max = (VEC_TYPE) ((F32) Q3+k * (F32) (Q3-Q1)); + + U32 i = 0; + while (i < data.size() && data[i] < min) + { + i++; + } + + S32 j = data.size()-1; + while (j > 0 && data[j] > max) + { + j--; + } + + if (j < data.size()-1) + { + data.erase(data.begin()+j, data.end()); + } + + if (i > 0) + { + data.erase(data.begin(), data.begin()+i); + } +} + +// Converts given value from a linear RGB floating point value (0..1) to a gamma corrected (sRGB) value. +// Some shaders require color values in linear space, while others require color values in gamma corrected (sRGB) space. +// Note: in our code, values labeled as sRGB are ALWAYS gamma corrected linear values, NOT linear values with monitor gamma applied +// Note: stored color values should always be gamma corrected linear (i.e. the values returned from an on-screen color swatch) +// Note: DO NOT cache the conversion. This leads to error prone synchronization and is actually slower in the typical case due to cache misses +inline float linearTosRGB(const float val) { + if (val < 0.0031308f) { + return val * 12.92f; + } + else { + return 1.055f * pow(val, 1.0f / 2.4f) - 0.055f; + } +} + +// Converts given value from a gamma corrected (sRGB) floating point value (0..1) to a linear color value. +// Some shaders require color values in linear space, while others require color values in gamma corrected (sRGB) space. +// Note: In our code, values labeled as sRGB are gamma corrected linear values, NOT linear values with monitor gamma applied +// Note: Stored color values should generally be gamma corrected sRGB. +// If you're serializing the return value of this function, you're probably doing it wrong. +// Note: DO NOT cache the conversion. This leads to error prone synchronization and is actually slower in the typical case due to cache misses. +inline float sRGBtoLinear(const float val) { + if (val < 0.04045f) { + return val / 12.92f; + } + else { + return pow((val + 0.055f) / 1.055f, 2.4f); + } +} + +// Include simd math header +#include "llsimdmath.h" + +#endif diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h index 6e43646b60..475ce20ae6 100644 --- a/indra/llmath/lloctree.h +++ b/indra/llmath/lloctree.h @@ -1,858 +1,858 @@ -/**
- * @file lloctree.h
- * @brief Octree declaration.
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLOCTREE_H
-#define LL_LLOCTREE_H
-
-#include "lltreenode.h"
-#include "v3math.h"
-#include "llvector4a.h"
-#include <vector>
-
-#define OCT_ERRS LL_WARNS("OctreeErrors")
-
-#define OCTREE_DEBUG_COLOR_REMOVE 0x0000FF // r
-#define OCTREE_DEBUG_COLOR_INSERT 0x00FF00 // g
-#define OCTREE_DEBUG_COLOR_BALANCE 0xFF0000 // b
-
-extern U32 gOctreeMaxCapacity;
-extern float gOctreeMinSize;
-
-/*#define LL_OCTREE_PARANOIA_CHECK 0
-#if LL_DARWIN
-#define LL_OCTREE_MAX_CAPACITY 32
-#else
-#define LL_OCTREE_MAX_CAPACITY 128
-#endif*/
-
-// T is the type of the element referenced by the octree node.
-// T_PTR determines how pointers to elements are stored internally.
-// LLOctreeNode<T, LLPointer<T>> assumes ownership of inserted elements and
-// deletes elements removed from the tree.
-// LLOctreeNode<T, T*> doesn't take ownership of inserted elements, so the API
-// user is responsible for managing the storage lifecycle of elements added to
-// the tree.
-template <class T, typename T_PTR> class LLOctreeNode;
-
-template <class T, typename T_PTR>
-class LLOctreeListener: public LLTreeListener<T>
-{
-public:
- typedef LLTreeListener<T> BaseType;
- typedef LLOctreeNode<T, T_PTR> oct_node;
-
- virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0;
- virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0;
-};
-
-template <class T, typename T_PTR>
-class LLOctreeTraveler
-{
-public:
- virtual void traverse(const LLOctreeNode<T, T_PTR>* node);
- virtual void visit(const LLOctreeNode<T, T_PTR>* branch) = 0;
-};
-
-template <class T, typename T_PTR>
-class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T, T_PTR>
-{
-public:
- virtual void traverse(const LLOctreeNode<T, T_PTR>* node) override;
-};
-
-template <class T, typename T_PTR>
-class alignas(16) LLOctreeNode : public LLTreeNode<T>
-{
- LL_ALIGN_NEW
-public:
-
- typedef LLOctreeTraveler<T, T_PTR> oct_traveler;
- typedef LLTreeTraveler<T> tree_traveler;
- typedef std::vector<T_PTR> element_list;
- typedef typename element_list::iterator element_iter;
- typedef typename element_list::const_iterator const_element_iter;
- typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter;
- typedef LLOctreeNode<T, T_PTR>** child_list;
- typedef LLOctreeNode<T, T_PTR>** child_iter;
-
- typedef LLTreeNode<T> BaseType;
- typedef LLOctreeNode<T, T_PTR> oct_node;
- typedef LLOctreeListener<T, T_PTR> oct_listener;
-
- enum
- {
- NO_CHILD_NODES = 255 // Note: This is an U8 to match the max value in mChildMap[]
- };
-
- LLOctreeNode( const LLVector4a& center,
- const LLVector4a& size,
- BaseType* parent,
- U8 octant = NO_CHILD_NODES)
- : mParent((oct_node*)parent),
- mOctant(octant)
- {
- llassert(size[0] >= gOctreeMinSize*0.5f);
-
- mCenter = center;
- mSize = size;
-
- updateMinMax();
- if ((mOctant == NO_CHILD_NODES) && mParent)
- {
- mOctant = ((oct_node*) mParent)->getOctant(mCenter);
- }
-
- clearChildren();
- }
-
- virtual ~LLOctreeNode()
- {
- BaseType::destroyListeners();
-
- const U32 element_count = getElementCount();
- for (U32 i = 0; i < element_count; ++i)
- {
- mData[i]->setBinIndex(-1);
- mData[i] = NULL;
- }
-
- mData.clear();
-
- for (U32 i = 0; i < getChildCount(); i++)
- {
- delete getChild(i);
- }
- }
-
- inline const BaseType* getParent() const { return mParent; }
- inline void setParent(BaseType* parent) { mParent = (oct_node*) parent; }
- inline const LLVector4a& getCenter() const { return mCenter; }
- inline const LLVector4a& getSize() const { return mSize; }
- inline void setCenter(const LLVector4a& center) { mCenter = center; }
- inline void setSize(const LLVector4a& size) { mSize = size; }
- inline oct_node* getNodeAt(T* data) { return getNodeAt(data->getPositionGroup(), data->getBinRadius()); }
- inline U8 getOctant() const { return mOctant; }
- inline const oct_node* getOctParent() const { return (const oct_node*) getParent(); }
- inline oct_node* getOctParent() { return (oct_node*) getParent(); }
-
- U8 getOctant(const LLVector4a& pos) const //get the octant pos is in
- {
- return (U8) (pos.greaterThan(mCenter).getGatheredBits() & 0x7);
- }
-
- inline bool isInside(const LLVector4a& pos, const F32& rad) const
- {
- return rad <= mSize[0]*2.f && isInside(pos);
- }
-
- inline bool isInside(T* data) const
- {
- return isInside(data->getPositionGroup(), data->getBinRadius());
- }
-
- bool isInside(const LLVector4a& pos) const
- {
- S32 gt = pos.greaterThan(mMax).getGatheredBits() & 0x7;
- if (gt)
- {
- return false;
- }
-
- S32 lt = pos.lessEqual(mMin).getGatheredBits() & 0x7;
- if (lt)
- {
- return false;
- }
-
- return true;
- }
-
- void updateMinMax()
- {
- mMax.setAdd(mCenter, mSize);
- mMin.setSub(mCenter, mSize);
- }
-
- inline oct_listener* getOctListener(U32 index)
- {
- return (oct_listener*) BaseType::getListener(index);
- }
-
- inline bool contains(T* xform)
- {
- return contains(xform->getBinRadius());
- }
-
- bool contains(F32 radius)
- {
- if (mParent == NULL)
- { //root node contains nothing
- return false;
- }
-
- F32 size = mSize[0];
- F32 p_size = size * 2.f;
-
- return (radius <= gOctreeMinSize && size <= gOctreeMinSize) ||
- (radius <= p_size && radius > size);
- }
-
- static void pushCenter(LLVector4a ¢er, const LLVector4a &size, const T* data)
- {
- const LLVector4a& pos = data->getPositionGroup();
-
- LLVector4Logical gt = pos.greaterThan(center);
-
- LLVector4a up;
- up = _mm_and_ps(size, gt);
-
- LLVector4a down;
- down = _mm_andnot_ps(gt, size);
-
- center.add(up);
- center.sub(down);
- }
-
- void accept(oct_traveler* visitor) { visitor->visit(this); }
- virtual bool isLeaf() const { return mChildCount == 0; }
-
- U32 getElementCount() const { return (U32)mData.size(); }
- bool isEmpty() const { return mData.empty(); }
- element_iter getDataBegin() { return mData.begin(); }
- element_iter getDataEnd() { return mData.end(); }
- const_element_iter getDataBegin() const { return mData.cbegin(); }
- const_element_iter getDataEnd() const { return mData.cend(); }
-
- U32 getChildCount() const { return mChildCount; }
- oct_node* getChild(U32 index) { return mChild[index]; }
- const oct_node* getChild(U32 index) const { return mChild[index]; }
- child_list& getChildren() { return mChild; }
- const child_list& getChildren() const { return mChild; }
-
- void accept(tree_traveler* visitor) const { visitor->visit(this); }
- void accept(oct_traveler* visitor) const { visitor->visit(this); }
-
- void validateChildMap()
- {
- for (U32 i = 0; i < 8; i++)
- {
- U8 idx = mChildMap[i];
- if (idx != NO_CHILD_NODES)
- {
- oct_node* child = mChild[idx];
-
- if (child->getOctant() != i)
- {
- LL_ERRS() << "Invalid child map, bad octant data." << LL_ENDL;
- }
-
- if (getOctant(child->getCenter()) != child->getOctant())
- {
- LL_ERRS() << "Invalid child octant compared to position data." << LL_ENDL;
- }
- }
- }
- }
-
-
- oct_node* getNodeAt(const LLVector4a& pos, const F32& rad)
- {
- oct_node* node = this;
-
- if (node->isInside(pos, rad))
- {
- //do a quick search by octant
- U8 octant = node->getOctant(pos);
-
- //traverse the tree until we find a node that has no node
- //at the appropriate octant or is smaller than the object.
- //by definition, that node is the smallest node that contains
- // the data
- U8 next_node = node->mChildMap[octant];
-
- while (next_node != NO_CHILD_NODES && node->getSize()[0] >= rad)
- {
- node = node->getChild(next_node);
- octant = node->getOctant(pos);
- next_node = node->mChildMap[octant];
- }
- }
- else if (!node->contains(rad) && node->getParent())
- { //if we got here, data does not exist in this node
- return ((oct_node*) node->getParent())->getNodeAt(pos, rad);
- }
-
- return node;
- }
-
- virtual bool insert(T* data)
- {
- //LL_PROFILE_ZONE_NAMED_COLOR("Octree::insert()",OCTREE_DEBUG_COLOR_INSERT);
-
- if (data == NULL || data->getBinIndex() != -1)
- {
- OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL;
- return false;
- }
- oct_node* parent = getOctParent();
-
- //is it here?
- if (isInside(data->getPositionGroup()))
- {
- if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) ||
- (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity)))
- { //it belongs here
- mData.push_back(data);
- data->setBinIndex(getElementCount() - 1);
- BaseType::insert(data);
- return true;
- }
- else
- {
- //find a child to give it to
- oct_node* child = NULL;
- for (U32 i = 0; i < getChildCount(); i++)
- {
- child = getChild(i);
- if (child->isInside(data->getPositionGroup()))
- {
- child->insert(data);
- return false;
- }
- }
-
- //it's here, but no kids are in the right place, make a new kid
- LLVector4a center = getCenter();
- LLVector4a size = getSize();
- size.mul(0.5f);
-
- //push center in direction of data
- oct_node::pushCenter(center, size, data);
-
- // handle case where floating point number gets too small
- LLVector4a val;
- val.setSub(center, getCenter());
- val.setAbs(val);
- LLVector4a min_diff(gOctreeMinSize);
-
- S32 lt = val.lessThan(min_diff).getGatheredBits() & 0x7;
-
- if( lt == 0x7 )
- {
- mData.push_back(data);
- data->setBinIndex(getElementCount() - 1);
- BaseType::insert(data);
- return true;
- }
-
-#if LL_OCTREE_PARANOIA_CHECK
- if (getChildCount() == 8)
- {
- //this really isn't possible, something bad has happened
- OCT_ERRS << "Octree detected floating point error and gave up." << LL_ENDL;
- return false;
- }
-
- //make sure no existing node matches this position
- for (U32 i = 0; i < getChildCount(); i++)
- {
- if (mChild[i]->getCenter().equals3(center))
- {
- OCT_ERRS << "Octree detected duplicate child center and gave up." << LL_ENDL;
- return false;
- }
- }
-#endif
-
- llassert(size[0] >= gOctreeMinSize*0.5f);
- //make the new kid
- child = new oct_node(center, size, this);
- addChild(child);
-
- child->insert(data);
- }
- }
- else if (parent)
- {
- //it's not in here, give it to the root
- OCT_ERRS << "Octree insertion failed, starting over from root!" << LL_ENDL;
-
- oct_node* node = this;
-
- while (parent)
- {
- node = parent;
- parent = node->getOctParent();
- }
-
- node->insert(data);
- }
- else
- {
- // It's not in here, and we are root.
- // LLOctreeRoot::insert() should have expanded
- // root by now, something is wrong
- OCT_ERRS << "Octree insertion failed! Root expansion failed." << LL_ENDL;
- }
-
- return false;
- }
-
- void _remove(T* data, S32 i)
- { //precondition -- getElementCount() > 0, idx is in range [0, getElementCount())
-
- data->setBinIndex(-1);
-
- const U32 new_element_count = getElementCount() - 1;
- if (new_element_count > 0)
- {
- if (new_element_count != i)
- {
- mData[i] = mData[new_element_count]; //might unref data, do not access data after this point
- mData[i]->setBinIndex(i);
- }
-
- mData[new_element_count] = NULL;
- mData.pop_back();
- }
- else
- {
- mData.clear();
- }
-
- this->notifyRemoval(data);
- checkAlive();
- }
-
- bool remove(T* data)
- {
- //LL_PROFILE_ZONE_NAMED_COLOR("Octree::remove()", OCTREE_DEBUG_COLOR_REMOVE);
-
- S32 i = data->getBinIndex();
-
- if (i >= 0 && i < getElementCount())
- {
- if (mData[i] == data)
- { //found it
- _remove(data, i);
- llassert(data->getBinIndex() == -1);
- return true;
- }
- }
-
- if (isInside(data))
- {
- oct_node* dest = getNodeAt(data);
-
- if (dest != this)
- {
- bool ret = dest->remove(data);
- llassert(data->getBinIndex() == -1);
- return ret;
- }
- }
-
- //SHE'S GONE MISSING...
- //none of the children have it, let's just brute force this bastard out
- //starting with the root node (UGLY CODE COMETH!)
- oct_node* parent = getOctParent();
- oct_node* node = this;
-
- while (parent != NULL)
- {
- node = parent;
- parent = node->getOctParent();
- }
-
- //node is now root
- LL_WARNS() << "!!! OCTREE REMOVING ELEMENT BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << LL_ENDL;
- node->removeByAddress(data);
- llassert(data->getBinIndex() == -1);
- return true;
- }
-
- void removeByAddress(T* data)
- {
- const U32 element_count = getElementCount();
- for (U32 i = 0; i < element_count; ++i)
- {
- if (mData[i] == data)
- { //we have data
- _remove(data, i);
- LL_WARNS() << "FOUND!" << LL_ENDL;
- return;
- }
- }
-
- for (U32 i = 0; i < getChildCount(); i++)
- { //we don't contain data, so pass this guy down
- oct_node* child = (oct_node*) getChild(i);
- child->removeByAddress(data);
- }
- }
-
- void clearChildren()
- {
- mChildCount = 0;
- memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap));
- }
-
- void validate()
- {
-#if LL_OCTREE_PARANOIA_CHECK
- for (U32 i = 0; i < getChildCount(); i++)
- {
- mChild[i]->validate();
- if (mChild[i]->getParent() != this)
- {
- LL_ERRS() << "Octree child has invalid parent." << LL_ENDL;
- }
- }
-#endif
- }
-
- virtual bool balance()
- {
- return false;
- }
-
- void destroy()
- {
- for (U32 i = 0; i < getChildCount(); i++)
- {
- mChild[i]->destroy();
- delete mChild[i];
- }
- }
-
- void addChild(oct_node* child, bool silent = false)
- {
-#if LL_OCTREE_PARANOIA_CHECK
-
- if (child->getSize().equals3(getSize()))
- {
- OCT_ERRS << "Child size is same as parent size!" << LL_ENDL;
- }
-
- for (U32 i = 0; i < getChildCount(); i++)
- {
- if(!mChild[i]->getSize().equals3(child->getSize()))
- {
- OCT_ERRS <<"Invalid octree child size." << LL_ENDL;
- }
- if (mChild[i]->getCenter().equals3(child->getCenter()))
- {
- OCT_ERRS <<"Duplicate octree child position." << LL_ENDL;
- }
- }
-
- if (mChild.size() >= 8)
- {
- OCT_ERRS <<"Octree node has too many children... why?" << LL_ENDL;
- }
-#endif
-
- mChildMap[child->getOctant()] = mChildCount;
-
- mChild[mChildCount] = child;
- ++mChildCount;
- child->setParent(this);
-
- if (!silent)
- {
- for (U32 i = 0; i < this->getListenerCount(); i++)
- {
- oct_listener* listener = getOctListener(i);
- listener->handleChildAddition(this, child);
- }
- }
- }
-
- void removeChild(S32 index, bool destroy = false)
- {
- for (U32 i = 0; i < this->getListenerCount(); i++)
- {
- oct_listener* listener = getOctListener(i);
- listener->handleChildRemoval(this, getChild(index));
- }
-
- if (destroy)
- {
- mChild[index]->destroy();
- delete mChild[index];
- }
-
- --mChildCount;
-
- mChild[index] = mChild[mChildCount];
-
- //rebuild child map
- memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap));
-
- for (U32 i = 0; i < mChildCount; ++i)
- {
- mChildMap[mChild[i]->getOctant()] = i;
- }
-
- checkAlive();
- }
-
- void checkAlive()
- {
- if (getChildCount() == 0 && getElementCount() == 0)
- {
- oct_node* parent = getOctParent();
- if (parent)
- {
- parent->deleteChild(this);
- }
- }
- }
-
- void deleteChild(oct_node* node)
- {
- for (U32 i = 0; i < getChildCount(); i++)
- {
- if (getChild(i) == node)
- {
- removeChild(i, true);
- return;
- }
- }
-
- OCT_ERRS << "Octree failed to delete requested child." << LL_ENDL;
- }
-
-protected:
- typedef enum
- {
- CENTER = 0,
- SIZE = 1,
- MAX = 2,
- MIN = 3
- } eDName;
-
- LLVector4a mCenter;
- LLVector4a mSize;
- LLVector4a mMax;
- LLVector4a mMin;
-
- oct_node* mParent;
- U8 mOctant;
-
- oct_node* mChild[8];
- U8 mChildMap[8];
- U32 mChildCount;
-
- element_list mData;
-};
-
-//just like a regular node, except it might expand on insert and compress on balance
-template <class T, typename T_PTR>
-class LLOctreeRoot : public LLOctreeNode<T, T_PTR>
-{
-public:
- typedef LLOctreeNode<T, T_PTR> BaseType;
- typedef LLOctreeNode<T, T_PTR> oct_node;
-
- LLOctreeRoot(const LLVector4a& center,
- const LLVector4a& size,
- BaseType* parent)
- : BaseType(center, size, parent)
- {
- }
-
- bool balance() override
- {
- //LL_PROFILE_ZONE_NAMED_COLOR("Octree::balance()",OCTREE_DEBUG_COLOR_BALANCE);
-
- if (this->getChildCount() == 1 &&
- !(this->mChild[0]->isLeaf()) &&
- this->mChild[0]->getElementCount() == 0)
- { //if we have only one child and that child is an empty branch, make that child the root
- oct_node* child = this->mChild[0];
-
- //make the root node look like the child
- this->setCenter(this->mChild[0]->getCenter());
- this->setSize(this->mChild[0]->getSize());
- this->updateMinMax();
-
- //reset root node child list
- this->clearChildren();
-
- //copy the child's children into the root node silently
- //(don't notify listeners of addition)
- for (U32 i = 0; i < child->getChildCount(); i++)
- {
- this->addChild(child->getChild(i), true);
- }
-
- //destroy child
- child->clearChildren();
- delete child;
-
- return false;
- }
-
- return true;
- }
-
- // LLOctreeRoot::insert
- bool insert(T* data) override
- {
- if (data == nullptr)
- {
- OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << LL_ENDL;
- return false;
- }
-
- if (data->getBinRadius() > 4096.0)
- {
- OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << LL_ENDL;
- return false;
- }
-
- LLVector4a MAX_MAG;
- MAX_MAG.splat(1024.f*1024.f);
-
- const LLVector4a& v = data->getPositionGroup();
-
- LLVector4a val;
- val.setSub(v, BaseType::mCenter);
- val.setAbs(val);
- S32 lt = val.lessThan(MAX_MAG).getGatheredBits() & 0x7;
-
- if (lt != 0x7)
- {
- //OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << LL_ENDL;
- return false;
- }
-
- if (this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup()))
- {
- //we got it, just act like a branch
- oct_node* node = this->getNodeAt(data);
- if (node == this)
- {
- oct_node::insert(data);
- }
- else if (node->isInside(data->getPositionGroup()))
- {
- node->insert(data);
- }
- else
- {
- // calling node->insert(data) will return us to root
- OCT_ERRS << "Failed to insert data at child node" << LL_ENDL;
- }
- }
- else if (this->getChildCount() == 0)
- {
- //first object being added, just wrap it up
- while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup())))
- {
- LLVector4a center, size;
- center = this->getCenter();
- size = this->getSize();
- oct_node::pushCenter(center, size, data);
- this->setCenter(center);
- size.mul(2.f);
- this->setSize(size);
- this->updateMinMax();
- }
- oct_node::insert(data);
- }
- else
- {
- while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup())))
- {
- //the data is outside the root node, we need to grow
- LLVector4a center(this->getCenter());
- LLVector4a size(this->getSize());
-
- //expand this node
- LLVector4a newcenter(center);
- oct_node::pushCenter(newcenter, size, data);
- this->setCenter(newcenter);
- LLVector4a size2 = size;
- size2.mul(2.f);
- this->setSize(size2);
- this->updateMinMax();
-
- llassert(size[0] >= gOctreeMinSize);
-
- //copy our children to a new branch
- oct_node* newnode = new oct_node(center, size, this);
-
- for (U32 i = 0; i < this->getChildCount(); i++)
- {
- oct_node* child = this->getChild(i);
- newnode->addChild(child);
- }
-
- //clear our children and add the root copy
- this->clearChildren();
- this->addChild(newnode);
- }
-
- //insert the data
- insert(data);
- }
-
- return false;
- }
-
- bool isLeaf() const override
- {
- // root can't be a leaf
- return false;
- }
-};
-
-//========================
-// LLOctreeTraveler
-//========================
-template <class T, typename T_PTR>
-void LLOctreeTraveler<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
-{
- node->accept(this);
- for (U32 i = 0; i < node->getChildCount(); i++)
- {
- traverse(node->getChild(i));
- }
-}
-
-template <class T, typename T_PTR>
-void LLOctreeTravelerDepthFirst<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
-{
- for (U32 i = 0; i < node->getChildCount(); i++)
- {
- traverse(node->getChild(i));
- }
- node->accept(this);
-}
-
-#endif
+/** + * @file lloctree.h + * @brief Octree declaration. + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLOCTREE_H +#define LL_LLOCTREE_H + +#include "lltreenode.h" +#include "v3math.h" +#include "llvector4a.h" +#include <vector> + +#define OCT_ERRS LL_WARNS("OctreeErrors") + +#define OCTREE_DEBUG_COLOR_REMOVE 0x0000FF // r +#define OCTREE_DEBUG_COLOR_INSERT 0x00FF00 // g +#define OCTREE_DEBUG_COLOR_BALANCE 0xFF0000 // b + +extern U32 gOctreeMaxCapacity; +extern float gOctreeMinSize; + +/*#define LL_OCTREE_PARANOIA_CHECK 0 +#if LL_DARWIN +#define LL_OCTREE_MAX_CAPACITY 32 +#else +#define LL_OCTREE_MAX_CAPACITY 128 +#endif*/ + +// T is the type of the element referenced by the octree node. +// T_PTR determines how pointers to elements are stored internally. +// LLOctreeNode<T, LLPointer<T>> assumes ownership of inserted elements and +// deletes elements removed from the tree. +// LLOctreeNode<T, T*> doesn't take ownership of inserted elements, so the API +// user is responsible for managing the storage lifecycle of elements added to +// the tree. +template <class T, typename T_PTR> class LLOctreeNode; + +template <class T, typename T_PTR> +class LLOctreeListener: public LLTreeListener<T> +{ +public: + typedef LLTreeListener<T> BaseType; + typedef LLOctreeNode<T, T_PTR> oct_node; + + virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0; + virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0; +}; + +template <class T, typename T_PTR> +class LLOctreeTraveler +{ +public: + virtual void traverse(const LLOctreeNode<T, T_PTR>* node); + virtual void visit(const LLOctreeNode<T, T_PTR>* branch) = 0; +}; + +template <class T, typename T_PTR> +class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T, T_PTR> +{ +public: + virtual void traverse(const LLOctreeNode<T, T_PTR>* node) override; +}; + +template <class T, typename T_PTR> +class alignas(16) LLOctreeNode : public LLTreeNode<T> +{ + LL_ALIGN_NEW +public: + + typedef LLOctreeTraveler<T, T_PTR> oct_traveler; + typedef LLTreeTraveler<T> tree_traveler; + typedef std::vector<T_PTR> element_list; + typedef typename element_list::iterator element_iter; + typedef typename element_list::const_iterator const_element_iter; + typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter; + typedef LLOctreeNode<T, T_PTR>** child_list; + typedef LLOctreeNode<T, T_PTR>** child_iter; + + typedef LLTreeNode<T> BaseType; + typedef LLOctreeNode<T, T_PTR> oct_node; + typedef LLOctreeListener<T, T_PTR> oct_listener; + + enum + { + NO_CHILD_NODES = 255 // Note: This is an U8 to match the max value in mChildMap[] + }; + + LLOctreeNode( const LLVector4a& center, + const LLVector4a& size, + BaseType* parent, + U8 octant = NO_CHILD_NODES) + : mParent((oct_node*)parent), + mOctant(octant) + { + llassert(size[0] >= gOctreeMinSize*0.5f); + + mCenter = center; + mSize = size; + + updateMinMax(); + if ((mOctant == NO_CHILD_NODES) && mParent) + { + mOctant = ((oct_node*) mParent)->getOctant(mCenter); + } + + clearChildren(); + } + + virtual ~LLOctreeNode() + { + BaseType::destroyListeners(); + + const U32 element_count = getElementCount(); + for (U32 i = 0; i < element_count; ++i) + { + mData[i]->setBinIndex(-1); + mData[i] = NULL; + } + + mData.clear(); + + for (U32 i = 0; i < getChildCount(); i++) + { + delete getChild(i); + } + } + + inline const BaseType* getParent() const { return mParent; } + inline void setParent(BaseType* parent) { mParent = (oct_node*) parent; } + inline const LLVector4a& getCenter() const { return mCenter; } + inline const LLVector4a& getSize() const { return mSize; } + inline void setCenter(const LLVector4a& center) { mCenter = center; } + inline void setSize(const LLVector4a& size) { mSize = size; } + inline oct_node* getNodeAt(T* data) { return getNodeAt(data->getPositionGroup(), data->getBinRadius()); } + inline U8 getOctant() const { return mOctant; } + inline const oct_node* getOctParent() const { return (const oct_node*) getParent(); } + inline oct_node* getOctParent() { return (oct_node*) getParent(); } + + U8 getOctant(const LLVector4a& pos) const //get the octant pos is in + { + return (U8) (pos.greaterThan(mCenter).getGatheredBits() & 0x7); + } + + inline bool isInside(const LLVector4a& pos, const F32& rad) const + { + return rad <= mSize[0]*2.f && isInside(pos); + } + + inline bool isInside(T* data) const + { + return isInside(data->getPositionGroup(), data->getBinRadius()); + } + + bool isInside(const LLVector4a& pos) const + { + S32 gt = pos.greaterThan(mMax).getGatheredBits() & 0x7; + if (gt) + { + return false; + } + + S32 lt = pos.lessEqual(mMin).getGatheredBits() & 0x7; + if (lt) + { + return false; + } + + return true; + } + + void updateMinMax() + { + mMax.setAdd(mCenter, mSize); + mMin.setSub(mCenter, mSize); + } + + inline oct_listener* getOctListener(U32 index) + { + return (oct_listener*) BaseType::getListener(index); + } + + inline bool contains(T* xform) + { + return contains(xform->getBinRadius()); + } + + bool contains(F32 radius) + { + if (mParent == NULL) + { //root node contains nothing + return false; + } + + F32 size = mSize[0]; + F32 p_size = size * 2.f; + + return (radius <= gOctreeMinSize && size <= gOctreeMinSize) || + (radius <= p_size && radius > size); + } + + static void pushCenter(LLVector4a ¢er, const LLVector4a &size, const T* data) + { + const LLVector4a& pos = data->getPositionGroup(); + + LLVector4Logical gt = pos.greaterThan(center); + + LLVector4a up; + up = _mm_and_ps(size, gt); + + LLVector4a down; + down = _mm_andnot_ps(gt, size); + + center.add(up); + center.sub(down); + } + + void accept(oct_traveler* visitor) { visitor->visit(this); } + virtual bool isLeaf() const { return mChildCount == 0; } + + U32 getElementCount() const { return (U32)mData.size(); } + bool isEmpty() const { return mData.empty(); } + element_iter getDataBegin() { return mData.begin(); } + element_iter getDataEnd() { return mData.end(); } + const_element_iter getDataBegin() const { return mData.cbegin(); } + const_element_iter getDataEnd() const { return mData.cend(); } + + U32 getChildCount() const { return mChildCount; } + oct_node* getChild(U32 index) { return mChild[index]; } + const oct_node* getChild(U32 index) const { return mChild[index]; } + child_list& getChildren() { return mChild; } + const child_list& getChildren() const { return mChild; } + + void accept(tree_traveler* visitor) const { visitor->visit(this); } + void accept(oct_traveler* visitor) const { visitor->visit(this); } + + void validateChildMap() + { + for (U32 i = 0; i < 8; i++) + { + U8 idx = mChildMap[i]; + if (idx != NO_CHILD_NODES) + { + oct_node* child = mChild[idx]; + + if (child->getOctant() != i) + { + LL_ERRS() << "Invalid child map, bad octant data." << LL_ENDL; + } + + if (getOctant(child->getCenter()) != child->getOctant()) + { + LL_ERRS() << "Invalid child octant compared to position data." << LL_ENDL; + } + } + } + } + + + oct_node* getNodeAt(const LLVector4a& pos, const F32& rad) + { + oct_node* node = this; + + if (node->isInside(pos, rad)) + { + //do a quick search by octant + U8 octant = node->getOctant(pos); + + //traverse the tree until we find a node that has no node + //at the appropriate octant or is smaller than the object. + //by definition, that node is the smallest node that contains + // the data + U8 next_node = node->mChildMap[octant]; + + while (next_node != NO_CHILD_NODES && node->getSize()[0] >= rad) + { + node = node->getChild(next_node); + octant = node->getOctant(pos); + next_node = node->mChildMap[octant]; + } + } + else if (!node->contains(rad) && node->getParent()) + { //if we got here, data does not exist in this node + return ((oct_node*) node->getParent())->getNodeAt(pos, rad); + } + + return node; + } + + virtual bool insert(T* data) + { + //LL_PROFILE_ZONE_NAMED_COLOR("Octree::insert()",OCTREE_DEBUG_COLOR_INSERT); + + if (data == NULL || data->getBinIndex() != -1) + { + OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL; + return false; + } + oct_node* parent = getOctParent(); + + //is it here? + if (isInside(data->getPositionGroup())) + { + if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) || + (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity))) + { //it belongs here + mData.push_back(data); + data->setBinIndex(getElementCount() - 1); + BaseType::insert(data); + return true; + } + else + { + //find a child to give it to + oct_node* child = NULL; + for (U32 i = 0; i < getChildCount(); i++) + { + child = getChild(i); + if (child->isInside(data->getPositionGroup())) + { + child->insert(data); + return false; + } + } + + //it's here, but no kids are in the right place, make a new kid + LLVector4a center = getCenter(); + LLVector4a size = getSize(); + size.mul(0.5f); + + //push center in direction of data + oct_node::pushCenter(center, size, data); + + // handle case where floating point number gets too small + LLVector4a val; + val.setSub(center, getCenter()); + val.setAbs(val); + LLVector4a min_diff(gOctreeMinSize); + + S32 lt = val.lessThan(min_diff).getGatheredBits() & 0x7; + + if( lt == 0x7 ) + { + mData.push_back(data); + data->setBinIndex(getElementCount() - 1); + BaseType::insert(data); + return true; + } + +#if LL_OCTREE_PARANOIA_CHECK + if (getChildCount() == 8) + { + //this really isn't possible, something bad has happened + OCT_ERRS << "Octree detected floating point error and gave up." << LL_ENDL; + return false; + } + + //make sure no existing node matches this position + for (U32 i = 0; i < getChildCount(); i++) + { + if (mChild[i]->getCenter().equals3(center)) + { + OCT_ERRS << "Octree detected duplicate child center and gave up." << LL_ENDL; + return false; + } + } +#endif + + llassert(size[0] >= gOctreeMinSize*0.5f); + //make the new kid + child = new oct_node(center, size, this); + addChild(child); + + child->insert(data); + } + } + else if (parent) + { + //it's not in here, give it to the root + OCT_ERRS << "Octree insertion failed, starting over from root!" << LL_ENDL; + + oct_node* node = this; + + while (parent) + { + node = parent; + parent = node->getOctParent(); + } + + node->insert(data); + } + else + { + // It's not in here, and we are root. + // LLOctreeRoot::insert() should have expanded + // root by now, something is wrong + OCT_ERRS << "Octree insertion failed! Root expansion failed." << LL_ENDL; + } + + return false; + } + + void _remove(T* data, S32 i) + { //precondition -- getElementCount() > 0, idx is in range [0, getElementCount()) + + data->setBinIndex(-1); + + const U32 new_element_count = getElementCount() - 1; + if (new_element_count > 0) + { + if (new_element_count != i) + { + mData[i] = mData[new_element_count]; //might unref data, do not access data after this point + mData[i]->setBinIndex(i); + } + + mData[new_element_count] = NULL; + mData.pop_back(); + } + else + { + mData.clear(); + } + + this->notifyRemoval(data); + checkAlive(); + } + + bool remove(T* data) + { + //LL_PROFILE_ZONE_NAMED_COLOR("Octree::remove()", OCTREE_DEBUG_COLOR_REMOVE); + + S32 i = data->getBinIndex(); + + if (i >= 0 && i < getElementCount()) + { + if (mData[i] == data) + { //found it + _remove(data, i); + llassert(data->getBinIndex() == -1); + return true; + } + } + + if (isInside(data)) + { + oct_node* dest = getNodeAt(data); + + if (dest != this) + { + bool ret = dest->remove(data); + llassert(data->getBinIndex() == -1); + return ret; + } + } + + //SHE'S GONE MISSING... + //none of the children have it, let's just brute force this bastard out + //starting with the root node (UGLY CODE COMETH!) + oct_node* parent = getOctParent(); + oct_node* node = this; + + while (parent != NULL) + { + node = parent; + parent = node->getOctParent(); + } + + //node is now root + LL_WARNS() << "!!! OCTREE REMOVING ELEMENT BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << LL_ENDL; + node->removeByAddress(data); + llassert(data->getBinIndex() == -1); + return true; + } + + void removeByAddress(T* data) + { + const U32 element_count = getElementCount(); + for (U32 i = 0; i < element_count; ++i) + { + if (mData[i] == data) + { //we have data + _remove(data, i); + LL_WARNS() << "FOUND!" << LL_ENDL; + return; + } + } + + for (U32 i = 0; i < getChildCount(); i++) + { //we don't contain data, so pass this guy down + oct_node* child = (oct_node*) getChild(i); + child->removeByAddress(data); + } + } + + void clearChildren() + { + mChildCount = 0; + memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap)); + } + + void validate() + { +#if LL_OCTREE_PARANOIA_CHECK + for (U32 i = 0; i < getChildCount(); i++) + { + mChild[i]->validate(); + if (mChild[i]->getParent() != this) + { + LL_ERRS() << "Octree child has invalid parent." << LL_ENDL; + } + } +#endif + } + + virtual bool balance() + { + return false; + } + + void destroy() + { + for (U32 i = 0; i < getChildCount(); i++) + { + mChild[i]->destroy(); + delete mChild[i]; + } + } + + void addChild(oct_node* child, bool silent = false) + { +#if LL_OCTREE_PARANOIA_CHECK + + if (child->getSize().equals3(getSize())) + { + OCT_ERRS << "Child size is same as parent size!" << LL_ENDL; + } + + for (U32 i = 0; i < getChildCount(); i++) + { + if(!mChild[i]->getSize().equals3(child->getSize())) + { + OCT_ERRS <<"Invalid octree child size." << LL_ENDL; + } + if (mChild[i]->getCenter().equals3(child->getCenter())) + { + OCT_ERRS <<"Duplicate octree child position." << LL_ENDL; + } + } + + if (mChild.size() >= 8) + { + OCT_ERRS <<"Octree node has too many children... why?" << LL_ENDL; + } +#endif + + mChildMap[child->getOctant()] = mChildCount; + + mChild[mChildCount] = child; + ++mChildCount; + child->setParent(this); + + if (!silent) + { + for (U32 i = 0; i < this->getListenerCount(); i++) + { + oct_listener* listener = getOctListener(i); + listener->handleChildAddition(this, child); + } + } + } + + void removeChild(S32 index, bool destroy = false) + { + for (U32 i = 0; i < this->getListenerCount(); i++) + { + oct_listener* listener = getOctListener(i); + listener->handleChildRemoval(this, getChild(index)); + } + + if (destroy) + { + mChild[index]->destroy(); + delete mChild[index]; + } + + --mChildCount; + + mChild[index] = mChild[mChildCount]; + + //rebuild child map + memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap)); + + for (U32 i = 0; i < mChildCount; ++i) + { + mChildMap[mChild[i]->getOctant()] = i; + } + + checkAlive(); + } + + void checkAlive() + { + if (getChildCount() == 0 && getElementCount() == 0) + { + oct_node* parent = getOctParent(); + if (parent) + { + parent->deleteChild(this); + } + } + } + + void deleteChild(oct_node* node) + { + for (U32 i = 0; i < getChildCount(); i++) + { + if (getChild(i) == node) + { + removeChild(i, true); + return; + } + } + + OCT_ERRS << "Octree failed to delete requested child." << LL_ENDL; + } + +protected: + typedef enum + { + CENTER = 0, + SIZE = 1, + MAX = 2, + MIN = 3 + } eDName; + + LLVector4a mCenter; + LLVector4a mSize; + LLVector4a mMax; + LLVector4a mMin; + + oct_node* mParent; + U8 mOctant; + + oct_node* mChild[8]; + U8 mChildMap[8]; + U32 mChildCount; + + element_list mData; +}; + +//just like a regular node, except it might expand on insert and compress on balance +template <class T, typename T_PTR> +class LLOctreeRoot : public LLOctreeNode<T, T_PTR> +{ +public: + typedef LLOctreeNode<T, T_PTR> BaseType; + typedef LLOctreeNode<T, T_PTR> oct_node; + + LLOctreeRoot(const LLVector4a& center, + const LLVector4a& size, + BaseType* parent) + : BaseType(center, size, parent) + { + } + + bool balance() override + { + //LL_PROFILE_ZONE_NAMED_COLOR("Octree::balance()",OCTREE_DEBUG_COLOR_BALANCE); + + if (this->getChildCount() == 1 && + !(this->mChild[0]->isLeaf()) && + this->mChild[0]->getElementCount() == 0) + { //if we have only one child and that child is an empty branch, make that child the root + oct_node* child = this->mChild[0]; + + //make the root node look like the child + this->setCenter(this->mChild[0]->getCenter()); + this->setSize(this->mChild[0]->getSize()); + this->updateMinMax(); + + //reset root node child list + this->clearChildren(); + + //copy the child's children into the root node silently + //(don't notify listeners of addition) + for (U32 i = 0; i < child->getChildCount(); i++) + { + this->addChild(child->getChild(i), true); + } + + //destroy child + child->clearChildren(); + delete child; + + return false; + } + + return true; + } + + // LLOctreeRoot::insert + bool insert(T* data) override + { + if (data == nullptr) + { + OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << LL_ENDL; + return false; + } + + if (data->getBinRadius() > 4096.0) + { + OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << LL_ENDL; + return false; + } + + LLVector4a MAX_MAG; + MAX_MAG.splat(1024.f*1024.f); + + const LLVector4a& v = data->getPositionGroup(); + + LLVector4a val; + val.setSub(v, BaseType::mCenter); + val.setAbs(val); + S32 lt = val.lessThan(MAX_MAG).getGatheredBits() & 0x7; + + if (lt != 0x7) + { + //OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << LL_ENDL; + return false; + } + + if (this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup())) + { + //we got it, just act like a branch + oct_node* node = this->getNodeAt(data); + if (node == this) + { + oct_node::insert(data); + } + else if (node->isInside(data->getPositionGroup())) + { + node->insert(data); + } + else + { + // calling node->insert(data) will return us to root + OCT_ERRS << "Failed to insert data at child node" << LL_ENDL; + } + } + else if (this->getChildCount() == 0) + { + //first object being added, just wrap it up + while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup()))) + { + LLVector4a center, size; + center = this->getCenter(); + size = this->getSize(); + oct_node::pushCenter(center, size, data); + this->setCenter(center); + size.mul(2.f); + this->setSize(size); + this->updateMinMax(); + } + oct_node::insert(data); + } + else + { + while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup()))) + { + //the data is outside the root node, we need to grow + LLVector4a center(this->getCenter()); + LLVector4a size(this->getSize()); + + //expand this node + LLVector4a newcenter(center); + oct_node::pushCenter(newcenter, size, data); + this->setCenter(newcenter); + LLVector4a size2 = size; + size2.mul(2.f); + this->setSize(size2); + this->updateMinMax(); + + llassert(size[0] >= gOctreeMinSize); + + //copy our children to a new branch + oct_node* newnode = new oct_node(center, size, this); + + for (U32 i = 0; i < this->getChildCount(); i++) + { + oct_node* child = this->getChild(i); + newnode->addChild(child); + } + + //clear our children and add the root copy + this->clearChildren(); + this->addChild(newnode); + } + + //insert the data + insert(data); + } + + return false; + } + + bool isLeaf() const override + { + // root can't be a leaf + return false; + } +}; + +//======================== +// LLOctreeTraveler +//======================== +template <class T, typename T_PTR> +void LLOctreeTraveler<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node) +{ + node->accept(this); + for (U32 i = 0; i < node->getChildCount(); i++) + { + traverse(node->getChild(i)); + } +} + +template <class T, typename T_PTR> +void LLOctreeTravelerDepthFirst<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node) +{ + for (U32 i = 0; i < node->getChildCount(); i++) + { + traverse(node->getChild(i)); + } + node->accept(this); +} + +#endif diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp index 90d908c07c..aefb82b2f0 100644 --- a/indra/llmath/llquaternion.cpp +++ b/indra/llmath/llquaternion.cpp @@ -1,981 +1,981 @@ -/**
- * @file llquaternion.cpp
- * @brief LLQuaternion class implementation.
- *
- * $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 "linden_common.h"
-
-#include "llmath.h" // for F_PI
-
-#include "llquaternion.h"
-
-//#include "vmath.h"
-#include "v3math.h"
-#include "v3dmath.h"
-#include "v4math.h"
-#include "m4math.h"
-#include "m3math.h"
-#include "llquantize.h"
-
-// WARNING: Don't use this for global const definitions! using this
-// at the top of a *.cpp file might not give you what you think.
-const LLQuaternion LLQuaternion::DEFAULT;
-
-// Constructors
-
-LLQuaternion::LLQuaternion(const LLMatrix4 &mat)
-{
- *this = mat.quaternion();
- normalize();
-}
-
-LLQuaternion::LLQuaternion(const LLMatrix3 &mat)
-{
- *this = mat.quaternion();
- normalize();
-}
-
-LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec)
-{
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
-}
-
-LLQuaternion::LLQuaternion(F32 angle, const LLVector3 &vec)
-{
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
-}
-
-LLQuaternion::LLQuaternion(const LLVector3 &x_axis,
- const LLVector3 &y_axis,
- const LLVector3 &z_axis)
-{
- LLMatrix3 mat;
- mat.setRows(x_axis, y_axis, z_axis);
- *this = mat.quaternion();
- normalize();
-}
-
-LLQuaternion::LLQuaternion(const LLSD &sd)
-{
- setValue(sd);
-}
-
-// Quatizations
-void LLQuaternion::quantize16(F32 lower, F32 upper)
-{
- F32 x = mQ[VX];
- F32 y = mQ[VY];
- F32 z = mQ[VZ];
- F32 s = mQ[VS];
-
- x = U16_to_F32(F32_to_U16_ROUND(x, lower, upper), lower, upper);
- y = U16_to_F32(F32_to_U16_ROUND(y, lower, upper), lower, upper);
- z = U16_to_F32(F32_to_U16_ROUND(z, lower, upper), lower, upper);
- s = U16_to_F32(F32_to_U16_ROUND(s, lower, upper), lower, upper);
-
- mQ[VX] = x;
- mQ[VY] = y;
- mQ[VZ] = z;
- mQ[VS] = s;
-
- normalize();
-}
-
-void LLQuaternion::quantize8(F32 lower, F32 upper)
-{
- mQ[VX] = U8_to_F32(F32_to_U8_ROUND(mQ[VX], lower, upper), lower, upper);
- mQ[VY] = U8_to_F32(F32_to_U8_ROUND(mQ[VY], lower, upper), lower, upper);
- mQ[VZ] = U8_to_F32(F32_to_U8_ROUND(mQ[VZ], lower, upper), lower, upper);
- mQ[VS] = U8_to_F32(F32_to_U8_ROUND(mQ[VS], lower, upper), lower, upper);
-
- normalize();
-}
-
-// LLVector3 Magnitude and Normalization Functions
-
-
-// Set LLQuaternion routines
-
-const LLQuaternion& LLQuaternion::setAngleAxis(F32 angle, F32 x, F32 y, F32 z)
-{
- F32 mag = sqrtf(x * x + y * y + z * z);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = x * s;
- mQ[VY] = y * s;
- mQ[VZ] = z * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- return (*this);
-}
-
-const LLQuaternion& LLQuaternion::setAngleAxis(F32 angle, const LLVector3 &vec)
-{
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- return (*this);
-}
-
-const LLQuaternion& LLQuaternion::setAngleAxis(F32 angle, const LLVector4 &vec)
-{
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- return (*this);
-}
-
-const LLQuaternion& LLQuaternion::setEulerAngles(F32 roll, F32 pitch, F32 yaw)
-{
- LLMatrix3 rot_mat(roll, pitch, yaw);
- rot_mat.orthogonalize();
- *this = rot_mat.quaternion();
-
- normalize();
- return (*this);
-}
-
-// deprecated
-const LLQuaternion& LLQuaternion::set(const LLMatrix3 &mat)
-{
- *this = mat.quaternion();
- normalize();
- return (*this);
-}
-
-// deprecated
-const LLQuaternion& LLQuaternion::set(const LLMatrix4 &mat)
-{
- *this = mat.quaternion();
- normalize();
- return (*this);
-}
-
-// deprecated
-const LLQuaternion& LLQuaternion::setQuat(F32 angle, F32 x, F32 y, F32 z)
-{
- F32 mag = sqrtf(x * x + y * y + z * z);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = x * s;
- mQ[VY] = y * s;
- mQ[VZ] = z * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- return (*this);
-}
-
-// deprecated
-const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector3 &vec)
-{
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- return (*this);
-}
-
-const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector4 &vec)
-{
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- return (*this);
-}
-
-const LLQuaternion& LLQuaternion::setQuat(F32 roll, F32 pitch, F32 yaw)
-{
- roll *= 0.5f;
- pitch *= 0.5f;
- yaw *= 0.5f;
- F32 sinX = sinf(roll);
- F32 cosX = cosf(roll);
- F32 sinY = sinf(pitch);
- F32 cosY = cosf(pitch);
- F32 sinZ = sinf(yaw);
- F32 cosZ = cosf(yaw);
- mQ[VW] = cosX * cosY * cosZ - sinX * sinY * sinZ;
- mQ[VX] = sinX * cosY * cosZ + cosX * sinY * sinZ;
- mQ[VY] = cosX * sinY * cosZ - sinX * cosY * sinZ;
- mQ[VZ] = cosX * cosY * sinZ + sinX * sinY * cosZ;
- return (*this);
-}
-
-const LLQuaternion& LLQuaternion::setQuat(const LLMatrix3 &mat)
-{
- *this = mat.quaternion();
- normalize();
- return (*this);
-}
-
-const LLQuaternion& LLQuaternion::setQuat(const LLMatrix4 &mat)
-{
- *this = mat.quaternion();
- normalize();
- return (*this);
-//#if 1
-// // NOTE: LLQuaternion's are actually inverted with respect to
-// // the matrices, so this code also assumes inverted quaternions
-// // (-x, -y, -z, w). The result is that roll,pitch,yaw are applied
-// // in reverse order (yaw,pitch,roll).
-// F64 cosX = cos(roll);
-// F64 cosY = cos(pitch);
-// F64 cosZ = cos(yaw);
-//
-// F64 sinX = sin(roll);
-// F64 sinY = sin(pitch);
-// F64 sinZ = sin(yaw);
-//
-// mQ[VW] = (F32)sqrt(cosY*cosZ - sinX*sinY*sinZ + cosX*cosZ + cosX*cosY + 1.0)*.5;
-// if (fabs(mQ[VW]) < F_APPROXIMATELY_ZERO)
-// {
-// // null rotation, any axis will do
-// mQ[VX] = 0.0f;
-// mQ[VY] = 1.0f;
-// mQ[VZ] = 0.0f;
-// }
-// else
-// {
-// F32 inv_s = 1.0f / (4.0f * mQ[VW]);
-// mQ[VX] = (F32)-(-sinX*cosY - cosX*sinY*sinZ - sinX*cosZ) * inv_s;
-// mQ[VY] = (F32)-(-cosX*sinY*cosZ + sinX*sinZ - sinY) * inv_s;
-// mQ[VZ] = (F32)-(-cosY*sinZ - sinX*sinY*cosZ - cosX*sinZ) * inv_s;
-// }
-//
-//#else // This only works on a certain subset of roll/pitch/yaw
-//
-// F64 cosX = cosf(roll/2.0);
-// F64 cosY = cosf(pitch/2.0);
-// F64 cosZ = cosf(yaw/2.0);
-//
-// F64 sinX = sinf(roll/2.0);
-// F64 sinY = sinf(pitch/2.0);
-// F64 sinZ = sinf(yaw/2.0);
-//
-// mQ[VW] = (F32)(cosX*cosY*cosZ + sinX*sinY*sinZ);
-// mQ[VX] = (F32)(sinX*cosY*cosZ - cosX*sinY*sinZ);
-// mQ[VY] = (F32)(cosX*sinY*cosZ + sinX*cosY*sinZ);
-// mQ[VZ] = (F32)(cosX*cosY*sinZ - sinX*sinY*cosZ);
-//#endif
-//
-// normalize();
-// return (*this);
-}
-
-// SJB: This code is correct for a logicly stored (non-transposed) matrix;
-// Our matrices are stored transposed, OpenGL style, so this generates the
-// INVERSE matrix, or the CORRECT matrix form an INVERSE quaternion.
-// Because we use similar logic in LLMatrix3::quaternion(),
-// we are internally consistant so everything works OK :)
-LLMatrix3 LLQuaternion::getMatrix3(void) const
-{
- LLMatrix3 mat;
- F32 xx, xy, xz, xw, yy, yz, yw, zz, zw;
-
- xx = mQ[VX] * mQ[VX];
- xy = mQ[VX] * mQ[VY];
- xz = mQ[VX] * mQ[VZ];
- xw = mQ[VX] * mQ[VW];
-
- yy = mQ[VY] * mQ[VY];
- yz = mQ[VY] * mQ[VZ];
- yw = mQ[VY] * mQ[VW];
-
- zz = mQ[VZ] * mQ[VZ];
- zw = mQ[VZ] * mQ[VW];
-
- mat.mMatrix[0][0] = 1.f - 2.f * ( yy + zz );
- mat.mMatrix[0][1] = 2.f * ( xy + zw );
- mat.mMatrix[0][2] = 2.f * ( xz - yw );
-
- mat.mMatrix[1][0] = 2.f * ( xy - zw );
- mat.mMatrix[1][1] = 1.f - 2.f * ( xx + zz );
- mat.mMatrix[1][2] = 2.f * ( yz + xw );
-
- mat.mMatrix[2][0] = 2.f * ( xz + yw );
- mat.mMatrix[2][1] = 2.f * ( yz - xw );
- mat.mMatrix[2][2] = 1.f - 2.f * ( xx + yy );
-
- return mat;
-}
-
-LLMatrix4 LLQuaternion::getMatrix4(void) const
-{
- LLMatrix4 mat;
- F32 xx, xy, xz, xw, yy, yz, yw, zz, zw;
-
- xx = mQ[VX] * mQ[VX];
- xy = mQ[VX] * mQ[VY];
- xz = mQ[VX] * mQ[VZ];
- xw = mQ[VX] * mQ[VW];
-
- yy = mQ[VY] * mQ[VY];
- yz = mQ[VY] * mQ[VZ];
- yw = mQ[VY] * mQ[VW];
-
- zz = mQ[VZ] * mQ[VZ];
- zw = mQ[VZ] * mQ[VW];
-
- mat.mMatrix[0][0] = 1.f - 2.f * ( yy + zz );
- mat.mMatrix[0][1] = 2.f * ( xy + zw );
- mat.mMatrix[0][2] = 2.f * ( xz - yw );
-
- mat.mMatrix[1][0] = 2.f * ( xy - zw );
- mat.mMatrix[1][1] = 1.f - 2.f * ( xx + zz );
- mat.mMatrix[1][2] = 2.f * ( yz + xw );
-
- mat.mMatrix[2][0] = 2.f * ( xz + yw );
- mat.mMatrix[2][1] = 2.f * ( yz - xw );
- mat.mMatrix[2][2] = 1.f - 2.f * ( xx + yy );
-
- // TODO -- should we set the translation portion to zero?
-
- return mat;
-}
-
-
-
-
-// Other useful methods
-
-
-// calculate the shortest rotation from a to b
-void LLQuaternion::shortestArc(const LLVector3 &a, const LLVector3 &b)
-{
- F32 ab = a * b; // dotproduct
- LLVector3 c = a % b; // crossproduct
- F32 cc = c * c; // squared length of the crossproduct
- if (ab * ab + cc) // test if the arguments have sufficient magnitude
- {
- if (cc > 0.0f) // test if the arguments are (anti)parallel
- {
- F32 s = sqrtf(ab * ab + cc) + ab; // note: don't try to optimize this line
- F32 m = 1.0f / sqrtf(cc + s * s); // the inverted magnitude of the quaternion
- mQ[VX] = c.mV[VX] * m;
- mQ[VY] = c.mV[VY] * m;
- mQ[VZ] = c.mV[VZ] * m;
- mQ[VW] = s * m;
- return;
- }
- if (ab < 0.0f) // test if the angle is bigger than PI/2 (anti parallel)
- {
- c = a - b; // the arguments are anti-parallel, we have to choose an axis
- F32 m = sqrtf(c.mV[VX] * c.mV[VX] + c.mV[VY] * c.mV[VY]); // the length projected on the XY-plane
- if (m > FP_MAG_THRESHOLD)
- {
- mQ[VX] = -c.mV[VY] / m; // return the quaternion with the axis in the XY-plane
- mQ[VY] = c.mV[VX] / m;
- mQ[VZ] = 0.0f;
- mQ[VW] = 0.0f;
- return;
- }
- else // the vectors are parallel to the Z-axis
- {
- mQ[VX] = 1.0f; // rotate around the X-axis
- mQ[VY] = 0.0f;
- mQ[VZ] = 0.0f;
- mQ[VW] = 0.0f;
- return;
- }
- }
- }
- loadIdentity();
-}
-
-// constrains rotation to a cone angle specified in radians
-const LLQuaternion &LLQuaternion::constrain(F32 radians)
-{
- const F32 cos_angle_lim = cosf( radians/2 ); // mQ[VW] limit
- const F32 sin_angle_lim = sinf( radians/2 ); // rotation axis length limit
-
- if (mQ[VW] < 0.f)
- {
- mQ[VX] *= -1.f;
- mQ[VY] *= -1.f;
- mQ[VZ] *= -1.f;
- mQ[VW] *= -1.f;
- }
-
- // if rotation angle is greater than limit (cos is less than limit)
- if( mQ[VW] < cos_angle_lim )
- {
- mQ[VW] = cos_angle_lim;
- F32 axis_len = sqrtf( mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] ); // sin(theta/2)
- F32 axis_mult_fact = sin_angle_lim / axis_len;
- mQ[VX] *= axis_mult_fact;
- mQ[VY] *= axis_mult_fact;
- mQ[VZ] *= axis_mult_fact;
- }
-
- return *this;
-}
-
-// Operators
-
-std::ostream& operator<<(std::ostream &s, const LLQuaternion &a)
-{
- s << "{ "
- << a.mQ[VX] << ", " << a.mQ[VY] << ", " << a.mQ[VZ] << ", " << a.mQ[VW]
- << " }";
- return s;
-}
-
-
-// Does NOT renormalize the result
-LLQuaternion operator*(const LLQuaternion &a, const LLQuaternion &b)
-{
-// LLQuaternion::mMultCount++;
-
- LLQuaternion q(
- b.mQ[3] * a.mQ[0] + b.mQ[0] * a.mQ[3] + b.mQ[1] * a.mQ[2] - b.mQ[2] * a.mQ[1],
- b.mQ[3] * a.mQ[1] + b.mQ[1] * a.mQ[3] + b.mQ[2] * a.mQ[0] - b.mQ[0] * a.mQ[2],
- b.mQ[3] * a.mQ[2] + b.mQ[2] * a.mQ[3] + b.mQ[0] * a.mQ[1] - b.mQ[1] * a.mQ[0],
- b.mQ[3] * a.mQ[3] - b.mQ[0] * a.mQ[0] - b.mQ[1] * a.mQ[1] - b.mQ[2] * a.mQ[2]
- );
- return q;
-}
-
-/*
-LLMatrix4 operator*(const LLMatrix4 &m, const LLQuaternion &q)
-{
- LLMatrix4 qmat(q);
- return (m*qmat);
-}
-*/
-
-
-
-LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot)
-{
- F32 rw = - rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] - rot.mQ[VZ] * a.mV[VZ];
- F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] - rot.mQ[VZ] * a.mV[VY];
- F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] - rot.mQ[VX] * a.mV[VZ];
- F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] - rot.mQ[VY] * a.mV[VX];
-
- F32 nx = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY];
- F32 ny = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ];
- F32 nz = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX];
-
- return LLVector4(nx, ny, nz, a.mV[VW]);
-}
-
-LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot)
-{
- F32 rw = - rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] - rot.mQ[VZ] * a.mV[VZ];
- F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] - rot.mQ[VZ] * a.mV[VY];
- F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] - rot.mQ[VX] * a.mV[VZ];
- F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] - rot.mQ[VY] * a.mV[VX];
-
- F32 nx = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY];
- F32 ny = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ];
- F32 nz = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX];
-
- return LLVector3(nx, ny, nz);
-}
-
-LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot)
-{
- F64 rw = - rot.mQ[VX] * a.mdV[VX] - rot.mQ[VY] * a.mdV[VY] - rot.mQ[VZ] * a.mdV[VZ];
- F64 rx = rot.mQ[VW] * a.mdV[VX] + rot.mQ[VY] * a.mdV[VZ] - rot.mQ[VZ] * a.mdV[VY];
- F64 ry = rot.mQ[VW] * a.mdV[VY] + rot.mQ[VZ] * a.mdV[VX] - rot.mQ[VX] * a.mdV[VZ];
- F64 rz = rot.mQ[VW] * a.mdV[VZ] + rot.mQ[VX] * a.mdV[VY] - rot.mQ[VY] * a.mdV[VX];
-
- F64 nx = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY];
- F64 ny = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ];
- F64 nz = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX];
-
- return LLVector3d(nx, ny, nz);
-}
-
-F32 dot(const LLQuaternion &a, const LLQuaternion &b)
-{
- return a.mQ[VX] * b.mQ[VX] +
- a.mQ[VY] * b.mQ[VY] +
- a.mQ[VZ] * b.mQ[VZ] +
- a.mQ[VW] * b.mQ[VW];
-}
-
-// DEMO HACK: This lerp is probably inocrrect now due intermediate normalization
-// it should look more like the lerp below
-#if 0
-// linear interpolation
-LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q)
-{
- LLQuaternion r;
- r = t * (q - p) + p;
- r.normalize();
- return r;
-}
-#endif
-
-// lerp from identity to q
-LLQuaternion lerp(F32 t, const LLQuaternion &q)
-{
- LLQuaternion r;
- r.mQ[VX] = t * q.mQ[VX];
- r.mQ[VY] = t * q.mQ[VY];
- r.mQ[VZ] = t * q.mQ[VZ];
- r.mQ[VW] = t * (q.mQ[VZ] - 1.f) + 1.f;
- r.normalize();
- return r;
-}
-
-LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q)
-{
- LLQuaternion r;
- F32 inv_t;
-
- inv_t = 1.f - t;
-
- r.mQ[VX] = t * q.mQ[VX] + (inv_t * p.mQ[VX]);
- r.mQ[VY] = t * q.mQ[VY] + (inv_t * p.mQ[VY]);
- r.mQ[VZ] = t * q.mQ[VZ] + (inv_t * p.mQ[VZ]);
- r.mQ[VW] = t * q.mQ[VW] + (inv_t * p.mQ[VW]);
- r.normalize();
- return r;
-}
-
-
-// spherical linear interpolation
-LLQuaternion slerp( F32 u, const LLQuaternion &a, const LLQuaternion &b )
-{
- // cosine theta = dot product of a and b
- F32 cos_t = a.mQ[0]*b.mQ[0] + a.mQ[1]*b.mQ[1] + a.mQ[2]*b.mQ[2] + a.mQ[3]*b.mQ[3];
-
- // if b is on opposite hemisphere from a, use -a instead
- bool bflip;
- if (cos_t < 0.0f)
- {
- cos_t = -cos_t;
- bflip = true;
- }
- else
- bflip = false;
-
- // if B is (within precision limits) the same as A,
- // just linear interpolate between A and B.
- F32 alpha; // interpolant
- F32 beta; // 1 - interpolant
- if (1.0f - cos_t < 0.00001f)
- {
- beta = 1.0f - u;
- alpha = u;
- }
- else
- {
- F32 theta = acosf(cos_t);
- F32 sin_t = sinf(theta);
- beta = sinf(theta - u*theta) / sin_t;
- alpha = sinf(u*theta) / sin_t;
- }
-
- if (bflip)
- beta = -beta;
-
- // interpolate
- LLQuaternion ret;
- ret.mQ[0] = beta*a.mQ[0] + alpha*b.mQ[0];
- ret.mQ[1] = beta*a.mQ[1] + alpha*b.mQ[1];
- ret.mQ[2] = beta*a.mQ[2] + alpha*b.mQ[2];
- ret.mQ[3] = beta*a.mQ[3] + alpha*b.mQ[3];
-
- return ret;
-}
-
-// lerp whenever possible
-LLQuaternion nlerp(F32 t, const LLQuaternion &a, const LLQuaternion &b)
-{
- if (dot(a, b) < 0.f)
- {
- return slerp(t, a, b);
- }
- else
- {
- return lerp(t, a, b);
- }
-}
-
-LLQuaternion nlerp(F32 t, const LLQuaternion &q)
-{
- if (q.mQ[VW] < 0.f)
- {
- return slerp(t, q);
- }
- else
- {
- return lerp(t, q);
- }
-}
-
-// slerp from identity quaternion to another quaternion
-LLQuaternion slerp(F32 t, const LLQuaternion &q)
-{
- F32 c = q.mQ[VW];
- if (1.0f == t || 1.0f == c)
- {
- // the trivial cases
- return q;
- }
-
- LLQuaternion r;
- F32 s, angle, stq, stp;
-
- s = (F32) sqrt(1.f - c*c);
-
- if (c < 0.0f)
- {
- // when c < 0.0 then theta > PI/2
- // since quat and -quat are the same rotation we invert one of
- // p or q to reduce unecessary spins
- // A equivalent way to do it is to convert acos(c) as if it had
- // been negative, and to negate stp
- angle = (F32) acos(-c);
- stp = -(F32) sin(angle * (1.f - t));
- stq = (F32) sin(angle * t);
- }
- else
- {
- angle = (F32) acos(c);
- stp = (F32) sin(angle * (1.f - t));
- stq = (F32) sin(angle * t);
- }
-
- r.mQ[VX] = (q.mQ[VX] * stq) / s;
- r.mQ[VY] = (q.mQ[VY] * stq) / s;
- r.mQ[VZ] = (q.mQ[VZ] * stq) / s;
- r.mQ[VW] = (stp + q.mQ[VW] * stq) / s;
-
- return r;
-}
-
-LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order)
-{
- LLQuaternion xQ( xRot*DEG_TO_RAD, LLVector3(1.0f, 0.0f, 0.0f) );
- LLQuaternion yQ( yRot*DEG_TO_RAD, LLVector3(0.0f, 1.0f, 0.0f) );
- LLQuaternion zQ( zRot*DEG_TO_RAD, LLVector3(0.0f, 0.0f, 1.0f) );
- LLQuaternion ret;
- switch( order )
- {
- case LLQuaternion::XYZ:
- ret = xQ * yQ * zQ;
- break;
- case LLQuaternion::YZX:
- ret = yQ * zQ * xQ;
- break;
- case LLQuaternion::ZXY:
- ret = zQ * xQ * yQ;
- break;
- case LLQuaternion::XZY:
- ret = xQ * zQ * yQ;
- break;
- case LLQuaternion::YXZ:
- ret = yQ * xQ * zQ;
- break;
- case LLQuaternion::ZYX:
- ret = zQ * yQ * xQ;
- break;
- }
- return ret;
-}
-
-const char *OrderToString( const LLQuaternion::Order order )
-{
- const char *p = NULL;
- switch( order )
- {
- default:
- case LLQuaternion::XYZ:
- p = "XYZ";
- break;
- case LLQuaternion::YZX:
- p = "YZX";
- break;
- case LLQuaternion::ZXY:
- p = "ZXY";
- break;
- case LLQuaternion::XZY:
- p = "XZY";
- break;
- case LLQuaternion::YXZ:
- p = "YXZ";
- break;
- case LLQuaternion::ZYX:
- p = "ZYX";
- break;
- }
- return p;
-}
-
-LLQuaternion::Order StringToOrder( const char *str )
-{
- if (strncmp(str, "XYZ", 3)==0 || strncmp(str, "xyz", 3)==0)
- return LLQuaternion::XYZ;
-
- if (strncmp(str, "YZX", 3)==0 || strncmp(str, "yzx", 3)==0)
- return LLQuaternion::YZX;
-
- if (strncmp(str, "ZXY", 3)==0 || strncmp(str, "zxy", 3)==0)
- return LLQuaternion::ZXY;
-
- if (strncmp(str, "XZY", 3)==0 || strncmp(str, "xzy", 3)==0)
- return LLQuaternion::XZY;
-
- if (strncmp(str, "YXZ", 3)==0 || strncmp(str, "yxz", 3)==0)
- return LLQuaternion::YXZ;
-
- if (strncmp(str, "ZYX", 3)==0 || strncmp(str, "zyx", 3)==0)
- return LLQuaternion::ZYX;
-
- return LLQuaternion::XYZ;
-}
-
-void LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) const
-{
- F32 v = sqrtf(mQ[VX] * mQ[VX] + mQ[VY] * mQ[VY] + mQ[VZ] * mQ[VZ]); // length of the vector-component
- if (v > FP_MAG_THRESHOLD)
- {
- F32 oomag = 1.0f / v;
- F32 w = mQ[VW];
- if (mQ[VW] < 0.0f)
- {
- w = -w; // make VW positive
- oomag = -oomag; // invert the axis
- }
- vec.mV[VX] = mQ[VX] * oomag; // normalize the axis
- vec.mV[VY] = mQ[VY] * oomag;
- vec.mV[VZ] = mQ[VZ] * oomag;
- *angle = 2.0f * atan2f(v, w); // get the angle
- }
- else
- {
- *angle = 0.0f; // no rotation
- vec.mV[VX] = 0.0f; // around some dummy axis
- vec.mV[VY] = 0.0f;
- vec.mV[VZ] = 1.0f;
- }
-}
-
-const LLQuaternion& LLQuaternion::setFromAzimuthAndAltitude(F32 azimuthRadians, F32 altitudeRadians)
-{
- // euler angle inputs are complements of azimuth/altitude which are measured from zenith
- F32 pitch = llclamp(F_PI_BY_TWO - altitudeRadians, 0.0f, F_PI_BY_TWO);
- F32 yaw = llclamp(F_PI_BY_TWO - azimuthRadians, 0.0f, F_PI_BY_TWO);
- setEulerAngles(0.0f, pitch, yaw);
- return *this;
-}
-
-void LLQuaternion::getAzimuthAndAltitude(F32 &azimuthRadians, F32 &altitudeRadians)
-{
- F32 rick_roll;
- F32 pitch;
- F32 yaw;
- getEulerAngles(&rick_roll, &pitch, &yaw);
- // make these measured from zenith
- altitudeRadians = llclamp(F_PI_BY_TWO - pitch, 0.0f, F_PI_BY_TWO);
- azimuthRadians = llclamp(F_PI_BY_TWO - yaw, 0.0f, F_PI_BY_TWO);
-}
-
-// quaternion does not need to be normalized
-void LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const
-{
- F32 sx = 2 * (mQ[VX] * mQ[VW] - mQ[VY] * mQ[VZ]); // sine of the roll
- F32 sy = 2 * (mQ[VY] * mQ[VW] + mQ[VX] * mQ[VZ]); // sine of the pitch
- F32 ys = mQ[VW] * mQ[VW] - mQ[VY] * mQ[VY]; // intermediate cosine 1
- F32 xz = mQ[VX] * mQ[VX] - mQ[VZ] * mQ[VZ]; // intermediate cosine 2
- F32 cx = ys - xz; // cosine of the roll
- F32 cy = sqrtf(sx * sx + cx * cx); // cosine of the pitch
- if (cy > GIMBAL_THRESHOLD) // no gimbal lock
- {
- *roll = atan2f(sx, cx);
- *pitch = atan2f(sy, cy);
- *yaw = atan2f(2 * (mQ[VZ] * mQ[VW] - mQ[VX] * mQ[VY]), ys + xz);
- }
- else // gimbal lock
- {
- if (sy > 0)
- {
- *pitch = F_PI_BY_TWO;
- *yaw = 2 * atan2f(mQ[VZ] + mQ[VX], mQ[VW] + mQ[VY]);
- }
- else
- {
- *pitch = -F_PI_BY_TWO;
- *yaw = 2 * atan2f(mQ[VZ] - mQ[VX], mQ[VW] - mQ[VY]);
- }
- *roll = 0;
- }
-}
-
-// Saves space by using the fact that our quaternions are normalized
-LLVector3 LLQuaternion::packToVector3() const
-{
- F32 x = mQ[VX];
- F32 y = mQ[VY];
- F32 z = mQ[VZ];
- F32 w = mQ[VW];
- F32 mag = sqrtf(x * x + y * y + z * z + w * w);
- if (mag > FP_MAG_THRESHOLD)
- {
- x /= mag;
- y /= mag;
- z /= mag; // no need to normalize w, it's not used
- }
- if( mQ[VW] >= 0 )
- {
- return LLVector3( x, y , z );
- }
- else
- {
- return LLVector3( -x, -y, -z );
- }
-}
-
-// Saves space by using the fact that our quaternions are normalized
-void LLQuaternion::unpackFromVector3( const LLVector3& vec )
-{
- mQ[VX] = vec.mV[VX];
- mQ[VY] = vec.mV[VY];
- mQ[VZ] = vec.mV[VZ];
- F32 t = 1.f - vec.magVecSquared();
- if( t > 0 )
- {
- mQ[VW] = sqrt( t );
- }
- else
- {
- // Need this to avoid trying to find the square root of a negative number due
- // to floating point error.
- mQ[VW] = 0;
- }
-}
-
-bool LLQuaternion::parseQuat(const std::string& buf, LLQuaternion* value)
-{
- if( buf.empty() || value == NULL)
- {
- return false;
- }
-
- LLQuaternion quat;
- S32 count = sscanf( buf.c_str(), "%f %f %f %f", quat.mQ + 0, quat.mQ + 1, quat.mQ + 2, quat.mQ + 3 );
- if( 4 == count )
- {
- value->set( quat );
- return true;
- }
-
- return false;
-}
-
-
-// End
+/** + * @file llquaternion.cpp + * @brief LLQuaternion class implementation. + * + * $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 "linden_common.h" + +#include "llmath.h" // for F_PI + +#include "llquaternion.h" + +//#include "vmath.h" +#include "v3math.h" +#include "v3dmath.h" +#include "v4math.h" +#include "m4math.h" +#include "m3math.h" +#include "llquantize.h" + +// WARNING: Don't use this for global const definitions! using this +// at the top of a *.cpp file might not give you what you think. +const LLQuaternion LLQuaternion::DEFAULT; + +// Constructors + +LLQuaternion::LLQuaternion(const LLMatrix4 &mat) +{ + *this = mat.quaternion(); + normalize(); +} + +LLQuaternion::LLQuaternion(const LLMatrix3 &mat) +{ + *this = mat.quaternion(); + normalize(); +} + +LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec) +{ + F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]); + if (mag > FP_MAG_THRESHOLD) + { + angle *= 0.5; + F32 c = cosf(angle); + F32 s = sinf(angle) / mag; + mQ[VX] = vec.mV[VX] * s; + mQ[VY] = vec.mV[VY] * s; + mQ[VZ] = vec.mV[VZ] * s; + mQ[VW] = c; + } + else + { + loadIdentity(); + } +} + +LLQuaternion::LLQuaternion(F32 angle, const LLVector3 &vec) +{ + F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]); + if (mag > FP_MAG_THRESHOLD) + { + angle *= 0.5; + F32 c = cosf(angle); + F32 s = sinf(angle) / mag; + mQ[VX] = vec.mV[VX] * s; + mQ[VY] = vec.mV[VY] * s; + mQ[VZ] = vec.mV[VZ] * s; + mQ[VW] = c; + } + else + { + loadIdentity(); + } +} + +LLQuaternion::LLQuaternion(const LLVector3 &x_axis, + const LLVector3 &y_axis, + const LLVector3 &z_axis) +{ + LLMatrix3 mat; + mat.setRows(x_axis, y_axis, z_axis); + *this = mat.quaternion(); + normalize(); +} + +LLQuaternion::LLQuaternion(const LLSD &sd) +{ + setValue(sd); +} + +// Quatizations +void LLQuaternion::quantize16(F32 lower, F32 upper) +{ + F32 x = mQ[VX]; + F32 y = mQ[VY]; + F32 z = mQ[VZ]; + F32 s = mQ[VS]; + + x = U16_to_F32(F32_to_U16_ROUND(x, lower, upper), lower, upper); + y = U16_to_F32(F32_to_U16_ROUND(y, lower, upper), lower, upper); + z = U16_to_F32(F32_to_U16_ROUND(z, lower, upper), lower, upper); + s = U16_to_F32(F32_to_U16_ROUND(s, lower, upper), lower, upper); + + mQ[VX] = x; + mQ[VY] = y; + mQ[VZ] = z; + mQ[VS] = s; + + normalize(); +} + +void LLQuaternion::quantize8(F32 lower, F32 upper) +{ + mQ[VX] = U8_to_F32(F32_to_U8_ROUND(mQ[VX], lower, upper), lower, upper); + mQ[VY] = U8_to_F32(F32_to_U8_ROUND(mQ[VY], lower, upper), lower, upper); + mQ[VZ] = U8_to_F32(F32_to_U8_ROUND(mQ[VZ], lower, upper), lower, upper); + mQ[VS] = U8_to_F32(F32_to_U8_ROUND(mQ[VS], lower, upper), lower, upper); + + normalize(); +} + +// LLVector3 Magnitude and Normalization Functions + + +// Set LLQuaternion routines + +const LLQuaternion& LLQuaternion::setAngleAxis(F32 angle, F32 x, F32 y, F32 z) +{ + F32 mag = sqrtf(x * x + y * y + z * z); + if (mag > FP_MAG_THRESHOLD) + { + angle *= 0.5; + F32 c = cosf(angle); + F32 s = sinf(angle) / mag; + mQ[VX] = x * s; + mQ[VY] = y * s; + mQ[VZ] = z * s; + mQ[VW] = c; + } + else + { + loadIdentity(); + } + return (*this); +} + +const LLQuaternion& LLQuaternion::setAngleAxis(F32 angle, const LLVector3 &vec) +{ + F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]); + if (mag > FP_MAG_THRESHOLD) + { + angle *= 0.5; + F32 c = cosf(angle); + F32 s = sinf(angle) / mag; + mQ[VX] = vec.mV[VX] * s; + mQ[VY] = vec.mV[VY] * s; + mQ[VZ] = vec.mV[VZ] * s; + mQ[VW] = c; + } + else + { + loadIdentity(); + } + return (*this); +} + +const LLQuaternion& LLQuaternion::setAngleAxis(F32 angle, const LLVector4 &vec) +{ + F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]); + if (mag > FP_MAG_THRESHOLD) + { + angle *= 0.5; + F32 c = cosf(angle); + F32 s = sinf(angle) / mag; + mQ[VX] = vec.mV[VX] * s; + mQ[VY] = vec.mV[VY] * s; + mQ[VZ] = vec.mV[VZ] * s; + mQ[VW] = c; + } + else + { + loadIdentity(); + } + return (*this); +} + +const LLQuaternion& LLQuaternion::setEulerAngles(F32 roll, F32 pitch, F32 yaw) +{ + LLMatrix3 rot_mat(roll, pitch, yaw); + rot_mat.orthogonalize(); + *this = rot_mat.quaternion(); + + normalize(); + return (*this); +} + +// deprecated +const LLQuaternion& LLQuaternion::set(const LLMatrix3 &mat) +{ + *this = mat.quaternion(); + normalize(); + return (*this); +} + +// deprecated +const LLQuaternion& LLQuaternion::set(const LLMatrix4 &mat) +{ + *this = mat.quaternion(); + normalize(); + return (*this); +} + +// deprecated +const LLQuaternion& LLQuaternion::setQuat(F32 angle, F32 x, F32 y, F32 z) +{ + F32 mag = sqrtf(x * x + y * y + z * z); + if (mag > FP_MAG_THRESHOLD) + { + angle *= 0.5; + F32 c = cosf(angle); + F32 s = sinf(angle) / mag; + mQ[VX] = x * s; + mQ[VY] = y * s; + mQ[VZ] = z * s; + mQ[VW] = c; + } + else + { + loadIdentity(); + } + return (*this); +} + +// deprecated +const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector3 &vec) +{ + F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]); + if (mag > FP_MAG_THRESHOLD) + { + angle *= 0.5; + F32 c = cosf(angle); + F32 s = sinf(angle) / mag; + mQ[VX] = vec.mV[VX] * s; + mQ[VY] = vec.mV[VY] * s; + mQ[VZ] = vec.mV[VZ] * s; + mQ[VW] = c; + } + else + { + loadIdentity(); + } + return (*this); +} + +const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector4 &vec) +{ + F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]); + if (mag > FP_MAG_THRESHOLD) + { + angle *= 0.5; + F32 c = cosf(angle); + F32 s = sinf(angle) / mag; + mQ[VX] = vec.mV[VX] * s; + mQ[VY] = vec.mV[VY] * s; + mQ[VZ] = vec.mV[VZ] * s; + mQ[VW] = c; + } + else + { + loadIdentity(); + } + return (*this); +} + +const LLQuaternion& LLQuaternion::setQuat(F32 roll, F32 pitch, F32 yaw) +{ + roll *= 0.5f; + pitch *= 0.5f; + yaw *= 0.5f; + F32 sinX = sinf(roll); + F32 cosX = cosf(roll); + F32 sinY = sinf(pitch); + F32 cosY = cosf(pitch); + F32 sinZ = sinf(yaw); + F32 cosZ = cosf(yaw); + mQ[VW] = cosX * cosY * cosZ - sinX * sinY * sinZ; + mQ[VX] = sinX * cosY * cosZ + cosX * sinY * sinZ; + mQ[VY] = cosX * sinY * cosZ - sinX * cosY * sinZ; + mQ[VZ] = cosX * cosY * sinZ + sinX * sinY * cosZ; + return (*this); +} + +const LLQuaternion& LLQuaternion::setQuat(const LLMatrix3 &mat) +{ + *this = mat.quaternion(); + normalize(); + return (*this); +} + +const LLQuaternion& LLQuaternion::setQuat(const LLMatrix4 &mat) +{ + *this = mat.quaternion(); + normalize(); + return (*this); +//#if 1 +// // NOTE: LLQuaternion's are actually inverted with respect to +// // the matrices, so this code also assumes inverted quaternions +// // (-x, -y, -z, w). The result is that roll,pitch,yaw are applied +// // in reverse order (yaw,pitch,roll). +// F64 cosX = cos(roll); +// F64 cosY = cos(pitch); +// F64 cosZ = cos(yaw); +// +// F64 sinX = sin(roll); +// F64 sinY = sin(pitch); +// F64 sinZ = sin(yaw); +// +// mQ[VW] = (F32)sqrt(cosY*cosZ - sinX*sinY*sinZ + cosX*cosZ + cosX*cosY + 1.0)*.5; +// if (fabs(mQ[VW]) < F_APPROXIMATELY_ZERO) +// { +// // null rotation, any axis will do +// mQ[VX] = 0.0f; +// mQ[VY] = 1.0f; +// mQ[VZ] = 0.0f; +// } +// else +// { +// F32 inv_s = 1.0f / (4.0f * mQ[VW]); +// mQ[VX] = (F32)-(-sinX*cosY - cosX*sinY*sinZ - sinX*cosZ) * inv_s; +// mQ[VY] = (F32)-(-cosX*sinY*cosZ + sinX*sinZ - sinY) * inv_s; +// mQ[VZ] = (F32)-(-cosY*sinZ - sinX*sinY*cosZ - cosX*sinZ) * inv_s; +// } +// +//#else // This only works on a certain subset of roll/pitch/yaw +// +// F64 cosX = cosf(roll/2.0); +// F64 cosY = cosf(pitch/2.0); +// F64 cosZ = cosf(yaw/2.0); +// +// F64 sinX = sinf(roll/2.0); +// F64 sinY = sinf(pitch/2.0); +// F64 sinZ = sinf(yaw/2.0); +// +// mQ[VW] = (F32)(cosX*cosY*cosZ + sinX*sinY*sinZ); +// mQ[VX] = (F32)(sinX*cosY*cosZ - cosX*sinY*sinZ); +// mQ[VY] = (F32)(cosX*sinY*cosZ + sinX*cosY*sinZ); +// mQ[VZ] = (F32)(cosX*cosY*sinZ - sinX*sinY*cosZ); +//#endif +// +// normalize(); +// return (*this); +} + +// SJB: This code is correct for a logicly stored (non-transposed) matrix; +// Our matrices are stored transposed, OpenGL style, so this generates the +// INVERSE matrix, or the CORRECT matrix form an INVERSE quaternion. +// Because we use similar logic in LLMatrix3::quaternion(), +// we are internally consistant so everything works OK :) +LLMatrix3 LLQuaternion::getMatrix3(void) const +{ + LLMatrix3 mat; + F32 xx, xy, xz, xw, yy, yz, yw, zz, zw; + + xx = mQ[VX] * mQ[VX]; + xy = mQ[VX] * mQ[VY]; + xz = mQ[VX] * mQ[VZ]; + xw = mQ[VX] * mQ[VW]; + + yy = mQ[VY] * mQ[VY]; + yz = mQ[VY] * mQ[VZ]; + yw = mQ[VY] * mQ[VW]; + + zz = mQ[VZ] * mQ[VZ]; + zw = mQ[VZ] * mQ[VW]; + + mat.mMatrix[0][0] = 1.f - 2.f * ( yy + zz ); + mat.mMatrix[0][1] = 2.f * ( xy + zw ); + mat.mMatrix[0][2] = 2.f * ( xz - yw ); + + mat.mMatrix[1][0] = 2.f * ( xy - zw ); + mat.mMatrix[1][1] = 1.f - 2.f * ( xx + zz ); + mat.mMatrix[1][2] = 2.f * ( yz + xw ); + + mat.mMatrix[2][0] = 2.f * ( xz + yw ); + mat.mMatrix[2][1] = 2.f * ( yz - xw ); + mat.mMatrix[2][2] = 1.f - 2.f * ( xx + yy ); + + return mat; +} + +LLMatrix4 LLQuaternion::getMatrix4(void) const +{ + LLMatrix4 mat; + F32 xx, xy, xz, xw, yy, yz, yw, zz, zw; + + xx = mQ[VX] * mQ[VX]; + xy = mQ[VX] * mQ[VY]; + xz = mQ[VX] * mQ[VZ]; + xw = mQ[VX] * mQ[VW]; + + yy = mQ[VY] * mQ[VY]; + yz = mQ[VY] * mQ[VZ]; + yw = mQ[VY] * mQ[VW]; + + zz = mQ[VZ] * mQ[VZ]; + zw = mQ[VZ] * mQ[VW]; + + mat.mMatrix[0][0] = 1.f - 2.f * ( yy + zz ); + mat.mMatrix[0][1] = 2.f * ( xy + zw ); + mat.mMatrix[0][2] = 2.f * ( xz - yw ); + + mat.mMatrix[1][0] = 2.f * ( xy - zw ); + mat.mMatrix[1][1] = 1.f - 2.f * ( xx + zz ); + mat.mMatrix[1][2] = 2.f * ( yz + xw ); + + mat.mMatrix[2][0] = 2.f * ( xz + yw ); + mat.mMatrix[2][1] = 2.f * ( yz - xw ); + mat.mMatrix[2][2] = 1.f - 2.f * ( xx + yy ); + + // TODO -- should we set the translation portion to zero? + + return mat; +} + + + + +// Other useful methods + + +// calculate the shortest rotation from a to b +void LLQuaternion::shortestArc(const LLVector3 &a, const LLVector3 &b) +{ + F32 ab = a * b; // dotproduct + LLVector3 c = a % b; // crossproduct + F32 cc = c * c; // squared length of the crossproduct + if (ab * ab + cc) // test if the arguments have sufficient magnitude + { + if (cc > 0.0f) // test if the arguments are (anti)parallel + { + F32 s = sqrtf(ab * ab + cc) + ab; // note: don't try to optimize this line + F32 m = 1.0f / sqrtf(cc + s * s); // the inverted magnitude of the quaternion + mQ[VX] = c.mV[VX] * m; + mQ[VY] = c.mV[VY] * m; + mQ[VZ] = c.mV[VZ] * m; + mQ[VW] = s * m; + return; + } + if (ab < 0.0f) // test if the angle is bigger than PI/2 (anti parallel) + { + c = a - b; // the arguments are anti-parallel, we have to choose an axis + F32 m = sqrtf(c.mV[VX] * c.mV[VX] + c.mV[VY] * c.mV[VY]); // the length projected on the XY-plane + if (m > FP_MAG_THRESHOLD) + { + mQ[VX] = -c.mV[VY] / m; // return the quaternion with the axis in the XY-plane + mQ[VY] = c.mV[VX] / m; + mQ[VZ] = 0.0f; + mQ[VW] = 0.0f; + return; + } + else // the vectors are parallel to the Z-axis + { + mQ[VX] = 1.0f; // rotate around the X-axis + mQ[VY] = 0.0f; + mQ[VZ] = 0.0f; + mQ[VW] = 0.0f; + return; + } + } + } + loadIdentity(); +} + +// constrains rotation to a cone angle specified in radians +const LLQuaternion &LLQuaternion::constrain(F32 radians) +{ + const F32 cos_angle_lim = cosf( radians/2 ); // mQ[VW] limit + const F32 sin_angle_lim = sinf( radians/2 ); // rotation axis length limit + + if (mQ[VW] < 0.f) + { + mQ[VX] *= -1.f; + mQ[VY] *= -1.f; + mQ[VZ] *= -1.f; + mQ[VW] *= -1.f; + } + + // if rotation angle is greater than limit (cos is less than limit) + if( mQ[VW] < cos_angle_lim ) + { + mQ[VW] = cos_angle_lim; + F32 axis_len = sqrtf( mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] ); // sin(theta/2) + F32 axis_mult_fact = sin_angle_lim / axis_len; + mQ[VX] *= axis_mult_fact; + mQ[VY] *= axis_mult_fact; + mQ[VZ] *= axis_mult_fact; + } + + return *this; +} + +// Operators + +std::ostream& operator<<(std::ostream &s, const LLQuaternion &a) +{ + s << "{ " + << a.mQ[VX] << ", " << a.mQ[VY] << ", " << a.mQ[VZ] << ", " << a.mQ[VW] + << " }"; + return s; +} + + +// Does NOT renormalize the result +LLQuaternion operator*(const LLQuaternion &a, const LLQuaternion &b) +{ +// LLQuaternion::mMultCount++; + + LLQuaternion q( + b.mQ[3] * a.mQ[0] + b.mQ[0] * a.mQ[3] + b.mQ[1] * a.mQ[2] - b.mQ[2] * a.mQ[1], + b.mQ[3] * a.mQ[1] + b.mQ[1] * a.mQ[3] + b.mQ[2] * a.mQ[0] - b.mQ[0] * a.mQ[2], + b.mQ[3] * a.mQ[2] + b.mQ[2] * a.mQ[3] + b.mQ[0] * a.mQ[1] - b.mQ[1] * a.mQ[0], + b.mQ[3] * a.mQ[3] - b.mQ[0] * a.mQ[0] - b.mQ[1] * a.mQ[1] - b.mQ[2] * a.mQ[2] + ); + return q; +} + +/* +LLMatrix4 operator*(const LLMatrix4 &m, const LLQuaternion &q) +{ + LLMatrix4 qmat(q); + return (m*qmat); +} +*/ + + + +LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot) +{ + F32 rw = - rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] - rot.mQ[VZ] * a.mV[VZ]; + F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] - rot.mQ[VZ] * a.mV[VY]; + F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] - rot.mQ[VX] * a.mV[VZ]; + F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] - rot.mQ[VY] * a.mV[VX]; + + F32 nx = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY]; + F32 ny = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ]; + F32 nz = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX]; + + return LLVector4(nx, ny, nz, a.mV[VW]); +} + +LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot) +{ + F32 rw = - rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] - rot.mQ[VZ] * a.mV[VZ]; + F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] - rot.mQ[VZ] * a.mV[VY]; + F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] - rot.mQ[VX] * a.mV[VZ]; + F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] - rot.mQ[VY] * a.mV[VX]; + + F32 nx = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY]; + F32 ny = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ]; + F32 nz = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX]; + + return LLVector3(nx, ny, nz); +} + +LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot) +{ + F64 rw = - rot.mQ[VX] * a.mdV[VX] - rot.mQ[VY] * a.mdV[VY] - rot.mQ[VZ] * a.mdV[VZ]; + F64 rx = rot.mQ[VW] * a.mdV[VX] + rot.mQ[VY] * a.mdV[VZ] - rot.mQ[VZ] * a.mdV[VY]; + F64 ry = rot.mQ[VW] * a.mdV[VY] + rot.mQ[VZ] * a.mdV[VX] - rot.mQ[VX] * a.mdV[VZ]; + F64 rz = rot.mQ[VW] * a.mdV[VZ] + rot.mQ[VX] * a.mdV[VY] - rot.mQ[VY] * a.mdV[VX]; + + F64 nx = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY]; + F64 ny = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ]; + F64 nz = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX]; + + return LLVector3d(nx, ny, nz); +} + +F32 dot(const LLQuaternion &a, const LLQuaternion &b) +{ + return a.mQ[VX] * b.mQ[VX] + + a.mQ[VY] * b.mQ[VY] + + a.mQ[VZ] * b.mQ[VZ] + + a.mQ[VW] * b.mQ[VW]; +} + +// DEMO HACK: This lerp is probably inocrrect now due intermediate normalization +// it should look more like the lerp below +#if 0 +// linear interpolation +LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q) +{ + LLQuaternion r; + r = t * (q - p) + p; + r.normalize(); + return r; +} +#endif + +// lerp from identity to q +LLQuaternion lerp(F32 t, const LLQuaternion &q) +{ + LLQuaternion r; + r.mQ[VX] = t * q.mQ[VX]; + r.mQ[VY] = t * q.mQ[VY]; + r.mQ[VZ] = t * q.mQ[VZ]; + r.mQ[VW] = t * (q.mQ[VZ] - 1.f) + 1.f; + r.normalize(); + return r; +} + +LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q) +{ + LLQuaternion r; + F32 inv_t; + + inv_t = 1.f - t; + + r.mQ[VX] = t * q.mQ[VX] + (inv_t * p.mQ[VX]); + r.mQ[VY] = t * q.mQ[VY] + (inv_t * p.mQ[VY]); + r.mQ[VZ] = t * q.mQ[VZ] + (inv_t * p.mQ[VZ]); + r.mQ[VW] = t * q.mQ[VW] + (inv_t * p.mQ[VW]); + r.normalize(); + return r; +} + + +// spherical linear interpolation +LLQuaternion slerp( F32 u, const LLQuaternion &a, const LLQuaternion &b ) +{ + // cosine theta = dot product of a and b + F32 cos_t = a.mQ[0]*b.mQ[0] + a.mQ[1]*b.mQ[1] + a.mQ[2]*b.mQ[2] + a.mQ[3]*b.mQ[3]; + + // if b is on opposite hemisphere from a, use -a instead + bool bflip; + if (cos_t < 0.0f) + { + cos_t = -cos_t; + bflip = true; + } + else + bflip = false; + + // if B is (within precision limits) the same as A, + // just linear interpolate between A and B. + F32 alpha; // interpolant + F32 beta; // 1 - interpolant + if (1.0f - cos_t < 0.00001f) + { + beta = 1.0f - u; + alpha = u; + } + else + { + F32 theta = acosf(cos_t); + F32 sin_t = sinf(theta); + beta = sinf(theta - u*theta) / sin_t; + alpha = sinf(u*theta) / sin_t; + } + + if (bflip) + beta = -beta; + + // interpolate + LLQuaternion ret; + ret.mQ[0] = beta*a.mQ[0] + alpha*b.mQ[0]; + ret.mQ[1] = beta*a.mQ[1] + alpha*b.mQ[1]; + ret.mQ[2] = beta*a.mQ[2] + alpha*b.mQ[2]; + ret.mQ[3] = beta*a.mQ[3] + alpha*b.mQ[3]; + + return ret; +} + +// lerp whenever possible +LLQuaternion nlerp(F32 t, const LLQuaternion &a, const LLQuaternion &b) +{ + if (dot(a, b) < 0.f) + { + return slerp(t, a, b); + } + else + { + return lerp(t, a, b); + } +} + +LLQuaternion nlerp(F32 t, const LLQuaternion &q) +{ + if (q.mQ[VW] < 0.f) + { + return slerp(t, q); + } + else + { + return lerp(t, q); + } +} + +// slerp from identity quaternion to another quaternion +LLQuaternion slerp(F32 t, const LLQuaternion &q) +{ + F32 c = q.mQ[VW]; + if (1.0f == t || 1.0f == c) + { + // the trivial cases + return q; + } + + LLQuaternion r; + F32 s, angle, stq, stp; + + s = (F32) sqrt(1.f - c*c); + + if (c < 0.0f) + { + // when c < 0.0 then theta > PI/2 + // since quat and -quat are the same rotation we invert one of + // p or q to reduce unecessary spins + // A equivalent way to do it is to convert acos(c) as if it had + // been negative, and to negate stp + angle = (F32) acos(-c); + stp = -(F32) sin(angle * (1.f - t)); + stq = (F32) sin(angle * t); + } + else + { + angle = (F32) acos(c); + stp = (F32) sin(angle * (1.f - t)); + stq = (F32) sin(angle * t); + } + + r.mQ[VX] = (q.mQ[VX] * stq) / s; + r.mQ[VY] = (q.mQ[VY] * stq) / s; + r.mQ[VZ] = (q.mQ[VZ] * stq) / s; + r.mQ[VW] = (stp + q.mQ[VW] * stq) / s; + + return r; +} + +LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) +{ + LLQuaternion xQ( xRot*DEG_TO_RAD, LLVector3(1.0f, 0.0f, 0.0f) ); + LLQuaternion yQ( yRot*DEG_TO_RAD, LLVector3(0.0f, 1.0f, 0.0f) ); + LLQuaternion zQ( zRot*DEG_TO_RAD, LLVector3(0.0f, 0.0f, 1.0f) ); + LLQuaternion ret; + switch( order ) + { + case LLQuaternion::XYZ: + ret = xQ * yQ * zQ; + break; + case LLQuaternion::YZX: + ret = yQ * zQ * xQ; + break; + case LLQuaternion::ZXY: + ret = zQ * xQ * yQ; + break; + case LLQuaternion::XZY: + ret = xQ * zQ * yQ; + break; + case LLQuaternion::YXZ: + ret = yQ * xQ * zQ; + break; + case LLQuaternion::ZYX: + ret = zQ * yQ * xQ; + break; + } + return ret; +} + +const char *OrderToString( const LLQuaternion::Order order ) +{ + const char *p = NULL; + switch( order ) + { + default: + case LLQuaternion::XYZ: + p = "XYZ"; + break; + case LLQuaternion::YZX: + p = "YZX"; + break; + case LLQuaternion::ZXY: + p = "ZXY"; + break; + case LLQuaternion::XZY: + p = "XZY"; + break; + case LLQuaternion::YXZ: + p = "YXZ"; + break; + case LLQuaternion::ZYX: + p = "ZYX"; + break; + } + return p; +} + +LLQuaternion::Order StringToOrder( const char *str ) +{ + if (strncmp(str, "XYZ", 3)==0 || strncmp(str, "xyz", 3)==0) + return LLQuaternion::XYZ; + + if (strncmp(str, "YZX", 3)==0 || strncmp(str, "yzx", 3)==0) + return LLQuaternion::YZX; + + if (strncmp(str, "ZXY", 3)==0 || strncmp(str, "zxy", 3)==0) + return LLQuaternion::ZXY; + + if (strncmp(str, "XZY", 3)==0 || strncmp(str, "xzy", 3)==0) + return LLQuaternion::XZY; + + if (strncmp(str, "YXZ", 3)==0 || strncmp(str, "yxz", 3)==0) + return LLQuaternion::YXZ; + + if (strncmp(str, "ZYX", 3)==0 || strncmp(str, "zyx", 3)==0) + return LLQuaternion::ZYX; + + return LLQuaternion::XYZ; +} + +void LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) const +{ + F32 v = sqrtf(mQ[VX] * mQ[VX] + mQ[VY] * mQ[VY] + mQ[VZ] * mQ[VZ]); // length of the vector-component + if (v > FP_MAG_THRESHOLD) + { + F32 oomag = 1.0f / v; + F32 w = mQ[VW]; + if (mQ[VW] < 0.0f) + { + w = -w; // make VW positive + oomag = -oomag; // invert the axis + } + vec.mV[VX] = mQ[VX] * oomag; // normalize the axis + vec.mV[VY] = mQ[VY] * oomag; + vec.mV[VZ] = mQ[VZ] * oomag; + *angle = 2.0f * atan2f(v, w); // get the angle + } + else + { + *angle = 0.0f; // no rotation + vec.mV[VX] = 0.0f; // around some dummy axis + vec.mV[VY] = 0.0f; + vec.mV[VZ] = 1.0f; + } +} + +const LLQuaternion& LLQuaternion::setFromAzimuthAndAltitude(F32 azimuthRadians, F32 altitudeRadians) +{ + // euler angle inputs are complements of azimuth/altitude which are measured from zenith + F32 pitch = llclamp(F_PI_BY_TWO - altitudeRadians, 0.0f, F_PI_BY_TWO); + F32 yaw = llclamp(F_PI_BY_TWO - azimuthRadians, 0.0f, F_PI_BY_TWO); + setEulerAngles(0.0f, pitch, yaw); + return *this; +} + +void LLQuaternion::getAzimuthAndAltitude(F32 &azimuthRadians, F32 &altitudeRadians) +{ + F32 rick_roll; + F32 pitch; + F32 yaw; + getEulerAngles(&rick_roll, &pitch, &yaw); + // make these measured from zenith + altitudeRadians = llclamp(F_PI_BY_TWO - pitch, 0.0f, F_PI_BY_TWO); + azimuthRadians = llclamp(F_PI_BY_TWO - yaw, 0.0f, F_PI_BY_TWO); +} + +// quaternion does not need to be normalized +void LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const +{ + F32 sx = 2 * (mQ[VX] * mQ[VW] - mQ[VY] * mQ[VZ]); // sine of the roll + F32 sy = 2 * (mQ[VY] * mQ[VW] + mQ[VX] * mQ[VZ]); // sine of the pitch + F32 ys = mQ[VW] * mQ[VW] - mQ[VY] * mQ[VY]; // intermediate cosine 1 + F32 xz = mQ[VX] * mQ[VX] - mQ[VZ] * mQ[VZ]; // intermediate cosine 2 + F32 cx = ys - xz; // cosine of the roll + F32 cy = sqrtf(sx * sx + cx * cx); // cosine of the pitch + if (cy > GIMBAL_THRESHOLD) // no gimbal lock + { + *roll = atan2f(sx, cx); + *pitch = atan2f(sy, cy); + *yaw = atan2f(2 * (mQ[VZ] * mQ[VW] - mQ[VX] * mQ[VY]), ys + xz); + } + else // gimbal lock + { + if (sy > 0) + { + *pitch = F_PI_BY_TWO; + *yaw = 2 * atan2f(mQ[VZ] + mQ[VX], mQ[VW] + mQ[VY]); + } + else + { + *pitch = -F_PI_BY_TWO; + *yaw = 2 * atan2f(mQ[VZ] - mQ[VX], mQ[VW] - mQ[VY]); + } + *roll = 0; + } +} + +// Saves space by using the fact that our quaternions are normalized +LLVector3 LLQuaternion::packToVector3() const +{ + F32 x = mQ[VX]; + F32 y = mQ[VY]; + F32 z = mQ[VZ]; + F32 w = mQ[VW]; + F32 mag = sqrtf(x * x + y * y + z * z + w * w); + if (mag > FP_MAG_THRESHOLD) + { + x /= mag; + y /= mag; + z /= mag; // no need to normalize w, it's not used + } + if( mQ[VW] >= 0 ) + { + return LLVector3( x, y , z ); + } + else + { + return LLVector3( -x, -y, -z ); + } +} + +// Saves space by using the fact that our quaternions are normalized +void LLQuaternion::unpackFromVector3( const LLVector3& vec ) +{ + mQ[VX] = vec.mV[VX]; + mQ[VY] = vec.mV[VY]; + mQ[VZ] = vec.mV[VZ]; + F32 t = 1.f - vec.magVecSquared(); + if( t > 0 ) + { + mQ[VW] = sqrt( t ); + } + else + { + // Need this to avoid trying to find the square root of a negative number due + // to floating point error. + mQ[VW] = 0; + } +} + +bool LLQuaternion::parseQuat(const std::string& buf, LLQuaternion* value) +{ + if( buf.empty() || value == NULL) + { + return false; + } + + LLQuaternion quat; + S32 count = sscanf( buf.c_str(), "%f %f %f %f", quat.mQ + 0, quat.mQ + 1, quat.mQ + 2, quat.mQ + 3 ); + if( 4 == count ) + { + value->set( quat ); + return true; + } + + return false; +} + + +// End diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h index 7a245475c1..2caa993007 100644 --- a/indra/llmath/llquaternion.h +++ b/indra/llmath/llquaternion.h @@ -1,617 +1,617 @@ -/**
- * @file llquaternion.h
- * @brief LLQuaternion class header file.
- *
- * $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$
- */
-
-#ifndef LLQUATERNION_H
-#define LLQUATERNION_H
-
-#include <iostream>
-#include "llsd.h"
-
-#ifndef LLMATH_H //enforce specific include order to avoid tangling inline dependencies
-#error "Please include llmath.h first."
-#endif
-
-class LLVector4;
-class LLVector3;
-class LLVector3d;
-class LLMatrix4;
-class LLMatrix3;
-
-// NOTA BENE: Quaternion code is written assuming Unit Quaternions!!!!
-// Moreover, it is written assuming that all vectors and matricies
-// passed as arguments are normalized and unitary respectively.
-// VERY VERY VERY VERY BAD THINGS will happen if these assumptions fail.
-
-static const U32 LENGTHOFQUAT = 4;
-
-class LLQuaternion
-{
-public:
- F32 mQ[LENGTHOFQUAT];
-
- static const LLQuaternion DEFAULT;
-
- LLQuaternion(); // Initializes Quaternion to (0,0,0,1)
- explicit LLQuaternion(const LLMatrix4 &mat); // Initializes Quaternion from Matrix4
- explicit LLQuaternion(const LLMatrix3 &mat); // Initializes Quaternion from Matrix3
- LLQuaternion(F32 x, F32 y, F32 z, F32 w); // Initializes Quaternion to normalize(x, y, z, w)
- LLQuaternion(F32 angle, const LLVector4 &vec); // Initializes Quaternion to axis_angle2quat(angle, vec)
- LLQuaternion(F32 angle, const LLVector3 &vec); // Initializes Quaternion to axis_angle2quat(angle, vec)
- LLQuaternion(const F32 *q); // Initializes Quaternion to normalize(x, y, z, w)
- LLQuaternion(const LLVector3 &x_axis,
- const LLVector3 &y_axis,
- const LLVector3 &z_axis); // Initializes Quaternion from Matrix3 = [x_axis ; y_axis ; z_axis]
- explicit LLQuaternion(const LLSD &sd); // Initializes Quaternion from LLSD array.
-
- LLSD getValue() const;
- void setValue(const LLSD& sd);
-
- bool isIdentity() const;
- bool isNotIdentity() const;
- bool isFinite() const; // checks to see if all values of LLQuaternion are finite
- void quantize16(F32 lower, F32 upper); // changes the vector to reflect quatization
- void quantize8(F32 lower, F32 upper); // changes the vector to reflect quatization
- void loadIdentity(); // Loads the quaternion that represents the identity rotation
-
- bool isEqualEps(const LLQuaternion &quat, F32 epsilon) const;
- bool isNotEqualEps(const LLQuaternion &quat, F32 epsilon) const;
-
- const LLQuaternion& set(F32 x, F32 y, F32 z, F32 w); // Sets Quaternion to normalize(x, y, z, w)
- const LLQuaternion& set(const LLQuaternion &quat); // Copies Quaternion
- const LLQuaternion& set(const F32 *q); // Sets Quaternion to normalize(quat[VX], quat[VY], quat[VZ], quat[VW])
- const LLQuaternion& set(const LLMatrix3 &mat); // Sets Quaternion to mat2quat(mat)
- const LLQuaternion& set(const LLMatrix4 &mat); // Sets Quaternion to mat2quat(mat)
- const LLQuaternion& setFromAzimuthAndAltitude(F32 azimuth, F32 altitude);
-
- const LLQuaternion& setAngleAxis(F32 angle, F32 x, F32 y, F32 z); // Sets Quaternion to axis_angle2quat(angle, x, y, z)
- const LLQuaternion& setAngleAxis(F32 angle, const LLVector3 &vec); // Sets Quaternion to axis_angle2quat(angle, vec)
- const LLQuaternion& setAngleAxis(F32 angle, const LLVector4 &vec); // Sets Quaternion to axis_angle2quat(angle, vec)
- const LLQuaternion& setEulerAngles(F32 roll, F32 pitch, F32 yaw); // Sets Quaternion to euler2quat(pitch, yaw, roll)
-
- const LLQuaternion& setQuatInit(F32 x, F32 y, F32 z, F32 w); // deprecated
- const LLQuaternion& setQuat(const LLQuaternion &quat); // deprecated
- const LLQuaternion& setQuat(const F32 *q); // deprecated
- const LLQuaternion& setQuat(const LLMatrix3 &mat); // deprecated
- const LLQuaternion& setQuat(const LLMatrix4 &mat); // deprecated
- const LLQuaternion& setQuat(F32 angle, F32 x, F32 y, F32 z); // deprecated
- const LLQuaternion& setQuat(F32 angle, const LLVector3 &vec); // deprecated
- const LLQuaternion& setQuat(F32 angle, const LLVector4 &vec); // deprecated
- const LLQuaternion& setQuat(F32 roll, F32 pitch, F32 yaw); // deprecated
-
- LLMatrix4 getMatrix4(void) const; // Returns the Matrix4 equivalent of Quaternion
- LLMatrix3 getMatrix3(void) const; // Returns the Matrix3 equivalent of Quaternion
- void getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const; // returns rotation in radians about axis x,y,z
- void getAngleAxis(F32* angle, LLVector3 &vec) const;
- void getEulerAngles(F32 *roll, F32* pitch, F32 *yaw) const;
- void getAzimuthAndAltitude(F32 &azimuth, F32 &altitude);
-
- F32 normalize(); // Normalizes Quaternion and returns magnitude
- F32 normQuat(); // deprecated
-
- const LLQuaternion& conjugate(void); // Conjugates Quaternion and returns result
- const LLQuaternion& conjQuat(void); // deprecated
-
- // Other useful methods
- const LLQuaternion& transpose(); // transpose (same as conjugate)
- const LLQuaternion& transQuat(); // deprecated
-
- void shortestArc(const LLVector3 &a, const LLVector3 &b); // shortest rotation from a to b
- const LLQuaternion& constrain(F32 radians); // constrains rotation to a cone angle specified in radians
-
- // Standard operators
- friend std::ostream& operator<<(std::ostream &s, const LLQuaternion &a); // Prints a
- friend LLQuaternion operator+(const LLQuaternion &a, const LLQuaternion &b); // Addition
- friend LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b); // Subtraction
- friend LLQuaternion operator-(const LLQuaternion &a); // Negation
- friend LLQuaternion operator*(F32 a, const LLQuaternion &q); // Scale
- friend LLQuaternion operator*(const LLQuaternion &q, F32 b); // Scale
- friend LLQuaternion operator*(const LLQuaternion &a, const LLQuaternion &b); // Returns a * b
- friend LLQuaternion operator~(const LLQuaternion &a); // Returns a* (Conjugate of a)
- bool operator==(const LLQuaternion &b) const; // Returns a == b
- bool operator!=(const LLQuaternion &b) const; // Returns a != b
-
- friend const LLQuaternion& operator*=(LLQuaternion &a, const LLQuaternion &b); // Returns a * b
-
- friend LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot); // Rotates a by rot
- friend LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot); // Rotates a by rot
- friend LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot); // Rotates a by rot
-
- // Non-standard operators
- friend F32 dot(const LLQuaternion &a, const LLQuaternion &b);
- friend LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q); // linear interpolation (t = 0 to 1) from p to q
- friend LLQuaternion lerp(F32 t, const LLQuaternion &q); // linear interpolation (t = 0 to 1) from identity to q
- friend LLQuaternion slerp(F32 t, const LLQuaternion &p, const LLQuaternion &q); // spherical linear interpolation from p to q
- friend LLQuaternion slerp(F32 t, const LLQuaternion &q); // spherical linear interpolation from identity to q
- friend LLQuaternion nlerp(F32 t, const LLQuaternion &p, const LLQuaternion &q); // normalized linear interpolation from p to q
- friend LLQuaternion nlerp(F32 t, const LLQuaternion &q); // normalized linear interpolation from p to q
-
- LLVector3 packToVector3() const; // Saves space by using the fact that our quaternions are normalized
- void unpackFromVector3(const LLVector3& vec); // Saves space by using the fact that our quaternions are normalized
-
- enum Order {
- XYZ = 0,
- YZX = 1,
- ZXY = 2,
- XZY = 3,
- YXZ = 4,
- ZYX = 5
- };
- // Creates a quaternions from maya's rotation representation,
- // which is 3 rotations (in DEGREES) in the specified order
- friend LLQuaternion mayaQ(F32 x, F32 y, F32 z, Order order);
-
- // Conversions between Order and strings like "xyz" or "ZYX"
- friend const char *OrderToString( const Order order );
- friend Order StringToOrder( const char *str );
-
- static bool parseQuat(const std::string& buf, LLQuaternion* value);
-
- // For debugging, only
- //static U32 mMultCount;
-};
-
-inline LLSD LLQuaternion::getValue() const
-{
- LLSD ret;
- ret[0] = mQ[0];
- ret[1] = mQ[1];
- ret[2] = mQ[2];
- ret[3] = mQ[3];
- return ret;
-}
-
-inline void LLQuaternion::setValue(const LLSD& sd)
-{
- mQ[0] = sd[0].asReal();
- mQ[1] = sd[1].asReal();
- mQ[2] = sd[2].asReal();
- mQ[3] = sd[3].asReal();
-}
-
-// checker
-inline bool LLQuaternion::isFinite() const
-{
- return (llfinite(mQ[VX]) && llfinite(mQ[VY]) && llfinite(mQ[VZ]) && llfinite(mQ[VS]));
-}
-
-inline bool LLQuaternion::isIdentity() const
-{
- return
- ( mQ[VX] == 0.f ) &&
- ( mQ[VY] == 0.f ) &&
- ( mQ[VZ] == 0.f ) &&
- ( mQ[VS] == 1.f );
-}
-
-inline bool LLQuaternion::isNotIdentity() const
-{
- return
- ( mQ[VX] != 0.f ) ||
- ( mQ[VY] != 0.f ) ||
- ( mQ[VZ] != 0.f ) ||
- ( mQ[VS] != 1.f );
-}
-
-
-
-inline LLQuaternion::LLQuaternion(void)
-{
- mQ[VX] = 0.f;
- mQ[VY] = 0.f;
- mQ[VZ] = 0.f;
- mQ[VS] = 1.f;
-}
-
-inline LLQuaternion::LLQuaternion(F32 x, F32 y, F32 z, F32 w)
-{
- mQ[VX] = x;
- mQ[VY] = y;
- mQ[VZ] = z;
- mQ[VS] = w;
-
- //RN: don't normalize this case as its used mainly for temporaries during calculations
- //normalize();
- /*
- F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
- mag -= 1.f;
- mag = fabs(mag);
- llassert(mag < 10.f*FP_MAG_THRESHOLD);
- */
-}
-
-inline LLQuaternion::LLQuaternion(const F32 *q)
-{
- mQ[VX] = q[VX];
- mQ[VY] = q[VY];
- mQ[VZ] = q[VZ];
- mQ[VS] = q[VW];
-
- normalize();
- /*
- F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
- mag -= 1.f;
- mag = fabs(mag);
- llassert(mag < FP_MAG_THRESHOLD);
- */
-}
-
-
-inline void LLQuaternion::loadIdentity()
-{
- mQ[VX] = 0.0f;
- mQ[VY] = 0.0f;
- mQ[VZ] = 0.0f;
- mQ[VW] = 1.0f;
-}
-
-inline bool LLQuaternion::isEqualEps(const LLQuaternion &quat, F32 epsilon) const
-{
- return ( fabs(mQ[VX] - quat.mQ[VX]) < epsilon
- && fabs(mQ[VY] - quat.mQ[VY]) < epsilon
- && fabs(mQ[VZ] - quat.mQ[VZ]) < epsilon
- && fabs(mQ[VS] - quat.mQ[VS]) < epsilon );
-}
-
-inline bool LLQuaternion::isNotEqualEps(const LLQuaternion &quat, F32 epsilon) const
-{
- return ( fabs(mQ[VX] - quat.mQ[VX]) > epsilon
- || fabs(mQ[VY] - quat.mQ[VY]) > epsilon
- || fabs(mQ[VZ] - quat.mQ[VZ]) > epsilon
- || fabs(mQ[VS] - quat.mQ[VS]) > epsilon );
-}
-
-inline const LLQuaternion& LLQuaternion::set(F32 x, F32 y, F32 z, F32 w)
-{
- mQ[VX] = x;
- mQ[VY] = y;
- mQ[VZ] = z;
- mQ[VS] = w;
- normalize();
- return (*this);
-}
-
-inline const LLQuaternion& LLQuaternion::set(const LLQuaternion &quat)
-{
- mQ[VX] = quat.mQ[VX];
- mQ[VY] = quat.mQ[VY];
- mQ[VZ] = quat.mQ[VZ];
- mQ[VW] = quat.mQ[VW];
- normalize();
- return (*this);
-}
-
-inline const LLQuaternion& LLQuaternion::set(const F32 *q)
-{
- mQ[VX] = q[VX];
- mQ[VY] = q[VY];
- mQ[VZ] = q[VZ];
- mQ[VS] = q[VW];
- normalize();
- return (*this);
-}
-
-
-// deprecated
-inline const LLQuaternion& LLQuaternion::setQuatInit(F32 x, F32 y, F32 z, F32 w)
-{
- mQ[VX] = x;
- mQ[VY] = y;
- mQ[VZ] = z;
- mQ[VS] = w;
- normalize();
- return (*this);
-}
-
-// deprecated
-inline const LLQuaternion& LLQuaternion::setQuat(const LLQuaternion &quat)
-{
- mQ[VX] = quat.mQ[VX];
- mQ[VY] = quat.mQ[VY];
- mQ[VZ] = quat.mQ[VZ];
- mQ[VW] = quat.mQ[VW];
- normalize();
- return (*this);
-}
-
-// deprecated
-inline const LLQuaternion& LLQuaternion::setQuat(const F32 *q)
-{
- mQ[VX] = q[VX];
- mQ[VY] = q[VY];
- mQ[VZ] = q[VZ];
- mQ[VS] = q[VW];
- normalize();
- return (*this);
-}
-
-inline void LLQuaternion::getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const
-{
- F32 v = sqrtf(mQ[VX] * mQ[VX] + mQ[VY] * mQ[VY] + mQ[VZ] * mQ[VZ]); // length of the vector-component
- if (v > FP_MAG_THRESHOLD)
- {
- F32 oomag = 1.0f / v;
- F32 w = mQ[VW];
- if (w < 0.0f)
- {
- w = -w; // make VW positive
- oomag = -oomag; // invert the axis
- }
- *x = mQ[VX] * oomag; // normalize the axis
- *y = mQ[VY] * oomag;
- *z = mQ[VZ] * oomag;
- *angle = 2.0f * atan2f(v, w); // get the angle
- }
- else
- {
- *angle = 0.0f; // no rotation
- *x = 0.0f; // around some dummy axis
- *y = 0.0f;
- *z = 1.0f;
- }
-}
-
-inline const LLQuaternion& LLQuaternion::conjugate()
-{
- mQ[VX] *= -1.f;
- mQ[VY] *= -1.f;
- mQ[VZ] *= -1.f;
- return (*this);
-}
-
-inline const LLQuaternion& LLQuaternion::conjQuat()
-{
- mQ[VX] *= -1.f;
- mQ[VY] *= -1.f;
- mQ[VZ] *= -1.f;
- return (*this);
-}
-
-// Transpose
-inline const LLQuaternion& LLQuaternion::transpose()
-{
- mQ[VX] *= -1.f;
- mQ[VY] *= -1.f;
- mQ[VZ] *= -1.f;
- return (*this);
-}
-
-// deprecated
-inline const LLQuaternion& LLQuaternion::transQuat()
-{
- mQ[VX] *= -1.f;
- mQ[VY] *= -1.f;
- mQ[VZ] *= -1.f;
- return (*this);
-}
-
-
-inline LLQuaternion operator+(const LLQuaternion &a, const LLQuaternion &b)
-{
- return LLQuaternion(
- a.mQ[VX] + b.mQ[VX],
- a.mQ[VY] + b.mQ[VY],
- a.mQ[VZ] + b.mQ[VZ],
- a.mQ[VW] + b.mQ[VW] );
-}
-
-
-inline LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b)
-{
- return LLQuaternion(
- a.mQ[VX] - b.mQ[VX],
- a.mQ[VY] - b.mQ[VY],
- a.mQ[VZ] - b.mQ[VZ],
- a.mQ[VW] - b.mQ[VW] );
-}
-
-
-inline LLQuaternion operator-(const LLQuaternion &a)
-{
- return LLQuaternion(
- -a.mQ[VX],
- -a.mQ[VY],
- -a.mQ[VZ],
- -a.mQ[VW] );
-}
-
-
-inline LLQuaternion operator*(F32 a, const LLQuaternion &q)
-{
- return LLQuaternion(
- a * q.mQ[VX],
- a * q.mQ[VY],
- a * q.mQ[VZ],
- a * q.mQ[VW] );
-}
-
-
-inline LLQuaternion operator*(const LLQuaternion &q, F32 a)
-{
- return LLQuaternion(
- a * q.mQ[VX],
- a * q.mQ[VY],
- a * q.mQ[VZ],
- a * q.mQ[VW] );
-}
-
-inline LLQuaternion operator~(const LLQuaternion &a)
-{
- LLQuaternion q(a);
- q.conjQuat();
- return q;
-}
-
-inline bool LLQuaternion::operator==(const LLQuaternion &b) const
-{
- return ( (mQ[VX] == b.mQ[VX])
- &&(mQ[VY] == b.mQ[VY])
- &&(mQ[VZ] == b.mQ[VZ])
- &&(mQ[VS] == b.mQ[VS]));
-}
-
-inline bool LLQuaternion::operator!=(const LLQuaternion &b) const
-{
- return ( (mQ[VX] != b.mQ[VX])
- ||(mQ[VY] != b.mQ[VY])
- ||(mQ[VZ] != b.mQ[VZ])
- ||(mQ[VS] != b.mQ[VS]));
-}
-
-inline const LLQuaternion& operator*=(LLQuaternion &a, const LLQuaternion &b)
-{
-#if 1
- LLQuaternion q(
- b.mQ[3] * a.mQ[0] + b.mQ[0] * a.mQ[3] + b.mQ[1] * a.mQ[2] - b.mQ[2] * a.mQ[1],
- b.mQ[3] * a.mQ[1] + b.mQ[1] * a.mQ[3] + b.mQ[2] * a.mQ[0] - b.mQ[0] * a.mQ[2],
- b.mQ[3] * a.mQ[2] + b.mQ[2] * a.mQ[3] + b.mQ[0] * a.mQ[1] - b.mQ[1] * a.mQ[0],
- b.mQ[3] * a.mQ[3] - b.mQ[0] * a.mQ[0] - b.mQ[1] * a.mQ[1] - b.mQ[2] * a.mQ[2]
- );
- a = q;
-#else
- a = a * b;
-#endif
- return a;
-}
-
-const F32 ONE_PART_IN_A_MILLION = 0.000001f;
-
-inline F32 LLQuaternion::normalize()
-{
- F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
-
- if (mag > FP_MAG_THRESHOLD)
- {
- // Floating point error can prevent some quaternions from achieving
- // exact unity length. When trying to renormalize such quaternions we
- // can oscillate between multiple quantized states. To prevent such
- // drifts we only renomalize if the length is far enough from unity.
- if (fabs(1.f - mag) > ONE_PART_IN_A_MILLION)
- {
- F32 oomag = 1.f/mag;
- mQ[VX] *= oomag;
- mQ[VY] *= oomag;
- mQ[VZ] *= oomag;
- mQ[VS] *= oomag;
- }
- }
- else
- {
- // we were given a very bad quaternion so we set it to identity
- mQ[VX] = 0.f;
- mQ[VY] = 0.f;
- mQ[VZ] = 0.f;
- mQ[VS] = 1.f;
- }
-
- return mag;
-}
-
-// deprecated
-inline F32 LLQuaternion::normQuat()
-{
- F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
-
- if (mag > FP_MAG_THRESHOLD)
- {
- if (fabs(1.f - mag) > ONE_PART_IN_A_MILLION)
- {
- // only renormalize if length not close enough to 1.0 already
- F32 oomag = 1.f/mag;
- mQ[VX] *= oomag;
- mQ[VY] *= oomag;
- mQ[VZ] *= oomag;
- mQ[VS] *= oomag;
- }
- }
- else
- {
- mQ[VX] = 0.f;
- mQ[VY] = 0.f;
- mQ[VZ] = 0.f;
- mQ[VS] = 1.f;
- }
-
- return mag;
-}
-
-LLQuaternion::Order StringToOrder( const char *str );
-
-// Some notes about Quaternions
-
-// What is a Quaternion?
-// ---------------------
-// A quaternion is a point in 4-dimensional complex space.
-// Q = { Qx, Qy, Qz, Qw }
-//
-//
-// Why Quaternions?
-// ----------------
-// The set of quaternions that make up the the 4-D unit sphere
-// can be mapped to the set of all rotations in 3-D space. Sometimes
-// it is easier to describe/manipulate rotations in quaternion space
-// than rotation-matrix space.
-//
-//
-// How Quaternions?
-// ----------------
-// In order to take advantage of quaternions we need to know how to
-// go from rotation-matricies to quaternions and back. We also have
-// to agree what variety of rotations we're generating.
-//
-// Consider the equation... v' = v * R
-//
-// There are two ways to think about rotations of vectors.
-// 1) v' is the same vector in a different reference frame
-// 2) v' is a new vector in the same reference frame
-//
-// bookmark -- which way are we using?
-//
-//
-// Quaternion from Angle-Axis:
-// ---------------------------
-// Suppose we wanted to represent a rotation of some angle (theta)
-// about some axis ({Ax, Ay, Az})...
-//
-// axis of rotation = {Ax, Ay, Az}
-// angle_of_rotation = theta
-//
-// s = sin(0.5 * theta)
-// c = cos(0.5 * theta)
-// Q = { s * Ax, s * Ay, s * Az, c }
-//
-//
-// 3x3 Matrix from Quaternion
-// --------------------------
-//
-// | |
-// | 1 - 2 * (y^2 + z^2) 2 * (x * y + z * w) 2 * (y * w - x * z) |
-// | |
-// M = | 2 * (x * y - z * w) 1 - 2 * (x^2 + z^2) 2 * (y * z + x * w) |
-// | |
-// | 2 * (x * z + y * w) 2 * (y * z - x * w) 1 - 2 * (x^2 + y^2) |
-// | |
-
-#endif
+/** + * @file llquaternion.h + * @brief LLQuaternion class header file. + * + * $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$ + */ + +#ifndef LLQUATERNION_H +#define LLQUATERNION_H + +#include <iostream> +#include "llsd.h" + +#ifndef LLMATH_H //enforce specific include order to avoid tangling inline dependencies +#error "Please include llmath.h first." +#endif + +class LLVector4; +class LLVector3; +class LLVector3d; +class LLMatrix4; +class LLMatrix3; + +// NOTA BENE: Quaternion code is written assuming Unit Quaternions!!!! +// Moreover, it is written assuming that all vectors and matricies +// passed as arguments are normalized and unitary respectively. +// VERY VERY VERY VERY BAD THINGS will happen if these assumptions fail. + +static const U32 LENGTHOFQUAT = 4; + +class LLQuaternion +{ +public: + F32 mQ[LENGTHOFQUAT]; + + static const LLQuaternion DEFAULT; + + LLQuaternion(); // Initializes Quaternion to (0,0,0,1) + explicit LLQuaternion(const LLMatrix4 &mat); // Initializes Quaternion from Matrix4 + explicit LLQuaternion(const LLMatrix3 &mat); // Initializes Quaternion from Matrix3 + LLQuaternion(F32 x, F32 y, F32 z, F32 w); // Initializes Quaternion to normalize(x, y, z, w) + LLQuaternion(F32 angle, const LLVector4 &vec); // Initializes Quaternion to axis_angle2quat(angle, vec) + LLQuaternion(F32 angle, const LLVector3 &vec); // Initializes Quaternion to axis_angle2quat(angle, vec) + LLQuaternion(const F32 *q); // Initializes Quaternion to normalize(x, y, z, w) + LLQuaternion(const LLVector3 &x_axis, + const LLVector3 &y_axis, + const LLVector3 &z_axis); // Initializes Quaternion from Matrix3 = [x_axis ; y_axis ; z_axis] + explicit LLQuaternion(const LLSD &sd); // Initializes Quaternion from LLSD array. + + LLSD getValue() const; + void setValue(const LLSD& sd); + + bool isIdentity() const; + bool isNotIdentity() const; + bool isFinite() const; // checks to see if all values of LLQuaternion are finite + void quantize16(F32 lower, F32 upper); // changes the vector to reflect quatization + void quantize8(F32 lower, F32 upper); // changes the vector to reflect quatization + void loadIdentity(); // Loads the quaternion that represents the identity rotation + + bool isEqualEps(const LLQuaternion &quat, F32 epsilon) const; + bool isNotEqualEps(const LLQuaternion &quat, F32 epsilon) const; + + const LLQuaternion& set(F32 x, F32 y, F32 z, F32 w); // Sets Quaternion to normalize(x, y, z, w) + const LLQuaternion& set(const LLQuaternion &quat); // Copies Quaternion + const LLQuaternion& set(const F32 *q); // Sets Quaternion to normalize(quat[VX], quat[VY], quat[VZ], quat[VW]) + const LLQuaternion& set(const LLMatrix3 &mat); // Sets Quaternion to mat2quat(mat) + const LLQuaternion& set(const LLMatrix4 &mat); // Sets Quaternion to mat2quat(mat) + const LLQuaternion& setFromAzimuthAndAltitude(F32 azimuth, F32 altitude); + + const LLQuaternion& setAngleAxis(F32 angle, F32 x, F32 y, F32 z); // Sets Quaternion to axis_angle2quat(angle, x, y, z) + const LLQuaternion& setAngleAxis(F32 angle, const LLVector3 &vec); // Sets Quaternion to axis_angle2quat(angle, vec) + const LLQuaternion& setAngleAxis(F32 angle, const LLVector4 &vec); // Sets Quaternion to axis_angle2quat(angle, vec) + const LLQuaternion& setEulerAngles(F32 roll, F32 pitch, F32 yaw); // Sets Quaternion to euler2quat(pitch, yaw, roll) + + const LLQuaternion& setQuatInit(F32 x, F32 y, F32 z, F32 w); // deprecated + const LLQuaternion& setQuat(const LLQuaternion &quat); // deprecated + const LLQuaternion& setQuat(const F32 *q); // deprecated + const LLQuaternion& setQuat(const LLMatrix3 &mat); // deprecated + const LLQuaternion& setQuat(const LLMatrix4 &mat); // deprecated + const LLQuaternion& setQuat(F32 angle, F32 x, F32 y, F32 z); // deprecated + const LLQuaternion& setQuat(F32 angle, const LLVector3 &vec); // deprecated + const LLQuaternion& setQuat(F32 angle, const LLVector4 &vec); // deprecated + const LLQuaternion& setQuat(F32 roll, F32 pitch, F32 yaw); // deprecated + + LLMatrix4 getMatrix4(void) const; // Returns the Matrix4 equivalent of Quaternion + LLMatrix3 getMatrix3(void) const; // Returns the Matrix3 equivalent of Quaternion + void getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const; // returns rotation in radians about axis x,y,z + void getAngleAxis(F32* angle, LLVector3 &vec) const; + void getEulerAngles(F32 *roll, F32* pitch, F32 *yaw) const; + void getAzimuthAndAltitude(F32 &azimuth, F32 &altitude); + + F32 normalize(); // Normalizes Quaternion and returns magnitude + F32 normQuat(); // deprecated + + const LLQuaternion& conjugate(void); // Conjugates Quaternion and returns result + const LLQuaternion& conjQuat(void); // deprecated + + // Other useful methods + const LLQuaternion& transpose(); // transpose (same as conjugate) + const LLQuaternion& transQuat(); // deprecated + + void shortestArc(const LLVector3 &a, const LLVector3 &b); // shortest rotation from a to b + const LLQuaternion& constrain(F32 radians); // constrains rotation to a cone angle specified in radians + + // Standard operators + friend std::ostream& operator<<(std::ostream &s, const LLQuaternion &a); // Prints a + friend LLQuaternion operator+(const LLQuaternion &a, const LLQuaternion &b); // Addition + friend LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b); // Subtraction + friend LLQuaternion operator-(const LLQuaternion &a); // Negation + friend LLQuaternion operator*(F32 a, const LLQuaternion &q); // Scale + friend LLQuaternion operator*(const LLQuaternion &q, F32 b); // Scale + friend LLQuaternion operator*(const LLQuaternion &a, const LLQuaternion &b); // Returns a * b + friend LLQuaternion operator~(const LLQuaternion &a); // Returns a* (Conjugate of a) + bool operator==(const LLQuaternion &b) const; // Returns a == b + bool operator!=(const LLQuaternion &b) const; // Returns a != b + + friend const LLQuaternion& operator*=(LLQuaternion &a, const LLQuaternion &b); // Returns a * b + + friend LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot); // Rotates a by rot + friend LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot); // Rotates a by rot + friend LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot); // Rotates a by rot + + // Non-standard operators + friend F32 dot(const LLQuaternion &a, const LLQuaternion &b); + friend LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q); // linear interpolation (t = 0 to 1) from p to q + friend LLQuaternion lerp(F32 t, const LLQuaternion &q); // linear interpolation (t = 0 to 1) from identity to q + friend LLQuaternion slerp(F32 t, const LLQuaternion &p, const LLQuaternion &q); // spherical linear interpolation from p to q + friend LLQuaternion slerp(F32 t, const LLQuaternion &q); // spherical linear interpolation from identity to q + friend LLQuaternion nlerp(F32 t, const LLQuaternion &p, const LLQuaternion &q); // normalized linear interpolation from p to q + friend LLQuaternion nlerp(F32 t, const LLQuaternion &q); // normalized linear interpolation from p to q + + LLVector3 packToVector3() const; // Saves space by using the fact that our quaternions are normalized + void unpackFromVector3(const LLVector3& vec); // Saves space by using the fact that our quaternions are normalized + + enum Order { + XYZ = 0, + YZX = 1, + ZXY = 2, + XZY = 3, + YXZ = 4, + ZYX = 5 + }; + // Creates a quaternions from maya's rotation representation, + // which is 3 rotations (in DEGREES) in the specified order + friend LLQuaternion mayaQ(F32 x, F32 y, F32 z, Order order); + + // Conversions between Order and strings like "xyz" or "ZYX" + friend const char *OrderToString( const Order order ); + friend Order StringToOrder( const char *str ); + + static bool parseQuat(const std::string& buf, LLQuaternion* value); + + // For debugging, only + //static U32 mMultCount; +}; + +inline LLSD LLQuaternion::getValue() const +{ + LLSD ret; + ret[0] = mQ[0]; + ret[1] = mQ[1]; + ret[2] = mQ[2]; + ret[3] = mQ[3]; + return ret; +} + +inline void LLQuaternion::setValue(const LLSD& sd) +{ + mQ[0] = sd[0].asReal(); + mQ[1] = sd[1].asReal(); + mQ[2] = sd[2].asReal(); + mQ[3] = sd[3].asReal(); +} + +// checker +inline bool LLQuaternion::isFinite() const +{ + return (llfinite(mQ[VX]) && llfinite(mQ[VY]) && llfinite(mQ[VZ]) && llfinite(mQ[VS])); +} + +inline bool LLQuaternion::isIdentity() const +{ + return + ( mQ[VX] == 0.f ) && + ( mQ[VY] == 0.f ) && + ( mQ[VZ] == 0.f ) && + ( mQ[VS] == 1.f ); +} + +inline bool LLQuaternion::isNotIdentity() const +{ + return + ( mQ[VX] != 0.f ) || + ( mQ[VY] != 0.f ) || + ( mQ[VZ] != 0.f ) || + ( mQ[VS] != 1.f ); +} + + + +inline LLQuaternion::LLQuaternion(void) +{ + mQ[VX] = 0.f; + mQ[VY] = 0.f; + mQ[VZ] = 0.f; + mQ[VS] = 1.f; +} + +inline LLQuaternion::LLQuaternion(F32 x, F32 y, F32 z, F32 w) +{ + mQ[VX] = x; + mQ[VY] = y; + mQ[VZ] = z; + mQ[VS] = w; + + //RN: don't normalize this case as its used mainly for temporaries during calculations + //normalize(); + /* + F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]); + mag -= 1.f; + mag = fabs(mag); + llassert(mag < 10.f*FP_MAG_THRESHOLD); + */ +} + +inline LLQuaternion::LLQuaternion(const F32 *q) +{ + mQ[VX] = q[VX]; + mQ[VY] = q[VY]; + mQ[VZ] = q[VZ]; + mQ[VS] = q[VW]; + + normalize(); + /* + F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]); + mag -= 1.f; + mag = fabs(mag); + llassert(mag < FP_MAG_THRESHOLD); + */ +} + + +inline void LLQuaternion::loadIdentity() +{ + mQ[VX] = 0.0f; + mQ[VY] = 0.0f; + mQ[VZ] = 0.0f; + mQ[VW] = 1.0f; +} + +inline bool LLQuaternion::isEqualEps(const LLQuaternion &quat, F32 epsilon) const +{ + return ( fabs(mQ[VX] - quat.mQ[VX]) < epsilon + && fabs(mQ[VY] - quat.mQ[VY]) < epsilon + && fabs(mQ[VZ] - quat.mQ[VZ]) < epsilon + && fabs(mQ[VS] - quat.mQ[VS]) < epsilon ); +} + +inline bool LLQuaternion::isNotEqualEps(const LLQuaternion &quat, F32 epsilon) const +{ + return ( fabs(mQ[VX] - quat.mQ[VX]) > epsilon + || fabs(mQ[VY] - quat.mQ[VY]) > epsilon + || fabs(mQ[VZ] - quat.mQ[VZ]) > epsilon + || fabs(mQ[VS] - quat.mQ[VS]) > epsilon ); +} + +inline const LLQuaternion& LLQuaternion::set(F32 x, F32 y, F32 z, F32 w) +{ + mQ[VX] = x; + mQ[VY] = y; + mQ[VZ] = z; + mQ[VS] = w; + normalize(); + return (*this); +} + +inline const LLQuaternion& LLQuaternion::set(const LLQuaternion &quat) +{ + mQ[VX] = quat.mQ[VX]; + mQ[VY] = quat.mQ[VY]; + mQ[VZ] = quat.mQ[VZ]; + mQ[VW] = quat.mQ[VW]; + normalize(); + return (*this); +} + +inline const LLQuaternion& LLQuaternion::set(const F32 *q) +{ + mQ[VX] = q[VX]; + mQ[VY] = q[VY]; + mQ[VZ] = q[VZ]; + mQ[VS] = q[VW]; + normalize(); + return (*this); +} + + +// deprecated +inline const LLQuaternion& LLQuaternion::setQuatInit(F32 x, F32 y, F32 z, F32 w) +{ + mQ[VX] = x; + mQ[VY] = y; + mQ[VZ] = z; + mQ[VS] = w; + normalize(); + return (*this); +} + +// deprecated +inline const LLQuaternion& LLQuaternion::setQuat(const LLQuaternion &quat) +{ + mQ[VX] = quat.mQ[VX]; + mQ[VY] = quat.mQ[VY]; + mQ[VZ] = quat.mQ[VZ]; + mQ[VW] = quat.mQ[VW]; + normalize(); + return (*this); +} + +// deprecated +inline const LLQuaternion& LLQuaternion::setQuat(const F32 *q) +{ + mQ[VX] = q[VX]; + mQ[VY] = q[VY]; + mQ[VZ] = q[VZ]; + mQ[VS] = q[VW]; + normalize(); + return (*this); +} + +inline void LLQuaternion::getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const +{ + F32 v = sqrtf(mQ[VX] * mQ[VX] + mQ[VY] * mQ[VY] + mQ[VZ] * mQ[VZ]); // length of the vector-component + if (v > FP_MAG_THRESHOLD) + { + F32 oomag = 1.0f / v; + F32 w = mQ[VW]; + if (w < 0.0f) + { + w = -w; // make VW positive + oomag = -oomag; // invert the axis + } + *x = mQ[VX] * oomag; // normalize the axis + *y = mQ[VY] * oomag; + *z = mQ[VZ] * oomag; + *angle = 2.0f * atan2f(v, w); // get the angle + } + else + { + *angle = 0.0f; // no rotation + *x = 0.0f; // around some dummy axis + *y = 0.0f; + *z = 1.0f; + } +} + +inline const LLQuaternion& LLQuaternion::conjugate() +{ + mQ[VX] *= -1.f; + mQ[VY] *= -1.f; + mQ[VZ] *= -1.f; + return (*this); +} + +inline const LLQuaternion& LLQuaternion::conjQuat() +{ + mQ[VX] *= -1.f; + mQ[VY] *= -1.f; + mQ[VZ] *= -1.f; + return (*this); +} + +// Transpose +inline const LLQuaternion& LLQuaternion::transpose() +{ + mQ[VX] *= -1.f; + mQ[VY] *= -1.f; + mQ[VZ] *= -1.f; + return (*this); +} + +// deprecated +inline const LLQuaternion& LLQuaternion::transQuat() +{ + mQ[VX] *= -1.f; + mQ[VY] *= -1.f; + mQ[VZ] *= -1.f; + return (*this); +} + + +inline LLQuaternion operator+(const LLQuaternion &a, const LLQuaternion &b) +{ + return LLQuaternion( + a.mQ[VX] + b.mQ[VX], + a.mQ[VY] + b.mQ[VY], + a.mQ[VZ] + b.mQ[VZ], + a.mQ[VW] + b.mQ[VW] ); +} + + +inline LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b) +{ + return LLQuaternion( + a.mQ[VX] - b.mQ[VX], + a.mQ[VY] - b.mQ[VY], + a.mQ[VZ] - b.mQ[VZ], + a.mQ[VW] - b.mQ[VW] ); +} + + +inline LLQuaternion operator-(const LLQuaternion &a) +{ + return LLQuaternion( + -a.mQ[VX], + -a.mQ[VY], + -a.mQ[VZ], + -a.mQ[VW] ); +} + + +inline LLQuaternion operator*(F32 a, const LLQuaternion &q) +{ + return LLQuaternion( + a * q.mQ[VX], + a * q.mQ[VY], + a * q.mQ[VZ], + a * q.mQ[VW] ); +} + + +inline LLQuaternion operator*(const LLQuaternion &q, F32 a) +{ + return LLQuaternion( + a * q.mQ[VX], + a * q.mQ[VY], + a * q.mQ[VZ], + a * q.mQ[VW] ); +} + +inline LLQuaternion operator~(const LLQuaternion &a) +{ + LLQuaternion q(a); + q.conjQuat(); + return q; +} + +inline bool LLQuaternion::operator==(const LLQuaternion &b) const +{ + return ( (mQ[VX] == b.mQ[VX]) + &&(mQ[VY] == b.mQ[VY]) + &&(mQ[VZ] == b.mQ[VZ]) + &&(mQ[VS] == b.mQ[VS])); +} + +inline bool LLQuaternion::operator!=(const LLQuaternion &b) const +{ + return ( (mQ[VX] != b.mQ[VX]) + ||(mQ[VY] != b.mQ[VY]) + ||(mQ[VZ] != b.mQ[VZ]) + ||(mQ[VS] != b.mQ[VS])); +} + +inline const LLQuaternion& operator*=(LLQuaternion &a, const LLQuaternion &b) +{ +#if 1 + LLQuaternion q( + b.mQ[3] * a.mQ[0] + b.mQ[0] * a.mQ[3] + b.mQ[1] * a.mQ[2] - b.mQ[2] * a.mQ[1], + b.mQ[3] * a.mQ[1] + b.mQ[1] * a.mQ[3] + b.mQ[2] * a.mQ[0] - b.mQ[0] * a.mQ[2], + b.mQ[3] * a.mQ[2] + b.mQ[2] * a.mQ[3] + b.mQ[0] * a.mQ[1] - b.mQ[1] * a.mQ[0], + b.mQ[3] * a.mQ[3] - b.mQ[0] * a.mQ[0] - b.mQ[1] * a.mQ[1] - b.mQ[2] * a.mQ[2] + ); + a = q; +#else + a = a * b; +#endif + return a; +} + +const F32 ONE_PART_IN_A_MILLION = 0.000001f; + +inline F32 LLQuaternion::normalize() +{ + F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]); + + if (mag > FP_MAG_THRESHOLD) + { + // Floating point error can prevent some quaternions from achieving + // exact unity length. When trying to renormalize such quaternions we + // can oscillate between multiple quantized states. To prevent such + // drifts we only renomalize if the length is far enough from unity. + if (fabs(1.f - mag) > ONE_PART_IN_A_MILLION) + { + F32 oomag = 1.f/mag; + mQ[VX] *= oomag; + mQ[VY] *= oomag; + mQ[VZ] *= oomag; + mQ[VS] *= oomag; + } + } + else + { + // we were given a very bad quaternion so we set it to identity + mQ[VX] = 0.f; + mQ[VY] = 0.f; + mQ[VZ] = 0.f; + mQ[VS] = 1.f; + } + + return mag; +} + +// deprecated +inline F32 LLQuaternion::normQuat() +{ + F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]); + + if (mag > FP_MAG_THRESHOLD) + { + if (fabs(1.f - mag) > ONE_PART_IN_A_MILLION) + { + // only renormalize if length not close enough to 1.0 already + F32 oomag = 1.f/mag; + mQ[VX] *= oomag; + mQ[VY] *= oomag; + mQ[VZ] *= oomag; + mQ[VS] *= oomag; + } + } + else + { + mQ[VX] = 0.f; + mQ[VY] = 0.f; + mQ[VZ] = 0.f; + mQ[VS] = 1.f; + } + + return mag; +} + +LLQuaternion::Order StringToOrder( const char *str ); + +// Some notes about Quaternions + +// What is a Quaternion? +// --------------------- +// A quaternion is a point in 4-dimensional complex space. +// Q = { Qx, Qy, Qz, Qw } +// +// +// Why Quaternions? +// ---------------- +// The set of quaternions that make up the the 4-D unit sphere +// can be mapped to the set of all rotations in 3-D space. Sometimes +// it is easier to describe/manipulate rotations in quaternion space +// than rotation-matrix space. +// +// +// How Quaternions? +// ---------------- +// In order to take advantage of quaternions we need to know how to +// go from rotation-matricies to quaternions and back. We also have +// to agree what variety of rotations we're generating. +// +// Consider the equation... v' = v * R +// +// There are two ways to think about rotations of vectors. +// 1) v' is the same vector in a different reference frame +// 2) v' is a new vector in the same reference frame +// +// bookmark -- which way are we using? +// +// +// Quaternion from Angle-Axis: +// --------------------------- +// Suppose we wanted to represent a rotation of some angle (theta) +// about some axis ({Ax, Ay, Az})... +// +// axis of rotation = {Ax, Ay, Az} +// angle_of_rotation = theta +// +// s = sin(0.5 * theta) +// c = cos(0.5 * theta) +// Q = { s * Ax, s * Ay, s * Az, c } +// +// +// 3x3 Matrix from Quaternion +// -------------------------- +// +// | | +// | 1 - 2 * (y^2 + z^2) 2 * (x * y + z * w) 2 * (y * w - x * z) | +// | | +// M = | 2 * (x * y - z * w) 1 - 2 * (x^2 + z^2) 2 * (y * z + x * w) | +// | | +// | 2 * (x * z + y * w) 2 * (y * z - x * w) 1 - 2 * (x^2 + y^2) | +// | | + +#endif diff --git a/indra/llmath/llrect.h b/indra/llmath/llrect.h index 5ec3ca0dff..317578da06 100644 --- a/indra/llmath/llrect.h +++ b/indra/llmath/llrect.h @@ -1,298 +1,298 @@ -/**
- * @file llrect.h
- * @brief A rectangle in GL coordinates, with bottom,left = 0,0
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-
-#ifndef LL_LLRECT_H
-#define LL_LLRECT_H
-
-#include <iostream>
-#include "llmath.h"
-#include "llsd.h"
-
-// Top > Bottom due to GL coords
-template <class Type> class LLRectBase
-{
-public:
- typedef Type tCoordType;
- Type mLeft;
- Type mTop;
- Type mRight;
- Type mBottom;
-
- // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
- Type getWidth() const { return mRight - mLeft; }
- Type getHeight() const { return mTop - mBottom; }
- Type getCenterX() const { return (mLeft + mRight) / 2; }
- Type getCenterY() const { return (mTop + mBottom) / 2; }
-
- LLRectBase(): mLeft(0), mTop(0), mRight(0), mBottom(0)
- {}
-
- LLRectBase(const LLRectBase &r):
- mLeft(r.mLeft), mTop(r.mTop), mRight(r.mRight), mBottom(r.mBottom)
- {}
-
- LLRectBase(Type left, Type top, Type right, Type bottom):
- mLeft(left), mTop(top), mRight(right), mBottom(bottom)
- {}
-
- explicit LLRectBase(const LLSD& sd)
- {
- setValue(sd);
- }
-
- void setValue(const LLSD& sd)
- {
- mLeft = (Type)sd[0].asInteger();
- mTop = (Type)sd[1].asInteger();
- mRight = (Type)sd[2].asInteger();
- mBottom = (Type)sd[3].asInteger();
- }
-
- LLSD getValue() const
- {
- LLSD ret;
- ret[0] = mLeft;
- ret[1] = mTop;
- ret[2] = mRight;
- ret[3] = mBottom;
- return ret;
- }
-
- // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
- bool pointInRect(const Type x, const Type y) const
- {
- return mLeft <= x && x < mRight &&
- mBottom <= y && y < mTop;
- }
-
- //// Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
- bool localPointInRect(const Type x, const Type y) const
- {
- return 0 <= x && x < getWidth() &&
- 0 <= y && y < getHeight();
- }
-
- void clampPointToRect(Type& x, Type& y)
- {
- x = llclamp(x, mLeft, mRight);
- y = llclamp(y, mBottom, mTop);
- }
-
- void clipPointToRect(const Type start_x, const Type start_y, Type& end_x, Type& end_y)
- {
- if (!pointInRect(start_x, start_y))
- {
- return;
- }
- Type clip_x = 0;
- Type clip_y = 0;
- Type delta_x = end_x - start_x;
- Type delta_y = end_y - start_y;
- if (end_x > mRight) clip_x = end_x - mRight;
- if (end_x < mLeft) clip_x = end_x - mLeft;
- if (end_y > mTop) clip_y = end_y - mTop;
- if (end_y < mBottom) clip_y = end_y - mBottom;
- // clip_? and delta_? should have same sign, since starting point is in rect
- // so ratios will be positive
- F32 ratio_x = 0;
- F32 ratio_y = 0;
- if (delta_x != 0) ratio_x = ((F32)clip_x / (F32)delta_x);
- if (delta_y != 0) ratio_y = ((F32)clip_y / (F32)delta_y);
- if (ratio_x > ratio_y)
- {
- // clip along x direction
- end_x -= (Type)(clip_x);
- end_y -= (Type)(delta_y * ratio_x);
- }
- else
- {
- // clip along y direction
- end_x -= (Type)(delta_x * ratio_y);
- end_y -= (Type)clip_y;
- }
- }
-
- // Note: Does NOT follow GL_QUAD conventions: the top and right edges ARE considered part of the rect
- // returns true if any part of rect is is inside this LLRect
- bool overlaps(const LLRectBase& rect) const
- {
- return !(mLeft > rect.mRight
- || mRight < rect.mLeft
- || mBottom > rect.mTop
- || mTop < rect.mBottom);
- }
-
- bool contains(const LLRectBase& rect) const
- {
- return mLeft <= rect.mLeft
- && mRight >= rect.mRight
- && mBottom <= rect.mBottom
- && mTop >= rect.mTop;
- }
-
- LLRectBase& set(Type left, Type top, Type right, Type bottom)
- {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- return *this;
- }
-
- // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
- LLRectBase& setOriginAndSize( Type left, Type bottom, Type width, Type height)
- {
- mLeft = left;
- mTop = bottom + height;
- mRight = left + width;
- mBottom = bottom;
- return *this;
- }
-
- // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
- LLRectBase& setLeftTopAndSize( Type left, Type top, Type width, Type height)
- {
- mLeft = left;
- mTop = top;
- mRight = left + width;
- mBottom = top - height;
- return *this;
- }
-
- LLRectBase& setCenterAndSize(Type x, Type y, Type width, Type height)
- {
- // width and height could be odd, so favor top, right with extra pixel
- mLeft = x - width/2;
- mBottom = y - height/2;
- mTop = mBottom + height;
- mRight = mLeft + width;
- return *this;
- }
-
-
- LLRectBase& translate(Type horiz, Type vertical)
- {
- mLeft += horiz;
- mRight += horiz;
- mTop += vertical;
- mBottom += vertical;
- return *this;
- }
-
- LLRectBase& stretch( Type dx, Type dy)
- {
- mLeft -= dx;
- mRight += dx;
- mTop += dy;
- mBottom -= dy;
- return makeValid();
- }
-
- LLRectBase& stretch( Type delta )
- {
- stretch(delta, delta);
- return *this;
- }
-
- LLRectBase& makeValid()
- {
- mLeft = llmin(mLeft, mRight);
- mBottom = llmin(mBottom, mTop);
- return *this;
- }
-
- bool isValid() const
- {
- return mLeft <= mRight && mBottom <= mTop;
- }
-
- bool isEmpty() const
- {
- return mLeft == mRight || mBottom == mTop;
- }
-
- bool notEmpty() const
- {
- return !isEmpty();
- }
-
- void unionWith(const LLRectBase &other)
- {
- mLeft = llmin(mLeft, other.mLeft);
- mRight = llmax(mRight, other.mRight);
- mBottom = llmin(mBottom, other.mBottom);
- mTop = llmax(mTop, other.mTop);
- }
-
- void intersectWith(const LLRectBase &other)
- {
- mLeft = llmax(mLeft, other.mLeft);
- mRight = llmin(mRight, other.mRight);
- mBottom = llmax(mBottom, other.mBottom);
- mTop = llmin(mTop, other.mTop);
- if (mLeft > mRight)
- {
- mLeft = mRight;
- }
- if (mBottom > mTop)
- {
- mBottom = mTop;
- }
- }
-
- friend std::ostream &operator<<(std::ostream &s, const LLRectBase &rect)
- {
- s << "{ L " << rect.mLeft << " B " << rect.mBottom
- << " W " << rect.getWidth() << " H " << rect.getHeight() << " }";
- return s;
- }
-
- bool operator==(const LLRectBase &b) const
- {
- return ((mLeft == b.mLeft) &&
- (mTop == b.mTop) &&
- (mRight == b.mRight) &&
- (mBottom == b.mBottom));
- }
-
- bool operator!=(const LLRectBase &b) const
- {
- return ((mLeft != b.mLeft) ||
- (mTop != b.mTop) ||
- (mRight != b.mRight) ||
- (mBottom != b.mBottom));
- }
-
- static LLRectBase<Type> null;
-};
-
-template <class Type> LLRectBase<Type> LLRectBase<Type>::null(0,0,0,0);
-
-typedef LLRectBase<S32> LLRect;
-typedef LLRectBase<F32> LLRectf;
-
-#endif
+/** + * @file llrect.h + * @brief A rectangle in GL coordinates, with bottom,left = 0,0 + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#ifndef LL_LLRECT_H +#define LL_LLRECT_H + +#include <iostream> +#include "llmath.h" +#include "llsd.h" + +// Top > Bottom due to GL coords +template <class Type> class LLRectBase +{ +public: + typedef Type tCoordType; + Type mLeft; + Type mTop; + Type mRight; + Type mBottom; + + // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect + Type getWidth() const { return mRight - mLeft; } + Type getHeight() const { return mTop - mBottom; } + Type getCenterX() const { return (mLeft + mRight) / 2; } + Type getCenterY() const { return (mTop + mBottom) / 2; } + + LLRectBase(): mLeft(0), mTop(0), mRight(0), mBottom(0) + {} + + LLRectBase(const LLRectBase &r): + mLeft(r.mLeft), mTop(r.mTop), mRight(r.mRight), mBottom(r.mBottom) + {} + + LLRectBase(Type left, Type top, Type right, Type bottom): + mLeft(left), mTop(top), mRight(right), mBottom(bottom) + {} + + explicit LLRectBase(const LLSD& sd) + { + setValue(sd); + } + + void setValue(const LLSD& sd) + { + mLeft = (Type)sd[0].asInteger(); + mTop = (Type)sd[1].asInteger(); + mRight = (Type)sd[2].asInteger(); + mBottom = (Type)sd[3].asInteger(); + } + + LLSD getValue() const + { + LLSD ret; + ret[0] = mLeft; + ret[1] = mTop; + ret[2] = mRight; + ret[3] = mBottom; + return ret; + } + + // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect + bool pointInRect(const Type x, const Type y) const + { + return mLeft <= x && x < mRight && + mBottom <= y && y < mTop; + } + + //// Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect + bool localPointInRect(const Type x, const Type y) const + { + return 0 <= x && x < getWidth() && + 0 <= y && y < getHeight(); + } + + void clampPointToRect(Type& x, Type& y) + { + x = llclamp(x, mLeft, mRight); + y = llclamp(y, mBottom, mTop); + } + + void clipPointToRect(const Type start_x, const Type start_y, Type& end_x, Type& end_y) + { + if (!pointInRect(start_x, start_y)) + { + return; + } + Type clip_x = 0; + Type clip_y = 0; + Type delta_x = end_x - start_x; + Type delta_y = end_y - start_y; + if (end_x > mRight) clip_x = end_x - mRight; + if (end_x < mLeft) clip_x = end_x - mLeft; + if (end_y > mTop) clip_y = end_y - mTop; + if (end_y < mBottom) clip_y = end_y - mBottom; + // clip_? and delta_? should have same sign, since starting point is in rect + // so ratios will be positive + F32 ratio_x = 0; + F32 ratio_y = 0; + if (delta_x != 0) ratio_x = ((F32)clip_x / (F32)delta_x); + if (delta_y != 0) ratio_y = ((F32)clip_y / (F32)delta_y); + if (ratio_x > ratio_y) + { + // clip along x direction + end_x -= (Type)(clip_x); + end_y -= (Type)(delta_y * ratio_x); + } + else + { + // clip along y direction + end_x -= (Type)(delta_x * ratio_y); + end_y -= (Type)clip_y; + } + } + + // Note: Does NOT follow GL_QUAD conventions: the top and right edges ARE considered part of the rect + // returns true if any part of rect is is inside this LLRect + bool overlaps(const LLRectBase& rect) const + { + return !(mLeft > rect.mRight + || mRight < rect.mLeft + || mBottom > rect.mTop + || mTop < rect.mBottom); + } + + bool contains(const LLRectBase& rect) const + { + return mLeft <= rect.mLeft + && mRight >= rect.mRight + && mBottom <= rect.mBottom + && mTop >= rect.mTop; + } + + LLRectBase& set(Type left, Type top, Type right, Type bottom) + { + mLeft = left; + mTop = top; + mRight = right; + mBottom = bottom; + return *this; + } + + // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect + LLRectBase& setOriginAndSize( Type left, Type bottom, Type width, Type height) + { + mLeft = left; + mTop = bottom + height; + mRight = left + width; + mBottom = bottom; + return *this; + } + + // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect + LLRectBase& setLeftTopAndSize( Type left, Type top, Type width, Type height) + { + mLeft = left; + mTop = top; + mRight = left + width; + mBottom = top - height; + return *this; + } + + LLRectBase& setCenterAndSize(Type x, Type y, Type width, Type height) + { + // width and height could be odd, so favor top, right with extra pixel + mLeft = x - width/2; + mBottom = y - height/2; + mTop = mBottom + height; + mRight = mLeft + width; + return *this; + } + + + LLRectBase& translate(Type horiz, Type vertical) + { + mLeft += horiz; + mRight += horiz; + mTop += vertical; + mBottom += vertical; + return *this; + } + + LLRectBase& stretch( Type dx, Type dy) + { + mLeft -= dx; + mRight += dx; + mTop += dy; + mBottom -= dy; + return makeValid(); + } + + LLRectBase& stretch( Type delta ) + { + stretch(delta, delta); + return *this; + } + + LLRectBase& makeValid() + { + mLeft = llmin(mLeft, mRight); + mBottom = llmin(mBottom, mTop); + return *this; + } + + bool isValid() const + { + return mLeft <= mRight && mBottom <= mTop; + } + + bool isEmpty() const + { + return mLeft == mRight || mBottom == mTop; + } + + bool notEmpty() const + { + return !isEmpty(); + } + + void unionWith(const LLRectBase &other) + { + mLeft = llmin(mLeft, other.mLeft); + mRight = llmax(mRight, other.mRight); + mBottom = llmin(mBottom, other.mBottom); + mTop = llmax(mTop, other.mTop); + } + + void intersectWith(const LLRectBase &other) + { + mLeft = llmax(mLeft, other.mLeft); + mRight = llmin(mRight, other.mRight); + mBottom = llmax(mBottom, other.mBottom); + mTop = llmin(mTop, other.mTop); + if (mLeft > mRight) + { + mLeft = mRight; + } + if (mBottom > mTop) + { + mBottom = mTop; + } + } + + friend std::ostream &operator<<(std::ostream &s, const LLRectBase &rect) + { + s << "{ L " << rect.mLeft << " B " << rect.mBottom + << " W " << rect.getWidth() << " H " << rect.getHeight() << " }"; + return s; + } + + bool operator==(const LLRectBase &b) const + { + return ((mLeft == b.mLeft) && + (mTop == b.mTop) && + (mRight == b.mRight) && + (mBottom == b.mBottom)); + } + + bool operator!=(const LLRectBase &b) const + { + return ((mLeft != b.mLeft) || + (mTop != b.mTop) || + (mRight != b.mRight) || + (mBottom != b.mBottom)); + } + + static LLRectBase<Type> null; +}; + +template <class Type> LLRectBase<Type> LLRectBase<Type>::null(0,0,0,0); + +typedef LLRectBase<S32> LLRect; +typedef LLRectBase<F32> LLRectf; + +#endif diff --git a/indra/llmath/llsphere.cpp b/indra/llmath/llsphere.cpp index b68743ea6e..89349af6c8 100644 --- a/indra/llmath/llsphere.cpp +++ b/indra/llmath/llsphere.cpp @@ -1,370 +1,370 @@ -/**
- * @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 );
- S32 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;
-}
-
-
+/** + * @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 ); + S32 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 index 413a307543..cb923dcd3c 100644 --- a/indra/llmath/llsphere.h +++ b/indra/llmath/llsphere.h @@ -1,77 +1,77 @@ -// 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
+// 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/lltreenode.h b/indra/llmath/lltreenode.h index 5875e9e8e0..f648a114ca 100644 --- a/indra/llmath/lltreenode.h +++ b/indra/llmath/lltreenode.h @@ -1,125 +1,125 @@ -/**
- * @file lltreenode.h
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTREENODE_H
-#define LL_LLTREENODE_H
-
-#include "stdtypes.h"
-#include "xform.h"
-#include "llpointer.h"
-#include "llrefcount.h"
-
-#include <vector>
-
-template <class T> class LLTreeNode;
-template <class T> class LLTreeTraveler;
-template <class T> class LLTreeListener;
-
-template <class T>
-class LLTreeListener: public LLRefCount
-{
-public:
- virtual void handleInsertion(const LLTreeNode<T>* node, T* data) = 0;
- virtual void handleRemoval(const LLTreeNode<T>* node, T* data) = 0;
- virtual void handleDestruction(const LLTreeNode<T>* node) = 0;
- virtual void handleStateChange(const LLTreeNode<T>* node) = 0;
-};
-
-template <class T>
-class LLTreeNode
-{
-public:
- virtual ~LLTreeNode();
-
- virtual bool insert(T* data);
- virtual bool remove(T* data);
- virtual void notifyRemoval(T* data);
- virtual U32 hasListeners() const { return !mListeners.empty(); }
- virtual U32 getListenerCount() const { return mListeners.size(); }
- virtual LLTreeListener<T>* getListener(U32 index) const
- {
- if (index < mListeners.size())
- {
- return mListeners[index];
- }
- return NULL;
- }
- virtual void addListener(LLTreeListener<T>* listener) { mListeners.push_back(listener); }
-
-protected:
- void destroyListeners()
- {
- for (U32 i = 0; i < mListeners.size(); i++)
- {
- mListeners[i]->handleDestruction(this);
- }
- mListeners.clear();
- }
-
-public:
- std::vector<LLPointer<LLTreeListener<T> > > mListeners;
-};
-
-template <class T>
-class LLTreeTraveler
-{
-public:
- virtual ~LLTreeTraveler() { };
- virtual void traverse(const LLTreeNode<T>* node) = 0;
- virtual void visit(const LLTreeNode<T>* node) = 0;
-};
-
-template <class T>
-LLTreeNode<T>::~LLTreeNode()
-{
- destroyListeners();
-};
-
-template <class T>
-bool LLTreeNode<T>::insert(T* data)
-{
- for (U32 i = 0; i < mListeners.size(); i++)
- {
- mListeners[i]->handleInsertion(this, data);
- }
- return true;
-};
-
-template <class T>
-bool LLTreeNode<T>::remove(T* data)
-{
- return true;
-};
-
-template <class T>
-void LLTreeNode<T>::notifyRemoval(T* data)
-{
- for (U32 i = 0; i < mListeners.size(); i++)
- {
- mListeners[i]->handleRemoval(this, data);
- }
-}
-
-#endif
+/** + * @file lltreenode.h + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLTREENODE_H +#define LL_LLTREENODE_H + +#include "stdtypes.h" +#include "xform.h" +#include "llpointer.h" +#include "llrefcount.h" + +#include <vector> + +template <class T> class LLTreeNode; +template <class T> class LLTreeTraveler; +template <class T> class LLTreeListener; + +template <class T> +class LLTreeListener: public LLRefCount +{ +public: + virtual void handleInsertion(const LLTreeNode<T>* node, T* data) = 0; + virtual void handleRemoval(const LLTreeNode<T>* node, T* data) = 0; + virtual void handleDestruction(const LLTreeNode<T>* node) = 0; + virtual void handleStateChange(const LLTreeNode<T>* node) = 0; +}; + +template <class T> +class LLTreeNode +{ +public: + virtual ~LLTreeNode(); + + virtual bool insert(T* data); + virtual bool remove(T* data); + virtual void notifyRemoval(T* data); + virtual U32 hasListeners() const { return !mListeners.empty(); } + virtual U32 getListenerCount() const { return mListeners.size(); } + virtual LLTreeListener<T>* getListener(U32 index) const + { + if (index < mListeners.size()) + { + return mListeners[index]; + } + return NULL; + } + virtual void addListener(LLTreeListener<T>* listener) { mListeners.push_back(listener); } + +protected: + void destroyListeners() + { + for (U32 i = 0; i < mListeners.size(); i++) + { + mListeners[i]->handleDestruction(this); + } + mListeners.clear(); + } + +public: + std::vector<LLPointer<LLTreeListener<T> > > mListeners; +}; + +template <class T> +class LLTreeTraveler +{ +public: + virtual ~LLTreeTraveler() { }; + virtual void traverse(const LLTreeNode<T>* node) = 0; + virtual void visit(const LLTreeNode<T>* node) = 0; +}; + +template <class T> +LLTreeNode<T>::~LLTreeNode() +{ + destroyListeners(); +}; + +template <class T> +bool LLTreeNode<T>::insert(T* data) +{ + for (U32 i = 0; i < mListeners.size(); i++) + { + mListeners[i]->handleInsertion(this, data); + } + return true; +}; + +template <class T> +bool LLTreeNode<T>::remove(T* data) +{ + return true; +}; + +template <class T> +void LLTreeNode<T>::notifyRemoval(T* data) +{ + for (U32 i = 0; i < mListeners.size(); i++) + { + mListeners[i]->handleRemoval(this, data); + } +} + +#endif diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index f9f4aea77b..e6001626f3 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1,7304 +1,7304 @@ -/**
- * @file llvolume.cpp
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llmemory.h"
-#include "llmath.h"
-
-#include <set>
-#if !LL_WINDOWS
-#include <stdint.h>
-#endif
-#include <cmath>
-#include <unordered_map>
-
-#include "llerror.h"
-
-#include "llvolumemgr.h"
-#include "v2math.h"
-#include "v3math.h"
-#include "v4math.h"
-#include "m4math.h"
-#include "m3math.h"
-#include "llmatrix3a.h"
-#include "lloctree.h"
-#include "llvolume.h"
-#include "llvolumeoctree.h"
-#include "llstl.h"
-#include "llsdserialize.h"
-#include "llvector4a.h"
-#include "llmatrix4a.h"
-#include "llmeshoptimizer.h"
-#include "lltimer.h"
-
-#include "mikktspace/mikktspace.h"
-#include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file
-
-#include "meshoptimizer/meshoptimizer.h"
-
-#define DEBUG_SILHOUETTE_BINORMALS 0
-#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
-#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette
-
-constexpr F32 MIN_CUT_DELTA = 0.02f;
-
-constexpr F32 HOLLOW_MIN = 0.f;
-constexpr F32 HOLLOW_MAX = 0.95f;
-constexpr F32 HOLLOW_MAX_SQUARE = 0.7f;
-
-constexpr F32 TWIST_MIN = -1.f;
-constexpr F32 TWIST_MAX = 1.f;
-
-constexpr F32 RATIO_MIN = 0.f;
-constexpr F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper
-
-constexpr F32 HOLE_X_MIN= 0.05f;
-constexpr F32 HOLE_X_MAX= 1.0f;
-
-constexpr F32 HOLE_Y_MIN= 0.05f;
-constexpr F32 HOLE_Y_MAX= 0.5f;
-
-constexpr F32 SHEAR_MIN = -0.5f;
-constexpr F32 SHEAR_MAX = 0.5f;
-
-constexpr F32 REV_MIN = 1.f;
-constexpr F32 REV_MAX = 4.f;
-
-constexpr F32 TAPER_MIN = -1.f;
-constexpr F32 TAPER_MAX = 1.f;
-
-constexpr F32 SKEW_MIN = -0.95f;
-constexpr F32 SKEW_MAX = 0.95f;
-
-constexpr F32 SCULPT_MIN_AREA = 0.002f;
-constexpr S32 SCULPT_MIN_AREA_DETAIL = 1;
-
-bool gDebugGL = false; // See settings.xml "RenderDebugGL"
-
-bool check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)
-{
- LLVector3 test = (pt2-pt1)%(pt3-pt2);
-
- //answer
- if(test * norm < 0)
- {
- return false;
- }
- else
- {
- return true;
- }
-}
-
-bool LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
-{
- return LLLineSegmentBoxIntersect(start.mV, end.mV, center.mV, size.mV);
-}
-
-bool LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size)
-{
- F32 fAWdU[3]{};
- F32 dir[3]{};
- F32 diff[3]{};
-
- for (U32 i = 0; i < 3; i++)
- {
- dir[i] = 0.5f * (end[i] - start[i]);
- diff[i] = (0.5f * (end[i] + start[i])) - center[i];
- fAWdU[i] = fabsf(dir[i]);
- if(fabsf(diff[i])>size[i] + fAWdU[i]) return false;
- }
-
- float f;
- f = dir[1] * diff[2] - dir[2] * diff[1]; if(fabsf(f)>size[1]*fAWdU[2] + size[2]*fAWdU[1]) return false;
- f = dir[2] * diff[0] - dir[0] * diff[2]; if(fabsf(f)>size[0]*fAWdU[2] + size[2]*fAWdU[0]) return false;
- f = dir[0] * diff[1] - dir[1] * diff[0]; if(fabsf(f)>size[0]*fAWdU[1] + size[1]*fAWdU[0]) return false;
-
- return true;
-}
-
-// Finds tangent vec based on three vertices with texture coordinates.
-// Fills in dummy values if the triangle has degenerate texture coordinates.
-void calc_tangent_from_triangle(
- LLVector4a& normal,
- LLVector4a& tangent_out,
- const LLVector4a& v1,
- const LLVector2& w1,
- const LLVector4a& v2,
- const LLVector2& w2,
- const LLVector4a& v3,
- const LLVector2& w3)
-{
- const F32* v1ptr = v1.getF32ptr();
- const F32* v2ptr = v2.getF32ptr();
- const F32* v3ptr = v3.getF32ptr();
-
- float x1 = v2ptr[0] - v1ptr[0];
- float x2 = v3ptr[0] - v1ptr[0];
- float y1 = v2ptr[1] - v1ptr[1];
- float y2 = v3ptr[1] - v1ptr[1];
- float z1 = v2ptr[2] - v1ptr[2];
- float z2 = v3ptr[2] - v1ptr[2];
-
- float s1 = w2.mV[0] - w1.mV[0];
- float s2 = w3.mV[0] - w1.mV[0];
- float t1 = w2.mV[1] - w1.mV[1];
- float t2 = w3.mV[1] - w1.mV[1];
-
- F32 rd = s1*t2-s2*t1;
-
- float r = ((rd*rd) > FLT_EPSILON) ? (1.0f / rd)
- : ((rd > 0.0f) ? 1024.f : -1024.f); //some made up large ratio for division by zero
-
- llassert(llfinite(r));
- llassert(!llisnan(r));
-
- LLVector4a sdir(
- (t2 * x1 - t1 * x2) * r,
- (t2 * y1 - t1 * y2) * r,
- (t2 * z1 - t1 * z2) * r);
-
- LLVector4a tdir(
- (s1 * x2 - s2 * x1) * r,
- (s1 * y2 - s2 * y1) * r,
- (s1 * z2 - s2 * z1) * r);
-
- LLVector4a n = normal;
- LLVector4a t = sdir;
-
- LLVector4a ncrosst;
- ncrosst.setCross3(n,t);
-
- // Gram-Schmidt orthogonalize
- n.mul(n.dot3(t).getF32());
-
- LLVector4a tsubn;
- tsubn.setSub(t,n);
-
- if (tsubn.dot3(tsubn).getF32() > F_APPROXIMATELY_ZERO)
- {
- tsubn.normalize3fast_checked();
-
- // Calculate handedness
- F32 handedness = ncrosst.dot3(tdir).getF32() < 0.f ? -1.f : 1.f;
-
- tsubn.getF32ptr()[3] = handedness;
-
- tangent_out = tsubn;
- }
- else
- {
- // degenerate, make up a value
- //
- tangent_out.set(0,0,1,1);
- }
-
-}
-
-
-// intersect test between triangle vert0, vert1, vert2 and a ray from orig in direction dir.
-// returns true if intersecting and returns barycentric coordinates in intersection_a, intersection_b,
-// and returns the intersection point along dir in intersection_t.
-
-// Moller-Trumbore algorithm
-bool LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
- F32& intersection_a, F32& intersection_b, F32& intersection_t)
-{
-
- /* find vectors for two edges sharing vert0 */
- LLVector4a edge1;
- edge1.setSub(vert1, vert0);
-
- LLVector4a edge2;
- edge2.setSub(vert2, vert0);
-
- /* begin calculating determinant - also used to calculate U parameter */
- LLVector4a pvec;
- pvec.setCross3(dir, edge2);
-
- /* if determinant is near zero, ray lies in plane of triangle */
- LLVector4a det;
- det.setAllDot3(edge1, pvec);
-
- if (det.greaterEqual(LLVector4a::getEpsilon()).getGatheredBits() & 0x7)
- {
- /* calculate distance from vert0 to ray origin */
- LLVector4a tvec;
- tvec.setSub(orig, vert0);
-
- /* calculate U parameter and test bounds */
- LLVector4a u;
- u.setAllDot3(tvec,pvec);
-
- if ((u.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7) &&
- (u.lessEqual(det).getGatheredBits() & 0x7))
- {
- /* prepare to test V parameter */
- LLVector4a qvec;
- qvec.setCross3(tvec, edge1);
-
- /* calculate V parameter and test bounds */
- LLVector4a v;
- v.setAllDot3(dir, qvec);
-
-
- //if (!(v < 0.f || u + v > det))
-
- LLVector4a sum_uv;
- sum_uv.setAdd(u, v);
-
- S32 v_gequal = v.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7;
- S32 sum_lequal = sum_uv.lessEqual(det).getGatheredBits() & 0x7;
-
- if (v_gequal && sum_lequal)
- {
- /* calculate t, scale parameters, ray intersects triangle */
- LLVector4a t;
- t.setAllDot3(edge2,qvec);
-
- t.div(det);
- u.div(det);
- v.div(det);
-
- intersection_a = u[0];
- intersection_b = v[0];
- intersection_t = t[0];
- return true;
- }
- }
- }
-
- return false;
-}
-
-bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
- F32& intersection_a, F32& intersection_b, F32& intersection_t)
-{
- F32 u, v, t;
-
- /* find vectors for two edges sharing vert0 */
- LLVector4a edge1;
- edge1.setSub(vert1, vert0);
-
-
- LLVector4a edge2;
- edge2.setSub(vert2, vert0);
-
- /* begin calculating determinant - also used to calculate U parameter */
- LLVector4a pvec;
- pvec.setCross3(dir, edge2);
-
- /* if determinant is near zero, ray lies in plane of triangle */
- F32 det = edge1.dot3(pvec).getF32();
-
-
- if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO)
- {
- return false;
- }
-
- F32 inv_det = 1.f / det;
-
- /* calculate distance from vert0 to ray origin */
- LLVector4a tvec;
- tvec.setSub(orig, vert0);
-
- /* calculate U parameter and test bounds */
- u = (tvec.dot3(pvec).getF32()) * inv_det;
- if (u < 0.f || u > 1.f)
- {
- return false;
- }
-
- /* prepare to test V parameter */
- tvec.sub(edge1);
-
- /* calculate V parameter and test bounds */
- v = (dir.dot3(tvec).getF32()) * inv_det;
-
- if (v < 0.f || u + v > 1.f)
- {
- return false;
- }
-
- /* calculate t, ray intersects triangle */
- t = (edge2.dot3(tvec).getF32()) * inv_det;
-
- intersection_a = u;
- intersection_b = v;
- intersection_t = t;
-
-
- return true;
-}
-
-class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*>
-{
-public:
- const LLVolumeFace* mFace;
-
- LLVolumeOctreeRebound(const LLVolumeFace* face)
- {
- mFace = face;
- }
-
- virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
- { //this is a depth first traversal, so it's safe to assum all children have complete
- //bounding data
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
-
- LLVector4a& min = node->mExtents[0];
- LLVector4a& max = node->mExtents[1];
-
- if (!branch->isEmpty())
- { //node has data, find AABB that binds data set
- const LLVolumeTriangle* tri = *(branch->getDataBegin());
-
- //initialize min/max to first available vertex
- min = *(tri->mV[0]);
- max = *(tri->mV[0]);
-
- for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter)
- { //for each triangle in node
-
- //stretch by triangles in node
- tri = *iter;
-
- min.setMin(min, *tri->mV[0]);
- min.setMin(min, *tri->mV[1]);
- min.setMin(min, *tri->mV[2]);
-
- max.setMax(max, *tri->mV[0]);
- max.setMax(max, *tri->mV[1]);
- max.setMax(max, *tri->mV[2]);
- }
- }
- else if (branch->getChildCount() > 0)
- { //no data, but child nodes exist
- LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0);
-
- //initialize min/max to extents of first child
- min = child->mExtents[0];
- max = child->mExtents[1];
- }
- else
- {
- llassert(!branch->isLeaf()); // Empty leaf
- }
-
- for (S32 i = 0; i < branch->getChildCount(); ++i)
- { //stretch by child extents
- LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0);
- min.setMin(min, child->mExtents[0]);
- max.setMax(max, child->mExtents[1]);
- }
-
- node->mBounds[0].setAdd(min, max);
- node->mBounds[0].mul(0.5f);
-
- node->mBounds[1].setSub(max,min);
- node->mBounds[1].mul(0.5f);
- }
-};
-
-//-------------------------------------------------------------------
-// statics
-//-------------------------------------------------------------------
-
-
-//----------------------------------------------------
-
-LLProfile::Face* LLProfile::addCap(S16 faceID)
-{
- Face *face = vector_append(mFaces, 1);
-
- face->mIndex = 0;
- face->mCount = mTotal;
- face->mScaleU= 1.0f;
- face->mCap = true;
- face->mFaceID = faceID;
- return face;
-}
-
-LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, bool flat)
-{
- Face *face = vector_append(mFaces, 1);
-
- face->mIndex = i;
- face->mCount = count;
- face->mScaleU= scaleU;
-
- face->mFlat = flat;
- face->mCap = false;
- face->mFaceID = faceID;
- return face;
-}
-
-//static
-S32 LLProfile::getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
-{ // this is basically LLProfile::genNGon stripped down to only the operations that influence the number of points
- S32 np = 0;
-
- // Generate an n-sided "circular" path.
- // 0 is (1,0), and we go counter-clockwise along a circular path from there.
- F32 t, t_step, t_first, t_fraction;
-
- F32 begin = params.getBegin();
- F32 end = params.getEnd();
-
- t_step = 1.0f / sides;
-
- t_first = floor(begin * sides) / (F32)sides;
-
- // pt1 is the first point on the fractional face.
- // Starting t and ang values for the first face
- t = t_first;
-
- // Increment to the next point.
- // pt2 is the end point on the fractional face
- t += t_step;
-
- t_fraction = (begin - t_first)*sides;
-
- // Only use if it's not almost exactly on an edge.
- if (t_fraction < 0.9999f)
- {
- np++;
- }
-
- // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
- while (t < end)
- {
- // Iterate through all the integer steps of t.
- np++;
-
- t += t_step;
- }
-
- t_fraction = (end - (t - t_step))*sides;
-
- // Find the fraction that we need to add to the end point.
- t_fraction = (end - (t - t_step))*sides;
- if (t_fraction > 0.0001f)
- {
- np++;
- }
-
- // If we're sliced, the profile is open.
- if ((end - begin)*ang_scale < 0.99f)
- {
- if (params.getHollow() <= 0)
- {
- // put center point if not hollow.
- np++;
- }
- }
-
- return np;
-}
-
-// What is the bevel parameter used for? - DJS 04/05/02
-// Bevel parameter is currently unused but presumedly would support
-// filleted and chamfered corners
-void LLProfile::genNGon(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
-{
- // Generate an n-sided "circular" path.
- // 0 is (1,0), and we go counter-clockwise along a circular path from there.
- constexpr F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
- F32 scale = 0.5f;
- F32 t, t_step, t_first, t_fraction, ang, ang_step;
- LLVector4a pt1,pt2;
-
- F32 begin = params.getBegin();
- F32 end = params.getEnd();
-
- t_step = 1.0f / sides;
- ang_step = 2.0f*F_PI*t_step*ang_scale;
-
- // Scale to have size "match" scale. Compensates to get object to generally fill bounding box.
-
- S32 total_sides = ll_round(sides / ang_scale); // Total number of sides all around
-
- if (total_sides < 8)
- {
- scale = tableScale[total_sides];
- }
-
- t_first = floor(begin * sides) / (F32)sides;
-
- // pt1 is the first point on the fractional face.
- // Starting t and ang values for the first face
- t = t_first;
- ang = 2.0f*F_PI*(t*ang_scale + offset);
- pt1.set(cos(ang)*scale,sin(ang)*scale, t);
-
- // Increment to the next point.
- // pt2 is the end point on the fractional face
- t += t_step;
- ang += ang_step;
- pt2.set(cos(ang)*scale,sin(ang)*scale,t);
-
- t_fraction = (begin - t_first)*sides;
-
- // Only use if it's not almost exactly on an edge.
- if (t_fraction < 0.9999f)
- {
- LLVector4a new_pt;
- new_pt.setLerp(pt1, pt2, t_fraction);
- mProfile.push_back(new_pt);
- }
-
- // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
- while (t < end)
- {
- // Iterate through all the integer steps of t.
- pt1.set(cos(ang)*scale,sin(ang)*scale,t);
-
- if (mProfile.size() > 0) {
- LLVector4a p = mProfile[mProfile.size()-1];
- for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
- //mProfile.push_back(p+(pt1-p) * 1.0f/(float)(split+1) * (float)(i+1));
- LLVector4a new_pt;
- new_pt.setSub(pt1, p);
- new_pt.mul(1.0f/(float)(split+1) * (float)(i+1));
- new_pt.add(p);
- mProfile.push_back(new_pt);
- }
- }
- mProfile.push_back(pt1);
-
- t += t_step;
- ang += ang_step;
- }
-
- t_fraction = (end - (t - t_step))*sides;
-
- // pt1 is the first point on the fractional face
- // pt2 is the end point on the fractional face
- pt2.set(cos(ang)*scale,sin(ang)*scale,t);
-
- // Find the fraction that we need to add to the end point.
- t_fraction = (end - (t - t_step))*sides;
- if (t_fraction > 0.0001f)
- {
- LLVector4a new_pt;
- new_pt.setLerp(pt1, pt2, t_fraction);
-
- if (mProfile.size() > 0) {
- LLVector4a p = mProfile[mProfile.size()-1];
- for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
- //mProfile.push_back(p+(new_pt-p) * 1.0f/(float)(split+1) * (float)(i+1));
-
- LLVector4a pt1;
- pt1.setSub(new_pt, p);
- pt1.mul(1.0f/(float)(split+1) * (float)(i+1));
- pt1.add(p);
- mProfile.push_back(pt1);
- }
- }
- mProfile.push_back(new_pt);
- }
-
- // If we're sliced, the profile is open.
- if ((end - begin)*ang_scale < 0.99f)
- {
- if ((end - begin)*ang_scale > 0.5f)
- {
- mConcave = true;
- }
- else
- {
- mConcave = false;
- }
- mOpen = true;
- if (params.getHollow() <= 0)
- {
- // put center point if not hollow.
- mProfile.push_back(LLVector4a(0,0,0));
- }
- }
- else
- {
- // The profile isn't open.
- mOpen = false;
- mConcave = false;
- }
-
- mTotal = mProfile.size();
-}
-
-// Hollow is percent of the original bounding box, not of this particular
-// profile's geometry. Thus, a swept triangle needs lower hollow values than
-// a swept square.
-LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, bool flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split)
-{
- // Note that addHole will NOT work for non-"circular" profiles, if we ever decide to use them.
-
- // Total add has number of vertices on outside.
- mTotalOut = mTotal;
-
- // Why is the "bevel" parameter -1? DJS 04/05/02
- genNGon(params, llfloor(sides),offset,-1, ang_scale, split);
-
- Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
-
- static thread_local LLAlignedArray<LLVector4a,64> pt;
- pt.resize(mTotal) ;
-
- for (S32 i=mTotalOut;i<mTotal;i++)
- {
- pt[i] = mProfile[i];
- pt[i].mul(box_hollow);
- }
-
- S32 j=mTotal-1;
- for (S32 i=mTotalOut;i<mTotal;i++)
- {
- mProfile[i] = pt[j--];
- }
-
- for (S32 i=0;i<(S32)mFaces.size();i++)
- {
- if (mFaces[i].mCap)
- {
- mFaces[i].mCount *= 2;
- }
- }
-
- return face;
-}
-
-//static
-S32 LLProfile::getNumPoints(const LLProfileParams& params, bool path_open,F32 detail, S32 split,
- bool is_sculpted, S32 sculpt_size)
-{ // this is basically LLProfile::generate stripped down to only operations that influence the number of points
- if (detail < MIN_LOD)
- {
- detail = MIN_LOD;
- }
-
- // Generate the face data
- F32 hollow = params.getHollow();
-
- S32 np = 0;
-
- switch (params.getCurveType() & LL_PCODE_PROFILE_MASK)
- {
- case LL_PCODE_PROFILE_SQUARE:
- {
- np = getNumNGonPoints(params, 4,-0.375, 0, 1, split);
-
- if (hollow)
- {
- np *= 2;
- }
- }
- break;
- case LL_PCODE_PROFILE_ISOTRI:
- case LL_PCODE_PROFILE_RIGHTTRI:
- case LL_PCODE_PROFILE_EQUALTRI:
- {
- np = getNumNGonPoints(params, 3,0, 0, 1, split);
-
- if (hollow)
- {
- np *= 2;
- }
- }
- break;
- case LL_PCODE_PROFILE_CIRCLE:
- {
- // If this has a square hollow, we should adjust the
- // number of faces a bit so that the geometry lines up.
- U8 hole_type=0;
- F32 circle_detail = MIN_DETAIL_FACES * detail;
- if (hollow)
- {
- hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
- if (hole_type == LL_PCODE_HOLE_SQUARE)
- {
- // Snap to the next multiple of four sides,
- // so that corners line up.
- circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
- }
- }
-
- S32 sides = (S32)circle_detail;
-
- if (is_sculpted)
- sides = sculpt_size;
-
- np = getNumNGonPoints(params, sides);
-
- if (hollow)
- {
- np *= 2;
- }
- }
- break;
- case LL_PCODE_PROFILE_CIRCLE_HALF:
- {
- // If this has a square hollow, we should adjust the
- // number of faces a bit so that the geometry lines up.
- U8 hole_type=0;
- // Number of faces is cut in half because it's only a half-circle.
- F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
- if (hollow)
- {
- hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
- if (hole_type == LL_PCODE_HOLE_SQUARE)
- {
- // Snap to the next multiple of four sides (div 2),
- // so that corners line up.
- circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
- }
- }
- np = getNumNGonPoints(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f);
-
- if (hollow)
- {
- np *= 2;
- }
-
- // Special case for openness of sphere
- if ((params.getEnd() - params.getBegin()) < 1.f)
- {
- }
- else if (!hollow)
- {
- np++;
- }
- }
- break;
- default:
- break;
- };
-
-
- return np;
-}
-
-
-bool LLProfile::generate(const LLProfileParams& params, bool path_open,F32 detail, S32 split,
- bool is_sculpted, S32 sculpt_size)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- if ((!mDirty) && (!is_sculpted))
- {
- return false;
- }
- mDirty = false;
-
- if (detail < MIN_LOD)
- {
- LL_INFOS() << "Generating profile with LOD < MIN_LOD. CLAMPING" << LL_ENDL;
- detail = MIN_LOD;
- }
-
- mProfile.resize(0);
- mFaces.resize(0);
-
- // Generate the face data
- S32 i;
- F32 begin = params.getBegin();
- F32 end = params.getEnd();
- F32 hollow = params.getHollow();
-
- // Quick validation to eliminate some server crashes.
- if (begin > end - 0.01f)
- {
- LL_WARNS() << "LLProfile::generate() assertion failed (begin >= end)" << LL_ENDL;
- return false;
- }
-
- S32 face_num = 0;
-
- switch (params.getCurveType() & LL_PCODE_PROFILE_MASK)
- {
- case LL_PCODE_PROFILE_SQUARE:
- {
- genNGon(params, 4,-0.375, 0, 1, split);
- if (path_open)
- {
- addCap (LL_FACE_PATH_BEGIN);
- }
-
- for (i = llfloor(begin * 4.f); i < llfloor(end * 4.f + .999f); i++)
- {
- addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, true);
- }
-
- LLVector4a scale(1,1,4,1);
-
- for (i = 0; i <(S32) mProfile.size(); i++)
- {
- // Scale by 4 to generate proper tex coords.
- mProfile[i].mul(scale);
- llassert(mProfile[i].isFinite3());
- }
-
- if (hollow)
- {
- switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
- {
- case LL_PCODE_HOLE_TRIANGLE:
- // This offset is not correct, but we can't change it now... DK 11/17/04
- addHole(params, true, 3, -0.375f, hollow, 1.f, split);
- break;
- case LL_PCODE_HOLE_CIRCLE:
- // TODO: Compute actual detail levels for cubes
- addHole(params, false, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f);
- break;
- case LL_PCODE_HOLE_SAME:
- case LL_PCODE_HOLE_SQUARE:
- default:
- addHole(params, true, 4, -0.375f, hollow, 1.f, split);
- break;
- }
- }
-
- if (path_open) {
- mFaces[0].mCount = mTotal;
- }
- }
- break;
- case LL_PCODE_PROFILE_ISOTRI:
- case LL_PCODE_PROFILE_RIGHTTRI:
- case LL_PCODE_PROFILE_EQUALTRI:
- {
- genNGon(params, 3,0, 0, 1, split);
- LLVector4a scale(1,1,3,1);
- for (i = 0; i <(S32) mProfile.size(); i++)
- {
- // Scale by 3 to generate proper tex coords.
- mProfile[i].mul(scale);
- llassert(mProfile[i].isFinite3());
- }
-
- if (path_open)
- {
- addCap(LL_FACE_PATH_BEGIN);
- }
-
- for (i = llfloor(begin * 3.f); i < llfloor(end * 3.f + .999f); i++)
- {
- addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, true);
- }
- if (hollow)
- {
- // Swept triangles need smaller hollowness values,
- // because the triangle doesn't fill the bounding box.
- F32 triangle_hollow = hollow / 2.f;
-
- switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
- {
- case LL_PCODE_HOLE_CIRCLE:
- // TODO: Actually generate level of detail for triangles
- addHole(params, false, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f);
- break;
- case LL_PCODE_HOLE_SQUARE:
- addHole(params, true, 4, 0, triangle_hollow, 1.f, split);
- break;
- case LL_PCODE_HOLE_SAME:
- case LL_PCODE_HOLE_TRIANGLE:
- default:
- addHole(params, true, 3, 0, triangle_hollow, 1.f, split);
- break;
- }
- }
- }
- break;
- case LL_PCODE_PROFILE_CIRCLE:
- {
- // If this has a square hollow, we should adjust the
- // number of faces a bit so that the geometry lines up.
- U8 hole_type=0;
- F32 circle_detail = MIN_DETAIL_FACES * detail;
- if (hollow)
- {
- hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
- if (hole_type == LL_PCODE_HOLE_SQUARE)
- {
- // Snap to the next multiple of four sides,
- // so that corners line up.
- circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
- }
- }
-
- S32 sides = (S32)circle_detail;
-
- if (is_sculpted)
- sides = sculpt_size;
-
- genNGon(params, sides);
-
- if (path_open)
- {
- addCap (LL_FACE_PATH_BEGIN);
- }
-
- if (mOpen && !hollow)
- {
- addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, false);
- }
- else
- {
- addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, false);
- }
-
- if (hollow)
- {
- switch (hole_type)
- {
- case LL_PCODE_HOLE_SQUARE:
- addHole(params, true, 4, 0, hollow, 1.f, split);
- break;
- case LL_PCODE_HOLE_TRIANGLE:
- addHole(params, true, 3, 0, hollow, 1.f, split);
- break;
- case LL_PCODE_HOLE_CIRCLE:
- case LL_PCODE_HOLE_SAME:
- default:
- addHole(params, true, circle_detail, 0, hollow, 1.f);
- break;
- }
- }
- }
- break;
- case LL_PCODE_PROFILE_CIRCLE_HALF:
- {
- // If this has a square hollow, we should adjust the
- // number of faces a bit so that the geometry lines up.
- U8 hole_type=0;
- // Number of faces is cut in half because it's only a half-circle.
- F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
- if (hollow)
- {
- hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
- if (hole_type == LL_PCODE_HOLE_SQUARE)
- {
- // Snap to the next multiple of four sides (div 2),
- // so that corners line up.
- circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
- }
- }
- genNGon(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f);
- if (path_open)
- {
- addCap(LL_FACE_PATH_BEGIN);
- }
- if (mOpen && !params.getHollow())
- {
- addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, false);
- }
- else
- {
- addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, false);
- }
-
- if (hollow)
- {
- switch (hole_type)
- {
- case LL_PCODE_HOLE_SQUARE:
- addHole(params, true, 2, 0.5f, hollow, 0.5f, split);
- break;
- case LL_PCODE_HOLE_TRIANGLE:
- addHole(params, true, 3, 0.5f, hollow, 0.5f, split);
- break;
- case LL_PCODE_HOLE_CIRCLE:
- case LL_PCODE_HOLE_SAME:
- default:
- addHole(params, false, circle_detail, 0.5f, hollow, 0.5f);
- break;
- }
- }
-
- // Special case for openness of sphere
- if ((params.getEnd() - params.getBegin()) < 1.f)
- {
- mOpen = true;
- }
- else if (!hollow)
- {
- mOpen = false;
- mProfile.push_back(mProfile[0]);
- mTotal++;
- }
- }
- break;
- default:
- LL_ERRS() << "Unknown profile: getCurveType()=" << params.getCurveType() << LL_ENDL;
- break;
- };
-
- if (path_open)
- {
- addCap(LL_FACE_PATH_END); // bottom
- }
-
- if ( mOpen) // interior edge caps
- {
- addFace(mTotal-1, 2,0.5,LL_FACE_PROFILE_BEGIN, true);
-
- if (hollow)
- {
- addFace(mTotalOut-1, 2,0.5,LL_FACE_PROFILE_END, true);
- }
- else
- {
- addFace(mTotal-2, 2,0.5,LL_FACE_PROFILE_END, true);
- }
- }
-
- return true;
-}
-
-
-
-bool LLProfileParams::importFile(LLFILE *fp)
-{
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of these buffers will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- char valuestr[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
- valuestr[0] = 0;
- F32 tempF32;
- U32 tempU32;
-
- while (!feof(fp))
- {
- if (fgets(buffer, BUFSIZE, fp) == NULL)
- {
- buffer[0] = '\0';
- }
-
- sscanf( /* Flawfinder: ignore */
- buffer,
- " %255s %255s",
- keyword, valuestr);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("curve", keyword))
- {
- sscanf(valuestr,"%d",&tempU32);
- setCurveType((U8) tempU32);
- }
- else if (!strcmp("begin",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setBegin(tempF32);
- }
- else if (!strcmp("end",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setEnd(tempF32);
- }
- else if (!strcmp("hollow",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setHollow(tempF32);
- }
- else
- {
- LL_WARNS() << "unknown keyword " << keyword << " in profile import" << LL_ENDL;
- }
- }
-
- return true;
-}
-
-
-bool LLProfileParams::exportFile(LLFILE *fp) const
-{
- fprintf(fp,"\t\tprofile 0\n");
- fprintf(fp,"\t\t{\n");
- fprintf(fp,"\t\t\tcurve\t%d\n", getCurveType());
- fprintf(fp,"\t\t\tbegin\t%g\n", getBegin());
- fprintf(fp,"\t\t\tend\t%g\n", getEnd());
- fprintf(fp,"\t\t\thollow\t%g\n", getHollow());
- fprintf(fp, "\t\t}\n");
- return true;
-}
-
-
-bool LLProfileParams::importLegacyStream(std::istream& input_stream)
-{
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of these buffers will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- char valuestr[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
- valuestr[0] = 0;
- F32 tempF32;
- U32 tempU32;
-
- while (input_stream.good())
- {
- input_stream.getline(buffer, BUFSIZE);
- sscanf( /* Flawfinder: ignore */
- buffer,
- " %255s %255s",
- keyword,
- valuestr);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("curve", keyword))
- {
- sscanf(valuestr,"%d",&tempU32);
- setCurveType((U8) tempU32);
- }
- else if (!strcmp("begin",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setBegin(tempF32);
- }
- else if (!strcmp("end",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setEnd(tempF32);
- }
- else if (!strcmp("hollow",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setHollow(tempF32);
- }
- else
- {
- LL_WARNS() << "unknown keyword " << keyword << " in profile import" << LL_ENDL;
- }
- }
-
- return true;
-}
-
-
-bool LLProfileParams::exportLegacyStream(std::ostream& output_stream) const
-{
- output_stream <<"\t\tprofile 0\n";
- output_stream <<"\t\t{\n";
- output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n";
- output_stream <<"\t\t\tbegin\t" << getBegin() << "\n";
- output_stream <<"\t\t\tend\t" << getEnd() << "\n";
- output_stream <<"\t\t\thollow\t" << getHollow() << "\n";
- output_stream << "\t\t}\n";
- return true;
-}
-
-LLSD LLProfileParams::asLLSD() const
-{
- LLSD sd;
-
- sd["curve"] = getCurveType();
- sd["begin"] = getBegin();
- sd["end"] = getEnd();
- sd["hollow"] = getHollow();
- return sd;
-}
-
-bool LLProfileParams::fromLLSD(LLSD& sd)
-{
- setCurveType(sd["curve"].asInteger());
- setBegin((F32)sd["begin"].asReal());
- setEnd((F32)sd["end"].asReal());
- setHollow((F32)sd["hollow"].asReal());
- return true;
-}
-
-void LLProfileParams::copyParams(const LLProfileParams ¶ms)
-{
- setCurveType(params.getCurveType());
- setBegin(params.getBegin());
- setEnd(params.getEnd());
- setHollow(params.getHollow());
-}
-
-
-LLPath::~LLPath()
-{
-}
-
-S32 LLPath::getNumNGonPoints(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
-{ //this is basically LLPath::genNGon stripped down to only operations that influence the number of points added
- S32 ret = 0;
-
- F32 step= 1.0f / sides;
- F32 t = params.getBegin();
- ret = 1;
-
- t+=step;
-
- // Snap to a quantized parameter, so that cut does not
- // affect most sample points.
- t = ((S32)(t * sides)) / (F32)sides;
-
- // Run through the non-cut dependent points.
- while (t < params.getEnd())
- {
- ret++;
- t+=step;
- }
-
- ret++;
-
- return ret;
-}
-
-void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- // Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane.
- constexpr F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
-
- F32 revolutions = params.getRevolutions();
- F32 skew = params.getSkew();
- F32 skew_mag = fabs(skew);
- F32 hole_x = params.getScaleX() * (1.0f - skew_mag);
- F32 hole_y = params.getScaleY();
-
- // Calculate taper begin/end for x,y (Negative means taper the beginning)
- F32 taper_x_begin = 1.0f;
- F32 taper_x_end = 1.0f - params.getTaperX();
- F32 taper_y_begin = 1.0f;
- F32 taper_y_end = 1.0f - params.getTaperY();
-
- if ( taper_x_end > 1.0f )
- {
- // Flip tapering.
- taper_x_begin = 2.0f - taper_x_end;
- taper_x_end = 1.0f;
- }
- if ( taper_y_end > 1.0f )
- {
- // Flip tapering.
- taper_y_begin = 2.0f - taper_y_end;
- taper_y_end = 1.0f;
- }
-
- // For spheres, the radius is usually zero.
- F32 radius_start = 0.5f;
- if (sides < 8)
- {
- radius_start = tableScale[sides];
- }
-
- // Scale the radius to take the hole size into account.
- radius_start *= 1.0f - hole_y;
-
- // Now check the radius offset to calculate the start,end radius. (Negative means
- // decrease the start radius instead).
- F32 radius_end = radius_start;
- F32 radius_offset = params.getRadiusOffset();
- if (radius_offset < 0.f)
- {
- radius_start *= 1.f + radius_offset;
- }
- else
- {
- radius_end *= 1.f - radius_offset;
- }
-
- // Is the path NOT a closed loop?
- mOpen = ( (params.getEnd()*end_scale - params.getBegin() < 1.0f) ||
- (skew_mag > 0.001f) ||
- (fabs(taper_x_end - taper_x_begin) > 0.001f) ||
- (fabs(taper_y_end - taper_y_begin) > 0.001f) ||
- (fabs(radius_end - radius_start) > 0.001f) );
-
- F32 ang, c, s;
- LLQuaternion twist, qang;
- PathPt *pt;
- LLVector3 path_axis (1.f, 0.f, 0.f);
- //LLVector3 twist_axis(0.f, 0.f, 1.f);
- F32 twist_begin = params.getTwistBegin() * twist_scale;
- F32 twist_end = params.getTwist() * twist_scale;
-
- // We run through this once before the main loop, to make sure
- // the path begins at the correct cut.
- F32 step= 1.0f / sides;
- F32 t = params.getBegin();
- pt = mPath.append(1);
- ang = 2.0f*F_PI*revolutions * t;
- s = sin(ang)*lerp(radius_start, radius_end, t);
- c = cos(ang)*lerp(radius_start, radius_end, t);
-
-
- pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s)
- + lerp(-skew ,skew, t) * 0.5f,
- c + lerp(0,params.getShear().mV[1],s),
- s);
- pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t),
- hole_y * lerp(taper_y_begin, taper_y_end, t),
- 0,1);
- pt->mTexT = t;
-
- // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
- twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
- // Rotate the point around the circle's center.
- qang.setQuat (ang,path_axis);
-
- LLMatrix3 rot(twist * qang);
-
- pt->mRot.loadu(rot);
-
- t+=step;
-
- // Snap to a quantized parameter, so that cut does not
- // affect most sample points.
- t = ((S32)(t * sides)) / (F32)sides;
-
- // Run through the non-cut dependent points.
- while (t < params.getEnd())
- {
- pt = mPath.append(1);
-
- ang = 2.0f*F_PI*revolutions * t;
- c = cos(ang)*lerp(radius_start, radius_end, t);
- s = sin(ang)*lerp(radius_start, radius_end, t);
-
- pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s)
- + lerp(-skew ,skew, t) * 0.5f,
- c + lerp(0,params.getShear().mV[1],s),
- s);
-
- pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t),
- hole_y * lerp(taper_y_begin, taper_y_end, t),
- 0,1);
- pt->mTexT = t;
-
- // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
- twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
- // Rotate the point around the circle's center.
- qang.setQuat (ang,path_axis);
- LLMatrix3 tmp(twist*qang);
- pt->mRot.loadu(tmp);
-
- t+=step;
- }
-
- // Make one final pass for the end cut.
- t = params.getEnd();
- pt = mPath.append(1);
- ang = 2.0f*F_PI*revolutions * t;
- c = cos(ang)*lerp(radius_start, radius_end, t);
- s = sin(ang)*lerp(radius_start, radius_end, t);
-
- pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s)
- + lerp(-skew ,skew, t) * 0.5f,
- c + lerp(0,params.getShear().mV[1],s),
- s);
- pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t),
- hole_y * lerp(taper_y_begin, taper_y_end, t),
- 0,1);
- pt->mTexT = t;
-
- // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
- twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
- // Rotate the point around the circle's center.
- qang.setQuat (ang,path_axis);
- LLMatrix3 tmp(twist*qang);
- pt->mRot.loadu(tmp);
-
- mTotal = mPath.size();
-}
-
-const LLVector2 LLPathParams::getBeginScale() const
-{
- LLVector2 begin_scale(1.f, 1.f);
- if (getScaleX() > 1)
- {
- begin_scale.mV[0] = 2-getScaleX();
- }
- if (getScaleY() > 1)
- {
- begin_scale.mV[1] = 2-getScaleY();
- }
- return begin_scale;
-}
-
-const LLVector2 LLPathParams::getEndScale() const
-{
- LLVector2 end_scale(1.f, 1.f);
- if (getScaleX() < 1)
- {
- end_scale.mV[0] = getScaleX();
- }
- if (getScaleY() < 1)
- {
- end_scale.mV[1] = getScaleY();
- }
- return end_scale;
-}
-
-S32 LLPath::getNumPoints(const LLPathParams& params, F32 detail)
-{ // this is basically LLPath::generate stripped down to only the operations that influence the number of points
- if (detail < MIN_LOD)
- {
- detail = MIN_LOD;
- }
-
- S32 np = 2; // hardcode for line
-
- // Is this 0xf0 mask really necessary? DK 03/02/05
-
- switch (params.getCurveType() & 0xf0)
- {
- default:
- case LL_PCODE_PATH_LINE:
- {
- // Take the begin/end twist into account for detail.
- np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2;
- }
- break;
-
- case LL_PCODE_PATH_CIRCLE:
- {
- // Increase the detail as the revolutions and twist increase.
- F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist());
-
- S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions());
-
- np = sides;
- }
- break;
-
- case LL_PCODE_PATH_CIRCLE2:
- {
- //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
- np = getNumNGonPoints(params, llfloor(MIN_DETAIL_FACES * detail));
- }
- break;
-
- case LL_PCODE_PATH_TEST:
-
- np = 5;
- break;
- };
-
- return np;
-}
-
-bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
- bool is_sculpted, S32 sculpt_size)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- if ((!mDirty) && (!is_sculpted))
- {
- return false;
- }
-
- if (detail < MIN_LOD)
- {
- LL_INFOS() << "Generating path with LOD < MIN! Clamping to 1" << LL_ENDL;
- detail = MIN_LOD;
- }
-
- mDirty = false;
- S32 np = 2; // hardcode for line
-
- mPath.resize(0);
- mOpen = true;
-
- // Is this 0xf0 mask really necessary? DK 03/02/05
- switch (params.getCurveType() & 0xf0)
- {
- default:
- case LL_PCODE_PATH_LINE:
- {
- // Take the begin/end twist into account for detail.
- np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2;
- if (np < split+2)
- {
- np = split+2;
- }
-
- mStep = 1.0f / (np-1);
-
- mPath.resize(np);
-
- LLVector2 start_scale = params.getBeginScale();
- LLVector2 end_scale = params.getEndScale();
-
- for (S32 i=0;i<np;i++)
- {
- F32 t = lerp(params.getBegin(),params.getEnd(),(F32)i * mStep);
- mPath[i].mPos.set(lerp(0,params.getShear().mV[0],t),
- lerp(0,params.getShear().mV[1],t),
- t - 0.5f);
- LLQuaternion quat;
- quat.setQuat(lerp(F_PI * params.getTwistBegin(),F_PI * params.getTwist(),t),0,0,1);
- LLMatrix3 tmp(quat);
- mPath[i].mRot.loadu(tmp);
- mPath[i].mScale.set(lerp(start_scale.mV[0],end_scale.mV[0],t),
- lerp(start_scale.mV[1],end_scale.mV[1],t),
- 0,1);
- mPath[i].mTexT = t;
- }
- }
- break;
-
- case LL_PCODE_PATH_CIRCLE:
- {
- // Increase the detail as the revolutions and twist increase.
- F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist());
-
- S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions());
-
- if (is_sculpted)
- sides = llmax(sculpt_size, 1);
-
- if (0 < sides)
- genNGon(params, sides);
- }
- break;
-
- case LL_PCODE_PATH_CIRCLE2:
- {
- if (params.getEnd() - params.getBegin() >= 0.99f &&
- params.getScaleX() >= .99f)
- {
- mOpen = false;
- }
-
- //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
- genNGon(params, llfloor(MIN_DETAIL_FACES * detail));
-
- F32 toggle = 0.5f;
- for (S32 i=0;i<(S32)mPath.size();i++)
- {
- mPath[i].mPos.getF32ptr()[0] = toggle;
- if (toggle == 0.5f)
- toggle = -0.5f;
- else
- toggle = 0.5f;
- }
- }
-
- break;
-
- case LL_PCODE_PATH_TEST:
-
- np = 5;
- mStep = 1.0f / (np-1);
-
- mPath.resize(np);
-
- for (S32 i=0;i<np;i++)
- {
- F32 t = (F32)i * mStep;
- mPath[i].mPos.set(0,
- lerp(0, -sin(F_PI*params.getTwist()*t)*0.5f,t),
- lerp(-0.5f, cos(F_PI*params.getTwist()*t)*0.5f,t));
- mPath[i].mScale.set(lerp(1,params.getScale().mV[0],t),
- lerp(1,params.getScale().mV[1],t), 0,1);
- mPath[i].mTexT = t;
- LLQuaternion quat;
- quat.setQuat(F_PI * params.getTwist() * t,1,0,0);
- LLMatrix3 tmp(quat);
- mPath[i].mRot.loadu(tmp);
- }
-
- break;
- };
-
- if (params.getTwist() != params.getTwistBegin()) mOpen = true;
-
- //if ((int(fabsf(params.getTwist() - params.getTwistBegin())*100))%100 != 0) {
- // mOpen = true;
- //}
-
- return true;
-}
-
-bool LLDynamicPath::generate(const LLPathParams& params, F32 detail, S32 split,
- bool is_sculpted, S32 sculpt_size)
-{
- mOpen = true; // Draw end caps
- if (getPathLength() == 0)
- {
- // Path hasn't been generated yet.
- // Some algorithms later assume at least TWO path points.
- resizePath(2);
- LLQuaternion quat;
- quat.setQuat(0,0,0);
- LLMatrix3 tmp(quat);
-
- for (U32 i = 0; i < 2; i++)
- {
- mPath[i].mPos.set(0, 0, 0);
- mPath[i].mRot.loadu(tmp);
- mPath[i].mScale.set(1, 1, 0, 1);
- mPath[i].mTexT = 0;
- }
- }
-
- return true;
-}
-
-
-bool LLPathParams::importFile(LLFILE *fp)
-{
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of these buffers will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- char valuestr[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
- valuestr[0] = 0;
-
- F32 tempF32;
- F32 x, y;
- U32 tempU32;
-
- while (!feof(fp))
- {
- if (fgets(buffer, BUFSIZE, fp) == NULL)
- {
- buffer[0] = '\0';
- }
-
- sscanf( /* Flawfinder: ignore */
- buffer,
- " %255s %255s",
- keyword, valuestr);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("curve", keyword))
- {
- sscanf(valuestr,"%d",&tempU32);
- setCurveType((U8) tempU32);
- }
- else if (!strcmp("begin",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setBegin(tempF32);
- }
- else if (!strcmp("end",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setEnd(tempF32);
- }
- else if (!strcmp("scale",keyword))
- {
- // Legacy for one dimensional scale per path
- sscanf(valuestr,"%g",&tempF32);
- setScale(tempF32, tempF32);
- }
- else if (!strcmp("scale_x", keyword))
- {
- sscanf(valuestr, "%g", &x);
- setScaleX(x);
- }
- else if (!strcmp("scale_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setScaleY(y);
- }
- else if (!strcmp("shear_x", keyword))
- {
- sscanf(valuestr, "%g", &x);
- setShearX(x);
- }
- else if (!strcmp("shear_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setShearY(y);
- }
- else if (!strcmp("twist",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setTwist(tempF32);
- }
- else if (!strcmp("twist_begin", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTwistBegin(y);
- }
- else if (!strcmp("radius_offset", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setRadiusOffset(y);
- }
- else if (!strcmp("taper_x", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTaperX(y);
- }
- else if (!strcmp("taper_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTaperY(y);
- }
- else if (!strcmp("revolutions", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setRevolutions(y);
- }
- else if (!strcmp("skew", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setSkew(y);
- }
- else
- {
- LL_WARNS() << "unknown keyword " << " in path import" << LL_ENDL;
- }
- }
- return true;
-}
-
-
-bool LLPathParams::exportFile(LLFILE *fp) const
-{
- fprintf(fp, "\t\tpath 0\n");
- fprintf(fp, "\t\t{\n");
- fprintf(fp, "\t\t\tcurve\t%d\n", getCurveType());
- fprintf(fp, "\t\t\tbegin\t%g\n", getBegin());
- fprintf(fp, "\t\t\tend\t%g\n", getEnd());
- fprintf(fp, "\t\t\tscale_x\t%g\n", getScaleX() );
- fprintf(fp, "\t\t\tscale_y\t%g\n", getScaleY() );
- fprintf(fp, "\t\t\tshear_x\t%g\n", getShearX() );
- fprintf(fp, "\t\t\tshear_y\t%g\n", getShearY() );
- fprintf(fp,"\t\t\ttwist\t%g\n", getTwist());
-
- fprintf(fp,"\t\t\ttwist_begin\t%g\n", getTwistBegin());
- fprintf(fp,"\t\t\tradius_offset\t%g\n", getRadiusOffset());
- fprintf(fp,"\t\t\ttaper_x\t%g\n", getTaperX());
- fprintf(fp,"\t\t\ttaper_y\t%g\n", getTaperY());
- fprintf(fp,"\t\t\trevolutions\t%g\n", getRevolutions());
- fprintf(fp,"\t\t\tskew\t%g\n", getSkew());
-
- fprintf(fp, "\t\t}\n");
- return true;
-}
-
-
-bool LLPathParams::importLegacyStream(std::istream& input_stream)
-{
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of these buffers will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- char valuestr[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
- valuestr[0] = 0;
-
- F32 tempF32;
- F32 x, y;
- U32 tempU32;
-
- while (input_stream.good())
- {
- input_stream.getline(buffer, BUFSIZE);
- sscanf( /* Flawfinder: ignore */
- buffer,
- " %255s %255s",
- keyword, valuestr);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("curve", keyword))
- {
- sscanf(valuestr,"%d",&tempU32);
- setCurveType((U8) tempU32);
- }
- else if (!strcmp("begin",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setBegin(tempF32);
- }
- else if (!strcmp("end",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setEnd(tempF32);
- }
- else if (!strcmp("scale",keyword))
- {
- // Legacy for one dimensional scale per path
- sscanf(valuestr,"%g",&tempF32);
- setScale(tempF32, tempF32);
- }
- else if (!strcmp("scale_x", keyword))
- {
- sscanf(valuestr, "%g", &x);
- setScaleX(x);
- }
- else if (!strcmp("scale_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setScaleY(y);
- }
- else if (!strcmp("shear_x", keyword))
- {
- sscanf(valuestr, "%g", &x);
- setShearX(x);
- }
- else if (!strcmp("shear_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setShearY(y);
- }
- else if (!strcmp("twist",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setTwist(tempF32);
- }
- else if (!strcmp("twist_begin", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTwistBegin(y);
- }
- else if (!strcmp("radius_offset", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setRadiusOffset(y);
- }
- else if (!strcmp("taper_x", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTaperX(y);
- }
- else if (!strcmp("taper_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTaperY(y);
- }
- else if (!strcmp("revolutions", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setRevolutions(y);
- }
- else if (!strcmp("skew", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setSkew(y);
- }
- else
- {
- LL_WARNS() << "unknown keyword " << " in path import" << LL_ENDL;
- }
- }
- return true;
-}
-
-
-bool LLPathParams::exportLegacyStream(std::ostream& output_stream) const
-{
- output_stream << "\t\tpath 0\n";
- output_stream << "\t\t{\n";
- output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n";
- output_stream << "\t\t\tbegin\t" << getBegin() << "\n";
- output_stream << "\t\t\tend\t" << getEnd() << "\n";
- output_stream << "\t\t\tscale_x\t" << getScaleX() << "\n";
- output_stream << "\t\t\tscale_y\t" << getScaleY() << "\n";
- output_stream << "\t\t\tshear_x\t" << getShearX() << "\n";
- output_stream << "\t\t\tshear_y\t" << getShearY() << "\n";
- output_stream <<"\t\t\ttwist\t" << getTwist() << "\n";
-
- output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n";
- output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n";
- output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n";
- output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n";
- output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n";
- output_stream <<"\t\t\tskew\t" << getSkew() << "\n";
-
- output_stream << "\t\t}\n";
- return true;
-}
-
-LLSD LLPathParams::asLLSD() const
-{
- LLSD sd = LLSD();
- sd["curve"] = getCurveType();
- sd["begin"] = getBegin();
- sd["end"] = getEnd();
- sd["scale_x"] = getScaleX();
- sd["scale_y"] = getScaleY();
- sd["shear_x"] = getShearX();
- sd["shear_y"] = getShearY();
- sd["twist"] = getTwist();
- sd["twist_begin"] = getTwistBegin();
- sd["radius_offset"] = getRadiusOffset();
- sd["taper_x"] = getTaperX();
- sd["taper_y"] = getTaperY();
- sd["revolutions"] = getRevolutions();
- sd["skew"] = getSkew();
-
- return sd;
-}
-
-bool LLPathParams::fromLLSD(LLSD& sd)
-{
- setCurveType(sd["curve"].asInteger());
- setBegin((F32)sd["begin"].asReal());
- setEnd((F32)sd["end"].asReal());
- setScaleX((F32)sd["scale_x"].asReal());
- setScaleY((F32)sd["scale_y"].asReal());
- setShearX((F32)sd["shear_x"].asReal());
- setShearY((F32)sd["shear_y"].asReal());
- setTwist((F32)sd["twist"].asReal());
- setTwistBegin((F32)sd["twist_begin"].asReal());
- setRadiusOffset((F32)sd["radius_offset"].asReal());
- setTaperX((F32)sd["taper_x"].asReal());
- setTaperY((F32)sd["taper_y"].asReal());
- setRevolutions((F32)sd["revolutions"].asReal());
- setSkew((F32)sd["skew"].asReal());
- return true;
-}
-
-void LLPathParams::copyParams(const LLPathParams ¶ms)
-{
- setCurveType(params.getCurveType());
- setBegin(params.getBegin());
- setEnd(params.getEnd());
- setScale(params.getScaleX(), params.getScaleY() );
- setShear(params.getShearX(), params.getShearY() );
- setTwist(params.getTwist());
- setTwistBegin(params.getTwistBegin());
- setRadiusOffset(params.getRadiusOffset());
- setTaper( params.getTaperX(), params.getTaperY() );
- setRevolutions(params.getRevolutions());
- setSkew(params.getSkew());
-}
-
-LLProfile::~LLProfile()
-{
-}
-
-
-S32 LLVolume::sNumMeshPoints = 0;
-
-LLVolume::LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool generate_single_face, const bool is_unique)
- : mParams(params)
-{
- mUnique = is_unique;
- mFaceMask = 0x0;
- mDetail = detail;
- mSculptLevel = -2;
- mSurfaceArea = 1.f; //only calculated for sculpts, defaults to 1 for all other prims
- mIsMeshAssetLoaded = false;
- mIsMeshAssetUnavaliable = false;
- mLODScaleBias.setVec(1,1,1);
- mHullPoints = nullptr;
- mHullIndices = nullptr;
- mNumHullPoints = 0;
- mNumHullIndices = 0;
-
- // set defaults
- if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
- {
- mPathp = new LLDynamicPath();
- }
- else
- {
- mPathp = new LLPath();
- }
- mProfilep = new LLProfile();
-
- mGenerateSingleFace = generate_single_face;
-
- generate();
-
- if ((mParams.getSculptID().isNull() && mParams.getSculptType() == LL_SCULPT_TYPE_NONE) || mParams.getSculptType() == LL_SCULPT_TYPE_MESH)
- {
- createVolumeFaces();
- }
-}
-
-void LLVolume::resizePath(S32 length)
-{
- mPathp->resizePath(length);
- mVolumeFaces.clear();
- setDirty();
-}
-
-void LLVolume::regen()
-{
- generate();
- createVolumeFaces();
-}
-
-void LLVolume::genTangents(S32 face)
-{
- // generate legacy tangents for the specified face
- llassert(!isMeshAssetLoaded() || mVolumeFaces[face].mTangents != nullptr); // if this is a complete mesh asset, we should already have tangents
- mVolumeFaces[face].createTangents();
-}
-
-LLVolume::~LLVolume()
-{
- sNumMeshPoints -= mMesh.size();
- delete mPathp;
-
- delete mProfilep;
-
- mPathp = NULL;
- mProfilep = NULL;
- mVolumeFaces.clear();
-
- ll_aligned_free_16(mHullPoints);
- mHullPoints = NULL;
- ll_aligned_free_16(mHullIndices);
- mHullIndices = NULL;
-}
-
-bool LLVolume::generate()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- LL_CHECK_MEMORY
- llassert_always(mProfilep);
-
- //Added 10.03.05 Dave Parks
- // Split is a parameter to LLProfile::generate that tesselates edges on the profile
- // to prevent lighting and texture interpolation errors on triangles that are
- // stretched due to twisting or scaling on the path.
- S32 split = (S32) ((mDetail)*0.66f);
-
- if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_LINE &&
- (mParams.getPathParams().getScale().mV[0] != 1.0f ||
- mParams.getPathParams().getScale().mV[1] != 1.0f) &&
- (mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_SQUARE ||
- mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_ISOTRI ||
- mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_EQUALTRI ||
- mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_RIGHTTRI))
- {
- split = 0;
- }
-
- mLODScaleBias.setVec(0.5f, 0.5f, 0.5f);
-
- F32 profile_detail = mDetail;
- F32 path_detail = mDetail;
-
- if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
- {
- U8 path_type = mParams.getPathParams().getCurveType();
- U8 profile_type = mParams.getProfileParams().getCurveType();
- if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE)
- {
- //cylinders don't care about Z-Axis
- mLODScaleBias.setVec(0.6f, 0.6f, 0.0f);
- }
- else if (path_type == LL_PCODE_PATH_CIRCLE)
- {
- mLODScaleBias.setVec(0.6f, 0.6f, 0.6f);
- }
- }
-
- bool regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split);
- bool regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split);
-
- if (regenPath || regenProf )
- {
- S32 sizeS = mPathp->mPath.size();
- S32 sizeT = mProfilep->mProfile.size();
-
- sNumMeshPoints -= mMesh.size();
- mMesh.resize(sizeT * sizeS);
- sNumMeshPoints += mMesh.size();
-
- //generate vertex positions
-
- // Run along the path.
- LLVector4a* dst = mMesh.mArray;
-
- for (S32 s = 0; s < sizeS; ++s)
- {
- F32* scale = mPathp->mPath[s].mScale.getF32ptr();
-
- F32 sc [] =
- { scale[0], 0, 0, 0,
- 0, scale[1], 0, 0,
- 0, 0, scale[2], 0,
- 0, 0, 0, 1 };
-
- LLMatrix4 rot((F32*) mPathp->mPath[s].mRot.mMatrix);
- LLMatrix4 scale_mat(sc);
-
- scale_mat *= rot;
-
- LLMatrix4a rot_mat;
- rot_mat.loadu(scale_mat);
-
- LLVector4a* profile = mProfilep->mProfile.mArray;
- LLVector4a* end_profile = profile+sizeT;
- LLVector4a offset = mPathp->mPath[s].mPos;
-
- // hack to work around MAINT-5660 for debug until we can suss out
- // what is wrong with the path generated that inserts NaNs...
- if (!offset.isFinite3())
- {
- offset.clear();
- }
-
- LLVector4a tmp;
-
- // Run along the profile.
- while (profile < end_profile)
- {
- rot_mat.rotate(*profile++, tmp);
- dst->setAdd(tmp,offset);
- ++dst;
- }
- }
-
- for (std::vector<LLProfile::Face>::iterator iter = mProfilep->mFaces.begin();
- iter != mProfilep->mFaces.end(); ++iter)
- {
- LLFaceID id = iter->mFaceID;
- mFaceMask |= id;
- }
- LL_CHECK_MEMORY
- return true;
- }
-
- LL_CHECK_MEMORY
- return false;
-}
-
-void LLVolumeFace::VertexData::init()
-{
- if (!mData)
- {
- mData = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*2);
- }
-}
-
-LLVolumeFace::VertexData::VertexData()
-{
- mData = NULL;
- init();
-}
-
-LLVolumeFace::VertexData::VertexData(const VertexData& rhs)
-{
- mData = NULL;
- *this = rhs;
-}
-
-const LLVolumeFace::VertexData& LLVolumeFace::VertexData::operator=(const LLVolumeFace::VertexData& rhs)
-{
- if (this != &rhs)
- {
- init();
- LLVector4a::memcpyNonAliased16((F32*) mData, (F32*) rhs.mData, 2*sizeof(LLVector4a));
- mTexCoord = rhs.mTexCoord;
- }
- return *this;
-}
-
-LLVolumeFace::VertexData::~VertexData()
-{
- ll_aligned_free_16(mData);
- mData = NULL;
-}
-
-LLVector4a& LLVolumeFace::VertexData::getPosition()
-{
- return mData[POSITION];
-}
-
-LLVector4a& LLVolumeFace::VertexData::getNormal()
-{
- return mData[NORMAL];
-}
-
-const LLVector4a& LLVolumeFace::VertexData::getPosition() const
-{
- return mData[POSITION];
-}
-
-const LLVector4a& LLVolumeFace::VertexData::getNormal() const
-{
- return mData[NORMAL];
-}
-
-
-void LLVolumeFace::VertexData::setPosition(const LLVector4a& pos)
-{
- mData[POSITION] = pos;
-}
-
-void LLVolumeFace::VertexData::setNormal(const LLVector4a& norm)
-{
- mData[NORMAL] = norm;
-}
-
-bool LLVolumeFace::VertexData::operator<(const LLVolumeFace::VertexData& rhs)const
-{
- const F32* lp = this->getPosition().getF32ptr();
- const F32* rp = rhs.getPosition().getF32ptr();
-
- if (lp[0] != rp[0])
- {
- return lp[0] < rp[0];
- }
-
- if (rp[1] != lp[1])
- {
- return lp[1] < rp[1];
- }
-
- if (rp[2] != lp[2])
- {
- return lp[2] < rp[2];
- }
-
- lp = getNormal().getF32ptr();
- rp = rhs.getNormal().getF32ptr();
-
- if (lp[0] != rp[0])
- {
- return lp[0] < rp[0];
- }
-
- if (rp[1] != lp[1])
- {
- return lp[1] < rp[1];
- }
-
- if (rp[2] != lp[2])
- {
- return lp[2] < rp[2];
- }
-
- if (mTexCoord.mV[0] != rhs.mTexCoord.mV[0])
- {
- return mTexCoord.mV[0] < rhs.mTexCoord.mV[0];
- }
-
- return mTexCoord.mV[1] < rhs.mTexCoord.mV[1];
-}
-
-bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)const
-{
- return mData[POSITION].equals3(rhs.getPosition()) &&
- mData[NORMAL].equals3(rhs.getNormal()) &&
- mTexCoord == rhs.mTexCoord;
-}
-
-bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const
-{
- bool retval = false;
-
- const F32 epsilon = 0.00001f;
-
- if (rhs.mData[POSITION].equals3(mData[POSITION], epsilon) &&
- fabs(rhs.mTexCoord[0]-mTexCoord[0]) < epsilon &&
- fabs(rhs.mTexCoord[1]-mTexCoord[1]) < epsilon)
- {
- if (angle_cutoff > 1.f)
- {
- retval = (mData[NORMAL].equals3(rhs.mData[NORMAL], epsilon));
- }
- else
- {
- F32 cur_angle = rhs.mData[NORMAL].dot3(mData[NORMAL]).getF32();
- retval = cur_angle > angle_cutoff;
- }
- }
-
- return retval;
-}
-
-bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- //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)
- {
- 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);
-}
-
-bool LLVolume::unpackVolumeFaces(U8* in_data, S32 size)
-{
- //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)
- {
- 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);
-}
-
-bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
-{
- {
- U32 face_count = mdl.size();
-
- if (face_count == 0)
- { //no faces unpacked, treat as failed decode
- LL_WARNS() << "found no faces!" << LL_ENDL;
- return false;
- }
-
- mVolumeFaces.resize(face_count);
-
- for (size_t i = 0; i < face_count; ++i)
- {
- LLVolumeFace& face = mVolumeFaces[i];
-
- if (mdl[i].has("NoGeometry"))
- { //face has no geometry, continue
- face.resizeIndices(3);
- face.resizeVertices(1);
- face.mPositions->clear();
- face.mNormals->clear();
- face.mTexCoords->setZero();
- memset(face.mIndices, 0, sizeof(U16)*3);
- continue;
- }
-
- LLSD::Binary pos = mdl[i]["Position"];
- LLSD::Binary norm = mdl[i]["Normal"];
- LLSD::Binary tangent = mdl[i]["Tangent"];
- LLSD::Binary tc = mdl[i]["TexCoord0"];
- LLSD::Binary idx = mdl[i]["TriangleList"];
-
- //copy out indices
- S32 num_indices = idx.size() / 2;
- const S32 indices_to_discard = num_indices % 3;
- if (indices_to_discard > 0)
- {
- // Invalid number of triangle indices
- LL_WARNS() << "Incomplete triangle discarded from face! Indices count " << num_indices << " was not divisible by 3. face index: " << i << " Total: " << face_count << LL_ENDL;
- num_indices -= indices_to_discard;
- }
- face.resizeIndices(num_indices);
-
- if (num_indices > 2 && !face.mIndices)
- {
- LL_WARNS() << "Failed to allocate " << num_indices << " indices for face index: " << i << " Total: " << face_count << LL_ENDL;
- continue;
- }
-
- if (idx.empty() || face.mNumIndices < 3)
- { //why is there an empty index list?
- LL_WARNS() << "Empty face present! Face index: " << i << " Total: " << face_count << LL_ENDL;
- continue;
- }
-
- U16* indices = (U16*) &(idx[0]);
- for (U32 j = 0; j < num_indices; ++j)
- {
- face.mIndices[j] = indices[j];
- }
-
- //copy out vertices
- U32 num_verts = pos.size()/(3*2);
- 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.resizeIndices(0);
- continue;
- }
-
- LLVector3 minp;
- LLVector3 maxp;
- LLVector2 min_tc;
- LLVector2 max_tc;
-
- minp.setValue(mdl[i]["PositionDomain"]["Min"]);
- maxp.setValue(mdl[i]["PositionDomain"]["Max"]);
- LLVector4a min_pos, max_pos;
- min_pos.load3(minp.mV);
- max_pos.load3(maxp.mV);
-
- min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
- max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
-
- //unpack normalized scale/translation
- if (mdl[i].has("NormalizedScale"))
- {
- face.mNormalizedScale.setValue(mdl[i]["NormalizedScale"]);
- }
- else
- {
- face.mNormalizedScale.set(1, 1, 1);
- }
-
- LLVector4a pos_range;
- pos_range.setSub(max_pos, min_pos);
- LLVector2 tc_range2 = max_tc - min_tc;
-
- LLVector4a tc_range;
- tc_range.set(tc_range2[0], tc_range2[1], tc_range2[0], tc_range2[1]);
- LLVector4a min_tc4(min_tc[0], min_tc[1], min_tc[0], min_tc[1]);
-
- LLVector4a* pos_out = face.mPositions;
- LLVector4a* norm_out = face.mNormals;
- LLVector4a* tc_out = (LLVector4a*) face.mTexCoords;
-
- {
- U16* v = (U16*) &(pos[0]);
- for (U32 j = 0; j < num_verts; ++j)
- {
- pos_out->set((F32) v[0], (F32) v[1], (F32) v[2]);
- pos_out->div(65535.f);
- pos_out->mul(pos_range);
- pos_out->add(min_pos);
- pos_out++;
- v += 3;
- }
-
- }
-
- {
- if (!norm.empty())
- {
- U16* n = (U16*) &(norm[0]);
- for (U32 j = 0; j < num_verts; ++j)
- {
- norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]);
- norm_out->div(65535.f);
- norm_out->mul(2.f);
- norm_out->sub(1.f);
- norm_out++;
- n += 3;
- }
- }
- else
- {
- for (U32 j = 0; j < num_verts; ++j)
- {
- norm_out->clear();
- norm_out++; // or just norm_out[j].clear();
- }
- }
- }
-
-#if 0 // keep this code for now in case we decide to add support for on-the-wire tangents
- {
- if (!tangent.empty())
- {
- face.allocateTangents(face.mNumVertices);
- U16* t = (U16*)&(tangent[0]);
-
- // NOTE: tangents coming from the asset may not be mikkt space, but they should always be used by the GLTF shaders to
- // maintain compliance with the GLTF spec
- LLVector4a* t_out = face.mTangents;
-
- for (U32 j = 0; j < num_verts; ++j)
- {
- t_out->set((F32)t[0], (F32)t[1], (F32)t[2], (F32) t[3]);
- t_out->div(65535.f);
- t_out->mul(2.f);
- t_out->sub(1.f);
-
- F32* tp = t_out->getF32ptr();
- tp[3] = tp[3] < 0.f ? -1.f : 1.f;
-
- t_out++;
- t += 4;
- }
- }
- }
-#endif
-
- {
- if (!tc.empty())
- {
- U16* t = (U16*) &(tc[0]);
- for (U32 j = 0; j < num_verts; j+=2)
- {
- if (j < num_verts-1)
- {
- tc_out->set((F32) t[0], (F32) t[1], (F32) t[2], (F32) t[3]);
- }
- else
- {
- tc_out->set((F32) t[0], (F32) t[1], 0.f, 0.f);
- }
-
- t += 4;
-
- tc_out->div(65535.f);
- tc_out->mul(tc_range);
- tc_out->add(min_tc4);
-
- tc_out++;
- }
- }
- else
- {
- for (U32 j = 0; j < num_verts; j += 2)
- {
- tc_out->clear();
- tc_out++;
- }
- }
- }
-
- if (mdl[i].has("Weights"))
- {
- face.allocateWeights(num_verts);
- if (!face.mWeights && num_verts)
- {
- LL_WARNS() << "Failed to allocate " << num_verts << " weights for face index: " << i << " Total: " << face_count << LL_ENDL;
- face.resizeIndices(0);
- face.resizeVertices(0);
- continue;
- }
-
- LLSD::Binary weights = mdl[i]["Weights"];
-
- U32 idx = 0;
-
- U32 cur_vertex = 0;
- while (idx < weights.size() && cur_vertex < num_verts)
- {
- const U8 END_INFLUENCES = 0xFF;
- U8 joint = weights[idx++];
-
- U32 cur_influence = 0;
- LLVector4 wght(0,0,0,0);
- U32 joints[4] = {0,0,0,0};
- LLVector4 joints_with_weights(0,0,0,0);
-
- while (joint != END_INFLUENCES && idx < weights.size())
- {
- U16 influence = weights[idx++];
- influence |= ((U16) weights[idx++] << 8);
-
- F32 w = llclamp((F32) influence / 65535.f, 0.001f, 0.999f);
- wght.mV[cur_influence] = w;
- joints[cur_influence] = joint;
- cur_influence++;
-
- if (cur_influence >= 4)
- {
- joint = END_INFLUENCES;
- }
- else
- {
- joint = weights[idx++];
- }
- }
- F32 wsum = wght.mV[VX] + wght.mV[VY] + wght.mV[VZ] + wght.mV[VW];
- if (wsum <= 0.f)
- {
- wght = LLVector4(0.999f,0.f,0.f,0.f);
- }
- for (U32 k=0; k<4; k++)
- {
- F32 f_combined = (F32) joints[k] + wght[k];
- joints_with_weights[k] = f_combined;
- // Any weights we added above should wind up non-zero and applied to a specific bone.
- // A failure here would indicate a floating point precision error in the math.
- llassert((k >= cur_influence) || (f_combined - S32(f_combined) > 0.0f));
- }
- face.mWeights[cur_vertex].loadua(joints_with_weights.mV);
-
- cur_vertex++;
- }
-
- if (cur_vertex != num_verts || idx != weights.size())
- {
- LL_WARNS() << "Vertex weight count does not match vertex count!" << LL_ENDL;
- }
-
- }
-
- // modifier flags?
- bool do_mirror = (mParams.getSculptType() & LL_SCULPT_FLAG_MIRROR);
- bool do_invert = (mParams.getSculptType() &LL_SCULPT_FLAG_INVERT);
-
-
- // translate to actions:
- bool do_reflect_x = false;
- bool do_reverse_triangles = false;
- bool do_invert_normals = false;
-
- if (do_mirror)
- {
- do_reflect_x = true;
- do_reverse_triangles = !do_reverse_triangles;
- }
-
- if (do_invert)
- {
- do_invert_normals = true;
- do_reverse_triangles = !do_reverse_triangles;
- }
-
- // now do the work
-
- if (do_reflect_x)
- {
- LLVector4a* p = (LLVector4a*) face.mPositions;
- LLVector4a* n = (LLVector4a*) face.mNormals;
-
- for (S32 i = 0; i < face.mNumVertices; i++)
- {
- p[i].mul(-1.0f);
- n[i].mul(-1.0f);
- }
- }
-
- if (do_invert_normals)
- {
- LLVector4a* n = (LLVector4a*) face.mNormals;
-
- for (S32 i = 0; i < face.mNumVertices; i++)
- {
- n[i].mul(-1.0f);
- }
- }
-
- if (do_reverse_triangles)
- {
- for (U32 j = 0; j < face.mNumIndices; j += 3)
- {
- // swap the 2nd and 3rd index
- S32 swap = face.mIndices[j+1];
- face.mIndices[j+1] = face.mIndices[j+2];
- face.mIndices[j+2] = swap;
- }
- }
-
- //calculate bounding box
- // VFExtents change
- LLVector4a& min = face.mExtents[0];
- LLVector4a& max = face.mExtents[1];
-
- if (face.mNumVertices < 3)
- { //empty face, use a dummy 1cm (at 1m scale) bounding box
- min.splat(-0.005f);
- max.splat(0.005f);
- }
- else
- {
- min = max = face.mPositions[0];
-
- for (S32 i = 1; i < face.mNumVertices; ++i)
- {
- min.setMin(min, face.mPositions[i]);
- max.setMax(max, face.mPositions[i]);
- }
-
- if (face.mTexCoords)
- {
- LLVector2& min_tc = face.mTexCoordExtents[0];
- LLVector2& max_tc = face.mTexCoordExtents[1];
-
- min_tc = face.mTexCoords[0];
- max_tc = face.mTexCoords[0];
-
- for (U32 j = 1; j < face.mNumVertices; ++j)
- {
- update_min_max(min_tc, max_tc, face.mTexCoords[j]);
- }
- }
- else
- {
- face.mTexCoordExtents[0].set(0,0);
- face.mTexCoordExtents[1].set(1,1);
- }
- }
- }
- }
-
- if (!cacheOptimize(true))
- {
- // Out of memory?
- LL_WARNS() << "Failed to optimize!" << LL_ENDL;
- mVolumeFaces.clear();
- return false;
- }
-
- mSculptLevel = 0; // success!
-
- return true;
-}
-
-
-bool LLVolume::isMeshAssetLoaded()
-{
- return mIsMeshAssetLoaded;
-}
-
-void LLVolume::setMeshAssetLoaded(bool loaded)
-{
- mIsMeshAssetLoaded = loaded;
- if (loaded)
- {
- mIsMeshAssetUnavaliable = false;
- }
-}
-
-void LLVolume::setMeshAssetUnavaliable(bool unavaliable)
-{
- // Don't set it if at least one lod loaded
- if (!mIsMeshAssetLoaded)
- {
- mIsMeshAssetUnavaliable = unavaliable;
- }
-}
-
-bool LLVolume::isMeshAssetUnavaliable()
-{
- return mIsMeshAssetUnavaliable;
-}
-
-void LLVolume::copyFacesTo(std::vector<LLVolumeFace> &faces) const
-{
- faces = mVolumeFaces;
-}
-
-void LLVolume::copyFacesFrom(const std::vector<LLVolumeFace> &faces)
-{
- mVolumeFaces = faces;
- mSculptLevel = 0;
-}
-
-void LLVolume::copyVolumeFaces(const LLVolume* volume)
-{
- mVolumeFaces = volume->mVolumeFaces;
- mSculptLevel = 0;
-}
-
-bool LLVolume::cacheOptimize(bool gen_tangents)
-{
- for (S32 i = 0; i < mVolumeFaces.size(); ++i)
- {
- if (!mVolumeFaces[i].cacheOptimize(gen_tangents))
- {
- return false;
- }
- }
- return true;
-}
-
-
-S32 LLVolume::getNumFaces() const
-{
- return mIsMeshAssetLoaded ? getNumVolumeFaces() : (S32)mProfilep->mFaces.size();
-}
-
-
-void LLVolume::createVolumeFaces()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- if (mGenerateSingleFace)
- {
- // do nothing
- }
- else
- {
- S32 num_faces = getNumFaces();
- bool partial_build = true;
- if (num_faces != mVolumeFaces.size())
- {
- partial_build = false;
- mVolumeFaces.resize(num_faces);
- }
- // Initialize volume faces with parameter data
- for (S32 i = 0; i < (S32)mVolumeFaces.size(); i++)
- {
- LLVolumeFace& vf = mVolumeFaces[i];
- LLProfile::Face& face = mProfilep->mFaces[i];
- vf.mBeginS = face.mIndex;
- vf.mNumS = face.mCount;
- if (vf.mNumS < 0)
- {
- LL_ERRS() << "Volume face corruption detected." << LL_ENDL;
- }
-
- vf.mBeginT = 0;
- vf.mNumT= getPath().mPath.size();
- vf.mID = i;
-
- // Set the type mask bits correctly
- if (mParams.getProfileParams().getHollow() > 0)
- {
- vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK;
- }
- if (mProfilep->isOpen())
- {
- vf.mTypeMask |= LLVolumeFace::OPEN_MASK;
- }
- if (face.mCap)
- {
- vf.mTypeMask |= LLVolumeFace::CAP_MASK;
- if (face.mFaceID == LL_FACE_PATH_BEGIN)
- {
- vf.mTypeMask |= LLVolumeFace::TOP_MASK;
- }
- else
- {
- llassert(face.mFaceID == LL_FACE_PATH_END);
- vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK;
- }
- }
- else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
- {
- vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK;
- }
- else
- {
- vf.mTypeMask |= LLVolumeFace::SIDE_MASK;
- if (face.mFlat)
- {
- vf.mTypeMask |= LLVolumeFace::FLAT_MASK;
- }
- if (face.mFaceID & LL_FACE_INNER_SIDE)
- {
- vf.mTypeMask |= LLVolumeFace::INNER_MASK;
- if (face.mFlat && vf.mNumS > 2)
- { //flat inner faces have to copy vert normals
- vf.mNumS = vf.mNumS*2;
- if (vf.mNumS < 0)
- {
- LL_ERRS() << "Volume face corruption detected." << LL_ENDL;
- }
- }
- }
- else
- {
- vf.mTypeMask |= LLVolumeFace::OUTER_MASK;
- }
- }
- }
-
- for (face_list_t::iterator iter = mVolumeFaces.begin();
- iter != mVolumeFaces.end(); ++iter)
- {
- (*iter).create(this, partial_build);
- }
- }
-}
-
-
-inline LLVector4a sculpt_rgb_to_vector(U8 r, U8 g, U8 b)
-{
- // maps RGB values to vector values [0..255] -> [-0.5..0.5]
- LLVector4a value;
- LLVector4a sub(0.5f, 0.5f, 0.5f);
-
- value.set(r,g,b);
- value.mul(1.f/255.f);
- value.sub(sub);
-
- return value;
-}
-
-inline U32 sculpt_xy_to_index(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
-{
- U32 index = (x + y * sculpt_width) * sculpt_components;
- return index;
-}
-
-
-inline U32 sculpt_st_to_index(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
-{
- U32 x = (U32) ((F32)s/(size_s) * (F32) sculpt_width);
- U32 y = (U32) ((F32)t/(size_t) * (F32) sculpt_height);
-
- return sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
-}
-
-
-inline LLVector4a sculpt_index_to_vector(U32 index, const U8* sculpt_data)
-{
- LLVector4a v = sculpt_rgb_to_vector(sculpt_data[index], sculpt_data[index+1], sculpt_data[index+2]);
-
- return v;
-}
-
-inline LLVector4a sculpt_st_to_vector(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
-{
- U32 index = sculpt_st_to_index(s, t, size_s, size_t, sculpt_width, sculpt_height, sculpt_components);
-
- return sculpt_index_to_vector(index, sculpt_data);
-}
-
-inline LLVector4a sculpt_xy_to_vector(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
-{
- U32 index = sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
-
- return sculpt_index_to_vector(index, sculpt_data);
-}
-
-
-F32 LLVolume::sculptGetSurfaceArea()
-{
- // test to see if image has enough variation to create non-degenerate geometry
-
- F32 area = 0;
-
- S32 sizeS = mPathp->mPath.size();
- S32 sizeT = mProfilep->mProfile.size();
-
- for (S32 s = 0; s < sizeS-1; s++)
- {
- for (S32 t = 0; t < sizeT-1; t++)
- {
- // get four corners of quad
- LLVector4a& p1 = mMesh[(s )*sizeT + (t )];
- LLVector4a& p2 = mMesh[(s+1)*sizeT + (t )];
- LLVector4a& p3 = mMesh[(s )*sizeT + (t+1)];
- LLVector4a& p4 = mMesh[(s+1)*sizeT + (t+1)];
-
- // compute the area of the quad by taking the length of the cross product of the two triangles
- LLVector4a v0,v1,v2,v3;
- v0.setSub(p1,p2);
- v1.setSub(p1,p3);
- v2.setSub(p4,p2);
- v3.setSub(p4,p3);
-
- LLVector4a cross1, cross2;
- cross1.setCross3(v0,v1);
- cross2.setCross3(v2,v3);
-
- //LLVector3 cross1 = (p1 - p2) % (p1 - p3);
- //LLVector3 cross2 = (p4 - p2) % (p4 - p3);
-
- area += (cross1.getLength3() + cross2.getLength3()).getF32() / 2.f;
- }
- }
-
- return area;
-}
-
-// create empty placeholder shape
-void LLVolume::sculptGenerateEmptyPlaceholder()
-{
- S32 sizeS = mPathp->mPath.size();
- S32 sizeT = mProfilep->mProfile.size();
-
- S32 line = 0;
-
- for (S32 s = 0; s < sizeS; s++)
- {
- for (S32 t = 0; t < sizeT; t++)
- {
- S32 i = t + line;
- LLVector4a& pt = mMesh[i];
-
- F32* p = pt.getF32ptr();
-
- p[0] = 0;
- p[1] = 0;
- p[2] = 0;
-
- llassert(pt.isFinite3());
- }
- line += sizeT;
- }
-}
-
-// create sphere placeholder shape
-void LLVolume::sculptGenerateSpherePlaceholder()
-{
- S32 sizeS = mPathp->mPath.size();
- S32 sizeT = mProfilep->mProfile.size();
-
- S32 line = 0;
-
- for (S32 s = 0; s < sizeS; s++)
- {
- for (S32 t = 0; t < sizeT; t++)
- {
- S32 i = t + line;
- LLVector4a& pt = mMesh[i];
-
-
- F32 u = (F32)s / (sizeS - 1);
- F32 v = (F32)t / (sizeT - 1);
-
- const F32 RADIUS = (F32) 0.3;
-
- F32* p = pt.getF32ptr();
-
- p[0] = (F32)(sin(F_PI * v) * cos(2.0 * F_PI * u) * RADIUS);
- p[1] = (F32)(sin(F_PI * v) * sin(2.0 * F_PI * u) * RADIUS);
- p[2] = (F32)(cos(F_PI * v) * RADIUS);
-
- llassert(pt.isFinite3());
- }
- line += sizeT;
- }
-}
-
-// create the vertices from the map
-void LLVolume::sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type)
-{
- U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
- bool sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
- bool sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
- bool reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR
-
- S32 sizeS = mPathp->mPath.size();
- S32 sizeT = mProfilep->mProfile.size();
-
- S32 line = 0;
- for (S32 s = 0; s < sizeS; s++)
- {
- // Run along the profile.
- for (S32 t = 0; t < sizeT; t++)
- {
- S32 i = t + line;
- LLVector4a& pt = mMesh[i];
-
- S32 reversed_t = t;
-
- if (reverse_horizontal)
- {
- reversed_t = sizeT - t - 1;
- }
-
- U32 x = (U32) ((F32)reversed_t/(sizeT-1) * (F32) sculpt_width);
- U32 y = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_height);
-
-
- if (y == 0) // top row stitching
- {
- // pinch?
- if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
- {
- x = sculpt_width / 2;
- }
- }
-
- if (y == sculpt_height) // bottom row stitching
- {
- // wrap?
- if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
- {
- y = 0;
- }
- else
- {
- y = sculpt_height - 1;
- }
-
- // pinch?
- if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
- {
- x = sculpt_width / 2;
- }
- }
-
- if (x == sculpt_width) // side stitching
- {
- // wrap?
- if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
- (sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
- (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
- {
- x = 0;
- }
-
- else
- {
- x = sculpt_width - 1;
- }
- }
-
- pt = sculpt_xy_to_vector(x, y, sculpt_width, sculpt_height, sculpt_components, sculpt_data);
-
- if (sculpt_mirror)
- {
- LLVector4a scale(-1.f,1,1,1);
- pt.mul(scale);
- }
-
- llassert(pt.isFinite3());
- }
-
- line += sizeT;
- }
-}
-
-
-constexpr S32 SCULPT_REZ_1 = 6; // changed from 4 to 6 - 6 looks round whereas 4 looks square
-constexpr S32 SCULPT_REZ_2 = 8;
-constexpr S32 SCULPT_REZ_3 = 16;
-constexpr S32 SCULPT_REZ_4 = 32;
-
-S32 sculpt_sides(F32 detail)
-{
- // detail is usually one of: 1, 1.5, 2.5, 4.0.
-
- if (detail <= 1.0)
- {
- return SCULPT_REZ_1;
- }
- if (detail <= 2.0)
- {
- return SCULPT_REZ_2;
- }
- if (detail <= 3.0)
- {
- return SCULPT_REZ_3;
- }
- else
- {
- return SCULPT_REZ_4;
- }
-}
-
-
-
-// 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)
-{
- // this code has the following properties:
- // 1) the aspect ratio of the mesh is as close as possible to the ratio of the map
- // while still using all available verts
- // 2) the mesh cannot have more verts than is allowed by LOD
- // 3) the mesh cannot have more verts than is allowed by the map
-
- S32 max_vertices_lod = (S32)pow((double)sculpt_sides(detail), 2.0);
- S32 max_vertices_map = width * height / 4;
-
- S32 vertices;
- if (max_vertices_map > 0)
- vertices = llmin(max_vertices_lod, max_vertices_map);
- else
- vertices = max_vertices_lod;
-
-
- F32 ratio;
- if ((width == 0) || (height == 0))
- ratio = 1.f;
- else
- ratio = (F32) width / (F32) height;
-
-
- s = (S32)(F32) sqrt(((F32)vertices / ratio));
-
- s = llmax(s, 4); // no degenerate sizes, please
- t = vertices / s;
-
- t = llmax(t, 4); // no degenerate sizes, please
- s = vertices / t;
-}
-
-// sculpt replaces generate() for sculpted surfaces
-void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level, bool visible_placeholder)
-{
- U8 sculpt_type = mParams.getSculptType();
-
- bool data_is_empty = false;
-
- if (sculpt_width == 0 || sculpt_height == 0 || sculpt_components < 3 || sculpt_data == NULL)
- {
- sculpt_level = -1;
- data_is_empty = true;
- }
-
- S32 requested_sizeS = 0;
- S32 requested_sizeT = 0;
-
- sculpt_calc_mesh_resolution(sculpt_width, sculpt_height, sculpt_type, mDetail, requested_sizeS, requested_sizeT);
-
- mPathp->generate(mParams.getPathParams(), mDetail, 0, true, requested_sizeS);
- mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(), mDetail, 0, true, requested_sizeT);
-
- S32 sizeS = mPathp->mPath.size(); // we requested a specific size, now see what we really got
- S32 sizeT = mProfilep->mProfile.size(); // we requested a specific size, now see what we really got
-
- // weird crash bug - DEV-11158 - trying to collect more data:
- if ((sizeS == 0) || (sizeT == 0))
- {
- LL_WARNS() << "sculpt bad mesh size " << sizeS << " " << sizeT << LL_ENDL;
- }
-
- sNumMeshPoints -= mMesh.size();
- mMesh.resize(sizeS * sizeT);
- sNumMeshPoints += mMesh.size();
-
- //generate vertex positions
- if (!data_is_empty)
- {
- sculptGenerateMapVertices(sculpt_width, sculpt_height, sculpt_components, sculpt_data, sculpt_type);
-
- // don't test lowest LOD to support legacy content DEV-33670
- if (mDetail > SCULPT_MIN_AREA_DETAIL)
- {
- F32 area = sculptGetSurfaceArea();
-
- mSurfaceArea = area;
-
- const F32 SCULPT_MAX_AREA = 384.f;
-
- if (area < SCULPT_MIN_AREA || area > SCULPT_MAX_AREA)
- {
- data_is_empty = true;
- visible_placeholder = true;
- }
- }
- }
-
- if (data_is_empty)
- {
- if (visible_placeholder)
- {
- // Object should be visible since there will be nothing else to display
- sculptGenerateSpherePlaceholder();
- }
- else
- {
- sculptGenerateEmptyPlaceholder();
- }
- }
-
- for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
- {
- mFaceMask |= mProfilep->mFaces[i].mFaceID;
- }
-
- mSculptLevel = sculpt_level;
-
- // Delete any existing faces so that they get regenerated
- mVolumeFaces.clear();
-
- createVolumeFaces();
-}
-
-
-
-
-bool LLVolume::isCap(S32 face)
-{
- return mProfilep->mFaces[face].mCap;
-}
-
-bool LLVolume::isFlat(S32 face)
-{
- return mProfilep->mFaces[face].mFlat;
-}
-
-
-bool LLVolumeParams::isSculpt() const
-{
- return (mSculptType & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_NONE;
-}
-
-bool LLVolumeParams::isMeshSculpt() const
-{
- return (mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH;
-}
-
-bool LLVolumeParams::operator==(const LLVolumeParams ¶ms) const
-{
- return ( (getPathParams() == params.getPathParams()) &&
- (getProfileParams() == params.getProfileParams()) &&
- (mSculptID == params.mSculptID) &&
- (mSculptType == params.mSculptType) );
-}
-
-bool LLVolumeParams::operator!=(const LLVolumeParams ¶ms) const
-{
- return ( (getPathParams() != params.getPathParams()) ||
- (getProfileParams() != params.getProfileParams()) ||
- (mSculptID != params.mSculptID) ||
- (mSculptType != params.mSculptType) );
-}
-
-bool LLVolumeParams::operator<(const LLVolumeParams ¶ms) const
-{
- if( getPathParams() != params.getPathParams() )
- {
- return getPathParams() < params.getPathParams();
- }
-
- if (getProfileParams() != params.getProfileParams())
- {
- return getProfileParams() < params.getProfileParams();
- }
-
- if (mSculptID != params.mSculptID)
- {
- return mSculptID < params.mSculptID;
- }
-
- return mSculptType < params.mSculptType;
-
-
-}
-
-void LLVolumeParams::copyParams(const LLVolumeParams ¶ms)
-{
- mProfileParams.copyParams(params.mProfileParams);
- mPathParams.copyParams(params.mPathParams);
- mSculptID = params.getSculptID();
- mSculptType = params.getSculptType();
-}
-
-// Less restricitve approx 0 for volumes
-constexpr F32 APPROXIMATELY_ZERO = 0.001f;
-bool approx_zero( F32 f, F32 tolerance = APPROXIMATELY_ZERO)
-{
- return (f >= -tolerance) && (f <= tolerance);
-}
-
-// return true if in range (or nearly so)
-static bool limit_range(F32& v, F32 min, F32 max, F32 tolerance = APPROXIMATELY_ZERO)
-{
- F32 min_delta = v - min;
- if (min_delta < 0.f)
- {
- v = min;
- if (!approx_zero(min_delta, tolerance))
- return false;
- }
- F32 max_delta = max - v;
- if (max_delta < 0.f)
- {
- v = max;
- if (!approx_zero(max_delta, tolerance))
- return false;
- }
- return true;
-}
-
-bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e)
-{
- bool valid = true;
-
- // First, clamp to valid ranges.
- F32 begin = b;
- valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
-
- F32 end = e;
- if (end >= .0149f && end < MIN_CUT_DELTA) end = MIN_CUT_DELTA; // eliminate warning for common rounding error
- valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
-
- valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
-
- // Now set them.
- mProfileParams.setBegin(begin);
- mProfileParams.setEnd(end);
-
- return valid;
-}
-
-bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e)
-{
- bool valid = true;
-
- // First, clamp to valid ranges.
- F32 begin = b;
- valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
-
- F32 end = e;
- valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
-
- valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
-
- // Now set them.
- mPathParams.setBegin(begin);
- mPathParams.setEnd(end);
-
- return valid;
-}
-
-bool LLVolumeParams::setHollow(const F32 h)
-{
- // Validate the hollow based on path and profile.
- U8 profile = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
- U8 hole_type = mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK;
-
- F32 max_hollow = HOLLOW_MAX;
-
- // Only square holes have trouble.
- if (LL_PCODE_HOLE_SQUARE == hole_type)
- {
- switch(profile)
- {
- case LL_PCODE_PROFILE_CIRCLE:
- case LL_PCODE_PROFILE_CIRCLE_HALF:
- case LL_PCODE_PROFILE_EQUALTRI:
- max_hollow = HOLLOW_MAX_SQUARE;
- }
- }
-
- F32 hollow = h;
- bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow);
- mProfileParams.setHollow(hollow);
-
- return valid;
-}
-
-bool LLVolumeParams::setTwistBegin(const F32 b)
-{
- F32 twist_begin = b;
- bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX);
- mPathParams.setTwistBegin(twist_begin);
- return valid;
-}
-
-bool LLVolumeParams::setTwistEnd(const F32 e)
-{
- F32 twist_end = e;
- bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX);
- mPathParams.setTwistEnd(twist_end);
- return valid;
-}
-
-bool LLVolumeParams::setRatio(const F32 x, const F32 y)
-{
- F32 min_x = RATIO_MIN;
- F32 max_x = RATIO_MAX;
- F32 min_y = RATIO_MIN;
- F32 max_y = RATIO_MAX;
- // If this is a circular path (and not a sphere) then 'ratio' is actually hole size.
- U8 path_type = mPathParams.getCurveType();
- U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
- if ( LL_PCODE_PATH_CIRCLE == path_type &&
- LL_PCODE_PROFILE_CIRCLE_HALF != profile_type)
- {
- // Holes are more restricted...
- min_x = HOLE_X_MIN;
- max_x = HOLE_X_MAX;
- min_y = HOLE_Y_MIN;
- max_y = HOLE_Y_MAX;
- }
-
- F32 ratio_x = x;
- bool valid = limit_range(ratio_x, min_x, max_x);
- F32 ratio_y = y;
- valid &= limit_range(ratio_y, min_y, max_y);
-
- mPathParams.setScale(ratio_x, ratio_y);
-
- return valid;
-}
-
-bool LLVolumeParams::setShear(const F32 x, const F32 y)
-{
- F32 shear_x = x;
- bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX);
- F32 shear_y = y;
- valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX);
- mPathParams.setShear(shear_x, shear_y);
- return valid;
-}
-
-bool LLVolumeParams::setTaperX(const F32 v)
-{
- F32 taper = v;
- bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
- mPathParams.setTaperX(taper);
- return valid;
-}
-
-bool LLVolumeParams::setTaperY(const F32 v)
-{
- F32 taper = v;
- bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
- mPathParams.setTaperY(taper);
- return valid;
-}
-
-bool LLVolumeParams::setRevolutions(const F32 r)
-{
- F32 revolutions = r;
- bool valid = limit_range(revolutions, REV_MIN, REV_MAX);
- mPathParams.setRevolutions(revolutions);
- return valid;
-}
-
-bool LLVolumeParams::setRadiusOffset(const F32 offset)
-{
- bool valid = true;
-
- // If this is a sphere, just set it to 0 and get out.
- U8 path_type = mPathParams.getCurveType();
- U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
- if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ||
- LL_PCODE_PATH_CIRCLE != path_type )
- {
- mPathParams.setRadiusOffset(0.f);
- return true;
- }
-
- // Limit radius offset, based on taper and hole size y.
- F32 radius_offset = offset;
- F32 taper_y = getTaperY();
- F32 radius_mag = fabs(radius_offset);
- F32 hole_y_mag = fabs(getRatioY());
- F32 taper_y_mag = fabs(taper_y);
- // Check to see if the taper effects us.
- if ( (radius_offset > 0.f && taper_y < 0.f) ||
- (radius_offset < 0.f && taper_y > 0.f) )
- {
- // The taper does not help increase the radius offset range.
- taper_y_mag = 0.f;
- }
- F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
-
- // Enforce the maximum magnitude.
- F32 delta = max_radius_mag - radius_mag;
- if (delta < 0.f)
- {
- // Check radius offset sign.
- if (radius_offset < 0.f)
- {
- radius_offset = -max_radius_mag;
- }
- else
- {
- radius_offset = max_radius_mag;
- }
- valid = approx_zero(delta, .1f);
- }
-
- mPathParams.setRadiusOffset(radius_offset);
- return valid;
-}
-
-bool LLVolumeParams::setSkew(const F32 skew_value)
-{
- bool valid = true;
-
- // Check the skew value against the revolutions.
- F32 skew = llclamp(skew_value, SKEW_MIN, SKEW_MAX);
- F32 skew_mag = fabs(skew);
- F32 revolutions = getRevolutions();
- F32 scale_x = getRatioX();
- F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
- // Discontinuity; A revolution of 1 allows skews below 0.5.
- if ( fabs(revolutions - 1.0f) < 0.001)
- min_skew_mag = 0.0f;
-
- // Clip skew.
- F32 delta = skew_mag - min_skew_mag;
- if (delta < 0.f)
- {
- // Check skew sign.
- if (skew < 0.0f)
- {
- skew = -min_skew_mag;
- }
- else
- {
- skew = min_skew_mag;
- }
- valid = approx_zero(delta, .01f);
- }
-
- mPathParams.setSkew(skew);
- return valid;
-}
-
-bool LLVolumeParams::setSculptID(const LLUUID& sculpt_id, U8 sculpt_type)
-{
- mSculptID = sculpt_id;
- mSculptType = sculpt_type;
- return true;
-}
-
-bool LLVolumeParams::setType(U8 profile, U8 path)
-{
- bool result = true;
- // First, check profile and path for validity.
- U8 profile_type = profile & LL_PCODE_PROFILE_MASK;
- U8 hole_type = (profile & LL_PCODE_HOLE_MASK) >> 4;
- U8 path_type = path >> 4;
-
- if (profile_type > LL_PCODE_PROFILE_MAX)
- {
- // Bad profile. Make it square.
- profile = LL_PCODE_PROFILE_SQUARE;
- result = false;
- LL_WARNS() << "LLVolumeParams::setType changing bad profile type (" << profile_type
- << ") to be LL_PCODE_PROFILE_SQUARE" << LL_ENDL;
- }
- else if (hole_type > LL_PCODE_HOLE_MAX)
- {
- // Bad hole. Make it the same.
- profile = profile_type;
- result = false;
- LL_WARNS() << "LLVolumeParams::setType changing bad hole type (" << hole_type
- << ") to be LL_PCODE_HOLE_SAME" << LL_ENDL;
- }
-
- if (path_type < LL_PCODE_PATH_MIN ||
- path_type > LL_PCODE_PATH_MAX)
- {
- // Bad path. Make it linear.
- result = false;
- LL_WARNS() << "LLVolumeParams::setType changing bad path (" << path
- << ") to be LL_PCODE_PATH_LINE" << LL_ENDL;
- path = LL_PCODE_PATH_LINE;
- }
-
- mProfileParams.setCurveType(profile);
- mPathParams.setCurveType(path);
- return result;
-}
-
-// static
-bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
- U8 path_curve, F32 path_begin, F32 path_end,
- F32 scx, F32 scy, F32 shx, F32 shy,
- F32 twistend, F32 twistbegin, F32 radiusoffset,
- F32 tx, F32 ty, F32 revolutions, F32 skew)
-{
- LLVolumeParams test_params;
- if (!test_params.setType (prof_curve, path_curve))
- {
- return false;
- }
- if (!test_params.setBeginAndEndS (prof_begin, prof_end))
- {
- return false;
- }
- if (!test_params.setBeginAndEndT (path_begin, path_end))
- {
- return false;
- }
- if (!test_params.setHollow (hollow))
- {
- return false;
- }
- if (!test_params.setTwistBegin (twistbegin))
- {
- return false;
- }
- if (!test_params.setTwistEnd (twistend))
- {
- return false;
- }
- if (!test_params.setRatio (scx, scy))
- {
- return false;
- }
- if (!test_params.setShear (shx, shy))
- {
- return false;
- }
- if (!test_params.setTaper (tx, ty))
- {
- return false;
- }
- if (!test_params.setRevolutions (revolutions))
- {
- return false;
- }
- if (!test_params.setRadiusOffset (radiusoffset))
- {
- return false;
- }
- if (!test_params.setSkew (skew))
- {
- return false;
- }
- return true;
-}
-
-void LLVolume::getLoDTriangleCounts(const LLVolumeParams& params, S32* counts)
-{ //attempt to approximate the number of triangles that will result from generating a volume LoD set for the
- //supplied LLVolumeParams -- inaccurate, but a close enough approximation for determining streaming cost
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
- F32 detail[] = {1.f, 1.5f, 2.5f, 4.f};
- for (S32 i = 0; i < 4; i++)
- {
- S32 count = 0;
- S32 path_points = LLPath::getNumPoints(params.getPathParams(), detail[i]);
- S32 profile_points = LLProfile::getNumPoints(params.getProfileParams(), false, detail[i]);
-
- count = (profile_points-1)*2*(path_points-1);
- count += profile_points*2;
-
- counts[i] = count;
- }
-}
-
-
-S32 LLVolume::getNumTriangles(S32* vcount) const
-{
- U32 triangle_count = 0;
- U32 vertex_count = 0;
-
- for (S32 i = 0; i < getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = getVolumeFace(i);
- triangle_count += face.mNumIndices/3;
-
- vertex_count += face.mNumVertices;
- }
-
-
- if (vcount)
- {
- *vcount = vertex_count;
- }
-
- return triangle_count;
-}
-
-
-//-----------------------------------------------------------------------------
-// generateSilhouetteVertices()
-//-----------------------------------------------------------------------------
-void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
- std::vector<LLVector3> &normals,
- const LLVector3& obj_cam_vec_in,
- const LLMatrix4& mat_in,
- const LLMatrix3& norm_mat_in,
- S32 face_mask)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- LLMatrix4a mat;
- mat.loadu(mat_in);
-
- LLMatrix4a norm_mat;
- norm_mat.loadu(norm_mat_in);
-
- LLVector4a obj_cam_vec;
- obj_cam_vec.load3(obj_cam_vec_in.mV);
-
- vertices.clear();
- normals.clear();
-
- if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
- {
- return;
- }
-
- S32 cur_index = 0;
- //for each face
- for (face_list_t::iterator iter = mVolumeFaces.begin();
- iter != mVolumeFaces.end(); ++iter)
- {
- LLVolumeFace& face = *iter;
-
- if (!(face_mask & (0x1 << cur_index++)) ||
- face.mNumIndices == 0 || face.mEdge.empty())
- {
- continue;
- }
-
- if (face.mTypeMask & (LLVolumeFace::CAP_MASK))
- {
- LLVector4a* v = (LLVector4a*)face.mPositions;
- LLVector4a* n = (LLVector4a*)face.mNormals;
-
- for (U32 j = 0; j < face.mNumIndices / 3; j++)
- {
- for (S32 k = 0; k < 3; k++)
- {
- S32 index = face.mEdge[j * 3 + k];
-
- if (index == -1)
- {
- // silhouette edge, currently only cubes, so no other conditions
-
- S32 v1 = face.mIndices[j * 3 + k];
- S32 v2 = face.mIndices[j * 3 + ((k + 1) % 3)];
-
- LLVector4a t;
- mat.affineTransform(v[v1], t);
- vertices.push_back(LLVector3(t[0], t[1], t[2]));
-
- norm_mat.rotate(n[v1], t);
-
- t.normalize3fast();
- normals.push_back(LLVector3(t[0], t[1], t[2]));
-
- mat.affineTransform(v[v2], t);
- vertices.push_back(LLVector3(t[0], t[1], t[2]));
-
- norm_mat.rotate(n[v2], t);
- t.normalize3fast();
- normals.push_back(LLVector3(t[0], t[1], t[2]));
- }
- }
- }
-
- }
- else
- {
- //==============================================
- //DEBUG draw edge map instead of silhouette edge
- //==============================================
-
-#if DEBUG_SILHOUETTE_EDGE_MAP
-
- //for each triangle
- U32 tri_count = face.mNumIndices / 3;
- for (U32 j = 0; j < tri_count; j++) {
- //get vertices
- S32 v1 = face.mIndices[j*3+0];
- S32 v2 = face.mIndices[j*3+1];
- S32 v3 = face.mIndices[j*3+2];
-
- //get current face center
- LLVector3 cCenter = (face.mVertices[v1].getPosition() +
- face.mVertices[v2].getPosition() +
- face.mVertices[v3].getPosition()) / 3.0f;
-
- //for each edge
- for (S32 k = 0; k < 3; k++) {
- S32 nIndex = face.mEdge[j*3+k];
- if (nIndex <= -1) {
- continue;
- }
-
- if (nIndex >= (S32)tri_count) {
- continue;
- }
- //get neighbor vertices
- v1 = face.mIndices[nIndex*3+0];
- v2 = face.mIndices[nIndex*3+1];
- v3 = face.mIndices[nIndex*3+2];
-
- //get neighbor face center
- LLVector3 nCenter = (face.mVertices[v1].getPosition() +
- face.mVertices[v2].getPosition() +
- face.mVertices[v3].getPosition()) / 3.0f;
-
- //draw line
- vertices.push_back(cCenter);
- vertices.push_back(nCenter);
- normals.push_back(LLVector3(1,1,1));
- normals.push_back(LLVector3(1,1,1));
- segments.push_back(vertices.size());
- }
- }
-
- continue;
-
- //==============================================
- //DEBUG
- //==============================================
-
- //==============================================
- //DEBUG draw normals instead of silhouette edge
- //==============================================
-#elif DEBUG_SILHOUETTE_NORMALS
-
- //for each vertex
- for (U32 j = 0; j < face.mNumVertices; j++) {
- vertices.push_back(face.mVertices[j].getPosition());
- vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].getNormal()*0.1f);
- normals.push_back(LLVector3(0,0,1));
- normals.push_back(LLVector3(0,0,1));
- segments.push_back(vertices.size());
-#if DEBUG_SILHOUETTE_BINORMALS
- vertices.push_back(face.mVertices[j].getPosition());
- vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].mTangent*0.1f);
- normals.push_back(LLVector3(0,0,1));
- normals.push_back(LLVector3(0,0,1));
- segments.push_back(vertices.size());
-#endif
- }
-
- continue;
-#else
- //==============================================
- //DEBUG
- //==============================================
-
- constexpr U8 AWAY = 0x01,
- TOWARDS = 0x02;
-
- //for each triangle
- std::vector<U8> fFacing;
- vector_append(fFacing, face.mNumIndices/3);
-
- LLVector4a* v = (LLVector4a*) face.mPositions;
- LLVector4a* n = (LLVector4a*) face.mNormals;
-
- for (U32 j = 0; j < face.mNumIndices/3; j++)
- {
- //approximate normal
- S32 v1 = face.mIndices[j*3+0];
- S32 v2 = face.mIndices[j*3+1];
- S32 v3 = face.mIndices[j*3+2];
-
- LLVector4a c1,c2;
- c1.setSub(v[v1], v[v2]);
- c2.setSub(v[v2], v[v3]);
-
- LLVector4a norm;
-
- norm.setCross3(c1, c2);
-
- if (norm.dot3(norm) < 0.00000001f)
- {
- fFacing[j] = AWAY | TOWARDS;
- }
- else
- {
- //get view vector
- LLVector4a view;
- view.setSub(obj_cam_vec, v[v1]);
- bool away = view.dot3(norm) > 0.0f;
- if (away)
- {
- fFacing[j] = AWAY;
- }
- else
- {
- fFacing[j] = TOWARDS;
- }
- }
- }
-
- //for each triangle
- for (U32 j = 0; j < face.mNumIndices/3; j++)
- {
- if (fFacing[j] == (AWAY | TOWARDS))
- { //this is a degenerate triangle
- //take neighbor facing (degenerate faces get facing of one of their neighbors)
- // *FIX IF NEEDED: this does not deal with neighboring degenerate faces
- for (S32 k = 0; k < 3; k++)
- {
- S32 index = face.mEdge[j*3+k];
- if (index != -1)
- {
- fFacing[j] = fFacing[index];
- break;
- }
- }
- continue; //skip degenerate face
- }
-
- //for each edge
- for (S32 k = 0; k < 3; k++) {
- S32 index = face.mEdge[j*3+k];
- if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) {
- //our neighbor is degenerate, make him face our direction
- fFacing[face.mEdge[j*3+k]] = fFacing[j];
- continue;
- }
-
- if (index == -1 || //edge has no neighbor, MUST be a silhouette edge
- (fFacing[index] & fFacing[j]) == 0) { //we found a silhouette edge
-
- S32 v1 = face.mIndices[j*3+k];
- S32 v2 = face.mIndices[j*3+((k+1)%3)];
-
- LLVector4a t;
- mat.affineTransform(v[v1], t);
- vertices.push_back(LLVector3(t[0], t[1], t[2]));
-
- norm_mat.rotate(n[v1], t);
-
- t.normalize3fast();
- normals.push_back(LLVector3(t[0], t[1], t[2]));
-
- mat.affineTransform(v[v2], t);
- vertices.push_back(LLVector3(t[0], t[1], t[2]));
-
- norm_mat.rotate(n[v2], t);
- t.normalize3fast();
- normals.push_back(LLVector3(t[0], t[1], t[2]));
- }
- }
- }
-#endif
- }
- }
-}
-
-S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
- S32 face,
- LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent_out)
-{
- S32 hit_face = -1;
-
- S32 start_face;
- S32 end_face;
-
- if (face == -1) // ALL_SIDES
- {
- start_face = 0;
- end_face = getNumVolumeFaces() - 1;
- }
- else
- {
- start_face = face;
- end_face = face;
- }
-
- LLVector4a dir;
- dir.setSub(end, start);
-
- F32 closest_t = 2.f; // must be larger than 1
-
- end_face = llmin(end_face, getNumVolumeFaces()-1);
-
- for (S32 i = start_face; i <= end_face; i++)
- {
- LLVolumeFace &face = mVolumeFaces[i];
-
- LLVector4a box_center;
- box_center.setAdd(face.mExtents[0], face.mExtents[1]);
- box_center.mul(0.5f);
-
- LLVector4a box_size;
- box_size.setSub(face.mExtents[1], face.mExtents[0]);
-
- if (LLLineSegmentBoxIntersect(start, end, box_center, box_size))
- {
- if (tangent_out != NULL) // if the caller wants tangents, we may need to generate them
- {
- genTangents(i);
- }
-
- if (isUnique())
- { //don't bother with an octree for flexi volumes
- U32 tri_count = face.mNumIndices/3;
-
- for (U32 j = 0; j < tri_count; ++j)
- {
- U16 idx0 = face.mIndices[j*3+0];
- U16 idx1 = face.mIndices[j*3+1];
- U16 idx2 = face.mIndices[j*3+2];
-
- const LLVector4a& v0 = face.mPositions[idx0];
- const LLVector4a& v1 = face.mPositions[idx1];
- const LLVector4a& v2 = face.mPositions[idx2];
-
- F32 a,b,t;
-
- if (LLTriangleRayIntersect(v0, v1, v2,
- start, dir, a, b, t))
- {
- if ((t >= 0.f) && // if hit is after start
- (t <= 1.f) && // and before end
- (t < closest_t)) // and this hit is closer
- {
- closest_t = t;
- hit_face = i;
-
- if (intersection != NULL)
- {
- LLVector4a intersect = dir;
- intersect.mul(closest_t);
- intersect.add(start);
- *intersection = intersect;
- }
-
-
- if (tex_coord != NULL)
- {
- LLVector2* tc = (LLVector2*) face.mTexCoords;
- *tex_coord = ((1.f - a - b) * tc[idx0] +
- a * tc[idx1] +
- b * tc[idx2]);
-
- }
-
- if (normal!= NULL)
- {
- LLVector4a* norm = face.mNormals;
-
- LLVector4a n1,n2,n3;
- n1 = norm[idx0];
- n1.mul(1.f-a-b);
-
- n2 = norm[idx1];
- n2.mul(a);
-
- n3 = norm[idx2];
- n3.mul(b);
-
- n1.add(n2);
- n1.add(n3);
-
- *normal = n1;
- }
-
- if (tangent_out != NULL)
- {
- LLVector4a* tangents = face.mTangents;
-
- LLVector4a t1,t2,t3;
- t1 = tangents[idx0];
- t1.mul(1.f-a-b);
-
- t2 = tangents[idx1];
- t2.mul(a);
-
- t3 = tangents[idx2];
- t3.mul(b);
-
- t1.add(t2);
- t1.add(t3);
-
- *tangent_out = t1;
- }
- }
- }
- }
- }
- else
- {
- if (!face.getOctree())
- {
- face.createOctree();
- }
-
- LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
- intersect.traverse(face.getOctree());
- if (intersect.mHitFace)
- {
- hit_face = i;
- }
- }
- }
- }
-
-
- return hit_face;
-}
-
-class LLVertexIndexPair
-{
-public:
- LLVertexIndexPair(const LLVector3 &vertex, const S32 index);
-
- LLVector3 mVertex;
- S32 mIndex;
-};
-
-LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index)
-{
- mVertex = vertex;
- mIndex = index;
-}
-
-constexpr F32 VERTEX_SLOP = 0.00001f;
-
-struct lessVertex
-{
- bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b)
- {
- const F32 slop = VERTEX_SLOP;
-
- if (a->mVertex.mV[0] + slop < b->mVertex.mV[0])
- {
- return true;
- }
- else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0])
- {
- return false;
- }
-
- if (a->mVertex.mV[1] + slop < b->mVertex.mV[1])
- {
- return true;
- }
- else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1])
- {
- return false;
- }
-
- if (a->mVertex.mV[2] + slop < b->mVertex.mV[2])
- {
- return true;
- }
- else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2])
- {
- return false;
- }
-
- return false;
- }
-};
-
-struct lessTriangle
-{
- bool operator()(const S32 *a, const S32 *b)
- {
- if (*a < *b)
- {
- return true;
- }
- else if (*a > *b)
- {
- return false;
- }
-
- if (*(a+1) < *(b+1))
- {
- return true;
- }
- else if (*(a+1) > *(b+1))
- {
- return false;
- }
-
- if (*(a+2) < *(b+2))
- {
- return true;
- }
- else if (*(a+2) > *(b+2))
- {
- return false;
- }
-
- return false;
- }
-};
-
-bool equalTriangle(const S32 *a, const S32 *b)
-{
- if ((*a == *b) && (*(a+1) == *(b+1)) && (*(a+2) == *(b+2)))
- {
- return true;
- }
- return false;
-}
-
-bool LLVolumeParams::importFile(LLFILE *fp)
-{
- //LL_INFOS() << "importing volume" << LL_ENDL;
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of this buffer will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
-
- while (!feof(fp))
- {
- if (fgets(buffer, BUFSIZE, fp) == NULL)
- {
- buffer[0] = '\0';
- }
-
- sscanf(buffer, " %255s", keyword); /* Flawfinder: ignore */
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("profile", keyword))
- {
- mProfileParams.importFile(fp);
- }
- else if (!strcmp("path",keyword))
- {
- mPathParams.importFile(fp);
- }
- else
- {
- LL_WARNS() << "unknown keyword " << keyword << " in volume import" << LL_ENDL;
- }
- }
-
- return true;
-}
-
-bool LLVolumeParams::exportFile(LLFILE *fp) const
-{
- fprintf(fp,"\tshape 0\n");
- fprintf(fp,"\t{\n");
- mPathParams.exportFile(fp);
- mProfileParams.exportFile(fp);
- fprintf(fp, "\t}\n");
- return true;
-}
-
-
-bool LLVolumeParams::importLegacyStream(std::istream& input_stream)
-{
- //LL_INFOS() << "importing volume" << LL_ENDL;
- const S32 BUFSIZE = 16384;
- // *NOTE: changing the size or type of this buffer will require
- // changing the sscanf below.
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- char keyword[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
-
- while (input_stream.good())
- {
- input_stream.getline(buffer, BUFSIZE);
- sscanf(buffer, " %255s", keyword);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("profile", keyword))
- {
- mProfileParams.importLegacyStream(input_stream);
- }
- else if (!strcmp("path",keyword))
- {
- mPathParams.importLegacyStream(input_stream);
- }
- else
- {
- LL_WARNS() << "unknown keyword " << keyword << " in volume import" << LL_ENDL;
- }
- }
-
- return true;
-}
-
-bool LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const
-{
- output_stream <<"\tshape 0\n";
- output_stream <<"\t{\n";
- mPathParams.exportLegacyStream(output_stream);
- mProfileParams.exportLegacyStream(output_stream);
- output_stream << "\t}\n";
- return true;
-}
-
-LLSD LLVolumeParams::sculptAsLLSD() const
-{
- LLSD sd = LLSD();
- sd["id"] = getSculptID();
- sd["type"] = getSculptType();
-
- return sd;
-}
-
-bool LLVolumeParams::sculptFromLLSD(LLSD& sd)
-{
- setSculptID(sd["id"].asUUID(), (U8)sd["type"].asInteger());
- return true;
-}
-
-LLSD LLVolumeParams::asLLSD() const
-{
- LLSD sd = LLSD();
- sd["path"] = mPathParams;
- sd["profile"] = mProfileParams;
- sd["sculpt"] = sculptAsLLSD();
-
- return sd;
-}
-
-bool LLVolumeParams::fromLLSD(LLSD& sd)
-{
- mPathParams.fromLLSD(sd["path"]);
- mProfileParams.fromLLSD(sd["profile"]);
- sculptFromLLSD(sd["sculpt"]);
-
- return true;
-}
-
-void LLVolumeParams::reduceS(F32 begin, F32 end)
-{
- begin = llclampf(begin);
- end = llclampf(end);
- if (begin > end)
- {
- F32 temp = begin;
- begin = end;
- end = temp;
- }
- F32 a = mProfileParams.getBegin();
- F32 b = mProfileParams.getEnd();
- mProfileParams.setBegin(a + begin * (b - a));
- mProfileParams.setEnd(a + end * (b - a));
-}
-
-void LLVolumeParams::reduceT(F32 begin, F32 end)
-{
- begin = llclampf(begin);
- end = llclampf(end);
- if (begin > end)
- {
- F32 temp = begin;
- begin = end;
- end = temp;
- }
- F32 a = mPathParams.getBegin();
- F32 b = mPathParams.getEnd();
- mPathParams.setBegin(a + begin * (b - a));
- mPathParams.setEnd(a + end * (b - a));
-}
-
-const F32 MIN_CONCAVE_PROFILE_WEDGE = 0.125f; // 1/8 unity
-const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f; // 1/9 unity
-
-// returns true if the shape can be approximated with a convex shape
-// for collison purposes
-bool LLVolumeParams::isConvex() const
-{
- if (!getSculptID().isNull())
- {
- // can't determine, be safe and say no:
- return false;
- }
-
- F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
- F32 hollow = mProfileParams.getHollow();
-
- U8 path_type = mPathParams.getCurveType();
- if ( path_length > MIN_CONCAVE_PATH_WEDGE
- && ( mPathParams.getTwist() != mPathParams.getTwistBegin()
- || (hollow > 0.f
- && LL_PCODE_PATH_LINE != path_type) ) )
- {
- // twist along a "not too short" path is concave
- return false;
- }
-
- F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
- bool same_hole = hollow == 0.f
- || (mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK) == LL_PCODE_HOLE_SAME;
-
- F32 min_profile_wedge = MIN_CONCAVE_PROFILE_WEDGE;
- U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
- if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
- {
- // it is a sphere and spheres get twice the minimum profile wedge
- min_profile_wedge = 2.f * MIN_CONCAVE_PROFILE_WEDGE;
- }
-
- bool convex_profile = ( ( profile_length == 1.f
- || profile_length <= 0.5f )
- && hollow == 0.f ) // trivially convex
- || ( profile_length <= min_profile_wedge
- && same_hole ); // effectvely convex (even when hollow)
-
- if (!convex_profile)
- {
- // profile is concave
- return false;
- }
-
- if ( LL_PCODE_PATH_LINE == path_type )
- {
- // straight paths with convex profile
- return true;
- }
-
- bool concave_path = (path_length < 1.0f) && (path_length > 0.5f);
- if (concave_path)
- {
- return false;
- }
-
- // we're left with spheres, toroids and tubes
- if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
- {
- // at this stage all spheres must be convex
- return true;
- }
-
- // it's a toroid or tube
- if ( path_length <= MIN_CONCAVE_PATH_WEDGE )
- {
- // effectively convex
- return true;
- }
-
- return false;
-}
-
-// debug
-void LLVolumeParams::setCube()
-{
- mProfileParams.setCurveType(LL_PCODE_PROFILE_SQUARE);
- mProfileParams.setBegin(0.f);
- mProfileParams.setEnd(1.f);
- mProfileParams.setHollow(0.f);
-
- mPathParams.setBegin(0.f);
- mPathParams.setEnd(1.f);
- mPathParams.setScale(1.f, 1.f);
- mPathParams.setShear(0.f, 0.f);
- mPathParams.setCurveType(LL_PCODE_PATH_LINE);
- mPathParams.setTwistBegin(0.f);
- mPathParams.setTwistEnd(0.f);
- mPathParams.setRadiusOffset(0.f);
- mPathParams.setTaper(0.f, 0.f);
- mPathParams.setRevolutions(0.f);
- mPathParams.setSkew(0.f);
-}
-
-LLFaceID LLVolume::generateFaceMask()
-{
- LLFaceID new_mask = 0x0000;
-
- switch(mParams.getProfileParams().getCurveType() & LL_PCODE_PROFILE_MASK)
- {
- case LL_PCODE_PROFILE_CIRCLE:
- case LL_PCODE_PROFILE_CIRCLE_HALF:
- new_mask |= LL_FACE_OUTER_SIDE_0;
- break;
- case LL_PCODE_PROFILE_SQUARE:
- {
- for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 4.f); side < llceil(mParams.getProfileParams().getEnd() * 4.f); side++)
- {
- new_mask |= LL_FACE_OUTER_SIDE_0 << side;
- }
- }
- break;
- case LL_PCODE_PROFILE_ISOTRI:
- case LL_PCODE_PROFILE_EQUALTRI:
- case LL_PCODE_PROFILE_RIGHTTRI:
- {
- for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 3.f); side < llceil(mParams.getProfileParams().getEnd() * 3.f); side++)
- {
- new_mask |= LL_FACE_OUTER_SIDE_0 << side;
- }
- }
- break;
- default:
- LL_ERRS() << "Unknown profile!" << LL_ENDL;
- break;
- }
-
- // handle hollow objects
- if (mParams.getProfileParams().getHollow() > 0)
- {
- new_mask |= LL_FACE_INNER_SIDE;
- }
-
- // handle open profile curves
- if (mProfilep->isOpen())
- {
- new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END;
- }
-
- // handle open path curves
- if (mPathp->isOpen())
- {
- new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END;
- }
-
- return new_mask;
-}
-
-bool LLVolume::isFaceMaskValid(LLFaceID face_mask)
-{
- LLFaceID test_mask = 0;
- for(S32 i = 0; i < getNumFaces(); i++)
- {
- test_mask |= mProfilep->mFaces[i].mFaceID;
- }
-
- return test_mask == face_mask;
-}
-
-bool LLVolume::isConvex() const
-{
- // mParams.isConvex() may return false even though the final
- // geometry is actually convex due to LOD approximations.
- // TODO -- provide LLPath and LLProfile with isConvex() methods
- // that correctly determine convexity. -- Leviathan
- return mParams.isConvex();
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params)
-{
- s << "{type=" << (U32) profile_params.mCurveType;
- s << ", begin=" << profile_params.mBegin;
- s << ", end=" << profile_params.mEnd;
- s << ", hollow=" << profile_params.mHollow;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params)
-{
- s << "{type=" << (U32) path_params.mCurveType;
- s << ", begin=" << path_params.mBegin;
- s << ", end=" << path_params.mEnd;
- s << ", twist=" << path_params.mTwistEnd;
- s << ", scale=" << path_params.mScale;
- s << ", shear=" << path_params.mShear;
- s << ", twist_begin=" << path_params.mTwistBegin;
- s << ", radius_offset=" << path_params.mRadiusOffset;
- s << ", taper=" << path_params.mTaper;
- s << ", revolutions=" << path_params.mRevolutions;
- s << ", skew=" << path_params.mSkew;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params)
-{
- s << "{profileparams = " << volume_params.mProfileParams;
- s << ", pathparams = " << volume_params.mPathParams;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLProfile &profile)
-{
- s << " {open=" << (U32) profile.mOpen;
- s << ", dirty=" << profile.mDirty;
- s << ", totalout=" << profile.mTotalOut;
- s << ", total=" << profile.mTotal;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLPath &path)
-{
- s << "{open=" << (U32) path.mOpen;
- s << ", dirty=" << path.mDirty;
- s << ", step=" << path.mStep;
- s << ", total=" << path.mTotal;
- s << "}";
- return s;
-}
-
-std::ostream& operator<<(std::ostream &s, const LLVolume &volume)
-{
- s << "{params = " << volume.getParams();
- s << ", path = " << *volume.mPathp;
- s << ", profile = " << *volume.mProfilep;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
-{
- s << "{params = " << volumep->getParams();
- s << ", path = " << *(volumep->mPathp);
- s << ", profile = " << *(volumep->mProfilep);
- s << "}";
- return s;
-}
-
-LLVolumeFace::LLVolumeFace() :
- mID(0),
- mTypeMask(0),
- mBeginS(0),
- mBeginT(0),
- mNumS(0),
- mNumT(0),
- mNumVertices(0),
- mNumAllocatedVertices(0),
- mNumIndices(0),
- mPositions(NULL),
- mNormals(NULL),
- mTangents(NULL),
- mTexCoords(NULL),
- mIndices(NULL),
- mWeights(NULL),
-#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
- mJustWeights(NULL),
- mJointIndices(NULL),
-#endif
- mWeightsScrubbed(false),
- mOctree(NULL),
- mOctreeTriangles(NULL),
- mOptimized(false)
-{
- mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
- mExtents[0].splat(-0.5f);
- mExtents[1].splat(0.5f);
- mCenter = mExtents+2;
-}
-
-LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
-: mID(0),
- mTypeMask(0),
- mBeginS(0),
- mBeginT(0),
- mNumS(0),
- mNumT(0),
- mNumVertices(0),
- mNumAllocatedVertices(0),
- mNumIndices(0),
- mPositions(NULL),
- mNormals(NULL),
- mTangents(NULL),
- mTexCoords(NULL),
- mIndices(NULL),
- mWeights(NULL),
-#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
- mJustWeights(NULL),
- mJointIndices(NULL),
-#endif
- mWeightsScrubbed(false),
- mOctree(NULL),
- mOctreeTriangles(NULL)
-{
- mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
- mCenter = mExtents+2;
- *this = src;
-}
-
-LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
-{
- if (&src == this)
- { //self assignment, do nothing
- return *this;
- }
-
- mID = src.mID;
- mTypeMask = src.mTypeMask;
- mBeginS = src.mBeginS;
- mBeginT = src.mBeginT;
- mNumS = src.mNumS;
- mNumT = src.mNumT;
-
- mExtents[0] = src.mExtents[0];
- mExtents[1] = src.mExtents[1];
- *mCenter = *src.mCenter;
-
- mNumVertices = 0;
- mNumIndices = 0;
-
- freeData();
-
- resizeVertices(src.mNumVertices);
- resizeIndices(src.mNumIndices);
-
- if (mNumVertices)
- {
- S32 vert_size = mNumVertices*sizeof(LLVector4a);
- S32 tc_size = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF;
-
- LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) src.mPositions, vert_size);
-
- if (src.mNormals)
- {
- LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) src.mNormals, vert_size);
- }
-
- if(src.mTexCoords)
- {
- LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size);
- }
-
- if (src.mTangents)
- {
- allocateTangents(src.mNumVertices);
- LLVector4a::memcpyNonAliased16((F32*) mTangents, (F32*) src.mTangents, vert_size);
- }
- else
- {
- ll_aligned_free_16(mTangents);
- mTangents = NULL;
- }
-
- if (src.mWeights)
- {
- llassert(!mWeights); // don't orphan an old alloc here accidentally
- allocateWeights(src.mNumVertices);
- LLVector4a::memcpyNonAliased16((F32*) mWeights, (F32*) src.mWeights, vert_size);
- mWeightsScrubbed = src.mWeightsScrubbed;
- }
- else
- {
- ll_aligned_free_16(mWeights);
- mWeights = NULL;
- mWeightsScrubbed = false;
- }
-
- #if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
- if (src.mJointIndices)
- {
- llassert(!mJointIndices); // don't orphan an old alloc here accidentally
- allocateJointIndices(src.mNumVertices);
- LLVector4a::memcpyNonAliased16((F32*) mJointIndices, (F32*) src.mJointIndices, src.mNumVertices * sizeof(U8) * 4);
- }
- else*/
- {
- ll_aligned_free_16(mJointIndices);
- mJointIndices = NULL;
- }
- #endif
-
- }
-
- if (mNumIndices)
- {
- S32 idx_size = (mNumIndices*sizeof(U16)+0xF) & ~0xF;
-
- LLVector4a::memcpyNonAliased16((F32*) mIndices, (F32*) src.mIndices, idx_size);
- }
- else
- {
- ll_aligned_free_16(mIndices);
- mIndices = NULL;
- }
-
- mOptimized = src.mOptimized;
- mNormalizedScale = src.mNormalizedScale;
-
- //delete
- return *this;
-}
-
-LLVolumeFace::~LLVolumeFace()
-{
- ll_aligned_free_16(mExtents);
- mExtents = NULL;
- mCenter = NULL;
-
- freeData();
-}
-
-void LLVolumeFace::freeData()
-{
- ll_aligned_free<64>(mPositions);
- mPositions = NULL;
-
- //normals and texture coordinates are part of the same buffer as mPositions, do not free them separately
- mNormals = NULL;
- mTexCoords = NULL;
-
- ll_aligned_free_16(mIndices);
- mIndices = NULL;
- ll_aligned_free_16(mTangents);
- mTangents = NULL;
- ll_aligned_free_16(mWeights);
- mWeights = NULL;
-
-#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
- ll_aligned_free_16(mJointIndices);
- mJointIndices = NULL;
- ll_aligned_free_16(mJustWeights);
- mJustWeights = NULL;
-#endif
-
- destroyOctree();
-}
-
-bool LLVolumeFace::create(LLVolume* volume, bool partial_build)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- //tree for this face is no longer valid
- destroyOctree();
-
- LL_CHECK_MEMORY
- bool ret = false ;
- if (mTypeMask & CAP_MASK)
- {
- ret = createCap(volume, partial_build);
- LL_CHECK_MEMORY
- }
- else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
- {
- ret = createSide(volume, partial_build);
- LL_CHECK_MEMORY
- }
- else
- {
- LL_ERRS() << "Unknown/uninitialized face type!" << LL_ENDL;
- }
-
- return ret ;
-}
-
-void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv)
-{
- cv.setPosition(mPositions[index]);
- if (mNormals)
- {
- cv.setNormal(mNormals[index]);
- }
- else
- {
- cv.getNormal().clear();
- }
-
- if (mTexCoords)
- {
- cv.mTexCoord = mTexCoords[index];
- }
- else
- {
- cv.mTexCoord.clear();
- }
-}
-
-bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const
-{
- return getPosition().equals3(rhs.getPosition()) &&
- mTexCoord == rhs.mTexCoord &&
- getNormal().equals3(rhs.getNormal());
-}
-
-bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a, const LLVector3& b) const
-{
- if (a.mV[0] != b.mV[0])
- {
- return a.mV[0] < b.mV[0];
- }
-
- if (a.mV[1] != b.mV[1])
- {
- return a.mV[1] < b.mV[1];
- }
-
- return a.mV[2] < b.mV[2];
-}
-
-void LLVolumeFace::remap()
-{
- // Generate a remap buffer
- std::vector<unsigned int> remap(mNumVertices);
- S32 remap_vertices_count = LLMeshOptimizer::generateRemapMultiU16(&remap[0],
- mIndices,
- mNumIndices,
- mPositions,
- mNormals,
- mTexCoords,
- mNumVertices);
-
- // Allocate new buffers
- S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF;
- U16* remap_indices = (U16*)ll_aligned_malloc_16(size);
-
- S32 tc_bytes_size = ((remap_vertices_count * sizeof(LLVector2)) + 0xF) & ~0xF;
- LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * remap_vertices_count + tc_bytes_size);
- LLVector4a* remap_normals = remap_positions + remap_vertices_count;
- LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count);
-
- // Fill the buffers
- LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]);
- LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]);
- LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]);
- LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]);
-
- // Free unused buffers
- ll_aligned_free_16(mIndices);
- ll_aligned_free<64>(mPositions);
-
- // Tangets are now invalid
- ll_aligned_free_16(mTangents);
- mTangents = NULL;
-
- // Assign new values
- mIndices = remap_indices;
- mPositions = remap_positions;
- mNormals = remap_normals;
- mTexCoords = remap_tex_coords;
- mNumVertices = remap_vertices_count;
- mNumAllocatedVertices = remap_vertices_count;
-}
-
-void LLVolumeFace::optimize(F32 angle_cutoff)
-{
- LLVolumeFace new_face;
-
- //map of points to vector of vertices at that point
- std::map<U64, std::vector<VertexMapData> > point_map;
-
- LLVector4a range;
- range.setSub(mExtents[1],mExtents[0]);
-
- //remove redundant vertices
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- U16 index = mIndices[i];
-
- if (index >= mNumVertices)
- {
- // invalid index
- // replace with a valid index to avoid crashes
- index = mNumVertices - 1;
- mIndices[i] = index;
-
- // Needs better logging
- LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL;
- }
-
- LLVolumeFace::VertexData cv;
- getVertexData(index, cv);
-
- bool found = false;
-
- LLVector4a pos;
- pos.setSub(mPositions[index], mExtents[0]);
- pos.div(range);
-
- U64 pos64 = 0;
-
- pos64 = (U16) (pos[0]*65535);
- pos64 = pos64 | (((U64) (pos[1]*65535)) << 16);
- pos64 = pos64 | (((U64) (pos[2]*65535)) << 32);
-
- std::map<U64, std::vector<VertexMapData> >::iterator point_iter = point_map.find(pos64);
-
- if (point_iter != point_map.end())
- { //duplicate point might exist
- for (U32 j = 0; j < point_iter->second.size(); ++j)
- {
- LLVolumeFace::VertexData& tv = (point_iter->second)[j];
- if (tv.compareNormal(cv, angle_cutoff))
- {
- found = true;
- new_face.pushIndex((point_iter->second)[j].mIndex);
- break;
- }
- }
- }
-
- if (!found)
- {
- new_face.pushVertex(cv);
- U16 index = (U16) new_face.mNumVertices-1;
- new_face.pushIndex(index);
-
- 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[pos64].push_back(d);
- }
- }
- }
-
-
- if (angle_cutoff > 1.f && !mNormals)
- {
- // Now alloc'd with positions
- //ll_aligned_free_16(new_face.mNormals);
- new_face.mNormals = NULL;
- }
-
- if (!mTexCoords)
- {
- // Now alloc'd with positions
- //ll_aligned_free_16(new_face.mTexCoords);
- new_face.mTexCoords = NULL;
- }
-
- // Only swap data if we've actually optimized the mesh
- //
- if (new_face.mNumVertices <= mNumVertices)
- {
- llassert(new_face.mNumIndices == mNumIndices);
- swapData(new_face);
- }
-
-}
-
-class LLVCacheTriangleData;
-
-class LLVCacheVertexData
-{
-public:
- S32 mIdx;
- S32 mCacheTag;
- F64 mScore;
- U32 mActiveTriangles;
- std::vector<LLVCacheTriangleData*> mTriangles;
-
- LLVCacheVertexData()
- {
- mCacheTag = -1;
- mScore = 0.0;
- mActiveTriangles = 0;
- mIdx = -1;
- }
-};
-
-class LLVCacheTriangleData
-{
-public:
- bool mActive;
- F64 mScore;
- LLVCacheVertexData* mVertex[3];
-
- LLVCacheTriangleData()
- {
- mActive = true;
- mScore = 0.0;
- mVertex[0] = mVertex[1] = mVertex[2] = NULL;
- }
-
- void complete()
- {
- mActive = false;
- for (S32 i = 0; i < 3; ++i)
- {
- if (mVertex[i])
- {
- llassert(mVertex[i]->mActiveTriangles > 0);
- mVertex[i]->mActiveTriangles--;
- }
- }
- }
-
- bool operator<(const LLVCacheTriangleData& rhs) const
- { //highest score first
- return rhs.mScore < mScore;
- }
-};
-
-constexpr F64 FindVertexScore_CacheDecayPower = 1.5;
-constexpr F64 FindVertexScore_LastTriScore = 0.75;
-constexpr F64 FindVertexScore_ValenceBoostScale = 2.0;
-constexpr F64 FindVertexScore_ValenceBoostPower = 0.5;
-constexpr U32 MaxSizeVertexCache = 32;
-constexpr F64 FindVertexScore_Scaler = 1.0/(MaxSizeVertexCache-3);
-
-F64 find_vertex_score(LLVCacheVertexData& data)
-{
- F64 score = -1.0;
-
- score = 0.0;
-
- S32 cache_idx = data.mCacheTag;
-
- if (cache_idx < 0)
- {
- //not in cache
- }
- else
- {
- if (cache_idx < 3)
- { //vertex was in the last triangle
- score = FindVertexScore_LastTriScore;
- }
- else
- { //more points for being higher in the cache
- score = 1.0-((cache_idx-3)*FindVertexScore_Scaler);
- score = pow(score, FindVertexScore_CacheDecayPower);
- }
- }
-
- //bonus points for having low valence
- F64 valence_boost = pow((F64)data.mActiveTriangles, -FindVertexScore_ValenceBoostPower);
- score += FindVertexScore_ValenceBoostScale * valence_boost;
-
- return score;
-}
-
-class LLVCacheFIFO
-{
-public:
- LLVCacheVertexData* mCache[MaxSizeVertexCache];
- U32 mMisses;
-
- LLVCacheFIFO()
- {
- mMisses = 0;
- for (U32 i = 0; i < MaxSizeVertexCache; ++i)
- {
- mCache[i] = NULL;
- }
- }
-
- void addVertex(LLVCacheVertexData* data)
- {
- if (data->mCacheTag == -1)
- {
- mMisses++;
-
- S32 end = MaxSizeVertexCache-1;
-
- if (mCache[end])
- {
- mCache[end]->mCacheTag = -1;
- }
-
- for (S32 i = end; i > 0; --i)
- {
- mCache[i] = mCache[i-1];
- if (mCache[i])
- {
- mCache[i]->mCacheTag = i;
- }
- }
-
- mCache[0] = data;
- data->mCacheTag = 0;
- }
- }
-};
-
-class LLVCacheLRU
-{
-public:
- LLVCacheVertexData* mCache[MaxSizeVertexCache+3];
-
- LLVCacheTriangleData* mBestTriangle;
-
- U32 mMisses;
-
- LLVCacheLRU()
- {
- for (U32 i = 0; i < MaxSizeVertexCache+3; ++i)
- {
- mCache[i] = NULL;
- }
-
- mBestTriangle = NULL;
- mMisses = 0;
- }
-
- void addVertex(LLVCacheVertexData* data)
- {
- S32 end = MaxSizeVertexCache+2;
- if (data->mCacheTag != -1)
- { //just moving a vertex to the front of the cache
- end = data->mCacheTag;
- }
- else
- {
- mMisses++;
- if (mCache[end])
- { //adding a new vertex, vertex at end of cache falls off
- mCache[end]->mCacheTag = -1;
- }
- }
-
- for (S32 i = end; i > 0; --i)
- { //adjust cache pointers and tags
- mCache[i] = mCache[i-1];
-
- if (mCache[i])
- {
- mCache[i]->mCacheTag = i;
- }
- }
-
- mCache[0] = data;
- mCache[0]->mCacheTag = 0;
- }
-
- void addTriangle(LLVCacheTriangleData* data)
- {
- addVertex(data->mVertex[0]);
- addVertex(data->mVertex[1]);
- addVertex(data->mVertex[2]);
- }
-
- void updateScores()
- {
- LLVCacheVertexData** data_iter = mCache+MaxSizeVertexCache;
- LLVCacheVertexData** end_data = mCache+MaxSizeVertexCache+3;
-
- while(data_iter != end_data)
- {
- LLVCacheVertexData* data = *data_iter++;
- //trailing 3 vertices aren't actually in the cache for scoring purposes
- if (data)
- {
- data->mCacheTag = -1;
- }
- }
-
- data_iter = mCache;
- end_data = mCache+MaxSizeVertexCache;
-
- while (data_iter != end_data)
- { //update scores of vertices in cache
- LLVCacheVertexData* data = *data_iter++;
- if (data)
- {
- data->mScore = find_vertex_score(*data);
- }
- }
-
- mBestTriangle = NULL;
- //update triangle scores
- data_iter = mCache;
- end_data = mCache+MaxSizeVertexCache+3;
-
- while (data_iter != end_data)
- {
- LLVCacheVertexData* data = *data_iter++;
- if (data)
- {
- for (std::vector<LLVCacheTriangleData*>::iterator iter = data->mTriangles.begin(), end_iter = data->mTriangles.end(); iter != end_iter; ++iter)
- {
- LLVCacheTriangleData* tri = *iter;
- if (tri->mActive)
- {
- tri->mScore = tri->mVertex[0] ? tri->mVertex[0]->mScore : 0;
- tri->mScore += tri->mVertex[1] ? tri->mVertex[1]->mScore : 0;
- tri->mScore += tri->mVertex[2] ? tri->mVertex[2]->mScore : 0;
-
- if (!mBestTriangle || mBestTriangle->mScore < tri->mScore)
- {
- mBestTriangle = tri;
- }
- }
- }
- }
- }
-
- //knock trailing 3 vertices off the cache
- data_iter = mCache+MaxSizeVertexCache;
- end_data = mCache+MaxSizeVertexCache+3;
- while (data_iter != end_data)
- {
- LLVCacheVertexData* data = *data_iter;
- if (data)
- {
- llassert(data->mCacheTag == -1);
- *data_iter = NULL;
- }
- ++data_iter;
- }
- }
-};
-
-// data structures for tangent generation
-
-struct MikktData
-{
- LLVolumeFace* face;
- std::vector<LLVector3> p;
- std::vector<LLVector3> n;
- std::vector<LLVector2> tc;
- std::vector<LLVector4> w;
- std::vector<LLVector4> t;
-
- MikktData(LLVolumeFace* f)
- : face(f)
- {
- U32 count = face->mNumIndices;
-
- p.resize(count);
- n.resize(count);
- tc.resize(count);
- t.resize(count);
-
- if (face->mWeights)
- {
- w.resize(count);
- }
-
-
- LLVector3 inv_scale(1.f / face->mNormalizedScale.mV[0], 1.f / face->mNormalizedScale.mV[1], 1.f / face->mNormalizedScale.mV[2]);
-
-
- for (int i = 0; i < face->mNumIndices; ++i)
- {
- U32 idx = face->mIndices[i];
-
- p[i].set(face->mPositions[idx].getF32ptr());
- p[i].scaleVec(face->mNormalizedScale); //put mesh in original coordinate frame when reconstructing tangents
- n[i].set(face->mNormals[idx].getF32ptr());
- n[i].scaleVec(inv_scale);
- n[i].normalize();
- tc[i].set(face->mTexCoords[idx]);
-
- if (idx >= face->mNumVertices)
- {
- // invalid index
- // replace with a valid index to avoid crashes
- idx = face->mNumVertices - 1;
- face->mIndices[i] = idx;
-
- // Needs better logging
- LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL;
- }
-
- if (face->mWeights)
- {
- w[i].set(face->mWeights[idx].getF32ptr());
- }
- }
- }
-};
-
-
-bool LLVolumeFace::cacheOptimize(bool gen_tangents)
-{ //optimize for vertex cache according to Forsyth method:
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
- llassert(!mOptimized);
- mOptimized = true;
-
- if (gen_tangents && mNormals && mTexCoords)
- { // 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
- SMikkTSpaceInterface ms;
-
- ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- LLVolumeFace* face = data->face;
- return face->mNumIndices / 3;
- };
-
- ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace)
- {
- return 3;
- };
-
- ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- F32* v = data->p[iFace * 3 + iVert].mV;
- fvPosOut[0] = v[0];
- fvPosOut[1] = v[1];
- fvPosOut[2] = v[2];
- };
-
- ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- F32* n = data->n[iFace * 3 + iVert].mV;
- fvNormOut[0] = n[0];
- fvNormOut[1] = n[1];
- fvNormOut[2] = n[2];
- };
-
- ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- F32* tc = data->tc[iFace * 3 + iVert].mV;
- fvTexcOut[0] = tc[0];
- fvTexcOut[1] = tc[1];
- };
-
- ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- S32 i = iFace * 3 + iVert;
-
- data->t[i].set(fvTangent);
- data->t[i].mV[3] = fSign;
- };
-
- ms.m_setTSpace = nullptr;
-
- MikktData data(this);
-
- SMikkTSpaceContext ctx = { &ms, &data };
-
- genTangSpaceDefault(&ctx);
-
- //re-weld
- meshopt_Stream mos[] =
- {
- { &data.p[0], sizeof(LLVector3), sizeof(LLVector3) },
- { &data.n[0], sizeof(LLVector3), sizeof(LLVector3) },
- { &data.t[0], sizeof(LLVector4), sizeof(LLVector4) },
- { &data.tc[0], sizeof(LLVector2), sizeof(LLVector2) },
- { data.w.empty() ? nullptr : &data.w[0], sizeof(LLVector4), sizeof(LLVector4) }
- };
-
- std::vector<U32> remap;
- remap.resize(data.p.size());
-
- U32 stream_count = data.w.empty() ? 4 : 5;
-
- size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count);
-
- if (vert_count < 65535 && vert_count != 0)
- {
- std::vector<U32> indices;
- indices.resize(mNumIndices);
-
- //copy results back into volume
- resizeVertices(vert_count);
-
- if (!data.w.empty())
- {
- allocateWeights(vert_count);
- }
-
- allocateTangents(mNumVertices);
-
- for (int i = 0; i < mNumIndices; ++i)
- {
- U32 src_idx = i;
- U32 dst_idx = remap[i];
- if (dst_idx >= mNumVertices)
- {
- dst_idx = mNumVertices - 1;
- // Shouldn't happen, figure out what gets returned in remap and why.
- llassert(false);
- LL_DEBUGS_ONCE("LLVOLUME") << "Invalid destination index, substituting" << LL_ENDL;
- }
- mIndices[i] = dst_idx;
-
- mPositions[dst_idx].load3(data.p[src_idx].mV);
- mNormals[dst_idx].load3(data.n[src_idx].mV);
- mTexCoords[dst_idx] = data.tc[src_idx];
-
- mTangents[dst_idx].loadua(data.t[src_idx].mV);
-
- if (mWeights)
- {
- mWeights[dst_idx].loadua(data.w[src_idx].mV);
- }
- }
-
- // put back in normalized coordinate frame
- LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]);
- LLVector4a scale;
- scale.load3(mNormalizedScale.mV);
- scale.getF32ptr()[3] = 1.f;
-
- for (int i = 0; i < mNumVertices; ++i)
- {
- mPositions[i].mul(inv_scale);
- mNormals[i].mul(scale);
- mNormals[i].normalize3();
- F32 w = mTangents[i].getF32ptr()[3];
- mTangents[i].mul(scale);
- mTangents[i].normalize3();
- mTangents[i].getF32ptr()[3] = w;
- }
- }
- else
- {
- if (vert_count == 0)
- {
- LL_WARNS_ONCE("LLVOLUME") << "meshopt_generateVertexRemapMulti failed to process a model or model was invalid" << LL_ENDL;
- }
- // blew past the max vertex size limit, use legacy tangent generation which never adds verts
- createTangents();
- }
- }
-
- // cache optimize index buffer
-
- // meshopt needs scratch space, do some pointer shuffling to avoid an extra index buffer copy
- U16* src_indices = mIndices;
- mIndices = nullptr;
- resizeIndices(mNumIndices);
-
- meshopt_optimizeVertexCache<U16>(mIndices, src_indices, mNumIndices, mNumVertices);
-
- ll_aligned_free_16(src_indices);
-
- return true;
-}
-
-void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVector4a& size)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- if (getOctree())
- {
- return;
- }
-
- llassert(mNumIndices % 3 == 0);
-
- mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL);
- new LLVolumeOctreeListener(mOctree);
- const U32 num_triangles = mNumIndices / 3;
- // Initialize all the triangles we need
- mOctreeTriangles = new LLVolumeTriangle[num_triangles];
-
- for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index)
- { //for each triangle
- const U32 index = triangle_index * 3;
- LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index];
-
- const LLVector4a& v0 = mPositions[mIndices[index]];
- const LLVector4a& v1 = mPositions[mIndices[index + 1]];
- const LLVector4a& v2 = mPositions[mIndices[index + 2]];
-
- //store pointers to vertex data
- tri->mV[0] = &v0;
- tri->mV[1] = &v1;
- tri->mV[2] = &v2;
-
- //store indices
- tri->mIndex[0] = mIndices[index];
- tri->mIndex[1] = mIndices[index + 1];
- tri->mIndex[2] = mIndices[index + 2];
-
- //get minimum point
- LLVector4a min = v0;
- min.setMin(min, v1);
- min.setMin(min, v2);
-
- //get maximum point
- LLVector4a max = v0;
- max.setMax(max, v1);
- max.setMax(max, v2);
-
- //compute center
- LLVector4a center;
- center.setAdd(min, max);
- center.mul(0.5f);
-
- tri->mPositionGroup = center;
-
- //compute "radius"
- LLVector4a size;
- size.setSub(max,min);
-
- tri->mRadius = size.getLength3().getF32() * scaler;
-
- //insert
- mOctree->insert(tri);
- }
-
- //remove unneeded octree layers
- while (!mOctree->balance()) { }
-
- //calculate AABB for each node
- LLVolumeOctreeRebound rebound(this);
- rebound.traverse(mOctree);
-
- if (gDebugGL)
- {
- LLVolumeOctreeValidate validate;
- validate.traverse(mOctree);
- }
-}
-
-void LLVolumeFace::destroyOctree()
-{
- delete mOctree;
- mOctree = NULL;
- delete[] mOctreeTriangles;
- mOctreeTriangles = NULL;
-}
-
-const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const
-{
- return mOctree;
-}
-
-
-void LLVolumeFace::swapData(LLVolumeFace& rhs)
-{
- llswap(rhs.mPositions, mPositions);
- llswap(rhs.mNormals, mNormals);
- llswap(rhs.mTangents, mTangents);
- llswap(rhs.mTexCoords, mTexCoords);
- llswap(rhs.mIndices,mIndices);
- llswap(rhs.mNumVertices, mNumVertices);
- llswap(rhs.mNumIndices, mNumIndices);
-}
-
-void LerpPlanarVertex(LLVolumeFace::VertexData& v0,
- LLVolumeFace::VertexData& v1,
- LLVolumeFace::VertexData& v2,
- LLVolumeFace::VertexData& vout,
- F32 coef01,
- F32 coef02)
-{
-
- LLVector4a lhs;
- lhs.setSub(v1.getPosition(), v0.getPosition());
- lhs.mul(coef01);
- LLVector4a rhs;
- rhs.setSub(v2.getPosition(), v0.getPosition());
- rhs.mul(coef02);
-
- rhs.add(lhs);
- rhs.add(v0.getPosition());
-
- vout.setPosition(rhs);
-
- vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02);
- vout.setNormal(v0.getNormal());
-}
-
-bool LLVolumeFace::createUnCutCubeCap(LLVolume* volume, bool partial_build)
-{
- LL_CHECK_MEMORY
-
- const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh();
- const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile;
- S32 max_s = volume->getProfile().getTotal();
- S32 max_t = volume->getPath().mPath.size();
-
- // S32 i;
- S32 grid_size = (profile.size()-1)/4;
- // VFExtents change
- LLVector4a& min = mExtents[0];
- LLVector4a& max = mExtents[1];
-
- S32 offset = 0;
- if (mTypeMask & TOP_MASK)
- {
- offset = (max_t-1) * max_s;
- }
- else
- {
- offset = mBeginS;
- }
-
- {
- VertexData corners[4];
- VertexData baseVert;
- for(S32 t = 0; t < 4; t++)
- {
- corners[t].getPosition().load4a(mesh[offset + (grid_size*t)].getF32ptr());
- corners[t].mTexCoord.mV[0] = profile[grid_size*t][0]+0.5f;
- corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t][1];
- }
-
- {
- LLVector4a lhs;
- lhs.setSub(corners[1].getPosition(), corners[0].getPosition());
- LLVector4a rhs;
- rhs.setSub(corners[2].getPosition(), corners[1].getPosition());
- baseVert.getNormal().setCross3(lhs, rhs);
- baseVert.getNormal().normalize3fast();
- }
-
- if(!(mTypeMask & TOP_MASK))
- {
- baseVert.getNormal().mul(-1.0f);
- }
- else
- {
- //Swap the UVs on the U(X) axis for top face
- LLVector2 swap;
- swap = corners[0].mTexCoord;
- corners[0].mTexCoord=corners[3].mTexCoord;
- corners[3].mTexCoord=swap;
- swap = corners[1].mTexCoord;
- corners[1].mTexCoord=corners[2].mTexCoord;
- corners[2].mTexCoord=swap;
- }
-
- S32 size = (grid_size+1)*(grid_size+1);
- resizeVertices(size);
-
- LLVector4a* pos = (LLVector4a*) mPositions;
- LLVector4a* norm = (LLVector4a*) mNormals;
- LLVector2* tc = (LLVector2*) mTexCoords;
-
- for(int gx = 0;gx<grid_size+1;gx++)
- {
- for(int gy = 0;gy<grid_size+1;gy++)
- {
- VertexData newVert;
- LerpPlanarVertex(
- corners[0],
- corners[1],
- corners[3],
- newVert,
- (F32)gx/(F32)grid_size,
- (F32)gy/(F32)grid_size);
-
- *pos++ = newVert.getPosition();
- *norm++ = baseVert.getNormal();
- *tc++ = newVert.mTexCoord;
-
- if (gx == 0 && gy == 0)
- {
- min = newVert.getPosition();
- max = min;
- }
- else
- {
- min.setMin(min, newVert.getPosition());
- max.setMax(max, newVert.getPosition());
- }
- }
- }
-
- mCenter->setAdd(min, max);
- mCenter->mul(0.5f);
- }
-
- if (!partial_build)
- {
- resizeIndices(grid_size*grid_size*6);
- if (!volume->isMeshAssetLoaded())
- {
- S32 size = grid_size * grid_size * 6;
- try
- {
- mEdge.resize(size);
- }
- catch (std::bad_alloc&)
- {
- LL_WARNS("LLVOLUME") << "Resize of mEdge to " << size << " failed" << LL_ENDL;
- return false;
- }
- }
-
- U16* out = mIndices;
-
- S32 idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
-
- int cur_edge = 0;
-
- for(S32 gx = 0;gx<grid_size;gx++)
- {
-
- for(S32 gy = 0;gy<grid_size;gy++)
- {
- if (mTypeMask & TOP_MASK)
- {
- for(S32 i=5;i>=0;i--)
- {
- *out++ = ((gy*(grid_size+1))+gx+idxs[i]);
- }
-
- S32 edge_value = grid_size * 2 * gy + gx * 2;
-
- if (gx > 0)
- {
- mEdge[cur_edge++] = edge_value;
- }
- else
- {
- mEdge[cur_edge++] = -1; // Mark face to higlight it
- }
-
- if (gy < grid_size - 1)
- {
- mEdge[cur_edge++] = edge_value;
- }
- else
- {
- mEdge[cur_edge++] = -1;
- }
-
- mEdge[cur_edge++] = edge_value;
-
- if (gx < grid_size - 1)
- {
- mEdge[cur_edge++] = edge_value;
- }
- else
- {
- mEdge[cur_edge++] = -1;
- }
-
- if (gy > 0)
- {
- mEdge[cur_edge++] = edge_value;
- }
- else
- {
- mEdge[cur_edge++] = -1;
- }
-
- mEdge[cur_edge++] = edge_value;
- }
- else
- {
- for(S32 i=0;i<6;i++)
- {
- *out++ = ((gy*(grid_size+1))+gx+idxs[i]);
- }
-
- S32 edge_value = grid_size * 2 * gy + gx * 2;
-
- if (gy > 0)
- {
- mEdge[cur_edge++] = edge_value;
- }
- else
- {
- mEdge[cur_edge++] = -1;
- }
-
- if (gx < grid_size - 1)
- {
- mEdge[cur_edge++] = edge_value;
- }
- else
- {
- mEdge[cur_edge++] = -1;
- }
-
- mEdge[cur_edge++] = edge_value;
-
- if (gy < grid_size - 1)
- {
- mEdge[cur_edge++] = edge_value;
- }
- else
- {
- mEdge[cur_edge++] = -1;
- }
-
- if (gx > 0)
- {
- mEdge[cur_edge++] = edge_value;
- }
- else
- {
- mEdge[cur_edge++] = -1;
- }
-
- mEdge[cur_edge++] = edge_value;
- }
- }
- }
- }
-
- LL_CHECK_MEMORY
- return true;
-}
-
-
-bool LLVolumeFace::createCap(LLVolume* volume, bool partial_build)
-{
- if (!(mTypeMask & HOLLOW_MASK) &&
- !(mTypeMask & OPEN_MASK) &&
- ((volume->getParams().getPathParams().getBegin()==0.0f)&&
- (volume->getParams().getPathParams().getEnd()==1.0f))&&
- (volume->getParams().getProfileParams().getCurveType()==LL_PCODE_PROFILE_SQUARE &&
- volume->getParams().getPathParams().getCurveType()==LL_PCODE_PATH_LINE)
- ){
- return createUnCutCubeCap(volume, partial_build);
- }
-
- S32 num_vertices = 0, num_indices = 0;
-
- const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh();
- const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile;
-
- // All types of caps have the same number of vertices and indices
- num_vertices = profile.size();
- num_indices = (profile.size() - 2)*3;
-
- if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
- {
- resizeVertices(num_vertices+1);
-
- //if (!partial_build)
- {
- resizeIndices(num_indices+3);
- }
- }
- else
- {
- resizeVertices(num_vertices);
- //if (!partial_build)
- {
- resizeIndices(num_indices);
- }
- }
-
- LL_CHECK_MEMORY;
-
- S32 max_s = volume->getProfile().getTotal();
- S32 max_t = volume->getPath().mPath.size();
-
- mCenter->clear();
-
- S32 offset = 0;
- if (mTypeMask & TOP_MASK)
- {
- offset = (max_t-1) * max_s;
- }
- else
- {
- offset = mBeginS;
- }
-
- // Figure out the normal, assume all caps are flat faces.
- // Cross product to get normals.
-
- LLVector2 cuv;
- LLVector2 min_uv, max_uv;
- // VFExtents change
- LLVector4a& min = mExtents[0];
- LLVector4a& max = mExtents[1];
-
- LLVector2* tc = (LLVector2*) mTexCoords;
- LLVector4a* pos = (LLVector4a*) mPositions;
- LLVector4a* norm = (LLVector4a*) mNormals;
-
- // Copy the vertices into the array
-
- const LLVector4a* src = mesh.mArray+offset;
- const LLVector4a* end = src+num_vertices;
-
- min = *src;
- max = min;
-
-
- const LLVector4a* p = profile.mArray;
-
- if (mTypeMask & TOP_MASK)
- {
- min_uv.set((*p)[0]+0.5f,
- (*p)[1]+0.5f);
-
- max_uv = min_uv;
-
- while(src < end)
- {
- tc->mV[0] = (*p)[0]+0.5f;
- tc->mV[1] = (*p)[1]+0.5f;
-
- llassert(src->isFinite3()); // MAINT-5660; don't know why this happens, does not affect Release builds
- update_min_max(min,max,*src);
- update_min_max(min_uv, max_uv, *tc);
-
- *pos = *src;
-
- llassert(pos->isFinite3());
-
- ++p;
- ++tc;
- ++src;
- ++pos;
- }
- }
- else
- {
-
- min_uv.set((*p)[0]+0.5f,
- 0.5f - (*p)[1]);
- max_uv = min_uv;
-
- while(src < end)
- {
- // Mirror for underside.
- tc->mV[0] = (*p)[0]+0.5f;
- tc->mV[1] = 0.5f - (*p)[1];
-
- llassert(src->isFinite3());
- update_min_max(min,max,*src);
- update_min_max(min_uv, max_uv, *tc);
-
- *pos = *src;
-
- llassert(pos->isFinite3());
-
- ++p;
- ++tc;
- ++src;
- ++pos;
- }
- }
-
- LL_CHECK_MEMORY
-
- mCenter->setAdd(min, max);
- mCenter->mul(0.5f);
-
- cuv = (min_uv + max_uv)*0.5f;
-
-
- VertexData vd;
- vd.setPosition(*mCenter);
- vd.mTexCoord = cuv;
-
- if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
- {
- *pos++ = *mCenter;
- *tc++ = cuv;
- num_vertices++;
- }
-
- LL_CHECK_MEMORY
-
- //if (partial_build)
- //{
- // return true;
- //}
-
- if (mTypeMask & HOLLOW_MASK)
- {
- if (mTypeMask & TOP_MASK)
- {
- // HOLLOW TOP
- // Does it matter if it's open or closed? - djs
-
- S32 pt1 = 0, pt2 = num_vertices - 1;
- S32 i = 0;
- while (pt2 - pt1 > 1)
- {
- // Use the profile points instead of the mesh, since you want
- // the un-transformed profile distances.
- const LLVector4a& p1 = profile[pt1];
- const LLVector4a& p2 = profile[pt2];
- const LLVector4a& pa = profile[pt1+1];
- const LLVector4a& pb = profile[pt2-1];
-
- const F32* p1V = p1.getF32ptr();
- const F32* p2V = p2.getF32ptr();
- const F32* paV = pa.getF32ptr();
- const F32* pbV = pb.getF32ptr();
-
- //p1.mV[VZ] = 0.f;
- //p2.mV[VZ] = 0.f;
- //pa.mV[VZ] = 0.f;
- //pb.mV[VZ] = 0.f;
-
- // Use area of triangle to determine backfacing
- F32 area_1a2, area_1ba, area_21b, area_2ab;
- area_1a2 = (p1V[0]*paV[1] - paV[0]*p1V[1]) +
- (paV[0]*p2V[1] - p2V[0]*paV[1]) +
- (p2V[0]*p1V[1] - p1V[0]*p2V[1]);
-
- area_1ba = (p1V[0]*pbV[1] - pbV[0]*p1V[1]) +
- (pbV[0]*paV[1] - paV[0]*pbV[1]) +
- (paV[0]*p1V[1] - p1V[0]*paV[1]);
-
- area_21b = (p2V[0]*p1V[1] - p1V[0]*p2V[1]) +
- (p1V[0]*pbV[1] - pbV[0]*p1V[1]) +
- (pbV[0]*p2V[1] - p2V[0]*pbV[1]);
-
- area_2ab = (p2V[0]*paV[1] - paV[0]*p2V[1]) +
- (paV[0]*pbV[1] - pbV[0]*paV[1]) +
- (pbV[0]*p2V[1] - p2V[0]*pbV[1]);
-
- bool use_tri1a2 = true;
- bool tri_1a2 = true;
- bool tri_21b = true;
-
- if (area_1a2 < 0)
- {
- tri_1a2 = false;
- }
- if (area_2ab < 0)
- {
- // Can't use, because it contains point b
- tri_1a2 = false;
- }
- if (area_21b < 0)
- {
- tri_21b = false;
- }
- if (area_1ba < 0)
- {
- // Can't use, because it contains point b
- tri_21b = false;
- }
-
- if (!tri_1a2)
- {
- use_tri1a2 = false;
- }
- else if (!tri_21b)
- {
- use_tri1a2 = true;
- }
- else
- {
- LLVector4a d1;
- d1.setSub(p1, pa);
-
- LLVector4a d2;
- d2.setSub(p2, pb);
-
- if (d1.dot3(d1) < d2.dot3(d2))
- {
- use_tri1a2 = true;
- }
- else
- {
- use_tri1a2 = false;
- }
- }
-
- if (use_tri1a2)
- {
- mIndices[i++] = pt1;
- mIndices[i++] = pt1 + 1;
- mIndices[i++] = pt2;
- pt1++;
- }
- else
- {
- mIndices[i++] = pt1;
- mIndices[i++] = pt2 - 1;
- mIndices[i++] = pt2;
- pt2--;
- }
- }
- }
- else
- {
- // HOLLOW BOTTOM
- // Does it matter if it's open or closed? - djs
-
- llassert(mTypeMask & BOTTOM_MASK);
- S32 pt1 = 0, pt2 = num_vertices - 1;
-
- S32 i = 0;
- while (pt2 - pt1 > 1)
- {
- // Use the profile points instead of the mesh, since you want
- // the un-transformed profile distances.
- const LLVector4a& p1 = profile[pt1];
- const LLVector4a& p2 = profile[pt2];
- const LLVector4a& pa = profile[pt1+1];
- const LLVector4a& pb = profile[pt2-1];
-
- const F32* p1V = p1.getF32ptr();
- const F32* p2V = p2.getF32ptr();
- const F32* paV = pa.getF32ptr();
- const F32* pbV = pb.getF32ptr();
-
- // Use area of triangle to determine backfacing
- F32 area_1a2, area_1ba, area_21b, area_2ab;
- area_1a2 = (p1V[0]*paV[1] - paV[0]*p1V[1]) +
- (paV[0]*p2V[1] - p2V[0]*paV[1]) +
- (p2V[0]*p1V[1] - p1V[0]*p2V[1]);
-
- area_1ba = (p1V[0]*pbV[1] - pbV[0]*p1V[1]) +
- (pbV[0]*paV[1] - paV[0]*pbV[1]) +
- (paV[0]*p1V[1] - p1V[0]*paV[1]);
-
- area_21b = (p2V[0]*p1V[1] - p1V[0]*p2V[1]) +
- (p1V[0]*pbV[1] - pbV[0]*p1V[1]) +
- (pbV[0]*p2V[1] - p2V[0]*pbV[1]);
-
- area_2ab = (p2V[0]*paV[1] - paV[0]*p2V[1]) +
- (paV[0]*pbV[1] - pbV[0]*paV[1]) +
- (pbV[0]*p2V[1] - p2V[0]*pbV[1]);
-
- bool use_tri1a2 = true;
- bool tri_1a2 = true;
- bool tri_21b = true;
-
- if (area_1a2 < 0)
- {
- tri_1a2 = false;
- }
- if (area_2ab < 0)
- {
- // Can't use, because it contains point b
- tri_1a2 = false;
- }
- if (area_21b < 0)
- {
- tri_21b = false;
- }
- if (area_1ba < 0)
- {
- // Can't use, because it contains point b
- tri_21b = false;
- }
-
- if (!tri_1a2)
- {
- use_tri1a2 = false;
- }
- else if (!tri_21b)
- {
- use_tri1a2 = true;
- }
- else
- {
- LLVector4a d1;
- d1.setSub(p1,pa);
- LLVector4a d2;
- d2.setSub(p2,pb);
-
- if (d1.dot3(d1) < d2.dot3(d2))
- {
- use_tri1a2 = true;
- }
- else
- {
- use_tri1a2 = false;
- }
- }
-
- // Flipped backfacing from top
- if (use_tri1a2)
- {
- mIndices[i++] = pt1;
- mIndices[i++] = pt2;
- mIndices[i++] = pt1 + 1;
- pt1++;
- }
- else
- {
- mIndices[i++] = pt1;
- mIndices[i++] = pt2;
- mIndices[i++] = pt2 - 1;
- pt2--;
- }
- }
- }
- }
- else
- {
- // Not hollow, generate the triangle fan.
- U16 v1 = 2;
- U16 v2 = 1;
-
- if (mTypeMask & TOP_MASK)
- {
- v1 = 1;
- v2 = 2;
- }
-
- for (S32 i = 0; i < (num_vertices - 2); i++)
- {
- mIndices[3*i] = num_vertices - 1;
- mIndices[3*i+v1] = i;
- mIndices[3*i+v2] = i + 1;
- }
-
-
- }
-
- LLVector4a d0,d1;
- LL_CHECK_MEMORY
-
-
- d0.setSub(mPositions[mIndices[1]], mPositions[mIndices[0]]);
- d1.setSub(mPositions[mIndices[2]], mPositions[mIndices[0]]);
-
- LLVector4a normal;
- normal.setCross3(d0,d1);
-
- if (normal.dot3(normal).getF32() > F_APPROXIMATELY_ZERO)
- {
- normal.normalize3fast();
- }
- else
- { //degenerate, make up a value
- if(normal.getF32ptr()[2] >= 0)
- normal.set(0.f,0.f,1.f);
- else
- normal.set(0.f,0.f,-1.f);
- }
-
- llassert(llfinite(normal.getF32ptr()[0]));
- llassert(llfinite(normal.getF32ptr()[1]));
- llassert(llfinite(normal.getF32ptr()[2]));
-
- llassert(!llisnan(normal.getF32ptr()[0]));
- llassert(!llisnan(normal.getF32ptr()[1]));
- llassert(!llisnan(normal.getF32ptr()[2]));
-
- for (S32 i = 0; i < num_vertices; i++)
- {
- norm[i].load4a(normal.getF32ptr());
- }
-
- return true;
-}
-
-void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
- const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent);
-
-void LLVolumeFace::createTangents()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
-
- if (!mTangents)
- {
- allocateTangents(mNumVertices);
-
- //generate tangents
- LLVector4a* ptr = (LLVector4a*)mTangents;
-
- LLVector4a* end = mTangents + mNumVertices;
- while (ptr < end)
- {
- (*ptr++).clear();
- }
-
- CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents);
-
- //normalize normals
- for (U32 i = 0; i < mNumVertices; i++)
- {
- //bump map/planar projection code requires normals to be normalized
- mNormals[i].normalize3fast();
- }
- }
-
-}
-
-void LLVolumeFace::resizeVertices(S32 num_verts)
-{
- ll_aligned_free<64>(mPositions);
- //DO NOT free mNormals and mTexCoords as they are part of mPositions buffer
- ll_aligned_free_16(mTangents);
-
- mTangents = NULL;
-
- if (num_verts)
- {
- //pad texture coordinate block end to allow for QWORD reads
- S32 tc_size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
-
- mPositions = (LLVector4a*) ll_aligned_malloc<64>(sizeof(LLVector4a)*2*num_verts+tc_size);
- mNormals = mPositions+num_verts;
- mTexCoords = (LLVector2*) (mNormals+num_verts);
-
- ll_assert_aligned(mPositions, 64);
- }
- else
- {
- mPositions = NULL;
- mNormals = NULL;
- mTexCoords = NULL;
- }
-
-
- if (mPositions)
- {
- mNumVertices = num_verts;
- mNumAllocatedVertices = num_verts;
- }
- else
- {
- // Either num_verts is zero or allocation failure
- mNumVertices = 0;
- mNumAllocatedVertices = 0;
- }
-
- // Force update
- mJointRiggingInfoTab.clear();
-}
-
-void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv)
-{
- pushVertex(cv.getPosition(), cv.getNormal(), cv.mTexCoord);
-}
-
-void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc)
-{
- S32 new_verts = mNumVertices+1;
-
- if (new_verts > mNumAllocatedVertices)
- {
- // double buffer size on expansion
- new_verts *= 2;
-
- S32 new_tc_size = ((new_verts*8)+0xF) & ~0xF;
- S32 old_tc_size = ((mNumVertices*8)+0xF) & ~0xF;
-
- S32 old_vsize = mNumVertices*16;
-
- S32 new_size = new_verts*16*2+new_tc_size;
-
- LLVector4a* old_buf = mPositions;
-
- mPositions = (LLVector4a*) ll_aligned_malloc<64>(new_size);
- mNormals = mPositions+new_verts;
- mTexCoords = (LLVector2*) (mNormals+new_verts);
-
- if (old_buf != NULL)
- {
- // copy old positions into new buffer
- LLVector4a::memcpyNonAliased16((F32*)mPositions, (F32*)old_buf, old_vsize);
-
- // normals
- LLVector4a::memcpyNonAliased16((F32*)mNormals, (F32*)(old_buf + mNumVertices), old_vsize);
-
- // tex coords
- LLVector4a::memcpyNonAliased16((F32*)mTexCoords, (F32*)(old_buf + mNumVertices * 2), old_tc_size);
- }
-
- // just clear tangents
- ll_aligned_free_16(mTangents);
- mTangents = NULL;
- ll_aligned_free<64>(old_buf);
-
- mNumAllocatedVertices = new_verts;
-
- }
-
- mPositions[mNumVertices] = pos;
- mNormals[mNumVertices] = norm;
- mTexCoords[mNumVertices] = tc;
-
- mNumVertices++;
-}
-
-void LLVolumeFace::allocateTangents(S32 num_verts)
-{
- ll_aligned_free_16(mTangents);
- mTangents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
-}
-
-void LLVolumeFace::allocateWeights(S32 num_verts)
-{
- ll_aligned_free_16(mWeights);
- mWeights = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
-
-}
-
-void LLVolumeFace::allocateJointIndices(S32 num_verts)
-{
-#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
- ll_aligned_free_16(mJointIndices);
- ll_aligned_free_16(mJustWeights);
-
- mJointIndices = (U8*)ll_aligned_malloc_16(sizeof(U8) * 4 * num_verts);
- mJustWeights = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a) * num_verts);
-#endif
-}
-
-void LLVolumeFace::resizeIndices(S32 num_indices)
-{
- ll_aligned_free_16(mIndices);
- llassert(num_indices % 3 == 0);
-
- if (num_indices)
- {
- //pad index block end to allow for QWORD reads
- S32 size = ((num_indices*sizeof(U16)) + 0xF) & ~0xF;
-
- mIndices = (U16*) ll_aligned_malloc_16(size);
- }
- else
- {
- mIndices = NULL;
- }
-
- if (mIndices)
- {
- mNumIndices = num_indices;
- }
- else
- {
- // Either num_indices is zero or allocation failure
- mNumIndices = 0;
- }
-}
-
-void LLVolumeFace::pushIndex(const U16& idx)
-{
- S32 new_count = mNumIndices + 1;
- S32 new_size = ((new_count*2)+0xF) & ~0xF;
-
- S32 old_size = ((mNumIndices*2)+0xF) & ~0xF;
- if (new_size != old_size)
- {
- mIndices = (U16*) ll_aligned_realloc_16(mIndices, new_size, old_size);
- ll_assert_aligned(mIndices,16);
- }
-
- mIndices[mNumIndices++] = idx;
-}
-
-void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx)
-{
- resizeVertices(v.size());
- resizeIndices(idx.size());
-
- for (U32 i = 0; i < v.size(); ++i)
- {
- mPositions[i] = v[i].getPosition();
- mNormals[i] = v[i].getNormal();
- mTexCoords[i] = v[i].mTexCoord;
- }
-
- for (U32 i = 0; i < idx.size(); ++i)
- {
- mIndices[i] = idx[i];
- }
-}
-
-bool LLVolumeFace::createSide(LLVolume* volume, bool partial_build)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- LL_CHECK_MEMORY
- bool flat = mTypeMask & FLAT_MASK;
-
- U8 sculpt_type = volume->getParams().getSculptType();
- U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
- bool sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
- bool sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
- bool sculpt_reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR
-
- S32 num_vertices, num_indices;
-
- const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh();
- const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile;
- const LLAlignedArray<LLPath::PathPt,64>& path_data = volume->getPath().mPath;
-
- S32 max_s = volume->getProfile().getTotal();
-
- S32 s, t, i;
- F32 ss, tt;
-
- num_vertices = mNumS*mNumT;
- num_indices = (mNumS-1)*(mNumT-1)*6;
-
- partial_build = (num_vertices > mNumVertices || num_indices > mNumIndices) ? false : partial_build;
-
- if (!partial_build)
- {
- resizeVertices(num_vertices);
- resizeIndices(num_indices);
-
- if (!volume->isMeshAssetLoaded())
- {
- try
- {
- mEdge.resize(num_indices);
- }
- catch (std::bad_alloc&)
- {
- LL_WARNS("LLVOLUME") << "Resize of mEdge to " << num_indices << " failed" << LL_ENDL;
- return false;
- }
- }
- }
-
- LL_CHECK_MEMORY
-
- LLVector4a* pos = (LLVector4a*) mPositions;
- LLVector2* tc = (LLVector2*) mTexCoords;
- F32 begin_stex = floorf(profile[mBeginS][2]);
- S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS;
-
- S32 cur_vertex = 0;
- S32 end_t = mBeginT+mNumT;
- bool test = (mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2;
-
- // Copy the vertices into the array
- for (t = mBeginT; t < end_t; t++)
- {
- tt = path_data[t].mTexT;
- for (s = 0; s < num_s; s++)
- {
- if (mTypeMask & END_MASK)
- {
- if (s)
- {
- ss = 1.f;
- }
- else
- {
- ss = 0.f;
- }
- }
- else
- {
- // Get s value for tex-coord.
- S32 index = mBeginS + s;
- if (index >= profile.size())
- {
- // edge?
- ss = flat ? 1.f - begin_stex : 1.f;
- }
- else if (!flat)
- {
- ss = profile[index][2];
- }
- else
- {
- ss = profile[index][2] - begin_stex;
- }
- }
-
- if (sculpt_reverse_horizontal)
- {
- ss = 1.f - ss;
- }
-
- // Check to see if this triangle wraps around the array.
- if (mBeginS + s >= max_s)
- {
- // We're wrapping
- i = mBeginS + s + max_s*(t-1);
- }
- else
- {
- i = mBeginS + s + max_s*t;
- }
-
- mesh[i].store4a((F32*)(pos+cur_vertex));
- tc[cur_vertex].set(ss,tt);
-
- cur_vertex++;
-
- if (test && s > 0)
- {
- mesh[i].store4a((F32*)(pos+cur_vertex));
- tc[cur_vertex].set(ss,tt);
- cur_vertex++;
- }
- }
-
- if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2)
- {
- if (mTypeMask & OPEN_MASK)
- {
- s = num_s-1;
- }
- else
- {
- s = 0;
- }
-
- i = mBeginS + s + max_s*t;
- ss = profile[mBeginS + s][2] - begin_stex;
-
- mesh[i].store4a((F32*)(pos+cur_vertex));
- tc[cur_vertex].set(ss,tt);
-
- cur_vertex++;
- }
- }
- LL_CHECK_MEMORY
-
- mCenter->clear();
-
- LLVector4a* cur_pos = pos;
- LLVector4a* end_pos = pos + mNumVertices;
-
- //get bounding box for this side
- LLVector4a face_min;
- LLVector4a face_max;
-
- face_min = face_max = *cur_pos++;
-
- while (cur_pos < end_pos)
- {
- update_min_max(face_min, face_max, *cur_pos++);
- }
- // VFExtents change
- mExtents[0] = face_min;
- mExtents[1] = face_max;
-
- U32 tc_count = mNumVertices;
- if (tc_count%2 == 1)
- { //odd number of texture coordinates, duplicate last entry to padded end of array
- tc_count++;
- mTexCoords[mNumVertices] = mTexCoords[mNumVertices-1];
- }
-
- LLVector4a* cur_tc = (LLVector4a*) mTexCoords;
- LLVector4a* end_tc = (LLVector4a*) (mTexCoords+tc_count);
-
- LLVector4a tc_min;
- LLVector4a tc_max;
-
- tc_min = tc_max = *cur_tc++;
-
- while (cur_tc < end_tc)
- {
- update_min_max(tc_min, tc_max, *cur_tc++);
- }
-
- F32* minp = tc_min.getF32ptr();
- F32* maxp = tc_max.getF32ptr();
-
- mTexCoordExtents[0].mV[0] = llmin(minp[0], minp[2]);
- mTexCoordExtents[0].mV[1] = llmin(minp[1], minp[3]);
- mTexCoordExtents[1].mV[0] = llmax(maxp[0], maxp[2]);
- mTexCoordExtents[1].mV[1] = llmax(maxp[1], maxp[3]);
-
- mCenter->setAdd(face_min, face_max);
- mCenter->mul(0.5f);
-
- S32 cur_index = 0;
- S32 cur_edge = 0;
- bool flat_face = mTypeMask & FLAT_MASK;
-
- if (!partial_build)
- {
- // Now we generate the indices.
- for (t = 0; t < (mNumT-1); t++)
- {
- for (s = 0; s < (mNumS-1); s++)
- {
- mIndices[cur_index++] = s + mNumS*t; //bottom left
- mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
- mIndices[cur_index++] = s + mNumS*(t+1); //top left
- mIndices[cur_index++] = s + mNumS*t; //bottom left
- mIndices[cur_index++] = s+1 + mNumS*t; //bottom right
- mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
-
- // bottom left/top right neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1;
-
- if (t < mNumT-2)
- { // top right/top left neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*(t+1)+s*2+1;
- }
- else if (mNumT <= 3 || volume->getPath().isOpen())
- { // no neighbor
- mEdge[cur_edge++] = -1;
- }
- else
- { // wrap on T
- mEdge[cur_edge++] = s*2+1;
- }
-
- if (s > 0)
- { // top left/bottom left neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*t+s*2-1;
- }
- else if (flat_face || volume->getProfile().isOpen())
- { // no neighbor
- mEdge[cur_edge++] = -1;
- }
- else
- { // wrap on S
- mEdge[cur_edge++] = (mNumS-1)*2*t+(mNumS-2)*2+1;
- }
-
- if (t > 0)
- { // bottom left/bottom right neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*(t-1)+s*2;
- }
- else if (mNumT <= 3 || volume->getPath().isOpen())
- { // no neighbor
- mEdge[cur_edge++] = -1;
- }
- else
- { // wrap on T
- mEdge[cur_edge++] = (mNumS-1)*2*(mNumT-2)+s*2;
- }
-
- if (s < mNumS-2)
- { // bottom right/top right neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*t+(s+1)*2;
- }
- else if (flat_face || volume->getProfile().isOpen())
- { // no neighbor
- mEdge[cur_edge++] = -1;
- }
- else
- { // wrap on S
- mEdge[cur_edge++] = (mNumS-1)*2*t;
- }
-
- // top right/bottom left neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*t+s*2;
- }
- }
- }
-
- LL_CHECK_MEMORY
-
- //clear normals
- F32* dst = (F32*) mNormals;
- F32* end = (F32*) (mNormals+mNumVertices);
- LLVector4a zero = LLVector4a::getZero();
-
- while (dst < end)
- {
- zero.store4a(dst);
- dst += 4;
- }
-
- LL_CHECK_MEMORY
-
- //generate normals
- U32 count = mNumIndices/3;
-
- LLVector4a* norm = mNormals;
-
- static thread_local LLAlignedArray<LLVector4a, 64> triangle_normals;
- try
- {
- triangle_normals.resize(count);
- }
- catch (std::bad_alloc&)
- {
- LL_WARNS("LLVOLUME") << "Resize of triangle_normals to " << count << " failed" << LL_ENDL;
- return false;
- }
- LLVector4a* output = triangle_normals.mArray;
- LLVector4a* end_output = output+count;
-
- U16* idx = mIndices;
-
- while (output < end_output)
- {
- LLVector4a b,v1,v2;
- b.load4a((F32*) (pos+idx[0]));
- v1.load4a((F32*) (pos+idx[1]));
- v2.load4a((F32*) (pos+idx[2]));
-
- //calculate triangle normal
- LLVector4a a;
-
- a.setSub(b, v1);
- b.sub(v2);
-
-
- LLQuad& vector1 = *((LLQuad*) &v1);
- LLQuad& vector2 = *((LLQuad*) &v2);
-
- LLQuad& amQ = *((LLQuad*) &a);
- LLQuad& bmQ = *((LLQuad*) &b);
-
- //v1.setCross3(t,v0);
- //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] }
- vector1 = _mm_shuffle_ps( amQ, amQ, _MM_SHUFFLE( 3, 0, 2, 1 ));
- // Set vector2 = { b[W], b[Y], b[X], b[Z] }
- vector2 = _mm_shuffle_ps( bmQ, bmQ, _MM_SHUFFLE( 3, 1, 0, 2 ));
- // mQ = { a[W]*b[W], a[X]*b[Y], a[Z]*b[X], a[Y]*b[Z] }
- vector2 = _mm_mul_ps( vector1, vector2 );
- // vector3 = { a[W], a[Y], a[X], a[Z] }
- amQ = _mm_shuffle_ps( amQ, amQ, _MM_SHUFFLE( 3, 1, 0, 2 ));
- // vector4 = { b[W], b[X], b[Z], b[Y] }
- bmQ = _mm_shuffle_ps( bmQ, bmQ, _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] }
- vector1 = _mm_sub_ps( vector2, _mm_mul_ps( amQ, bmQ ));
-
- llassert(v1.isFinite3());
-
- v1.store4a((F32*) output);
-
-
- output++;
- idx += 3;
- }
-
- idx = mIndices;
-
- LLVector4a* src = triangle_normals.mArray;
-
- for (U32 i = 0; i < count; i++) //for each triangle
- {
- LLVector4a c;
- c.load4a((F32*) (src++));
-
- LLVector4a* n0p = norm+idx[0];
- LLVector4a* n1p = norm+idx[1];
- LLVector4a* n2p = norm+idx[2];
-
- idx += 3;
-
- LLVector4a n0,n1,n2;
- n0.load4a((F32*) n0p);
- n1.load4a((F32*) n1p);
- n2.load4a((F32*) n2p);
-
- n0.add(c);
- n1.add(c);
- n2.add(c);
-
- llassert(c.isFinite3());
-
- //even out quad contributions
- switch (i%2+1)
- {
- case 0: n0.add(c); break;
- case 1: n1.add(c); break;
- case 2: n2.add(c); break;
- };
-
- n0.store4a((F32*) n0p);
- n1.store4a((F32*) n1p);
- n2.store4a((F32*) n2p);
- }
-
- LL_CHECK_MEMORY
-
- // adjust normals based on wrapping and stitching
-
- LLVector4a top;
- top.setSub(pos[0], pos[mNumS*(mNumT-2)]);
- bool s_bottom_converges = (top.dot3(top) < 0.000001f);
-
- top.setSub(pos[mNumS-1], pos[mNumS*(mNumT-2)+mNumS-1]);
- bool s_top_converges = (top.dot3(top) < 0.000001f);
-
- if (sculpt_stitching == LL_SCULPT_TYPE_NONE) // logic for non-sculpt volumes
- {
- if (!volume->getPath().isOpen())
- { //wrap normals on T
- for (S32 i = 0; i < mNumS; i++)
- {
- LLVector4a n;
- n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
- norm[i] = n;
- norm[mNumS*(mNumT-1)+i] = n;
- }
- }
-
- if (!volume->getProfile().isOpen() && !s_bottom_converges)
- { //wrap normals on S
- for (S32 i = 0; i < mNumT; i++)
- {
- LLVector4a n;
- n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
- norm[mNumS * i] = n;
- norm[mNumS * i+mNumS-1] = n;
- }
- }
-
- if (volume->getPathType() == LL_PCODE_PATH_CIRCLE &&
- ((volume->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF))
- {
- if (s_bottom_converges)
- { //all lower S have same normal
- for (S32 i = 0; i < mNumT; i++)
- {
- norm[mNumS*i].set(1,0,0);
- }
- }
-
- if (s_top_converges)
- { //all upper S have same normal
- for (S32 i = 0; i < mNumT; i++)
- {
- norm[mNumS*i+mNumS-1].set(-1,0,0);
- }
- }
- }
- }
- else // logic for sculpt volumes
- {
- bool average_poles = false;
- bool wrap_s = false;
- bool wrap_t = false;
-
- if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
- average_poles = true;
-
- if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
- (sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
- (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
- wrap_s = true;
-
- if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
- wrap_t = true;
-
-
- if (average_poles)
- {
- // average normals for north pole
-
- LLVector4a average;
- average.clear();
-
- for (S32 i = 0; i < mNumS; i++)
- {
- average.add(norm[i]);
- }
-
- // set average
- for (S32 i = 0; i < mNumS; i++)
- {
- norm[i] = average;
- }
-
- // average normals for south pole
-
- average.clear();
-
- for (S32 i = 0; i < mNumS; i++)
- {
- average.add(norm[i + mNumS * (mNumT - 1)]);
- }
-
- // set average
- for (S32 i = 0; i < mNumS; i++)
- {
- norm[i + mNumS * (mNumT - 1)] = average;
- }
-
- }
-
-
- if (wrap_s)
- {
- for (S32 i = 0; i < mNumT; i++)
- {
- LLVector4a n;
- n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
- norm[mNumS * i] = n;
- norm[mNumS * i+mNumS-1] = n;
- }
- }
-
- if (wrap_t)
- {
- for (S32 i = 0; i < mNumS; i++)
- {
- LLVector4a n;
- n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
- norm[i] = n;
- norm[mNumS*(mNumT-1)+i] = n;
- }
- }
-
- }
-
- LL_CHECK_MEMORY
-
- return true;
-}
-
-//adapted from Lengyel, Eric. "Computing Tangent Space Basis Vectors for an Arbitrary Mesh". Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html
-void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
- const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- //LLVector4a *tan1 = new LLVector4a[vertexCount * 2];
- LLVector4a* tan1 = (LLVector4a*) ll_aligned_malloc_16(vertexCount*2*sizeof(LLVector4a));
- // new(tan1) LLVector4a;
-
- LLVector4a* tan2 = tan1 + vertexCount;
-
- U32 count = vertexCount * 2;
- for (U32 i = 0; i < count; i++)
- {
- tan1[i].clear();
- }
-
- for (U32 a = 0; a < triangleCount; a++)
- {
- U32 i1 = *index_array++;
- U32 i2 = *index_array++;
- U32 i3 = *index_array++;
-
- const LLVector4a& v1 = vertex[i1];
- const LLVector4a& v2 = vertex[i2];
- const LLVector4a& v3 = vertex[i3];
-
- const LLVector2& w1 = texcoord[i1];
- const LLVector2& w2 = texcoord[i2];
- const LLVector2& w3 = texcoord[i3];
-
- const F32* v1ptr = v1.getF32ptr();
- const F32* v2ptr = v2.getF32ptr();
- const F32* v3ptr = v3.getF32ptr();
-
- float x1 = v2ptr[0] - v1ptr[0];
- float x2 = v3ptr[0] - v1ptr[0];
- float y1 = v2ptr[1] - v1ptr[1];
- float y2 = v3ptr[1] - v1ptr[1];
- float z1 = v2ptr[2] - v1ptr[2];
- float z2 = v3ptr[2] - v1ptr[2];
-
- float s1 = w2.mV[0] - w1.mV[0];
- float s2 = w3.mV[0] - w1.mV[0];
- float t1 = w2.mV[1] - w1.mV[1];
- float t2 = w3.mV[1] - w1.mV[1];
-
- F32 rd = s1*t2-s2*t1;
-
- float r = ((rd*rd) > FLT_EPSILON) ? (1.0f / rd)
- : ((rd > 0.0f) ? 1024.f : -1024.f); //some made up large ratio for division by zero
-
- llassert(llfinite(r));
- llassert(!llisnan(r));
-
- LLVector4a sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
- (t2 * z1 - t1 * z2) * r);
- LLVector4a tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
- (s1 * z2 - s2 * z1) * r);
-
- tan1[i1].add(sdir);
- tan1[i2].add(sdir);
- tan1[i3].add(sdir);
-
- tan2[i1].add(tdir);
- tan2[i2].add(tdir);
- tan2[i3].add(tdir);
- }
-
- for (U32 a = 0; a < vertexCount; a++)
- {
- LLVector4a n = normal[a];
-
- const LLVector4a& t = tan1[a];
-
- LLVector4a ncrosst;
- ncrosst.setCross3(n,t);
-
- // Gram-Schmidt orthogonalize
- n.mul(n.dot3(t).getF32());
-
- LLVector4a tsubn;
- tsubn.setSub(t,n);
-
- if (tsubn.dot3(tsubn).getF32() > F_APPROXIMATELY_ZERO)
- {
- tsubn.normalize3fast();
-
- // Calculate handedness
- F32 handedness = ncrosst.dot3(tan2[a]).getF32() < 0.f ? -1.f : 1.f;
-
- tsubn.getF32ptr()[3] = handedness;
-
- tangent[a] = tsubn;
- }
- else
- { //degenerate, make up a value
- tangent[a].set(0,0,1,1);
- }
- }
-
- ll_aligned_free_16(tan1);
-}
-
-
+/** + * @file llvolume.cpp + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llmemory.h" +#include "llmath.h" + +#include <set> +#if !LL_WINDOWS +#include <stdint.h> +#endif +#include <cmath> +#include <unordered_map> + +#include "llerror.h" + +#include "llvolumemgr.h" +#include "v2math.h" +#include "v3math.h" +#include "v4math.h" +#include "m4math.h" +#include "m3math.h" +#include "llmatrix3a.h" +#include "lloctree.h" +#include "llvolume.h" +#include "llvolumeoctree.h" +#include "llstl.h" +#include "llsdserialize.h" +#include "llvector4a.h" +#include "llmatrix4a.h" +#include "llmeshoptimizer.h" +#include "lltimer.h" + +#include "mikktspace/mikktspace.h" +#include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file + +#include "meshoptimizer/meshoptimizer.h" + +#define DEBUG_SILHOUETTE_BINORMALS 0 +#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette +#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette + +constexpr F32 MIN_CUT_DELTA = 0.02f; + +constexpr F32 HOLLOW_MIN = 0.f; +constexpr F32 HOLLOW_MAX = 0.95f; +constexpr F32 HOLLOW_MAX_SQUARE = 0.7f; + +constexpr F32 TWIST_MIN = -1.f; +constexpr F32 TWIST_MAX = 1.f; + +constexpr F32 RATIO_MIN = 0.f; +constexpr F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper + +constexpr F32 HOLE_X_MIN= 0.05f; +constexpr F32 HOLE_X_MAX= 1.0f; + +constexpr F32 HOLE_Y_MIN= 0.05f; +constexpr F32 HOLE_Y_MAX= 0.5f; + +constexpr F32 SHEAR_MIN = -0.5f; +constexpr F32 SHEAR_MAX = 0.5f; + +constexpr F32 REV_MIN = 1.f; +constexpr F32 REV_MAX = 4.f; + +constexpr F32 TAPER_MIN = -1.f; +constexpr F32 TAPER_MAX = 1.f; + +constexpr F32 SKEW_MIN = -0.95f; +constexpr F32 SKEW_MAX = 0.95f; + +constexpr F32 SCULPT_MIN_AREA = 0.002f; +constexpr S32 SCULPT_MIN_AREA_DETAIL = 1; + +bool gDebugGL = false; // See settings.xml "RenderDebugGL" + +bool check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm) +{ + LLVector3 test = (pt2-pt1)%(pt3-pt2); + + //answer + if(test * norm < 0) + { + return false; + } + else + { + return true; + } +} + +bool LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size) +{ + return LLLineSegmentBoxIntersect(start.mV, end.mV, center.mV, size.mV); +} + +bool LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size) +{ + F32 fAWdU[3]{}; + F32 dir[3]{}; + F32 diff[3]{}; + + for (U32 i = 0; i < 3; i++) + { + dir[i] = 0.5f * (end[i] - start[i]); + diff[i] = (0.5f * (end[i] + start[i])) - center[i]; + fAWdU[i] = fabsf(dir[i]); + if(fabsf(diff[i])>size[i] + fAWdU[i]) return false; + } + + float f; + f = dir[1] * diff[2] - dir[2] * diff[1]; if(fabsf(f)>size[1]*fAWdU[2] + size[2]*fAWdU[1]) return false; + f = dir[2] * diff[0] - dir[0] * diff[2]; if(fabsf(f)>size[0]*fAWdU[2] + size[2]*fAWdU[0]) return false; + f = dir[0] * diff[1] - dir[1] * diff[0]; if(fabsf(f)>size[0]*fAWdU[1] + size[1]*fAWdU[0]) return false; + + return true; +} + +// Finds tangent vec based on three vertices with texture coordinates. +// Fills in dummy values if the triangle has degenerate texture coordinates. +void calc_tangent_from_triangle( + LLVector4a& normal, + LLVector4a& tangent_out, + const LLVector4a& v1, + const LLVector2& w1, + const LLVector4a& v2, + const LLVector2& w2, + const LLVector4a& v3, + const LLVector2& w3) +{ + const F32* v1ptr = v1.getF32ptr(); + const F32* v2ptr = v2.getF32ptr(); + const F32* v3ptr = v3.getF32ptr(); + + float x1 = v2ptr[0] - v1ptr[0]; + float x2 = v3ptr[0] - v1ptr[0]; + float y1 = v2ptr[1] - v1ptr[1]; + float y2 = v3ptr[1] - v1ptr[1]; + float z1 = v2ptr[2] - v1ptr[2]; + float z2 = v3ptr[2] - v1ptr[2]; + + float s1 = w2.mV[0] - w1.mV[0]; + float s2 = w3.mV[0] - w1.mV[0]; + float t1 = w2.mV[1] - w1.mV[1]; + float t2 = w3.mV[1] - w1.mV[1]; + + F32 rd = s1*t2-s2*t1; + + float r = ((rd*rd) > FLT_EPSILON) ? (1.0f / rd) + : ((rd > 0.0f) ? 1024.f : -1024.f); //some made up large ratio for division by zero + + llassert(llfinite(r)); + llassert(!llisnan(r)); + + LLVector4a sdir( + (t2 * x1 - t1 * x2) * r, + (t2 * y1 - t1 * y2) * r, + (t2 * z1 - t1 * z2) * r); + + LLVector4a tdir( + (s1 * x2 - s2 * x1) * r, + (s1 * y2 - s2 * y1) * r, + (s1 * z2 - s2 * z1) * r); + + LLVector4a n = normal; + LLVector4a t = sdir; + + LLVector4a ncrosst; + ncrosst.setCross3(n,t); + + // Gram-Schmidt orthogonalize + n.mul(n.dot3(t).getF32()); + + LLVector4a tsubn; + tsubn.setSub(t,n); + + if (tsubn.dot3(tsubn).getF32() > F_APPROXIMATELY_ZERO) + { + tsubn.normalize3fast_checked(); + + // Calculate handedness + F32 handedness = ncrosst.dot3(tdir).getF32() < 0.f ? -1.f : 1.f; + + tsubn.getF32ptr()[3] = handedness; + + tangent_out = tsubn; + } + else + { + // degenerate, make up a value + // + tangent_out.set(0,0,1,1); + } + +} + + +// intersect test between triangle vert0, vert1, vert2 and a ray from orig in direction dir. +// returns true if intersecting and returns barycentric coordinates in intersection_a, intersection_b, +// and returns the intersection point along dir in intersection_t. + +// Moller-Trumbore algorithm +bool LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, + F32& intersection_a, F32& intersection_b, F32& intersection_t) +{ + + /* find vectors for two edges sharing vert0 */ + LLVector4a edge1; + edge1.setSub(vert1, vert0); + + LLVector4a edge2; + edge2.setSub(vert2, vert0); + + /* begin calculating determinant - also used to calculate U parameter */ + LLVector4a pvec; + pvec.setCross3(dir, edge2); + + /* if determinant is near zero, ray lies in plane of triangle */ + LLVector4a det; + det.setAllDot3(edge1, pvec); + + if (det.greaterEqual(LLVector4a::getEpsilon()).getGatheredBits() & 0x7) + { + /* calculate distance from vert0 to ray origin */ + LLVector4a tvec; + tvec.setSub(orig, vert0); + + /* calculate U parameter and test bounds */ + LLVector4a u; + u.setAllDot3(tvec,pvec); + + if ((u.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7) && + (u.lessEqual(det).getGatheredBits() & 0x7)) + { + /* prepare to test V parameter */ + LLVector4a qvec; + qvec.setCross3(tvec, edge1); + + /* calculate V parameter and test bounds */ + LLVector4a v; + v.setAllDot3(dir, qvec); + + + //if (!(v < 0.f || u + v > det)) + + LLVector4a sum_uv; + sum_uv.setAdd(u, v); + + S32 v_gequal = v.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7; + S32 sum_lequal = sum_uv.lessEqual(det).getGatheredBits() & 0x7; + + if (v_gequal && sum_lequal) + { + /* calculate t, scale parameters, ray intersects triangle */ + LLVector4a t; + t.setAllDot3(edge2,qvec); + + t.div(det); + u.div(det); + v.div(det); + + intersection_a = u[0]; + intersection_b = v[0]; + intersection_t = t[0]; + return true; + } + } + } + + return false; +} + +bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, + F32& intersection_a, F32& intersection_b, F32& intersection_t) +{ + F32 u, v, t; + + /* find vectors for two edges sharing vert0 */ + LLVector4a edge1; + edge1.setSub(vert1, vert0); + + + LLVector4a edge2; + edge2.setSub(vert2, vert0); + + /* begin calculating determinant - also used to calculate U parameter */ + LLVector4a pvec; + pvec.setCross3(dir, edge2); + + /* if determinant is near zero, ray lies in plane of triangle */ + F32 det = edge1.dot3(pvec).getF32(); + + + if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO) + { + return false; + } + + F32 inv_det = 1.f / det; + + /* calculate distance from vert0 to ray origin */ + LLVector4a tvec; + tvec.setSub(orig, vert0); + + /* calculate U parameter and test bounds */ + u = (tvec.dot3(pvec).getF32()) * inv_det; + if (u < 0.f || u > 1.f) + { + return false; + } + + /* prepare to test V parameter */ + tvec.sub(edge1); + + /* calculate V parameter and test bounds */ + v = (dir.dot3(tvec).getF32()) * inv_det; + + if (v < 0.f || u + v > 1.f) + { + return false; + } + + /* calculate t, ray intersects triangle */ + t = (edge2.dot3(tvec).getF32()) * inv_det; + + intersection_a = u; + intersection_b = v; + intersection_t = t; + + + return true; +} + +class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*> +{ +public: + const LLVolumeFace* mFace; + + LLVolumeOctreeRebound(const LLVolumeFace* face) + { + mFace = face; + } + + virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch) + { //this is a depth first traversal, so it's safe to assum all children have complete + //bounding data + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0); + + LLVector4a& min = node->mExtents[0]; + LLVector4a& max = node->mExtents[1]; + + if (!branch->isEmpty()) + { //node has data, find AABB that binds data set + const LLVolumeTriangle* tri = *(branch->getDataBegin()); + + //initialize min/max to first available vertex + min = *(tri->mV[0]); + max = *(tri->mV[0]); + + for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) + { //for each triangle in node + + //stretch by triangles in node + tri = *iter; + + min.setMin(min, *tri->mV[0]); + min.setMin(min, *tri->mV[1]); + min.setMin(min, *tri->mV[2]); + + max.setMax(max, *tri->mV[0]); + max.setMax(max, *tri->mV[1]); + max.setMax(max, *tri->mV[2]); + } + } + else if (branch->getChildCount() > 0) + { //no data, but child nodes exist + LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0); + + //initialize min/max to extents of first child + min = child->mExtents[0]; + max = child->mExtents[1]; + } + else + { + llassert(!branch->isLeaf()); // Empty leaf + } + + for (S32 i = 0; i < branch->getChildCount(); ++i) + { //stretch by child extents + LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0); + min.setMin(min, child->mExtents[0]); + max.setMax(max, child->mExtents[1]); + } + + node->mBounds[0].setAdd(min, max); + node->mBounds[0].mul(0.5f); + + node->mBounds[1].setSub(max,min); + node->mBounds[1].mul(0.5f); + } +}; + +//------------------------------------------------------------------- +// statics +//------------------------------------------------------------------- + + +//---------------------------------------------------- + +LLProfile::Face* LLProfile::addCap(S16 faceID) +{ + Face *face = vector_append(mFaces, 1); + + face->mIndex = 0; + face->mCount = mTotal; + face->mScaleU= 1.0f; + face->mCap = true; + face->mFaceID = faceID; + return face; +} + +LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, bool flat) +{ + Face *face = vector_append(mFaces, 1); + + face->mIndex = i; + face->mCount = count; + face->mScaleU= scaleU; + + face->mFlat = flat; + face->mCap = false; + face->mFaceID = faceID; + return face; +} + +//static +S32 LLProfile::getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split) +{ // this is basically LLProfile::genNGon stripped down to only the operations that influence the number of points + S32 np = 0; + + // Generate an n-sided "circular" path. + // 0 is (1,0), and we go counter-clockwise along a circular path from there. + F32 t, t_step, t_first, t_fraction; + + F32 begin = params.getBegin(); + F32 end = params.getEnd(); + + t_step = 1.0f / sides; + + t_first = floor(begin * sides) / (F32)sides; + + // pt1 is the first point on the fractional face. + // Starting t and ang values for the first face + t = t_first; + + // Increment to the next point. + // pt2 is the end point on the fractional face + t += t_step; + + t_fraction = (begin - t_first)*sides; + + // Only use if it's not almost exactly on an edge. + if (t_fraction < 0.9999f) + { + np++; + } + + // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02 + while (t < end) + { + // Iterate through all the integer steps of t. + np++; + + t += t_step; + } + + t_fraction = (end - (t - t_step))*sides; + + // Find the fraction that we need to add to the end point. + t_fraction = (end - (t - t_step))*sides; + if (t_fraction > 0.0001f) + { + np++; + } + + // If we're sliced, the profile is open. + if ((end - begin)*ang_scale < 0.99f) + { + if (params.getHollow() <= 0) + { + // put center point if not hollow. + np++; + } + } + + return np; +} + +// What is the bevel parameter used for? - DJS 04/05/02 +// Bevel parameter is currently unused but presumedly would support +// filleted and chamfered corners +void LLProfile::genNGon(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split) +{ + // Generate an n-sided "circular" path. + // 0 is (1,0), and we go counter-clockwise along a circular path from there. + constexpr F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f }; + F32 scale = 0.5f; + F32 t, t_step, t_first, t_fraction, ang, ang_step; + LLVector4a pt1,pt2; + + F32 begin = params.getBegin(); + F32 end = params.getEnd(); + + t_step = 1.0f / sides; + ang_step = 2.0f*F_PI*t_step*ang_scale; + + // Scale to have size "match" scale. Compensates to get object to generally fill bounding box. + + S32 total_sides = ll_round(sides / ang_scale); // Total number of sides all around + + if (total_sides < 8) + { + scale = tableScale[total_sides]; + } + + t_first = floor(begin * sides) / (F32)sides; + + // pt1 is the first point on the fractional face. + // Starting t and ang values for the first face + t = t_first; + ang = 2.0f*F_PI*(t*ang_scale + offset); + pt1.set(cos(ang)*scale,sin(ang)*scale, t); + + // Increment to the next point. + // pt2 is the end point on the fractional face + t += t_step; + ang += ang_step; + pt2.set(cos(ang)*scale,sin(ang)*scale,t); + + t_fraction = (begin - t_first)*sides; + + // Only use if it's not almost exactly on an edge. + if (t_fraction < 0.9999f) + { + LLVector4a new_pt; + new_pt.setLerp(pt1, pt2, t_fraction); + mProfile.push_back(new_pt); + } + + // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02 + while (t < end) + { + // Iterate through all the integer steps of t. + pt1.set(cos(ang)*scale,sin(ang)*scale,t); + + if (mProfile.size() > 0) { + LLVector4a p = mProfile[mProfile.size()-1]; + for (S32 i = 0; i < split && mProfile.size() > 0; i++) { + //mProfile.push_back(p+(pt1-p) * 1.0f/(float)(split+1) * (float)(i+1)); + LLVector4a new_pt; + new_pt.setSub(pt1, p); + new_pt.mul(1.0f/(float)(split+1) * (float)(i+1)); + new_pt.add(p); + mProfile.push_back(new_pt); + } + } + mProfile.push_back(pt1); + + t += t_step; + ang += ang_step; + } + + t_fraction = (end - (t - t_step))*sides; + + // pt1 is the first point on the fractional face + // pt2 is the end point on the fractional face + pt2.set(cos(ang)*scale,sin(ang)*scale,t); + + // Find the fraction that we need to add to the end point. + t_fraction = (end - (t - t_step))*sides; + if (t_fraction > 0.0001f) + { + LLVector4a new_pt; + new_pt.setLerp(pt1, pt2, t_fraction); + + if (mProfile.size() > 0) { + LLVector4a p = mProfile[mProfile.size()-1]; + for (S32 i = 0; i < split && mProfile.size() > 0; i++) { + //mProfile.push_back(p+(new_pt-p) * 1.0f/(float)(split+1) * (float)(i+1)); + + LLVector4a pt1; + pt1.setSub(new_pt, p); + pt1.mul(1.0f/(float)(split+1) * (float)(i+1)); + pt1.add(p); + mProfile.push_back(pt1); + } + } + mProfile.push_back(new_pt); + } + + // If we're sliced, the profile is open. + if ((end - begin)*ang_scale < 0.99f) + { + if ((end - begin)*ang_scale > 0.5f) + { + mConcave = true; + } + else + { + mConcave = false; + } + mOpen = true; + if (params.getHollow() <= 0) + { + // put center point if not hollow. + mProfile.push_back(LLVector4a(0,0,0)); + } + } + else + { + // The profile isn't open. + mOpen = false; + mConcave = false; + } + + mTotal = mProfile.size(); +} + +// Hollow is percent of the original bounding box, not of this particular +// profile's geometry. Thus, a swept triangle needs lower hollow values than +// a swept square. +LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, bool flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split) +{ + // Note that addHole will NOT work for non-"circular" profiles, if we ever decide to use them. + + // Total add has number of vertices on outside. + mTotalOut = mTotal; + + // Why is the "bevel" parameter -1? DJS 04/05/02 + genNGon(params, llfloor(sides),offset,-1, ang_scale, split); + + Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat); + + static thread_local LLAlignedArray<LLVector4a,64> pt; + pt.resize(mTotal) ; + + for (S32 i=mTotalOut;i<mTotal;i++) + { + pt[i] = mProfile[i]; + pt[i].mul(box_hollow); + } + + S32 j=mTotal-1; + for (S32 i=mTotalOut;i<mTotal;i++) + { + mProfile[i] = pt[j--]; + } + + for (S32 i=0;i<(S32)mFaces.size();i++) + { + if (mFaces[i].mCap) + { + mFaces[i].mCount *= 2; + } + } + + return face; +} + +//static +S32 LLProfile::getNumPoints(const LLProfileParams& params, bool path_open,F32 detail, S32 split, + bool is_sculpted, S32 sculpt_size) +{ // this is basically LLProfile::generate stripped down to only operations that influence the number of points + if (detail < MIN_LOD) + { + detail = MIN_LOD; + } + + // Generate the face data + F32 hollow = params.getHollow(); + + S32 np = 0; + + switch (params.getCurveType() & LL_PCODE_PROFILE_MASK) + { + case LL_PCODE_PROFILE_SQUARE: + { + np = getNumNGonPoints(params, 4,-0.375, 0, 1, split); + + if (hollow) + { + np *= 2; + } + } + break; + case LL_PCODE_PROFILE_ISOTRI: + case LL_PCODE_PROFILE_RIGHTTRI: + case LL_PCODE_PROFILE_EQUALTRI: + { + np = getNumNGonPoints(params, 3,0, 0, 1, split); + + if (hollow) + { + np *= 2; + } + } + break; + case LL_PCODE_PROFILE_CIRCLE: + { + // If this has a square hollow, we should adjust the + // number of faces a bit so that the geometry lines up. + U8 hole_type=0; + F32 circle_detail = MIN_DETAIL_FACES * detail; + if (hollow) + { + hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK; + if (hole_type == LL_PCODE_HOLE_SQUARE) + { + // Snap to the next multiple of four sides, + // so that corners line up. + circle_detail = llceil(circle_detail / 4.0f) * 4.0f; + } + } + + S32 sides = (S32)circle_detail; + + if (is_sculpted) + sides = sculpt_size; + + np = getNumNGonPoints(params, sides); + + if (hollow) + { + np *= 2; + } + } + break; + case LL_PCODE_PROFILE_CIRCLE_HALF: + { + // If this has a square hollow, we should adjust the + // number of faces a bit so that the geometry lines up. + U8 hole_type=0; + // Number of faces is cut in half because it's only a half-circle. + F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f; + if (hollow) + { + hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK; + if (hole_type == LL_PCODE_HOLE_SQUARE) + { + // Snap to the next multiple of four sides (div 2), + // so that corners line up. + circle_detail = llceil(circle_detail / 2.0f) * 2.0f; + } + } + np = getNumNGonPoints(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f); + + if (hollow) + { + np *= 2; + } + + // Special case for openness of sphere + if ((params.getEnd() - params.getBegin()) < 1.f) + { + } + else if (!hollow) + { + np++; + } + } + break; + default: + break; + }; + + + return np; +} + + +bool LLProfile::generate(const LLProfileParams& params, bool path_open,F32 detail, S32 split, + bool is_sculpted, S32 sculpt_size) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + if ((!mDirty) && (!is_sculpted)) + { + return false; + } + mDirty = false; + + if (detail < MIN_LOD) + { + LL_INFOS() << "Generating profile with LOD < MIN_LOD. CLAMPING" << LL_ENDL; + detail = MIN_LOD; + } + + mProfile.resize(0); + mFaces.resize(0); + + // Generate the face data + S32 i; + F32 begin = params.getBegin(); + F32 end = params.getEnd(); + F32 hollow = params.getHollow(); + + // Quick validation to eliminate some server crashes. + if (begin > end - 0.01f) + { + LL_WARNS() << "LLProfile::generate() assertion failed (begin >= end)" << LL_ENDL; + return false; + } + + S32 face_num = 0; + + switch (params.getCurveType() & LL_PCODE_PROFILE_MASK) + { + case LL_PCODE_PROFILE_SQUARE: + { + genNGon(params, 4,-0.375, 0, 1, split); + if (path_open) + { + addCap (LL_FACE_PATH_BEGIN); + } + + for (i = llfloor(begin * 4.f); i < llfloor(end * 4.f + .999f); i++) + { + addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, true); + } + + LLVector4a scale(1,1,4,1); + + for (i = 0; i <(S32) mProfile.size(); i++) + { + // Scale by 4 to generate proper tex coords. + mProfile[i].mul(scale); + llassert(mProfile[i].isFinite3()); + } + + if (hollow) + { + switch (params.getCurveType() & LL_PCODE_HOLE_MASK) + { + case LL_PCODE_HOLE_TRIANGLE: + // This offset is not correct, but we can't change it now... DK 11/17/04 + addHole(params, true, 3, -0.375f, hollow, 1.f, split); + break; + case LL_PCODE_HOLE_CIRCLE: + // TODO: Compute actual detail levels for cubes + addHole(params, false, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f); + break; + case LL_PCODE_HOLE_SAME: + case LL_PCODE_HOLE_SQUARE: + default: + addHole(params, true, 4, -0.375f, hollow, 1.f, split); + break; + } + } + + if (path_open) { + mFaces[0].mCount = mTotal; + } + } + break; + case LL_PCODE_PROFILE_ISOTRI: + case LL_PCODE_PROFILE_RIGHTTRI: + case LL_PCODE_PROFILE_EQUALTRI: + { + genNGon(params, 3,0, 0, 1, split); + LLVector4a scale(1,1,3,1); + for (i = 0; i <(S32) mProfile.size(); i++) + { + // Scale by 3 to generate proper tex coords. + mProfile[i].mul(scale); + llassert(mProfile[i].isFinite3()); + } + + if (path_open) + { + addCap(LL_FACE_PATH_BEGIN); + } + + for (i = llfloor(begin * 3.f); i < llfloor(end * 3.f + .999f); i++) + { + addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, true); + } + if (hollow) + { + // Swept triangles need smaller hollowness values, + // because the triangle doesn't fill the bounding box. + F32 triangle_hollow = hollow / 2.f; + + switch (params.getCurveType() & LL_PCODE_HOLE_MASK) + { + case LL_PCODE_HOLE_CIRCLE: + // TODO: Actually generate level of detail for triangles + addHole(params, false, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f); + break; + case LL_PCODE_HOLE_SQUARE: + addHole(params, true, 4, 0, triangle_hollow, 1.f, split); + break; + case LL_PCODE_HOLE_SAME: + case LL_PCODE_HOLE_TRIANGLE: + default: + addHole(params, true, 3, 0, triangle_hollow, 1.f, split); + break; + } + } + } + break; + case LL_PCODE_PROFILE_CIRCLE: + { + // If this has a square hollow, we should adjust the + // number of faces a bit so that the geometry lines up. + U8 hole_type=0; + F32 circle_detail = MIN_DETAIL_FACES * detail; + if (hollow) + { + hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK; + if (hole_type == LL_PCODE_HOLE_SQUARE) + { + // Snap to the next multiple of four sides, + // so that corners line up. + circle_detail = llceil(circle_detail / 4.0f) * 4.0f; + } + } + + S32 sides = (S32)circle_detail; + + if (is_sculpted) + sides = sculpt_size; + + genNGon(params, sides); + + if (path_open) + { + addCap (LL_FACE_PATH_BEGIN); + } + + if (mOpen && !hollow) + { + addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, false); + } + else + { + addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, false); + } + + if (hollow) + { + switch (hole_type) + { + case LL_PCODE_HOLE_SQUARE: + addHole(params, true, 4, 0, hollow, 1.f, split); + break; + case LL_PCODE_HOLE_TRIANGLE: + addHole(params, true, 3, 0, hollow, 1.f, split); + break; + case LL_PCODE_HOLE_CIRCLE: + case LL_PCODE_HOLE_SAME: + default: + addHole(params, true, circle_detail, 0, hollow, 1.f); + break; + } + } + } + break; + case LL_PCODE_PROFILE_CIRCLE_HALF: + { + // If this has a square hollow, we should adjust the + // number of faces a bit so that the geometry lines up. + U8 hole_type=0; + // Number of faces is cut in half because it's only a half-circle. + F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f; + if (hollow) + { + hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK; + if (hole_type == LL_PCODE_HOLE_SQUARE) + { + // Snap to the next multiple of four sides (div 2), + // so that corners line up. + circle_detail = llceil(circle_detail / 2.0f) * 2.0f; + } + } + genNGon(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f); + if (path_open) + { + addCap(LL_FACE_PATH_BEGIN); + } + if (mOpen && !params.getHollow()) + { + addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, false); + } + else + { + addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, false); + } + + if (hollow) + { + switch (hole_type) + { + case LL_PCODE_HOLE_SQUARE: + addHole(params, true, 2, 0.5f, hollow, 0.5f, split); + break; + case LL_PCODE_HOLE_TRIANGLE: + addHole(params, true, 3, 0.5f, hollow, 0.5f, split); + break; + case LL_PCODE_HOLE_CIRCLE: + case LL_PCODE_HOLE_SAME: + default: + addHole(params, false, circle_detail, 0.5f, hollow, 0.5f); + break; + } + } + + // Special case for openness of sphere + if ((params.getEnd() - params.getBegin()) < 1.f) + { + mOpen = true; + } + else if (!hollow) + { + mOpen = false; + mProfile.push_back(mProfile[0]); + mTotal++; + } + } + break; + default: + LL_ERRS() << "Unknown profile: getCurveType()=" << params.getCurveType() << LL_ENDL; + break; + }; + + if (path_open) + { + addCap(LL_FACE_PATH_END); // bottom + } + + if ( mOpen) // interior edge caps + { + addFace(mTotal-1, 2,0.5,LL_FACE_PROFILE_BEGIN, true); + + if (hollow) + { + addFace(mTotalOut-1, 2,0.5,LL_FACE_PROFILE_END, true); + } + else + { + addFace(mTotal-2, 2,0.5,LL_FACE_PROFILE_END, true); + } + } + + return true; +} + + + +bool LLProfileParams::importFile(LLFILE *fp) +{ + const S32 BUFSIZE = 16384; + char buffer[BUFSIZE]; /* Flawfinder: ignore */ + // *NOTE: changing the size or type of these buffers will require + // changing the sscanf below. + char keyword[256]; /* Flawfinder: ignore */ + char valuestr[256]; /* Flawfinder: ignore */ + keyword[0] = 0; + valuestr[0] = 0; + F32 tempF32; + U32 tempU32; + + while (!feof(fp)) + { + if (fgets(buffer, BUFSIZE, fp) == NULL) + { + buffer[0] = '\0'; + } + + sscanf( /* Flawfinder: ignore */ + buffer, + " %255s %255s", + keyword, valuestr); + if (!strcmp("{", keyword)) + { + continue; + } + if (!strcmp("}",keyword)) + { + break; + } + else if (!strcmp("curve", keyword)) + { + sscanf(valuestr,"%d",&tempU32); + setCurveType((U8) tempU32); + } + else if (!strcmp("begin",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setBegin(tempF32); + } + else if (!strcmp("end",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setEnd(tempF32); + } + else if (!strcmp("hollow",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setHollow(tempF32); + } + else + { + LL_WARNS() << "unknown keyword " << keyword << " in profile import" << LL_ENDL; + } + } + + return true; +} + + +bool LLProfileParams::exportFile(LLFILE *fp) const +{ + fprintf(fp,"\t\tprofile 0\n"); + fprintf(fp,"\t\t{\n"); + fprintf(fp,"\t\t\tcurve\t%d\n", getCurveType()); + fprintf(fp,"\t\t\tbegin\t%g\n", getBegin()); + fprintf(fp,"\t\t\tend\t%g\n", getEnd()); + fprintf(fp,"\t\t\thollow\t%g\n", getHollow()); + fprintf(fp, "\t\t}\n"); + return true; +} + + +bool LLProfileParams::importLegacyStream(std::istream& input_stream) +{ + const S32 BUFSIZE = 16384; + char buffer[BUFSIZE]; /* Flawfinder: ignore */ + // *NOTE: changing the size or type of these buffers will require + // changing the sscanf below. + char keyword[256]; /* Flawfinder: ignore */ + char valuestr[256]; /* Flawfinder: ignore */ + keyword[0] = 0; + valuestr[0] = 0; + F32 tempF32; + U32 tempU32; + + while (input_stream.good()) + { + input_stream.getline(buffer, BUFSIZE); + sscanf( /* Flawfinder: ignore */ + buffer, + " %255s %255s", + keyword, + valuestr); + if (!strcmp("{", keyword)) + { + continue; + } + if (!strcmp("}",keyword)) + { + break; + } + else if (!strcmp("curve", keyword)) + { + sscanf(valuestr,"%d",&tempU32); + setCurveType((U8) tempU32); + } + else if (!strcmp("begin",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setBegin(tempF32); + } + else if (!strcmp("end",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setEnd(tempF32); + } + else if (!strcmp("hollow",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setHollow(tempF32); + } + else + { + LL_WARNS() << "unknown keyword " << keyword << " in profile import" << LL_ENDL; + } + } + + return true; +} + + +bool LLProfileParams::exportLegacyStream(std::ostream& output_stream) const +{ + output_stream <<"\t\tprofile 0\n"; + output_stream <<"\t\t{\n"; + output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n"; + output_stream <<"\t\t\tbegin\t" << getBegin() << "\n"; + output_stream <<"\t\t\tend\t" << getEnd() << "\n"; + output_stream <<"\t\t\thollow\t" << getHollow() << "\n"; + output_stream << "\t\t}\n"; + return true; +} + +LLSD LLProfileParams::asLLSD() const +{ + LLSD sd; + + sd["curve"] = getCurveType(); + sd["begin"] = getBegin(); + sd["end"] = getEnd(); + sd["hollow"] = getHollow(); + return sd; +} + +bool LLProfileParams::fromLLSD(LLSD& sd) +{ + setCurveType(sd["curve"].asInteger()); + setBegin((F32)sd["begin"].asReal()); + setEnd((F32)sd["end"].asReal()); + setHollow((F32)sd["hollow"].asReal()); + return true; +} + +void LLProfileParams::copyParams(const LLProfileParams ¶ms) +{ + setCurveType(params.getCurveType()); + setBegin(params.getBegin()); + setEnd(params.getEnd()); + setHollow(params.getHollow()); +} + + +LLPath::~LLPath() +{ +} + +S32 LLPath::getNumNGonPoints(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale) +{ //this is basically LLPath::genNGon stripped down to only operations that influence the number of points added + S32 ret = 0; + + F32 step= 1.0f / sides; + F32 t = params.getBegin(); + ret = 1; + + t+=step; + + // Snap to a quantized parameter, so that cut does not + // affect most sample points. + t = ((S32)(t * sides)) / (F32)sides; + + // Run through the non-cut dependent points. + while (t < params.getEnd()) + { + ret++; + t+=step; + } + + ret++; + + return ret; +} + +void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + // Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane. + constexpr F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f }; + + F32 revolutions = params.getRevolutions(); + F32 skew = params.getSkew(); + F32 skew_mag = fabs(skew); + F32 hole_x = params.getScaleX() * (1.0f - skew_mag); + F32 hole_y = params.getScaleY(); + + // Calculate taper begin/end for x,y (Negative means taper the beginning) + F32 taper_x_begin = 1.0f; + F32 taper_x_end = 1.0f - params.getTaperX(); + F32 taper_y_begin = 1.0f; + F32 taper_y_end = 1.0f - params.getTaperY(); + + if ( taper_x_end > 1.0f ) + { + // Flip tapering. + taper_x_begin = 2.0f - taper_x_end; + taper_x_end = 1.0f; + } + if ( taper_y_end > 1.0f ) + { + // Flip tapering. + taper_y_begin = 2.0f - taper_y_end; + taper_y_end = 1.0f; + } + + // For spheres, the radius is usually zero. + F32 radius_start = 0.5f; + if (sides < 8) + { + radius_start = tableScale[sides]; + } + + // Scale the radius to take the hole size into account. + radius_start *= 1.0f - hole_y; + + // Now check the radius offset to calculate the start,end radius. (Negative means + // decrease the start radius instead). + F32 radius_end = radius_start; + F32 radius_offset = params.getRadiusOffset(); + if (radius_offset < 0.f) + { + radius_start *= 1.f + radius_offset; + } + else + { + radius_end *= 1.f - radius_offset; + } + + // Is the path NOT a closed loop? + mOpen = ( (params.getEnd()*end_scale - params.getBegin() < 1.0f) || + (skew_mag > 0.001f) || + (fabs(taper_x_end - taper_x_begin) > 0.001f) || + (fabs(taper_y_end - taper_y_begin) > 0.001f) || + (fabs(radius_end - radius_start) > 0.001f) ); + + F32 ang, c, s; + LLQuaternion twist, qang; + PathPt *pt; + LLVector3 path_axis (1.f, 0.f, 0.f); + //LLVector3 twist_axis(0.f, 0.f, 1.f); + F32 twist_begin = params.getTwistBegin() * twist_scale; + F32 twist_end = params.getTwist() * twist_scale; + + // We run through this once before the main loop, to make sure + // the path begins at the correct cut. + F32 step= 1.0f / sides; + F32 t = params.getBegin(); + pt = mPath.append(1); + ang = 2.0f*F_PI*revolutions * t; + s = sin(ang)*lerp(radius_start, radius_end, t); + c = cos(ang)*lerp(radius_start, radius_end, t); + + + pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) + + lerp(-skew ,skew, t) * 0.5f, + c + lerp(0,params.getShear().mV[1],s), + s); + pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), + hole_y * lerp(taper_y_begin, taper_y_end, t), + 0,1); + pt->mTexT = t; + + // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02 + twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1); + // Rotate the point around the circle's center. + qang.setQuat (ang,path_axis); + + LLMatrix3 rot(twist * qang); + + pt->mRot.loadu(rot); + + t+=step; + + // Snap to a quantized parameter, so that cut does not + // affect most sample points. + t = ((S32)(t * sides)) / (F32)sides; + + // Run through the non-cut dependent points. + while (t < params.getEnd()) + { + pt = mPath.append(1); + + ang = 2.0f*F_PI*revolutions * t; + c = cos(ang)*lerp(radius_start, radius_end, t); + s = sin(ang)*lerp(radius_start, radius_end, t); + + pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) + + lerp(-skew ,skew, t) * 0.5f, + c + lerp(0,params.getShear().mV[1],s), + s); + + pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), + hole_y * lerp(taper_y_begin, taper_y_end, t), + 0,1); + pt->mTexT = t; + + // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02 + twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1); + // Rotate the point around the circle's center. + qang.setQuat (ang,path_axis); + LLMatrix3 tmp(twist*qang); + pt->mRot.loadu(tmp); + + t+=step; + } + + // Make one final pass for the end cut. + t = params.getEnd(); + pt = mPath.append(1); + ang = 2.0f*F_PI*revolutions * t; + c = cos(ang)*lerp(radius_start, radius_end, t); + s = sin(ang)*lerp(radius_start, radius_end, t); + + pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) + + lerp(-skew ,skew, t) * 0.5f, + c + lerp(0,params.getShear().mV[1],s), + s); + pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), + hole_y * lerp(taper_y_begin, taper_y_end, t), + 0,1); + pt->mTexT = t; + + // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02 + twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1); + // Rotate the point around the circle's center. + qang.setQuat (ang,path_axis); + LLMatrix3 tmp(twist*qang); + pt->mRot.loadu(tmp); + + mTotal = mPath.size(); +} + +const LLVector2 LLPathParams::getBeginScale() const +{ + LLVector2 begin_scale(1.f, 1.f); + if (getScaleX() > 1) + { + begin_scale.mV[0] = 2-getScaleX(); + } + if (getScaleY() > 1) + { + begin_scale.mV[1] = 2-getScaleY(); + } + return begin_scale; +} + +const LLVector2 LLPathParams::getEndScale() const +{ + LLVector2 end_scale(1.f, 1.f); + if (getScaleX() < 1) + { + end_scale.mV[0] = getScaleX(); + } + if (getScaleY() < 1) + { + end_scale.mV[1] = getScaleY(); + } + return end_scale; +} + +S32 LLPath::getNumPoints(const LLPathParams& params, F32 detail) +{ // this is basically LLPath::generate stripped down to only the operations that influence the number of points + if (detail < MIN_LOD) + { + detail = MIN_LOD; + } + + S32 np = 2; // hardcode for line + + // Is this 0xf0 mask really necessary? DK 03/02/05 + + switch (params.getCurveType() & 0xf0) + { + default: + case LL_PCODE_PATH_LINE: + { + // Take the begin/end twist into account for detail. + np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2; + } + break; + + case LL_PCODE_PATH_CIRCLE: + { + // Increase the detail as the revolutions and twist increase. + F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist()); + + S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions()); + + np = sides; + } + break; + + case LL_PCODE_PATH_CIRCLE2: + { + //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f); + np = getNumNGonPoints(params, llfloor(MIN_DETAIL_FACES * detail)); + } + break; + + case LL_PCODE_PATH_TEST: + + np = 5; + break; + }; + + return np; +} + +bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split, + bool is_sculpted, S32 sculpt_size) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + if ((!mDirty) && (!is_sculpted)) + { + return false; + } + + if (detail < MIN_LOD) + { + LL_INFOS() << "Generating path with LOD < MIN! Clamping to 1" << LL_ENDL; + detail = MIN_LOD; + } + + mDirty = false; + S32 np = 2; // hardcode for line + + mPath.resize(0); + mOpen = true; + + // Is this 0xf0 mask really necessary? DK 03/02/05 + switch (params.getCurveType() & 0xf0) + { + default: + case LL_PCODE_PATH_LINE: + { + // Take the begin/end twist into account for detail. + np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2; + if (np < split+2) + { + np = split+2; + } + + mStep = 1.0f / (np-1); + + mPath.resize(np); + + LLVector2 start_scale = params.getBeginScale(); + LLVector2 end_scale = params.getEndScale(); + + for (S32 i=0;i<np;i++) + { + F32 t = lerp(params.getBegin(),params.getEnd(),(F32)i * mStep); + mPath[i].mPos.set(lerp(0,params.getShear().mV[0],t), + lerp(0,params.getShear().mV[1],t), + t - 0.5f); + LLQuaternion quat; + quat.setQuat(lerp(F_PI * params.getTwistBegin(),F_PI * params.getTwist(),t),0,0,1); + LLMatrix3 tmp(quat); + mPath[i].mRot.loadu(tmp); + mPath[i].mScale.set(lerp(start_scale.mV[0],end_scale.mV[0],t), + lerp(start_scale.mV[1],end_scale.mV[1],t), + 0,1); + mPath[i].mTexT = t; + } + } + break; + + case LL_PCODE_PATH_CIRCLE: + { + // Increase the detail as the revolutions and twist increase. + F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist()); + + S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions()); + + if (is_sculpted) + sides = llmax(sculpt_size, 1); + + if (0 < sides) + genNGon(params, sides); + } + break; + + case LL_PCODE_PATH_CIRCLE2: + { + if (params.getEnd() - params.getBegin() >= 0.99f && + params.getScaleX() >= .99f) + { + mOpen = false; + } + + //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f); + genNGon(params, llfloor(MIN_DETAIL_FACES * detail)); + + F32 toggle = 0.5f; + for (S32 i=0;i<(S32)mPath.size();i++) + { + mPath[i].mPos.getF32ptr()[0] = toggle; + if (toggle == 0.5f) + toggle = -0.5f; + else + toggle = 0.5f; + } + } + + break; + + case LL_PCODE_PATH_TEST: + + np = 5; + mStep = 1.0f / (np-1); + + mPath.resize(np); + + for (S32 i=0;i<np;i++) + { + F32 t = (F32)i * mStep; + mPath[i].mPos.set(0, + lerp(0, -sin(F_PI*params.getTwist()*t)*0.5f,t), + lerp(-0.5f, cos(F_PI*params.getTwist()*t)*0.5f,t)); + mPath[i].mScale.set(lerp(1,params.getScale().mV[0],t), + lerp(1,params.getScale().mV[1],t), 0,1); + mPath[i].mTexT = t; + LLQuaternion quat; + quat.setQuat(F_PI * params.getTwist() * t,1,0,0); + LLMatrix3 tmp(quat); + mPath[i].mRot.loadu(tmp); + } + + break; + }; + + if (params.getTwist() != params.getTwistBegin()) mOpen = true; + + //if ((int(fabsf(params.getTwist() - params.getTwistBegin())*100))%100 != 0) { + // mOpen = true; + //} + + return true; +} + +bool LLDynamicPath::generate(const LLPathParams& params, F32 detail, S32 split, + bool is_sculpted, S32 sculpt_size) +{ + mOpen = true; // Draw end caps + if (getPathLength() == 0) + { + // Path hasn't been generated yet. + // Some algorithms later assume at least TWO path points. + resizePath(2); + LLQuaternion quat; + quat.setQuat(0,0,0); + LLMatrix3 tmp(quat); + + for (U32 i = 0; i < 2; i++) + { + mPath[i].mPos.set(0, 0, 0); + mPath[i].mRot.loadu(tmp); + mPath[i].mScale.set(1, 1, 0, 1); + mPath[i].mTexT = 0; + } + } + + return true; +} + + +bool LLPathParams::importFile(LLFILE *fp) +{ + const S32 BUFSIZE = 16384; + char buffer[BUFSIZE]; /* Flawfinder: ignore */ + // *NOTE: changing the size or type of these buffers will require + // changing the sscanf below. + char keyword[256]; /* Flawfinder: ignore */ + char valuestr[256]; /* Flawfinder: ignore */ + keyword[0] = 0; + valuestr[0] = 0; + + F32 tempF32; + F32 x, y; + U32 tempU32; + + while (!feof(fp)) + { + if (fgets(buffer, BUFSIZE, fp) == NULL) + { + buffer[0] = '\0'; + } + + sscanf( /* Flawfinder: ignore */ + buffer, + " %255s %255s", + keyword, valuestr); + if (!strcmp("{", keyword)) + { + continue; + } + if (!strcmp("}",keyword)) + { + break; + } + else if (!strcmp("curve", keyword)) + { + sscanf(valuestr,"%d",&tempU32); + setCurveType((U8) tempU32); + } + else if (!strcmp("begin",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setBegin(tempF32); + } + else if (!strcmp("end",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setEnd(tempF32); + } + else if (!strcmp("scale",keyword)) + { + // Legacy for one dimensional scale per path + sscanf(valuestr,"%g",&tempF32); + setScale(tempF32, tempF32); + } + else if (!strcmp("scale_x", keyword)) + { + sscanf(valuestr, "%g", &x); + setScaleX(x); + } + else if (!strcmp("scale_y", keyword)) + { + sscanf(valuestr, "%g", &y); + setScaleY(y); + } + else if (!strcmp("shear_x", keyword)) + { + sscanf(valuestr, "%g", &x); + setShearX(x); + } + else if (!strcmp("shear_y", keyword)) + { + sscanf(valuestr, "%g", &y); + setShearY(y); + } + else if (!strcmp("twist",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setTwist(tempF32); + } + else if (!strcmp("twist_begin", keyword)) + { + sscanf(valuestr, "%g", &y); + setTwistBegin(y); + } + else if (!strcmp("radius_offset", keyword)) + { + sscanf(valuestr, "%g", &y); + setRadiusOffset(y); + } + else if (!strcmp("taper_x", keyword)) + { + sscanf(valuestr, "%g", &y); + setTaperX(y); + } + else if (!strcmp("taper_y", keyword)) + { + sscanf(valuestr, "%g", &y); + setTaperY(y); + } + else if (!strcmp("revolutions", keyword)) + { + sscanf(valuestr, "%g", &y); + setRevolutions(y); + } + else if (!strcmp("skew", keyword)) + { + sscanf(valuestr, "%g", &y); + setSkew(y); + } + else + { + LL_WARNS() << "unknown keyword " << " in path import" << LL_ENDL; + } + } + return true; +} + + +bool LLPathParams::exportFile(LLFILE *fp) const +{ + fprintf(fp, "\t\tpath 0\n"); + fprintf(fp, "\t\t{\n"); + fprintf(fp, "\t\t\tcurve\t%d\n", getCurveType()); + fprintf(fp, "\t\t\tbegin\t%g\n", getBegin()); + fprintf(fp, "\t\t\tend\t%g\n", getEnd()); + fprintf(fp, "\t\t\tscale_x\t%g\n", getScaleX() ); + fprintf(fp, "\t\t\tscale_y\t%g\n", getScaleY() ); + fprintf(fp, "\t\t\tshear_x\t%g\n", getShearX() ); + fprintf(fp, "\t\t\tshear_y\t%g\n", getShearY() ); + fprintf(fp,"\t\t\ttwist\t%g\n", getTwist()); + + fprintf(fp,"\t\t\ttwist_begin\t%g\n", getTwistBegin()); + fprintf(fp,"\t\t\tradius_offset\t%g\n", getRadiusOffset()); + fprintf(fp,"\t\t\ttaper_x\t%g\n", getTaperX()); + fprintf(fp,"\t\t\ttaper_y\t%g\n", getTaperY()); + fprintf(fp,"\t\t\trevolutions\t%g\n", getRevolutions()); + fprintf(fp,"\t\t\tskew\t%g\n", getSkew()); + + fprintf(fp, "\t\t}\n"); + return true; +} + + +bool LLPathParams::importLegacyStream(std::istream& input_stream) +{ + const S32 BUFSIZE = 16384; + char buffer[BUFSIZE]; /* Flawfinder: ignore */ + // *NOTE: changing the size or type of these buffers will require + // changing the sscanf below. + char keyword[256]; /* Flawfinder: ignore */ + char valuestr[256]; /* Flawfinder: ignore */ + keyword[0] = 0; + valuestr[0] = 0; + + F32 tempF32; + F32 x, y; + U32 tempU32; + + while (input_stream.good()) + { + input_stream.getline(buffer, BUFSIZE); + sscanf( /* Flawfinder: ignore */ + buffer, + " %255s %255s", + keyword, valuestr); + if (!strcmp("{", keyword)) + { + continue; + } + if (!strcmp("}",keyword)) + { + break; + } + else if (!strcmp("curve", keyword)) + { + sscanf(valuestr,"%d",&tempU32); + setCurveType((U8) tempU32); + } + else if (!strcmp("begin",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setBegin(tempF32); + } + else if (!strcmp("end",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setEnd(tempF32); + } + else if (!strcmp("scale",keyword)) + { + // Legacy for one dimensional scale per path + sscanf(valuestr,"%g",&tempF32); + setScale(tempF32, tempF32); + } + else if (!strcmp("scale_x", keyword)) + { + sscanf(valuestr, "%g", &x); + setScaleX(x); + } + else if (!strcmp("scale_y", keyword)) + { + sscanf(valuestr, "%g", &y); + setScaleY(y); + } + else if (!strcmp("shear_x", keyword)) + { + sscanf(valuestr, "%g", &x); + setShearX(x); + } + else if (!strcmp("shear_y", keyword)) + { + sscanf(valuestr, "%g", &y); + setShearY(y); + } + else if (!strcmp("twist",keyword)) + { + sscanf(valuestr,"%g",&tempF32); + setTwist(tempF32); + } + else if (!strcmp("twist_begin", keyword)) + { + sscanf(valuestr, "%g", &y); + setTwistBegin(y); + } + else if (!strcmp("radius_offset", keyword)) + { + sscanf(valuestr, "%g", &y); + setRadiusOffset(y); + } + else if (!strcmp("taper_x", keyword)) + { + sscanf(valuestr, "%g", &y); + setTaperX(y); + } + else if (!strcmp("taper_y", keyword)) + { + sscanf(valuestr, "%g", &y); + setTaperY(y); + } + else if (!strcmp("revolutions", keyword)) + { + sscanf(valuestr, "%g", &y); + setRevolutions(y); + } + else if (!strcmp("skew", keyword)) + { + sscanf(valuestr, "%g", &y); + setSkew(y); + } + else + { + LL_WARNS() << "unknown keyword " << " in path import" << LL_ENDL; + } + } + return true; +} + + +bool LLPathParams::exportLegacyStream(std::ostream& output_stream) const +{ + output_stream << "\t\tpath 0\n"; + output_stream << "\t\t{\n"; + output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n"; + output_stream << "\t\t\tbegin\t" << getBegin() << "\n"; + output_stream << "\t\t\tend\t" << getEnd() << "\n"; + output_stream << "\t\t\tscale_x\t" << getScaleX() << "\n"; + output_stream << "\t\t\tscale_y\t" << getScaleY() << "\n"; + output_stream << "\t\t\tshear_x\t" << getShearX() << "\n"; + output_stream << "\t\t\tshear_y\t" << getShearY() << "\n"; + output_stream <<"\t\t\ttwist\t" << getTwist() << "\n"; + + output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n"; + output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n"; + output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n"; + output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n"; + output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n"; + output_stream <<"\t\t\tskew\t" << getSkew() << "\n"; + + output_stream << "\t\t}\n"; + return true; +} + +LLSD LLPathParams::asLLSD() const +{ + LLSD sd = LLSD(); + sd["curve"] = getCurveType(); + sd["begin"] = getBegin(); + sd["end"] = getEnd(); + sd["scale_x"] = getScaleX(); + sd["scale_y"] = getScaleY(); + sd["shear_x"] = getShearX(); + sd["shear_y"] = getShearY(); + sd["twist"] = getTwist(); + sd["twist_begin"] = getTwistBegin(); + sd["radius_offset"] = getRadiusOffset(); + sd["taper_x"] = getTaperX(); + sd["taper_y"] = getTaperY(); + sd["revolutions"] = getRevolutions(); + sd["skew"] = getSkew(); + + return sd; +} + +bool LLPathParams::fromLLSD(LLSD& sd) +{ + setCurveType(sd["curve"].asInteger()); + setBegin((F32)sd["begin"].asReal()); + setEnd((F32)sd["end"].asReal()); + setScaleX((F32)sd["scale_x"].asReal()); + setScaleY((F32)sd["scale_y"].asReal()); + setShearX((F32)sd["shear_x"].asReal()); + setShearY((F32)sd["shear_y"].asReal()); + setTwist((F32)sd["twist"].asReal()); + setTwistBegin((F32)sd["twist_begin"].asReal()); + setRadiusOffset((F32)sd["radius_offset"].asReal()); + setTaperX((F32)sd["taper_x"].asReal()); + setTaperY((F32)sd["taper_y"].asReal()); + setRevolutions((F32)sd["revolutions"].asReal()); + setSkew((F32)sd["skew"].asReal()); + return true; +} + +void LLPathParams::copyParams(const LLPathParams ¶ms) +{ + setCurveType(params.getCurveType()); + setBegin(params.getBegin()); + setEnd(params.getEnd()); + setScale(params.getScaleX(), params.getScaleY() ); + setShear(params.getShearX(), params.getShearY() ); + setTwist(params.getTwist()); + setTwistBegin(params.getTwistBegin()); + setRadiusOffset(params.getRadiusOffset()); + setTaper( params.getTaperX(), params.getTaperY() ); + setRevolutions(params.getRevolutions()); + setSkew(params.getSkew()); +} + +LLProfile::~LLProfile() +{ +} + + +S32 LLVolume::sNumMeshPoints = 0; + +LLVolume::LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool generate_single_face, const bool is_unique) + : mParams(params) +{ + mUnique = is_unique; + mFaceMask = 0x0; + mDetail = detail; + mSculptLevel = -2; + mSurfaceArea = 1.f; //only calculated for sculpts, defaults to 1 for all other prims + mIsMeshAssetLoaded = false; + mIsMeshAssetUnavaliable = false; + mLODScaleBias.setVec(1,1,1); + mHullPoints = nullptr; + mHullIndices = nullptr; + mNumHullPoints = 0; + mNumHullIndices = 0; + + // set defaults + if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE) + { + mPathp = new LLDynamicPath(); + } + else + { + mPathp = new LLPath(); + } + mProfilep = new LLProfile(); + + mGenerateSingleFace = generate_single_face; + + generate(); + + if ((mParams.getSculptID().isNull() && mParams.getSculptType() == LL_SCULPT_TYPE_NONE) || mParams.getSculptType() == LL_SCULPT_TYPE_MESH) + { + createVolumeFaces(); + } +} + +void LLVolume::resizePath(S32 length) +{ + mPathp->resizePath(length); + mVolumeFaces.clear(); + setDirty(); +} + +void LLVolume::regen() +{ + generate(); + createVolumeFaces(); +} + +void LLVolume::genTangents(S32 face) +{ + // generate legacy tangents for the specified face + llassert(!isMeshAssetLoaded() || mVolumeFaces[face].mTangents != nullptr); // if this is a complete mesh asset, we should already have tangents + mVolumeFaces[face].createTangents(); +} + +LLVolume::~LLVolume() +{ + sNumMeshPoints -= mMesh.size(); + delete mPathp; + + delete mProfilep; + + mPathp = NULL; + mProfilep = NULL; + mVolumeFaces.clear(); + + ll_aligned_free_16(mHullPoints); + mHullPoints = NULL; + ll_aligned_free_16(mHullIndices); + mHullIndices = NULL; +} + +bool LLVolume::generate() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + LL_CHECK_MEMORY + llassert_always(mProfilep); + + //Added 10.03.05 Dave Parks + // Split is a parameter to LLProfile::generate that tesselates edges on the profile + // to prevent lighting and texture interpolation errors on triangles that are + // stretched due to twisting or scaling on the path. + S32 split = (S32) ((mDetail)*0.66f); + + if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_LINE && + (mParams.getPathParams().getScale().mV[0] != 1.0f || + mParams.getPathParams().getScale().mV[1] != 1.0f) && + (mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_SQUARE || + mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_ISOTRI || + mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_EQUALTRI || + mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_RIGHTTRI)) + { + split = 0; + } + + mLODScaleBias.setVec(0.5f, 0.5f, 0.5f); + + F32 profile_detail = mDetail; + F32 path_detail = mDetail; + + if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH) + { + U8 path_type = mParams.getPathParams().getCurveType(); + U8 profile_type = mParams.getProfileParams().getCurveType(); + if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE) + { + //cylinders don't care about Z-Axis + mLODScaleBias.setVec(0.6f, 0.6f, 0.0f); + } + else if (path_type == LL_PCODE_PATH_CIRCLE) + { + mLODScaleBias.setVec(0.6f, 0.6f, 0.6f); + } + } + + bool regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split); + bool regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split); + + if (regenPath || regenProf ) + { + S32 sizeS = mPathp->mPath.size(); + S32 sizeT = mProfilep->mProfile.size(); + + sNumMeshPoints -= mMesh.size(); + mMesh.resize(sizeT * sizeS); + sNumMeshPoints += mMesh.size(); + + //generate vertex positions + + // Run along the path. + LLVector4a* dst = mMesh.mArray; + + for (S32 s = 0; s < sizeS; ++s) + { + F32* scale = mPathp->mPath[s].mScale.getF32ptr(); + + F32 sc [] = + { scale[0], 0, 0, 0, + 0, scale[1], 0, 0, + 0, 0, scale[2], 0, + 0, 0, 0, 1 }; + + LLMatrix4 rot((F32*) mPathp->mPath[s].mRot.mMatrix); + LLMatrix4 scale_mat(sc); + + scale_mat *= rot; + + LLMatrix4a rot_mat; + rot_mat.loadu(scale_mat); + + LLVector4a* profile = mProfilep->mProfile.mArray; + LLVector4a* end_profile = profile+sizeT; + LLVector4a offset = mPathp->mPath[s].mPos; + + // hack to work around MAINT-5660 for debug until we can suss out + // what is wrong with the path generated that inserts NaNs... + if (!offset.isFinite3()) + { + offset.clear(); + } + + LLVector4a tmp; + + // Run along the profile. + while (profile < end_profile) + { + rot_mat.rotate(*profile++, tmp); + dst->setAdd(tmp,offset); + ++dst; + } + } + + for (std::vector<LLProfile::Face>::iterator iter = mProfilep->mFaces.begin(); + iter != mProfilep->mFaces.end(); ++iter) + { + LLFaceID id = iter->mFaceID; + mFaceMask |= id; + } + LL_CHECK_MEMORY + return true; + } + + LL_CHECK_MEMORY + return false; +} + +void LLVolumeFace::VertexData::init() +{ + if (!mData) + { + mData = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*2); + } +} + +LLVolumeFace::VertexData::VertexData() +{ + mData = NULL; + init(); +} + +LLVolumeFace::VertexData::VertexData(const VertexData& rhs) +{ + mData = NULL; + *this = rhs; +} + +const LLVolumeFace::VertexData& LLVolumeFace::VertexData::operator=(const LLVolumeFace::VertexData& rhs) +{ + if (this != &rhs) + { + init(); + LLVector4a::memcpyNonAliased16((F32*) mData, (F32*) rhs.mData, 2*sizeof(LLVector4a)); + mTexCoord = rhs.mTexCoord; + } + return *this; +} + +LLVolumeFace::VertexData::~VertexData() +{ + ll_aligned_free_16(mData); + mData = NULL; +} + +LLVector4a& LLVolumeFace::VertexData::getPosition() +{ + return mData[POSITION]; +} + +LLVector4a& LLVolumeFace::VertexData::getNormal() +{ + return mData[NORMAL]; +} + +const LLVector4a& LLVolumeFace::VertexData::getPosition() const +{ + return mData[POSITION]; +} + +const LLVector4a& LLVolumeFace::VertexData::getNormal() const +{ + return mData[NORMAL]; +} + + +void LLVolumeFace::VertexData::setPosition(const LLVector4a& pos) +{ + mData[POSITION] = pos; +} + +void LLVolumeFace::VertexData::setNormal(const LLVector4a& norm) +{ + mData[NORMAL] = norm; +} + +bool LLVolumeFace::VertexData::operator<(const LLVolumeFace::VertexData& rhs)const +{ + const F32* lp = this->getPosition().getF32ptr(); + const F32* rp = rhs.getPosition().getF32ptr(); + + if (lp[0] != rp[0]) + { + return lp[0] < rp[0]; + } + + if (rp[1] != lp[1]) + { + return lp[1] < rp[1]; + } + + if (rp[2] != lp[2]) + { + return lp[2] < rp[2]; + } + + lp = getNormal().getF32ptr(); + rp = rhs.getNormal().getF32ptr(); + + if (lp[0] != rp[0]) + { + return lp[0] < rp[0]; + } + + if (rp[1] != lp[1]) + { + return lp[1] < rp[1]; + } + + if (rp[2] != lp[2]) + { + return lp[2] < rp[2]; + } + + if (mTexCoord.mV[0] != rhs.mTexCoord.mV[0]) + { + return mTexCoord.mV[0] < rhs.mTexCoord.mV[0]; + } + + return mTexCoord.mV[1] < rhs.mTexCoord.mV[1]; +} + +bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)const +{ + return mData[POSITION].equals3(rhs.getPosition()) && + mData[NORMAL].equals3(rhs.getNormal()) && + mTexCoord == rhs.mTexCoord; +} + +bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const +{ + bool retval = false; + + const F32 epsilon = 0.00001f; + + if (rhs.mData[POSITION].equals3(mData[POSITION], epsilon) && + fabs(rhs.mTexCoord[0]-mTexCoord[0]) < epsilon && + fabs(rhs.mTexCoord[1]-mTexCoord[1]) < epsilon) + { + if (angle_cutoff > 1.f) + { + retval = (mData[NORMAL].equals3(rhs.mData[NORMAL], epsilon)); + } + else + { + F32 cur_angle = rhs.mData[NORMAL].dot3(mData[NORMAL]).getF32(); + retval = cur_angle > angle_cutoff; + } + } + + return retval; +} + +bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + //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) + { + 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); +} + +bool LLVolume::unpackVolumeFaces(U8* in_data, S32 size) +{ + //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) + { + 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); +} + +bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) +{ + { + U32 face_count = mdl.size(); + + if (face_count == 0) + { //no faces unpacked, treat as failed decode + LL_WARNS() << "found no faces!" << LL_ENDL; + return false; + } + + mVolumeFaces.resize(face_count); + + for (size_t i = 0; i < face_count; ++i) + { + LLVolumeFace& face = mVolumeFaces[i]; + + if (mdl[i].has("NoGeometry")) + { //face has no geometry, continue + face.resizeIndices(3); + face.resizeVertices(1); + face.mPositions->clear(); + face.mNormals->clear(); + face.mTexCoords->setZero(); + memset(face.mIndices, 0, sizeof(U16)*3); + continue; + } + + LLSD::Binary pos = mdl[i]["Position"]; + LLSD::Binary norm = mdl[i]["Normal"]; + LLSD::Binary tangent = mdl[i]["Tangent"]; + LLSD::Binary tc = mdl[i]["TexCoord0"]; + LLSD::Binary idx = mdl[i]["TriangleList"]; + + //copy out indices + S32 num_indices = idx.size() / 2; + const S32 indices_to_discard = num_indices % 3; + if (indices_to_discard > 0) + { + // Invalid number of triangle indices + LL_WARNS() << "Incomplete triangle discarded from face! Indices count " << num_indices << " was not divisible by 3. face index: " << i << " Total: " << face_count << LL_ENDL; + num_indices -= indices_to_discard; + } + face.resizeIndices(num_indices); + + if (num_indices > 2 && !face.mIndices) + { + LL_WARNS() << "Failed to allocate " << num_indices << " indices for face index: " << i << " Total: " << face_count << LL_ENDL; + continue; + } + + if (idx.empty() || face.mNumIndices < 3) + { //why is there an empty index list? + LL_WARNS() << "Empty face present! Face index: " << i << " Total: " << face_count << LL_ENDL; + continue; + } + + U16* indices = (U16*) &(idx[0]); + for (U32 j = 0; j < num_indices; ++j) + { + face.mIndices[j] = indices[j]; + } + + //copy out vertices + U32 num_verts = pos.size()/(3*2); + 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.resizeIndices(0); + continue; + } + + LLVector3 minp; + LLVector3 maxp; + LLVector2 min_tc; + LLVector2 max_tc; + + minp.setValue(mdl[i]["PositionDomain"]["Min"]); + maxp.setValue(mdl[i]["PositionDomain"]["Max"]); + LLVector4a min_pos, max_pos; + min_pos.load3(minp.mV); + max_pos.load3(maxp.mV); + + min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]); + max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]); + + //unpack normalized scale/translation + if (mdl[i].has("NormalizedScale")) + { + face.mNormalizedScale.setValue(mdl[i]["NormalizedScale"]); + } + else + { + face.mNormalizedScale.set(1, 1, 1); + } + + LLVector4a pos_range; + pos_range.setSub(max_pos, min_pos); + LLVector2 tc_range2 = max_tc - min_tc; + + LLVector4a tc_range; + tc_range.set(tc_range2[0], tc_range2[1], tc_range2[0], tc_range2[1]); + LLVector4a min_tc4(min_tc[0], min_tc[1], min_tc[0], min_tc[1]); + + LLVector4a* pos_out = face.mPositions; + LLVector4a* norm_out = face.mNormals; + LLVector4a* tc_out = (LLVector4a*) face.mTexCoords; + + { + U16* v = (U16*) &(pos[0]); + for (U32 j = 0; j < num_verts; ++j) + { + pos_out->set((F32) v[0], (F32) v[1], (F32) v[2]); + pos_out->div(65535.f); + pos_out->mul(pos_range); + pos_out->add(min_pos); + pos_out++; + v += 3; + } + + } + + { + if (!norm.empty()) + { + U16* n = (U16*) &(norm[0]); + for (U32 j = 0; j < num_verts; ++j) + { + norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]); + norm_out->div(65535.f); + norm_out->mul(2.f); + norm_out->sub(1.f); + norm_out++; + n += 3; + } + } + else + { + for (U32 j = 0; j < num_verts; ++j) + { + norm_out->clear(); + norm_out++; // or just norm_out[j].clear(); + } + } + } + +#if 0 // keep this code for now in case we decide to add support for on-the-wire tangents + { + if (!tangent.empty()) + { + face.allocateTangents(face.mNumVertices); + U16* t = (U16*)&(tangent[0]); + + // NOTE: tangents coming from the asset may not be mikkt space, but they should always be used by the GLTF shaders to + // maintain compliance with the GLTF spec + LLVector4a* t_out = face.mTangents; + + for (U32 j = 0; j < num_verts; ++j) + { + t_out->set((F32)t[0], (F32)t[1], (F32)t[2], (F32) t[3]); + t_out->div(65535.f); + t_out->mul(2.f); + t_out->sub(1.f); + + F32* tp = t_out->getF32ptr(); + tp[3] = tp[3] < 0.f ? -1.f : 1.f; + + t_out++; + t += 4; + } + } + } +#endif + + { + if (!tc.empty()) + { + U16* t = (U16*) &(tc[0]); + for (U32 j = 0; j < num_verts; j+=2) + { + if (j < num_verts-1) + { + tc_out->set((F32) t[0], (F32) t[1], (F32) t[2], (F32) t[3]); + } + else + { + tc_out->set((F32) t[0], (F32) t[1], 0.f, 0.f); + } + + t += 4; + + tc_out->div(65535.f); + tc_out->mul(tc_range); + tc_out->add(min_tc4); + + tc_out++; + } + } + else + { + for (U32 j = 0; j < num_verts; j += 2) + { + tc_out->clear(); + tc_out++; + } + } + } + + if (mdl[i].has("Weights")) + { + face.allocateWeights(num_verts); + if (!face.mWeights && num_verts) + { + LL_WARNS() << "Failed to allocate " << num_verts << " weights for face index: " << i << " Total: " << face_count << LL_ENDL; + face.resizeIndices(0); + face.resizeVertices(0); + continue; + } + + LLSD::Binary weights = mdl[i]["Weights"]; + + U32 idx = 0; + + U32 cur_vertex = 0; + while (idx < weights.size() && cur_vertex < num_verts) + { + const U8 END_INFLUENCES = 0xFF; + U8 joint = weights[idx++]; + + U32 cur_influence = 0; + LLVector4 wght(0,0,0,0); + U32 joints[4] = {0,0,0,0}; + LLVector4 joints_with_weights(0,0,0,0); + + while (joint != END_INFLUENCES && idx < weights.size()) + { + U16 influence = weights[idx++]; + influence |= ((U16) weights[idx++] << 8); + + F32 w = llclamp((F32) influence / 65535.f, 0.001f, 0.999f); + wght.mV[cur_influence] = w; + joints[cur_influence] = joint; + cur_influence++; + + if (cur_influence >= 4) + { + joint = END_INFLUENCES; + } + else + { + joint = weights[idx++]; + } + } + F32 wsum = wght.mV[VX] + wght.mV[VY] + wght.mV[VZ] + wght.mV[VW]; + if (wsum <= 0.f) + { + wght = LLVector4(0.999f,0.f,0.f,0.f); + } + for (U32 k=0; k<4; k++) + { + F32 f_combined = (F32) joints[k] + wght[k]; + joints_with_weights[k] = f_combined; + // Any weights we added above should wind up non-zero and applied to a specific bone. + // A failure here would indicate a floating point precision error in the math. + llassert((k >= cur_influence) || (f_combined - S32(f_combined) > 0.0f)); + } + face.mWeights[cur_vertex].loadua(joints_with_weights.mV); + + cur_vertex++; + } + + if (cur_vertex != num_verts || idx != weights.size()) + { + LL_WARNS() << "Vertex weight count does not match vertex count!" << LL_ENDL; + } + + } + + // modifier flags? + bool do_mirror = (mParams.getSculptType() & LL_SCULPT_FLAG_MIRROR); + bool do_invert = (mParams.getSculptType() &LL_SCULPT_FLAG_INVERT); + + + // translate to actions: + bool do_reflect_x = false; + bool do_reverse_triangles = false; + bool do_invert_normals = false; + + if (do_mirror) + { + do_reflect_x = true; + do_reverse_triangles = !do_reverse_triangles; + } + + if (do_invert) + { + do_invert_normals = true; + do_reverse_triangles = !do_reverse_triangles; + } + + // now do the work + + if (do_reflect_x) + { + LLVector4a* p = (LLVector4a*) face.mPositions; + LLVector4a* n = (LLVector4a*) face.mNormals; + + for (S32 i = 0; i < face.mNumVertices; i++) + { + p[i].mul(-1.0f); + n[i].mul(-1.0f); + } + } + + if (do_invert_normals) + { + LLVector4a* n = (LLVector4a*) face.mNormals; + + for (S32 i = 0; i < face.mNumVertices; i++) + { + n[i].mul(-1.0f); + } + } + + if (do_reverse_triangles) + { + for (U32 j = 0; j < face.mNumIndices; j += 3) + { + // swap the 2nd and 3rd index + S32 swap = face.mIndices[j+1]; + face.mIndices[j+1] = face.mIndices[j+2]; + face.mIndices[j+2] = swap; + } + } + + //calculate bounding box + // VFExtents change + LLVector4a& min = face.mExtents[0]; + LLVector4a& max = face.mExtents[1]; + + if (face.mNumVertices < 3) + { //empty face, use a dummy 1cm (at 1m scale) bounding box + min.splat(-0.005f); + max.splat(0.005f); + } + else + { + min = max = face.mPositions[0]; + + for (S32 i = 1; i < face.mNumVertices; ++i) + { + min.setMin(min, face.mPositions[i]); + max.setMax(max, face.mPositions[i]); + } + + if (face.mTexCoords) + { + LLVector2& min_tc = face.mTexCoordExtents[0]; + LLVector2& max_tc = face.mTexCoordExtents[1]; + + min_tc = face.mTexCoords[0]; + max_tc = face.mTexCoords[0]; + + for (U32 j = 1; j < face.mNumVertices; ++j) + { + update_min_max(min_tc, max_tc, face.mTexCoords[j]); + } + } + else + { + face.mTexCoordExtents[0].set(0,0); + face.mTexCoordExtents[1].set(1,1); + } + } + } + } + + if (!cacheOptimize(true)) + { + // Out of memory? + LL_WARNS() << "Failed to optimize!" << LL_ENDL; + mVolumeFaces.clear(); + return false; + } + + mSculptLevel = 0; // success! + + return true; +} + + +bool LLVolume::isMeshAssetLoaded() +{ + return mIsMeshAssetLoaded; +} + +void LLVolume::setMeshAssetLoaded(bool loaded) +{ + mIsMeshAssetLoaded = loaded; + if (loaded) + { + mIsMeshAssetUnavaliable = false; + } +} + +void LLVolume::setMeshAssetUnavaliable(bool unavaliable) +{ + // Don't set it if at least one lod loaded + if (!mIsMeshAssetLoaded) + { + mIsMeshAssetUnavaliable = unavaliable; + } +} + +bool LLVolume::isMeshAssetUnavaliable() +{ + return mIsMeshAssetUnavaliable; +} + +void LLVolume::copyFacesTo(std::vector<LLVolumeFace> &faces) const +{ + faces = mVolumeFaces; +} + +void LLVolume::copyFacesFrom(const std::vector<LLVolumeFace> &faces) +{ + mVolumeFaces = faces; + mSculptLevel = 0; +} + +void LLVolume::copyVolumeFaces(const LLVolume* volume) +{ + mVolumeFaces = volume->mVolumeFaces; + mSculptLevel = 0; +} + +bool LLVolume::cacheOptimize(bool gen_tangents) +{ + for (S32 i = 0; i < mVolumeFaces.size(); ++i) + { + if (!mVolumeFaces[i].cacheOptimize(gen_tangents)) + { + return false; + } + } + return true; +} + + +S32 LLVolume::getNumFaces() const +{ + return mIsMeshAssetLoaded ? getNumVolumeFaces() : (S32)mProfilep->mFaces.size(); +} + + +void LLVolume::createVolumeFaces() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + if (mGenerateSingleFace) + { + // do nothing + } + else + { + S32 num_faces = getNumFaces(); + bool partial_build = true; + if (num_faces != mVolumeFaces.size()) + { + partial_build = false; + mVolumeFaces.resize(num_faces); + } + // Initialize volume faces with parameter data + for (S32 i = 0; i < (S32)mVolumeFaces.size(); i++) + { + LLVolumeFace& vf = mVolumeFaces[i]; + LLProfile::Face& face = mProfilep->mFaces[i]; + vf.mBeginS = face.mIndex; + vf.mNumS = face.mCount; + if (vf.mNumS < 0) + { + LL_ERRS() << "Volume face corruption detected." << LL_ENDL; + } + + vf.mBeginT = 0; + vf.mNumT= getPath().mPath.size(); + vf.mID = i; + + // Set the type mask bits correctly + if (mParams.getProfileParams().getHollow() > 0) + { + vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK; + } + if (mProfilep->isOpen()) + { + vf.mTypeMask |= LLVolumeFace::OPEN_MASK; + } + if (face.mCap) + { + vf.mTypeMask |= LLVolumeFace::CAP_MASK; + if (face.mFaceID == LL_FACE_PATH_BEGIN) + { + vf.mTypeMask |= LLVolumeFace::TOP_MASK; + } + else + { + llassert(face.mFaceID == LL_FACE_PATH_END); + vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK; + } + } + else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END)) + { + vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK; + } + else + { + vf.mTypeMask |= LLVolumeFace::SIDE_MASK; + if (face.mFlat) + { + vf.mTypeMask |= LLVolumeFace::FLAT_MASK; + } + if (face.mFaceID & LL_FACE_INNER_SIDE) + { + vf.mTypeMask |= LLVolumeFace::INNER_MASK; + if (face.mFlat && vf.mNumS > 2) + { //flat inner faces have to copy vert normals + vf.mNumS = vf.mNumS*2; + if (vf.mNumS < 0) + { + LL_ERRS() << "Volume face corruption detected." << LL_ENDL; + } + } + } + else + { + vf.mTypeMask |= LLVolumeFace::OUTER_MASK; + } + } + } + + for (face_list_t::iterator iter = mVolumeFaces.begin(); + iter != mVolumeFaces.end(); ++iter) + { + (*iter).create(this, partial_build); + } + } +} + + +inline LLVector4a sculpt_rgb_to_vector(U8 r, U8 g, U8 b) +{ + // maps RGB values to vector values [0..255] -> [-0.5..0.5] + LLVector4a value; + LLVector4a sub(0.5f, 0.5f, 0.5f); + + value.set(r,g,b); + value.mul(1.f/255.f); + value.sub(sub); + + return value; +} + +inline U32 sculpt_xy_to_index(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components) +{ + U32 index = (x + y * sculpt_width) * sculpt_components; + return index; +} + + +inline U32 sculpt_st_to_index(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components) +{ + U32 x = (U32) ((F32)s/(size_s) * (F32) sculpt_width); + U32 y = (U32) ((F32)t/(size_t) * (F32) sculpt_height); + + return sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components); +} + + +inline LLVector4a sculpt_index_to_vector(U32 index, const U8* sculpt_data) +{ + LLVector4a v = sculpt_rgb_to_vector(sculpt_data[index], sculpt_data[index+1], sculpt_data[index+2]); + + return v; +} + +inline LLVector4a sculpt_st_to_vector(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data) +{ + U32 index = sculpt_st_to_index(s, t, size_s, size_t, sculpt_width, sculpt_height, sculpt_components); + + return sculpt_index_to_vector(index, sculpt_data); +} + +inline LLVector4a sculpt_xy_to_vector(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data) +{ + U32 index = sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components); + + return sculpt_index_to_vector(index, sculpt_data); +} + + +F32 LLVolume::sculptGetSurfaceArea() +{ + // test to see if image has enough variation to create non-degenerate geometry + + F32 area = 0; + + S32 sizeS = mPathp->mPath.size(); + S32 sizeT = mProfilep->mProfile.size(); + + for (S32 s = 0; s < sizeS-1; s++) + { + for (S32 t = 0; t < sizeT-1; t++) + { + // get four corners of quad + LLVector4a& p1 = mMesh[(s )*sizeT + (t )]; + LLVector4a& p2 = mMesh[(s+1)*sizeT + (t )]; + LLVector4a& p3 = mMesh[(s )*sizeT + (t+1)]; + LLVector4a& p4 = mMesh[(s+1)*sizeT + (t+1)]; + + // compute the area of the quad by taking the length of the cross product of the two triangles + LLVector4a v0,v1,v2,v3; + v0.setSub(p1,p2); + v1.setSub(p1,p3); + v2.setSub(p4,p2); + v3.setSub(p4,p3); + + LLVector4a cross1, cross2; + cross1.setCross3(v0,v1); + cross2.setCross3(v2,v3); + + //LLVector3 cross1 = (p1 - p2) % (p1 - p3); + //LLVector3 cross2 = (p4 - p2) % (p4 - p3); + + area += (cross1.getLength3() + cross2.getLength3()).getF32() / 2.f; + } + } + + return area; +} + +// create empty placeholder shape +void LLVolume::sculptGenerateEmptyPlaceholder() +{ + S32 sizeS = mPathp->mPath.size(); + S32 sizeT = mProfilep->mProfile.size(); + + S32 line = 0; + + for (S32 s = 0; s < sizeS; s++) + { + for (S32 t = 0; t < sizeT; t++) + { + S32 i = t + line; + LLVector4a& pt = mMesh[i]; + + F32* p = pt.getF32ptr(); + + p[0] = 0; + p[1] = 0; + p[2] = 0; + + llassert(pt.isFinite3()); + } + line += sizeT; + } +} + +// create sphere placeholder shape +void LLVolume::sculptGenerateSpherePlaceholder() +{ + S32 sizeS = mPathp->mPath.size(); + S32 sizeT = mProfilep->mProfile.size(); + + S32 line = 0; + + for (S32 s = 0; s < sizeS; s++) + { + for (S32 t = 0; t < sizeT; t++) + { + S32 i = t + line; + LLVector4a& pt = mMesh[i]; + + + F32 u = (F32)s / (sizeS - 1); + F32 v = (F32)t / (sizeT - 1); + + const F32 RADIUS = (F32) 0.3; + + F32* p = pt.getF32ptr(); + + p[0] = (F32)(sin(F_PI * v) * cos(2.0 * F_PI * u) * RADIUS); + p[1] = (F32)(sin(F_PI * v) * sin(2.0 * F_PI * u) * RADIUS); + p[2] = (F32)(cos(F_PI * v) * RADIUS); + + llassert(pt.isFinite3()); + } + line += sizeT; + } +} + +// create the vertices from the map +void LLVolume::sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type) +{ + U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK; + bool sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT; + bool sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR; + bool reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR + + S32 sizeS = mPathp->mPath.size(); + S32 sizeT = mProfilep->mProfile.size(); + + S32 line = 0; + for (S32 s = 0; s < sizeS; s++) + { + // Run along the profile. + for (S32 t = 0; t < sizeT; t++) + { + S32 i = t + line; + LLVector4a& pt = mMesh[i]; + + S32 reversed_t = t; + + if (reverse_horizontal) + { + reversed_t = sizeT - t - 1; + } + + U32 x = (U32) ((F32)reversed_t/(sizeT-1) * (F32) sculpt_width); + U32 y = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_height); + + + if (y == 0) // top row stitching + { + // pinch? + if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE) + { + x = sculpt_width / 2; + } + } + + if (y == sculpt_height) // bottom row stitching + { + // wrap? + if (sculpt_stitching == LL_SCULPT_TYPE_TORUS) + { + y = 0; + } + else + { + y = sculpt_height - 1; + } + + // pinch? + if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE) + { + x = sculpt_width / 2; + } + } + + if (x == sculpt_width) // side stitching + { + // wrap? + if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) || + (sculpt_stitching == LL_SCULPT_TYPE_TORUS) || + (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER)) + { + x = 0; + } + + else + { + x = sculpt_width - 1; + } + } + + pt = sculpt_xy_to_vector(x, y, sculpt_width, sculpt_height, sculpt_components, sculpt_data); + + if (sculpt_mirror) + { + LLVector4a scale(-1.f,1,1,1); + pt.mul(scale); + } + + llassert(pt.isFinite3()); + } + + line += sizeT; + } +} + + +constexpr S32 SCULPT_REZ_1 = 6; // changed from 4 to 6 - 6 looks round whereas 4 looks square +constexpr S32 SCULPT_REZ_2 = 8; +constexpr S32 SCULPT_REZ_3 = 16; +constexpr S32 SCULPT_REZ_4 = 32; + +S32 sculpt_sides(F32 detail) +{ + // detail is usually one of: 1, 1.5, 2.5, 4.0. + + if (detail <= 1.0) + { + return SCULPT_REZ_1; + } + if (detail <= 2.0) + { + return SCULPT_REZ_2; + } + if (detail <= 3.0) + { + return SCULPT_REZ_3; + } + else + { + return SCULPT_REZ_4; + } +} + + + +// 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) +{ + // this code has the following properties: + // 1) the aspect ratio of the mesh is as close as possible to the ratio of the map + // while still using all available verts + // 2) the mesh cannot have more verts than is allowed by LOD + // 3) the mesh cannot have more verts than is allowed by the map + + S32 max_vertices_lod = (S32)pow((double)sculpt_sides(detail), 2.0); + S32 max_vertices_map = width * height / 4; + + S32 vertices; + if (max_vertices_map > 0) + vertices = llmin(max_vertices_lod, max_vertices_map); + else + vertices = max_vertices_lod; + + + F32 ratio; + if ((width == 0) || (height == 0)) + ratio = 1.f; + else + ratio = (F32) width / (F32) height; + + + s = (S32)(F32) sqrt(((F32)vertices / ratio)); + + s = llmax(s, 4); // no degenerate sizes, please + t = vertices / s; + + t = llmax(t, 4); // no degenerate sizes, please + s = vertices / t; +} + +// sculpt replaces generate() for sculpted surfaces +void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level, bool visible_placeholder) +{ + U8 sculpt_type = mParams.getSculptType(); + + bool data_is_empty = false; + + if (sculpt_width == 0 || sculpt_height == 0 || sculpt_components < 3 || sculpt_data == NULL) + { + sculpt_level = -1; + data_is_empty = true; + } + + S32 requested_sizeS = 0; + S32 requested_sizeT = 0; + + sculpt_calc_mesh_resolution(sculpt_width, sculpt_height, sculpt_type, mDetail, requested_sizeS, requested_sizeT); + + mPathp->generate(mParams.getPathParams(), mDetail, 0, true, requested_sizeS); + mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(), mDetail, 0, true, requested_sizeT); + + S32 sizeS = mPathp->mPath.size(); // we requested a specific size, now see what we really got + S32 sizeT = mProfilep->mProfile.size(); // we requested a specific size, now see what we really got + + // weird crash bug - DEV-11158 - trying to collect more data: + if ((sizeS == 0) || (sizeT == 0)) + { + LL_WARNS() << "sculpt bad mesh size " << sizeS << " " << sizeT << LL_ENDL; + } + + sNumMeshPoints -= mMesh.size(); + mMesh.resize(sizeS * sizeT); + sNumMeshPoints += mMesh.size(); + + //generate vertex positions + if (!data_is_empty) + { + sculptGenerateMapVertices(sculpt_width, sculpt_height, sculpt_components, sculpt_data, sculpt_type); + + // don't test lowest LOD to support legacy content DEV-33670 + if (mDetail > SCULPT_MIN_AREA_DETAIL) + { + F32 area = sculptGetSurfaceArea(); + + mSurfaceArea = area; + + const F32 SCULPT_MAX_AREA = 384.f; + + if (area < SCULPT_MIN_AREA || area > SCULPT_MAX_AREA) + { + data_is_empty = true; + visible_placeholder = true; + } + } + } + + if (data_is_empty) + { + if (visible_placeholder) + { + // Object should be visible since there will be nothing else to display + sculptGenerateSpherePlaceholder(); + } + else + { + sculptGenerateEmptyPlaceholder(); + } + } + + for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++) + { + mFaceMask |= mProfilep->mFaces[i].mFaceID; + } + + mSculptLevel = sculpt_level; + + // Delete any existing faces so that they get regenerated + mVolumeFaces.clear(); + + createVolumeFaces(); +} + + + + +bool LLVolume::isCap(S32 face) +{ + return mProfilep->mFaces[face].mCap; +} + +bool LLVolume::isFlat(S32 face) +{ + return mProfilep->mFaces[face].mFlat; +} + + +bool LLVolumeParams::isSculpt() const +{ + return (mSculptType & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_NONE; +} + +bool LLVolumeParams::isMeshSculpt() const +{ + return (mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH; +} + +bool LLVolumeParams::operator==(const LLVolumeParams ¶ms) const +{ + return ( (getPathParams() == params.getPathParams()) && + (getProfileParams() == params.getProfileParams()) && + (mSculptID == params.mSculptID) && + (mSculptType == params.mSculptType) ); +} + +bool LLVolumeParams::operator!=(const LLVolumeParams ¶ms) const +{ + return ( (getPathParams() != params.getPathParams()) || + (getProfileParams() != params.getProfileParams()) || + (mSculptID != params.mSculptID) || + (mSculptType != params.mSculptType) ); +} + +bool LLVolumeParams::operator<(const LLVolumeParams ¶ms) const +{ + if( getPathParams() != params.getPathParams() ) + { + return getPathParams() < params.getPathParams(); + } + + if (getProfileParams() != params.getProfileParams()) + { + return getProfileParams() < params.getProfileParams(); + } + + if (mSculptID != params.mSculptID) + { + return mSculptID < params.mSculptID; + } + + return mSculptType < params.mSculptType; + + +} + +void LLVolumeParams::copyParams(const LLVolumeParams ¶ms) +{ + mProfileParams.copyParams(params.mProfileParams); + mPathParams.copyParams(params.mPathParams); + mSculptID = params.getSculptID(); + mSculptType = params.getSculptType(); +} + +// Less restricitve approx 0 for volumes +constexpr F32 APPROXIMATELY_ZERO = 0.001f; +bool approx_zero( F32 f, F32 tolerance = APPROXIMATELY_ZERO) +{ + return (f >= -tolerance) && (f <= tolerance); +} + +// return true if in range (or nearly so) +static bool limit_range(F32& v, F32 min, F32 max, F32 tolerance = APPROXIMATELY_ZERO) +{ + F32 min_delta = v - min; + if (min_delta < 0.f) + { + v = min; + if (!approx_zero(min_delta, tolerance)) + return false; + } + F32 max_delta = max - v; + if (max_delta < 0.f) + { + v = max; + if (!approx_zero(max_delta, tolerance)) + return false; + } + return true; +} + +bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e) +{ + bool valid = true; + + // First, clamp to valid ranges. + F32 begin = b; + valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA); + + F32 end = e; + if (end >= .0149f && end < MIN_CUT_DELTA) end = MIN_CUT_DELTA; // eliminate warning for common rounding error + valid &= limit_range(end, MIN_CUT_DELTA, 1.f); + + valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f); + + // Now set them. + mProfileParams.setBegin(begin); + mProfileParams.setEnd(end); + + return valid; +} + +bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e) +{ + bool valid = true; + + // First, clamp to valid ranges. + F32 begin = b; + valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA); + + F32 end = e; + valid &= limit_range(end, MIN_CUT_DELTA, 1.f); + + valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f); + + // Now set them. + mPathParams.setBegin(begin); + mPathParams.setEnd(end); + + return valid; +} + +bool LLVolumeParams::setHollow(const F32 h) +{ + // Validate the hollow based on path and profile. + U8 profile = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK; + U8 hole_type = mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK; + + F32 max_hollow = HOLLOW_MAX; + + // Only square holes have trouble. + if (LL_PCODE_HOLE_SQUARE == hole_type) + { + switch(profile) + { + case LL_PCODE_PROFILE_CIRCLE: + case LL_PCODE_PROFILE_CIRCLE_HALF: + case LL_PCODE_PROFILE_EQUALTRI: + max_hollow = HOLLOW_MAX_SQUARE; + } + } + + F32 hollow = h; + bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow); + mProfileParams.setHollow(hollow); + + return valid; +} + +bool LLVolumeParams::setTwistBegin(const F32 b) +{ + F32 twist_begin = b; + bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX); + mPathParams.setTwistBegin(twist_begin); + return valid; +} + +bool LLVolumeParams::setTwistEnd(const F32 e) +{ + F32 twist_end = e; + bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX); + mPathParams.setTwistEnd(twist_end); + return valid; +} + +bool LLVolumeParams::setRatio(const F32 x, const F32 y) +{ + F32 min_x = RATIO_MIN; + F32 max_x = RATIO_MAX; + F32 min_y = RATIO_MIN; + F32 max_y = RATIO_MAX; + // If this is a circular path (and not a sphere) then 'ratio' is actually hole size. + U8 path_type = mPathParams.getCurveType(); + U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK; + if ( LL_PCODE_PATH_CIRCLE == path_type && + LL_PCODE_PROFILE_CIRCLE_HALF != profile_type) + { + // Holes are more restricted... + min_x = HOLE_X_MIN; + max_x = HOLE_X_MAX; + min_y = HOLE_Y_MIN; + max_y = HOLE_Y_MAX; + } + + F32 ratio_x = x; + bool valid = limit_range(ratio_x, min_x, max_x); + F32 ratio_y = y; + valid &= limit_range(ratio_y, min_y, max_y); + + mPathParams.setScale(ratio_x, ratio_y); + + return valid; +} + +bool LLVolumeParams::setShear(const F32 x, const F32 y) +{ + F32 shear_x = x; + bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX); + F32 shear_y = y; + valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX); + mPathParams.setShear(shear_x, shear_y); + return valid; +} + +bool LLVolumeParams::setTaperX(const F32 v) +{ + F32 taper = v; + bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX); + mPathParams.setTaperX(taper); + return valid; +} + +bool LLVolumeParams::setTaperY(const F32 v) +{ + F32 taper = v; + bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX); + mPathParams.setTaperY(taper); + return valid; +} + +bool LLVolumeParams::setRevolutions(const F32 r) +{ + F32 revolutions = r; + bool valid = limit_range(revolutions, REV_MIN, REV_MAX); + mPathParams.setRevolutions(revolutions); + return valid; +} + +bool LLVolumeParams::setRadiusOffset(const F32 offset) +{ + bool valid = true; + + // If this is a sphere, just set it to 0 and get out. + U8 path_type = mPathParams.getCurveType(); + U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK; + if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type || + LL_PCODE_PATH_CIRCLE != path_type ) + { + mPathParams.setRadiusOffset(0.f); + return true; + } + + // Limit radius offset, based on taper and hole size y. + F32 radius_offset = offset; + F32 taper_y = getTaperY(); + F32 radius_mag = fabs(radius_offset); + F32 hole_y_mag = fabs(getRatioY()); + F32 taper_y_mag = fabs(taper_y); + // Check to see if the taper effects us. + if ( (radius_offset > 0.f && taper_y < 0.f) || + (radius_offset < 0.f && taper_y > 0.f) ) + { + // The taper does not help increase the radius offset range. + taper_y_mag = 0.f; + } + F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag); + + // Enforce the maximum magnitude. + F32 delta = max_radius_mag - radius_mag; + if (delta < 0.f) + { + // Check radius offset sign. + if (radius_offset < 0.f) + { + radius_offset = -max_radius_mag; + } + else + { + radius_offset = max_radius_mag; + } + valid = approx_zero(delta, .1f); + } + + mPathParams.setRadiusOffset(radius_offset); + return valid; +} + +bool LLVolumeParams::setSkew(const F32 skew_value) +{ + bool valid = true; + + // Check the skew value against the revolutions. + F32 skew = llclamp(skew_value, SKEW_MIN, SKEW_MAX); + F32 skew_mag = fabs(skew); + F32 revolutions = getRevolutions(); + F32 scale_x = getRatioX(); + F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f); + // Discontinuity; A revolution of 1 allows skews below 0.5. + if ( fabs(revolutions - 1.0f) < 0.001) + min_skew_mag = 0.0f; + + // Clip skew. + F32 delta = skew_mag - min_skew_mag; + if (delta < 0.f) + { + // Check skew sign. + if (skew < 0.0f) + { + skew = -min_skew_mag; + } + else + { + skew = min_skew_mag; + } + valid = approx_zero(delta, .01f); + } + + mPathParams.setSkew(skew); + return valid; +} + +bool LLVolumeParams::setSculptID(const LLUUID& sculpt_id, U8 sculpt_type) +{ + mSculptID = sculpt_id; + mSculptType = sculpt_type; + return true; +} + +bool LLVolumeParams::setType(U8 profile, U8 path) +{ + bool result = true; + // First, check profile and path for validity. + U8 profile_type = profile & LL_PCODE_PROFILE_MASK; + U8 hole_type = (profile & LL_PCODE_HOLE_MASK) >> 4; + U8 path_type = path >> 4; + + if (profile_type > LL_PCODE_PROFILE_MAX) + { + // Bad profile. Make it square. + profile = LL_PCODE_PROFILE_SQUARE; + result = false; + LL_WARNS() << "LLVolumeParams::setType changing bad profile type (" << profile_type + << ") to be LL_PCODE_PROFILE_SQUARE" << LL_ENDL; + } + else if (hole_type > LL_PCODE_HOLE_MAX) + { + // Bad hole. Make it the same. + profile = profile_type; + result = false; + LL_WARNS() << "LLVolumeParams::setType changing bad hole type (" << hole_type + << ") to be LL_PCODE_HOLE_SAME" << LL_ENDL; + } + + if (path_type < LL_PCODE_PATH_MIN || + path_type > LL_PCODE_PATH_MAX) + { + // Bad path. Make it linear. + result = false; + LL_WARNS() << "LLVolumeParams::setType changing bad path (" << path + << ") to be LL_PCODE_PATH_LINE" << LL_ENDL; + path = LL_PCODE_PATH_LINE; + } + + mProfileParams.setCurveType(profile); + mPathParams.setCurveType(path); + return result; +} + +// static +bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow, + U8 path_curve, F32 path_begin, F32 path_end, + F32 scx, F32 scy, F32 shx, F32 shy, + F32 twistend, F32 twistbegin, F32 radiusoffset, + F32 tx, F32 ty, F32 revolutions, F32 skew) +{ + LLVolumeParams test_params; + if (!test_params.setType (prof_curve, path_curve)) + { + return false; + } + if (!test_params.setBeginAndEndS (prof_begin, prof_end)) + { + return false; + } + if (!test_params.setBeginAndEndT (path_begin, path_end)) + { + return false; + } + if (!test_params.setHollow (hollow)) + { + return false; + } + if (!test_params.setTwistBegin (twistbegin)) + { + return false; + } + if (!test_params.setTwistEnd (twistend)) + { + return false; + } + if (!test_params.setRatio (scx, scy)) + { + return false; + } + if (!test_params.setShear (shx, shy)) + { + return false; + } + if (!test_params.setTaper (tx, ty)) + { + return false; + } + if (!test_params.setRevolutions (revolutions)) + { + return false; + } + if (!test_params.setRadiusOffset (radiusoffset)) + { + return false; + } + if (!test_params.setSkew (skew)) + { + return false; + } + return true; +} + +void LLVolume::getLoDTriangleCounts(const LLVolumeParams& params, S32* counts) +{ //attempt to approximate the number of triangles that will result from generating a volume LoD set for the + //supplied LLVolumeParams -- inaccurate, but a close enough approximation for determining streaming cost + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + F32 detail[] = {1.f, 1.5f, 2.5f, 4.f}; + for (S32 i = 0; i < 4; i++) + { + S32 count = 0; + S32 path_points = LLPath::getNumPoints(params.getPathParams(), detail[i]); + S32 profile_points = LLProfile::getNumPoints(params.getProfileParams(), false, detail[i]); + + count = (profile_points-1)*2*(path_points-1); + count += profile_points*2; + + counts[i] = count; + } +} + + +S32 LLVolume::getNumTriangles(S32* vcount) const +{ + U32 triangle_count = 0; + U32 vertex_count = 0; + + for (S32 i = 0; i < getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = getVolumeFace(i); + triangle_count += face.mNumIndices/3; + + vertex_count += face.mNumVertices; + } + + + if (vcount) + { + *vcount = vertex_count; + } + + return triangle_count; +} + + +//----------------------------------------------------------------------------- +// generateSilhouetteVertices() +//----------------------------------------------------------------------------- +void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices, + std::vector<LLVector3> &normals, + const LLVector3& obj_cam_vec_in, + const LLMatrix4& mat_in, + const LLMatrix3& norm_mat_in, + S32 face_mask) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + LLMatrix4a mat; + mat.loadu(mat_in); + + LLMatrix4a norm_mat; + norm_mat.loadu(norm_mat_in); + + LLVector4a obj_cam_vec; + obj_cam_vec.load3(obj_cam_vec_in.mV); + + vertices.clear(); + normals.clear(); + + if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) + { + return; + } + + S32 cur_index = 0; + //for each face + for (face_list_t::iterator iter = mVolumeFaces.begin(); + iter != mVolumeFaces.end(); ++iter) + { + LLVolumeFace& face = *iter; + + if (!(face_mask & (0x1 << cur_index++)) || + face.mNumIndices == 0 || face.mEdge.empty()) + { + continue; + } + + if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) + { + LLVector4a* v = (LLVector4a*)face.mPositions; + LLVector4a* n = (LLVector4a*)face.mNormals; + + for (U32 j = 0; j < face.mNumIndices / 3; j++) + { + for (S32 k = 0; k < 3; k++) + { + S32 index = face.mEdge[j * 3 + k]; + + if (index == -1) + { + // silhouette edge, currently only cubes, so no other conditions + + S32 v1 = face.mIndices[j * 3 + k]; + S32 v2 = face.mIndices[j * 3 + ((k + 1) % 3)]; + + LLVector4a t; + mat.affineTransform(v[v1], t); + vertices.push_back(LLVector3(t[0], t[1], t[2])); + + norm_mat.rotate(n[v1], t); + + t.normalize3fast(); + normals.push_back(LLVector3(t[0], t[1], t[2])); + + mat.affineTransform(v[v2], t); + vertices.push_back(LLVector3(t[0], t[1], t[2])); + + norm_mat.rotate(n[v2], t); + t.normalize3fast(); + normals.push_back(LLVector3(t[0], t[1], t[2])); + } + } + } + + } + else + { + //============================================== + //DEBUG draw edge map instead of silhouette edge + //============================================== + +#if DEBUG_SILHOUETTE_EDGE_MAP + + //for each triangle + U32 tri_count = face.mNumIndices / 3; + for (U32 j = 0; j < tri_count; j++) { + //get vertices + S32 v1 = face.mIndices[j*3+0]; + S32 v2 = face.mIndices[j*3+1]; + S32 v3 = face.mIndices[j*3+2]; + + //get current face center + LLVector3 cCenter = (face.mVertices[v1].getPosition() + + face.mVertices[v2].getPosition() + + face.mVertices[v3].getPosition()) / 3.0f; + + //for each edge + for (S32 k = 0; k < 3; k++) { + S32 nIndex = face.mEdge[j*3+k]; + if (nIndex <= -1) { + continue; + } + + if (nIndex >= (S32)tri_count) { + continue; + } + //get neighbor vertices + v1 = face.mIndices[nIndex*3+0]; + v2 = face.mIndices[nIndex*3+1]; + v3 = face.mIndices[nIndex*3+2]; + + //get neighbor face center + LLVector3 nCenter = (face.mVertices[v1].getPosition() + + face.mVertices[v2].getPosition() + + face.mVertices[v3].getPosition()) / 3.0f; + + //draw line + vertices.push_back(cCenter); + vertices.push_back(nCenter); + normals.push_back(LLVector3(1,1,1)); + normals.push_back(LLVector3(1,1,1)); + segments.push_back(vertices.size()); + } + } + + continue; + + //============================================== + //DEBUG + //============================================== + + //============================================== + //DEBUG draw normals instead of silhouette edge + //============================================== +#elif DEBUG_SILHOUETTE_NORMALS + + //for each vertex + for (U32 j = 0; j < face.mNumVertices; j++) { + vertices.push_back(face.mVertices[j].getPosition()); + vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].getNormal()*0.1f); + normals.push_back(LLVector3(0,0,1)); + normals.push_back(LLVector3(0,0,1)); + segments.push_back(vertices.size()); +#if DEBUG_SILHOUETTE_BINORMALS + vertices.push_back(face.mVertices[j].getPosition()); + vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].mTangent*0.1f); + normals.push_back(LLVector3(0,0,1)); + normals.push_back(LLVector3(0,0,1)); + segments.push_back(vertices.size()); +#endif + } + + continue; +#else + //============================================== + //DEBUG + //============================================== + + constexpr U8 AWAY = 0x01, + TOWARDS = 0x02; + + //for each triangle + std::vector<U8> fFacing; + vector_append(fFacing, face.mNumIndices/3); + + LLVector4a* v = (LLVector4a*) face.mPositions; + LLVector4a* n = (LLVector4a*) face.mNormals; + + for (U32 j = 0; j < face.mNumIndices/3; j++) + { + //approximate normal + S32 v1 = face.mIndices[j*3+0]; + S32 v2 = face.mIndices[j*3+1]; + S32 v3 = face.mIndices[j*3+2]; + + LLVector4a c1,c2; + c1.setSub(v[v1], v[v2]); + c2.setSub(v[v2], v[v3]); + + LLVector4a norm; + + norm.setCross3(c1, c2); + + if (norm.dot3(norm) < 0.00000001f) + { + fFacing[j] = AWAY | TOWARDS; + } + else + { + //get view vector + LLVector4a view; + view.setSub(obj_cam_vec, v[v1]); + bool away = view.dot3(norm) > 0.0f; + if (away) + { + fFacing[j] = AWAY; + } + else + { + fFacing[j] = TOWARDS; + } + } + } + + //for each triangle + for (U32 j = 0; j < face.mNumIndices/3; j++) + { + if (fFacing[j] == (AWAY | TOWARDS)) + { //this is a degenerate triangle + //take neighbor facing (degenerate faces get facing of one of their neighbors) + // *FIX IF NEEDED: this does not deal with neighboring degenerate faces + for (S32 k = 0; k < 3; k++) + { + S32 index = face.mEdge[j*3+k]; + if (index != -1) + { + fFacing[j] = fFacing[index]; + break; + } + } + continue; //skip degenerate face + } + + //for each edge + for (S32 k = 0; k < 3; k++) { + S32 index = face.mEdge[j*3+k]; + if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) { + //our neighbor is degenerate, make him face our direction + fFacing[face.mEdge[j*3+k]] = fFacing[j]; + continue; + } + + if (index == -1 || //edge has no neighbor, MUST be a silhouette edge + (fFacing[index] & fFacing[j]) == 0) { //we found a silhouette edge + + S32 v1 = face.mIndices[j*3+k]; + S32 v2 = face.mIndices[j*3+((k+1)%3)]; + + LLVector4a t; + mat.affineTransform(v[v1], t); + vertices.push_back(LLVector3(t[0], t[1], t[2])); + + norm_mat.rotate(n[v1], t); + + t.normalize3fast(); + normals.push_back(LLVector3(t[0], t[1], t[2])); + + mat.affineTransform(v[v2], t); + vertices.push_back(LLVector3(t[0], t[1], t[2])); + + norm_mat.rotate(n[v2], t); + t.normalize3fast(); + normals.push_back(LLVector3(t[0], t[1], t[2])); + } + } + } +#endif + } + } +} + +S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + S32 face, + LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent_out) +{ + S32 hit_face = -1; + + S32 start_face; + S32 end_face; + + if (face == -1) // ALL_SIDES + { + start_face = 0; + end_face = getNumVolumeFaces() - 1; + } + else + { + start_face = face; + end_face = face; + } + + LLVector4a dir; + dir.setSub(end, start); + + F32 closest_t = 2.f; // must be larger than 1 + + end_face = llmin(end_face, getNumVolumeFaces()-1); + + for (S32 i = start_face; i <= end_face; i++) + { + LLVolumeFace &face = mVolumeFaces[i]; + + LLVector4a box_center; + box_center.setAdd(face.mExtents[0], face.mExtents[1]); + box_center.mul(0.5f); + + LLVector4a box_size; + box_size.setSub(face.mExtents[1], face.mExtents[0]); + + if (LLLineSegmentBoxIntersect(start, end, box_center, box_size)) + { + if (tangent_out != NULL) // if the caller wants tangents, we may need to generate them + { + genTangents(i); + } + + if (isUnique()) + { //don't bother with an octree for flexi volumes + U32 tri_count = face.mNumIndices/3; + + for (U32 j = 0; j < tri_count; ++j) + { + U16 idx0 = face.mIndices[j*3+0]; + U16 idx1 = face.mIndices[j*3+1]; + U16 idx2 = face.mIndices[j*3+2]; + + const LLVector4a& v0 = face.mPositions[idx0]; + const LLVector4a& v1 = face.mPositions[idx1]; + const LLVector4a& v2 = face.mPositions[idx2]; + + F32 a,b,t; + + if (LLTriangleRayIntersect(v0, v1, v2, + start, dir, a, b, t)) + { + if ((t >= 0.f) && // if hit is after start + (t <= 1.f) && // and before end + (t < closest_t)) // and this hit is closer + { + closest_t = t; + hit_face = i; + + if (intersection != NULL) + { + LLVector4a intersect = dir; + intersect.mul(closest_t); + intersect.add(start); + *intersection = intersect; + } + + + if (tex_coord != NULL) + { + LLVector2* tc = (LLVector2*) face.mTexCoords; + *tex_coord = ((1.f - a - b) * tc[idx0] + + a * tc[idx1] + + b * tc[idx2]); + + } + + if (normal!= NULL) + { + LLVector4a* norm = face.mNormals; + + LLVector4a n1,n2,n3; + n1 = norm[idx0]; + n1.mul(1.f-a-b); + + n2 = norm[idx1]; + n2.mul(a); + + n3 = norm[idx2]; + n3.mul(b); + + n1.add(n2); + n1.add(n3); + + *normal = n1; + } + + if (tangent_out != NULL) + { + LLVector4a* tangents = face.mTangents; + + LLVector4a t1,t2,t3; + t1 = tangents[idx0]; + t1.mul(1.f-a-b); + + t2 = tangents[idx1]; + t2.mul(a); + + t3 = tangents[idx2]; + t3.mul(b); + + t1.add(t2); + t1.add(t3); + + *tangent_out = t1; + } + } + } + } + } + else + { + if (!face.getOctree()) + { + face.createOctree(); + } + + LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out); + intersect.traverse(face.getOctree()); + if (intersect.mHitFace) + { + hit_face = i; + } + } + } + } + + + return hit_face; +} + +class LLVertexIndexPair +{ +public: + LLVertexIndexPair(const LLVector3 &vertex, const S32 index); + + LLVector3 mVertex; + S32 mIndex; +}; + +LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index) +{ + mVertex = vertex; + mIndex = index; +} + +constexpr F32 VERTEX_SLOP = 0.00001f; + +struct lessVertex +{ + bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b) + { + const F32 slop = VERTEX_SLOP; + + if (a->mVertex.mV[0] + slop < b->mVertex.mV[0]) + { + return true; + } + else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0]) + { + return false; + } + + if (a->mVertex.mV[1] + slop < b->mVertex.mV[1]) + { + return true; + } + else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1]) + { + return false; + } + + if (a->mVertex.mV[2] + slop < b->mVertex.mV[2]) + { + return true; + } + else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2]) + { + return false; + } + + return false; + } +}; + +struct lessTriangle +{ + bool operator()(const S32 *a, const S32 *b) + { + if (*a < *b) + { + return true; + } + else if (*a > *b) + { + return false; + } + + if (*(a+1) < *(b+1)) + { + return true; + } + else if (*(a+1) > *(b+1)) + { + return false; + } + + if (*(a+2) < *(b+2)) + { + return true; + } + else if (*(a+2) > *(b+2)) + { + return false; + } + + return false; + } +}; + +bool equalTriangle(const S32 *a, const S32 *b) +{ + if ((*a == *b) && (*(a+1) == *(b+1)) && (*(a+2) == *(b+2))) + { + return true; + } + return false; +} + +bool LLVolumeParams::importFile(LLFILE *fp) +{ + //LL_INFOS() << "importing volume" << LL_ENDL; + const S32 BUFSIZE = 16384; + char buffer[BUFSIZE]; /* Flawfinder: ignore */ + // *NOTE: changing the size or type of this buffer will require + // changing the sscanf below. + char keyword[256]; /* Flawfinder: ignore */ + keyword[0] = 0; + + while (!feof(fp)) + { + if (fgets(buffer, BUFSIZE, fp) == NULL) + { + buffer[0] = '\0'; + } + + sscanf(buffer, " %255s", keyword); /* Flawfinder: ignore */ + if (!strcmp("{", keyword)) + { + continue; + } + if (!strcmp("}",keyword)) + { + break; + } + else if (!strcmp("profile", keyword)) + { + mProfileParams.importFile(fp); + } + else if (!strcmp("path",keyword)) + { + mPathParams.importFile(fp); + } + else + { + LL_WARNS() << "unknown keyword " << keyword << " in volume import" << LL_ENDL; + } + } + + return true; +} + +bool LLVolumeParams::exportFile(LLFILE *fp) const +{ + fprintf(fp,"\tshape 0\n"); + fprintf(fp,"\t{\n"); + mPathParams.exportFile(fp); + mProfileParams.exportFile(fp); + fprintf(fp, "\t}\n"); + return true; +} + + +bool LLVolumeParams::importLegacyStream(std::istream& input_stream) +{ + //LL_INFOS() << "importing volume" << LL_ENDL; + const S32 BUFSIZE = 16384; + // *NOTE: changing the size or type of this buffer will require + // changing the sscanf below. + char buffer[BUFSIZE]; /* Flawfinder: ignore */ + char keyword[256]; /* Flawfinder: ignore */ + keyword[0] = 0; + + while (input_stream.good()) + { + input_stream.getline(buffer, BUFSIZE); + sscanf(buffer, " %255s", keyword); + if (!strcmp("{", keyword)) + { + continue; + } + if (!strcmp("}",keyword)) + { + break; + } + else if (!strcmp("profile", keyword)) + { + mProfileParams.importLegacyStream(input_stream); + } + else if (!strcmp("path",keyword)) + { + mPathParams.importLegacyStream(input_stream); + } + else + { + LL_WARNS() << "unknown keyword " << keyword << " in volume import" << LL_ENDL; + } + } + + return true; +} + +bool LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const +{ + output_stream <<"\tshape 0\n"; + output_stream <<"\t{\n"; + mPathParams.exportLegacyStream(output_stream); + mProfileParams.exportLegacyStream(output_stream); + output_stream << "\t}\n"; + return true; +} + +LLSD LLVolumeParams::sculptAsLLSD() const +{ + LLSD sd = LLSD(); + sd["id"] = getSculptID(); + sd["type"] = getSculptType(); + + return sd; +} + +bool LLVolumeParams::sculptFromLLSD(LLSD& sd) +{ + setSculptID(sd["id"].asUUID(), (U8)sd["type"].asInteger()); + return true; +} + +LLSD LLVolumeParams::asLLSD() const +{ + LLSD sd = LLSD(); + sd["path"] = mPathParams; + sd["profile"] = mProfileParams; + sd["sculpt"] = sculptAsLLSD(); + + return sd; +} + +bool LLVolumeParams::fromLLSD(LLSD& sd) +{ + mPathParams.fromLLSD(sd["path"]); + mProfileParams.fromLLSD(sd["profile"]); + sculptFromLLSD(sd["sculpt"]); + + return true; +} + +void LLVolumeParams::reduceS(F32 begin, F32 end) +{ + begin = llclampf(begin); + end = llclampf(end); + if (begin > end) + { + F32 temp = begin; + begin = end; + end = temp; + } + F32 a = mProfileParams.getBegin(); + F32 b = mProfileParams.getEnd(); + mProfileParams.setBegin(a + begin * (b - a)); + mProfileParams.setEnd(a + end * (b - a)); +} + +void LLVolumeParams::reduceT(F32 begin, F32 end) +{ + begin = llclampf(begin); + end = llclampf(end); + if (begin > end) + { + F32 temp = begin; + begin = end; + end = temp; + } + F32 a = mPathParams.getBegin(); + F32 b = mPathParams.getEnd(); + mPathParams.setBegin(a + begin * (b - a)); + mPathParams.setEnd(a + end * (b - a)); +} + +const F32 MIN_CONCAVE_PROFILE_WEDGE = 0.125f; // 1/8 unity +const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f; // 1/9 unity + +// returns true if the shape can be approximated with a convex shape +// for collison purposes +bool LLVolumeParams::isConvex() const +{ + if (!getSculptID().isNull()) + { + // can't determine, be safe and say no: + return false; + } + + F32 path_length = mPathParams.getEnd() - mPathParams.getBegin(); + F32 hollow = mProfileParams.getHollow(); + + U8 path_type = mPathParams.getCurveType(); + if ( path_length > MIN_CONCAVE_PATH_WEDGE + && ( mPathParams.getTwist() != mPathParams.getTwistBegin() + || (hollow > 0.f + && LL_PCODE_PATH_LINE != path_type) ) ) + { + // twist along a "not too short" path is concave + return false; + } + + F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin(); + bool same_hole = hollow == 0.f + || (mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK) == LL_PCODE_HOLE_SAME; + + F32 min_profile_wedge = MIN_CONCAVE_PROFILE_WEDGE; + U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK; + if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ) + { + // it is a sphere and spheres get twice the minimum profile wedge + min_profile_wedge = 2.f * MIN_CONCAVE_PROFILE_WEDGE; + } + + bool convex_profile = ( ( profile_length == 1.f + || profile_length <= 0.5f ) + && hollow == 0.f ) // trivially convex + || ( profile_length <= min_profile_wedge + && same_hole ); // effectvely convex (even when hollow) + + if (!convex_profile) + { + // profile is concave + return false; + } + + if ( LL_PCODE_PATH_LINE == path_type ) + { + // straight paths with convex profile + return true; + } + + bool concave_path = (path_length < 1.0f) && (path_length > 0.5f); + if (concave_path) + { + return false; + } + + // we're left with spheres, toroids and tubes + if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ) + { + // at this stage all spheres must be convex + return true; + } + + // it's a toroid or tube + if ( path_length <= MIN_CONCAVE_PATH_WEDGE ) + { + // effectively convex + return true; + } + + return false; +} + +// debug +void LLVolumeParams::setCube() +{ + mProfileParams.setCurveType(LL_PCODE_PROFILE_SQUARE); + mProfileParams.setBegin(0.f); + mProfileParams.setEnd(1.f); + mProfileParams.setHollow(0.f); + + mPathParams.setBegin(0.f); + mPathParams.setEnd(1.f); + mPathParams.setScale(1.f, 1.f); + mPathParams.setShear(0.f, 0.f); + mPathParams.setCurveType(LL_PCODE_PATH_LINE); + mPathParams.setTwistBegin(0.f); + mPathParams.setTwistEnd(0.f); + mPathParams.setRadiusOffset(0.f); + mPathParams.setTaper(0.f, 0.f); + mPathParams.setRevolutions(0.f); + mPathParams.setSkew(0.f); +} + +LLFaceID LLVolume::generateFaceMask() +{ + LLFaceID new_mask = 0x0000; + + switch(mParams.getProfileParams().getCurveType() & LL_PCODE_PROFILE_MASK) + { + case LL_PCODE_PROFILE_CIRCLE: + case LL_PCODE_PROFILE_CIRCLE_HALF: + new_mask |= LL_FACE_OUTER_SIDE_0; + break; + case LL_PCODE_PROFILE_SQUARE: + { + for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 4.f); side < llceil(mParams.getProfileParams().getEnd() * 4.f); side++) + { + new_mask |= LL_FACE_OUTER_SIDE_0 << side; + } + } + break; + case LL_PCODE_PROFILE_ISOTRI: + case LL_PCODE_PROFILE_EQUALTRI: + case LL_PCODE_PROFILE_RIGHTTRI: + { + for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 3.f); side < llceil(mParams.getProfileParams().getEnd() * 3.f); side++) + { + new_mask |= LL_FACE_OUTER_SIDE_0 << side; + } + } + break; + default: + LL_ERRS() << "Unknown profile!" << LL_ENDL; + break; + } + + // handle hollow objects + if (mParams.getProfileParams().getHollow() > 0) + { + new_mask |= LL_FACE_INNER_SIDE; + } + + // handle open profile curves + if (mProfilep->isOpen()) + { + new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END; + } + + // handle open path curves + if (mPathp->isOpen()) + { + new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END; + } + + return new_mask; +} + +bool LLVolume::isFaceMaskValid(LLFaceID face_mask) +{ + LLFaceID test_mask = 0; + for(S32 i = 0; i < getNumFaces(); i++) + { + test_mask |= mProfilep->mFaces[i].mFaceID; + } + + return test_mask == face_mask; +} + +bool LLVolume::isConvex() const +{ + // mParams.isConvex() may return false even though the final + // geometry is actually convex due to LOD approximations. + // TODO -- provide LLPath and LLProfile with isConvex() methods + // that correctly determine convexity. -- Leviathan + return mParams.isConvex(); +} + + +std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params) +{ + s << "{type=" << (U32) profile_params.mCurveType; + s << ", begin=" << profile_params.mBegin; + s << ", end=" << profile_params.mEnd; + s << ", hollow=" << profile_params.mHollow; + s << "}"; + return s; +} + + +std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params) +{ + s << "{type=" << (U32) path_params.mCurveType; + s << ", begin=" << path_params.mBegin; + s << ", end=" << path_params.mEnd; + s << ", twist=" << path_params.mTwistEnd; + s << ", scale=" << path_params.mScale; + s << ", shear=" << path_params.mShear; + s << ", twist_begin=" << path_params.mTwistBegin; + s << ", radius_offset=" << path_params.mRadiusOffset; + s << ", taper=" << path_params.mTaper; + s << ", revolutions=" << path_params.mRevolutions; + s << ", skew=" << path_params.mSkew; + s << "}"; + return s; +} + + +std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params) +{ + s << "{profileparams = " << volume_params.mProfileParams; + s << ", pathparams = " << volume_params.mPathParams; + s << "}"; + return s; +} + + +std::ostream& operator<<(std::ostream &s, const LLProfile &profile) +{ + s << " {open=" << (U32) profile.mOpen; + s << ", dirty=" << profile.mDirty; + s << ", totalout=" << profile.mTotalOut; + s << ", total=" << profile.mTotal; + s << "}"; + return s; +} + + +std::ostream& operator<<(std::ostream &s, const LLPath &path) +{ + s << "{open=" << (U32) path.mOpen; + s << ", dirty=" << path.mDirty; + s << ", step=" << path.mStep; + s << ", total=" << path.mTotal; + s << "}"; + return s; +} + +std::ostream& operator<<(std::ostream &s, const LLVolume &volume) +{ + s << "{params = " << volume.getParams(); + s << ", path = " << *volume.mPathp; + s << ", profile = " << *volume.mProfilep; + s << "}"; + return s; +} + + +std::ostream& operator<<(std::ostream &s, const LLVolume *volumep) +{ + s << "{params = " << volumep->getParams(); + s << ", path = " << *(volumep->mPathp); + s << ", profile = " << *(volumep->mProfilep); + s << "}"; + return s; +} + +LLVolumeFace::LLVolumeFace() : + mID(0), + mTypeMask(0), + mBeginS(0), + mBeginT(0), + mNumS(0), + mNumT(0), + mNumVertices(0), + mNumAllocatedVertices(0), + mNumIndices(0), + mPositions(NULL), + mNormals(NULL), + mTangents(NULL), + mTexCoords(NULL), + mIndices(NULL), + mWeights(NULL), +#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS + mJustWeights(NULL), + mJointIndices(NULL), +#endif + mWeightsScrubbed(false), + mOctree(NULL), + mOctreeTriangles(NULL), + mOptimized(false) +{ + mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); + mExtents[0].splat(-0.5f); + mExtents[1].splat(0.5f); + mCenter = mExtents+2; +} + +LLVolumeFace::LLVolumeFace(const LLVolumeFace& src) +: mID(0), + mTypeMask(0), + mBeginS(0), + mBeginT(0), + mNumS(0), + mNumT(0), + mNumVertices(0), + mNumAllocatedVertices(0), + mNumIndices(0), + mPositions(NULL), + mNormals(NULL), + mTangents(NULL), + mTexCoords(NULL), + mIndices(NULL), + mWeights(NULL), +#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS + mJustWeights(NULL), + mJointIndices(NULL), +#endif + mWeightsScrubbed(false), + mOctree(NULL), + mOctreeTriangles(NULL) +{ + mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); + mCenter = mExtents+2; + *this = src; +} + +LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) +{ + if (&src == this) + { //self assignment, do nothing + return *this; + } + + mID = src.mID; + mTypeMask = src.mTypeMask; + mBeginS = src.mBeginS; + mBeginT = src.mBeginT; + mNumS = src.mNumS; + mNumT = src.mNumT; + + mExtents[0] = src.mExtents[0]; + mExtents[1] = src.mExtents[1]; + *mCenter = *src.mCenter; + + mNumVertices = 0; + mNumIndices = 0; + + freeData(); + + resizeVertices(src.mNumVertices); + resizeIndices(src.mNumIndices); + + if (mNumVertices) + { + S32 vert_size = mNumVertices*sizeof(LLVector4a); + S32 tc_size = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF; + + LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) src.mPositions, vert_size); + + if (src.mNormals) + { + LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) src.mNormals, vert_size); + } + + if(src.mTexCoords) + { + LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size); + } + + if (src.mTangents) + { + allocateTangents(src.mNumVertices); + LLVector4a::memcpyNonAliased16((F32*) mTangents, (F32*) src.mTangents, vert_size); + } + else + { + ll_aligned_free_16(mTangents); + mTangents = NULL; + } + + if (src.mWeights) + { + llassert(!mWeights); // don't orphan an old alloc here accidentally + allocateWeights(src.mNumVertices); + LLVector4a::memcpyNonAliased16((F32*) mWeights, (F32*) src.mWeights, vert_size); + mWeightsScrubbed = src.mWeightsScrubbed; + } + else + { + ll_aligned_free_16(mWeights); + mWeights = NULL; + mWeightsScrubbed = false; + } + + #if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS + if (src.mJointIndices) + { + llassert(!mJointIndices); // don't orphan an old alloc here accidentally + allocateJointIndices(src.mNumVertices); + LLVector4a::memcpyNonAliased16((F32*) mJointIndices, (F32*) src.mJointIndices, src.mNumVertices * sizeof(U8) * 4); + } + else*/ + { + ll_aligned_free_16(mJointIndices); + mJointIndices = NULL; + } + #endif + + } + + if (mNumIndices) + { + S32 idx_size = (mNumIndices*sizeof(U16)+0xF) & ~0xF; + + LLVector4a::memcpyNonAliased16((F32*) mIndices, (F32*) src.mIndices, idx_size); + } + else + { + ll_aligned_free_16(mIndices); + mIndices = NULL; + } + + mOptimized = src.mOptimized; + mNormalizedScale = src.mNormalizedScale; + + //delete + return *this; +} + +LLVolumeFace::~LLVolumeFace() +{ + ll_aligned_free_16(mExtents); + mExtents = NULL; + mCenter = NULL; + + freeData(); +} + +void LLVolumeFace::freeData() +{ + ll_aligned_free<64>(mPositions); + mPositions = NULL; + + //normals and texture coordinates are part of the same buffer as mPositions, do not free them separately + mNormals = NULL; + mTexCoords = NULL; + + ll_aligned_free_16(mIndices); + mIndices = NULL; + ll_aligned_free_16(mTangents); + mTangents = NULL; + ll_aligned_free_16(mWeights); + mWeights = NULL; + +#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS + ll_aligned_free_16(mJointIndices); + mJointIndices = NULL; + ll_aligned_free_16(mJustWeights); + mJustWeights = NULL; +#endif + + destroyOctree(); +} + +bool LLVolumeFace::create(LLVolume* volume, bool partial_build) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + //tree for this face is no longer valid + destroyOctree(); + + LL_CHECK_MEMORY + bool ret = false ; + if (mTypeMask & CAP_MASK) + { + ret = createCap(volume, partial_build); + LL_CHECK_MEMORY + } + else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK)) + { + ret = createSide(volume, partial_build); + LL_CHECK_MEMORY + } + else + { + LL_ERRS() << "Unknown/uninitialized face type!" << LL_ENDL; + } + + return ret ; +} + +void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv) +{ + cv.setPosition(mPositions[index]); + if (mNormals) + { + cv.setNormal(mNormals[index]); + } + else + { + cv.getNormal().clear(); + } + + if (mTexCoords) + { + cv.mTexCoord = mTexCoords[index]; + } + else + { + cv.mTexCoord.clear(); + } +} + +bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const +{ + return getPosition().equals3(rhs.getPosition()) && + mTexCoord == rhs.mTexCoord && + getNormal().equals3(rhs.getNormal()); +} + +bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a, const LLVector3& b) const +{ + if (a.mV[0] != b.mV[0]) + { + return a.mV[0] < b.mV[0]; + } + + if (a.mV[1] != b.mV[1]) + { + return a.mV[1] < b.mV[1]; + } + + return a.mV[2] < b.mV[2]; +} + +void LLVolumeFace::remap() +{ + // Generate a remap buffer + std::vector<unsigned int> remap(mNumVertices); + S32 remap_vertices_count = LLMeshOptimizer::generateRemapMultiU16(&remap[0], + mIndices, + mNumIndices, + mPositions, + mNormals, + mTexCoords, + mNumVertices); + + // Allocate new buffers + S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF; + U16* remap_indices = (U16*)ll_aligned_malloc_16(size); + + S32 tc_bytes_size = ((remap_vertices_count * sizeof(LLVector2)) + 0xF) & ~0xF; + LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * remap_vertices_count + tc_bytes_size); + LLVector4a* remap_normals = remap_positions + remap_vertices_count; + LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count); + + // Fill the buffers + LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]); + LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]); + LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]); + LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]); + + // Free unused buffers + ll_aligned_free_16(mIndices); + ll_aligned_free<64>(mPositions); + + // Tangets are now invalid + ll_aligned_free_16(mTangents); + mTangents = NULL; + + // Assign new values + mIndices = remap_indices; + mPositions = remap_positions; + mNormals = remap_normals; + mTexCoords = remap_tex_coords; + mNumVertices = remap_vertices_count; + mNumAllocatedVertices = remap_vertices_count; +} + +void LLVolumeFace::optimize(F32 angle_cutoff) +{ + LLVolumeFace new_face; + + //map of points to vector of vertices at that point + std::map<U64, std::vector<VertexMapData> > point_map; + + LLVector4a range; + range.setSub(mExtents[1],mExtents[0]); + + //remove redundant vertices + for (U32 i = 0; i < mNumIndices; ++i) + { + U16 index = mIndices[i]; + + if (index >= mNumVertices) + { + // invalid index + // replace with a valid index to avoid crashes + index = mNumVertices - 1; + mIndices[i] = index; + + // Needs better logging + LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL; + } + + LLVolumeFace::VertexData cv; + getVertexData(index, cv); + + bool found = false; + + LLVector4a pos; + pos.setSub(mPositions[index], mExtents[0]); + pos.div(range); + + U64 pos64 = 0; + + pos64 = (U16) (pos[0]*65535); + pos64 = pos64 | (((U64) (pos[1]*65535)) << 16); + pos64 = pos64 | (((U64) (pos[2]*65535)) << 32); + + std::map<U64, std::vector<VertexMapData> >::iterator point_iter = point_map.find(pos64); + + if (point_iter != point_map.end()) + { //duplicate point might exist + for (U32 j = 0; j < point_iter->second.size(); ++j) + { + LLVolumeFace::VertexData& tv = (point_iter->second)[j]; + if (tv.compareNormal(cv, angle_cutoff)) + { + found = true; + new_face.pushIndex((point_iter->second)[j].mIndex); + break; + } + } + } + + if (!found) + { + new_face.pushVertex(cv); + U16 index = (U16) new_face.mNumVertices-1; + new_face.pushIndex(index); + + 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[pos64].push_back(d); + } + } + } + + + if (angle_cutoff > 1.f && !mNormals) + { + // Now alloc'd with positions + //ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!mTexCoords) + { + // Now alloc'd with positions + //ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + + // Only swap data if we've actually optimized the mesh + // + if (new_face.mNumVertices <= mNumVertices) + { + llassert(new_face.mNumIndices == mNumIndices); + swapData(new_face); + } + +} + +class LLVCacheTriangleData; + +class LLVCacheVertexData +{ +public: + S32 mIdx; + S32 mCacheTag; + F64 mScore; + U32 mActiveTriangles; + std::vector<LLVCacheTriangleData*> mTriangles; + + LLVCacheVertexData() + { + mCacheTag = -1; + mScore = 0.0; + mActiveTriangles = 0; + mIdx = -1; + } +}; + +class LLVCacheTriangleData +{ +public: + bool mActive; + F64 mScore; + LLVCacheVertexData* mVertex[3]; + + LLVCacheTriangleData() + { + mActive = true; + mScore = 0.0; + mVertex[0] = mVertex[1] = mVertex[2] = NULL; + } + + void complete() + { + mActive = false; + for (S32 i = 0; i < 3; ++i) + { + if (mVertex[i]) + { + llassert(mVertex[i]->mActiveTriangles > 0); + mVertex[i]->mActiveTriangles--; + } + } + } + + bool operator<(const LLVCacheTriangleData& rhs) const + { //highest score first + return rhs.mScore < mScore; + } +}; + +constexpr F64 FindVertexScore_CacheDecayPower = 1.5; +constexpr F64 FindVertexScore_LastTriScore = 0.75; +constexpr F64 FindVertexScore_ValenceBoostScale = 2.0; +constexpr F64 FindVertexScore_ValenceBoostPower = 0.5; +constexpr U32 MaxSizeVertexCache = 32; +constexpr F64 FindVertexScore_Scaler = 1.0/(MaxSizeVertexCache-3); + +F64 find_vertex_score(LLVCacheVertexData& data) +{ + F64 score = -1.0; + + score = 0.0; + + S32 cache_idx = data.mCacheTag; + + if (cache_idx < 0) + { + //not in cache + } + else + { + if (cache_idx < 3) + { //vertex was in the last triangle + score = FindVertexScore_LastTriScore; + } + else + { //more points for being higher in the cache + score = 1.0-((cache_idx-3)*FindVertexScore_Scaler); + score = pow(score, FindVertexScore_CacheDecayPower); + } + } + + //bonus points for having low valence + F64 valence_boost = pow((F64)data.mActiveTriangles, -FindVertexScore_ValenceBoostPower); + score += FindVertexScore_ValenceBoostScale * valence_boost; + + return score; +} + +class LLVCacheFIFO +{ +public: + LLVCacheVertexData* mCache[MaxSizeVertexCache]; + U32 mMisses; + + LLVCacheFIFO() + { + mMisses = 0; + for (U32 i = 0; i < MaxSizeVertexCache; ++i) + { + mCache[i] = NULL; + } + } + + void addVertex(LLVCacheVertexData* data) + { + if (data->mCacheTag == -1) + { + mMisses++; + + S32 end = MaxSizeVertexCache-1; + + if (mCache[end]) + { + mCache[end]->mCacheTag = -1; + } + + for (S32 i = end; i > 0; --i) + { + mCache[i] = mCache[i-1]; + if (mCache[i]) + { + mCache[i]->mCacheTag = i; + } + } + + mCache[0] = data; + data->mCacheTag = 0; + } + } +}; + +class LLVCacheLRU +{ +public: + LLVCacheVertexData* mCache[MaxSizeVertexCache+3]; + + LLVCacheTriangleData* mBestTriangle; + + U32 mMisses; + + LLVCacheLRU() + { + for (U32 i = 0; i < MaxSizeVertexCache+3; ++i) + { + mCache[i] = NULL; + } + + mBestTriangle = NULL; + mMisses = 0; + } + + void addVertex(LLVCacheVertexData* data) + { + S32 end = MaxSizeVertexCache+2; + if (data->mCacheTag != -1) + { //just moving a vertex to the front of the cache + end = data->mCacheTag; + } + else + { + mMisses++; + if (mCache[end]) + { //adding a new vertex, vertex at end of cache falls off + mCache[end]->mCacheTag = -1; + } + } + + for (S32 i = end; i > 0; --i) + { //adjust cache pointers and tags + mCache[i] = mCache[i-1]; + + if (mCache[i]) + { + mCache[i]->mCacheTag = i; + } + } + + mCache[0] = data; + mCache[0]->mCacheTag = 0; + } + + void addTriangle(LLVCacheTriangleData* data) + { + addVertex(data->mVertex[0]); + addVertex(data->mVertex[1]); + addVertex(data->mVertex[2]); + } + + void updateScores() + { + LLVCacheVertexData** data_iter = mCache+MaxSizeVertexCache; + LLVCacheVertexData** end_data = mCache+MaxSizeVertexCache+3; + + while(data_iter != end_data) + { + LLVCacheVertexData* data = *data_iter++; + //trailing 3 vertices aren't actually in the cache for scoring purposes + if (data) + { + data->mCacheTag = -1; + } + } + + data_iter = mCache; + end_data = mCache+MaxSizeVertexCache; + + while (data_iter != end_data) + { //update scores of vertices in cache + LLVCacheVertexData* data = *data_iter++; + if (data) + { + data->mScore = find_vertex_score(*data); + } + } + + mBestTriangle = NULL; + //update triangle scores + data_iter = mCache; + end_data = mCache+MaxSizeVertexCache+3; + + while (data_iter != end_data) + { + LLVCacheVertexData* data = *data_iter++; + if (data) + { + for (std::vector<LLVCacheTriangleData*>::iterator iter = data->mTriangles.begin(), end_iter = data->mTriangles.end(); iter != end_iter; ++iter) + { + LLVCacheTriangleData* tri = *iter; + if (tri->mActive) + { + tri->mScore = tri->mVertex[0] ? tri->mVertex[0]->mScore : 0; + tri->mScore += tri->mVertex[1] ? tri->mVertex[1]->mScore : 0; + tri->mScore += tri->mVertex[2] ? tri->mVertex[2]->mScore : 0; + + if (!mBestTriangle || mBestTriangle->mScore < tri->mScore) + { + mBestTriangle = tri; + } + } + } + } + } + + //knock trailing 3 vertices off the cache + data_iter = mCache+MaxSizeVertexCache; + end_data = mCache+MaxSizeVertexCache+3; + while (data_iter != end_data) + { + LLVCacheVertexData* data = *data_iter; + if (data) + { + llassert(data->mCacheTag == -1); + *data_iter = NULL; + } + ++data_iter; + } + } +}; + +// data structures for tangent generation + +struct MikktData +{ + LLVolumeFace* face; + std::vector<LLVector3> p; + std::vector<LLVector3> n; + std::vector<LLVector2> tc; + std::vector<LLVector4> w; + std::vector<LLVector4> t; + + MikktData(LLVolumeFace* f) + : face(f) + { + U32 count = face->mNumIndices; + + p.resize(count); + n.resize(count); + tc.resize(count); + t.resize(count); + + if (face->mWeights) + { + w.resize(count); + } + + + LLVector3 inv_scale(1.f / face->mNormalizedScale.mV[0], 1.f / face->mNormalizedScale.mV[1], 1.f / face->mNormalizedScale.mV[2]); + + + for (int i = 0; i < face->mNumIndices; ++i) + { + U32 idx = face->mIndices[i]; + + p[i].set(face->mPositions[idx].getF32ptr()); + p[i].scaleVec(face->mNormalizedScale); //put mesh in original coordinate frame when reconstructing tangents + n[i].set(face->mNormals[idx].getF32ptr()); + n[i].scaleVec(inv_scale); + n[i].normalize(); + tc[i].set(face->mTexCoords[idx]); + + if (idx >= face->mNumVertices) + { + // invalid index + // replace with a valid index to avoid crashes + idx = face->mNumVertices - 1; + face->mIndices[i] = idx; + + // Needs better logging + LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL; + } + + if (face->mWeights) + { + w[i].set(face->mWeights[idx].getF32ptr()); + } + } + } +}; + + +bool LLVolumeFace::cacheOptimize(bool gen_tangents) +{ //optimize for vertex cache according to Forsyth method: + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + llassert(!mOptimized); + mOptimized = true; + + if (gen_tangents && mNormals && mTexCoords) + { // 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 + SMikkTSpaceInterface ms; + + ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + return face->mNumIndices / 3; + }; + + ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) + { + return 3; + }; + + ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + F32* v = data->p[iFace * 3 + iVert].mV; + fvPosOut[0] = v[0]; + fvPosOut[1] = v[1]; + fvPosOut[2] = v[2]; + }; + + ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + F32* n = data->n[iFace * 3 + iVert].mV; + fvNormOut[0] = n[0]; + fvNormOut[1] = n[1]; + fvNormOut[2] = n[2]; + }; + + ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + F32* tc = data->tc[iFace * 3 + iVert].mV; + fvTexcOut[0] = tc[0]; + fvTexcOut[1] = tc[1]; + }; + + ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + S32 i = iFace * 3 + iVert; + + data->t[i].set(fvTangent); + data->t[i].mV[3] = fSign; + }; + + ms.m_setTSpace = nullptr; + + MikktData data(this); + + SMikkTSpaceContext ctx = { &ms, &data }; + + genTangSpaceDefault(&ctx); + + //re-weld + meshopt_Stream mos[] = + { + { &data.p[0], sizeof(LLVector3), sizeof(LLVector3) }, + { &data.n[0], sizeof(LLVector3), sizeof(LLVector3) }, + { &data.t[0], sizeof(LLVector4), sizeof(LLVector4) }, + { &data.tc[0], sizeof(LLVector2), sizeof(LLVector2) }, + { data.w.empty() ? nullptr : &data.w[0], sizeof(LLVector4), sizeof(LLVector4) } + }; + + std::vector<U32> remap; + remap.resize(data.p.size()); + + U32 stream_count = data.w.empty() ? 4 : 5; + + size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count); + + if (vert_count < 65535 && vert_count != 0) + { + std::vector<U32> indices; + indices.resize(mNumIndices); + + //copy results back into volume + resizeVertices(vert_count); + + if (!data.w.empty()) + { + allocateWeights(vert_count); + } + + allocateTangents(mNumVertices); + + for (int i = 0; i < mNumIndices; ++i) + { + U32 src_idx = i; + U32 dst_idx = remap[i]; + if (dst_idx >= mNumVertices) + { + dst_idx = mNumVertices - 1; + // Shouldn't happen, figure out what gets returned in remap and why. + llassert(false); + LL_DEBUGS_ONCE("LLVOLUME") << "Invalid destination index, substituting" << LL_ENDL; + } + mIndices[i] = dst_idx; + + mPositions[dst_idx].load3(data.p[src_idx].mV); + mNormals[dst_idx].load3(data.n[src_idx].mV); + mTexCoords[dst_idx] = data.tc[src_idx]; + + mTangents[dst_idx].loadua(data.t[src_idx].mV); + + if (mWeights) + { + mWeights[dst_idx].loadua(data.w[src_idx].mV); + } + } + + // put back in normalized coordinate frame + LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]); + LLVector4a scale; + scale.load3(mNormalizedScale.mV); + scale.getF32ptr()[3] = 1.f; + + for (int i = 0; i < mNumVertices; ++i) + { + mPositions[i].mul(inv_scale); + mNormals[i].mul(scale); + mNormals[i].normalize3(); + F32 w = mTangents[i].getF32ptr()[3]; + mTangents[i].mul(scale); + mTangents[i].normalize3(); + mTangents[i].getF32ptr()[3] = w; + } + } + else + { + if (vert_count == 0) + { + LL_WARNS_ONCE("LLVOLUME") << "meshopt_generateVertexRemapMulti failed to process a model or model was invalid" << LL_ENDL; + } + // blew past the max vertex size limit, use legacy tangent generation which never adds verts + createTangents(); + } + } + + // cache optimize index buffer + + // meshopt needs scratch space, do some pointer shuffling to avoid an extra index buffer copy + U16* src_indices = mIndices; + mIndices = nullptr; + resizeIndices(mNumIndices); + + meshopt_optimizeVertexCache<U16>(mIndices, src_indices, mNumIndices, mNumVertices); + + ll_aligned_free_16(src_indices); + + return true; +} + +void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVector4a& size) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + if (getOctree()) + { + return; + } + + llassert(mNumIndices % 3 == 0); + + mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL); + new LLVolumeOctreeListener(mOctree); + const U32 num_triangles = mNumIndices / 3; + // Initialize all the triangles we need + mOctreeTriangles = new LLVolumeTriangle[num_triangles]; + + for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index) + { //for each triangle + const U32 index = triangle_index * 3; + LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index]; + + const LLVector4a& v0 = mPositions[mIndices[index]]; + const LLVector4a& v1 = mPositions[mIndices[index + 1]]; + const LLVector4a& v2 = mPositions[mIndices[index + 2]]; + + //store pointers to vertex data + tri->mV[0] = &v0; + tri->mV[1] = &v1; + tri->mV[2] = &v2; + + //store indices + tri->mIndex[0] = mIndices[index]; + tri->mIndex[1] = mIndices[index + 1]; + tri->mIndex[2] = mIndices[index + 2]; + + //get minimum point + LLVector4a min = v0; + min.setMin(min, v1); + min.setMin(min, v2); + + //get maximum point + LLVector4a max = v0; + max.setMax(max, v1); + max.setMax(max, v2); + + //compute center + LLVector4a center; + center.setAdd(min, max); + center.mul(0.5f); + + tri->mPositionGroup = center; + + //compute "radius" + LLVector4a size; + size.setSub(max,min); + + tri->mRadius = size.getLength3().getF32() * scaler; + + //insert + mOctree->insert(tri); + } + + //remove unneeded octree layers + while (!mOctree->balance()) { } + + //calculate AABB for each node + LLVolumeOctreeRebound rebound(this); + rebound.traverse(mOctree); + + if (gDebugGL) + { + LLVolumeOctreeValidate validate; + validate.traverse(mOctree); + } +} + +void LLVolumeFace::destroyOctree() +{ + delete mOctree; + mOctree = NULL; + delete[] mOctreeTriangles; + mOctreeTriangles = NULL; +} + +const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const +{ + return mOctree; +} + + +void LLVolumeFace::swapData(LLVolumeFace& rhs) +{ + llswap(rhs.mPositions, mPositions); + llswap(rhs.mNormals, mNormals); + llswap(rhs.mTangents, mTangents); + llswap(rhs.mTexCoords, mTexCoords); + llswap(rhs.mIndices,mIndices); + llswap(rhs.mNumVertices, mNumVertices); + llswap(rhs.mNumIndices, mNumIndices); +} + +void LerpPlanarVertex(LLVolumeFace::VertexData& v0, + LLVolumeFace::VertexData& v1, + LLVolumeFace::VertexData& v2, + LLVolumeFace::VertexData& vout, + F32 coef01, + F32 coef02) +{ + + LLVector4a lhs; + lhs.setSub(v1.getPosition(), v0.getPosition()); + lhs.mul(coef01); + LLVector4a rhs; + rhs.setSub(v2.getPosition(), v0.getPosition()); + rhs.mul(coef02); + + rhs.add(lhs); + rhs.add(v0.getPosition()); + + vout.setPosition(rhs); + + vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02); + vout.setNormal(v0.getNormal()); +} + +bool LLVolumeFace::createUnCutCubeCap(LLVolume* volume, bool partial_build) +{ + LL_CHECK_MEMORY + + const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh(); + const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile; + S32 max_s = volume->getProfile().getTotal(); + S32 max_t = volume->getPath().mPath.size(); + + // S32 i; + S32 grid_size = (profile.size()-1)/4; + // VFExtents change + LLVector4a& min = mExtents[0]; + LLVector4a& max = mExtents[1]; + + S32 offset = 0; + if (mTypeMask & TOP_MASK) + { + offset = (max_t-1) * max_s; + } + else + { + offset = mBeginS; + } + + { + VertexData corners[4]; + VertexData baseVert; + for(S32 t = 0; t < 4; t++) + { + corners[t].getPosition().load4a(mesh[offset + (grid_size*t)].getF32ptr()); + corners[t].mTexCoord.mV[0] = profile[grid_size*t][0]+0.5f; + corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t][1]; + } + + { + LLVector4a lhs; + lhs.setSub(corners[1].getPosition(), corners[0].getPosition()); + LLVector4a rhs; + rhs.setSub(corners[2].getPosition(), corners[1].getPosition()); + baseVert.getNormal().setCross3(lhs, rhs); + baseVert.getNormal().normalize3fast(); + } + + if(!(mTypeMask & TOP_MASK)) + { + baseVert.getNormal().mul(-1.0f); + } + else + { + //Swap the UVs on the U(X) axis for top face + LLVector2 swap; + swap = corners[0].mTexCoord; + corners[0].mTexCoord=corners[3].mTexCoord; + corners[3].mTexCoord=swap; + swap = corners[1].mTexCoord; + corners[1].mTexCoord=corners[2].mTexCoord; + corners[2].mTexCoord=swap; + } + + S32 size = (grid_size+1)*(grid_size+1); + resizeVertices(size); + + LLVector4a* pos = (LLVector4a*) mPositions; + LLVector4a* norm = (LLVector4a*) mNormals; + LLVector2* tc = (LLVector2*) mTexCoords; + + for(int gx = 0;gx<grid_size+1;gx++) + { + for(int gy = 0;gy<grid_size+1;gy++) + { + VertexData newVert; + LerpPlanarVertex( + corners[0], + corners[1], + corners[3], + newVert, + (F32)gx/(F32)grid_size, + (F32)gy/(F32)grid_size); + + *pos++ = newVert.getPosition(); + *norm++ = baseVert.getNormal(); + *tc++ = newVert.mTexCoord; + + if (gx == 0 && gy == 0) + { + min = newVert.getPosition(); + max = min; + } + else + { + min.setMin(min, newVert.getPosition()); + max.setMax(max, newVert.getPosition()); + } + } + } + + mCenter->setAdd(min, max); + mCenter->mul(0.5f); + } + + if (!partial_build) + { + resizeIndices(grid_size*grid_size*6); + if (!volume->isMeshAssetLoaded()) + { + S32 size = grid_size * grid_size * 6; + try + { + mEdge.resize(size); + } + catch (std::bad_alloc&) + { + LL_WARNS("LLVOLUME") << "Resize of mEdge to " << size << " failed" << LL_ENDL; + return false; + } + } + + U16* out = mIndices; + + S32 idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0}; + + int cur_edge = 0; + + for(S32 gx = 0;gx<grid_size;gx++) + { + + for(S32 gy = 0;gy<grid_size;gy++) + { + if (mTypeMask & TOP_MASK) + { + for(S32 i=5;i>=0;i--) + { + *out++ = ((gy*(grid_size+1))+gx+idxs[i]); + } + + S32 edge_value = grid_size * 2 * gy + gx * 2; + + if (gx > 0) + { + mEdge[cur_edge++] = edge_value; + } + else + { + mEdge[cur_edge++] = -1; // Mark face to higlight it + } + + if (gy < grid_size - 1) + { + mEdge[cur_edge++] = edge_value; + } + else + { + mEdge[cur_edge++] = -1; + } + + mEdge[cur_edge++] = edge_value; + + if (gx < grid_size - 1) + { + mEdge[cur_edge++] = edge_value; + } + else + { + mEdge[cur_edge++] = -1; + } + + if (gy > 0) + { + mEdge[cur_edge++] = edge_value; + } + else + { + mEdge[cur_edge++] = -1; + } + + mEdge[cur_edge++] = edge_value; + } + else + { + for(S32 i=0;i<6;i++) + { + *out++ = ((gy*(grid_size+1))+gx+idxs[i]); + } + + S32 edge_value = grid_size * 2 * gy + gx * 2; + + if (gy > 0) + { + mEdge[cur_edge++] = edge_value; + } + else + { + mEdge[cur_edge++] = -1; + } + + if (gx < grid_size - 1) + { + mEdge[cur_edge++] = edge_value; + } + else + { + mEdge[cur_edge++] = -1; + } + + mEdge[cur_edge++] = edge_value; + + if (gy < grid_size - 1) + { + mEdge[cur_edge++] = edge_value; + } + else + { + mEdge[cur_edge++] = -1; + } + + if (gx > 0) + { + mEdge[cur_edge++] = edge_value; + } + else + { + mEdge[cur_edge++] = -1; + } + + mEdge[cur_edge++] = edge_value; + } + } + } + } + + LL_CHECK_MEMORY + return true; +} + + +bool LLVolumeFace::createCap(LLVolume* volume, bool partial_build) +{ + if (!(mTypeMask & HOLLOW_MASK) && + !(mTypeMask & OPEN_MASK) && + ((volume->getParams().getPathParams().getBegin()==0.0f)&& + (volume->getParams().getPathParams().getEnd()==1.0f))&& + (volume->getParams().getProfileParams().getCurveType()==LL_PCODE_PROFILE_SQUARE && + volume->getParams().getPathParams().getCurveType()==LL_PCODE_PATH_LINE) + ){ + return createUnCutCubeCap(volume, partial_build); + } + + S32 num_vertices = 0, num_indices = 0; + + const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh(); + const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile; + + // All types of caps have the same number of vertices and indices + num_vertices = profile.size(); + num_indices = (profile.size() - 2)*3; + + if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK)) + { + resizeVertices(num_vertices+1); + + //if (!partial_build) + { + resizeIndices(num_indices+3); + } + } + else + { + resizeVertices(num_vertices); + //if (!partial_build) + { + resizeIndices(num_indices); + } + } + + LL_CHECK_MEMORY; + + S32 max_s = volume->getProfile().getTotal(); + S32 max_t = volume->getPath().mPath.size(); + + mCenter->clear(); + + S32 offset = 0; + if (mTypeMask & TOP_MASK) + { + offset = (max_t-1) * max_s; + } + else + { + offset = mBeginS; + } + + // Figure out the normal, assume all caps are flat faces. + // Cross product to get normals. + + LLVector2 cuv; + LLVector2 min_uv, max_uv; + // VFExtents change + LLVector4a& min = mExtents[0]; + LLVector4a& max = mExtents[1]; + + LLVector2* tc = (LLVector2*) mTexCoords; + LLVector4a* pos = (LLVector4a*) mPositions; + LLVector4a* norm = (LLVector4a*) mNormals; + + // Copy the vertices into the array + + const LLVector4a* src = mesh.mArray+offset; + const LLVector4a* end = src+num_vertices; + + min = *src; + max = min; + + + const LLVector4a* p = profile.mArray; + + if (mTypeMask & TOP_MASK) + { + min_uv.set((*p)[0]+0.5f, + (*p)[1]+0.5f); + + max_uv = min_uv; + + while(src < end) + { + tc->mV[0] = (*p)[0]+0.5f; + tc->mV[1] = (*p)[1]+0.5f; + + llassert(src->isFinite3()); // MAINT-5660; don't know why this happens, does not affect Release builds + update_min_max(min,max,*src); + update_min_max(min_uv, max_uv, *tc); + + *pos = *src; + + llassert(pos->isFinite3()); + + ++p; + ++tc; + ++src; + ++pos; + } + } + else + { + + min_uv.set((*p)[0]+0.5f, + 0.5f - (*p)[1]); + max_uv = min_uv; + + while(src < end) + { + // Mirror for underside. + tc->mV[0] = (*p)[0]+0.5f; + tc->mV[1] = 0.5f - (*p)[1]; + + llassert(src->isFinite3()); + update_min_max(min,max,*src); + update_min_max(min_uv, max_uv, *tc); + + *pos = *src; + + llassert(pos->isFinite3()); + + ++p; + ++tc; + ++src; + ++pos; + } + } + + LL_CHECK_MEMORY + + mCenter->setAdd(min, max); + mCenter->mul(0.5f); + + cuv = (min_uv + max_uv)*0.5f; + + + VertexData vd; + vd.setPosition(*mCenter); + vd.mTexCoord = cuv; + + if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK)) + { + *pos++ = *mCenter; + *tc++ = cuv; + num_vertices++; + } + + LL_CHECK_MEMORY + + //if (partial_build) + //{ + // return true; + //} + + if (mTypeMask & HOLLOW_MASK) + { + if (mTypeMask & TOP_MASK) + { + // HOLLOW TOP + // Does it matter if it's open or closed? - djs + + S32 pt1 = 0, pt2 = num_vertices - 1; + S32 i = 0; + while (pt2 - pt1 > 1) + { + // Use the profile points instead of the mesh, since you want + // the un-transformed profile distances. + const LLVector4a& p1 = profile[pt1]; + const LLVector4a& p2 = profile[pt2]; + const LLVector4a& pa = profile[pt1+1]; + const LLVector4a& pb = profile[pt2-1]; + + const F32* p1V = p1.getF32ptr(); + const F32* p2V = p2.getF32ptr(); + const F32* paV = pa.getF32ptr(); + const F32* pbV = pb.getF32ptr(); + + //p1.mV[VZ] = 0.f; + //p2.mV[VZ] = 0.f; + //pa.mV[VZ] = 0.f; + //pb.mV[VZ] = 0.f; + + // Use area of triangle to determine backfacing + F32 area_1a2, area_1ba, area_21b, area_2ab; + area_1a2 = (p1V[0]*paV[1] - paV[0]*p1V[1]) + + (paV[0]*p2V[1] - p2V[0]*paV[1]) + + (p2V[0]*p1V[1] - p1V[0]*p2V[1]); + + area_1ba = (p1V[0]*pbV[1] - pbV[0]*p1V[1]) + + (pbV[0]*paV[1] - paV[0]*pbV[1]) + + (paV[0]*p1V[1] - p1V[0]*paV[1]); + + area_21b = (p2V[0]*p1V[1] - p1V[0]*p2V[1]) + + (p1V[0]*pbV[1] - pbV[0]*p1V[1]) + + (pbV[0]*p2V[1] - p2V[0]*pbV[1]); + + area_2ab = (p2V[0]*paV[1] - paV[0]*p2V[1]) + + (paV[0]*pbV[1] - pbV[0]*paV[1]) + + (pbV[0]*p2V[1] - p2V[0]*pbV[1]); + + bool use_tri1a2 = true; + bool tri_1a2 = true; + bool tri_21b = true; + + if (area_1a2 < 0) + { + tri_1a2 = false; + } + if (area_2ab < 0) + { + // Can't use, because it contains point b + tri_1a2 = false; + } + if (area_21b < 0) + { + tri_21b = false; + } + if (area_1ba < 0) + { + // Can't use, because it contains point b + tri_21b = false; + } + + if (!tri_1a2) + { + use_tri1a2 = false; + } + else if (!tri_21b) + { + use_tri1a2 = true; + } + else + { + LLVector4a d1; + d1.setSub(p1, pa); + + LLVector4a d2; + d2.setSub(p2, pb); + + if (d1.dot3(d1) < d2.dot3(d2)) + { + use_tri1a2 = true; + } + else + { + use_tri1a2 = false; + } + } + + if (use_tri1a2) + { + mIndices[i++] = pt1; + mIndices[i++] = pt1 + 1; + mIndices[i++] = pt2; + pt1++; + } + else + { + mIndices[i++] = pt1; + mIndices[i++] = pt2 - 1; + mIndices[i++] = pt2; + pt2--; + } + } + } + else + { + // HOLLOW BOTTOM + // Does it matter if it's open or closed? - djs + + llassert(mTypeMask & BOTTOM_MASK); + S32 pt1 = 0, pt2 = num_vertices - 1; + + S32 i = 0; + while (pt2 - pt1 > 1) + { + // Use the profile points instead of the mesh, since you want + // the un-transformed profile distances. + const LLVector4a& p1 = profile[pt1]; + const LLVector4a& p2 = profile[pt2]; + const LLVector4a& pa = profile[pt1+1]; + const LLVector4a& pb = profile[pt2-1]; + + const F32* p1V = p1.getF32ptr(); + const F32* p2V = p2.getF32ptr(); + const F32* paV = pa.getF32ptr(); + const F32* pbV = pb.getF32ptr(); + + // Use area of triangle to determine backfacing + F32 area_1a2, area_1ba, area_21b, area_2ab; + area_1a2 = (p1V[0]*paV[1] - paV[0]*p1V[1]) + + (paV[0]*p2V[1] - p2V[0]*paV[1]) + + (p2V[0]*p1V[1] - p1V[0]*p2V[1]); + + area_1ba = (p1V[0]*pbV[1] - pbV[0]*p1V[1]) + + (pbV[0]*paV[1] - paV[0]*pbV[1]) + + (paV[0]*p1V[1] - p1V[0]*paV[1]); + + area_21b = (p2V[0]*p1V[1] - p1V[0]*p2V[1]) + + (p1V[0]*pbV[1] - pbV[0]*p1V[1]) + + (pbV[0]*p2V[1] - p2V[0]*pbV[1]); + + area_2ab = (p2V[0]*paV[1] - paV[0]*p2V[1]) + + (paV[0]*pbV[1] - pbV[0]*paV[1]) + + (pbV[0]*p2V[1] - p2V[0]*pbV[1]); + + bool use_tri1a2 = true; + bool tri_1a2 = true; + bool tri_21b = true; + + if (area_1a2 < 0) + { + tri_1a2 = false; + } + if (area_2ab < 0) + { + // Can't use, because it contains point b + tri_1a2 = false; + } + if (area_21b < 0) + { + tri_21b = false; + } + if (area_1ba < 0) + { + // Can't use, because it contains point b + tri_21b = false; + } + + if (!tri_1a2) + { + use_tri1a2 = false; + } + else if (!tri_21b) + { + use_tri1a2 = true; + } + else + { + LLVector4a d1; + d1.setSub(p1,pa); + LLVector4a d2; + d2.setSub(p2,pb); + + if (d1.dot3(d1) < d2.dot3(d2)) + { + use_tri1a2 = true; + } + else + { + use_tri1a2 = false; + } + } + + // Flipped backfacing from top + if (use_tri1a2) + { + mIndices[i++] = pt1; + mIndices[i++] = pt2; + mIndices[i++] = pt1 + 1; + pt1++; + } + else + { + mIndices[i++] = pt1; + mIndices[i++] = pt2; + mIndices[i++] = pt2 - 1; + pt2--; + } + } + } + } + else + { + // Not hollow, generate the triangle fan. + U16 v1 = 2; + U16 v2 = 1; + + if (mTypeMask & TOP_MASK) + { + v1 = 1; + v2 = 2; + } + + for (S32 i = 0; i < (num_vertices - 2); i++) + { + mIndices[3*i] = num_vertices - 1; + mIndices[3*i+v1] = i; + mIndices[3*i+v2] = i + 1; + } + + + } + + LLVector4a d0,d1; + LL_CHECK_MEMORY + + + d0.setSub(mPositions[mIndices[1]], mPositions[mIndices[0]]); + d1.setSub(mPositions[mIndices[2]], mPositions[mIndices[0]]); + + LLVector4a normal; + normal.setCross3(d0,d1); + + if (normal.dot3(normal).getF32() > F_APPROXIMATELY_ZERO) + { + normal.normalize3fast(); + } + else + { //degenerate, make up a value + if(normal.getF32ptr()[2] >= 0) + normal.set(0.f,0.f,1.f); + else + normal.set(0.f,0.f,-1.f); + } + + llassert(llfinite(normal.getF32ptr()[0])); + llassert(llfinite(normal.getF32ptr()[1])); + llassert(llfinite(normal.getF32ptr()[2])); + + llassert(!llisnan(normal.getF32ptr()[0])); + llassert(!llisnan(normal.getF32ptr()[1])); + llassert(!llisnan(normal.getF32ptr()[2])); + + for (S32 i = 0; i < num_vertices; i++) + { + norm[i].load4a(normal.getF32ptr()); + } + + return true; +} + +void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, + const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent); + +void LLVolumeFace::createTangents() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + + if (!mTangents) + { + allocateTangents(mNumVertices); + + //generate tangents + LLVector4a* ptr = (LLVector4a*)mTangents; + + LLVector4a* end = mTangents + mNumVertices; + while (ptr < end) + { + (*ptr++).clear(); + } + + CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents); + + //normalize normals + for (U32 i = 0; i < mNumVertices; i++) + { + //bump map/planar projection code requires normals to be normalized + mNormals[i].normalize3fast(); + } + } + +} + +void LLVolumeFace::resizeVertices(S32 num_verts) +{ + ll_aligned_free<64>(mPositions); + //DO NOT free mNormals and mTexCoords as they are part of mPositions buffer + ll_aligned_free_16(mTangents); + + mTangents = NULL; + + if (num_verts) + { + //pad texture coordinate block end to allow for QWORD reads + S32 tc_size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF; + + mPositions = (LLVector4a*) ll_aligned_malloc<64>(sizeof(LLVector4a)*2*num_verts+tc_size); + mNormals = mPositions+num_verts; + mTexCoords = (LLVector2*) (mNormals+num_verts); + + ll_assert_aligned(mPositions, 64); + } + else + { + mPositions = NULL; + mNormals = NULL; + mTexCoords = NULL; + } + + + if (mPositions) + { + mNumVertices = num_verts; + mNumAllocatedVertices = num_verts; + } + else + { + // Either num_verts is zero or allocation failure + mNumVertices = 0; + mNumAllocatedVertices = 0; + } + + // Force update + mJointRiggingInfoTab.clear(); +} + +void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv) +{ + pushVertex(cv.getPosition(), cv.getNormal(), cv.mTexCoord); +} + +void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc) +{ + S32 new_verts = mNumVertices+1; + + if (new_verts > mNumAllocatedVertices) + { + // double buffer size on expansion + new_verts *= 2; + + S32 new_tc_size = ((new_verts*8)+0xF) & ~0xF; + S32 old_tc_size = ((mNumVertices*8)+0xF) & ~0xF; + + S32 old_vsize = mNumVertices*16; + + S32 new_size = new_verts*16*2+new_tc_size; + + LLVector4a* old_buf = mPositions; + + mPositions = (LLVector4a*) ll_aligned_malloc<64>(new_size); + mNormals = mPositions+new_verts; + mTexCoords = (LLVector2*) (mNormals+new_verts); + + if (old_buf != NULL) + { + // copy old positions into new buffer + LLVector4a::memcpyNonAliased16((F32*)mPositions, (F32*)old_buf, old_vsize); + + // normals + LLVector4a::memcpyNonAliased16((F32*)mNormals, (F32*)(old_buf + mNumVertices), old_vsize); + + // tex coords + LLVector4a::memcpyNonAliased16((F32*)mTexCoords, (F32*)(old_buf + mNumVertices * 2), old_tc_size); + } + + // just clear tangents + ll_aligned_free_16(mTangents); + mTangents = NULL; + ll_aligned_free<64>(old_buf); + + mNumAllocatedVertices = new_verts; + + } + + mPositions[mNumVertices] = pos; + mNormals[mNumVertices] = norm; + mTexCoords[mNumVertices] = tc; + + mNumVertices++; +} + +void LLVolumeFace::allocateTangents(S32 num_verts) +{ + ll_aligned_free_16(mTangents); + mTangents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); +} + +void LLVolumeFace::allocateWeights(S32 num_verts) +{ + ll_aligned_free_16(mWeights); + mWeights = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); + +} + +void LLVolumeFace::allocateJointIndices(S32 num_verts) +{ +#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS + ll_aligned_free_16(mJointIndices); + ll_aligned_free_16(mJustWeights); + + mJointIndices = (U8*)ll_aligned_malloc_16(sizeof(U8) * 4 * num_verts); + mJustWeights = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a) * num_verts); +#endif +} + +void LLVolumeFace::resizeIndices(S32 num_indices) +{ + ll_aligned_free_16(mIndices); + llassert(num_indices % 3 == 0); + + if (num_indices) + { + //pad index block end to allow for QWORD reads + S32 size = ((num_indices*sizeof(U16)) + 0xF) & ~0xF; + + mIndices = (U16*) ll_aligned_malloc_16(size); + } + else + { + mIndices = NULL; + } + + if (mIndices) + { + mNumIndices = num_indices; + } + else + { + // Either num_indices is zero or allocation failure + mNumIndices = 0; + } +} + +void LLVolumeFace::pushIndex(const U16& idx) +{ + S32 new_count = mNumIndices + 1; + S32 new_size = ((new_count*2)+0xF) & ~0xF; + + S32 old_size = ((mNumIndices*2)+0xF) & ~0xF; + if (new_size != old_size) + { + mIndices = (U16*) ll_aligned_realloc_16(mIndices, new_size, old_size); + ll_assert_aligned(mIndices,16); + } + + mIndices[mNumIndices++] = idx; +} + +void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx) +{ + resizeVertices(v.size()); + resizeIndices(idx.size()); + + for (U32 i = 0; i < v.size(); ++i) + { + mPositions[i] = v[i].getPosition(); + mNormals[i] = v[i].getNormal(); + mTexCoords[i] = v[i].mTexCoord; + } + + for (U32 i = 0; i < idx.size(); ++i) + { + mIndices[i] = idx[i]; + } +} + +bool LLVolumeFace::createSide(LLVolume* volume, bool partial_build) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + LL_CHECK_MEMORY + bool flat = mTypeMask & FLAT_MASK; + + U8 sculpt_type = volume->getParams().getSculptType(); + U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK; + bool sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT; + bool sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR; + bool sculpt_reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR + + S32 num_vertices, num_indices; + + const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh(); + const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile; + const LLAlignedArray<LLPath::PathPt,64>& path_data = volume->getPath().mPath; + + S32 max_s = volume->getProfile().getTotal(); + + S32 s, t, i; + F32 ss, tt; + + num_vertices = mNumS*mNumT; + num_indices = (mNumS-1)*(mNumT-1)*6; + + partial_build = (num_vertices > mNumVertices || num_indices > mNumIndices) ? false : partial_build; + + if (!partial_build) + { + resizeVertices(num_vertices); + resizeIndices(num_indices); + + if (!volume->isMeshAssetLoaded()) + { + try + { + mEdge.resize(num_indices); + } + catch (std::bad_alloc&) + { + LL_WARNS("LLVOLUME") << "Resize of mEdge to " << num_indices << " failed" << LL_ENDL; + return false; + } + } + } + + LL_CHECK_MEMORY + + LLVector4a* pos = (LLVector4a*) mPositions; + LLVector2* tc = (LLVector2*) mTexCoords; + F32 begin_stex = floorf(profile[mBeginS][2]); + S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS; + + S32 cur_vertex = 0; + S32 end_t = mBeginT+mNumT; + bool test = (mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2; + + // Copy the vertices into the array + for (t = mBeginT; t < end_t; t++) + { + tt = path_data[t].mTexT; + for (s = 0; s < num_s; s++) + { + if (mTypeMask & END_MASK) + { + if (s) + { + ss = 1.f; + } + else + { + ss = 0.f; + } + } + else + { + // Get s value for tex-coord. + S32 index = mBeginS + s; + if (index >= profile.size()) + { + // edge? + ss = flat ? 1.f - begin_stex : 1.f; + } + else if (!flat) + { + ss = profile[index][2]; + } + else + { + ss = profile[index][2] - begin_stex; + } + } + + if (sculpt_reverse_horizontal) + { + ss = 1.f - ss; + } + + // Check to see if this triangle wraps around the array. + if (mBeginS + s >= max_s) + { + // We're wrapping + i = mBeginS + s + max_s*(t-1); + } + else + { + i = mBeginS + s + max_s*t; + } + + mesh[i].store4a((F32*)(pos+cur_vertex)); + tc[cur_vertex].set(ss,tt); + + cur_vertex++; + + if (test && s > 0) + { + mesh[i].store4a((F32*)(pos+cur_vertex)); + tc[cur_vertex].set(ss,tt); + cur_vertex++; + } + } + + if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) + { + if (mTypeMask & OPEN_MASK) + { + s = num_s-1; + } + else + { + s = 0; + } + + i = mBeginS + s + max_s*t; + ss = profile[mBeginS + s][2] - begin_stex; + + mesh[i].store4a((F32*)(pos+cur_vertex)); + tc[cur_vertex].set(ss,tt); + + cur_vertex++; + } + } + LL_CHECK_MEMORY + + mCenter->clear(); + + LLVector4a* cur_pos = pos; + LLVector4a* end_pos = pos + mNumVertices; + + //get bounding box for this side + LLVector4a face_min; + LLVector4a face_max; + + face_min = face_max = *cur_pos++; + + while (cur_pos < end_pos) + { + update_min_max(face_min, face_max, *cur_pos++); + } + // VFExtents change + mExtents[0] = face_min; + mExtents[1] = face_max; + + U32 tc_count = mNumVertices; + if (tc_count%2 == 1) + { //odd number of texture coordinates, duplicate last entry to padded end of array + tc_count++; + mTexCoords[mNumVertices] = mTexCoords[mNumVertices-1]; + } + + LLVector4a* cur_tc = (LLVector4a*) mTexCoords; + LLVector4a* end_tc = (LLVector4a*) (mTexCoords+tc_count); + + LLVector4a tc_min; + LLVector4a tc_max; + + tc_min = tc_max = *cur_tc++; + + while (cur_tc < end_tc) + { + update_min_max(tc_min, tc_max, *cur_tc++); + } + + F32* minp = tc_min.getF32ptr(); + F32* maxp = tc_max.getF32ptr(); + + mTexCoordExtents[0].mV[0] = llmin(minp[0], minp[2]); + mTexCoordExtents[0].mV[1] = llmin(minp[1], minp[3]); + mTexCoordExtents[1].mV[0] = llmax(maxp[0], maxp[2]); + mTexCoordExtents[1].mV[1] = llmax(maxp[1], maxp[3]); + + mCenter->setAdd(face_min, face_max); + mCenter->mul(0.5f); + + S32 cur_index = 0; + S32 cur_edge = 0; + bool flat_face = mTypeMask & FLAT_MASK; + + if (!partial_build) + { + // Now we generate the indices. + for (t = 0; t < (mNumT-1); t++) + { + for (s = 0; s < (mNumS-1); s++) + { + mIndices[cur_index++] = s + mNumS*t; //bottom left + mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right + mIndices[cur_index++] = s + mNumS*(t+1); //top left + mIndices[cur_index++] = s + mNumS*t; //bottom left + mIndices[cur_index++] = s+1 + mNumS*t; //bottom right + mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right + + // bottom left/top right neighbor face + mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1; + + if (t < mNumT-2) + { // top right/top left neighbor face + mEdge[cur_edge++] = (mNumS-1)*2*(t+1)+s*2+1; + } + else if (mNumT <= 3 || volume->getPath().isOpen()) + { // no neighbor + mEdge[cur_edge++] = -1; + } + else + { // wrap on T + mEdge[cur_edge++] = s*2+1; + } + + if (s > 0) + { // top left/bottom left neighbor face + mEdge[cur_edge++] = (mNumS-1)*2*t+s*2-1; + } + else if (flat_face || volume->getProfile().isOpen()) + { // no neighbor + mEdge[cur_edge++] = -1; + } + else + { // wrap on S + mEdge[cur_edge++] = (mNumS-1)*2*t+(mNumS-2)*2+1; + } + + if (t > 0) + { // bottom left/bottom right neighbor face + mEdge[cur_edge++] = (mNumS-1)*2*(t-1)+s*2; + } + else if (mNumT <= 3 || volume->getPath().isOpen()) + { // no neighbor + mEdge[cur_edge++] = -1; + } + else + { // wrap on T + mEdge[cur_edge++] = (mNumS-1)*2*(mNumT-2)+s*2; + } + + if (s < mNumS-2) + { // bottom right/top right neighbor face + mEdge[cur_edge++] = (mNumS-1)*2*t+(s+1)*2; + } + else if (flat_face || volume->getProfile().isOpen()) + { // no neighbor + mEdge[cur_edge++] = -1; + } + else + { // wrap on S + mEdge[cur_edge++] = (mNumS-1)*2*t; + } + + // top right/bottom left neighbor face + mEdge[cur_edge++] = (mNumS-1)*2*t+s*2; + } + } + } + + LL_CHECK_MEMORY + + //clear normals + F32* dst = (F32*) mNormals; + F32* end = (F32*) (mNormals+mNumVertices); + LLVector4a zero = LLVector4a::getZero(); + + while (dst < end) + { + zero.store4a(dst); + dst += 4; + } + + LL_CHECK_MEMORY + + //generate normals + U32 count = mNumIndices/3; + + LLVector4a* norm = mNormals; + + static thread_local LLAlignedArray<LLVector4a, 64> triangle_normals; + try + { + triangle_normals.resize(count); + } + catch (std::bad_alloc&) + { + LL_WARNS("LLVOLUME") << "Resize of triangle_normals to " << count << " failed" << LL_ENDL; + return false; + } + LLVector4a* output = triangle_normals.mArray; + LLVector4a* end_output = output+count; + + U16* idx = mIndices; + + while (output < end_output) + { + LLVector4a b,v1,v2; + b.load4a((F32*) (pos+idx[0])); + v1.load4a((F32*) (pos+idx[1])); + v2.load4a((F32*) (pos+idx[2])); + + //calculate triangle normal + LLVector4a a; + + a.setSub(b, v1); + b.sub(v2); + + + LLQuad& vector1 = *((LLQuad*) &v1); + LLQuad& vector2 = *((LLQuad*) &v2); + + LLQuad& amQ = *((LLQuad*) &a); + LLQuad& bmQ = *((LLQuad*) &b); + + //v1.setCross3(t,v0); + //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] } + vector1 = _mm_shuffle_ps( amQ, amQ, _MM_SHUFFLE( 3, 0, 2, 1 )); + // Set vector2 = { b[W], b[Y], b[X], b[Z] } + vector2 = _mm_shuffle_ps( bmQ, bmQ, _MM_SHUFFLE( 3, 1, 0, 2 )); + // mQ = { a[W]*b[W], a[X]*b[Y], a[Z]*b[X], a[Y]*b[Z] } + vector2 = _mm_mul_ps( vector1, vector2 ); + // vector3 = { a[W], a[Y], a[X], a[Z] } + amQ = _mm_shuffle_ps( amQ, amQ, _MM_SHUFFLE( 3, 1, 0, 2 )); + // vector4 = { b[W], b[X], b[Z], b[Y] } + bmQ = _mm_shuffle_ps( bmQ, bmQ, _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] } + vector1 = _mm_sub_ps( vector2, _mm_mul_ps( amQ, bmQ )); + + llassert(v1.isFinite3()); + + v1.store4a((F32*) output); + + + output++; + idx += 3; + } + + idx = mIndices; + + LLVector4a* src = triangle_normals.mArray; + + for (U32 i = 0; i < count; i++) //for each triangle + { + LLVector4a c; + c.load4a((F32*) (src++)); + + LLVector4a* n0p = norm+idx[0]; + LLVector4a* n1p = norm+idx[1]; + LLVector4a* n2p = norm+idx[2]; + + idx += 3; + + LLVector4a n0,n1,n2; + n0.load4a((F32*) n0p); + n1.load4a((F32*) n1p); + n2.load4a((F32*) n2p); + + n0.add(c); + n1.add(c); + n2.add(c); + + llassert(c.isFinite3()); + + //even out quad contributions + switch (i%2+1) + { + case 0: n0.add(c); break; + case 1: n1.add(c); break; + case 2: n2.add(c); break; + }; + + n0.store4a((F32*) n0p); + n1.store4a((F32*) n1p); + n2.store4a((F32*) n2p); + } + + LL_CHECK_MEMORY + + // adjust normals based on wrapping and stitching + + LLVector4a top; + top.setSub(pos[0], pos[mNumS*(mNumT-2)]); + bool s_bottom_converges = (top.dot3(top) < 0.000001f); + + top.setSub(pos[mNumS-1], pos[mNumS*(mNumT-2)+mNumS-1]); + bool s_top_converges = (top.dot3(top) < 0.000001f); + + if (sculpt_stitching == LL_SCULPT_TYPE_NONE) // logic for non-sculpt volumes + { + if (!volume->getPath().isOpen()) + { //wrap normals on T + for (S32 i = 0; i < mNumS; i++) + { + LLVector4a n; + n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]); + norm[i] = n; + norm[mNumS*(mNumT-1)+i] = n; + } + } + + if (!volume->getProfile().isOpen() && !s_bottom_converges) + { //wrap normals on S + for (S32 i = 0; i < mNumT; i++) + { + LLVector4a n; + n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]); + norm[mNumS * i] = n; + norm[mNumS * i+mNumS-1] = n; + } + } + + if (volume->getPathType() == LL_PCODE_PATH_CIRCLE && + ((volume->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF)) + { + if (s_bottom_converges) + { //all lower S have same normal + for (S32 i = 0; i < mNumT; i++) + { + norm[mNumS*i].set(1,0,0); + } + } + + if (s_top_converges) + { //all upper S have same normal + for (S32 i = 0; i < mNumT; i++) + { + norm[mNumS*i+mNumS-1].set(-1,0,0); + } + } + } + } + else // logic for sculpt volumes + { + bool average_poles = false; + bool wrap_s = false; + bool wrap_t = false; + + if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE) + average_poles = true; + + if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) || + (sculpt_stitching == LL_SCULPT_TYPE_TORUS) || + (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER)) + wrap_s = true; + + if (sculpt_stitching == LL_SCULPT_TYPE_TORUS) + wrap_t = true; + + + if (average_poles) + { + // average normals for north pole + + LLVector4a average; + average.clear(); + + for (S32 i = 0; i < mNumS; i++) + { + average.add(norm[i]); + } + + // set average + for (S32 i = 0; i < mNumS; i++) + { + norm[i] = average; + } + + // average normals for south pole + + average.clear(); + + for (S32 i = 0; i < mNumS; i++) + { + average.add(norm[i + mNumS * (mNumT - 1)]); + } + + // set average + for (S32 i = 0; i < mNumS; i++) + { + norm[i + mNumS * (mNumT - 1)] = average; + } + + } + + + if (wrap_s) + { + for (S32 i = 0; i < mNumT; i++) + { + LLVector4a n; + n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]); + norm[mNumS * i] = n; + norm[mNumS * i+mNumS-1] = n; + } + } + + if (wrap_t) + { + for (S32 i = 0; i < mNumS; i++) + { + LLVector4a n; + n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]); + norm[i] = n; + norm[mNumS*(mNumT-1)+i] = n; + } + } + + } + + LL_CHECK_MEMORY + + return true; +} + +//adapted from Lengyel, Eric. "Computing Tangent Space Basis Vectors for an Arbitrary Mesh". Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html +void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, + const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + //LLVector4a *tan1 = new LLVector4a[vertexCount * 2]; + LLVector4a* tan1 = (LLVector4a*) ll_aligned_malloc_16(vertexCount*2*sizeof(LLVector4a)); + // new(tan1) LLVector4a; + + LLVector4a* tan2 = tan1 + vertexCount; + + U32 count = vertexCount * 2; + for (U32 i = 0; i < count; i++) + { + tan1[i].clear(); + } + + for (U32 a = 0; a < triangleCount; a++) + { + U32 i1 = *index_array++; + U32 i2 = *index_array++; + U32 i3 = *index_array++; + + const LLVector4a& v1 = vertex[i1]; + const LLVector4a& v2 = vertex[i2]; + const LLVector4a& v3 = vertex[i3]; + + const LLVector2& w1 = texcoord[i1]; + const LLVector2& w2 = texcoord[i2]; + const LLVector2& w3 = texcoord[i3]; + + const F32* v1ptr = v1.getF32ptr(); + const F32* v2ptr = v2.getF32ptr(); + const F32* v3ptr = v3.getF32ptr(); + + float x1 = v2ptr[0] - v1ptr[0]; + float x2 = v3ptr[0] - v1ptr[0]; + float y1 = v2ptr[1] - v1ptr[1]; + float y2 = v3ptr[1] - v1ptr[1]; + float z1 = v2ptr[2] - v1ptr[2]; + float z2 = v3ptr[2] - v1ptr[2]; + + float s1 = w2.mV[0] - w1.mV[0]; + float s2 = w3.mV[0] - w1.mV[0]; + float t1 = w2.mV[1] - w1.mV[1]; + float t2 = w3.mV[1] - w1.mV[1]; + + F32 rd = s1*t2-s2*t1; + + float r = ((rd*rd) > FLT_EPSILON) ? (1.0f / rd) + : ((rd > 0.0f) ? 1024.f : -1024.f); //some made up large ratio for division by zero + + llassert(llfinite(r)); + llassert(!llisnan(r)); + + LLVector4a sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, + (t2 * z1 - t1 * z2) * r); + LLVector4a tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, + (s1 * z2 - s2 * z1) * r); + + tan1[i1].add(sdir); + tan1[i2].add(sdir); + tan1[i3].add(sdir); + + tan2[i1].add(tdir); + tan2[i2].add(tdir); + tan2[i3].add(tdir); + } + + for (U32 a = 0; a < vertexCount; a++) + { + LLVector4a n = normal[a]; + + const LLVector4a& t = tan1[a]; + + LLVector4a ncrosst; + ncrosst.setCross3(n,t); + + // Gram-Schmidt orthogonalize + n.mul(n.dot3(t).getF32()); + + LLVector4a tsubn; + tsubn.setSub(t,n); + + if (tsubn.dot3(tsubn).getF32() > F_APPROXIMATELY_ZERO) + { + tsubn.normalize3fast(); + + // Calculate handedness + F32 handedness = ncrosst.dot3(tan2[a]).getF32() < 0.f ? -1.f : 1.f; + + tsubn.getF32ptr()[3] = handedness; + + tangent[a] = tsubn; + } + else + { //degenerate, make up a value + tangent[a].set(0,0,1,1); + } + } + + ll_aligned_free_16(tan1); +} + + diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 4f8d9bef84..44a24f8496 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -1,1152 +1,1152 @@ -/**
- * @file llvolume.h
- * @brief LLVolume base class.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLVOLUME_H
-#define LL_LLVOLUME_H
-
-#include <iostream>
-
-class LLProfileParams;
-class LLPathParams;
-class LLVolumeParams;
-class LLProfile;
-class LLPath;
-
-template<class T> class LLPointer;
-template <class T, typename T_PTR> class LLOctreeNode;
-
-class LLVolumeFace;
-class LLVolume;
-class LLVolumeTriangle;
-
-#include "lluuid.h"
-#include "v4color.h"
-//#include "vmath.h"
-#include "v2math.h"
-#include "v3math.h"
-#include "v3dmath.h"
-#include "v4math.h"
-#include "llvector4a.h"
-#include "llmatrix4a.h"
-#include "llquaternion.h"
-#include "llstrider.h"
-#include "v4coloru.h"
-#include "llrefcount.h"
-#include "llpointer.h"
-#include "llfile.h"
-#include "llalignedarray.h"
-#include "llrigginginfo.h"
-
-//============================================================================
-
-constexpr S32 MIN_DETAIL_FACES = 6;
-constexpr S32 MIN_LOD = 0;
-constexpr S32 MAX_LOD = 3;
-
-// These are defined here but are not enforced at this level,
-// rather they are here for the convenience of code that uses
-// the LLVolume class.
-constexpr F32 MIN_VOLUME_PROFILE_WIDTH = 0.05f;
-constexpr F32 MIN_VOLUME_PATH_WIDTH = 0.05f;
-
-constexpr F32 CUT_QUANTA = 0.00002f;
-constexpr F32 SCALE_QUANTA = 0.01f;
-constexpr F32 SHEAR_QUANTA = 0.01f;
-constexpr F32 TAPER_QUANTA = 0.01f;
-constexpr F32 REV_QUANTA = 0.015f;
-constexpr F32 HOLLOW_QUANTA = 0.00002f;
-
-constexpr S32 MAX_VOLUME_TRIANGLE_INDICES = 10000;
-
-//============================================================================
-
-// useful masks
-constexpr LLPCode LL_PCODE_HOLLOW_MASK = 0x80; // has a thickness
-constexpr LLPCode LL_PCODE_SEGMENT_MASK = 0x40; // segments (1 angle)
-constexpr LLPCode LL_PCODE_PATCH_MASK = 0x20; // segmented segments (2 angles)
-constexpr LLPCode LL_PCODE_HEMI_MASK = 0x10; // half-primitives get their own type per PR's dictum
-constexpr LLPCode LL_PCODE_BASE_MASK = 0x0F;
-
- // primitive shapes
-constexpr LLPCode LL_PCODE_CUBE = 1;
-constexpr LLPCode LL_PCODE_PRISM = 2;
-constexpr LLPCode LL_PCODE_TETRAHEDRON = 3;
-constexpr LLPCode LL_PCODE_PYRAMID = 4;
-constexpr LLPCode LL_PCODE_CYLINDER = 5;
-constexpr LLPCode LL_PCODE_CONE = 6;
-constexpr LLPCode LL_PCODE_SPHERE = 7;
-constexpr LLPCode LL_PCODE_TORUS = 8;
-constexpr LLPCode LL_PCODE_VOLUME = 9;
-
- // surfaces
-//constexpr LLPCode LL_PCODE_SURFACE_TRIANGLE = 10;
-//constexpr LLPCode LL_PCODE_SURFACE_SQUARE = 11;
-//constexpr LLPCode LL_PCODE_SURFACE_DISC = 12;
-
-constexpr LLPCode LL_PCODE_APP = 14; // App specific pcode (for viewer/sim side only objects)
-constexpr LLPCode LL_PCODE_LEGACY = 15;
-
-// Pcodes for legacy objects
-//constexpr LLPCode LL_PCODE_LEGACY_ATOR = 0x10 | LL_PCODE_LEGACY; // ATOR
-constexpr LLPCode LL_PCODE_LEGACY_AVATAR = 0x20 | LL_PCODE_LEGACY; // PLAYER
-//constexpr LLPCode LL_PCODE_LEGACY_BIRD = 0x30 | LL_PCODE_LEGACY; // BIRD
-//constexpr LLPCode LL_PCODE_LEGACY_DEMON = 0x40 | LL_PCODE_LEGACY; // DEMON
-constexpr LLPCode LL_PCODE_LEGACY_GRASS = 0x50 | LL_PCODE_LEGACY; // GRASS
-constexpr LLPCode LL_PCODE_TREE_NEW = 0x60 | LL_PCODE_LEGACY; // new trees
-//constexpr LLPCode LL_PCODE_LEGACY_ORACLE = 0x70 | LL_PCODE_LEGACY; // ORACLE
-constexpr LLPCode LL_PCODE_LEGACY_PART_SYS = 0x80 | LL_PCODE_LEGACY; // PART_SYS
-constexpr LLPCode LL_PCODE_LEGACY_ROCK = 0x90 | LL_PCODE_LEGACY; // ROCK
-//constexpr LLPCode LL_PCODE_LEGACY_SHOT = 0xA0 | LL_PCODE_LEGACY; // BASIC_SHOT
-//constexpr LLPCode LL_PCODE_LEGACY_SHOT_BIG = 0xB0 | LL_PCODE_LEGACY;
-//constexpr LLPCode LL_PCODE_LEGACY_SMOKE = 0xC0 | LL_PCODE_LEGACY; // SMOKE
-//constexpr LLPCode LL_PCODE_LEGACY_SPARK = 0xD0 | LL_PCODE_LEGACY;// SPARK
-constexpr LLPCode LL_PCODE_LEGACY_TEXT_BUBBLE = 0xE0 | LL_PCODE_LEGACY; // TEXTBUBBLE
-constexpr LLPCode LL_PCODE_LEGACY_TREE = 0xF0 | LL_PCODE_LEGACY; // TREE
-
- // hemis
-constexpr LLPCode LL_PCODE_CYLINDER_HEMI = LL_PCODE_CYLINDER | LL_PCODE_HEMI_MASK;
-constexpr LLPCode LL_PCODE_CONE_HEMI = LL_PCODE_CONE | LL_PCODE_HEMI_MASK;
-constexpr LLPCode LL_PCODE_SPHERE_HEMI = LL_PCODE_SPHERE | LL_PCODE_HEMI_MASK;
-constexpr LLPCode LL_PCODE_TORUS_HEMI = LL_PCODE_TORUS | LL_PCODE_HEMI_MASK;
-
-
-// Volumes consist of a profile at the base that is swept around
-// a path to make a volume.
-// The profile code
-constexpr U8 LL_PCODE_PROFILE_MASK = 0x0f;
-constexpr U8 LL_PCODE_PROFILE_MIN = 0x00;
-constexpr U8 LL_PCODE_PROFILE_CIRCLE = 0x00;
-constexpr U8 LL_PCODE_PROFILE_SQUARE = 0x01;
-constexpr U8 LL_PCODE_PROFILE_ISOTRI = 0x02;
-constexpr U8 LL_PCODE_PROFILE_EQUALTRI = 0x03;
-constexpr U8 LL_PCODE_PROFILE_RIGHTTRI = 0x04;
-constexpr U8 LL_PCODE_PROFILE_CIRCLE_HALF = 0x05;
-constexpr U8 LL_PCODE_PROFILE_MAX = 0x05;
-
-// Stored in the profile byte
-constexpr U8 LL_PCODE_HOLE_MASK = 0xf0;
-constexpr U8 LL_PCODE_HOLE_MIN = 0x00;
-constexpr U8 LL_PCODE_HOLE_SAME = 0x00; // same as outside profile
-constexpr U8 LL_PCODE_HOLE_CIRCLE = 0x10;
-constexpr U8 LL_PCODE_HOLE_SQUARE = 0x20;
-constexpr U8 LL_PCODE_HOLE_TRIANGLE = 0x30;
-constexpr U8 LL_PCODE_HOLE_MAX = 0x03; // min/max needs to be >> 4 of real min/max
-
-constexpr U8 LL_PCODE_PATH_IGNORE = 0x00;
-constexpr U8 LL_PCODE_PATH_MIN = 0x01; // min/max needs to be >> 4 of real min/max
-constexpr U8 LL_PCODE_PATH_LINE = 0x10;
-constexpr U8 LL_PCODE_PATH_CIRCLE = 0x20;
-constexpr U8 LL_PCODE_PATH_CIRCLE2 = 0x30;
-constexpr U8 LL_PCODE_PATH_TEST = 0x40;
-constexpr U8 LL_PCODE_PATH_FLEXIBLE = 0x80;
-constexpr U8 LL_PCODE_PATH_MAX = 0x08;
-
-//============================================================================
-
-// face identifiers
-typedef U16 LLFaceID;
-
-constexpr LLFaceID LL_FACE_PATH_BEGIN = 0x1 << 0;
-constexpr LLFaceID LL_FACE_PATH_END = 0x1 << 1;
-constexpr LLFaceID LL_FACE_INNER_SIDE = 0x1 << 2;
-constexpr LLFaceID LL_FACE_PROFILE_BEGIN = 0x1 << 3;
-constexpr LLFaceID LL_FACE_PROFILE_END = 0x1 << 4;
-constexpr LLFaceID LL_FACE_OUTER_SIDE_0 = 0x1 << 5;
-constexpr LLFaceID LL_FACE_OUTER_SIDE_1 = 0x1 << 6;
-constexpr LLFaceID LL_FACE_OUTER_SIDE_2 = 0x1 << 7;
-constexpr LLFaceID LL_FACE_OUTER_SIDE_3 = 0x1 << 8;
-
-//============================================================================
-
-// sculpt types + flags
-
-constexpr U8 LL_SCULPT_TYPE_NONE = 0;
-constexpr U8 LL_SCULPT_TYPE_SPHERE = 1;
-constexpr U8 LL_SCULPT_TYPE_TORUS = 2;
-constexpr U8 LL_SCULPT_TYPE_PLANE = 3;
-constexpr U8 LL_SCULPT_TYPE_CYLINDER = 4;
-constexpr U8 LL_SCULPT_TYPE_MESH = 5;
-constexpr U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE |
- LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH;
-
-// for value checks, assign new value after adding new types
-constexpr U8 LL_SCULPT_TYPE_MAX = LL_SCULPT_TYPE_MESH;
-
-constexpr U8 LL_SCULPT_FLAG_INVERT = 64;
-constexpr U8 LL_SCULPT_FLAG_MIRROR = 128;
-constexpr U8 LL_SCULPT_FLAG_MASK = LL_SCULPT_FLAG_INVERT | LL_SCULPT_FLAG_MIRROR;
-
-constexpr S32 LL_SCULPT_MESH_MAX_FACES = 8;
-
-extern bool gDebugGL;
-
-class LLProfileParams
-{
-public:
- LLProfileParams()
- : mCurveType(LL_PCODE_PROFILE_SQUARE),
- mBegin(0.f),
- mEnd(1.f),
- mHollow(0.f),
- mCRC(0)
- {
- }
-
- LLProfileParams(U8 curve, F32 begin, F32 end, F32 hollow)
- : mCurveType(curve),
- mBegin(begin),
- mEnd(end),
- mHollow(hollow),
- mCRC(0)
- {
- }
-
- LLProfileParams(U8 curve, U16 begin, U16 end, U16 hollow)
- {
- mCurveType = curve;
- F32 temp_f32 = begin * CUT_QUANTA;
- if (temp_f32 > 1.f)
- {
- temp_f32 = 1.f;
- }
- mBegin = temp_f32;
- temp_f32 = end * CUT_QUANTA;
- if (temp_f32 > 1.f)
- {
- temp_f32 = 1.f;
- }
- mEnd = 1.f - temp_f32;
- temp_f32 = hollow * HOLLOW_QUANTA;
- if (temp_f32 > 1.f)
- {
- temp_f32 = 1.f;
- }
- mHollow = temp_f32;
- mCRC = 0;
- }
-
- bool operator==(const LLProfileParams ¶ms) const;
- bool operator!=(const LLProfileParams ¶ms) const;
- bool operator<(const LLProfileParams ¶ms) const;
-
- void copyParams(const LLProfileParams ¶ms);
-
- bool importFile(LLFILE *fp);
- bool exportFile(LLFILE *fp) const;
-
- bool importLegacyStream(std::istream& input_stream);
- bool exportLegacyStream(std::ostream& output_stream) const;
-
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-
- const F32& getBegin () const { return mBegin; }
- const F32& getEnd () const { return mEnd; }
- const F32& getHollow() const { return mHollow; }
- const U8& getCurveType () const { return mCurveType; }
-
- void setCurveType(const U32 type) { mCurveType = type;}
- void setBegin(const F32 begin) { mBegin = (begin >= 1.0f) ? 0.0f : ((int) (begin * 100000))/100000.0f;}
- void setEnd(const F32 end) { mEnd = (end <= 0.0f) ? 1.0f : ((int) (end * 100000))/100000.0f;}
- void setHollow(const F32 hollow) { mHollow = ((int) (hollow * 100000))/100000.0f;}
-
- friend std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params);
-
-protected:
- // Profile params
- U8 mCurveType;
- F32 mBegin;
- F32 mEnd;
- F32 mHollow;
-
- U32 mCRC;
-};
-
-inline bool LLProfileParams::operator==(const LLProfileParams ¶ms) const
-{
- return
- (getCurveType() == params.getCurveType()) &&
- (getBegin() == params.getBegin()) &&
- (getEnd() == params.getEnd()) &&
- (getHollow() == params.getHollow());
-}
-
-inline bool LLProfileParams::operator!=(const LLProfileParams ¶ms) const
-{
- return
- (getCurveType() != params.getCurveType()) ||
- (getBegin() != params.getBegin()) ||
- (getEnd() != params.getEnd()) ||
- (getHollow() != params.getHollow());
-}
-
-
-inline bool LLProfileParams::operator<(const LLProfileParams ¶ms) const
-{
- if (getCurveType() != params.getCurveType())
- {
- return getCurveType() < params.getCurveType();
- }
- else
- if (getBegin() != params.getBegin())
- {
- return getBegin() < params.getBegin();
- }
- else
- if (getEnd() != params.getEnd())
- {
- return getEnd() < params.getEnd();
- }
- else
- {
- return getHollow() < params.getHollow();
- }
-}
-
-#define U8_TO_F32(x) (F32)(*((S8 *)&x))
-
-class LLPathParams
-{
-public:
- LLPathParams()
- :
- mCurveType(LL_PCODE_PATH_LINE),
- mBegin(0.f),
- mEnd(1.f),
- mScale(1.f,1.f),
- mShear(0.f,0.f),
- mTwistBegin(0.f),
- mTwistEnd(0.f),
- mRadiusOffset(0.f),
- mTaper(0.f,0.f),
- mRevolutions(1.f),
- mSkew(0.f),
- mCRC(0)
- {
- }
-
- LLPathParams(U8 curve, F32 begin, F32 end, F32 scx, F32 scy, F32 shx, F32 shy, F32 twistend, F32 twistbegin, F32 radiusoffset, F32 tx, F32 ty, F32 revolutions, F32 skew)
- : mCurveType(curve),
- mBegin(begin),
- mEnd(end),
- mScale(scx,scy),
- mShear(shx,shy),
- mTwistBegin(twistbegin),
- mTwistEnd(twistend),
- mRadiusOffset(radiusoffset),
- mTaper(tx,ty),
- mRevolutions(revolutions),
- mSkew(skew),
- mCRC(0)
- {
- }
-
- LLPathParams(U8 curve, U16 begin, U16 end, U8 scx, U8 scy, U8 shx, U8 shy, U8 twistend, U8 twistbegin, U8 radiusoffset, U8 tx, U8 ty, U8 revolutions, U8 skew)
- {
- mCurveType = curve;
- mBegin = (F32)(begin * CUT_QUANTA);
- mEnd = (F32)(100.f - end) * CUT_QUANTA;
- if (mEnd > 1.f)
- mEnd = 1.f;
- mScale.setVec((F32) (200 - scx) * SCALE_QUANTA,(F32) (200 - scy) * SCALE_QUANTA);
- mShear.setVec(U8_TO_F32(shx) * SHEAR_QUANTA,U8_TO_F32(shy) * SHEAR_QUANTA);
- mTwistBegin = U8_TO_F32(twistbegin) * SCALE_QUANTA;
- mTwistEnd = U8_TO_F32(twistend) * SCALE_QUANTA;
- mRadiusOffset = U8_TO_F32(radiusoffset) * SCALE_QUANTA;
- mTaper.setVec(U8_TO_F32(tx) * TAPER_QUANTA,U8_TO_F32(ty) * TAPER_QUANTA);
- mRevolutions = ((F32)revolutions) * REV_QUANTA + 1.0f;
- mSkew = U8_TO_F32(skew) * SCALE_QUANTA;
-
- mCRC = 0;
- }
-
- bool operator==(const LLPathParams ¶ms) const;
- bool operator!=(const LLPathParams ¶ms) const;
- bool operator<(const LLPathParams ¶ms) const;
-
- void copyParams(const LLPathParams ¶ms);
-
- bool importFile(LLFILE *fp);
- bool exportFile(LLFILE *fp) const;
-
- bool importLegacyStream(std::istream& input_stream);
- bool exportLegacyStream(std::ostream& output_stream) const;
-
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-
- const F32& getBegin() const { return mBegin; }
- const F32& getEnd() const { return mEnd; }
- const LLVector2 &getScale() const { return mScale; }
- const F32& getScaleX() const { return mScale.mV[0]; }
- const F32& getScaleY() const { return mScale.mV[1]; }
- const LLVector2 getBeginScale() const;
- const LLVector2 getEndScale() const;
- const LLVector2 &getShear() const { return mShear; }
- const F32& getShearX() const { return mShear.mV[0]; }
- const F32& getShearY() const { return mShear.mV[1]; }
- const U8& getCurveType () const { return mCurveType; }
-
- const F32& getTwistBegin() const { return mTwistBegin; }
- const F32& getTwistEnd() const { return mTwistEnd; }
- const F32& getTwist() const { return mTwistEnd; } // deprecated
- const F32& getRadiusOffset() const { return mRadiusOffset; }
- const LLVector2 &getTaper() const { return mTaper; }
- const F32& getTaperX() const { return mTaper.mV[0]; }
- const F32& getTaperY() const { return mTaper.mV[1]; }
- const F32& getRevolutions() const { return mRevolutions; }
- const F32& getSkew() const { return mSkew; }
-
- void setCurveType(const U8 type) { mCurveType = type; }
- void setBegin(const F32 begin) { mBegin = begin; }
- void setEnd(const F32 end) { mEnd = end; }
-
- void setScale(const F32 x, const F32 y) { mScale.setVec(x,y); }
- void setScaleX(const F32 v) { mScale.mV[VX] = v; }
- void setScaleY(const F32 v) { mScale.mV[VY] = v; }
- void setShear(const F32 x, const F32 y) { mShear.setVec(x,y); }
- void setShearX(const F32 v) { mShear.mV[VX] = v; }
- void setShearY(const F32 v) { mShear.mV[VY] = v; }
-
- void setTwistBegin(const F32 twist_begin) { mTwistBegin = twist_begin; }
- void setTwistEnd(const F32 twist_end) { mTwistEnd = twist_end; }
- void setTwist(const F32 twist) { setTwistEnd(twist); } // deprecated
- void setRadiusOffset(const F32 radius_offset){ mRadiusOffset = radius_offset; }
- void setTaper(const F32 x, const F32 y) { mTaper.setVec(x,y); }
- void setTaperX(const F32 v) { mTaper.mV[VX] = v; }
- void setTaperY(const F32 v) { mTaper.mV[VY] = v; }
- void setRevolutions(const F32 revolutions) { mRevolutions = revolutions; }
- void setSkew(const F32 skew) { mSkew = skew; }
-
- friend std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params);
-
-protected:
- // Path params
- U8 mCurveType;
- F32 mBegin;
- F32 mEnd;
- LLVector2 mScale;
- LLVector2 mShear;
-
- F32 mTwistBegin;
- F32 mTwistEnd;
- F32 mRadiusOffset;
- LLVector2 mTaper;
- F32 mRevolutions;
- F32 mSkew;
-
- U32 mCRC;
-};
-
-inline bool LLPathParams::operator==(const LLPathParams ¶ms) const
-{
- return
- (getCurveType() == params.getCurveType()) &&
- (getScale() == params.getScale()) &&
- (getBegin() == params.getBegin()) &&
- (getEnd() == params.getEnd()) &&
- (getShear() == params.getShear()) &&
- (getTwist() == params.getTwist()) &&
- (getTwistBegin() == params.getTwistBegin()) &&
- (getRadiusOffset() == params.getRadiusOffset()) &&
- (getTaper() == params.getTaper()) &&
- (getRevolutions() == params.getRevolutions()) &&
- (getSkew() == params.getSkew());
-}
-
-inline bool LLPathParams::operator!=(const LLPathParams ¶ms) const
-{
- return
- (getCurveType() != params.getCurveType()) ||
- (getScale() != params.getScale()) ||
- (getBegin() != params.getBegin()) ||
- (getEnd() != params.getEnd()) ||
- (getShear() != params.getShear()) ||
- (getTwist() != params.getTwist()) ||
- (getTwistBegin() !=params.getTwistBegin()) ||
- (getRadiusOffset() != params.getRadiusOffset()) ||
- (getTaper() != params.getTaper()) ||
- (getRevolutions() != params.getRevolutions()) ||
- (getSkew() != params.getSkew());
-}
-
-
-inline bool LLPathParams::operator<(const LLPathParams ¶ms) const
-{
- if( getCurveType() != params.getCurveType())
- {
- return getCurveType() < params.getCurveType();
- }
- else
- if( getScale() != params.getScale())
- {
- return getScale() < params.getScale();
- }
- else
- if( getBegin() != params.getBegin())
- {
- return getBegin() < params.getBegin();
- }
- else
- if( getEnd() != params.getEnd())
- {
- return getEnd() < params.getEnd();
- }
- else
- if( getShear() != params.getShear())
- {
- return getShear() < params.getShear();
- }
- else
- if( getTwist() != params.getTwist())
- {
- return getTwist() < params.getTwist();
- }
- else
- if( getTwistBegin() != params.getTwistBegin())
- {
- return getTwistBegin() < params.getTwistBegin();
- }
- else
- if( getRadiusOffset() != params.getRadiusOffset())
- {
- return getRadiusOffset() < params.getRadiusOffset();
- }
- else
- if( getTaper() != params.getTaper())
- {
- return getTaper() < params.getTaper();
- }
- else
- if( getRevolutions() != params.getRevolutions())
- {
- return getRevolutions() < params.getRevolutions();
- }
- else
- {
- return getSkew() < params.getSkew();
- }
-}
-
-typedef LLVolumeParams* LLVolumeParamsPtr;
-typedef const LLVolumeParams* const_LLVolumeParamsPtr;
-
-class LLVolumeParams
-{
-public:
- LLVolumeParams()
- : mSculptType(LL_SCULPT_TYPE_NONE)
- {
- }
-
- LLVolumeParams(LLProfileParams &profile, LLPathParams &path,
- LLUUID sculpt_id = LLUUID::null, U8 sculpt_type = LL_SCULPT_TYPE_NONE)
- : mProfileParams(profile), mPathParams(path), mSculptID(sculpt_id), mSculptType(sculpt_type)
- {
- }
-
- bool operator==(const LLVolumeParams ¶ms) const;
- bool operator!=(const LLVolumeParams ¶ms) const;
- bool operator<(const LLVolumeParams ¶ms) const;
-
-
- void copyParams(const LLVolumeParams ¶ms);
-
- const LLProfileParams &getProfileParams() const {return mProfileParams;}
- LLProfileParams &getProfileParams() {return mProfileParams;}
- const LLPathParams &getPathParams() const {return mPathParams;}
- LLPathParams &getPathParams() {return mPathParams;}
-
- bool importFile(LLFILE *fp);
- bool exportFile(LLFILE *fp) const;
-
- bool importLegacyStream(std::istream& input_stream);
- bool exportLegacyStream(std::ostream& output_stream) const;
-
- LLSD sculptAsLLSD() const;
- bool sculptFromLLSD(LLSD& sd);
-
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-
- bool setType(U8 profile, U8 path);
-
- //void setBeginS(const F32 beginS) { mProfileParams.setBegin(beginS); } // range 0 to 1
- //void setBeginT(const F32 beginT) { mPathParams.setBegin(beginT); } // range 0 to 1
- //void setEndS(const F32 endS) { mProfileParams.setEnd(endS); } // range 0 to 1, must be greater than begin
- //void setEndT(const F32 endT) { mPathParams.setEnd(endT); } // range 0 to 1, must be greater than begin
-
- bool setBeginAndEndS(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end
- bool setBeginAndEndT(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end
-
- bool setHollow(const F32 hollow); // range 0 to 1
- bool setRatio(const F32 x) { return setRatio(x,x); } // 0 = point, 1 = same as base
- bool setShear(const F32 x) { return setShear(x,x); } // 0 = no movement,
- bool setRatio(const F32 x, const F32 y); // 0 = point, 1 = same as base
- bool setShear(const F32 x, const F32 y); // 0 = no movement
-
- bool setTwistBegin(const F32 twist_begin); // range -1 to 1
- bool setTwistEnd(const F32 twist_end); // range -1 to 1
- bool setTwist(const F32 twist) { return setTwistEnd(twist); } // deprecated
- bool setTaper(const F32 x, const F32 y) { bool pass_x = setTaperX(x); bool pass_y = setTaperY(y); return pass_x && pass_y; }
- bool setTaperX(const F32 v); // -1 to 1
- bool setTaperY(const F32 v); // -1 to 1
- bool setRevolutions(const F32 revolutions); // 1 to 4
- bool setRadiusOffset(const F32 radius_offset);
- bool setSkew(const F32 skew);
- bool setSculptID(const LLUUID& sculpt_id, U8 sculpt_type);
-
- static bool validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
- U8 path_curve, F32 path_begin, F32 path_end,
- F32 scx, F32 scy, F32 shx, F32 shy,
- F32 twistend, F32 twistbegin, F32 radiusoffset,
- F32 tx, F32 ty, F32 revolutions, F32 skew);
-
- const F32& getBeginS() const { return mProfileParams.getBegin(); }
- const F32& getBeginT() const { return mPathParams.getBegin(); }
- const F32& getEndS() const { return mProfileParams.getEnd(); }
- const F32& getEndT() const { return mPathParams.getEnd(); }
-
- const F32& getHollow() const { return mProfileParams.getHollow(); }
- const F32& getTwist() const { return mPathParams.getTwist(); }
- const F32& getRatio() const { return mPathParams.getScaleX(); }
- const F32& getRatioX() const { return mPathParams.getScaleX(); }
- const F32& getRatioY() const { return mPathParams.getScaleY(); }
- const F32& getShearX() const { return mPathParams.getShearX(); }
- const F32& getShearY() const { return mPathParams.getShearY(); }
-
- const F32& getTwistBegin()const { return mPathParams.getTwistBegin(); }
- const F32& getRadiusOffset() const { return mPathParams.getRadiusOffset(); }
- const F32& getTaper() const { return mPathParams.getTaperX(); }
- const F32& getTaperX() const { return mPathParams.getTaperX(); }
- const F32& getTaperY() const { return mPathParams.getTaperY(); }
- const F32& getRevolutions() const { return mPathParams.getRevolutions(); }
- const F32& getSkew() const { return mPathParams.getSkew(); }
- const LLUUID& getSculptID() const { return mSculptID; }
- const U8& getSculptType() const { return mSculptType; }
- bool isSculpt() const;
- bool isMeshSculpt() const;
- bool isConvex() const;
-
- // 'begin' and 'end' should be in range [0, 1] (they will be clamped)
- // (begin, end) = (0, 1) will not change the volume
- // (begin, end) = (0, 0.5) will reduce the volume to the first half of its profile/path (S/T)
- void reduceS(F32 begin, F32 end);
- void reduceT(F32 begin, F32 end);
-
- struct compare
- {
- bool operator()( const const_LLVolumeParamsPtr& first, const const_LLVolumeParamsPtr& second) const
- {
- return (*first < *second);
- }
- };
-
- friend std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
-
- // debug helper functions
- void setCube();
-
-protected:
- LLProfileParams mProfileParams;
- LLPathParams mPathParams;
- LLUUID mSculptID;
- U8 mSculptType;
-};
-
-
-class LLProfile
-{
- friend class LLVolume;
-
-public:
- LLProfile()
- : mOpen(false),
- mConcave(false),
- mDirty(true),
- mTotalOut(0),
- mTotal(2)
- {
- }
-
- S32 getTotal() const { return mTotal; }
- S32 getTotalOut() const { return mTotalOut; } // Total number of outside points
- bool isFlat(S32 face) const { return (mFaces[face].mCount == 2); }
- bool isOpen() const { return mOpen; }
- void setDirty() { mDirty = true; }
-
- static S32 getNumPoints(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0,
- bool is_sculpted = false, S32 sculpt_size = 0);
- bool generate(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0,
- bool is_sculpted = false, S32 sculpt_size = 0);
- bool isConcave() const { return mConcave; }
-public:
- struct Face
- {
- S32 mIndex;
- S32 mCount;
- F32 mScaleU;
- bool mCap;
- bool mFlat;
- LLFaceID mFaceID;
- };
-
- LLAlignedArray<LLVector4a, 64> mProfile;
- //LLAlignedArray<LLVector4a, 64> mNormals;
- std::vector<Face> mFaces;
-
- //LLAlignedArray<LLVector4a, 64> mEdgeNormals;
- //LLAlignedArray<LLVector4a, 64> mEdgeCenters;
-
- friend std::ostream& operator<<(std::ostream &s, const LLProfile &profile);
-
-protected:
- ~LLProfile();
-
- static S32 getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0);
- void genNGon(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0);
-
- Face* addHole(const LLProfileParams& params, bool flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split = 0);
- Face* addCap (S16 faceID);
- Face* addFace(S32 index, S32 count, F32 scaleU, S16 faceID, bool flat);
-
-protected:
- bool mOpen;
- bool mConcave;
- bool mDirty;
-
- S32 mTotalOut;
- S32 mTotal;
-};
-
-//-------------------------------------------------------------------
-// SWEEP/EXTRUDE PATHS
-//-------------------------------------------------------------------
-
-class LLPath
-{
-public:
- class PathPt
- {
- public:
- LLMatrix4a mRot;
- LLVector4a mPos;
-
- LLVector4a mScale;
- F32 mTexT;
- F32 pad[3]; //for alignment
- PathPt()
- {
- mPos.clear();
- mTexT = 0;
- mScale.clear();
- mRot.setRows(LLVector4a(1,0,0,0),
- LLVector4a(0,1,0,0),
- LLVector4a(0,0,1,0));
-
- //distinguished data in the pad for debugging
- pad[0] = 3.14159f;
- pad[1] = -3.14159f;
- pad[2] = 0.585f;
- }
- };
-
-public:
- LLPath()
- : mOpen(false),
- mTotal(0),
- mDirty(true),
- mStep(1)
- {
- }
-
- virtual ~LLPath();
-
- static S32 getNumPoints(const LLPathParams& params, F32 detail);
- static S32 getNumNGonPoints(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f);
-
- void genNGon(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f);
- virtual bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0,
- bool is_sculpted = false, S32 sculpt_size = 0);
-
- bool isOpen() const { return mOpen; }
- F32 getStep() const { return mStep; }
- void setDirty() { mDirty = true; }
-
- S32 getPathLength() const { return (S32)mPath.size(); }
-
- void resizePath(S32 length) { mPath.resize(length); }
-
- friend std::ostream& operator<<(std::ostream &s, const LLPath &path);
-
-public:
- LLAlignedArray<PathPt, 64> mPath;
-
-protected:
- bool mOpen;
- S32 mTotal;
- bool mDirty;
- F32 mStep;
-};
-
-class LLDynamicPath : public LLPath
-{
-public:
- LLDynamicPath() : LLPath() { }
- /*virtual*/ bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0,
- bool is_sculpted = false, S32 sculpt_size = 0);
-};
-
-// Yet another "face" class - caches volume-specific, but not instance-specific data for faces)
-class LLVolumeFace
-{
-public:
- class VertexData
- {
- enum
- {
- POSITION = 0,
- NORMAL = 1
- };
-
- private:
- void init();
- public:
- VertexData();
- VertexData(const VertexData& rhs);
- const VertexData& operator=(const VertexData& rhs);
-
- ~VertexData();
- LLVector4a& getPosition();
- LLVector4a& getNormal();
- const LLVector4a& getPosition() const;
- const LLVector4a& getNormal() const;
- void setPosition(const LLVector4a& pos);
- void setNormal(const LLVector4a& norm);
-
-
- LLVector2 mTexCoord;
-
- bool operator<(const VertexData& rhs) const;
- bool operator==(const VertexData& rhs) const;
- bool compareNormal(const VertexData& rhs, F32 angle_cutoff) const;
-
- private:
- LLVector4a* mData;
- };
-
- LLVolumeFace();
- LLVolumeFace(const LLVolumeFace& src);
- LLVolumeFace& operator=(const LLVolumeFace& rhs);
-
- ~LLVolumeFace();
-private:
- void freeData();
-public:
-
- bool create(LLVolume* volume, bool partial_build = false);
- void createTangents();
-
- void resizeVertices(S32 num_verts);
- void allocateTangents(S32 num_verts);
- void allocateWeights(S32 num_verts);
- void allocateJointIndices(S32 num_verts);
- void resizeIndices(S32 num_indices);
- void fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx);
-
- void pushVertex(const VertexData& cv);
- void pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc);
- void pushIndex(const U16& idx);
-
- void swapData(LLVolumeFace& rhs);
-
- void getVertexData(U16 indx, LLVolumeFace::VertexData& cv);
-
- class VertexMapData : public LLVolumeFace::VertexData
- {
- public:
- U16 mIndex;
-
- bool operator==(const LLVolumeFace::VertexData& rhs) const;
-
- struct ComparePosition
- {
- bool operator()(const LLVector3& a, const LLVector3& b) const;
- };
-
- typedef std::map<LLVector3, std::vector<VertexMapData>, VertexMapData::ComparePosition > PointMap;
- };
-
- // Eliminates non unique triangles, takes positions,
- // normals and texture coordinates into account.
- void remap();
-
- void optimize(F32 angle_cutoff = 2.f);
- bool cacheOptimize(bool gen_tangents = false);
-
- void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f));
- void destroyOctree();
- // Get a reference to the octree, which may be null
- const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const;
-
- enum
- {
- SINGLE_MASK = 0x0001,
- CAP_MASK = 0x0002,
- END_MASK = 0x0004,
- SIDE_MASK = 0x0008,
- INNER_MASK = 0x0010,
- OUTER_MASK = 0x0020,
- HOLLOW_MASK = 0x0040,
- OPEN_MASK = 0x0080,
- FLAT_MASK = 0x0100,
- TOP_MASK = 0x0200,
- BOTTOM_MASK = 0x0400
- };
-
-public:
- S32 mID;
- U32 mTypeMask;
-
- // Only used for INNER/OUTER faces
- S32 mBeginS;
- S32 mBeginT;
- S32 mNumS;
- S32 mNumT;
-
- LLVector4a* mExtents; //minimum and maximum point of face
- LLVector4a* mCenter;
- LLVector2 mTexCoordExtents[2]; //minimum and maximum of texture coordinates of the face.
-
- S32 mNumVertices; // num vertices == num normals == num texcoords
- S32 mNumAllocatedVertices;
- S32 mNumIndices;
-
- LLVector4a* mPositions; // Contains vertices, nortmals and texcoords
- LLVector4a* mNormals; // pointer into mPositions
- LLVector4a* mTangents;
- LLVector2* mTexCoords; // pointer into mPositions
-
- // mIndices contains mNumIndices amount of elements.
- // It contains triangles, each 3 indices describe one triangle.
- // If mIndices contains {0, 2, 3, 1, 2, 4}, it means there
- // are two triangles {0, 2, 3} and {1, 2, 4} with values being
- // indexes for mPositions/mNormals/mTexCoords
- U16* mIndices;
-
- std::vector<S32> mEdge;
-
- //list of skin weights for rigged volumes
- // format is mWeights[vertex_index].mV[influence] = <joint_index>.<weight>
- // mWeights.size() should be empty or match mVertices.size()
- LLVector4a* mWeights;
-
-#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
- LLVector4a* mJustWeights;
- U8* mJointIndices;
-#endif
-
- mutable bool mWeightsScrubbed;
-
- // Which joints are rigged to, and the bounding box of any rigged
- // vertices per joint.
- LLJointRiggingInfoTab mJointRiggingInfoTab;
-
- //whether or not face has been cache optimized
- bool mOptimized;
-
- // if this is a mesh asset, scale and translation that were applied
- // when encoding the source mesh into a unit cube
- // used for regenerating tangents
- LLVector3 mNormalizedScale = LLVector3(1,1,1);
-
-private:
- LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree;
- LLVolumeTriangle* mOctreeTriangles;
-
- bool createUnCutCubeCap(LLVolume* volume, bool partial_build = false);
- bool createCap(LLVolume* volume, bool partial_build = false);
- bool createSide(LLVolume* volume, bool partial_build = false);
-};
-
-class LLVolume : public LLRefCount
-{
- friend class LLVolumeLODGroup;
-
-protected:
- virtual ~LLVolume(); // use unref
-
-public:
- typedef std::vector<LLVolumeFace> face_list_t;
-
- struct FaceParams
- {
- LLFaceID mFaceID;
- S32 mBeginS;
- S32 mCountS;
- S32 mBeginT;
- S32 mCountT;
- };
-
- LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool generate_single_face = false, const bool is_unique = false);
-
- U8 getProfileType() const { return mParams.getProfileParams().getCurveType(); }
- U8 getPathType() const { return mParams.getPathParams().getCurveType(); }
- S32 getNumFaces() const;
- S32 getNumVolumeFaces() const { return mVolumeFaces.size(); }
- F32 getDetail() const { return mDetail; }
- F32 getSurfaceArea() const { return mSurfaceArea; }
- const LLVolumeParams& getParams() const { return mParams; }
- LLVolumeParams getCopyOfParams() const { return mParams; }
- const LLProfile& getProfile() const { return *mProfilep; }
- LLPath& getPath() const { return *mPathp; }
- void resizePath(S32 length);
- const LLAlignedArray<LLVector4a,64>& getMesh() const { return mMesh; }
- const LLVector4a& getMeshPt(const U32 i) const { return mMesh[i]; }
-
-
- void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); }
-
- void regen();
- void genTangents(S32 face);
-
- bool isConvex() const;
- bool isCap(S32 face);
- bool isFlat(S32 face);
- bool isUnique() const { return mUnique; }
-
- S32 getSculptLevel() const { return mSculptLevel; }
- void setSculptLevel(S32 level) { mSculptLevel = level; }
-
-
- static void getLoDTriangleCounts(const LLVolumeParams& params, S32* counts);
-
- S32 getNumTriangles(S32* vcount = nullptr) const;
-
- void generateSilhouetteVertices(std::vector<LLVector3> &vertices,
- std::vector<LLVector3> &normals,
- const LLVector3& view_vec,
- const LLMatrix4& mat,
- const LLMatrix3& norm_mat,
- S32 face_index);
-
- //get the face index of the face that intersects with the given line segment at the point
- //closest to start. Moves end to the point of intersection. Returns -1 if no intersection.
- //Line segment must be in volume space.
- S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
- S32 face = -1, // which face to check, -1 = ALL_SIDES
- LLVector4a* intersection = nullptr, // return the intersection point
- LLVector2* tex_coord = nullptr, // return the texture coordinates of the intersection point
- LLVector4a* normal = nullptr, // return the surface normal at the intersection point
- LLVector4a* tangent = nullptr // return the surface tangent at the intersection point
- );
-
- LLFaceID generateFaceMask();
-
- bool isFaceMaskValid(LLFaceID face_mask);
- static S32 sNumMeshPoints;
-
- friend std::ostream& operator<<(std::ostream &s, const LLVolume &volume);
- friend std::ostream& operator<<(std::ostream &s, const LLVolume *volumep); // HACK to bypass Windoze confusion over
- // conversion if *(LLVolume*) to LLVolume&
- const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE
-
- LLVolumeFace &getVolumeFace(const S32 f) {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE
-
- face_list_t& getVolumeFaces() { return mVolumeFaces; }
-
- U32 mFaceMask; // bit array of which faces exist in this volume
- LLVector3 mLODScaleBias; // vector for biasing LOD based on scale
-
- void sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level, bool visible_placeholder);
- void copyVolumeFaces(const LLVolume* volume);
- void copyFacesTo(std::vector<LLVolumeFace> &faces) const;
- void copyFacesFrom(const std::vector<LLVolumeFace> &faces);
-
- // use meshoptimizer to optimize index buffer for vertex shader cache
- // gen_tangents - if true, generate MikkTSpace tangents if needed before optimizing index buffer
- bool cacheOptimize(bool gen_tangents = false);
-
-private:
- void sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type);
- F32 sculptGetSurfaceArea();
- void sculptGenerateEmptyPlaceholder();
- void sculptGenerateSpherePlaceholder();
-
-protected:
- bool generate();
- void createVolumeFaces();
-public:
- bool unpackVolumeFaces(std::istream& is, S32 size);
- bool unpackVolumeFaces(U8* in_data, S32 size);
-private:
- bool unpackVolumeFacesInternal(const LLSD& mdl);
-
-public:
- virtual void setMeshAssetLoaded(bool loaded);
- virtual bool isMeshAssetLoaded();
- virtual void setMeshAssetUnavaliable(bool unavaliable);
- virtual bool isMeshAssetUnavaliable();
-
- protected:
- bool mUnique;
- F32 mDetail;
- S32 mSculptLevel;
- F32 mSurfaceArea; //unscaled surface area
- bool mIsMeshAssetLoaded;
- bool mIsMeshAssetUnavaliable;
-
- const LLVolumeParams mParams;
- LLPath *mPathp;
- LLProfile *mProfilep;
- LLAlignedArray<LLVector4a,64> mMesh;
-
-
- bool mGenerateSingleFace;
- face_list_t mVolumeFaces;
-
-public:
- LLVector4a* mHullPoints;
- U16* mHullIndices;
- S32 mNumHullPoints;
- S32 mNumHullIndices;
-};
-
-std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
-
-bool LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size);
-bool LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
-bool LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size);
-
-bool LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
- F32& intersection_a, F32& intersection_b, F32& intersection_t);
-bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
- F32& intersection_a, F32& intersection_b, F32& intersection_t);
-
-#endif
+/** + * @file llvolume.h + * @brief LLVolume base class. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLVOLUME_H +#define LL_LLVOLUME_H + +#include <iostream> + +class LLProfileParams; +class LLPathParams; +class LLVolumeParams; +class LLProfile; +class LLPath; + +template<class T> class LLPointer; +template <class T, typename T_PTR> class LLOctreeNode; + +class LLVolumeFace; +class LLVolume; +class LLVolumeTriangle; + +#include "lluuid.h" +#include "v4color.h" +//#include "vmath.h" +#include "v2math.h" +#include "v3math.h" +#include "v3dmath.h" +#include "v4math.h" +#include "llvector4a.h" +#include "llmatrix4a.h" +#include "llquaternion.h" +#include "llstrider.h" +#include "v4coloru.h" +#include "llrefcount.h" +#include "llpointer.h" +#include "llfile.h" +#include "llalignedarray.h" +#include "llrigginginfo.h" + +//============================================================================ + +constexpr S32 MIN_DETAIL_FACES = 6; +constexpr S32 MIN_LOD = 0; +constexpr S32 MAX_LOD = 3; + +// These are defined here but are not enforced at this level, +// rather they are here for the convenience of code that uses +// the LLVolume class. +constexpr F32 MIN_VOLUME_PROFILE_WIDTH = 0.05f; +constexpr F32 MIN_VOLUME_PATH_WIDTH = 0.05f; + +constexpr F32 CUT_QUANTA = 0.00002f; +constexpr F32 SCALE_QUANTA = 0.01f; +constexpr F32 SHEAR_QUANTA = 0.01f; +constexpr F32 TAPER_QUANTA = 0.01f; +constexpr F32 REV_QUANTA = 0.015f; +constexpr F32 HOLLOW_QUANTA = 0.00002f; + +constexpr S32 MAX_VOLUME_TRIANGLE_INDICES = 10000; + +//============================================================================ + +// useful masks +constexpr LLPCode LL_PCODE_HOLLOW_MASK = 0x80; // has a thickness +constexpr LLPCode LL_PCODE_SEGMENT_MASK = 0x40; // segments (1 angle) +constexpr LLPCode LL_PCODE_PATCH_MASK = 0x20; // segmented segments (2 angles) +constexpr LLPCode LL_PCODE_HEMI_MASK = 0x10; // half-primitives get their own type per PR's dictum +constexpr LLPCode LL_PCODE_BASE_MASK = 0x0F; + + // primitive shapes +constexpr LLPCode LL_PCODE_CUBE = 1; +constexpr LLPCode LL_PCODE_PRISM = 2; +constexpr LLPCode LL_PCODE_TETRAHEDRON = 3; +constexpr LLPCode LL_PCODE_PYRAMID = 4; +constexpr LLPCode LL_PCODE_CYLINDER = 5; +constexpr LLPCode LL_PCODE_CONE = 6; +constexpr LLPCode LL_PCODE_SPHERE = 7; +constexpr LLPCode LL_PCODE_TORUS = 8; +constexpr LLPCode LL_PCODE_VOLUME = 9; + + // surfaces +//constexpr LLPCode LL_PCODE_SURFACE_TRIANGLE = 10; +//constexpr LLPCode LL_PCODE_SURFACE_SQUARE = 11; +//constexpr LLPCode LL_PCODE_SURFACE_DISC = 12; + +constexpr LLPCode LL_PCODE_APP = 14; // App specific pcode (for viewer/sim side only objects) +constexpr LLPCode LL_PCODE_LEGACY = 15; + +// Pcodes for legacy objects +//constexpr LLPCode LL_PCODE_LEGACY_ATOR = 0x10 | LL_PCODE_LEGACY; // ATOR +constexpr LLPCode LL_PCODE_LEGACY_AVATAR = 0x20 | LL_PCODE_LEGACY; // PLAYER +//constexpr LLPCode LL_PCODE_LEGACY_BIRD = 0x30 | LL_PCODE_LEGACY; // BIRD +//constexpr LLPCode LL_PCODE_LEGACY_DEMON = 0x40 | LL_PCODE_LEGACY; // DEMON +constexpr LLPCode LL_PCODE_LEGACY_GRASS = 0x50 | LL_PCODE_LEGACY; // GRASS +constexpr LLPCode LL_PCODE_TREE_NEW = 0x60 | LL_PCODE_LEGACY; // new trees +//constexpr LLPCode LL_PCODE_LEGACY_ORACLE = 0x70 | LL_PCODE_LEGACY; // ORACLE +constexpr LLPCode LL_PCODE_LEGACY_PART_SYS = 0x80 | LL_PCODE_LEGACY; // PART_SYS +constexpr LLPCode LL_PCODE_LEGACY_ROCK = 0x90 | LL_PCODE_LEGACY; // ROCK +//constexpr LLPCode LL_PCODE_LEGACY_SHOT = 0xA0 | LL_PCODE_LEGACY; // BASIC_SHOT +//constexpr LLPCode LL_PCODE_LEGACY_SHOT_BIG = 0xB0 | LL_PCODE_LEGACY; +//constexpr LLPCode LL_PCODE_LEGACY_SMOKE = 0xC0 | LL_PCODE_LEGACY; // SMOKE +//constexpr LLPCode LL_PCODE_LEGACY_SPARK = 0xD0 | LL_PCODE_LEGACY;// SPARK +constexpr LLPCode LL_PCODE_LEGACY_TEXT_BUBBLE = 0xE0 | LL_PCODE_LEGACY; // TEXTBUBBLE +constexpr LLPCode LL_PCODE_LEGACY_TREE = 0xF0 | LL_PCODE_LEGACY; // TREE + + // hemis +constexpr LLPCode LL_PCODE_CYLINDER_HEMI = LL_PCODE_CYLINDER | LL_PCODE_HEMI_MASK; +constexpr LLPCode LL_PCODE_CONE_HEMI = LL_PCODE_CONE | LL_PCODE_HEMI_MASK; +constexpr LLPCode LL_PCODE_SPHERE_HEMI = LL_PCODE_SPHERE | LL_PCODE_HEMI_MASK; +constexpr LLPCode LL_PCODE_TORUS_HEMI = LL_PCODE_TORUS | LL_PCODE_HEMI_MASK; + + +// Volumes consist of a profile at the base that is swept around +// a path to make a volume. +// The profile code +constexpr U8 LL_PCODE_PROFILE_MASK = 0x0f; +constexpr U8 LL_PCODE_PROFILE_MIN = 0x00; +constexpr U8 LL_PCODE_PROFILE_CIRCLE = 0x00; +constexpr U8 LL_PCODE_PROFILE_SQUARE = 0x01; +constexpr U8 LL_PCODE_PROFILE_ISOTRI = 0x02; +constexpr U8 LL_PCODE_PROFILE_EQUALTRI = 0x03; +constexpr U8 LL_PCODE_PROFILE_RIGHTTRI = 0x04; +constexpr U8 LL_PCODE_PROFILE_CIRCLE_HALF = 0x05; +constexpr U8 LL_PCODE_PROFILE_MAX = 0x05; + +// Stored in the profile byte +constexpr U8 LL_PCODE_HOLE_MASK = 0xf0; +constexpr U8 LL_PCODE_HOLE_MIN = 0x00; +constexpr U8 LL_PCODE_HOLE_SAME = 0x00; // same as outside profile +constexpr U8 LL_PCODE_HOLE_CIRCLE = 0x10; +constexpr U8 LL_PCODE_HOLE_SQUARE = 0x20; +constexpr U8 LL_PCODE_HOLE_TRIANGLE = 0x30; +constexpr U8 LL_PCODE_HOLE_MAX = 0x03; // min/max needs to be >> 4 of real min/max + +constexpr U8 LL_PCODE_PATH_IGNORE = 0x00; +constexpr U8 LL_PCODE_PATH_MIN = 0x01; // min/max needs to be >> 4 of real min/max +constexpr U8 LL_PCODE_PATH_LINE = 0x10; +constexpr U8 LL_PCODE_PATH_CIRCLE = 0x20; +constexpr U8 LL_PCODE_PATH_CIRCLE2 = 0x30; +constexpr U8 LL_PCODE_PATH_TEST = 0x40; +constexpr U8 LL_PCODE_PATH_FLEXIBLE = 0x80; +constexpr U8 LL_PCODE_PATH_MAX = 0x08; + +//============================================================================ + +// face identifiers +typedef U16 LLFaceID; + +constexpr LLFaceID LL_FACE_PATH_BEGIN = 0x1 << 0; +constexpr LLFaceID LL_FACE_PATH_END = 0x1 << 1; +constexpr LLFaceID LL_FACE_INNER_SIDE = 0x1 << 2; +constexpr LLFaceID LL_FACE_PROFILE_BEGIN = 0x1 << 3; +constexpr LLFaceID LL_FACE_PROFILE_END = 0x1 << 4; +constexpr LLFaceID LL_FACE_OUTER_SIDE_0 = 0x1 << 5; +constexpr LLFaceID LL_FACE_OUTER_SIDE_1 = 0x1 << 6; +constexpr LLFaceID LL_FACE_OUTER_SIDE_2 = 0x1 << 7; +constexpr LLFaceID LL_FACE_OUTER_SIDE_3 = 0x1 << 8; + +//============================================================================ + +// sculpt types + flags + +constexpr U8 LL_SCULPT_TYPE_NONE = 0; +constexpr U8 LL_SCULPT_TYPE_SPHERE = 1; +constexpr U8 LL_SCULPT_TYPE_TORUS = 2; +constexpr U8 LL_SCULPT_TYPE_PLANE = 3; +constexpr U8 LL_SCULPT_TYPE_CYLINDER = 4; +constexpr U8 LL_SCULPT_TYPE_MESH = 5; +constexpr U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE | + LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH; + +// for value checks, assign new value after adding new types +constexpr U8 LL_SCULPT_TYPE_MAX = LL_SCULPT_TYPE_MESH; + +constexpr U8 LL_SCULPT_FLAG_INVERT = 64; +constexpr U8 LL_SCULPT_FLAG_MIRROR = 128; +constexpr U8 LL_SCULPT_FLAG_MASK = LL_SCULPT_FLAG_INVERT | LL_SCULPT_FLAG_MIRROR; + +constexpr S32 LL_SCULPT_MESH_MAX_FACES = 8; + +extern bool gDebugGL; + +class LLProfileParams +{ +public: + LLProfileParams() + : mCurveType(LL_PCODE_PROFILE_SQUARE), + mBegin(0.f), + mEnd(1.f), + mHollow(0.f), + mCRC(0) + { + } + + LLProfileParams(U8 curve, F32 begin, F32 end, F32 hollow) + : mCurveType(curve), + mBegin(begin), + mEnd(end), + mHollow(hollow), + mCRC(0) + { + } + + LLProfileParams(U8 curve, U16 begin, U16 end, U16 hollow) + { + mCurveType = curve; + F32 temp_f32 = begin * CUT_QUANTA; + if (temp_f32 > 1.f) + { + temp_f32 = 1.f; + } + mBegin = temp_f32; + temp_f32 = end * CUT_QUANTA; + if (temp_f32 > 1.f) + { + temp_f32 = 1.f; + } + mEnd = 1.f - temp_f32; + temp_f32 = hollow * HOLLOW_QUANTA; + if (temp_f32 > 1.f) + { + temp_f32 = 1.f; + } + mHollow = temp_f32; + mCRC = 0; + } + + bool operator==(const LLProfileParams ¶ms) const; + bool operator!=(const LLProfileParams ¶ms) const; + bool operator<(const LLProfileParams ¶ms) const; + + void copyParams(const LLProfileParams ¶ms); + + bool importFile(LLFILE *fp); + bool exportFile(LLFILE *fp) const; + + bool importLegacyStream(std::istream& input_stream); + bool exportLegacyStream(std::ostream& output_stream) const; + + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + const F32& getBegin () const { return mBegin; } + const F32& getEnd () const { return mEnd; } + const F32& getHollow() const { return mHollow; } + const U8& getCurveType () const { return mCurveType; } + + void setCurveType(const U32 type) { mCurveType = type;} + void setBegin(const F32 begin) { mBegin = (begin >= 1.0f) ? 0.0f : ((int) (begin * 100000))/100000.0f;} + void setEnd(const F32 end) { mEnd = (end <= 0.0f) ? 1.0f : ((int) (end * 100000))/100000.0f;} + void setHollow(const F32 hollow) { mHollow = ((int) (hollow * 100000))/100000.0f;} + + friend std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params); + +protected: + // Profile params + U8 mCurveType; + F32 mBegin; + F32 mEnd; + F32 mHollow; + + U32 mCRC; +}; + +inline bool LLProfileParams::operator==(const LLProfileParams ¶ms) const +{ + return + (getCurveType() == params.getCurveType()) && + (getBegin() == params.getBegin()) && + (getEnd() == params.getEnd()) && + (getHollow() == params.getHollow()); +} + +inline bool LLProfileParams::operator!=(const LLProfileParams ¶ms) const +{ + return + (getCurveType() != params.getCurveType()) || + (getBegin() != params.getBegin()) || + (getEnd() != params.getEnd()) || + (getHollow() != params.getHollow()); +} + + +inline bool LLProfileParams::operator<(const LLProfileParams ¶ms) const +{ + if (getCurveType() != params.getCurveType()) + { + return getCurveType() < params.getCurveType(); + } + else + if (getBegin() != params.getBegin()) + { + return getBegin() < params.getBegin(); + } + else + if (getEnd() != params.getEnd()) + { + return getEnd() < params.getEnd(); + } + else + { + return getHollow() < params.getHollow(); + } +} + +#define U8_TO_F32(x) (F32)(*((S8 *)&x)) + +class LLPathParams +{ +public: + LLPathParams() + : + mCurveType(LL_PCODE_PATH_LINE), + mBegin(0.f), + mEnd(1.f), + mScale(1.f,1.f), + mShear(0.f,0.f), + mTwistBegin(0.f), + mTwistEnd(0.f), + mRadiusOffset(0.f), + mTaper(0.f,0.f), + mRevolutions(1.f), + mSkew(0.f), + mCRC(0) + { + } + + LLPathParams(U8 curve, F32 begin, F32 end, F32 scx, F32 scy, F32 shx, F32 shy, F32 twistend, F32 twistbegin, F32 radiusoffset, F32 tx, F32 ty, F32 revolutions, F32 skew) + : mCurveType(curve), + mBegin(begin), + mEnd(end), + mScale(scx,scy), + mShear(shx,shy), + mTwistBegin(twistbegin), + mTwistEnd(twistend), + mRadiusOffset(radiusoffset), + mTaper(tx,ty), + mRevolutions(revolutions), + mSkew(skew), + mCRC(0) + { + } + + LLPathParams(U8 curve, U16 begin, U16 end, U8 scx, U8 scy, U8 shx, U8 shy, U8 twistend, U8 twistbegin, U8 radiusoffset, U8 tx, U8 ty, U8 revolutions, U8 skew) + { + mCurveType = curve; + mBegin = (F32)(begin * CUT_QUANTA); + mEnd = (F32)(100.f - end) * CUT_QUANTA; + if (mEnd > 1.f) + mEnd = 1.f; + mScale.setVec((F32) (200 - scx) * SCALE_QUANTA,(F32) (200 - scy) * SCALE_QUANTA); + mShear.setVec(U8_TO_F32(shx) * SHEAR_QUANTA,U8_TO_F32(shy) * SHEAR_QUANTA); + mTwistBegin = U8_TO_F32(twistbegin) * SCALE_QUANTA; + mTwistEnd = U8_TO_F32(twistend) * SCALE_QUANTA; + mRadiusOffset = U8_TO_F32(radiusoffset) * SCALE_QUANTA; + mTaper.setVec(U8_TO_F32(tx) * TAPER_QUANTA,U8_TO_F32(ty) * TAPER_QUANTA); + mRevolutions = ((F32)revolutions) * REV_QUANTA + 1.0f; + mSkew = U8_TO_F32(skew) * SCALE_QUANTA; + + mCRC = 0; + } + + bool operator==(const LLPathParams ¶ms) const; + bool operator!=(const LLPathParams ¶ms) const; + bool operator<(const LLPathParams ¶ms) const; + + void copyParams(const LLPathParams ¶ms); + + bool importFile(LLFILE *fp); + bool exportFile(LLFILE *fp) const; + + bool importLegacyStream(std::istream& input_stream); + bool exportLegacyStream(std::ostream& output_stream) const; + + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + const F32& getBegin() const { return mBegin; } + const F32& getEnd() const { return mEnd; } + const LLVector2 &getScale() const { return mScale; } + const F32& getScaleX() const { return mScale.mV[0]; } + const F32& getScaleY() const { return mScale.mV[1]; } + const LLVector2 getBeginScale() const; + const LLVector2 getEndScale() const; + const LLVector2 &getShear() const { return mShear; } + const F32& getShearX() const { return mShear.mV[0]; } + const F32& getShearY() const { return mShear.mV[1]; } + const U8& getCurveType () const { return mCurveType; } + + const F32& getTwistBegin() const { return mTwistBegin; } + const F32& getTwistEnd() const { return mTwistEnd; } + const F32& getTwist() const { return mTwistEnd; } // deprecated + const F32& getRadiusOffset() const { return mRadiusOffset; } + const LLVector2 &getTaper() const { return mTaper; } + const F32& getTaperX() const { return mTaper.mV[0]; } + const F32& getTaperY() const { return mTaper.mV[1]; } + const F32& getRevolutions() const { return mRevolutions; } + const F32& getSkew() const { return mSkew; } + + void setCurveType(const U8 type) { mCurveType = type; } + void setBegin(const F32 begin) { mBegin = begin; } + void setEnd(const F32 end) { mEnd = end; } + + void setScale(const F32 x, const F32 y) { mScale.setVec(x,y); } + void setScaleX(const F32 v) { mScale.mV[VX] = v; } + void setScaleY(const F32 v) { mScale.mV[VY] = v; } + void setShear(const F32 x, const F32 y) { mShear.setVec(x,y); } + void setShearX(const F32 v) { mShear.mV[VX] = v; } + void setShearY(const F32 v) { mShear.mV[VY] = v; } + + void setTwistBegin(const F32 twist_begin) { mTwistBegin = twist_begin; } + void setTwistEnd(const F32 twist_end) { mTwistEnd = twist_end; } + void setTwist(const F32 twist) { setTwistEnd(twist); } // deprecated + void setRadiusOffset(const F32 radius_offset){ mRadiusOffset = radius_offset; } + void setTaper(const F32 x, const F32 y) { mTaper.setVec(x,y); } + void setTaperX(const F32 v) { mTaper.mV[VX] = v; } + void setTaperY(const F32 v) { mTaper.mV[VY] = v; } + void setRevolutions(const F32 revolutions) { mRevolutions = revolutions; } + void setSkew(const F32 skew) { mSkew = skew; } + + friend std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params); + +protected: + // Path params + U8 mCurveType; + F32 mBegin; + F32 mEnd; + LLVector2 mScale; + LLVector2 mShear; + + F32 mTwistBegin; + F32 mTwistEnd; + F32 mRadiusOffset; + LLVector2 mTaper; + F32 mRevolutions; + F32 mSkew; + + U32 mCRC; +}; + +inline bool LLPathParams::operator==(const LLPathParams ¶ms) const +{ + return + (getCurveType() == params.getCurveType()) && + (getScale() == params.getScale()) && + (getBegin() == params.getBegin()) && + (getEnd() == params.getEnd()) && + (getShear() == params.getShear()) && + (getTwist() == params.getTwist()) && + (getTwistBegin() == params.getTwistBegin()) && + (getRadiusOffset() == params.getRadiusOffset()) && + (getTaper() == params.getTaper()) && + (getRevolutions() == params.getRevolutions()) && + (getSkew() == params.getSkew()); +} + +inline bool LLPathParams::operator!=(const LLPathParams ¶ms) const +{ + return + (getCurveType() != params.getCurveType()) || + (getScale() != params.getScale()) || + (getBegin() != params.getBegin()) || + (getEnd() != params.getEnd()) || + (getShear() != params.getShear()) || + (getTwist() != params.getTwist()) || + (getTwistBegin() !=params.getTwistBegin()) || + (getRadiusOffset() != params.getRadiusOffset()) || + (getTaper() != params.getTaper()) || + (getRevolutions() != params.getRevolutions()) || + (getSkew() != params.getSkew()); +} + + +inline bool LLPathParams::operator<(const LLPathParams ¶ms) const +{ + if( getCurveType() != params.getCurveType()) + { + return getCurveType() < params.getCurveType(); + } + else + if( getScale() != params.getScale()) + { + return getScale() < params.getScale(); + } + else + if( getBegin() != params.getBegin()) + { + return getBegin() < params.getBegin(); + } + else + if( getEnd() != params.getEnd()) + { + return getEnd() < params.getEnd(); + } + else + if( getShear() != params.getShear()) + { + return getShear() < params.getShear(); + } + else + if( getTwist() != params.getTwist()) + { + return getTwist() < params.getTwist(); + } + else + if( getTwistBegin() != params.getTwistBegin()) + { + return getTwistBegin() < params.getTwistBegin(); + } + else + if( getRadiusOffset() != params.getRadiusOffset()) + { + return getRadiusOffset() < params.getRadiusOffset(); + } + else + if( getTaper() != params.getTaper()) + { + return getTaper() < params.getTaper(); + } + else + if( getRevolutions() != params.getRevolutions()) + { + return getRevolutions() < params.getRevolutions(); + } + else + { + return getSkew() < params.getSkew(); + } +} + +typedef LLVolumeParams* LLVolumeParamsPtr; +typedef const LLVolumeParams* const_LLVolumeParamsPtr; + +class LLVolumeParams +{ +public: + LLVolumeParams() + : mSculptType(LL_SCULPT_TYPE_NONE) + { + } + + LLVolumeParams(LLProfileParams &profile, LLPathParams &path, + LLUUID sculpt_id = LLUUID::null, U8 sculpt_type = LL_SCULPT_TYPE_NONE) + : mProfileParams(profile), mPathParams(path), mSculptID(sculpt_id), mSculptType(sculpt_type) + { + } + + bool operator==(const LLVolumeParams ¶ms) const; + bool operator!=(const LLVolumeParams ¶ms) const; + bool operator<(const LLVolumeParams ¶ms) const; + + + void copyParams(const LLVolumeParams ¶ms); + + const LLProfileParams &getProfileParams() const {return mProfileParams;} + LLProfileParams &getProfileParams() {return mProfileParams;} + const LLPathParams &getPathParams() const {return mPathParams;} + LLPathParams &getPathParams() {return mPathParams;} + + bool importFile(LLFILE *fp); + bool exportFile(LLFILE *fp) const; + + bool importLegacyStream(std::istream& input_stream); + bool exportLegacyStream(std::ostream& output_stream) const; + + LLSD sculptAsLLSD() const; + bool sculptFromLLSD(LLSD& sd); + + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + bool setType(U8 profile, U8 path); + + //void setBeginS(const F32 beginS) { mProfileParams.setBegin(beginS); } // range 0 to 1 + //void setBeginT(const F32 beginT) { mPathParams.setBegin(beginT); } // range 0 to 1 + //void setEndS(const F32 endS) { mProfileParams.setEnd(endS); } // range 0 to 1, must be greater than begin + //void setEndT(const F32 endT) { mPathParams.setEnd(endT); } // range 0 to 1, must be greater than begin + + bool setBeginAndEndS(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end + bool setBeginAndEndT(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end + + bool setHollow(const F32 hollow); // range 0 to 1 + bool setRatio(const F32 x) { return setRatio(x,x); } // 0 = point, 1 = same as base + bool setShear(const F32 x) { return setShear(x,x); } // 0 = no movement, + bool setRatio(const F32 x, const F32 y); // 0 = point, 1 = same as base + bool setShear(const F32 x, const F32 y); // 0 = no movement + + bool setTwistBegin(const F32 twist_begin); // range -1 to 1 + bool setTwistEnd(const F32 twist_end); // range -1 to 1 + bool setTwist(const F32 twist) { return setTwistEnd(twist); } // deprecated + bool setTaper(const F32 x, const F32 y) { bool pass_x = setTaperX(x); bool pass_y = setTaperY(y); return pass_x && pass_y; } + bool setTaperX(const F32 v); // -1 to 1 + bool setTaperY(const F32 v); // -1 to 1 + bool setRevolutions(const F32 revolutions); // 1 to 4 + bool setRadiusOffset(const F32 radius_offset); + bool setSkew(const F32 skew); + bool setSculptID(const LLUUID& sculpt_id, U8 sculpt_type); + + static bool validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow, + U8 path_curve, F32 path_begin, F32 path_end, + F32 scx, F32 scy, F32 shx, F32 shy, + F32 twistend, F32 twistbegin, F32 radiusoffset, + F32 tx, F32 ty, F32 revolutions, F32 skew); + + const F32& getBeginS() const { return mProfileParams.getBegin(); } + const F32& getBeginT() const { return mPathParams.getBegin(); } + const F32& getEndS() const { return mProfileParams.getEnd(); } + const F32& getEndT() const { return mPathParams.getEnd(); } + + const F32& getHollow() const { return mProfileParams.getHollow(); } + const F32& getTwist() const { return mPathParams.getTwist(); } + const F32& getRatio() const { return mPathParams.getScaleX(); } + const F32& getRatioX() const { return mPathParams.getScaleX(); } + const F32& getRatioY() const { return mPathParams.getScaleY(); } + const F32& getShearX() const { return mPathParams.getShearX(); } + const F32& getShearY() const { return mPathParams.getShearY(); } + + const F32& getTwistBegin()const { return mPathParams.getTwistBegin(); } + const F32& getRadiusOffset() const { return mPathParams.getRadiusOffset(); } + const F32& getTaper() const { return mPathParams.getTaperX(); } + const F32& getTaperX() const { return mPathParams.getTaperX(); } + const F32& getTaperY() const { return mPathParams.getTaperY(); } + const F32& getRevolutions() const { return mPathParams.getRevolutions(); } + const F32& getSkew() const { return mPathParams.getSkew(); } + const LLUUID& getSculptID() const { return mSculptID; } + const U8& getSculptType() const { return mSculptType; } + bool isSculpt() const; + bool isMeshSculpt() const; + bool isConvex() const; + + // 'begin' and 'end' should be in range [0, 1] (they will be clamped) + // (begin, end) = (0, 1) will not change the volume + // (begin, end) = (0, 0.5) will reduce the volume to the first half of its profile/path (S/T) + void reduceS(F32 begin, F32 end); + void reduceT(F32 begin, F32 end); + + struct compare + { + bool operator()( const const_LLVolumeParamsPtr& first, const const_LLVolumeParamsPtr& second) const + { + return (*first < *second); + } + }; + + friend std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params); + + // debug helper functions + void setCube(); + +protected: + LLProfileParams mProfileParams; + LLPathParams mPathParams; + LLUUID mSculptID; + U8 mSculptType; +}; + + +class LLProfile +{ + friend class LLVolume; + +public: + LLProfile() + : mOpen(false), + mConcave(false), + mDirty(true), + mTotalOut(0), + mTotal(2) + { + } + + S32 getTotal() const { return mTotal; } + S32 getTotalOut() const { return mTotalOut; } // Total number of outside points + bool isFlat(S32 face) const { return (mFaces[face].mCount == 2); } + bool isOpen() const { return mOpen; } + void setDirty() { mDirty = true; } + + static S32 getNumPoints(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0, + bool is_sculpted = false, S32 sculpt_size = 0); + bool generate(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0, + bool is_sculpted = false, S32 sculpt_size = 0); + bool isConcave() const { return mConcave; } +public: + struct Face + { + S32 mIndex; + S32 mCount; + F32 mScaleU; + bool mCap; + bool mFlat; + LLFaceID mFaceID; + }; + + LLAlignedArray<LLVector4a, 64> mProfile; + //LLAlignedArray<LLVector4a, 64> mNormals; + std::vector<Face> mFaces; + + //LLAlignedArray<LLVector4a, 64> mEdgeNormals; + //LLAlignedArray<LLVector4a, 64> mEdgeCenters; + + friend std::ostream& operator<<(std::ostream &s, const LLProfile &profile); + +protected: + ~LLProfile(); + + static S32 getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0); + void genNGon(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0); + + Face* addHole(const LLProfileParams& params, bool flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split = 0); + Face* addCap (S16 faceID); + Face* addFace(S32 index, S32 count, F32 scaleU, S16 faceID, bool flat); + +protected: + bool mOpen; + bool mConcave; + bool mDirty; + + S32 mTotalOut; + S32 mTotal; +}; + +//------------------------------------------------------------------- +// SWEEP/EXTRUDE PATHS +//------------------------------------------------------------------- + +class LLPath +{ +public: + class PathPt + { + public: + LLMatrix4a mRot; + LLVector4a mPos; + + LLVector4a mScale; + F32 mTexT; + F32 pad[3]; //for alignment + PathPt() + { + mPos.clear(); + mTexT = 0; + mScale.clear(); + mRot.setRows(LLVector4a(1,0,0,0), + LLVector4a(0,1,0,0), + LLVector4a(0,0,1,0)); + + //distinguished data in the pad for debugging + pad[0] = 3.14159f; + pad[1] = -3.14159f; + pad[2] = 0.585f; + } + }; + +public: + LLPath() + : mOpen(false), + mTotal(0), + mDirty(true), + mStep(1) + { + } + + virtual ~LLPath(); + + static S32 getNumPoints(const LLPathParams& params, F32 detail); + static S32 getNumNGonPoints(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f); + + void genNGon(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f); + virtual bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0, + bool is_sculpted = false, S32 sculpt_size = 0); + + bool isOpen() const { return mOpen; } + F32 getStep() const { return mStep; } + void setDirty() { mDirty = true; } + + S32 getPathLength() const { return (S32)mPath.size(); } + + void resizePath(S32 length) { mPath.resize(length); } + + friend std::ostream& operator<<(std::ostream &s, const LLPath &path); + +public: + LLAlignedArray<PathPt, 64> mPath; + +protected: + bool mOpen; + S32 mTotal; + bool mDirty; + F32 mStep; +}; + +class LLDynamicPath : public LLPath +{ +public: + LLDynamicPath() : LLPath() { } + /*virtual*/ bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0, + bool is_sculpted = false, S32 sculpt_size = 0); +}; + +// Yet another "face" class - caches volume-specific, but not instance-specific data for faces) +class LLVolumeFace +{ +public: + class VertexData + { + enum + { + POSITION = 0, + NORMAL = 1 + }; + + private: + void init(); + public: + VertexData(); + VertexData(const VertexData& rhs); + const VertexData& operator=(const VertexData& rhs); + + ~VertexData(); + LLVector4a& getPosition(); + LLVector4a& getNormal(); + const LLVector4a& getPosition() const; + const LLVector4a& getNormal() const; + void setPosition(const LLVector4a& pos); + void setNormal(const LLVector4a& norm); + + + LLVector2 mTexCoord; + + bool operator<(const VertexData& rhs) const; + bool operator==(const VertexData& rhs) const; + bool compareNormal(const VertexData& rhs, F32 angle_cutoff) const; + + private: + LLVector4a* mData; + }; + + LLVolumeFace(); + LLVolumeFace(const LLVolumeFace& src); + LLVolumeFace& operator=(const LLVolumeFace& rhs); + + ~LLVolumeFace(); +private: + void freeData(); +public: + + bool create(LLVolume* volume, bool partial_build = false); + void createTangents(); + + void resizeVertices(S32 num_verts); + void allocateTangents(S32 num_verts); + void allocateWeights(S32 num_verts); + void allocateJointIndices(S32 num_verts); + void resizeIndices(S32 num_indices); + void fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx); + + void pushVertex(const VertexData& cv); + void pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc); + void pushIndex(const U16& idx); + + void swapData(LLVolumeFace& rhs); + + void getVertexData(U16 indx, LLVolumeFace::VertexData& cv); + + class VertexMapData : public LLVolumeFace::VertexData + { + public: + U16 mIndex; + + bool operator==(const LLVolumeFace::VertexData& rhs) const; + + struct ComparePosition + { + bool operator()(const LLVector3& a, const LLVector3& b) const; + }; + + typedef std::map<LLVector3, std::vector<VertexMapData>, VertexMapData::ComparePosition > PointMap; + }; + + // Eliminates non unique triangles, takes positions, + // normals and texture coordinates into account. + void remap(); + + void optimize(F32 angle_cutoff = 2.f); + bool cacheOptimize(bool gen_tangents = false); + + void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f)); + void destroyOctree(); + // Get a reference to the octree, which may be null + const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const; + + enum + { + SINGLE_MASK = 0x0001, + CAP_MASK = 0x0002, + END_MASK = 0x0004, + SIDE_MASK = 0x0008, + INNER_MASK = 0x0010, + OUTER_MASK = 0x0020, + HOLLOW_MASK = 0x0040, + OPEN_MASK = 0x0080, + FLAT_MASK = 0x0100, + TOP_MASK = 0x0200, + BOTTOM_MASK = 0x0400 + }; + +public: + S32 mID; + U32 mTypeMask; + + // Only used for INNER/OUTER faces + S32 mBeginS; + S32 mBeginT; + S32 mNumS; + S32 mNumT; + + LLVector4a* mExtents; //minimum and maximum point of face + LLVector4a* mCenter; + LLVector2 mTexCoordExtents[2]; //minimum and maximum of texture coordinates of the face. + + S32 mNumVertices; // num vertices == num normals == num texcoords + S32 mNumAllocatedVertices; + S32 mNumIndices; + + LLVector4a* mPositions; // Contains vertices, nortmals and texcoords + LLVector4a* mNormals; // pointer into mPositions + LLVector4a* mTangents; + LLVector2* mTexCoords; // pointer into mPositions + + // mIndices contains mNumIndices amount of elements. + // It contains triangles, each 3 indices describe one triangle. + // If mIndices contains {0, 2, 3, 1, 2, 4}, it means there + // are two triangles {0, 2, 3} and {1, 2, 4} with values being + // indexes for mPositions/mNormals/mTexCoords + U16* mIndices; + + std::vector<S32> mEdge; + + //list of skin weights for rigged volumes + // format is mWeights[vertex_index].mV[influence] = <joint_index>.<weight> + // mWeights.size() should be empty or match mVertices.size() + LLVector4a* mWeights; + +#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS + LLVector4a* mJustWeights; + U8* mJointIndices; +#endif + + mutable bool mWeightsScrubbed; + + // Which joints are rigged to, and the bounding box of any rigged + // vertices per joint. + LLJointRiggingInfoTab mJointRiggingInfoTab; + + //whether or not face has been cache optimized + bool mOptimized; + + // if this is a mesh asset, scale and translation that were applied + // when encoding the source mesh into a unit cube + // used for regenerating tangents + LLVector3 mNormalizedScale = LLVector3(1,1,1); + +private: + LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree; + LLVolumeTriangle* mOctreeTriangles; + + bool createUnCutCubeCap(LLVolume* volume, bool partial_build = false); + bool createCap(LLVolume* volume, bool partial_build = false); + bool createSide(LLVolume* volume, bool partial_build = false); +}; + +class LLVolume : public LLRefCount +{ + friend class LLVolumeLODGroup; + +protected: + virtual ~LLVolume(); // use unref + +public: + typedef std::vector<LLVolumeFace> face_list_t; + + struct FaceParams + { + LLFaceID mFaceID; + S32 mBeginS; + S32 mCountS; + S32 mBeginT; + S32 mCountT; + }; + + LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool generate_single_face = false, const bool is_unique = false); + + U8 getProfileType() const { return mParams.getProfileParams().getCurveType(); } + U8 getPathType() const { return mParams.getPathParams().getCurveType(); } + S32 getNumFaces() const; + S32 getNumVolumeFaces() const { return mVolumeFaces.size(); } + F32 getDetail() const { return mDetail; } + F32 getSurfaceArea() const { return mSurfaceArea; } + const LLVolumeParams& getParams() const { return mParams; } + LLVolumeParams getCopyOfParams() const { return mParams; } + const LLProfile& getProfile() const { return *mProfilep; } + LLPath& getPath() const { return *mPathp; } + void resizePath(S32 length); + const LLAlignedArray<LLVector4a,64>& getMesh() const { return mMesh; } + const LLVector4a& getMeshPt(const U32 i) const { return mMesh[i]; } + + + void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); } + + void regen(); + void genTangents(S32 face); + + bool isConvex() const; + bool isCap(S32 face); + bool isFlat(S32 face); + bool isUnique() const { return mUnique; } + + S32 getSculptLevel() const { return mSculptLevel; } + void setSculptLevel(S32 level) { mSculptLevel = level; } + + + static void getLoDTriangleCounts(const LLVolumeParams& params, S32* counts); + + S32 getNumTriangles(S32* vcount = nullptr) const; + + void generateSilhouetteVertices(std::vector<LLVector3> &vertices, + std::vector<LLVector3> &normals, + const LLVector3& view_vec, + const LLMatrix4& mat, + const LLMatrix3& norm_mat, + S32 face_index); + + //get the face index of the face that intersects with the given line segment at the point + //closest to start. Moves end to the point of intersection. Returns -1 if no intersection. + //Line segment must be in volume space. + S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + S32 face = -1, // which face to check, -1 = ALL_SIDES + LLVector4a* intersection = nullptr, // return the intersection point + LLVector2* tex_coord = nullptr, // return the texture coordinates of the intersection point + LLVector4a* normal = nullptr, // return the surface normal at the intersection point + LLVector4a* tangent = nullptr // return the surface tangent at the intersection point + ); + + LLFaceID generateFaceMask(); + + bool isFaceMaskValid(LLFaceID face_mask); + static S32 sNumMeshPoints; + + friend std::ostream& operator<<(std::ostream &s, const LLVolume &volume); + friend std::ostream& operator<<(std::ostream &s, const LLVolume *volumep); // HACK to bypass Windoze confusion over + // conversion if *(LLVolume*) to LLVolume& + const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE + + LLVolumeFace &getVolumeFace(const S32 f) {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE + + face_list_t& getVolumeFaces() { return mVolumeFaces; } + + U32 mFaceMask; // bit array of which faces exist in this volume + LLVector3 mLODScaleBias; // vector for biasing LOD based on scale + + void sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level, bool visible_placeholder); + void copyVolumeFaces(const LLVolume* volume); + void copyFacesTo(std::vector<LLVolumeFace> &faces) const; + void copyFacesFrom(const std::vector<LLVolumeFace> &faces); + + // use meshoptimizer to optimize index buffer for vertex shader cache + // gen_tangents - if true, generate MikkTSpace tangents if needed before optimizing index buffer + bool cacheOptimize(bool gen_tangents = false); + +private: + void sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type); + F32 sculptGetSurfaceArea(); + void sculptGenerateEmptyPlaceholder(); + void sculptGenerateSpherePlaceholder(); + +protected: + bool generate(); + void createVolumeFaces(); +public: + bool unpackVolumeFaces(std::istream& is, S32 size); + bool unpackVolumeFaces(U8* in_data, S32 size); +private: + bool unpackVolumeFacesInternal(const LLSD& mdl); + +public: + virtual void setMeshAssetLoaded(bool loaded); + virtual bool isMeshAssetLoaded(); + virtual void setMeshAssetUnavaliable(bool unavaliable); + virtual bool isMeshAssetUnavaliable(); + + protected: + bool mUnique; + F32 mDetail; + S32 mSculptLevel; + F32 mSurfaceArea; //unscaled surface area + bool mIsMeshAssetLoaded; + bool mIsMeshAssetUnavaliable; + + const LLVolumeParams mParams; + LLPath *mPathp; + LLProfile *mProfilep; + LLAlignedArray<LLVector4a,64> mMesh; + + + bool mGenerateSingleFace; + face_list_t mVolumeFaces; + +public: + LLVector4a* mHullPoints; + U16* mHullIndices; + S32 mNumHullPoints; + S32 mNumHullIndices; +}; + +std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params); + +bool LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size); +bool LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size); +bool LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size); + +bool LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, + F32& intersection_a, F32& intersection_b, F32& intersection_t); +bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, + F32& intersection_a, F32& intersection_b, F32& intersection_t); + +#endif diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp index f796fbf551..bb0c94d513 100644 --- a/indra/llmath/llvolumemgr.cpp +++ b/indra/llmath/llvolumemgr.cpp @@ -1,408 +1,408 @@ -/**
- * @file llvolumemgr.cpp
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llvolumemgr.h"
-#include "llvolume.h"
-
-
-const F32 BASE_THRESHOLD = 0.03f;
-
-//static
-F32 LLVolumeLODGroup::mDetailThresholds[NUM_LODS] = {BASE_THRESHOLD,
- 2*BASE_THRESHOLD,
- 8*BASE_THRESHOLD,
- 100*BASE_THRESHOLD};
-
-//static
-F32 LLVolumeLODGroup::mDetailScales[NUM_LODS] = {1.f, 1.5f, 2.5f, 4.f};
-
-
-//============================================================================
-
-LLVolumeMgr::LLVolumeMgr()
-: mDataMutex(NULL)
-{
- // the LLMutex magic interferes with easy unit testing,
- // so you now must manually call useMutex() to use it
- //mDataMutex = new LLMutex();
-}
-
-LLVolumeMgr::~LLVolumeMgr()
-{
- cleanup();
-
- delete mDataMutex;
- mDataMutex = NULL;
-}
-
-bool LLVolumeMgr::cleanup()
-{
- bool no_refs = true;
- if (mDataMutex)
- {
- mDataMutex->lock();
- }
- for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(),
- end = mVolumeLODGroups.end();
- iter != end; iter++)
- {
- LLVolumeLODGroup *volgroupp = iter->second;
- if (!volgroupp->cleanupRefs())
- {
- no_refs = false;
- }
- delete volgroupp;
- }
- mVolumeLODGroups.clear();
- if (mDataMutex)
- {
- mDataMutex->unlock();
- }
- return no_refs;
-}
-
-// Always only ever store the results of refVolume in a LLPointer
-// Note however that LLVolumeLODGroup that contains the volume
-// also holds a LLPointer so the volume will only go away after
-// anything holding the volume and the LODGroup are destroyed
-LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 lod)
-{
- LLVolumeLODGroup* volgroupp;
- if (mDataMutex)
- {
- mDataMutex->lock();
- }
- volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(&volume_params);
- if( iter == mVolumeLODGroups.end() )
- {
- volgroupp = createNewGroup(volume_params);
- }
- else
- {
- volgroupp = iter->second;
- }
- if (mDataMutex)
- {
- mDataMutex->unlock();
- }
- return volgroupp->refLOD(lod);
-}
-
-// virtual
-LLVolumeLODGroup* LLVolumeMgr::getGroup( const LLVolumeParams& volume_params ) const
-{
- LLVolumeLODGroup* volgroupp = NULL;
- if (mDataMutex)
- {
- mDataMutex->lock();
- }
- volume_lod_group_map_t::const_iterator iter = mVolumeLODGroups.find(&volume_params);
- if( iter != mVolumeLODGroups.end() )
- {
- volgroupp = iter->second;
- }
- if (mDataMutex)
- {
- mDataMutex->unlock();
- }
- return volgroupp;
-}
-
-void LLVolumeMgr::unrefVolume(LLVolume *volumep)
-{
- if (volumep->isUnique())
- {
- // TomY: Don't need to manage this volume. It is a unique instance.
- return;
- }
- const LLVolumeParams* params = &(volumep->getParams());
- if (mDataMutex)
- {
- mDataMutex->lock();
- }
- volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(params);
- if( iter == mVolumeLODGroups.end() )
- {
- LL_ERRS() << "Warning! Tried to cleanup unknown volume type! " << *params << LL_ENDL;
- if (mDataMutex)
- {
- mDataMutex->unlock();
- }
- return;
- }
- else
- {
- LLVolumeLODGroup* volgroupp = iter->second;
-
- volgroupp->derefLOD(volumep);
- if (volgroupp->getNumRefs() == 0)
- {
- mVolumeLODGroups.erase(params);
- delete volgroupp;
- }
- }
- if (mDataMutex)
- {
- mDataMutex->unlock();
- }
-
-}
-
-// protected
-void LLVolumeMgr::insertGroup(LLVolumeLODGroup* volgroup)
-{
- mVolumeLODGroups[volgroup->getVolumeParams()] = volgroup;
-}
-
-// protected
-LLVolumeLODGroup* LLVolumeMgr::createNewGroup(const LLVolumeParams& volume_params)
-{
- LLVolumeLODGroup* volgroup = new LLVolumeLODGroup(volume_params);
- insertGroup(volgroup);
- return volgroup;
-}
-
-// virtual
-void LLVolumeMgr::dump()
-{
- F32 avg = 0.f;
- if (mDataMutex)
- {
- mDataMutex->lock();
- }
- for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(),
- end = mVolumeLODGroups.end();
- iter != end; iter++)
- {
- LLVolumeLODGroup *volgroupp = iter->second;
- avg += volgroupp->dump();
- }
- int count = (int)mVolumeLODGroups.size();
- avg = count ? avg / (F32)count : 0.0f;
- if (mDataMutex)
- {
- mDataMutex->unlock();
- }
- LL_INFOS() << "Average usage of LODs " << avg << LL_ENDL;
-}
-
-void LLVolumeMgr::useMutex()
-{
- if (!mDataMutex)
- {
- mDataMutex = new LLMutex();
- }
-}
-
-std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr)
-{
- s << "{ numLODgroups=" << volume_mgr.mVolumeLODGroups.size() << ", ";
-
- S32 total_refs = 0;
- if (volume_mgr.mDataMutex)
- {
- volume_mgr.mDataMutex->lock();
- }
-
- for (LLVolumeMgr::volume_lod_group_map_t::const_iterator iter = volume_mgr.mVolumeLODGroups.begin();
- iter != volume_mgr.mVolumeLODGroups.end(); ++iter)
- {
- LLVolumeLODGroup *volgroupp = iter->second;
- total_refs += volgroupp->getNumRefs();
- s << ", " << (*volgroupp);
- }
-
- if (volume_mgr.mDataMutex)
- {
- volume_mgr.mDataMutex->unlock();
- }
-
- s << ", total_refs=" << total_refs << " }";
- return s;
-}
-
-LLVolumeLODGroup::LLVolumeLODGroup(const LLVolumeParams ¶ms)
- : mVolumeParams(params),
- mRefs(0)
-{
- for (S32 i = 0; i < NUM_LODS; i++)
- {
- mLODRefs[i] = 0;
- mAccessCount[i] = 0;
- }
-}
-
-LLVolumeLODGroup::~LLVolumeLODGroup()
-{
- for (S32 i = 0; i < NUM_LODS; i++)
- {
- llassert_always(mLODRefs[i] == 0);
- }
-}
-
-// Called from LLVolumeMgr::cleanup
-bool LLVolumeLODGroup::cleanupRefs()
-{
- bool res = true;
- if (mRefs != 0)
- {
- LL_WARNS() << "Volume group has remaining refs:" << getNumRefs() << LL_ENDL;
- mRefs = 0;
- for (S32 i = 0; i < NUM_LODS; i++)
- {
- if (mLODRefs[i] > 0)
- {
- LL_WARNS() << " LOD " << i << " refs = " << mLODRefs[i] << LL_ENDL;
- mLODRefs[i] = 0;
- mVolumeLODs[i] = NULL;
- }
- }
- LL_WARNS() << *getVolumeParams() << LL_ENDL;
- res = false;
- }
- return res;
-}
-
-LLVolume* LLVolumeLODGroup::refLOD(const S32 lod)
-{
- llassert(lod >=0 && lod < NUM_LODS);
- mAccessCount[lod]++;
-
- mRefs++;
- if (mVolumeLODs[lod].isNull())
- {
- mVolumeLODs[lod] = new LLVolume(mVolumeParams, mDetailScales[lod]);
- }
- mLODRefs[lod]++;
- return mVolumeLODs[lod];
-}
-
-bool LLVolumeLODGroup::derefLOD(LLVolume *volumep)
-{
- llassert_always(mRefs > 0);
- mRefs--;
- for (S32 i = 0; i < NUM_LODS; i++)
- {
- if (mVolumeLODs[i] == volumep)
- {
- llassert_always(mLODRefs[i] > 0);
- mLODRefs[i]--;
-#if 0 // SJB: Possible opt: keep other lods around
- if (!mLODRefs[i])
- {
- mVolumeLODs[i] = NULL;
- }
-#endif
- return true;
- }
- }
- LL_ERRS() << "Deref of non-matching LOD in volume LOD group" << LL_ENDL;
- return false;
-}
-
-S32 LLVolumeLODGroup::getDetailFromTan(const F32 tan_angle)
-{
- S32 i = 0;
- while (i < (NUM_LODS - 1))
- {
- if (tan_angle <= mDetailThresholds[i])
- {
- return i;
- }
- i++;
- }
- return NUM_LODS - 1;
-}
-
-void LLVolumeLODGroup::getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher)
-{
- S32 detail = getDetailFromTan(tan_angle);
-
- if (detail > 0)
- {
- to_lower = tan_angle - mDetailThresholds[detail];
- }
- else
- {
- to_lower = 1024.f*1024.f;
- }
-
- if (detail < NUM_LODS-1)
- {
- to_higher = mDetailThresholds[detail+1] - tan_angle;
- }
- else
- {
- to_higher = 1024.f*1024.f;
- }
-}
-
-F32 LLVolumeLODGroup::getVolumeScaleFromDetail(const S32 detail)
-{
- return mDetailScales[detail];
-}
-
-S32 LLVolumeLODGroup::getVolumeDetailFromScale(const F32 detail)
-{
- for (S32 i = 1; i < 4; i++)
- {
- if (mDetailScales[i] > detail)
- {
- return i-1;
- }
- }
-
- return 3;
-}
-
-F32 LLVolumeLODGroup::dump()
-{
- F32 usage = 0.f;
- for (S32 i = 0; i < NUM_LODS; i++)
- {
- if (mAccessCount[i] > 0)
- {
- usage += 1.f;
- }
- }
- usage = usage / (F32)NUM_LODS;
-
- std::string dump_str = llformat("%.3f %d %d %d %d", usage, mAccessCount[0], mAccessCount[1], mAccessCount[2], mAccessCount[3]);
-
- LL_INFOS() << dump_str << LL_ENDL;
- return usage;
-}
-
-std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup)
-{
- s << "{ numRefs=" << volgroup.getNumRefs();
- s << ", mParams=" << volgroup.getVolumeParams();
- s << " }";
-
- return s;
-}
-
+/** + * @file llvolumemgr.cpp + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llvolumemgr.h" +#include "llvolume.h" + + +const F32 BASE_THRESHOLD = 0.03f; + +//static +F32 LLVolumeLODGroup::mDetailThresholds[NUM_LODS] = {BASE_THRESHOLD, + 2*BASE_THRESHOLD, + 8*BASE_THRESHOLD, + 100*BASE_THRESHOLD}; + +//static +F32 LLVolumeLODGroup::mDetailScales[NUM_LODS] = {1.f, 1.5f, 2.5f, 4.f}; + + +//============================================================================ + +LLVolumeMgr::LLVolumeMgr() +: mDataMutex(NULL) +{ + // the LLMutex magic interferes with easy unit testing, + // so you now must manually call useMutex() to use it + //mDataMutex = new LLMutex(); +} + +LLVolumeMgr::~LLVolumeMgr() +{ + cleanup(); + + delete mDataMutex; + mDataMutex = NULL; +} + +bool LLVolumeMgr::cleanup() +{ + bool no_refs = true; + if (mDataMutex) + { + mDataMutex->lock(); + } + for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(), + end = mVolumeLODGroups.end(); + iter != end; iter++) + { + LLVolumeLODGroup *volgroupp = iter->second; + if (!volgroupp->cleanupRefs()) + { + no_refs = false; + } + delete volgroupp; + } + mVolumeLODGroups.clear(); + if (mDataMutex) + { + mDataMutex->unlock(); + } + return no_refs; +} + +// Always only ever store the results of refVolume in a LLPointer +// Note however that LLVolumeLODGroup that contains the volume +// also holds a LLPointer so the volume will only go away after +// anything holding the volume and the LODGroup are destroyed +LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 lod) +{ + LLVolumeLODGroup* volgroupp; + if (mDataMutex) + { + mDataMutex->lock(); + } + volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(&volume_params); + if( iter == mVolumeLODGroups.end() ) + { + volgroupp = createNewGroup(volume_params); + } + else + { + volgroupp = iter->second; + } + if (mDataMutex) + { + mDataMutex->unlock(); + } + return volgroupp->refLOD(lod); +} + +// virtual +LLVolumeLODGroup* LLVolumeMgr::getGroup( const LLVolumeParams& volume_params ) const +{ + LLVolumeLODGroup* volgroupp = NULL; + if (mDataMutex) + { + mDataMutex->lock(); + } + volume_lod_group_map_t::const_iterator iter = mVolumeLODGroups.find(&volume_params); + if( iter != mVolumeLODGroups.end() ) + { + volgroupp = iter->second; + } + if (mDataMutex) + { + mDataMutex->unlock(); + } + return volgroupp; +} + +void LLVolumeMgr::unrefVolume(LLVolume *volumep) +{ + if (volumep->isUnique()) + { + // TomY: Don't need to manage this volume. It is a unique instance. + return; + } + const LLVolumeParams* params = &(volumep->getParams()); + if (mDataMutex) + { + mDataMutex->lock(); + } + volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(params); + if( iter == mVolumeLODGroups.end() ) + { + LL_ERRS() << "Warning! Tried to cleanup unknown volume type! " << *params << LL_ENDL; + if (mDataMutex) + { + mDataMutex->unlock(); + } + return; + } + else + { + LLVolumeLODGroup* volgroupp = iter->second; + + volgroupp->derefLOD(volumep); + if (volgroupp->getNumRefs() == 0) + { + mVolumeLODGroups.erase(params); + delete volgroupp; + } + } + if (mDataMutex) + { + mDataMutex->unlock(); + } + +} + +// protected +void LLVolumeMgr::insertGroup(LLVolumeLODGroup* volgroup) +{ + mVolumeLODGroups[volgroup->getVolumeParams()] = volgroup; +} + +// protected +LLVolumeLODGroup* LLVolumeMgr::createNewGroup(const LLVolumeParams& volume_params) +{ + LLVolumeLODGroup* volgroup = new LLVolumeLODGroup(volume_params); + insertGroup(volgroup); + return volgroup; +} + +// virtual +void LLVolumeMgr::dump() +{ + F32 avg = 0.f; + if (mDataMutex) + { + mDataMutex->lock(); + } + for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(), + end = mVolumeLODGroups.end(); + iter != end; iter++) + { + LLVolumeLODGroup *volgroupp = iter->second; + avg += volgroupp->dump(); + } + int count = (int)mVolumeLODGroups.size(); + avg = count ? avg / (F32)count : 0.0f; + if (mDataMutex) + { + mDataMutex->unlock(); + } + LL_INFOS() << "Average usage of LODs " << avg << LL_ENDL; +} + +void LLVolumeMgr::useMutex() +{ + if (!mDataMutex) + { + mDataMutex = new LLMutex(); + } +} + +std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr) +{ + s << "{ numLODgroups=" << volume_mgr.mVolumeLODGroups.size() << ", "; + + S32 total_refs = 0; + if (volume_mgr.mDataMutex) + { + volume_mgr.mDataMutex->lock(); + } + + for (LLVolumeMgr::volume_lod_group_map_t::const_iterator iter = volume_mgr.mVolumeLODGroups.begin(); + iter != volume_mgr.mVolumeLODGroups.end(); ++iter) + { + LLVolumeLODGroup *volgroupp = iter->second; + total_refs += volgroupp->getNumRefs(); + s << ", " << (*volgroupp); + } + + if (volume_mgr.mDataMutex) + { + volume_mgr.mDataMutex->unlock(); + } + + s << ", total_refs=" << total_refs << " }"; + return s; +} + +LLVolumeLODGroup::LLVolumeLODGroup(const LLVolumeParams ¶ms) + : mVolumeParams(params), + mRefs(0) +{ + for (S32 i = 0; i < NUM_LODS; i++) + { + mLODRefs[i] = 0; + mAccessCount[i] = 0; + } +} + +LLVolumeLODGroup::~LLVolumeLODGroup() +{ + for (S32 i = 0; i < NUM_LODS; i++) + { + llassert_always(mLODRefs[i] == 0); + } +} + +// Called from LLVolumeMgr::cleanup +bool LLVolumeLODGroup::cleanupRefs() +{ + bool res = true; + if (mRefs != 0) + { + LL_WARNS() << "Volume group has remaining refs:" << getNumRefs() << LL_ENDL; + mRefs = 0; + for (S32 i = 0; i < NUM_LODS; i++) + { + if (mLODRefs[i] > 0) + { + LL_WARNS() << " LOD " << i << " refs = " << mLODRefs[i] << LL_ENDL; + mLODRefs[i] = 0; + mVolumeLODs[i] = NULL; + } + } + LL_WARNS() << *getVolumeParams() << LL_ENDL; + res = false; + } + return res; +} + +LLVolume* LLVolumeLODGroup::refLOD(const S32 lod) +{ + llassert(lod >=0 && lod < NUM_LODS); + mAccessCount[lod]++; + + mRefs++; + if (mVolumeLODs[lod].isNull()) + { + mVolumeLODs[lod] = new LLVolume(mVolumeParams, mDetailScales[lod]); + } + mLODRefs[lod]++; + return mVolumeLODs[lod]; +} + +bool LLVolumeLODGroup::derefLOD(LLVolume *volumep) +{ + llassert_always(mRefs > 0); + mRefs--; + for (S32 i = 0; i < NUM_LODS; i++) + { + if (mVolumeLODs[i] == volumep) + { + llassert_always(mLODRefs[i] > 0); + mLODRefs[i]--; +#if 0 // SJB: Possible opt: keep other lods around + if (!mLODRefs[i]) + { + mVolumeLODs[i] = NULL; + } +#endif + return true; + } + } + LL_ERRS() << "Deref of non-matching LOD in volume LOD group" << LL_ENDL; + return false; +} + +S32 LLVolumeLODGroup::getDetailFromTan(const F32 tan_angle) +{ + S32 i = 0; + while (i < (NUM_LODS - 1)) + { + if (tan_angle <= mDetailThresholds[i]) + { + return i; + } + i++; + } + return NUM_LODS - 1; +} + +void LLVolumeLODGroup::getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher) +{ + S32 detail = getDetailFromTan(tan_angle); + + if (detail > 0) + { + to_lower = tan_angle - mDetailThresholds[detail]; + } + else + { + to_lower = 1024.f*1024.f; + } + + if (detail < NUM_LODS-1) + { + to_higher = mDetailThresholds[detail+1] - tan_angle; + } + else + { + to_higher = 1024.f*1024.f; + } +} + +F32 LLVolumeLODGroup::getVolumeScaleFromDetail(const S32 detail) +{ + return mDetailScales[detail]; +} + +S32 LLVolumeLODGroup::getVolumeDetailFromScale(const F32 detail) +{ + for (S32 i = 1; i < 4; i++) + { + if (mDetailScales[i] > detail) + { + return i-1; + } + } + + return 3; +} + +F32 LLVolumeLODGroup::dump() +{ + F32 usage = 0.f; + for (S32 i = 0; i < NUM_LODS; i++) + { + if (mAccessCount[i] > 0) + { + usage += 1.f; + } + } + usage = usage / (F32)NUM_LODS; + + std::string dump_str = llformat("%.3f %d %d %d %d", usage, mAccessCount[0], mAccessCount[1], mAccessCount[2], mAccessCount[3]); + + LL_INFOS() << dump_str << LL_ENDL; + return usage; +} + +std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup) +{ + s << "{ numRefs=" << volgroup.getNumRefs(); + s << ", mParams=" << volgroup.getVolumeParams(); + s << " }"; + + return s; +} + diff --git a/indra/llmath/llvolumemgr.h b/indra/llmath/llvolumemgr.h index daeaf378d2..2e0ce3e88a 100644 --- a/indra/llmath/llvolumemgr.h +++ b/indra/llmath/llvolumemgr.h @@ -1,112 +1,112 @@ -/**
- * @file llvolumemgr.h
- * @brief LLVolumeMgr class.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLVOLUMEMGR_H
-#define LL_LLVOLUMEMGR_H
-
-#include <map>
-
-#include "llvolume.h"
-#include "llpointer.h"
-#include "llthread.h"
-
-class LLVolumeParams;
-class LLVolumeLODGroup;
-
-class LLVolumeLODGroup
-{
- LOG_CLASS(LLVolumeLODGroup);
-
-public:
- enum
- {
- NUM_LODS = 4
- };
-
- LLVolumeLODGroup(const LLVolumeParams ¶ms);
- ~LLVolumeLODGroup();
- bool cleanupRefs();
-
- static S32 getDetailFromTan(const F32 tan_angle);
- static void getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher);
- static F32 getVolumeScaleFromDetail(const S32 detail);
- static S32 getVolumeDetailFromScale(F32 scale);
-
- LLVolume* refLOD(const S32 detail);
- bool derefLOD(LLVolume *volumep);
- S32 getNumRefs() const { return mRefs; }
-
- const LLVolumeParams* getVolumeParams() const { return &mVolumeParams; };
-
- F32 dump();
- friend std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup);
-
-protected:
- LLVolumeParams mVolumeParams;
-
- S32 mRefs;
- S32 mLODRefs[NUM_LODS];
- LLPointer<LLVolume> mVolumeLODs[NUM_LODS];
- static F32 mDetailThresholds[NUM_LODS];
- static F32 mDetailScales[NUM_LODS];
- S32 mAccessCount[NUM_LODS];
-};
-
-class LLVolumeMgr
-{
-public:
- LLVolumeMgr();
- virtual ~LLVolumeMgr();
- bool cleanup(); // Cleanup all volumes being managed, returns true if no dangling references
-
- virtual LLVolumeLODGroup* getGroup( const LLVolumeParams& volume_params ) const;
-
- // whatever calls getVolume() never owns the LLVolume* and
- // cannot keep references for long since it may be deleted
- // later. For best results hold it in an LLPointer<LLVolume>.
- virtual LLVolume *refVolume(const LLVolumeParams &volume_params, const S32 detail);
- virtual void unrefVolume(LLVolume *volumep);
-
- void dump();
-
- // manually call this for mutex magic
- void useMutex();
-
- friend std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr);
-
-protected:
- void insertGroup(LLVolumeLODGroup* volgroup);
- // Overridden in llphysics/abstract/utils/llphysicsvolumemanager.h
- virtual LLVolumeLODGroup* createNewGroup(const LLVolumeParams& volume_params);
-
-protected:
- typedef std::map<const LLVolumeParams*, LLVolumeLODGroup*, LLVolumeParams::compare> volume_lod_group_map_t;
- volume_lod_group_map_t mVolumeLODGroups;
-
- LLMutex* mDataMutex;
-};
-
-#endif // LL_LLVOLUMEMGR_H
+/** + * @file llvolumemgr.h + * @brief LLVolumeMgr class. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLVOLUMEMGR_H +#define LL_LLVOLUMEMGR_H + +#include <map> + +#include "llvolume.h" +#include "llpointer.h" +#include "llthread.h" + +class LLVolumeParams; +class LLVolumeLODGroup; + +class LLVolumeLODGroup +{ + LOG_CLASS(LLVolumeLODGroup); + +public: + enum + { + NUM_LODS = 4 + }; + + LLVolumeLODGroup(const LLVolumeParams ¶ms); + ~LLVolumeLODGroup(); + bool cleanupRefs(); + + static S32 getDetailFromTan(const F32 tan_angle); + static void getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher); + static F32 getVolumeScaleFromDetail(const S32 detail); + static S32 getVolumeDetailFromScale(F32 scale); + + LLVolume* refLOD(const S32 detail); + bool derefLOD(LLVolume *volumep); + S32 getNumRefs() const { return mRefs; } + + const LLVolumeParams* getVolumeParams() const { return &mVolumeParams; }; + + F32 dump(); + friend std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup); + +protected: + LLVolumeParams mVolumeParams; + + S32 mRefs; + S32 mLODRefs[NUM_LODS]; + LLPointer<LLVolume> mVolumeLODs[NUM_LODS]; + static F32 mDetailThresholds[NUM_LODS]; + static F32 mDetailScales[NUM_LODS]; + S32 mAccessCount[NUM_LODS]; +}; + +class LLVolumeMgr +{ +public: + LLVolumeMgr(); + virtual ~LLVolumeMgr(); + bool cleanup(); // Cleanup all volumes being managed, returns true if no dangling references + + virtual LLVolumeLODGroup* getGroup( const LLVolumeParams& volume_params ) const; + + // whatever calls getVolume() never owns the LLVolume* and + // cannot keep references for long since it may be deleted + // later. For best results hold it in an LLPointer<LLVolume>. + virtual LLVolume *refVolume(const LLVolumeParams &volume_params, const S32 detail); + virtual void unrefVolume(LLVolume *volumep); + + void dump(); + + // manually call this for mutex magic + void useMutex(); + + friend std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr); + +protected: + void insertGroup(LLVolumeLODGroup* volgroup); + // Overridden in llphysics/abstract/utils/llphysicsvolumemanager.h + virtual LLVolumeLODGroup* createNewGroup(const LLVolumeParams& volume_params); + +protected: + typedef std::map<const LLVolumeParams*, LLVolumeLODGroup*, LLVolumeParams::compare> volume_lod_group_map_t; + volume_lod_group_map_t mVolumeLODGroups; + + LLMutex* mDataMutex; +}; + +#endif // LL_LLVOLUMEMGR_H diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp index ebac5522e7..74d496fa02 100644 --- a/indra/llmath/llvolumeoctree.cpp +++ b/indra/llmath/llvolumeoctree.cpp @@ -1,273 +1,273 @@ -/**
-
- * @file llvolumeoctree.cpp
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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) == 0;
-}
-
-LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
-{
- node->addListener(this);
-}
-
-LLVolumeOctreeListener::~LLVolumeOctreeListener()
-{
-
-}
-
-void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent,
- LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child)
-{
- new LLVolumeOctreeListener(child);
-}
-
-LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir,
- const LLVolumeFace* face, F32* closest_t,
- LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
- : mFace(face),
- mStart(start),
- mDir(dir),
- mIntersection(intersection),
- mTexCoord(tex_coord),
- mNormal(normal),
- mTangent(tangent),
- mClosestT(closest_t),
- mHitFace(false)
-{
- mEnd.setAdd(mStart, mDir);
-}
-
-void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
-{
- LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0);
-
- 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, LLVolumeTriangle*>* node)
-{
- for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter =
- node->getDataBegin(); iter != node->getDataEnd(); ++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 = intersect;
- }
-
- U32 idx0 = tri->mIndex[0];
- U32 idx1 = tri->mIndex[1];
- U32 idx2 = tri->mIndex[2];
-
- if (mTexCoord != NULL)
- {
- LLVector2* tc = (LLVector2*) mFace->mTexCoords;
- *mTexCoord = ((1.f - a - b) * tc[idx0] +
- a * tc[idx1] +
- b * tc[idx2]);
-
- }
-
- if (mNormal != NULL)
- {
- LLVector4a* norm = mFace->mNormals;
-
- LLVector4a n1,n2,n3;
- n1 = norm[idx0];
- n1.mul(1.f-a-b);
-
- n2 = norm[idx1];
- n2.mul(a);
-
- n3 = norm[idx2];
- n3.mul(b);
-
- n1.add(n2);
- n1.add(n3);
-
- *mNormal = n1;
- }
-
- if (mTangent != NULL)
- {
- LLVector4a* tangents = mFace->mTangents;
-
- LLVector4a t1,t2,t3;
- t1 = tangents[idx0];
- t1.mul(1.f-a-b);
-
- t2 = tangents[idx1];
- t2.mul(a);
-
- t3 = tangents[idx2];
- t3.mul(b);
-
- t1.add(t2);
- t1.add(t3);
-
- *mTangent = t1;
- }
- }
- }
- }
-}
-
-const LLVector4a& LLVolumeTriangle::getPositionGroup() const
-{
- return mPositionGroup;
-}
-
-const F32& LLVolumeTriangle::getBinRadius() const
-{
- return mRadius;
-}
-
-
-//TEST CODE
-
-void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
-{
- LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
-
- //make sure bounds matches extents
- LLVector4a& min = node->mExtents[0];
- LLVector4a& max = node->mExtents[1];
-
- LLVector4a& center = node->mBounds[0];
- LLVector4a& size = node->mBounds[1];
-
- LLVector4a test_min, test_max;
- test_min.setSub(center, size);
- test_max.setAdd(center, size);
-
- if (!test_min.equals3(min, 0.001f) ||
- !test_max.equals3(max, 0.001f))
- {
- LL_ERRS() << "Bad bounding box data found." << LL_ENDL;
- }
-
- test_min.sub(LLVector4a(0.001f));
- test_max.add(LLVector4a(0.001f));
-
- for (U32 i = 0; i < branch->getChildCount(); ++i)
- {
- LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0);
-
- //make sure all children fit inside this node
- if (child->mExtents[0].lessThan(test_min).areAnySet(LLVector4Logical::MASK_XYZ) ||
- child->mExtents[1].greaterThan(test_max).areAnySet(LLVector4Logical::MASK_XYZ))
- {
- LL_ERRS() << "Child protrudes from bounding box." << LL_ENDL;
- }
- }
-
- //children fit, check data
- for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin();
- iter != branch->getDataEnd(); ++iter)
- {
- const LLVolumeTriangle* tri = *iter;
-
- //validate triangle
- for (U32 i = 0; i < 3; i++)
- {
- if (tri->mV[i]->greaterThan(test_max).areAnySet(LLVector4Logical::MASK_XYZ) ||
- tri->mV[i]->lessThan(test_min).areAnySet(LLVector4Logical::MASK_XYZ))
- {
- LL_ERRS() << "Triangle protrudes from node." << LL_ENDL;
- }
- }
- }
-}
-
+/** + + * @file llvolumeoctree.cpp + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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) == 0; +} + +LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node) +{ + node->addListener(this); +} + +LLVolumeOctreeListener::~LLVolumeOctreeListener() +{ + +} + +void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, + LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child) +{ + new LLVolumeOctreeListener(child); +} + +LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, + const LLVolumeFace* face, F32* closest_t, + LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) + : mFace(face), + mStart(start), + mDir(dir), + mIntersection(intersection), + mTexCoord(tex_coord), + mNormal(normal), + mTangent(tangent), + mClosestT(closest_t), + mHitFace(false) +{ + mEnd.setAdd(mStart, mDir); +} + +void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node) +{ + LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0); + + 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, LLVolumeTriangle*>* node) +{ + for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = + node->getDataBegin(); iter != node->getDataEnd(); ++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 = intersect; + } + + U32 idx0 = tri->mIndex[0]; + U32 idx1 = tri->mIndex[1]; + U32 idx2 = tri->mIndex[2]; + + if (mTexCoord != NULL) + { + LLVector2* tc = (LLVector2*) mFace->mTexCoords; + *mTexCoord = ((1.f - a - b) * tc[idx0] + + a * tc[idx1] + + b * tc[idx2]); + + } + + if (mNormal != NULL) + { + LLVector4a* norm = mFace->mNormals; + + LLVector4a n1,n2,n3; + n1 = norm[idx0]; + n1.mul(1.f-a-b); + + n2 = norm[idx1]; + n2.mul(a); + + n3 = norm[idx2]; + n3.mul(b); + + n1.add(n2); + n1.add(n3); + + *mNormal = n1; + } + + if (mTangent != NULL) + { + LLVector4a* tangents = mFace->mTangents; + + LLVector4a t1,t2,t3; + t1 = tangents[idx0]; + t1.mul(1.f-a-b); + + t2 = tangents[idx1]; + t2.mul(a); + + t3 = tangents[idx2]; + t3.mul(b); + + t1.add(t2); + t1.add(t3); + + *mTangent = t1; + } + } + } + } +} + +const LLVector4a& LLVolumeTriangle::getPositionGroup() const +{ + return mPositionGroup; +} + +const F32& LLVolumeTriangle::getBinRadius() const +{ + return mRadius; +} + + +//TEST CODE + +void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch) +{ + LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0); + + //make sure bounds matches extents + LLVector4a& min = node->mExtents[0]; + LLVector4a& max = node->mExtents[1]; + + LLVector4a& center = node->mBounds[0]; + LLVector4a& size = node->mBounds[1]; + + LLVector4a test_min, test_max; + test_min.setSub(center, size); + test_max.setAdd(center, size); + + if (!test_min.equals3(min, 0.001f) || + !test_max.equals3(max, 0.001f)) + { + LL_ERRS() << "Bad bounding box data found." << LL_ENDL; + } + + test_min.sub(LLVector4a(0.001f)); + test_max.add(LLVector4a(0.001f)); + + for (U32 i = 0; i < branch->getChildCount(); ++i) + { + LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0); + + //make sure all children fit inside this node + if (child->mExtents[0].lessThan(test_min).areAnySet(LLVector4Logical::MASK_XYZ) || + child->mExtents[1].greaterThan(test_max).areAnySet(LLVector4Logical::MASK_XYZ)) + { + LL_ERRS() << "Child protrudes from bounding box." << LL_ENDL; + } + } + + //children fit, check data + for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); + iter != branch->getDataEnd(); ++iter) + { + const LLVolumeTriangle* tri = *iter; + + //validate triangle + for (U32 i = 0; i < 3; i++) + { + if (tri->mV[i]->greaterThan(test_max).areAnySet(LLVector4Logical::MASK_XYZ) || + tri->mV[i]->lessThan(test_min).areAnySet(LLVector4Logical::MASK_XYZ)) + { + LL_ERRS() << "Triangle protrudes from node." << LL_ENDL; + } + } + } +} + diff --git a/indra/llmath/m3math.cpp b/indra/llmath/m3math.cpp index e48c47d1ef..472d340af5 100644 --- a/indra/llmath/m3math.cpp +++ b/indra/llmath/m3math.cpp @@ -1,590 +1,590 @@ -/**
- * @file m3math.cpp
- * @brief LLMatrix3 class implementation.
- *
- * $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 "linden_common.h"
-
-//#include "vmath.h"
-#include "v3math.h"
-#include "v3dmath.h"
-#include "v4math.h"
-#include "m4math.h"
-#include "m3math.h"
-#include "llquaternion.h"
-
-// LLMatrix3
-
-// ji
-// LLMatrix3 = |00 01 02 |
-// |10 11 12 |
-// |20 21 22 |
-
-// LLMatrix3 = |fx fy fz | forward-axis
-// |lx ly lz | left-axis
-// |ux uy uz | up-axis
-
-
-// Constructors
-
-
-LLMatrix3::LLMatrix3(const LLQuaternion &q)
-{
- setRot(q);
-}
-
-
-LLMatrix3::LLMatrix3(const F32 angle, const LLVector3 &vec)
-{
- LLQuaternion quat(angle, vec);
- setRot(quat);
-}
-
-LLMatrix3::LLMatrix3(const F32 angle, const LLVector3d &vec)
-{
- LLVector3 vec_f;
- vec_f.setVec(vec);
- LLQuaternion quat(angle, vec_f);
- setRot(quat);
-}
-
-LLMatrix3::LLMatrix3(const F32 angle, const LLVector4 &vec)
-{
- LLQuaternion quat(angle, vec);
- setRot(quat);
-}
-
-LLMatrix3::LLMatrix3(const F32 roll, const F32 pitch, const F32 yaw)
-{
- setRot(roll,pitch,yaw);
-}
-
-// From Matrix and Quaternion FAQ
-void LLMatrix3::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const
-{
- F64 angle_x, angle_y, angle_z;
- F64 cx, cy, cz; // cosine of angle_x, angle_y, angle_z
- F64 sx, sz; // sine of angle_x, angle_y, angle_z
-
- angle_y = asin(llclamp(mMatrix[2][0], -1.f, 1.f));
- cy = cos(angle_y);
-
- if (fabs(cy) > 0.005) // non-zero
- {
- // no gimbal lock
- cx = mMatrix[2][2] / cy;
- sx = - mMatrix[2][1] / cy;
-
- angle_x = (F32) atan2(sx, cx);
-
- cz = mMatrix[0][0] / cy;
- sz = - mMatrix[1][0] / cy;
-
- angle_z = (F32) atan2(sz, cz);
- }
- else
- {
- // yup, gimbal lock
- angle_x = 0;
-
- // some tricky math thereby avoided, see article
-
- cz = mMatrix[1][1];
- sz = mMatrix[0][1];
-
- angle_z = atan2(sz, cz);
- }
-
- *roll = (F32)angle_x;
- *pitch = (F32)angle_y;
- *yaw = (F32)angle_z;
-}
-
-
-// Clear and Assignment Functions
-
-const LLMatrix3& LLMatrix3::setIdentity()
-{
- mMatrix[0][0] = 1.f;
- mMatrix[0][1] = 0.f;
- mMatrix[0][2] = 0.f;
-
- mMatrix[1][0] = 0.f;
- mMatrix[1][1] = 1.f;
- mMatrix[1][2] = 0.f;
-
- mMatrix[2][0] = 0.f;
- mMatrix[2][1] = 0.f;
- mMatrix[2][2] = 1.f;
- return (*this);
-}
-
-const LLMatrix3& LLMatrix3::clear()
-{
- mMatrix[0][0] = 0.f;
- mMatrix[0][1] = 0.f;
- mMatrix[0][2] = 0.f;
-
- mMatrix[1][0] = 0.f;
- mMatrix[1][1] = 0.f;
- mMatrix[1][2] = 0.f;
-
- mMatrix[2][0] = 0.f;
- mMatrix[2][1] = 0.f;
- mMatrix[2][2] = 0.f;
- return (*this);
-}
-
-const LLMatrix3& LLMatrix3::setZero()
-{
- mMatrix[0][0] = 0.f;
- mMatrix[0][1] = 0.f;
- mMatrix[0][2] = 0.f;
-
- mMatrix[1][0] = 0.f;
- mMatrix[1][1] = 0.f;
- mMatrix[1][2] = 0.f;
-
- mMatrix[2][0] = 0.f;
- mMatrix[2][1] = 0.f;
- mMatrix[2][2] = 0.f;
- return (*this);
-}
-
-// various useful mMatrix functions
-
-const LLMatrix3& LLMatrix3::transpose()
-{
- // transpose the matrix
- F32 temp;
- temp = mMatrix[VX][VY]; mMatrix[VX][VY] = mMatrix[VY][VX]; mMatrix[VY][VX] = temp;
- temp = mMatrix[VX][VZ]; mMatrix[VX][VZ] = mMatrix[VZ][VX]; mMatrix[VZ][VX] = temp;
- temp = mMatrix[VY][VZ]; mMatrix[VY][VZ] = mMatrix[VZ][VY]; mMatrix[VZ][VY] = temp;
- return *this;
-}
-
-
-F32 LLMatrix3::determinant() const
-{
- // Is this a useful method when we assume the matrices are valid rotation
- // matrices throughout this implementation?
- return mMatrix[0][0] * (mMatrix[1][1] * mMatrix[2][2] - mMatrix[1][2] * mMatrix[2][1]) +
- mMatrix[0][1] * (mMatrix[1][2] * mMatrix[2][0] - mMatrix[1][0] * mMatrix[2][2]) +
- mMatrix[0][2] * (mMatrix[1][0] * mMatrix[2][1] - mMatrix[1][1] * mMatrix[2][0]);
-}
-
-// inverts this matrix
-void LLMatrix3::invert()
-{
- // fails silently if determinant is zero too small
- F32 det = determinant();
- const F32 VERY_SMALL_DETERMINANT = 0.000001f;
- if (fabs(det) > VERY_SMALL_DETERMINANT)
- {
- // invertiable
- LLMatrix3 t(*this);
- mMatrix[VX][VX] = ( t.mMatrix[VY][VY] * t.mMatrix[VZ][VZ] - t.mMatrix[VY][VZ] * t.mMatrix[VZ][VY] ) / det;
- mMatrix[VY][VX] = ( t.mMatrix[VY][VZ] * t.mMatrix[VZ][VX] - t.mMatrix[VY][VX] * t.mMatrix[VZ][VZ] ) / det;
- mMatrix[VZ][VX] = ( t.mMatrix[VY][VX] * t.mMatrix[VZ][VY] - t.mMatrix[VY][VY] * t.mMatrix[VZ][VX] ) / det;
- mMatrix[VX][VY] = ( t.mMatrix[VZ][VY] * t.mMatrix[VX][VZ] - t.mMatrix[VZ][VZ] * t.mMatrix[VX][VY] ) / det;
- mMatrix[VY][VY] = ( t.mMatrix[VZ][VZ] * t.mMatrix[VX][VX] - t.mMatrix[VZ][VX] * t.mMatrix[VX][VZ] ) / det;
- mMatrix[VZ][VY] = ( t.mMatrix[VZ][VX] * t.mMatrix[VX][VY] - t.mMatrix[VZ][VY] * t.mMatrix[VX][VX] ) / det;
- mMatrix[VX][VZ] = ( t.mMatrix[VX][VY] * t.mMatrix[VY][VZ] - t.mMatrix[VX][VZ] * t.mMatrix[VY][VY] ) / det;
- mMatrix[VY][VZ] = ( t.mMatrix[VX][VZ] * t.mMatrix[VY][VX] - t.mMatrix[VX][VX] * t.mMatrix[VY][VZ] ) / det;
- mMatrix[VZ][VZ] = ( t.mMatrix[VX][VX] * t.mMatrix[VY][VY] - t.mMatrix[VX][VY] * t.mMatrix[VY][VX] ) / det;
- }
-}
-
-// does not assume a rotation matrix, and does not divide by determinant, assuming results will be renormalized
-const LLMatrix3& LLMatrix3::adjointTranspose()
-{
- LLMatrix3 adjoint_transpose;
- adjoint_transpose.mMatrix[VX][VX] = mMatrix[VY][VY] * mMatrix[VZ][VZ] - mMatrix[VY][VZ] * mMatrix[VZ][VY] ;
- adjoint_transpose.mMatrix[VY][VX] = mMatrix[VY][VZ] * mMatrix[VZ][VX] - mMatrix[VY][VX] * mMatrix[VZ][VZ] ;
- adjoint_transpose.mMatrix[VZ][VX] = mMatrix[VY][VX] * mMatrix[VZ][VY] - mMatrix[VY][VY] * mMatrix[VZ][VX] ;
- adjoint_transpose.mMatrix[VX][VY] = mMatrix[VZ][VY] * mMatrix[VX][VZ] - mMatrix[VZ][VZ] * mMatrix[VX][VY] ;
- adjoint_transpose.mMatrix[VY][VY] = mMatrix[VZ][VZ] * mMatrix[VX][VX] - mMatrix[VZ][VX] * mMatrix[VX][VZ] ;
- adjoint_transpose.mMatrix[VZ][VY] = mMatrix[VZ][VX] * mMatrix[VX][VY] - mMatrix[VZ][VY] * mMatrix[VX][VX] ;
- adjoint_transpose.mMatrix[VX][VZ] = mMatrix[VX][VY] * mMatrix[VY][VZ] - mMatrix[VX][VZ] * mMatrix[VY][VY] ;
- adjoint_transpose.mMatrix[VY][VZ] = mMatrix[VX][VZ] * mMatrix[VY][VX] - mMatrix[VX][VX] * mMatrix[VY][VZ] ;
- adjoint_transpose.mMatrix[VZ][VZ] = mMatrix[VX][VX] * mMatrix[VY][VY] - mMatrix[VX][VY] * mMatrix[VY][VX] ;
-
- *this = adjoint_transpose;
- return *this;
-}
-
-// SJB: This code is correct for a logicly stored (non-transposed) matrix;
-// Our matrices are stored transposed, OpenGL style, so this generates the
-// INVERSE quaternion (-x, -y, -z, w)!
-// Because we use similar logic in LLQuaternion::getMatrix3,
-// we are internally consistant so everything works OK :)
-LLQuaternion LLMatrix3::quaternion() const
-{
- LLQuaternion quat;
- F32 tr, s, q[4];
- U32 i, j, k;
- U32 nxt[3] = {1, 2, 0};
-
- tr = mMatrix[0][0] + mMatrix[1][1] + mMatrix[2][2];
-
- // check the diagonal
- if (tr > 0.f)
- {
- s = (F32)sqrt (tr + 1.f);
- quat.mQ[VS] = s / 2.f;
- s = 0.5f / s;
- quat.mQ[VX] = (mMatrix[1][2] - mMatrix[2][1]) * s;
- quat.mQ[VY] = (mMatrix[2][0] - mMatrix[0][2]) * s;
- quat.mQ[VZ] = (mMatrix[0][1] - mMatrix[1][0]) * s;
- }
- else
- {
- // diagonal is negative
- i = 0;
- if (mMatrix[1][1] > mMatrix[0][0])
- i = 1;
- if (mMatrix[2][2] > mMatrix[i][i])
- i = 2;
-
- j = nxt[i];
- k = nxt[j];
-
-
- s = (F32)sqrt ((mMatrix[i][i] - (mMatrix[j][j] + mMatrix[k][k])) + 1.f);
-
- q[i] = s * 0.5f;
-
- if (s != 0.f)
- s = 0.5f / s;
-
- q[3] = (mMatrix[j][k] - mMatrix[k][j]) * s;
- q[j] = (mMatrix[i][j] + mMatrix[j][i]) * s;
- q[k] = (mMatrix[i][k] + mMatrix[k][i]) * s;
-
- quat.setQuat(q);
- }
- return quat;
-}
-
-const LLMatrix3& LLMatrix3::setRot(const F32 angle, const LLVector3 &vec)
-{
- setRot(LLQuaternion(angle, vec));
- return *this;
-}
-
-const LLMatrix3& LLMatrix3::setRot(const F32 roll, const F32 pitch, const F32 yaw)
-{
- // Rotates RH about x-axis by 'roll' then
- // rotates RH about the old y-axis by 'pitch' then
- // rotates RH about the original z-axis by 'yaw'.
- // .
- // /|\ yaw axis
- // | __.
- // ._ ___| /| pitch axis
- // _||\ \\ |-. /
- // \|| \_______\_|__\_/_______
- // | _ _ o o o_o_o_o o /_\_ ________\ roll axis
- // // /_______/ /__________> /
- // /_,-' // /
- // /__,-'
-
- F32 cx, sx, cy, sy, cz, sz;
- F32 cxsy, sxsy;
-
- cx = (F32)cos(roll); //A
- sx = (F32)sin(roll); //B
- cy = (F32)cos(pitch); //C
- sy = (F32)sin(pitch); //D
- cz = (F32)cos(yaw); //E
- sz = (F32)sin(yaw); //F
-
- cxsy = cx * sy; //AD
- sxsy = sx * sy; //BD
-
- mMatrix[0][0] = cy * cz;
- mMatrix[1][0] = -cy * sz;
- mMatrix[2][0] = sy;
- mMatrix[0][1] = sxsy * cz + cx * sz;
- mMatrix[1][1] = -sxsy * sz + cx * cz;
- mMatrix[2][1] = -sx * cy;
- mMatrix[0][2] = -cxsy * cz + sx * sz;
- mMatrix[1][2] = cxsy * sz + sx * cz;
- mMatrix[2][2] = cx * cy;
- return *this;
-}
-
-
-const LLMatrix3& LLMatrix3::setRot(const LLQuaternion &q)
-{
- *this = q.getMatrix3();
- return *this;
-}
-
-const LLMatrix3& LLMatrix3::setRows(const LLVector3 &fwd, const LLVector3 &left, const LLVector3 &up)
-{
- mMatrix[0][0] = fwd.mV[0];
- mMatrix[0][1] = fwd.mV[1];
- mMatrix[0][2] = fwd.mV[2];
-
- mMatrix[1][0] = left.mV[0];
- mMatrix[1][1] = left.mV[1];
- mMatrix[1][2] = left.mV[2];
-
- mMatrix[2][0] = up.mV[0];
- mMatrix[2][1] = up.mV[1];
- mMatrix[2][2] = up.mV[2];
-
- return *this;
-}
-
-const LLMatrix3& LLMatrix3::setRow( U32 rowIndex, const LLVector3& row )
-{
- llassert( rowIndex >= 0 && rowIndex < NUM_VALUES_IN_MAT3 );
-
- mMatrix[rowIndex][0] = row[0];
- mMatrix[rowIndex][1] = row[1];
- mMatrix[rowIndex][2] = row[2];
-
- return *this;
-}
-
-const LLMatrix3& LLMatrix3::setCol( U32 colIndex, const LLVector3& col )
-{
- llassert( colIndex >= 0 && colIndex < NUM_VALUES_IN_MAT3 );
-
- mMatrix[0][colIndex] = col[0];
- mMatrix[1][colIndex] = col[1];
- mMatrix[2][colIndex] = col[2];
-
- return *this;
-}
-
-const LLMatrix3& LLMatrix3::rotate(const F32 angle, const LLVector3 &vec)
-{
- LLMatrix3 mat(angle, vec);
- *this *= mat;
- return *this;
-}
-
-
-const LLMatrix3& LLMatrix3::rotate(const F32 roll, const F32 pitch, const F32 yaw)
-{
- LLMatrix3 mat(roll, pitch, yaw);
- *this *= mat;
- return *this;
-}
-
-
-const LLMatrix3& LLMatrix3::rotate(const LLQuaternion &q)
-{
- LLMatrix3 mat(q);
- *this *= mat;
- return *this;
-}
-
-void LLMatrix3::add(const LLMatrix3& other_matrix)
-{
- for (S32 i = 0; i < 3; ++i)
- {
- for (S32 j = 0; j < 3; ++j)
- {
- mMatrix[i][j] += other_matrix.mMatrix[i][j];
- }
- }
-}
-
-LLVector3 LLMatrix3::getFwdRow() const
-{
- return LLVector3(mMatrix[VX]);
-}
-
-LLVector3 LLMatrix3::getLeftRow() const
-{
- return LLVector3(mMatrix[VY]);
-}
-
-LLVector3 LLMatrix3::getUpRow() const
-{
- return LLVector3(mMatrix[VZ]);
-}
-
-
-
-const LLMatrix3& LLMatrix3::orthogonalize()
-{
- LLVector3 x_axis(mMatrix[VX]);
- LLVector3 y_axis(mMatrix[VY]);
- LLVector3 z_axis(mMatrix[VZ]);
-
- x_axis.normVec();
- y_axis -= x_axis * (x_axis * y_axis);
- y_axis.normVec();
- z_axis = x_axis % y_axis;
- setRows(x_axis, y_axis, z_axis);
- return (*this);
-}
-
-
-// LLMatrix3 Operators
-
-LLMatrix3 operator*(const LLMatrix3 &a, const LLMatrix3 &b)
-{
- U32 i, j;
- LLMatrix3 mat;
- for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
- {
- for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
- {
- mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] +
- a.mMatrix[j][1] * b.mMatrix[1][i] +
- a.mMatrix[j][2] * b.mMatrix[2][i];
- }
- }
- return mat;
-}
-
-/* Not implemented to help enforce code consistency with the syntax of
- row-major notation. This is a Good Thing.
-LLVector3 operator*(const LLMatrix3 &a, const LLVector3 &b)
-{
- LLVector3 vec;
- // matrix operates "from the left" on column vector
- vec.mV[VX] = a.mMatrix[VX][VX] * b.mV[VX] +
- a.mMatrix[VX][VY] * b.mV[VY] +
- a.mMatrix[VX][VZ] * b.mV[VZ];
-
- vec.mV[VY] = a.mMatrix[VY][VX] * b.mV[VX] +
- a.mMatrix[VY][VY] * b.mV[VY] +
- a.mMatrix[VY][VZ] * b.mV[VZ];
-
- vec.mV[VZ] = a.mMatrix[VZ][VX] * b.mV[VX] +
- a.mMatrix[VZ][VY] * b.mV[VY] +
- a.mMatrix[VZ][VZ] * b.mV[VZ];
- return vec;
-}
-*/
-
-
-LLVector3 operator*(const LLVector3 &a, const LLMatrix3 &b)
-{
- // matrix operates "from the right" on row vector
- return LLVector3(
- a.mV[VX] * b.mMatrix[VX][VX] +
- a.mV[VY] * b.mMatrix[VY][VX] +
- a.mV[VZ] * b.mMatrix[VZ][VX],
-
- a.mV[VX] * b.mMatrix[VX][VY] +
- a.mV[VY] * b.mMatrix[VY][VY] +
- a.mV[VZ] * b.mMatrix[VZ][VY],
-
- a.mV[VX] * b.mMatrix[VX][VZ] +
- a.mV[VY] * b.mMatrix[VY][VZ] +
- a.mV[VZ] * b.mMatrix[VZ][VZ] );
-}
-
-LLVector3d operator*(const LLVector3d &a, const LLMatrix3 &b)
-{
- // matrix operates "from the right" on row vector
- return LLVector3d(
- a.mdV[VX] * b.mMatrix[VX][VX] +
- a.mdV[VY] * b.mMatrix[VY][VX] +
- a.mdV[VZ] * b.mMatrix[VZ][VX],
-
- a.mdV[VX] * b.mMatrix[VX][VY] +
- a.mdV[VY] * b.mMatrix[VY][VY] +
- a.mdV[VZ] * b.mMatrix[VZ][VY],
-
- a.mdV[VX] * b.mMatrix[VX][VZ] +
- a.mdV[VY] * b.mMatrix[VY][VZ] +
- a.mdV[VZ] * b.mMatrix[VZ][VZ] );
-}
-
-bool operator==(const LLMatrix3 &a, const LLMatrix3 &b)
-{
- U32 i, j;
- for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
- {
- for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
- {
- if (a.mMatrix[j][i] != b.mMatrix[j][i])
- return false;
- }
- }
- return true;
-}
-
-bool operator!=(const LLMatrix3 &a, const LLMatrix3 &b)
-{
- U32 i, j;
- for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
- {
- for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
- {
- if (a.mMatrix[j][i] != b.mMatrix[j][i])
- return true;
- }
- }
- return false;
-}
-
-const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b)
-{
- U32 i, j;
- LLMatrix3 mat;
- for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
- {
- for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
- {
- mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] +
- a.mMatrix[j][1] * b.mMatrix[1][i] +
- a.mMatrix[j][2] * b.mMatrix[2][i];
- }
- }
- a = mat;
- return a;
-}
-
-const LLMatrix3& operator*=(LLMatrix3 &a, F32 scalar )
-{
- for( U32 i = 0; i < NUM_VALUES_IN_MAT3; ++i )
- {
- for( U32 j = 0; j < NUM_VALUES_IN_MAT3; ++j )
- {
- a.mMatrix[i][j] *= scalar;
- }
- }
-
- return a;
-}
-
-std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a)
-{
- s << "{ "
- << a.mMatrix[VX][VX] << ", " << a.mMatrix[VX][VY] << ", " << a.mMatrix[VX][VZ] << "; "
- << a.mMatrix[VY][VX] << ", " << a.mMatrix[VY][VY] << ", " << a.mMatrix[VY][VZ] << "; "
- << a.mMatrix[VZ][VX] << ", " << a.mMatrix[VZ][VY] << ", " << a.mMatrix[VZ][VZ]
- << " }";
- return s;
-}
-
+/** + * @file m3math.cpp + * @brief LLMatrix3 class implementation. + * + * $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 "linden_common.h" + +//#include "vmath.h" +#include "v3math.h" +#include "v3dmath.h" +#include "v4math.h" +#include "m4math.h" +#include "m3math.h" +#include "llquaternion.h" + +// LLMatrix3 + +// ji +// LLMatrix3 = |00 01 02 | +// |10 11 12 | +// |20 21 22 | + +// LLMatrix3 = |fx fy fz | forward-axis +// |lx ly lz | left-axis +// |ux uy uz | up-axis + + +// Constructors + + +LLMatrix3::LLMatrix3(const LLQuaternion &q) +{ + setRot(q); +} + + +LLMatrix3::LLMatrix3(const F32 angle, const LLVector3 &vec) +{ + LLQuaternion quat(angle, vec); + setRot(quat); +} + +LLMatrix3::LLMatrix3(const F32 angle, const LLVector3d &vec) +{ + LLVector3 vec_f; + vec_f.setVec(vec); + LLQuaternion quat(angle, vec_f); + setRot(quat); +} + +LLMatrix3::LLMatrix3(const F32 angle, const LLVector4 &vec) +{ + LLQuaternion quat(angle, vec); + setRot(quat); +} + +LLMatrix3::LLMatrix3(const F32 roll, const F32 pitch, const F32 yaw) +{ + setRot(roll,pitch,yaw); +} + +// From Matrix and Quaternion FAQ +void LLMatrix3::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const +{ + F64 angle_x, angle_y, angle_z; + F64 cx, cy, cz; // cosine of angle_x, angle_y, angle_z + F64 sx, sz; // sine of angle_x, angle_y, angle_z + + angle_y = asin(llclamp(mMatrix[2][0], -1.f, 1.f)); + cy = cos(angle_y); + + if (fabs(cy) > 0.005) // non-zero + { + // no gimbal lock + cx = mMatrix[2][2] / cy; + sx = - mMatrix[2][1] / cy; + + angle_x = (F32) atan2(sx, cx); + + cz = mMatrix[0][0] / cy; + sz = - mMatrix[1][0] / cy; + + angle_z = (F32) atan2(sz, cz); + } + else + { + // yup, gimbal lock + angle_x = 0; + + // some tricky math thereby avoided, see article + + cz = mMatrix[1][1]; + sz = mMatrix[0][1]; + + angle_z = atan2(sz, cz); + } + + *roll = (F32)angle_x; + *pitch = (F32)angle_y; + *yaw = (F32)angle_z; +} + + +// Clear and Assignment Functions + +const LLMatrix3& LLMatrix3::setIdentity() +{ + mMatrix[0][0] = 1.f; + mMatrix[0][1] = 0.f; + mMatrix[0][2] = 0.f; + + mMatrix[1][0] = 0.f; + mMatrix[1][1] = 1.f; + mMatrix[1][2] = 0.f; + + mMatrix[2][0] = 0.f; + mMatrix[2][1] = 0.f; + mMatrix[2][2] = 1.f; + return (*this); +} + +const LLMatrix3& LLMatrix3::clear() +{ + mMatrix[0][0] = 0.f; + mMatrix[0][1] = 0.f; + mMatrix[0][2] = 0.f; + + mMatrix[1][0] = 0.f; + mMatrix[1][1] = 0.f; + mMatrix[1][2] = 0.f; + + mMatrix[2][0] = 0.f; + mMatrix[2][1] = 0.f; + mMatrix[2][2] = 0.f; + return (*this); +} + +const LLMatrix3& LLMatrix3::setZero() +{ + mMatrix[0][0] = 0.f; + mMatrix[0][1] = 0.f; + mMatrix[0][2] = 0.f; + + mMatrix[1][0] = 0.f; + mMatrix[1][1] = 0.f; + mMatrix[1][2] = 0.f; + + mMatrix[2][0] = 0.f; + mMatrix[2][1] = 0.f; + mMatrix[2][2] = 0.f; + return (*this); +} + +// various useful mMatrix functions + +const LLMatrix3& LLMatrix3::transpose() +{ + // transpose the matrix + F32 temp; + temp = mMatrix[VX][VY]; mMatrix[VX][VY] = mMatrix[VY][VX]; mMatrix[VY][VX] = temp; + temp = mMatrix[VX][VZ]; mMatrix[VX][VZ] = mMatrix[VZ][VX]; mMatrix[VZ][VX] = temp; + temp = mMatrix[VY][VZ]; mMatrix[VY][VZ] = mMatrix[VZ][VY]; mMatrix[VZ][VY] = temp; + return *this; +} + + +F32 LLMatrix3::determinant() const +{ + // Is this a useful method when we assume the matrices are valid rotation + // matrices throughout this implementation? + return mMatrix[0][0] * (mMatrix[1][1] * mMatrix[2][2] - mMatrix[1][2] * mMatrix[2][1]) + + mMatrix[0][1] * (mMatrix[1][2] * mMatrix[2][0] - mMatrix[1][0] * mMatrix[2][2]) + + mMatrix[0][2] * (mMatrix[1][0] * mMatrix[2][1] - mMatrix[1][1] * mMatrix[2][0]); +} + +// inverts this matrix +void LLMatrix3::invert() +{ + // fails silently if determinant is zero too small + F32 det = determinant(); + const F32 VERY_SMALL_DETERMINANT = 0.000001f; + if (fabs(det) > VERY_SMALL_DETERMINANT) + { + // invertiable + LLMatrix3 t(*this); + mMatrix[VX][VX] = ( t.mMatrix[VY][VY] * t.mMatrix[VZ][VZ] - t.mMatrix[VY][VZ] * t.mMatrix[VZ][VY] ) / det; + mMatrix[VY][VX] = ( t.mMatrix[VY][VZ] * t.mMatrix[VZ][VX] - t.mMatrix[VY][VX] * t.mMatrix[VZ][VZ] ) / det; + mMatrix[VZ][VX] = ( t.mMatrix[VY][VX] * t.mMatrix[VZ][VY] - t.mMatrix[VY][VY] * t.mMatrix[VZ][VX] ) / det; + mMatrix[VX][VY] = ( t.mMatrix[VZ][VY] * t.mMatrix[VX][VZ] - t.mMatrix[VZ][VZ] * t.mMatrix[VX][VY] ) / det; + mMatrix[VY][VY] = ( t.mMatrix[VZ][VZ] * t.mMatrix[VX][VX] - t.mMatrix[VZ][VX] * t.mMatrix[VX][VZ] ) / det; + mMatrix[VZ][VY] = ( t.mMatrix[VZ][VX] * t.mMatrix[VX][VY] - t.mMatrix[VZ][VY] * t.mMatrix[VX][VX] ) / det; + mMatrix[VX][VZ] = ( t.mMatrix[VX][VY] * t.mMatrix[VY][VZ] - t.mMatrix[VX][VZ] * t.mMatrix[VY][VY] ) / det; + mMatrix[VY][VZ] = ( t.mMatrix[VX][VZ] * t.mMatrix[VY][VX] - t.mMatrix[VX][VX] * t.mMatrix[VY][VZ] ) / det; + mMatrix[VZ][VZ] = ( t.mMatrix[VX][VX] * t.mMatrix[VY][VY] - t.mMatrix[VX][VY] * t.mMatrix[VY][VX] ) / det; + } +} + +// does not assume a rotation matrix, and does not divide by determinant, assuming results will be renormalized +const LLMatrix3& LLMatrix3::adjointTranspose() +{ + LLMatrix3 adjoint_transpose; + adjoint_transpose.mMatrix[VX][VX] = mMatrix[VY][VY] * mMatrix[VZ][VZ] - mMatrix[VY][VZ] * mMatrix[VZ][VY] ; + adjoint_transpose.mMatrix[VY][VX] = mMatrix[VY][VZ] * mMatrix[VZ][VX] - mMatrix[VY][VX] * mMatrix[VZ][VZ] ; + adjoint_transpose.mMatrix[VZ][VX] = mMatrix[VY][VX] * mMatrix[VZ][VY] - mMatrix[VY][VY] * mMatrix[VZ][VX] ; + adjoint_transpose.mMatrix[VX][VY] = mMatrix[VZ][VY] * mMatrix[VX][VZ] - mMatrix[VZ][VZ] * mMatrix[VX][VY] ; + adjoint_transpose.mMatrix[VY][VY] = mMatrix[VZ][VZ] * mMatrix[VX][VX] - mMatrix[VZ][VX] * mMatrix[VX][VZ] ; + adjoint_transpose.mMatrix[VZ][VY] = mMatrix[VZ][VX] * mMatrix[VX][VY] - mMatrix[VZ][VY] * mMatrix[VX][VX] ; + adjoint_transpose.mMatrix[VX][VZ] = mMatrix[VX][VY] * mMatrix[VY][VZ] - mMatrix[VX][VZ] * mMatrix[VY][VY] ; + adjoint_transpose.mMatrix[VY][VZ] = mMatrix[VX][VZ] * mMatrix[VY][VX] - mMatrix[VX][VX] * mMatrix[VY][VZ] ; + adjoint_transpose.mMatrix[VZ][VZ] = mMatrix[VX][VX] * mMatrix[VY][VY] - mMatrix[VX][VY] * mMatrix[VY][VX] ; + + *this = adjoint_transpose; + return *this; +} + +// SJB: This code is correct for a logicly stored (non-transposed) matrix; +// Our matrices are stored transposed, OpenGL style, so this generates the +// INVERSE quaternion (-x, -y, -z, w)! +// Because we use similar logic in LLQuaternion::getMatrix3, +// we are internally consistant so everything works OK :) +LLQuaternion LLMatrix3::quaternion() const +{ + LLQuaternion quat; + F32 tr, s, q[4]; + U32 i, j, k; + U32 nxt[3] = {1, 2, 0}; + + tr = mMatrix[0][0] + mMatrix[1][1] + mMatrix[2][2]; + + // check the diagonal + if (tr > 0.f) + { + s = (F32)sqrt (tr + 1.f); + quat.mQ[VS] = s / 2.f; + s = 0.5f / s; + quat.mQ[VX] = (mMatrix[1][2] - mMatrix[2][1]) * s; + quat.mQ[VY] = (mMatrix[2][0] - mMatrix[0][2]) * s; + quat.mQ[VZ] = (mMatrix[0][1] - mMatrix[1][0]) * s; + } + else + { + // diagonal is negative + i = 0; + if (mMatrix[1][1] > mMatrix[0][0]) + i = 1; + if (mMatrix[2][2] > mMatrix[i][i]) + i = 2; + + j = nxt[i]; + k = nxt[j]; + + + s = (F32)sqrt ((mMatrix[i][i] - (mMatrix[j][j] + mMatrix[k][k])) + 1.f); + + q[i] = s * 0.5f; + + if (s != 0.f) + s = 0.5f / s; + + q[3] = (mMatrix[j][k] - mMatrix[k][j]) * s; + q[j] = (mMatrix[i][j] + mMatrix[j][i]) * s; + q[k] = (mMatrix[i][k] + mMatrix[k][i]) * s; + + quat.setQuat(q); + } + return quat; +} + +const LLMatrix3& LLMatrix3::setRot(const F32 angle, const LLVector3 &vec) +{ + setRot(LLQuaternion(angle, vec)); + return *this; +} + +const LLMatrix3& LLMatrix3::setRot(const F32 roll, const F32 pitch, const F32 yaw) +{ + // Rotates RH about x-axis by 'roll' then + // rotates RH about the old y-axis by 'pitch' then + // rotates RH about the original z-axis by 'yaw'. + // . + // /|\ yaw axis + // | __. + // ._ ___| /| pitch axis + // _||\ \\ |-. / + // \|| \_______\_|__\_/_______ + // | _ _ o o o_o_o_o o /_\_ ________\ roll axis + // // /_______/ /__________> / + // /_,-' // / + // /__,-' + + F32 cx, sx, cy, sy, cz, sz; + F32 cxsy, sxsy; + + cx = (F32)cos(roll); //A + sx = (F32)sin(roll); //B + cy = (F32)cos(pitch); //C + sy = (F32)sin(pitch); //D + cz = (F32)cos(yaw); //E + sz = (F32)sin(yaw); //F + + cxsy = cx * sy; //AD + sxsy = sx * sy; //BD + + mMatrix[0][0] = cy * cz; + mMatrix[1][0] = -cy * sz; + mMatrix[2][0] = sy; + mMatrix[0][1] = sxsy * cz + cx * sz; + mMatrix[1][1] = -sxsy * sz + cx * cz; + mMatrix[2][1] = -sx * cy; + mMatrix[0][2] = -cxsy * cz + sx * sz; + mMatrix[1][2] = cxsy * sz + sx * cz; + mMatrix[2][2] = cx * cy; + return *this; +} + + +const LLMatrix3& LLMatrix3::setRot(const LLQuaternion &q) +{ + *this = q.getMatrix3(); + return *this; +} + +const LLMatrix3& LLMatrix3::setRows(const LLVector3 &fwd, const LLVector3 &left, const LLVector3 &up) +{ + mMatrix[0][0] = fwd.mV[0]; + mMatrix[0][1] = fwd.mV[1]; + mMatrix[0][2] = fwd.mV[2]; + + mMatrix[1][0] = left.mV[0]; + mMatrix[1][1] = left.mV[1]; + mMatrix[1][2] = left.mV[2]; + + mMatrix[2][0] = up.mV[0]; + mMatrix[2][1] = up.mV[1]; + mMatrix[2][2] = up.mV[2]; + + return *this; +} + +const LLMatrix3& LLMatrix3::setRow( U32 rowIndex, const LLVector3& row ) +{ + llassert( rowIndex >= 0 && rowIndex < NUM_VALUES_IN_MAT3 ); + + mMatrix[rowIndex][0] = row[0]; + mMatrix[rowIndex][1] = row[1]; + mMatrix[rowIndex][2] = row[2]; + + return *this; +} + +const LLMatrix3& LLMatrix3::setCol( U32 colIndex, const LLVector3& col ) +{ + llassert( colIndex >= 0 && colIndex < NUM_VALUES_IN_MAT3 ); + + mMatrix[0][colIndex] = col[0]; + mMatrix[1][colIndex] = col[1]; + mMatrix[2][colIndex] = col[2]; + + return *this; +} + +const LLMatrix3& LLMatrix3::rotate(const F32 angle, const LLVector3 &vec) +{ + LLMatrix3 mat(angle, vec); + *this *= mat; + return *this; +} + + +const LLMatrix3& LLMatrix3::rotate(const F32 roll, const F32 pitch, const F32 yaw) +{ + LLMatrix3 mat(roll, pitch, yaw); + *this *= mat; + return *this; +} + + +const LLMatrix3& LLMatrix3::rotate(const LLQuaternion &q) +{ + LLMatrix3 mat(q); + *this *= mat; + return *this; +} + +void LLMatrix3::add(const LLMatrix3& other_matrix) +{ + for (S32 i = 0; i < 3; ++i) + { + for (S32 j = 0; j < 3; ++j) + { + mMatrix[i][j] += other_matrix.mMatrix[i][j]; + } + } +} + +LLVector3 LLMatrix3::getFwdRow() const +{ + return LLVector3(mMatrix[VX]); +} + +LLVector3 LLMatrix3::getLeftRow() const +{ + return LLVector3(mMatrix[VY]); +} + +LLVector3 LLMatrix3::getUpRow() const +{ + return LLVector3(mMatrix[VZ]); +} + + + +const LLMatrix3& LLMatrix3::orthogonalize() +{ + LLVector3 x_axis(mMatrix[VX]); + LLVector3 y_axis(mMatrix[VY]); + LLVector3 z_axis(mMatrix[VZ]); + + x_axis.normVec(); + y_axis -= x_axis * (x_axis * y_axis); + y_axis.normVec(); + z_axis = x_axis % y_axis; + setRows(x_axis, y_axis, z_axis); + return (*this); +} + + +// LLMatrix3 Operators + +LLMatrix3 operator*(const LLMatrix3 &a, const LLMatrix3 &b) +{ + U32 i, j; + LLMatrix3 mat; + for (i = 0; i < NUM_VALUES_IN_MAT3; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT3; j++) + { + mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] + + a.mMatrix[j][1] * b.mMatrix[1][i] + + a.mMatrix[j][2] * b.mMatrix[2][i]; + } + } + return mat; +} + +/* Not implemented to help enforce code consistency with the syntax of + row-major notation. This is a Good Thing. +LLVector3 operator*(const LLMatrix3 &a, const LLVector3 &b) +{ + LLVector3 vec; + // matrix operates "from the left" on column vector + vec.mV[VX] = a.mMatrix[VX][VX] * b.mV[VX] + + a.mMatrix[VX][VY] * b.mV[VY] + + a.mMatrix[VX][VZ] * b.mV[VZ]; + + vec.mV[VY] = a.mMatrix[VY][VX] * b.mV[VX] + + a.mMatrix[VY][VY] * b.mV[VY] + + a.mMatrix[VY][VZ] * b.mV[VZ]; + + vec.mV[VZ] = a.mMatrix[VZ][VX] * b.mV[VX] + + a.mMatrix[VZ][VY] * b.mV[VY] + + a.mMatrix[VZ][VZ] * b.mV[VZ]; + return vec; +} +*/ + + +LLVector3 operator*(const LLVector3 &a, const LLMatrix3 &b) +{ + // matrix operates "from the right" on row vector + return LLVector3( + a.mV[VX] * b.mMatrix[VX][VX] + + a.mV[VY] * b.mMatrix[VY][VX] + + a.mV[VZ] * b.mMatrix[VZ][VX], + + a.mV[VX] * b.mMatrix[VX][VY] + + a.mV[VY] * b.mMatrix[VY][VY] + + a.mV[VZ] * b.mMatrix[VZ][VY], + + a.mV[VX] * b.mMatrix[VX][VZ] + + a.mV[VY] * b.mMatrix[VY][VZ] + + a.mV[VZ] * b.mMatrix[VZ][VZ] ); +} + +LLVector3d operator*(const LLVector3d &a, const LLMatrix3 &b) +{ + // matrix operates "from the right" on row vector + return LLVector3d( + a.mdV[VX] * b.mMatrix[VX][VX] + + a.mdV[VY] * b.mMatrix[VY][VX] + + a.mdV[VZ] * b.mMatrix[VZ][VX], + + a.mdV[VX] * b.mMatrix[VX][VY] + + a.mdV[VY] * b.mMatrix[VY][VY] + + a.mdV[VZ] * b.mMatrix[VZ][VY], + + a.mdV[VX] * b.mMatrix[VX][VZ] + + a.mdV[VY] * b.mMatrix[VY][VZ] + + a.mdV[VZ] * b.mMatrix[VZ][VZ] ); +} + +bool operator==(const LLMatrix3 &a, const LLMatrix3 &b) +{ + U32 i, j; + for (i = 0; i < NUM_VALUES_IN_MAT3; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT3; j++) + { + if (a.mMatrix[j][i] != b.mMatrix[j][i]) + return false; + } + } + return true; +} + +bool operator!=(const LLMatrix3 &a, const LLMatrix3 &b) +{ + U32 i, j; + for (i = 0; i < NUM_VALUES_IN_MAT3; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT3; j++) + { + if (a.mMatrix[j][i] != b.mMatrix[j][i]) + return true; + } + } + return false; +} + +const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b) +{ + U32 i, j; + LLMatrix3 mat; + for (i = 0; i < NUM_VALUES_IN_MAT3; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT3; j++) + { + mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] + + a.mMatrix[j][1] * b.mMatrix[1][i] + + a.mMatrix[j][2] * b.mMatrix[2][i]; + } + } + a = mat; + return a; +} + +const LLMatrix3& operator*=(LLMatrix3 &a, F32 scalar ) +{ + for( U32 i = 0; i < NUM_VALUES_IN_MAT3; ++i ) + { + for( U32 j = 0; j < NUM_VALUES_IN_MAT3; ++j ) + { + a.mMatrix[i][j] *= scalar; + } + } + + return a; +} + +std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a) +{ + s << "{ " + << a.mMatrix[VX][VX] << ", " << a.mMatrix[VX][VY] << ", " << a.mMatrix[VX][VZ] << "; " + << a.mMatrix[VY][VX] << ", " << a.mMatrix[VY][VY] << ", " << a.mMatrix[VY][VZ] << "; " + << a.mMatrix[VZ][VX] << ", " << a.mMatrix[VZ][VY] << ", " << a.mMatrix[VZ][VZ] + << " }"; + return s; +} + diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp index 976535dd84..c46ee587cb 100644 --- a/indra/llmath/m4math.cpp +++ b/indra/llmath/m4math.cpp @@ -1,873 +1,873 @@ -/**
- * @file m4math.cpp
- * @brief LLMatrix4 class implementation.
- *
- * $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 "linden_common.h"
-
-//#include "vmath.h"
-#include "v3math.h"
-#include "v4math.h"
-#include "m4math.h"
-#include "m3math.h"
-#include "llquaternion.h"
-#include "llmatrix4a.h"
-
-
-// LLMatrix4
-
-// Constructors
-
-
-LLMatrix4::LLMatrix4(const F32 *mat)
-{
- mMatrix[0][0] = mat[0];
- mMatrix[0][1] = mat[1];
- mMatrix[0][2] = mat[2];
- mMatrix[0][3] = mat[3];
-
- mMatrix[1][0] = mat[4];
- mMatrix[1][1] = mat[5];
- mMatrix[1][2] = mat[6];
- mMatrix[1][3] = mat[7];
-
- mMatrix[2][0] = mat[8];
- mMatrix[2][1] = mat[9];
- mMatrix[2][2] = mat[10];
- mMatrix[2][3] = mat[11];
-
- mMatrix[3][0] = mat[12];
- mMatrix[3][1] = mat[13];
- mMatrix[3][2] = mat[14];
- mMatrix[3][3] = mat[15];
-}
-
-LLMatrix4::LLMatrix4(const LLMatrix3 &mat, const LLVector4 &vec)
-{
- mMatrix[0][0] = mat.mMatrix[0][0];
- mMatrix[0][1] = mat.mMatrix[0][1];
- mMatrix[0][2] = mat.mMatrix[0][2];
- mMatrix[0][3] = 0.f;
-
- mMatrix[1][0] = mat.mMatrix[1][0];
- mMatrix[1][1] = mat.mMatrix[1][1];
- mMatrix[1][2] = mat.mMatrix[1][2];
- mMatrix[1][3] = 0.f;
-
- mMatrix[2][0] = mat.mMatrix[2][0];
- mMatrix[2][1] = mat.mMatrix[2][1];
- mMatrix[2][2] = mat.mMatrix[2][2];
- mMatrix[2][3] = 0.f;
-
- mMatrix[3][0] = vec.mV[0];
- mMatrix[3][1] = vec.mV[1];
- mMatrix[3][2] = vec.mV[2];
- mMatrix[3][3] = 1.f;
-}
-
-LLMatrix4::LLMatrix4(const LLMatrix3 &mat)
-{
- mMatrix[0][0] = mat.mMatrix[0][0];
- mMatrix[0][1] = mat.mMatrix[0][1];
- mMatrix[0][2] = mat.mMatrix[0][2];
- mMatrix[0][3] = 0.f;
-
- mMatrix[1][0] = mat.mMatrix[1][0];
- mMatrix[1][1] = mat.mMatrix[1][1];
- mMatrix[1][2] = mat.mMatrix[1][2];
- mMatrix[1][3] = 0.f;
-
- mMatrix[2][0] = mat.mMatrix[2][0];
- mMatrix[2][1] = mat.mMatrix[2][1];
- mMatrix[2][2] = mat.mMatrix[2][2];
- mMatrix[2][3] = 0.f;
-
- mMatrix[3][0] = 0.f;
- mMatrix[3][1] = 0.f;
- mMatrix[3][2] = 0.f;
- mMatrix[3][3] = 1.f;
-}
-
-LLMatrix4::LLMatrix4(const LLQuaternion &q)
-{
- *this = initRotation(q);
-}
-
-LLMatrix4::LLMatrix4(const LLMatrix4a& mat)
- : LLMatrix4(mat.getF32ptr())
-{
-
-}
-
-LLMatrix4::LLMatrix4(const LLQuaternion &q, const LLVector4 &pos)
-{
- *this = initRotTrans(q, pos);
-}
-
-LLMatrix4::LLMatrix4(const F32 angle, const LLVector4 &vec, const LLVector4 &pos)
-{
- initRotTrans(LLQuaternion(angle, vec), pos);
-}
-
-LLMatrix4::LLMatrix4(const F32 angle, const LLVector4 &vec)
-{
- initRotation(LLQuaternion(angle, vec));
-
- mMatrix[3][0] = 0.f;
- mMatrix[3][1] = 0.f;
- mMatrix[3][2] = 0.f;
- mMatrix[3][3] = 1.f;
-}
-
-LLMatrix4::LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &pos)
-{
- LLMatrix3 mat(roll, pitch, yaw);
- initRotTrans(LLQuaternion(mat), pos);
-}
-
-LLMatrix4::LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw)
-{
- LLMatrix3 mat(roll, pitch, yaw);
- initRotation(LLQuaternion(mat));
-
- mMatrix[3][0] = 0.f;
- mMatrix[3][1] = 0.f;
- mMatrix[3][2] = 0.f;
- mMatrix[3][3] = 1.f;
-}
-
-LLMatrix4::~LLMatrix4(void)
-{
-}
-
-// Clear and Assignment Functions
-
-const LLMatrix4& LLMatrix4::setZero()
-{
- mMatrix[0][0] = 0.f;
- mMatrix[0][1] = 0.f;
- mMatrix[0][2] = 0.f;
- mMatrix[0][3] = 0.f;
-
- mMatrix[1][0] = 0.f;
- mMatrix[1][1] = 0.f;
- mMatrix[1][2] = 0.f;
- mMatrix[1][3] = 0.f;
-
- mMatrix[2][0] = 0.f;
- mMatrix[2][1] = 0.f;
- mMatrix[2][2] = 0.f;
- mMatrix[2][3] = 0.f;
-
- mMatrix[3][0] = 0.f;
- mMatrix[3][1] = 0.f;
- mMatrix[3][2] = 0.f;
- mMatrix[3][3] = 0.f;
- return *this;
-}
-
-
-// various useful mMatrix functions
-
-const LLMatrix4& LLMatrix4::transpose()
-{
- LLMatrix4 mat;
- mat.mMatrix[0][0] = mMatrix[0][0];
- mat.mMatrix[1][0] = mMatrix[0][1];
- mat.mMatrix[2][0] = mMatrix[0][2];
- mat.mMatrix[3][0] = mMatrix[0][3];
-
- mat.mMatrix[0][1] = mMatrix[1][0];
- mat.mMatrix[1][1] = mMatrix[1][1];
- mat.mMatrix[2][1] = mMatrix[1][2];
- mat.mMatrix[3][1] = mMatrix[1][3];
-
- mat.mMatrix[0][2] = mMatrix[2][0];
- mat.mMatrix[1][2] = mMatrix[2][1];
- mat.mMatrix[2][2] = mMatrix[2][2];
- mat.mMatrix[3][2] = mMatrix[2][3];
-
- mat.mMatrix[0][3] = mMatrix[3][0];
- mat.mMatrix[1][3] = mMatrix[3][1];
- mat.mMatrix[2][3] = mMatrix[3][2];
- mat.mMatrix[3][3] = mMatrix[3][3];
-
- *this = mat;
- return *this;
-}
-
-
-F32 LLMatrix4::determinant() const
-{
- F32 value =
- mMatrix[0][3] * mMatrix[1][2] * mMatrix[2][1] * mMatrix[3][0] -
- mMatrix[0][2] * mMatrix[1][3] * mMatrix[2][1] * mMatrix[3][0] -
- mMatrix[0][3] * mMatrix[1][1] * mMatrix[2][2] * mMatrix[3][0] +
- mMatrix[0][1] * mMatrix[1][3] * mMatrix[2][2] * mMatrix[3][0] +
- mMatrix[0][2] * mMatrix[1][1] * mMatrix[2][3] * mMatrix[3][0] -
- mMatrix[0][1] * mMatrix[1][2] * mMatrix[2][3] * mMatrix[3][0] -
- mMatrix[0][3] * mMatrix[1][2] * mMatrix[2][0] * mMatrix[3][1] +
- mMatrix[0][2] * mMatrix[1][3] * mMatrix[2][0] * mMatrix[3][1] +
- mMatrix[0][3] * mMatrix[1][0] * mMatrix[2][2] * mMatrix[3][1] -
- mMatrix[0][0] * mMatrix[1][3] * mMatrix[2][2] * mMatrix[3][1] -
- mMatrix[0][2] * mMatrix[1][0] * mMatrix[2][3] * mMatrix[3][1] +
- mMatrix[0][0] * mMatrix[1][2] * mMatrix[2][3] * mMatrix[3][1] +
- mMatrix[0][3] * mMatrix[1][1] * mMatrix[2][0] * mMatrix[3][2] -
- mMatrix[0][1] * mMatrix[1][3] * mMatrix[2][0] * mMatrix[3][2] -
- mMatrix[0][3] * mMatrix[1][0] * mMatrix[2][1] * mMatrix[3][2] +
- mMatrix[0][0] * mMatrix[1][3] * mMatrix[2][1] * mMatrix[3][2] +
- mMatrix[0][1] * mMatrix[1][0] * mMatrix[2][3] * mMatrix[3][2] -
- mMatrix[0][0] * mMatrix[1][1] * mMatrix[2][3] * mMatrix[3][2] -
- mMatrix[0][2] * mMatrix[1][1] * mMatrix[2][0] * mMatrix[3][3] +
- mMatrix[0][1] * mMatrix[1][2] * mMatrix[2][0] * mMatrix[3][3] +
- mMatrix[0][2] * mMatrix[1][0] * mMatrix[2][1] * mMatrix[3][3] -
- mMatrix[0][0] * mMatrix[1][2] * mMatrix[2][1] * mMatrix[3][3] -
- mMatrix[0][1] * mMatrix[1][0] * mMatrix[2][2] * mMatrix[3][3] +
- mMatrix[0][0] * mMatrix[1][1] * mMatrix[2][2] * mMatrix[3][3];
-
- return value;
-}
-
-// Only works for pure orthonormal, homogeneous transform matrices.
-const LLMatrix4& LLMatrix4::invert(void)
-{
- // transpose the rotation part
- F32 temp;
- temp = mMatrix[VX][VY]; mMatrix[VX][VY] = mMatrix[VY][VX]; mMatrix[VY][VX] = temp;
- temp = mMatrix[VX][VZ]; mMatrix[VX][VZ] = mMatrix[VZ][VX]; mMatrix[VZ][VX] = temp;
- temp = mMatrix[VY][VZ]; mMatrix[VY][VZ] = mMatrix[VZ][VY]; mMatrix[VZ][VY] = temp;
-
- // rotate the translation part by the new rotation
- // (temporarily store in empty column of matrix)
- U32 j;
- for (j=0; j<3; j++)
- {
- mMatrix[j][VW] = mMatrix[VW][VX] * mMatrix[VX][j] +
- mMatrix[VW][VY] * mMatrix[VY][j] +
- mMatrix[VW][VZ] * mMatrix[VZ][j];
- }
-
- // negate and copy the temporary vector back to the tranlation row
- mMatrix[VW][VX] = -mMatrix[VX][VW];
- mMatrix[VW][VY] = -mMatrix[VY][VW];
- mMatrix[VW][VZ] = -mMatrix[VZ][VW];
-
- // zero the empty column again
- mMatrix[VX][VW] = mMatrix[VY][VW] = mMatrix[VZ][VW] = 0.0f;
-
- return *this;
-}
-
-// Convenience func for simplifying comparison-heavy code by
-// intentionally stomping values in [-FLT_EPS,FLT_EPS] to 0.0f
-//
-void LLMatrix4::condition(void)
-{
- U32 i;
- U32 j;
- for (i = 0; i < 3;i++)
- for (j = 0; j < 3;j++)
- mMatrix[i][j] = ((mMatrix[i][j] > -FLT_EPSILON)
- && (mMatrix[i][j] < FLT_EPSILON)) ? 0.0f : mMatrix[i][j];
-}
-
-LLVector4 LLMatrix4::getFwdRow4() const
-{
- return LLVector4(mMatrix[VX][VX], mMatrix[VX][VY], mMatrix[VX][VZ], mMatrix[VX][VW]);
-}
-
-LLVector4 LLMatrix4::getLeftRow4() const
-{
- return LLVector4(mMatrix[VY][VX], mMatrix[VY][VY], mMatrix[VY][VZ], mMatrix[VY][VW]);
-}
-
-LLVector4 LLMatrix4::getUpRow4() const
-{
- return LLVector4(mMatrix[VZ][VX], mMatrix[VZ][VY], mMatrix[VZ][VZ], mMatrix[VZ][VW]);
-}
-
-// SJB: This code is correct for a logicly stored (non-transposed) matrix;
-// Our matrices are stored transposed, OpenGL style, so this generates the
-// INVERSE quaternion (-x, -y, -z, w)!
-// Because we use similar logic in LLQuaternion::getMatrix3,
-// we are internally consistant so everything works OK :)
-LLQuaternion LLMatrix4::quaternion() const
-{
- LLQuaternion quat;
- F32 tr, s, q[4];
- U32 i, j, k;
- U32 nxt[3] = {1, 2, 0};
-
- tr = mMatrix[0][0] + mMatrix[1][1] + mMatrix[2][2];
-
- // check the diagonal
- if (tr > 0.f)
- {
- s = (F32)sqrt (tr + 1.f);
- quat.mQ[VS] = s / 2.f;
- s = 0.5f / s;
- quat.mQ[VX] = (mMatrix[1][2] - mMatrix[2][1]) * s;
- quat.mQ[VY] = (mMatrix[2][0] - mMatrix[0][2]) * s;
- quat.mQ[VZ] = (mMatrix[0][1] - mMatrix[1][0]) * s;
- }
- else
- {
- // diagonal is negative
- i = 0;
- if (mMatrix[1][1] > mMatrix[0][0])
- i = 1;
- if (mMatrix[2][2] > mMatrix[i][i])
- i = 2;
-
- j = nxt[i];
- k = nxt[j];
-
-
- s = (F32)sqrt ((mMatrix[i][i] - (mMatrix[j][j] + mMatrix[k][k])) + 1.f);
-
- q[i] = s * 0.5f;
-
- if (s != 0.f)
- s = 0.5f / s;
-
- q[3] = (mMatrix[j][k] - mMatrix[k][j]) * s;
- q[j] = (mMatrix[i][j] + mMatrix[j][i]) * s;
- q[k] = (mMatrix[i][k] + mMatrix[k][i]) * s;
-
- quat.setQuat(q);
- }
- return quat;
-}
-
-
-
-void LLMatrix4::initRows(const LLVector4 &row0,
- const LLVector4 &row1,
- const LLVector4 &row2,
- const LLVector4 &row3)
-{
- mMatrix[0][0] = row0.mV[0];
- mMatrix[0][1] = row0.mV[1];
- mMatrix[0][2] = row0.mV[2];
- mMatrix[0][3] = row0.mV[3];
-
- mMatrix[1][0] = row1.mV[0];
- mMatrix[1][1] = row1.mV[1];
- mMatrix[1][2] = row1.mV[2];
- mMatrix[1][3] = row1.mV[3];
-
- mMatrix[2][0] = row2.mV[0];
- mMatrix[2][1] = row2.mV[1];
- mMatrix[2][2] = row2.mV[2];
- mMatrix[2][3] = row2.mV[3];
-
- mMatrix[3][0] = row3.mV[0];
- mMatrix[3][1] = row3.mV[1];
- mMatrix[3][2] = row3.mV[2];
- mMatrix[3][3] = row3.mV[3];
-}
-
-
-const LLMatrix4& LLMatrix4::initRotation(F32 angle, const LLVector4 &vec)
-{
- LLMatrix3 mat(angle, vec);
- return initMatrix(mat);
-}
-
-
-const LLMatrix4& LLMatrix4::initRotation(const F32 roll, const F32 pitch, const F32 yaw)
-{
- LLMatrix3 mat(roll, pitch, yaw);
- return initMatrix(mat);
-}
-
-
-const LLMatrix4& LLMatrix4::initRotation(const LLQuaternion &q)
-{
- LLMatrix3 mat(q);
- return initMatrix(mat);
-}
-
-
-const LLMatrix4& LLMatrix4::initRotTrans(const F32 angle, const LLVector3 &axis, const LLVector3&translation)
-{
- LLMatrix3 mat(angle, axis);
- initMatrix(mat);
- setTranslation(translation);
- return (*this);
-}
-
-const LLMatrix4& LLMatrix4::initRotTrans(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &translation)
-{
- LLMatrix3 mat(roll, pitch, yaw);
- initMatrix(mat);
- setTranslation(translation);
- return (*this);
-}
-
-/*
-const LLMatrix4& LLMatrix4::initRotTrans(const LLVector4 &fwd,
- const LLVector4 &left,
- const LLVector4 &up,
- const LLVector4 &translation)
-{
- LLMatrix3 mat(fwd, left, up);
- initMatrix(mat);
- setTranslation(translation);
- return (*this);
-}
-*/
-
-const LLMatrix4& LLMatrix4::initRotTrans(const LLQuaternion &q, const LLVector4 &translation)
-{
- LLMatrix3 mat(q);
- initMatrix(mat);
- setTranslation(translation);
- return (*this);
-}
-
-const LLMatrix4& LLMatrix4::initScale(const LLVector3 &scale)
-{
- setIdentity();
-
- mMatrix[VX][VX] = scale.mV[VX];
- mMatrix[VY][VY] = scale.mV[VY];
- mMatrix[VZ][VZ] = scale.mV[VZ];
-
- return (*this);
-}
-
-const LLMatrix4& LLMatrix4::initAll(const LLVector3 &scale, const LLQuaternion &q, const LLVector3 &pos)
-{
- F32 sx, sy, sz;
- F32 xx, xy, xz, xw, yy, yz, yw, zz, zw;
-
- sx = scale.mV[0];
- sy = scale.mV[1];
- sz = scale.mV[2];
-
- xx = q.mQ[VX] * q.mQ[VX];
- xy = q.mQ[VX] * q.mQ[VY];
- xz = q.mQ[VX] * q.mQ[VZ];
- xw = q.mQ[VX] * q.mQ[VW];
-
- yy = q.mQ[VY] * q.mQ[VY];
- yz = q.mQ[VY] * q.mQ[VZ];
- yw = q.mQ[VY] * q.mQ[VW];
-
- zz = q.mQ[VZ] * q.mQ[VZ];
- zw = q.mQ[VZ] * q.mQ[VW];
-
- mMatrix[0][0] = (1.f - 2.f * ( yy + zz )) *sx;
- mMatrix[0][1] = ( 2.f * ( xy + zw )) *sx;
- mMatrix[0][2] = ( 2.f * ( xz - yw )) *sx;
-
- mMatrix[1][0] = ( 2.f * ( xy - zw )) *sy;
- mMatrix[1][1] = (1.f - 2.f * ( xx + zz )) *sy;
- mMatrix[1][2] = ( 2.f * ( yz + xw )) *sy;
-
- mMatrix[2][0] = ( 2.f * ( xz + yw )) *sz;
- mMatrix[2][1] = ( 2.f * ( yz - xw )) *sz;
- mMatrix[2][2] = (1.f - 2.f * ( xx + yy )) *sz;
-
- mMatrix[3][0] = pos.mV[0];
- mMatrix[3][1] = pos.mV[1];
- mMatrix[3][2] = pos.mV[2];
- mMatrix[3][3] = 1.0;
-
- // TODO -- should we set the translation portion to zero?
- return (*this);
-}
-
-const LLMatrix4& LLMatrix4::rotate(const F32 angle, const LLVector4 &vec)
-{
- LLMatrix4 mat(angle, vec);
- *this *= mat;
- return *this;
-}
-
-const LLMatrix4& LLMatrix4::rotate(const F32 roll, const F32 pitch, const F32 yaw)
-{
- LLMatrix4 mat(roll, pitch, yaw);
- *this *= mat;
- return *this;
-}
-
-const LLMatrix4& LLMatrix4::rotate(const LLQuaternion &q)
-{
- LLMatrix4 mat(q);
- *this *= mat;
- return *this;
-}
-
-
-const LLMatrix4& LLMatrix4::translate(const LLVector3 &vec)
-{
- mMatrix[3][0] += vec.mV[0];
- mMatrix[3][1] += vec.mV[1];
- mMatrix[3][2] += vec.mV[2];
- return (*this);
-}
-
-
-void LLMatrix4::setFwdRow(const LLVector3 &row)
-{
- mMatrix[VX][VX] = row.mV[VX];
- mMatrix[VX][VY] = row.mV[VY];
- mMatrix[VX][VZ] = row.mV[VZ];
-}
-
-void LLMatrix4::setLeftRow(const LLVector3 &row)
-{
- mMatrix[VY][VX] = row.mV[VX];
- mMatrix[VY][VY] = row.mV[VY];
- mMatrix[VY][VZ] = row.mV[VZ];
-}
-
-void LLMatrix4::setUpRow(const LLVector3 &row)
-{
- mMatrix[VZ][VX] = row.mV[VX];
- mMatrix[VZ][VY] = row.mV[VY];
- mMatrix[VZ][VZ] = row.mV[VZ];
-}
-
-
-void LLMatrix4::setFwdCol(const LLVector3 &col)
-{
- mMatrix[VX][VX] = col.mV[VX];
- mMatrix[VY][VX] = col.mV[VY];
- mMatrix[VZ][VX] = col.mV[VZ];
-}
-
-void LLMatrix4::setLeftCol(const LLVector3 &col)
-{
- mMatrix[VX][VY] = col.mV[VX];
- mMatrix[VY][VY] = col.mV[VY];
- mMatrix[VZ][VY] = col.mV[VZ];
-}
-
-void LLMatrix4::setUpCol(const LLVector3 &col)
-{
- mMatrix[VX][VZ] = col.mV[VX];
- mMatrix[VY][VZ] = col.mV[VY];
- mMatrix[VZ][VZ] = col.mV[VZ];
-}
-
-
-const LLMatrix4& LLMatrix4::setTranslation(const F32 tx, const F32 ty, const F32 tz)
-{
- mMatrix[VW][VX] = tx;
- mMatrix[VW][VY] = ty;
- mMatrix[VW][VZ] = tz;
- return (*this);
-}
-
-const LLMatrix4& LLMatrix4::setTranslation(const LLVector3 &translation)
-{
- mMatrix[VW][VX] = translation.mV[VX];
- mMatrix[VW][VY] = translation.mV[VY];
- mMatrix[VW][VZ] = translation.mV[VZ];
- return (*this);
-}
-
-const LLMatrix4& LLMatrix4::setTranslation(const LLVector4 &translation)
-{
- mMatrix[VW][VX] = translation.mV[VX];
- mMatrix[VW][VY] = translation.mV[VY];
- mMatrix[VW][VZ] = translation.mV[VZ];
- return (*this);
-}
-
-// LLMatrix3 Extraction and Setting
-LLMatrix3 LLMatrix4::getMat3() const
-{
- LLMatrix3 retmat;
-
- retmat.mMatrix[0][0] = mMatrix[0][0];
- retmat.mMatrix[0][1] = mMatrix[0][1];
- retmat.mMatrix[0][2] = mMatrix[0][2];
-
- retmat.mMatrix[1][0] = mMatrix[1][0];
- retmat.mMatrix[1][1] = mMatrix[1][1];
- retmat.mMatrix[1][2] = mMatrix[1][2];
-
- retmat.mMatrix[2][0] = mMatrix[2][0];
- retmat.mMatrix[2][1] = mMatrix[2][1];
- retmat.mMatrix[2][2] = mMatrix[2][2];
-
- return retmat;
-}
-
-const LLMatrix4& LLMatrix4::initMatrix(const LLMatrix3 &mat)
-{
- mMatrix[0][0] = mat.mMatrix[0][0];
- mMatrix[0][1] = mat.mMatrix[0][1];
- mMatrix[0][2] = mat.mMatrix[0][2];
- mMatrix[0][3] = 0.f;
-
- mMatrix[1][0] = mat.mMatrix[1][0];
- mMatrix[1][1] = mat.mMatrix[1][1];
- mMatrix[1][2] = mat.mMatrix[1][2];
- mMatrix[1][3] = 0.f;
-
- mMatrix[2][0] = mat.mMatrix[2][0];
- mMatrix[2][1] = mat.mMatrix[2][1];
- mMatrix[2][2] = mat.mMatrix[2][2];
- mMatrix[2][3] = 0.f;
-
- mMatrix[3][0] = 0.f;
- mMatrix[3][1] = 0.f;
- mMatrix[3][2] = 0.f;
- mMatrix[3][3] = 1.f;
- return (*this);
-}
-
-const LLMatrix4& LLMatrix4::initMatrix(const LLMatrix3 &mat, const LLVector4 &translation)
-{
- mMatrix[0][0] = mat.mMatrix[0][0];
- mMatrix[0][1] = mat.mMatrix[0][1];
- mMatrix[0][2] = mat.mMatrix[0][2];
- mMatrix[0][3] = 0.f;
-
- mMatrix[1][0] = mat.mMatrix[1][0];
- mMatrix[1][1] = mat.mMatrix[1][1];
- mMatrix[1][2] = mat.mMatrix[1][2];
- mMatrix[1][3] = 0.f;
-
- mMatrix[2][0] = mat.mMatrix[2][0];
- mMatrix[2][1] = mat.mMatrix[2][1];
- mMatrix[2][2] = mat.mMatrix[2][2];
- mMatrix[2][3] = 0.f;
-
- mMatrix[3][0] = translation.mV[0];
- mMatrix[3][1] = translation.mV[1];
- mMatrix[3][2] = translation.mV[2];
- mMatrix[3][3] = 1.f;
- return (*this);
-}
-
-// LLMatrix4 Operators
-
-LLVector4 operator*(const LLVector4 &a, const LLMatrix4 &b)
-{
- // Operate "to the left" on row-vector a
- return LLVector4(a.mV[VX] * b.mMatrix[VX][VX] +
- a.mV[VY] * b.mMatrix[VY][VX] +
- a.mV[VZ] * b.mMatrix[VZ][VX] +
- a.mV[VW] * b.mMatrix[VW][VX],
-
- a.mV[VX] * b.mMatrix[VX][VY] +
- a.mV[VY] * b.mMatrix[VY][VY] +
- a.mV[VZ] * b.mMatrix[VZ][VY] +
- a.mV[VW] * b.mMatrix[VW][VY],
-
- a.mV[VX] * b.mMatrix[VX][VZ] +
- a.mV[VY] * b.mMatrix[VY][VZ] +
- a.mV[VZ] * b.mMatrix[VZ][VZ] +
- a.mV[VW] * b.mMatrix[VW][VZ],
-
- a.mV[VX] * b.mMatrix[VX][VW] +
- a.mV[VY] * b.mMatrix[VY][VW] +
- a.mV[VZ] * b.mMatrix[VZ][VW] +
- a.mV[VW] * b.mMatrix[VW][VW]);
-}
-
-LLVector4 rotate_vector(const LLVector4 &a, const LLMatrix4 &b)
-{
- // Rotates but does not translate
- // Operate "to the left" on row-vector a
- LLVector4 vec;
- vec.mV[VX] = a.mV[VX] * b.mMatrix[VX][VX] +
- a.mV[VY] * b.mMatrix[VY][VX] +
- a.mV[VZ] * b.mMatrix[VZ][VX];
-
- vec.mV[VY] = a.mV[VX] * b.mMatrix[VX][VY] +
- a.mV[VY] * b.mMatrix[VY][VY] +
- a.mV[VZ] * b.mMatrix[VZ][VY];
-
- vec.mV[VZ] = a.mV[VX] * b.mMatrix[VX][VZ] +
- a.mV[VY] * b.mMatrix[VY][VZ] +
- a.mV[VZ] * b.mMatrix[VZ][VZ];
-
-// vec.mV[VW] = a.mV[VX] * b.mMatrix[VX][VW] +
-// a.mV[VY] * b.mMatrix[VY][VW] +
-// a.mV[VZ] * b.mMatrix[VZ][VW] +
- vec.mV[VW] = a.mV[VW];
- return vec;
-}
-
-LLVector3 rotate_vector(const LLVector3 &a, const LLMatrix4 &b)
-{
- // Rotates but does not translate
- // Operate "to the left" on row-vector a
- LLVector3 vec;
- vec.mV[VX] = a.mV[VX] * b.mMatrix[VX][VX] +
- a.mV[VY] * b.mMatrix[VY][VX] +
- a.mV[VZ] * b.mMatrix[VZ][VX];
-
- vec.mV[VY] = a.mV[VX] * b.mMatrix[VX][VY] +
- a.mV[VY] * b.mMatrix[VY][VY] +
- a.mV[VZ] * b.mMatrix[VZ][VY];
-
- vec.mV[VZ] = a.mV[VX] * b.mMatrix[VX][VZ] +
- a.mV[VY] * b.mMatrix[VY][VZ] +
- a.mV[VZ] * b.mMatrix[VZ][VZ];
- return vec;
-}
-
-bool operator==(const LLMatrix4 &a, const LLMatrix4 &b)
-{
- U32 i, j;
- for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
- {
- for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
- {
- if (a.mMatrix[j][i] != b.mMatrix[j][i])
- return false;
- }
- }
- return true;
-}
-
-bool operator!=(const LLMatrix4 &a, const LLMatrix4 &b)
-{
- U32 i, j;
- for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
- {
- for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
- {
- if (a.mMatrix[j][i] != b.mMatrix[j][i])
- return true;
- }
- }
- return false;
-}
-
-bool operator<(const LLMatrix4& a, const LLMatrix4 &b)
-{
- U32 i, j;
- for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
- {
- for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
- {
- if (a.mMatrix[i][j] != b.mMatrix[i][j])
- {
- return a.mMatrix[i][j] < b.mMatrix[i][j];
- }
- }
- }
-
- return false;
-}
-
-const LLMatrix4& operator*=(LLMatrix4 &a, F32 k)
-{
- U32 i, j;
- for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
- {
- for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
- {
- a.mMatrix[j][i] *= k;
- }
- }
- return a;
-}
-
-std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a)
-{
- s << "{ "
- << a.mMatrix[VX][VX] << ", "
- << a.mMatrix[VX][VY] << ", "
- << a.mMatrix[VX][VZ] << ", "
- << a.mMatrix[VX][VW]
- << "; "
- << a.mMatrix[VY][VX] << ", "
- << a.mMatrix[VY][VY] << ", "
- << a.mMatrix[VY][VZ] << ", "
- << a.mMatrix[VY][VW]
- << "; "
- << a.mMatrix[VZ][VX] << ", "
- << a.mMatrix[VZ][VY] << ", "
- << a.mMatrix[VZ][VZ] << ", "
- << a.mMatrix[VZ][VW]
- << "; "
- << a.mMatrix[VW][VX] << ", "
- << a.mMatrix[VW][VY] << ", "
- << a.mMatrix[VW][VZ] << ", "
- << a.mMatrix[VW][VW]
- << " }";
- return s;
-}
-
-LLSD LLMatrix4::getValue() const
-{
- LLSD ret;
-
- ret[0] = mMatrix[0][0];
- ret[1] = mMatrix[0][1];
- ret[2] = mMatrix[0][2];
- ret[3] = mMatrix[0][3];
-
- ret[4] = mMatrix[1][0];
- ret[5] = mMatrix[1][1];
- ret[6] = mMatrix[1][2];
- ret[7] = mMatrix[1][3];
-
- ret[8] = mMatrix[2][0];
- ret[9] = mMatrix[2][1];
- ret[10] = mMatrix[2][2];
- ret[11] = mMatrix[2][3];
-
- ret[12] = mMatrix[3][0];
- ret[13] = mMatrix[3][1];
- ret[14] = mMatrix[3][2];
- ret[15] = mMatrix[3][3];
-
- return ret;
-}
-
-void LLMatrix4::setValue(const LLSD& data)
-{
- mMatrix[0][0] = (F32)data[0].asReal();
- mMatrix[0][1] = (F32)data[1].asReal();
- mMatrix[0][2] = (F32)data[2].asReal();
- mMatrix[0][3] = (F32)data[3].asReal();
-
- mMatrix[1][0] = (F32)data[4].asReal();
- mMatrix[1][1] = (F32)data[5].asReal();
- mMatrix[1][2] = (F32)data[6].asReal();
- mMatrix[1][3] = (F32)data[7].asReal();
-
- mMatrix[2][0] = (F32)data[8].asReal();
- mMatrix[2][1] = (F32)data[9].asReal();
- mMatrix[2][2] = (F32)data[10].asReal();
- mMatrix[2][3] = (F32)data[11].asReal();
-
- mMatrix[3][0] = (F32)data[12].asReal();
- mMatrix[3][1] = (F32)data[13].asReal();
- mMatrix[3][2] = (F32)data[14].asReal();
- mMatrix[3][3] = (F32)data[15].asReal();
-}
-
-
+/** + * @file m4math.cpp + * @brief LLMatrix4 class implementation. + * + * $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 "linden_common.h" + +//#include "vmath.h" +#include "v3math.h" +#include "v4math.h" +#include "m4math.h" +#include "m3math.h" +#include "llquaternion.h" +#include "llmatrix4a.h" + + +// LLMatrix4 + +// Constructors + + +LLMatrix4::LLMatrix4(const F32 *mat) +{ + mMatrix[0][0] = mat[0]; + mMatrix[0][1] = mat[1]; + mMatrix[0][2] = mat[2]; + mMatrix[0][3] = mat[3]; + + mMatrix[1][0] = mat[4]; + mMatrix[1][1] = mat[5]; + mMatrix[1][2] = mat[6]; + mMatrix[1][3] = mat[7]; + + mMatrix[2][0] = mat[8]; + mMatrix[2][1] = mat[9]; + mMatrix[2][2] = mat[10]; + mMatrix[2][3] = mat[11]; + + mMatrix[3][0] = mat[12]; + mMatrix[3][1] = mat[13]; + mMatrix[3][2] = mat[14]; + mMatrix[3][3] = mat[15]; +} + +LLMatrix4::LLMatrix4(const LLMatrix3 &mat, const LLVector4 &vec) +{ + mMatrix[0][0] = mat.mMatrix[0][0]; + mMatrix[0][1] = mat.mMatrix[0][1]; + mMatrix[0][2] = mat.mMatrix[0][2]; + mMatrix[0][3] = 0.f; + + mMatrix[1][0] = mat.mMatrix[1][0]; + mMatrix[1][1] = mat.mMatrix[1][1]; + mMatrix[1][2] = mat.mMatrix[1][2]; + mMatrix[1][3] = 0.f; + + mMatrix[2][0] = mat.mMatrix[2][0]; + mMatrix[2][1] = mat.mMatrix[2][1]; + mMatrix[2][2] = mat.mMatrix[2][2]; + mMatrix[2][3] = 0.f; + + mMatrix[3][0] = vec.mV[0]; + mMatrix[3][1] = vec.mV[1]; + mMatrix[3][2] = vec.mV[2]; + mMatrix[3][3] = 1.f; +} + +LLMatrix4::LLMatrix4(const LLMatrix3 &mat) +{ + mMatrix[0][0] = mat.mMatrix[0][0]; + mMatrix[0][1] = mat.mMatrix[0][1]; + mMatrix[0][2] = mat.mMatrix[0][2]; + mMatrix[0][3] = 0.f; + + mMatrix[1][0] = mat.mMatrix[1][0]; + mMatrix[1][1] = mat.mMatrix[1][1]; + mMatrix[1][2] = mat.mMatrix[1][2]; + mMatrix[1][3] = 0.f; + + mMatrix[2][0] = mat.mMatrix[2][0]; + mMatrix[2][1] = mat.mMatrix[2][1]; + mMatrix[2][2] = mat.mMatrix[2][2]; + mMatrix[2][3] = 0.f; + + mMatrix[3][0] = 0.f; + mMatrix[3][1] = 0.f; + mMatrix[3][2] = 0.f; + mMatrix[3][3] = 1.f; +} + +LLMatrix4::LLMatrix4(const LLQuaternion &q) +{ + *this = initRotation(q); +} + +LLMatrix4::LLMatrix4(const LLMatrix4a& mat) + : LLMatrix4(mat.getF32ptr()) +{ + +} + +LLMatrix4::LLMatrix4(const LLQuaternion &q, const LLVector4 &pos) +{ + *this = initRotTrans(q, pos); +} + +LLMatrix4::LLMatrix4(const F32 angle, const LLVector4 &vec, const LLVector4 &pos) +{ + initRotTrans(LLQuaternion(angle, vec), pos); +} + +LLMatrix4::LLMatrix4(const F32 angle, const LLVector4 &vec) +{ + initRotation(LLQuaternion(angle, vec)); + + mMatrix[3][0] = 0.f; + mMatrix[3][1] = 0.f; + mMatrix[3][2] = 0.f; + mMatrix[3][3] = 1.f; +} + +LLMatrix4::LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &pos) +{ + LLMatrix3 mat(roll, pitch, yaw); + initRotTrans(LLQuaternion(mat), pos); +} + +LLMatrix4::LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw) +{ + LLMatrix3 mat(roll, pitch, yaw); + initRotation(LLQuaternion(mat)); + + mMatrix[3][0] = 0.f; + mMatrix[3][1] = 0.f; + mMatrix[3][2] = 0.f; + mMatrix[3][3] = 1.f; +} + +LLMatrix4::~LLMatrix4(void) +{ +} + +// Clear and Assignment Functions + +const LLMatrix4& LLMatrix4::setZero() +{ + mMatrix[0][0] = 0.f; + mMatrix[0][1] = 0.f; + mMatrix[0][2] = 0.f; + mMatrix[0][3] = 0.f; + + mMatrix[1][0] = 0.f; + mMatrix[1][1] = 0.f; + mMatrix[1][2] = 0.f; + mMatrix[1][3] = 0.f; + + mMatrix[2][0] = 0.f; + mMatrix[2][1] = 0.f; + mMatrix[2][2] = 0.f; + mMatrix[2][3] = 0.f; + + mMatrix[3][0] = 0.f; + mMatrix[3][1] = 0.f; + mMatrix[3][2] = 0.f; + mMatrix[3][3] = 0.f; + return *this; +} + + +// various useful mMatrix functions + +const LLMatrix4& LLMatrix4::transpose() +{ + LLMatrix4 mat; + mat.mMatrix[0][0] = mMatrix[0][0]; + mat.mMatrix[1][0] = mMatrix[0][1]; + mat.mMatrix[2][0] = mMatrix[0][2]; + mat.mMatrix[3][0] = mMatrix[0][3]; + + mat.mMatrix[0][1] = mMatrix[1][0]; + mat.mMatrix[1][1] = mMatrix[1][1]; + mat.mMatrix[2][1] = mMatrix[1][2]; + mat.mMatrix[3][1] = mMatrix[1][3]; + + mat.mMatrix[0][2] = mMatrix[2][0]; + mat.mMatrix[1][2] = mMatrix[2][1]; + mat.mMatrix[2][2] = mMatrix[2][2]; + mat.mMatrix[3][2] = mMatrix[2][3]; + + mat.mMatrix[0][3] = mMatrix[3][0]; + mat.mMatrix[1][3] = mMatrix[3][1]; + mat.mMatrix[2][3] = mMatrix[3][2]; + mat.mMatrix[3][3] = mMatrix[3][3]; + + *this = mat; + return *this; +} + + +F32 LLMatrix4::determinant() const +{ + F32 value = + mMatrix[0][3] * mMatrix[1][2] * mMatrix[2][1] * mMatrix[3][0] - + mMatrix[0][2] * mMatrix[1][3] * mMatrix[2][1] * mMatrix[3][0] - + mMatrix[0][3] * mMatrix[1][1] * mMatrix[2][2] * mMatrix[3][0] + + mMatrix[0][1] * mMatrix[1][3] * mMatrix[2][2] * mMatrix[3][0] + + mMatrix[0][2] * mMatrix[1][1] * mMatrix[2][3] * mMatrix[3][0] - + mMatrix[0][1] * mMatrix[1][2] * mMatrix[2][3] * mMatrix[3][0] - + mMatrix[0][3] * mMatrix[1][2] * mMatrix[2][0] * mMatrix[3][1] + + mMatrix[0][2] * mMatrix[1][3] * mMatrix[2][0] * mMatrix[3][1] + + mMatrix[0][3] * mMatrix[1][0] * mMatrix[2][2] * mMatrix[3][1] - + mMatrix[0][0] * mMatrix[1][3] * mMatrix[2][2] * mMatrix[3][1] - + mMatrix[0][2] * mMatrix[1][0] * mMatrix[2][3] * mMatrix[3][1] + + mMatrix[0][0] * mMatrix[1][2] * mMatrix[2][3] * mMatrix[3][1] + + mMatrix[0][3] * mMatrix[1][1] * mMatrix[2][0] * mMatrix[3][2] - + mMatrix[0][1] * mMatrix[1][3] * mMatrix[2][0] * mMatrix[3][2] - + mMatrix[0][3] * mMatrix[1][0] * mMatrix[2][1] * mMatrix[3][2] + + mMatrix[0][0] * mMatrix[1][3] * mMatrix[2][1] * mMatrix[3][2] + + mMatrix[0][1] * mMatrix[1][0] * mMatrix[2][3] * mMatrix[3][2] - + mMatrix[0][0] * mMatrix[1][1] * mMatrix[2][3] * mMatrix[3][2] - + mMatrix[0][2] * mMatrix[1][1] * mMatrix[2][0] * mMatrix[3][3] + + mMatrix[0][1] * mMatrix[1][2] * mMatrix[2][0] * mMatrix[3][3] + + mMatrix[0][2] * mMatrix[1][0] * mMatrix[2][1] * mMatrix[3][3] - + mMatrix[0][0] * mMatrix[1][2] * mMatrix[2][1] * mMatrix[3][3] - + mMatrix[0][1] * mMatrix[1][0] * mMatrix[2][2] * mMatrix[3][3] + + mMatrix[0][0] * mMatrix[1][1] * mMatrix[2][2] * mMatrix[3][3]; + + return value; +} + +// Only works for pure orthonormal, homogeneous transform matrices. +const LLMatrix4& LLMatrix4::invert(void) +{ + // transpose the rotation part + F32 temp; + temp = mMatrix[VX][VY]; mMatrix[VX][VY] = mMatrix[VY][VX]; mMatrix[VY][VX] = temp; + temp = mMatrix[VX][VZ]; mMatrix[VX][VZ] = mMatrix[VZ][VX]; mMatrix[VZ][VX] = temp; + temp = mMatrix[VY][VZ]; mMatrix[VY][VZ] = mMatrix[VZ][VY]; mMatrix[VZ][VY] = temp; + + // rotate the translation part by the new rotation + // (temporarily store in empty column of matrix) + U32 j; + for (j=0; j<3; j++) + { + mMatrix[j][VW] = mMatrix[VW][VX] * mMatrix[VX][j] + + mMatrix[VW][VY] * mMatrix[VY][j] + + mMatrix[VW][VZ] * mMatrix[VZ][j]; + } + + // negate and copy the temporary vector back to the tranlation row + mMatrix[VW][VX] = -mMatrix[VX][VW]; + mMatrix[VW][VY] = -mMatrix[VY][VW]; + mMatrix[VW][VZ] = -mMatrix[VZ][VW]; + + // zero the empty column again + mMatrix[VX][VW] = mMatrix[VY][VW] = mMatrix[VZ][VW] = 0.0f; + + return *this; +} + +// Convenience func for simplifying comparison-heavy code by +// intentionally stomping values in [-FLT_EPS,FLT_EPS] to 0.0f +// +void LLMatrix4::condition(void) +{ + U32 i; + U32 j; + for (i = 0; i < 3;i++) + for (j = 0; j < 3;j++) + mMatrix[i][j] = ((mMatrix[i][j] > -FLT_EPSILON) + && (mMatrix[i][j] < FLT_EPSILON)) ? 0.0f : mMatrix[i][j]; +} + +LLVector4 LLMatrix4::getFwdRow4() const +{ + return LLVector4(mMatrix[VX][VX], mMatrix[VX][VY], mMatrix[VX][VZ], mMatrix[VX][VW]); +} + +LLVector4 LLMatrix4::getLeftRow4() const +{ + return LLVector4(mMatrix[VY][VX], mMatrix[VY][VY], mMatrix[VY][VZ], mMatrix[VY][VW]); +} + +LLVector4 LLMatrix4::getUpRow4() const +{ + return LLVector4(mMatrix[VZ][VX], mMatrix[VZ][VY], mMatrix[VZ][VZ], mMatrix[VZ][VW]); +} + +// SJB: This code is correct for a logicly stored (non-transposed) matrix; +// Our matrices are stored transposed, OpenGL style, so this generates the +// INVERSE quaternion (-x, -y, -z, w)! +// Because we use similar logic in LLQuaternion::getMatrix3, +// we are internally consistant so everything works OK :) +LLQuaternion LLMatrix4::quaternion() const +{ + LLQuaternion quat; + F32 tr, s, q[4]; + U32 i, j, k; + U32 nxt[3] = {1, 2, 0}; + + tr = mMatrix[0][0] + mMatrix[1][1] + mMatrix[2][2]; + + // check the diagonal + if (tr > 0.f) + { + s = (F32)sqrt (tr + 1.f); + quat.mQ[VS] = s / 2.f; + s = 0.5f / s; + quat.mQ[VX] = (mMatrix[1][2] - mMatrix[2][1]) * s; + quat.mQ[VY] = (mMatrix[2][0] - mMatrix[0][2]) * s; + quat.mQ[VZ] = (mMatrix[0][1] - mMatrix[1][0]) * s; + } + else + { + // diagonal is negative + i = 0; + if (mMatrix[1][1] > mMatrix[0][0]) + i = 1; + if (mMatrix[2][2] > mMatrix[i][i]) + i = 2; + + j = nxt[i]; + k = nxt[j]; + + + s = (F32)sqrt ((mMatrix[i][i] - (mMatrix[j][j] + mMatrix[k][k])) + 1.f); + + q[i] = s * 0.5f; + + if (s != 0.f) + s = 0.5f / s; + + q[3] = (mMatrix[j][k] - mMatrix[k][j]) * s; + q[j] = (mMatrix[i][j] + mMatrix[j][i]) * s; + q[k] = (mMatrix[i][k] + mMatrix[k][i]) * s; + + quat.setQuat(q); + } + return quat; +} + + + +void LLMatrix4::initRows(const LLVector4 &row0, + const LLVector4 &row1, + const LLVector4 &row2, + const LLVector4 &row3) +{ + mMatrix[0][0] = row0.mV[0]; + mMatrix[0][1] = row0.mV[1]; + mMatrix[0][2] = row0.mV[2]; + mMatrix[0][3] = row0.mV[3]; + + mMatrix[1][0] = row1.mV[0]; + mMatrix[1][1] = row1.mV[1]; + mMatrix[1][2] = row1.mV[2]; + mMatrix[1][3] = row1.mV[3]; + + mMatrix[2][0] = row2.mV[0]; + mMatrix[2][1] = row2.mV[1]; + mMatrix[2][2] = row2.mV[2]; + mMatrix[2][3] = row2.mV[3]; + + mMatrix[3][0] = row3.mV[0]; + mMatrix[3][1] = row3.mV[1]; + mMatrix[3][2] = row3.mV[2]; + mMatrix[3][3] = row3.mV[3]; +} + + +const LLMatrix4& LLMatrix4::initRotation(F32 angle, const LLVector4 &vec) +{ + LLMatrix3 mat(angle, vec); + return initMatrix(mat); +} + + +const LLMatrix4& LLMatrix4::initRotation(const F32 roll, const F32 pitch, const F32 yaw) +{ + LLMatrix3 mat(roll, pitch, yaw); + return initMatrix(mat); +} + + +const LLMatrix4& LLMatrix4::initRotation(const LLQuaternion &q) +{ + LLMatrix3 mat(q); + return initMatrix(mat); +} + + +const LLMatrix4& LLMatrix4::initRotTrans(const F32 angle, const LLVector3 &axis, const LLVector3&translation) +{ + LLMatrix3 mat(angle, axis); + initMatrix(mat); + setTranslation(translation); + return (*this); +} + +const LLMatrix4& LLMatrix4::initRotTrans(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &translation) +{ + LLMatrix3 mat(roll, pitch, yaw); + initMatrix(mat); + setTranslation(translation); + return (*this); +} + +/* +const LLMatrix4& LLMatrix4::initRotTrans(const LLVector4 &fwd, + const LLVector4 &left, + const LLVector4 &up, + const LLVector4 &translation) +{ + LLMatrix3 mat(fwd, left, up); + initMatrix(mat); + setTranslation(translation); + return (*this); +} +*/ + +const LLMatrix4& LLMatrix4::initRotTrans(const LLQuaternion &q, const LLVector4 &translation) +{ + LLMatrix3 mat(q); + initMatrix(mat); + setTranslation(translation); + return (*this); +} + +const LLMatrix4& LLMatrix4::initScale(const LLVector3 &scale) +{ + setIdentity(); + + mMatrix[VX][VX] = scale.mV[VX]; + mMatrix[VY][VY] = scale.mV[VY]; + mMatrix[VZ][VZ] = scale.mV[VZ]; + + return (*this); +} + +const LLMatrix4& LLMatrix4::initAll(const LLVector3 &scale, const LLQuaternion &q, const LLVector3 &pos) +{ + F32 sx, sy, sz; + F32 xx, xy, xz, xw, yy, yz, yw, zz, zw; + + sx = scale.mV[0]; + sy = scale.mV[1]; + sz = scale.mV[2]; + + xx = q.mQ[VX] * q.mQ[VX]; + xy = q.mQ[VX] * q.mQ[VY]; + xz = q.mQ[VX] * q.mQ[VZ]; + xw = q.mQ[VX] * q.mQ[VW]; + + yy = q.mQ[VY] * q.mQ[VY]; + yz = q.mQ[VY] * q.mQ[VZ]; + yw = q.mQ[VY] * q.mQ[VW]; + + zz = q.mQ[VZ] * q.mQ[VZ]; + zw = q.mQ[VZ] * q.mQ[VW]; + + mMatrix[0][0] = (1.f - 2.f * ( yy + zz )) *sx; + mMatrix[0][1] = ( 2.f * ( xy + zw )) *sx; + mMatrix[0][2] = ( 2.f * ( xz - yw )) *sx; + + mMatrix[1][0] = ( 2.f * ( xy - zw )) *sy; + mMatrix[1][1] = (1.f - 2.f * ( xx + zz )) *sy; + mMatrix[1][2] = ( 2.f * ( yz + xw )) *sy; + + mMatrix[2][0] = ( 2.f * ( xz + yw )) *sz; + mMatrix[2][1] = ( 2.f * ( yz - xw )) *sz; + mMatrix[2][2] = (1.f - 2.f * ( xx + yy )) *sz; + + mMatrix[3][0] = pos.mV[0]; + mMatrix[3][1] = pos.mV[1]; + mMatrix[3][2] = pos.mV[2]; + mMatrix[3][3] = 1.0; + + // TODO -- should we set the translation portion to zero? + return (*this); +} + +const LLMatrix4& LLMatrix4::rotate(const F32 angle, const LLVector4 &vec) +{ + LLMatrix4 mat(angle, vec); + *this *= mat; + return *this; +} + +const LLMatrix4& LLMatrix4::rotate(const F32 roll, const F32 pitch, const F32 yaw) +{ + LLMatrix4 mat(roll, pitch, yaw); + *this *= mat; + return *this; +} + +const LLMatrix4& LLMatrix4::rotate(const LLQuaternion &q) +{ + LLMatrix4 mat(q); + *this *= mat; + return *this; +} + + +const LLMatrix4& LLMatrix4::translate(const LLVector3 &vec) +{ + mMatrix[3][0] += vec.mV[0]; + mMatrix[3][1] += vec.mV[1]; + mMatrix[3][2] += vec.mV[2]; + return (*this); +} + + +void LLMatrix4::setFwdRow(const LLVector3 &row) +{ + mMatrix[VX][VX] = row.mV[VX]; + mMatrix[VX][VY] = row.mV[VY]; + mMatrix[VX][VZ] = row.mV[VZ]; +} + +void LLMatrix4::setLeftRow(const LLVector3 &row) +{ + mMatrix[VY][VX] = row.mV[VX]; + mMatrix[VY][VY] = row.mV[VY]; + mMatrix[VY][VZ] = row.mV[VZ]; +} + +void LLMatrix4::setUpRow(const LLVector3 &row) +{ + mMatrix[VZ][VX] = row.mV[VX]; + mMatrix[VZ][VY] = row.mV[VY]; + mMatrix[VZ][VZ] = row.mV[VZ]; +} + + +void LLMatrix4::setFwdCol(const LLVector3 &col) +{ + mMatrix[VX][VX] = col.mV[VX]; + mMatrix[VY][VX] = col.mV[VY]; + mMatrix[VZ][VX] = col.mV[VZ]; +} + +void LLMatrix4::setLeftCol(const LLVector3 &col) +{ + mMatrix[VX][VY] = col.mV[VX]; + mMatrix[VY][VY] = col.mV[VY]; + mMatrix[VZ][VY] = col.mV[VZ]; +} + +void LLMatrix4::setUpCol(const LLVector3 &col) +{ + mMatrix[VX][VZ] = col.mV[VX]; + mMatrix[VY][VZ] = col.mV[VY]; + mMatrix[VZ][VZ] = col.mV[VZ]; +} + + +const LLMatrix4& LLMatrix4::setTranslation(const F32 tx, const F32 ty, const F32 tz) +{ + mMatrix[VW][VX] = tx; + mMatrix[VW][VY] = ty; + mMatrix[VW][VZ] = tz; + return (*this); +} + +const LLMatrix4& LLMatrix4::setTranslation(const LLVector3 &translation) +{ + mMatrix[VW][VX] = translation.mV[VX]; + mMatrix[VW][VY] = translation.mV[VY]; + mMatrix[VW][VZ] = translation.mV[VZ]; + return (*this); +} + +const LLMatrix4& LLMatrix4::setTranslation(const LLVector4 &translation) +{ + mMatrix[VW][VX] = translation.mV[VX]; + mMatrix[VW][VY] = translation.mV[VY]; + mMatrix[VW][VZ] = translation.mV[VZ]; + return (*this); +} + +// LLMatrix3 Extraction and Setting +LLMatrix3 LLMatrix4::getMat3() const +{ + LLMatrix3 retmat; + + retmat.mMatrix[0][0] = mMatrix[0][0]; + retmat.mMatrix[0][1] = mMatrix[0][1]; + retmat.mMatrix[0][2] = mMatrix[0][2]; + + retmat.mMatrix[1][0] = mMatrix[1][0]; + retmat.mMatrix[1][1] = mMatrix[1][1]; + retmat.mMatrix[1][2] = mMatrix[1][2]; + + retmat.mMatrix[2][0] = mMatrix[2][0]; + retmat.mMatrix[2][1] = mMatrix[2][1]; + retmat.mMatrix[2][2] = mMatrix[2][2]; + + return retmat; +} + +const LLMatrix4& LLMatrix4::initMatrix(const LLMatrix3 &mat) +{ + mMatrix[0][0] = mat.mMatrix[0][0]; + mMatrix[0][1] = mat.mMatrix[0][1]; + mMatrix[0][2] = mat.mMatrix[0][2]; + mMatrix[0][3] = 0.f; + + mMatrix[1][0] = mat.mMatrix[1][0]; + mMatrix[1][1] = mat.mMatrix[1][1]; + mMatrix[1][2] = mat.mMatrix[1][2]; + mMatrix[1][3] = 0.f; + + mMatrix[2][0] = mat.mMatrix[2][0]; + mMatrix[2][1] = mat.mMatrix[2][1]; + mMatrix[2][2] = mat.mMatrix[2][2]; + mMatrix[2][3] = 0.f; + + mMatrix[3][0] = 0.f; + mMatrix[3][1] = 0.f; + mMatrix[3][2] = 0.f; + mMatrix[3][3] = 1.f; + return (*this); +} + +const LLMatrix4& LLMatrix4::initMatrix(const LLMatrix3 &mat, const LLVector4 &translation) +{ + mMatrix[0][0] = mat.mMatrix[0][0]; + mMatrix[0][1] = mat.mMatrix[0][1]; + mMatrix[0][2] = mat.mMatrix[0][2]; + mMatrix[0][3] = 0.f; + + mMatrix[1][0] = mat.mMatrix[1][0]; + mMatrix[1][1] = mat.mMatrix[1][1]; + mMatrix[1][2] = mat.mMatrix[1][2]; + mMatrix[1][3] = 0.f; + + mMatrix[2][0] = mat.mMatrix[2][0]; + mMatrix[2][1] = mat.mMatrix[2][1]; + mMatrix[2][2] = mat.mMatrix[2][2]; + mMatrix[2][3] = 0.f; + + mMatrix[3][0] = translation.mV[0]; + mMatrix[3][1] = translation.mV[1]; + mMatrix[3][2] = translation.mV[2]; + mMatrix[3][3] = 1.f; + return (*this); +} + +// LLMatrix4 Operators + +LLVector4 operator*(const LLVector4 &a, const LLMatrix4 &b) +{ + // Operate "to the left" on row-vector a + return LLVector4(a.mV[VX] * b.mMatrix[VX][VX] + + a.mV[VY] * b.mMatrix[VY][VX] + + a.mV[VZ] * b.mMatrix[VZ][VX] + + a.mV[VW] * b.mMatrix[VW][VX], + + a.mV[VX] * b.mMatrix[VX][VY] + + a.mV[VY] * b.mMatrix[VY][VY] + + a.mV[VZ] * b.mMatrix[VZ][VY] + + a.mV[VW] * b.mMatrix[VW][VY], + + a.mV[VX] * b.mMatrix[VX][VZ] + + a.mV[VY] * b.mMatrix[VY][VZ] + + a.mV[VZ] * b.mMatrix[VZ][VZ] + + a.mV[VW] * b.mMatrix[VW][VZ], + + a.mV[VX] * b.mMatrix[VX][VW] + + a.mV[VY] * b.mMatrix[VY][VW] + + a.mV[VZ] * b.mMatrix[VZ][VW] + + a.mV[VW] * b.mMatrix[VW][VW]); +} + +LLVector4 rotate_vector(const LLVector4 &a, const LLMatrix4 &b) +{ + // Rotates but does not translate + // Operate "to the left" on row-vector a + LLVector4 vec; + vec.mV[VX] = a.mV[VX] * b.mMatrix[VX][VX] + + a.mV[VY] * b.mMatrix[VY][VX] + + a.mV[VZ] * b.mMatrix[VZ][VX]; + + vec.mV[VY] = a.mV[VX] * b.mMatrix[VX][VY] + + a.mV[VY] * b.mMatrix[VY][VY] + + a.mV[VZ] * b.mMatrix[VZ][VY]; + + vec.mV[VZ] = a.mV[VX] * b.mMatrix[VX][VZ] + + a.mV[VY] * b.mMatrix[VY][VZ] + + a.mV[VZ] * b.mMatrix[VZ][VZ]; + +// vec.mV[VW] = a.mV[VX] * b.mMatrix[VX][VW] + +// a.mV[VY] * b.mMatrix[VY][VW] + +// a.mV[VZ] * b.mMatrix[VZ][VW] + + vec.mV[VW] = a.mV[VW]; + return vec; +} + +LLVector3 rotate_vector(const LLVector3 &a, const LLMatrix4 &b) +{ + // Rotates but does not translate + // Operate "to the left" on row-vector a + LLVector3 vec; + vec.mV[VX] = a.mV[VX] * b.mMatrix[VX][VX] + + a.mV[VY] * b.mMatrix[VY][VX] + + a.mV[VZ] * b.mMatrix[VZ][VX]; + + vec.mV[VY] = a.mV[VX] * b.mMatrix[VX][VY] + + a.mV[VY] * b.mMatrix[VY][VY] + + a.mV[VZ] * b.mMatrix[VZ][VY]; + + vec.mV[VZ] = a.mV[VX] * b.mMatrix[VX][VZ] + + a.mV[VY] * b.mMatrix[VY][VZ] + + a.mV[VZ] * b.mMatrix[VZ][VZ]; + return vec; +} + +bool operator==(const LLMatrix4 &a, const LLMatrix4 &b) +{ + U32 i, j; + for (i = 0; i < NUM_VALUES_IN_MAT4; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT4; j++) + { + if (a.mMatrix[j][i] != b.mMatrix[j][i]) + return false; + } + } + return true; +} + +bool operator!=(const LLMatrix4 &a, const LLMatrix4 &b) +{ + U32 i, j; + for (i = 0; i < NUM_VALUES_IN_MAT4; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT4; j++) + { + if (a.mMatrix[j][i] != b.mMatrix[j][i]) + return true; + } + } + return false; +} + +bool operator<(const LLMatrix4& a, const LLMatrix4 &b) +{ + U32 i, j; + for (i = 0; i < NUM_VALUES_IN_MAT4; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT4; j++) + { + if (a.mMatrix[i][j] != b.mMatrix[i][j]) + { + return a.mMatrix[i][j] < b.mMatrix[i][j]; + } + } + } + + return false; +} + +const LLMatrix4& operator*=(LLMatrix4 &a, F32 k) +{ + U32 i, j; + for (i = 0; i < NUM_VALUES_IN_MAT4; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT4; j++) + { + a.mMatrix[j][i] *= k; + } + } + return a; +} + +std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a) +{ + s << "{ " + << a.mMatrix[VX][VX] << ", " + << a.mMatrix[VX][VY] << ", " + << a.mMatrix[VX][VZ] << ", " + << a.mMatrix[VX][VW] + << "; " + << a.mMatrix[VY][VX] << ", " + << a.mMatrix[VY][VY] << ", " + << a.mMatrix[VY][VZ] << ", " + << a.mMatrix[VY][VW] + << "; " + << a.mMatrix[VZ][VX] << ", " + << a.mMatrix[VZ][VY] << ", " + << a.mMatrix[VZ][VZ] << ", " + << a.mMatrix[VZ][VW] + << "; " + << a.mMatrix[VW][VX] << ", " + << a.mMatrix[VW][VY] << ", " + << a.mMatrix[VW][VZ] << ", " + << a.mMatrix[VW][VW] + << " }"; + return s; +} + +LLSD LLMatrix4::getValue() const +{ + LLSD ret; + + ret[0] = mMatrix[0][0]; + ret[1] = mMatrix[0][1]; + ret[2] = mMatrix[0][2]; + ret[3] = mMatrix[0][3]; + + ret[4] = mMatrix[1][0]; + ret[5] = mMatrix[1][1]; + ret[6] = mMatrix[1][2]; + ret[7] = mMatrix[1][3]; + + ret[8] = mMatrix[2][0]; + ret[9] = mMatrix[2][1]; + ret[10] = mMatrix[2][2]; + ret[11] = mMatrix[2][3]; + + ret[12] = mMatrix[3][0]; + ret[13] = mMatrix[3][1]; + ret[14] = mMatrix[3][2]; + ret[15] = mMatrix[3][3]; + + return ret; +} + +void LLMatrix4::setValue(const LLSD& data) +{ + mMatrix[0][0] = (F32)data[0].asReal(); + mMatrix[0][1] = (F32)data[1].asReal(); + mMatrix[0][2] = (F32)data[2].asReal(); + mMatrix[0][3] = (F32)data[3].asReal(); + + mMatrix[1][0] = (F32)data[4].asReal(); + mMatrix[1][1] = (F32)data[5].asReal(); + mMatrix[1][2] = (F32)data[6].asReal(); + mMatrix[1][3] = (F32)data[7].asReal(); + + mMatrix[2][0] = (F32)data[8].asReal(); + mMatrix[2][1] = (F32)data[9].asReal(); + mMatrix[2][2] = (F32)data[10].asReal(); + mMatrix[2][3] = (F32)data[11].asReal(); + + mMatrix[3][0] = (F32)data[12].asReal(); + mMatrix[3][1] = (F32)data[13].asReal(); + mMatrix[3][2] = (F32)data[14].asReal(); + mMatrix[3][3] = (F32)data[15].asReal(); +} + + diff --git a/indra/llmath/raytrace.cpp b/indra/llmath/raytrace.cpp index 6bdb1280ba..893bf1fc70 100644 --- a/indra/llmath/raytrace.cpp +++ b/indra/llmath/raytrace.cpp @@ -1,1269 +1,1269 @@ -/**
- * @file raytrace.cpp
- * @brief Functions called by box object scripts.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "math.h"
-//#include "vmath.h"
-#include "v3math.h"
-#include "llquaternion.h"
-#include "m3math.h"
-#include "raytrace.h"
-
-
-bool line_plane(const LLVector3 &line_point, const LLVector3 &line_direction,
- const LLVector3 &plane_point, const LLVector3 plane_normal,
- LLVector3 &intersection)
-{
- F32 N = line_direction * plane_normal;
- if (0.0f == N)
- {
- // line is perpendicular to plane normal
- // so it is either entirely on plane, or not on plane at all
- return false;
- }
- // Ax + By, + Cz + D = 0
- // D = - (plane_point * plane_normal)
- // N = line_direction * plane_normal
- // intersection = line_point - ((D + plane_normal * line_point) / N) * line_direction
- intersection = line_point - ((plane_normal * line_point - plane_point * plane_normal) / N) * line_direction;
- return true;
-}
-
-
-bool ray_plane(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &plane_point, const LLVector3 plane_normal,
- LLVector3 &intersection)
-{
- F32 N = ray_direction * plane_normal;
- if (0.0f == N)
- {
- // ray is perpendicular to plane normal
- // so it is either entirely on plane, or not on plane at all
- return false;
- }
- // Ax + By, + Cz + D = 0
- // D = - (plane_point * plane_normal)
- // N = ray_direction * plane_normal
- // intersection = ray_point - ((D + plane_normal * ray_point) / N) * ray_direction
- F32 alpha = -(plane_normal * ray_point - plane_point * plane_normal) / N;
- if (alpha < 0.0f)
- {
- // ray points away from plane
- return false;
- }
- intersection = ray_point + alpha * ray_direction;
- return true;
-}
-
-
-bool ray_circle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius,
- LLVector3 &intersection)
-{
- if (ray_plane(ray_point, ray_direction, circle_center, plane_normal, intersection))
- {
- if (circle_radius >= (intersection - circle_center).magVec())
- {
- return true;
- }
- }
- return false;
-}
-
-
-bool ray_triangle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 side_01 = point_1 - point_0;
- LLVector3 side_12 = point_2 - point_1;
-
- intersection_normal = side_01 % side_12;
- intersection_normal.normVec();
-
- if (ray_plane(ray_point, ray_direction, point_0, intersection_normal, intersection))
- {
- LLVector3 side_20 = point_0 - point_2;
- if (intersection_normal * (side_01 % (intersection - point_0)) >= 0.0f &&
- intersection_normal * (side_12 % (intersection - point_1)) >= 0.0f &&
- intersection_normal * (side_20 % (intersection - point_2)) >= 0.0f)
- {
- return true;
- }
- }
- return false;
-}
-
-
-// assumes a parallelogram
-bool ray_quadrangle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 side_01 = point_1 - point_0;
- LLVector3 side_12 = point_2 - point_1;
-
- intersection_normal = side_01 % side_12;
- intersection_normal.normVec();
-
- if (ray_plane(ray_point, ray_direction, point_0, intersection_normal, intersection))
- {
- LLVector3 point_3 = point_0 + (side_12);
- LLVector3 side_23 = point_3 - point_2;
- LLVector3 side_30 = point_0 - point_3;
- if (intersection_normal * (side_01 % (intersection - point_0)) >= 0.0f &&
- intersection_normal * (side_12 % (intersection - point_1)) >= 0.0f &&
- intersection_normal * (side_23 % (intersection - point_2)) >= 0.0f &&
- intersection_normal * (side_30 % (intersection - point_3)) >= 0.0f)
- {
- return true;
- }
- }
- return false;
-}
-
-
-bool ray_sphere(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &sphere_center, F32 sphere_radius,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 ray_to_sphere = sphere_center - ray_point;
- F32 dot = ray_to_sphere * ray_direction;
-
- LLVector3 closest_approach = dot * ray_direction - ray_to_sphere;
-
- F32 shortest_distance = closest_approach.magVecSquared();
- F32 radius_squared = sphere_radius * sphere_radius;
- if (shortest_distance > radius_squared)
- {
- return false;
- }
-
- F32 half_chord = (F32) sqrt(radius_squared - shortest_distance);
- closest_approach = sphere_center + closest_approach; // closest_approach now in absolute coordinates
- intersection = closest_approach + half_chord * ray_direction;
- dot = ray_direction * (intersection - ray_point);
- if (dot < 0.0f)
- {
- // ray shoots away from sphere and is not inside it
- return false;
- }
-
- shortest_distance = ray_direction * ((closest_approach - half_chord * ray_direction) - ray_point);
- if (shortest_distance > 0.0f)
- {
- // ray enters sphere
- intersection = intersection - (2.0f * half_chord) * ray_direction;
- }
- else
- {
- // do nothing
- // ray starts inside sphere and intersects as it leaves the sphere
- }
-
- intersection_normal = intersection - sphere_center;
- if (sphere_radius > 0.0f)
- {
- intersection_normal *= 1.0f / sphere_radius;
- }
- else
- {
- intersection_normal.setVec(0.0f, 0.0f, 0.0f);
- }
-
- return true;
-}
-
-
-bool ray_cylinder(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- // calculate the centers of the cylinder caps in the absolute frame
- LLVector3 cyl_top(0.0f, 0.0f, 0.5f * cyl_scale.mV[VZ]);
- LLVector3 cyl_bottom(0.0f, 0.0f, -cyl_top.mV[VZ]);
- cyl_top = (cyl_top * cyl_rotation) + cyl_center;
- cyl_bottom = (cyl_bottom * cyl_rotation) + cyl_center;
-
- // we only handle cylinders with circular cross-sections at the moment
- F32 cyl_radius = 0.5f * llmax(cyl_scale.mV[VX], cyl_scale.mV[VY]); // HACK until scaled cylinders are supported
-
- // This implementation is based on the intcyl() function from Graphics_Gems_IV, page 361
- LLVector3 cyl_axis; // axis direction (bottom toward top)
- LLVector3 ray_to_cyl; // ray_point to cyl_top
- F32 shortest_distance; // shortest distance from ray to axis
- F32 cyl_length;
- LLVector3 shortest_direction;
- LLVector3 temp_vector;
-
- cyl_axis = cyl_bottom - cyl_top;
- cyl_length = cyl_axis.normVec();
- ray_to_cyl = ray_point - cyl_bottom;
- shortest_direction = ray_direction % cyl_axis;
- shortest_distance = shortest_direction.normVec(); // recycle shortest_distance
-
- // check for ray parallel to cylinder axis
- if (0.0f == shortest_distance)
- {
- // ray is parallel to cylinder axis
- temp_vector = ray_to_cyl - (ray_to_cyl * cyl_axis) * cyl_axis;
- shortest_distance = temp_vector.magVec();
- if (shortest_distance <= cyl_radius)
- {
- shortest_distance = ray_to_cyl * cyl_axis;
- F32 dot = ray_direction * cyl_axis;
-
- if (shortest_distance > 0.0)
- {
- if (dot > 0.0f)
- {
- // ray points away from cylinder bottom
- return false;
- }
- // ray hit bottom of cylinder from outside
- intersection = ray_point - shortest_distance * cyl_axis;
- intersection_normal = cyl_axis;
-
- }
- else if (shortest_distance > -cyl_length)
- {
- // ray starts inside cylinder
- if (dot < 0.0f)
- {
- // ray hit top from inside
- intersection = ray_point - (cyl_length + shortest_distance) * cyl_axis;
- intersection_normal = -cyl_axis;
- }
- else
- {
- // ray hit bottom from inside
- intersection = ray_point - shortest_distance * cyl_axis;
- intersection_normal = cyl_axis;
- }
- }
- else
- {
- if (dot < 0.0f)
- {
- // ray points away from cylinder bottom
- return false;
- }
- // ray hit top from outside
- intersection = ray_point - (shortest_distance + cyl_length) * cyl_axis;
- intersection_normal = -cyl_axis;
- }
- return true;
- }
- return false;
- }
-
- // check for intersection with infinite cylinder
- shortest_distance = (F32) fabs(ray_to_cyl * shortest_direction);
- if (shortest_distance <= cyl_radius)
- {
- F32 dist_to_closest_point; // dist from ray_point to closest_point
- F32 half_chord_length; // half length of intersection chord
- F32 in, out; // distances to entering/exiting points
- temp_vector = ray_to_cyl % cyl_axis;
- dist_to_closest_point = - (temp_vector * shortest_direction);
- temp_vector = shortest_direction % cyl_axis;
- temp_vector.normVec();
- half_chord_length = (F32) fabs( sqrt(cyl_radius*cyl_radius - shortest_distance * shortest_distance) /
- (ray_direction * temp_vector) );
-
- out = dist_to_closest_point + half_chord_length; // dist to exiting point
- if (out < 0.0f)
- {
- // cylinder is behind the ray, so we return false
- return false;
- }
-
- in = dist_to_closest_point - half_chord_length; // dist to entering point
- if (in < 0.0f)
- {
- // ray_point is inside the cylinder
- // so we store the exiting intersection
- intersection = ray_point + out * ray_direction;
- shortest_distance = out;
- }
- else
- {
- // ray hit cylinder from outside
- // so we store the entering intersection
- intersection = ray_point + in * ray_direction;
- shortest_distance = in;
- }
-
- // calculate the normal at intersection
- if (0.0f == cyl_radius)
- {
- intersection_normal.setVec(0.0f, 0.0f, 0.0f);
- }
- else
- {
- temp_vector = intersection - cyl_bottom;
- intersection_normal = temp_vector - (temp_vector * cyl_axis) * cyl_axis;
- intersection_normal.normVec();
- }
-
- // check for intersection with end caps
- // calculate intersection of ray and top plane
- if (line_plane(ray_point, ray_direction, cyl_top, -cyl_axis, temp_vector)) // NOTE side-effect: changing temp_vector
- {
- shortest_distance = (temp_vector - ray_point).magVec();
- if ( (ray_direction * cyl_axis) > 0.0f)
- {
- // ray potentially enters the cylinder at top
- if (shortest_distance > out)
- {
- // ray missed the finite cylinder
- return false;
- }
- if (shortest_distance > in)
- {
- // ray intersects cylinder at top plane
- intersection = temp_vector;
- intersection_normal = -cyl_axis;
- return true;
- }
- }
- else
- {
- // ray potentially exits the cylinder at top
- if (shortest_distance < in)
- {
- // missed the finite cylinder
- return false;
- }
- }
-
- // calculate intersection of ray and bottom plane
- line_plane(ray_point, ray_direction, cyl_bottom, cyl_axis, temp_vector); // NOTE side-effect: changing temp_vector
- shortest_distance = (temp_vector - ray_point).magVec();
- if ( (ray_direction * cyl_axis) < 0.0)
- {
- // ray potentially enters the cylinder at bottom
- if (shortest_distance > out)
- {
- // ray missed the finite cylinder
- return false;
- }
- if (shortest_distance > in)
- {
- // ray intersects cylinder at bottom plane
- intersection = temp_vector;
- intersection_normal = cyl_axis;
- return true;
- }
- }
- else
- {
- // ray potentially exits the cylinder at bottom
- if (shortest_distance < in)
- {
- // ray missed the finite cylinder
- return false;
- }
- }
-
- }
- else
- {
- // ray is parallel to end cap planes
- temp_vector = cyl_bottom - ray_point;
- shortest_distance = temp_vector * cyl_axis;
- if (shortest_distance < 0.0f || shortest_distance > cyl_length)
- {
- // ray missed finite cylinder
- return false;
- }
- }
-
- return true;
- }
-
- return false;
-}
-
-
-U32 ray_box(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
-
- // Need to rotate into box frame
- LLQuaternion into_box_frame(box_rotation); // rotates things from box frame to absolute
- into_box_frame.conjQuat(); // now rotates things into box frame
- LLVector3 line_point = (ray_point - box_center) * into_box_frame;
- LLVector3 line_direction = ray_direction * into_box_frame;
-
- // Suppose we have a plane: Ax + By + Cz + D = 0
- // then, assuming [A, B, C] is a unit vector:
- //
- // plane_normal = [A, B, C]
- // D = - (plane_normal * plane_point)
- //
- // Suppose we have a line: X = line_point + alpha * line_direction
- //
- // the intersection of the plane and line determines alpha
- //
- // alpha = - (D + plane_normal * line_point) / (plane_normal * line_direction)
-
- LLVector3 line_plane_intersection;
-
- F32 pointX = line_point.mV[VX];
- F32 pointY = line_point.mV[VY];
- F32 pointZ = line_point.mV[VZ];
-
- F32 dirX = line_direction.mV[VX];
- F32 dirY = line_direction.mV[VY];
- F32 dirZ = line_direction.mV[VZ];
-
- // we'll be using the half-scales of the box
- F32 boxX = 0.5f * box_scale.mV[VX];
- F32 boxY = 0.5f * box_scale.mV[VY];
- F32 boxZ = 0.5f * box_scale.mV[VZ];
-
- // check to see if line_point is OUTSIDE the box
- if (pointX < -boxX ||
- pointX > boxX ||
- pointY < -boxY ||
- pointY > boxY ||
- pointZ < -boxZ ||
- pointZ > boxZ)
- {
- // -------------- point is OUTSIDE the box ----------------
-
- // front
- if (pointX > 0.0f && dirX < 0.0f)
- {
- // plane_normal = [ 1, 0, 0]
- // plane_normal*line_point = pointX
- // plane_normal*line_direction = dirX
- // D = -boxX
- // alpha = - (-boxX + pointX) / dirX
- line_plane_intersection = line_point - ((pointX - boxX) / dirX) * line_direction;
- if (line_plane_intersection.mV[VY] < boxY &&
- line_plane_intersection.mV[VY] > -boxY &&
- line_plane_intersection.mV[VZ] < boxZ &&
- line_plane_intersection.mV[VZ] > -boxZ )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(1.0f, 0.0f, 0.0f) * box_rotation;
- return FRONT_SIDE;
- }
- }
-
- // back
- if (pointX < 0.0f && dirX > 0.0f)
- {
- // plane_normal = [ -1, 0, 0]
- // plane_normal*line_point = -pX
- // plane_normal*line_direction = -direction.mV[VX]
- // D = -bX
- // alpha = - (-bX - pX) / (-dirX)
- line_plane_intersection = line_point - ((boxX + pointX)/ dirX) * line_direction;
- if (line_plane_intersection.mV[VY] < boxY &&
- line_plane_intersection.mV[VY] > -boxY &&
- line_plane_intersection.mV[VZ] < boxZ &&
- line_plane_intersection.mV[VZ] > -boxZ )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(-1.0f, 0.0f, 0.0f) * box_rotation;
- return BACK_SIDE;
- }
- }
-
- // left
- if (pointY > 0.0f && dirY < 0.0f)
- {
- // plane_normal = [0, 1, 0]
- // plane_normal*line_point = pointY
- // plane_normal*line_direction = dirY
- // D = -boxY
- // alpha = - (-boxY + pointY) / dirY
- line_plane_intersection = line_point + ((boxY - pointY)/dirY) * line_direction;
-
- if (line_plane_intersection.mV[VX] < boxX &&
- line_plane_intersection.mV[VX] > -boxX &&
- line_plane_intersection.mV[VZ] < boxZ &&
- line_plane_intersection.mV[VZ] > -boxZ )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(0.0f, 1.0f, 0.0f) * box_rotation;
- return LEFT_SIDE;
- }
- }
-
- // right
- if (pointY < 0.0f && dirY > 0.0f)
- {
- // plane_normal = [0, -1, 0]
- // plane_normal*line_point = -pointY
- // plane_normal*line_direction = -dirY
- // D = -boxY
- // alpha = - (-boxY - pointY) / (-dirY)
- line_plane_intersection = line_point - ((boxY + pointY)/dirY) * line_direction;
- if (line_plane_intersection.mV[VX] < boxX &&
- line_plane_intersection.mV[VX] > -boxX &&
- line_plane_intersection.mV[VZ] < boxZ &&
- line_plane_intersection.mV[VZ] > -boxZ )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(0.0f, -1.0f, 0.0f) * box_rotation;
- return RIGHT_SIDE;
- }
- }
-
- // top
- if (pointZ > 0.0f && dirZ < 0.0f)
- {
- // plane_normal = [0, 0, 1]
- // plane_normal*line_point = pointZ
- // plane_normal*line_direction = dirZ
- // D = -boxZ
- // alpha = - (-boxZ + pointZ) / dirZ
- line_plane_intersection = line_point - ((pointZ - boxZ)/dirZ) * line_direction;
- if (line_plane_intersection.mV[VX] < boxX &&
- line_plane_intersection.mV[VX] > -boxX &&
- line_plane_intersection.mV[VY] < boxY &&
- line_plane_intersection.mV[VY] > -boxY )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(0.0f, 0.0f, 1.0f) * box_rotation;
- return TOP_SIDE;
- }
- }
-
- // bottom
- if (pointZ < 0.0f && dirZ > 0.0f)
- {
- // plane_normal = [0, 0, -1]
- // plane_normal*line_point = -pointZ
- // plane_normal*line_direction = -dirZ
- // D = -boxZ
- // alpha = - (-boxZ - pointZ) / (-dirZ)
- line_plane_intersection = line_point - ((boxZ + pointZ)/dirZ) * line_direction;
- if (line_plane_intersection.mV[VX] < boxX &&
- line_plane_intersection.mV[VX] > -boxX &&
- line_plane_intersection.mV[VY] < boxY &&
- line_plane_intersection.mV[VY] > -boxY )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(0.0f, 0.0f, -1.0f) * box_rotation;
- return BOTTOM_SIDE;
- }
- }
- return NO_SIDE;
- }
-
- // -------------- point is INSIDE the box ----------------
-
- // front
- if (dirX > 0.0f)
- {
- // plane_normal = [ 1, 0, 0]
- // plane_normal*line_point = pointX
- // plane_normal*line_direction = dirX
- // D = -boxX
- // alpha = - (-boxX + pointX) / dirX
- line_plane_intersection = line_point - ((pointX - boxX) / dirX) * line_direction;
- if (line_plane_intersection.mV[VY] < boxY &&
- line_plane_intersection.mV[VY] > -boxY &&
- line_plane_intersection.mV[VZ] < boxZ &&
- line_plane_intersection.mV[VZ] > -boxZ )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(1.0f, 0.0f, 0.0f) * box_rotation;
- return FRONT_SIDE;
- }
- }
-
- // back
- if (dirX < 0.0f)
- {
- // plane_normal = [ -1, 0, 0]
- // plane_normal*line_point = -pX
- // plane_normal*line_direction = -direction.mV[VX]
- // D = -bX
- // alpha = - (-bX - pX) / (-dirX)
- line_plane_intersection = line_point - ((boxX + pointX)/ dirX) * line_direction;
- if (line_plane_intersection.mV[VY] < boxY &&
- line_plane_intersection.mV[VY] > -boxY &&
- line_plane_intersection.mV[VZ] < boxZ &&
- line_plane_intersection.mV[VZ] > -boxZ )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(-1.0f, 0.0f, 0.0f) * box_rotation;
- return BACK_SIDE;
- }
- }
-
- // left
- if (dirY > 0.0f)
- {
- // plane_normal = [0, 1, 0]
- // plane_normal*line_point = pointY
- // plane_normal*line_direction = dirY
- // D = -boxY
- // alpha = - (-boxY + pointY) / dirY
- line_plane_intersection = line_point + ((boxY - pointY)/dirY) * line_direction;
-
- if (line_plane_intersection.mV[VX] < boxX &&
- line_plane_intersection.mV[VX] > -boxX &&
- line_plane_intersection.mV[VZ] < boxZ &&
- line_plane_intersection.mV[VZ] > -boxZ )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(0.0f, 1.0f, 0.0f) * box_rotation;
- return LEFT_SIDE;
- }
- }
-
- // right
- if (dirY < 0.0f)
- {
- // plane_normal = [0, -1, 0]
- // plane_normal*line_point = -pointY
- // plane_normal*line_direction = -dirY
- // D = -boxY
- // alpha = - (-boxY - pointY) / (-dirY)
- line_plane_intersection = line_point - ((boxY + pointY)/dirY) * line_direction;
- if (line_plane_intersection.mV[VX] < boxX &&
- line_plane_intersection.mV[VX] > -boxX &&
- line_plane_intersection.mV[VZ] < boxZ &&
- line_plane_intersection.mV[VZ] > -boxZ )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(0.0f, -1.0f, 0.0f) * box_rotation;
- return RIGHT_SIDE;
- }
- }
-
- // top
- if (dirZ > 0.0f)
- {
- // plane_normal = [0, 0, 1]
- // plane_normal*line_point = pointZ
- // plane_normal*line_direction = dirZ
- // D = -boxZ
- // alpha = - (-boxZ + pointZ) / dirZ
- line_plane_intersection = line_point - ((pointZ - boxZ)/dirZ) * line_direction;
- if (line_plane_intersection.mV[VX] < boxX &&
- line_plane_intersection.mV[VX] > -boxX &&
- line_plane_intersection.mV[VY] < boxY &&
- line_plane_intersection.mV[VY] > -boxY )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(0.0f, 0.0f, 1.0f) * box_rotation;
- return TOP_SIDE;
- }
- }
-
- // bottom
- if (dirZ < 0.0f)
- {
- // plane_normal = [0, 0, -1]
- // plane_normal*line_point = -pointZ
- // plane_normal*line_direction = -dirZ
- // D = -boxZ
- // alpha = - (-boxZ - pointZ) / (-dirZ)
- line_plane_intersection = line_point - ((boxZ + pointZ)/dirZ) * line_direction;
- if (line_plane_intersection.mV[VX] < boxX &&
- line_plane_intersection.mV[VX] > -boxX &&
- line_plane_intersection.mV[VY] < boxY &&
- line_plane_intersection.mV[VY] > -boxY )
- {
- intersection = (line_plane_intersection * box_rotation) + box_center;
- intersection_normal = LLVector3(0.0f, 0.0f, -1.0f) * box_rotation;
- return BOTTOM_SIDE;
- }
- }
-
- // should never get here unless line instersects at tangent point on edge or corner
- // however such cases will be EXTREMELY rare
- return NO_SIDE;
-}
-
-
-bool ray_prism(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- // (0) Z
- // /| \ .
- // (1)| \ /|\ _.Y
- // | \ \ | /|
- // | |\ \ | /
- // | | \(0)\ | /
- // | | \ \ |/
- // | | \ \ (*)----> X
- // |(3)---\---(2)
- // |/ \ /
- // (4)-------(5)
-
- // need to calculate the points of the prism so we can run ray tests with each face
- F32 x = prism_scale.mV[VX];
- F32 y = prism_scale.mV[VY];
- F32 z = prism_scale.mV[VZ];
-
- F32 tx = x * 2.0f / 3.0f;
- F32 ty = y * 0.5f;
- F32 tz = z * 2.0f / 3.0f;
-
- LLVector3 point0(tx-x, ty, tz);
- LLVector3 point1(tx-x, -ty, tz);
- LLVector3 point2(tx, ty, tz-z);
- LLVector3 point3(tx-x, ty, tz-z);
- LLVector3 point4(tx-x, -ty, tz-z);
- LLVector3 point5(tx, -ty, tz-z);
-
- // transform these points into absolute frame
- point0 = (point0 * prism_rotation) + prism_center;
- point1 = (point1 * prism_rotation) + prism_center;
- point2 = (point2 * prism_rotation) + prism_center;
- point3 = (point3 * prism_rotation) + prism_center;
- point4 = (point4 * prism_rotation) + prism_center;
- point5 = (point5 * prism_rotation) + prism_center;
-
- // test ray intersection for each face
- bool b_hit = false;
- LLVector3 face_intersection, face_normal;
- F32 distance_squared = 0.0f;
- F32 temp;
-
- // face 0
- if (ray_direction * ( (point0 - point2) % (point5 - point2)) < 0.0f &&
- ray_quadrangle(ray_point, ray_direction, point5, point2, point0, intersection, intersection_normal))
- {
- distance_squared = (ray_point - intersection).magVecSquared();
- b_hit = true;
- }
-
- // face 1
- if (ray_direction * ( (point0 - point3) % (point2 - point3)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point2, point3, point0, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- distance_squared = temp;
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- distance_squared = (ray_point - face_intersection).magVecSquared();
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- // face 2
- if (ray_direction * ( (point1 - point4) % (point3 - point4)) < 0.0f &&
- ray_quadrangle(ray_point, ray_direction, point3, point4, point1, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- distance_squared = temp;
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- distance_squared = (ray_point - face_intersection).magVecSquared();
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- // face 3
- if (ray_direction * ( (point5 - point4) % (point1 - point4)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point1, point4, point5, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- distance_squared = temp;
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- distance_squared = (ray_point - face_intersection).magVecSquared();
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- // face 4
- if (ray_direction * ( (point4 - point5) % (point2 - point5)) < 0.0f &&
- ray_quadrangle(ray_point, ray_direction, point2, point5, point4, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- distance_squared = temp;
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- distance_squared = (ray_point - face_intersection).magVecSquared();
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- return b_hit;
-}
-
-
-bool ray_tetrahedron(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- F32 a = 0.5f * F_SQRT3; // height of unit triangle
- F32 b = 1.0f / F_SQRT3; // distance of center of unit triangle to each point
- F32 c = F_SQRT2 / F_SQRT3; // height of unit tetrahedron
- F32 d = 0.5f * F_SQRT3 / F_SQRT2; // distance of center of tetrahedron to each point
-
- // if we want the tetrahedron to have unit height (c = 1.0) then we need to divide
- // each constant by hieght of a unit tetrahedron
- F32 oo_c = 1.0f / c;
- a = a * oo_c;
- b = b * oo_c;
- c = 1.0f;
- d = d * oo_c;
- F32 e = 0.5f * oo_c;
-
- LLVector3 point0( 0.0f, 0.0f, t_scale.mV[VZ] * d);
- LLVector3 point1(t_scale.mV[VX] * b, 0.0f, t_scale.mV[VZ] * (d-c));
- LLVector3 point2(t_scale.mV[VX] * (b-a), e * t_scale.mV[VY], t_scale.mV[VZ] * (d-c));
- LLVector3 point3(t_scale.mV[VX] * (b-a), -e * t_scale.mV[VY], t_scale.mV[VZ] * (d-c));
-
- // transform these points into absolute frame
- point0 = (point0 * t_rotation) + t_center;
- point1 = (point1 * t_rotation) + t_center;
- point2 = (point2 * t_rotation) + t_center;
- point3 = (point3 * t_rotation) + t_center;
-
- // test ray intersection for each face
- bool b_hit = false;
- LLVector3 face_intersection, face_normal;
- F32 distance_squared = 1.0e12f;
- F32 temp;
-
- // face 0
- if (ray_direction * ( (point2 - point1) % (point0 - point1)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point1, point2, point0, intersection, intersection_normal))
- {
- distance_squared = (ray_point - intersection).magVecSquared();
- b_hit = true;
- }
-
- // face 1
- if (ray_direction * ( (point3 - point2) % (point0 - point2)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point2, point3, point0, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- distance_squared = temp;
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- distance_squared = (ray_point - face_intersection).magVecSquared();
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- // face 2
- if (ray_direction * ( (point1 - point3) % (point0 - point3)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point3, point1, point0, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- distance_squared = temp;
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- distance_squared = (ray_point - face_intersection).magVecSquared();
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- // face 3
- if (ray_direction * ( (point2 - point3) % (point1 - point3)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point3, point2, point1, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- return b_hit;
-}
-
-
-bool ray_pyramid(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- // center of mass of pyramid is located 1/4 its height from the base
- F32 x = 0.5f * p_scale.mV[VX];
- F32 y = 0.5f * p_scale.mV[VY];
- F32 z = 0.25f * p_scale.mV[VZ];
-
- LLVector3 point0(0.0f, 0.0f, p_scale.mV[VZ] - z);
- LLVector3 point1( x, y, -z);
- LLVector3 point2(-x, y, -z);
- LLVector3 point3(-x, -y, -z);
- LLVector3 point4( x, -y, -z);
-
- // transform these points into absolute frame
- point0 = (point0 * p_rotation) + p_center;
- point1 = (point1 * p_rotation) + p_center;
- point2 = (point2 * p_rotation) + p_center;
- point3 = (point3 * p_rotation) + p_center;
- point4 = (point4 * p_rotation) + p_center;
-
- // test ray intersection for each face
- bool b_hit = false;
- LLVector3 face_intersection, face_normal;
- F32 distance_squared = 1.0e12f;
- F32 temp;
-
- // face 0
- if (ray_direction * ( (point1 - point4) % (point0 - point4)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point4, point1, point0, intersection, intersection_normal))
- {
- distance_squared = (ray_point - intersection).magVecSquared();
- b_hit = true;
- }
-
- // face 1
- if (ray_direction * ( (point2 - point1) % (point0 - point1)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point1, point2, point0, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- distance_squared = temp;
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- distance_squared = (ray_point - face_intersection).magVecSquared();
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- // face 2
- if (ray_direction * ( (point3 - point2) % (point0 - point2)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point2, point3, point0, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- distance_squared = temp;
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- distance_squared = (ray_point - face_intersection).magVecSquared();
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- // face 3
- if (ray_direction * ( (point4 - point3) % (point0 - point3)) < 0.0f &&
- ray_triangle(ray_point, ray_direction, point3, point4, point0, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- distance_squared = temp;
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- distance_squared = (ray_point - face_intersection).magVecSquared();
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- // face 4
- if (ray_direction * ( (point3 - point4) % (point2 - point4)) < 0.0f &&
- ray_quadrangle(ray_point, ray_direction, point4, point3, point2, face_intersection, face_normal))
- {
- if (b_hit)
- {
- temp = (ray_point - face_intersection).magVecSquared();
- if (temp < distance_squared)
- {
- intersection = face_intersection;
- intersection_normal = face_normal;
- }
- }
- else
- {
- intersection = face_intersection;
- intersection_normal = face_normal;
- b_hit = true;
- }
- }
-
- return b_hit;
-}
-
-
-bool linesegment_circle(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius,
- LLVector3 &intersection)
-{
- LLVector3 ray_direction = point_b - point_a;
- F32 segment_length = ray_direction.normVec();
-
- if (ray_circle(point_a, ray_direction, circle_center, plane_normal, circle_radius, intersection))
- {
- if (segment_length >= (point_a - intersection).magVec())
- {
- return true;
- }
- }
- return false;
-}
-
-
-bool linesegment_triangle(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 ray_direction = point_b - point_a;
- F32 segment_length = ray_direction.normVec();
-
- if (ray_triangle(point_a, ray_direction, point_0, point_1, point_2, intersection, intersection_normal))
- {
- if (segment_length >= (point_a - intersection).magVec())
- {
- return true;
- }
- }
- return false;
-}
-
-
-bool linesegment_quadrangle(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 ray_direction = point_b - point_a;
- F32 segment_length = ray_direction.normVec();
-
- if (ray_quadrangle(point_a, ray_direction, point_0, point_1, point_2, intersection, intersection_normal))
- {
- if (segment_length >= (point_a - intersection).magVec())
- {
- return true;
- }
- }
- return false;
-}
-
-
-bool linesegment_sphere(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &sphere_center, F32 sphere_radius,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 ray_direction = point_b - point_a;
- F32 segment_length = ray_direction.normVec();
-
- if (ray_sphere(point_a, ray_direction, sphere_center, sphere_radius, intersection, intersection_normal))
- {
- if (segment_length >= (point_a - intersection).magVec())
- {
- return true;
- }
- }
- return false;
-}
-
-
-bool linesegment_cylinder(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 ray_direction = point_b - point_a;
- F32 segment_length = ray_direction.normVec();
-
- if (ray_cylinder(point_a, ray_direction, cyl_center, cyl_scale, cyl_rotation, intersection, intersection_normal))
- {
- if (segment_length >= (point_a - intersection).magVec())
- {
- return true;
- }
- }
- return false;
-}
-
-
-U32 linesegment_box(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 direction = point_b - point_a;
- if (direction.isNull())
- {
- return NO_SIDE;
- }
-
- F32 segment_length = direction.normVec();
- U32 box_side = ray_box(point_a, direction, box_center, box_scale, box_rotation, intersection, intersection_normal);
- if (NO_SIDE == box_side || segment_length < (intersection - point_a).magVec())
- {
- return NO_SIDE;
- }
-
- return box_side;
-}
-
-
-bool linesegment_prism(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 ray_direction = point_b - point_a;
- F32 segment_length = ray_direction.normVec();
-
- if (ray_prism(point_a, ray_direction, prism_center, prism_scale, prism_rotation, intersection, intersection_normal))
- {
- if (segment_length >= (point_a - intersection).magVec())
- {
- return true;
- }
- }
- return false;
-}
-
-
-bool linesegment_tetrahedron(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 ray_direction = point_b - point_a;
- F32 segment_length = ray_direction.normVec();
-
- if (ray_tetrahedron(point_a, ray_direction, t_center, t_scale, t_rotation, intersection, intersection_normal))
- {
- if (segment_length >= (point_a - intersection).magVec())
- {
- return true;
- }
- }
- return false;
-}
-
-
-bool linesegment_pyramid(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal)
-{
- LLVector3 ray_direction = point_b - point_a;
- F32 segment_length = ray_direction.normVec();
-
- if (ray_pyramid(point_a, ray_direction, p_center, p_scale, p_rotation, intersection, intersection_normal))
- {
- if (segment_length >= (point_a - intersection).magVec())
- {
- return true;
- }
- }
- return false;
-}
+/** + * @file raytrace.cpp + * @brief Functions called by box object scripts. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "math.h" +//#include "vmath.h" +#include "v3math.h" +#include "llquaternion.h" +#include "m3math.h" +#include "raytrace.h" + + +bool line_plane(const LLVector3 &line_point, const LLVector3 &line_direction, + const LLVector3 &plane_point, const LLVector3 plane_normal, + LLVector3 &intersection) +{ + F32 N = line_direction * plane_normal; + if (0.0f == N) + { + // line is perpendicular to plane normal + // so it is either entirely on plane, or not on plane at all + return false; + } + // Ax + By, + Cz + D = 0 + // D = - (plane_point * plane_normal) + // N = line_direction * plane_normal + // intersection = line_point - ((D + plane_normal * line_point) / N) * line_direction + intersection = line_point - ((plane_normal * line_point - plane_point * plane_normal) / N) * line_direction; + return true; +} + + +bool ray_plane(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &plane_point, const LLVector3 plane_normal, + LLVector3 &intersection) +{ + F32 N = ray_direction * plane_normal; + if (0.0f == N) + { + // ray is perpendicular to plane normal + // so it is either entirely on plane, or not on plane at all + return false; + } + // Ax + By, + Cz + D = 0 + // D = - (plane_point * plane_normal) + // N = ray_direction * plane_normal + // intersection = ray_point - ((D + plane_normal * ray_point) / N) * ray_direction + F32 alpha = -(plane_normal * ray_point - plane_point * plane_normal) / N; + if (alpha < 0.0f) + { + // ray points away from plane + return false; + } + intersection = ray_point + alpha * ray_direction; + return true; +} + + +bool ray_circle(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius, + LLVector3 &intersection) +{ + if (ray_plane(ray_point, ray_direction, circle_center, plane_normal, intersection)) + { + if (circle_radius >= (intersection - circle_center).magVec()) + { + return true; + } + } + return false; +} + + +bool ray_triangle(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 side_01 = point_1 - point_0; + LLVector3 side_12 = point_2 - point_1; + + intersection_normal = side_01 % side_12; + intersection_normal.normVec(); + + if (ray_plane(ray_point, ray_direction, point_0, intersection_normal, intersection)) + { + LLVector3 side_20 = point_0 - point_2; + if (intersection_normal * (side_01 % (intersection - point_0)) >= 0.0f && + intersection_normal * (side_12 % (intersection - point_1)) >= 0.0f && + intersection_normal * (side_20 % (intersection - point_2)) >= 0.0f) + { + return true; + } + } + return false; +} + + +// assumes a parallelogram +bool ray_quadrangle(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 side_01 = point_1 - point_0; + LLVector3 side_12 = point_2 - point_1; + + intersection_normal = side_01 % side_12; + intersection_normal.normVec(); + + if (ray_plane(ray_point, ray_direction, point_0, intersection_normal, intersection)) + { + LLVector3 point_3 = point_0 + (side_12); + LLVector3 side_23 = point_3 - point_2; + LLVector3 side_30 = point_0 - point_3; + if (intersection_normal * (side_01 % (intersection - point_0)) >= 0.0f && + intersection_normal * (side_12 % (intersection - point_1)) >= 0.0f && + intersection_normal * (side_23 % (intersection - point_2)) >= 0.0f && + intersection_normal * (side_30 % (intersection - point_3)) >= 0.0f) + { + return true; + } + } + return false; +} + + +bool ray_sphere(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &sphere_center, F32 sphere_radius, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 ray_to_sphere = sphere_center - ray_point; + F32 dot = ray_to_sphere * ray_direction; + + LLVector3 closest_approach = dot * ray_direction - ray_to_sphere; + + F32 shortest_distance = closest_approach.magVecSquared(); + F32 radius_squared = sphere_radius * sphere_radius; + if (shortest_distance > radius_squared) + { + return false; + } + + F32 half_chord = (F32) sqrt(radius_squared - shortest_distance); + closest_approach = sphere_center + closest_approach; // closest_approach now in absolute coordinates + intersection = closest_approach + half_chord * ray_direction; + dot = ray_direction * (intersection - ray_point); + if (dot < 0.0f) + { + // ray shoots away from sphere and is not inside it + return false; + } + + shortest_distance = ray_direction * ((closest_approach - half_chord * ray_direction) - ray_point); + if (shortest_distance > 0.0f) + { + // ray enters sphere + intersection = intersection - (2.0f * half_chord) * ray_direction; + } + else + { + // do nothing + // ray starts inside sphere and intersects as it leaves the sphere + } + + intersection_normal = intersection - sphere_center; + if (sphere_radius > 0.0f) + { + intersection_normal *= 1.0f / sphere_radius; + } + else + { + intersection_normal.setVec(0.0f, 0.0f, 0.0f); + } + + return true; +} + + +bool ray_cylinder(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + // calculate the centers of the cylinder caps in the absolute frame + LLVector3 cyl_top(0.0f, 0.0f, 0.5f * cyl_scale.mV[VZ]); + LLVector3 cyl_bottom(0.0f, 0.0f, -cyl_top.mV[VZ]); + cyl_top = (cyl_top * cyl_rotation) + cyl_center; + cyl_bottom = (cyl_bottom * cyl_rotation) + cyl_center; + + // we only handle cylinders with circular cross-sections at the moment + F32 cyl_radius = 0.5f * llmax(cyl_scale.mV[VX], cyl_scale.mV[VY]); // HACK until scaled cylinders are supported + + // This implementation is based on the intcyl() function from Graphics_Gems_IV, page 361 + LLVector3 cyl_axis; // axis direction (bottom toward top) + LLVector3 ray_to_cyl; // ray_point to cyl_top + F32 shortest_distance; // shortest distance from ray to axis + F32 cyl_length; + LLVector3 shortest_direction; + LLVector3 temp_vector; + + cyl_axis = cyl_bottom - cyl_top; + cyl_length = cyl_axis.normVec(); + ray_to_cyl = ray_point - cyl_bottom; + shortest_direction = ray_direction % cyl_axis; + shortest_distance = shortest_direction.normVec(); // recycle shortest_distance + + // check for ray parallel to cylinder axis + if (0.0f == shortest_distance) + { + // ray is parallel to cylinder axis + temp_vector = ray_to_cyl - (ray_to_cyl * cyl_axis) * cyl_axis; + shortest_distance = temp_vector.magVec(); + if (shortest_distance <= cyl_radius) + { + shortest_distance = ray_to_cyl * cyl_axis; + F32 dot = ray_direction * cyl_axis; + + if (shortest_distance > 0.0) + { + if (dot > 0.0f) + { + // ray points away from cylinder bottom + return false; + } + // ray hit bottom of cylinder from outside + intersection = ray_point - shortest_distance * cyl_axis; + intersection_normal = cyl_axis; + + } + else if (shortest_distance > -cyl_length) + { + // ray starts inside cylinder + if (dot < 0.0f) + { + // ray hit top from inside + intersection = ray_point - (cyl_length + shortest_distance) * cyl_axis; + intersection_normal = -cyl_axis; + } + else + { + // ray hit bottom from inside + intersection = ray_point - shortest_distance * cyl_axis; + intersection_normal = cyl_axis; + } + } + else + { + if (dot < 0.0f) + { + // ray points away from cylinder bottom + return false; + } + // ray hit top from outside + intersection = ray_point - (shortest_distance + cyl_length) * cyl_axis; + intersection_normal = -cyl_axis; + } + return true; + } + return false; + } + + // check for intersection with infinite cylinder + shortest_distance = (F32) fabs(ray_to_cyl * shortest_direction); + if (shortest_distance <= cyl_radius) + { + F32 dist_to_closest_point; // dist from ray_point to closest_point + F32 half_chord_length; // half length of intersection chord + F32 in, out; // distances to entering/exiting points + temp_vector = ray_to_cyl % cyl_axis; + dist_to_closest_point = - (temp_vector * shortest_direction); + temp_vector = shortest_direction % cyl_axis; + temp_vector.normVec(); + half_chord_length = (F32) fabs( sqrt(cyl_radius*cyl_radius - shortest_distance * shortest_distance) / + (ray_direction * temp_vector) ); + + out = dist_to_closest_point + half_chord_length; // dist to exiting point + if (out < 0.0f) + { + // cylinder is behind the ray, so we return false + return false; + } + + in = dist_to_closest_point - half_chord_length; // dist to entering point + if (in < 0.0f) + { + // ray_point is inside the cylinder + // so we store the exiting intersection + intersection = ray_point + out * ray_direction; + shortest_distance = out; + } + else + { + // ray hit cylinder from outside + // so we store the entering intersection + intersection = ray_point + in * ray_direction; + shortest_distance = in; + } + + // calculate the normal at intersection + if (0.0f == cyl_radius) + { + intersection_normal.setVec(0.0f, 0.0f, 0.0f); + } + else + { + temp_vector = intersection - cyl_bottom; + intersection_normal = temp_vector - (temp_vector * cyl_axis) * cyl_axis; + intersection_normal.normVec(); + } + + // check for intersection with end caps + // calculate intersection of ray and top plane + if (line_plane(ray_point, ray_direction, cyl_top, -cyl_axis, temp_vector)) // NOTE side-effect: changing temp_vector + { + shortest_distance = (temp_vector - ray_point).magVec(); + if ( (ray_direction * cyl_axis) > 0.0f) + { + // ray potentially enters the cylinder at top + if (shortest_distance > out) + { + // ray missed the finite cylinder + return false; + } + if (shortest_distance > in) + { + // ray intersects cylinder at top plane + intersection = temp_vector; + intersection_normal = -cyl_axis; + return true; + } + } + else + { + // ray potentially exits the cylinder at top + if (shortest_distance < in) + { + // missed the finite cylinder + return false; + } + } + + // calculate intersection of ray and bottom plane + line_plane(ray_point, ray_direction, cyl_bottom, cyl_axis, temp_vector); // NOTE side-effect: changing temp_vector + shortest_distance = (temp_vector - ray_point).magVec(); + if ( (ray_direction * cyl_axis) < 0.0) + { + // ray potentially enters the cylinder at bottom + if (shortest_distance > out) + { + // ray missed the finite cylinder + return false; + } + if (shortest_distance > in) + { + // ray intersects cylinder at bottom plane + intersection = temp_vector; + intersection_normal = cyl_axis; + return true; + } + } + else + { + // ray potentially exits the cylinder at bottom + if (shortest_distance < in) + { + // ray missed the finite cylinder + return false; + } + } + + } + else + { + // ray is parallel to end cap planes + temp_vector = cyl_bottom - ray_point; + shortest_distance = temp_vector * cyl_axis; + if (shortest_distance < 0.0f || shortest_distance > cyl_length) + { + // ray missed finite cylinder + return false; + } + } + + return true; + } + + return false; +} + + +U32 ray_box(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + + // Need to rotate into box frame + LLQuaternion into_box_frame(box_rotation); // rotates things from box frame to absolute + into_box_frame.conjQuat(); // now rotates things into box frame + LLVector3 line_point = (ray_point - box_center) * into_box_frame; + LLVector3 line_direction = ray_direction * into_box_frame; + + // Suppose we have a plane: Ax + By + Cz + D = 0 + // then, assuming [A, B, C] is a unit vector: + // + // plane_normal = [A, B, C] + // D = - (plane_normal * plane_point) + // + // Suppose we have a line: X = line_point + alpha * line_direction + // + // the intersection of the plane and line determines alpha + // + // alpha = - (D + plane_normal * line_point) / (plane_normal * line_direction) + + LLVector3 line_plane_intersection; + + F32 pointX = line_point.mV[VX]; + F32 pointY = line_point.mV[VY]; + F32 pointZ = line_point.mV[VZ]; + + F32 dirX = line_direction.mV[VX]; + F32 dirY = line_direction.mV[VY]; + F32 dirZ = line_direction.mV[VZ]; + + // we'll be using the half-scales of the box + F32 boxX = 0.5f * box_scale.mV[VX]; + F32 boxY = 0.5f * box_scale.mV[VY]; + F32 boxZ = 0.5f * box_scale.mV[VZ]; + + // check to see if line_point is OUTSIDE the box + if (pointX < -boxX || + pointX > boxX || + pointY < -boxY || + pointY > boxY || + pointZ < -boxZ || + pointZ > boxZ) + { + // -------------- point is OUTSIDE the box ---------------- + + // front + if (pointX > 0.0f && dirX < 0.0f) + { + // plane_normal = [ 1, 0, 0] + // plane_normal*line_point = pointX + // plane_normal*line_direction = dirX + // D = -boxX + // alpha = - (-boxX + pointX) / dirX + line_plane_intersection = line_point - ((pointX - boxX) / dirX) * line_direction; + if (line_plane_intersection.mV[VY] < boxY && + line_plane_intersection.mV[VY] > -boxY && + line_plane_intersection.mV[VZ] < boxZ && + line_plane_intersection.mV[VZ] > -boxZ ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(1.0f, 0.0f, 0.0f) * box_rotation; + return FRONT_SIDE; + } + } + + // back + if (pointX < 0.0f && dirX > 0.0f) + { + // plane_normal = [ -1, 0, 0] + // plane_normal*line_point = -pX + // plane_normal*line_direction = -direction.mV[VX] + // D = -bX + // alpha = - (-bX - pX) / (-dirX) + line_plane_intersection = line_point - ((boxX + pointX)/ dirX) * line_direction; + if (line_plane_intersection.mV[VY] < boxY && + line_plane_intersection.mV[VY] > -boxY && + line_plane_intersection.mV[VZ] < boxZ && + line_plane_intersection.mV[VZ] > -boxZ ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(-1.0f, 0.0f, 0.0f) * box_rotation; + return BACK_SIDE; + } + } + + // left + if (pointY > 0.0f && dirY < 0.0f) + { + // plane_normal = [0, 1, 0] + // plane_normal*line_point = pointY + // plane_normal*line_direction = dirY + // D = -boxY + // alpha = - (-boxY + pointY) / dirY + line_plane_intersection = line_point + ((boxY - pointY)/dirY) * line_direction; + + if (line_plane_intersection.mV[VX] < boxX && + line_plane_intersection.mV[VX] > -boxX && + line_plane_intersection.mV[VZ] < boxZ && + line_plane_intersection.mV[VZ] > -boxZ ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(0.0f, 1.0f, 0.0f) * box_rotation; + return LEFT_SIDE; + } + } + + // right + if (pointY < 0.0f && dirY > 0.0f) + { + // plane_normal = [0, -1, 0] + // plane_normal*line_point = -pointY + // plane_normal*line_direction = -dirY + // D = -boxY + // alpha = - (-boxY - pointY) / (-dirY) + line_plane_intersection = line_point - ((boxY + pointY)/dirY) * line_direction; + if (line_plane_intersection.mV[VX] < boxX && + line_plane_intersection.mV[VX] > -boxX && + line_plane_intersection.mV[VZ] < boxZ && + line_plane_intersection.mV[VZ] > -boxZ ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(0.0f, -1.0f, 0.0f) * box_rotation; + return RIGHT_SIDE; + } + } + + // top + if (pointZ > 0.0f && dirZ < 0.0f) + { + // plane_normal = [0, 0, 1] + // plane_normal*line_point = pointZ + // plane_normal*line_direction = dirZ + // D = -boxZ + // alpha = - (-boxZ + pointZ) / dirZ + line_plane_intersection = line_point - ((pointZ - boxZ)/dirZ) * line_direction; + if (line_plane_intersection.mV[VX] < boxX && + line_plane_intersection.mV[VX] > -boxX && + line_plane_intersection.mV[VY] < boxY && + line_plane_intersection.mV[VY] > -boxY ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(0.0f, 0.0f, 1.0f) * box_rotation; + return TOP_SIDE; + } + } + + // bottom + if (pointZ < 0.0f && dirZ > 0.0f) + { + // plane_normal = [0, 0, -1] + // plane_normal*line_point = -pointZ + // plane_normal*line_direction = -dirZ + // D = -boxZ + // alpha = - (-boxZ - pointZ) / (-dirZ) + line_plane_intersection = line_point - ((boxZ + pointZ)/dirZ) * line_direction; + if (line_plane_intersection.mV[VX] < boxX && + line_plane_intersection.mV[VX] > -boxX && + line_plane_intersection.mV[VY] < boxY && + line_plane_intersection.mV[VY] > -boxY ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(0.0f, 0.0f, -1.0f) * box_rotation; + return BOTTOM_SIDE; + } + } + return NO_SIDE; + } + + // -------------- point is INSIDE the box ---------------- + + // front + if (dirX > 0.0f) + { + // plane_normal = [ 1, 0, 0] + // plane_normal*line_point = pointX + // plane_normal*line_direction = dirX + // D = -boxX + // alpha = - (-boxX + pointX) / dirX + line_plane_intersection = line_point - ((pointX - boxX) / dirX) * line_direction; + if (line_plane_intersection.mV[VY] < boxY && + line_plane_intersection.mV[VY] > -boxY && + line_plane_intersection.mV[VZ] < boxZ && + line_plane_intersection.mV[VZ] > -boxZ ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(1.0f, 0.0f, 0.0f) * box_rotation; + return FRONT_SIDE; + } + } + + // back + if (dirX < 0.0f) + { + // plane_normal = [ -1, 0, 0] + // plane_normal*line_point = -pX + // plane_normal*line_direction = -direction.mV[VX] + // D = -bX + // alpha = - (-bX - pX) / (-dirX) + line_plane_intersection = line_point - ((boxX + pointX)/ dirX) * line_direction; + if (line_plane_intersection.mV[VY] < boxY && + line_plane_intersection.mV[VY] > -boxY && + line_plane_intersection.mV[VZ] < boxZ && + line_plane_intersection.mV[VZ] > -boxZ ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(-1.0f, 0.0f, 0.0f) * box_rotation; + return BACK_SIDE; + } + } + + // left + if (dirY > 0.0f) + { + // plane_normal = [0, 1, 0] + // plane_normal*line_point = pointY + // plane_normal*line_direction = dirY + // D = -boxY + // alpha = - (-boxY + pointY) / dirY + line_plane_intersection = line_point + ((boxY - pointY)/dirY) * line_direction; + + if (line_plane_intersection.mV[VX] < boxX && + line_plane_intersection.mV[VX] > -boxX && + line_plane_intersection.mV[VZ] < boxZ && + line_plane_intersection.mV[VZ] > -boxZ ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(0.0f, 1.0f, 0.0f) * box_rotation; + return LEFT_SIDE; + } + } + + // right + if (dirY < 0.0f) + { + // plane_normal = [0, -1, 0] + // plane_normal*line_point = -pointY + // plane_normal*line_direction = -dirY + // D = -boxY + // alpha = - (-boxY - pointY) / (-dirY) + line_plane_intersection = line_point - ((boxY + pointY)/dirY) * line_direction; + if (line_plane_intersection.mV[VX] < boxX && + line_plane_intersection.mV[VX] > -boxX && + line_plane_intersection.mV[VZ] < boxZ && + line_plane_intersection.mV[VZ] > -boxZ ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(0.0f, -1.0f, 0.0f) * box_rotation; + return RIGHT_SIDE; + } + } + + // top + if (dirZ > 0.0f) + { + // plane_normal = [0, 0, 1] + // plane_normal*line_point = pointZ + // plane_normal*line_direction = dirZ + // D = -boxZ + // alpha = - (-boxZ + pointZ) / dirZ + line_plane_intersection = line_point - ((pointZ - boxZ)/dirZ) * line_direction; + if (line_plane_intersection.mV[VX] < boxX && + line_plane_intersection.mV[VX] > -boxX && + line_plane_intersection.mV[VY] < boxY && + line_plane_intersection.mV[VY] > -boxY ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(0.0f, 0.0f, 1.0f) * box_rotation; + return TOP_SIDE; + } + } + + // bottom + if (dirZ < 0.0f) + { + // plane_normal = [0, 0, -1] + // plane_normal*line_point = -pointZ + // plane_normal*line_direction = -dirZ + // D = -boxZ + // alpha = - (-boxZ - pointZ) / (-dirZ) + line_plane_intersection = line_point - ((boxZ + pointZ)/dirZ) * line_direction; + if (line_plane_intersection.mV[VX] < boxX && + line_plane_intersection.mV[VX] > -boxX && + line_plane_intersection.mV[VY] < boxY && + line_plane_intersection.mV[VY] > -boxY ) + { + intersection = (line_plane_intersection * box_rotation) + box_center; + intersection_normal = LLVector3(0.0f, 0.0f, -1.0f) * box_rotation; + return BOTTOM_SIDE; + } + } + + // should never get here unless line instersects at tangent point on edge or corner + // however such cases will be EXTREMELY rare + return NO_SIDE; +} + + +bool ray_prism(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + // (0) Z + // /| \ . + // (1)| \ /|\ _.Y + // | \ \ | /| + // | |\ \ | / + // | | \(0)\ | / + // | | \ \ |/ + // | | \ \ (*)----> X + // |(3)---\---(2) + // |/ \ / + // (4)-------(5) + + // need to calculate the points of the prism so we can run ray tests with each face + F32 x = prism_scale.mV[VX]; + F32 y = prism_scale.mV[VY]; + F32 z = prism_scale.mV[VZ]; + + F32 tx = x * 2.0f / 3.0f; + F32 ty = y * 0.5f; + F32 tz = z * 2.0f / 3.0f; + + LLVector3 point0(tx-x, ty, tz); + LLVector3 point1(tx-x, -ty, tz); + LLVector3 point2(tx, ty, tz-z); + LLVector3 point3(tx-x, ty, tz-z); + LLVector3 point4(tx-x, -ty, tz-z); + LLVector3 point5(tx, -ty, tz-z); + + // transform these points into absolute frame + point0 = (point0 * prism_rotation) + prism_center; + point1 = (point1 * prism_rotation) + prism_center; + point2 = (point2 * prism_rotation) + prism_center; + point3 = (point3 * prism_rotation) + prism_center; + point4 = (point4 * prism_rotation) + prism_center; + point5 = (point5 * prism_rotation) + prism_center; + + // test ray intersection for each face + bool b_hit = false; + LLVector3 face_intersection, face_normal; + F32 distance_squared = 0.0f; + F32 temp; + + // face 0 + if (ray_direction * ( (point0 - point2) % (point5 - point2)) < 0.0f && + ray_quadrangle(ray_point, ray_direction, point5, point2, point0, intersection, intersection_normal)) + { + distance_squared = (ray_point - intersection).magVecSquared(); + b_hit = true; + } + + // face 1 + if (ray_direction * ( (point0 - point3) % (point2 - point3)) < 0.0f && + ray_triangle(ray_point, ray_direction, point2, point3, point0, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + distance_squared = temp; + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + distance_squared = (ray_point - face_intersection).magVecSquared(); + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + // face 2 + if (ray_direction * ( (point1 - point4) % (point3 - point4)) < 0.0f && + ray_quadrangle(ray_point, ray_direction, point3, point4, point1, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + distance_squared = temp; + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + distance_squared = (ray_point - face_intersection).magVecSquared(); + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + // face 3 + if (ray_direction * ( (point5 - point4) % (point1 - point4)) < 0.0f && + ray_triangle(ray_point, ray_direction, point1, point4, point5, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + distance_squared = temp; + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + distance_squared = (ray_point - face_intersection).magVecSquared(); + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + // face 4 + if (ray_direction * ( (point4 - point5) % (point2 - point5)) < 0.0f && + ray_quadrangle(ray_point, ray_direction, point2, point5, point4, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + distance_squared = temp; + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + distance_squared = (ray_point - face_intersection).magVecSquared(); + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + return b_hit; +} + + +bool ray_tetrahedron(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + F32 a = 0.5f * F_SQRT3; // height of unit triangle + F32 b = 1.0f / F_SQRT3; // distance of center of unit triangle to each point + F32 c = F_SQRT2 / F_SQRT3; // height of unit tetrahedron + F32 d = 0.5f * F_SQRT3 / F_SQRT2; // distance of center of tetrahedron to each point + + // if we want the tetrahedron to have unit height (c = 1.0) then we need to divide + // each constant by hieght of a unit tetrahedron + F32 oo_c = 1.0f / c; + a = a * oo_c; + b = b * oo_c; + c = 1.0f; + d = d * oo_c; + F32 e = 0.5f * oo_c; + + LLVector3 point0( 0.0f, 0.0f, t_scale.mV[VZ] * d); + LLVector3 point1(t_scale.mV[VX] * b, 0.0f, t_scale.mV[VZ] * (d-c)); + LLVector3 point2(t_scale.mV[VX] * (b-a), e * t_scale.mV[VY], t_scale.mV[VZ] * (d-c)); + LLVector3 point3(t_scale.mV[VX] * (b-a), -e * t_scale.mV[VY], t_scale.mV[VZ] * (d-c)); + + // transform these points into absolute frame + point0 = (point0 * t_rotation) + t_center; + point1 = (point1 * t_rotation) + t_center; + point2 = (point2 * t_rotation) + t_center; + point3 = (point3 * t_rotation) + t_center; + + // test ray intersection for each face + bool b_hit = false; + LLVector3 face_intersection, face_normal; + F32 distance_squared = 1.0e12f; + F32 temp; + + // face 0 + if (ray_direction * ( (point2 - point1) % (point0 - point1)) < 0.0f && + ray_triangle(ray_point, ray_direction, point1, point2, point0, intersection, intersection_normal)) + { + distance_squared = (ray_point - intersection).magVecSquared(); + b_hit = true; + } + + // face 1 + if (ray_direction * ( (point3 - point2) % (point0 - point2)) < 0.0f && + ray_triangle(ray_point, ray_direction, point2, point3, point0, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + distance_squared = temp; + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + distance_squared = (ray_point - face_intersection).magVecSquared(); + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + // face 2 + if (ray_direction * ( (point1 - point3) % (point0 - point3)) < 0.0f && + ray_triangle(ray_point, ray_direction, point3, point1, point0, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + distance_squared = temp; + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + distance_squared = (ray_point - face_intersection).magVecSquared(); + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + // face 3 + if (ray_direction * ( (point2 - point3) % (point1 - point3)) < 0.0f && + ray_triangle(ray_point, ray_direction, point3, point2, point1, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + return b_hit; +} + + +bool ray_pyramid(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + // center of mass of pyramid is located 1/4 its height from the base + F32 x = 0.5f * p_scale.mV[VX]; + F32 y = 0.5f * p_scale.mV[VY]; + F32 z = 0.25f * p_scale.mV[VZ]; + + LLVector3 point0(0.0f, 0.0f, p_scale.mV[VZ] - z); + LLVector3 point1( x, y, -z); + LLVector3 point2(-x, y, -z); + LLVector3 point3(-x, -y, -z); + LLVector3 point4( x, -y, -z); + + // transform these points into absolute frame + point0 = (point0 * p_rotation) + p_center; + point1 = (point1 * p_rotation) + p_center; + point2 = (point2 * p_rotation) + p_center; + point3 = (point3 * p_rotation) + p_center; + point4 = (point4 * p_rotation) + p_center; + + // test ray intersection for each face + bool b_hit = false; + LLVector3 face_intersection, face_normal; + F32 distance_squared = 1.0e12f; + F32 temp; + + // face 0 + if (ray_direction * ( (point1 - point4) % (point0 - point4)) < 0.0f && + ray_triangle(ray_point, ray_direction, point4, point1, point0, intersection, intersection_normal)) + { + distance_squared = (ray_point - intersection).magVecSquared(); + b_hit = true; + } + + // face 1 + if (ray_direction * ( (point2 - point1) % (point0 - point1)) < 0.0f && + ray_triangle(ray_point, ray_direction, point1, point2, point0, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + distance_squared = temp; + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + distance_squared = (ray_point - face_intersection).magVecSquared(); + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + // face 2 + if (ray_direction * ( (point3 - point2) % (point0 - point2)) < 0.0f && + ray_triangle(ray_point, ray_direction, point2, point3, point0, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + distance_squared = temp; + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + distance_squared = (ray_point - face_intersection).magVecSquared(); + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + // face 3 + if (ray_direction * ( (point4 - point3) % (point0 - point3)) < 0.0f && + ray_triangle(ray_point, ray_direction, point3, point4, point0, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + distance_squared = temp; + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + distance_squared = (ray_point - face_intersection).magVecSquared(); + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + // face 4 + if (ray_direction * ( (point3 - point4) % (point2 - point4)) < 0.0f && + ray_quadrangle(ray_point, ray_direction, point4, point3, point2, face_intersection, face_normal)) + { + if (b_hit) + { + temp = (ray_point - face_intersection).magVecSquared(); + if (temp < distance_squared) + { + intersection = face_intersection; + intersection_normal = face_normal; + } + } + else + { + intersection = face_intersection; + intersection_normal = face_normal; + b_hit = true; + } + } + + return b_hit; +} + + +bool linesegment_circle(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius, + LLVector3 &intersection) +{ + LLVector3 ray_direction = point_b - point_a; + F32 segment_length = ray_direction.normVec(); + + if (ray_circle(point_a, ray_direction, circle_center, plane_normal, circle_radius, intersection)) + { + if (segment_length >= (point_a - intersection).magVec()) + { + return true; + } + } + return false; +} + + +bool linesegment_triangle(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 ray_direction = point_b - point_a; + F32 segment_length = ray_direction.normVec(); + + if (ray_triangle(point_a, ray_direction, point_0, point_1, point_2, intersection, intersection_normal)) + { + if (segment_length >= (point_a - intersection).magVec()) + { + return true; + } + } + return false; +} + + +bool linesegment_quadrangle(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 ray_direction = point_b - point_a; + F32 segment_length = ray_direction.normVec(); + + if (ray_quadrangle(point_a, ray_direction, point_0, point_1, point_2, intersection, intersection_normal)) + { + if (segment_length >= (point_a - intersection).magVec()) + { + return true; + } + } + return false; +} + + +bool linesegment_sphere(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &sphere_center, F32 sphere_radius, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 ray_direction = point_b - point_a; + F32 segment_length = ray_direction.normVec(); + + if (ray_sphere(point_a, ray_direction, sphere_center, sphere_radius, intersection, intersection_normal)) + { + if (segment_length >= (point_a - intersection).magVec()) + { + return true; + } + } + return false; +} + + +bool linesegment_cylinder(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 ray_direction = point_b - point_a; + F32 segment_length = ray_direction.normVec(); + + if (ray_cylinder(point_a, ray_direction, cyl_center, cyl_scale, cyl_rotation, intersection, intersection_normal)) + { + if (segment_length >= (point_a - intersection).magVec()) + { + return true; + } + } + return false; +} + + +U32 linesegment_box(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 direction = point_b - point_a; + if (direction.isNull()) + { + return NO_SIDE; + } + + F32 segment_length = direction.normVec(); + U32 box_side = ray_box(point_a, direction, box_center, box_scale, box_rotation, intersection, intersection_normal); + if (NO_SIDE == box_side || segment_length < (intersection - point_a).magVec()) + { + return NO_SIDE; + } + + return box_side; +} + + +bool linesegment_prism(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 ray_direction = point_b - point_a; + F32 segment_length = ray_direction.normVec(); + + if (ray_prism(point_a, ray_direction, prism_center, prism_scale, prism_rotation, intersection, intersection_normal)) + { + if (segment_length >= (point_a - intersection).magVec()) + { + return true; + } + } + return false; +} + + +bool linesegment_tetrahedron(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 ray_direction = point_b - point_a; + F32 segment_length = ray_direction.normVec(); + + if (ray_tetrahedron(point_a, ray_direction, t_center, t_scale, t_rotation, intersection, intersection_normal)) + { + if (segment_length >= (point_a - intersection).magVec()) + { + return true; + } + } + return false; +} + + +bool linesegment_pyramid(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal) +{ + LLVector3 ray_direction = point_b - point_a; + F32 segment_length = ray_direction.normVec(); + + if (ray_pyramid(point_a, ray_direction, p_center, p_scale, p_rotation, intersection, intersection_normal)) + { + if (segment_length >= (point_a - intersection).magVec()) + { + return true; + } + } + return false; +} diff --git a/indra/llmath/raytrace.h b/indra/llmath/raytrace.h index 8892347f9b..b55f29aef6 100644 --- a/indra/llmath/raytrace.h +++ b/indra/llmath/raytrace.h @@ -1,229 +1,229 @@ -/**
- * @file raytrace.h
- * @brief Ray intersection tests for primitives.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_RAYTRACE_H
-#define LL_RAYTRACE_H
-
-class LLVector3;
-class LLQuaternion;
-
-// All functions produce results in the same reference frame as the arguments.
-//
-// Any arguments of the form "foo_direction" or "foo_normal" are assumed to
-// be normalized, or normalized vectors are stored in them.
-//
-// Vector arguments of the form "shape_scale" represent the scale of the
-// object along the three axes.
-//
-// All functions return the expected true or false, unless otherwise noted.
-// When false is returned, any resulting values that might have been stored
-// are undefined.
-//
-// Rays are defined by a "ray_point" and a "ray_direction" (unit).
-//
-// Lines are defined by a "line_point" and a "line_direction" (unit).
-//
-// Line segements are defined by "point_a" and "point_b", and for intersection
-// purposes are assumed to point from "point_a" to "point_b".
-//
-// A ray is different from a line in that it starts at a point and extends
-// in only one direction.
-//
-// Intersection normals always point outside the object, normal to the object's
-// surface at the point of intersection.
-//
-// Object rotations passed as quaternions are expected to rotate from the
-// object's local frame to the absolute frame. So, if "foo" is a vector in
-// the object's local frame, then "foo * object_rotation" is in the absolute
-// frame.
-
-
-// returns true if line is not parallel to plane.
-bool line_plane(const LLVector3 &line_point, const LLVector3 &line_direction,
- const LLVector3 &plane_point, const LLVector3 plane_normal,
- LLVector3 &intersection);
-
-
-// returns true if line is not parallel to plane.
-bool ray_plane(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &plane_point, const LLVector3 plane_normal,
- LLVector3 &intersection);
-
-
-bool ray_circle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius,
- LLVector3 &intersection);
-
-// point_0 through point_2 define the plane_normal via the right-hand rule:
-// circle from point_0 to point_2 with fingers ==> thumb points in direction of normal
-bool ray_triangle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-// point_0 is the lower-left corner, point_1 is the lower-right, point_2 is the upper-right
-// right-hand-rule... curl fingers from lower-left toward lower-right then toward upper-right
-// ==> thumb points in direction of normal
-// assumes a parallelogram, so point_3 is determined by the other points
-bool ray_quadrangle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool ray_sphere(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &sphere_center, F32 sphere_radius,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-// finite right cylinder is defined by end centers: "cyl_top", "cyl_bottom",
-// and by the cylinder radius "cyl_radius"
-bool ray_cylinder(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-// this function doesn't just return a bool because the return is currently
-// used to decide how to break up boxes that have been hit by shots...
-// a hack that will probably be changed later
-//
-// returns a number representing the side of the box that was hit by the ray,
-// or NO_SIDE if intersection test failed.
-U32 ray_box(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-/* TODO
-bool ray_ellipsoid(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &e_center, const LLVector3 &e_scale, const LLQuaternion &e_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool ray_cone(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &cone_tip, const LLVector3 &cone_bottom,
- const LLVector3 &cone_scale, const LLQuaternion &cone_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-*/
-
-
-bool ray_prism(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool ray_tetrahedron(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool ray_pyramid(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-
-/* TODO
-bool ray_hemiellipsoid(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &e_center, const LLVector3 &e_scale, const LLQuaternion &e_rotation,
- const LLVector3 &e_cut_normal,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool ray_hemisphere(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &sphere_center, F32 sphere_radius, const LLVector3 &sphere_cut_normal,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool ray_hemicylinder(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &cyl_top, const LLVector3 &cyl_bottom, F32 cyl_radius,
- const LLVector3 &cyl_cut_normal,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool ray_hemicone(const LLVector3 &ray_point, const LLVector3 &ray_direction,
- const LLVector3 &cone_tip, const LLVector3 &cone_bottom,
- const LLVector3 &cone_scale, const LLVector3 &cyl_cut_normal,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-*/
-
-
-bool linesegment_circle(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius,
- LLVector3 &intersection);
-
-// point_0 through point_2 define the plane_normal via the right-hand rule:
-// circle from point_0 to point_2 with fingers ==> thumb points in direction of normal
-bool linesegment_triangle(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-// point_0 is the lower-left corner, point_1 is the lower-right, point_2 is the upper-right
-// right-hand-rule... curl fingers from lower-left toward lower-right then toward upper-right
-// ==> thumb points in direction of normal
-// assumes a parallelogram, so point_3 is determined by the other points
-bool linesegment_quadrangle(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool linesegment_sphere(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &sphere_center, F32 sphere_radius,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-// finite right cylinder is defined by end centers: "cyl_top", "cyl_bottom",
-// and by the cylinder radius "cyl_radius"
-bool linesegment_cylinder(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-// this function doesn't just return a bool because the return is currently
-// used to decide how to break up boxes that have been hit by shots...
-// a hack that will probably be changed later
-//
-// returns a number representing the side of the box that was hit by the ray,
-// or NO_SIDE if intersection test failed.
-U32 linesegment_box(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool linesegment_prism(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool linesegment_tetrahedron(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-
-
-bool linesegment_pyramid(const LLVector3 &point_a, const LLVector3 &point_b,
- const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation,
- LLVector3 &intersection, LLVector3 &intersection_normal);
-#endif
+/** + * @file raytrace.h + * @brief Ray intersection tests for primitives. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_RAYTRACE_H +#define LL_RAYTRACE_H + +class LLVector3; +class LLQuaternion; + +// All functions produce results in the same reference frame as the arguments. +// +// Any arguments of the form "foo_direction" or "foo_normal" are assumed to +// be normalized, or normalized vectors are stored in them. +// +// Vector arguments of the form "shape_scale" represent the scale of the +// object along the three axes. +// +// All functions return the expected true or false, unless otherwise noted. +// When false is returned, any resulting values that might have been stored +// are undefined. +// +// Rays are defined by a "ray_point" and a "ray_direction" (unit). +// +// Lines are defined by a "line_point" and a "line_direction" (unit). +// +// Line segements are defined by "point_a" and "point_b", and for intersection +// purposes are assumed to point from "point_a" to "point_b". +// +// A ray is different from a line in that it starts at a point and extends +// in only one direction. +// +// Intersection normals always point outside the object, normal to the object's +// surface at the point of intersection. +// +// Object rotations passed as quaternions are expected to rotate from the +// object's local frame to the absolute frame. So, if "foo" is a vector in +// the object's local frame, then "foo * object_rotation" is in the absolute +// frame. + + +// returns true if line is not parallel to plane. +bool line_plane(const LLVector3 &line_point, const LLVector3 &line_direction, + const LLVector3 &plane_point, const LLVector3 plane_normal, + LLVector3 &intersection); + + +// returns true if line is not parallel to plane. +bool ray_plane(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &plane_point, const LLVector3 plane_normal, + LLVector3 &intersection); + + +bool ray_circle(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius, + LLVector3 &intersection); + +// point_0 through point_2 define the plane_normal via the right-hand rule: +// circle from point_0 to point_2 with fingers ==> thumb points in direction of normal +bool ray_triangle(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +// point_0 is the lower-left corner, point_1 is the lower-right, point_2 is the upper-right +// right-hand-rule... curl fingers from lower-left toward lower-right then toward upper-right +// ==> thumb points in direction of normal +// assumes a parallelogram, so point_3 is determined by the other points +bool ray_quadrangle(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool ray_sphere(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &sphere_center, F32 sphere_radius, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +// finite right cylinder is defined by end centers: "cyl_top", "cyl_bottom", +// and by the cylinder radius "cyl_radius" +bool ray_cylinder(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +// this function doesn't just return a bool because the return is currently +// used to decide how to break up boxes that have been hit by shots... +// a hack that will probably be changed later +// +// returns a number representing the side of the box that was hit by the ray, +// or NO_SIDE if intersection test failed. +U32 ray_box(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +/* TODO +bool ray_ellipsoid(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &e_center, const LLVector3 &e_scale, const LLQuaternion &e_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool ray_cone(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &cone_tip, const LLVector3 &cone_bottom, + const LLVector3 &cone_scale, const LLQuaternion &cone_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); +*/ + + +bool ray_prism(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool ray_tetrahedron(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool ray_pyramid(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + + +/* TODO +bool ray_hemiellipsoid(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &e_center, const LLVector3 &e_scale, const LLQuaternion &e_rotation, + const LLVector3 &e_cut_normal, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool ray_hemisphere(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &sphere_center, F32 sphere_radius, const LLVector3 &sphere_cut_normal, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool ray_hemicylinder(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &cyl_top, const LLVector3 &cyl_bottom, F32 cyl_radius, + const LLVector3 &cyl_cut_normal, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool ray_hemicone(const LLVector3 &ray_point, const LLVector3 &ray_direction, + const LLVector3 &cone_tip, const LLVector3 &cone_bottom, + const LLVector3 &cone_scale, const LLVector3 &cyl_cut_normal, + LLVector3 &intersection, LLVector3 &intersection_normal); +*/ + + +bool linesegment_circle(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius, + LLVector3 &intersection); + +// point_0 through point_2 define the plane_normal via the right-hand rule: +// circle from point_0 to point_2 with fingers ==> thumb points in direction of normal +bool linesegment_triangle(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +// point_0 is the lower-left corner, point_1 is the lower-right, point_2 is the upper-right +// right-hand-rule... curl fingers from lower-left toward lower-right then toward upper-right +// ==> thumb points in direction of normal +// assumes a parallelogram, so point_3 is determined by the other points +bool linesegment_quadrangle(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool linesegment_sphere(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &sphere_center, F32 sphere_radius, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +// finite right cylinder is defined by end centers: "cyl_top", "cyl_bottom", +// and by the cylinder radius "cyl_radius" +bool linesegment_cylinder(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +// this function doesn't just return a bool because the return is currently +// used to decide how to break up boxes that have been hit by shots... +// a hack that will probably be changed later +// +// returns a number representing the side of the box that was hit by the ray, +// or NO_SIDE if intersection test failed. +U32 linesegment_box(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool linesegment_prism(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool linesegment_tetrahedron(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); + + +bool linesegment_pyramid(const LLVector3 &point_a, const LLVector3 &point_b, + const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation, + LLVector3 &intersection, LLVector3 &intersection_normal); +#endif diff --git a/indra/llmath/tests/llbbox_test.cpp b/indra/llmath/tests/llbbox_test.cpp index 0847420a3e..bd6bd9e4b0 100644 --- a/indra/llmath/tests/llbbox_test.cpp +++ b/indra/llmath/tests/llbbox_test.cpp @@ -1,367 +1,367 @@ -/**
- * @file llbbox_test.cpp
- * @author Martin Reddy
- * @date 2009-06-25
- * @brief Test for llbbox.cpp.
- *
- * $LicenseInfo:firstyear=2009&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 "../test/lltut.h"
-
-#include "../llbbox.h"
-
-
-#define ANGLE (3.14159265f / 2.0f)
-#define APPROX_EQUAL(a, b) (dist_vec_squared((a),(b)) < 1e-10)
-
-namespace tut
-{
- struct LLBBoxData
- {
- };
-
- typedef test_group<LLBBoxData> factory;
- typedef factory::object object;
-}
-
-namespace
-{
- tut::factory llbbox_test_factory("LLBBox");
-}
-
-namespace tut
-{
- template<> template<>
- void object::test<1>()
- {
- //
- // test the default constructor
- //
-
- LLBBox bbox1;
-
- ensure_equals("Default bbox min", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f));
- ensure_equals("Default bbox max", bbox1.getMaxLocal(), LLVector3(0.0f, 0.0f, 0.0f));
- ensure_equals("Default bbox pos agent", bbox1.getPositionAgent(), LLVector3(0.0f, 0.0f, 0.0f));
- ensure_equals("Default bbox rotation", bbox1.getRotation(), LLQuaternion(0.0f, 0.0f, 0.0f, 1.0f));
- }
-
- template<> template<>
- void object::test<2>()
- {
- //
- // test the non-default constructor
- //
-
- LLBBox bbox2(LLVector3(1.0f, 2.0f, 3.0f), LLQuaternion(),
- LLVector3(2.0f, 3.0f, 4.0f), LLVector3(4.0f, 5.0f, 6.0f));
-
- ensure_equals("Custom bbox min", bbox2.getMinLocal(), LLVector3(2.0f, 3.0f, 4.0f));
- ensure_equals("Custom bbox max", bbox2.getMaxLocal(), LLVector3(4.0f, 5.0f, 6.0f));
- ensure_equals("Custom bbox pos agent", bbox2.getPositionAgent(), LLVector3(1.0f, 2.0f, 3.0f));
- ensure_equals("Custom bbox rotation", bbox2.getRotation(), LLQuaternion(0.0f, 0.0f, 0.0f, 1.0f));
- }
-
- template<> template<>
- void object::test<3>()
- {
- //
- // test the setMinLocal() method
- //
- LLBBox bbox2;
- bbox2.setMinLocal(LLVector3(3.0f, 3.0f, 3.0f));
- ensure_equals("Custom bbox min (2)", bbox2.getMinLocal(), LLVector3(3.0f, 3.0f, 3.0f));
- }
-
- template<> template<>
- void object::test<4>()
- {
- //
- // test the setMaxLocal() method
- //
- LLBBox bbox2;
- bbox2.setMaxLocal(LLVector3(5.0f, 5.0f, 5.0f));
- ensure_equals("Custom bbox max (2)", bbox2.getMaxLocal(), LLVector3(5.0f, 5.0f, 5.0f));
- }
-
- template<> template<>
- void object::test<5>()
- {
- //
- // test the getCenterLocal() method
- //
-
- ensure_equals("Default bbox local center", LLBBox().getCenterLocal(), LLVector3(0.0f, 0.0f, 0.0f));
-
- LLBBox bbox1(LLVector3(1.0f, 2.0f, 3.0f), LLQuaternion(),
- LLVector3(2.0f, 4.0f, 6.0f), LLVector3(4.0f, 6.0f, 8.0f));
-
- ensure_equals("Custom bbox center local", bbox1.getCenterLocal(), LLVector3(3.0f, 5.0f, 7.0f));
-
- LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(0.0f, 0.0f, 1.0f)),
- LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f));
-
- ensure_equals("Custom bbox center local with rot", bbox2.getCenterLocal(), LLVector3(3.0f, 3.0f, 3.0f));
- }
-
- template<> template<>
- void object::test<6>()
- {
- //
- // test the getCenterAgent()
- //
-
- ensure_equals("Default bbox agent center", LLBBox().getCenterAgent(), LLVector3(0.0f, 0.0f, 0.0f));
-
- LLBBox bbox1(LLVector3(1.0f, 2.0f, 3.0f), LLQuaternion(),
- LLVector3(2.0f, 4.0f, 6.0f), LLVector3(4.0f, 6.0f, 8.0f));
-
- ensure_equals("Custom bbox center agent", bbox1.getCenterAgent(), LLVector3(4.0f, 7.0f, 10.0f));
-
- LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(0.0f, 0.0f, 1.0f)),
- LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f));
-
- ensure("Custom bbox center agent with rot", APPROX_EQUAL(bbox2.getCenterAgent(), LLVector3(-2.0f, 4.0f, 4.0f)));
- }
-
- template<> template<>
- void object::test<7>()
- {
- //
- // test the getExtentLocal() method
- //
-
- ensure_equals("Default bbox local extent", LLBBox().getExtentLocal(), LLVector3(0.0f, 0.0f, 0.0f));
-
- LLBBox bbox1(LLVector3(1.0f, 2.0f, 3.0f), LLQuaternion(),
- LLVector3(2.0f, 4.0f, 6.0f), LLVector3(4.0f, 6.0f, 8.0f));
-
- ensure_equals("Custom bbox extent local", bbox1.getExtentLocal(), LLVector3(2.0f, 2.0f, 2.0f));
-
- LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(0.0f, 0.0f, 1.0f)),
- LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f));
-
- ensure_equals("Custom bbox extent local with rot", bbox1.getExtentLocal(), LLVector3(2.0f, 2.0f, 2.0f));
- }
-
- template<> template<>
- void object::test<8>()
- {
- //
- // test the addPointLocal() method
- //
-
- LLBBox bbox1;
- bbox1.addPointLocal(LLVector3(1.0f, 1.0f, 1.0f));
- bbox1.addPointLocal(LLVector3(3.0f, 3.0f, 3.0f));
-
- ensure_equals("addPointLocal center local (1)", bbox1.getCenterLocal(), LLVector3(2.0f, 2.0f, 2.0f));
- ensure_equals("addPointLocal center agent (1)", bbox1.getCenterAgent(), LLVector3(2.0f, 2.0f, 2.0f));
- ensure_equals("addPointLocal min (1)", bbox1.getMinLocal(), LLVector3(1.0f, 1.0f, 1.0f));
- ensure_equals("addPointLocal max (1)", bbox1.getMaxLocal(), LLVector3(3.0f, 3.0f, 3.0f));
-
- bbox1.addPointLocal(LLVector3(0.0f, 0.0f, 0.0f));
- bbox1.addPointLocal(LLVector3(1.0f, 1.0f, 1.0f));
- bbox1.addPointLocal(LLVector3(2.0f, 2.0f, 2.0f));
-
- ensure_equals("addPointLocal center local (2)", bbox1.getCenterLocal(), LLVector3(1.5f, 1.5f, 1.5f));
- ensure_equals("addPointLocal min (2)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f));
- ensure_equals("addPointLocal max (2)", bbox1.getMaxLocal(), LLVector3(3.0f, 3.0f, 3.0f));
- }
-
- template<> template<>
- void object::test<9>()
- {
- //
- // test the addBBoxLocal() method
- //
-
- LLBBox bbox1;
- bbox1.addBBoxLocal(LLBBox(LLVector3(), LLQuaternion(),
- LLVector3(0.0f, 0.0f, 0.0f), LLVector3(3.0f, 3.0f, 3.0f)));
-
- ensure_equals("addPointLocal center local (3)", bbox1.getCenterLocal(), LLVector3(1.5f, 1.5f, 1.5f));
- ensure_equals("addPointLocal min (3)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f));
- ensure_equals("addPointLocal max (3)", bbox1.getMaxLocal(), LLVector3(3.0f, 3.0f, 3.0f));
-
- bbox1.addBBoxLocal(LLBBox(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(),
- LLVector3(5.0f, 5.0f, 5.0f), LLVector3(10.0f, 10.0f, 10.0f)));
-
- ensure_equals("addPointLocal center local (4)", bbox1.getCenterLocal(), LLVector3(5.0f, 5.0f, 5.0f));
- ensure_equals("addPointLocal center agent (4)", bbox1.getCenterAgent(), LLVector3(5.0f, 5.0f, 5.0f));
- ensure_equals("addPointLocal min (4)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f));
- ensure_equals("addPointLocal max (4)", bbox1.getMaxLocal(), LLVector3(10.0f, 10.0f, 10.0f));
- }
-
- template<> template<>
- void object::test<10>()
- {
- //
- // test the addPointAgent() method
- //
-
- LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(1.0, 0.0, 0.0, 1.0),
- LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f));
-
- bbox1.addPointAgent(LLVector3(1.0f, 1.0f, 1.0f));
- bbox1.addPointAgent(LLVector3(3.0f, 3.0f, 3.0f));
-
- ensure_equals("addPointAgent center local (1)", bbox1.getCenterLocal(), LLVector3(2.0f, 2.0f, -2.0f));
- ensure_equals("addPointAgent center agent (1)", bbox1.getCenterAgent(), LLVector3(3.0f, 3.0f, 7.0f));
- ensure_equals("addPointAgent min (1)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, -4.0f));
- ensure_equals("addPointAgent max (1)", bbox1.getMaxLocal(), LLVector3(4.0f, 4.0f, 0.0f));
- }
-
- template<> template<>
- void object::test<11>()
- {
- //
- // test the addBBoxAgent() method
- //
-
- LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(1.0, 0.0, 0.0, 1.0),
- LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f));
-
- bbox1.addPointAgent(LLVector3(1.0f, 1.0f, 1.0f));
- bbox1.addPointAgent(LLVector3(3.0f, 3.0f, 3.0f));
-
- bbox1.addBBoxLocal(LLBBox(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(),
- LLVector3(5.0f, 5.0f, 5.0f), LLVector3(10.0f, 10.0f, 10.0f)));
-
- ensure_equals("addPointAgent center local (2)", bbox1.getCenterLocal(), LLVector3(5.0f, 5.0f, 3.0f));
- ensure_equals("addPointAgent center agent (2)", bbox1.getCenterAgent(), LLVector3(6.0f, -10.0f, 8.0f));
- ensure_equals("addPointAgent min (2)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, -4.0f));
- ensure_equals("addPointAgent max (2)", bbox1.getMaxLocal(), LLVector3(10.0f, 10.0f, 10.0f));
- }
-
- template<> template<>
- void object::test<12>()
- {
- //
- // test the expand() method
- //
-
- LLBBox bbox1;
- bbox1.expand(0.0);
-
- ensure_equals("Zero-expanded Default BBox center", bbox1.getCenterLocal(), LLVector3(0.0f, 0.0f, 0.0f));
-
- LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(),
- LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f));
- bbox2.expand(0.0);
-
- ensure_equals("Zero-expanded center local", bbox2.getCenterLocal(), LLVector3(2.0f, 2.0f, 2.0f));
- ensure_equals("Zero-expanded center agent", bbox2.getCenterAgent(), LLVector3(3.0f, 3.0f, 3.0f));
- ensure_equals("Zero-expanded min", bbox2.getMinLocal(), LLVector3(1.0f, 1.0f, 1.0f));
- ensure_equals("Zero-expanded max", bbox2.getMaxLocal(), LLVector3(3.0f, 3.0f, 3.0f));
-
- bbox2.expand(0.5);
-
- ensure_equals("Positive-expanded center", bbox2.getCenterLocal(), LLVector3(2.0f, 2.0f, 2.0f));
- ensure_equals("Positive-expanded min", bbox2.getMinLocal(), LLVector3(0.5f, 0.5f, 0.5f));
- ensure_equals("Positive-expanded max", bbox2.getMaxLocal(), LLVector3(3.5f, 3.5f, 3.5f));
-
- bbox2.expand(-1.0);
-
- ensure_equals("Negative-expanded center", bbox2.getCenterLocal(), LLVector3(2.0f, 2.0f, 2.0f));
- ensure_equals("Negative-expanded min", bbox2.getMinLocal(), LLVector3(1.5f, 1.5f, 1.5f));
- ensure_equals("Negative-expanded max", bbox2.getMaxLocal(), LLVector3(2.5f, 2.5f, 2.5f));
- }
-
- template<> template<>
- void object::test<13>()
- {
- //
- // test the localToAgent() method
- //
-
- LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(),
- LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f));
-
- ensure_equals("localToAgent(1,2,3)", bbox1.localToAgent(LLVector3(1.0f, 2.0f, 3.0f)), LLVector3(2.0f, 3.0f, 4.0f));
-
- LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(1.0f, 0.0f, 0.0f)),
- LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f));
-
- ensure("localToAgent(1,2,3) rot", APPROX_EQUAL(bbox2.localToAgent(LLVector3(1.0f, 2.0f, 3.0f)), LLVector3(2.0f, -2.0f, 3.0f)));
- }
-
- template<> template<>
- void object::test<14>()
- {
- //
- // test the agentToLocal() method
- //
-
- LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(),
- LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f));
-
- ensure_equals("agentToLocal(1,2,3)", bbox1.agentToLocal(LLVector3(1.0f, 2.0f, 3.0f)), LLVector3(0.0f, 1.0f, 2.0f));
- ensure_equals("agentToLocal(localToAgent)", bbox1.agentToLocal(bbox1.localToAgent(LLVector3(1.0f, 2.0f, 3.0f))),
- LLVector3(1.0f, 2.0f, 3.0f));
-
- LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(1.0f, 0.0f, 0.0f)),
- LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f));
-
- ensure("agentToLocal(1,2,3) rot", APPROX_EQUAL(bbox2.agentToLocal(LLVector3(1.0f, 2.0f, 3.0f)), LLVector3(0.0f, 2.0f, -1.0f)));
- ensure("agentToLocal(localToAgent) rot", APPROX_EQUAL(bbox2.agentToLocal(bbox2.localToAgent(LLVector3(1.0f, 2.0f, 3.0f))),
- LLVector3(1.0f, 2.0f, 3.0f)));
- }
-
- template<> template<>
- void object::test<15>()
- {
- //
- // test the containsPointLocal() method
- //
-
- LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(),
- LLVector3(1.0f, 2.0f, 3.0f), LLVector3(3.0f, 4.0f, 5.0f));
-
- ensure("containsPointLocal(0,0,0)", bbox1.containsPointLocal(LLVector3(0.0f, 0.0f, 0.0f)) == false);
- ensure("containsPointLocal(1,2,3)", bbox1.containsPointLocal(LLVector3(1.0f, 2.0f, 3.0f)) == true);
- ensure("containsPointLocal(0.999,2,3)", bbox1.containsPointLocal(LLVector3(0.999f, 2.0f, 3.0f)) == false);
- ensure("containsPointLocal(3,4,5)", bbox1.containsPointLocal(LLVector3(3.0f, 4.0f, 5.0f)) == true);
- ensure("containsPointLocal(3,4,5.001)", bbox1.containsPointLocal(LLVector3(3.0f, 4.0f, 5.001f)) == false);
- }
-
- template<> template<>
- void object::test<16>()
- {
- //
- // test the containsPointAgent() method
- //
-
- LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(),
- LLVector3(1.0f, 2.0f, 3.0f), LLVector3(3.0f, 4.0f, 5.0f));
-
- ensure("containsPointAgent(0,0,0)", bbox1.containsPointAgent(LLVector3(0.0f, 0.0f, 0.0f)) == false);
- ensure("containsPointAgent(2,3,4)", bbox1.containsPointAgent(LLVector3(2.0f, 3.0f, 4.0f)) == true);
- ensure("containsPointAgent(2,2.999,4)", bbox1.containsPointAgent(LLVector3(2.0f, 2.999f, 4.0f)) == false);
- ensure("containsPointAgent(4,5,6)", bbox1.containsPointAgent(LLVector3(4.0f, 5.0f, 6.0f)) == true);
- ensure("containsPointAgent(4,5.001,6)", bbox1.containsPointAgent(LLVector3(4.0f, 5.001f, 6.0f)) == false);
- }
-}
-
+/** + * @file llbbox_test.cpp + * @author Martin Reddy + * @date 2009-06-25 + * @brief Test for llbbox.cpp. + * + * $LicenseInfo:firstyear=2009&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 "../test/lltut.h" + +#include "../llbbox.h" + + +#define ANGLE (3.14159265f / 2.0f) +#define APPROX_EQUAL(a, b) (dist_vec_squared((a),(b)) < 1e-10) + +namespace tut +{ + struct LLBBoxData + { + }; + + typedef test_group<LLBBoxData> factory; + typedef factory::object object; +} + +namespace +{ + tut::factory llbbox_test_factory("LLBBox"); +} + +namespace tut +{ + template<> template<> + void object::test<1>() + { + // + // test the default constructor + // + + LLBBox bbox1; + + ensure_equals("Default bbox min", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f)); + ensure_equals("Default bbox max", bbox1.getMaxLocal(), LLVector3(0.0f, 0.0f, 0.0f)); + ensure_equals("Default bbox pos agent", bbox1.getPositionAgent(), LLVector3(0.0f, 0.0f, 0.0f)); + ensure_equals("Default bbox rotation", bbox1.getRotation(), LLQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); + } + + template<> template<> + void object::test<2>() + { + // + // test the non-default constructor + // + + LLBBox bbox2(LLVector3(1.0f, 2.0f, 3.0f), LLQuaternion(), + LLVector3(2.0f, 3.0f, 4.0f), LLVector3(4.0f, 5.0f, 6.0f)); + + ensure_equals("Custom bbox min", bbox2.getMinLocal(), LLVector3(2.0f, 3.0f, 4.0f)); + ensure_equals("Custom bbox max", bbox2.getMaxLocal(), LLVector3(4.0f, 5.0f, 6.0f)); + ensure_equals("Custom bbox pos agent", bbox2.getPositionAgent(), LLVector3(1.0f, 2.0f, 3.0f)); + ensure_equals("Custom bbox rotation", bbox2.getRotation(), LLQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); + } + + template<> template<> + void object::test<3>() + { + // + // test the setMinLocal() method + // + LLBBox bbox2; + bbox2.setMinLocal(LLVector3(3.0f, 3.0f, 3.0f)); + ensure_equals("Custom bbox min (2)", bbox2.getMinLocal(), LLVector3(3.0f, 3.0f, 3.0f)); + } + + template<> template<> + void object::test<4>() + { + // + // test the setMaxLocal() method + // + LLBBox bbox2; + bbox2.setMaxLocal(LLVector3(5.0f, 5.0f, 5.0f)); + ensure_equals("Custom bbox max (2)", bbox2.getMaxLocal(), LLVector3(5.0f, 5.0f, 5.0f)); + } + + template<> template<> + void object::test<5>() + { + // + // test the getCenterLocal() method + // + + ensure_equals("Default bbox local center", LLBBox().getCenterLocal(), LLVector3(0.0f, 0.0f, 0.0f)); + + LLBBox bbox1(LLVector3(1.0f, 2.0f, 3.0f), LLQuaternion(), + LLVector3(2.0f, 4.0f, 6.0f), LLVector3(4.0f, 6.0f, 8.0f)); + + ensure_equals("Custom bbox center local", bbox1.getCenterLocal(), LLVector3(3.0f, 5.0f, 7.0f)); + + LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(0.0f, 0.0f, 1.0f)), + LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f)); + + ensure_equals("Custom bbox center local with rot", bbox2.getCenterLocal(), LLVector3(3.0f, 3.0f, 3.0f)); + } + + template<> template<> + void object::test<6>() + { + // + // test the getCenterAgent() + // + + ensure_equals("Default bbox agent center", LLBBox().getCenterAgent(), LLVector3(0.0f, 0.0f, 0.0f)); + + LLBBox bbox1(LLVector3(1.0f, 2.0f, 3.0f), LLQuaternion(), + LLVector3(2.0f, 4.0f, 6.0f), LLVector3(4.0f, 6.0f, 8.0f)); + + ensure_equals("Custom bbox center agent", bbox1.getCenterAgent(), LLVector3(4.0f, 7.0f, 10.0f)); + + LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(0.0f, 0.0f, 1.0f)), + LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f)); + + ensure("Custom bbox center agent with rot", APPROX_EQUAL(bbox2.getCenterAgent(), LLVector3(-2.0f, 4.0f, 4.0f))); + } + + template<> template<> + void object::test<7>() + { + // + // test the getExtentLocal() method + // + + ensure_equals("Default bbox local extent", LLBBox().getExtentLocal(), LLVector3(0.0f, 0.0f, 0.0f)); + + LLBBox bbox1(LLVector3(1.0f, 2.0f, 3.0f), LLQuaternion(), + LLVector3(2.0f, 4.0f, 6.0f), LLVector3(4.0f, 6.0f, 8.0f)); + + ensure_equals("Custom bbox extent local", bbox1.getExtentLocal(), LLVector3(2.0f, 2.0f, 2.0f)); + + LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(0.0f, 0.0f, 1.0f)), + LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f)); + + ensure_equals("Custom bbox extent local with rot", bbox1.getExtentLocal(), LLVector3(2.0f, 2.0f, 2.0f)); + } + + template<> template<> + void object::test<8>() + { + // + // test the addPointLocal() method + // + + LLBBox bbox1; + bbox1.addPointLocal(LLVector3(1.0f, 1.0f, 1.0f)); + bbox1.addPointLocal(LLVector3(3.0f, 3.0f, 3.0f)); + + ensure_equals("addPointLocal center local (1)", bbox1.getCenterLocal(), LLVector3(2.0f, 2.0f, 2.0f)); + ensure_equals("addPointLocal center agent (1)", bbox1.getCenterAgent(), LLVector3(2.0f, 2.0f, 2.0f)); + ensure_equals("addPointLocal min (1)", bbox1.getMinLocal(), LLVector3(1.0f, 1.0f, 1.0f)); + ensure_equals("addPointLocal max (1)", bbox1.getMaxLocal(), LLVector3(3.0f, 3.0f, 3.0f)); + + bbox1.addPointLocal(LLVector3(0.0f, 0.0f, 0.0f)); + bbox1.addPointLocal(LLVector3(1.0f, 1.0f, 1.0f)); + bbox1.addPointLocal(LLVector3(2.0f, 2.0f, 2.0f)); + + ensure_equals("addPointLocal center local (2)", bbox1.getCenterLocal(), LLVector3(1.5f, 1.5f, 1.5f)); + ensure_equals("addPointLocal min (2)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f)); + ensure_equals("addPointLocal max (2)", bbox1.getMaxLocal(), LLVector3(3.0f, 3.0f, 3.0f)); + } + + template<> template<> + void object::test<9>() + { + // + // test the addBBoxLocal() method + // + + LLBBox bbox1; + bbox1.addBBoxLocal(LLBBox(LLVector3(), LLQuaternion(), + LLVector3(0.0f, 0.0f, 0.0f), LLVector3(3.0f, 3.0f, 3.0f))); + + ensure_equals("addPointLocal center local (3)", bbox1.getCenterLocal(), LLVector3(1.5f, 1.5f, 1.5f)); + ensure_equals("addPointLocal min (3)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f)); + ensure_equals("addPointLocal max (3)", bbox1.getMaxLocal(), LLVector3(3.0f, 3.0f, 3.0f)); + + bbox1.addBBoxLocal(LLBBox(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(), + LLVector3(5.0f, 5.0f, 5.0f), LLVector3(10.0f, 10.0f, 10.0f))); + + ensure_equals("addPointLocal center local (4)", bbox1.getCenterLocal(), LLVector3(5.0f, 5.0f, 5.0f)); + ensure_equals("addPointLocal center agent (4)", bbox1.getCenterAgent(), LLVector3(5.0f, 5.0f, 5.0f)); + ensure_equals("addPointLocal min (4)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f)); + ensure_equals("addPointLocal max (4)", bbox1.getMaxLocal(), LLVector3(10.0f, 10.0f, 10.0f)); + } + + template<> template<> + void object::test<10>() + { + // + // test the addPointAgent() method + // + + LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(1.0, 0.0, 0.0, 1.0), + LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f)); + + bbox1.addPointAgent(LLVector3(1.0f, 1.0f, 1.0f)); + bbox1.addPointAgent(LLVector3(3.0f, 3.0f, 3.0f)); + + ensure_equals("addPointAgent center local (1)", bbox1.getCenterLocal(), LLVector3(2.0f, 2.0f, -2.0f)); + ensure_equals("addPointAgent center agent (1)", bbox1.getCenterAgent(), LLVector3(3.0f, 3.0f, 7.0f)); + ensure_equals("addPointAgent min (1)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, -4.0f)); + ensure_equals("addPointAgent max (1)", bbox1.getMaxLocal(), LLVector3(4.0f, 4.0f, 0.0f)); + } + + template<> template<> + void object::test<11>() + { + // + // test the addBBoxAgent() method + // + + LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(1.0, 0.0, 0.0, 1.0), + LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f)); + + bbox1.addPointAgent(LLVector3(1.0f, 1.0f, 1.0f)); + bbox1.addPointAgent(LLVector3(3.0f, 3.0f, 3.0f)); + + bbox1.addBBoxLocal(LLBBox(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(), + LLVector3(5.0f, 5.0f, 5.0f), LLVector3(10.0f, 10.0f, 10.0f))); + + ensure_equals("addPointAgent center local (2)", bbox1.getCenterLocal(), LLVector3(5.0f, 5.0f, 3.0f)); + ensure_equals("addPointAgent center agent (2)", bbox1.getCenterAgent(), LLVector3(6.0f, -10.0f, 8.0f)); + ensure_equals("addPointAgent min (2)", bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, -4.0f)); + ensure_equals("addPointAgent max (2)", bbox1.getMaxLocal(), LLVector3(10.0f, 10.0f, 10.0f)); + } + + template<> template<> + void object::test<12>() + { + // + // test the expand() method + // + + LLBBox bbox1; + bbox1.expand(0.0); + + ensure_equals("Zero-expanded Default BBox center", bbox1.getCenterLocal(), LLVector3(0.0f, 0.0f, 0.0f)); + + LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(), + LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f)); + bbox2.expand(0.0); + + ensure_equals("Zero-expanded center local", bbox2.getCenterLocal(), LLVector3(2.0f, 2.0f, 2.0f)); + ensure_equals("Zero-expanded center agent", bbox2.getCenterAgent(), LLVector3(3.0f, 3.0f, 3.0f)); + ensure_equals("Zero-expanded min", bbox2.getMinLocal(), LLVector3(1.0f, 1.0f, 1.0f)); + ensure_equals("Zero-expanded max", bbox2.getMaxLocal(), LLVector3(3.0f, 3.0f, 3.0f)); + + bbox2.expand(0.5); + + ensure_equals("Positive-expanded center", bbox2.getCenterLocal(), LLVector3(2.0f, 2.0f, 2.0f)); + ensure_equals("Positive-expanded min", bbox2.getMinLocal(), LLVector3(0.5f, 0.5f, 0.5f)); + ensure_equals("Positive-expanded max", bbox2.getMaxLocal(), LLVector3(3.5f, 3.5f, 3.5f)); + + bbox2.expand(-1.0); + + ensure_equals("Negative-expanded center", bbox2.getCenterLocal(), LLVector3(2.0f, 2.0f, 2.0f)); + ensure_equals("Negative-expanded min", bbox2.getMinLocal(), LLVector3(1.5f, 1.5f, 1.5f)); + ensure_equals("Negative-expanded max", bbox2.getMaxLocal(), LLVector3(2.5f, 2.5f, 2.5f)); + } + + template<> template<> + void object::test<13>() + { + // + // test the localToAgent() method + // + + LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(), + LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f)); + + ensure_equals("localToAgent(1,2,3)", bbox1.localToAgent(LLVector3(1.0f, 2.0f, 3.0f)), LLVector3(2.0f, 3.0f, 4.0f)); + + LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(1.0f, 0.0f, 0.0f)), + LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f)); + + ensure("localToAgent(1,2,3) rot", APPROX_EQUAL(bbox2.localToAgent(LLVector3(1.0f, 2.0f, 3.0f)), LLVector3(2.0f, -2.0f, 3.0f))); + } + + template<> template<> + void object::test<14>() + { + // + // test the agentToLocal() method + // + + LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(), + LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f)); + + ensure_equals("agentToLocal(1,2,3)", bbox1.agentToLocal(LLVector3(1.0f, 2.0f, 3.0f)), LLVector3(0.0f, 1.0f, 2.0f)); + ensure_equals("agentToLocal(localToAgent)", bbox1.agentToLocal(bbox1.localToAgent(LLVector3(1.0f, 2.0f, 3.0f))), + LLVector3(1.0f, 2.0f, 3.0f)); + + LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(1.0f, 0.0f, 0.0f)), + LLVector3(1.0f, 1.0f, 1.0f), LLVector3(3.0f, 3.0f, 3.0f)); + + ensure("agentToLocal(1,2,3) rot", APPROX_EQUAL(bbox2.agentToLocal(LLVector3(1.0f, 2.0f, 3.0f)), LLVector3(0.0f, 2.0f, -1.0f))); + ensure("agentToLocal(localToAgent) rot", APPROX_EQUAL(bbox2.agentToLocal(bbox2.localToAgent(LLVector3(1.0f, 2.0f, 3.0f))), + LLVector3(1.0f, 2.0f, 3.0f))); + } + + template<> template<> + void object::test<15>() + { + // + // test the containsPointLocal() method + // + + LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(), + LLVector3(1.0f, 2.0f, 3.0f), LLVector3(3.0f, 4.0f, 5.0f)); + + ensure("containsPointLocal(0,0,0)", bbox1.containsPointLocal(LLVector3(0.0f, 0.0f, 0.0f)) == false); + ensure("containsPointLocal(1,2,3)", bbox1.containsPointLocal(LLVector3(1.0f, 2.0f, 3.0f)) == true); + ensure("containsPointLocal(0.999,2,3)", bbox1.containsPointLocal(LLVector3(0.999f, 2.0f, 3.0f)) == false); + ensure("containsPointLocal(3,4,5)", bbox1.containsPointLocal(LLVector3(3.0f, 4.0f, 5.0f)) == true); + ensure("containsPointLocal(3,4,5.001)", bbox1.containsPointLocal(LLVector3(3.0f, 4.0f, 5.001f)) == false); + } + + template<> template<> + void object::test<16>() + { + // + // test the containsPointAgent() method + // + + LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(), + LLVector3(1.0f, 2.0f, 3.0f), LLVector3(3.0f, 4.0f, 5.0f)); + + ensure("containsPointAgent(0,0,0)", bbox1.containsPointAgent(LLVector3(0.0f, 0.0f, 0.0f)) == false); + ensure("containsPointAgent(2,3,4)", bbox1.containsPointAgent(LLVector3(2.0f, 3.0f, 4.0f)) == true); + ensure("containsPointAgent(2,2.999,4)", bbox1.containsPointAgent(LLVector3(2.0f, 2.999f, 4.0f)) == false); + ensure("containsPointAgent(4,5,6)", bbox1.containsPointAgent(LLVector3(4.0f, 5.0f, 6.0f)) == true); + ensure("containsPointAgent(4,5.001,6)", bbox1.containsPointAgent(LLVector3(4.0f, 5.001f, 6.0f)) == false); + } +} + diff --git a/indra/llmath/tests/llrect_test.cpp b/indra/llmath/tests/llrect_test.cpp index 5e3104e293..0fd5602d4a 100644 --- a/indra/llmath/tests/llrect_test.cpp +++ b/indra/llmath/tests/llrect_test.cpp @@ -1,526 +1,526 @@ -/**
- * @file llrect_test.cpp
- * @author Martin Reddy
- * @date 2009-06-25
- * @brief Test for llrect.cpp.
- *
- * $LicenseInfo:firstyear=2009&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 "../test/lltut.h"
-#include "../llrect.h"
-
-namespace tut
-{
- struct LLRectData
- {
- };
-
- typedef test_group<LLRectData> factory;
- typedef factory::object object;
-}
-
-namespace
-{
- tut::factory llrect_test_factory("LLRect");
-}
-
-namespace tut
-{
- template<> template<>
- void object::test<1>()
- {
- //
- // test the LLRect default constructor
- //
-
- LLSD zero;
- zero.append(0); zero.append(0); zero.append(0); zero.append(0);
-
- // default constructor
- LLRect rect1;
- ensure_equals("Empty rect", rect1.getValue(), zero);
- ensure_equals("Empty rect left", rect1.mLeft, 0);
- ensure_equals("Empty rect top", rect1.mTop, 0);
- ensure_equals("Empty rect right", rect1.mRight, 0);
- ensure_equals("Empty rect bottom", rect1.mBottom, 0);
- ensure_equals("Empty rect width", rect1.getWidth(), 0);
- ensure_equals("Empty rect height", rect1.getHeight(), 0);
- ensure_equals("Empty rect centerx", rect1.getCenterX(), 0);
- ensure_equals("Empty rect centery", rect1.getCenterY(), 0);
- }
-
- template<> template<>
- void object::test<2>()
- {
- //
- // test the LLRectf default constructor
- //
-
- LLSD zerof;
- zerof.append(0.0f); zerof.append(0.0f); zerof.append(0.0f); zerof.append(0.0f);
-
- LLRectf rect2;
- ensure_equals("Empty rectf", rect2.getValue(), zerof);
- ensure_equals("Empty rectf left", rect2.mLeft, 0.0f);
- ensure_equals("Empty rectf top", rect2.mTop, 0.0f);
- ensure_equals("Empty rectf right", rect2.mRight, 0.0f);
- ensure_equals("Empty rectf bottom", rect2.mBottom, 0.0f);
- ensure_equals("Empty rectf width", rect2.getWidth(), 0.0f);
- ensure_equals("Empty rectf height", rect2.getHeight(), 0.0f);
- ensure_equals("Empty rectf centerx", rect2.getCenterX(), 0.0f);
- ensure_equals("Empty rectf centery", rect2.getCenterY(), 0.0f);
- }
-
- template<> template<>
- void object::test<3>()
- {
- //
- // test the LLRect constructor from another LLRect
- //
-
- LLRect rect3(LLRect(1, 6, 7, 2));
- ensure_equals("Default rect left", rect3.mLeft, 1);
- ensure_equals("Default rect top", rect3.mTop, 6);
- ensure_equals("Default rect right", rect3.mRight, 7);
- ensure_equals("Default rect bottom", rect3.mBottom, 2);
- ensure_equals("Default rect width", rect3.getWidth(), 6);
- ensure_equals("Default rect height", rect3.getHeight(), 4);
- ensure_equals("Default rect centerx", rect3.getCenterX(), 4);
- ensure_equals("Default rect centery", rect3.getCenterY(), 4);
- }
-
- template<> template<>
- void object::test<4>()
- {
- //
- // test the LLRectf four-float constructor
- //
-
- LLRectf rect4(1.0f, 5.0f, 6.0f, 2.0f);
- ensure_equals("Default rectf left", rect4.mLeft, 1.0f);
- ensure_equals("Default rectf top", rect4.mTop, 5.0f);
- ensure_equals("Default rectf right", rect4.mRight, 6.0f);
- ensure_equals("Default rectf bottom", rect4.mBottom, 2.0f);
- ensure_equals("Default rectf width", rect4.getWidth(), 5.0f);
- ensure_equals("Default rectf height", rect4.getHeight(), 3.0f);
- ensure_equals("Default rectf centerx", rect4.getCenterX(), 3.5f);
- ensure_equals("Default rectf centery", rect4.getCenterY(), 3.5f);
- }
-
- template<> template<>
- void object::test<5>()
- {
- //
- // test the LLRectf LLSD constructor
- //
-
- LLSD array;
- array.append(-1.0f); array.append(0.0f); array.append(0.0f); array.append(-1.0f);
- LLRectf rect5(array);
- ensure_equals("LLSD rectf left", rect5.mLeft, -1.0f);
- ensure_equals("LLSD rectf top", rect5.mTop, 0.0f);
- ensure_equals("LLSD rectf right", rect5.mRight, 0.0f);
- ensure_equals("LLSD rectf bottom", rect5.mBottom, -1.0f);
- ensure_equals("LLSD rectf width", rect5.getWidth(), 1.0f);
- ensure_equals("LLSD rectf height", rect5.getHeight(), 1.0f);
- ensure_equals("LLSD rectf centerx", rect5.getCenterX(), -0.5f);
- ensure_equals("LLSD rectf centery", rect5.getCenterY(), -0.5f);
- }
-
- template<> template<>
- void object::test<6>()
- {
- //
- // test directly setting the member variables for dimensions
- //
-
- LLRectf rectf;
-
- rectf.mLeft = -1.0f;
- rectf.mTop = 1.0f;
- rectf.mRight = 1.0f;
- rectf.mBottom = -1.0f;
- ensure_equals("Member-set rectf left", rectf.mLeft, -1.0f);
- ensure_equals("Member-set rectf top", rectf.mTop, 1.0f);
- ensure_equals("Member-set rectf right", rectf.mRight, 1.0f);
- ensure_equals("Member-set rectf bottom", rectf.mBottom, -1.0f);
- ensure_equals("Member-set rectf width", rectf.getWidth(), 2.0f);
- ensure_equals("Member-set rectf height", rectf.getHeight(), 2.0f);
- ensure_equals("Member-set rectf centerx", rectf.getCenterX(), 0.0f);
- ensure_equals("Member-set rectf centery", rectf.getCenterY(), 0.0f);
- }
-
- template<> template<>
- void object::test<7>()
- {
- //
- // test the setValue() method
- //
-
- LLRectf rectf;
-
- LLSD array;
- array.append(-1.0f); array.append(0.0f); array.append(0.0f); array.append(-1.0f);
- rectf.setValue(array);
- ensure_equals("setValue() rectf left", rectf.mLeft, -1.0f);
- ensure_equals("setValue() rectf top", rectf.mTop, 0.0f);
- ensure_equals("setValue() rectf right", rectf.mRight, 0.0f);
- ensure_equals("setValue() rectf bottom", rectf.mBottom, -1.0f);
- ensure_equals("setValue() rectf width", rectf.getWidth(), 1.0f);
- ensure_equals("setValue() rectf height", rectf.getHeight(), 1.0f);
- ensure_equals("setValue() rectf centerx", rectf.getCenterX(), -0.5f);
- ensure_equals("setValue() rectf centery", rectf.getCenterY(), -0.5f);
- }
-
- template<> template<>
- void object::test<8>()
- {
- //
- // test the set() method
- //
-
- LLRect rect;
-
- rect.set(10, 90, 70, 10);
- ensure_equals("set() rectf left", rect.mLeft, 10);
- ensure_equals("set() rectf top", rect.mTop, 90);
- ensure_equals("set() rectf right", rect.mRight, 70);
- ensure_equals("set() rectf bottom", rect.mBottom, 10);
- ensure_equals("set() rectf width", rect.getWidth(), 60);
- ensure_equals("set() rectf height", rect.getHeight(), 80);
- ensure_equals("set() rectf centerx", rect.getCenterX(), 40);
- ensure_equals("set() rectf centery", rect.getCenterY(), 50);
- }
-
- template<> template<>
- void object::test<9>()
- {
- //
- // test the setOriginAndSize() method
- //
-
- LLRectf rectf;
-
- rectf.setOriginAndSize(0.0f, 0.0f, 2.0f, 1.0f);
- ensure_equals("setOriginAndSize() rectf left", rectf.mLeft, 0.0f);
- ensure_equals("setOriginAndSize() rectf top", rectf.mTop, 1.0f);
- ensure_equals("setOriginAndSize() rectf right", rectf.mRight, 2.0f);
- ensure_equals("setOriginAndSize() rectf bottom", rectf.mBottom, 0.0f);
- ensure_equals("setOriginAndSize() rectf width", rectf.getWidth(), 2.0f);
- ensure_equals("setOriginAndSize() rectf height", rectf.getHeight(), 1.0f);
- ensure_equals("setOriginAndSize() rectf centerx", rectf.getCenterX(), 1.0f);
- ensure_equals("setOriginAndSize() rectf centery", rectf.getCenterY(), 0.5f);
- }
-
- template<> template<>
- void object::test<10>()
- {
- //
- // test the setLeftTopAndSize() method
- //
-
- LLRectf rectf;
-
- rectf.setLeftTopAndSize(0.0f, 0.0f, 2.0f, 1.0f);
- ensure_equals("setLeftTopAndSize() rectf left", rectf.mLeft, 0.0f);
- ensure_equals("setLeftTopAndSize() rectf top", rectf.mTop, 0.0f);
- ensure_equals("setLeftTopAndSize() rectf right", rectf.mRight, 2.0f);
- ensure_equals("setLeftTopAndSize() rectf bottom", rectf.mBottom, -1.0f);
- ensure_equals("setLeftTopAndSize() rectf width", rectf.getWidth(), 2.0f);
- ensure_equals("setLeftTopAndSize() rectf height", rectf.getHeight(), 1.0f);
- ensure_equals("setLeftTopAndSize() rectf centerx", rectf.getCenterX(), 1.0f);
- ensure_equals("setLeftTopAndSize() rectf centery", rectf.getCenterY(), -0.5f);
- }
-
- template<> template<>
- void object::test<11>()
- {
- //
- // test the setCenterAndSize() method
- //
-
- LLRectf rectf;
-
- rectf.setCenterAndSize(0.0f, 0.0f, 2.0f, 1.0f);
- ensure_equals("setCenterAndSize() rectf left", rectf.mLeft, -1.0f);
- ensure_equals("setCenterAndSize() rectf top", rectf.mTop, 0.5f);
- ensure_equals("setCenterAndSize() rectf right", rectf.mRight, 1.0f);
- ensure_equals("setCenterAndSize() rectf bottom", rectf.mBottom, -0.5f);
- ensure_equals("setCenterAndSize() rectf width", rectf.getWidth(), 2.0f);
- ensure_equals("setCenterAndSize() rectf height", rectf.getHeight(), 1.0f);
- ensure_equals("setCenterAndSize() rectf centerx", rectf.getCenterX(), 0.0f);
- ensure_equals("setCenterAndSize() rectf centery", rectf.getCenterY(), 0.0f);
- }
-
- template<> template<>
- void object::test<12>()
- {
- //
- // test the validity checking method
- //
-
- LLRectf rectf;
-
- rectf.set(-1.0f, 1.0f, 1.0f, -1.0f);
- ensure("BBox is valid", rectf.isValid());
-
- rectf.mLeft = 2.0f;
- ensure("BBox is not valid", ! rectf.isValid());
-
- rectf.makeValid();
- ensure("BBox forced valid", rectf.isValid());
-
- rectf.set(-1.0f, -1.0f, -1.0f, -1.0f);
- ensure("BBox(0,0,0,0) is valid", rectf.isValid());
- }
-
- template<> template<>
- void object::test<13>()
- {
- //
- // test the null checking methods
- //
-
- LLRectf rectf;
-
- rectf.set(-1.0f, 1.0f, 1.0f, -1.0f);
- ensure("BBox is not Null", ! rectf.isEmpty());
- ensure("BBox notNull", rectf.notEmpty());
-
- rectf.mLeft = 2.0f;
- rectf.makeValid();
- ensure("BBox is now Null", rectf.isEmpty());
-
- rectf.set(-1.0f, -1.0f, -1.0f, -1.0f);
- ensure("BBox(0,0,0,0) is Null", rectf.isEmpty());
- }
-
- template<> template<>
- void object::test<14>()
- {
- //
- // test the (in)equality operators
- //
-
- LLRectf rect1, rect2;
-
- rect1.set(-1.0f, 1.0f, 1.0f, -1.0f);
- rect2.set(-1.0f, 0.9f, 1.0f, -1.0f);
-
- ensure("rect1 == rect2 (false)", ! (rect1 == rect2));
- ensure("rect1 != rect2 (true)", rect1 != rect2);
-
- ensure("rect1 == rect1 (true)", rect1 == rect1);
- ensure("rect1 != rect1 (false)", ! (rect1 != rect1));
- }
-
- template<> template<>
- void object::test<15>()
- {
- //
- // test the copy constructor
- //
-
- LLRectf rect1, rect2(rect1);
-
- ensure("rect1 == rect2 (true)", rect1 == rect2);
- ensure("rect1 != rect2 (false)", ! (rect1 != rect2));
- }
-
- template<> template<>
- void object::test<16>()
- {
- //
- // test the translate() method
- //
-
- LLRectf rect1(-1.0f, 1.0f, 1.0f, -1.0f);
- LLRectf rect2(rect1);
-
- rect1.translate(0.0f, 0.0f);
-
- ensure("translate(0, 0)", rect1 == rect2);
-
- rect1.translate(100.0f, 100.0f);
- rect1.translate(-100.0f, -100.0f);
-
- ensure("translate(100, 100) + translate(-100, -100)", rect1 == rect2);
-
- rect1.translate(10.0f, 0.0f);
- rect2.set(9.0f, 1.0f, 11.0f, -1.0f);
- ensure("translate(10, 0)", rect1 == rect2);
-
- rect1.translate(0.0f, 10.0f);
- rect2.set(9.0f, 11.0f, 11.0f, 9.0f);
- ensure("translate(0, 10)", rect1 == rect2);
-
- rect1.translate(-10.0f, -10.0f);
- rect2.set(-1.0f, 1.0f, 1.0f, -1.0f);
- ensure("translate(-10, -10)", rect1 == rect2);
- }
-
- template<> template<>
- void object::test<17>()
- {
- //
- // test the stretch() method
- //
-
- LLRectf rect1(-1.0f, 1.0f, 1.0f, -1.0f);
- LLRectf rect2(rect1);
-
- rect1.stretch(0.0f);
- ensure("stretch(0)", rect1 == rect2);
-
- rect1.stretch(0.0f, 0.0f);
- ensure("stretch(0, 0)", rect1 == rect2);
-
- rect1.stretch(10.0f);
- rect1.stretch(-10.0f);
- ensure("stretch(10) + stretch(-10)", rect1 == rect2);
-
- rect1.stretch(2.0f, 1.0f);
- rect2.set(-3.0f, 2.0f, 3.0f, -2.0f);
- ensure("stretch(2, 1)", rect1 == rect2);
- }
-
-
- template<> template<>
- void object::test<18>()
- {
- //
- // test the unionWith() method
- //
-
- LLRectf rect1, rect2, rect3;
-
- rect1.set(-1.0f, 1.0f, 1.0f, -1.0f);
- rect2.set(-1.0f, 1.0f, 1.0f, -1.0f);
- rect3 = rect1;
- rect3.unionWith(rect2);
- ensure_equals("union with self", rect3, rect1);
-
- rect1.set(-1.0f, 1.0f, 1.0f, -1.0f);
- rect2.set(-2.0f, 2.0f, 0.0f, 0.0f);
- rect3 = rect1;
- rect3.unionWith(rect2);
- ensure_equals("union - overlap", rect3, LLRectf(-2.0f, 2.0f, 1.0f, -1.0f));
-
- rect1.set(-1.0f, 1.0f, 1.0f, -1.0f);
- rect2.set(5.0f, 10.0f, 10.0f, 5.0f);
- rect3 = rect1;
- rect3.unionWith(rect2);
- ensure_equals("union - no overlap", rect3, LLRectf(-1.0f, 10.0f, 10.0f, -1.0f));
- }
-
- template<> template<>
- void object::test<19>()
- {
- //
- // test the intersectWith() methods
- //
-
- LLRectf rect1, rect2, rect3;
-
- rect1.set(-1.0f, 1.0f, 1.0f, -1.0f);
- rect2.set(-1.0f, 1.0f, 1.0f, -1.0f);
- rect3 = rect1;
- rect3.intersectWith(rect2);
- ensure_equals("intersect with self", rect3, rect1);
-
- rect1.set(-1.0f, 1.0f, 1.0f, -1.0f);
- rect2.set(-2.0f, 2.0f, 0.0f, 0.0f);
- rect3 = rect1;
- rect3.intersectWith(rect2);
- ensure_equals("intersect - overlap", rect3, LLRectf(-1.0f, 1.0f, 0.0f, 0.0f));
-
- rect1.set(-1.0f, 1.0f, 1.0f, -1.0f);
- rect2.set(5.0f, 10.0f, 10.0f, 5.0f);
- rect3 = rect1;
- rect3.intersectWith(rect2);
- ensure("intersect - no overlap", rect3.isEmpty());
- }
-
- template<> template<>
- void object::test<20>()
- {
- //
- // test the pointInRect() method
- //
-
- LLRectf rect(1.0f, 3.0f, 3.0f, 1.0f);
-
- ensure("(0,0) not in rect", rect.pointInRect(0.0f, 0.0f) == false);
- ensure("(2,2) in rect", rect.pointInRect(2.0f, 2.0f) == true);
- ensure("(1,1) in rect", rect.pointInRect(1.0f, 1.0f) == true);
- ensure("(3,3) not in rect", rect.pointInRect(3.0f, 3.0f) == false);
- ensure("(2.999,2.999) in rect", rect.pointInRect(2.999f, 2.999f) == true);
- ensure("(2.999,3.0) not in rect", rect.pointInRect(2.999f, 3.0f) == false);
- ensure("(3.0,2.999) not in rect", rect.pointInRect(3.0f, 2.999f) == false);
- }
-
- template<> template<>
- void object::test<21>()
- {
- //
- // test the localPointInRect() method
- //
-
- LLRectf rect(1.0f, 3.0f, 3.0f, 1.0f);
-
- ensure("(0,0) in local rect", rect.localPointInRect(0.0f, 0.0f) == true);
- ensure("(-0.0001,-0.0001) not in local rect", rect.localPointInRect(-0.0001f, -0.001f) == false);
- ensure("(1,1) in local rect", rect.localPointInRect(1.0f, 1.0f) == true);
- ensure("(2,2) not in local rect", rect.localPointInRect(2.0f, 2.0f) == false);
- ensure("(1.999,1.999) in local rect", rect.localPointInRect(1.999f, 1.999f) == true);
- ensure("(1.999,2.0) not in local rect", rect.localPointInRect(1.999f, 2.0f) == false);
- ensure("(2.0,1.999) not in local rect", rect.localPointInRect(2.0f, 1.999f) == false);
- }
-
- template<> template<>
- void object::test<22>()
- {
- //
- // test the clampPointToRect() method
- //
-
- LLRectf rect(1.0f, 3.0f, 3.0f, 1.0f);
- F32 x, y;
-
- x = 2.0f; y = 2.0f;
- rect.clampPointToRect(x, y);
- ensure_equals("clamp x-coord within rect", x, 2.0f);
- ensure_equals("clamp y-coord within rect", y, 2.0f);
-
- x = -100.0f; y = 100.0f;
- rect.clampPointToRect(x, y);
- ensure_equals("clamp x-coord outside rect", x, 1.0f);
- ensure_equals("clamp y-coord outside rect", y, 3.0f);
-
- x = 3.0f; y = 1.0f;
- rect.clampPointToRect(x, y);
- ensure_equals("clamp x-coord edge rect", x, 3.0f);
- ensure_equals("clamp y-coord edge rect", y, 1.0f);
- }
-}
+/** + * @file llrect_test.cpp + * @author Martin Reddy + * @date 2009-06-25 + * @brief Test for llrect.cpp. + * + * $LicenseInfo:firstyear=2009&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 "../test/lltut.h" +#include "../llrect.h" + +namespace tut +{ + struct LLRectData + { + }; + + typedef test_group<LLRectData> factory; + typedef factory::object object; +} + +namespace +{ + tut::factory llrect_test_factory("LLRect"); +} + +namespace tut +{ + template<> template<> + void object::test<1>() + { + // + // test the LLRect default constructor + // + + LLSD zero; + zero.append(0); zero.append(0); zero.append(0); zero.append(0); + + // default constructor + LLRect rect1; + ensure_equals("Empty rect", rect1.getValue(), zero); + ensure_equals("Empty rect left", rect1.mLeft, 0); + ensure_equals("Empty rect top", rect1.mTop, 0); + ensure_equals("Empty rect right", rect1.mRight, 0); + ensure_equals("Empty rect bottom", rect1.mBottom, 0); + ensure_equals("Empty rect width", rect1.getWidth(), 0); + ensure_equals("Empty rect height", rect1.getHeight(), 0); + ensure_equals("Empty rect centerx", rect1.getCenterX(), 0); + ensure_equals("Empty rect centery", rect1.getCenterY(), 0); + } + + template<> template<> + void object::test<2>() + { + // + // test the LLRectf default constructor + // + + LLSD zerof; + zerof.append(0.0f); zerof.append(0.0f); zerof.append(0.0f); zerof.append(0.0f); + + LLRectf rect2; + ensure_equals("Empty rectf", rect2.getValue(), zerof); + ensure_equals("Empty rectf left", rect2.mLeft, 0.0f); + ensure_equals("Empty rectf top", rect2.mTop, 0.0f); + ensure_equals("Empty rectf right", rect2.mRight, 0.0f); + ensure_equals("Empty rectf bottom", rect2.mBottom, 0.0f); + ensure_equals("Empty rectf width", rect2.getWidth(), 0.0f); + ensure_equals("Empty rectf height", rect2.getHeight(), 0.0f); + ensure_equals("Empty rectf centerx", rect2.getCenterX(), 0.0f); + ensure_equals("Empty rectf centery", rect2.getCenterY(), 0.0f); + } + + template<> template<> + void object::test<3>() + { + // + // test the LLRect constructor from another LLRect + // + + LLRect rect3(LLRect(1, 6, 7, 2)); + ensure_equals("Default rect left", rect3.mLeft, 1); + ensure_equals("Default rect top", rect3.mTop, 6); + ensure_equals("Default rect right", rect3.mRight, 7); + ensure_equals("Default rect bottom", rect3.mBottom, 2); + ensure_equals("Default rect width", rect3.getWidth(), 6); + ensure_equals("Default rect height", rect3.getHeight(), 4); + ensure_equals("Default rect centerx", rect3.getCenterX(), 4); + ensure_equals("Default rect centery", rect3.getCenterY(), 4); + } + + template<> template<> + void object::test<4>() + { + // + // test the LLRectf four-float constructor + // + + LLRectf rect4(1.0f, 5.0f, 6.0f, 2.0f); + ensure_equals("Default rectf left", rect4.mLeft, 1.0f); + ensure_equals("Default rectf top", rect4.mTop, 5.0f); + ensure_equals("Default rectf right", rect4.mRight, 6.0f); + ensure_equals("Default rectf bottom", rect4.mBottom, 2.0f); + ensure_equals("Default rectf width", rect4.getWidth(), 5.0f); + ensure_equals("Default rectf height", rect4.getHeight(), 3.0f); + ensure_equals("Default rectf centerx", rect4.getCenterX(), 3.5f); + ensure_equals("Default rectf centery", rect4.getCenterY(), 3.5f); + } + + template<> template<> + void object::test<5>() + { + // + // test the LLRectf LLSD constructor + // + + LLSD array; + array.append(-1.0f); array.append(0.0f); array.append(0.0f); array.append(-1.0f); + LLRectf rect5(array); + ensure_equals("LLSD rectf left", rect5.mLeft, -1.0f); + ensure_equals("LLSD rectf top", rect5.mTop, 0.0f); + ensure_equals("LLSD rectf right", rect5.mRight, 0.0f); + ensure_equals("LLSD rectf bottom", rect5.mBottom, -1.0f); + ensure_equals("LLSD rectf width", rect5.getWidth(), 1.0f); + ensure_equals("LLSD rectf height", rect5.getHeight(), 1.0f); + ensure_equals("LLSD rectf centerx", rect5.getCenterX(), -0.5f); + ensure_equals("LLSD rectf centery", rect5.getCenterY(), -0.5f); + } + + template<> template<> + void object::test<6>() + { + // + // test directly setting the member variables for dimensions + // + + LLRectf rectf; + + rectf.mLeft = -1.0f; + rectf.mTop = 1.0f; + rectf.mRight = 1.0f; + rectf.mBottom = -1.0f; + ensure_equals("Member-set rectf left", rectf.mLeft, -1.0f); + ensure_equals("Member-set rectf top", rectf.mTop, 1.0f); + ensure_equals("Member-set rectf right", rectf.mRight, 1.0f); + ensure_equals("Member-set rectf bottom", rectf.mBottom, -1.0f); + ensure_equals("Member-set rectf width", rectf.getWidth(), 2.0f); + ensure_equals("Member-set rectf height", rectf.getHeight(), 2.0f); + ensure_equals("Member-set rectf centerx", rectf.getCenterX(), 0.0f); + ensure_equals("Member-set rectf centery", rectf.getCenterY(), 0.0f); + } + + template<> template<> + void object::test<7>() + { + // + // test the setValue() method + // + + LLRectf rectf; + + LLSD array; + array.append(-1.0f); array.append(0.0f); array.append(0.0f); array.append(-1.0f); + rectf.setValue(array); + ensure_equals("setValue() rectf left", rectf.mLeft, -1.0f); + ensure_equals("setValue() rectf top", rectf.mTop, 0.0f); + ensure_equals("setValue() rectf right", rectf.mRight, 0.0f); + ensure_equals("setValue() rectf bottom", rectf.mBottom, -1.0f); + ensure_equals("setValue() rectf width", rectf.getWidth(), 1.0f); + ensure_equals("setValue() rectf height", rectf.getHeight(), 1.0f); + ensure_equals("setValue() rectf centerx", rectf.getCenterX(), -0.5f); + ensure_equals("setValue() rectf centery", rectf.getCenterY(), -0.5f); + } + + template<> template<> + void object::test<8>() + { + // + // test the set() method + // + + LLRect rect; + + rect.set(10, 90, 70, 10); + ensure_equals("set() rectf left", rect.mLeft, 10); + ensure_equals("set() rectf top", rect.mTop, 90); + ensure_equals("set() rectf right", rect.mRight, 70); + ensure_equals("set() rectf bottom", rect.mBottom, 10); + ensure_equals("set() rectf width", rect.getWidth(), 60); + ensure_equals("set() rectf height", rect.getHeight(), 80); + ensure_equals("set() rectf centerx", rect.getCenterX(), 40); + ensure_equals("set() rectf centery", rect.getCenterY(), 50); + } + + template<> template<> + void object::test<9>() + { + // + // test the setOriginAndSize() method + // + + LLRectf rectf; + + rectf.setOriginAndSize(0.0f, 0.0f, 2.0f, 1.0f); + ensure_equals("setOriginAndSize() rectf left", rectf.mLeft, 0.0f); + ensure_equals("setOriginAndSize() rectf top", rectf.mTop, 1.0f); + ensure_equals("setOriginAndSize() rectf right", rectf.mRight, 2.0f); + ensure_equals("setOriginAndSize() rectf bottom", rectf.mBottom, 0.0f); + ensure_equals("setOriginAndSize() rectf width", rectf.getWidth(), 2.0f); + ensure_equals("setOriginAndSize() rectf height", rectf.getHeight(), 1.0f); + ensure_equals("setOriginAndSize() rectf centerx", rectf.getCenterX(), 1.0f); + ensure_equals("setOriginAndSize() rectf centery", rectf.getCenterY(), 0.5f); + } + + template<> template<> + void object::test<10>() + { + // + // test the setLeftTopAndSize() method + // + + LLRectf rectf; + + rectf.setLeftTopAndSize(0.0f, 0.0f, 2.0f, 1.0f); + ensure_equals("setLeftTopAndSize() rectf left", rectf.mLeft, 0.0f); + ensure_equals("setLeftTopAndSize() rectf top", rectf.mTop, 0.0f); + ensure_equals("setLeftTopAndSize() rectf right", rectf.mRight, 2.0f); + ensure_equals("setLeftTopAndSize() rectf bottom", rectf.mBottom, -1.0f); + ensure_equals("setLeftTopAndSize() rectf width", rectf.getWidth(), 2.0f); + ensure_equals("setLeftTopAndSize() rectf height", rectf.getHeight(), 1.0f); + ensure_equals("setLeftTopAndSize() rectf centerx", rectf.getCenterX(), 1.0f); + ensure_equals("setLeftTopAndSize() rectf centery", rectf.getCenterY(), -0.5f); + } + + template<> template<> + void object::test<11>() + { + // + // test the setCenterAndSize() method + // + + LLRectf rectf; + + rectf.setCenterAndSize(0.0f, 0.0f, 2.0f, 1.0f); + ensure_equals("setCenterAndSize() rectf left", rectf.mLeft, -1.0f); + ensure_equals("setCenterAndSize() rectf top", rectf.mTop, 0.5f); + ensure_equals("setCenterAndSize() rectf right", rectf.mRight, 1.0f); + ensure_equals("setCenterAndSize() rectf bottom", rectf.mBottom, -0.5f); + ensure_equals("setCenterAndSize() rectf width", rectf.getWidth(), 2.0f); + ensure_equals("setCenterAndSize() rectf height", rectf.getHeight(), 1.0f); + ensure_equals("setCenterAndSize() rectf centerx", rectf.getCenterX(), 0.0f); + ensure_equals("setCenterAndSize() rectf centery", rectf.getCenterY(), 0.0f); + } + + template<> template<> + void object::test<12>() + { + // + // test the validity checking method + // + + LLRectf rectf; + + rectf.set(-1.0f, 1.0f, 1.0f, -1.0f); + ensure("BBox is valid", rectf.isValid()); + + rectf.mLeft = 2.0f; + ensure("BBox is not valid", ! rectf.isValid()); + + rectf.makeValid(); + ensure("BBox forced valid", rectf.isValid()); + + rectf.set(-1.0f, -1.0f, -1.0f, -1.0f); + ensure("BBox(0,0,0,0) is valid", rectf.isValid()); + } + + template<> template<> + void object::test<13>() + { + // + // test the null checking methods + // + + LLRectf rectf; + + rectf.set(-1.0f, 1.0f, 1.0f, -1.0f); + ensure("BBox is not Null", ! rectf.isEmpty()); + ensure("BBox notNull", rectf.notEmpty()); + + rectf.mLeft = 2.0f; + rectf.makeValid(); + ensure("BBox is now Null", rectf.isEmpty()); + + rectf.set(-1.0f, -1.0f, -1.0f, -1.0f); + ensure("BBox(0,0,0,0) is Null", rectf.isEmpty()); + } + + template<> template<> + void object::test<14>() + { + // + // test the (in)equality operators + // + + LLRectf rect1, rect2; + + rect1.set(-1.0f, 1.0f, 1.0f, -1.0f); + rect2.set(-1.0f, 0.9f, 1.0f, -1.0f); + + ensure("rect1 == rect2 (false)", ! (rect1 == rect2)); + ensure("rect1 != rect2 (true)", rect1 != rect2); + + ensure("rect1 == rect1 (true)", rect1 == rect1); + ensure("rect1 != rect1 (false)", ! (rect1 != rect1)); + } + + template<> template<> + void object::test<15>() + { + // + // test the copy constructor + // + + LLRectf rect1, rect2(rect1); + + ensure("rect1 == rect2 (true)", rect1 == rect2); + ensure("rect1 != rect2 (false)", ! (rect1 != rect2)); + } + + template<> template<> + void object::test<16>() + { + // + // test the translate() method + // + + LLRectf rect1(-1.0f, 1.0f, 1.0f, -1.0f); + LLRectf rect2(rect1); + + rect1.translate(0.0f, 0.0f); + + ensure("translate(0, 0)", rect1 == rect2); + + rect1.translate(100.0f, 100.0f); + rect1.translate(-100.0f, -100.0f); + + ensure("translate(100, 100) + translate(-100, -100)", rect1 == rect2); + + rect1.translate(10.0f, 0.0f); + rect2.set(9.0f, 1.0f, 11.0f, -1.0f); + ensure("translate(10, 0)", rect1 == rect2); + + rect1.translate(0.0f, 10.0f); + rect2.set(9.0f, 11.0f, 11.0f, 9.0f); + ensure("translate(0, 10)", rect1 == rect2); + + rect1.translate(-10.0f, -10.0f); + rect2.set(-1.0f, 1.0f, 1.0f, -1.0f); + ensure("translate(-10, -10)", rect1 == rect2); + } + + template<> template<> + void object::test<17>() + { + // + // test the stretch() method + // + + LLRectf rect1(-1.0f, 1.0f, 1.0f, -1.0f); + LLRectf rect2(rect1); + + rect1.stretch(0.0f); + ensure("stretch(0)", rect1 == rect2); + + rect1.stretch(0.0f, 0.0f); + ensure("stretch(0, 0)", rect1 == rect2); + + rect1.stretch(10.0f); + rect1.stretch(-10.0f); + ensure("stretch(10) + stretch(-10)", rect1 == rect2); + + rect1.stretch(2.0f, 1.0f); + rect2.set(-3.0f, 2.0f, 3.0f, -2.0f); + ensure("stretch(2, 1)", rect1 == rect2); + } + + + template<> template<> + void object::test<18>() + { + // + // test the unionWith() method + // + + LLRectf rect1, rect2, rect3; + + rect1.set(-1.0f, 1.0f, 1.0f, -1.0f); + rect2.set(-1.0f, 1.0f, 1.0f, -1.0f); + rect3 = rect1; + rect3.unionWith(rect2); + ensure_equals("union with self", rect3, rect1); + + rect1.set(-1.0f, 1.0f, 1.0f, -1.0f); + rect2.set(-2.0f, 2.0f, 0.0f, 0.0f); + rect3 = rect1; + rect3.unionWith(rect2); + ensure_equals("union - overlap", rect3, LLRectf(-2.0f, 2.0f, 1.0f, -1.0f)); + + rect1.set(-1.0f, 1.0f, 1.0f, -1.0f); + rect2.set(5.0f, 10.0f, 10.0f, 5.0f); + rect3 = rect1; + rect3.unionWith(rect2); + ensure_equals("union - no overlap", rect3, LLRectf(-1.0f, 10.0f, 10.0f, -1.0f)); + } + + template<> template<> + void object::test<19>() + { + // + // test the intersectWith() methods + // + + LLRectf rect1, rect2, rect3; + + rect1.set(-1.0f, 1.0f, 1.0f, -1.0f); + rect2.set(-1.0f, 1.0f, 1.0f, -1.0f); + rect3 = rect1; + rect3.intersectWith(rect2); + ensure_equals("intersect with self", rect3, rect1); + + rect1.set(-1.0f, 1.0f, 1.0f, -1.0f); + rect2.set(-2.0f, 2.0f, 0.0f, 0.0f); + rect3 = rect1; + rect3.intersectWith(rect2); + ensure_equals("intersect - overlap", rect3, LLRectf(-1.0f, 1.0f, 0.0f, 0.0f)); + + rect1.set(-1.0f, 1.0f, 1.0f, -1.0f); + rect2.set(5.0f, 10.0f, 10.0f, 5.0f); + rect3 = rect1; + rect3.intersectWith(rect2); + ensure("intersect - no overlap", rect3.isEmpty()); + } + + template<> template<> + void object::test<20>() + { + // + // test the pointInRect() method + // + + LLRectf rect(1.0f, 3.0f, 3.0f, 1.0f); + + ensure("(0,0) not in rect", rect.pointInRect(0.0f, 0.0f) == false); + ensure("(2,2) in rect", rect.pointInRect(2.0f, 2.0f) == true); + ensure("(1,1) in rect", rect.pointInRect(1.0f, 1.0f) == true); + ensure("(3,3) not in rect", rect.pointInRect(3.0f, 3.0f) == false); + ensure("(2.999,2.999) in rect", rect.pointInRect(2.999f, 2.999f) == true); + ensure("(2.999,3.0) not in rect", rect.pointInRect(2.999f, 3.0f) == false); + ensure("(3.0,2.999) not in rect", rect.pointInRect(3.0f, 2.999f) == false); + } + + template<> template<> + void object::test<21>() + { + // + // test the localPointInRect() method + // + + LLRectf rect(1.0f, 3.0f, 3.0f, 1.0f); + + ensure("(0,0) in local rect", rect.localPointInRect(0.0f, 0.0f) == true); + ensure("(-0.0001,-0.0001) not in local rect", rect.localPointInRect(-0.0001f, -0.001f) == false); + ensure("(1,1) in local rect", rect.localPointInRect(1.0f, 1.0f) == true); + ensure("(2,2) not in local rect", rect.localPointInRect(2.0f, 2.0f) == false); + ensure("(1.999,1.999) in local rect", rect.localPointInRect(1.999f, 1.999f) == true); + ensure("(1.999,2.0) not in local rect", rect.localPointInRect(1.999f, 2.0f) == false); + ensure("(2.0,1.999) not in local rect", rect.localPointInRect(2.0f, 1.999f) == false); + } + + template<> template<> + void object::test<22>() + { + // + // test the clampPointToRect() method + // + + LLRectf rect(1.0f, 3.0f, 3.0f, 1.0f); + F32 x, y; + + x = 2.0f; y = 2.0f; + rect.clampPointToRect(x, y); + ensure_equals("clamp x-coord within rect", x, 2.0f); + ensure_equals("clamp y-coord within rect", y, 2.0f); + + x = -100.0f; y = 100.0f; + rect.clampPointToRect(x, y); + ensure_equals("clamp x-coord outside rect", x, 1.0f); + ensure_equals("clamp y-coord outside rect", y, 3.0f); + + x = 3.0f; y = 1.0f; + rect.clampPointToRect(x, y); + ensure_equals("clamp x-coord edge rect", x, 3.0f); + ensure_equals("clamp y-coord edge rect", y, 1.0f); + } +} diff --git a/indra/llmath/tests/mathmisc_test.cpp b/indra/llmath/tests/mathmisc_test.cpp index 63d35bee96..ff0899e975 100644 --- a/indra/llmath/tests/mathmisc_test.cpp +++ b/indra/llmath/tests/mathmisc_test.cpp @@ -1,723 +1,723 @@ -/**
- * @file math.cpp
- * @author Phoenix
- * @date 2005-09-26
- * @brief Tests for the llmath library.
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "../test/lltut.h"
-
-#include "llcrc.h"
-#include "llrand.h"
-#include "lluuid.h"
-
-#include "../llline.h"
-#include "../llmath.h"
-#include "../llsphere.h"
-#include "../v3math.h"
-
-namespace tut
-{
- struct math_data
- {
- };
- typedef test_group<math_data> math_test;
- typedef math_test::object math_object;
- tut::math_test tm("BasicLindenMath");
-
- template<> template<>
- void math_object::test<1>()
- {
- S32 val = 89543;
- val = llabs(val);
- ensure("integer absolute value 1", (89543 == val));
- val = -500;
- val = llabs(val);
- ensure("integer absolute value 2", (500 == val));
- }
-
- template<> template<>
- void math_object::test<2>()
- {
- F32 val = -2583.4f;
- val = llabs(val);
- ensure("float absolute value 1", (2583.4f == val));
- val = 430903.f;
- val = llabs(val);
- ensure("float absolute value 2", (430903.f == val));
- }
-
- template<> template<>
- void math_object::test<3>()
- {
- F64 val = 387439393.987329839;
- val = llabs(val);
- ensure("double absolute value 1", (387439393.987329839 == val));
- val = -8937843.9394878;
- val = llabs(val);
- ensure("double absolute value 2", (8937843.9394878 == val));
- }
-
- template<> template<>
- void math_object::test<4>()
- {
- F32 val = 430903.9f;
- S32 val1 = lltrunc(val);
- ensure("float truncate value 1", (430903 == val1));
- val = -2303.9f;
- val1 = lltrunc(val);
- ensure("float truncate value 2", (-2303 == val1));
- }
-
- template<> template<>
- void math_object::test<5>()
- {
- F64 val = 387439393.987329839 ;
- S32 val1 = lltrunc(val);
- ensure("float truncate value 1", (387439393 == val1));
- val = -387439393.987329839;
- val1 = lltrunc(val);
- ensure("float truncate value 2", (-387439393 == val1));
- }
-
- template<> template<>
- void math_object::test<6>()
- {
- F32 val = 430903.2f;
- S32 val1 = llfloor(val);
- ensure("float llfloor value 1", (430903 == val1));
- val = -430903.9f;
- val1 = llfloor(val);
- ensure("float llfloor value 2", (-430904 == val1));
- }
-
- template<> template<>
- void math_object::test<7>()
- {
- F32 val = 430903.2f;
- S32 val1 = llceil(val);
- ensure("float llceil value 1", (430904 == val1));
- val = -430903.9f;
- val1 = llceil(val);
- ensure("float llceil value 2", (-430903 == val1));
- }
-
- template<> template<>
- void math_object::test<8>()
- {
- F32 val = 430903.2f;
- S32 val1 = ll_round(val);
- ensure("float ll_round value 1", (430903 == val1));
- val = -430903.9f;
- val1 = ll_round(val);
- ensure("float ll_round value 2", (-430904 == val1));
- }
-
- template<> template<>
- void math_object::test<9>()
- {
- F32 val = 430905.2654f, nearest = 100.f;
- val = ll_round(val, nearest);
- ensure("float ll_round value 1", (430900 == val));
- val = -430905.2654f, nearest = 10.f;
- val = ll_round(val, nearest);
- ensure("float ll_round value 1", (-430910 == val));
- }
-
- template<> template<>
- void math_object::test<10>()
- {
- F64 val = 430905.2654, nearest = 100.0;
- val = ll_round(val, nearest);
- ensure("double ll_round value 1", (430900 == val));
- val = -430905.2654, nearest = 10.0;
- val = ll_round(val, nearest);
- ensure("double ll_round value 1", (-430910.00000 == val));
- }
-
- template<> template<>
- void math_object::test<11>()
- {
- const F32 F_PI = 3.1415926535897932384626433832795f;
- F32 angle = 3506.f;
- angle = llsimple_angle(angle);
- ensure("llsimple_angle value 1", (angle <=F_PI && angle >= -F_PI));
- angle = -431.f;
- angle = llsimple_angle(angle);
- ensure("llsimple_angle value 1", (angle <=F_PI && angle >= -F_PI));
- }
-}
-
-namespace tut
-{
- struct uuid_data
- {
- LLUUID id;
- };
- typedef test_group<uuid_data> uuid_test;
- typedef uuid_test::object uuid_object;
- tut::uuid_test tu("LLUUID");
-
- template<> template<>
- void uuid_object::test<1>()
- {
- ensure("uuid null", id.isNull());
- id.generate();
- ensure("generate not null", id.notNull());
- id.setNull();
- ensure("set null", id.isNull());
- }
-
- template<> template<>
- void uuid_object::test<2>()
- {
- id.generate();
- LLUUID a(id);
- ensure_equals("copy equal", id, a);
- a.generate();
- ensure_not_equals("generate not equal", id, a);
- a = id;
- ensure_equals("assignment equal", id, a);
- }
-
- template<> template<>
- void uuid_object::test<3>()
- {
- id.generate();
- LLUUID copy(id);
- LLUUID mask;
- mask.generate();
- copy ^= mask;
- ensure_not_equals("mask not equal", id, copy);
- copy ^= mask;
- ensure_equals("mask back", id, copy);
- }
-
- template<> template<>
- void uuid_object::test<4>()
- {
- id.generate();
- std::string id_str = id.asString();
- LLUUID copy(id_str.c_str());
- ensure_equals("string serialization", id, copy);
- }
-
-}
-
-namespace tut
-{
- struct crc_data
- {
- };
- typedef test_group<crc_data> crc_test;
- typedef crc_test::object crc_object;
- tut::crc_test tc("LLCrc");
-
- template<> template<>
- void crc_object::test<1>()
- {
- /* Test buffer update and individual char update */
- const char TEST_BUFFER[] = "hello &#$)$&Nd0";
- LLCRC c1, c2;
- c1.update((U8*)TEST_BUFFER, sizeof(TEST_BUFFER) - 1);
- char* rh = (char*)TEST_BUFFER;
- while(*rh != '\0')
- {
- c2.update(*rh);
- ++rh;
- }
- ensure_equals("crc update 1", c1.getCRC(), c2.getCRC());
- }
-
- template<> template<>
- void crc_object::test<2>()
- {
- /* Test mixing of buffer and individual char update */
- const char TEST_BUFFER1[] = "Split Buffer one $^%$%#@$";
- const char TEST_BUFFER2[] = "Split Buffer two )(8723#5dsds";
- LLCRC c1, c2;
- c1.update((U8*)TEST_BUFFER1, sizeof(TEST_BUFFER1) - 1);
- char* rh = (char*)TEST_BUFFER2;
- while(*rh != '\0')
- {
- c1.update(*rh);
- ++rh;
- }
-
- rh = (char*)TEST_BUFFER1;
- while(*rh != '\0')
- {
- c2.update(*rh);
- ++rh;
- }
- c2.update((U8*)TEST_BUFFER2, sizeof(TEST_BUFFER2) - 1);
-
- ensure_equals("crc update 2", c1.getCRC(), c2.getCRC());
- }
-}
-
-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;
-
- struct line_data
- {
- };
- typedef test_group<line_data> line_test;
- typedef line_test::object line_object;
- tut::line_test tline("LLLine");
-
- template<> template<>
- void line_object::test<1>()
- {
- // this is a test for LLLine::intersects(point) which returns true
- // if the line passes within some tolerance of point
-
- // these tests will have some floating point error,
- // so we need to specify how much error is ok
- F32 allowable_relative_error = 0.00001f;
- S32 number_of_tests = 100;
- for (S32 test = 0; test < number_of_tests; ++test)
- {
- // generate some random point to be on the line
- LLVector3 point_on_line( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- point_on_line.normalize();
- point_on_line *= ll_frand(LARGE_RADIUS);
-
- // generate some random point to "intersect"
- LLVector3 random_direction ( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- random_direction.normalize();
-
- LLVector3 random_offset( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- random_offset.normalize();
- random_offset *= ll_frand(SMALL_RADIUS);
-
- LLVector3 point = point_on_line + MEDIUM_RADIUS * random_direction
- + random_offset;
-
- // compute the axis of approach (a unit vector between the points)
- LLVector3 axis_of_approach = point - point_on_line;
- axis_of_approach.normalize();
-
- // compute the direction of the the first line (perp to axis_of_approach)
- LLVector3 first_dir( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- first_dir.normalize();
- F32 dot = first_dir * axis_of_approach;
- first_dir -= dot * axis_of_approach; // subtract component parallel to axis
- first_dir.normalize();
-
- // construct the line
- LLVector3 another_point_on_line = point_on_line + ll_frand(LARGE_RADIUS) * first_dir;
- LLLine line(another_point_on_line, point_on_line);
-
- // test that the intersection point is within MEDIUM_RADIUS + SMALL_RADIUS
- F32 test_radius = MEDIUM_RADIUS + SMALL_RADIUS;
- test_radius += (LARGE_RADIUS * allowable_relative_error);
- ensure("line should pass near intersection point", line.intersects(point, test_radius));
-
- test_radius = allowable_relative_error * (point - point_on_line).length();
- ensure("line should intersect point used to define it", line.intersects(point_on_line, test_radius));
- }
- }
-
- template<> template<>
- void line_object::test<2>()
- {
- /*
- These tests fail intermittently on all platforms - see DEV-16600
- Commenting this out until dev has time to investigate.
-
- // this is a test for LLLine::nearestApproach(LLLIne) method
- // which computes the point on a line nearest another line
-
- // these tests will have some floating point error,
- // so we need to specify how much error is ok
- // TODO -- make nearestApproach() algorithm more accurate so
- // we can tighten the allowable_error. Most tests are tighter
- // than one milimeter, however when doing randomized testing
- // you can walk into inaccurate cases.
- F32 allowable_relative_error = 0.001f;
- S32 number_of_tests = 100;
- for (S32 test = 0; test < number_of_tests; ++test)
- {
- // generate two points to be our known nearest approaches
- LLVector3 some_point( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- some_point.normalize();
- some_point *= ll_frand(LARGE_RADIUS);
-
- LLVector3 another_point( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- another_point.normalize();
- another_point *= ll_frand(LARGE_RADIUS);
-
- // compute the axis of approach (a unit vector between the points)
- LLVector3 axis_of_approach = another_point - some_point;
- axis_of_approach.normalize();
-
- // compute the direction of the the first line (perp to axis_of_approach)
- LLVector3 first_dir( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- F32 dot = first_dir * axis_of_approach;
- first_dir -= dot * axis_of_approach; // subtract component parallel to axis
- first_dir.normalize(); // normalize
-
- // compute the direction of the the second line
- LLVector3 second_dir( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- dot = second_dir * axis_of_approach;
- second_dir -= dot * axis_of_approach;
- second_dir.normalize();
-
- // make sure the lines aren't too parallel,
- dot = fabsf(first_dir * second_dir);
- if (dot > 0.99f)
- {
- // skip this test, we're not interested in testing
- // the intractible cases
- continue;
- }
-
- // construct the lines
- LLVector3 first_point = some_point + ll_frand(LARGE_RADIUS) * first_dir;
- LLLine first_line(first_point, some_point);
-
- LLVector3 second_point = another_point + ll_frand(LARGE_RADIUS) * second_dir;
- LLLine second_line(second_point, another_point);
-
- // compute the points of nearest approach
- LLVector3 some_computed_point = first_line.nearestApproach(second_line);
- LLVector3 another_computed_point = second_line.nearestApproach(first_line);
-
- // compute the error
- F32 first_error = (some_point - some_computed_point).length();
- F32 scale = llmax((some_point - another_point).length(), some_point.length());
- scale = llmax(scale, another_point.length());
- scale = llmax(scale, 1.f);
- F32 first_relative_error = first_error / scale;
-
- F32 second_error = (another_point - another_computed_point).length();
- F32 second_relative_error = second_error / scale;
-
- //if (first_relative_error > allowable_relative_error)
- //{
- // std::cout << "first_error = " << first_error
- // << " first_relative_error = " << first_relative_error
- // << " scale = " << scale
- // << " dir_dot = " << (first_dir * second_dir)
- // << std::endl;
- //}
- //if (second_relative_error > allowable_relative_error)
- //{
- // std::cout << "second_error = " << second_error
- // << " second_relative_error = " << second_relative_error
- // << " scale = " << scale
- // << " dist = " << (some_point - another_point).length()
- // << " dir_dot = " << (first_dir * second_dir)
- // << std::endl;
- //}
-
- // test that the errors are small
-
- ensure("first line should accurately compute its closest approach",
- first_relative_error <= allowable_relative_error);
- ensure("second line should accurately compute its closest approach",
- second_relative_error <= allowable_relative_error);
- }
- */
- }
-
- F32 ALMOST_PARALLEL = 0.99f;
- template<> template<>
- void line_object::test<3>()
- {
- // this is a test for LLLine::getIntersectionBetweenTwoPlanes() method
-
- // first some known tests
- LLLine xy_plane(LLVector3(0.f, 0.f, 2.f), LLVector3(0.f, 0.f, 3.f));
- LLLine yz_plane(LLVector3(2.f, 0.f, 0.f), LLVector3(3.f, 0.f, 0.f));
- LLLine zx_plane(LLVector3(0.f, 2.f, 0.f), LLVector3(0.f, 3.f, 0.f));
-
- LLLine x_line;
- LLLine y_line;
- LLLine z_line;
-
- bool x_success = LLLine::getIntersectionBetweenTwoPlanes(x_line, xy_plane, zx_plane);
- bool y_success = LLLine::getIntersectionBetweenTwoPlanes(y_line, yz_plane, xy_plane);
- bool z_success = LLLine::getIntersectionBetweenTwoPlanes(z_line, zx_plane, yz_plane);
-
- ensure("xy and zx planes should intersect", x_success);
- ensure("yz and xy planes should intersect", y_success);
- ensure("zx and yz planes should intersect", z_success);
-
- LLVector3 direction = x_line.getDirection();
- ensure("x_line should be parallel to x_axis", fabs(direction.mV[VX]) == 1.f
- && 0.f == direction.mV[VY]
- && 0.f == direction.mV[VZ] );
- direction = y_line.getDirection();
- ensure("y_line should be parallel to y_axis", 0.f == direction.mV[VX]
- && fabs(direction.mV[VY]) == 1.f
- && 0.f == direction.mV[VZ] );
- direction = z_line.getDirection();
- ensure("z_line should be parallel to z_axis", 0.f == direction.mV[VX]
- && 0.f == direction.mV[VY]
- && fabs(direction.mV[VZ]) == 1.f );
-
- // next some random tests
- F32 allowable_relative_error = 0.0001f;
- S32 number_of_tests = 20;
- for (S32 test = 0; test < number_of_tests; ++test)
- {
- // generate the known line
- LLVector3 some_point( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- some_point.normalize();
- some_point *= ll_frand(LARGE_RADIUS);
- LLVector3 another_point( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- another_point.normalize();
- another_point *= ll_frand(LARGE_RADIUS);
- LLLine known_intersection(some_point, another_point);
-
- // compute a plane that intersect the line
- LLVector3 point_on_plane( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- point_on_plane.normalize();
- point_on_plane *= ll_frand(LARGE_RADIUS);
- LLVector3 plane_normal = (point_on_plane - some_point) % known_intersection.getDirection();
- plane_normal.normalize();
- LLLine first_plane(point_on_plane, point_on_plane + plane_normal);
-
- // compute a different plane that intersect the line
- LLVector3 point_on_different_plane( ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f,
- ll_frand(2.f) - 1.f);
- point_on_different_plane.normalize();
- point_on_different_plane *= ll_frand(LARGE_RADIUS);
- LLVector3 different_plane_normal = (point_on_different_plane - another_point) % known_intersection.getDirection();
- different_plane_normal.normalize();
- LLLine second_plane(point_on_different_plane, point_on_different_plane + different_plane_normal);
-
- if (fabs(plane_normal * different_plane_normal) > ALMOST_PARALLEL)
- {
- // the two planes are approximately parallel, so we won't test this case
- continue;
- }
-
- LLLine measured_intersection;
- bool success = LLLine::getIntersectionBetweenTwoPlanes(
- measured_intersection,
- first_plane,
- second_plane);
-
- ensure("plane intersection should succeed", success);
-
- F32 dot = fabs(known_intersection.getDirection() * measured_intersection.getDirection());
- ensure("measured intersection should be parallel to known intersection",
- dot > ALMOST_PARALLEL);
-
- ensure("measured intersection should pass near known point",
- measured_intersection.intersects(some_point, LARGE_RADIUS * allowable_relative_error));
- }
- }
-}
-
+/** + * @file math.cpp + * @author Phoenix + * @date 2005-09-26 + * @brief Tests for the llmath library. + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "../test/lltut.h" + +#include "llcrc.h" +#include "llrand.h" +#include "lluuid.h" + +#include "../llline.h" +#include "../llmath.h" +#include "../llsphere.h" +#include "../v3math.h" + +namespace tut +{ + struct math_data + { + }; + typedef test_group<math_data> math_test; + typedef math_test::object math_object; + tut::math_test tm("BasicLindenMath"); + + template<> template<> + void math_object::test<1>() + { + S32 val = 89543; + val = llabs(val); + ensure("integer absolute value 1", (89543 == val)); + val = -500; + val = llabs(val); + ensure("integer absolute value 2", (500 == val)); + } + + template<> template<> + void math_object::test<2>() + { + F32 val = -2583.4f; + val = llabs(val); + ensure("float absolute value 1", (2583.4f == val)); + val = 430903.f; + val = llabs(val); + ensure("float absolute value 2", (430903.f == val)); + } + + template<> template<> + void math_object::test<3>() + { + F64 val = 387439393.987329839; + val = llabs(val); + ensure("double absolute value 1", (387439393.987329839 == val)); + val = -8937843.9394878; + val = llabs(val); + ensure("double absolute value 2", (8937843.9394878 == val)); + } + + template<> template<> + void math_object::test<4>() + { + F32 val = 430903.9f; + S32 val1 = lltrunc(val); + ensure("float truncate value 1", (430903 == val1)); + val = -2303.9f; + val1 = lltrunc(val); + ensure("float truncate value 2", (-2303 == val1)); + } + + template<> template<> + void math_object::test<5>() + { + F64 val = 387439393.987329839 ; + S32 val1 = lltrunc(val); + ensure("float truncate value 1", (387439393 == val1)); + val = -387439393.987329839; + val1 = lltrunc(val); + ensure("float truncate value 2", (-387439393 == val1)); + } + + template<> template<> + void math_object::test<6>() + { + F32 val = 430903.2f; + S32 val1 = llfloor(val); + ensure("float llfloor value 1", (430903 == val1)); + val = -430903.9f; + val1 = llfloor(val); + ensure("float llfloor value 2", (-430904 == val1)); + } + + template<> template<> + void math_object::test<7>() + { + F32 val = 430903.2f; + S32 val1 = llceil(val); + ensure("float llceil value 1", (430904 == val1)); + val = -430903.9f; + val1 = llceil(val); + ensure("float llceil value 2", (-430903 == val1)); + } + + template<> template<> + void math_object::test<8>() + { + F32 val = 430903.2f; + S32 val1 = ll_round(val); + ensure("float ll_round value 1", (430903 == val1)); + val = -430903.9f; + val1 = ll_round(val); + ensure("float ll_round value 2", (-430904 == val1)); + } + + template<> template<> + void math_object::test<9>() + { + F32 val = 430905.2654f, nearest = 100.f; + val = ll_round(val, nearest); + ensure("float ll_round value 1", (430900 == val)); + val = -430905.2654f, nearest = 10.f; + val = ll_round(val, nearest); + ensure("float ll_round value 1", (-430910 == val)); + } + + template<> template<> + void math_object::test<10>() + { + F64 val = 430905.2654, nearest = 100.0; + val = ll_round(val, nearest); + ensure("double ll_round value 1", (430900 == val)); + val = -430905.2654, nearest = 10.0; + val = ll_round(val, nearest); + ensure("double ll_round value 1", (-430910.00000 == val)); + } + + template<> template<> + void math_object::test<11>() + { + const F32 F_PI = 3.1415926535897932384626433832795f; + F32 angle = 3506.f; + angle = llsimple_angle(angle); + ensure("llsimple_angle value 1", (angle <=F_PI && angle >= -F_PI)); + angle = -431.f; + angle = llsimple_angle(angle); + ensure("llsimple_angle value 1", (angle <=F_PI && angle >= -F_PI)); + } +} + +namespace tut +{ + struct uuid_data + { + LLUUID id; + }; + typedef test_group<uuid_data> uuid_test; + typedef uuid_test::object uuid_object; + tut::uuid_test tu("LLUUID"); + + template<> template<> + void uuid_object::test<1>() + { + ensure("uuid null", id.isNull()); + id.generate(); + ensure("generate not null", id.notNull()); + id.setNull(); + ensure("set null", id.isNull()); + } + + template<> template<> + void uuid_object::test<2>() + { + id.generate(); + LLUUID a(id); + ensure_equals("copy equal", id, a); + a.generate(); + ensure_not_equals("generate not equal", id, a); + a = id; + ensure_equals("assignment equal", id, a); + } + + template<> template<> + void uuid_object::test<3>() + { + id.generate(); + LLUUID copy(id); + LLUUID mask; + mask.generate(); + copy ^= mask; + ensure_not_equals("mask not equal", id, copy); + copy ^= mask; + ensure_equals("mask back", id, copy); + } + + template<> template<> + void uuid_object::test<4>() + { + id.generate(); + std::string id_str = id.asString(); + LLUUID copy(id_str.c_str()); + ensure_equals("string serialization", id, copy); + } + +} + +namespace tut +{ + struct crc_data + { + }; + typedef test_group<crc_data> crc_test; + typedef crc_test::object crc_object; + tut::crc_test tc("LLCrc"); + + template<> template<> + void crc_object::test<1>() + { + /* Test buffer update and individual char update */ + const char TEST_BUFFER[] = "hello &#$)$&Nd0"; + LLCRC c1, c2; + c1.update((U8*)TEST_BUFFER, sizeof(TEST_BUFFER) - 1); + char* rh = (char*)TEST_BUFFER; + while(*rh != '\0') + { + c2.update(*rh); + ++rh; + } + ensure_equals("crc update 1", c1.getCRC(), c2.getCRC()); + } + + template<> template<> + void crc_object::test<2>() + { + /* Test mixing of buffer and individual char update */ + const char TEST_BUFFER1[] = "Split Buffer one $^%$%#@$"; + const char TEST_BUFFER2[] = "Split Buffer two )(8723#5dsds"; + LLCRC c1, c2; + c1.update((U8*)TEST_BUFFER1, sizeof(TEST_BUFFER1) - 1); + char* rh = (char*)TEST_BUFFER2; + while(*rh != '\0') + { + c1.update(*rh); + ++rh; + } + + rh = (char*)TEST_BUFFER1; + while(*rh != '\0') + { + c2.update(*rh); + ++rh; + } + c2.update((U8*)TEST_BUFFER2, sizeof(TEST_BUFFER2) - 1); + + ensure_equals("crc update 2", c1.getCRC(), c2.getCRC()); + } +} + +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; + + struct line_data + { + }; + typedef test_group<line_data> line_test; + typedef line_test::object line_object; + tut::line_test tline("LLLine"); + + template<> template<> + void line_object::test<1>() + { + // this is a test for LLLine::intersects(point) which returns true + // if the line passes within some tolerance of point + + // these tests will have some floating point error, + // so we need to specify how much error is ok + F32 allowable_relative_error = 0.00001f; + S32 number_of_tests = 100; + for (S32 test = 0; test < number_of_tests; ++test) + { + // generate some random point to be on the line + LLVector3 point_on_line( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + point_on_line.normalize(); + point_on_line *= ll_frand(LARGE_RADIUS); + + // generate some random point to "intersect" + LLVector3 random_direction ( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + random_direction.normalize(); + + LLVector3 random_offset( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + random_offset.normalize(); + random_offset *= ll_frand(SMALL_RADIUS); + + LLVector3 point = point_on_line + MEDIUM_RADIUS * random_direction + + random_offset; + + // compute the axis of approach (a unit vector between the points) + LLVector3 axis_of_approach = point - point_on_line; + axis_of_approach.normalize(); + + // compute the direction of the the first line (perp to axis_of_approach) + LLVector3 first_dir( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + first_dir.normalize(); + F32 dot = first_dir * axis_of_approach; + first_dir -= dot * axis_of_approach; // subtract component parallel to axis + first_dir.normalize(); + + // construct the line + LLVector3 another_point_on_line = point_on_line + ll_frand(LARGE_RADIUS) * first_dir; + LLLine line(another_point_on_line, point_on_line); + + // test that the intersection point is within MEDIUM_RADIUS + SMALL_RADIUS + F32 test_radius = MEDIUM_RADIUS + SMALL_RADIUS; + test_radius += (LARGE_RADIUS * allowable_relative_error); + ensure("line should pass near intersection point", line.intersects(point, test_radius)); + + test_radius = allowable_relative_error * (point - point_on_line).length(); + ensure("line should intersect point used to define it", line.intersects(point_on_line, test_radius)); + } + } + + template<> template<> + void line_object::test<2>() + { + /* + These tests fail intermittently on all platforms - see DEV-16600 + Commenting this out until dev has time to investigate. + + // this is a test for LLLine::nearestApproach(LLLIne) method + // which computes the point on a line nearest another line + + // these tests will have some floating point error, + // so we need to specify how much error is ok + // TODO -- make nearestApproach() algorithm more accurate so + // we can tighten the allowable_error. Most tests are tighter + // than one milimeter, however when doing randomized testing + // you can walk into inaccurate cases. + F32 allowable_relative_error = 0.001f; + S32 number_of_tests = 100; + for (S32 test = 0; test < number_of_tests; ++test) + { + // generate two points to be our known nearest approaches + LLVector3 some_point( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + some_point.normalize(); + some_point *= ll_frand(LARGE_RADIUS); + + LLVector3 another_point( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + another_point.normalize(); + another_point *= ll_frand(LARGE_RADIUS); + + // compute the axis of approach (a unit vector between the points) + LLVector3 axis_of_approach = another_point - some_point; + axis_of_approach.normalize(); + + // compute the direction of the the first line (perp to axis_of_approach) + LLVector3 first_dir( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + F32 dot = first_dir * axis_of_approach; + first_dir -= dot * axis_of_approach; // subtract component parallel to axis + first_dir.normalize(); // normalize + + // compute the direction of the the second line + LLVector3 second_dir( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + dot = second_dir * axis_of_approach; + second_dir -= dot * axis_of_approach; + second_dir.normalize(); + + // make sure the lines aren't too parallel, + dot = fabsf(first_dir * second_dir); + if (dot > 0.99f) + { + // skip this test, we're not interested in testing + // the intractible cases + continue; + } + + // construct the lines + LLVector3 first_point = some_point + ll_frand(LARGE_RADIUS) * first_dir; + LLLine first_line(first_point, some_point); + + LLVector3 second_point = another_point + ll_frand(LARGE_RADIUS) * second_dir; + LLLine second_line(second_point, another_point); + + // compute the points of nearest approach + LLVector3 some_computed_point = first_line.nearestApproach(second_line); + LLVector3 another_computed_point = second_line.nearestApproach(first_line); + + // compute the error + F32 first_error = (some_point - some_computed_point).length(); + F32 scale = llmax((some_point - another_point).length(), some_point.length()); + scale = llmax(scale, another_point.length()); + scale = llmax(scale, 1.f); + F32 first_relative_error = first_error / scale; + + F32 second_error = (another_point - another_computed_point).length(); + F32 second_relative_error = second_error / scale; + + //if (first_relative_error > allowable_relative_error) + //{ + // std::cout << "first_error = " << first_error + // << " first_relative_error = " << first_relative_error + // << " scale = " << scale + // << " dir_dot = " << (first_dir * second_dir) + // << std::endl; + //} + //if (second_relative_error > allowable_relative_error) + //{ + // std::cout << "second_error = " << second_error + // << " second_relative_error = " << second_relative_error + // << " scale = " << scale + // << " dist = " << (some_point - another_point).length() + // << " dir_dot = " << (first_dir * second_dir) + // << std::endl; + //} + + // test that the errors are small + + ensure("first line should accurately compute its closest approach", + first_relative_error <= allowable_relative_error); + ensure("second line should accurately compute its closest approach", + second_relative_error <= allowable_relative_error); + } + */ + } + + F32 ALMOST_PARALLEL = 0.99f; + template<> template<> + void line_object::test<3>() + { + // this is a test for LLLine::getIntersectionBetweenTwoPlanes() method + + // first some known tests + LLLine xy_plane(LLVector3(0.f, 0.f, 2.f), LLVector3(0.f, 0.f, 3.f)); + LLLine yz_plane(LLVector3(2.f, 0.f, 0.f), LLVector3(3.f, 0.f, 0.f)); + LLLine zx_plane(LLVector3(0.f, 2.f, 0.f), LLVector3(0.f, 3.f, 0.f)); + + LLLine x_line; + LLLine y_line; + LLLine z_line; + + bool x_success = LLLine::getIntersectionBetweenTwoPlanes(x_line, xy_plane, zx_plane); + bool y_success = LLLine::getIntersectionBetweenTwoPlanes(y_line, yz_plane, xy_plane); + bool z_success = LLLine::getIntersectionBetweenTwoPlanes(z_line, zx_plane, yz_plane); + + ensure("xy and zx planes should intersect", x_success); + ensure("yz and xy planes should intersect", y_success); + ensure("zx and yz planes should intersect", z_success); + + LLVector3 direction = x_line.getDirection(); + ensure("x_line should be parallel to x_axis", fabs(direction.mV[VX]) == 1.f + && 0.f == direction.mV[VY] + && 0.f == direction.mV[VZ] ); + direction = y_line.getDirection(); + ensure("y_line should be parallel to y_axis", 0.f == direction.mV[VX] + && fabs(direction.mV[VY]) == 1.f + && 0.f == direction.mV[VZ] ); + direction = z_line.getDirection(); + ensure("z_line should be parallel to z_axis", 0.f == direction.mV[VX] + && 0.f == direction.mV[VY] + && fabs(direction.mV[VZ]) == 1.f ); + + // next some random tests + F32 allowable_relative_error = 0.0001f; + S32 number_of_tests = 20; + for (S32 test = 0; test < number_of_tests; ++test) + { + // generate the known line + LLVector3 some_point( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + some_point.normalize(); + some_point *= ll_frand(LARGE_RADIUS); + LLVector3 another_point( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + another_point.normalize(); + another_point *= ll_frand(LARGE_RADIUS); + LLLine known_intersection(some_point, another_point); + + // compute a plane that intersect the line + LLVector3 point_on_plane( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + point_on_plane.normalize(); + point_on_plane *= ll_frand(LARGE_RADIUS); + LLVector3 plane_normal = (point_on_plane - some_point) % known_intersection.getDirection(); + plane_normal.normalize(); + LLLine first_plane(point_on_plane, point_on_plane + plane_normal); + + // compute a different plane that intersect the line + LLVector3 point_on_different_plane( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + point_on_different_plane.normalize(); + point_on_different_plane *= ll_frand(LARGE_RADIUS); + LLVector3 different_plane_normal = (point_on_different_plane - another_point) % known_intersection.getDirection(); + different_plane_normal.normalize(); + LLLine second_plane(point_on_different_plane, point_on_different_plane + different_plane_normal); + + if (fabs(plane_normal * different_plane_normal) > ALMOST_PARALLEL) + { + // the two planes are approximately parallel, so we won't test this case + continue; + } + + LLLine measured_intersection; + bool success = LLLine::getIntersectionBetweenTwoPlanes( + measured_intersection, + first_plane, + second_plane); + + ensure("plane intersection should succeed", success); + + F32 dot = fabs(known_intersection.getDirection() * measured_intersection.getDirection()); + ensure("measured intersection should be parallel to known intersection", + dot > ALMOST_PARALLEL); + + ensure("measured intersection should pass near known point", + measured_intersection.intersects(some_point, LARGE_RADIUS * allowable_relative_error)); + } + } +} + diff --git a/indra/llmath/tests/v2math_test.cpp b/indra/llmath/tests/v2math_test.cpp index 229edc56f5..860e3ef672 100644 --- a/indra/llmath/tests/v2math_test.cpp +++ b/indra/llmath/tests/v2math_test.cpp @@ -1,448 +1,448 @@ -/**
- * @file v2math_test.cpp
- * @author Adroit
- * @date 2007-02
- * @brief v2math test cases.
- *
- * $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 "../test/lltut.h"
-
-#include "../v2math.h"
-
-
-namespace tut
-{
- struct v2math_data
- {
- };
- typedef test_group<v2math_data> v2math_test;
- typedef v2math_test::object v2math_object;
- tut::v2math_test v2math_testcase("v2math_h");
-
- template<> template<>
- void v2math_object::test<1>()
- {
- LLVector2 vec2;
- ensure("LLVector2:Fail to initialize ", (0.f == vec2.mV[VX] && 0.f == vec2.mV[VY]));
-
- F32 x =2.0f, y = 3.2f ;
- LLVector2 vec3(x,y);
- ensure("LLVector2(F32 x, F32 y):Fail to initialize ", (x == vec3.mV[VX]) && (y == vec3.mV[VY]));
-
- const F32 vec[2] = {3.2f, 4.5f};
- LLVector2 vec4(vec);
- ensure("LLVector2(const F32 *vec):Fail to initialize ", (vec[0] == vec4.mV[VX]) && (vec[1] == vec4.mV[VY]));
-
- vec4.clearVec();
- ensure("clearVec():Fail to clean the values ", (0.f == vec4.mV[VX] && 0.f == vec4.mV[VY]));
-
- vec3.zeroVec();
- ensure("zeroVec():Fail to fill the zero ", (0.f == vec3.mV[VX] && 0.f == vec3.mV[VY]));
- }
-
- template<> template<>
- void v2math_object::test<2>()
- {
- F32 x = 123.356f, y = 2387.453f;
- LLVector2 vec2,vec3;
- vec2.setVec(x, y);
- ensure("1:setVec: Fail ", (x == vec2.mV[VX]) && (y == vec2.mV[VY]));
-
- vec3.setVec(vec2);
- ensure("2:setVec: Fail " ,(vec2 == vec3));
-
- vec3.zeroVec();
- const F32 vec[2] = {3.24653f, 457653.4f};
- vec3.setVec(vec);
- ensure("3:setVec: Fail ", (vec[0] == vec3.mV[VX]) && (vec[1] == vec3.mV[VY]));
- }
-
- template<> template<>
- void v2math_object::test<3>()
- {
- F32 x = 2.2345f, y = 3.5678f ;
- LLVector2 vec2(x,y);
- ensure("magVecSquared:Fail ", is_approx_equal(vec2.magVecSquared(), (x*x + y*y)));
- ensure("magVec:Fail ", is_approx_equal(vec2.magVec(), (F32) sqrt(x*x + y*y)));
- }
-
- template<> template<>
- void v2math_object::test<4>()
- {
- F32 x =-2.0f, y = -3.0f ;
- LLVector2 vec2(x,y);
- ensure_equals("abs():Fail", vec2.abs(), true);
- ensure("abs() x", is_approx_equal(vec2.mV[VX], 2.f));
- ensure("abs() y", is_approx_equal(vec2.mV[VY], 3.f));
-
- ensure("isNull():Fail ", false == vec2.isNull()); //Returns true if vector has a _very_small_ length
-
- x =.00000001f, y = .000001001f;
- vec2.setVec(x, y);
- ensure("isNull(): Fail ", true == vec2.isNull());
- }
-
- template<> template<>
- void v2math_object::test<5>()
- {
- F32 x =1.f, y = 2.f;
- LLVector2 vec2(x, y), vec3;
- vec3 = vec3.scaleVec(vec2);
- ensure("scaleVec: Fail ", vec3.mV[VX] == 0. && vec3.mV[VY] == 0.);
- ensure("isExactlyZero(): Fail", true == vec3.isExactlyZero());
-
- vec3.setVec(2.f, 1.f);
- vec3 = vec3.scaleVec(vec2);
- ensure("scaleVec: Fail ", (2.f == vec3.mV[VX]) && (2.f == vec3.mV[VY]));
- ensure("isExactlyZero():Fail", false == vec3.isExactlyZero());
- }
-
- template<> template<>
- void v2math_object::test<6>()
- {
- F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1), vec3(x2, y2), vec4;
- vec4 = vec2 + vec3 ;
- val1 = x1+x2;
- val2 = y1+y2;
- ensure("1:operator+ failed",(val1 == vec4.mV[VX]) && ((val2 == vec4.mV[VY])));
-
- vec2.clearVec();
- vec3.clearVec();
- x1 = -.235f, y1 = -24.32f, x2 = -2.3f, y2 = 1.f;
- vec2.setVec(x1, y1);
- vec3.setVec(x2, y2);
- vec4 = vec2 + vec3;
- val1 = x1+x2;
- val2 = y1+y2;
- ensure("2:operator+ failed",(val1 == vec4.mV[VX]) && ((val2 == vec4.mV[VY])));
- }
-
- template<> template<>
- void v2math_object::test<7>()
- {
- F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1), vec3(x2, y2), vec4;
- vec4 = vec2 - vec3 ;
- val1 = x1-x2;
- val2 = y1-y2;
- ensure("1:operator- failed",(val1 == vec4.mV[VX]) && ((val2 == vec4.mV[VY])));
-
- vec2.clearVec();
- vec3.clearVec();
- vec4.clearVec();
- x1 = -.235f, y1 = -24.32f, x2 = -2.3f, y2 = 1.f;
- vec2.setVec(x1, y1);
- vec3.setVec(x2, y2);
- vec4 = vec2 - vec3;
- val1 = x1-x2;
- val2 = y1-y2;
- ensure("2:operator- failed",(val1 == vec4.mV[VX]) && ((val2 == vec4.mV[VY])));
- }
-
- template<> template<>
- void v2math_object::test<8>()
- {
- F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1), vec3(x2, y2);
- val1 = vec2 * vec3;
- val2 = x1*x2 + y1*y2;
- ensure("1:operator* failed",(val1 == val2));
-
- vec3.clearVec();
- F32 mulVal = 4.332f;
- vec3 = vec2 * mulVal;
- val1 = x1*mulVal;
- val2 = y1*mulVal;
- ensure("2:operator* failed",(val1 == vec3.mV[VX]) && (val2 == vec3.mV[VY]));
-
- vec3.clearVec();
- vec3 = mulVal * vec2;
- ensure("3:operator* failed",(val1 == vec3.mV[VX]) && (val2 == vec3.mV[VY]));
- }
-
- template<> template<>
- void v2math_object::test<9>()
- {
- F32 x1 =1.f, y1 = 2.f, div = 3.2f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1), vec3;
- vec3 = vec2 / div;
- val1 = x1 / div;
- val2 = y1 / div;
- ensure("1:operator/ failed", is_approx_equal(val1, vec3.mV[VX]) && is_approx_equal(val2, vec3.mV[VY]));
-
- vec3.clearVec();
- x1 = -.235f, y1 = -24.32f, div = -2.2f;
- vec2.setVec(x1, y1);
- vec3 = vec2 / div;
- val1 = x1 / div;
- val2 = y1 / div;
- ensure("2:operator/ failed", is_approx_equal(val1, vec3.mV[VX]) && is_approx_equal(val2, vec3.mV[VY]));
- }
-
- template<> template<>
- void v2math_object::test<10>()
- {
- F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1), vec3(x2, y2), vec4;
- vec4 = vec2 % vec3;
- val1 = x1*y2 - x2*y1;
- val2 = y1*x2 - y2*x1;
- ensure("1:operator% failed",(val1 == vec4.mV[VX]) && (val2 == vec4.mV[VY]));
-
- vec2.clearVec();
- vec3.clearVec();
- vec4.clearVec();
- x1 = -.235f, y1 = -24.32f, x2 = -2.3f, y2 = 1.f;
- vec2.setVec(x1, y1);
- vec3.setVec(x2, y2);
- vec4 = vec2 % vec3;
- val1 = x1*y2 - x2*y1;
- val2 = y1*x2 - y2*x1;
- ensure("2:operator% failed",(val1 == vec4.mV[VX]) && (val2 == vec4.mV[VY]));
- }
- template<> template<>
- void v2math_object::test<11>()
- {
- F32 x1 =1.f, y1 = 2.f;
- LLVector2 vec2(x1, y1), vec3(x1, y1);
- ensure("1:operator== failed",(vec2 == vec3));
-
- vec2.clearVec();
- vec3.clearVec();
- x1 = -.235f, y1 = -24.32f;
- vec2.setVec(x1, y1);
- vec3.setVec(vec2);
- ensure("2:operator== failed",(vec2 == vec3));
- }
-
- template<> template<>
- void v2math_object::test<12>()
- {
- F32 x1 = 1.f, y1 = 2.f,x2 = 2.332f, y2 = -1.23f;
- LLVector2 vec2(x1, y1), vec3(x2, y2);
- ensure("1:operator!= failed",(vec2 != vec3));
-
- vec2.clearVec();
- vec3.clearVec();
- vec2.setVec(x1, y1);
- vec3.setVec(vec2);
- ensure("2:operator!= failed", (false == (vec2 != vec3)));
- }
- template<> template<>
- void v2math_object::test<13>()
- {
- F32 x1 = 1.f, y1 = 2.f,x2 = 2.332f, y2 = -1.23f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1), vec3(x2, y2);
- vec2 +=vec3;
- val1 = x1+x2;
- val2 = y1+y2;
- ensure("1:operator+= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY]));
-
- vec2.setVec(x1, y1);
- vec2 -=vec3;
- val1 = x1-x2;
- val2 = y1-y2;
- ensure("2:operator-= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY]));
-
- vec2.clearVec();
- vec3.clearVec();
- x1 = -21.000466f, y1 = 2.98382f,x2 = 0.332f, y2 = -01.23f;
- vec2.setVec(x1, y1);
- vec3.setVec(x2, y2);
- vec2 +=vec3;
- val1 = x1+x2;
- val2 = y1+y2;
- ensure("3:operator+= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY]));
-
- vec2.setVec(x1, y1);
- vec2 -=vec3;
- val1 = x1-x2;
- val2 = y1-y2;
- ensure("4:operator-= failed", is_approx_equal(val1, vec2.mV[VX]) && is_approx_equal(val2, vec2.mV[VY]));
- }
-
- template<> template<>
- void v2math_object::test<14>()
- {
- F32 x1 =1.f, y1 = 2.f;
- F32 val1, val2, mulVal = 4.332f;
- LLVector2 vec2(x1, y1);
- vec2 /=mulVal;
- val1 = x1 / mulVal;
- val2 = y1 / mulVal;
- ensure("1:operator/= failed", is_approx_equal(val1, vec2.mV[VX]) && is_approx_equal(val2, vec2.mV[VY]));
-
- vec2.clearVec();
- x1 = .213f, y1 = -2.34f, mulVal = -.23f;
- vec2.setVec(x1, y1);
- vec2 /=mulVal;
- val1 = x1 / mulVal;
- val2 = y1 / mulVal;
- ensure("2:operator/= failed", is_approx_equal(val1, vec2.mV[VX]) && is_approx_equal(val2, vec2.mV[VY]));
- }
-
- template<> template<>
- void v2math_object::test<15>()
- {
- F32 x1 =1.f, y1 = 2.f;
- F32 val1, val2, mulVal = 4.332f;
- LLVector2 vec2(x1, y1);
- vec2 *=mulVal;
- val1 = x1*mulVal;
- val2 = y1*mulVal;
- ensure("1:operator*= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY]));
-
- vec2.clearVec();
- x1 = .213f, y1 = -2.34f, mulVal = -.23f;
- vec2.setVec(x1, y1);
- vec2 *=mulVal;
- val1 = x1*mulVal;
- val2 = y1*mulVal;
- ensure("2:operator*= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY]));
- }
-
- template<> template<>
- void v2math_object::test<16>()
- {
- F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1), vec3(x2, y2);
- vec2 %= vec3;
- val1 = x1*y2 - x2*y1;
- val2 = y1*x2 - y2*x1;
- ensure("1:operator%= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY]));
- }
-
- template<> template<>
- void v2math_object::test<17>()
- {
- F32 x1 =1.f, y1 = 2.f;
- LLVector2 vec2(x1, y1),vec3;
- vec3 = -vec2;
- ensure("1:operator- failed",(-vec3 == vec2));
- }
-
- template<> template<>
- void v2math_object::test<18>()
- {
- F32 x1 =1.f, y1 = 2.f;
- std::ostringstream stream1, stream2;
- LLVector2 vec2(x1, y1),vec3;
- stream1 << vec2;
- vec3.setVec(x1, y1);
- stream2 << vec3;
- ensure("1:operator << failed",(stream1.str() == stream2.str()));
- }
-
- template<> template<>
- void v2math_object::test<19>()
- {
- F32 x1 =1.0f, y1 = 2.0f, x2 = -.32f, y2 = .2234f;
- LLVector2 vec2(x1, y1),vec3(x2, y2);
- ensure("1:operator < failed",(vec3 < vec2));
-
- x1 = 1.0f, y1 = 2.0f, x2 = 1.0f, y2 = 3.2234f;
- vec2.setVec(x1, y1);
- vec3.setVec(x2, y2);
- ensure("2:operator < failed", (false == (vec3 < vec2)));
- }
-
- template<> template<>
- void v2math_object::test<20>()
- {
- F32 x1 =1.0f, y1 = 2.0f;
- LLVector2 vec2(x1, y1);
- ensure("1:operator [] failed",( x1 == vec2[0]));
- ensure("2:operator [] failed",( y1 == vec2[1]));
-
- vec2.clearVec();
- x1 = 23.0f, y1 = -.2361f;
- vec2.setVec(x1, y1);
- F32 ref1 = vec2[0];
- ensure("3:operator [] failed", ( ref1 == x1));
- F32 ref2 = vec2[1];
- ensure("4:operator [] failed", ( ref2 == y1));
- }
-
- template<> template<>
- void v2math_object::test<21>()
- {
- F32 x1 =1.f, y1 = 2.f, x2 = -.32f, y2 = .2234f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1),vec3(x2, y2);
- val1 = dist_vec_squared2D(vec2, vec3);
- val2 = (x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2);
- ensure_equals("dist_vec_squared2D values are not equal",val2, val1);
-
- val1 = dist_vec_squared(vec2, vec3);
- ensure_equals("dist_vec_squared values are not equal",val2, val1);
-
- val1 = dist_vec(vec2, vec3);
- val2 = (F32) sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2));
- ensure_equals("dist_vec values are not equal",val2, val1);
- }
-
- template<> template<>
- void v2math_object::test<22>()
- {
- F32 x1 =1.f, y1 = 2.f, x2 = -.32f, y2 = .2234f,fVal = .0121f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1),vec3(x2, y2);
- LLVector2 vec4 = lerp(vec2, vec3, fVal);
- val1 = x1 + (x2 - x1) * fVal;
- val2 = y1 + (y2 - y1) * fVal;
- ensure("lerp values are not equal", ((val1 == vec4.mV[VX]) && (val2 == vec4.mV[VY])));
- }
-
- template<> template<>
- void v2math_object::test<23>()
- {
- F32 x1 =1.f, y1 = 2.f;
- F32 val1, val2;
- LLVector2 vec2(x1, y1);
-
- F32 vecMag = vec2.normVec();
- F32 mag = (F32) sqrt(x1*x1 + y1*y1);
-
- F32 oomag = 1.f / mag;
- val1 = x1 * oomag;
- val2 = y1 * oomag;
-
- ensure("normVec failed", is_approx_equal(val1, vec2.mV[VX]) && is_approx_equal(val2, vec2.mV[VY]) && is_approx_equal(vecMag, mag));
-
- x1 =.00000001f, y1 = 0.f;
-
- vec2.setVec(x1, y1);
- vecMag = vec2.normVec();
- ensure("normVec failed should be 0.", 0. == vec2.mV[VX] && 0. == vec2.mV[VY] && vecMag == 0.);
- }
-}
+/** + * @file v2math_test.cpp + * @author Adroit + * @date 2007-02 + * @brief v2math test cases. + * + * $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 "../test/lltut.h" + +#include "../v2math.h" + + +namespace tut +{ + struct v2math_data + { + }; + typedef test_group<v2math_data> v2math_test; + typedef v2math_test::object v2math_object; + tut::v2math_test v2math_testcase("v2math_h"); + + template<> template<> + void v2math_object::test<1>() + { + LLVector2 vec2; + ensure("LLVector2:Fail to initialize ", (0.f == vec2.mV[VX] && 0.f == vec2.mV[VY])); + + F32 x =2.0f, y = 3.2f ; + LLVector2 vec3(x,y); + ensure("LLVector2(F32 x, F32 y):Fail to initialize ", (x == vec3.mV[VX]) && (y == vec3.mV[VY])); + + const F32 vec[2] = {3.2f, 4.5f}; + LLVector2 vec4(vec); + ensure("LLVector2(const F32 *vec):Fail to initialize ", (vec[0] == vec4.mV[VX]) && (vec[1] == vec4.mV[VY])); + + vec4.clearVec(); + ensure("clearVec():Fail to clean the values ", (0.f == vec4.mV[VX] && 0.f == vec4.mV[VY])); + + vec3.zeroVec(); + ensure("zeroVec():Fail to fill the zero ", (0.f == vec3.mV[VX] && 0.f == vec3.mV[VY])); + } + + template<> template<> + void v2math_object::test<2>() + { + F32 x = 123.356f, y = 2387.453f; + LLVector2 vec2,vec3; + vec2.setVec(x, y); + ensure("1:setVec: Fail ", (x == vec2.mV[VX]) && (y == vec2.mV[VY])); + + vec3.setVec(vec2); + ensure("2:setVec: Fail " ,(vec2 == vec3)); + + vec3.zeroVec(); + const F32 vec[2] = {3.24653f, 457653.4f}; + vec3.setVec(vec); + ensure("3:setVec: Fail ", (vec[0] == vec3.mV[VX]) && (vec[1] == vec3.mV[VY])); + } + + template<> template<> + void v2math_object::test<3>() + { + F32 x = 2.2345f, y = 3.5678f ; + LLVector2 vec2(x,y); + ensure("magVecSquared:Fail ", is_approx_equal(vec2.magVecSquared(), (x*x + y*y))); + ensure("magVec:Fail ", is_approx_equal(vec2.magVec(), (F32) sqrt(x*x + y*y))); + } + + template<> template<> + void v2math_object::test<4>() + { + F32 x =-2.0f, y = -3.0f ; + LLVector2 vec2(x,y); + ensure_equals("abs():Fail", vec2.abs(), true); + ensure("abs() x", is_approx_equal(vec2.mV[VX], 2.f)); + ensure("abs() y", is_approx_equal(vec2.mV[VY], 3.f)); + + ensure("isNull():Fail ", false == vec2.isNull()); //Returns true if vector has a _very_small_ length + + x =.00000001f, y = .000001001f; + vec2.setVec(x, y); + ensure("isNull(): Fail ", true == vec2.isNull()); + } + + template<> template<> + void v2math_object::test<5>() + { + F32 x =1.f, y = 2.f; + LLVector2 vec2(x, y), vec3; + vec3 = vec3.scaleVec(vec2); + ensure("scaleVec: Fail ", vec3.mV[VX] == 0. && vec3.mV[VY] == 0.); + ensure("isExactlyZero(): Fail", true == vec3.isExactlyZero()); + + vec3.setVec(2.f, 1.f); + vec3 = vec3.scaleVec(vec2); + ensure("scaleVec: Fail ", (2.f == vec3.mV[VX]) && (2.f == vec3.mV[VY])); + ensure("isExactlyZero():Fail", false == vec3.isExactlyZero()); + } + + template<> template<> + void v2math_object::test<6>() + { + F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f; + F32 val1, val2; + LLVector2 vec2(x1, y1), vec3(x2, y2), vec4; + vec4 = vec2 + vec3 ; + val1 = x1+x2; + val2 = y1+y2; + ensure("1:operator+ failed",(val1 == vec4.mV[VX]) && ((val2 == vec4.mV[VY]))); + + vec2.clearVec(); + vec3.clearVec(); + x1 = -.235f, y1 = -24.32f, x2 = -2.3f, y2 = 1.f; + vec2.setVec(x1, y1); + vec3.setVec(x2, y2); + vec4 = vec2 + vec3; + val1 = x1+x2; + val2 = y1+y2; + ensure("2:operator+ failed",(val1 == vec4.mV[VX]) && ((val2 == vec4.mV[VY]))); + } + + template<> template<> + void v2math_object::test<7>() + { + F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f; + F32 val1, val2; + LLVector2 vec2(x1, y1), vec3(x2, y2), vec4; + vec4 = vec2 - vec3 ; + val1 = x1-x2; + val2 = y1-y2; + ensure("1:operator- failed",(val1 == vec4.mV[VX]) && ((val2 == vec4.mV[VY]))); + + vec2.clearVec(); + vec3.clearVec(); + vec4.clearVec(); + x1 = -.235f, y1 = -24.32f, x2 = -2.3f, y2 = 1.f; + vec2.setVec(x1, y1); + vec3.setVec(x2, y2); + vec4 = vec2 - vec3; + val1 = x1-x2; + val2 = y1-y2; + ensure("2:operator- failed",(val1 == vec4.mV[VX]) && ((val2 == vec4.mV[VY]))); + } + + template<> template<> + void v2math_object::test<8>() + { + F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f; + F32 val1, val2; + LLVector2 vec2(x1, y1), vec3(x2, y2); + val1 = vec2 * vec3; + val2 = x1*x2 + y1*y2; + ensure("1:operator* failed",(val1 == val2)); + + vec3.clearVec(); + F32 mulVal = 4.332f; + vec3 = vec2 * mulVal; + val1 = x1*mulVal; + val2 = y1*mulVal; + ensure("2:operator* failed",(val1 == vec3.mV[VX]) && (val2 == vec3.mV[VY])); + + vec3.clearVec(); + vec3 = mulVal * vec2; + ensure("3:operator* failed",(val1 == vec3.mV[VX]) && (val2 == vec3.mV[VY])); + } + + template<> template<> + void v2math_object::test<9>() + { + F32 x1 =1.f, y1 = 2.f, div = 3.2f; + F32 val1, val2; + LLVector2 vec2(x1, y1), vec3; + vec3 = vec2 / div; + val1 = x1 / div; + val2 = y1 / div; + ensure("1:operator/ failed", is_approx_equal(val1, vec3.mV[VX]) && is_approx_equal(val2, vec3.mV[VY])); + + vec3.clearVec(); + x1 = -.235f, y1 = -24.32f, div = -2.2f; + vec2.setVec(x1, y1); + vec3 = vec2 / div; + val1 = x1 / div; + val2 = y1 / div; + ensure("2:operator/ failed", is_approx_equal(val1, vec3.mV[VX]) && is_approx_equal(val2, vec3.mV[VY])); + } + + template<> template<> + void v2math_object::test<10>() + { + F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f; + F32 val1, val2; + LLVector2 vec2(x1, y1), vec3(x2, y2), vec4; + vec4 = vec2 % vec3; + val1 = x1*y2 - x2*y1; + val2 = y1*x2 - y2*x1; + ensure("1:operator% failed",(val1 == vec4.mV[VX]) && (val2 == vec4.mV[VY])); + + vec2.clearVec(); + vec3.clearVec(); + vec4.clearVec(); + x1 = -.235f, y1 = -24.32f, x2 = -2.3f, y2 = 1.f; + vec2.setVec(x1, y1); + vec3.setVec(x2, y2); + vec4 = vec2 % vec3; + val1 = x1*y2 - x2*y1; + val2 = y1*x2 - y2*x1; + ensure("2:operator% failed",(val1 == vec4.mV[VX]) && (val2 == vec4.mV[VY])); + } + template<> template<> + void v2math_object::test<11>() + { + F32 x1 =1.f, y1 = 2.f; + LLVector2 vec2(x1, y1), vec3(x1, y1); + ensure("1:operator== failed",(vec2 == vec3)); + + vec2.clearVec(); + vec3.clearVec(); + x1 = -.235f, y1 = -24.32f; + vec2.setVec(x1, y1); + vec3.setVec(vec2); + ensure("2:operator== failed",(vec2 == vec3)); + } + + template<> template<> + void v2math_object::test<12>() + { + F32 x1 = 1.f, y1 = 2.f,x2 = 2.332f, y2 = -1.23f; + LLVector2 vec2(x1, y1), vec3(x2, y2); + ensure("1:operator!= failed",(vec2 != vec3)); + + vec2.clearVec(); + vec3.clearVec(); + vec2.setVec(x1, y1); + vec3.setVec(vec2); + ensure("2:operator!= failed", (false == (vec2 != vec3))); + } + template<> template<> + void v2math_object::test<13>() + { + F32 x1 = 1.f, y1 = 2.f,x2 = 2.332f, y2 = -1.23f; + F32 val1, val2; + LLVector2 vec2(x1, y1), vec3(x2, y2); + vec2 +=vec3; + val1 = x1+x2; + val2 = y1+y2; + ensure("1:operator+= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY])); + + vec2.setVec(x1, y1); + vec2 -=vec3; + val1 = x1-x2; + val2 = y1-y2; + ensure("2:operator-= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY])); + + vec2.clearVec(); + vec3.clearVec(); + x1 = -21.000466f, y1 = 2.98382f,x2 = 0.332f, y2 = -01.23f; + vec2.setVec(x1, y1); + vec3.setVec(x2, y2); + vec2 +=vec3; + val1 = x1+x2; + val2 = y1+y2; + ensure("3:operator+= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY])); + + vec2.setVec(x1, y1); + vec2 -=vec3; + val1 = x1-x2; + val2 = y1-y2; + ensure("4:operator-= failed", is_approx_equal(val1, vec2.mV[VX]) && is_approx_equal(val2, vec2.mV[VY])); + } + + template<> template<> + void v2math_object::test<14>() + { + F32 x1 =1.f, y1 = 2.f; + F32 val1, val2, mulVal = 4.332f; + LLVector2 vec2(x1, y1); + vec2 /=mulVal; + val1 = x1 / mulVal; + val2 = y1 / mulVal; + ensure("1:operator/= failed", is_approx_equal(val1, vec2.mV[VX]) && is_approx_equal(val2, vec2.mV[VY])); + + vec2.clearVec(); + x1 = .213f, y1 = -2.34f, mulVal = -.23f; + vec2.setVec(x1, y1); + vec2 /=mulVal; + val1 = x1 / mulVal; + val2 = y1 / mulVal; + ensure("2:operator/= failed", is_approx_equal(val1, vec2.mV[VX]) && is_approx_equal(val2, vec2.mV[VY])); + } + + template<> template<> + void v2math_object::test<15>() + { + F32 x1 =1.f, y1 = 2.f; + F32 val1, val2, mulVal = 4.332f; + LLVector2 vec2(x1, y1); + vec2 *=mulVal; + val1 = x1*mulVal; + val2 = y1*mulVal; + ensure("1:operator*= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY])); + + vec2.clearVec(); + x1 = .213f, y1 = -2.34f, mulVal = -.23f; + vec2.setVec(x1, y1); + vec2 *=mulVal; + val1 = x1*mulVal; + val2 = y1*mulVal; + ensure("2:operator*= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY])); + } + + template<> template<> + void v2math_object::test<16>() + { + F32 x1 =1.f, y1 = 2.f, x2 = -2.3f, y2 = 1.11f; + F32 val1, val2; + LLVector2 vec2(x1, y1), vec3(x2, y2); + vec2 %= vec3; + val1 = x1*y2 - x2*y1; + val2 = y1*x2 - y2*x1; + ensure("1:operator%= failed",(val1 == vec2.mV[VX]) && (val2 == vec2.mV[VY])); + } + + template<> template<> + void v2math_object::test<17>() + { + F32 x1 =1.f, y1 = 2.f; + LLVector2 vec2(x1, y1),vec3; + vec3 = -vec2; + ensure("1:operator- failed",(-vec3 == vec2)); + } + + template<> template<> + void v2math_object::test<18>() + { + F32 x1 =1.f, y1 = 2.f; + std::ostringstream stream1, stream2; + LLVector2 vec2(x1, y1),vec3; + stream1 << vec2; + vec3.setVec(x1, y1); + stream2 << vec3; + ensure("1:operator << failed",(stream1.str() == stream2.str())); + } + + template<> template<> + void v2math_object::test<19>() + { + F32 x1 =1.0f, y1 = 2.0f, x2 = -.32f, y2 = .2234f; + LLVector2 vec2(x1, y1),vec3(x2, y2); + ensure("1:operator < failed",(vec3 < vec2)); + + x1 = 1.0f, y1 = 2.0f, x2 = 1.0f, y2 = 3.2234f; + vec2.setVec(x1, y1); + vec3.setVec(x2, y2); + ensure("2:operator < failed", (false == (vec3 < vec2))); + } + + template<> template<> + void v2math_object::test<20>() + { + F32 x1 =1.0f, y1 = 2.0f; + LLVector2 vec2(x1, y1); + ensure("1:operator [] failed",( x1 == vec2[0])); + ensure("2:operator [] failed",( y1 == vec2[1])); + + vec2.clearVec(); + x1 = 23.0f, y1 = -.2361f; + vec2.setVec(x1, y1); + F32 ref1 = vec2[0]; + ensure("3:operator [] failed", ( ref1 == x1)); + F32 ref2 = vec2[1]; + ensure("4:operator [] failed", ( ref2 == y1)); + } + + template<> template<> + void v2math_object::test<21>() + { + F32 x1 =1.f, y1 = 2.f, x2 = -.32f, y2 = .2234f; + F32 val1, val2; + LLVector2 vec2(x1, y1),vec3(x2, y2); + val1 = dist_vec_squared2D(vec2, vec3); + val2 = (x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2); + ensure_equals("dist_vec_squared2D values are not equal",val2, val1); + + val1 = dist_vec_squared(vec2, vec3); + ensure_equals("dist_vec_squared values are not equal",val2, val1); + + val1 = dist_vec(vec2, vec3); + val2 = (F32) sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2)); + ensure_equals("dist_vec values are not equal",val2, val1); + } + + template<> template<> + void v2math_object::test<22>() + { + F32 x1 =1.f, y1 = 2.f, x2 = -.32f, y2 = .2234f,fVal = .0121f; + F32 val1, val2; + LLVector2 vec2(x1, y1),vec3(x2, y2); + LLVector2 vec4 = lerp(vec2, vec3, fVal); + val1 = x1 + (x2 - x1) * fVal; + val2 = y1 + (y2 - y1) * fVal; + ensure("lerp values are not equal", ((val1 == vec4.mV[VX]) && (val2 == vec4.mV[VY]))); + } + + template<> template<> + void v2math_object::test<23>() + { + F32 x1 =1.f, y1 = 2.f; + F32 val1, val2; + LLVector2 vec2(x1, y1); + + F32 vecMag = vec2.normVec(); + F32 mag = (F32) sqrt(x1*x1 + y1*y1); + + F32 oomag = 1.f / mag; + val1 = x1 * oomag; + val2 = y1 * oomag; + + ensure("normVec failed", is_approx_equal(val1, vec2.mV[VX]) && is_approx_equal(val2, vec2.mV[VY]) && is_approx_equal(vecMag, mag)); + + x1 =.00000001f, y1 = 0.f; + + vec2.setVec(x1, y1); + vecMag = vec2.normVec(); + ensure("normVec failed should be 0.", 0. == vec2.mV[VX] && 0. == vec2.mV[VY] && vecMag == 0.); + } +} diff --git a/indra/llmath/tests/v3color_test.cpp b/indra/llmath/tests/v3color_test.cpp index 11298bebd2..8897d48365 100644 --- a/indra/llmath/tests/v3color_test.cpp +++ b/indra/llmath/tests/v3color_test.cpp @@ -1,309 +1,309 @@ -/**
- * @file v3color_test.cpp
- * @author Adroit
- * @date 2007-03
- * @brief v3color test cases.
- *
- * $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 "../test/lltut.h"
-
-#include "../v3color.h"
-
-
-namespace tut
-{
- struct v3color_data
- {
- };
- typedef test_group<v3color_data> v3color_test;
- typedef v3color_test::object v3color_object;
- tut::v3color_test v3color_testcase("v3color_h");
-
- template<> template<>
- void v3color_object::test<1>()
- {
- LLColor3 llcolor3;
- ensure("1:LLColor3:Fail to default-initialize ", (0.0f == llcolor3.mV[0]) && (0.0f == llcolor3.mV[1]) && (0.0f == llcolor3.mV[2]));
- F32 r = 2.0f, g = 3.2f, b = 1.f;
- F32 v1,v2,v3;
- LLColor3 llcolor3a(r,g,b);
- ensure("2:LLColor3:Fail to initialize " ,(2.0f == llcolor3a.mV[0]) && (3.2f == llcolor3a.mV[1]) && (1.f == llcolor3a.mV[2]));
-
- const F32 vec[3] = {2.0f, 3.2f,1.f};
- LLColor3 llcolor3b(vec);
- ensure("3:LLColor3:Fail to initialize " ,(2.0f == llcolor3b.mV[0]) && (3.2f == llcolor3b.mV[1]) && (1.f == llcolor3b.mV[2]));
- const char* str = "561122";
- LLColor3 llcolor3c(str);
- v1 = (F32)86.0f/255.0f; // 0x56 = 86
- v2 = (F32)17.0f/255.0f; // 0x11 = 17
- v3 = (F32)34.0f/255.f; // 0x22 = 34
- ensure("4:LLColor3:Fail to initialize " , is_approx_equal(v1, llcolor3c.mV[0]) && is_approx_equal(v2, llcolor3c.mV[1]) && is_approx_equal(v3, llcolor3c.mV[2]));
- }
-
- template<> template<>
- void v3color_object::test<2>()
- {
- LLColor3 llcolor3;
- llcolor3.setToBlack();
- ensure("setToBlack:Fail to set black ", ((llcolor3.mV[0] == 0.f) && (llcolor3.mV[1] == 0.f) && (llcolor3.mV[2] == 0.f)));
- llcolor3.setToWhite();
- ensure("setToWhite:Fail to set white ", ((llcolor3.mV[0] == 1.f) && (llcolor3.mV[1] == 1.f) && (llcolor3.mV[2] == 1.f)));
- }
-
- template<> template<>
- void v3color_object::test<3>()
- {
- F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f;
- LLColor3 llcolor3, llcolor3a;
- llcolor3.setVec(r,g,b);
- ensure("1:setVec(r,g,b) Fail ",((r == llcolor3.mV[0]) && (g == llcolor3.mV[1]) && (b == llcolor3.mV[2])));
- llcolor3a.setVec(llcolor3);
- ensure_equals("2:setVec(LLColor3) Fail ", llcolor3,llcolor3a);
- F32 vec[3] = {1.2324f, 2.45634f, .234563f};
- llcolor3.setToBlack();
- llcolor3.setVec(vec);
- ensure("3:setVec(F32*) Fail ",((vec[0] == llcolor3.mV[0]) && (vec[1] == llcolor3.mV[1]) && (vec[2] == llcolor3.mV[2])));
- }
-
- template<> template<>
- void v3color_object::test<4>()
- {
- F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f;
- LLColor3 llcolor3(r,g,b);
- ensure("magVecSquared:Fail ", is_approx_equal(llcolor3.magVecSquared(), (r*r + g*g + b*b)));
- ensure("magVec:Fail ", is_approx_equal(llcolor3.magVec(), (F32) sqrt(r*r + g*g + b*b)));
- }
-
- template<> template<>
- void v3color_object::test<5>()
- {
- F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f;
- F32 val1, val2,val3;
- LLColor3 llcolor3(r,g,b);
- F32 vecMag = llcolor3.normVec();
- F32 mag = (F32) sqrt(r*r + g*g + b*b);
- F32 oomag = 1.f / mag;
- val1 = r * oomag;
- val2 = g * oomag;
- val3 = b * oomag;
- ensure("1:normVec failed ", (is_approx_equal(val1, llcolor3.mV[0]) && is_approx_equal(val2, llcolor3.mV[1]) && is_approx_equal(val3, llcolor3.mV[2]) && is_approx_equal(vecMag, mag)));
- r = .000000000f, g = 0.f, b = 0.0f;
- llcolor3.setVec(r,g,b);
- vecMag = llcolor3.normVec();
- ensure("2:normVec failed should be 0. ", (0. == llcolor3.mV[0] && 0. == llcolor3.mV[1] && 0. == llcolor3.mV[2] && vecMag == 0.));
- }
-
- template<> template<>
- void v3color_object::test<6>()
- {
- F32 r = 2.3436212f, g = -1231.f, b = .7849321232f;
- std::ostringstream stream1, stream2;
- LLColor3 llcolor3(r,g,b),llcolor3a;
- stream1 << llcolor3;
- llcolor3a.setVec(r,g,b);
- stream2 << llcolor3a;
- ensure("operator << failed ", (stream1.str() == stream2.str()));
- }
-
- template<> template<>
- void v3color_object::test<7>()
- {
- F32 r = 2.3436212f, g = -1231.f, b = .7849321232f;
- LLColor3 llcolor3(r,g,b),llcolor3a;
- llcolor3a = llcolor3;
- ensure("operator == failed ", (llcolor3a == llcolor3));
- }
-
- template<> template<>
- void v3color_object::test<8>()
- {
- F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
- LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2),llcolor3b;
- llcolor3b = llcolor3 + llcolor3a ;
- ensure("1:operator+ failed",is_approx_equal(r1+r2 ,llcolor3b.mV[0]) && is_approx_equal(g1+g2,llcolor3b.mV[1])&& is_approx_equal(b1+b2,llcolor3b.mV[2]));
- r1 = -.235f, g1 = -24.32f, b1 = 2.13f, r2 = -2.3f, g2 = 1.f, b2 = 34.21f;
- llcolor3.setVec(r1,g1,b1);
- llcolor3a.setVec(r2,g2,b2);
- llcolor3b = llcolor3 + llcolor3a;
- ensure("2:operator+ failed",is_approx_equal(r1+r2 ,llcolor3b.mV[0]) && is_approx_equal(g1+g2,llcolor3b.mV[1])&& is_approx_equal(b1+b2,llcolor3b.mV[2]));
- }
-
- template<> template<>
- void v3color_object::test<9>()
- {
- F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
- LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2),llcolor3b;
- llcolor3b = llcolor3 - llcolor3a ;
- ensure("1:operator- failed",is_approx_equal(r1-r2 ,llcolor3b.mV[0]) && is_approx_equal(g1-g2,llcolor3b.mV[1])&& is_approx_equal(b1-b2,llcolor3b.mV[2]));
- r1 = -.235f, g1 = -24.32f, b1 = 2.13f, r2 = -2.3f, g2 = 1.f, b2 = 34.21f;
- llcolor3.setVec(r1,g1,b1);
- llcolor3a.setVec(r2,g2,b2);
- llcolor3b = llcolor3 - llcolor3a;
- ensure("2:operator- failed",is_approx_equal(r1-r2 ,llcolor3b.mV[0]) && is_approx_equal(g1-g2,llcolor3b.mV[1])&& is_approx_equal(b1-b2,llcolor3b.mV[2]));
- }
-
- template<> template<>
- void v3color_object::test<10>()
- {
- F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
- LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2),llcolor3b;
- llcolor3b = llcolor3 * llcolor3a;
- ensure("1:operator* failed",is_approx_equal(r1*r2 ,llcolor3b.mV[0]) && is_approx_equal(g1*g2,llcolor3b.mV[1])&& is_approx_equal(b1*b2,llcolor3b.mV[2]));
- llcolor3a.setToBlack();
- F32 mulVal = 4.332f;
- llcolor3a = llcolor3 * mulVal;
- ensure("2:operator* failed",is_approx_equal(r1*mulVal ,llcolor3a.mV[0]) && is_approx_equal(g1*mulVal,llcolor3a.mV[1])&& is_approx_equal(b1*mulVal,llcolor3a.mV[2]));
- llcolor3a.setToBlack();
- llcolor3a = mulVal * llcolor3;
- ensure("3:operator* failed",is_approx_equal(r1*mulVal ,llcolor3a.mV[0]) && is_approx_equal(g1*mulVal,llcolor3a.mV[1])&& is_approx_equal(b1*mulVal,llcolor3a.mV[2]));
- }
-
- template<> template<>
- void v3color_object::test<11>()
- {
- F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f;
- LLColor3 llcolor3(r,g,b),llcolor3a;
- llcolor3a = -llcolor3;
- ensure("operator- failed ", (-llcolor3a == llcolor3));
- }
-
- template<> template<>
- void v3color_object::test<12>()
- {
- F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f;
- LLColor3 llcolor3(r,g,b),llcolor3a(r,g,b);
- ensure_equals("1:operator== failed",llcolor3a,llcolor3);
- r = 13.3436212f, g = -11.f, b = .7849321232f;
- llcolor3.setVec(r,g,b);
- llcolor3a.setVec(r,g,b);
- ensure_equals("2:operator== failed",llcolor3a,llcolor3);
- }
-
- template<> template<>
- void v3color_object::test<13>()
- {
- F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
- LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2);
- ensure("1:operator!= failed",(llcolor3 != llcolor3a));
- llcolor3.setToBlack();
- llcolor3a.setVec(llcolor3);
- ensure("2:operator!= failed", ( false == (llcolor3a != llcolor3)));
- }
-
- template<> template<>
- void v3color_object::test<14>()
- {
- F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
- LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2);
- llcolor3a += llcolor3;
- ensure("1:operator+= failed",is_approx_equal(r1+r2 ,llcolor3a.mV[0]) && is_approx_equal(g1+g2,llcolor3a.mV[1])&& is_approx_equal(b1+b2,llcolor3a.mV[2]));
- llcolor3.setVec(r1,g1,b1);
- llcolor3a.setVec(r2,g2,b2);
- llcolor3a += llcolor3;
- ensure("2:operator+= failed",is_approx_equal(r1+r2 ,llcolor3a.mV[0]) && is_approx_equal(g1+g2,llcolor3a.mV[1])&& is_approx_equal(b1+b2,llcolor3a.mV[2]));
- }
-
- template<> template<>
- void v3color_object::test<15>()
- {
- F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
- LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2);
- llcolor3a -= llcolor3;
- ensure("1:operator-= failed", is_approx_equal(r2-r1, llcolor3a.mV[0]));
- ensure("2:operator-= failed", is_approx_equal(g2-g1, llcolor3a.mV[1]));
- ensure("3:operator-= failed", is_approx_equal(b2-b1, llcolor3a.mV[2]));
- llcolor3.setVec(r1,g1,b1);
- llcolor3a.setVec(r2,g2,b2);
- llcolor3a -= llcolor3;
- ensure("4:operator-= failed", is_approx_equal(r2-r1, llcolor3a.mV[0]));
- ensure("5:operator-= failed", is_approx_equal(g2-g1, llcolor3a.mV[1]));
- ensure("6:operator-= failed", is_approx_equal(b2-b1, llcolor3a.mV[2]));
- }
-
- template<> template<>
- void v3color_object::test<16>()
- {
- F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
- LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2);
- llcolor3a *= llcolor3;
- ensure("1:operator*= failed",is_approx_equal(r1*r2 ,llcolor3a.mV[0]) && is_approx_equal(g1*g2,llcolor3a.mV[1])&& is_approx_equal(b1*b2,llcolor3a.mV[2]));
- F32 mulVal = 4.332f;
- llcolor3 *=mulVal;
- ensure("2:operator*= failed",is_approx_equal(r1*mulVal ,llcolor3.mV[0]) && is_approx_equal(g1*mulVal,llcolor3.mV[1])&& is_approx_equal(b1*mulVal,llcolor3.mV[2]));
- }
-
- template<> template<>
- void v3color_object::test<17>()
- {
- F32 r = 2.3436212f, g = -1231.f, b = .7849321232f;
- LLColor3 llcolor3(r,g,b);
- llcolor3.clamp();
- ensure("1:clamp:Fail to clamp " ,(1.0f == llcolor3.mV[0]) && (0.f == llcolor3.mV[1]) && (b == llcolor3.mV[2]));
- r = -2.3436212f, g = -1231.f, b = 67.7849321232f;
- llcolor3.setVec(r,g,b);
- llcolor3.clamp();
- ensure("2:clamp:Fail to clamp " ,(0.f == llcolor3.mV[0]) && (0.f == llcolor3.mV[1]) && (1.f == llcolor3.mV[2]));
- }
-
- template<> template<>
- void v3color_object::test<18>()
- {
- F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
- F32 val = 2.3f,val1,val2,val3;
- LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2);
- val1 = r1 + (r2 - r1)* val;
- val2 = g1 + (g2 - g1)* val;
- val3 = b1 + (b2 - b1)* val;
- LLColor3 llcolor3b = lerp(llcolor3,llcolor3a,val);
- ensure("lerp failed ", ((val1 ==llcolor3b.mV[0])&& (val2 ==llcolor3b.mV[1]) && (val3 ==llcolor3b.mV[2])));
- }
-
- template<> template<>
- void v3color_object::test<19>()
- {
- F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f;
- LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2);
- F32 val = distVec(llcolor3,llcolor3a);
- ensure("distVec failed ", is_approx_equal((F32) sqrt((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2)) ,val));
-
- F32 val1 = distVec_squared(llcolor3,llcolor3a);
- ensure("distVec_squared failed ", is_approx_equal(((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2)) ,val1));
- }
-
- template<> template<>
- void v3color_object::test<20>()
- {
- F32 r1 = 1.02223f, g1 = 22222.212f, b1 = 122222.00002f;
- LLColor3 llcolor31(r1,g1,b1);
-
- LLSD sd = llcolor31.getValue();
- LLColor3 llcolor32;
- llcolor32.setValue(sd);
- ensure_equals("LLColor3::setValue/getValue failed", llcolor31, llcolor32);
-
- LLColor3 llcolor33(sd);
- ensure_equals("LLColor3(LLSD) failed", llcolor31, llcolor33);
- }
-}
+/** + * @file v3color_test.cpp + * @author Adroit + * @date 2007-03 + * @brief v3color test cases. + * + * $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 "../test/lltut.h" + +#include "../v3color.h" + + +namespace tut +{ + struct v3color_data + { + }; + typedef test_group<v3color_data> v3color_test; + typedef v3color_test::object v3color_object; + tut::v3color_test v3color_testcase("v3color_h"); + + template<> template<> + void v3color_object::test<1>() + { + LLColor3 llcolor3; + ensure("1:LLColor3:Fail to default-initialize ", (0.0f == llcolor3.mV[0]) && (0.0f == llcolor3.mV[1]) && (0.0f == llcolor3.mV[2])); + F32 r = 2.0f, g = 3.2f, b = 1.f; + F32 v1,v2,v3; + LLColor3 llcolor3a(r,g,b); + ensure("2:LLColor3:Fail to initialize " ,(2.0f == llcolor3a.mV[0]) && (3.2f == llcolor3a.mV[1]) && (1.f == llcolor3a.mV[2])); + + const F32 vec[3] = {2.0f, 3.2f,1.f}; + LLColor3 llcolor3b(vec); + ensure("3:LLColor3:Fail to initialize " ,(2.0f == llcolor3b.mV[0]) && (3.2f == llcolor3b.mV[1]) && (1.f == llcolor3b.mV[2])); + const char* str = "561122"; + LLColor3 llcolor3c(str); + v1 = (F32)86.0f/255.0f; // 0x56 = 86 + v2 = (F32)17.0f/255.0f; // 0x11 = 17 + v3 = (F32)34.0f/255.f; // 0x22 = 34 + ensure("4:LLColor3:Fail to initialize " , is_approx_equal(v1, llcolor3c.mV[0]) && is_approx_equal(v2, llcolor3c.mV[1]) && is_approx_equal(v3, llcolor3c.mV[2])); + } + + template<> template<> + void v3color_object::test<2>() + { + LLColor3 llcolor3; + llcolor3.setToBlack(); + ensure("setToBlack:Fail to set black ", ((llcolor3.mV[0] == 0.f) && (llcolor3.mV[1] == 0.f) && (llcolor3.mV[2] == 0.f))); + llcolor3.setToWhite(); + ensure("setToWhite:Fail to set white ", ((llcolor3.mV[0] == 1.f) && (llcolor3.mV[1] == 1.f) && (llcolor3.mV[2] == 1.f))); + } + + template<> template<> + void v3color_object::test<3>() + { + F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f; + LLColor3 llcolor3, llcolor3a; + llcolor3.setVec(r,g,b); + ensure("1:setVec(r,g,b) Fail ",((r == llcolor3.mV[0]) && (g == llcolor3.mV[1]) && (b == llcolor3.mV[2]))); + llcolor3a.setVec(llcolor3); + ensure_equals("2:setVec(LLColor3) Fail ", llcolor3,llcolor3a); + F32 vec[3] = {1.2324f, 2.45634f, .234563f}; + llcolor3.setToBlack(); + llcolor3.setVec(vec); + ensure("3:setVec(F32*) Fail ",((vec[0] == llcolor3.mV[0]) && (vec[1] == llcolor3.mV[1]) && (vec[2] == llcolor3.mV[2]))); + } + + template<> template<> + void v3color_object::test<4>() + { + F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f; + LLColor3 llcolor3(r,g,b); + ensure("magVecSquared:Fail ", is_approx_equal(llcolor3.magVecSquared(), (r*r + g*g + b*b))); + ensure("magVec:Fail ", is_approx_equal(llcolor3.magVec(), (F32) sqrt(r*r + g*g + b*b))); + } + + template<> template<> + void v3color_object::test<5>() + { + F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f; + F32 val1, val2,val3; + LLColor3 llcolor3(r,g,b); + F32 vecMag = llcolor3.normVec(); + F32 mag = (F32) sqrt(r*r + g*g + b*b); + F32 oomag = 1.f / mag; + val1 = r * oomag; + val2 = g * oomag; + val3 = b * oomag; + ensure("1:normVec failed ", (is_approx_equal(val1, llcolor3.mV[0]) && is_approx_equal(val2, llcolor3.mV[1]) && is_approx_equal(val3, llcolor3.mV[2]) && is_approx_equal(vecMag, mag))); + r = .000000000f, g = 0.f, b = 0.0f; + llcolor3.setVec(r,g,b); + vecMag = llcolor3.normVec(); + ensure("2:normVec failed should be 0. ", (0. == llcolor3.mV[0] && 0. == llcolor3.mV[1] && 0. == llcolor3.mV[2] && vecMag == 0.)); + } + + template<> template<> + void v3color_object::test<6>() + { + F32 r = 2.3436212f, g = -1231.f, b = .7849321232f; + std::ostringstream stream1, stream2; + LLColor3 llcolor3(r,g,b),llcolor3a; + stream1 << llcolor3; + llcolor3a.setVec(r,g,b); + stream2 << llcolor3a; + ensure("operator << failed ", (stream1.str() == stream2.str())); + } + + template<> template<> + void v3color_object::test<7>() + { + F32 r = 2.3436212f, g = -1231.f, b = .7849321232f; + LLColor3 llcolor3(r,g,b),llcolor3a; + llcolor3a = llcolor3; + ensure("operator == failed ", (llcolor3a == llcolor3)); + } + + template<> template<> + void v3color_object::test<8>() + { + F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f; + LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2),llcolor3b; + llcolor3b = llcolor3 + llcolor3a ; + ensure("1:operator+ failed",is_approx_equal(r1+r2 ,llcolor3b.mV[0]) && is_approx_equal(g1+g2,llcolor3b.mV[1])&& is_approx_equal(b1+b2,llcolor3b.mV[2])); + r1 = -.235f, g1 = -24.32f, b1 = 2.13f, r2 = -2.3f, g2 = 1.f, b2 = 34.21f; + llcolor3.setVec(r1,g1,b1); + llcolor3a.setVec(r2,g2,b2); + llcolor3b = llcolor3 + llcolor3a; + ensure("2:operator+ failed",is_approx_equal(r1+r2 ,llcolor3b.mV[0]) && is_approx_equal(g1+g2,llcolor3b.mV[1])&& is_approx_equal(b1+b2,llcolor3b.mV[2])); + } + + template<> template<> + void v3color_object::test<9>() + { + F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f; + LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2),llcolor3b; + llcolor3b = llcolor3 - llcolor3a ; + ensure("1:operator- failed",is_approx_equal(r1-r2 ,llcolor3b.mV[0]) && is_approx_equal(g1-g2,llcolor3b.mV[1])&& is_approx_equal(b1-b2,llcolor3b.mV[2])); + r1 = -.235f, g1 = -24.32f, b1 = 2.13f, r2 = -2.3f, g2 = 1.f, b2 = 34.21f; + llcolor3.setVec(r1,g1,b1); + llcolor3a.setVec(r2,g2,b2); + llcolor3b = llcolor3 - llcolor3a; + ensure("2:operator- failed",is_approx_equal(r1-r2 ,llcolor3b.mV[0]) && is_approx_equal(g1-g2,llcolor3b.mV[1])&& is_approx_equal(b1-b2,llcolor3b.mV[2])); + } + + template<> template<> + void v3color_object::test<10>() + { + F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f; + LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2),llcolor3b; + llcolor3b = llcolor3 * llcolor3a; + ensure("1:operator* failed",is_approx_equal(r1*r2 ,llcolor3b.mV[0]) && is_approx_equal(g1*g2,llcolor3b.mV[1])&& is_approx_equal(b1*b2,llcolor3b.mV[2])); + llcolor3a.setToBlack(); + F32 mulVal = 4.332f; + llcolor3a = llcolor3 * mulVal; + ensure("2:operator* failed",is_approx_equal(r1*mulVal ,llcolor3a.mV[0]) && is_approx_equal(g1*mulVal,llcolor3a.mV[1])&& is_approx_equal(b1*mulVal,llcolor3a.mV[2])); + llcolor3a.setToBlack(); + llcolor3a = mulVal * llcolor3; + ensure("3:operator* failed",is_approx_equal(r1*mulVal ,llcolor3a.mV[0]) && is_approx_equal(g1*mulVal,llcolor3a.mV[1])&& is_approx_equal(b1*mulVal,llcolor3a.mV[2])); + } + + template<> template<> + void v3color_object::test<11>() + { + F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f; + LLColor3 llcolor3(r,g,b),llcolor3a; + llcolor3a = -llcolor3; + ensure("operator- failed ", (-llcolor3a == llcolor3)); + } + + template<> template<> + void v3color_object::test<12>() + { + F32 r = 2.3436212f, g = 1231.f, b = 4.7849321232f; + LLColor3 llcolor3(r,g,b),llcolor3a(r,g,b); + ensure_equals("1:operator== failed",llcolor3a,llcolor3); + r = 13.3436212f, g = -11.f, b = .7849321232f; + llcolor3.setVec(r,g,b); + llcolor3a.setVec(r,g,b); + ensure_equals("2:operator== failed",llcolor3a,llcolor3); + } + + template<> template<> + void v3color_object::test<13>() + { + F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f; + LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2); + ensure("1:operator!= failed",(llcolor3 != llcolor3a)); + llcolor3.setToBlack(); + llcolor3a.setVec(llcolor3); + ensure("2:operator!= failed", ( false == (llcolor3a != llcolor3))); + } + + template<> template<> + void v3color_object::test<14>() + { + F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f; + LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2); + llcolor3a += llcolor3; + ensure("1:operator+= failed",is_approx_equal(r1+r2 ,llcolor3a.mV[0]) && is_approx_equal(g1+g2,llcolor3a.mV[1])&& is_approx_equal(b1+b2,llcolor3a.mV[2])); + llcolor3.setVec(r1,g1,b1); + llcolor3a.setVec(r2,g2,b2); + llcolor3a += llcolor3; + ensure("2:operator+= failed",is_approx_equal(r1+r2 ,llcolor3a.mV[0]) && is_approx_equal(g1+g2,llcolor3a.mV[1])&& is_approx_equal(b1+b2,llcolor3a.mV[2])); + } + + template<> template<> + void v3color_object::test<15>() + { + F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f; + LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2); + llcolor3a -= llcolor3; + ensure("1:operator-= failed", is_approx_equal(r2-r1, llcolor3a.mV[0])); + ensure("2:operator-= failed", is_approx_equal(g2-g1, llcolor3a.mV[1])); + ensure("3:operator-= failed", is_approx_equal(b2-b1, llcolor3a.mV[2])); + llcolor3.setVec(r1,g1,b1); + llcolor3a.setVec(r2,g2,b2); + llcolor3a -= llcolor3; + ensure("4:operator-= failed", is_approx_equal(r2-r1, llcolor3a.mV[0])); + ensure("5:operator-= failed", is_approx_equal(g2-g1, llcolor3a.mV[1])); + ensure("6:operator-= failed", is_approx_equal(b2-b1, llcolor3a.mV[2])); + } + + template<> template<> + void v3color_object::test<16>() + { + F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f; + LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2); + llcolor3a *= llcolor3; + ensure("1:operator*= failed",is_approx_equal(r1*r2 ,llcolor3a.mV[0]) && is_approx_equal(g1*g2,llcolor3a.mV[1])&& is_approx_equal(b1*b2,llcolor3a.mV[2])); + F32 mulVal = 4.332f; + llcolor3 *=mulVal; + ensure("2:operator*= failed",is_approx_equal(r1*mulVal ,llcolor3.mV[0]) && is_approx_equal(g1*mulVal,llcolor3.mV[1])&& is_approx_equal(b1*mulVal,llcolor3.mV[2])); + } + + template<> template<> + void v3color_object::test<17>() + { + F32 r = 2.3436212f, g = -1231.f, b = .7849321232f; + LLColor3 llcolor3(r,g,b); + llcolor3.clamp(); + ensure("1:clamp:Fail to clamp " ,(1.0f == llcolor3.mV[0]) && (0.f == llcolor3.mV[1]) && (b == llcolor3.mV[2])); + r = -2.3436212f, g = -1231.f, b = 67.7849321232f; + llcolor3.setVec(r,g,b); + llcolor3.clamp(); + ensure("2:clamp:Fail to clamp " ,(0.f == llcolor3.mV[0]) && (0.f == llcolor3.mV[1]) && (1.f == llcolor3.mV[2])); + } + + template<> template<> + void v3color_object::test<18>() + { + F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f; + F32 val = 2.3f,val1,val2,val3; + LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2); + val1 = r1 + (r2 - r1)* val; + val2 = g1 + (g2 - g1)* val; + val3 = b1 + (b2 - b1)* val; + LLColor3 llcolor3b = lerp(llcolor3,llcolor3a,val); + ensure("lerp failed ", ((val1 ==llcolor3b.mV[0])&& (val2 ==llcolor3b.mV[1]) && (val3 ==llcolor3b.mV[2]))); + } + + template<> template<> + void v3color_object::test<19>() + { + F32 r1 =1.f, g1 = 2.f,b1 = 1.2f, r2 = -2.3f, g2 = 1.11f, b2 = 1234.234f; + LLColor3 llcolor3(r1,g1,b1),llcolor3a(r2,g2,b2); + F32 val = distVec(llcolor3,llcolor3a); + ensure("distVec failed ", is_approx_equal((F32) sqrt((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2)) ,val)); + + F32 val1 = distVec_squared(llcolor3,llcolor3a); + ensure("distVec_squared failed ", is_approx_equal(((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2)) ,val1)); + } + + template<> template<> + void v3color_object::test<20>() + { + F32 r1 = 1.02223f, g1 = 22222.212f, b1 = 122222.00002f; + LLColor3 llcolor31(r1,g1,b1); + + LLSD sd = llcolor31.getValue(); + LLColor3 llcolor32; + llcolor32.setValue(sd); + ensure_equals("LLColor3::setValue/getValue failed", llcolor31, llcolor32); + + LLColor3 llcolor33(sd); + ensure_equals("LLColor3(LLSD) failed", llcolor31, llcolor33); + } +} diff --git a/indra/llmath/tests/v3dmath_test.cpp b/indra/llmath/tests/v3dmath_test.cpp index fc9e7d9dc1..db08419012 100644 --- a/indra/llmath/tests/v3dmath_test.cpp +++ b/indra/llmath/tests/v3dmath_test.cpp @@ -1,531 +1,531 @@ -/**
- * @file v3dmath_test.cpp
- * @author Adroit
- * @date 2007-03
- * @brief v3dmath test cases.
- *
- * $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 "llsd.h"
-#include "../test/lltut.h"
-
-#include "../m3math.h"
-#include "../v4math.h"
-#include "../v3dmath.h"
-#include "../v3dmath.h"
-#include "../llquaternion.h"
-
-namespace tut
-{
- struct v3dmath_data
- {
- };
- typedef test_group<v3dmath_data> v3dmath_test;
- typedef v3dmath_test::object v3dmath_object;
- tut::v3dmath_test v3dmath_testcase("v3dmath_h");
-
- template<> template<>
- void v3dmath_object::test<1>()
- {
- LLVector3d vec3D;
- ensure("1:LLVector3d:Fail to initialize ", ((0 == vec3D.mdV[VX]) && (0 == vec3D.mdV[VY]) && (0 == vec3D.mdV[VZ])));
- F64 x = 2.32f, y = 1.212f, z = -.12f;
- LLVector3d vec3Da(x,y,z);
- ensure("2:LLVector3d:Fail to initialize ", ((2.32f == vec3Da.mdV[VX]) && (1.212f == vec3Da.mdV[VY]) && (-.12f == vec3Da.mdV[VZ])));
- const F64 vec[3] = {1.2f ,3.2f, -4.2f};
- LLVector3d vec3Db(vec);
- ensure("3:LLVector3d:Fail to initialize ", ((1.2f == vec3Db.mdV[VX]) && (3.2f == vec3Db.mdV[VY]) && (-4.2f == vec3Db.mdV[VZ])));
- LLVector3 vec3((F32)x,(F32)y,(F32)z);
- LLVector3d vec3Dc(vec3);
- ensure_equals("4:LLVector3d Fail to initialize",vec3Da,vec3Dc);
- }
-
- template<> template<>
- void v3dmath_object::test<2>()
- {
- S32 a = -235;
- LLSD llsd(a);
- LLVector3d vec3d(llsd);
- LLSD sd = vec3d.getValue();
- LLVector3d vec3da(sd);
- ensure("1:getValue:Fail ", (vec3d == vec3da));
- }
-
- template<> template<>
- void v3dmath_object::test<3>()
- {
- F64 a = 232345521.411132;
- LLSD llsd(a);
- LLVector3d vec3d;
- vec3d.setValue(llsd);
- LLSD sd = vec3d.getValue();
- LLVector3d vec3da(sd);
- ensure("1:setValue:Fail to initialize ", (vec3d == vec3da));
- }
-
- template<> template<>
- void v3dmath_object::test<4>()
- {
- F64 a[3] = {222231.43222, 12345.2343, -434343.33222};
- LLSD llsd;
- llsd[0] = a[0];
- llsd[1] = a[1];
- llsd[2] = a[2];
- LLVector3d vec3D;
- vec3D = (LLVector3d)llsd;
- ensure("1:operator=:Fail to initialize ", ((llsd[0].asReal()== vec3D.mdV[VX]) && (llsd[1].asReal() == vec3D.mdV[VY]) && (llsd[2].asReal() == vec3D.mdV[VZ])));
- }
-
- template<> template<>
- void v3dmath_object::test<5>()
- {
- F64 x = 2.32f, y = 1.212f, z = -.12f;
- LLVector3d vec3D(x,y,z);
- vec3D.clearVec();
- ensure("1:clearVec:Fail to initialize ", ((0 == vec3D.mdV[VX]) && (0 == vec3D.mdV[VY]) && (0 == vec3D.mdV[VZ])));
- vec3D.setVec(x,y,z);
- ensure("2:setVec:Fail to initialize ", ((x == vec3D.mdV[VX]) && (y == vec3D.mdV[VY]) && (z == vec3D.mdV[VZ])));
- vec3D.zeroVec();
- ensure("3:zeroVec:Fail to initialize ", ((0 == vec3D.mdV[VX]) && (0 == vec3D.mdV[VY]) && (0 == vec3D.mdV[VZ])));
- vec3D.clearVec();
- LLVector3 vec3((F32)x,(F32)y,(F32)z);
- vec3D.setVec(vec3);
- ensure("4:setVec:Fail to initialize ", ((x == vec3D.mdV[VX]) && (y == vec3D.mdV[VY]) && (z == vec3D.mdV[VZ])));
- vec3D.clearVec();
- const F64 vec[3] = {x,y,z};
- vec3D.setVec(vec);
- ensure("5:setVec:Fail to initialize ", ((x == vec3D.mdV[VX]) && (y == vec3D.mdV[VY]) && (z == vec3D.mdV[VZ])));
- LLVector3d vec3Da;
- vec3Da.setVec(vec3D);
- ensure_equals("6:setVec: Fail to initialize", vec3D, vec3Da);
- }
-
- template<> template<>
- void v3dmath_object::test<6>()
- {
- F64 x = -2.32, y = 1.212, z = -.12;
- LLVector3d vec3D(x,y,z);
- vec3D.abs();
- ensure("1:abs:Fail ", ((-x == vec3D.mdV[VX]) && (y == vec3D.mdV[VY]) && (-z == vec3D.mdV[VZ])));
- ensure("2:isNull():Fail ", (false == vec3D.isNull()));
- vec3D.clearVec();
- x =.00000001, y = .000001001, z = .000001001;
- vec3D.setVec(x,y,z);
- ensure("3:isNull():Fail ", (true == vec3D.isNull()));
- ensure("4:isExactlyZero():Fail ", (false == vec3D.isExactlyZero()));
- x =.0000000, y = .00000000, z = .00000000;
- vec3D.setVec(x,y,z);
- ensure("5:isExactlyZero():Fail ", (true == vec3D.isExactlyZero()));
- }
-
- template<> template<>
- void v3dmath_object::test<7>()
- {
- F64 x = -2.32, y = 1.212, z = -.12;
- LLVector3d vec3D(x,y,z);
-
- ensure("1:operator [] failed",( x == vec3D[0]));
- ensure("2:operator [] failed",( y == vec3D[1]));
- ensure("3:operator [] failed",( z == vec3D[2]));
- vec3D.clearVec();
- x = 23.23, y = -.2361, z = 3.25;
- vec3D.setVec(x,y,z);
- F64 &ref1 = vec3D[0];
- ensure("4:operator [] failed",( ref1 == vec3D[0]));
- F64 &ref2 = vec3D[1];
- ensure("5:operator [] failed",( ref2 == vec3D[1]));
- F64 &ref3 = vec3D[2];
- ensure("6:operator [] failed",( ref3 == vec3D[2]));
- }
-
- template<> template<>
- void v3dmath_object::test<8>()
- {
- F32 x = 1.f, y = 2.f, z = -1.f;
- LLVector4 vec4(x,y,z);
- LLVector3d vec3D;
- vec3D = vec4;
- ensure("1:operator=:Fail to initialize ", ((vec4.mV[VX] == vec3D.mdV[VX]) && (vec4.mV[VY] == vec3D.mdV[VY]) && (vec4.mV[VZ] == vec3D.mdV[VZ])));
- }
-
- template<> template<>
- void v3dmath_object::test<9>()
- {
- F64 x1 = 1.78787878, y1 = 232322.2121, z1 = -12121.121212;
- F64 x2 = 1.2, y2 = 2.5, z2 = 1.;
- LLVector3d vec3D(x1,y1,z1),vec3Da(x2,y2,z2),vec3Db;
- vec3Db = vec3Da+ vec3D;
- ensure("1:operator+:Fail to initialize ", ((x1+x2 == vec3Db.mdV[VX]) && (y1+y2 == vec3Db.mdV[VY]) && (z1+z2 == vec3Db.mdV[VZ])));
- x1 = -2.45, y1 = 2.1, z1 = 3.0;
- vec3D.clearVec();
- vec3Da.clearVec();
- vec3D.setVec(x1,y1,z1);
- vec3Da += vec3D;
- ensure_equals("2:operator+=: Fail to initialize", vec3Da,vec3D);
- vec3Da += vec3D;
- ensure("3:operator+=:Fail to initialize ", ((2*x1 == vec3Da.mdV[VX]) && (2*y1 == vec3Da.mdV[VY]) && (2*z1 == vec3Da.mdV[VZ])));
- }
-
- template<> template<>
- void v3dmath_object::test<10>()
- {
- F64 x1 = 1., y1 = 2., z1 = -1.1;
- F64 x2 = 1.2, y2 = 2.5, z2 = 1.;
- LLVector3d vec3D(x1,y1,z1),vec3Da(x2,y2,z2),vec3Db;
- vec3Db = vec3Da - vec3D;
- ensure("1:operator-:Fail to initialize ", ((x2-x1 == vec3Db.mdV[VX]) && (y2-y1 == vec3Db.mdV[VY]) && (z2-z1 == vec3Db.mdV[VZ])));
- x1 = -2.45, y1 = 2.1, z1 = 3.0;
- vec3D.clearVec();
- vec3Da.clearVec();
- vec3D.setVec(x1,y1,z1);
- vec3Da -=vec3D;
- ensure("2:operator-=:Fail to initialize ", ((2.45 == vec3Da.mdV[VX]) && (-2.1 == vec3Da.mdV[VY]) && (-3.0 == vec3Da.mdV[VZ])));
- vec3Da -= vec3D;
- ensure("3:operator-=:Fail to initialize ", ((-2*x1 == vec3Da.mdV[VX]) && (-2*y1 == vec3Da.mdV[VY]) && (-2*z1 == vec3Da.mdV[VZ])));
- }
- template<> template<>
- void v3dmath_object::test<11>()
- {
- F64 x1 = 1., y1 = 2., z1 = -1.1;
- F64 x2 = 1.2, y2 = 2.5, z2 = 1.;
- LLVector3d vec3D(x1,y1,z1),vec3Da(x2,y2,z2);
- F64 res = vec3D * vec3Da;
- ensure_approximately_equals(
- "1:operator* failed",
- res,
- (x1*x2 + y1*y2 + z1*z2),
- 8);
- vec3Da.clearVec();
- F64 mulVal = 4.2;
- vec3Da = vec3D * mulVal;
- ensure_approximately_equals(
- "2a:operator* failed",
- vec3Da.mdV[VX],
- x1*mulVal,
- 8);
- ensure_approximately_equals(
- "2b:operator* failed",
- vec3Da.mdV[VY],
- y1*mulVal,
- 8);
- ensure_approximately_equals(
- "2c:operator* failed",
- vec3Da.mdV[VZ],
- z1*mulVal,
- 8);
- vec3Da.clearVec();
- vec3Da = mulVal * vec3D;
- ensure_approximately_equals(
- "3a:operator* failed",
- vec3Da.mdV[VX],
- x1*mulVal,
- 8);
- ensure_approximately_equals(
- "3b:operator* failed",
- vec3Da.mdV[VY],
- y1*mulVal,
- 8);
- ensure_approximately_equals(
- "3c:operator* failed",
- vec3Da.mdV[VZ],
- z1*mulVal,
- 8);
- vec3D *= mulVal;
- ensure_approximately_equals(
- "4a:operator*= failed",
- vec3D.mdV[VX],
- x1*mulVal,
- 8);
- ensure_approximately_equals(
- "4b:operator*= failed",
- vec3D.mdV[VY],
- y1*mulVal,
- 8);
- ensure_approximately_equals(
- "4c:operator*= failed",
- vec3D.mdV[VZ],
- z1*mulVal,
- 8);
- }
-
- template<> template<>
- void v3dmath_object::test<12>()
- {
- F64 x1 = 1., y1 = 2., z1 = -1.1;
- F64 x2 = 1.2, y2 = 2.5, z2 = 1.;
- F64 val1, val2, val3;
- LLVector3d vec3D(x1,y1,z1),vec3Da(x2,y2,z2), vec3Db;
- vec3Db = vec3D % vec3Da;
- val1 = y1*z2 - y2*z1;
- val2 = z1*x2 -z2*x1;
- val3 = x1*y2-x2*y1;
- ensure("1:operator% failed",(val1 == vec3Db.mdV[VX]) && (val2 == vec3Db.mdV[VY]) && (val3 == vec3Db.mdV[VZ]));
- vec3D %= vec3Da;
- ensure("2:operator%= failed",
- is_approx_equal(vec3D.mdV[VX],vec3Db.mdV[VX]) &&
- is_approx_equal(vec3D.mdV[VY],vec3Db.mdV[VY]) &&
- is_approx_equal(vec3D.mdV[VZ],vec3Db.mdV[VZ]) );
- }
-
- template<> template<>
- void v3dmath_object::test<13>()
- {
- F64 x1 = 1., y1 = 2., z1 = -1.1,div = 4.2;
- F64 t = 1.f / div;
- LLVector3d vec3D(x1,y1,z1), vec3Da;
- vec3Da = vec3D/div;
- ensure_approximately_equals(
- "1a:operator/ failed",
- vec3Da.mdV[VX],
- x1*t,
- 8);
- ensure_approximately_equals(
- "1b:operator/ failed",
- vec3Da.mdV[VY],
- y1*t,
- 8);
- ensure_approximately_equals(
- "1c:operator/ failed",
- vec3Da.mdV[VZ],
- z1*t,
- 8);
- x1 = 1.23, y1 = 4., z1 = -2.32;
- vec3D.clearVec();
- vec3Da.clearVec();
- vec3D.setVec(x1,y1,z1);
- vec3Da = vec3D/div;
- ensure_approximately_equals(
- "2a:operator/ failed",
- vec3Da.mdV[VX],
- x1*t,
- 8);
- ensure_approximately_equals(
- "2b:operator/ failed",
- vec3Da.mdV[VY],
- y1*t,
- 8);
- ensure_approximately_equals(
- "2c:operator/ failed",
- vec3Da.mdV[VZ],
- z1*t,
- 8);
- vec3D /= div;
- ensure_approximately_equals(
- "3a:operator/= failed",
- vec3D.mdV[VX],
- x1*t,
- 8);
- ensure_approximately_equals(
- "3b:operator/= failed",
- vec3D.mdV[VY],
- y1*t,
- 8);
- ensure_approximately_equals(
- "3c:operator/= failed",
- vec3D.mdV[VZ],
- z1*t,
- 8);
- }
-
- template<> template<>
- void v3dmath_object::test<14>()
- {
- F64 x1 = 1., y1 = 2., z1 = -1.1;
- LLVector3d vec3D(x1,y1,z1), vec3Da;
- ensure("1:operator!= failed",(true == (vec3D !=vec3Da)));
- vec3Da = vec3D;
- ensure("2:operator== failed",(vec3D ==vec3Da));
- vec3D.clearVec();
- vec3Da.clearVec();
- x1 = .211, y1 = 21.111, z1 = 23.22;
- vec3D.setVec(x1,y1,z1);
- vec3Da.setVec(x1,y1,z1);
- ensure("3:operator== failed",(vec3D ==vec3Da));
- ensure("4:operator!= failed",(false == (vec3D !=vec3Da)));
- }
-
- template<> template<>
- void v3dmath_object::test<15>()
- {
- F64 x1 = 1., y1 = 2., z1 = -1.1;
- LLVector3d vec3D(x1,y1,z1), vec3Da;
- std::ostringstream stream1, stream2;
- stream1 << vec3D;
- vec3Da.setVec(x1,y1,z1);
- stream2 << vec3Da;
- ensure("1:operator << failed",(stream1.str() == stream2.str()));
- }
-
- template<> template<>
- void v3dmath_object::test<16>()
- {
- F64 x1 = 1.23, y1 = 2.0, z1 = 4.;
- std::string buf("1.23 2. 4");
- LLVector3d vec3D, vec3Da(x1,y1,z1);
- LLVector3d::parseVector3d(buf, &vec3D);
- ensure_equals("1:parseVector3d: failed " , vec3D, vec3Da);
- }
-
- template<> template<>
- void v3dmath_object::test<17>()
- {
- F64 x1 = 1., y1 = 2., z1 = -1.1;
- LLVector3d vec3D(x1,y1,z1), vec3Da;
- vec3Da = -vec3D;
- ensure("1:operator- failed", (vec3D == - vec3Da));
- }
-
- template<> template<>
- void v3dmath_object::test<18>()
- {
- F64 x = 1., y = 2., z = -1.1;
- LLVector3d vec3D(x,y,z);
- F64 res = (x*x + y*y + z*z) - vec3D.magVecSquared();
- ensure("1:magVecSquared:Fail ", ((-F_APPROXIMATELY_ZERO <= res)&& (res <=F_APPROXIMATELY_ZERO)));
- res = (F32) sqrt(x*x + y*y + z*z) - vec3D.magVec();
- ensure("2:magVec: Fail ", ((-F_APPROXIMATELY_ZERO <= res)&& (res <=F_APPROXIMATELY_ZERO)));
- }
-
- template<> template<>
- void v3dmath_object::test<19>()
- {
- F64 x = 1., y = 2., z = -1.1;
- LLVector3d vec3D(x,y,z);
- F64 mag = vec3D.normVec();
- mag = 1.f/ mag;
- ensure_approximately_equals(
- "1a:normVec: Fail ",
- vec3D.mdV[VX],
- x * mag,
- 8);
- ensure_approximately_equals(
- "1b:normVec: Fail ",
- vec3D.mdV[VY],
- y * mag,
- 8);
- ensure_approximately_equals(
- "1c:normVec: Fail ",
- vec3D.mdV[VZ],
- z * mag,
- 8);
- x = 0.000000001, y = 0.000000001, z = 0.000000001;
- vec3D.clearVec();
- vec3D.setVec(x,y,z);
- mag = vec3D.normVec();
- ensure_approximately_equals(
- "2a:normVec: Fail ",
- vec3D.mdV[VX],
- x * mag,
- 8);
- ensure_approximately_equals(
- "2b:normVec: Fail ",
- vec3D.mdV[VY],
- y * mag,
- 8);
- ensure_approximately_equals(
- "2c:normVec: Fail ",
- vec3D.mdV[VZ],
- z * mag,
- 8);
- }
-
- template<> template<>
- void v3dmath_object::test<20>()
- {
- F64 x1 = 1111.232222;
- F64 y1 = 2222222222.22;
- F64 z1 = 422222222222.0;
- std::string buf("1111.232222 2222222222.22 422222222222");
- LLVector3d vec3Da, vec3Db(x1,y1,z1);
- LLVector3d::parseVector3d(buf, &vec3Da);
- ensure_equals("1:parseVector3 failed", vec3Da, vec3Db);
- }
-
- template<> template<>
- void v3dmath_object::test<21>()
- {
- F64 x1 = 1., y1 = 2., z1 = -1.1;
- F64 x2 = 1.2, y2 = 2.5, z2 = 1.;
- F64 val = 2.3f,val1,val2,val3;
- val1 = x1 + (x2 - x1)* val;
- val2 = y1 + (y2 - y1)* val;
- val3 = z1 + (z2 - z1)* val;
- LLVector3d vec3Da(x1,y1,z1),vec3Db(x2,y2,z2);
- LLVector3d vec3d = lerp(vec3Da,vec3Db,val);
- ensure("1:lerp failed", ((val1 ==vec3d.mdV[VX])&& (val2 ==vec3d.mdV[VY]) && (val3 ==vec3d.mdV[VZ])));
- }
-
- template<> template<>
- void v3dmath_object::test<22>()
- {
- F64 x = 2.32, y = 1.212, z = -.12;
- F64 min = 0.0001, max = 3.0;
- LLVector3d vec3d(x,y,z);
- ensure("1:clamp:Fail ", (true == (vec3d.clamp(min, max))));
- x = 0.000001f, z = 5.3f;
- vec3d.setVec(x,y,z);
- ensure("2:clamp:Fail ", (true == (vec3d.clamp(min, max))));
- }
-
- template<> template<>
- void v3dmath_object::test<23>()
- {
- F64 x = 10., y = 20., z = -15.;
- F64 epsilon = .23425;
- LLVector3d vec3Da(x,y,z), vec3Db(x,y,z);
- ensure("1:are_parallel: Fail ", (true == are_parallel(vec3Da,vec3Db,epsilon)));
- F64 x1 = -12., y1 = -20., z1 = -100.;
- vec3Db.clearVec();
- vec3Db.setVec(x1,y1,z1);
- ensure("2:are_parallel: Fail ", (false == are_parallel(vec3Da,vec3Db,epsilon)));
- }
-
- template<> template<>
- void v3dmath_object::test<24>()
- {
-#if LL_WINDOWS && _MSC_VER < 1400
- skip("This fails on VS2003!");
-#else
- F64 x = 10., y = 20., z = -15.;
- F64 angle1, angle2;
- LLVector3d vec3Da(x,y,z), vec3Db(x,y,z);
- angle1 = angle_between(vec3Da, vec3Db);
- ensure("1:angle_between: Fail ", (0 == angle1));
- F64 x1 = -1., y1 = -20., z1 = -1.;
- vec3Da.clearVec();
- vec3Da.setVec(x1,y1,z1);
- angle2 = angle_between(vec3Da, vec3Db);
- vec3Db.normVec();
- vec3Da.normVec();
- F64 angle = vec3Db*vec3Da;
- angle = acos(angle);
-#if LL_WINDOWS && _MSC_VER > 1900
- skip("This fails on VS2017!");
-#else
- ensure("2:angle_between: Fail ", (angle == angle2));
-#endif
-
-#endif
- }
-}
+/** + * @file v3dmath_test.cpp + * @author Adroit + * @date 2007-03 + * @brief v3dmath test cases. + * + * $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 "llsd.h" +#include "../test/lltut.h" + +#include "../m3math.h" +#include "../v4math.h" +#include "../v3dmath.h" +#include "../v3dmath.h" +#include "../llquaternion.h" + +namespace tut +{ + struct v3dmath_data + { + }; + typedef test_group<v3dmath_data> v3dmath_test; + typedef v3dmath_test::object v3dmath_object; + tut::v3dmath_test v3dmath_testcase("v3dmath_h"); + + template<> template<> + void v3dmath_object::test<1>() + { + LLVector3d vec3D; + ensure("1:LLVector3d:Fail to initialize ", ((0 == vec3D.mdV[VX]) && (0 == vec3D.mdV[VY]) && (0 == vec3D.mdV[VZ]))); + F64 x = 2.32f, y = 1.212f, z = -.12f; + LLVector3d vec3Da(x,y,z); + ensure("2:LLVector3d:Fail to initialize ", ((2.32f == vec3Da.mdV[VX]) && (1.212f == vec3Da.mdV[VY]) && (-.12f == vec3Da.mdV[VZ]))); + const F64 vec[3] = {1.2f ,3.2f, -4.2f}; + LLVector3d vec3Db(vec); + ensure("3:LLVector3d:Fail to initialize ", ((1.2f == vec3Db.mdV[VX]) && (3.2f == vec3Db.mdV[VY]) && (-4.2f == vec3Db.mdV[VZ]))); + LLVector3 vec3((F32)x,(F32)y,(F32)z); + LLVector3d vec3Dc(vec3); + ensure_equals("4:LLVector3d Fail to initialize",vec3Da,vec3Dc); + } + + template<> template<> + void v3dmath_object::test<2>() + { + S32 a = -235; + LLSD llsd(a); + LLVector3d vec3d(llsd); + LLSD sd = vec3d.getValue(); + LLVector3d vec3da(sd); + ensure("1:getValue:Fail ", (vec3d == vec3da)); + } + + template<> template<> + void v3dmath_object::test<3>() + { + F64 a = 232345521.411132; + LLSD llsd(a); + LLVector3d vec3d; + vec3d.setValue(llsd); + LLSD sd = vec3d.getValue(); + LLVector3d vec3da(sd); + ensure("1:setValue:Fail to initialize ", (vec3d == vec3da)); + } + + template<> template<> + void v3dmath_object::test<4>() + { + F64 a[3] = {222231.43222, 12345.2343, -434343.33222}; + LLSD llsd; + llsd[0] = a[0]; + llsd[1] = a[1]; + llsd[2] = a[2]; + LLVector3d vec3D; + vec3D = (LLVector3d)llsd; + ensure("1:operator=:Fail to initialize ", ((llsd[0].asReal()== vec3D.mdV[VX]) && (llsd[1].asReal() == vec3D.mdV[VY]) && (llsd[2].asReal() == vec3D.mdV[VZ]))); + } + + template<> template<> + void v3dmath_object::test<5>() + { + F64 x = 2.32f, y = 1.212f, z = -.12f; + LLVector3d vec3D(x,y,z); + vec3D.clearVec(); + ensure("1:clearVec:Fail to initialize ", ((0 == vec3D.mdV[VX]) && (0 == vec3D.mdV[VY]) && (0 == vec3D.mdV[VZ]))); + vec3D.setVec(x,y,z); + ensure("2:setVec:Fail to initialize ", ((x == vec3D.mdV[VX]) && (y == vec3D.mdV[VY]) && (z == vec3D.mdV[VZ]))); + vec3D.zeroVec(); + ensure("3:zeroVec:Fail to initialize ", ((0 == vec3D.mdV[VX]) && (0 == vec3D.mdV[VY]) && (0 == vec3D.mdV[VZ]))); + vec3D.clearVec(); + LLVector3 vec3((F32)x,(F32)y,(F32)z); + vec3D.setVec(vec3); + ensure("4:setVec:Fail to initialize ", ((x == vec3D.mdV[VX]) && (y == vec3D.mdV[VY]) && (z == vec3D.mdV[VZ]))); + vec3D.clearVec(); + const F64 vec[3] = {x,y,z}; + vec3D.setVec(vec); + ensure("5:setVec:Fail to initialize ", ((x == vec3D.mdV[VX]) && (y == vec3D.mdV[VY]) && (z == vec3D.mdV[VZ]))); + LLVector3d vec3Da; + vec3Da.setVec(vec3D); + ensure_equals("6:setVec: Fail to initialize", vec3D, vec3Da); + } + + template<> template<> + void v3dmath_object::test<6>() + { + F64 x = -2.32, y = 1.212, z = -.12; + LLVector3d vec3D(x,y,z); + vec3D.abs(); + ensure("1:abs:Fail ", ((-x == vec3D.mdV[VX]) && (y == vec3D.mdV[VY]) && (-z == vec3D.mdV[VZ]))); + ensure("2:isNull():Fail ", (false == vec3D.isNull())); + vec3D.clearVec(); + x =.00000001, y = .000001001, z = .000001001; + vec3D.setVec(x,y,z); + ensure("3:isNull():Fail ", (true == vec3D.isNull())); + ensure("4:isExactlyZero():Fail ", (false == vec3D.isExactlyZero())); + x =.0000000, y = .00000000, z = .00000000; + vec3D.setVec(x,y,z); + ensure("5:isExactlyZero():Fail ", (true == vec3D.isExactlyZero())); + } + + template<> template<> + void v3dmath_object::test<7>() + { + F64 x = -2.32, y = 1.212, z = -.12; + LLVector3d vec3D(x,y,z); + + ensure("1:operator [] failed",( x == vec3D[0])); + ensure("2:operator [] failed",( y == vec3D[1])); + ensure("3:operator [] failed",( z == vec3D[2])); + vec3D.clearVec(); + x = 23.23, y = -.2361, z = 3.25; + vec3D.setVec(x,y,z); + F64 &ref1 = vec3D[0]; + ensure("4:operator [] failed",( ref1 == vec3D[0])); + F64 &ref2 = vec3D[1]; + ensure("5:operator [] failed",( ref2 == vec3D[1])); + F64 &ref3 = vec3D[2]; + ensure("6:operator [] failed",( ref3 == vec3D[2])); + } + + template<> template<> + void v3dmath_object::test<8>() + { + F32 x = 1.f, y = 2.f, z = -1.f; + LLVector4 vec4(x,y,z); + LLVector3d vec3D; + vec3D = vec4; + ensure("1:operator=:Fail to initialize ", ((vec4.mV[VX] == vec3D.mdV[VX]) && (vec4.mV[VY] == vec3D.mdV[VY]) && (vec4.mV[VZ] == vec3D.mdV[VZ]))); + } + + template<> template<> + void v3dmath_object::test<9>() + { + F64 x1 = 1.78787878, y1 = 232322.2121, z1 = -12121.121212; + F64 x2 = 1.2, y2 = 2.5, z2 = 1.; + LLVector3d vec3D(x1,y1,z1),vec3Da(x2,y2,z2),vec3Db; + vec3Db = vec3Da+ vec3D; + ensure("1:operator+:Fail to initialize ", ((x1+x2 == vec3Db.mdV[VX]) && (y1+y2 == vec3Db.mdV[VY]) && (z1+z2 == vec3Db.mdV[VZ]))); + x1 = -2.45, y1 = 2.1, z1 = 3.0; + vec3D.clearVec(); + vec3Da.clearVec(); + vec3D.setVec(x1,y1,z1); + vec3Da += vec3D; + ensure_equals("2:operator+=: Fail to initialize", vec3Da,vec3D); + vec3Da += vec3D; + ensure("3:operator+=:Fail to initialize ", ((2*x1 == vec3Da.mdV[VX]) && (2*y1 == vec3Da.mdV[VY]) && (2*z1 == vec3Da.mdV[VZ]))); + } + + template<> template<> + void v3dmath_object::test<10>() + { + F64 x1 = 1., y1 = 2., z1 = -1.1; + F64 x2 = 1.2, y2 = 2.5, z2 = 1.; + LLVector3d vec3D(x1,y1,z1),vec3Da(x2,y2,z2),vec3Db; + vec3Db = vec3Da - vec3D; + ensure("1:operator-:Fail to initialize ", ((x2-x1 == vec3Db.mdV[VX]) && (y2-y1 == vec3Db.mdV[VY]) && (z2-z1 == vec3Db.mdV[VZ]))); + x1 = -2.45, y1 = 2.1, z1 = 3.0; + vec3D.clearVec(); + vec3Da.clearVec(); + vec3D.setVec(x1,y1,z1); + vec3Da -=vec3D; + ensure("2:operator-=:Fail to initialize ", ((2.45 == vec3Da.mdV[VX]) && (-2.1 == vec3Da.mdV[VY]) && (-3.0 == vec3Da.mdV[VZ]))); + vec3Da -= vec3D; + ensure("3:operator-=:Fail to initialize ", ((-2*x1 == vec3Da.mdV[VX]) && (-2*y1 == vec3Da.mdV[VY]) && (-2*z1 == vec3Da.mdV[VZ]))); + } + template<> template<> + void v3dmath_object::test<11>() + { + F64 x1 = 1., y1 = 2., z1 = -1.1; + F64 x2 = 1.2, y2 = 2.5, z2 = 1.; + LLVector3d vec3D(x1,y1,z1),vec3Da(x2,y2,z2); + F64 res = vec3D * vec3Da; + ensure_approximately_equals( + "1:operator* failed", + res, + (x1*x2 + y1*y2 + z1*z2), + 8); + vec3Da.clearVec(); + F64 mulVal = 4.2; + vec3Da = vec3D * mulVal; + ensure_approximately_equals( + "2a:operator* failed", + vec3Da.mdV[VX], + x1*mulVal, + 8); + ensure_approximately_equals( + "2b:operator* failed", + vec3Da.mdV[VY], + y1*mulVal, + 8); + ensure_approximately_equals( + "2c:operator* failed", + vec3Da.mdV[VZ], + z1*mulVal, + 8); + vec3Da.clearVec(); + vec3Da = mulVal * vec3D; + ensure_approximately_equals( + "3a:operator* failed", + vec3Da.mdV[VX], + x1*mulVal, + 8); + ensure_approximately_equals( + "3b:operator* failed", + vec3Da.mdV[VY], + y1*mulVal, + 8); + ensure_approximately_equals( + "3c:operator* failed", + vec3Da.mdV[VZ], + z1*mulVal, + 8); + vec3D *= mulVal; + ensure_approximately_equals( + "4a:operator*= failed", + vec3D.mdV[VX], + x1*mulVal, + 8); + ensure_approximately_equals( + "4b:operator*= failed", + vec3D.mdV[VY], + y1*mulVal, + 8); + ensure_approximately_equals( + "4c:operator*= failed", + vec3D.mdV[VZ], + z1*mulVal, + 8); + } + + template<> template<> + void v3dmath_object::test<12>() + { + F64 x1 = 1., y1 = 2., z1 = -1.1; + F64 x2 = 1.2, y2 = 2.5, z2 = 1.; + F64 val1, val2, val3; + LLVector3d vec3D(x1,y1,z1),vec3Da(x2,y2,z2), vec3Db; + vec3Db = vec3D % vec3Da; + val1 = y1*z2 - y2*z1; + val2 = z1*x2 -z2*x1; + val3 = x1*y2-x2*y1; + ensure("1:operator% failed",(val1 == vec3Db.mdV[VX]) && (val2 == vec3Db.mdV[VY]) && (val3 == vec3Db.mdV[VZ])); + vec3D %= vec3Da; + ensure("2:operator%= failed", + is_approx_equal(vec3D.mdV[VX],vec3Db.mdV[VX]) && + is_approx_equal(vec3D.mdV[VY],vec3Db.mdV[VY]) && + is_approx_equal(vec3D.mdV[VZ],vec3Db.mdV[VZ]) ); + } + + template<> template<> + void v3dmath_object::test<13>() + { + F64 x1 = 1., y1 = 2., z1 = -1.1,div = 4.2; + F64 t = 1.f / div; + LLVector3d vec3D(x1,y1,z1), vec3Da; + vec3Da = vec3D/div; + ensure_approximately_equals( + "1a:operator/ failed", + vec3Da.mdV[VX], + x1*t, + 8); + ensure_approximately_equals( + "1b:operator/ failed", + vec3Da.mdV[VY], + y1*t, + 8); + ensure_approximately_equals( + "1c:operator/ failed", + vec3Da.mdV[VZ], + z1*t, + 8); + x1 = 1.23, y1 = 4., z1 = -2.32; + vec3D.clearVec(); + vec3Da.clearVec(); + vec3D.setVec(x1,y1,z1); + vec3Da = vec3D/div; + ensure_approximately_equals( + "2a:operator/ failed", + vec3Da.mdV[VX], + x1*t, + 8); + ensure_approximately_equals( + "2b:operator/ failed", + vec3Da.mdV[VY], + y1*t, + 8); + ensure_approximately_equals( + "2c:operator/ failed", + vec3Da.mdV[VZ], + z1*t, + 8); + vec3D /= div; + ensure_approximately_equals( + "3a:operator/= failed", + vec3D.mdV[VX], + x1*t, + 8); + ensure_approximately_equals( + "3b:operator/= failed", + vec3D.mdV[VY], + y1*t, + 8); + ensure_approximately_equals( + "3c:operator/= failed", + vec3D.mdV[VZ], + z1*t, + 8); + } + + template<> template<> + void v3dmath_object::test<14>() + { + F64 x1 = 1., y1 = 2., z1 = -1.1; + LLVector3d vec3D(x1,y1,z1), vec3Da; + ensure("1:operator!= failed",(true == (vec3D !=vec3Da))); + vec3Da = vec3D; + ensure("2:operator== failed",(vec3D ==vec3Da)); + vec3D.clearVec(); + vec3Da.clearVec(); + x1 = .211, y1 = 21.111, z1 = 23.22; + vec3D.setVec(x1,y1,z1); + vec3Da.setVec(x1,y1,z1); + ensure("3:operator== failed",(vec3D ==vec3Da)); + ensure("4:operator!= failed",(false == (vec3D !=vec3Da))); + } + + template<> template<> + void v3dmath_object::test<15>() + { + F64 x1 = 1., y1 = 2., z1 = -1.1; + LLVector3d vec3D(x1,y1,z1), vec3Da; + std::ostringstream stream1, stream2; + stream1 << vec3D; + vec3Da.setVec(x1,y1,z1); + stream2 << vec3Da; + ensure("1:operator << failed",(stream1.str() == stream2.str())); + } + + template<> template<> + void v3dmath_object::test<16>() + { + F64 x1 = 1.23, y1 = 2.0, z1 = 4.; + std::string buf("1.23 2. 4"); + LLVector3d vec3D, vec3Da(x1,y1,z1); + LLVector3d::parseVector3d(buf, &vec3D); + ensure_equals("1:parseVector3d: failed " , vec3D, vec3Da); + } + + template<> template<> + void v3dmath_object::test<17>() + { + F64 x1 = 1., y1 = 2., z1 = -1.1; + LLVector3d vec3D(x1,y1,z1), vec3Da; + vec3Da = -vec3D; + ensure("1:operator- failed", (vec3D == - vec3Da)); + } + + template<> template<> + void v3dmath_object::test<18>() + { + F64 x = 1., y = 2., z = -1.1; + LLVector3d vec3D(x,y,z); + F64 res = (x*x + y*y + z*z) - vec3D.magVecSquared(); + ensure("1:magVecSquared:Fail ", ((-F_APPROXIMATELY_ZERO <= res)&& (res <=F_APPROXIMATELY_ZERO))); + res = (F32) sqrt(x*x + y*y + z*z) - vec3D.magVec(); + ensure("2:magVec: Fail ", ((-F_APPROXIMATELY_ZERO <= res)&& (res <=F_APPROXIMATELY_ZERO))); + } + + template<> template<> + void v3dmath_object::test<19>() + { + F64 x = 1., y = 2., z = -1.1; + LLVector3d vec3D(x,y,z); + F64 mag = vec3D.normVec(); + mag = 1.f/ mag; + ensure_approximately_equals( + "1a:normVec: Fail ", + vec3D.mdV[VX], + x * mag, + 8); + ensure_approximately_equals( + "1b:normVec: Fail ", + vec3D.mdV[VY], + y * mag, + 8); + ensure_approximately_equals( + "1c:normVec: Fail ", + vec3D.mdV[VZ], + z * mag, + 8); + x = 0.000000001, y = 0.000000001, z = 0.000000001; + vec3D.clearVec(); + vec3D.setVec(x,y,z); + mag = vec3D.normVec(); + ensure_approximately_equals( + "2a:normVec: Fail ", + vec3D.mdV[VX], + x * mag, + 8); + ensure_approximately_equals( + "2b:normVec: Fail ", + vec3D.mdV[VY], + y * mag, + 8); + ensure_approximately_equals( + "2c:normVec: Fail ", + vec3D.mdV[VZ], + z * mag, + 8); + } + + template<> template<> + void v3dmath_object::test<20>() + { + F64 x1 = 1111.232222; + F64 y1 = 2222222222.22; + F64 z1 = 422222222222.0; + std::string buf("1111.232222 2222222222.22 422222222222"); + LLVector3d vec3Da, vec3Db(x1,y1,z1); + LLVector3d::parseVector3d(buf, &vec3Da); + ensure_equals("1:parseVector3 failed", vec3Da, vec3Db); + } + + template<> template<> + void v3dmath_object::test<21>() + { + F64 x1 = 1., y1 = 2., z1 = -1.1; + F64 x2 = 1.2, y2 = 2.5, z2 = 1.; + F64 val = 2.3f,val1,val2,val3; + val1 = x1 + (x2 - x1)* val; + val2 = y1 + (y2 - y1)* val; + val3 = z1 + (z2 - z1)* val; + LLVector3d vec3Da(x1,y1,z1),vec3Db(x2,y2,z2); + LLVector3d vec3d = lerp(vec3Da,vec3Db,val); + ensure("1:lerp failed", ((val1 ==vec3d.mdV[VX])&& (val2 ==vec3d.mdV[VY]) && (val3 ==vec3d.mdV[VZ]))); + } + + template<> template<> + void v3dmath_object::test<22>() + { + F64 x = 2.32, y = 1.212, z = -.12; + F64 min = 0.0001, max = 3.0; + LLVector3d vec3d(x,y,z); + ensure("1:clamp:Fail ", (true == (vec3d.clamp(min, max)))); + x = 0.000001f, z = 5.3f; + vec3d.setVec(x,y,z); + ensure("2:clamp:Fail ", (true == (vec3d.clamp(min, max)))); + } + + template<> template<> + void v3dmath_object::test<23>() + { + F64 x = 10., y = 20., z = -15.; + F64 epsilon = .23425; + LLVector3d vec3Da(x,y,z), vec3Db(x,y,z); + ensure("1:are_parallel: Fail ", (true == are_parallel(vec3Da,vec3Db,epsilon))); + F64 x1 = -12., y1 = -20., z1 = -100.; + vec3Db.clearVec(); + vec3Db.setVec(x1,y1,z1); + ensure("2:are_parallel: Fail ", (false == are_parallel(vec3Da,vec3Db,epsilon))); + } + + template<> template<> + void v3dmath_object::test<24>() + { +#if LL_WINDOWS && _MSC_VER < 1400 + skip("This fails on VS2003!"); +#else + F64 x = 10., y = 20., z = -15.; + F64 angle1, angle2; + LLVector3d vec3Da(x,y,z), vec3Db(x,y,z); + angle1 = angle_between(vec3Da, vec3Db); + ensure("1:angle_between: Fail ", (0 == angle1)); + F64 x1 = -1., y1 = -20., z1 = -1.; + vec3Da.clearVec(); + vec3Da.setVec(x1,y1,z1); + angle2 = angle_between(vec3Da, vec3Db); + vec3Db.normVec(); + vec3Da.normVec(); + F64 angle = vec3Db*vec3Da; + angle = acos(angle); +#if LL_WINDOWS && _MSC_VER > 1900 + skip("This fails on VS2017!"); +#else + ensure("2:angle_between: Fail ", (angle == angle2)); +#endif + +#endif + } +} diff --git a/indra/llmath/tests/v3math_test.cpp b/indra/llmath/tests/v3math_test.cpp index 93d9f4a006..6d95a9e5b7 100644 --- a/indra/llmath/tests/v3math_test.cpp +++ b/indra/llmath/tests/v3math_test.cpp @@ -1,585 +1,585 @@ -/**
- * @file v3math_test.cpp
- * @author Adroit
- * @date 2007-02
- * @brief v3math test cases.
- *
- * $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 "../test/lltut.h"
-#include "llsd.h"
-
-#include "../v3dmath.h"
-#include "../m3math.h"
-#include "../v4math.h"
-#include "../v3math.h"
-#include "../llquaternion.h"
-#include "../llquantize.h"
-
-
-namespace tut
-{
- struct v3math_data
- {
- };
- typedef test_group<v3math_data> v3math_test;
- typedef v3math_test::object v3math_object;
- tut::v3math_test v3math_testcase("v3math_h");
-
- template<> template<>
- void v3math_object::test<1>()
- {
- LLVector3 vec3;
- ensure("1:LLVector3:Fail to initialize ", ((0.f == vec3.mV[VX]) && (0.f == vec3.mV[VY]) && (0.f == vec3.mV[VZ])));
- F32 x = 2.32f, y = 1.212f, z = -.12f;
- LLVector3 vec3a(x,y,z);
- ensure("2:LLVector3:Fail to initialize ", ((2.32f == vec3a.mV[VX]) && (1.212f == vec3a.mV[VY]) && (-.12f == vec3a.mV[VZ])));
- const F32 vec[3] = {1.2f ,3.2f, -4.2f};
- LLVector3 vec3b(vec);
- ensure("3:LLVector3:Fail to initialize ", ((1.2f == vec3b.mV[VX]) && (3.2f == vec3b.mV[VY]) && (-4.2f == vec3b.mV[VZ])));
- }
-
- template<> template<>
- void v3math_object::test<2>()
- {
- F32 x = 2.32f, y = 1.212f, z = -.12f;
- LLVector3 vec3(x,y,z);
- LLVector3d vector3d(vec3);
- LLVector3 vec3a(vector3d);
- ensure("1:LLVector3:Fail to initialize ", vec3 == vec3a);
- LLVector4 vector4(vec3);
- LLVector3 vec3b(vector4);
- ensure("2:LLVector3:Fail to initialize ", vec3 == vec3b);
- }
-
- template<> template<>
- void v3math_object::test<3>()
- {
- S32 a = 231;
- LLSD llsd(a);
- LLVector3 vec3(llsd);
- LLSD sd = vec3.getValue();
- LLVector3 vec3a(sd);
- ensure("1:LLVector3:Fail to initialize ", (vec3 == vec3a));
- }
-
- template<> template<>
- void v3math_object::test<4>()
- {
- S32 a = 231;
- LLSD llsd(a);
- LLVector3 vec3(llsd),vec3a;
- vec3a = vec3;
- ensure("1:Operator= Fail to initialize " ,(vec3 == vec3a));
- }
-
- template<> template<>
- void v3math_object::test<5>()
- {
- F32 x = 2.32f, y = 1.212f, z = -.12f;
- LLVector3 vec3(x,y,z);
- ensure("1:isFinite= Fail to initialize ", (true == vec3.isFinite()));//need more test cases:
- vec3.clearVec();
- ensure("2:clearVec:Fail to set values ", ((0.f == vec3.mV[VX]) && (0.f == vec3.mV[VY]) && (0.f == vec3.mV[VZ])));
- vec3.setVec(x,y,z);
- ensure("3:setVec:Fail to set values ", ((2.32f == vec3.mV[VX]) && (1.212f == vec3.mV[VY]) && (-.12f == vec3.mV[VZ])));
- vec3.zeroVec();
- ensure("4:zeroVec:Fail to set values ", ((0.f == vec3.mV[VX]) && (0.f == vec3.mV[VY]) && (0.f == vec3.mV[VZ])));
- }
-
- template<> template<>
- void v3math_object::test<6>()
- {
- F32 x = 2.32f, y = 1.212f, z = -.12f;
- LLVector3 vec3(x,y,z),vec3a;
- vec3.abs();
- ensure("1:abs:Fail ", ((x == vec3.mV[VX]) && (y == vec3.mV[VY]) && (-z == vec3.mV[VZ])));
- vec3a.setVec(vec3);
- ensure("2:setVec:Fail to initialize ", (vec3a == vec3));
- const F32 vec[3] = {1.2f ,3.2f, -4.2f};
- vec3.clearVec();
- vec3.setVec(vec);
- ensure("3:setVec:Fail to initialize ", ((1.2f == vec3.mV[VX]) && (3.2f == vec3.mV[VY]) && (-4.2f == vec3.mV[VZ])));
- vec3a.clearVec();
- LLVector3d vector3d(vec3);
- vec3a.setVec(vector3d);
- ensure("4:setVec:Fail to initialize ", (vec3 == vec3a));
- LLVector4 vector4(vec3);
- vec3a.clearVec();
- vec3a.setVec(vector4);
- ensure("5:setVec:Fail to initialize ", (vec3 == vec3a));
- }
-
- template<> template<>
- void v3math_object::test<7>()
- {
- F32 x = 2.32f, y = 3.212f, z = -.12f;
- F32 min = 0.0001f, max = 3.0f;
- LLVector3 vec3(x,y,z);
- ensure("1:clamp:Fail ", true == vec3.clamp(min, max) && x == vec3.mV[VX] && max == vec3.mV[VY] && min == vec3.mV[VZ]);
- x = 1.f, y = 2.2f, z = 2.8f;
- vec3.setVec(x,y,z);
- ensure("2:clamp:Fail ", false == vec3.clamp(min, max));
- }
-
- template<> template<>
- void v3math_object::test<8>()
- {
- F32 x = 2.32f, y = 1.212f, z = -.12f;
- LLVector3 vec3(x,y,z);
- ensure("1:magVecSquared:Fail ", is_approx_equal(vec3.magVecSquared(), (x*x + y*y + z*z)));
- ensure("2:magVec:Fail ", is_approx_equal(vec3.magVec(), (F32) sqrt(x*x + y*y + z*z)));
- }
-
- template<> template<>
- void v3math_object::test<9>()
- {
- F32 x =-2.0f, y = -3.0f, z = 1.23f ;
- LLVector3 vec3(x,y,z);
- ensure("1:abs():Fail ", (true == vec3.abs()));
- ensure("2:isNull():Fail", (false == vec3.isNull())); //Returns true if vector has a _very_small_ length
- x =.00000001f, y = .000001001f, z = .000001001f;
- vec3.setVec(x,y,z);
- ensure("3:isNull(): Fail ", (true == vec3.isNull()));
- }
-
- template<> template<>
- void v3math_object::test<10>()
- {
- F32 x =-2.0f, y = -3.0f, z = 1.f ;
- LLVector3 vec3(x,y,z),vec3a;
- ensure("1:isExactlyZero():Fail ", (true == vec3a.isExactlyZero()));
- vec3a = vec3a.scaleVec(vec3);
- ensure("2:scaleVec: Fail ", vec3a.mV[VX] == 0.f && vec3a.mV[VY] == 0.f && vec3a.mV[VZ] == 0.f);
- vec3a.setVec(x,y,z);
- vec3a = vec3a.scaleVec(vec3);
- ensure("3:scaleVec: Fail ", ((4 == vec3a.mV[VX]) && (9 == vec3a.mV[VY]) &&(1 == vec3a.mV[VZ])));
- ensure("4:isExactlyZero():Fail ", (false == vec3.isExactlyZero()));
- }
-
- template<> template<>
- void v3math_object::test<11>()
- {
- F32 x =20.0f, y = 30.0f, z = 15.f ;
- F32 angle = 100.f;
- LLVector3 vec3(x,y,z),vec3a(1.f,2.f,3.f);
- vec3a = vec3a.rotVec(angle, vec3);
- LLVector3 vec3b(1.f,2.f,3.f);
- vec3b = vec3b.rotVec(angle, vec3);
- ensure_equals("rotVec():Fail" ,vec3b,vec3a);
- }
-
- template<> template<>
- void v3math_object::test<12>()
- {
- F32 x =-2.0f, y = -3.0f, z = 1.f ;
- LLVector3 vec3(x,y,z);
- ensure("1:operator [] failed",( x == vec3[0]));
- ensure("2:operator [] failed",( y == vec3[1]));
- ensure("3:operator [] failed",( z == vec3[2]));
-
- vec3.clearVec();
- x = 23.f, y = -.2361f, z = 3.25;
- vec3.setVec(x,y,z);
- F32 &ref1 = vec3[0];
- ensure("4:operator [] failed",( ref1 == vec3[0]));
- F32 &ref2 = vec3[1];
- ensure("5:operator [] failed",( ref2 == vec3[1]));
- F32 &ref3 = vec3[2];
- ensure("6:operator [] failed",( ref3 == vec3[2]));
- }
-
- template<> template<>
- void v3math_object::test<13>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f;
- F32 val1, val2, val3;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2), vec3b;
- vec3b = vec3 + vec3a ;
- val1 = x1+x2;
- val2 = y1+y2;
- val3 = z1+z2;
- ensure("1:operator+ failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ]));
-
- vec3.clearVec();
- vec3a.clearVec();
- vec3b.clearVec();
- x1 = -.235f, y1 = -24.32f,z1 = 2.13f, x2 = -2.3f, y2 = 1.f, z2 = 34.21f;
- vec3.setVec(x1,y1,z1);
- vec3a.setVec(x2,y2,z2);
- vec3b = vec3 + vec3a;
- val1 = x1+x2;
- val2 = y1+y2;
- val3 = z1+z2;
- ensure("2:operator+ failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<14>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f;
- F32 val1, val2, val3;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2), vec3b;
- vec3b = vec3 - vec3a ;
- val1 = x1-x2;
- val2 = y1-y2;
- val3 = z1-z2;
- ensure("1:operator- failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ]));
-
- vec3.clearVec();
- vec3a.clearVec();
- vec3b.clearVec();
- x1 = -.235f, y1 = -24.32f,z1 = 2.13f, x2 = -2.3f, y2 = 1.f, z2 = 34.21f;
- vec3.setVec(x1,y1,z1);
- vec3a.setVec(x2,y2,z2);
- vec3b = vec3 - vec3a;
- val1 = x1-x2;
- val2 = y1-y2;
- val3 = z1-z2;
- ensure("2:operator- failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<15>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f;
- F32 val1, val2, val3;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2);
- val1 = vec3 * vec3a;
- val2 = x1*x2 + y1*y2 + z1*z2;
- ensure_equals("1:operator* failed",val1,val2);
-
- vec3a.clearVec();
- F32 mulVal = 4.332f;
- vec3a = vec3 * mulVal;
- val1 = x1*mulVal;
- val2 = y1*mulVal;
- val3 = z1*mulVal;
- ensure("2:operator* failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ]));
- vec3a.clearVec();
- vec3a = mulVal * vec3;
- ensure("3:operator* failed ", (val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<16>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f;
- F32 val1, val2, val3;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2), vec3b;
- vec3b = vec3 % vec3a ;
- val1 = y1*z2 - y2*z1;
- val2 = z1*x2 -z2*x1;
- val3 = x1*y2-x2*y1;
- ensure("1:operator% failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ]));
-
- vec3.clearVec();
- vec3a.clearVec();
- vec3b.clearVec();
- x1 =112.f, y1 = 22.3f,z1 = 1.2f, x2 = -2.3f, y2 = 341.11f, z2 = 1234.234f;
- vec3.setVec(x1,y1,z1);
- vec3a.setVec(x2,y2,z2);
- vec3b = vec3 % vec3a ;
- val1 = y1*z2 - y2*z1;
- val2 = z1*x2 -z2*x1;
- val3 = x1*y2-x2*y1;
- ensure("2:operator% failed ", (val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<17>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, div = 3.2f;
- F32 t = 1.f / div, val1, val2, val3;
- LLVector3 vec3(x1,y1,z1), vec3a;
- vec3a = vec3 / div;
- val1 = x1 * t;
- val2 = y1 * t;
- val3 = z1 *t;
- ensure("1:operator/ failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY]) && (val3 == vec3a.mV[VZ]));
-
- vec3a.clearVec();
- x1 = -.235f, y1 = -24.32f, z1 = .342f, div = -2.2f;
- t = 1.f / div;
- vec3.setVec(x1,y1,z1);
- vec3a = vec3 / div;
- val1 = x1 * t;
- val2 = y1 * t;
- val3 = z1 *t;
- ensure("2:operator/ failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY]) && (val3 == vec3a.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<18>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f;
- LLVector3 vec3(x1,y1,z1), vec3a(x1,y1,z1);
- ensure("1:operator== failed",(vec3 == vec3a));
-
- vec3a.clearVec();
- x1 = -.235f, y1 = -24.32f, z1 = .342f;
- vec3.clearVec();
- vec3a.clearVec();
- vec3.setVec(x1,y1,z1);
- vec3a.setVec(x1,y1,z1);
- ensure("2:operator== failed ", (vec3 == vec3a));
- }
-
- template<> template<>
- void v3math_object::test<19>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 =112.f, y2 = 2.234f,z2 = 11.2f;;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2);
- ensure("1:operator!= failed",(vec3a != vec3));
-
- vec3.clearVec();
- vec3.clearVec();
- vec3a.setVec(vec3);
- ensure("2:operator!= failed", ( false == (vec3a != vec3)));
- }
-
- template<> template<>
- void v3math_object::test<20>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 =112.f, y2 = 2.2f,z2 = 11.2f;;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2);
- vec3a += vec3;
- F32 val1, val2, val3;
- val1 = x1+x2;
- val2 = y1+y2;
- val3 = z1+z2;
- ensure("1:operator+= failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<21>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 =112.f, y2 = 2.2f,z2 = 11.2f;;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2);
- vec3a -= vec3;
- F32 val1, val2, val3;
- val1 = x2-x1;
- val2 = y2-y1;
- val3 = z2-z1;
- ensure("1:operator-= failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<22>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f;
- F32 val1,val2,val3;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2);
- vec3a *= vec3;
- val1 = x1*x2;
- val2 = y1*y2;
- val3 = z1*z2;
- ensure("1:operator*= failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ]));
-
- F32 mulVal = 4.332f;
- vec3 *=mulVal;
- val1 = x1*mulVal;
- val2 = y1*mulVal;
- val3 = z1*mulVal;
- ensure("2:operator*= failed ", is_approx_equal(val1, vec3.mV[VX]) && is_approx_equal(val2, vec3.mV[VY]) && is_approx_equal(val3, vec3.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<23>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2),vec3b;
- vec3b = vec3a % vec3;
- vec3a %= vec3;
- ensure_equals("1:operator%= failed",vec3a,vec3b);
- }
-
- template<> template<>
- void v3math_object::test<24>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, div = 3.2f;
- F32 t = 1.f / div, val1, val2, val3;
- LLVector3 vec3a(x1,y1,z1);
- vec3a /= div;
- val1 = x1 * t;
- val2 = y1 * t;
- val3 = z1 *t;
- ensure("1:operator/= failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY]) && (val3 == vec3a.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<25>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f;
- LLVector3 vec3(x1,y1,z1), vec3a;
- vec3a = -vec3;
- ensure("1:operator- failed",(-vec3a == vec3));
- }
-
- template<> template<>
- void v3math_object::test<26>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 1.2f;
- std::ostringstream stream1, stream2;
- LLVector3 vec3(x1,y1,z1), vec3a;
- stream1 << vec3;
- vec3a.setVec(x1,y1,z1);
- stream2 << vec3a;
- ensure("1:operator << failed",(stream1.str() == stream2.str()));
- }
-
- template<> template<>
- void v3math_object::test<27>()
- {
- F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 1.11f, z2 = 1234.234f;
- LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2);
- ensure("1:operator< failed", (true == (vec3 < vec3a)));
- x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 2.f, z2 = 1234.234f;
- vec3.setVec(x1,y1,z1);
- vec3a.setVec(x2,y2,z2);
- ensure("2:operator< failed ", (true == (vec3 < vec3a)));
- x1 =2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f,
- vec3.setVec(x1,y1,z1);
- vec3a.setVec(x2,y2,z2);
- ensure("3:operator< failed ", (false == (vec3 < vec3a)));
- }
-
- template<> template<>
- void v3math_object::test<28>()
- {
- F32 x1 =1.23f, y1 = 2.f,z1 = 4.f;
- std::string buf("1.23 2. 4");
- LLVector3 vec3, vec3a(x1,y1,z1);
- LLVector3::parseVector3(buf, &vec3);
- ensure_equals("1:parseVector3 failed", vec3, vec3a);
- }
-
- template<> template<>
- void v3math_object::test<29>()
- {
- F32 x1 =1.f, y1 = 2.f,z1 = 4.f;
- LLVector3 vec3(x1,y1,z1),vec3a,vec3b;
- vec3a.setVec(1,1,1);
- vec3a.scaleVec(vec3);
- ensure_equals("1:scaleVec failed", vec3, vec3a);
- vec3a.clearVec();
- vec3a.setVec(x1,y1,z1);
- vec3a.scaleVec(vec3);
- ensure("2:scaleVec failed", ((1.f ==vec3a.mV[VX])&& (4.f ==vec3a.mV[VY]) && (16.f ==vec3a.mV[VZ])));
- }
-
- template<> template<>
- void v3math_object::test<30>()
- {
- F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 1.11f, z2 = 1234.234f;
- F32 val = 2.3f,val1,val2,val3;
- val1 = x1 + (x2 - x1)* val;
- val2 = y1 + (y2 - y1)* val;
- val3 = z1 + (z2 - z1)* val;
- LLVector3 vec3(x1,y1,z1),vec3a(x2,y2,z2);
- LLVector3 vec3b = lerp(vec3,vec3a,val);
- ensure("1:lerp failed", ((val1 ==vec3b.mV[VX])&& (val2 ==vec3b.mV[VY]) && (val3 ==vec3b.mV[VZ])));
- }
-
- template<> template<>
- void v3math_object::test<31>()
- {
- F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 1.f, z2 = 1.f;
- F32 val1,val2;
- LLVector3 vec3(x1,y1,z1),vec3a(x2,y2,z2);
- val1 = dist_vec(vec3,vec3a);
- val2 = (F32) sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
- ensure_equals("1:dist_vec: Fail ",val2, val1);
- val1 = dist_vec_squared(vec3,vec3a);
- val2 =((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
- ensure_equals("2:dist_vec_squared: Fail ",val2, val1);
- val1 = dist_vec_squared2D(vec3, vec3a);
- val2 =(x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2);
- ensure_equals("3:dist_vec_squared2D: Fail ",val2, val1);
- }
-
- template<> template<>
- void v3math_object::test<32>()
- {
- F32 x =12.3524f, y = -342.f,z = 4.126341f;
- LLVector3 vec3(x,y,z);
- F32 mag = vec3.normVec();
- mag = 1.f/ mag;
- F32 val1 = x* mag, val2 = y* mag, val3 = z* mag;
- ensure("1:normVec: Fail ", is_approx_equal(val1, vec3.mV[VX]) && is_approx_equal(val2, vec3.mV[VY]) && is_approx_equal(val3, vec3.mV[VZ]));
- x = 0.000000001f, y = 0.f, z = 0.f;
- vec3.clearVec();
- vec3.setVec(x,y,z);
- mag = vec3.normVec();
- val1 = x* mag, val2 = y* mag, val3 = z* mag;
- ensure("2:normVec: Fail ", (mag == 0.) && (0. == vec3.mV[VX]) && (0. == vec3.mV[VY])&& (0. == vec3.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<33>()
- {
- F32 x = -202.23412f, y = 123.2312f, z = -89.f;
- LLVector3 vec(x,y,z);
- vec.snap(2);
- ensure("1:snap: Fail ", is_approx_equal(-202.23f, vec.mV[VX]) && is_approx_equal(123.23f, vec.mV[VY]) && is_approx_equal(-89.f, vec.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<34>()
- {
- F32 x = 10.f, y = 20.f, z = -15.f;
- F32 x1, y1, z1;
- F32 lowerxy = 0.f, upperxy = 1.0f, lowerz = -1.0f, upperz = 1.f;
- LLVector3 vec3(x,y,z);
- vec3.quantize16(lowerxy,upperxy,lowerz,upperz);
- x1 = U16_to_F32(F32_to_U16(x, lowerxy, upperxy), lowerxy, upperxy);
- y1 = U16_to_F32(F32_to_U16(y, lowerxy, upperxy), lowerxy, upperxy);
- z1 = U16_to_F32(F32_to_U16(z, lowerz, upperz), lowerz, upperz);
- ensure("1:quantize16: Fail ", is_approx_equal(x1, vec3.mV[VX]) && is_approx_equal(y1, vec3.mV[VY]) && is_approx_equal(z1, vec3.mV[VZ]));
- LLVector3 vec3a(x,y,z);
- vec3a.quantize8(lowerxy,upperxy,lowerz,upperz);
- x1 = U8_to_F32(F32_to_U8(x, lowerxy, upperxy), lowerxy, upperxy);
- y1 = U8_to_F32(F32_to_U8(y, lowerxy, upperxy), lowerxy, upperxy);
- z1 = U8_to_F32(F32_to_U8(z, lowerz, upperz), lowerz, upperz);
- ensure("2:quantize8: Fail ", is_approx_equal(x1, vec3a.mV[VX]) && is_approx_equal(y1, vec3a.mV[VY]) && is_approx_equal(z1, vec3a.mV[VZ]));
- }
-
- template<> template<>
- void v3math_object::test<35>()
- {
- LLSD sd = LLSD::emptyArray();
- sd[0] = 1.f;
-
- LLVector3 parsed_1(sd);
- ensure("1:LLSD parse: Fail ", is_approx_equal(parsed_1.mV[VX], 1.f) && is_approx_equal(parsed_1.mV[VY], 0.f) && is_approx_equal(parsed_1.mV[VZ], 0.f));
-
- sd[1] = 2.f;
- LLVector3 parsed_2(sd);
- ensure("2:LLSD parse: Fail ", is_approx_equal(parsed_2.mV[VX], 1.f) && is_approx_equal(parsed_2.mV[VY], 2.f) && is_approx_equal(parsed_2.mV[VZ], 0.f));
-
- sd[2] = 3.f;
- LLVector3 parsed_3(sd);
- ensure("3:LLSD parse: Fail ", is_approx_equal(parsed_3.mV[VX], 1.f) && is_approx_equal(parsed_3.mV[VY], 2.f) && is_approx_equal(parsed_3.mV[VZ], 3.f));
- }
-}
+/** + * @file v3math_test.cpp + * @author Adroit + * @date 2007-02 + * @brief v3math test cases. + * + * $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 "../test/lltut.h" +#include "llsd.h" + +#include "../v3dmath.h" +#include "../m3math.h" +#include "../v4math.h" +#include "../v3math.h" +#include "../llquaternion.h" +#include "../llquantize.h" + + +namespace tut +{ + struct v3math_data + { + }; + typedef test_group<v3math_data> v3math_test; + typedef v3math_test::object v3math_object; + tut::v3math_test v3math_testcase("v3math_h"); + + template<> template<> + void v3math_object::test<1>() + { + LLVector3 vec3; + ensure("1:LLVector3:Fail to initialize ", ((0.f == vec3.mV[VX]) && (0.f == vec3.mV[VY]) && (0.f == vec3.mV[VZ]))); + F32 x = 2.32f, y = 1.212f, z = -.12f; + LLVector3 vec3a(x,y,z); + ensure("2:LLVector3:Fail to initialize ", ((2.32f == vec3a.mV[VX]) && (1.212f == vec3a.mV[VY]) && (-.12f == vec3a.mV[VZ]))); + const F32 vec[3] = {1.2f ,3.2f, -4.2f}; + LLVector3 vec3b(vec); + ensure("3:LLVector3:Fail to initialize ", ((1.2f == vec3b.mV[VX]) && (3.2f == vec3b.mV[VY]) && (-4.2f == vec3b.mV[VZ]))); + } + + template<> template<> + void v3math_object::test<2>() + { + F32 x = 2.32f, y = 1.212f, z = -.12f; + LLVector3 vec3(x,y,z); + LLVector3d vector3d(vec3); + LLVector3 vec3a(vector3d); + ensure("1:LLVector3:Fail to initialize ", vec3 == vec3a); + LLVector4 vector4(vec3); + LLVector3 vec3b(vector4); + ensure("2:LLVector3:Fail to initialize ", vec3 == vec3b); + } + + template<> template<> + void v3math_object::test<3>() + { + S32 a = 231; + LLSD llsd(a); + LLVector3 vec3(llsd); + LLSD sd = vec3.getValue(); + LLVector3 vec3a(sd); + ensure("1:LLVector3:Fail to initialize ", (vec3 == vec3a)); + } + + template<> template<> + void v3math_object::test<4>() + { + S32 a = 231; + LLSD llsd(a); + LLVector3 vec3(llsd),vec3a; + vec3a = vec3; + ensure("1:Operator= Fail to initialize " ,(vec3 == vec3a)); + } + + template<> template<> + void v3math_object::test<5>() + { + F32 x = 2.32f, y = 1.212f, z = -.12f; + LLVector3 vec3(x,y,z); + ensure("1:isFinite= Fail to initialize ", (true == vec3.isFinite()));//need more test cases: + vec3.clearVec(); + ensure("2:clearVec:Fail to set values ", ((0.f == vec3.mV[VX]) && (0.f == vec3.mV[VY]) && (0.f == vec3.mV[VZ]))); + vec3.setVec(x,y,z); + ensure("3:setVec:Fail to set values ", ((2.32f == vec3.mV[VX]) && (1.212f == vec3.mV[VY]) && (-.12f == vec3.mV[VZ]))); + vec3.zeroVec(); + ensure("4:zeroVec:Fail to set values ", ((0.f == vec3.mV[VX]) && (0.f == vec3.mV[VY]) && (0.f == vec3.mV[VZ]))); + } + + template<> template<> + void v3math_object::test<6>() + { + F32 x = 2.32f, y = 1.212f, z = -.12f; + LLVector3 vec3(x,y,z),vec3a; + vec3.abs(); + ensure("1:abs:Fail ", ((x == vec3.mV[VX]) && (y == vec3.mV[VY]) && (-z == vec3.mV[VZ]))); + vec3a.setVec(vec3); + ensure("2:setVec:Fail to initialize ", (vec3a == vec3)); + const F32 vec[3] = {1.2f ,3.2f, -4.2f}; + vec3.clearVec(); + vec3.setVec(vec); + ensure("3:setVec:Fail to initialize ", ((1.2f == vec3.mV[VX]) && (3.2f == vec3.mV[VY]) && (-4.2f == vec3.mV[VZ]))); + vec3a.clearVec(); + LLVector3d vector3d(vec3); + vec3a.setVec(vector3d); + ensure("4:setVec:Fail to initialize ", (vec3 == vec3a)); + LLVector4 vector4(vec3); + vec3a.clearVec(); + vec3a.setVec(vector4); + ensure("5:setVec:Fail to initialize ", (vec3 == vec3a)); + } + + template<> template<> + void v3math_object::test<7>() + { + F32 x = 2.32f, y = 3.212f, z = -.12f; + F32 min = 0.0001f, max = 3.0f; + LLVector3 vec3(x,y,z); + ensure("1:clamp:Fail ", true == vec3.clamp(min, max) && x == vec3.mV[VX] && max == vec3.mV[VY] && min == vec3.mV[VZ]); + x = 1.f, y = 2.2f, z = 2.8f; + vec3.setVec(x,y,z); + ensure("2:clamp:Fail ", false == vec3.clamp(min, max)); + } + + template<> template<> + void v3math_object::test<8>() + { + F32 x = 2.32f, y = 1.212f, z = -.12f; + LLVector3 vec3(x,y,z); + ensure("1:magVecSquared:Fail ", is_approx_equal(vec3.magVecSquared(), (x*x + y*y + z*z))); + ensure("2:magVec:Fail ", is_approx_equal(vec3.magVec(), (F32) sqrt(x*x + y*y + z*z))); + } + + template<> template<> + void v3math_object::test<9>() + { + F32 x =-2.0f, y = -3.0f, z = 1.23f ; + LLVector3 vec3(x,y,z); + ensure("1:abs():Fail ", (true == vec3.abs())); + ensure("2:isNull():Fail", (false == vec3.isNull())); //Returns true if vector has a _very_small_ length + x =.00000001f, y = .000001001f, z = .000001001f; + vec3.setVec(x,y,z); + ensure("3:isNull(): Fail ", (true == vec3.isNull())); + } + + template<> template<> + void v3math_object::test<10>() + { + F32 x =-2.0f, y = -3.0f, z = 1.f ; + LLVector3 vec3(x,y,z),vec3a; + ensure("1:isExactlyZero():Fail ", (true == vec3a.isExactlyZero())); + vec3a = vec3a.scaleVec(vec3); + ensure("2:scaleVec: Fail ", vec3a.mV[VX] == 0.f && vec3a.mV[VY] == 0.f && vec3a.mV[VZ] == 0.f); + vec3a.setVec(x,y,z); + vec3a = vec3a.scaleVec(vec3); + ensure("3:scaleVec: Fail ", ((4 == vec3a.mV[VX]) && (9 == vec3a.mV[VY]) &&(1 == vec3a.mV[VZ]))); + ensure("4:isExactlyZero():Fail ", (false == vec3.isExactlyZero())); + } + + template<> template<> + void v3math_object::test<11>() + { + F32 x =20.0f, y = 30.0f, z = 15.f ; + F32 angle = 100.f; + LLVector3 vec3(x,y,z),vec3a(1.f,2.f,3.f); + vec3a = vec3a.rotVec(angle, vec3); + LLVector3 vec3b(1.f,2.f,3.f); + vec3b = vec3b.rotVec(angle, vec3); + ensure_equals("rotVec():Fail" ,vec3b,vec3a); + } + + template<> template<> + void v3math_object::test<12>() + { + F32 x =-2.0f, y = -3.0f, z = 1.f ; + LLVector3 vec3(x,y,z); + ensure("1:operator [] failed",( x == vec3[0])); + ensure("2:operator [] failed",( y == vec3[1])); + ensure("3:operator [] failed",( z == vec3[2])); + + vec3.clearVec(); + x = 23.f, y = -.2361f, z = 3.25; + vec3.setVec(x,y,z); + F32 &ref1 = vec3[0]; + ensure("4:operator [] failed",( ref1 == vec3[0])); + F32 &ref2 = vec3[1]; + ensure("5:operator [] failed",( ref2 == vec3[1])); + F32 &ref3 = vec3[2]; + ensure("6:operator [] failed",( ref3 == vec3[2])); + } + + template<> template<> + void v3math_object::test<13>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f; + F32 val1, val2, val3; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2), vec3b; + vec3b = vec3 + vec3a ; + val1 = x1+x2; + val2 = y1+y2; + val3 = z1+z2; + ensure("1:operator+ failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ])); + + vec3.clearVec(); + vec3a.clearVec(); + vec3b.clearVec(); + x1 = -.235f, y1 = -24.32f,z1 = 2.13f, x2 = -2.3f, y2 = 1.f, z2 = 34.21f; + vec3.setVec(x1,y1,z1); + vec3a.setVec(x2,y2,z2); + vec3b = vec3 + vec3a; + val1 = x1+x2; + val2 = y1+y2; + val3 = z1+z2; + ensure("2:operator+ failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ])); + } + + template<> template<> + void v3math_object::test<14>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f; + F32 val1, val2, val3; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2), vec3b; + vec3b = vec3 - vec3a ; + val1 = x1-x2; + val2 = y1-y2; + val3 = z1-z2; + ensure("1:operator- failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ])); + + vec3.clearVec(); + vec3a.clearVec(); + vec3b.clearVec(); + x1 = -.235f, y1 = -24.32f,z1 = 2.13f, x2 = -2.3f, y2 = 1.f, z2 = 34.21f; + vec3.setVec(x1,y1,z1); + vec3a.setVec(x2,y2,z2); + vec3b = vec3 - vec3a; + val1 = x1-x2; + val2 = y1-y2; + val3 = z1-z2; + ensure("2:operator- failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ])); + } + + template<> template<> + void v3math_object::test<15>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f; + F32 val1, val2, val3; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2); + val1 = vec3 * vec3a; + val2 = x1*x2 + y1*y2 + z1*z2; + ensure_equals("1:operator* failed",val1,val2); + + vec3a.clearVec(); + F32 mulVal = 4.332f; + vec3a = vec3 * mulVal; + val1 = x1*mulVal; + val2 = y1*mulVal; + val3 = z1*mulVal; + ensure("2:operator* failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ])); + vec3a.clearVec(); + vec3a = mulVal * vec3; + ensure("3:operator* failed ", (val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ])); + } + + template<> template<> + void v3math_object::test<16>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f; + F32 val1, val2, val3; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2), vec3b; + vec3b = vec3 % vec3a ; + val1 = y1*z2 - y2*z1; + val2 = z1*x2 -z2*x1; + val3 = x1*y2-x2*y1; + ensure("1:operator% failed",(val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ])); + + vec3.clearVec(); + vec3a.clearVec(); + vec3b.clearVec(); + x1 =112.f, y1 = 22.3f,z1 = 1.2f, x2 = -2.3f, y2 = 341.11f, z2 = 1234.234f; + vec3.setVec(x1,y1,z1); + vec3a.setVec(x2,y2,z2); + vec3b = vec3 % vec3a ; + val1 = y1*z2 - y2*z1; + val2 = z1*x2 -z2*x1; + val3 = x1*y2-x2*y1; + ensure("2:operator% failed ", (val1 == vec3b.mV[VX]) && (val2 == vec3b.mV[VY]) && (val3 == vec3b.mV[VZ])); + } + + template<> template<> + void v3math_object::test<17>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, div = 3.2f; + F32 t = 1.f / div, val1, val2, val3; + LLVector3 vec3(x1,y1,z1), vec3a; + vec3a = vec3 / div; + val1 = x1 * t; + val2 = y1 * t; + val3 = z1 *t; + ensure("1:operator/ failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY]) && (val3 == vec3a.mV[VZ])); + + vec3a.clearVec(); + x1 = -.235f, y1 = -24.32f, z1 = .342f, div = -2.2f; + t = 1.f / div; + vec3.setVec(x1,y1,z1); + vec3a = vec3 / div; + val1 = x1 * t; + val2 = y1 * t; + val3 = z1 *t; + ensure("2:operator/ failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY]) && (val3 == vec3a.mV[VZ])); + } + + template<> template<> + void v3math_object::test<18>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f; + LLVector3 vec3(x1,y1,z1), vec3a(x1,y1,z1); + ensure("1:operator== failed",(vec3 == vec3a)); + + vec3a.clearVec(); + x1 = -.235f, y1 = -24.32f, z1 = .342f; + vec3.clearVec(); + vec3a.clearVec(); + vec3.setVec(x1,y1,z1); + vec3a.setVec(x1,y1,z1); + ensure("2:operator== failed ", (vec3 == vec3a)); + } + + template<> template<> + void v3math_object::test<19>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 =112.f, y2 = 2.234f,z2 = 11.2f;; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2); + ensure("1:operator!= failed",(vec3a != vec3)); + + vec3.clearVec(); + vec3.clearVec(); + vec3a.setVec(vec3); + ensure("2:operator!= failed", ( false == (vec3a != vec3))); + } + + template<> template<> + void v3math_object::test<20>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 =112.f, y2 = 2.2f,z2 = 11.2f;; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2); + vec3a += vec3; + F32 val1, val2, val3; + val1 = x1+x2; + val2 = y1+y2; + val3 = z1+z2; + ensure("1:operator+= failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ])); + } + + template<> template<> + void v3math_object::test<21>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 =112.f, y2 = 2.2f,z2 = 11.2f;; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2); + vec3a -= vec3; + F32 val1, val2, val3; + val1 = x2-x1; + val2 = y2-y1; + val3 = z2-z1; + ensure("1:operator-= failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ])); + } + + template<> template<> + void v3math_object::test<22>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f; + F32 val1,val2,val3; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2); + vec3a *= vec3; + val1 = x1*x2; + val2 = y1*y2; + val3 = z1*z2; + ensure("1:operator*= failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY])&& (val3 == vec3a.mV[VZ])); + + F32 mulVal = 4.332f; + vec3 *=mulVal; + val1 = x1*mulVal; + val2 = y1*mulVal; + val3 = z1*mulVal; + ensure("2:operator*= failed ", is_approx_equal(val1, vec3.mV[VX]) && is_approx_equal(val2, vec3.mV[VY]) && is_approx_equal(val3, vec3.mV[VZ])); + } + + template<> template<> + void v3math_object::test<23>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, x2 = -2.3f, y2 = 1.11f, z2 = 1234.234f; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2),vec3b; + vec3b = vec3a % vec3; + vec3a %= vec3; + ensure_equals("1:operator%= failed",vec3a,vec3b); + } + + template<> template<> + void v3math_object::test<24>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f, div = 3.2f; + F32 t = 1.f / div, val1, val2, val3; + LLVector3 vec3a(x1,y1,z1); + vec3a /= div; + val1 = x1 * t; + val2 = y1 * t; + val3 = z1 *t; + ensure("1:operator/= failed",(val1 == vec3a.mV[VX]) && (val2 == vec3a.mV[VY]) && (val3 == vec3a.mV[VZ])); + } + + template<> template<> + void v3math_object::test<25>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f; + LLVector3 vec3(x1,y1,z1), vec3a; + vec3a = -vec3; + ensure("1:operator- failed",(-vec3a == vec3)); + } + + template<> template<> + void v3math_object::test<26>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 1.2f; + std::ostringstream stream1, stream2; + LLVector3 vec3(x1,y1,z1), vec3a; + stream1 << vec3; + vec3a.setVec(x1,y1,z1); + stream2 << vec3a; + ensure("1:operator << failed",(stream1.str() == stream2.str())); + } + + template<> template<> + void v3math_object::test<27>() + { + F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 1.11f, z2 = 1234.234f; + LLVector3 vec3(x1,y1,z1), vec3a(x2,y2,z2); + ensure("1:operator< failed", (true == (vec3 < vec3a))); + x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 2.f, z2 = 1234.234f; + vec3.setVec(x1,y1,z1); + vec3a.setVec(x2,y2,z2); + ensure("2:operator< failed ", (true == (vec3 < vec3a))); + x1 =2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, + vec3.setVec(x1,y1,z1); + vec3a.setVec(x2,y2,z2); + ensure("3:operator< failed ", (false == (vec3 < vec3a))); + } + + template<> template<> + void v3math_object::test<28>() + { + F32 x1 =1.23f, y1 = 2.f,z1 = 4.f; + std::string buf("1.23 2. 4"); + LLVector3 vec3, vec3a(x1,y1,z1); + LLVector3::parseVector3(buf, &vec3); + ensure_equals("1:parseVector3 failed", vec3, vec3a); + } + + template<> template<> + void v3math_object::test<29>() + { + F32 x1 =1.f, y1 = 2.f,z1 = 4.f; + LLVector3 vec3(x1,y1,z1),vec3a,vec3b; + vec3a.setVec(1,1,1); + vec3a.scaleVec(vec3); + ensure_equals("1:scaleVec failed", vec3, vec3a); + vec3a.clearVec(); + vec3a.setVec(x1,y1,z1); + vec3a.scaleVec(vec3); + ensure("2:scaleVec failed", ((1.f ==vec3a.mV[VX])&& (4.f ==vec3a.mV[VY]) && (16.f ==vec3a.mV[VZ]))); + } + + template<> template<> + void v3math_object::test<30>() + { + F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 1.11f, z2 = 1234.234f; + F32 val = 2.3f,val1,val2,val3; + val1 = x1 + (x2 - x1)* val; + val2 = y1 + (y2 - y1)* val; + val3 = z1 + (z2 - z1)* val; + LLVector3 vec3(x1,y1,z1),vec3a(x2,y2,z2); + LLVector3 vec3b = lerp(vec3,vec3a,val); + ensure("1:lerp failed", ((val1 ==vec3b.mV[VX])&& (val2 ==vec3b.mV[VY]) && (val3 ==vec3b.mV[VZ]))); + } + + template<> template<> + void v3math_object::test<31>() + { + F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 1.f, z2 = 1.f; + F32 val1,val2; + LLVector3 vec3(x1,y1,z1),vec3a(x2,y2,z2); + val1 = dist_vec(vec3,vec3a); + val2 = (F32) sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2)); + ensure_equals("1:dist_vec: Fail ",val2, val1); + val1 = dist_vec_squared(vec3,vec3a); + val2 =((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2)); + ensure_equals("2:dist_vec_squared: Fail ",val2, val1); + val1 = dist_vec_squared2D(vec3, vec3a); + val2 =(x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2); + ensure_equals("3:dist_vec_squared2D: Fail ",val2, val1); + } + + template<> template<> + void v3math_object::test<32>() + { + F32 x =12.3524f, y = -342.f,z = 4.126341f; + LLVector3 vec3(x,y,z); + F32 mag = vec3.normVec(); + mag = 1.f/ mag; + F32 val1 = x* mag, val2 = y* mag, val3 = z* mag; + ensure("1:normVec: Fail ", is_approx_equal(val1, vec3.mV[VX]) && is_approx_equal(val2, vec3.mV[VY]) && is_approx_equal(val3, vec3.mV[VZ])); + x = 0.000000001f, y = 0.f, z = 0.f; + vec3.clearVec(); + vec3.setVec(x,y,z); + mag = vec3.normVec(); + val1 = x* mag, val2 = y* mag, val3 = z* mag; + ensure("2:normVec: Fail ", (mag == 0.) && (0. == vec3.mV[VX]) && (0. == vec3.mV[VY])&& (0. == vec3.mV[VZ])); + } + + template<> template<> + void v3math_object::test<33>() + { + F32 x = -202.23412f, y = 123.2312f, z = -89.f; + LLVector3 vec(x,y,z); + vec.snap(2); + ensure("1:snap: Fail ", is_approx_equal(-202.23f, vec.mV[VX]) && is_approx_equal(123.23f, vec.mV[VY]) && is_approx_equal(-89.f, vec.mV[VZ])); + } + + template<> template<> + void v3math_object::test<34>() + { + F32 x = 10.f, y = 20.f, z = -15.f; + F32 x1, y1, z1; + F32 lowerxy = 0.f, upperxy = 1.0f, lowerz = -1.0f, upperz = 1.f; + LLVector3 vec3(x,y,z); + vec3.quantize16(lowerxy,upperxy,lowerz,upperz); + x1 = U16_to_F32(F32_to_U16(x, lowerxy, upperxy), lowerxy, upperxy); + y1 = U16_to_F32(F32_to_U16(y, lowerxy, upperxy), lowerxy, upperxy); + z1 = U16_to_F32(F32_to_U16(z, lowerz, upperz), lowerz, upperz); + ensure("1:quantize16: Fail ", is_approx_equal(x1, vec3.mV[VX]) && is_approx_equal(y1, vec3.mV[VY]) && is_approx_equal(z1, vec3.mV[VZ])); + LLVector3 vec3a(x,y,z); + vec3a.quantize8(lowerxy,upperxy,lowerz,upperz); + x1 = U8_to_F32(F32_to_U8(x, lowerxy, upperxy), lowerxy, upperxy); + y1 = U8_to_F32(F32_to_U8(y, lowerxy, upperxy), lowerxy, upperxy); + z1 = U8_to_F32(F32_to_U8(z, lowerz, upperz), lowerz, upperz); + ensure("2:quantize8: Fail ", is_approx_equal(x1, vec3a.mV[VX]) && is_approx_equal(y1, vec3a.mV[VY]) && is_approx_equal(z1, vec3a.mV[VZ])); + } + + template<> template<> + void v3math_object::test<35>() + { + LLSD sd = LLSD::emptyArray(); + sd[0] = 1.f; + + LLVector3 parsed_1(sd); + ensure("1:LLSD parse: Fail ", is_approx_equal(parsed_1.mV[VX], 1.f) && is_approx_equal(parsed_1.mV[VY], 0.f) && is_approx_equal(parsed_1.mV[VZ], 0.f)); + + sd[1] = 2.f; + LLVector3 parsed_2(sd); + ensure("2:LLSD parse: Fail ", is_approx_equal(parsed_2.mV[VX], 1.f) && is_approx_equal(parsed_2.mV[VY], 2.f) && is_approx_equal(parsed_2.mV[VZ], 0.f)); + + sd[2] = 3.f; + LLVector3 parsed_3(sd); + ensure("3:LLSD parse: Fail ", is_approx_equal(parsed_3.mV[VX], 1.f) && is_approx_equal(parsed_3.mV[VY], 2.f) && is_approx_equal(parsed_3.mV[VZ], 3.f)); + } +} diff --git a/indra/llmath/tests/v4coloru_test.cpp b/indra/llmath/tests/v4coloru_test.cpp index b9968d0cd0..55cef0fea1 100644 --- a/indra/llmath/tests/v4coloru_test.cpp +++ b/indra/llmath/tests/v4coloru_test.cpp @@ -1,336 +1,336 @@ -/**
- * @file v4coloru_test.cpp
- * @author Adroit
- * @date 2007-03
- * @brief v4coloru test cases.
- *
- * $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 "../test/lltut.h"
-
-#include "llsd.h"
-
-#include "../v4coloru.h"
-
-
-namespace tut
-{
- struct v4coloru_data
- {
- };
- typedef test_group<v4coloru_data> v4coloru_test;
- typedef v4coloru_test::object v4coloru_object;
- tut::v4coloru_test v4coloru_testcase("v4coloru_h");
-
- template<> template<>
- void v4coloru_object::test<1>()
- {
- LLColor4U llcolor4u;
- ensure("1:LLColor4u:Fail to initialize ", ((0 == llcolor4u.mV[VX]) && (0 == llcolor4u.mV[VY]) && (0 == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW])));
-
- U8 r = 0x12, g = 0xFF, b = 0xAF, a = 0x23;
- LLColor4U llcolor4u1(r,g,b);
- ensure("2:LLColor4u:Fail to initialize ", ((r == llcolor4u1.mV[VX]) && (g == llcolor4u1.mV[VY]) && (b == llcolor4u1.mV[VZ])&& (255 == llcolor4u1.mV[VW])));
-
- LLColor4U llcolor4u2(r,g,b,a);
- ensure("3:LLColor4u:Fail to initialize ", ((r == llcolor4u2.mV[VX]) && (g == llcolor4u2.mV[VY]) && (b == llcolor4u2.mV[VZ])&& (a == llcolor4u2.mV[VW])));
-
- const U8 vec[4] = {0x12,0xFF,0xAF,0x23};
- LLColor4U llcolor4u3(vec);
- ensure("4:LLColor4u:Fail to initialize ", ((vec[0] == llcolor4u3.mV[VX]) && (vec[1] == llcolor4u3.mV[VY]) && (vec[2] == llcolor4u3.mV[VZ])&& (vec[3] == llcolor4u3.mV[VW])));
-
- LLSD sd = llcolor4u3.getValue();
- LLColor4U llcolor4u4(sd);
- ensure_equals("5:LLColor4u (LLSD) Failed ", llcolor4u4, llcolor4u3);
- }
-
- template<> template<>
- void v4coloru_object::test<2>()
- {
- LLColor4U llcolor4ua(1, 2, 3, 4);
- LLSD sd = llcolor4ua.getValue();
- LLColor4U llcolor4u;
- llcolor4u.setValue(sd);
- ensure_equals("setValue(LLSD)/getValue Failed ", llcolor4u, llcolor4ua);
- }
-
- template<> template<>
- void v4coloru_object::test<3>()
- {
- U8 r = 0x12, g = 0xFF, b = 0xAF, a = 0x23;
- LLColor4U llcolor4u(r,g,b,a);
- llcolor4u.setToBlack();
- ensure("setToBlack:Fail to set black ", ((0 == llcolor4u.mV[VX]) && (0 == llcolor4u.mV[VY]) && (0 == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW])));
-
- llcolor4u.setToWhite();
- ensure("setToWhite:Fail to white ", ((255 == llcolor4u.mV[VX]) && (255 == llcolor4u.mV[VY]) && (255 == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW])));
- }
-
- template<> template<>
- void v4coloru_object::test<4>()
- {
- U8 r = 0x12, g = 0xFF, b = 0xAF, a = 0x23;
- LLColor4U llcolor4ua(r,g,b,a);
- LLSD sd = llcolor4ua.getValue();
- LLColor4U llcolor4u = (LLColor4U)sd;
- ensure_equals("Operator=(LLSD) Failed ", llcolor4u, llcolor4ua);
- }
-
- template<> template<>
- void v4coloru_object::test<5>()
- {
- U8 r = 0x12, g = 0xFF, b = 0xAF, a = 0x23;
- LLColor4U llcolor4u;
- llcolor4u.setVec(r,g,b,a);
- ensure("1:setVec:Fail to set the values ", ((r == llcolor4u.mV[VX]) && (g == llcolor4u.mV[VY]) && (b == llcolor4u.mV[VZ])&& (a == llcolor4u.mV[VW])));
-
- llcolor4u.setToBlack();
- llcolor4u.setVec(r,g,b);
- ensure("2:setVec:Fail to set the values ", ((r == llcolor4u.mV[VX]) && (g == llcolor4u.mV[VY]) && (b == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW])));
-
- LLColor4U llcolor4u1;
- llcolor4u1.setVec(llcolor4u);
- ensure_equals("3:setVec:Fail to set the values ", llcolor4u1,llcolor4u);
-
- const U8 vec[4] = {0x12,0xFF,0xAF,0x23};
- LLColor4U llcolor4u2;
- llcolor4u2.setVec(vec);
- ensure("4:setVec:Fail to set the values ", ((vec[0] == llcolor4u2.mV[VX]) && (vec[1] == llcolor4u2.mV[VY]) && (vec[2] == llcolor4u2.mV[VZ])&& (vec[3] == llcolor4u2.mV[VW])));
- }
-
- template<> template<>
- void v4coloru_object::test<6>()
- {
- U8 alpha = 0x12;
- LLColor4U llcolor4u;
- llcolor4u.setAlpha(alpha);
- ensure("setAlpha:Fail to set alpha value ", (alpha == llcolor4u.mV[VW]));
- }
-
- template<> template<>
- void v4coloru_object::test<7>()
- {
- U8 r = 0x12, g = 0xFF, b = 0xAF;
- LLColor4U llcolor4u(r,g,b);
- ensure("magVecSquared:Fail ", is_approx_equal(llcolor4u.magVecSquared(), (F32)(r*r + g*g + b*b)));
- ensure("magVec:Fail ", is_approx_equal(llcolor4u.magVec(), (F32) sqrt((F32) (r*r + g*g + b*b))));
- }
-
- template<> template<>
- void v4coloru_object::test<8>()
- {
- U8 r = 0x12, g = 0xFF, b = 0xAF;
- std::ostringstream stream1, stream2;
- LLColor4U llcolor4u1(r,g,b),llcolor4u2;
- stream1 << llcolor4u1;
- llcolor4u2.setVec(r,g,b);
- stream2 << llcolor4u2;
- ensure("operator << failed ", (stream1.str() == stream2.str()));
- }
-
- template<> template<>
- void v4coloru_object::test<9>()
- {
- U8 r1 = 0x12, g1 = 0xFF, b1 = 0xAF;
- U8 r2 = 0x1C, g2 = 0x9A, b2 = 0x1B;
- LLColor4U llcolor4u1(r1,g1,b1), llcolor4u2(r2,g2,b2),llcolor4u3;
- llcolor4u3 = llcolor4u1 + llcolor4u2;
- ensure_equals(
- "1a.operator+:Fail to Add the values ",
- llcolor4u3.mV[VX],
- (U8)(r1+r2));
- ensure_equals(
- "1b.operator+:Fail to Add the values ",
- llcolor4u3.mV[VY],
- (U8)(g1+g2));
- ensure_equals(
- "1c.operator+:Fail to Add the values ",
- llcolor4u3.mV[VZ],
- (U8)(b1+b2));
-
- llcolor4u2 += llcolor4u1;
- ensure_equals(
- "2a.operator+=:Fail to Add the values ",
- llcolor4u2.mV[VX],
- (U8)(r1+r2));
- ensure_equals(
- "2b.operator+=:Fail to Add the values ",
- llcolor4u2.mV[VY],
- (U8)(g1+g2));
- ensure_equals(
- "2c.operator+=:Fail to Add the values ",
- llcolor4u2.mV[VZ],
- (U8)(b1+b2));
- }
-
- template<> template<>
- void v4coloru_object::test<10>()
- {
- U8 r1 = 0x12, g1 = 0xFF, b1 = 0xAF;
- U8 r2 = 0x1C, g2 = 0x9A, b2 = 0x1B;
- LLColor4U llcolor4u1(r1,g1,b1), llcolor4u2(r2,g2,b2),llcolor4u3;
- llcolor4u3 = llcolor4u1 - llcolor4u2;
- ensure_equals(
- "1a. operator-:Fail to Add the values ",
- llcolor4u3.mV[VX],
- (U8)(r1-r2));
- ensure_equals(
- "1b. operator-:Fail to Add the values ",
- llcolor4u3.mV[VY],
- (U8)(g1-g2));
- ensure_equals(
- "1c. operator-:Fail to Add the values ",
- llcolor4u3.mV[VZ],
- (U8)(b1-b2));
-
- llcolor4u1 -= llcolor4u2;
- ensure_equals(
- "2a. operator-=:Fail to Add the values ",
- llcolor4u1.mV[VX],
- (U8)(r1-r2));
- ensure_equals(
- "2b. operator-=:Fail to Add the values ",
- llcolor4u1.mV[VY],
- (U8)(g1-g2));
- ensure_equals(
- "2c. operator-=:Fail to Add the values ",
- llcolor4u1.mV[VZ],
- (U8)(b1-b2));
- }
-
- template<> template<>
- void v4coloru_object::test<11>()
- {
- U8 r1 = 0x12, g1 = 0xFF, b1 = 0xAF;
- U8 r2 = 0x1C, g2 = 0x9A, b2 = 0x1B;
- LLColor4U llcolor4u1(r1,g1,b1), llcolor4u2(r2,g2,b2),llcolor4u3;
- llcolor4u3 = llcolor4u1 * llcolor4u2;
- ensure_equals(
- "1a. operator*:Fail to multiply the values",
- llcolor4u3.mV[VX],
- (U8)(r1*r2));
- ensure_equals(
- "1b. operator*:Fail to multiply the values",
- llcolor4u3.mV[VY],
- (U8)(g1*g2));
- ensure_equals(
- "1c. operator*:Fail to multiply the values",
- llcolor4u3.mV[VZ],
- (U8)(b1*b2));
-
- U8 mulVal = 123;
- llcolor4u1 *= mulVal;
- ensure_equals(
- "2a. operator*=:Fail to multiply the values",
- llcolor4u1.mV[VX],
- (U8)(r1*mulVal));
- ensure_equals(
- "2b. operator*=:Fail to multiply the values",
- llcolor4u1.mV[VY],
- (U8)(g1*mulVal));
- ensure_equals(
- "2c. operator*=:Fail to multiply the values",
- llcolor4u1.mV[VZ],
- (U8)(b1*mulVal));
- }
-
- template<> template<>
- void v4coloru_object::test<12>()
- {
- U8 r = 0x12, g = 0xFF, b = 0xAF;
- LLColor4U llcolor4u(r,g,b),llcolor4u1;
- llcolor4u1 = llcolor4u;
- ensure("operator== failed to ensure the equality ", (llcolor4u1 == llcolor4u));
- llcolor4u1.setToBlack();
- ensure("operator!= failed to ensure the equality ", (llcolor4u1 != llcolor4u));
- }
-
- template<> template<>
- void v4coloru_object::test<13>()
- {
- U8 r = 0x12, g = 0xFF, b = 0xAF, a = 12;
- LLColor4U llcolor4u(r,g,b,a);
- U8 modVal = 45;
- llcolor4u %= modVal;
- ensure_equals("operator%=:Fail ", llcolor4u.mV[VW], (U8)(a * modVal));
- }
-
- template<> template<>
- void v4coloru_object::test<14>()
- {
- U8 r = 0x12, g = 0xFF, b = 0xAF, a = 12;
- LLColor4U llcolor4u1(r,g,b,a);
- std::string color("12, 23, 132, 50");
- LLColor4U::parseColor4U(color, &llcolor4u1);
- ensure("parseColor4U() failed to parse the color value ", ((12 == llcolor4u1.mV[VX]) && (23 == llcolor4u1.mV[VY]) && (132 == llcolor4u1.mV[VZ])&& (50 == llcolor4u1.mV[VW])));
-
- color = "12, 23, 132";
- ensure("2:parseColor4U() failed to parse the color value ", (false == LLColor4U::parseColor4U(color, &llcolor4u1)));
-
- color = "12";
- ensure("2:parseColor4U() failed to parse the color value ", (false == LLColor4U::parseColor4U(color, &llcolor4u1)));
- }
-
- template<> template<>
- void v4coloru_object::test<15>()
- {
- U8 r = 12, g = 123, b = 3, a = 2;
- LLColor4U llcolor4u(r,g,b,a),llcolor4u1;
- const F32 fVal = 3.f;
- llcolor4u1 = llcolor4u.multAll(fVal);
- ensure("multAll:Fail to multiply ", (((U8)ll_round(r * fVal) == llcolor4u1.mV[VX]) && (U8)ll_round(g * fVal) == llcolor4u1.mV[VY]
- && ((U8)ll_round(b * fVal) == llcolor4u1.mV[VZ])&& ((U8)ll_round(a * fVal) == llcolor4u1.mV[VW])));
- }
-
- template<> template<>
- void v4coloru_object::test<16>()
- {
- U8 r1 = 12, g1 = 123, b1 = 3, a1 = 2;
- U8 r2 = 23, g2 = 230, b2 = 124, a2 = 255;
- LLColor4U llcolor4u(r1,g1,b1,a1),llcolor4u1(r2,g2,b2,a2);
- llcolor4u1 = llcolor4u1.addClampMax(llcolor4u);
- ensure("1:addClampMax():Fail to add the value ", ((r1+r2 == llcolor4u1.mV[VX]) && (255 == llcolor4u1.mV[VY]) && (b1+b2 == llcolor4u1.mV[VZ])&& (255 == llcolor4u1.mV[VW])));
-
- r1 = 132, g1 = 3, b1 = 3, a1 = 2;
- r2 = 123, g2 = 230, b2 = 154, a2 = 25;
- LLColor4U llcolor4u2(r1,g1,b1,a1),llcolor4u3(r2,g2,b2,a2);
- llcolor4u3 = llcolor4u3.addClampMax(llcolor4u2);
- ensure("2:addClampMax():Fail to add the value ", ((255 == llcolor4u3.mV[VX]) && (g1+g2 == llcolor4u3.mV[VY]) && (b1+b2 == llcolor4u3.mV[VZ])&& (a1+a2 == llcolor4u3.mV[VW])));
- }
-
- template<> template<>
- void v4coloru_object::test<17>()
- {
- F32 r = 23.f, g = 12.32f, b = -12.3f;
- LLColor3 color3(r,g,b);
- LLColor4U llcolor4u;
- llcolor4u.setVecScaleClamp(color3);
- const S32 MAX_COLOR = 255;
- F32 color_scale_factor = MAX_COLOR/r;
- S32 r2 = ll_round(r * color_scale_factor);
- S32 g2 = ll_round(g * color_scale_factor);
- ensure("setVecScaleClamp():Fail to add the value ", ((r2 == llcolor4u.mV[VX]) && (g2 == llcolor4u.mV[VY]) && (0 == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW])));
- }
-}
+/** + * @file v4coloru_test.cpp + * @author Adroit + * @date 2007-03 + * @brief v4coloru test cases. + * + * $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 "../test/lltut.h" + +#include "llsd.h" + +#include "../v4coloru.h" + + +namespace tut +{ + struct v4coloru_data + { + }; + typedef test_group<v4coloru_data> v4coloru_test; + typedef v4coloru_test::object v4coloru_object; + tut::v4coloru_test v4coloru_testcase("v4coloru_h"); + + template<> template<> + void v4coloru_object::test<1>() + { + LLColor4U llcolor4u; + ensure("1:LLColor4u:Fail to initialize ", ((0 == llcolor4u.mV[VX]) && (0 == llcolor4u.mV[VY]) && (0 == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW]))); + + U8 r = 0x12, g = 0xFF, b = 0xAF, a = 0x23; + LLColor4U llcolor4u1(r,g,b); + ensure("2:LLColor4u:Fail to initialize ", ((r == llcolor4u1.mV[VX]) && (g == llcolor4u1.mV[VY]) && (b == llcolor4u1.mV[VZ])&& (255 == llcolor4u1.mV[VW]))); + + LLColor4U llcolor4u2(r,g,b,a); + ensure("3:LLColor4u:Fail to initialize ", ((r == llcolor4u2.mV[VX]) && (g == llcolor4u2.mV[VY]) && (b == llcolor4u2.mV[VZ])&& (a == llcolor4u2.mV[VW]))); + + const U8 vec[4] = {0x12,0xFF,0xAF,0x23}; + LLColor4U llcolor4u3(vec); + ensure("4:LLColor4u:Fail to initialize ", ((vec[0] == llcolor4u3.mV[VX]) && (vec[1] == llcolor4u3.mV[VY]) && (vec[2] == llcolor4u3.mV[VZ])&& (vec[3] == llcolor4u3.mV[VW]))); + + LLSD sd = llcolor4u3.getValue(); + LLColor4U llcolor4u4(sd); + ensure_equals("5:LLColor4u (LLSD) Failed ", llcolor4u4, llcolor4u3); + } + + template<> template<> + void v4coloru_object::test<2>() + { + LLColor4U llcolor4ua(1, 2, 3, 4); + LLSD sd = llcolor4ua.getValue(); + LLColor4U llcolor4u; + llcolor4u.setValue(sd); + ensure_equals("setValue(LLSD)/getValue Failed ", llcolor4u, llcolor4ua); + } + + template<> template<> + void v4coloru_object::test<3>() + { + U8 r = 0x12, g = 0xFF, b = 0xAF, a = 0x23; + LLColor4U llcolor4u(r,g,b,a); + llcolor4u.setToBlack(); + ensure("setToBlack:Fail to set black ", ((0 == llcolor4u.mV[VX]) && (0 == llcolor4u.mV[VY]) && (0 == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW]))); + + llcolor4u.setToWhite(); + ensure("setToWhite:Fail to white ", ((255 == llcolor4u.mV[VX]) && (255 == llcolor4u.mV[VY]) && (255 == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW]))); + } + + template<> template<> + void v4coloru_object::test<4>() + { + U8 r = 0x12, g = 0xFF, b = 0xAF, a = 0x23; + LLColor4U llcolor4ua(r,g,b,a); + LLSD sd = llcolor4ua.getValue(); + LLColor4U llcolor4u = (LLColor4U)sd; + ensure_equals("Operator=(LLSD) Failed ", llcolor4u, llcolor4ua); + } + + template<> template<> + void v4coloru_object::test<5>() + { + U8 r = 0x12, g = 0xFF, b = 0xAF, a = 0x23; + LLColor4U llcolor4u; + llcolor4u.setVec(r,g,b,a); + ensure("1:setVec:Fail to set the values ", ((r == llcolor4u.mV[VX]) && (g == llcolor4u.mV[VY]) && (b == llcolor4u.mV[VZ])&& (a == llcolor4u.mV[VW]))); + + llcolor4u.setToBlack(); + llcolor4u.setVec(r,g,b); + ensure("2:setVec:Fail to set the values ", ((r == llcolor4u.mV[VX]) && (g == llcolor4u.mV[VY]) && (b == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW]))); + + LLColor4U llcolor4u1; + llcolor4u1.setVec(llcolor4u); + ensure_equals("3:setVec:Fail to set the values ", llcolor4u1,llcolor4u); + + const U8 vec[4] = {0x12,0xFF,0xAF,0x23}; + LLColor4U llcolor4u2; + llcolor4u2.setVec(vec); + ensure("4:setVec:Fail to set the values ", ((vec[0] == llcolor4u2.mV[VX]) && (vec[1] == llcolor4u2.mV[VY]) && (vec[2] == llcolor4u2.mV[VZ])&& (vec[3] == llcolor4u2.mV[VW]))); + } + + template<> template<> + void v4coloru_object::test<6>() + { + U8 alpha = 0x12; + LLColor4U llcolor4u; + llcolor4u.setAlpha(alpha); + ensure("setAlpha:Fail to set alpha value ", (alpha == llcolor4u.mV[VW])); + } + + template<> template<> + void v4coloru_object::test<7>() + { + U8 r = 0x12, g = 0xFF, b = 0xAF; + LLColor4U llcolor4u(r,g,b); + ensure("magVecSquared:Fail ", is_approx_equal(llcolor4u.magVecSquared(), (F32)(r*r + g*g + b*b))); + ensure("magVec:Fail ", is_approx_equal(llcolor4u.magVec(), (F32) sqrt((F32) (r*r + g*g + b*b)))); + } + + template<> template<> + void v4coloru_object::test<8>() + { + U8 r = 0x12, g = 0xFF, b = 0xAF; + std::ostringstream stream1, stream2; + LLColor4U llcolor4u1(r,g,b),llcolor4u2; + stream1 << llcolor4u1; + llcolor4u2.setVec(r,g,b); + stream2 << llcolor4u2; + ensure("operator << failed ", (stream1.str() == stream2.str())); + } + + template<> template<> + void v4coloru_object::test<9>() + { + U8 r1 = 0x12, g1 = 0xFF, b1 = 0xAF; + U8 r2 = 0x1C, g2 = 0x9A, b2 = 0x1B; + LLColor4U llcolor4u1(r1,g1,b1), llcolor4u2(r2,g2,b2),llcolor4u3; + llcolor4u3 = llcolor4u1 + llcolor4u2; + ensure_equals( + "1a.operator+:Fail to Add the values ", + llcolor4u3.mV[VX], + (U8)(r1+r2)); + ensure_equals( + "1b.operator+:Fail to Add the values ", + llcolor4u3.mV[VY], + (U8)(g1+g2)); + ensure_equals( + "1c.operator+:Fail to Add the values ", + llcolor4u3.mV[VZ], + (U8)(b1+b2)); + + llcolor4u2 += llcolor4u1; + ensure_equals( + "2a.operator+=:Fail to Add the values ", + llcolor4u2.mV[VX], + (U8)(r1+r2)); + ensure_equals( + "2b.operator+=:Fail to Add the values ", + llcolor4u2.mV[VY], + (U8)(g1+g2)); + ensure_equals( + "2c.operator+=:Fail to Add the values ", + llcolor4u2.mV[VZ], + (U8)(b1+b2)); + } + + template<> template<> + void v4coloru_object::test<10>() + { + U8 r1 = 0x12, g1 = 0xFF, b1 = 0xAF; + U8 r2 = 0x1C, g2 = 0x9A, b2 = 0x1B; + LLColor4U llcolor4u1(r1,g1,b1), llcolor4u2(r2,g2,b2),llcolor4u3; + llcolor4u3 = llcolor4u1 - llcolor4u2; + ensure_equals( + "1a. operator-:Fail to Add the values ", + llcolor4u3.mV[VX], + (U8)(r1-r2)); + ensure_equals( + "1b. operator-:Fail to Add the values ", + llcolor4u3.mV[VY], + (U8)(g1-g2)); + ensure_equals( + "1c. operator-:Fail to Add the values ", + llcolor4u3.mV[VZ], + (U8)(b1-b2)); + + llcolor4u1 -= llcolor4u2; + ensure_equals( + "2a. operator-=:Fail to Add the values ", + llcolor4u1.mV[VX], + (U8)(r1-r2)); + ensure_equals( + "2b. operator-=:Fail to Add the values ", + llcolor4u1.mV[VY], + (U8)(g1-g2)); + ensure_equals( + "2c. operator-=:Fail to Add the values ", + llcolor4u1.mV[VZ], + (U8)(b1-b2)); + } + + template<> template<> + void v4coloru_object::test<11>() + { + U8 r1 = 0x12, g1 = 0xFF, b1 = 0xAF; + U8 r2 = 0x1C, g2 = 0x9A, b2 = 0x1B; + LLColor4U llcolor4u1(r1,g1,b1), llcolor4u2(r2,g2,b2),llcolor4u3; + llcolor4u3 = llcolor4u1 * llcolor4u2; + ensure_equals( + "1a. operator*:Fail to multiply the values", + llcolor4u3.mV[VX], + (U8)(r1*r2)); + ensure_equals( + "1b. operator*:Fail to multiply the values", + llcolor4u3.mV[VY], + (U8)(g1*g2)); + ensure_equals( + "1c. operator*:Fail to multiply the values", + llcolor4u3.mV[VZ], + (U8)(b1*b2)); + + U8 mulVal = 123; + llcolor4u1 *= mulVal; + ensure_equals( + "2a. operator*=:Fail to multiply the values", + llcolor4u1.mV[VX], + (U8)(r1*mulVal)); + ensure_equals( + "2b. operator*=:Fail to multiply the values", + llcolor4u1.mV[VY], + (U8)(g1*mulVal)); + ensure_equals( + "2c. operator*=:Fail to multiply the values", + llcolor4u1.mV[VZ], + (U8)(b1*mulVal)); + } + + template<> template<> + void v4coloru_object::test<12>() + { + U8 r = 0x12, g = 0xFF, b = 0xAF; + LLColor4U llcolor4u(r,g,b),llcolor4u1; + llcolor4u1 = llcolor4u; + ensure("operator== failed to ensure the equality ", (llcolor4u1 == llcolor4u)); + llcolor4u1.setToBlack(); + ensure("operator!= failed to ensure the equality ", (llcolor4u1 != llcolor4u)); + } + + template<> template<> + void v4coloru_object::test<13>() + { + U8 r = 0x12, g = 0xFF, b = 0xAF, a = 12; + LLColor4U llcolor4u(r,g,b,a); + U8 modVal = 45; + llcolor4u %= modVal; + ensure_equals("operator%=:Fail ", llcolor4u.mV[VW], (U8)(a * modVal)); + } + + template<> template<> + void v4coloru_object::test<14>() + { + U8 r = 0x12, g = 0xFF, b = 0xAF, a = 12; + LLColor4U llcolor4u1(r,g,b,a); + std::string color("12, 23, 132, 50"); + LLColor4U::parseColor4U(color, &llcolor4u1); + ensure("parseColor4U() failed to parse the color value ", ((12 == llcolor4u1.mV[VX]) && (23 == llcolor4u1.mV[VY]) && (132 == llcolor4u1.mV[VZ])&& (50 == llcolor4u1.mV[VW]))); + + color = "12, 23, 132"; + ensure("2:parseColor4U() failed to parse the color value ", (false == LLColor4U::parseColor4U(color, &llcolor4u1))); + + color = "12"; + ensure("2:parseColor4U() failed to parse the color value ", (false == LLColor4U::parseColor4U(color, &llcolor4u1))); + } + + template<> template<> + void v4coloru_object::test<15>() + { + U8 r = 12, g = 123, b = 3, a = 2; + LLColor4U llcolor4u(r,g,b,a),llcolor4u1; + const F32 fVal = 3.f; + llcolor4u1 = llcolor4u.multAll(fVal); + ensure("multAll:Fail to multiply ", (((U8)ll_round(r * fVal) == llcolor4u1.mV[VX]) && (U8)ll_round(g * fVal) == llcolor4u1.mV[VY] + && ((U8)ll_round(b * fVal) == llcolor4u1.mV[VZ])&& ((U8)ll_round(a * fVal) == llcolor4u1.mV[VW]))); + } + + template<> template<> + void v4coloru_object::test<16>() + { + U8 r1 = 12, g1 = 123, b1 = 3, a1 = 2; + U8 r2 = 23, g2 = 230, b2 = 124, a2 = 255; + LLColor4U llcolor4u(r1,g1,b1,a1),llcolor4u1(r2,g2,b2,a2); + llcolor4u1 = llcolor4u1.addClampMax(llcolor4u); + ensure("1:addClampMax():Fail to add the value ", ((r1+r2 == llcolor4u1.mV[VX]) && (255 == llcolor4u1.mV[VY]) && (b1+b2 == llcolor4u1.mV[VZ])&& (255 == llcolor4u1.mV[VW]))); + + r1 = 132, g1 = 3, b1 = 3, a1 = 2; + r2 = 123, g2 = 230, b2 = 154, a2 = 25; + LLColor4U llcolor4u2(r1,g1,b1,a1),llcolor4u3(r2,g2,b2,a2); + llcolor4u3 = llcolor4u3.addClampMax(llcolor4u2); + ensure("2:addClampMax():Fail to add the value ", ((255 == llcolor4u3.mV[VX]) && (g1+g2 == llcolor4u3.mV[VY]) && (b1+b2 == llcolor4u3.mV[VZ])&& (a1+a2 == llcolor4u3.mV[VW]))); + } + + template<> template<> + void v4coloru_object::test<17>() + { + F32 r = 23.f, g = 12.32f, b = -12.3f; + LLColor3 color3(r,g,b); + LLColor4U llcolor4u; + llcolor4u.setVecScaleClamp(color3); + const S32 MAX_COLOR = 255; + F32 color_scale_factor = MAX_COLOR/r; + S32 r2 = ll_round(r * color_scale_factor); + S32 g2 = ll_round(g * color_scale_factor); + ensure("setVecScaleClamp():Fail to add the value ", ((r2 == llcolor4u.mV[VX]) && (g2 == llcolor4u.mV[VY]) && (0 == llcolor4u.mV[VZ])&& (255 == llcolor4u.mV[VW]))); + } +} diff --git a/indra/llmath/tests/v4math_test.cpp b/indra/llmath/tests/v4math_test.cpp index 5e443663de..236585f13b 100644 --- a/indra/llmath/tests/v4math_test.cpp +++ b/indra/llmath/tests/v4math_test.cpp @@ -1,383 +1,383 @@ -/**
- * @file v4math_test.cpp
- * @author Adroit
- * @date 2007-03
- * @brief v4math test cases.
- *
- * $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 "../test/lltut.h"
-#include "llsd.h"
-
-#include "../m4math.h"
-#include "../v4math.h"
-#include "../llquaternion.h"
-
-namespace tut
-{
- struct v4math_data
- {
- };
- typedef test_group<v4math_data> v4math_test;
- typedef v4math_test::object v4math_object;
- tut::v4math_test v4math_testcase("v4math_h");
-
- template<> template<>
- void v4math_object::test<1>()
- {
- LLVector4 vec4;
- ensure("1:LLVector4:Fail to initialize " ,((0 == vec4.mV[VX]) && (0 == vec4.mV[VY]) && (0 == vec4.mV[VZ])&& (1.0f == vec4.mV[VW])));
- F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f;
- LLVector4 vec4a(x,y,z);
- ensure("2:LLVector4:Fail to initialize " ,((x == vec4a.mV[VX]) && (y == vec4a.mV[VY]) && (z == vec4a.mV[VZ])&& (1.0f == vec4a.mV[VW])));
- LLVector4 vec4b(x,y,z,w);
- ensure("3:LLVector4:Fail to initialize " ,((x == vec4b.mV[VX]) && (y == vec4b.mV[VY]) && (z == vec4b.mV[VZ])&& (w == vec4b.mV[VW])));
- const F32 vec[4] = {.112f ,23.2f, -4.2f, -.0001f};
- LLVector4 vec4c(vec);
- ensure("4:LLVector4:Fail to initialize " ,((vec[0] == vec4c.mV[VX]) && (vec[1] == vec4c.mV[VY]) && (vec[2] == vec4c.mV[VZ])&& (vec[3] == vec4c.mV[VW])));
- LLVector3 vec3(-2.23f,1.01f,42.3f);
- LLVector4 vec4d(vec3);
- ensure("5:LLVector4:Fail to initialize " ,((vec3.mV[VX] == vec4d.mV[VX]) && (vec3.mV[VY] == vec4d.mV[VY]) && (vec3.mV[VZ] == vec4d.mV[VZ])&& (1.f == vec4d.mV[VW])));
- F32 w1 = -.234f;
- LLVector4 vec4e(vec3,w1);
- ensure("6:LLVector4:Fail to initialize " ,((vec3.mV[VX] == vec4e.mV[VX]) && (vec3.mV[VY] == vec4e.mV[VY]) && (vec3.mV[VZ] == vec4e.mV[VZ])&& (w1 == vec4e.mV[VW])));
- }
-
- template<> template<>
- void v4math_object::test<2>()
- {
- F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f;
- LLVector4 vec4;
- vec4.setVec(x,y,z);
- ensure("1:setVec:Fail to initialize " ,((x == vec4.mV[VX]) && (y == vec4.mV[VY]) && (z == vec4.mV[VZ])&& (1.0f == vec4.mV[VW])));
- vec4.clearVec();
- ensure("2:clearVec:Fail " ,((0 == vec4.mV[VX]) && (0 == vec4.mV[VY]) && (0 == vec4.mV[VZ])&& (1.0f == vec4.mV[VW])));
- vec4.setVec(x,y,z,w);
- ensure("3:setVec:Fail to initialize " ,((x == vec4.mV[VX]) && (y == vec4.mV[VY]) && (z == vec4.mV[VZ])&& (w == vec4.mV[VW])));
- vec4.zeroVec();
- ensure("4:zeroVec:Fail " ,((0 == vec4.mV[VX]) && (0 == vec4.mV[VY]) && (0 == vec4.mV[VZ])&& (0 == vec4.mV[VW])));
- LLVector3 vec3(-2.23f,1.01f,42.3f);
- vec4.clearVec();
- vec4.setVec(vec3);
- ensure("5:setVec:Fail to initialize " ,((vec3.mV[VX] == vec4.mV[VX]) && (vec3.mV[VY] == vec4.mV[VY]) && (vec3.mV[VZ] == vec4.mV[VZ])&& (1.f == vec4.mV[VW])));
- F32 w1 = -.234f;
- vec4.zeroVec();
- vec4.setVec(vec3,w1);
- ensure("6:setVec:Fail to initialize " ,((vec3.mV[VX] == vec4.mV[VX]) && (vec3.mV[VY] == vec4.mV[VY]) && (vec3.mV[VZ] == vec4.mV[VZ])&& (w1 == vec4.mV[VW])));
- const F32 vec[4] = {.112f ,23.2f, -4.2f, -.0001f};
- LLVector4 vec4a;
- vec4a.setVec(vec);
- ensure("7:setVec:Fail to initialize " ,((vec[0] == vec4a.mV[VX]) && (vec[1] == vec4a.mV[VY]) && (vec[2] == vec4a.mV[VZ])&& (vec[3] == vec4a.mV[VW])));
- }
-
- template<> template<>
- void v4math_object::test<3>()
- {
- F32 x = 10.f, y = -2.3f, z = -.023f;
- LLVector4 vec4(x,y,z);
- ensure("magVec:Fail ", is_approx_equal(vec4.magVec(), (F32) sqrt(x*x + y*y + z*z)));
- ensure("magVecSquared:Fail ", is_approx_equal(vec4.magVecSquared(), (x*x + y*y + z*z)));
- }
-
- template<> template<>
- void v4math_object::test<4>()
- {
- F32 x = 10.f, y = -2.3f, z = -.023f;
- LLVector4 vec4(x,y,z);
- F32 mag = vec4.normVec();
- mag = 1.f/ mag;
- ensure("1:normVec: Fail " ,is_approx_equal(mag*x,vec4.mV[VX]) && is_approx_equal(mag*y, vec4.mV[VY])&& is_approx_equal(mag*z, vec4.mV[VZ]));
- x = 0.000000001f, y = 0.000000001f, z = 0.000000001f;
- vec4.clearVec();
- vec4.setVec(x,y,z);
- mag = vec4.normVec();
- ensure("2:normVec: Fail " ,is_approx_equal(mag*x,vec4.mV[VX]) && is_approx_equal(mag*y, vec4.mV[VY])&& is_approx_equal(mag*z, vec4.mV[VZ]));
- }
-
- template<> template<>
- void v4math_object::test<5>()
- {
- F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f;
- LLVector4 vec4(x,y,z,w);
- vec4.abs();
- ensure("abs:Fail " ,((x == vec4.mV[VX]) && (-y == vec4.mV[VY]) && (-z == vec4.mV[VZ])&& (-w == vec4.mV[VW])));
- vec4.clearVec();
- ensure("isExactlyClear:Fail " ,(true == vec4.isExactlyClear()));
- vec4.zeroVec();
- ensure("isExactlyZero:Fail " ,(true == vec4.isExactlyZero()));
- }
-
- template<> template<>
- void v4math_object::test<6>()
- {
- F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f;
- LLVector4 vec4(x,y,z,w),vec4a;
- vec4a = vec4.scaleVec(vec4);
- ensure("scaleVec:Fail " ,(is_approx_equal(x*x, vec4a.mV[VX]) && is_approx_equal(y*y, vec4a.mV[VY]) && is_approx_equal(z*z, vec4a.mV[VZ])&& is_approx_equal(w*w, vec4a.mV[VW])));
- }
-
- template<> template<>
- void v4math_object::test<7>()
- {
- F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f;
- LLVector4 vec4(x,y,z,w);
- ensure("1:operator [] failed " ,( x == vec4[0]));
- ensure("2:operator [] failed " ,( y == vec4[1]));
- ensure("3:operator [] failed " ,( z == vec4[2]));
- ensure("4:operator [] failed " ,( w == vec4[3]));
- x = 23.f, y = -.2361f, z = 3.25;
- vec4.setVec(x,y,z);
- F32 &ref1 = vec4[0];
- ensure("5:operator [] failed " ,( ref1 == vec4[0]));
- F32 &ref2 = vec4[1];
- ensure("6:operator [] failed " ,( ref2 == vec4[1]));
- F32 &ref3 = vec4[2];
- ensure("7:operator [] failed " ,( ref3 == vec4[2]));
- F32 &ref4 = vec4[3];
- ensure("8:operator [] failed " ,( ref4 == vec4[3]));
- }
-
- template<> template<>
- void v4math_object::test<8>()
- {
- F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f;
- const F32 val[16] = {
- 1.f, 2.f, 3.f, 0.f,
- .34f, .1f, -.5f, 0.f,
- 2.f, 1.23f, 1.234f, 0.f,
- .89f, 0.f, 0.f, 0.f
- };
- LLMatrix4 mat(val);
- LLVector4 vec4(x,y,z,w),vec4a;
- vec4.rotVec(mat);
- vec4a.setVec(x,y,z,w);
- vec4a.rotVec(mat);
- ensure_equals("1:rotVec: Fail " ,vec4a, vec4);
- F32 a = 2.32f, b = -23.2f, c = -34.1112f, d = 1.010112f;
- LLQuaternion q(a,b,c,d);
- LLVector4 vec4b(a,b,c,d),vec4c;
- vec4b.rotVec(q);
- vec4c.setVec(a, b, c, d);
- vec4c.rotVec(q);
- ensure_equals("2:rotVec: Fail " ,vec4b, vec4c);
- }
-
- template<> template<>
- void v4math_object::test<9>()
- {
- F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f;
- LLVector4 vec4(x,y,z,w),vec4a;;
- std::ostringstream stream1, stream2;
- stream1 << vec4;
- vec4a.setVec(x,y,z,w);
- stream2 << vec4a;
- ensure("operator << failed",(stream1.str() == stream2.str()));
- }
-
- template<> template<>
- void v4math_object::test<10>()
- {
- F32 x1 = 1.f, y1 = 2.f, z1 = -1.1f, w1 = .23f;
- F32 x2 = 1.2f, y2 = 2.5f, z2 = 1.f, w2 = 1.3f;
- LLVector4 vec4(x1,y1,z1,w1),vec4a(x2,y2,z2,w2),vec4b;
- vec4b = vec4a + vec4;
- ensure("1:operator+:Fail to initialize " ,(is_approx_equal(x1+x2,vec4b.mV[VX]) && is_approx_equal(y1+y2,vec4b.mV[VY]) && is_approx_equal(z1+z2,vec4b.mV[VZ])));
- x1 = -2.45f, y1 = 2.1f, z1 = 3.0f;
- vec4.clearVec();
- vec4a.clearVec();
- vec4.setVec(x1,y1,z1);
- vec4a +=vec4;
- ensure_equals("2:operator+=: Fail to initialize", vec4a,vec4);
- vec4a += vec4;
- ensure("3:operator+=:Fail to initialize " ,(is_approx_equal(2*x1,vec4a.mV[VX]) && is_approx_equal(2*y1,vec4a.mV[VY]) && is_approx_equal(2*z1,vec4a.mV[VZ])));
- }
- template<> template<>
- void v4math_object::test<11>()
- {
- F32 x1 = 1.f, y1 = 2.f, z1 = -1.1f, w1 = .23f;
- F32 x2 = 1.2f, y2 = 2.5f, z2 = 1.f, w2 = 1.3f;
- LLVector4 vec4(x1,y1,z1,w1),vec4a(x2,y2,z2,w2),vec4b;
- vec4b = vec4a - vec4;
- ensure("1:operator-:Fail to initialize " ,(is_approx_equal(x2-x1,vec4b.mV[VX]) && is_approx_equal(y2-y1,vec4b.mV[VY]) && is_approx_equal(z2-z1,vec4b.mV[VZ])));
- x1 = -2.45f, y1 = 2.1f, z1 = 3.0f;
- vec4.clearVec();
- vec4a.clearVec();
- vec4.setVec(x1,y1,z1);
- vec4a -=vec4;
- ensure_equals("2:operator-=: Fail to initialize" , vec4a,-vec4);
- vec4a -=vec4;
- ensure("3:operator-=:Fail to initialize " ,(is_approx_equal(-2*x1,vec4a.mV[VX]) && is_approx_equal(-2*y1,vec4a.mV[VY]) && is_approx_equal(-2*z1,vec4a.mV[VZ])));
- }
-
- template<> template<>
- void v4math_object::test<12>()
- {
- F32 x1 = 1.f, y1 = 2.f, z1 = -1.1f;
- F32 x2 = 1.2f, y2 = 2.5f, z2 = 1.f;
- LLVector4 vec4(x1,y1,z1),vec4a(x2,y2,z2);
- F32 res = vec4 * vec4a;
- ensure("1:operator* failed " ,is_approx_equal(res, x1*x2 + y1*y2 + z1*z2));
- vec4a.clearVec();
- F32 mulVal = 4.2f;
- vec4a = vec4 * mulVal;
- ensure("2:operator* failed " ,is_approx_equal(x1*mulVal,vec4a.mV[VX]) && is_approx_equal(y1*mulVal, vec4a.mV[VY])&& is_approx_equal(z1*mulVal, vec4a.mV[VZ]));
- vec4a.clearVec();
- vec4a = mulVal * vec4 ;
- ensure("3:operator* failed " ,is_approx_equal(x1*mulVal, vec4a.mV[VX]) && is_approx_equal(y1*mulVal, vec4a.mV[VY])&& is_approx_equal(z1*mulVal, vec4a.mV[VZ]));
- vec4 *= mulVal;
- ensure("4:operator*= failed " ,is_approx_equal(x1*mulVal, vec4.mV[VX]) && is_approx_equal(y1*mulVal, vec4.mV[VY])&& is_approx_equal(z1*mulVal, vec4.mV[VZ]));
- }
-
- template<> template<>
- void v4math_object::test<13>()
- {
- F32 x1 = 1.f, y1 = 2.f, z1 = -1.1f;
- F32 x2 = 1.2f, y2 = 2.5f, z2 = 1.f;
- LLVector4 vec4(x1,y1,z1),vec4a(x2,y2,z2),vec4b;
- vec4b = vec4 % vec4a;
- ensure("1:operator% failed " ,is_approx_equal(y1*z2 - y2*z1, vec4b.mV[VX]) && is_approx_equal(z1*x2 -z2*x1, vec4b.mV[VY]) && is_approx_equal(x1*y2-x2*y1, vec4b.mV[VZ]));
- vec4 %= vec4a;
- ensure_equals("operator%= failed " ,vec4,vec4b);
- }
-
- template<> template<>
- void v4math_object::test<14>()
- {
- F32 x = 1.f, y = 2.f, z = -1.1f,div = 4.2f;
- F32 t = 1.f / div;
- LLVector4 vec4(x,y,z), vec4a;
- vec4a = vec4/div;
- ensure("1:operator/ failed " ,is_approx_equal(x*t, vec4a.mV[VX]) && is_approx_equal(y*t, vec4a.mV[VY])&& is_approx_equal(z*t, vec4a.mV[VZ]));
- x = 1.23f, y = 4.f, z = -2.32f;
- vec4.clearVec();
- vec4a.clearVec();
- vec4.setVec(x,y,z);
- vec4a = vec4/div;
- ensure("2:operator/ failed " ,is_approx_equal(x*t, vec4a.mV[VX]) && is_approx_equal(y*t, vec4a.mV[VY])&& is_approx_equal(z*t, vec4a.mV[VZ]));
- vec4 /= div;
- ensure("3:operator/ failed " ,is_approx_equal(x*t, vec4.mV[VX]) && is_approx_equal(y*t, vec4.mV[VY])&& is_approx_equal(z*t, vec4.mV[VZ]));
- }
-
- template<> template<>
- void v4math_object::test<15>()
- {
- F32 x = 1.f, y = 2.f, z = -1.1f;
- LLVector4 vec4(x,y,z), vec4a;
- ensure("operator!= failed " ,(vec4 != vec4a));
- vec4a = vec4;
- ensure("operator== failed " ,(vec4 ==vec4a));
- }
-
- template<> template<>
- void v4math_object::test<16>()
- {
- F32 x = 1.f, y = 2.f, z = -1.1f;
- LLVector4 vec4(x,y,z), vec4a;
- vec4a = - vec4;
- ensure("operator- failed " , (vec4 == - vec4a));
- }
-
- template<> template<>
- void v4math_object::test<17>()
- {
- F32 x = 1.f, y = 2.f, z = -1.1f,epsilon = .23425f;
- LLVector4 vec4(x,y,z), vec4a(x,y,z);
- ensure("1:are_parallel: Fail " ,(true == are_parallel(vec4a,vec4,epsilon)));
- x = 21.f, y = 12.f, z = -123.1f;
- vec4a.clearVec();
- vec4a.setVec(x,y,z);
- ensure("2:are_parallel: Fail " ,(false == are_parallel(vec4a,vec4,epsilon)));
- }
-
- template<> template<>
- void v4math_object::test<18>()
- {
- F32 x = 1.f, y = 2.f, z = -1.1f;
- F32 angle1, angle2;
- LLVector4 vec4(x,y,z), vec4a(x,y,z);
- angle1 = angle_between(vec4, vec4a);
- vec4.normVec();
- vec4a.normVec();
- angle2 = acos(vec4 * vec4a);
- ensure_approximately_equals("1:angle_between: Fail " ,angle1,angle2,8);
- F32 x1 = 21.f, y1 = 2.23f, z1 = -1.1f;
- LLVector4 vec4b(x,y,z), vec4c(x1,y1,z1);
- angle1 = angle_between(vec4b, vec4c);
- vec4b.normVec();
- vec4c.normVec();
- angle2 = acos(vec4b * vec4c);
- ensure_approximately_equals("2:angle_between: Fail " ,angle1,angle2,8);
- }
-
- template<> template<>
- void v4math_object::test<19>()
- {
- F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 1.f, z2 = 1.f;
- F32 val1,val2;
- LLVector4 vec4(x1,y1,z1),vec4a(x2,y2,z2);
- val1 = dist_vec(vec4,vec4a);
- val2 = (F32) sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
- ensure_equals("dist_vec: Fail ",val2, val1);
- val1 = dist_vec_squared(vec4,vec4a);
- val2 =((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2));
- ensure_equals("dist_vec_squared: Fail ",val2, val1);
- }
-
- template<> template<>
- void v4math_object::test<20>()
- {
- F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, w1 = -.23f, x2 = 1.3f, y2 = 1.f, z2 = 1.f,w2 = .12f;
- F32 val = 2.3f,val1,val2,val3,val4;
- LLVector4 vec4(x1,y1,z1,w1),vec4a(x2,y2,z2,w2);
- val1 = x1 + (x2 - x1)* val;
- val2 = y1 + (y2 - y1)* val;
- val3 = z1 + (z2 - z1)* val;
- val4 = w1 + (w2 - w1)* val;
- LLVector4 vec4b = lerp(vec4,vec4a,val);
- LLVector4 check(val1, val2, val3, val4);
- ensure_equals("lerp failed", check, vec4b);
- }
-
- template<> template<>
- void v4math_object::test<21>()
- {
- F32 x = 1.f, y = 2.f, z = -1.1f;
- LLVector4 vec4(x,y,z);
- LLVector3 vec3 = vec4to3(vec4);
- ensure("vec4to3 failed", ((x == vec3.mV[VX])&& (y == vec3.mV[VY]) && (z == vec3.mV[VZ])));
- LLVector4 vec4a = vec3to4(vec3);
- ensure_equals("vec3to4 failed",vec4a,vec4);
- }
-
- template<> template<>
- void v4math_object::test<22>()
- {
- F32 x = 1.f, y = 2.f, z = -1.1f;
- LLVector4 vec4(x,y,z);
- LLSD llsd = vec4.getValue();
- LLVector3 vec3(llsd);
- LLVector4 vec4a = vec3to4(vec3);
- ensure_equals("getValue failed",vec4a,vec4);
- }
-}
+/** + * @file v4math_test.cpp + * @author Adroit + * @date 2007-03 + * @brief v4math test cases. + * + * $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 "../test/lltut.h" +#include "llsd.h" + +#include "../m4math.h" +#include "../v4math.h" +#include "../llquaternion.h" + +namespace tut +{ + struct v4math_data + { + }; + typedef test_group<v4math_data> v4math_test; + typedef v4math_test::object v4math_object; + tut::v4math_test v4math_testcase("v4math_h"); + + template<> template<> + void v4math_object::test<1>() + { + LLVector4 vec4; + ensure("1:LLVector4:Fail to initialize " ,((0 == vec4.mV[VX]) && (0 == vec4.mV[VY]) && (0 == vec4.mV[VZ])&& (1.0f == vec4.mV[VW]))); + F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f; + LLVector4 vec4a(x,y,z); + ensure("2:LLVector4:Fail to initialize " ,((x == vec4a.mV[VX]) && (y == vec4a.mV[VY]) && (z == vec4a.mV[VZ])&& (1.0f == vec4a.mV[VW]))); + LLVector4 vec4b(x,y,z,w); + ensure("3:LLVector4:Fail to initialize " ,((x == vec4b.mV[VX]) && (y == vec4b.mV[VY]) && (z == vec4b.mV[VZ])&& (w == vec4b.mV[VW]))); + const F32 vec[4] = {.112f ,23.2f, -4.2f, -.0001f}; + LLVector4 vec4c(vec); + ensure("4:LLVector4:Fail to initialize " ,((vec[0] == vec4c.mV[VX]) && (vec[1] == vec4c.mV[VY]) && (vec[2] == vec4c.mV[VZ])&& (vec[3] == vec4c.mV[VW]))); + LLVector3 vec3(-2.23f,1.01f,42.3f); + LLVector4 vec4d(vec3); + ensure("5:LLVector4:Fail to initialize " ,((vec3.mV[VX] == vec4d.mV[VX]) && (vec3.mV[VY] == vec4d.mV[VY]) && (vec3.mV[VZ] == vec4d.mV[VZ])&& (1.f == vec4d.mV[VW]))); + F32 w1 = -.234f; + LLVector4 vec4e(vec3,w1); + ensure("6:LLVector4:Fail to initialize " ,((vec3.mV[VX] == vec4e.mV[VX]) && (vec3.mV[VY] == vec4e.mV[VY]) && (vec3.mV[VZ] == vec4e.mV[VZ])&& (w1 == vec4e.mV[VW]))); + } + + template<> template<> + void v4math_object::test<2>() + { + F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f; + LLVector4 vec4; + vec4.setVec(x,y,z); + ensure("1:setVec:Fail to initialize " ,((x == vec4.mV[VX]) && (y == vec4.mV[VY]) && (z == vec4.mV[VZ])&& (1.0f == vec4.mV[VW]))); + vec4.clearVec(); + ensure("2:clearVec:Fail " ,((0 == vec4.mV[VX]) && (0 == vec4.mV[VY]) && (0 == vec4.mV[VZ])&& (1.0f == vec4.mV[VW]))); + vec4.setVec(x,y,z,w); + ensure("3:setVec:Fail to initialize " ,((x == vec4.mV[VX]) && (y == vec4.mV[VY]) && (z == vec4.mV[VZ])&& (w == vec4.mV[VW]))); + vec4.zeroVec(); + ensure("4:zeroVec:Fail " ,((0 == vec4.mV[VX]) && (0 == vec4.mV[VY]) && (0 == vec4.mV[VZ])&& (0 == vec4.mV[VW]))); + LLVector3 vec3(-2.23f,1.01f,42.3f); + vec4.clearVec(); + vec4.setVec(vec3); + ensure("5:setVec:Fail to initialize " ,((vec3.mV[VX] == vec4.mV[VX]) && (vec3.mV[VY] == vec4.mV[VY]) && (vec3.mV[VZ] == vec4.mV[VZ])&& (1.f == vec4.mV[VW]))); + F32 w1 = -.234f; + vec4.zeroVec(); + vec4.setVec(vec3,w1); + ensure("6:setVec:Fail to initialize " ,((vec3.mV[VX] == vec4.mV[VX]) && (vec3.mV[VY] == vec4.mV[VY]) && (vec3.mV[VZ] == vec4.mV[VZ])&& (w1 == vec4.mV[VW]))); + const F32 vec[4] = {.112f ,23.2f, -4.2f, -.0001f}; + LLVector4 vec4a; + vec4a.setVec(vec); + ensure("7:setVec:Fail to initialize " ,((vec[0] == vec4a.mV[VX]) && (vec[1] == vec4a.mV[VY]) && (vec[2] == vec4a.mV[VZ])&& (vec[3] == vec4a.mV[VW]))); + } + + template<> template<> + void v4math_object::test<3>() + { + F32 x = 10.f, y = -2.3f, z = -.023f; + LLVector4 vec4(x,y,z); + ensure("magVec:Fail ", is_approx_equal(vec4.magVec(), (F32) sqrt(x*x + y*y + z*z))); + ensure("magVecSquared:Fail ", is_approx_equal(vec4.magVecSquared(), (x*x + y*y + z*z))); + } + + template<> template<> + void v4math_object::test<4>() + { + F32 x = 10.f, y = -2.3f, z = -.023f; + LLVector4 vec4(x,y,z); + F32 mag = vec4.normVec(); + mag = 1.f/ mag; + ensure("1:normVec: Fail " ,is_approx_equal(mag*x,vec4.mV[VX]) && is_approx_equal(mag*y, vec4.mV[VY])&& is_approx_equal(mag*z, vec4.mV[VZ])); + x = 0.000000001f, y = 0.000000001f, z = 0.000000001f; + vec4.clearVec(); + vec4.setVec(x,y,z); + mag = vec4.normVec(); + ensure("2:normVec: Fail " ,is_approx_equal(mag*x,vec4.mV[VX]) && is_approx_equal(mag*y, vec4.mV[VY])&& is_approx_equal(mag*z, vec4.mV[VZ])); + } + + template<> template<> + void v4math_object::test<5>() + { + F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f; + LLVector4 vec4(x,y,z,w); + vec4.abs(); + ensure("abs:Fail " ,((x == vec4.mV[VX]) && (-y == vec4.mV[VY]) && (-z == vec4.mV[VZ])&& (-w == vec4.mV[VW]))); + vec4.clearVec(); + ensure("isExactlyClear:Fail " ,(true == vec4.isExactlyClear())); + vec4.zeroVec(); + ensure("isExactlyZero:Fail " ,(true == vec4.isExactlyZero())); + } + + template<> template<> + void v4math_object::test<6>() + { + F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f; + LLVector4 vec4(x,y,z,w),vec4a; + vec4a = vec4.scaleVec(vec4); + ensure("scaleVec:Fail " ,(is_approx_equal(x*x, vec4a.mV[VX]) && is_approx_equal(y*y, vec4a.mV[VY]) && is_approx_equal(z*z, vec4a.mV[VZ])&& is_approx_equal(w*w, vec4a.mV[VW]))); + } + + template<> template<> + void v4math_object::test<7>() + { + F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f; + LLVector4 vec4(x,y,z,w); + ensure("1:operator [] failed " ,( x == vec4[0])); + ensure("2:operator [] failed " ,( y == vec4[1])); + ensure("3:operator [] failed " ,( z == vec4[2])); + ensure("4:operator [] failed " ,( w == vec4[3])); + x = 23.f, y = -.2361f, z = 3.25; + vec4.setVec(x,y,z); + F32 &ref1 = vec4[0]; + ensure("5:operator [] failed " ,( ref1 == vec4[0])); + F32 &ref2 = vec4[1]; + ensure("6:operator [] failed " ,( ref2 == vec4[1])); + F32 &ref3 = vec4[2]; + ensure("7:operator [] failed " ,( ref3 == vec4[2])); + F32 &ref4 = vec4[3]; + ensure("8:operator [] failed " ,( ref4 == vec4[3])); + } + + template<> template<> + void v4math_object::test<8>() + { + F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f; + const F32 val[16] = { + 1.f, 2.f, 3.f, 0.f, + .34f, .1f, -.5f, 0.f, + 2.f, 1.23f, 1.234f, 0.f, + .89f, 0.f, 0.f, 0.f + }; + LLMatrix4 mat(val); + LLVector4 vec4(x,y,z,w),vec4a; + vec4.rotVec(mat); + vec4a.setVec(x,y,z,w); + vec4a.rotVec(mat); + ensure_equals("1:rotVec: Fail " ,vec4a, vec4); + F32 a = 2.32f, b = -23.2f, c = -34.1112f, d = 1.010112f; + LLQuaternion q(a,b,c,d); + LLVector4 vec4b(a,b,c,d),vec4c; + vec4b.rotVec(q); + vec4c.setVec(a, b, c, d); + vec4c.rotVec(q); + ensure_equals("2:rotVec: Fail " ,vec4b, vec4c); + } + + template<> template<> + void v4math_object::test<9>() + { + F32 x = 10.f, y = -2.3f, z = -.023f, w = -2.0f; + LLVector4 vec4(x,y,z,w),vec4a;; + std::ostringstream stream1, stream2; + stream1 << vec4; + vec4a.setVec(x,y,z,w); + stream2 << vec4a; + ensure("operator << failed",(stream1.str() == stream2.str())); + } + + template<> template<> + void v4math_object::test<10>() + { + F32 x1 = 1.f, y1 = 2.f, z1 = -1.1f, w1 = .23f; + F32 x2 = 1.2f, y2 = 2.5f, z2 = 1.f, w2 = 1.3f; + LLVector4 vec4(x1,y1,z1,w1),vec4a(x2,y2,z2,w2),vec4b; + vec4b = vec4a + vec4; + ensure("1:operator+:Fail to initialize " ,(is_approx_equal(x1+x2,vec4b.mV[VX]) && is_approx_equal(y1+y2,vec4b.mV[VY]) && is_approx_equal(z1+z2,vec4b.mV[VZ]))); + x1 = -2.45f, y1 = 2.1f, z1 = 3.0f; + vec4.clearVec(); + vec4a.clearVec(); + vec4.setVec(x1,y1,z1); + vec4a +=vec4; + ensure_equals("2:operator+=: Fail to initialize", vec4a,vec4); + vec4a += vec4; + ensure("3:operator+=:Fail to initialize " ,(is_approx_equal(2*x1,vec4a.mV[VX]) && is_approx_equal(2*y1,vec4a.mV[VY]) && is_approx_equal(2*z1,vec4a.mV[VZ]))); + } + template<> template<> + void v4math_object::test<11>() + { + F32 x1 = 1.f, y1 = 2.f, z1 = -1.1f, w1 = .23f; + F32 x2 = 1.2f, y2 = 2.5f, z2 = 1.f, w2 = 1.3f; + LLVector4 vec4(x1,y1,z1,w1),vec4a(x2,y2,z2,w2),vec4b; + vec4b = vec4a - vec4; + ensure("1:operator-:Fail to initialize " ,(is_approx_equal(x2-x1,vec4b.mV[VX]) && is_approx_equal(y2-y1,vec4b.mV[VY]) && is_approx_equal(z2-z1,vec4b.mV[VZ]))); + x1 = -2.45f, y1 = 2.1f, z1 = 3.0f; + vec4.clearVec(); + vec4a.clearVec(); + vec4.setVec(x1,y1,z1); + vec4a -=vec4; + ensure_equals("2:operator-=: Fail to initialize" , vec4a,-vec4); + vec4a -=vec4; + ensure("3:operator-=:Fail to initialize " ,(is_approx_equal(-2*x1,vec4a.mV[VX]) && is_approx_equal(-2*y1,vec4a.mV[VY]) && is_approx_equal(-2*z1,vec4a.mV[VZ]))); + } + + template<> template<> + void v4math_object::test<12>() + { + F32 x1 = 1.f, y1 = 2.f, z1 = -1.1f; + F32 x2 = 1.2f, y2 = 2.5f, z2 = 1.f; + LLVector4 vec4(x1,y1,z1),vec4a(x2,y2,z2); + F32 res = vec4 * vec4a; + ensure("1:operator* failed " ,is_approx_equal(res, x1*x2 + y1*y2 + z1*z2)); + vec4a.clearVec(); + F32 mulVal = 4.2f; + vec4a = vec4 * mulVal; + ensure("2:operator* failed " ,is_approx_equal(x1*mulVal,vec4a.mV[VX]) && is_approx_equal(y1*mulVal, vec4a.mV[VY])&& is_approx_equal(z1*mulVal, vec4a.mV[VZ])); + vec4a.clearVec(); + vec4a = mulVal * vec4 ; + ensure("3:operator* failed " ,is_approx_equal(x1*mulVal, vec4a.mV[VX]) && is_approx_equal(y1*mulVal, vec4a.mV[VY])&& is_approx_equal(z1*mulVal, vec4a.mV[VZ])); + vec4 *= mulVal; + ensure("4:operator*= failed " ,is_approx_equal(x1*mulVal, vec4.mV[VX]) && is_approx_equal(y1*mulVal, vec4.mV[VY])&& is_approx_equal(z1*mulVal, vec4.mV[VZ])); + } + + template<> template<> + void v4math_object::test<13>() + { + F32 x1 = 1.f, y1 = 2.f, z1 = -1.1f; + F32 x2 = 1.2f, y2 = 2.5f, z2 = 1.f; + LLVector4 vec4(x1,y1,z1),vec4a(x2,y2,z2),vec4b; + vec4b = vec4 % vec4a; + ensure("1:operator% failed " ,is_approx_equal(y1*z2 - y2*z1, vec4b.mV[VX]) && is_approx_equal(z1*x2 -z2*x1, vec4b.mV[VY]) && is_approx_equal(x1*y2-x2*y1, vec4b.mV[VZ])); + vec4 %= vec4a; + ensure_equals("operator%= failed " ,vec4,vec4b); + } + + template<> template<> + void v4math_object::test<14>() + { + F32 x = 1.f, y = 2.f, z = -1.1f,div = 4.2f; + F32 t = 1.f / div; + LLVector4 vec4(x,y,z), vec4a; + vec4a = vec4/div; + ensure("1:operator/ failed " ,is_approx_equal(x*t, vec4a.mV[VX]) && is_approx_equal(y*t, vec4a.mV[VY])&& is_approx_equal(z*t, vec4a.mV[VZ])); + x = 1.23f, y = 4.f, z = -2.32f; + vec4.clearVec(); + vec4a.clearVec(); + vec4.setVec(x,y,z); + vec4a = vec4/div; + ensure("2:operator/ failed " ,is_approx_equal(x*t, vec4a.mV[VX]) && is_approx_equal(y*t, vec4a.mV[VY])&& is_approx_equal(z*t, vec4a.mV[VZ])); + vec4 /= div; + ensure("3:operator/ failed " ,is_approx_equal(x*t, vec4.mV[VX]) && is_approx_equal(y*t, vec4.mV[VY])&& is_approx_equal(z*t, vec4.mV[VZ])); + } + + template<> template<> + void v4math_object::test<15>() + { + F32 x = 1.f, y = 2.f, z = -1.1f; + LLVector4 vec4(x,y,z), vec4a; + ensure("operator!= failed " ,(vec4 != vec4a)); + vec4a = vec4; + ensure("operator== failed " ,(vec4 ==vec4a)); + } + + template<> template<> + void v4math_object::test<16>() + { + F32 x = 1.f, y = 2.f, z = -1.1f; + LLVector4 vec4(x,y,z), vec4a; + vec4a = - vec4; + ensure("operator- failed " , (vec4 == - vec4a)); + } + + template<> template<> + void v4math_object::test<17>() + { + F32 x = 1.f, y = 2.f, z = -1.1f,epsilon = .23425f; + LLVector4 vec4(x,y,z), vec4a(x,y,z); + ensure("1:are_parallel: Fail " ,(true == are_parallel(vec4a,vec4,epsilon))); + x = 21.f, y = 12.f, z = -123.1f; + vec4a.clearVec(); + vec4a.setVec(x,y,z); + ensure("2:are_parallel: Fail " ,(false == are_parallel(vec4a,vec4,epsilon))); + } + + template<> template<> + void v4math_object::test<18>() + { + F32 x = 1.f, y = 2.f, z = -1.1f; + F32 angle1, angle2; + LLVector4 vec4(x,y,z), vec4a(x,y,z); + angle1 = angle_between(vec4, vec4a); + vec4.normVec(); + vec4a.normVec(); + angle2 = acos(vec4 * vec4a); + ensure_approximately_equals("1:angle_between: Fail " ,angle1,angle2,8); + F32 x1 = 21.f, y1 = 2.23f, z1 = -1.1f; + LLVector4 vec4b(x,y,z), vec4c(x1,y1,z1); + angle1 = angle_between(vec4b, vec4c); + vec4b.normVec(); + vec4c.normVec(); + angle2 = acos(vec4b * vec4c); + ensure_approximately_equals("2:angle_between: Fail " ,angle1,angle2,8); + } + + template<> template<> + void v4math_object::test<19>() + { + F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, x2 = 1.3f, y2 = 1.f, z2 = 1.f; + F32 val1,val2; + LLVector4 vec4(x1,y1,z1),vec4a(x2,y2,z2); + val1 = dist_vec(vec4,vec4a); + val2 = (F32) sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2)); + ensure_equals("dist_vec: Fail ",val2, val1); + val1 = dist_vec_squared(vec4,vec4a); + val2 =((x1 - x2)*(x1 - x2) + (y1 - y2)* (y1 - y2) + (z1 - z2)* (z1 -z2)); + ensure_equals("dist_vec_squared: Fail ",val2, val1); + } + + template<> template<> + void v4math_object::test<20>() + { + F32 x1 =-2.3f, y1 = 2.f,z1 = 1.2f, w1 = -.23f, x2 = 1.3f, y2 = 1.f, z2 = 1.f,w2 = .12f; + F32 val = 2.3f,val1,val2,val3,val4; + LLVector4 vec4(x1,y1,z1,w1),vec4a(x2,y2,z2,w2); + val1 = x1 + (x2 - x1)* val; + val2 = y1 + (y2 - y1)* val; + val3 = z1 + (z2 - z1)* val; + val4 = w1 + (w2 - w1)* val; + LLVector4 vec4b = lerp(vec4,vec4a,val); + LLVector4 check(val1, val2, val3, val4); + ensure_equals("lerp failed", check, vec4b); + } + + template<> template<> + void v4math_object::test<21>() + { + F32 x = 1.f, y = 2.f, z = -1.1f; + LLVector4 vec4(x,y,z); + LLVector3 vec3 = vec4to3(vec4); + ensure("vec4to3 failed", ((x == vec3.mV[VX])&& (y == vec3.mV[VY]) && (z == vec3.mV[VZ]))); + LLVector4 vec4a = vec3to4(vec3); + ensure_equals("vec3to4 failed",vec4a,vec4); + } + + template<> template<> + void v4math_object::test<22>() + { + F32 x = 1.f, y = 2.f, z = -1.1f; + LLVector4 vec4(x,y,z); + LLSD llsd = vec4.getValue(); + LLVector3 vec3(llsd); + LLVector4 vec4a = vec3to4(vec3); + ensure_equals("getValue failed",vec4a,vec4); + } +} diff --git a/indra/llmath/tests/xform_test.cpp b/indra/llmath/tests/xform_test.cpp index 983ac37574..6d6a64ec4c 100644 --- a/indra/llmath/tests/xform_test.cpp +++ b/indra/llmath/tests/xform_test.cpp @@ -1,245 +1,245 @@ -/**
- * @file xform_test.cpp
- * @author Adroit
- * @date March 2007
- * @brief Test cases for LLXform
- *
- * $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 "../test/lltut.h"
-
-#include "../xform.h"
-
-namespace tut
-{
- struct xform_test
- {
- };
- typedef test_group<xform_test> xform_test_t;
- typedef xform_test_t::object xform_test_object_t;
- tut::xform_test_t tut_xform_test("LLXForm");
-
- //test case for init(), getParent(), getRotation(), getPositionW(), getWorldRotation() fns.
- template<> template<>
- void xform_test_object_t::test<1>()
- {
- LLXform xform_obj;
- LLVector3 emptyVec(0.f,0.f,0.f);
- LLVector3 initialScaleVec(1.f,1.f,1.f);
-
- ensure("LLXform empty constructor failed: ", !xform_obj.getParent() && !xform_obj.isChanged() &&
- xform_obj.getPosition() == emptyVec &&
- (xform_obj.getRotation()).isIdentity() &&
- xform_obj.getScale() == initialScaleVec &&
- xform_obj.getPositionW() == emptyVec &&
- (xform_obj.getWorldRotation()).isIdentity() &&
- !xform_obj.getScaleChildOffset());
- }
-
- // test cases for
- // setScale(const LLVector3& scale)
- // setScale(const F32 x, const F32 y, const F32 z)
- // setRotation(const F32 x, const F32 y, const F32 z)
- // setPosition(const F32 x, const F32 y, const F32 z)
- // getLocalMat4(LLMatrix4 &mat)
- template<> template<>
- void xform_test_object_t::test<2>()
- {
- LLMatrix4 llmat4;
- LLXform xform_obj;
-
- F32 x = 3.6f;
- F32 y = 5.5f;
- F32 z = 4.2f;
- F32 w = 0.f;
- F32 posz = z + 2.122f;
- LLVector3 vec(x, y, z);
- xform_obj.setScale(x, y, z);
- xform_obj.setPosition(x, y, posz);
- ensure("setScale failed: ", xform_obj.getScale() == vec);
-
- vec.setVec(x, y, posz);
- ensure("getPosition failed: ", xform_obj.getPosition() == vec);
-
- x = x * 2.f;
- y = y + 2.3f;
- z = posz * 4.f;
- vec.setVec(x, y, z);
- xform_obj.setPositionX(x);
- xform_obj.setPositionY(y);
- xform_obj.setPositionZ(z);
- ensure("setPositionX/Y/Z failed: ", xform_obj.getPosition() == vec);
-
- xform_obj.setScaleChildOffset(true);
- ensure("setScaleChildOffset failed: ", xform_obj.getScaleChildOffset());
-
- vec.setVec(x, y, z);
-
- xform_obj.addPosition(vec);
- vec += vec;
- ensure("addPosition failed: ", xform_obj.getPosition() == vec);
-
- xform_obj.setScale(vec);
- ensure("setScale vector failed: ", xform_obj.getScale() == vec);
-
- LLQuaternion quat(x, y, z, w);
- xform_obj.setRotation(quat);
- ensure("setRotation quat failed: ", xform_obj.getRotation() == quat);
-
- xform_obj.setRotation(x, y, z, w);
- ensure("getRotation 2 failed: ", xform_obj.getRotation() == quat);
-
- xform_obj.setRotation(x, y, z);
- quat.setQuat(x,y,z);
- ensure("setRotation xyz failed: ", xform_obj.getRotation() == quat);
-
- // LLXform::setRotation(const F32 x, const F32 y, const F32 z)
- // Does normalization
- // LLXform::setRotation(const F32 x, const F32 y, const F32 z, const F32 s)
- // Simply copies the individual values - does not do any normalization.
- // Is that the expected behavior?
- }
-
- // test cases for inline bool setParent(LLXform *parent) and getParent() fn.
- template<> template<>
- void xform_test_object_t::test<3>()
- {
- LLXform xform_obj;
- LLXform par;
- LLXform grandpar;
- xform_obj.setParent(&par);
- par.setParent(&grandpar);
- ensure("setParent/getParent failed: ", &par == xform_obj.getParent());
- ensure("getRoot failed: ", &grandpar == xform_obj.getRoot());
- ensure("isRoot failed: ", grandpar.isRoot() && !par.isRoot() && !xform_obj.isRoot());
- ensure("isRootEdit failed: ", grandpar.isRootEdit() && !par.isRootEdit() && !xform_obj.isRootEdit());
- }
-
- template<> template<>
- void xform_test_object_t::test<4>()
- {
- LLXform xform_obj;
- xform_obj.setChanged(LLXform::TRANSLATED | LLXform::ROTATED | LLXform::SCALED);
- ensure("setChanged/isChanged failed: ", xform_obj.isChanged());
-
- xform_obj.clearChanged(LLXform::TRANSLATED | LLXform::ROTATED | LLXform::SCALED);
- ensure("clearChanged failed: ", !xform_obj.isChanged());
-
- LLVector3 llvect3(12.4f, -5.6f, 0.34f);
- xform_obj.setScale(llvect3);
- ensure("setScale did not set SCALED flag: ", xform_obj.isChanged(LLXform::SCALED));
- xform_obj.setPosition(1.2f, 2.3f, 3.4f);
- ensure("setScale did not set TRANSLATED flag: ", xform_obj.isChanged(LLXform::TRANSLATED));
- ensure("TRANSLATED reset SCALED flag: ", xform_obj.isChanged(LLXform::TRANSLATED | LLXform::SCALED));
- xform_obj.clearChanged(LLXform::SCALED);
- ensure("reset SCALED failed: ", !xform_obj.isChanged(LLXform::SCALED));
- xform_obj.setRotation(1, 2, 3, 4);
- ensure("ROTATION flag not set ", xform_obj.isChanged(LLXform::TRANSLATED | LLXform::ROTATED));
- xform_obj.setScale(llvect3);
- ensure("ROTATION flag not set ", xform_obj.isChanged(LLXform::MOVED));
- }
-
- //to test init() and getWorldMatrix() fns.
- template<> template<>
- void xform_test_object_t::test<5>()
- {
- LLXformMatrix formMatrix_obj;
- formMatrix_obj.init();
- LLMatrix4 mat4_obj;
-
- ensure("1. The value is not NULL", 1.f == formMatrix_obj.getWorldMatrix().mMatrix[0][0]);
- ensure("2. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[0][1]);
- ensure("3. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[0][2]);
- ensure("4. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[0][3]);
- ensure("5. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[1][0]);
- ensure("6. The value is not NULL", 1.f == formMatrix_obj.getWorldMatrix().mMatrix[1][1]);
- ensure("7. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[1][2]);
- ensure("8. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[1][3]);
- ensure("9. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[2][0]);
- ensure("10. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[2][1]);
- ensure("11. The value is not NULL", 1.f == formMatrix_obj.getWorldMatrix().mMatrix[2][2]);
- ensure("12. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[2][3]);
- ensure("13. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[3][0]);
- ensure("14. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[3][1]);
- ensure("15. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[3][2]);
- ensure("16. The value is not NULL", 1.f == formMatrix_obj.getWorldMatrix().mMatrix[3][3]);
- }
-
- //to test mMin.clearVec() and mMax.clearVec() fns
- template<> template<>
- void xform_test_object_t::test<6>()
- {
- LLXformMatrix formMatrix_obj;
- formMatrix_obj.init();
- LLVector3 llmin_vec3;
- LLVector3 llmax_vec3;
- formMatrix_obj.getMinMax(llmin_vec3, llmax_vec3);
- ensure("1. The value is not NULL", 0.f == llmin_vec3.mV[0]);
- ensure("2. The value is not NULL", 0.f == llmin_vec3.mV[1]);
- ensure("3. The value is not NULL", 0.f == llmin_vec3.mV[2]);
- ensure("4. The value is not NULL", 0.f == llmin_vec3.mV[0]);
- ensure("5. The value is not NULL", 0.f == llmin_vec3.mV[1]);
- ensure("6. The value is not NULL", 0.f == llmin_vec3.mV[2]);
- }
-
- //test case of update() fn.
- template<> template<>
- void xform_test_object_t::test<7>()
- {
- LLXformMatrix formMatrix_obj;
-
- LLXformMatrix parent;
- LLVector3 llvecpos(1.0, 2.0, 3.0);
- LLVector3 llvecpospar(10.0, 20.0, 30.0);
- formMatrix_obj.setPosition(llvecpos);
- parent.setPosition(llvecpospar);
-
- LLVector3 llvecparentscale(1.0, 2.0, 0);
- parent.setScaleChildOffset(true);
- parent.setScale(llvecparentscale);
-
- LLQuaternion quat(1, 2, 3, 4);
- LLQuaternion quatparent(5, 6, 7, 8);
- formMatrix_obj.setRotation(quat);
- parent.setRotation(quatparent);
- formMatrix_obj.setParent(&parent);
-
- parent.update();
- formMatrix_obj.update();
-
- LLVector3 worldPos = llvecpos;
- worldPos.scaleVec(llvecparentscale);
- worldPos *= quatparent;
- worldPos += llvecpospar;
-
- LLQuaternion worldRot = quat * quatparent;
-
- ensure("getWorldPosition failed: ", formMatrix_obj.getWorldPosition() == worldPos);
- ensure("getWorldRotation failed: ", formMatrix_obj.getWorldRotation() == worldRot);
-
- ensure("getWorldPosition for parent failed: ", parent.getWorldPosition() == llvecpospar);
- ensure("getWorldRotation for parent failed: ", parent.getWorldRotation() == quatparent);
- }
-}
-
+/** + * @file xform_test.cpp + * @author Adroit + * @date March 2007 + * @brief Test cases for LLXform + * + * $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 "../test/lltut.h" + +#include "../xform.h" + +namespace tut +{ + struct xform_test + { + }; + typedef test_group<xform_test> xform_test_t; + typedef xform_test_t::object xform_test_object_t; + tut::xform_test_t tut_xform_test("LLXForm"); + + //test case for init(), getParent(), getRotation(), getPositionW(), getWorldRotation() fns. + template<> template<> + void xform_test_object_t::test<1>() + { + LLXform xform_obj; + LLVector3 emptyVec(0.f,0.f,0.f); + LLVector3 initialScaleVec(1.f,1.f,1.f); + + ensure("LLXform empty constructor failed: ", !xform_obj.getParent() && !xform_obj.isChanged() && + xform_obj.getPosition() == emptyVec && + (xform_obj.getRotation()).isIdentity() && + xform_obj.getScale() == initialScaleVec && + xform_obj.getPositionW() == emptyVec && + (xform_obj.getWorldRotation()).isIdentity() && + !xform_obj.getScaleChildOffset()); + } + + // test cases for + // setScale(const LLVector3& scale) + // setScale(const F32 x, const F32 y, const F32 z) + // setRotation(const F32 x, const F32 y, const F32 z) + // setPosition(const F32 x, const F32 y, const F32 z) + // getLocalMat4(LLMatrix4 &mat) + template<> template<> + void xform_test_object_t::test<2>() + { + LLMatrix4 llmat4; + LLXform xform_obj; + + F32 x = 3.6f; + F32 y = 5.5f; + F32 z = 4.2f; + F32 w = 0.f; + F32 posz = z + 2.122f; + LLVector3 vec(x, y, z); + xform_obj.setScale(x, y, z); + xform_obj.setPosition(x, y, posz); + ensure("setScale failed: ", xform_obj.getScale() == vec); + + vec.setVec(x, y, posz); + ensure("getPosition failed: ", xform_obj.getPosition() == vec); + + x = x * 2.f; + y = y + 2.3f; + z = posz * 4.f; + vec.setVec(x, y, z); + xform_obj.setPositionX(x); + xform_obj.setPositionY(y); + xform_obj.setPositionZ(z); + ensure("setPositionX/Y/Z failed: ", xform_obj.getPosition() == vec); + + xform_obj.setScaleChildOffset(true); + ensure("setScaleChildOffset failed: ", xform_obj.getScaleChildOffset()); + + vec.setVec(x, y, z); + + xform_obj.addPosition(vec); + vec += vec; + ensure("addPosition failed: ", xform_obj.getPosition() == vec); + + xform_obj.setScale(vec); + ensure("setScale vector failed: ", xform_obj.getScale() == vec); + + LLQuaternion quat(x, y, z, w); + xform_obj.setRotation(quat); + ensure("setRotation quat failed: ", xform_obj.getRotation() == quat); + + xform_obj.setRotation(x, y, z, w); + ensure("getRotation 2 failed: ", xform_obj.getRotation() == quat); + + xform_obj.setRotation(x, y, z); + quat.setQuat(x,y,z); + ensure("setRotation xyz failed: ", xform_obj.getRotation() == quat); + + // LLXform::setRotation(const F32 x, const F32 y, const F32 z) + // Does normalization + // LLXform::setRotation(const F32 x, const F32 y, const F32 z, const F32 s) + // Simply copies the individual values - does not do any normalization. + // Is that the expected behavior? + } + + // test cases for inline bool setParent(LLXform *parent) and getParent() fn. + template<> template<> + void xform_test_object_t::test<3>() + { + LLXform xform_obj; + LLXform par; + LLXform grandpar; + xform_obj.setParent(&par); + par.setParent(&grandpar); + ensure("setParent/getParent failed: ", &par == xform_obj.getParent()); + ensure("getRoot failed: ", &grandpar == xform_obj.getRoot()); + ensure("isRoot failed: ", grandpar.isRoot() && !par.isRoot() && !xform_obj.isRoot()); + ensure("isRootEdit failed: ", grandpar.isRootEdit() && !par.isRootEdit() && !xform_obj.isRootEdit()); + } + + template<> template<> + void xform_test_object_t::test<4>() + { + LLXform xform_obj; + xform_obj.setChanged(LLXform::TRANSLATED | LLXform::ROTATED | LLXform::SCALED); + ensure("setChanged/isChanged failed: ", xform_obj.isChanged()); + + xform_obj.clearChanged(LLXform::TRANSLATED | LLXform::ROTATED | LLXform::SCALED); + ensure("clearChanged failed: ", !xform_obj.isChanged()); + + LLVector3 llvect3(12.4f, -5.6f, 0.34f); + xform_obj.setScale(llvect3); + ensure("setScale did not set SCALED flag: ", xform_obj.isChanged(LLXform::SCALED)); + xform_obj.setPosition(1.2f, 2.3f, 3.4f); + ensure("setScale did not set TRANSLATED flag: ", xform_obj.isChanged(LLXform::TRANSLATED)); + ensure("TRANSLATED reset SCALED flag: ", xform_obj.isChanged(LLXform::TRANSLATED | LLXform::SCALED)); + xform_obj.clearChanged(LLXform::SCALED); + ensure("reset SCALED failed: ", !xform_obj.isChanged(LLXform::SCALED)); + xform_obj.setRotation(1, 2, 3, 4); + ensure("ROTATION flag not set ", xform_obj.isChanged(LLXform::TRANSLATED | LLXform::ROTATED)); + xform_obj.setScale(llvect3); + ensure("ROTATION flag not set ", xform_obj.isChanged(LLXform::MOVED)); + } + + //to test init() and getWorldMatrix() fns. + template<> template<> + void xform_test_object_t::test<5>() + { + LLXformMatrix formMatrix_obj; + formMatrix_obj.init(); + LLMatrix4 mat4_obj; + + ensure("1. The value is not NULL", 1.f == formMatrix_obj.getWorldMatrix().mMatrix[0][0]); + ensure("2. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[0][1]); + ensure("3. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[0][2]); + ensure("4. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[0][3]); + ensure("5. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[1][0]); + ensure("6. The value is not NULL", 1.f == formMatrix_obj.getWorldMatrix().mMatrix[1][1]); + ensure("7. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[1][2]); + ensure("8. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[1][3]); + ensure("9. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[2][0]); + ensure("10. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[2][1]); + ensure("11. The value is not NULL", 1.f == formMatrix_obj.getWorldMatrix().mMatrix[2][2]); + ensure("12. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[2][3]); + ensure("13. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[3][0]); + ensure("14. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[3][1]); + ensure("15. The value is not NULL", 0.f == formMatrix_obj.getWorldMatrix().mMatrix[3][2]); + ensure("16. The value is not NULL", 1.f == formMatrix_obj.getWorldMatrix().mMatrix[3][3]); + } + + //to test mMin.clearVec() and mMax.clearVec() fns + template<> template<> + void xform_test_object_t::test<6>() + { + LLXformMatrix formMatrix_obj; + formMatrix_obj.init(); + LLVector3 llmin_vec3; + LLVector3 llmax_vec3; + formMatrix_obj.getMinMax(llmin_vec3, llmax_vec3); + ensure("1. The value is not NULL", 0.f == llmin_vec3.mV[0]); + ensure("2. The value is not NULL", 0.f == llmin_vec3.mV[1]); + ensure("3. The value is not NULL", 0.f == llmin_vec3.mV[2]); + ensure("4. The value is not NULL", 0.f == llmin_vec3.mV[0]); + ensure("5. The value is not NULL", 0.f == llmin_vec3.mV[1]); + ensure("6. The value is not NULL", 0.f == llmin_vec3.mV[2]); + } + + //test case of update() fn. + template<> template<> + void xform_test_object_t::test<7>() + { + LLXformMatrix formMatrix_obj; + + LLXformMatrix parent; + LLVector3 llvecpos(1.0, 2.0, 3.0); + LLVector3 llvecpospar(10.0, 20.0, 30.0); + formMatrix_obj.setPosition(llvecpos); + parent.setPosition(llvecpospar); + + LLVector3 llvecparentscale(1.0, 2.0, 0); + parent.setScaleChildOffset(true); + parent.setScale(llvecparentscale); + + LLQuaternion quat(1, 2, 3, 4); + LLQuaternion quatparent(5, 6, 7, 8); + formMatrix_obj.setRotation(quat); + parent.setRotation(quatparent); + formMatrix_obj.setParent(&parent); + + parent.update(); + formMatrix_obj.update(); + + LLVector3 worldPos = llvecpos; + worldPos.scaleVec(llvecparentscale); + worldPos *= quatparent; + worldPos += llvecpospar; + + LLQuaternion worldRot = quat * quatparent; + + ensure("getWorldPosition failed: ", formMatrix_obj.getWorldPosition() == worldPos); + ensure("getWorldRotation failed: ", formMatrix_obj.getWorldRotation() == worldRot); + + ensure("getWorldPosition for parent failed: ", parent.getWorldPosition() == llvecpospar); + ensure("getWorldRotation for parent failed: ", parent.getWorldRotation() == quatparent); + } +} + diff --git a/indra/llmath/v2math.cpp b/indra/llmath/v2math.cpp index fd71c337fa..4649e13376 100644 --- a/indra/llmath/v2math.cpp +++ b/indra/llmath/v2math.cpp @@ -1,126 +1,126 @@ -/**
- * @file v2math.cpp
- * @brief LLVector2 class implementation.
- *
- * $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 "linden_common.h"
-
-//#include "vmath.h"
-#include "v2math.h"
-#include "v3math.h"
-#include "v4math.h"
-#include "m4math.h"
-#include "m3math.h"
-#include "llquaternion.h"
-
-// LLVector2
-
-LLVector2 LLVector2::zero(0,0);
-
-
-// Non-member functions
-
-// Sets all values to absolute value of their original values
-// Returns true if data changed
-bool LLVector2::abs()
-{
- bool ret{ false };
-
- if (mV[0] < 0.f) { mV[0] = -mV[0]; ret = true; }
- if (mV[1] < 0.f) { mV[1] = -mV[1]; ret = true; }
-
- return ret;
-}
-
-
-F32 angle_between(const LLVector2& a, const LLVector2& b)
-{
- LLVector2 an = a;
- LLVector2 bn = b;
- an.normVec();
- bn.normVec();
- F32 cosine = an * bn;
- F32 angle = (cosine >= 1.0f) ? 0.0f :
- (cosine <= -1.0f) ? F_PI :
- acos(cosine);
- return angle;
-}
-
-bool are_parallel(const LLVector2 &a, const LLVector2 &b, float epsilon)
-{
- LLVector2 an = a;
- LLVector2 bn = b;
- an.normVec();
- bn.normVec();
- F32 dot = an * bn;
- if ( (1.0f - fabs(dot)) < epsilon)
- {
- return true;
- }
- return false;
-}
-
-
-F32 dist_vec(const LLVector2 &a, const LLVector2 &b)
-{
- F32 x = a.mV[0] - b.mV[0];
- F32 y = a.mV[1] - b.mV[1];
- return (F32) sqrt( x*x + y*y );
-}
-
-F32 dist_vec_squared(const LLVector2 &a, const LLVector2 &b)
-{
- F32 x = a.mV[0] - b.mV[0];
- F32 y = a.mV[1] - b.mV[1];
- return x*x + y*y;
-}
-
-F32 dist_vec_squared2D(const LLVector2 &a, const LLVector2 &b)
-{
- F32 x = a.mV[0] - b.mV[0];
- F32 y = a.mV[1] - b.mV[1];
- return x*x + y*y;
-}
-
-LLVector2 lerp(const LLVector2 &a, const LLVector2 &b, F32 u)
-{
- return LLVector2(
- a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
- a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u );
-}
-
-LLSD LLVector2::getValue() const
-{
- LLSD ret;
- ret[0] = mV[0];
- ret[1] = mV[1];
- return ret;
-}
-
-void LLVector2::setValue(const LLSD& sd)
-{
- mV[0] = (F32) sd[0].asReal();
- mV[1] = (F32) sd[1].asReal();
-}
-
+/** + * @file v2math.cpp + * @brief LLVector2 class implementation. + * + * $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 "linden_common.h" + +//#include "vmath.h" +#include "v2math.h" +#include "v3math.h" +#include "v4math.h" +#include "m4math.h" +#include "m3math.h" +#include "llquaternion.h" + +// LLVector2 + +LLVector2 LLVector2::zero(0,0); + + +// Non-member functions + +// Sets all values to absolute value of their original values +// Returns true if data changed +bool LLVector2::abs() +{ + bool ret{ false }; + + if (mV[0] < 0.f) { mV[0] = -mV[0]; ret = true; } + if (mV[1] < 0.f) { mV[1] = -mV[1]; ret = true; } + + return ret; +} + + +F32 angle_between(const LLVector2& a, const LLVector2& b) +{ + LLVector2 an = a; + LLVector2 bn = b; + an.normVec(); + bn.normVec(); + F32 cosine = an * bn; + F32 angle = (cosine >= 1.0f) ? 0.0f : + (cosine <= -1.0f) ? F_PI : + acos(cosine); + return angle; +} + +bool are_parallel(const LLVector2 &a, const LLVector2 &b, float epsilon) +{ + LLVector2 an = a; + LLVector2 bn = b; + an.normVec(); + bn.normVec(); + F32 dot = an * bn; + if ( (1.0f - fabs(dot)) < epsilon) + { + return true; + } + return false; +} + + +F32 dist_vec(const LLVector2 &a, const LLVector2 &b) +{ + F32 x = a.mV[0] - b.mV[0]; + F32 y = a.mV[1] - b.mV[1]; + return (F32) sqrt( x*x + y*y ); +} + +F32 dist_vec_squared(const LLVector2 &a, const LLVector2 &b) +{ + F32 x = a.mV[0] - b.mV[0]; + F32 y = a.mV[1] - b.mV[1]; + return x*x + y*y; +} + +F32 dist_vec_squared2D(const LLVector2 &a, const LLVector2 &b) +{ + F32 x = a.mV[0] - b.mV[0]; + F32 y = a.mV[1] - b.mV[1]; + return x*x + y*y; +} + +LLVector2 lerp(const LLVector2 &a, const LLVector2 &b, F32 u) +{ + return LLVector2( + a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u, + a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u ); +} + +LLSD LLVector2::getValue() const +{ + LLSD ret; + ret[0] = mV[0]; + ret[1] = mV[1]; + return ret; +} + +void LLVector2::setValue(const LLSD& sd) +{ + mV[0] = (F32) sd[0].asReal(); + mV[1] = (F32) sd[1].asReal(); +} + diff --git a/indra/llmath/v2math.h b/indra/llmath/v2math.h index 327e937675..a61c946304 100644 --- a/indra/llmath/v2math.h +++ b/indra/llmath/v2math.h @@ -1,440 +1,440 @@ -/**
- * @file v2math.h
- * @brief LLVector2 class header file.
- *
- * $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$
- */
-
-#ifndef LL_V2MATH_H
-#define LL_V2MATH_H
-
-#include "llmath.h"
-#include "v3math.h"
-
-class LLVector4;
-class LLMatrix3;
-class LLQuaternion;
-
-// Llvector2 = |x y z w|
-
-static const U32 LENGTHOFVECTOR2 = 2;
-
-class LLVector2
-{
- public:
- F32 mV[LENGTHOFVECTOR2];
-
- static LLVector2 zero;
-
- LLVector2(); // Initializes LLVector2 to (0, 0)
- LLVector2(F32 x, F32 y); // Initializes LLVector2 to (x. y)
- LLVector2(const F32 *vec); // Initializes LLVector2 to (vec[0]. vec[1])
- explicit LLVector2(const LLVector3 &vec); // Initializes LLVector2 to (vec[0]. vec[1])
- explicit LLVector2(const LLSD &sd);
-
- // Clears LLVector2 to (0, 0). DEPRECATED - prefer zeroVec.
- void clear();
- void setZero();
- void clearVec(); // deprecated
- void zeroVec(); // deprecated
-
- void set(F32 x, F32 y); // Sets LLVector2 to (x, y)
- void set(const LLVector2 &vec); // Sets LLVector2 to vec
- void set(const F32 *vec); // Sets LLVector2 to vec
-
- LLSD getValue() const;
- void setValue(const LLSD& sd);
-
- void setVec(F32 x, F32 y); // deprecated
- void setVec(const LLVector2 &vec); // deprecated
- void setVec(const F32 *vec); // deprecated
-
- inline bool isFinite() const; // checks to see if all values of LLVector2 are finite
-
- F32 length() const; // Returns magnitude of LLVector2
- F32 lengthSquared() const; // Returns magnitude squared of LLVector2
- F32 normalize(); // Normalizes and returns the magnitude of LLVector2
-
- F32 magVec() const; // deprecated
- F32 magVecSquared() const; // deprecated
- F32 normVec(); // deprecated
-
- bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed
-
- const LLVector2& scaleVec(const LLVector2& vec); // scales per component by vec
-
- bool isNull(); // Returns true if vector has a _very_small_ length
- bool isExactlyZero() const { return !mV[VX] && !mV[VY]; }
-
- F32 operator[](int idx) const { return mV[idx]; }
- F32 &operator[](int idx) { return mV[idx]; }
-
- friend bool operator<(const LLVector2 &a, const LLVector2 &b); // For sorting. x is "more significant" than y
- friend LLVector2 operator+(const LLVector2 &a, const LLVector2 &b); // Return vector a + b
- friend LLVector2 operator-(const LLVector2 &a, const LLVector2 &b); // Return vector a minus b
- friend F32 operator*(const LLVector2 &a, const LLVector2 &b); // Return a dot b
- friend LLVector2 operator%(const LLVector2 &a, const LLVector2 &b); // Return a cross b
- friend LLVector2 operator/(const LLVector2 &a, F32 k); // Return a divided by scaler k
- friend LLVector2 operator*(const LLVector2 &a, F32 k); // Return a times scaler k
- friend LLVector2 operator*(F32 k, const LLVector2 &a); // Return a times scaler k
- friend bool operator==(const LLVector2 &a, const LLVector2 &b); // Return a == b
- friend bool operator!=(const LLVector2 &a, const LLVector2 &b); // Return a != b
-
- friend const LLVector2& operator+=(LLVector2 &a, const LLVector2 &b); // Return vector a + b
- friend const LLVector2& operator-=(LLVector2 &a, const LLVector2 &b); // Return vector a minus b
- friend const LLVector2& operator%=(LLVector2 &a, const LLVector2 &b); // Return a cross b
- friend const LLVector2& operator*=(LLVector2 &a, F32 k); // Return a times scaler k
- friend const LLVector2& operator/=(LLVector2 &a, F32 k); // Return a divided by scaler k
-
- friend LLVector2 operator-(const LLVector2 &a); // Return vector -a
-
- friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a
-};
-
-
-// Non-member functions
-
-F32 angle_between(const LLVector2 &a, const LLVector2 &b); // Returns angle (radians) between a and b
-bool are_parallel(const LLVector2 &a, const LLVector2 &b, F32 epsilon=F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel
-F32 dist_vec(const LLVector2 &a, const LLVector2 &b); // Returns distance between a and b
-F32 dist_vec_squared(const LLVector2 &a, const LLVector2 &b);// Returns distance squared between a and b
-F32 dist_vec_squared2D(const LLVector2 &a, const LLVector2 &b);// Returns distance squared between a and b ignoring Z component
-LLVector2 lerp(const LLVector2 &a, const LLVector2 &b, F32 u); // Returns a vector that is a linear interpolation between a and b
-
-// Constructors
-
-inline LLVector2::LLVector2(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
-}
-
-inline LLVector2::LLVector2(F32 x, F32 y)
-{
- mV[VX] = x;
- mV[VY] = y;
-}
-
-inline LLVector2::LLVector2(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
-}
-
-inline LLVector2::LLVector2(const LLVector3 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
-}
-
-inline LLVector2::LLVector2(const LLSD &sd)
-{
- setValue(sd);
-}
-
-// Clear and Assignment Functions
-
-inline void LLVector2::clear(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
-}
-
-inline void LLVector2::setZero(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
-}
-
-// deprecated
-inline void LLVector2::clearVec(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
-}
-
-// deprecated
-inline void LLVector2::zeroVec(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
-}
-
-inline void LLVector2::set(F32 x, F32 y)
-{
- mV[VX] = x;
- mV[VY] = y;
-}
-
-inline void LLVector2::set(const LLVector2 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
-}
-
-inline void LLVector2::set(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
-}
-
-
-// deprecated
-inline void LLVector2::setVec(F32 x, F32 y)
-{
- mV[VX] = x;
- mV[VY] = y;
-}
-
-// deprecated
-inline void LLVector2::setVec(const LLVector2 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
-}
-
-// deprecated
-inline void LLVector2::setVec(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
-}
-
-
-// LLVector2 Magnitude and Normalization Functions
-
-inline F32 LLVector2::length(void) const
-{
- return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]);
-}
-
-inline F32 LLVector2::lengthSquared(void) const
-{
- return mV[0]*mV[0] + mV[1]*mV[1];
-}
-
-inline F32 LLVector2::normalize(void)
-{
- F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]);
- F32 oomag;
-
- if (mag > FP_MAG_THRESHOLD)
- {
- oomag = 1.f/mag;
- mV[0] *= oomag;
- mV[1] *= oomag;
- }
- else
- {
- mV[0] = 0.f;
- mV[1] = 0.f;
- mag = 0;
- }
- return (mag);
-}
-
-// checker
-inline bool LLVector2::isFinite() const
-{
- return (llfinite(mV[VX]) && llfinite(mV[VY]));
-}
-
-// deprecated
-inline F32 LLVector2::magVec(void) const
-{
- return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]);
-}
-
-// deprecated
-inline F32 LLVector2::magVecSquared(void) const
-{
- return mV[0]*mV[0] + mV[1]*mV[1];
-}
-
-// deprecated
-inline F32 LLVector2::normVec(void)
-{
- F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]);
- F32 oomag;
-
- if (mag > FP_MAG_THRESHOLD)
- {
- oomag = 1.f/mag;
- mV[0] *= oomag;
- mV[1] *= oomag;
- }
- else
- {
- mV[0] = 0.f;
- mV[1] = 0.f;
- mag = 0;
- }
- return (mag);
-}
-
-inline const LLVector2& LLVector2::scaleVec(const LLVector2& vec)
-{
- mV[VX] *= vec.mV[VX];
- mV[VY] *= vec.mV[VY];
-
- return *this;
-}
-
-inline bool LLVector2::isNull()
-{
- if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] )
- {
- return true;
- }
- return false;
-}
-
-
-// LLVector2 Operators
-
-// For sorting. By convention, x is "more significant" than y.
-inline bool operator<(const LLVector2 &a, const LLVector2 &b)
-{
- if( a.mV[VX] == b.mV[VX] )
- {
- return a.mV[VY] < b.mV[VY];
- }
- else
- {
- return a.mV[VX] < b.mV[VX];
- }
-}
-
-
-inline LLVector2 operator+(const LLVector2 &a, const LLVector2 &b)
-{
- LLVector2 c(a);
- return c += b;
-}
-
-inline LLVector2 operator-(const LLVector2 &a, const LLVector2 &b)
-{
- LLVector2 c(a);
- return c -= b;
-}
-
-inline F32 operator*(const LLVector2 &a, const LLVector2 &b)
-{
- return (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1]);
-}
-
-inline LLVector2 operator%(const LLVector2 &a, const LLVector2 &b)
-{
- return LLVector2(a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1], a.mV[1]*b.mV[0] - b.mV[1]*a.mV[0]);
-}
-
-inline LLVector2 operator/(const LLVector2 &a, F32 k)
-{
- F32 t = 1.f / k;
- return LLVector2( a.mV[0] * t, a.mV[1] * t );
-}
-
-inline LLVector2 operator*(const LLVector2 &a, F32 k)
-{
- return LLVector2( a.mV[0] * k, a.mV[1] * k );
-}
-
-inline LLVector2 operator*(F32 k, const LLVector2 &a)
-{
- return LLVector2( a.mV[0] * k, a.mV[1] * k );
-}
-
-inline bool operator==(const LLVector2 &a, const LLVector2 &b)
-{
- return ( (a.mV[0] == b.mV[0])
- &&(a.mV[1] == b.mV[1]));
-}
-
-inline bool operator!=(const LLVector2 &a, const LLVector2 &b)
-{
- return ( (a.mV[0] != b.mV[0])
- ||(a.mV[1] != b.mV[1]));
-}
-
-inline const LLVector2& operator+=(LLVector2 &a, const LLVector2 &b)
-{
- a.mV[0] += b.mV[0];
- a.mV[1] += b.mV[1];
- return a;
-}
-
-inline const LLVector2& operator-=(LLVector2 &a, const LLVector2 &b)
-{
- a.mV[0] -= b.mV[0];
- a.mV[1] -= b.mV[1];
- return a;
-}
-
-inline const LLVector2& operator%=(LLVector2 &a, const LLVector2 &b)
-{
- LLVector2 ret(a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1], a.mV[1]*b.mV[0] - b.mV[1]*a.mV[0]);
- a = ret;
- return a;
-}
-
-inline const LLVector2& operator*=(LLVector2 &a, F32 k)
-{
- a.mV[0] *= k;
- a.mV[1] *= k;
- return a;
-}
-
-inline const LLVector2& operator/=(LLVector2 &a, F32 k)
-{
- F32 t = 1.f / k;
- a.mV[0] *= t;
- a.mV[1] *= t;
- return a;
-}
-
-inline LLVector2 operator-(const LLVector2 &a)
-{
- return LLVector2( -a.mV[0], -a.mV[1] );
-}
-
-inline void update_min_max(LLVector2& min, LLVector2& max, const LLVector2& pos)
-{
- for (U32 i = 0; i < 2; i++)
- {
- if (min.mV[i] > pos.mV[i])
- {
- min.mV[i] = pos.mV[i];
- }
- if (max.mV[i] < pos.mV[i])
- {
- max.mV[i] = pos.mV[i];
- }
- }
-}
-
-inline std::ostream& operator<<(std::ostream& s, const LLVector2 &a)
-{
- s << "{ " << a.mV[VX] << ", " << a.mV[VY] << " }";
- return s;
-}
-
-#endif
+/** + * @file v2math.h + * @brief LLVector2 class header file. + * + * $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$ + */ + +#ifndef LL_V2MATH_H +#define LL_V2MATH_H + +#include "llmath.h" +#include "v3math.h" + +class LLVector4; +class LLMatrix3; +class LLQuaternion; + +// Llvector2 = |x y z w| + +static const U32 LENGTHOFVECTOR2 = 2; + +class LLVector2 +{ + public: + F32 mV[LENGTHOFVECTOR2]; + + static LLVector2 zero; + + LLVector2(); // Initializes LLVector2 to (0, 0) + LLVector2(F32 x, F32 y); // Initializes LLVector2 to (x. y) + LLVector2(const F32 *vec); // Initializes LLVector2 to (vec[0]. vec[1]) + explicit LLVector2(const LLVector3 &vec); // Initializes LLVector2 to (vec[0]. vec[1]) + explicit LLVector2(const LLSD &sd); + + // Clears LLVector2 to (0, 0). DEPRECATED - prefer zeroVec. + void clear(); + void setZero(); + void clearVec(); // deprecated + void zeroVec(); // deprecated + + void set(F32 x, F32 y); // Sets LLVector2 to (x, y) + void set(const LLVector2 &vec); // Sets LLVector2 to vec + void set(const F32 *vec); // Sets LLVector2 to vec + + LLSD getValue() const; + void setValue(const LLSD& sd); + + void setVec(F32 x, F32 y); // deprecated + void setVec(const LLVector2 &vec); // deprecated + void setVec(const F32 *vec); // deprecated + + inline bool isFinite() const; // checks to see if all values of LLVector2 are finite + + F32 length() const; // Returns magnitude of LLVector2 + F32 lengthSquared() const; // Returns magnitude squared of LLVector2 + F32 normalize(); // Normalizes and returns the magnitude of LLVector2 + + F32 magVec() const; // deprecated + F32 magVecSquared() const; // deprecated + F32 normVec(); // deprecated + + bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed + + const LLVector2& scaleVec(const LLVector2& vec); // scales per component by vec + + bool isNull(); // Returns true if vector has a _very_small_ length + bool isExactlyZero() const { return !mV[VX] && !mV[VY]; } + + F32 operator[](int idx) const { return mV[idx]; } + F32 &operator[](int idx) { return mV[idx]; } + + friend bool operator<(const LLVector2 &a, const LLVector2 &b); // For sorting. x is "more significant" than y + friend LLVector2 operator+(const LLVector2 &a, const LLVector2 &b); // Return vector a + b + friend LLVector2 operator-(const LLVector2 &a, const LLVector2 &b); // Return vector a minus b + friend F32 operator*(const LLVector2 &a, const LLVector2 &b); // Return a dot b + friend LLVector2 operator%(const LLVector2 &a, const LLVector2 &b); // Return a cross b + friend LLVector2 operator/(const LLVector2 &a, F32 k); // Return a divided by scaler k + friend LLVector2 operator*(const LLVector2 &a, F32 k); // Return a times scaler k + friend LLVector2 operator*(F32 k, const LLVector2 &a); // Return a times scaler k + friend bool operator==(const LLVector2 &a, const LLVector2 &b); // Return a == b + friend bool operator!=(const LLVector2 &a, const LLVector2 &b); // Return a != b + + friend const LLVector2& operator+=(LLVector2 &a, const LLVector2 &b); // Return vector a + b + friend const LLVector2& operator-=(LLVector2 &a, const LLVector2 &b); // Return vector a minus b + friend const LLVector2& operator%=(LLVector2 &a, const LLVector2 &b); // Return a cross b + friend const LLVector2& operator*=(LLVector2 &a, F32 k); // Return a times scaler k + friend const LLVector2& operator/=(LLVector2 &a, F32 k); // Return a divided by scaler k + + friend LLVector2 operator-(const LLVector2 &a); // Return vector -a + + friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a +}; + + +// Non-member functions + +F32 angle_between(const LLVector2 &a, const LLVector2 &b); // Returns angle (radians) between a and b +bool are_parallel(const LLVector2 &a, const LLVector2 &b, F32 epsilon=F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel +F32 dist_vec(const LLVector2 &a, const LLVector2 &b); // Returns distance between a and b +F32 dist_vec_squared(const LLVector2 &a, const LLVector2 &b);// Returns distance squared between a and b +F32 dist_vec_squared2D(const LLVector2 &a, const LLVector2 &b);// Returns distance squared between a and b ignoring Z component +LLVector2 lerp(const LLVector2 &a, const LLVector2 &b, F32 u); // Returns a vector that is a linear interpolation between a and b + +// Constructors + +inline LLVector2::LLVector2(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; +} + +inline LLVector2::LLVector2(F32 x, F32 y) +{ + mV[VX] = x; + mV[VY] = y; +} + +inline LLVector2::LLVector2(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; +} + +inline LLVector2::LLVector2(const LLVector3 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; +} + +inline LLVector2::LLVector2(const LLSD &sd) +{ + setValue(sd); +} + +// Clear and Assignment Functions + +inline void LLVector2::clear(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; +} + +inline void LLVector2::setZero(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; +} + +// deprecated +inline void LLVector2::clearVec(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; +} + +// deprecated +inline void LLVector2::zeroVec(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; +} + +inline void LLVector2::set(F32 x, F32 y) +{ + mV[VX] = x; + mV[VY] = y; +} + +inline void LLVector2::set(const LLVector2 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; +} + +inline void LLVector2::set(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; +} + + +// deprecated +inline void LLVector2::setVec(F32 x, F32 y) +{ + mV[VX] = x; + mV[VY] = y; +} + +// deprecated +inline void LLVector2::setVec(const LLVector2 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; +} + +// deprecated +inline void LLVector2::setVec(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; +} + + +// LLVector2 Magnitude and Normalization Functions + +inline F32 LLVector2::length(void) const +{ + return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]); +} + +inline F32 LLVector2::lengthSquared(void) const +{ + return mV[0]*mV[0] + mV[1]*mV[1]; +} + +inline F32 LLVector2::normalize(void) +{ + F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]); + F32 oomag; + + if (mag > FP_MAG_THRESHOLD) + { + oomag = 1.f/mag; + mV[0] *= oomag; + mV[1] *= oomag; + } + else + { + mV[0] = 0.f; + mV[1] = 0.f; + mag = 0; + } + return (mag); +} + +// checker +inline bool LLVector2::isFinite() const +{ + return (llfinite(mV[VX]) && llfinite(mV[VY])); +} + +// deprecated +inline F32 LLVector2::magVec(void) const +{ + return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]); +} + +// deprecated +inline F32 LLVector2::magVecSquared(void) const +{ + return mV[0]*mV[0] + mV[1]*mV[1]; +} + +// deprecated +inline F32 LLVector2::normVec(void) +{ + F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1]); + F32 oomag; + + if (mag > FP_MAG_THRESHOLD) + { + oomag = 1.f/mag; + mV[0] *= oomag; + mV[1] *= oomag; + } + else + { + mV[0] = 0.f; + mV[1] = 0.f; + mag = 0; + } + return (mag); +} + +inline const LLVector2& LLVector2::scaleVec(const LLVector2& vec) +{ + mV[VX] *= vec.mV[VX]; + mV[VY] *= vec.mV[VY]; + + return *this; +} + +inline bool LLVector2::isNull() +{ + if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] ) + { + return true; + } + return false; +} + + +// LLVector2 Operators + +// For sorting. By convention, x is "more significant" than y. +inline bool operator<(const LLVector2 &a, const LLVector2 &b) +{ + if( a.mV[VX] == b.mV[VX] ) + { + return a.mV[VY] < b.mV[VY]; + } + else + { + return a.mV[VX] < b.mV[VX]; + } +} + + +inline LLVector2 operator+(const LLVector2 &a, const LLVector2 &b) +{ + LLVector2 c(a); + return c += b; +} + +inline LLVector2 operator-(const LLVector2 &a, const LLVector2 &b) +{ + LLVector2 c(a); + return c -= b; +} + +inline F32 operator*(const LLVector2 &a, const LLVector2 &b) +{ + return (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1]); +} + +inline LLVector2 operator%(const LLVector2 &a, const LLVector2 &b) +{ + return LLVector2(a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1], a.mV[1]*b.mV[0] - b.mV[1]*a.mV[0]); +} + +inline LLVector2 operator/(const LLVector2 &a, F32 k) +{ + F32 t = 1.f / k; + return LLVector2( a.mV[0] * t, a.mV[1] * t ); +} + +inline LLVector2 operator*(const LLVector2 &a, F32 k) +{ + return LLVector2( a.mV[0] * k, a.mV[1] * k ); +} + +inline LLVector2 operator*(F32 k, const LLVector2 &a) +{ + return LLVector2( a.mV[0] * k, a.mV[1] * k ); +} + +inline bool operator==(const LLVector2 &a, const LLVector2 &b) +{ + return ( (a.mV[0] == b.mV[0]) + &&(a.mV[1] == b.mV[1])); +} + +inline bool operator!=(const LLVector2 &a, const LLVector2 &b) +{ + return ( (a.mV[0] != b.mV[0]) + ||(a.mV[1] != b.mV[1])); +} + +inline const LLVector2& operator+=(LLVector2 &a, const LLVector2 &b) +{ + a.mV[0] += b.mV[0]; + a.mV[1] += b.mV[1]; + return a; +} + +inline const LLVector2& operator-=(LLVector2 &a, const LLVector2 &b) +{ + a.mV[0] -= b.mV[0]; + a.mV[1] -= b.mV[1]; + return a; +} + +inline const LLVector2& operator%=(LLVector2 &a, const LLVector2 &b) +{ + LLVector2 ret(a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1], a.mV[1]*b.mV[0] - b.mV[1]*a.mV[0]); + a = ret; + return a; +} + +inline const LLVector2& operator*=(LLVector2 &a, F32 k) +{ + a.mV[0] *= k; + a.mV[1] *= k; + return a; +} + +inline const LLVector2& operator/=(LLVector2 &a, F32 k) +{ + F32 t = 1.f / k; + a.mV[0] *= t; + a.mV[1] *= t; + return a; +} + +inline LLVector2 operator-(const LLVector2 &a) +{ + return LLVector2( -a.mV[0], -a.mV[1] ); +} + +inline void update_min_max(LLVector2& min, LLVector2& max, const LLVector2& pos) +{ + for (U32 i = 0; i < 2; i++) + { + if (min.mV[i] > pos.mV[i]) + { + min.mV[i] = pos.mV[i]; + } + if (max.mV[i] < pos.mV[i]) + { + max.mV[i] = pos.mV[i]; + } + } +} + +inline std::ostream& operator<<(std::ostream& s, const LLVector2 &a) +{ + s << "{ " << a.mV[VX] << ", " << a.mV[VY] << " }"; + return s; +} + +#endif diff --git a/indra/llmath/v3dmath.cpp b/indra/llmath/v3dmath.cpp index faa877a5cb..bb55c812b5 100644 --- a/indra/llmath/v3dmath.cpp +++ b/indra/llmath/v3dmath.cpp @@ -1,147 +1,147 @@ -/**
- * @file v3dmath.cpp
- * @brief LLVector3d class implementation.
- *
- * $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 "linden_common.h"
-
-//#include <sstream> // gcc 2.95.2 doesn't support sstream
-
-#include "v3dmath.h"
-
-//#include "vmath.h"
-#include "v4math.h"
-#include "m4math.h"
-#include "m3math.h"
-#include "llquaternion.h"
-#include "llquantize.h"
-
-// LLVector3d
-// WARNING: Don't use these for global const definitions!
-// For example:
-// const LLQuaternion(0.5f * F_PI, LLVector3d::zero);
-// at the top of a *.cpp file might not give you what you think.
-const LLVector3d LLVector3d::zero(0,0,0);
-const LLVector3d LLVector3d::x_axis(1, 0, 0);
-const LLVector3d LLVector3d::y_axis(0, 1, 0);
-const LLVector3d LLVector3d::z_axis(0, 0, 1);
-const LLVector3d LLVector3d::x_axis_neg(-1, 0, 0);
-const LLVector3d LLVector3d::y_axis_neg(0, -1, 0);
-const LLVector3d LLVector3d::z_axis_neg(0, 0, -1);
-
-
-// Clamps each values to range (min,max).
-// Returns true if data changed.
-bool LLVector3d::clamp(F64 min, F64 max)
-{
- bool ret{ false };
-
- if (mdV[0] < min) { mdV[0] = min; ret = true; }
- if (mdV[1] < min) { mdV[1] = min; ret = true; }
- if (mdV[2] < min) { mdV[2] = min; ret = true; }
-
- if (mdV[0] > max) { mdV[0] = max; ret = true; }
- if (mdV[1] > max) { mdV[1] = max; ret = true; }
- if (mdV[2] > max) { mdV[2] = max; ret = true; }
-
- return ret;
-}
-
-// Sets all values to absolute value of their original values
-// Returns true if data changed
-bool LLVector3d::abs()
-{
- bool ret{ false };
-
- if (mdV[0] < 0.0) { mdV[0] = -mdV[0]; ret = true; }
- if (mdV[1] < 0.0) { mdV[1] = -mdV[1]; ret = true; }
- if (mdV[2] < 0.0) { mdV[2] = -mdV[2]; ret = true; }
-
- return ret;
-}
-
-std::ostream& operator<<(std::ostream& s, const LLVector3d &a)
-{
- s << "{ " << a.mdV[VX] << ", " << a.mdV[VY] << ", " << a.mdV[VZ] << " }";
- return s;
-}
-
-const LLVector3d& LLVector3d::operator=(const LLVector4 &a)
-{
- mdV[0] = a.mV[0];
- mdV[1] = a.mV[1];
- mdV[2] = a.mV[2];
- return *this;
-}
-
-const LLVector3d& LLVector3d::rotVec(const LLMatrix3 &mat)
-{
- *this = *this * mat;
- return *this;
-}
-
-const LLVector3d& LLVector3d::rotVec(const LLQuaternion &q)
-{
- *this = *this * q;
- return *this;
-}
-
-const LLVector3d& LLVector3d::rotVec(F64 angle, const LLVector3d &vec)
-{
- if ( !vec.isExactlyZero() && angle )
- {
- *this = *this * LLMatrix3((F32)angle, vec);
- }
- return *this;
-}
-
-const LLVector3d& LLVector3d::rotVec(F64 angle, F64 x, F64 y, F64 z)
-{
- LLVector3d vec(x, y, z);
- if ( !vec.isExactlyZero() && angle )
- {
- *this = *this * LLMatrix3((F32)angle, vec);
- }
- return *this;
-}
-
-
-bool LLVector3d::parseVector3d(const std::string& buf, LLVector3d* value)
-{
- if( buf.empty() || value == nullptr)
- {
- return false;
- }
-
- LLVector3d v;
- S32 count = sscanf( buf.c_str(), "%lf %lf %lf", v.mdV + 0, v.mdV + 1, v.mdV + 2 );
- if( 3 == count )
- {
- value->setVec( v );
- return true;
- }
-
- return false;
-}
-
+/** + * @file v3dmath.cpp + * @brief LLVector3d class implementation. + * + * $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 "linden_common.h" + +//#include <sstream> // gcc 2.95.2 doesn't support sstream + +#include "v3dmath.h" + +//#include "vmath.h" +#include "v4math.h" +#include "m4math.h" +#include "m3math.h" +#include "llquaternion.h" +#include "llquantize.h" + +// LLVector3d +// WARNING: Don't use these for global const definitions! +// For example: +// const LLQuaternion(0.5f * F_PI, LLVector3d::zero); +// at the top of a *.cpp file might not give you what you think. +const LLVector3d LLVector3d::zero(0,0,0); +const LLVector3d LLVector3d::x_axis(1, 0, 0); +const LLVector3d LLVector3d::y_axis(0, 1, 0); +const LLVector3d LLVector3d::z_axis(0, 0, 1); +const LLVector3d LLVector3d::x_axis_neg(-1, 0, 0); +const LLVector3d LLVector3d::y_axis_neg(0, -1, 0); +const LLVector3d LLVector3d::z_axis_neg(0, 0, -1); + + +// Clamps each values to range (min,max). +// Returns true if data changed. +bool LLVector3d::clamp(F64 min, F64 max) +{ + bool ret{ false }; + + if (mdV[0] < min) { mdV[0] = min; ret = true; } + if (mdV[1] < min) { mdV[1] = min; ret = true; } + if (mdV[2] < min) { mdV[2] = min; ret = true; } + + if (mdV[0] > max) { mdV[0] = max; ret = true; } + if (mdV[1] > max) { mdV[1] = max; ret = true; } + if (mdV[2] > max) { mdV[2] = max; ret = true; } + + return ret; +} + +// Sets all values to absolute value of their original values +// Returns true if data changed +bool LLVector3d::abs() +{ + bool ret{ false }; + + if (mdV[0] < 0.0) { mdV[0] = -mdV[0]; ret = true; } + if (mdV[1] < 0.0) { mdV[1] = -mdV[1]; ret = true; } + if (mdV[2] < 0.0) { mdV[2] = -mdV[2]; ret = true; } + + return ret; +} + +std::ostream& operator<<(std::ostream& s, const LLVector3d &a) +{ + s << "{ " << a.mdV[VX] << ", " << a.mdV[VY] << ", " << a.mdV[VZ] << " }"; + return s; +} + +const LLVector3d& LLVector3d::operator=(const LLVector4 &a) +{ + mdV[0] = a.mV[0]; + mdV[1] = a.mV[1]; + mdV[2] = a.mV[2]; + return *this; +} + +const LLVector3d& LLVector3d::rotVec(const LLMatrix3 &mat) +{ + *this = *this * mat; + return *this; +} + +const LLVector3d& LLVector3d::rotVec(const LLQuaternion &q) +{ + *this = *this * q; + return *this; +} + +const LLVector3d& LLVector3d::rotVec(F64 angle, const LLVector3d &vec) +{ + if ( !vec.isExactlyZero() && angle ) + { + *this = *this * LLMatrix3((F32)angle, vec); + } + return *this; +} + +const LLVector3d& LLVector3d::rotVec(F64 angle, F64 x, F64 y, F64 z) +{ + LLVector3d vec(x, y, z); + if ( !vec.isExactlyZero() && angle ) + { + *this = *this * LLMatrix3((F32)angle, vec); + } + return *this; +} + + +bool LLVector3d::parseVector3d(const std::string& buf, LLVector3d* value) +{ + if( buf.empty() || value == nullptr) + { + return false; + } + + LLVector3d v; + S32 count = sscanf( buf.c_str(), "%lf %lf %lf", v.mdV + 0, v.mdV + 1, v.mdV + 2 ); + if( 3 == count ) + { + value->setVec( v ); + return true; + } + + return false; +} + diff --git a/indra/llmath/v3dmath.h b/indra/llmath/v3dmath.h index 2968bcd511..ece8c54ea4 100644 --- a/indra/llmath/v3dmath.h +++ b/indra/llmath/v3dmath.h @@ -1,530 +1,530 @@ -/**
- * @file v3dmath.h
- * @brief High precision 3 dimensional vector.
- *
- * $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$
- */
-
-#ifndef LL_V3DMATH_H
-#define LL_V3DMATH_H
-
-#include "llerror.h"
-#include "v3math.h"
-
-class LLVector3d
-{
- public:
- F64 mdV[3];
-
- const static LLVector3d zero;
- const static LLVector3d x_axis;
- const static LLVector3d y_axis;
- const static LLVector3d z_axis;
- const static LLVector3d x_axis_neg;
- const static LLVector3d y_axis_neg;
- const static LLVector3d z_axis_neg;
-
- inline LLVector3d(); // Initializes LLVector3d to (0, 0, 0)
- inline LLVector3d(const F64 x, const F64 y, const F64 z); // Initializes LLVector3d to (x. y, z)
- inline explicit LLVector3d(const F64 *vec); // Initializes LLVector3d to (vec[0]. vec[1], vec[2])
- inline explicit LLVector3d(const LLVector3 &vec);
- explicit LLVector3d(const LLSD& sd)
- {
- setValue(sd);
- }
-
- void setValue(const LLSD& sd)
- {
- mdV[0] = sd[0].asReal();
- mdV[1] = sd[1].asReal();
- mdV[2] = sd[2].asReal();
- }
-
- LLSD getValue() const
- {
- LLSD ret;
- ret[0] = mdV[0];
- ret[1] = mdV[1];
- ret[2] = mdV[2];
- return ret;
- }
-
- inline bool isFinite() const; // checks to see if all values of LLVector3d are finite
- bool clamp(const F64 min, const F64 max); // Clamps all values to (min,max), returns true if data changed
- bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed
-
- inline const LLVector3d& clear(); // Clears LLVector3d to (0, 0, 0, 1)
- inline const LLVector3d& clearVec(); // deprecated
- inline const LLVector3d& setZero(); // Zero LLVector3d to (0, 0, 0, 0)
- inline const LLVector3d& zeroVec(); // deprecated
- inline const LLVector3d& set(const F64 x, const F64 y, const F64 z); // Sets LLVector3d to (x, y, z, 1)
- inline const LLVector3d& set(const LLVector3d &vec); // Sets LLVector3d to vec
- inline const LLVector3d& set(const F64 *vec); // Sets LLVector3d to vec
- inline const LLVector3d& set(const LLVector3 &vec);
- inline const LLVector3d& setVec(const F64 x, const F64 y, const F64 z); // deprecated
- inline const LLVector3d& setVec(const LLVector3d &vec); // deprecated
- inline const LLVector3d& setVec(const F64 *vec); // deprecated
- inline const LLVector3d& setVec(const LLVector3 &vec); // deprecated
-
- F64 magVec() const; // deprecated
- F64 magVecSquared() const; // deprecated
- inline F64 normVec(); // deprecated
-
- F64 length() const; // Returns magnitude of LLVector3d
- F64 lengthSquared() const; // Returns magnitude squared of LLVector3d
- inline F64 normalize(); // Normalizes and returns the magnitude of LLVector3d
-
- const LLVector3d& rotVec(const F64 angle, const LLVector3d &vec); // Rotates about vec by angle radians
- const LLVector3d& rotVec(const F64 angle, const F64 x, const F64 y, const F64 z); // Rotates about x,y,z by angle radians
- const LLVector3d& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat
- const LLVector3d& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q
-
- bool isNull() const; // Returns true if vector has a _very_small_ length
- bool isExactlyZero() const { return !mdV[VX] && !mdV[VY] && !mdV[VZ]; }
-
- const LLVector3d& operator=(const LLVector4 &a);
-
- F64 operator[](int idx) const { return mdV[idx]; }
- F64 &operator[](int idx) { return mdV[idx]; }
-
- friend LLVector3d operator+(const LLVector3d& a, const LLVector3d& b); // Return vector a + b
- friend LLVector3d operator-(const LLVector3d& a, const LLVector3d& b); // Return vector a minus b
- friend F64 operator*(const LLVector3d& a, const LLVector3d& b); // Return a dot b
- friend LLVector3d operator%(const LLVector3d& a, const LLVector3d& b); // Return a cross b
- friend LLVector3d operator*(const LLVector3d& a, const F64 k); // Return a times scaler k
- friend LLVector3d operator/(const LLVector3d& a, const F64 k); // Return a divided by scaler k
- friend LLVector3d operator*(const F64 k, const LLVector3d& a); // Return a times scaler k
- friend bool operator==(const LLVector3d& a, const LLVector3d& b); // Return a == b
- friend bool operator!=(const LLVector3d& a, const LLVector3d& b); // Return a != b
-
- friend const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b); // Return vector a + b
- friend const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b); // Return vector a minus b
- friend const LLVector3d& operator%=(LLVector3d& a, const LLVector3d& b); // Return a cross b
- friend const LLVector3d& operator*=(LLVector3d& a, const F64 k); // Return a times scaler k
- friend const LLVector3d& operator/=(LLVector3d& a, const F64 k); // Return a divided by scaler k
-
- friend LLVector3d operator-(const LLVector3d& a); // Return vector -a
-
- friend std::ostream& operator<<(std::ostream& s, const LLVector3d& a); // Stream a
-
- static bool parseVector3d(const std::string& buf, LLVector3d* value);
-
-};
-
-typedef LLVector3d LLGlobalVec;
-
-inline const LLVector3d &LLVector3d::set(const LLVector3 &vec)
-{
- mdV[0] = vec.mV[0];
- mdV[1] = vec.mV[1];
- mdV[2] = vec.mV[2];
- return *this;
-}
-
-inline const LLVector3d &LLVector3d::setVec(const LLVector3 &vec)
-{
- mdV[0] = vec.mV[0];
- mdV[1] = vec.mV[1];
- mdV[2] = vec.mV[2];
- return *this;
-}
-
-
-inline LLVector3d::LLVector3d(void)
-{
- mdV[0] = 0.f;
- mdV[1] = 0.f;
- mdV[2] = 0.f;
-}
-
-inline LLVector3d::LLVector3d(const F64 x, const F64 y, const F64 z)
-{
- mdV[VX] = x;
- mdV[VY] = y;
- mdV[VZ] = z;
-}
-
-inline LLVector3d::LLVector3d(const F64 *vec)
-{
- mdV[VX] = vec[VX];
- mdV[VY] = vec[VY];
- mdV[VZ] = vec[VZ];
-}
-
-inline LLVector3d::LLVector3d(const LLVector3 &vec)
-{
- mdV[VX] = vec.mV[VX];
- mdV[VY] = vec.mV[VY];
- mdV[VZ] = vec.mV[VZ];
-}
-
-/*
-inline LLVector3d::LLVector3d(const LLVector3d ©)
-{
- mdV[VX] = copy.mdV[VX];
- mdV[VY] = copy.mdV[VY];
- mdV[VZ] = copy.mdV[VZ];
-}
-*/
-
-// Destructors
-
-// checker
-inline bool LLVector3d::isFinite() const
-{
- return (llfinite(mdV[VX]) && llfinite(mdV[VY]) && llfinite(mdV[VZ]));
-}
-
-
-// Clear and Assignment Functions
-
-inline const LLVector3d& LLVector3d::clear(void)
-{
- mdV[0] = 0.f;
- mdV[1] = 0.f;
- mdV[2]= 0.f;
- return (*this);
-}
-
-inline const LLVector3d& LLVector3d::clearVec(void)
-{
- mdV[0] = 0.f;
- mdV[1] = 0.f;
- mdV[2]= 0.f;
- return (*this);
-}
-
-inline const LLVector3d& LLVector3d::setZero(void)
-{
- mdV[0] = 0.f;
- mdV[1] = 0.f;
- mdV[2] = 0.f;
- return (*this);
-}
-
-inline const LLVector3d& LLVector3d::zeroVec(void)
-{
- mdV[0] = 0.f;
- mdV[1] = 0.f;
- mdV[2] = 0.f;
- return (*this);
-}
-
-inline const LLVector3d& LLVector3d::set(const F64 x, const F64 y, const F64 z)
-{
- mdV[VX] = x;
- mdV[VY] = y;
- mdV[VZ] = z;
- return (*this);
-}
-
-inline const LLVector3d& LLVector3d::set(const LLVector3d &vec)
-{
- mdV[0] = vec.mdV[0];
- mdV[1] = vec.mdV[1];
- mdV[2] = vec.mdV[2];
- return (*this);
-}
-
-inline const LLVector3d& LLVector3d::set(const F64 *vec)
-{
- mdV[0] = vec[0];
- mdV[1] = vec[1];
- mdV[2] = vec[2];
- return (*this);
-}
-
-inline const LLVector3d& LLVector3d::setVec(const F64 x, const F64 y, const F64 z)
-{
- mdV[VX] = x;
- mdV[VY] = y;
- mdV[VZ] = z;
- return (*this);
-}
-
-inline const LLVector3d& LLVector3d::setVec(const LLVector3d &vec)
-{
- mdV[0] = vec.mdV[0];
- mdV[1] = vec.mdV[1];
- mdV[2] = vec.mdV[2];
- return (*this);
-}
-
-inline const LLVector3d& LLVector3d::setVec(const F64 *vec)
-{
- mdV[0] = vec[0];
- mdV[1] = vec[1];
- mdV[2] = vec[2];
- return (*this);
-}
-
-inline F64 LLVector3d::normVec(void)
-{
- F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
- F64 oomag;
-
- if (mag > FP_MAG_THRESHOLD)
- {
- oomag = 1.f/mag;
- mdV[0] *= oomag;
- mdV[1] *= oomag;
- mdV[2] *= oomag;
- }
- else
- {
- mdV[0] = 0.f;
- mdV[1] = 0.f;
- mdV[2] = 0.f;
- mag = 0;
- }
- return (mag);
-}
-
-inline F64 LLVector3d::normalize(void)
-{
- F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
- F64 oomag;
-
- if (mag > FP_MAG_THRESHOLD)
- {
- oomag = 1.f/mag;
- mdV[0] *= oomag;
- mdV[1] *= oomag;
- mdV[2] *= oomag;
- }
- else
- {
- mdV[0] = 0.f;
- mdV[1] = 0.f;
- mdV[2] = 0.f;
- mag = 0;
- }
- return (mag);
-}
-
-// LLVector3d Magnitude and Normalization Functions
-
-inline F64 LLVector3d::magVec(void) const
-{
- return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
-}
-
-inline F64 LLVector3d::magVecSquared(void) const
-{
- return mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2];
-}
-
-inline F64 LLVector3d::length(void) const
-{
- return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
-}
-
-inline F64 LLVector3d::lengthSquared(void) const
-{
- return mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2];
-}
-
-inline LLVector3d operator+(const LLVector3d& a, const LLVector3d& b)
-{
- LLVector3d c(a);
- return c += b;
-}
-
-inline LLVector3d operator-(const LLVector3d& a, const LLVector3d& b)
-{
- LLVector3d c(a);
- return c -= b;
-}
-
-inline F64 operator*(const LLVector3d& a, const LLVector3d& b)
-{
- return (a.mdV[0]*b.mdV[0] + a.mdV[1]*b.mdV[1] + a.mdV[2]*b.mdV[2]);
-}
-
-inline LLVector3d operator%(const LLVector3d& a, const LLVector3d& b)
-{
- return LLVector3d( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1] );
-}
-
-inline LLVector3d operator/(const LLVector3d& a, const F64 k)
-{
- F64 t = 1.f / k;
- return LLVector3d( a.mdV[0] * t, a.mdV[1] * t, a.mdV[2] * t );
-}
-
-inline LLVector3d operator*(const LLVector3d& a, const F64 k)
-{
- return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k );
-}
-
-inline LLVector3d operator*(F64 k, const LLVector3d& a)
-{
- return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k );
-}
-
-inline bool operator==(const LLVector3d& a, const LLVector3d& b)
-{
- return ( (a.mdV[0] == b.mdV[0])
- &&(a.mdV[1] == b.mdV[1])
- &&(a.mdV[2] == b.mdV[2]));
-}
-
-inline bool operator!=(const LLVector3d& a, const LLVector3d& b)
-{
- return ( (a.mdV[0] != b.mdV[0])
- ||(a.mdV[1] != b.mdV[1])
- ||(a.mdV[2] != b.mdV[2]));
-}
-
-inline const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b)
-{
- a.mdV[0] += b.mdV[0];
- a.mdV[1] += b.mdV[1];
- a.mdV[2] += b.mdV[2];
- return a;
-}
-
-inline const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b)
-{
- a.mdV[0] -= b.mdV[0];
- a.mdV[1] -= b.mdV[1];
- a.mdV[2] -= b.mdV[2];
- return a;
-}
-
-inline const LLVector3d& operator%=(LLVector3d& a, const LLVector3d& b)
-{
- LLVector3d ret( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1]);
- a = ret;
- return a;
-}
-
-inline const LLVector3d& operator*=(LLVector3d& a, const F64 k)
-{
- a.mdV[0] *= k;
- a.mdV[1] *= k;
- a.mdV[2] *= k;
- return a;
-}
-
-inline const LLVector3d& operator/=(LLVector3d& a, const F64 k)
-{
- F64 t = 1.f / k;
- a.mdV[0] *= t;
- a.mdV[1] *= t;
- a.mdV[2] *= t;
- return a;
-}
-
-inline LLVector3d operator-(const LLVector3d& a)
-{
- return LLVector3d( -a.mdV[0], -a.mdV[1], -a.mdV[2] );
-}
-
-inline F64 dist_vec(const LLVector3d& a, const LLVector3d& b)
-{
- F64 x = a.mdV[0] - b.mdV[0];
- F64 y = a.mdV[1] - b.mdV[1];
- F64 z = a.mdV[2] - b.mdV[2];
- return (F32) sqrt( x*x + y*y + z*z );
-}
-
-inline F64 dist_vec_squared(const LLVector3d& a, const LLVector3d& b)
-{
- F64 x = a.mdV[0] - b.mdV[0];
- F64 y = a.mdV[1] - b.mdV[1];
- F64 z = a.mdV[2] - b.mdV[2];
- return x*x + y*y + z*z;
-}
-
-inline F64 dist_vec_squared2D(const LLVector3d& a, const LLVector3d& b)
-{
- F64 x = a.mdV[0] - b.mdV[0];
- F64 y = a.mdV[1] - b.mdV[1];
- return x*x + y*y;
-}
-
-inline LLVector3d lerp(const LLVector3d& a, const LLVector3d& b, const F64 u)
-{
- return LLVector3d(
- a.mdV[VX] + (b.mdV[VX] - a.mdV[VX]) * u,
- a.mdV[VY] + (b.mdV[VY] - a.mdV[VY]) * u,
- a.mdV[VZ] + (b.mdV[VZ] - a.mdV[VZ]) * u);
-}
-
-
-inline bool LLVector3d::isNull() const
-{
- if ( F_APPROXIMATELY_ZERO > mdV[VX]*mdV[VX] + mdV[VY]*mdV[VY] + mdV[VZ]*mdV[VZ] )
- {
- return true;
- }
- return false;
-}
-
-
-inline F64 angle_between(const LLVector3d& a, const LLVector3d& b)
-{
- LLVector3d an = a;
- LLVector3d bn = b;
- an.normalize();
- bn.normalize();
- F64 cosine = an * bn;
- F64 angle = (cosine >= 1.0f) ? 0.0f :
- (cosine <= -1.0f) ? F_PI :
- acos(cosine);
- return angle;
-}
-
-inline bool are_parallel(const LLVector3d& a, const LLVector3d& b, const F64 epsilon)
-{
- LLVector3d an = a;
- LLVector3d bn = b;
- an.normalize();
- bn.normalize();
- F64 dot = an * bn;
- if ( (1.0f - fabs(dot)) < epsilon)
- {
- return true;
- }
- return false;
-}
-
-inline LLVector3d projected_vec(const LLVector3d& a, const LLVector3d& b)
-{
- LLVector3d project_axis = b;
- project_axis.normalize();
- return project_axis * (a * project_axis);
-}
-
-inline LLVector3d inverse_projected_vec(const LLVector3d& a, const LLVector3d& b)
-{
- LLVector3d normalized_a = a;
- normalized_a.normalize();
- LLVector3d normalized_b = b;
- F64 b_length = normalized_b.normalize();
-
- F64 dot_product = normalized_a * normalized_b;
- return normalized_a * (b_length / dot_product);
-}
-
-#endif // LL_V3DMATH_H
+/** + * @file v3dmath.h + * @brief High precision 3 dimensional vector. + * + * $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$ + */ + +#ifndef LL_V3DMATH_H +#define LL_V3DMATH_H + +#include "llerror.h" +#include "v3math.h" + +class LLVector3d +{ + public: + F64 mdV[3]; + + const static LLVector3d zero; + const static LLVector3d x_axis; + const static LLVector3d y_axis; + const static LLVector3d z_axis; + const static LLVector3d x_axis_neg; + const static LLVector3d y_axis_neg; + const static LLVector3d z_axis_neg; + + inline LLVector3d(); // Initializes LLVector3d to (0, 0, 0) + inline LLVector3d(const F64 x, const F64 y, const F64 z); // Initializes LLVector3d to (x. y, z) + inline explicit LLVector3d(const F64 *vec); // Initializes LLVector3d to (vec[0]. vec[1], vec[2]) + inline explicit LLVector3d(const LLVector3 &vec); + explicit LLVector3d(const LLSD& sd) + { + setValue(sd); + } + + void setValue(const LLSD& sd) + { + mdV[0] = sd[0].asReal(); + mdV[1] = sd[1].asReal(); + mdV[2] = sd[2].asReal(); + } + + LLSD getValue() const + { + LLSD ret; + ret[0] = mdV[0]; + ret[1] = mdV[1]; + ret[2] = mdV[2]; + return ret; + } + + inline bool isFinite() const; // checks to see if all values of LLVector3d are finite + bool clamp(const F64 min, const F64 max); // Clamps all values to (min,max), returns true if data changed + bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed + + inline const LLVector3d& clear(); // Clears LLVector3d to (0, 0, 0, 1) + inline const LLVector3d& clearVec(); // deprecated + inline const LLVector3d& setZero(); // Zero LLVector3d to (0, 0, 0, 0) + inline const LLVector3d& zeroVec(); // deprecated + inline const LLVector3d& set(const F64 x, const F64 y, const F64 z); // Sets LLVector3d to (x, y, z, 1) + inline const LLVector3d& set(const LLVector3d &vec); // Sets LLVector3d to vec + inline const LLVector3d& set(const F64 *vec); // Sets LLVector3d to vec + inline const LLVector3d& set(const LLVector3 &vec); + inline const LLVector3d& setVec(const F64 x, const F64 y, const F64 z); // deprecated + inline const LLVector3d& setVec(const LLVector3d &vec); // deprecated + inline const LLVector3d& setVec(const F64 *vec); // deprecated + inline const LLVector3d& setVec(const LLVector3 &vec); // deprecated + + F64 magVec() const; // deprecated + F64 magVecSquared() const; // deprecated + inline F64 normVec(); // deprecated + + F64 length() const; // Returns magnitude of LLVector3d + F64 lengthSquared() const; // Returns magnitude squared of LLVector3d + inline F64 normalize(); // Normalizes and returns the magnitude of LLVector3d + + const LLVector3d& rotVec(const F64 angle, const LLVector3d &vec); // Rotates about vec by angle radians + const LLVector3d& rotVec(const F64 angle, const F64 x, const F64 y, const F64 z); // Rotates about x,y,z by angle radians + const LLVector3d& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat + const LLVector3d& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q + + bool isNull() const; // Returns true if vector has a _very_small_ length + bool isExactlyZero() const { return !mdV[VX] && !mdV[VY] && !mdV[VZ]; } + + const LLVector3d& operator=(const LLVector4 &a); + + F64 operator[](int idx) const { return mdV[idx]; } + F64 &operator[](int idx) { return mdV[idx]; } + + friend LLVector3d operator+(const LLVector3d& a, const LLVector3d& b); // Return vector a + b + friend LLVector3d operator-(const LLVector3d& a, const LLVector3d& b); // Return vector a minus b + friend F64 operator*(const LLVector3d& a, const LLVector3d& b); // Return a dot b + friend LLVector3d operator%(const LLVector3d& a, const LLVector3d& b); // Return a cross b + friend LLVector3d operator*(const LLVector3d& a, const F64 k); // Return a times scaler k + friend LLVector3d operator/(const LLVector3d& a, const F64 k); // Return a divided by scaler k + friend LLVector3d operator*(const F64 k, const LLVector3d& a); // Return a times scaler k + friend bool operator==(const LLVector3d& a, const LLVector3d& b); // Return a == b + friend bool operator!=(const LLVector3d& a, const LLVector3d& b); // Return a != b + + friend const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b); // Return vector a + b + friend const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b); // Return vector a minus b + friend const LLVector3d& operator%=(LLVector3d& a, const LLVector3d& b); // Return a cross b + friend const LLVector3d& operator*=(LLVector3d& a, const F64 k); // Return a times scaler k + friend const LLVector3d& operator/=(LLVector3d& a, const F64 k); // Return a divided by scaler k + + friend LLVector3d operator-(const LLVector3d& a); // Return vector -a + + friend std::ostream& operator<<(std::ostream& s, const LLVector3d& a); // Stream a + + static bool parseVector3d(const std::string& buf, LLVector3d* value); + +}; + +typedef LLVector3d LLGlobalVec; + +inline const LLVector3d &LLVector3d::set(const LLVector3 &vec) +{ + mdV[0] = vec.mV[0]; + mdV[1] = vec.mV[1]; + mdV[2] = vec.mV[2]; + return *this; +} + +inline const LLVector3d &LLVector3d::setVec(const LLVector3 &vec) +{ + mdV[0] = vec.mV[0]; + mdV[1] = vec.mV[1]; + mdV[2] = vec.mV[2]; + return *this; +} + + +inline LLVector3d::LLVector3d(void) +{ + mdV[0] = 0.f; + mdV[1] = 0.f; + mdV[2] = 0.f; +} + +inline LLVector3d::LLVector3d(const F64 x, const F64 y, const F64 z) +{ + mdV[VX] = x; + mdV[VY] = y; + mdV[VZ] = z; +} + +inline LLVector3d::LLVector3d(const F64 *vec) +{ + mdV[VX] = vec[VX]; + mdV[VY] = vec[VY]; + mdV[VZ] = vec[VZ]; +} + +inline LLVector3d::LLVector3d(const LLVector3 &vec) +{ + mdV[VX] = vec.mV[VX]; + mdV[VY] = vec.mV[VY]; + mdV[VZ] = vec.mV[VZ]; +} + +/* +inline LLVector3d::LLVector3d(const LLVector3d ©) +{ + mdV[VX] = copy.mdV[VX]; + mdV[VY] = copy.mdV[VY]; + mdV[VZ] = copy.mdV[VZ]; +} +*/ + +// Destructors + +// checker +inline bool LLVector3d::isFinite() const +{ + return (llfinite(mdV[VX]) && llfinite(mdV[VY]) && llfinite(mdV[VZ])); +} + + +// Clear and Assignment Functions + +inline const LLVector3d& LLVector3d::clear(void) +{ + mdV[0] = 0.f; + mdV[1] = 0.f; + mdV[2]= 0.f; + return (*this); +} + +inline const LLVector3d& LLVector3d::clearVec(void) +{ + mdV[0] = 0.f; + mdV[1] = 0.f; + mdV[2]= 0.f; + return (*this); +} + +inline const LLVector3d& LLVector3d::setZero(void) +{ + mdV[0] = 0.f; + mdV[1] = 0.f; + mdV[2] = 0.f; + return (*this); +} + +inline const LLVector3d& LLVector3d::zeroVec(void) +{ + mdV[0] = 0.f; + mdV[1] = 0.f; + mdV[2] = 0.f; + return (*this); +} + +inline const LLVector3d& LLVector3d::set(const F64 x, const F64 y, const F64 z) +{ + mdV[VX] = x; + mdV[VY] = y; + mdV[VZ] = z; + return (*this); +} + +inline const LLVector3d& LLVector3d::set(const LLVector3d &vec) +{ + mdV[0] = vec.mdV[0]; + mdV[1] = vec.mdV[1]; + mdV[2] = vec.mdV[2]; + return (*this); +} + +inline const LLVector3d& LLVector3d::set(const F64 *vec) +{ + mdV[0] = vec[0]; + mdV[1] = vec[1]; + mdV[2] = vec[2]; + return (*this); +} + +inline const LLVector3d& LLVector3d::setVec(const F64 x, const F64 y, const F64 z) +{ + mdV[VX] = x; + mdV[VY] = y; + mdV[VZ] = z; + return (*this); +} + +inline const LLVector3d& LLVector3d::setVec(const LLVector3d &vec) +{ + mdV[0] = vec.mdV[0]; + mdV[1] = vec.mdV[1]; + mdV[2] = vec.mdV[2]; + return (*this); +} + +inline const LLVector3d& LLVector3d::setVec(const F64 *vec) +{ + mdV[0] = vec[0]; + mdV[1] = vec[1]; + mdV[2] = vec[2]; + return (*this); +} + +inline F64 LLVector3d::normVec(void) +{ + F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]); + F64 oomag; + + if (mag > FP_MAG_THRESHOLD) + { + oomag = 1.f/mag; + mdV[0] *= oomag; + mdV[1] *= oomag; + mdV[2] *= oomag; + } + else + { + mdV[0] = 0.f; + mdV[1] = 0.f; + mdV[2] = 0.f; + mag = 0; + } + return (mag); +} + +inline F64 LLVector3d::normalize(void) +{ + F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]); + F64 oomag; + + if (mag > FP_MAG_THRESHOLD) + { + oomag = 1.f/mag; + mdV[0] *= oomag; + mdV[1] *= oomag; + mdV[2] *= oomag; + } + else + { + mdV[0] = 0.f; + mdV[1] = 0.f; + mdV[2] = 0.f; + mag = 0; + } + return (mag); +} + +// LLVector3d Magnitude and Normalization Functions + +inline F64 LLVector3d::magVec(void) const +{ + return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]); +} + +inline F64 LLVector3d::magVecSquared(void) const +{ + return mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]; +} + +inline F64 LLVector3d::length(void) const +{ + return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]); +} + +inline F64 LLVector3d::lengthSquared(void) const +{ + return mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]; +} + +inline LLVector3d operator+(const LLVector3d& a, const LLVector3d& b) +{ + LLVector3d c(a); + return c += b; +} + +inline LLVector3d operator-(const LLVector3d& a, const LLVector3d& b) +{ + LLVector3d c(a); + return c -= b; +} + +inline F64 operator*(const LLVector3d& a, const LLVector3d& b) +{ + return (a.mdV[0]*b.mdV[0] + a.mdV[1]*b.mdV[1] + a.mdV[2]*b.mdV[2]); +} + +inline LLVector3d operator%(const LLVector3d& a, const LLVector3d& b) +{ + return LLVector3d( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1] ); +} + +inline LLVector3d operator/(const LLVector3d& a, const F64 k) +{ + F64 t = 1.f / k; + return LLVector3d( a.mdV[0] * t, a.mdV[1] * t, a.mdV[2] * t ); +} + +inline LLVector3d operator*(const LLVector3d& a, const F64 k) +{ + return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k ); +} + +inline LLVector3d operator*(F64 k, const LLVector3d& a) +{ + return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k ); +} + +inline bool operator==(const LLVector3d& a, const LLVector3d& b) +{ + return ( (a.mdV[0] == b.mdV[0]) + &&(a.mdV[1] == b.mdV[1]) + &&(a.mdV[2] == b.mdV[2])); +} + +inline bool operator!=(const LLVector3d& a, const LLVector3d& b) +{ + return ( (a.mdV[0] != b.mdV[0]) + ||(a.mdV[1] != b.mdV[1]) + ||(a.mdV[2] != b.mdV[2])); +} + +inline const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b) +{ + a.mdV[0] += b.mdV[0]; + a.mdV[1] += b.mdV[1]; + a.mdV[2] += b.mdV[2]; + return a; +} + +inline const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b) +{ + a.mdV[0] -= b.mdV[0]; + a.mdV[1] -= b.mdV[1]; + a.mdV[2] -= b.mdV[2]; + return a; +} + +inline const LLVector3d& operator%=(LLVector3d& a, const LLVector3d& b) +{ + LLVector3d ret( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1]); + a = ret; + return a; +} + +inline const LLVector3d& operator*=(LLVector3d& a, const F64 k) +{ + a.mdV[0] *= k; + a.mdV[1] *= k; + a.mdV[2] *= k; + return a; +} + +inline const LLVector3d& operator/=(LLVector3d& a, const F64 k) +{ + F64 t = 1.f / k; + a.mdV[0] *= t; + a.mdV[1] *= t; + a.mdV[2] *= t; + return a; +} + +inline LLVector3d operator-(const LLVector3d& a) +{ + return LLVector3d( -a.mdV[0], -a.mdV[1], -a.mdV[2] ); +} + +inline F64 dist_vec(const LLVector3d& a, const LLVector3d& b) +{ + F64 x = a.mdV[0] - b.mdV[0]; + F64 y = a.mdV[1] - b.mdV[1]; + F64 z = a.mdV[2] - b.mdV[2]; + return (F32) sqrt( x*x + y*y + z*z ); +} + +inline F64 dist_vec_squared(const LLVector3d& a, const LLVector3d& b) +{ + F64 x = a.mdV[0] - b.mdV[0]; + F64 y = a.mdV[1] - b.mdV[1]; + F64 z = a.mdV[2] - b.mdV[2]; + return x*x + y*y + z*z; +} + +inline F64 dist_vec_squared2D(const LLVector3d& a, const LLVector3d& b) +{ + F64 x = a.mdV[0] - b.mdV[0]; + F64 y = a.mdV[1] - b.mdV[1]; + return x*x + y*y; +} + +inline LLVector3d lerp(const LLVector3d& a, const LLVector3d& b, const F64 u) +{ + return LLVector3d( + a.mdV[VX] + (b.mdV[VX] - a.mdV[VX]) * u, + a.mdV[VY] + (b.mdV[VY] - a.mdV[VY]) * u, + a.mdV[VZ] + (b.mdV[VZ] - a.mdV[VZ]) * u); +} + + +inline bool LLVector3d::isNull() const +{ + if ( F_APPROXIMATELY_ZERO > mdV[VX]*mdV[VX] + mdV[VY]*mdV[VY] + mdV[VZ]*mdV[VZ] ) + { + return true; + } + return false; +} + + +inline F64 angle_between(const LLVector3d& a, const LLVector3d& b) +{ + LLVector3d an = a; + LLVector3d bn = b; + an.normalize(); + bn.normalize(); + F64 cosine = an * bn; + F64 angle = (cosine >= 1.0f) ? 0.0f : + (cosine <= -1.0f) ? F_PI : + acos(cosine); + return angle; +} + +inline bool are_parallel(const LLVector3d& a, const LLVector3d& b, const F64 epsilon) +{ + LLVector3d an = a; + LLVector3d bn = b; + an.normalize(); + bn.normalize(); + F64 dot = an * bn; + if ( (1.0f - fabs(dot)) < epsilon) + { + return true; + } + return false; +} + +inline LLVector3d projected_vec(const LLVector3d& a, const LLVector3d& b) +{ + LLVector3d project_axis = b; + project_axis.normalize(); + return project_axis * (a * project_axis); +} + +inline LLVector3d inverse_projected_vec(const LLVector3d& a, const LLVector3d& b) +{ + LLVector3d normalized_a = a; + normalized_a.normalize(); + LLVector3d normalized_b = b; + F64 b_length = normalized_b.normalize(); + + F64 dot_product = normalized_a * normalized_b; + return normalized_a * (b_length / dot_product); +} + +#endif // LL_V3DMATH_H diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp index 089d4ce6db..73ad2a4ed6 100644 --- a/indra/llmath/v3math.cpp +++ b/indra/llmath/v3math.cpp @@ -1,413 +1,413 @@ -/**
- * @file v3math.cpp
- * @brief LLVector3 class implementation.
- *
- * $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 "linden_common.h"
-
-#include "v3math.h"
-
-//#include "vmath.h"
-#include "v2math.h"
-#include "v4math.h"
-#include "m4math.h"
-#include "m3math.h"
-#include "llquaternion.h"
-#include "llquantize.h"
-#include "v3dmath.h"
-
-// LLVector3
-// WARNING: Don't use these for global const definitions!
-// For example:
-// const LLQuaternion(0.5f * F_PI, LLVector3::zero);
-// at the top of a *.cpp file might not give you what you think.
-const LLVector3 LLVector3::zero(0,0,0);
-const LLVector3 LLVector3::x_axis(1.f, 0, 0);
-const LLVector3 LLVector3::y_axis(0, 1.f, 0);
-const LLVector3 LLVector3::z_axis(0, 0, 1.f);
-const LLVector3 LLVector3::x_axis_neg(-1.f, 0, 0);
-const LLVector3 LLVector3::y_axis_neg(0, -1.f, 0);
-const LLVector3 LLVector3::z_axis_neg(0, 0, -1.f);
-const LLVector3 LLVector3::all_one(1.f,1.f,1.f);
-
-
-// Clamps each values to range (min,max).
-// Returns true if data changed.
-bool LLVector3::clamp(F32 min, F32 max)
-{
- bool ret{ false };
-
- if (mV[0] < min) { mV[0] = min; ret = true; }
- if (mV[1] < min) { mV[1] = min; ret = true; }
- if (mV[2] < min) { mV[2] = min; ret = true; }
-
- if (mV[0] > max) { mV[0] = max; ret = true; }
- if (mV[1] > max) { mV[1] = max; ret = true; }
- if (mV[2] > max) { mV[2] = max; ret = true; }
-
- return ret;
-}
-
-// Clamps length to an upper limit.
-// Returns true if the data changed
-bool LLVector3::clampLength( F32 length_limit )
-{
- bool changed{ false };
-
- F32 len = length();
- if (llfinite(len))
- {
- if ( len > length_limit)
- {
- normalize();
- if (length_limit < 0.f)
- {
- length_limit = 0.f;
- }
- mV[0] *= length_limit;
- mV[1] *= length_limit;
- mV[2] *= length_limit;
- changed = true;
- }
- }
- else
- { // this vector may still be salvagable
- F32 max_abs_component = 0.f;
- for (S32 i = 0; i < 3; ++i)
- {
- F32 abs_component = fabs(mV[i]);
- if (llfinite(abs_component))
- {
- if (abs_component > max_abs_component)
- {
- max_abs_component = abs_component;
- }
- }
- else
- {
- // no it can't be salvaged --> clear it
- clear();
- changed = true;
- break;
- }
- }
- if (!changed)
- {
- // yes it can be salvaged -->
- // bring the components down before we normalize
- mV[0] /= max_abs_component;
- mV[1] /= max_abs_component;
- mV[2] /= max_abs_component;
- normalize();
-
- if (length_limit < 0.f)
- {
- length_limit = 0.f;
- }
- mV[0] *= length_limit;
- mV[1] *= length_limit;
- mV[2] *= length_limit;
- }
- }
-
- return changed;
-}
-
-bool LLVector3::clamp(const LLVector3 &min_vec, const LLVector3 &max_vec)
-{
- bool ret{ false };
-
- if (mV[0] < min_vec[0]) { mV[0] = min_vec[0]; ret = true; }
- if (mV[1] < min_vec[1]) { mV[1] = min_vec[1]; ret = true; }
- if (mV[2] < min_vec[2]) { mV[2] = min_vec[2]; ret = true; }
-
- if (mV[0] > max_vec[0]) { mV[0] = max_vec[0]; ret = true; }
- if (mV[1] > max_vec[1]) { mV[1] = max_vec[1]; ret = true; }
- if (mV[2] > max_vec[2]) { mV[2] = max_vec[2]; ret = true; }
-
- return ret;
-}
-
-
-// Sets all values to absolute value of their original values
-// Returns true if data changed
-bool LLVector3::abs()
-{
- bool ret{ false };
-
- if (mV[0] < 0.f) { mV[0] = -mV[0]; ret = true; }
- if (mV[1] < 0.f) { mV[1] = -mV[1]; ret = true; }
- if (mV[2] < 0.f) { mV[2] = -mV[2]; ret = true; }
-
- return ret;
-}
-
-// Quatizations
-void LLVector3::quantize16(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz)
-{
- F32 x = mV[VX];
- F32 y = mV[VY];
- F32 z = mV[VZ];
-
- x = U16_to_F32(F32_to_U16(x, lowerxy, upperxy), lowerxy, upperxy);
- y = U16_to_F32(F32_to_U16(y, lowerxy, upperxy), lowerxy, upperxy);
- z = U16_to_F32(F32_to_U16(z, lowerz, upperz), lowerz, upperz);
-
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
-}
-
-void LLVector3::quantize8(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz)
-{
- mV[VX] = U8_to_F32(F32_to_U8(mV[VX], lowerxy, upperxy), lowerxy, upperxy);;
- mV[VY] = U8_to_F32(F32_to_U8(mV[VY], lowerxy, upperxy), lowerxy, upperxy);
- mV[VZ] = U8_to_F32(F32_to_U8(mV[VZ], lowerz, upperz), lowerz, upperz);
-}
-
-
-void LLVector3::snap(S32 sig_digits)
-{
- mV[VX] = snap_to_sig_figs(mV[VX], sig_digits);
- mV[VY] = snap_to_sig_figs(mV[VY], sig_digits);
- mV[VZ] = snap_to_sig_figs(mV[VZ], sig_digits);
-}
-
-const LLVector3& LLVector3::rotVec(const LLMatrix3 &mat)
-{
- *this = *this * mat;
- return *this;
-}
-
-const LLVector3& LLVector3::rotVec(const LLQuaternion &q)
-{
- *this = *this * q;
- return *this;
-}
-
-const LLVector3& LLVector3::transVec(const LLMatrix4& mat)
-{
- setVec(
- mV[VX] * mat.mMatrix[VX][VX] +
- mV[VY] * mat.mMatrix[VX][VY] +
- mV[VZ] * mat.mMatrix[VX][VZ] +
- mat.mMatrix[VX][VW],
-
- mV[VX] * mat.mMatrix[VY][VX] +
- mV[VY] * mat.mMatrix[VY][VY] +
- mV[VZ] * mat.mMatrix[VY][VZ] +
- mat.mMatrix[VY][VW],
-
- mV[VX] * mat.mMatrix[VZ][VX] +
- mV[VY] * mat.mMatrix[VZ][VY] +
- mV[VZ] * mat.mMatrix[VZ][VZ] +
- mat.mMatrix[VZ][VW]);
-
- return *this;
-}
-
-
-const LLVector3& LLVector3::rotVec(F32 angle, const LLVector3 &vec)
-{
- if ( !vec.isExactlyZero() && angle )
- {
- *this = *this * LLQuaternion(angle, vec);
- }
- return *this;
-}
-
-const LLVector3& LLVector3::rotVec(F32 angle, F32 x, F32 y, F32 z)
-{
- LLVector3 vec(x, y, z);
- if ( !vec.isExactlyZero() && angle )
- {
- *this = *this * LLQuaternion(angle, vec);
- }
- return *this;
-}
-
-const LLVector3& LLVector3::scaleVec(const LLVector3& vec)
-{
- mV[VX] *= vec.mV[VX];
- mV[VY] *= vec.mV[VY];
- mV[VZ] *= vec.mV[VZ];
-
- return *this;
-}
-
-LLVector3 LLVector3::scaledVec(const LLVector3& vec) const
-{
- LLVector3 ret = LLVector3(*this);
- ret.scaleVec(vec);
- return ret;
-}
-
-const LLVector3& LLVector3::set(const LLVector3d &vec)
-{
- mV[0] = (F32)vec.mdV[0];
- mV[1] = (F32)vec.mdV[1];
- mV[2] = (F32)vec.mdV[2];
- return (*this);
-}
-
-const LLVector3& LLVector3::set(const LLVector4 &vec)
-{
- mV[0] = vec.mV[0];
- mV[1] = vec.mV[1];
- mV[2] = vec.mV[2];
- return (*this);
-}
-
-const LLVector3& LLVector3::setVec(const LLVector3d &vec)
-{
- mV[0] = (F32)vec.mdV[0];
- mV[1] = (F32)vec.mdV[1];
- mV[2] = (F32)vec.mdV[2];
- return (*this);
-}
-
-const LLVector3& LLVector3::setVec(const LLVector4 &vec)
-{
- mV[0] = vec.mV[0];
- mV[1] = vec.mV[1];
- mV[2] = vec.mV[2];
- return (*this);
-}
-
-LLVector3::LLVector3(const LLVector2 &vec)
-{
- mV[VX] = (F32)vec.mV[VX];
- mV[VY] = (F32)vec.mV[VY];
- mV[VZ] = 0;
-}
-
-LLVector3::LLVector3(const LLVector3d &vec)
-{
- mV[VX] = (F32)vec.mdV[VX];
- mV[VY] = (F32)vec.mdV[VY];
- mV[VZ] = (F32)vec.mdV[VZ];
-}
-
-LLVector3::LLVector3(const LLVector4 &vec)
-{
- mV[VX] = (F32)vec.mV[VX];
- mV[VY] = (F32)vec.mV[VY];
- mV[VZ] = (F32)vec.mV[VZ];
-}
-
-LLVector3::LLVector3(const LLVector4a& vec)
- : LLVector3(vec.getF32ptr())
-{
-
-}
-
-LLVector3::LLVector3(const LLSD& sd)
-{
- setValue(sd);
-}
-
-LLSD LLVector3::getValue() const
-{
- LLSD ret;
- ret[0] = mV[0];
- ret[1] = mV[1];
- ret[2] = mV[2];
- return ret;
-}
-
-void LLVector3::setValue(const LLSD& sd)
-{
- mV[0] = (F32) sd[0].asReal();
- mV[1] = (F32) sd[1].asReal();
- mV[2] = (F32) sd[2].asReal();
-}
-
-const LLVector3& operator*=(LLVector3 &a, const LLQuaternion &rot)
-{
- const F32 rw = - rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] - rot.mQ[VZ] * a.mV[VZ];
- const F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] - rot.mQ[VZ] * a.mV[VY];
- const F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] - rot.mQ[VX] * a.mV[VZ];
- const F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] - rot.mQ[VY] * a.mV[VX];
-
- a.mV[VX] = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY];
- a.mV[VY] = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ];
- a.mV[VZ] = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX];
-
- return a;
-}
-
-// static
-bool LLVector3::parseVector3(const std::string& buf, LLVector3* value)
-{
- if( buf.empty() || value == nullptr)
- {
- return false;
- }
-
- LLVector3 v;
- S32 count = sscanf( buf.c_str(), "%f %f %f", v.mV + 0, v.mV + 1, v.mV + 2 );
- if( 3 == count )
- {
- value->setVec( v );
- return true;
- }
-
- 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;
-}
-
+/** + * @file v3math.cpp + * @brief LLVector3 class implementation. + * + * $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 "linden_common.h" + +#include "v3math.h" + +//#include "vmath.h" +#include "v2math.h" +#include "v4math.h" +#include "m4math.h" +#include "m3math.h" +#include "llquaternion.h" +#include "llquantize.h" +#include "v3dmath.h" + +// LLVector3 +// WARNING: Don't use these for global const definitions! +// For example: +// const LLQuaternion(0.5f * F_PI, LLVector3::zero); +// at the top of a *.cpp file might not give you what you think. +const LLVector3 LLVector3::zero(0,0,0); +const LLVector3 LLVector3::x_axis(1.f, 0, 0); +const LLVector3 LLVector3::y_axis(0, 1.f, 0); +const LLVector3 LLVector3::z_axis(0, 0, 1.f); +const LLVector3 LLVector3::x_axis_neg(-1.f, 0, 0); +const LLVector3 LLVector3::y_axis_neg(0, -1.f, 0); +const LLVector3 LLVector3::z_axis_neg(0, 0, -1.f); +const LLVector3 LLVector3::all_one(1.f,1.f,1.f); + + +// Clamps each values to range (min,max). +// Returns true if data changed. +bool LLVector3::clamp(F32 min, F32 max) +{ + bool ret{ false }; + + if (mV[0] < min) { mV[0] = min; ret = true; } + if (mV[1] < min) { mV[1] = min; ret = true; } + if (mV[2] < min) { mV[2] = min; ret = true; } + + if (mV[0] > max) { mV[0] = max; ret = true; } + if (mV[1] > max) { mV[1] = max; ret = true; } + if (mV[2] > max) { mV[2] = max; ret = true; } + + return ret; +} + +// Clamps length to an upper limit. +// Returns true if the data changed +bool LLVector3::clampLength( F32 length_limit ) +{ + bool changed{ false }; + + F32 len = length(); + if (llfinite(len)) + { + if ( len > length_limit) + { + normalize(); + if (length_limit < 0.f) + { + length_limit = 0.f; + } + mV[0] *= length_limit; + mV[1] *= length_limit; + mV[2] *= length_limit; + changed = true; + } + } + else + { // this vector may still be salvagable + F32 max_abs_component = 0.f; + for (S32 i = 0; i < 3; ++i) + { + F32 abs_component = fabs(mV[i]); + if (llfinite(abs_component)) + { + if (abs_component > max_abs_component) + { + max_abs_component = abs_component; + } + } + else + { + // no it can't be salvaged --> clear it + clear(); + changed = true; + break; + } + } + if (!changed) + { + // yes it can be salvaged --> + // bring the components down before we normalize + mV[0] /= max_abs_component; + mV[1] /= max_abs_component; + mV[2] /= max_abs_component; + normalize(); + + if (length_limit < 0.f) + { + length_limit = 0.f; + } + mV[0] *= length_limit; + mV[1] *= length_limit; + mV[2] *= length_limit; + } + } + + return changed; +} + +bool LLVector3::clamp(const LLVector3 &min_vec, const LLVector3 &max_vec) +{ + bool ret{ false }; + + if (mV[0] < min_vec[0]) { mV[0] = min_vec[0]; ret = true; } + if (mV[1] < min_vec[1]) { mV[1] = min_vec[1]; ret = true; } + if (mV[2] < min_vec[2]) { mV[2] = min_vec[2]; ret = true; } + + if (mV[0] > max_vec[0]) { mV[0] = max_vec[0]; ret = true; } + if (mV[1] > max_vec[1]) { mV[1] = max_vec[1]; ret = true; } + if (mV[2] > max_vec[2]) { mV[2] = max_vec[2]; ret = true; } + + return ret; +} + + +// Sets all values to absolute value of their original values +// Returns true if data changed +bool LLVector3::abs() +{ + bool ret{ false }; + + if (mV[0] < 0.f) { mV[0] = -mV[0]; ret = true; } + if (mV[1] < 0.f) { mV[1] = -mV[1]; ret = true; } + if (mV[2] < 0.f) { mV[2] = -mV[2]; ret = true; } + + return ret; +} + +// Quatizations +void LLVector3::quantize16(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz) +{ + F32 x = mV[VX]; + F32 y = mV[VY]; + F32 z = mV[VZ]; + + x = U16_to_F32(F32_to_U16(x, lowerxy, upperxy), lowerxy, upperxy); + y = U16_to_F32(F32_to_U16(y, lowerxy, upperxy), lowerxy, upperxy); + z = U16_to_F32(F32_to_U16(z, lowerz, upperz), lowerz, upperz); + + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; +} + +void LLVector3::quantize8(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz) +{ + mV[VX] = U8_to_F32(F32_to_U8(mV[VX], lowerxy, upperxy), lowerxy, upperxy);; + mV[VY] = U8_to_F32(F32_to_U8(mV[VY], lowerxy, upperxy), lowerxy, upperxy); + mV[VZ] = U8_to_F32(F32_to_U8(mV[VZ], lowerz, upperz), lowerz, upperz); +} + + +void LLVector3::snap(S32 sig_digits) +{ + mV[VX] = snap_to_sig_figs(mV[VX], sig_digits); + mV[VY] = snap_to_sig_figs(mV[VY], sig_digits); + mV[VZ] = snap_to_sig_figs(mV[VZ], sig_digits); +} + +const LLVector3& LLVector3::rotVec(const LLMatrix3 &mat) +{ + *this = *this * mat; + return *this; +} + +const LLVector3& LLVector3::rotVec(const LLQuaternion &q) +{ + *this = *this * q; + return *this; +} + +const LLVector3& LLVector3::transVec(const LLMatrix4& mat) +{ + setVec( + mV[VX] * mat.mMatrix[VX][VX] + + mV[VY] * mat.mMatrix[VX][VY] + + mV[VZ] * mat.mMatrix[VX][VZ] + + mat.mMatrix[VX][VW], + + mV[VX] * mat.mMatrix[VY][VX] + + mV[VY] * mat.mMatrix[VY][VY] + + mV[VZ] * mat.mMatrix[VY][VZ] + + mat.mMatrix[VY][VW], + + mV[VX] * mat.mMatrix[VZ][VX] + + mV[VY] * mat.mMatrix[VZ][VY] + + mV[VZ] * mat.mMatrix[VZ][VZ] + + mat.mMatrix[VZ][VW]); + + return *this; +} + + +const LLVector3& LLVector3::rotVec(F32 angle, const LLVector3 &vec) +{ + if ( !vec.isExactlyZero() && angle ) + { + *this = *this * LLQuaternion(angle, vec); + } + return *this; +} + +const LLVector3& LLVector3::rotVec(F32 angle, F32 x, F32 y, F32 z) +{ + LLVector3 vec(x, y, z); + if ( !vec.isExactlyZero() && angle ) + { + *this = *this * LLQuaternion(angle, vec); + } + return *this; +} + +const LLVector3& LLVector3::scaleVec(const LLVector3& vec) +{ + mV[VX] *= vec.mV[VX]; + mV[VY] *= vec.mV[VY]; + mV[VZ] *= vec.mV[VZ]; + + return *this; +} + +LLVector3 LLVector3::scaledVec(const LLVector3& vec) const +{ + LLVector3 ret = LLVector3(*this); + ret.scaleVec(vec); + return ret; +} + +const LLVector3& LLVector3::set(const LLVector3d &vec) +{ + mV[0] = (F32)vec.mdV[0]; + mV[1] = (F32)vec.mdV[1]; + mV[2] = (F32)vec.mdV[2]; + return (*this); +} + +const LLVector3& LLVector3::set(const LLVector4 &vec) +{ + mV[0] = vec.mV[0]; + mV[1] = vec.mV[1]; + mV[2] = vec.mV[2]; + return (*this); +} + +const LLVector3& LLVector3::setVec(const LLVector3d &vec) +{ + mV[0] = (F32)vec.mdV[0]; + mV[1] = (F32)vec.mdV[1]; + mV[2] = (F32)vec.mdV[2]; + return (*this); +} + +const LLVector3& LLVector3::setVec(const LLVector4 &vec) +{ + mV[0] = vec.mV[0]; + mV[1] = vec.mV[1]; + mV[2] = vec.mV[2]; + return (*this); +} + +LLVector3::LLVector3(const LLVector2 &vec) +{ + mV[VX] = (F32)vec.mV[VX]; + mV[VY] = (F32)vec.mV[VY]; + mV[VZ] = 0; +} + +LLVector3::LLVector3(const LLVector3d &vec) +{ + mV[VX] = (F32)vec.mdV[VX]; + mV[VY] = (F32)vec.mdV[VY]; + mV[VZ] = (F32)vec.mdV[VZ]; +} + +LLVector3::LLVector3(const LLVector4 &vec) +{ + mV[VX] = (F32)vec.mV[VX]; + mV[VY] = (F32)vec.mV[VY]; + mV[VZ] = (F32)vec.mV[VZ]; +} + +LLVector3::LLVector3(const LLVector4a& vec) + : LLVector3(vec.getF32ptr()) +{ + +} + +LLVector3::LLVector3(const LLSD& sd) +{ + setValue(sd); +} + +LLSD LLVector3::getValue() const +{ + LLSD ret; + ret[0] = mV[0]; + ret[1] = mV[1]; + ret[2] = mV[2]; + return ret; +} + +void LLVector3::setValue(const LLSD& sd) +{ + mV[0] = (F32) sd[0].asReal(); + mV[1] = (F32) sd[1].asReal(); + mV[2] = (F32) sd[2].asReal(); +} + +const LLVector3& operator*=(LLVector3 &a, const LLQuaternion &rot) +{ + const F32 rw = - rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] - rot.mQ[VZ] * a.mV[VZ]; + const F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] - rot.mQ[VZ] * a.mV[VY]; + const F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] - rot.mQ[VX] * a.mV[VZ]; + const F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] - rot.mQ[VY] * a.mV[VX]; + + a.mV[VX] = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY]; + a.mV[VY] = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ]; + a.mV[VZ] = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX]; + + return a; +} + +// static +bool LLVector3::parseVector3(const std::string& buf, LLVector3* value) +{ + if( buf.empty() || value == nullptr) + { + return false; + } + + LLVector3 v; + S32 count = sscanf( buf.c_str(), "%f %f %f", v.mV + 0, v.mV + 1, v.mV + 2 ); + if( 3 == count ) + { + value->setVec( v ); + return true; + } + + 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 e43c756fe7..d063b15c74 100644 --- a/indra/llmath/v3math.h +++ b/indra/llmath/v3math.h @@ -1,612 +1,612 @@ -/**
- * @file v3math.h
- * @brief LLVector3 class header file.
- *
- * $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$
- */
-
-#ifndef LL_V3MATH_H
-#define LL_V3MATH_H
-
-#include "llerror.h"
-#include "llmath.h"
-
-#include "llsd.h"
-class LLVector2;
-class LLVector4;
-class LLVector4a;
-class LLMatrix3;
-class LLMatrix4;
-class LLVector3d;
-class LLQuaternion;
-
-// LLvector3 = |x y z w|
-
-static const U32 LENGTHOFVECTOR3 = 3;
-
-class LLVector3
-{
- public:
- F32 mV[LENGTHOFVECTOR3];
-
- static const LLVector3 zero;
- static const LLVector3 x_axis;
- static const LLVector3 y_axis;
- static const LLVector3 z_axis;
- static const LLVector3 x_axis_neg;
- static const LLVector3 y_axis_neg;
- static const LLVector3 z_axis_neg;
- static const LLVector3 all_one;
-
- inline LLVector3(); // Initializes LLVector3 to (0, 0, 0)
- inline LLVector3(const F32 x, const F32 y, const F32 z); // Initializes LLVector3 to (x. y, z)
- inline explicit LLVector3(const F32 *vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2])
- explicit LLVector3(const LLVector2 &vec); // Initializes LLVector3 to (vec[0]. vec[1], 0)
- explicit LLVector3(const LLVector3d &vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2])
- explicit LLVector3(const LLVector4 &vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2])
- explicit LLVector3(const LLVector4a& vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2])
- explicit LLVector3(const LLSD& sd);
-
-
- LLSD getValue() const;
-
- void setValue(const LLSD& sd);
-
- inline bool isFinite() const; // checks to see if all values of LLVector3 are finite
- bool clamp(F32 min, F32 max); // Clamps all values to (min,max), returns true if data changed
- bool clamp(const LLVector3 &min_vec, const LLVector3 &max_vec); // Scales vector by another vector
- bool clampLength( F32 length_limit ); // Scales vector to limit length to a value
-
- void quantize16(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization
- void quantize8(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization
- void snap(S32 sig_digits); // snaps x,y,z to sig_digits decimal places
-
- bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed
-
- inline void clear(); // Clears LLVector3 to (0, 0, 0)
- inline void setZero(); // Clears LLVector3 to (0, 0, 0)
- inline void clearVec(); // deprecated
- inline void zeroVec(); // deprecated
-
- inline void set(F32 x, F32 y, F32 z); // Sets LLVector3 to (x, y, z, 1)
- inline void set(const LLVector3 &vec); // Sets LLVector3 to vec
- inline void set(const F32 *vec); // Sets LLVector3 to vec
- const LLVector3& set(const LLVector4 &vec);
- const LLVector3& set(const LLVector3d &vec);// Sets LLVector3 to vec
-
- inline void setVec(F32 x, F32 y, F32 z); // deprecated
- inline void setVec(const LLVector3 &vec); // deprecated
- inline void setVec(const F32 *vec); // deprecated
-
- const LLVector3& setVec(const LLVector4 &vec); // deprecated
- const LLVector3& setVec(const LLVector3d &vec); // deprecated
-
- F32 length() const; // Returns magnitude of LLVector3
- F32 lengthSquared() const; // Returns magnitude squared of LLVector3
- F32 magVec() const; // deprecated
- F32 magVecSquared() const; // deprecated
-
- inline F32 normalize(); // Normalizes and returns the magnitude of LLVector3
- inline F32 normVec(); // deprecated
-
- inline bool inRange( F32 min, F32 max ) const; // Returns true if all values of the vector are between min and max
-
- const LLVector3& rotVec(F32 angle, const LLVector3 &vec); // Rotates about vec by angle radians
- const LLVector3& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians
- const LLVector3& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat
- const LLVector3& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q
- const LLVector3& transVec(const LLMatrix4& mat); // Transforms by LLMatrix4 mat (mat * v)
-
- const LLVector3& scaleVec(const LLVector3& vec); // scales per component by vec
- LLVector3 scaledVec(const LLVector3& vec) const; // get a copy of this vector scaled by vec
-
- bool isNull() const; // Returns true if vector has a _very_small_ length
- bool isExactlyZero() const { return !mV[VX] && !mV[VY] && !mV[VZ]; }
-
- F32 operator[](int idx) const { return mV[idx]; }
- F32 &operator[](int idx) { return mV[idx]; }
-
- friend LLVector3 operator+(const LLVector3 &a, const LLVector3 &b); // Return vector a + b
- friend LLVector3 operator-(const LLVector3 &a, const LLVector3 &b); // Return vector a minus b
- friend F32 operator*(const LLVector3 &a, const LLVector3 &b); // Return a dot b
- friend LLVector3 operator%(const LLVector3 &a, const LLVector3 &b); // Return a cross b
- friend LLVector3 operator*(const LLVector3 &a, F32 k); // Return a times scaler k
- friend LLVector3 operator/(const LLVector3 &a, F32 k); // Return a divided by scaler k
- friend LLVector3 operator*(F32 k, const LLVector3 &a); // Return a times scaler k
- friend bool operator==(const LLVector3 &a, const LLVector3 &b); // Return a == b
- friend bool operator!=(const LLVector3 &a, const LLVector3 &b); // Return a != b
- // less than operator useful for using vectors as std::map keys
- friend bool operator<(const LLVector3 &a, const LLVector3 &b); // Return a < b
-
- friend const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b); // Return vector a + b
- friend const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b); // Return vector a minus b
- friend const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b); // Return a cross b
- friend const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b); // Returns a * b;
- friend const LLVector3& operator*=(LLVector3 &a, F32 k); // Return a times scaler k
- friend const LLVector3& operator/=(LLVector3 &a, F32 k); // Return a divided by scaler k
- friend const LLVector3& operator*=(LLVector3 &a, const LLQuaternion &b); // Returns a * b;
-
- friend LLVector3 operator-(const LLVector3 &a); // Return vector -a
-
- friend std::ostream& operator<<(std::ostream& s, const LLVector3 &a); // Stream a
-
- static bool parseVector3(const std::string& buf, LLVector3* value);
-};
-
-typedef LLVector3 LLSimLocalVec;
-
-// Non-member functions
-
-F32 angle_between(const LLVector3 &a, const LLVector3 &b); // Returns angle (radians) between a and b
-bool are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon=F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel
-F32 dist_vec(const LLVector3 &a, const LLVector3 &b); // Returns distance between a and b
-F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b);// Returns distance squared between a and b
-F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b);// Returns distance squared between a and b ignoring Z component
-LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b
-LLVector3 inverse_projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a scaled such that projected_vec(inverse_projected_vec(a, b), b) == b;
-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)
-{
- mV[0] = 0.f;
- mV[1] = 0.f;
- mV[2] = 0.f;
-}
-
-inline LLVector3::LLVector3(const F32 x, const F32 y, const F32 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
-}
-
-inline LLVector3::LLVector3(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
-}
-
-/*
-inline LLVector3::LLVector3(const LLVector3 ©)
-{
- mV[VX] = copy.mV[VX];
- mV[VY] = copy.mV[VY];
- mV[VZ] = copy.mV[VZ];
-}
-*/
-
-// Destructors
-
-// checker
-inline bool LLVector3::isFinite() const
-{
- return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]));
-}
-
-
-// Clear and Assignment Functions
-
-inline void LLVector3::clear(void)
-{
- mV[0] = 0.f;
- mV[1] = 0.f;
- mV[2] = 0.f;
-}
-
-inline void LLVector3::setZero(void)
-{
- mV[0] = 0.f;
- mV[1] = 0.f;
- mV[2] = 0.f;
-}
-
-inline void LLVector3::clearVec(void)
-{
- mV[0] = 0.f;
- mV[1] = 0.f;
- mV[2] = 0.f;
-}
-
-inline void LLVector3::zeroVec(void)
-{
- mV[0] = 0.f;
- mV[1] = 0.f;
- mV[2] = 0.f;
-}
-
-inline void LLVector3::set(F32 x, F32 y, F32 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
-}
-
-inline void LLVector3::set(const LLVector3 &vec)
-{
- mV[0] = vec.mV[0];
- mV[1] = vec.mV[1];
- mV[2] = vec.mV[2];
-}
-
-inline void LLVector3::set(const F32 *vec)
-{
- mV[0] = vec[0];
- mV[1] = vec[1];
- mV[2] = vec[2];
-}
-
-// deprecated
-inline void LLVector3::setVec(F32 x, F32 y, F32 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
-}
-
-// deprecated
-inline void LLVector3::setVec(const LLVector3 &vec)
-{
- mV[0] = vec.mV[0];
- mV[1] = vec.mV[1];
- mV[2] = vec.mV[2];
-}
-
-// deprecated
-inline void LLVector3::setVec(const F32 *vec)
-{
- mV[0] = vec[0];
- mV[1] = vec[1];
- mV[2] = vec[2];
-}
-
-inline F32 LLVector3::normalize(void)
-{
- F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
- F32 oomag;
-
- if (mag > FP_MAG_THRESHOLD)
- {
- oomag = 1.f/mag;
- mV[0] *= oomag;
- mV[1] *= oomag;
- mV[2] *= oomag;
- }
- else
- {
- mV[0] = 0.f;
- mV[1] = 0.f;
- mV[2] = 0.f;
- mag = 0;
- }
- return (mag);
-}
-
-// deprecated
-inline F32 LLVector3::normVec(void)
-{
- F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
- F32 oomag;
-
- if (mag > FP_MAG_THRESHOLD)
- {
- oomag = 1.f/mag;
- mV[0] *= oomag;
- mV[1] *= oomag;
- mV[2] *= oomag;
- }
- else
- {
- mV[0] = 0.f;
- mV[1] = 0.f;
- mV[2] = 0.f;
- mag = 0;
- }
- return (mag);
-}
-
-// LLVector3 Magnitude and Normalization Functions
-
-inline F32 LLVector3::length(void) const
-{
- return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
-}
-
-inline F32 LLVector3::lengthSquared(void) const
-{
- return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
-}
-
-inline F32 LLVector3::magVec(void) const
-{
- return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
-}
-
-inline F32 LLVector3::magVecSquared(void) const
-{
- return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
-}
-
-inline bool LLVector3::inRange( F32 min, F32 max ) const
-{
- return mV[0] >= min && mV[0] <= max &&
- mV[1] >= min && mV[1] <= max &&
- mV[2] >= min && mV[2] <= max;
-}
-
-inline LLVector3 operator+(const LLVector3 &a, const LLVector3 &b)
-{
- LLVector3 c(a);
- return c += b;
-}
-
-inline LLVector3 operator-(const LLVector3 &a, const LLVector3 &b)
-{
- LLVector3 c(a);
- return c -= b;
-}
-
-inline F32 operator*(const LLVector3 &a, const LLVector3 &b)
-{
- return (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1] + a.mV[2]*b.mV[2]);
-}
-
-inline LLVector3 operator%(const LLVector3 &a, const LLVector3 &b)
-{
- return LLVector3( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1] );
-}
-
-inline LLVector3 operator/(const LLVector3 &a, F32 k)
-{
- F32 t = 1.f / k;
- return LLVector3( a.mV[0] * t, a.mV[1] * t, a.mV[2] * t );
-}
-
-inline LLVector3 operator*(const LLVector3 &a, F32 k)
-{
- return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
-}
-
-inline LLVector3 operator*(F32 k, const LLVector3 &a)
-{
- return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
-}
-
-inline bool operator==(const LLVector3 &a, const LLVector3 &b)
-{
- return ( (a.mV[0] == b.mV[0])
- &&(a.mV[1] == b.mV[1])
- &&(a.mV[2] == b.mV[2]));
-}
-
-inline bool operator!=(const LLVector3 &a, const LLVector3 &b)
-{
- return ( (a.mV[0] != b.mV[0])
- ||(a.mV[1] != b.mV[1])
- ||(a.mV[2] != b.mV[2]));
-}
-
-inline bool operator<(const LLVector3 &a, const LLVector3 &b)
-{
- return (a.mV[0] < b.mV[0]
- || (a.mV[0] == b.mV[0]
- && (a.mV[1] < b.mV[1]
- || ((a.mV[1] == b.mV[1])
- && a.mV[2] < b.mV[2]))));
-}
-
-inline const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b)
-{
- a.mV[0] += b.mV[0];
- a.mV[1] += b.mV[1];
- a.mV[2] += b.mV[2];
- return a;
-}
-
-inline const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b)
-{
- a.mV[0] -= b.mV[0];
- a.mV[1] -= b.mV[1];
- a.mV[2] -= b.mV[2];
- return a;
-}
-
-inline const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b)
-{
- LLVector3 ret( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1]);
- a = ret;
- return a;
-}
-
-inline const LLVector3& operator*=(LLVector3 &a, F32 k)
-{
- a.mV[0] *= k;
- a.mV[1] *= k;
- a.mV[2] *= k;
- return a;
-}
-
-inline const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b)
-{
- a.mV[0] *= b.mV[0];
- a.mV[1] *= b.mV[1];
- a.mV[2] *= b.mV[2];
- return a;
-}
-
-inline const LLVector3& operator/=(LLVector3 &a, F32 k)
-{
- F32 t = 1.f / k;
- a.mV[0] *= t;
- a.mV[1] *= t;
- a.mV[2] *= t;
- return a;
-}
-
-inline LLVector3 operator-(const LLVector3 &a)
-{
- return LLVector3( -a.mV[0], -a.mV[1], -a.mV[2] );
-}
-
-inline F32 dist_vec(const LLVector3 &a, const LLVector3 &b)
-{
- F32 x = a.mV[0] - b.mV[0];
- F32 y = a.mV[1] - b.mV[1];
- F32 z = a.mV[2] - b.mV[2];
- return (F32) sqrt( x*x + y*y + z*z );
-}
-
-inline F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b)
-{
- F32 x = a.mV[0] - b.mV[0];
- F32 y = a.mV[1] - b.mV[1];
- F32 z = a.mV[2] - b.mV[2];
- return x*x + y*y + z*z;
-}
-
-inline F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b)
-{
- F32 x = a.mV[0] - b.mV[0];
- F32 y = a.mV[1] - b.mV[1];
- return x*x + y*y;
-}
-
-inline LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b)
-{
- F32 bb = b * b;
- if (bb > FP_MAG_THRESHOLD * FP_MAG_THRESHOLD)
- {
- return ((a * b) / bb) * b;
- }
- else
- {
- return b.zero;
- }
-}
-
-inline LLVector3 inverse_projected_vec(const LLVector3& a, const LLVector3& b)
-{
- LLVector3 normalized_a = a;
- normalized_a.normalize();
- LLVector3 normalized_b = b;
- F32 b_length = normalized_b.normalize();
-
- F32 dot_product = normalized_a * normalized_b;
- //NB: if a _|_ b, then returns an infinite vector
- return normalized_a * (b_length / dot_product);
-}
-
-inline LLVector3 parallel_component(const LLVector3 &a, const LLVector3 &b)
-{
- return projected_vec(a, b);
-}
-
-inline LLVector3 orthogonal_component(const LLVector3 &a, const LLVector3 &b)
-{
- return a - projected_vec(a, b);
-}
-
-
-inline LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u)
-{
- return LLVector3(
- a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
- a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u,
- a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u);
-}
-
-
-inline bool LLVector3::isNull() const
-{
- if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ] )
- {
- return true;
- }
- return false;
-}
-
-inline void update_min_max(LLVector3& min, LLVector3& max, const LLVector3& pos)
-{
- for (U32 i = 0; i < 3; i++)
- {
- if (min.mV[i] > pos.mV[i])
- {
- min.mV[i] = pos.mV[i];
- }
- if (max.mV[i] < pos.mV[i])
- {
- max.mV[i] = pos.mV[i];
- }
- }
-}
-
-inline void update_min_max(LLVector3& min, LLVector3& max, const F32* pos)
-{
- for (U32 i = 0; i < 3; i++)
- {
- if (min.mV[i] > pos[i])
- {
- min.mV[i] = pos[i];
- }
- if (max.mV[i] < pos[i])
- {
- max.mV[i] = pos[i];
- }
- }
-}
-
-inline F32 angle_between(const LLVector3& a, const LLVector3& b)
-{
- F32 ab = a * b; // dotproduct
- if (ab == -0.0f)
- {
- ab = 0.0f; // get rid of negative zero
- }
- LLVector3 c = a % b; // crossproduct
- return atan2f(sqrtf(c * c), ab); // return the angle
-}
-
-inline bool are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon)
-{
- LLVector3 an = a;
- LLVector3 bn = b;
- an.normalize();
- bn.normalize();
- F32 dot = an * bn;
- if ( (1.0f - fabs(dot)) < epsilon)
- {
- return true;
- }
- return false;
-}
-
-inline std::ostream& operator<<(std::ostream& s, const LLVector3 &a)
-{
- s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }";
- return s;
-}
-
-#endif
+/** + * @file v3math.h + * @brief LLVector3 class header file. + * + * $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$ + */ + +#ifndef LL_V3MATH_H +#define LL_V3MATH_H + +#include "llerror.h" +#include "llmath.h" + +#include "llsd.h" +class LLVector2; +class LLVector4; +class LLVector4a; +class LLMatrix3; +class LLMatrix4; +class LLVector3d; +class LLQuaternion; + +// LLvector3 = |x y z w| + +static const U32 LENGTHOFVECTOR3 = 3; + +class LLVector3 +{ + public: + F32 mV[LENGTHOFVECTOR3]; + + static const LLVector3 zero; + static const LLVector3 x_axis; + static const LLVector3 y_axis; + static const LLVector3 z_axis; + static const LLVector3 x_axis_neg; + static const LLVector3 y_axis_neg; + static const LLVector3 z_axis_neg; + static const LLVector3 all_one; + + inline LLVector3(); // Initializes LLVector3 to (0, 0, 0) + inline LLVector3(const F32 x, const F32 y, const F32 z); // Initializes LLVector3 to (x. y, z) + inline explicit LLVector3(const F32 *vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2]) + explicit LLVector3(const LLVector2 &vec); // Initializes LLVector3 to (vec[0]. vec[1], 0) + explicit LLVector3(const LLVector3d &vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2]) + explicit LLVector3(const LLVector4 &vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2]) + explicit LLVector3(const LLVector4a& vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2]) + explicit LLVector3(const LLSD& sd); + + + LLSD getValue() const; + + void setValue(const LLSD& sd); + + inline bool isFinite() const; // checks to see if all values of LLVector3 are finite + bool clamp(F32 min, F32 max); // Clamps all values to (min,max), returns true if data changed + bool clamp(const LLVector3 &min_vec, const LLVector3 &max_vec); // Scales vector by another vector + bool clampLength( F32 length_limit ); // Scales vector to limit length to a value + + void quantize16(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization + void quantize8(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization + void snap(S32 sig_digits); // snaps x,y,z to sig_digits decimal places + + bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed + + inline void clear(); // Clears LLVector3 to (0, 0, 0) + inline void setZero(); // Clears LLVector3 to (0, 0, 0) + inline void clearVec(); // deprecated + inline void zeroVec(); // deprecated + + inline void set(F32 x, F32 y, F32 z); // Sets LLVector3 to (x, y, z, 1) + inline void set(const LLVector3 &vec); // Sets LLVector3 to vec + inline void set(const F32 *vec); // Sets LLVector3 to vec + const LLVector3& set(const LLVector4 &vec); + const LLVector3& set(const LLVector3d &vec);// Sets LLVector3 to vec + + inline void setVec(F32 x, F32 y, F32 z); // deprecated + inline void setVec(const LLVector3 &vec); // deprecated + inline void setVec(const F32 *vec); // deprecated + + const LLVector3& setVec(const LLVector4 &vec); // deprecated + const LLVector3& setVec(const LLVector3d &vec); // deprecated + + F32 length() const; // Returns magnitude of LLVector3 + F32 lengthSquared() const; // Returns magnitude squared of LLVector3 + F32 magVec() const; // deprecated + F32 magVecSquared() const; // deprecated + + inline F32 normalize(); // Normalizes and returns the magnitude of LLVector3 + inline F32 normVec(); // deprecated + + inline bool inRange( F32 min, F32 max ) const; // Returns true if all values of the vector are between min and max + + const LLVector3& rotVec(F32 angle, const LLVector3 &vec); // Rotates about vec by angle radians + const LLVector3& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians + const LLVector3& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat + const LLVector3& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q + const LLVector3& transVec(const LLMatrix4& mat); // Transforms by LLMatrix4 mat (mat * v) + + const LLVector3& scaleVec(const LLVector3& vec); // scales per component by vec + LLVector3 scaledVec(const LLVector3& vec) const; // get a copy of this vector scaled by vec + + bool isNull() const; // Returns true if vector has a _very_small_ length + bool isExactlyZero() const { return !mV[VX] && !mV[VY] && !mV[VZ]; } + + F32 operator[](int idx) const { return mV[idx]; } + F32 &operator[](int idx) { return mV[idx]; } + + friend LLVector3 operator+(const LLVector3 &a, const LLVector3 &b); // Return vector a + b + friend LLVector3 operator-(const LLVector3 &a, const LLVector3 &b); // Return vector a minus b + friend F32 operator*(const LLVector3 &a, const LLVector3 &b); // Return a dot b + friend LLVector3 operator%(const LLVector3 &a, const LLVector3 &b); // Return a cross b + friend LLVector3 operator*(const LLVector3 &a, F32 k); // Return a times scaler k + friend LLVector3 operator/(const LLVector3 &a, F32 k); // Return a divided by scaler k + friend LLVector3 operator*(F32 k, const LLVector3 &a); // Return a times scaler k + friend bool operator==(const LLVector3 &a, const LLVector3 &b); // Return a == b + friend bool operator!=(const LLVector3 &a, const LLVector3 &b); // Return a != b + // less than operator useful for using vectors as std::map keys + friend bool operator<(const LLVector3 &a, const LLVector3 &b); // Return a < b + + friend const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b); // Return vector a + b + friend const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b); // Return vector a minus b + friend const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b); // Return a cross b + friend const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b); // Returns a * b; + friend const LLVector3& operator*=(LLVector3 &a, F32 k); // Return a times scaler k + friend const LLVector3& operator/=(LLVector3 &a, F32 k); // Return a divided by scaler k + friend const LLVector3& operator*=(LLVector3 &a, const LLQuaternion &b); // Returns a * b; + + friend LLVector3 operator-(const LLVector3 &a); // Return vector -a + + friend std::ostream& operator<<(std::ostream& s, const LLVector3 &a); // Stream a + + static bool parseVector3(const std::string& buf, LLVector3* value); +}; + +typedef LLVector3 LLSimLocalVec; + +// Non-member functions + +F32 angle_between(const LLVector3 &a, const LLVector3 &b); // Returns angle (radians) between a and b +bool are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon=F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel +F32 dist_vec(const LLVector3 &a, const LLVector3 &b); // Returns distance between a and b +F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b);// Returns distance squared between a and b +F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b);// Returns distance squared between a and b ignoring Z component +LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b +LLVector3 inverse_projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a scaled such that projected_vec(inverse_projected_vec(a, b), b) == b; +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) +{ + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; +} + +inline LLVector3::LLVector3(const F32 x, const F32 y, const F32 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; +} + +inline LLVector3::LLVector3(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; +} + +/* +inline LLVector3::LLVector3(const LLVector3 ©) +{ + mV[VX] = copy.mV[VX]; + mV[VY] = copy.mV[VY]; + mV[VZ] = copy.mV[VZ]; +} +*/ + +// Destructors + +// checker +inline bool LLVector3::isFinite() const +{ + return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ])); +} + + +// Clear and Assignment Functions + +inline void LLVector3::clear(void) +{ + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; +} + +inline void LLVector3::setZero(void) +{ + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; +} + +inline void LLVector3::clearVec(void) +{ + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; +} + +inline void LLVector3::zeroVec(void) +{ + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; +} + +inline void LLVector3::set(F32 x, F32 y, F32 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; +} + +inline void LLVector3::set(const LLVector3 &vec) +{ + mV[0] = vec.mV[0]; + mV[1] = vec.mV[1]; + mV[2] = vec.mV[2]; +} + +inline void LLVector3::set(const F32 *vec) +{ + mV[0] = vec[0]; + mV[1] = vec[1]; + mV[2] = vec[2]; +} + +// deprecated +inline void LLVector3::setVec(F32 x, F32 y, F32 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; +} + +// deprecated +inline void LLVector3::setVec(const LLVector3 &vec) +{ + mV[0] = vec.mV[0]; + mV[1] = vec.mV[1]; + mV[2] = vec.mV[2]; +} + +// deprecated +inline void LLVector3::setVec(const F32 *vec) +{ + mV[0] = vec[0]; + mV[1] = vec[1]; + mV[2] = vec[2]; +} + +inline F32 LLVector3::normalize(void) +{ + F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + F32 oomag; + + if (mag > FP_MAG_THRESHOLD) + { + oomag = 1.f/mag; + mV[0] *= oomag; + mV[1] *= oomag; + mV[2] *= oomag; + } + else + { + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; + mag = 0; + } + return (mag); +} + +// deprecated +inline F32 LLVector3::normVec(void) +{ + F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + F32 oomag; + + if (mag > FP_MAG_THRESHOLD) + { + oomag = 1.f/mag; + mV[0] *= oomag; + mV[1] *= oomag; + mV[2] *= oomag; + } + else + { + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; + mag = 0; + } + return (mag); +} + +// LLVector3 Magnitude and Normalization Functions + +inline F32 LLVector3::length(void) const +{ + return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); +} + +inline F32 LLVector3::lengthSquared(void) const +{ + return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; +} + +inline F32 LLVector3::magVec(void) const +{ + return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); +} + +inline F32 LLVector3::magVecSquared(void) const +{ + return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; +} + +inline bool LLVector3::inRange( F32 min, F32 max ) const +{ + return mV[0] >= min && mV[0] <= max && + mV[1] >= min && mV[1] <= max && + mV[2] >= min && mV[2] <= max; +} + +inline LLVector3 operator+(const LLVector3 &a, const LLVector3 &b) +{ + LLVector3 c(a); + return c += b; +} + +inline LLVector3 operator-(const LLVector3 &a, const LLVector3 &b) +{ + LLVector3 c(a); + return c -= b; +} + +inline F32 operator*(const LLVector3 &a, const LLVector3 &b) +{ + return (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1] + a.mV[2]*b.mV[2]); +} + +inline LLVector3 operator%(const LLVector3 &a, const LLVector3 &b) +{ + return LLVector3( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1] ); +} + +inline LLVector3 operator/(const LLVector3 &a, F32 k) +{ + F32 t = 1.f / k; + return LLVector3( a.mV[0] * t, a.mV[1] * t, a.mV[2] * t ); +} + +inline LLVector3 operator*(const LLVector3 &a, F32 k) +{ + return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); +} + +inline LLVector3 operator*(F32 k, const LLVector3 &a) +{ + return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); +} + +inline bool operator==(const LLVector3 &a, const LLVector3 &b) +{ + return ( (a.mV[0] == b.mV[0]) + &&(a.mV[1] == b.mV[1]) + &&(a.mV[2] == b.mV[2])); +} + +inline bool operator!=(const LLVector3 &a, const LLVector3 &b) +{ + return ( (a.mV[0] != b.mV[0]) + ||(a.mV[1] != b.mV[1]) + ||(a.mV[2] != b.mV[2])); +} + +inline bool operator<(const LLVector3 &a, const LLVector3 &b) +{ + return (a.mV[0] < b.mV[0] + || (a.mV[0] == b.mV[0] + && (a.mV[1] < b.mV[1] + || ((a.mV[1] == b.mV[1]) + && a.mV[2] < b.mV[2])))); +} + +inline const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b) +{ + a.mV[0] += b.mV[0]; + a.mV[1] += b.mV[1]; + a.mV[2] += b.mV[2]; + return a; +} + +inline const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b) +{ + a.mV[0] -= b.mV[0]; + a.mV[1] -= b.mV[1]; + a.mV[2] -= b.mV[2]; + return a; +} + +inline const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b) +{ + LLVector3 ret( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1]); + a = ret; + return a; +} + +inline const LLVector3& operator*=(LLVector3 &a, F32 k) +{ + a.mV[0] *= k; + a.mV[1] *= k; + a.mV[2] *= k; + return a; +} + +inline const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b) +{ + a.mV[0] *= b.mV[0]; + a.mV[1] *= b.mV[1]; + a.mV[2] *= b.mV[2]; + return a; +} + +inline const LLVector3& operator/=(LLVector3 &a, F32 k) +{ + F32 t = 1.f / k; + a.mV[0] *= t; + a.mV[1] *= t; + a.mV[2] *= t; + return a; +} + +inline LLVector3 operator-(const LLVector3 &a) +{ + return LLVector3( -a.mV[0], -a.mV[1], -a.mV[2] ); +} + +inline F32 dist_vec(const LLVector3 &a, const LLVector3 &b) +{ + F32 x = a.mV[0] - b.mV[0]; + F32 y = a.mV[1] - b.mV[1]; + F32 z = a.mV[2] - b.mV[2]; + return (F32) sqrt( x*x + y*y + z*z ); +} + +inline F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b) +{ + F32 x = a.mV[0] - b.mV[0]; + F32 y = a.mV[1] - b.mV[1]; + F32 z = a.mV[2] - b.mV[2]; + return x*x + y*y + z*z; +} + +inline F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b) +{ + F32 x = a.mV[0] - b.mV[0]; + F32 y = a.mV[1] - b.mV[1]; + return x*x + y*y; +} + +inline LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b) +{ + F32 bb = b * b; + if (bb > FP_MAG_THRESHOLD * FP_MAG_THRESHOLD) + { + return ((a * b) / bb) * b; + } + else + { + return b.zero; + } +} + +inline LLVector3 inverse_projected_vec(const LLVector3& a, const LLVector3& b) +{ + LLVector3 normalized_a = a; + normalized_a.normalize(); + LLVector3 normalized_b = b; + F32 b_length = normalized_b.normalize(); + + F32 dot_product = normalized_a * normalized_b; + //NB: if a _|_ b, then returns an infinite vector + return normalized_a * (b_length / dot_product); +} + +inline LLVector3 parallel_component(const LLVector3 &a, const LLVector3 &b) +{ + return projected_vec(a, b); +} + +inline LLVector3 orthogonal_component(const LLVector3 &a, const LLVector3 &b) +{ + return a - projected_vec(a, b); +} + + +inline LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u) +{ + return LLVector3( + a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u, + a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u, + a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u); +} + + +inline bool LLVector3::isNull() const +{ + if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ] ) + { + return true; + } + return false; +} + +inline void update_min_max(LLVector3& min, LLVector3& max, const LLVector3& pos) +{ + for (U32 i = 0; i < 3; i++) + { + if (min.mV[i] > pos.mV[i]) + { + min.mV[i] = pos.mV[i]; + } + if (max.mV[i] < pos.mV[i]) + { + max.mV[i] = pos.mV[i]; + } + } +} + +inline void update_min_max(LLVector3& min, LLVector3& max, const F32* pos) +{ + for (U32 i = 0; i < 3; i++) + { + if (min.mV[i] > pos[i]) + { + min.mV[i] = pos[i]; + } + if (max.mV[i] < pos[i]) + { + max.mV[i] = pos[i]; + } + } +} + +inline F32 angle_between(const LLVector3& a, const LLVector3& b) +{ + F32 ab = a * b; // dotproduct + if (ab == -0.0f) + { + ab = 0.0f; // get rid of negative zero + } + LLVector3 c = a % b; // crossproduct + return atan2f(sqrtf(c * c), ab); // return the angle +} + +inline bool are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon) +{ + LLVector3 an = a; + LLVector3 bn = b; + an.normalize(); + bn.normalize(); + F32 dot = an * bn; + if ( (1.0f - fabs(dot)) < epsilon) + { + return true; + } + return false; +} + +inline std::ostream& operator<<(std::ostream& s, const LLVector3 &a) +{ + s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }"; + return s; +} + +#endif diff --git a/indra/llmath/v4color.cpp b/indra/llmath/v4color.cpp index 63ac1e9088..fd3548bc48 100644 --- a/indra/llmath/v4color.cpp +++ b/indra/llmath/v4color.cpp @@ -1,738 +1,738 @@ -/**
- * @file v4color.cpp
- * @brief LLColor4 class implementation.
- *
- * $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 "linden_common.h"
-
-#include "llboost.h"
-
-#include "v4color.h"
-#include "v4coloru.h"
-#include "v3color.h"
-#include "v4math.h"
-#include "llmath.h"
-
-// LLColor4
-
-//////////////////////////////////////////////////////////////////////////////
-
-LLColor4 LLColor4::red( 1.f, 0.f, 0.f, 1.f);
-LLColor4 LLColor4::green( 0.f, 1.f, 0.f, 1.f);
-LLColor4 LLColor4::blue( 0.f, 0.f, 1.f, 1.f);
-LLColor4 LLColor4::black( 0.f, 0.f, 0.f, 1.f);
-LLColor4 LLColor4::yellow( 1.f, 1.f, 0.f, 1.f);
-LLColor4 LLColor4::magenta( 1.0f, 0.0f, 1.0f, 1.0f);
-LLColor4 LLColor4::cyan( 0.0f, 1.0f, 1.0f, 1.0f);
-LLColor4 LLColor4::white( 1.f, 1.f, 1.f, 1.f);
-LLColor4 LLColor4::smoke( 0.5f, 0.5f, 0.5f, 0.5f);
-LLColor4 LLColor4::grey( 0.5f, 0.5f, 0.5f, 1.0f);
-LLColor4 LLColor4::orange( 1.f, 0.5, 0.f, 1.f );
-LLColor4 LLColor4::purple( 0.6f, 0.2f, 0.8f, 1.0f);
-LLColor4 LLColor4::pink( 1.0f, 0.5f, 0.8f, 1.0f);
-LLColor4 LLColor4::transparent( 0.f, 0.f, 0.f, 0.f );
-
-//////////////////////////////////////////////////////////////////////////////
-
-LLColor4 LLColor4::grey1(0.8f, 0.8f, 0.8f, 1.0f);
-LLColor4 LLColor4::grey2(0.6f, 0.6f, 0.6f, 1.0f);
-LLColor4 LLColor4::grey3(0.4f, 0.4f, 0.4f, 1.0f);
-LLColor4 LLColor4::grey4(0.3f, 0.3f, 0.3f, 1.0f);
-
-LLColor4 LLColor4::red1(1.0f, 0.0f, 0.0f, 1.0f);
-LLColor4 LLColor4::red2(0.6f, 0.0f, 0.0f, 1.0f);
-LLColor4 LLColor4::red3(1.0f, 0.2f, 0.2f, 1.0f);
-LLColor4 LLColor4::red4(0.5f, 0.1f, 0.1f, 1.0f);
-LLColor4 LLColor4::red5(0.8f, 0.1f, 0.0f, 1.0f);
-
-LLColor4 LLColor4::green1(0.0f, 1.0f, 0.0f, 1.0f);
-LLColor4 LLColor4::green2(0.0f, 0.6f, 0.0f, 1.0f);
-LLColor4 LLColor4::green3(0.0f, 0.4f, 0.0f, 1.0f);
-LLColor4 LLColor4::green4(0.0f, 1.0f, 0.4f, 1.0f);
-LLColor4 LLColor4::green5(0.2f, 0.6f, 0.4f, 1.0f);
-LLColor4 LLColor4::green6(0.4f, 0.6f, 0.2f, 1.0f);
-
-LLColor4 LLColor4::blue1(0.0f, 0.0f, 1.0f, 1.0f);
-LLColor4 LLColor4::blue2(0.0f, 0.4f, 1.0f, 1.0f);
-LLColor4 LLColor4::blue3(0.2f, 0.2f, 0.8f, 1.0f);
-LLColor4 LLColor4::blue4(0.0f, 0.0f, 0.6f, 1.0f);
-LLColor4 LLColor4::blue5(0.4f, 0.2f, 1.0f, 1.0f);
-LLColor4 LLColor4::blue6(0.4f, 0.5f, 1.0f, 1.0f);
-
-LLColor4 LLColor4::yellow1(1.0f, 1.0f, 0.0f, 1.0f);
-LLColor4 LLColor4::yellow2(0.6f, 0.6f, 0.0f, 1.0f);
-LLColor4 LLColor4::yellow3(0.8f, 1.0f, 0.2f, 1.0f);
-LLColor4 LLColor4::yellow4(1.0f, 1.0f, 0.4f, 1.0f);
-LLColor4 LLColor4::yellow5(0.6f, 0.4f, 0.2f, 1.0f);
-LLColor4 LLColor4::yellow6(1.0f, 0.8f, 0.4f, 1.0f);
-LLColor4 LLColor4::yellow7(0.8f, 0.8f, 0.0f, 1.0f);
-LLColor4 LLColor4::yellow8(0.8f, 0.8f, 0.2f, 1.0f);
-LLColor4 LLColor4::yellow9(0.8f, 0.8f, 0.4f, 1.0f);
-
-LLColor4 LLColor4::orange1(1.0f, 0.8f, 0.0f, 1.0f);
-LLColor4 LLColor4::orange2(1.0f, 0.6f, 0.0f, 1.0f);
-LLColor4 LLColor4::orange3(1.0f, 0.4f, 0.2f, 1.0f);
-LLColor4 LLColor4::orange4(0.8f, 0.4f, 0.0f, 1.0f);
-LLColor4 LLColor4::orange5(0.9f, 0.5f, 0.0f, 1.0f);
-LLColor4 LLColor4::orange6(1.0f, 0.8f, 0.2f, 1.0f);
-
-LLColor4 LLColor4::magenta1(1.0f, 0.0f, 1.0f, 1.0f);
-LLColor4 LLColor4::magenta2(0.6f, 0.2f, 0.4f, 1.0f);
-LLColor4 LLColor4::magenta3(1.0f, 0.4f, 0.6f, 1.0f);
-LLColor4 LLColor4::magenta4(1.0f, 0.2f, 0.8f, 1.0f);
-
-LLColor4 LLColor4::purple1(0.6f, 0.2f, 0.8f, 1.0f);
-LLColor4 LLColor4::purple2(0.8f, 0.2f, 1.0f, 1.0f);
-LLColor4 LLColor4::purple3(0.6f, 0.0f, 1.0f, 1.0f);
-LLColor4 LLColor4::purple4(0.4f, 0.0f, 0.8f, 1.0f);
-LLColor4 LLColor4::purple5(0.6f, 0.0f, 0.8f, 1.0f);
-LLColor4 LLColor4::purple6(0.8f, 0.0f, 0.6f, 1.0f);
-
-LLColor4 LLColor4::pink1(1.0f, 0.5f, 0.8f, 1.0f);
-LLColor4 LLColor4::pink2(1.0f, 0.8f, 0.9f, 1.0f);
-
-LLColor4 LLColor4::cyan1(0.0f, 1.0f, 1.0f, 1.0f);
-LLColor4 LLColor4::cyan2(0.4f, 0.8f, 0.8f, 1.0f);
-LLColor4 LLColor4::cyan3(0.0f, 1.0f, 0.6f, 1.0f);
-LLColor4 LLColor4::cyan4(0.6f, 1.0f, 1.0f, 1.0f);
-LLColor4 LLColor4::cyan5(0.2f, 0.6f, 1.0f, 1.0f);
-LLColor4 LLColor4::cyan6(0.2f, 0.6f, 0.6f, 1.0f);
-
-//////////////////////////////////////////////////////////////////////////////
-
-// conversion
-LLColor4::operator LLColor4U() const
-{
- return LLColor4U(
- (U8)llclampb(ll_round(mV[VRED]*255.f)),
- (U8)llclampb(ll_round(mV[VGREEN]*255.f)),
- (U8)llclampb(ll_round(mV[VBLUE]*255.f)),
- (U8)llclampb(ll_round(mV[VALPHA]*255.f)));
-}
-
-LLColor4::LLColor4(const LLColor3 &vec, F32 a)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = a;
-}
-
-LLColor4::LLColor4(const LLColor4U& color4u)
-{
- const F32 SCALE = 1.f/255.f;
- mV[VX] = color4u.mV[VX] * SCALE;
- mV[VY] = color4u.mV[VY] * SCALE;
- mV[VZ] = color4u.mV[VZ] * SCALE;
- mV[VW] = color4u.mV[VW] * SCALE;
-}
-
-LLColor4::LLColor4(const LLVector4& vector4)
-{
- mV[VX] = vector4.mV[VX];
- mV[VY] = vector4.mV[VY];
- mV[VZ] = vector4.mV[VZ];
- mV[VW] = vector4.mV[VW];
-}
-
-const LLColor4& LLColor4::set(const LLColor4U& color4u)
-{
- const F32 SCALE = 1.f/255.f;
- mV[VX] = color4u.mV[VX] * SCALE;
- mV[VY] = color4u.mV[VY] * SCALE;
- mV[VZ] = color4u.mV[VZ] * SCALE;
- mV[VW] = color4u.mV[VW] * SCALE;
- return (*this);
-}
-
-const LLColor4& LLColor4::set(const LLColor3 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
-
-// no change to alpha!
-// mV[VW] = 1.f;
-
- return (*this);
-}
-
-const LLColor4& LLColor4::set(const LLColor3 &vec, F32 a)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = a;
- return (*this);
-}
-
-// deprecated -- use set()
-const LLColor4& LLColor4::setVec(const LLColor4U& color4u)
-{
- const F32 SCALE = 1.f/255.f;
- mV[VX] = color4u.mV[VX] * SCALE;
- mV[VY] = color4u.mV[VY] * SCALE;
- mV[VZ] = color4u.mV[VZ] * SCALE;
- mV[VW] = color4u.mV[VW] * SCALE;
- return (*this);
-}
-
-// deprecated -- use set()
-const LLColor4& LLColor4::setVec(const LLColor3 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
-
-// no change to alpha!
-// mV[VW] = 1.f;
-
- return (*this);
-}
-
-// deprecated -- use set()
-const LLColor4& LLColor4::setVec(const LLColor3 &vec, F32 a)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = a;
- return (*this);
-}
-
-void LLColor4::setValue(const LLSD& sd)
-{
-#if 0
- // Clamping on setValue from LLSD is inconsistent with other set behavior
- F32 val;
- bool out_of_range = false;
- val = sd[0].asReal();
- mV[0] = llclamp(val, 0.f, 1.f);
- out_of_range = mV[0] != val;
-
- val = sd[1].asReal();
- mV[1] = llclamp(val, 0.f, 1.f);
- out_of_range |= mV[1] != val;
-
- val = sd[2].asReal();
- mV[2] = llclamp(val, 0.f, 1.f);
- out_of_range |= mV[2] != val;
-
- val = sd[3].asReal();
- mV[3] = llclamp(val, 0.f, 1.f);
- out_of_range |= mV[3] != val;
-
- if (out_of_range)
- {
- LL_WARNS() << "LLSD color value out of range!" << LL_ENDL;
- }
-#else
- mV[0] = (F32) sd[0].asReal();
- mV[1] = (F32) sd[1].asReal();
- mV[2] = (F32) sd[2].asReal();
- mV[3] = (F32) sd[3].asReal();
-#endif
-}
-
-const LLColor4& LLColor4::operator=(const LLColor3 &a)
-{
- mV[VX] = a.mV[VX];
- mV[VY] = a.mV[VY];
- mV[VZ] = a.mV[VZ];
-
-// converting from an rgb sets a=1 (opaque)
- mV[VW] = 1.f;
- return (*this);
-}
-
-
-std::ostream& operator<<(std::ostream& s, const LLColor4 &a)
-{
- s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << ", " << a.mV[VW] << " }";
- return s;
-}
-
-bool operator==(const LLColor4 &a, const LLColor3 &b)
-{
- return ( (a.mV[VX] == b.mV[VX])
- &&(a.mV[VY] == b.mV[VY])
- &&(a.mV[VZ] == b.mV[VZ]));
-}
-
-bool operator!=(const LLColor4 &a, const LLColor3 &b)
-{
- return ( (a.mV[VX] != b.mV[VX])
- ||(a.mV[VY] != b.mV[VY])
- ||(a.mV[VZ] != b.mV[VZ]));
-}
-
-LLColor3 vec4to3(const LLColor4 &vec)
-{
- LLColor3 temp(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
- return temp;
-}
-
-LLColor4 vec3to4(const LLColor3 &vec)
-{
- LLColor3 temp(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
- return temp;
-}
-
-static F32 hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn )
-{
- if ( valHUeIn < 0.0f ) valHUeIn += 1.0f;
- if ( valHUeIn > 1.0f ) valHUeIn -= 1.0f;
- if ( ( 6.0f * valHUeIn ) < 1.0f ) return ( val1In + ( val2In - val1In ) * 6.0f * valHUeIn );
- if ( ( 2.0f * valHUeIn ) < 1.0f ) return ( val2In );
- if ( ( 3.0f * valHUeIn ) < 2.0f ) return ( val1In + ( val2In - val1In ) * ( ( 2.0f / 3.0f ) - valHUeIn ) * 6.0f );
- return ( val1In );
-}
-
-void LLColor4::setHSL ( F32 hValIn, F32 sValIn, F32 lValIn)
-{
- if ( sValIn < 0.00001f )
- {
- mV[VRED] = lValIn;
- mV[VGREEN] = lValIn;
- mV[VBLUE] = lValIn;
- }
- else
- {
- F32 interVal1;
- F32 interVal2;
-
- if ( lValIn < 0.5f )
- interVal2 = lValIn * ( 1.0f + sValIn );
- else
- interVal2 = ( lValIn + sValIn ) - ( sValIn * lValIn );
-
- interVal1 = 2.0f * lValIn - interVal2;
-
- mV[VRED] = hueToRgb ( interVal1, interVal2, hValIn + ( 1.f / 3.f ) );
- mV[VGREEN] = hueToRgb ( interVal1, interVal2, hValIn );
- mV[VBLUE] = hueToRgb ( interVal1, interVal2, hValIn - ( 1.f / 3.f ) );
- }
-}
-
-void LLColor4::calcHSL(F32* hue, F32* saturation, F32* luminance) const
-{
- F32 var_R = mV[VRED];
- F32 var_G = mV[VGREEN];
- F32 var_B = mV[VBLUE];
-
- F32 var_Min = ( var_R < ( var_G < var_B ? var_G : var_B ) ? var_R : ( var_G < var_B ? var_G : var_B ) );
- F32 var_Max = ( var_R > ( var_G > var_B ? var_G : var_B ) ? var_R : ( var_G > var_B ? var_G : var_B ) );
-
- F32 del_Max = var_Max - var_Min;
-
- F32 L = ( var_Max + var_Min ) / 2.0f;
- F32 H = 0.0f;
- F32 S = 0.0f;
-
- if ( del_Max == 0.0f )
- {
- H = 0.0f;
- S = 0.0f;
- }
- else
- {
- if ( L < 0.5 )
- S = del_Max / ( var_Max + var_Min );
- else
- S = del_Max / ( 2.0f - var_Max - var_Min );
-
- F32 del_R = ( ( ( var_Max - var_R ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max;
- F32 del_G = ( ( ( var_Max - var_G ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max;
- F32 del_B = ( ( ( var_Max - var_B ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max;
-
- if ( var_R >= var_Max )
- H = del_B - del_G;
- else
- if ( var_G >= var_Max )
- H = ( 1.0f / 3.0f ) + del_R - del_B;
- else
- if ( var_B >= var_Max )
- H = ( 2.0f / 3.0f ) + del_G - del_R;
-
- if ( H < 0.0f ) H += 1.0f;
- if ( H > 1.0f ) H -= 1.0f;
- }
-
- if (hue) *hue = H;
- if (saturation) *saturation = S;
- if (luminance) *luminance = L;
-}
-
-// static
-bool LLColor4::parseColor(const std::string& buf, LLColor4* color)
-{
- if( buf.empty() || color == nullptr)
- {
- return false;
- }
-
- boost_tokenizer tokens(buf, boost::char_separator<char>(", "));
- boost_tokenizer::iterator token_iter = tokens.begin();
- if (token_iter == tokens.end())
- {
- return false;
- }
-
- // Grab the first token into a string, since we don't know
- // if this is a float or a color name.
- std::string color_name( (*token_iter) );
- ++token_iter;
-
- if (token_iter != tokens.end())
- {
- // There are more tokens to read. This must be a vector.
- LLColor4 v;
- LLStringUtil::convertToF32( color_name, v.mV[VX] );
- LLStringUtil::convertToF32( *token_iter, v.mV[VY] );
- v.mV[VZ] = 0.0f;
- v.mV[VW] = 1.0f;
-
- ++token_iter;
- if (token_iter == tokens.end())
- {
- // This is a malformed vector.
- LL_WARNS() << "LLColor4::parseColor() malformed color " << buf << LL_ENDL;
- }
- else
- {
- // There is a z-component.
- LLStringUtil::convertToF32( *token_iter, v.mV[VZ] );
-
- ++token_iter;
- if (token_iter != tokens.end())
- {
- // There is an alpha component.
- LLStringUtil::convertToF32( *token_iter, v.mV[VW] );
- }
- }
-
- // Make sure all values are between 0 and 1.
- if (v.mV[VX] > 1.f || v.mV[VY] > 1.f || v.mV[VZ] > 1.f || v.mV[VW] > 1.f)
- {
- v = v * (1.f / 255.f);
- }
- color->set( v );
- }
- else // Single value. Read as a named color.
- {
- // We have a color name
- if ( "red" == color_name )
- {
- color->set(LLColor4::red);
- }
- else if ( "red1" == color_name )
- {
- color->set(LLColor4::red1);
- }
- else if ( "red2" == color_name )
- {
- color->set(LLColor4::red2);
- }
- else if ( "red3" == color_name )
- {
- color->set(LLColor4::red3);
- }
- else if ( "red4" == color_name )
- {
- color->set(LLColor4::red4);
- }
- else if ( "red5" == color_name )
- {
- color->set(LLColor4::red5);
- }
- else if( "green" == color_name )
- {
- color->set(LLColor4::green);
- }
- else if( "green1" == color_name )
- {
- color->set(LLColor4::green1);
- }
- else if( "green2" == color_name )
- {
- color->set(LLColor4::green2);
- }
- else if( "green3" == color_name )
- {
- color->set(LLColor4::green3);
- }
- else if( "green4" == color_name )
- {
- color->set(LLColor4::green4);
- }
- else if( "green5" == color_name )
- {
- color->set(LLColor4::green5);
- }
- else if( "green6" == color_name )
- {
- color->set(LLColor4::green6);
- }
- else if( "blue" == color_name )
- {
- color->set(LLColor4::blue);
- }
- else if( "blue1" == color_name )
- {
- color->set(LLColor4::blue1);
- }
- else if( "blue2" == color_name )
- {
- color->set(LLColor4::blue2);
- }
- else if( "blue3" == color_name )
- {
- color->set(LLColor4::blue3);
- }
- else if( "blue4" == color_name )
- {
- color->set(LLColor4::blue4);
- }
- else if( "blue5" == color_name )
- {
- color->set(LLColor4::blue5);
- }
- else if( "blue6" == color_name )
- {
- color->set(LLColor4::blue6);
- }
- else if( "black" == color_name )
- {
- color->set(LLColor4::black);
- }
- else if( "white" == color_name )
- {
- color->set(LLColor4::white);
- }
- else if( "yellow" == color_name )
- {
- color->set(LLColor4::yellow);
- }
- else if( "yellow1" == color_name )
- {
- color->set(LLColor4::yellow1);
- }
- else if( "yellow2" == color_name )
- {
- color->set(LLColor4::yellow2);
- }
- else if( "yellow3" == color_name )
- {
- color->set(LLColor4::yellow3);
- }
- else if( "yellow4" == color_name )
- {
- color->set(LLColor4::yellow4);
- }
- else if( "yellow5" == color_name )
- {
- color->set(LLColor4::yellow5);
- }
- else if( "yellow6" == color_name )
- {
- color->set(LLColor4::yellow6);
- }
- else if( "magenta" == color_name )
- {
- color->set(LLColor4::magenta);
- }
- else if( "magenta1" == color_name )
- {
- color->set(LLColor4::magenta1);
- }
- else if( "magenta2" == color_name )
- {
- color->set(LLColor4::magenta2);
- }
- else if( "magenta3" == color_name )
- {
- color->set(LLColor4::magenta3);
- }
- else if( "magenta4" == color_name )
- {
- color->set(LLColor4::magenta4);
- }
- else if( "purple" == color_name )
- {
- color->set(LLColor4::purple);
- }
- else if( "purple1" == color_name )
- {
- color->set(LLColor4::purple1);
- }
- else if( "purple2" == color_name )
- {
- color->set(LLColor4::purple2);
- }
- else if( "purple3" == color_name )
- {
- color->set(LLColor4::purple3);
- }
- else if( "purple4" == color_name )
- {
- color->set(LLColor4::purple4);
- }
- else if( "purple5" == color_name )
- {
- color->set(LLColor4::purple5);
- }
- else if( "purple6" == color_name )
- {
- color->set(LLColor4::purple6);
- }
- else if( "pink" == color_name )
- {
- color->set(LLColor4::pink);
- }
- else if( "pink1" == color_name )
- {
- color->set(LLColor4::pink1);
- }
- else if( "pink2" == color_name )
- {
- color->set(LLColor4::pink2);
- }
- else if( "cyan" == color_name )
- {
- color->set(LLColor4::cyan);
- }
- else if( "cyan1" == color_name )
- {
- color->set(LLColor4::cyan1);
- }
- else if( "cyan2" == color_name )
- {
- color->set(LLColor4::cyan2);
- }
- else if( "cyan3" == color_name )
- {
- color->set(LLColor4::cyan3);
- }
- else if( "cyan4" == color_name )
- {
- color->set(LLColor4::cyan4);
- }
- else if( "cyan5" == color_name )
- {
- color->set(LLColor4::cyan5);
- }
- else if( "cyan6" == color_name )
- {
- color->set(LLColor4::cyan6);
- }
- else if( "smoke" == color_name )
- {
- color->set(LLColor4::smoke);
- }
- else if( "grey" == color_name )
- {
- color->set(LLColor4::grey);
- }
- else if( "grey1" == color_name )
- {
- color->set(LLColor4::grey1);
- }
- else if( "grey2" == color_name )
- {
- color->set(LLColor4::grey2);
- }
- else if( "grey3" == color_name )
- {
- color->set(LLColor4::grey3);
- }
- else if( "grey4" == color_name )
- {
- color->set(LLColor4::grey4);
- }
- else if( "orange" == color_name )
- {
- color->set(LLColor4::orange);
- }
- else if( "orange1" == color_name )
- {
- color->set(LLColor4::orange1);
- }
- else if( "orange2" == color_name )
- {
- color->set(LLColor4::orange2);
- }
- else if( "orange3" == color_name )
- {
- color->set(LLColor4::orange3);
- }
- else if( "orange4" == color_name )
- {
- color->set(LLColor4::orange4);
- }
- else if( "orange5" == color_name )
- {
- color->set(LLColor4::orange5);
- }
- else if( "orange6" == color_name )
- {
- color->set(LLColor4::orange6);
- }
- else if ( "clear" == color_name )
- {
- color->set(0.f, 0.f, 0.f, 0.f);
- }
- else
- {
- LL_WARNS() << "invalid color " << color_name << LL_ENDL;
- }
- }
-
- return true;
-}
-
-// static
-bool LLColor4::parseColor4(const std::string& buf, LLColor4* value)
-{
- if( buf.empty() || value == nullptr)
- {
- return false;
- }
-
- LLColor4 v;
- S32 count = sscanf( buf.c_str(), "%f, %f, %f, %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 );
- if (1 == count )
- {
- // try this format
- count = sscanf( buf.c_str(), "%f %f %f %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 );
- }
- if( 4 == count )
- {
- value->setVec( v );
- return true;
- }
-
- return false;
-}
-
-// EOF
+/** + * @file v4color.cpp + * @brief LLColor4 class implementation. + * + * $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 "linden_common.h" + +#include "llboost.h" + +#include "v4color.h" +#include "v4coloru.h" +#include "v3color.h" +#include "v4math.h" +#include "llmath.h" + +// LLColor4 + +////////////////////////////////////////////////////////////////////////////// + +LLColor4 LLColor4::red( 1.f, 0.f, 0.f, 1.f); +LLColor4 LLColor4::green( 0.f, 1.f, 0.f, 1.f); +LLColor4 LLColor4::blue( 0.f, 0.f, 1.f, 1.f); +LLColor4 LLColor4::black( 0.f, 0.f, 0.f, 1.f); +LLColor4 LLColor4::yellow( 1.f, 1.f, 0.f, 1.f); +LLColor4 LLColor4::magenta( 1.0f, 0.0f, 1.0f, 1.0f); +LLColor4 LLColor4::cyan( 0.0f, 1.0f, 1.0f, 1.0f); +LLColor4 LLColor4::white( 1.f, 1.f, 1.f, 1.f); +LLColor4 LLColor4::smoke( 0.5f, 0.5f, 0.5f, 0.5f); +LLColor4 LLColor4::grey( 0.5f, 0.5f, 0.5f, 1.0f); +LLColor4 LLColor4::orange( 1.f, 0.5, 0.f, 1.f ); +LLColor4 LLColor4::purple( 0.6f, 0.2f, 0.8f, 1.0f); +LLColor4 LLColor4::pink( 1.0f, 0.5f, 0.8f, 1.0f); +LLColor4 LLColor4::transparent( 0.f, 0.f, 0.f, 0.f ); + +////////////////////////////////////////////////////////////////////////////// + +LLColor4 LLColor4::grey1(0.8f, 0.8f, 0.8f, 1.0f); +LLColor4 LLColor4::grey2(0.6f, 0.6f, 0.6f, 1.0f); +LLColor4 LLColor4::grey3(0.4f, 0.4f, 0.4f, 1.0f); +LLColor4 LLColor4::grey4(0.3f, 0.3f, 0.3f, 1.0f); + +LLColor4 LLColor4::red1(1.0f, 0.0f, 0.0f, 1.0f); +LLColor4 LLColor4::red2(0.6f, 0.0f, 0.0f, 1.0f); +LLColor4 LLColor4::red3(1.0f, 0.2f, 0.2f, 1.0f); +LLColor4 LLColor4::red4(0.5f, 0.1f, 0.1f, 1.0f); +LLColor4 LLColor4::red5(0.8f, 0.1f, 0.0f, 1.0f); + +LLColor4 LLColor4::green1(0.0f, 1.0f, 0.0f, 1.0f); +LLColor4 LLColor4::green2(0.0f, 0.6f, 0.0f, 1.0f); +LLColor4 LLColor4::green3(0.0f, 0.4f, 0.0f, 1.0f); +LLColor4 LLColor4::green4(0.0f, 1.0f, 0.4f, 1.0f); +LLColor4 LLColor4::green5(0.2f, 0.6f, 0.4f, 1.0f); +LLColor4 LLColor4::green6(0.4f, 0.6f, 0.2f, 1.0f); + +LLColor4 LLColor4::blue1(0.0f, 0.0f, 1.0f, 1.0f); +LLColor4 LLColor4::blue2(0.0f, 0.4f, 1.0f, 1.0f); +LLColor4 LLColor4::blue3(0.2f, 0.2f, 0.8f, 1.0f); +LLColor4 LLColor4::blue4(0.0f, 0.0f, 0.6f, 1.0f); +LLColor4 LLColor4::blue5(0.4f, 0.2f, 1.0f, 1.0f); +LLColor4 LLColor4::blue6(0.4f, 0.5f, 1.0f, 1.0f); + +LLColor4 LLColor4::yellow1(1.0f, 1.0f, 0.0f, 1.0f); +LLColor4 LLColor4::yellow2(0.6f, 0.6f, 0.0f, 1.0f); +LLColor4 LLColor4::yellow3(0.8f, 1.0f, 0.2f, 1.0f); +LLColor4 LLColor4::yellow4(1.0f, 1.0f, 0.4f, 1.0f); +LLColor4 LLColor4::yellow5(0.6f, 0.4f, 0.2f, 1.0f); +LLColor4 LLColor4::yellow6(1.0f, 0.8f, 0.4f, 1.0f); +LLColor4 LLColor4::yellow7(0.8f, 0.8f, 0.0f, 1.0f); +LLColor4 LLColor4::yellow8(0.8f, 0.8f, 0.2f, 1.0f); +LLColor4 LLColor4::yellow9(0.8f, 0.8f, 0.4f, 1.0f); + +LLColor4 LLColor4::orange1(1.0f, 0.8f, 0.0f, 1.0f); +LLColor4 LLColor4::orange2(1.0f, 0.6f, 0.0f, 1.0f); +LLColor4 LLColor4::orange3(1.0f, 0.4f, 0.2f, 1.0f); +LLColor4 LLColor4::orange4(0.8f, 0.4f, 0.0f, 1.0f); +LLColor4 LLColor4::orange5(0.9f, 0.5f, 0.0f, 1.0f); +LLColor4 LLColor4::orange6(1.0f, 0.8f, 0.2f, 1.0f); + +LLColor4 LLColor4::magenta1(1.0f, 0.0f, 1.0f, 1.0f); +LLColor4 LLColor4::magenta2(0.6f, 0.2f, 0.4f, 1.0f); +LLColor4 LLColor4::magenta3(1.0f, 0.4f, 0.6f, 1.0f); +LLColor4 LLColor4::magenta4(1.0f, 0.2f, 0.8f, 1.0f); + +LLColor4 LLColor4::purple1(0.6f, 0.2f, 0.8f, 1.0f); +LLColor4 LLColor4::purple2(0.8f, 0.2f, 1.0f, 1.0f); +LLColor4 LLColor4::purple3(0.6f, 0.0f, 1.0f, 1.0f); +LLColor4 LLColor4::purple4(0.4f, 0.0f, 0.8f, 1.0f); +LLColor4 LLColor4::purple5(0.6f, 0.0f, 0.8f, 1.0f); +LLColor4 LLColor4::purple6(0.8f, 0.0f, 0.6f, 1.0f); + +LLColor4 LLColor4::pink1(1.0f, 0.5f, 0.8f, 1.0f); +LLColor4 LLColor4::pink2(1.0f, 0.8f, 0.9f, 1.0f); + +LLColor4 LLColor4::cyan1(0.0f, 1.0f, 1.0f, 1.0f); +LLColor4 LLColor4::cyan2(0.4f, 0.8f, 0.8f, 1.0f); +LLColor4 LLColor4::cyan3(0.0f, 1.0f, 0.6f, 1.0f); +LLColor4 LLColor4::cyan4(0.6f, 1.0f, 1.0f, 1.0f); +LLColor4 LLColor4::cyan5(0.2f, 0.6f, 1.0f, 1.0f); +LLColor4 LLColor4::cyan6(0.2f, 0.6f, 0.6f, 1.0f); + +////////////////////////////////////////////////////////////////////////////// + +// conversion +LLColor4::operator LLColor4U() const +{ + return LLColor4U( + (U8)llclampb(ll_round(mV[VRED]*255.f)), + (U8)llclampb(ll_round(mV[VGREEN]*255.f)), + (U8)llclampb(ll_round(mV[VBLUE]*255.f)), + (U8)llclampb(ll_round(mV[VALPHA]*255.f))); +} + +LLColor4::LLColor4(const LLColor3 &vec, F32 a) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = a; +} + +LLColor4::LLColor4(const LLColor4U& color4u) +{ + const F32 SCALE = 1.f/255.f; + mV[VX] = color4u.mV[VX] * SCALE; + mV[VY] = color4u.mV[VY] * SCALE; + mV[VZ] = color4u.mV[VZ] * SCALE; + mV[VW] = color4u.mV[VW] * SCALE; +} + +LLColor4::LLColor4(const LLVector4& vector4) +{ + mV[VX] = vector4.mV[VX]; + mV[VY] = vector4.mV[VY]; + mV[VZ] = vector4.mV[VZ]; + mV[VW] = vector4.mV[VW]; +} + +const LLColor4& LLColor4::set(const LLColor4U& color4u) +{ + const F32 SCALE = 1.f/255.f; + mV[VX] = color4u.mV[VX] * SCALE; + mV[VY] = color4u.mV[VY] * SCALE; + mV[VZ] = color4u.mV[VZ] * SCALE; + mV[VW] = color4u.mV[VW] * SCALE; + return (*this); +} + +const LLColor4& LLColor4::set(const LLColor3 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + +// no change to alpha! +// mV[VW] = 1.f; + + return (*this); +} + +const LLColor4& LLColor4::set(const LLColor3 &vec, F32 a) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = a; + return (*this); +} + +// deprecated -- use set() +const LLColor4& LLColor4::setVec(const LLColor4U& color4u) +{ + const F32 SCALE = 1.f/255.f; + mV[VX] = color4u.mV[VX] * SCALE; + mV[VY] = color4u.mV[VY] * SCALE; + mV[VZ] = color4u.mV[VZ] * SCALE; + mV[VW] = color4u.mV[VW] * SCALE; + return (*this); +} + +// deprecated -- use set() +const LLColor4& LLColor4::setVec(const LLColor3 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + +// no change to alpha! +// mV[VW] = 1.f; + + return (*this); +} + +// deprecated -- use set() +const LLColor4& LLColor4::setVec(const LLColor3 &vec, F32 a) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = a; + return (*this); +} + +void LLColor4::setValue(const LLSD& sd) +{ +#if 0 + // Clamping on setValue from LLSD is inconsistent with other set behavior + F32 val; + bool out_of_range = false; + val = sd[0].asReal(); + mV[0] = llclamp(val, 0.f, 1.f); + out_of_range = mV[0] != val; + + val = sd[1].asReal(); + mV[1] = llclamp(val, 0.f, 1.f); + out_of_range |= mV[1] != val; + + val = sd[2].asReal(); + mV[2] = llclamp(val, 0.f, 1.f); + out_of_range |= mV[2] != val; + + val = sd[3].asReal(); + mV[3] = llclamp(val, 0.f, 1.f); + out_of_range |= mV[3] != val; + + if (out_of_range) + { + LL_WARNS() << "LLSD color value out of range!" << LL_ENDL; + } +#else + mV[0] = (F32) sd[0].asReal(); + mV[1] = (F32) sd[1].asReal(); + mV[2] = (F32) sd[2].asReal(); + mV[3] = (F32) sd[3].asReal(); +#endif +} + +const LLColor4& LLColor4::operator=(const LLColor3 &a) +{ + mV[VX] = a.mV[VX]; + mV[VY] = a.mV[VY]; + mV[VZ] = a.mV[VZ]; + +// converting from an rgb sets a=1 (opaque) + mV[VW] = 1.f; + return (*this); +} + + +std::ostream& operator<<(std::ostream& s, const LLColor4 &a) +{ + s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << ", " << a.mV[VW] << " }"; + return s; +} + +bool operator==(const LLColor4 &a, const LLColor3 &b) +{ + return ( (a.mV[VX] == b.mV[VX]) + &&(a.mV[VY] == b.mV[VY]) + &&(a.mV[VZ] == b.mV[VZ])); +} + +bool operator!=(const LLColor4 &a, const LLColor3 &b) +{ + return ( (a.mV[VX] != b.mV[VX]) + ||(a.mV[VY] != b.mV[VY]) + ||(a.mV[VZ] != b.mV[VZ])); +} + +LLColor3 vec4to3(const LLColor4 &vec) +{ + LLColor3 temp(vec.mV[VX], vec.mV[VY], vec.mV[VZ]); + return temp; +} + +LLColor4 vec3to4(const LLColor3 &vec) +{ + LLColor3 temp(vec.mV[VX], vec.mV[VY], vec.mV[VZ]); + return temp; +} + +static F32 hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn ) +{ + if ( valHUeIn < 0.0f ) valHUeIn += 1.0f; + if ( valHUeIn > 1.0f ) valHUeIn -= 1.0f; + if ( ( 6.0f * valHUeIn ) < 1.0f ) return ( val1In + ( val2In - val1In ) * 6.0f * valHUeIn ); + if ( ( 2.0f * valHUeIn ) < 1.0f ) return ( val2In ); + if ( ( 3.0f * valHUeIn ) < 2.0f ) return ( val1In + ( val2In - val1In ) * ( ( 2.0f / 3.0f ) - valHUeIn ) * 6.0f ); + return ( val1In ); +} + +void LLColor4::setHSL ( F32 hValIn, F32 sValIn, F32 lValIn) +{ + if ( sValIn < 0.00001f ) + { + mV[VRED] = lValIn; + mV[VGREEN] = lValIn; + mV[VBLUE] = lValIn; + } + else + { + F32 interVal1; + F32 interVal2; + + if ( lValIn < 0.5f ) + interVal2 = lValIn * ( 1.0f + sValIn ); + else + interVal2 = ( lValIn + sValIn ) - ( sValIn * lValIn ); + + interVal1 = 2.0f * lValIn - interVal2; + + mV[VRED] = hueToRgb ( interVal1, interVal2, hValIn + ( 1.f / 3.f ) ); + mV[VGREEN] = hueToRgb ( interVal1, interVal2, hValIn ); + mV[VBLUE] = hueToRgb ( interVal1, interVal2, hValIn - ( 1.f / 3.f ) ); + } +} + +void LLColor4::calcHSL(F32* hue, F32* saturation, F32* luminance) const +{ + F32 var_R = mV[VRED]; + F32 var_G = mV[VGREEN]; + F32 var_B = mV[VBLUE]; + + F32 var_Min = ( var_R < ( var_G < var_B ? var_G : var_B ) ? var_R : ( var_G < var_B ? var_G : var_B ) ); + F32 var_Max = ( var_R > ( var_G > var_B ? var_G : var_B ) ? var_R : ( var_G > var_B ? var_G : var_B ) ); + + F32 del_Max = var_Max - var_Min; + + F32 L = ( var_Max + var_Min ) / 2.0f; + F32 H = 0.0f; + F32 S = 0.0f; + + if ( del_Max == 0.0f ) + { + H = 0.0f; + S = 0.0f; + } + else + { + if ( L < 0.5 ) + S = del_Max / ( var_Max + var_Min ); + else + S = del_Max / ( 2.0f - var_Max - var_Min ); + + F32 del_R = ( ( ( var_Max - var_R ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; + F32 del_G = ( ( ( var_Max - var_G ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; + F32 del_B = ( ( ( var_Max - var_B ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; + + if ( var_R >= var_Max ) + H = del_B - del_G; + else + if ( var_G >= var_Max ) + H = ( 1.0f / 3.0f ) + del_R - del_B; + else + if ( var_B >= var_Max ) + H = ( 2.0f / 3.0f ) + del_G - del_R; + + if ( H < 0.0f ) H += 1.0f; + if ( H > 1.0f ) H -= 1.0f; + } + + if (hue) *hue = H; + if (saturation) *saturation = S; + if (luminance) *luminance = L; +} + +// static +bool LLColor4::parseColor(const std::string& buf, LLColor4* color) +{ + if( buf.empty() || color == nullptr) + { + return false; + } + + boost_tokenizer tokens(buf, boost::char_separator<char>(", ")); + boost_tokenizer::iterator token_iter = tokens.begin(); + if (token_iter == tokens.end()) + { + return false; + } + + // Grab the first token into a string, since we don't know + // if this is a float or a color name. + std::string color_name( (*token_iter) ); + ++token_iter; + + if (token_iter != tokens.end()) + { + // There are more tokens to read. This must be a vector. + LLColor4 v; + LLStringUtil::convertToF32( color_name, v.mV[VX] ); + LLStringUtil::convertToF32( *token_iter, v.mV[VY] ); + v.mV[VZ] = 0.0f; + v.mV[VW] = 1.0f; + + ++token_iter; + if (token_iter == tokens.end()) + { + // This is a malformed vector. + LL_WARNS() << "LLColor4::parseColor() malformed color " << buf << LL_ENDL; + } + else + { + // There is a z-component. + LLStringUtil::convertToF32( *token_iter, v.mV[VZ] ); + + ++token_iter; + if (token_iter != tokens.end()) + { + // There is an alpha component. + LLStringUtil::convertToF32( *token_iter, v.mV[VW] ); + } + } + + // Make sure all values are between 0 and 1. + if (v.mV[VX] > 1.f || v.mV[VY] > 1.f || v.mV[VZ] > 1.f || v.mV[VW] > 1.f) + { + v = v * (1.f / 255.f); + } + color->set( v ); + } + else // Single value. Read as a named color. + { + // We have a color name + if ( "red" == color_name ) + { + color->set(LLColor4::red); + } + else if ( "red1" == color_name ) + { + color->set(LLColor4::red1); + } + else if ( "red2" == color_name ) + { + color->set(LLColor4::red2); + } + else if ( "red3" == color_name ) + { + color->set(LLColor4::red3); + } + else if ( "red4" == color_name ) + { + color->set(LLColor4::red4); + } + else if ( "red5" == color_name ) + { + color->set(LLColor4::red5); + } + else if( "green" == color_name ) + { + color->set(LLColor4::green); + } + else if( "green1" == color_name ) + { + color->set(LLColor4::green1); + } + else if( "green2" == color_name ) + { + color->set(LLColor4::green2); + } + else if( "green3" == color_name ) + { + color->set(LLColor4::green3); + } + else if( "green4" == color_name ) + { + color->set(LLColor4::green4); + } + else if( "green5" == color_name ) + { + color->set(LLColor4::green5); + } + else if( "green6" == color_name ) + { + color->set(LLColor4::green6); + } + else if( "blue" == color_name ) + { + color->set(LLColor4::blue); + } + else if( "blue1" == color_name ) + { + color->set(LLColor4::blue1); + } + else if( "blue2" == color_name ) + { + color->set(LLColor4::blue2); + } + else if( "blue3" == color_name ) + { + color->set(LLColor4::blue3); + } + else if( "blue4" == color_name ) + { + color->set(LLColor4::blue4); + } + else if( "blue5" == color_name ) + { + color->set(LLColor4::blue5); + } + else if( "blue6" == color_name ) + { + color->set(LLColor4::blue6); + } + else if( "black" == color_name ) + { + color->set(LLColor4::black); + } + else if( "white" == color_name ) + { + color->set(LLColor4::white); + } + else if( "yellow" == color_name ) + { + color->set(LLColor4::yellow); + } + else if( "yellow1" == color_name ) + { + color->set(LLColor4::yellow1); + } + else if( "yellow2" == color_name ) + { + color->set(LLColor4::yellow2); + } + else if( "yellow3" == color_name ) + { + color->set(LLColor4::yellow3); + } + else if( "yellow4" == color_name ) + { + color->set(LLColor4::yellow4); + } + else if( "yellow5" == color_name ) + { + color->set(LLColor4::yellow5); + } + else if( "yellow6" == color_name ) + { + color->set(LLColor4::yellow6); + } + else if( "magenta" == color_name ) + { + color->set(LLColor4::magenta); + } + else if( "magenta1" == color_name ) + { + color->set(LLColor4::magenta1); + } + else if( "magenta2" == color_name ) + { + color->set(LLColor4::magenta2); + } + else if( "magenta3" == color_name ) + { + color->set(LLColor4::magenta3); + } + else if( "magenta4" == color_name ) + { + color->set(LLColor4::magenta4); + } + else if( "purple" == color_name ) + { + color->set(LLColor4::purple); + } + else if( "purple1" == color_name ) + { + color->set(LLColor4::purple1); + } + else if( "purple2" == color_name ) + { + color->set(LLColor4::purple2); + } + else if( "purple3" == color_name ) + { + color->set(LLColor4::purple3); + } + else if( "purple4" == color_name ) + { + color->set(LLColor4::purple4); + } + else if( "purple5" == color_name ) + { + color->set(LLColor4::purple5); + } + else if( "purple6" == color_name ) + { + color->set(LLColor4::purple6); + } + else if( "pink" == color_name ) + { + color->set(LLColor4::pink); + } + else if( "pink1" == color_name ) + { + color->set(LLColor4::pink1); + } + else if( "pink2" == color_name ) + { + color->set(LLColor4::pink2); + } + else if( "cyan" == color_name ) + { + color->set(LLColor4::cyan); + } + else if( "cyan1" == color_name ) + { + color->set(LLColor4::cyan1); + } + else if( "cyan2" == color_name ) + { + color->set(LLColor4::cyan2); + } + else if( "cyan3" == color_name ) + { + color->set(LLColor4::cyan3); + } + else if( "cyan4" == color_name ) + { + color->set(LLColor4::cyan4); + } + else if( "cyan5" == color_name ) + { + color->set(LLColor4::cyan5); + } + else if( "cyan6" == color_name ) + { + color->set(LLColor4::cyan6); + } + else if( "smoke" == color_name ) + { + color->set(LLColor4::smoke); + } + else if( "grey" == color_name ) + { + color->set(LLColor4::grey); + } + else if( "grey1" == color_name ) + { + color->set(LLColor4::grey1); + } + else if( "grey2" == color_name ) + { + color->set(LLColor4::grey2); + } + else if( "grey3" == color_name ) + { + color->set(LLColor4::grey3); + } + else if( "grey4" == color_name ) + { + color->set(LLColor4::grey4); + } + else if( "orange" == color_name ) + { + color->set(LLColor4::orange); + } + else if( "orange1" == color_name ) + { + color->set(LLColor4::orange1); + } + else if( "orange2" == color_name ) + { + color->set(LLColor4::orange2); + } + else if( "orange3" == color_name ) + { + color->set(LLColor4::orange3); + } + else if( "orange4" == color_name ) + { + color->set(LLColor4::orange4); + } + else if( "orange5" == color_name ) + { + color->set(LLColor4::orange5); + } + else if( "orange6" == color_name ) + { + color->set(LLColor4::orange6); + } + else if ( "clear" == color_name ) + { + color->set(0.f, 0.f, 0.f, 0.f); + } + else + { + LL_WARNS() << "invalid color " << color_name << LL_ENDL; + } + } + + return true; +} + +// static +bool LLColor4::parseColor4(const std::string& buf, LLColor4* value) +{ + if( buf.empty() || value == nullptr) + { + return false; + } + + LLColor4 v; + S32 count = sscanf( buf.c_str(), "%f, %f, %f, %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 ); + if (1 == count ) + { + // try this format + count = sscanf( buf.c_str(), "%f %f %f %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 ); + } + if( 4 == count ) + { + value->setVec( v ); + return true; + } + + return false; +} + +// EOF diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h index b92522a5db..1e20ab977a 100644 --- a/indra/llmath/v4color.h +++ b/indra/llmath/v4color.h @@ -1,723 +1,723 @@ -/**
- * @file v4color.h
- * @brief LLColor4 class header file.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_V4COLOR_H
-#define LL_V4COLOR_H
-
-#include "llerror.h"
-//#include "vmath.h"
-#include "llmath.h"
-#include "llsd.h"
-
-class LLColor3;
-class LLColor4U;
-class LLVector4;
-
-// LLColor4 = |x y z w|
-
-static const U32 LENGTHOFCOLOR4 = 4;
-
-static const U32 MAX_LENGTH_OF_COLOR_NAME = 15; //Give plenty of room for additional colors...
-
-class LLColor4
-{
- public:
- F32 mV[LENGTHOFCOLOR4];
- LLColor4(); // Initializes LLColor4 to (0, 0, 0, 1)
- LLColor4(F32 r, F32 g, F32 b); // Initializes LLColor4 to (r, g, b, 1)
- LLColor4(F32 r, F32 g, F32 b, F32 a); // Initializes LLColor4 to (r. g, b, a)
- LLColor4(const LLColor3 &vec, F32 a = 1.f); // Initializes LLColor4 to (vec, a)
- explicit LLColor4(const LLSD& sd);
- explicit LLColor4(const F32 *vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], 1)
- explicit LLColor4(U32 clr); // Initializes LLColor4 to (r=clr>>24, etc))
- explicit LLColor4(const LLColor4U& color4u); // "explicit" to avoid automatic conversion
- explicit LLColor4(const LLVector4& vector4); // "explicit" to avoid automatic conversion
-
- LLSD getValue() const
- {
- LLSD ret;
- ret[0] = mV[0];
- ret[1] = mV[1];
- ret[2] = mV[2];
- ret[3] = mV[3];
- return ret;
- }
-
- void setValue(const LLSD& sd);
-
- void setHSL(F32 hue, F32 saturation, F32 luminance);
- void calcHSL(F32* hue, F32* saturation, F32* luminance) const;
-
- const LLColor4& setToBlack(); // zero LLColor4 to (0, 0, 0, 1)
- const LLColor4& setToWhite(); // zero LLColor4 to (0, 0, 0, 1)
-
- const LLColor4& setVec(F32 r, F32 g, F32 b, F32 a); // deprecated -- use set()
- const LLColor4& setVec(F32 r, F32 g, F32 b); // deprecated -- use set()
- const LLColor4& setVec(const LLColor4 &vec); // deprecated -- use set()
- const LLColor4& setVec(const LLColor3 &vec); // deprecated -- use set()
- const LLColor4& setVec(const LLColor3 &vec, F32 a); // deprecated -- use set()
- const LLColor4& setVec(const F32 *vec); // deprecated -- use set()
- const LLColor4& setVec(const LLColor4U& color4u); // deprecated -- use set()
-
- const LLColor4& set(F32 r, F32 g, F32 b, F32 a); // Sets LLColor4 to (r, g, b, a)
- const LLColor4& set(F32 r, F32 g, F32 b); // Sets LLColor4 to (r, g, b) (no change in a)
- const LLColor4& set(const LLColor4 &vec); // Sets LLColor4 to vec
- const LLColor4& set(const LLColor3 &vec); // Sets LLColor4 to LLColor3 vec (no change in alpha)
- const LLColor4& set(const LLColor3 &vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified
- const LLColor4& set(const F32 *vec); // Sets LLColor4 to vec
- const LLColor4& set(const F64 *vec); // Sets LLColor4 to (double)vec
- const LLColor4& set(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled.
-
- // set from a vector of unknown type and size
- // may leave some data unmodified
- template<typename T>
- const LLColor4& set(const std::vector<T>& v);
-
- // write to a vector of unknown type and size
- // maye leave some data unmodified
- template<typename T>
- void write(std::vector<T>& v) const;
-
- const LLColor4& setAlpha(F32 a);
-
- F32 magVec() const; // deprecated -- use length()
- F32 magVecSquared() const; // deprecated -- use lengthSquared()
- F32 normVec(); // deprecated -- use normalize()
-
- F32 length() const; // Returns magnitude of LLColor4
- F32 lengthSquared() const; // Returns magnitude squared of LLColor4
- F32 normalize(); // deprecated -- use normalize()
-
- bool isOpaque() { return mV[VALPHA] == 1.f; }
-
- F32 operator[](int idx) const { return mV[idx]; }
- F32 &operator[](int idx) { return mV[idx]; }
-
- const LLColor4& operator=(const LLColor3 &a); // Assigns vec3 to vec4 and returns vec4
-
- bool operator<(const LLColor4& rhs) const;
- friend std::ostream& operator<<(std::ostream& s, const LLColor4 &a); // Print a
- friend LLColor4 operator+(const LLColor4 &a, const LLColor4 &b); // Return vector a + b
- friend LLColor4 operator-(const LLColor4 &a, const LLColor4 &b); // Return vector a minus b
- friend LLColor4 operator*(const LLColor4 &a, const LLColor4 &b); // Return component wise a * b
- friend LLColor4 operator*(const LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change)
- friend LLColor4 operator/(const LLColor4 &a, F32 k); // Return rgb divided by scalar k (no alpha change)
- friend LLColor4 operator*(F32 k, const LLColor4 &a); // Return rgb times scaler k (no alpha change)
- friend LLColor4 operator%(const LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change)
- friend LLColor4 operator%(F32 k, const LLColor4 &a); // Return alpha times scaler k (no rgb change)
-
- friend bool operator==(const LLColor4 &a, const LLColor4 &b); // Return a == b
- friend bool operator!=(const LLColor4 &a, const LLColor4 &b); // Return a != b
-
- friend bool operator==(const LLColor4 &a, const LLColor3 &b); // Return a == b
- friend bool operator!=(const LLColor4 &a, const LLColor3 &b); // Return a != b
-
- friend const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b); // Return vector a + b
- friend const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b); // Return vector a minus b
- friend const LLColor4& operator*=(LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change)
- friend const LLColor4& operator%=(LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change)
-
- friend const LLColor4& operator*=(LLColor4 &a, const LLColor4 &b); // Doesn't multiply alpha! (for lighting)
-
- // conversion
- operator LLColor4U() const;
-
- // Basic color values.
- static LLColor4 red;
- static LLColor4 green;
- static LLColor4 blue;
- static LLColor4 black;
- static LLColor4 white;
- static LLColor4 yellow;
- static LLColor4 magenta;
- static LLColor4 cyan;
- static LLColor4 smoke;
- static LLColor4 grey;
- static LLColor4 orange;
- static LLColor4 purple;
- static LLColor4 pink;
- static LLColor4 transparent;
-
- // Extra color values.
- static LLColor4 grey1;
- static LLColor4 grey2;
- static LLColor4 grey3;
- static LLColor4 grey4;
-
- static LLColor4 red1;
- static LLColor4 red2;
- static LLColor4 red3;
- static LLColor4 red4;
- static LLColor4 red5;
-
- static LLColor4 green1;
- static LLColor4 green2;
- static LLColor4 green3;
- static LLColor4 green4;
- static LLColor4 green5;
- static LLColor4 green6;
-
- static LLColor4 blue1;
- static LLColor4 blue2;
- static LLColor4 blue3;
- static LLColor4 blue4;
- static LLColor4 blue5;
- static LLColor4 blue6;
-
- static LLColor4 yellow1;
- static LLColor4 yellow2;
- static LLColor4 yellow3;
- static LLColor4 yellow4;
- static LLColor4 yellow5;
- static LLColor4 yellow6;
- static LLColor4 yellow7;
- static LLColor4 yellow8;
- static LLColor4 yellow9;
-
- static LLColor4 orange1;
- static LLColor4 orange2;
- static LLColor4 orange3;
- static LLColor4 orange4;
- static LLColor4 orange5;
- static LLColor4 orange6;
-
- static LLColor4 magenta1;
- static LLColor4 magenta2;
- static LLColor4 magenta3;
- static LLColor4 magenta4;
-
- static LLColor4 purple1;
- static LLColor4 purple2;
- static LLColor4 purple3;
- static LLColor4 purple4;
- static LLColor4 purple5;
- static LLColor4 purple6;
-
- static LLColor4 pink1;
- static LLColor4 pink2;
-
- static LLColor4 cyan1;
- static LLColor4 cyan2;
- static LLColor4 cyan3;
- static LLColor4 cyan4;
- static LLColor4 cyan5;
- static LLColor4 cyan6;
-
- static bool parseColor(const std::string& buf, LLColor4* color);
- static bool parseColor4(const std::string& buf, LLColor4* color);
-
- inline void clamp();
-};
-
-
-// Non-member functions
-F32 distVec(const LLColor4 &a, const LLColor4 &b); // Returns distance between a and b
-F32 distVec_squared(const LLColor4 &a, const LLColor4 &b); // Returns distance squared between a and b
-LLColor3 vec4to3(const LLColor4 &vec);
-LLColor4 vec3to4(const LLColor3 &vec);
-LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u);
-
-inline LLColor4::LLColor4(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
- mV[VZ] = 0.f;
- mV[VW] = 1.f;
-}
-
-inline LLColor4::LLColor4(const LLSD& sd)
-{
- this->setValue(sd);
-}
-
-inline LLColor4::LLColor4(F32 r, F32 g, F32 b)
-{
- mV[VX] = r;
- mV[VY] = g;
- mV[VZ] = b;
- mV[VW] = 1.f;
-}
-
-inline LLColor4::LLColor4(F32 r, F32 g, F32 b, F32 a)
-{
- mV[VX] = r;
- mV[VY] = g;
- mV[VZ] = b;
- mV[VW] = a;
-}
-
-inline LLColor4::LLColor4(U32 clr)
-{
- mV[VX] = (clr&0xff) * (1.0f/255.0f);
- mV[VY] = ((clr>>8)&0xff) * (1.0f/255.0f);
- mV[VZ] = ((clr>>16)&0xff) * (1.0f/255.0f);
- mV[VW] = (clr>>24) * (1.0f/255.0f);
-}
-
-
-inline LLColor4::LLColor4(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
- mV[VW] = vec[VW];
-}
-
-inline const LLColor4& LLColor4::setToBlack(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
- mV[VZ] = 0.f;
- mV[VW] = 1.f;
- return (*this);
-}
-
-inline const LLColor4& LLColor4::setToWhite(void)
-{
- mV[VX] = 1.f;
- mV[VY] = 1.f;
- mV[VZ] = 1.f;
- mV[VW] = 1.f;
- return (*this);
-}
-
-inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
-
-// no change to alpha!
-// mV[VW] = 1.f;
-
- return (*this);
-}
-
-inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z, F32 a)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
- mV[VW] = a;
- return (*this);
-}
-
-inline const LLColor4& LLColor4::set(const LLColor4 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = vec.mV[VW];
- return (*this);
-}
-
-
-inline const LLColor4& LLColor4::set(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
- mV[VW] = vec[VW];
- return (*this);
-}
-
-inline const LLColor4& LLColor4::set(const F64 *vec)
-{
- mV[VX] = static_cast<F32>(vec[VX]);
- mV[VY] = static_cast<F32>(vec[VY]);
- mV[VZ] = static_cast<F32>(vec[VZ]);
- mV[VW] = static_cast<F32>(vec[VW]);
- return (*this);
-}
-
-// deprecated
-inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
-
-// no change to alpha!
-// mV[VW] = 1.f;
-
- return (*this);
-}
-
-// deprecated
-inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z, F32 a)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
- mV[VW] = a;
- return (*this);
-}
-
-// deprecated
-inline const LLColor4& LLColor4::setVec(const LLColor4 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = vec.mV[VW];
- return (*this);
-}
-
-
-// deprecated
-inline const LLColor4& LLColor4::setVec(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
- mV[VW] = vec[VW];
- return (*this);
-}
-
-inline const LLColor4& LLColor4::setAlpha(F32 a)
-{
- mV[VW] = a;
- return (*this);
-}
-
-// LLColor4 Magnitude and Normalization Functions
-
-inline F32 LLColor4::length(void) const
-{
- return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
-}
-
-inline F32 LLColor4::lengthSquared(void) const
-{
- return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
-}
-
-inline F32 LLColor4::normalize(void)
-{
- F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
- F32 oomag;
-
- if (mag)
- {
- oomag = 1.f/mag;
- mV[VX] *= oomag;
- mV[VY] *= oomag;
- mV[VZ] *= oomag;
- }
- return (mag);
-}
-
-// deprecated
-inline F32 LLColor4::magVec(void) const
-{
- return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
-}
-
-// deprecated
-inline F32 LLColor4::magVecSquared(void) const
-{
- return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
-}
-
-// deprecated
-inline F32 LLColor4::normVec(void)
-{
- F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
- F32 oomag;
-
- if (mag)
- {
- oomag = 1.f/mag;
- mV[VX] *= oomag;
- mV[VY] *= oomag;
- mV[VZ] *= oomag;
- }
- return (mag);
-}
-
-// LLColor4 Operators
-
-
-inline LLColor4 operator+(const LLColor4 &a, const LLColor4 &b)
-{
- return LLColor4(
- a.mV[VX] + b.mV[VX],
- a.mV[VY] + b.mV[VY],
- a.mV[VZ] + b.mV[VZ],
- a.mV[VW] + b.mV[VW]);
-}
-
-inline LLColor4 operator-(const LLColor4 &a, const LLColor4 &b)
-{
- return LLColor4(
- a.mV[VX] - b.mV[VX],
- a.mV[VY] - b.mV[VY],
- a.mV[VZ] - b.mV[VZ],
- a.mV[VW] - b.mV[VW]);
-}
-
-inline LLColor4 operator*(const LLColor4 &a, const LLColor4 &b)
-{
- return LLColor4(
- a.mV[VX] * b.mV[VX],
- a.mV[VY] * b.mV[VY],
- a.mV[VZ] * b.mV[VZ],
- a.mV[VW] * b.mV[VW]);
-}
-
-inline LLColor4 operator*(const LLColor4 &a, F32 k)
-{
- // only affects rgb (not a!)
- return LLColor4(
- a.mV[VX] * k,
- a.mV[VY] * k,
- a.mV[VZ] * k,
- a.mV[VW]);
-}
-
-inline LLColor4 operator/(const LLColor4 &a, F32 k)
-{
- return LLColor4(
- a.mV[VX] / k,
- a.mV[VY] / k,
- a.mV[VZ] / k,
- a.mV[VW]);
-}
-
-inline LLColor4 operator*(F32 k, const LLColor4 &a)
-{
- // only affects rgb (not a!)
- return LLColor4(
- a.mV[VX] * k,
- a.mV[VY] * k,
- a.mV[VZ] * k,
- a.mV[VW]);
-}
-
-inline LLColor4 operator%(F32 k, const LLColor4 &a)
-{
- // only affects alpha (not rgb!)
- return LLColor4(
- a.mV[VX],
- a.mV[VY],
- a.mV[VZ],
- a.mV[VW] * k);
-}
-
-inline LLColor4 operator%(const LLColor4 &a, F32 k)
-{
- // only affects alpha (not rgb!)
- return LLColor4(
- a.mV[VX],
- a.mV[VY],
- a.mV[VZ],
- a.mV[VW] * k);
-}
-
-inline bool operator==(const LLColor4 &a, const LLColor4 &b)
-{
- return ( (a.mV[VX] == b.mV[VX])
- &&(a.mV[VY] == b.mV[VY])
- &&(a.mV[VZ] == b.mV[VZ])
- &&(a.mV[VW] == b.mV[VW]));
-}
-
-inline bool operator!=(const LLColor4 &a, const LLColor4 &b)
-{
- return ( (a.mV[VX] != b.mV[VX])
- ||(a.mV[VY] != b.mV[VY])
- ||(a.mV[VZ] != b.mV[VZ])
- ||(a.mV[VW] != b.mV[VW]));
-}
-
-inline const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b)
-{
- a.mV[VX] += b.mV[VX];
- a.mV[VY] += b.mV[VY];
- a.mV[VZ] += b.mV[VZ];
- a.mV[VW] += b.mV[VW];
- return a;
-}
-
-inline const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b)
-{
- a.mV[VX] -= b.mV[VX];
- a.mV[VY] -= b.mV[VY];
- a.mV[VZ] -= b.mV[VZ];
- a.mV[VW] -= b.mV[VW];
- return a;
-}
-
-inline const LLColor4& operator*=(LLColor4 &a, F32 k)
-{
- // only affects rgb (not a!)
- a.mV[VX] *= k;
- a.mV[VY] *= k;
- a.mV[VZ] *= k;
- return a;
-}
-
-inline const LLColor4& operator *=(LLColor4 &a, const LLColor4 &b)
-{
- a.mV[VX] *= b.mV[VX];
- a.mV[VY] *= b.mV[VY];
- a.mV[VZ] *= b.mV[VZ];
-// a.mV[VW] *= b.mV[VW];
- return a;
-}
-
-inline const LLColor4& operator%=(LLColor4 &a, F32 k)
-{
- // only affects alpha (not rgb!)
- a.mV[VW] *= k;
- return a;
-}
-
-
-// Non-member functions
-
-inline F32 distVec(const LLColor4 &a, const LLColor4 &b)
-{
- LLColor4 vec = a - b;
- return (vec.length());
-}
-
-inline F32 distVec_squared(const LLColor4 &a, const LLColor4 &b)
-{
- LLColor4 vec = a - b;
- return (vec.lengthSquared());
-}
-
-inline LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u)
-{
- return LLColor4(
- a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
- a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u,
- a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u,
- a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u);
-}
-
-inline bool LLColor4::operator<(const LLColor4& rhs) const
-{
- if (mV[0] != rhs.mV[0])
- {
- return mV[0] < rhs.mV[0];
- }
- if (mV[1] != rhs.mV[1])
- {
- return mV[1] < rhs.mV[1];
- }
- if (mV[2] != rhs.mV[2])
- {
- return mV[2] < rhs.mV[2];
- }
-
- return mV[3] < rhs.mV[3];
-}
-
-void LLColor4::clamp()
-{
- // Clamp the color...
- if (mV[0] < 0.f)
- {
- mV[0] = 0.f;
- }
- else if (mV[0] > 1.f)
- {
- mV[0] = 1.f;
- }
- if (mV[1] < 0.f)
- {
- mV[1] = 0.f;
- }
- else if (mV[1] > 1.f)
- {
- mV[1] = 1.f;
- }
- if (mV[2] < 0.f)
- {
- mV[2] = 0.f;
- }
- else if (mV[2] > 1.f)
- {
- mV[2] = 1.f;
- }
- if (mV[3] < 0.f)
- {
- mV[3] = 0.f;
- }
- else if (mV[3] > 1.f)
- {
- mV[3] = 1.f;
- }
-}
-
-// Return the given linear space color value in gamma corrected (sRGB) space
-inline const LLColor4 srgbColor4(const LLColor4 &a) {
- LLColor4 srgbColor;
-
- srgbColor.mV[0] = linearTosRGB(a.mV[0]);
- srgbColor.mV[1] = linearTosRGB(a.mV[1]);
- srgbColor.mV[2] = linearTosRGB(a.mV[2]);
- srgbColor.mV[3] = a.mV[3];
-
- return srgbColor;
-}
-
-// Return the given gamma corrected (sRGB) color in linear space
-inline const LLColor4 linearColor4(const LLColor4 &a)
-{
- LLColor4 linearColor;
- linearColor.mV[0] = sRGBtoLinear(a.mV[0]);
- linearColor.mV[1] = sRGBtoLinear(a.mV[1]);
- linearColor.mV[2] = sRGBtoLinear(a.mV[2]);
- linearColor.mV[3] = a.mV[3];
-
- return linearColor;
-}
-
-template<typename T>
-const LLColor4& LLColor4::set(const std::vector<T>& v)
-{
- for (S32 i = 0; i < llmin((S32)v.size(), 4); ++i)
- {
- mV[i] = v[i];
- }
-
- return *this;
-}
-
-template<typename T>
-void LLColor4::write(std::vector<T>& v) const
-{
- for (int i = 0; i < llmin((S32)v.size(), 4); ++i)
- {
- v[i] = mV[i];
- }
-}
-
-#endif
-
+/** + * @file v4color.h + * @brief LLColor4 class header file. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_V4COLOR_H +#define LL_V4COLOR_H + +#include "llerror.h" +//#include "vmath.h" +#include "llmath.h" +#include "llsd.h" + +class LLColor3; +class LLColor4U; +class LLVector4; + +// LLColor4 = |x y z w| + +static const U32 LENGTHOFCOLOR4 = 4; + +static const U32 MAX_LENGTH_OF_COLOR_NAME = 15; //Give plenty of room for additional colors... + +class LLColor4 +{ + public: + F32 mV[LENGTHOFCOLOR4]; + LLColor4(); // Initializes LLColor4 to (0, 0, 0, 1) + LLColor4(F32 r, F32 g, F32 b); // Initializes LLColor4 to (r, g, b, 1) + LLColor4(F32 r, F32 g, F32 b, F32 a); // Initializes LLColor4 to (r. g, b, a) + LLColor4(const LLColor3 &vec, F32 a = 1.f); // Initializes LLColor4 to (vec, a) + explicit LLColor4(const LLSD& sd); + explicit LLColor4(const F32 *vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], 1) + explicit LLColor4(U32 clr); // Initializes LLColor4 to (r=clr>>24, etc)) + explicit LLColor4(const LLColor4U& color4u); // "explicit" to avoid automatic conversion + explicit LLColor4(const LLVector4& vector4); // "explicit" to avoid automatic conversion + + LLSD getValue() const + { + LLSD ret; + ret[0] = mV[0]; + ret[1] = mV[1]; + ret[2] = mV[2]; + ret[3] = mV[3]; + return ret; + } + + void setValue(const LLSD& sd); + + void setHSL(F32 hue, F32 saturation, F32 luminance); + void calcHSL(F32* hue, F32* saturation, F32* luminance) const; + + const LLColor4& setToBlack(); // zero LLColor4 to (0, 0, 0, 1) + const LLColor4& setToWhite(); // zero LLColor4 to (0, 0, 0, 1) + + const LLColor4& setVec(F32 r, F32 g, F32 b, F32 a); // deprecated -- use set() + const LLColor4& setVec(F32 r, F32 g, F32 b); // deprecated -- use set() + const LLColor4& setVec(const LLColor4 &vec); // deprecated -- use set() + const LLColor4& setVec(const LLColor3 &vec); // deprecated -- use set() + const LLColor4& setVec(const LLColor3 &vec, F32 a); // deprecated -- use set() + const LLColor4& setVec(const F32 *vec); // deprecated -- use set() + const LLColor4& setVec(const LLColor4U& color4u); // deprecated -- use set() + + const LLColor4& set(F32 r, F32 g, F32 b, F32 a); // Sets LLColor4 to (r, g, b, a) + const LLColor4& set(F32 r, F32 g, F32 b); // Sets LLColor4 to (r, g, b) (no change in a) + const LLColor4& set(const LLColor4 &vec); // Sets LLColor4 to vec + const LLColor4& set(const LLColor3 &vec); // Sets LLColor4 to LLColor3 vec (no change in alpha) + const LLColor4& set(const LLColor3 &vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified + const LLColor4& set(const F32 *vec); // Sets LLColor4 to vec + const LLColor4& set(const F64 *vec); // Sets LLColor4 to (double)vec + const LLColor4& set(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled. + + // set from a vector of unknown type and size + // may leave some data unmodified + template<typename T> + const LLColor4& set(const std::vector<T>& v); + + // write to a vector of unknown type and size + // maye leave some data unmodified + template<typename T> + void write(std::vector<T>& v) const; + + const LLColor4& setAlpha(F32 a); + + F32 magVec() const; // deprecated -- use length() + F32 magVecSquared() const; // deprecated -- use lengthSquared() + F32 normVec(); // deprecated -- use normalize() + + F32 length() const; // Returns magnitude of LLColor4 + F32 lengthSquared() const; // Returns magnitude squared of LLColor4 + F32 normalize(); // deprecated -- use normalize() + + bool isOpaque() { return mV[VALPHA] == 1.f; } + + F32 operator[](int idx) const { return mV[idx]; } + F32 &operator[](int idx) { return mV[idx]; } + + const LLColor4& operator=(const LLColor3 &a); // Assigns vec3 to vec4 and returns vec4 + + bool operator<(const LLColor4& rhs) const; + friend std::ostream& operator<<(std::ostream& s, const LLColor4 &a); // Print a + friend LLColor4 operator+(const LLColor4 &a, const LLColor4 &b); // Return vector a + b + friend LLColor4 operator-(const LLColor4 &a, const LLColor4 &b); // Return vector a minus b + friend LLColor4 operator*(const LLColor4 &a, const LLColor4 &b); // Return component wise a * b + friend LLColor4 operator*(const LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change) + friend LLColor4 operator/(const LLColor4 &a, F32 k); // Return rgb divided by scalar k (no alpha change) + friend LLColor4 operator*(F32 k, const LLColor4 &a); // Return rgb times scaler k (no alpha change) + friend LLColor4 operator%(const LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change) + friend LLColor4 operator%(F32 k, const LLColor4 &a); // Return alpha times scaler k (no rgb change) + + friend bool operator==(const LLColor4 &a, const LLColor4 &b); // Return a == b + friend bool operator!=(const LLColor4 &a, const LLColor4 &b); // Return a != b + + friend bool operator==(const LLColor4 &a, const LLColor3 &b); // Return a == b + friend bool operator!=(const LLColor4 &a, const LLColor3 &b); // Return a != b + + friend const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b); // Return vector a + b + friend const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b); // Return vector a minus b + friend const LLColor4& operator*=(LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change) + friend const LLColor4& operator%=(LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change) + + friend const LLColor4& operator*=(LLColor4 &a, const LLColor4 &b); // Doesn't multiply alpha! (for lighting) + + // conversion + operator LLColor4U() const; + + // Basic color values. + static LLColor4 red; + static LLColor4 green; + static LLColor4 blue; + static LLColor4 black; + static LLColor4 white; + static LLColor4 yellow; + static LLColor4 magenta; + static LLColor4 cyan; + static LLColor4 smoke; + static LLColor4 grey; + static LLColor4 orange; + static LLColor4 purple; + static LLColor4 pink; + static LLColor4 transparent; + + // Extra color values. + static LLColor4 grey1; + static LLColor4 grey2; + static LLColor4 grey3; + static LLColor4 grey4; + + static LLColor4 red1; + static LLColor4 red2; + static LLColor4 red3; + static LLColor4 red4; + static LLColor4 red5; + + static LLColor4 green1; + static LLColor4 green2; + static LLColor4 green3; + static LLColor4 green4; + static LLColor4 green5; + static LLColor4 green6; + + static LLColor4 blue1; + static LLColor4 blue2; + static LLColor4 blue3; + static LLColor4 blue4; + static LLColor4 blue5; + static LLColor4 blue6; + + static LLColor4 yellow1; + static LLColor4 yellow2; + static LLColor4 yellow3; + static LLColor4 yellow4; + static LLColor4 yellow5; + static LLColor4 yellow6; + static LLColor4 yellow7; + static LLColor4 yellow8; + static LLColor4 yellow9; + + static LLColor4 orange1; + static LLColor4 orange2; + static LLColor4 orange3; + static LLColor4 orange4; + static LLColor4 orange5; + static LLColor4 orange6; + + static LLColor4 magenta1; + static LLColor4 magenta2; + static LLColor4 magenta3; + static LLColor4 magenta4; + + static LLColor4 purple1; + static LLColor4 purple2; + static LLColor4 purple3; + static LLColor4 purple4; + static LLColor4 purple5; + static LLColor4 purple6; + + static LLColor4 pink1; + static LLColor4 pink2; + + static LLColor4 cyan1; + static LLColor4 cyan2; + static LLColor4 cyan3; + static LLColor4 cyan4; + static LLColor4 cyan5; + static LLColor4 cyan6; + + static bool parseColor(const std::string& buf, LLColor4* color); + static bool parseColor4(const std::string& buf, LLColor4* color); + + inline void clamp(); +}; + + +// Non-member functions +F32 distVec(const LLColor4 &a, const LLColor4 &b); // Returns distance between a and b +F32 distVec_squared(const LLColor4 &a, const LLColor4 &b); // Returns distance squared between a and b +LLColor3 vec4to3(const LLColor4 &vec); +LLColor4 vec3to4(const LLColor3 &vec); +LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u); + +inline LLColor4::LLColor4(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; + mV[VZ] = 0.f; + mV[VW] = 1.f; +} + +inline LLColor4::LLColor4(const LLSD& sd) +{ + this->setValue(sd); +} + +inline LLColor4::LLColor4(F32 r, F32 g, F32 b) +{ + mV[VX] = r; + mV[VY] = g; + mV[VZ] = b; + mV[VW] = 1.f; +} + +inline LLColor4::LLColor4(F32 r, F32 g, F32 b, F32 a) +{ + mV[VX] = r; + mV[VY] = g; + mV[VZ] = b; + mV[VW] = a; +} + +inline LLColor4::LLColor4(U32 clr) +{ + mV[VX] = (clr&0xff) * (1.0f/255.0f); + mV[VY] = ((clr>>8)&0xff) * (1.0f/255.0f); + mV[VZ] = ((clr>>16)&0xff) * (1.0f/255.0f); + mV[VW] = (clr>>24) * (1.0f/255.0f); +} + + +inline LLColor4::LLColor4(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; + mV[VW] = vec[VW]; +} + +inline const LLColor4& LLColor4::setToBlack(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; + mV[VZ] = 0.f; + mV[VW] = 1.f; + return (*this); +} + +inline const LLColor4& LLColor4::setToWhite(void) +{ + mV[VX] = 1.f; + mV[VY] = 1.f; + mV[VZ] = 1.f; + mV[VW] = 1.f; + return (*this); +} + +inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + +// no change to alpha! +// mV[VW] = 1.f; + + return (*this); +} + +inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z, F32 a) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + mV[VW] = a; + return (*this); +} + +inline const LLColor4& LLColor4::set(const LLColor4 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = vec.mV[VW]; + return (*this); +} + + +inline const LLColor4& LLColor4::set(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; + mV[VW] = vec[VW]; + return (*this); +} + +inline const LLColor4& LLColor4::set(const F64 *vec) +{ + mV[VX] = static_cast<F32>(vec[VX]); + mV[VY] = static_cast<F32>(vec[VY]); + mV[VZ] = static_cast<F32>(vec[VZ]); + mV[VW] = static_cast<F32>(vec[VW]); + return (*this); +} + +// deprecated +inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + +// no change to alpha! +// mV[VW] = 1.f; + + return (*this); +} + +// deprecated +inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z, F32 a) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + mV[VW] = a; + return (*this); +} + +// deprecated +inline const LLColor4& LLColor4::setVec(const LLColor4 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = vec.mV[VW]; + return (*this); +} + + +// deprecated +inline const LLColor4& LLColor4::setVec(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; + mV[VW] = vec[VW]; + return (*this); +} + +inline const LLColor4& LLColor4::setAlpha(F32 a) +{ + mV[VW] = a; + return (*this); +} + +// LLColor4 Magnitude and Normalization Functions + +inline F32 LLColor4::length(void) const +{ + return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); +} + +inline F32 LLColor4::lengthSquared(void) const +{ + return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]; +} + +inline F32 LLColor4::normalize(void) +{ + F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); + F32 oomag; + + if (mag) + { + oomag = 1.f/mag; + mV[VX] *= oomag; + mV[VY] *= oomag; + mV[VZ] *= oomag; + } + return (mag); +} + +// deprecated +inline F32 LLColor4::magVec(void) const +{ + return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); +} + +// deprecated +inline F32 LLColor4::magVecSquared(void) const +{ + return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]; +} + +// deprecated +inline F32 LLColor4::normVec(void) +{ + F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); + F32 oomag; + + if (mag) + { + oomag = 1.f/mag; + mV[VX] *= oomag; + mV[VY] *= oomag; + mV[VZ] *= oomag; + } + return (mag); +} + +// LLColor4 Operators + + +inline LLColor4 operator+(const LLColor4 &a, const LLColor4 &b) +{ + return LLColor4( + a.mV[VX] + b.mV[VX], + a.mV[VY] + b.mV[VY], + a.mV[VZ] + b.mV[VZ], + a.mV[VW] + b.mV[VW]); +} + +inline LLColor4 operator-(const LLColor4 &a, const LLColor4 &b) +{ + return LLColor4( + a.mV[VX] - b.mV[VX], + a.mV[VY] - b.mV[VY], + a.mV[VZ] - b.mV[VZ], + a.mV[VW] - b.mV[VW]); +} + +inline LLColor4 operator*(const LLColor4 &a, const LLColor4 &b) +{ + return LLColor4( + a.mV[VX] * b.mV[VX], + a.mV[VY] * b.mV[VY], + a.mV[VZ] * b.mV[VZ], + a.mV[VW] * b.mV[VW]); +} + +inline LLColor4 operator*(const LLColor4 &a, F32 k) +{ + // only affects rgb (not a!) + return LLColor4( + a.mV[VX] * k, + a.mV[VY] * k, + a.mV[VZ] * k, + a.mV[VW]); +} + +inline LLColor4 operator/(const LLColor4 &a, F32 k) +{ + return LLColor4( + a.mV[VX] / k, + a.mV[VY] / k, + a.mV[VZ] / k, + a.mV[VW]); +} + +inline LLColor4 operator*(F32 k, const LLColor4 &a) +{ + // only affects rgb (not a!) + return LLColor4( + a.mV[VX] * k, + a.mV[VY] * k, + a.mV[VZ] * k, + a.mV[VW]); +} + +inline LLColor4 operator%(F32 k, const LLColor4 &a) +{ + // only affects alpha (not rgb!) + return LLColor4( + a.mV[VX], + a.mV[VY], + a.mV[VZ], + a.mV[VW] * k); +} + +inline LLColor4 operator%(const LLColor4 &a, F32 k) +{ + // only affects alpha (not rgb!) + return LLColor4( + a.mV[VX], + a.mV[VY], + a.mV[VZ], + a.mV[VW] * k); +} + +inline bool operator==(const LLColor4 &a, const LLColor4 &b) +{ + return ( (a.mV[VX] == b.mV[VX]) + &&(a.mV[VY] == b.mV[VY]) + &&(a.mV[VZ] == b.mV[VZ]) + &&(a.mV[VW] == b.mV[VW])); +} + +inline bool operator!=(const LLColor4 &a, const LLColor4 &b) +{ + return ( (a.mV[VX] != b.mV[VX]) + ||(a.mV[VY] != b.mV[VY]) + ||(a.mV[VZ] != b.mV[VZ]) + ||(a.mV[VW] != b.mV[VW])); +} + +inline const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b) +{ + a.mV[VX] += b.mV[VX]; + a.mV[VY] += b.mV[VY]; + a.mV[VZ] += b.mV[VZ]; + a.mV[VW] += b.mV[VW]; + return a; +} + +inline const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b) +{ + a.mV[VX] -= b.mV[VX]; + a.mV[VY] -= b.mV[VY]; + a.mV[VZ] -= b.mV[VZ]; + a.mV[VW] -= b.mV[VW]; + return a; +} + +inline const LLColor4& operator*=(LLColor4 &a, F32 k) +{ + // only affects rgb (not a!) + a.mV[VX] *= k; + a.mV[VY] *= k; + a.mV[VZ] *= k; + return a; +} + +inline const LLColor4& operator *=(LLColor4 &a, const LLColor4 &b) +{ + a.mV[VX] *= b.mV[VX]; + a.mV[VY] *= b.mV[VY]; + a.mV[VZ] *= b.mV[VZ]; +// a.mV[VW] *= b.mV[VW]; + return a; +} + +inline const LLColor4& operator%=(LLColor4 &a, F32 k) +{ + // only affects alpha (not rgb!) + a.mV[VW] *= k; + return a; +} + + +// Non-member functions + +inline F32 distVec(const LLColor4 &a, const LLColor4 &b) +{ + LLColor4 vec = a - b; + return (vec.length()); +} + +inline F32 distVec_squared(const LLColor4 &a, const LLColor4 &b) +{ + LLColor4 vec = a - b; + return (vec.lengthSquared()); +} + +inline LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u) +{ + return LLColor4( + a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u, + a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u, + a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u, + a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u); +} + +inline bool LLColor4::operator<(const LLColor4& rhs) const +{ + if (mV[0] != rhs.mV[0]) + { + return mV[0] < rhs.mV[0]; + } + if (mV[1] != rhs.mV[1]) + { + return mV[1] < rhs.mV[1]; + } + if (mV[2] != rhs.mV[2]) + { + return mV[2] < rhs.mV[2]; + } + + return mV[3] < rhs.mV[3]; +} + +void LLColor4::clamp() +{ + // Clamp the color... + if (mV[0] < 0.f) + { + mV[0] = 0.f; + } + else if (mV[0] > 1.f) + { + mV[0] = 1.f; + } + if (mV[1] < 0.f) + { + mV[1] = 0.f; + } + else if (mV[1] > 1.f) + { + mV[1] = 1.f; + } + if (mV[2] < 0.f) + { + mV[2] = 0.f; + } + else if (mV[2] > 1.f) + { + mV[2] = 1.f; + } + if (mV[3] < 0.f) + { + mV[3] = 0.f; + } + else if (mV[3] > 1.f) + { + mV[3] = 1.f; + } +} + +// Return the given linear space color value in gamma corrected (sRGB) space +inline const LLColor4 srgbColor4(const LLColor4 &a) { + LLColor4 srgbColor; + + srgbColor.mV[0] = linearTosRGB(a.mV[0]); + srgbColor.mV[1] = linearTosRGB(a.mV[1]); + srgbColor.mV[2] = linearTosRGB(a.mV[2]); + srgbColor.mV[3] = a.mV[3]; + + return srgbColor; +} + +// Return the given gamma corrected (sRGB) color in linear space +inline const LLColor4 linearColor4(const LLColor4 &a) +{ + LLColor4 linearColor; + linearColor.mV[0] = sRGBtoLinear(a.mV[0]); + linearColor.mV[1] = sRGBtoLinear(a.mV[1]); + linearColor.mV[2] = sRGBtoLinear(a.mV[2]); + linearColor.mV[3] = a.mV[3]; + + return linearColor; +} + +template<typename T> +const LLColor4& LLColor4::set(const std::vector<T>& v) +{ + for (S32 i = 0; i < llmin((S32)v.size(), 4); ++i) + { + mV[i] = v[i]; + } + + return *this; +} + +template<typename T> +void LLColor4::write(std::vector<T>& v) const +{ + for (int i = 0; i < llmin((S32)v.size(), 4); ++i) + { + v[i] = mV[i]; + } +} + +#endif + diff --git a/indra/llmath/v4coloru.cpp b/indra/llmath/v4coloru.cpp index 500d8ed8ab..45d310856a 100644 --- a/indra/llmath/v4coloru.cpp +++ b/indra/llmath/v4coloru.cpp @@ -1,120 +1,120 @@ -/**
- * @file v4coloru.cpp
- * @brief LLColor4U class implementation.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-//#include "v3coloru.h"
-#include "v4coloru.h"
-#include "v4color.h"
-//#include "vmath.h"
-#include "llmath.h"
-
-// LLColor4U
-LLColor4U LLColor4U::white(255, 255, 255, 255);
-LLColor4U LLColor4U::black( 0, 0, 0, 255);
-LLColor4U LLColor4U::red (255, 0, 0, 255);
-LLColor4U LLColor4U::green( 0, 255, 0, 255);
-LLColor4U LLColor4U::blue ( 0, 0, 255, 255);
-
-// conversion
-/* inlined to fix gcc compile link error
-LLColor4U::operator LLColor4()
-{
- return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f));
-}
-*/
-
-// Constructors
-
-
-/*
-LLColor4U::LLColor4U(const LLColor3 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = 255;
-}
-*/
-
-
-// Clear and Assignment Functions
-
-
-
-// LLColor4U Operators
-
-/*
-LLColor4U LLColor4U::operator=(const LLColor3 &a)
-{
- mV[VX] = a.mV[VX];
- mV[VY] = a.mV[VY];
- mV[VZ] = a.mV[VZ];
-
-// converting from an rgb sets a=1 (opaque)
- mV[VW] = 255;
- return (*this);
-}
-*/
-
-
-std::ostream& operator<<(std::ostream& s, const LLColor4U &a)
-{
- s << "{ " << (S32)a.mV[VX] << ", " << (S32)a.mV[VY] << ", " << (S32)a.mV[VZ] << ", " << (S32)a.mV[VW] << " }";
- return s;
-}
-
-// static
-bool LLColor4U::parseColor4U(const std::string& buf, LLColor4U* value)
-{
- if( buf.empty() || value == nullptr)
- {
- return false;
- }
-
- U32 v[4];
- S32 count = sscanf( buf.c_str(), "%u, %u, %u, %u", v + 0, v + 1, v + 2, v + 3 );
- if (1 == count )
- {
- // try this format
- count = sscanf( buf.c_str(), "%u %u %u %u", v + 0, v + 1, v + 2, v + 3 );
- }
- if( 4 != count )
- {
- return false;
- }
-
- for( S32 i = 0; i < 4; i++ )
- {
- if( v[i] > U8_MAX )
- {
- return false;
- }
- }
-
- value->set( U8(v[0]), U8(v[1]), U8(v[2]), U8(v[3]) );
- return true;
-}
+/** + * @file v4coloru.cpp + * @brief LLColor4U class implementation. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +//#include "v3coloru.h" +#include "v4coloru.h" +#include "v4color.h" +//#include "vmath.h" +#include "llmath.h" + +// LLColor4U +LLColor4U LLColor4U::white(255, 255, 255, 255); +LLColor4U LLColor4U::black( 0, 0, 0, 255); +LLColor4U LLColor4U::red (255, 0, 0, 255); +LLColor4U LLColor4U::green( 0, 255, 0, 255); +LLColor4U LLColor4U::blue ( 0, 0, 255, 255); + +// conversion +/* inlined to fix gcc compile link error +LLColor4U::operator LLColor4() +{ + return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f)); +} +*/ + +// Constructors + + +/* +LLColor4U::LLColor4U(const LLColor3 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = 255; +} +*/ + + +// Clear and Assignment Functions + + + +// LLColor4U Operators + +/* +LLColor4U LLColor4U::operator=(const LLColor3 &a) +{ + mV[VX] = a.mV[VX]; + mV[VY] = a.mV[VY]; + mV[VZ] = a.mV[VZ]; + +// converting from an rgb sets a=1 (opaque) + mV[VW] = 255; + return (*this); +} +*/ + + +std::ostream& operator<<(std::ostream& s, const LLColor4U &a) +{ + s << "{ " << (S32)a.mV[VX] << ", " << (S32)a.mV[VY] << ", " << (S32)a.mV[VZ] << ", " << (S32)a.mV[VW] << " }"; + return s; +} + +// static +bool LLColor4U::parseColor4U(const std::string& buf, LLColor4U* value) +{ + if( buf.empty() || value == nullptr) + { + return false; + } + + U32 v[4]; + S32 count = sscanf( buf.c_str(), "%u, %u, %u, %u", v + 0, v + 1, v + 2, v + 3 ); + if (1 == count ) + { + // try this format + count = sscanf( buf.c_str(), "%u %u %u %u", v + 0, v + 1, v + 2, v + 3 ); + } + if( 4 != count ) + { + return false; + } + + for( S32 i = 0; i < 4; i++ ) + { + if( v[i] > U8_MAX ) + { + return false; + } + } + + value->set( U8(v[0]), U8(v[1]), U8(v[2]), U8(v[3]) ); + return true; +} diff --git a/indra/llmath/v4coloru.h b/indra/llmath/v4coloru.h index ce15540866..6d921d12fa 100644 --- a/indra/llmath/v4coloru.h +++ b/indra/llmath/v4coloru.h @@ -1,585 +1,585 @@ -/**
- * @file v4coloru.h
- * @brief The LLColor4U class.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_V4COLORU_H
-#define LL_V4COLORU_H
-
-#include "llerror.h"
-//#include "vmath.h"
-#include "llmath.h"
-//#include "v4color.h"
-
-#include "v3color.h"
-#include "v4color.h"
-
-//class LLColor3U;
-class LLColor4;
-
-// LLColor4U = | red green blue alpha |
-
-static const U32 LENGTHOFCOLOR4U = 4;
-
-
-class LLColor4U
-{
-public:
-
- U8 mV[LENGTHOFCOLOR4U];
-
- LLColor4U(); // Initializes LLColor4U to (0, 0, 0, 1)
- LLColor4U(U8 r, U8 g, U8 b); // Initializes LLColor4U to (r, g, b, 1)
- LLColor4U(U8 r, U8 g, U8 b, U8 a); // Initializes LLColor4U to (r. g, b, a)
- LLColor4U(const U8 *vec); // Initializes LLColor4U to (vec[0]. vec[1], vec[2], 1)
- explicit LLColor4U(const LLSD& sd)
- {
- setValue(sd);
- }
-
- void setValue(const LLSD& sd)
- {
- mV[0] = sd[0].asInteger();
- mV[1] = sd[1].asInteger();
- mV[2] = sd[2].asInteger();
- mV[3] = sd[3].asInteger();
- }
-
- LLSD getValue() const
- {
- LLSD ret;
- ret[0] = mV[0];
- ret[1] = mV[1];
- ret[2] = mV[2];
- ret[3] = mV[3];
- return ret;
- }
-
- const LLColor4U& setToBlack(); // zero LLColor4U to (0, 0, 0, 1)
- const LLColor4U& setToWhite(); // zero LLColor4U to (0, 0, 0, 1)
-
- const LLColor4U& set(U8 r, U8 g, U8 b, U8 a);// Sets LLColor4U to (r, g, b, a)
- const LLColor4U& set(U8 r, U8 g, U8 b); // Sets LLColor4U to (r, g, b) (no change in a)
- const LLColor4U& set(const LLColor4U &vec); // Sets LLColor4U to vec
- const LLColor4U& set(const U8 *vec); // Sets LLColor4U to vec
-
- const LLColor4U& setVec(U8 r, U8 g, U8 b, U8 a); // deprecated -- use set()
- const LLColor4U& setVec(U8 r, U8 g, U8 b); // deprecated -- use set()
- const LLColor4U& setVec(const LLColor4U &vec); // deprecated -- use set()
- const LLColor4U& setVec(const U8 *vec); // deprecated -- use set()
-
- const LLColor4U& setAlpha(U8 a);
-
- F32 magVec() const; // deprecated -- use length()
- F32 magVecSquared() const; // deprecated -- use lengthSquared()
-
- F32 length() const; // Returns magnitude squared of LLColor4U
- F32 lengthSquared() const; // Returns magnitude squared of LLColor4U
-
- friend std::ostream& operator<<(std::ostream& s, const LLColor4U &a); // Print a
- friend LLColor4U operator+(const LLColor4U &a, const LLColor4U &b); // Return vector a + b
- friend LLColor4U operator-(const LLColor4U &a, const LLColor4U &b); // Return vector a minus b
- friend LLColor4U operator*(const LLColor4U &a, const LLColor4U &b); // Return a * b
- friend bool operator==(const LLColor4U &a, const LLColor4U &b); // Return a == b
- friend bool operator!=(const LLColor4U &a, const LLColor4U &b); // Return a != b
-
- friend const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b); // Return vector a + b
- friend const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b); // Return vector a minus b
- friend const LLColor4U& operator*=(LLColor4U &a, U8 k); // Return rgb times scaler k (no alpha change)
- friend const LLColor4U& operator%=(LLColor4U &a, U8 k); // Return alpha times scaler k (no rgb change)
-
- LLColor4U addClampMax(const LLColor4U &color); // Add and clamp the max
-
- LLColor4U multAll(const F32 k); // Multiply ALL channels by scalar k
-
- inline void setVecScaleClamp(const LLColor3 &color);
- inline void setVecScaleClamp(const LLColor4 &color);
-
- static bool parseColor4U(const std::string& buf, LLColor4U* value);
-
- // conversion
- operator LLColor4() const
- {
- return LLColor4(*this);
- }
-
- U32 asRGBA() const;
- void fromRGBA( U32 aVal );
-
- static LLColor4U white;
- static LLColor4U black;
- static LLColor4U red;
- static LLColor4U green;
- static LLColor4U blue;
-};
-
-
-// Non-member functions
-F32 distVec(const LLColor4U &a, const LLColor4U &b); // Returns distance between a and b
-F32 distVec_squared(const LLColor4U &a, const LLColor4U &b); // Returns distance squared between a and b
-
-
-inline LLColor4U::LLColor4U()
-{
- mV[VX] = 0;
- mV[VY] = 0;
- mV[VZ] = 0;
- mV[VW] = 255;
-}
-
-inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b)
-{
- mV[VX] = r;
- mV[VY] = g;
- mV[VZ] = b;
- mV[VW] = 255;
-}
-
-inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b, U8 a)
-{
- mV[VX] = r;
- mV[VY] = g;
- mV[VZ] = b;
- mV[VW] = a;
-}
-
-inline LLColor4U::LLColor4U(const U8 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
- mV[VW] = vec[VW];
-}
-
-/*
-inline LLColor4U::operator LLColor4()
-{
- return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f));
-}
-*/
-
-inline const LLColor4U& LLColor4U::setToBlack(void)
-{
- mV[VX] = 0;
- mV[VY] = 0;
- mV[VZ] = 0;
- mV[VW] = 255;
- return (*this);
-}
-
-inline const LLColor4U& LLColor4U::setToWhite(void)
-{
- mV[VX] = 255;
- mV[VY] = 255;
- mV[VZ] = 255;
- mV[VW] = 255;
- return (*this);
-}
-
-inline const LLColor4U& LLColor4U::set(const U8 x, const U8 y, const U8 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
-
-// no change to alpha!
-// mV[VW] = 255;
-
- return (*this);
-}
-
-inline const LLColor4U& LLColor4U::set(const U8 r, const U8 g, const U8 b, U8 a)
-{
- mV[0] = r;
- mV[1] = g;
- mV[2] = b;
- mV[3] = a;
- return (*this);
-}
-
-inline const LLColor4U& LLColor4U::set(const LLColor4U &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = vec.mV[VW];
- return (*this);
-}
-
-inline const LLColor4U& LLColor4U::set(const U8 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
- mV[VW] = vec[VW];
- return (*this);
-}
-
-// deprecated
-inline const LLColor4U& LLColor4U::setVec(const U8 x, const U8 y, const U8 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
-
-// no change to alpha!
-// mV[VW] = 255;
-
- return (*this);
-}
-
-// deprecated
-inline const LLColor4U& LLColor4U::setVec(const U8 r, const U8 g, const U8 b, U8 a)
-{
- mV[0] = r;
- mV[1] = g;
- mV[2] = b;
- mV[3] = a;
- return (*this);
-}
-
-// deprecated
-inline const LLColor4U& LLColor4U::setVec(const LLColor4U &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = vec.mV[VW];
- return (*this);
-}
-
-// deprecated
-inline const LLColor4U& LLColor4U::setVec(const U8 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
- mV[VW] = vec[VW];
- return (*this);
-}
-
-inline const LLColor4U& LLColor4U::setAlpha(U8 a)
-{
- mV[VW] = a;
- return (*this);
-}
-
-// LLColor4U Magnitude and Normalization Functions
-
-inline F32 LLColor4U::length(void) const
-{
- return (F32) sqrt( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] );
-}
-
-inline F32 LLColor4U::lengthSquared(void) const
-{
- return ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ];
-}
-
-// deprecated
-inline F32 LLColor4U::magVec(void) const
-{
- return (F32) sqrt( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] );
-}
-
-// deprecated
-inline F32 LLColor4U::magVecSquared(void) const
-{
- return ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ];
-}
-
-inline LLColor4U operator+(const LLColor4U &a, const LLColor4U &b)
-{
- return LLColor4U(
- a.mV[VX] + b.mV[VX],
- a.mV[VY] + b.mV[VY],
- a.mV[VZ] + b.mV[VZ],
- a.mV[VW] + b.mV[VW]);
-}
-
-inline LLColor4U operator-(const LLColor4U &a, const LLColor4U &b)
-{
- return LLColor4U(
- a.mV[VX] - b.mV[VX],
- a.mV[VY] - b.mV[VY],
- a.mV[VZ] - b.mV[VZ],
- a.mV[VW] - b.mV[VW]);
-}
-
-inline LLColor4U operator*(const LLColor4U &a, const LLColor4U &b)
-{
- return LLColor4U(
- a.mV[VX] * b.mV[VX],
- a.mV[VY] * b.mV[VY],
- a.mV[VZ] * b.mV[VZ],
- a.mV[VW] * b.mV[VW]);
-}
-
-inline LLColor4U LLColor4U::addClampMax(const LLColor4U &color)
-{
- return LLColor4U(llmin((S32)mV[VX] + color.mV[VX], 255),
- llmin((S32)mV[VY] + color.mV[VY], 255),
- llmin((S32)mV[VZ] + color.mV[VZ], 255),
- llmin((S32)mV[VW] + color.mV[VW], 255));
-}
-
-inline LLColor4U LLColor4U::multAll(const F32 k)
-{
- // Round to nearest
- return LLColor4U(
- (U8)ll_round(mV[VX] * k),
- (U8)ll_round(mV[VY] * k),
- (U8)ll_round(mV[VZ] * k),
- (U8)ll_round(mV[VW] * k));
-}
-/*
-inline LLColor4U operator*(const LLColor4U &a, U8 k)
-{
- // only affects rgb (not a!)
- return LLColor4U(
- a.mV[VX] * k,
- a.mV[VY] * k,
- a.mV[VZ] * k,
- a.mV[VW]);
-}
-
-inline LLColor4U operator*(U8 k, const LLColor4U &a)
-{
- // only affects rgb (not a!)
- return LLColor4U(
- a.mV[VX] * k,
- a.mV[VY] * k,
- a.mV[VZ] * k,
- a.mV[VW]);
-}
-
-inline LLColor4U operator%(U8 k, const LLColor4U &a)
-{
- // only affects alpha (not rgb!)
- return LLColor4U(
- a.mV[VX],
- a.mV[VY],
- a.mV[VZ],
- a.mV[VW] * k );
-}
-
-inline LLColor4U operator%(const LLColor4U &a, U8 k)
-{
- // only affects alpha (not rgb!)
- return LLColor4U(
- a.mV[VX],
- a.mV[VY],
- a.mV[VZ],
- a.mV[VW] * k );
-}
-*/
-
-inline bool operator==(const LLColor4U &a, const LLColor4U &b)
-{
- return ( (a.mV[VX] == b.mV[VX])
- &&(a.mV[VY] == b.mV[VY])
- &&(a.mV[VZ] == b.mV[VZ])
- &&(a.mV[VW] == b.mV[VW]));
-}
-
-inline bool operator!=(const LLColor4U &a, const LLColor4U &b)
-{
- return ( (a.mV[VX] != b.mV[VX])
- ||(a.mV[VY] != b.mV[VY])
- ||(a.mV[VZ] != b.mV[VZ])
- ||(a.mV[VW] != b.mV[VW]));
-}
-
-inline const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b)
-{
- a.mV[VX] += b.mV[VX];
- a.mV[VY] += b.mV[VY];
- a.mV[VZ] += b.mV[VZ];
- a.mV[VW] += b.mV[VW];
- return a;
-}
-
-inline const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b)
-{
- a.mV[VX] -= b.mV[VX];
- a.mV[VY] -= b.mV[VY];
- a.mV[VZ] -= b.mV[VZ];
- a.mV[VW] -= b.mV[VW];
- return a;
-}
-
-inline const LLColor4U& operator*=(LLColor4U &a, U8 k)
-{
- // only affects rgb (not a!)
- a.mV[VX] *= k;
- a.mV[VY] *= k;
- a.mV[VZ] *= k;
- return a;
-}
-
-inline const LLColor4U& operator%=(LLColor4U &a, U8 k)
-{
- // only affects alpha (not rgb!)
- a.mV[VW] *= k;
- return a;
-}
-
-inline F32 distVec(const LLColor4U &a, const LLColor4U &b)
-{
- LLColor4U vec = a - b;
- return (vec.length());
-}
-
-inline F32 distVec_squared(const LLColor4U &a, const LLColor4U &b)
-{
- LLColor4U vec = a - b;
- return (vec.lengthSquared());
-}
-
-void LLColor4U::setVecScaleClamp(const LLColor4& color)
-{
- F32 color_scale_factor = 255.f;
- F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]);
- if (max_color > 1.f)
- {
- color_scale_factor /= max_color;
- }
- const S32 MAX_COLOR = 255;
- S32 r = ll_round(color.mV[0] * color_scale_factor);
- if (r > MAX_COLOR)
- {
- r = MAX_COLOR;
- }
- else if (r < 0)
- {
- r = 0;
- }
- mV[0] = r;
-
- S32 g = ll_round(color.mV[1] * color_scale_factor);
- if (g > MAX_COLOR)
- {
- g = MAX_COLOR;
- }
- else if (g < 0)
- {
- g = 0;
- }
- mV[1] = g;
-
- S32 b = ll_round(color.mV[2] * color_scale_factor);
- if (b > MAX_COLOR)
- {
- b = MAX_COLOR;
- }
- else if (b < 0)
- {
- b = 0;
- }
- mV[2] = b;
-
- // Alpha shouldn't be scaled, just clamped...
- S32 a = ll_round(color.mV[3] * MAX_COLOR);
- if (a > MAX_COLOR)
- {
- a = MAX_COLOR;
- }
- else if (a < 0)
- {
- a = 0;
- }
- mV[3] = a;
-}
-
-void LLColor4U::setVecScaleClamp(const LLColor3& color)
-{
- F32 color_scale_factor = 255.f;
- F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]);
- if (max_color > 1.f)
- {
- color_scale_factor /= max_color;
- }
-
- const S32 MAX_COLOR = 255;
- S32 r = ll_round(color.mV[0] * color_scale_factor);
- if (r > MAX_COLOR)
- {
- r = MAX_COLOR;
- }
- else
- if (r < 0)
- {
- r = 0;
- }
- mV[0] = r;
-
- S32 g = ll_round(color.mV[1] * color_scale_factor);
- if (g > MAX_COLOR)
- {
- g = MAX_COLOR;
- }
- else
- if (g < 0)
- {
- g = 0;
- }
- mV[1] = g;
-
- S32 b = ll_round(color.mV[2] * color_scale_factor);
- if (b > MAX_COLOR)
- {
- b = MAX_COLOR;
- }
- if (b < 0)
- {
- b = 0;
- }
- mV[2] = b;
-
- mV[3] = 255;
-}
-
-inline U32 LLColor4U::asRGBA() const
-{
- // Little endian: values are swapped in memory. The original code access the array like a U32, so we need to swap here
-
- return (mV[3] << 24) | (mV[2] << 16) | (mV[1] << 8) | mV[0];
-}
-
-inline void LLColor4U::fromRGBA( U32 aVal )
-{
- // Little endian: values are swapped in memory. The original code access the array like a U32, so we need to swap here
-
- mV[ 0 ] = aVal & 0xFF;
- aVal >>= 8;
- mV[ 1 ] = aVal & 0xFF;
- aVal >>= 8;
- mV[ 2 ] = aVal & 0xFF;
- aVal >>= 8;
- mV[ 3 ] = aVal & 0xFF;
-}
-
-
-#endif
-
+/** + * @file v4coloru.h + * @brief The LLColor4U class. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_V4COLORU_H +#define LL_V4COLORU_H + +#include "llerror.h" +//#include "vmath.h" +#include "llmath.h" +//#include "v4color.h" + +#include "v3color.h" +#include "v4color.h" + +//class LLColor3U; +class LLColor4; + +// LLColor4U = | red green blue alpha | + +static const U32 LENGTHOFCOLOR4U = 4; + + +class LLColor4U +{ +public: + + U8 mV[LENGTHOFCOLOR4U]; + + LLColor4U(); // Initializes LLColor4U to (0, 0, 0, 1) + LLColor4U(U8 r, U8 g, U8 b); // Initializes LLColor4U to (r, g, b, 1) + LLColor4U(U8 r, U8 g, U8 b, U8 a); // Initializes LLColor4U to (r. g, b, a) + LLColor4U(const U8 *vec); // Initializes LLColor4U to (vec[0]. vec[1], vec[2], 1) + explicit LLColor4U(const LLSD& sd) + { + setValue(sd); + } + + void setValue(const LLSD& sd) + { + mV[0] = sd[0].asInteger(); + mV[1] = sd[1].asInteger(); + mV[2] = sd[2].asInteger(); + mV[3] = sd[3].asInteger(); + } + + LLSD getValue() const + { + LLSD ret; + ret[0] = mV[0]; + ret[1] = mV[1]; + ret[2] = mV[2]; + ret[3] = mV[3]; + return ret; + } + + const LLColor4U& setToBlack(); // zero LLColor4U to (0, 0, 0, 1) + const LLColor4U& setToWhite(); // zero LLColor4U to (0, 0, 0, 1) + + const LLColor4U& set(U8 r, U8 g, U8 b, U8 a);// Sets LLColor4U to (r, g, b, a) + const LLColor4U& set(U8 r, U8 g, U8 b); // Sets LLColor4U to (r, g, b) (no change in a) + const LLColor4U& set(const LLColor4U &vec); // Sets LLColor4U to vec + const LLColor4U& set(const U8 *vec); // Sets LLColor4U to vec + + const LLColor4U& setVec(U8 r, U8 g, U8 b, U8 a); // deprecated -- use set() + const LLColor4U& setVec(U8 r, U8 g, U8 b); // deprecated -- use set() + const LLColor4U& setVec(const LLColor4U &vec); // deprecated -- use set() + const LLColor4U& setVec(const U8 *vec); // deprecated -- use set() + + const LLColor4U& setAlpha(U8 a); + + F32 magVec() const; // deprecated -- use length() + F32 magVecSquared() const; // deprecated -- use lengthSquared() + + F32 length() const; // Returns magnitude squared of LLColor4U + F32 lengthSquared() const; // Returns magnitude squared of LLColor4U + + friend std::ostream& operator<<(std::ostream& s, const LLColor4U &a); // Print a + friend LLColor4U operator+(const LLColor4U &a, const LLColor4U &b); // Return vector a + b + friend LLColor4U operator-(const LLColor4U &a, const LLColor4U &b); // Return vector a minus b + friend LLColor4U operator*(const LLColor4U &a, const LLColor4U &b); // Return a * b + friend bool operator==(const LLColor4U &a, const LLColor4U &b); // Return a == b + friend bool operator!=(const LLColor4U &a, const LLColor4U &b); // Return a != b + + friend const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b); // Return vector a + b + friend const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b); // Return vector a minus b + friend const LLColor4U& operator*=(LLColor4U &a, U8 k); // Return rgb times scaler k (no alpha change) + friend const LLColor4U& operator%=(LLColor4U &a, U8 k); // Return alpha times scaler k (no rgb change) + + LLColor4U addClampMax(const LLColor4U &color); // Add and clamp the max + + LLColor4U multAll(const F32 k); // Multiply ALL channels by scalar k + + inline void setVecScaleClamp(const LLColor3 &color); + inline void setVecScaleClamp(const LLColor4 &color); + + static bool parseColor4U(const std::string& buf, LLColor4U* value); + + // conversion + operator LLColor4() const + { + return LLColor4(*this); + } + + U32 asRGBA() const; + void fromRGBA( U32 aVal ); + + static LLColor4U white; + static LLColor4U black; + static LLColor4U red; + static LLColor4U green; + static LLColor4U blue; +}; + + +// Non-member functions +F32 distVec(const LLColor4U &a, const LLColor4U &b); // Returns distance between a and b +F32 distVec_squared(const LLColor4U &a, const LLColor4U &b); // Returns distance squared between a and b + + +inline LLColor4U::LLColor4U() +{ + mV[VX] = 0; + mV[VY] = 0; + mV[VZ] = 0; + mV[VW] = 255; +} + +inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b) +{ + mV[VX] = r; + mV[VY] = g; + mV[VZ] = b; + mV[VW] = 255; +} + +inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b, U8 a) +{ + mV[VX] = r; + mV[VY] = g; + mV[VZ] = b; + mV[VW] = a; +} + +inline LLColor4U::LLColor4U(const U8 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; + mV[VW] = vec[VW]; +} + +/* +inline LLColor4U::operator LLColor4() +{ + return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f)); +} +*/ + +inline const LLColor4U& LLColor4U::setToBlack(void) +{ + mV[VX] = 0; + mV[VY] = 0; + mV[VZ] = 0; + mV[VW] = 255; + return (*this); +} + +inline const LLColor4U& LLColor4U::setToWhite(void) +{ + mV[VX] = 255; + mV[VY] = 255; + mV[VZ] = 255; + mV[VW] = 255; + return (*this); +} + +inline const LLColor4U& LLColor4U::set(const U8 x, const U8 y, const U8 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + +// no change to alpha! +// mV[VW] = 255; + + return (*this); +} + +inline const LLColor4U& LLColor4U::set(const U8 r, const U8 g, const U8 b, U8 a) +{ + mV[0] = r; + mV[1] = g; + mV[2] = b; + mV[3] = a; + return (*this); +} + +inline const LLColor4U& LLColor4U::set(const LLColor4U &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = vec.mV[VW]; + return (*this); +} + +inline const LLColor4U& LLColor4U::set(const U8 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; + mV[VW] = vec[VW]; + return (*this); +} + +// deprecated +inline const LLColor4U& LLColor4U::setVec(const U8 x, const U8 y, const U8 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + +// no change to alpha! +// mV[VW] = 255; + + return (*this); +} + +// deprecated +inline const LLColor4U& LLColor4U::setVec(const U8 r, const U8 g, const U8 b, U8 a) +{ + mV[0] = r; + mV[1] = g; + mV[2] = b; + mV[3] = a; + return (*this); +} + +// deprecated +inline const LLColor4U& LLColor4U::setVec(const LLColor4U &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = vec.mV[VW]; + return (*this); +} + +// deprecated +inline const LLColor4U& LLColor4U::setVec(const U8 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; + mV[VW] = vec[VW]; + return (*this); +} + +inline const LLColor4U& LLColor4U::setAlpha(U8 a) +{ + mV[VW] = a; + return (*this); +} + +// LLColor4U Magnitude and Normalization Functions + +inline F32 LLColor4U::length(void) const +{ + return (F32) sqrt( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] ); +} + +inline F32 LLColor4U::lengthSquared(void) const +{ + return ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ]; +} + +// deprecated +inline F32 LLColor4U::magVec(void) const +{ + return (F32) sqrt( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] ); +} + +// deprecated +inline F32 LLColor4U::magVecSquared(void) const +{ + return ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ]; +} + +inline LLColor4U operator+(const LLColor4U &a, const LLColor4U &b) +{ + return LLColor4U( + a.mV[VX] + b.mV[VX], + a.mV[VY] + b.mV[VY], + a.mV[VZ] + b.mV[VZ], + a.mV[VW] + b.mV[VW]); +} + +inline LLColor4U operator-(const LLColor4U &a, const LLColor4U &b) +{ + return LLColor4U( + a.mV[VX] - b.mV[VX], + a.mV[VY] - b.mV[VY], + a.mV[VZ] - b.mV[VZ], + a.mV[VW] - b.mV[VW]); +} + +inline LLColor4U operator*(const LLColor4U &a, const LLColor4U &b) +{ + return LLColor4U( + a.mV[VX] * b.mV[VX], + a.mV[VY] * b.mV[VY], + a.mV[VZ] * b.mV[VZ], + a.mV[VW] * b.mV[VW]); +} + +inline LLColor4U LLColor4U::addClampMax(const LLColor4U &color) +{ + return LLColor4U(llmin((S32)mV[VX] + color.mV[VX], 255), + llmin((S32)mV[VY] + color.mV[VY], 255), + llmin((S32)mV[VZ] + color.mV[VZ], 255), + llmin((S32)mV[VW] + color.mV[VW], 255)); +} + +inline LLColor4U LLColor4U::multAll(const F32 k) +{ + // Round to nearest + return LLColor4U( + (U8)ll_round(mV[VX] * k), + (U8)ll_round(mV[VY] * k), + (U8)ll_round(mV[VZ] * k), + (U8)ll_round(mV[VW] * k)); +} +/* +inline LLColor4U operator*(const LLColor4U &a, U8 k) +{ + // only affects rgb (not a!) + return LLColor4U( + a.mV[VX] * k, + a.mV[VY] * k, + a.mV[VZ] * k, + a.mV[VW]); +} + +inline LLColor4U operator*(U8 k, const LLColor4U &a) +{ + // only affects rgb (not a!) + return LLColor4U( + a.mV[VX] * k, + a.mV[VY] * k, + a.mV[VZ] * k, + a.mV[VW]); +} + +inline LLColor4U operator%(U8 k, const LLColor4U &a) +{ + // only affects alpha (not rgb!) + return LLColor4U( + a.mV[VX], + a.mV[VY], + a.mV[VZ], + a.mV[VW] * k ); +} + +inline LLColor4U operator%(const LLColor4U &a, U8 k) +{ + // only affects alpha (not rgb!) + return LLColor4U( + a.mV[VX], + a.mV[VY], + a.mV[VZ], + a.mV[VW] * k ); +} +*/ + +inline bool operator==(const LLColor4U &a, const LLColor4U &b) +{ + return ( (a.mV[VX] == b.mV[VX]) + &&(a.mV[VY] == b.mV[VY]) + &&(a.mV[VZ] == b.mV[VZ]) + &&(a.mV[VW] == b.mV[VW])); +} + +inline bool operator!=(const LLColor4U &a, const LLColor4U &b) +{ + return ( (a.mV[VX] != b.mV[VX]) + ||(a.mV[VY] != b.mV[VY]) + ||(a.mV[VZ] != b.mV[VZ]) + ||(a.mV[VW] != b.mV[VW])); +} + +inline const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b) +{ + a.mV[VX] += b.mV[VX]; + a.mV[VY] += b.mV[VY]; + a.mV[VZ] += b.mV[VZ]; + a.mV[VW] += b.mV[VW]; + return a; +} + +inline const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b) +{ + a.mV[VX] -= b.mV[VX]; + a.mV[VY] -= b.mV[VY]; + a.mV[VZ] -= b.mV[VZ]; + a.mV[VW] -= b.mV[VW]; + return a; +} + +inline const LLColor4U& operator*=(LLColor4U &a, U8 k) +{ + // only affects rgb (not a!) + a.mV[VX] *= k; + a.mV[VY] *= k; + a.mV[VZ] *= k; + return a; +} + +inline const LLColor4U& operator%=(LLColor4U &a, U8 k) +{ + // only affects alpha (not rgb!) + a.mV[VW] *= k; + return a; +} + +inline F32 distVec(const LLColor4U &a, const LLColor4U &b) +{ + LLColor4U vec = a - b; + return (vec.length()); +} + +inline F32 distVec_squared(const LLColor4U &a, const LLColor4U &b) +{ + LLColor4U vec = a - b; + return (vec.lengthSquared()); +} + +void LLColor4U::setVecScaleClamp(const LLColor4& color) +{ + F32 color_scale_factor = 255.f; + F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]); + if (max_color > 1.f) + { + color_scale_factor /= max_color; + } + const S32 MAX_COLOR = 255; + S32 r = ll_round(color.mV[0] * color_scale_factor); + if (r > MAX_COLOR) + { + r = MAX_COLOR; + } + else if (r < 0) + { + r = 0; + } + mV[0] = r; + + S32 g = ll_round(color.mV[1] * color_scale_factor); + if (g > MAX_COLOR) + { + g = MAX_COLOR; + } + else if (g < 0) + { + g = 0; + } + mV[1] = g; + + S32 b = ll_round(color.mV[2] * color_scale_factor); + if (b > MAX_COLOR) + { + b = MAX_COLOR; + } + else if (b < 0) + { + b = 0; + } + mV[2] = b; + + // Alpha shouldn't be scaled, just clamped... + S32 a = ll_round(color.mV[3] * MAX_COLOR); + if (a > MAX_COLOR) + { + a = MAX_COLOR; + } + else if (a < 0) + { + a = 0; + } + mV[3] = a; +} + +void LLColor4U::setVecScaleClamp(const LLColor3& color) +{ + F32 color_scale_factor = 255.f; + F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]); + if (max_color > 1.f) + { + color_scale_factor /= max_color; + } + + const S32 MAX_COLOR = 255; + S32 r = ll_round(color.mV[0] * color_scale_factor); + if (r > MAX_COLOR) + { + r = MAX_COLOR; + } + else + if (r < 0) + { + r = 0; + } + mV[0] = r; + + S32 g = ll_round(color.mV[1] * color_scale_factor); + if (g > MAX_COLOR) + { + g = MAX_COLOR; + } + else + if (g < 0) + { + g = 0; + } + mV[1] = g; + + S32 b = ll_round(color.mV[2] * color_scale_factor); + if (b > MAX_COLOR) + { + b = MAX_COLOR; + } + if (b < 0) + { + b = 0; + } + mV[2] = b; + + mV[3] = 255; +} + +inline U32 LLColor4U::asRGBA() const +{ + // Little endian: values are swapped in memory. The original code access the array like a U32, so we need to swap here + + return (mV[3] << 24) | (mV[2] << 16) | (mV[1] << 8) | mV[0]; +} + +inline void LLColor4U::fromRGBA( U32 aVal ) +{ + // Little endian: values are swapped in memory. The original code access the array like a U32, so we need to swap here + + mV[ 0 ] = aVal & 0xFF; + aVal >>= 8; + mV[ 1 ] = aVal & 0xFF; + aVal >>= 8; + mV[ 2 ] = aVal & 0xFF; + aVal >>= 8; + mV[ 3 ] = aVal & 0xFF; +} + + +#endif + diff --git a/indra/llmath/v4math.cpp b/indra/llmath/v4math.cpp index bee6fc3ee8..0aa6eb09c3 100644 --- a/indra/llmath/v4math.cpp +++ b/indra/llmath/v4math.cpp @@ -1,120 +1,120 @@ -/**
- * @file v4math.cpp
- * @brief LLVector4 class implementation.
- *
- * $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 "linden_common.h"
-
-//#include "vmath.h"
-#include "v3math.h"
-#include "v4math.h"
-#include "m4math.h"
-#include "m3math.h"
-#include "llquaternion.h"
-
-// LLVector4
-
-// Axis-Angle rotations
-const LLVector4& LLVector4::rotVec(const LLMatrix4 &mat)
-{
- *this = *this * mat;
- return *this;
-}
-
-const LLVector4& LLVector4::rotVec(const LLQuaternion &q)
-{
- *this = *this * q;
- return *this;
-}
-
-const LLVector4& LLVector4::scaleVec(const LLVector4& vec)
-{
- mV[VX] *= vec.mV[VX];
- mV[VY] *= vec.mV[VY];
- mV[VZ] *= vec.mV[VZ];
- mV[VW] *= vec.mV[VW];
-
- return *this;
-}
-
-// Sets all values to absolute value of their original values
-// Returns true if data changed
-bool LLVector4::abs()
-{
- bool ret{ false };
-
- if (mV[0] < 0.f) { mV[0] = -mV[0]; ret = true; }
- if (mV[1] < 0.f) { mV[1] = -mV[1]; ret = true; }
- if (mV[2] < 0.f) { mV[2] = -mV[2]; ret = true; }
- if (mV[3] < 0.f) { mV[3] = -mV[3]; ret = true; }
-
- return ret;
-}
-
-
-std::ostream& operator<<(std::ostream& s, const LLVector4 &a)
-{
- s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << ", " << a.mV[VW] << " }";
- return s;
-}
-
-
-// Non-member functions
-
-F32 angle_between( const LLVector4& a, const LLVector4& b )
-{
- LLVector4 an = a;
- LLVector4 bn = b;
- an.normalize();
- bn.normalize();
- F32 cosine = an * bn;
- F32 angle = (cosine >= 1.0f) ? 0.0f :
- (cosine <= -1.0f) ? F_PI :
- acos(cosine);
- return angle;
-}
-
-bool are_parallel(const LLVector4 &a, const LLVector4 &b, F32 epsilon)
-{
- LLVector4 an = a;
- LLVector4 bn = b;
- an.normalize();
- bn.normalize();
- F32 dot = an * bn;
- if ( (1.0f - fabs(dot)) < epsilon)
- return true;
- return false;
-}
-
-
-LLVector3 vec4to3(const LLVector4 &vec)
-{
- return LLVector3( vec.mV[VX], vec.mV[VY], vec.mV[VZ] );
-}
-
-LLVector4 vec3to4(const LLVector3 &vec)
-{
- return LLVector4(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
-}
-
+/** + * @file v4math.cpp + * @brief LLVector4 class implementation. + * + * $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 "linden_common.h" + +//#include "vmath.h" +#include "v3math.h" +#include "v4math.h" +#include "m4math.h" +#include "m3math.h" +#include "llquaternion.h" + +// LLVector4 + +// Axis-Angle rotations +const LLVector4& LLVector4::rotVec(const LLMatrix4 &mat) +{ + *this = *this * mat; + return *this; +} + +const LLVector4& LLVector4::rotVec(const LLQuaternion &q) +{ + *this = *this * q; + return *this; +} + +const LLVector4& LLVector4::scaleVec(const LLVector4& vec) +{ + mV[VX] *= vec.mV[VX]; + mV[VY] *= vec.mV[VY]; + mV[VZ] *= vec.mV[VZ]; + mV[VW] *= vec.mV[VW]; + + return *this; +} + +// Sets all values to absolute value of their original values +// Returns true if data changed +bool LLVector4::abs() +{ + bool ret{ false }; + + if (mV[0] < 0.f) { mV[0] = -mV[0]; ret = true; } + if (mV[1] < 0.f) { mV[1] = -mV[1]; ret = true; } + if (mV[2] < 0.f) { mV[2] = -mV[2]; ret = true; } + if (mV[3] < 0.f) { mV[3] = -mV[3]; ret = true; } + + return ret; +} + + +std::ostream& operator<<(std::ostream& s, const LLVector4 &a) +{ + s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << ", " << a.mV[VW] << " }"; + return s; +} + + +// Non-member functions + +F32 angle_between( const LLVector4& a, const LLVector4& b ) +{ + LLVector4 an = a; + LLVector4 bn = b; + an.normalize(); + bn.normalize(); + F32 cosine = an * bn; + F32 angle = (cosine >= 1.0f) ? 0.0f : + (cosine <= -1.0f) ? F_PI : + acos(cosine); + return angle; +} + +bool are_parallel(const LLVector4 &a, const LLVector4 &b, F32 epsilon) +{ + LLVector4 an = a; + LLVector4 bn = b; + an.normalize(); + bn.normalize(); + F32 dot = an * bn; + if ( (1.0f - fabs(dot)) < epsilon) + return true; + return false; +} + + +LLVector3 vec4to3(const LLVector4 &vec) +{ + return LLVector3( vec.mV[VX], vec.mV[VY], vec.mV[VZ] ); +} + +LLVector4 vec3to4(const LLVector3 &vec) +{ + return LLVector4(vec.mV[VX], vec.mV[VY], vec.mV[VZ]); +} + diff --git a/indra/llmath/v4math.h b/indra/llmath/v4math.h index 2a21edf198..7ed22212d3 100644 --- a/indra/llmath/v4math.h +++ b/indra/llmath/v4math.h @@ -1,549 +1,549 @@ -/**
- * @file v4math.h
- * @brief LLVector4 class header file.
- *
- * $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$
- */
-
-#ifndef LL_V4MATH_H
-#define LL_V4MATH_H
-
-#include "llerror.h"
-#include "llmath.h"
-#include "v3math.h"
-#include "v2math.h"
-
-class LLMatrix3;
-class LLMatrix4;
-class LLQuaternion;
-
-// LLVector4 = |x y z w|
-
-static const U32 LENGTHOFVECTOR4 = 4;
-
-class LLVector4
-{
- public:
- F32 mV[LENGTHOFVECTOR4];
- LLVector4(); // Initializes LLVector4 to (0, 0, 0, 1)
- explicit LLVector4(const F32 *vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2], vec[3])
- explicit LLVector4(const F64 *vec); // Initialized LLVector4 to ((F32) vec[0], (F32) vec[1], (F32) vec[3], (F32) vec[4]);
- explicit LLVector4(const LLVector2 &vec);
- explicit LLVector4(const LLVector2 &vec, F32 z, F32 w);
- explicit LLVector4(const LLVector3 &vec); // Initializes LLVector4 to (vec, 1)
- explicit LLVector4(const LLVector3 &vec, F32 w); // Initializes LLVector4 to (vec, w)
- explicit LLVector4(const LLSD &sd);
- LLVector4(F32 x, F32 y, F32 z); // Initializes LLVector4 to (x. y, z, 1)
- LLVector4(F32 x, F32 y, F32 z, F32 w);
-
- LLSD getValue() const
- {
- LLSD ret;
- ret[0] = mV[0];
- ret[1] = mV[1];
- ret[2] = mV[2];
- ret[3] = mV[3];
- return ret;
- }
-
- void setValue(const LLSD& sd)
- {
- mV[0] = sd[0].asReal();
- mV[1] = sd[1].asReal();
- mV[2] = sd[2].asReal();
- mV[3] = sd[3].asReal();
- }
-
-
- inline bool isFinite() const; // checks to see if all values of LLVector3 are finite
-
- inline void clear(); // Clears LLVector4 to (0, 0, 0, 1)
- inline void clearVec(); // deprecated
- inline void zeroVec(); // deprecated
-
- inline void set(F32 x, F32 y, F32 z); // Sets LLVector4 to (x, y, z, 1)
- inline void set(F32 x, F32 y, F32 z, F32 w); // Sets LLVector4 to (x, y, z, w)
- inline void set(const LLVector4 &vec); // Sets LLVector4 to vec
- inline void set(const LLVector3 &vec, F32 w = 1.f); // Sets LLVector4 to LLVector3 vec
- inline void set(const F32 *vec); // Sets LLVector4 to vec
-
- inline void setVec(F32 x, F32 y, F32 z); // deprecated
- inline void setVec(F32 x, F32 y, F32 z, F32 w); // deprecated
- inline void setVec(const LLVector4 &vec); // deprecated
- inline void setVec(const LLVector3 &vec, F32 w = 1.f); // deprecated
- inline void setVec(const F32 *vec); // deprecated
-
- F32 length() const; // Returns magnitude of LLVector4
- F32 lengthSquared() const; // Returns magnitude squared of LLVector4
- F32 normalize(); // Normalizes and returns the magnitude of LLVector4
-
- F32 magVec() const; // deprecated
- F32 magVecSquared() const; // deprecated
- F32 normVec(); // deprecated
-
- // Sets all values to absolute value of their original values
- // Returns true if data changed
- bool abs();
-
- bool isExactlyClear() const { return (mV[VW] == 1.0f) && !mV[VX] && !mV[VY] && !mV[VZ]; }
- bool isExactlyZero() const { return !mV[VW] && !mV[VX] && !mV[VY] && !mV[VZ]; }
-
- const LLVector4& rotVec(const LLMatrix4 &mat); // Rotates by MAT4 mat
- const LLVector4& rotVec(const LLQuaternion &q); // Rotates by QUAT q
-
- const LLVector4& scaleVec(const LLVector4& vec); // Scales component-wise by vec
-
- F32 operator[](int idx) const { return mV[idx]; }
- F32 &operator[](int idx) { return mV[idx]; }
-
- friend std::ostream& operator<<(std::ostream& s, const LLVector4 &a); // Print a
- friend LLVector4 operator+(const LLVector4 &a, const LLVector4 &b); // Return vector a + b
- friend LLVector4 operator-(const LLVector4 &a, const LLVector4 &b); // Return vector a minus b
- friend F32 operator*(const LLVector4 &a, const LLVector4 &b); // Return a dot b
- friend LLVector4 operator%(const LLVector4 &a, const LLVector4 &b); // Return a cross b
- friend LLVector4 operator/(const LLVector4 &a, F32 k); // Return a divided by scaler k
- friend LLVector4 operator*(const LLVector4 &a, F32 k); // Return a times scaler k
- friend LLVector4 operator*(F32 k, const LLVector4 &a); // Return a times scaler k
- friend bool operator==(const LLVector4 &a, const LLVector4 &b); // Return a == b
- friend bool operator!=(const LLVector4 &a, const LLVector4 &b); // Return a != b
-
- friend const LLVector4& operator+=(LLVector4 &a, const LLVector4 &b); // Return vector a + b
- friend const LLVector4& operator-=(LLVector4 &a, const LLVector4 &b); // Return vector a minus b
- friend const LLVector4& operator%=(LLVector4 &a, const LLVector4 &b); // Return a cross b
- friend const LLVector4& operator*=(LLVector4 &a, F32 k); // Return a times scaler k
- friend const LLVector4& operator/=(LLVector4 &a, F32 k); // Return a divided by scaler k
-
- friend LLVector4 operator-(const LLVector4 &a); // Return vector -a
-};
-
-// Non-member functions
-F32 angle_between(const LLVector4 &a, const LLVector4 &b); // Returns angle (radians) between a and b
-bool are_parallel(const LLVector4 &a, const LLVector4 &b, F32 epsilon = F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel
-F32 dist_vec(const LLVector4 &a, const LLVector4 &b); // Returns distance between a and b
-F32 dist_vec_squared(const LLVector4 &a, const LLVector4 &b); // Returns distance squared between a and b
-LLVector3 vec4to3(const LLVector4 &vec);
-LLVector4 vec3to4(const LLVector3 &vec);
-LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u); // Returns a vector that is a linear interpolation between a and b
-
-// Constructors
-
-inline LLVector4::LLVector4(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
- mV[VZ] = 0.f;
- mV[VW] = 1.f;
-}
-
-inline LLVector4::LLVector4(F32 x, F32 y, F32 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
- mV[VW] = 1.f;
-}
-
-inline LLVector4::LLVector4(F32 x, F32 y, F32 z, F32 w)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
- mV[VW] = w;
-}
-
-inline LLVector4::LLVector4(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
- mV[VW] = vec[VW];
-}
-
-inline LLVector4::LLVector4(const F64 *vec)
-{
- mV[VX] = (F32) vec[VX];
- mV[VY] = (F32) vec[VY];
- mV[VZ] = (F32) vec[VZ];
- mV[VW] = (F32) vec[VW];
-}
-
-inline LLVector4::LLVector4(const LLVector2 &vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = 0.f;
- mV[VW] = 0.f;
-}
-
-inline LLVector4::LLVector4(const LLVector2 &vec, F32 z, F32 w)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = z;
- mV[VW] = w;
-}
-
-inline LLVector4::LLVector4(const LLVector3 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = 1.f;
-}
-
-inline LLVector4::LLVector4(const LLVector3 &vec, F32 w)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = w;
-}
-
-inline LLVector4::LLVector4(const LLSD &sd)
-{
- setValue(sd);
-}
-
-
-inline bool LLVector4::isFinite() const
-{
- return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]) && llfinite(mV[VW]));
-}
-
-// Clear and Assignment Functions
-
-inline void LLVector4::clear(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
- mV[VZ] = 0.f;
- mV[VW] = 1.f;
-}
-
-// deprecated
-inline void LLVector4::clearVec(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
- mV[VZ] = 0.f;
- mV[VW] = 1.f;
-}
-
-// deprecated
-inline void LLVector4::zeroVec(void)
-{
- mV[VX] = 0.f;
- mV[VY] = 0.f;
- mV[VZ] = 0.f;
- mV[VW] = 0.f;
-}
-
-inline void LLVector4::set(F32 x, F32 y, F32 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
- mV[VW] = 1.f;
-}
-
-inline void LLVector4::set(F32 x, F32 y, F32 z, F32 w)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
- mV[VW] = w;
-}
-
-inline void LLVector4::set(const LLVector4 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = vec.mV[VW];
-}
-
-inline void LLVector4::set(const LLVector3 &vec, F32 w)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = w;
-}
-
-inline void LLVector4::set(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
- mV[VW] = vec[VW];
-}
-
-
-// deprecated
-inline void LLVector4::setVec(F32 x, F32 y, F32 z)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
- mV[VW] = 1.f;
-}
-
-// deprecated
-inline void LLVector4::setVec(F32 x, F32 y, F32 z, F32 w)
-{
- mV[VX] = x;
- mV[VY] = y;
- mV[VZ] = z;
- mV[VW] = w;
-}
-
-// deprecated
-inline void LLVector4::setVec(const LLVector4 &vec)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = vec.mV[VW];
-}
-
-// deprecated
-inline void LLVector4::setVec(const LLVector3 &vec, F32 w)
-{
- mV[VX] = vec.mV[VX];
- mV[VY] = vec.mV[VY];
- mV[VZ] = vec.mV[VZ];
- mV[VW] = w;
-}
-
-// deprecated
-inline void LLVector4::setVec(const F32 *vec)
-{
- mV[VX] = vec[VX];
- mV[VY] = vec[VY];
- mV[VZ] = vec[VZ];
- mV[VW] = vec[VW];
-}
-
-// LLVector4 Magnitude and Normalization Functions
-
-inline F32 LLVector4::length(void) const
-{
- return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
-}
-
-inline F32 LLVector4::lengthSquared(void) const
-{
- return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
-}
-
-inline F32 LLVector4::magVec(void) const
-{
- return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
-}
-
-inline F32 LLVector4::magVecSquared(void) const
-{
- return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
-}
-
-// LLVector4 Operators
-
-inline LLVector4 operator+(const LLVector4 &a, const LLVector4 &b)
-{
- LLVector4 c(a);
- return c += b;
-}
-
-inline LLVector4 operator-(const LLVector4 &a, const LLVector4 &b)
-{
- LLVector4 c(a);
- return c -= b;
-}
-
-inline F32 operator*(const LLVector4 &a, const LLVector4 &b)
-{
- return (a.mV[VX]*b.mV[VX] + a.mV[VY]*b.mV[VY] + a.mV[VZ]*b.mV[VZ]);
-}
-
-inline LLVector4 operator%(const LLVector4 &a, const LLVector4 &b)
-{
- return LLVector4(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]);
-}
-
-inline LLVector4 operator/(const LLVector4 &a, F32 k)
-{
- F32 t = 1.f / k;
- return LLVector4( a.mV[VX] * t, a.mV[VY] * t, a.mV[VZ] * t );
-}
-
-
-inline LLVector4 operator*(const LLVector4 &a, F32 k)
-{
- return LLVector4( a.mV[VX] * k, a.mV[VY] * k, a.mV[VZ] * k );
-}
-
-inline LLVector4 operator*(F32 k, const LLVector4 &a)
-{
- return LLVector4( a.mV[VX] * k, a.mV[VY] * k, a.mV[VZ] * k );
-}
-
-inline bool operator==(const LLVector4 &a, const LLVector4 &b)
-{
- return ( (a.mV[VX] == b.mV[VX])
- &&(a.mV[VY] == b.mV[VY])
- &&(a.mV[VZ] == b.mV[VZ]));
-}
-
-inline bool operator!=(const LLVector4 &a, const LLVector4 &b)
-{
- return ( (a.mV[VX] != b.mV[VX])
- ||(a.mV[VY] != b.mV[VY])
- ||(a.mV[VZ] != b.mV[VZ])
- ||(a.mV[VW] != b.mV[VW]) );
-}
-
-inline const LLVector4& operator+=(LLVector4 &a, const LLVector4 &b)
-{
- a.mV[VX] += b.mV[VX];
- a.mV[VY] += b.mV[VY];
- a.mV[VZ] += b.mV[VZ];
- return a;
-}
-
-inline const LLVector4& operator-=(LLVector4 &a, const LLVector4 &b)
-{
- a.mV[VX] -= b.mV[VX];
- a.mV[VY] -= b.mV[VY];
- a.mV[VZ] -= b.mV[VZ];
- return a;
-}
-
-inline const LLVector4& operator%=(LLVector4 &a, const LLVector4 &b)
-{
- LLVector4 ret(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]);
- a = ret;
- return a;
-}
-
-inline const LLVector4& operator*=(LLVector4 &a, F32 k)
-{
- a.mV[VX] *= k;
- a.mV[VY] *= k;
- a.mV[VZ] *= k;
- return a;
-}
-
-inline const LLVector4& operator/=(LLVector4 &a, F32 k)
-{
- F32 t = 1.f / k;
- a.mV[VX] *= t;
- a.mV[VY] *= t;
- a.mV[VZ] *= t;
- return a;
-}
-
-inline LLVector4 operator-(const LLVector4 &a)
-{
- return LLVector4( -a.mV[VX], -a.mV[VY], -a.mV[VZ] );
-}
-
-inline F32 dist_vec(const LLVector4 &a, const LLVector4 &b)
-{
- LLVector4 vec = a - b;
- return (vec.length());
-}
-
-inline F32 dist_vec_squared(const LLVector4 &a, const LLVector4 &b)
-{
- LLVector4 vec = a - b;
- return (vec.lengthSquared());
-}
-
-inline LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u)
-{
- return LLVector4(
- a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
- a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u,
- a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u,
- a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u);
-}
-
-inline F32 LLVector4::normalize(void)
-{
- F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
- F32 oomag;
-
- if (mag > FP_MAG_THRESHOLD)
- {
- oomag = 1.f/mag;
- mV[VX] *= oomag;
- mV[VY] *= oomag;
- mV[VZ] *= oomag;
- }
- else
- {
- mV[0] = 0.f;
- mV[1] = 0.f;
- mV[2] = 0.f;
- mag = 0;
- }
- return (mag);
-}
-
-// deprecated
-inline F32 LLVector4::normVec(void)
-{
- F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
- F32 oomag;
-
- if (mag > FP_MAG_THRESHOLD)
- {
- oomag = 1.f/mag;
- mV[VX] *= oomag;
- mV[VY] *= oomag;
- mV[VZ] *= oomag;
- }
- else
- {
- mV[0] = 0.f;
- mV[1] = 0.f;
- mV[2] = 0.f;
- mag = 0;
- }
- return (mag);
-}
-
-// Because apparently some parts of the viewer use this for color info.
-inline const LLVector4 srgbVector4(const LLVector4 &a) {
- LLVector4 srgbColor;
-
- srgbColor.mV[0] = linearTosRGB(a.mV[0]);
- srgbColor.mV[1] = linearTosRGB(a.mV[1]);
- srgbColor.mV[2] = linearTosRGB(a.mV[2]);
- srgbColor.mV[3] = a.mV[3];
-
- return srgbColor;
-}
-
-
-#endif
-
+/** + * @file v4math.h + * @brief LLVector4 class header file. + * + * $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$ + */ + +#ifndef LL_V4MATH_H +#define LL_V4MATH_H + +#include "llerror.h" +#include "llmath.h" +#include "v3math.h" +#include "v2math.h" + +class LLMatrix3; +class LLMatrix4; +class LLQuaternion; + +// LLVector4 = |x y z w| + +static const U32 LENGTHOFVECTOR4 = 4; + +class LLVector4 +{ + public: + F32 mV[LENGTHOFVECTOR4]; + LLVector4(); // Initializes LLVector4 to (0, 0, 0, 1) + explicit LLVector4(const F32 *vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2], vec[3]) + explicit LLVector4(const F64 *vec); // Initialized LLVector4 to ((F32) vec[0], (F32) vec[1], (F32) vec[3], (F32) vec[4]); + explicit LLVector4(const LLVector2 &vec); + explicit LLVector4(const LLVector2 &vec, F32 z, F32 w); + explicit LLVector4(const LLVector3 &vec); // Initializes LLVector4 to (vec, 1) + explicit LLVector4(const LLVector3 &vec, F32 w); // Initializes LLVector4 to (vec, w) + explicit LLVector4(const LLSD &sd); + LLVector4(F32 x, F32 y, F32 z); // Initializes LLVector4 to (x. y, z, 1) + LLVector4(F32 x, F32 y, F32 z, F32 w); + + LLSD getValue() const + { + LLSD ret; + ret[0] = mV[0]; + ret[1] = mV[1]; + ret[2] = mV[2]; + ret[3] = mV[3]; + return ret; + } + + void setValue(const LLSD& sd) + { + mV[0] = sd[0].asReal(); + mV[1] = sd[1].asReal(); + mV[2] = sd[2].asReal(); + mV[3] = sd[3].asReal(); + } + + + inline bool isFinite() const; // checks to see if all values of LLVector3 are finite + + inline void clear(); // Clears LLVector4 to (0, 0, 0, 1) + inline void clearVec(); // deprecated + inline void zeroVec(); // deprecated + + inline void set(F32 x, F32 y, F32 z); // Sets LLVector4 to (x, y, z, 1) + inline void set(F32 x, F32 y, F32 z, F32 w); // Sets LLVector4 to (x, y, z, w) + inline void set(const LLVector4 &vec); // Sets LLVector4 to vec + inline void set(const LLVector3 &vec, F32 w = 1.f); // Sets LLVector4 to LLVector3 vec + inline void set(const F32 *vec); // Sets LLVector4 to vec + + inline void setVec(F32 x, F32 y, F32 z); // deprecated + inline void setVec(F32 x, F32 y, F32 z, F32 w); // deprecated + inline void setVec(const LLVector4 &vec); // deprecated + inline void setVec(const LLVector3 &vec, F32 w = 1.f); // deprecated + inline void setVec(const F32 *vec); // deprecated + + F32 length() const; // Returns magnitude of LLVector4 + F32 lengthSquared() const; // Returns magnitude squared of LLVector4 + F32 normalize(); // Normalizes and returns the magnitude of LLVector4 + + F32 magVec() const; // deprecated + F32 magVecSquared() const; // deprecated + F32 normVec(); // deprecated + + // Sets all values to absolute value of their original values + // Returns true if data changed + bool abs(); + + bool isExactlyClear() const { return (mV[VW] == 1.0f) && !mV[VX] && !mV[VY] && !mV[VZ]; } + bool isExactlyZero() const { return !mV[VW] && !mV[VX] && !mV[VY] && !mV[VZ]; } + + const LLVector4& rotVec(const LLMatrix4 &mat); // Rotates by MAT4 mat + const LLVector4& rotVec(const LLQuaternion &q); // Rotates by QUAT q + + const LLVector4& scaleVec(const LLVector4& vec); // Scales component-wise by vec + + F32 operator[](int idx) const { return mV[idx]; } + F32 &operator[](int idx) { return mV[idx]; } + + friend std::ostream& operator<<(std::ostream& s, const LLVector4 &a); // Print a + friend LLVector4 operator+(const LLVector4 &a, const LLVector4 &b); // Return vector a + b + friend LLVector4 operator-(const LLVector4 &a, const LLVector4 &b); // Return vector a minus b + friend F32 operator*(const LLVector4 &a, const LLVector4 &b); // Return a dot b + friend LLVector4 operator%(const LLVector4 &a, const LLVector4 &b); // Return a cross b + friend LLVector4 operator/(const LLVector4 &a, F32 k); // Return a divided by scaler k + friend LLVector4 operator*(const LLVector4 &a, F32 k); // Return a times scaler k + friend LLVector4 operator*(F32 k, const LLVector4 &a); // Return a times scaler k + friend bool operator==(const LLVector4 &a, const LLVector4 &b); // Return a == b + friend bool operator!=(const LLVector4 &a, const LLVector4 &b); // Return a != b + + friend const LLVector4& operator+=(LLVector4 &a, const LLVector4 &b); // Return vector a + b + friend const LLVector4& operator-=(LLVector4 &a, const LLVector4 &b); // Return vector a minus b + friend const LLVector4& operator%=(LLVector4 &a, const LLVector4 &b); // Return a cross b + friend const LLVector4& operator*=(LLVector4 &a, F32 k); // Return a times scaler k + friend const LLVector4& operator/=(LLVector4 &a, F32 k); // Return a divided by scaler k + + friend LLVector4 operator-(const LLVector4 &a); // Return vector -a +}; + +// Non-member functions +F32 angle_between(const LLVector4 &a, const LLVector4 &b); // Returns angle (radians) between a and b +bool are_parallel(const LLVector4 &a, const LLVector4 &b, F32 epsilon = F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel +F32 dist_vec(const LLVector4 &a, const LLVector4 &b); // Returns distance between a and b +F32 dist_vec_squared(const LLVector4 &a, const LLVector4 &b); // Returns distance squared between a and b +LLVector3 vec4to3(const LLVector4 &vec); +LLVector4 vec3to4(const LLVector3 &vec); +LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u); // Returns a vector that is a linear interpolation between a and b + +// Constructors + +inline LLVector4::LLVector4(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; + mV[VZ] = 0.f; + mV[VW] = 1.f; +} + +inline LLVector4::LLVector4(F32 x, F32 y, F32 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + mV[VW] = 1.f; +} + +inline LLVector4::LLVector4(F32 x, F32 y, F32 z, F32 w) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + mV[VW] = w; +} + +inline LLVector4::LLVector4(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; + mV[VW] = vec[VW]; +} + +inline LLVector4::LLVector4(const F64 *vec) +{ + mV[VX] = (F32) vec[VX]; + mV[VY] = (F32) vec[VY]; + mV[VZ] = (F32) vec[VZ]; + mV[VW] = (F32) vec[VW]; +} + +inline LLVector4::LLVector4(const LLVector2 &vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = 0.f; + mV[VW] = 0.f; +} + +inline LLVector4::LLVector4(const LLVector2 &vec, F32 z, F32 w) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = z; + mV[VW] = w; +} + +inline LLVector4::LLVector4(const LLVector3 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = 1.f; +} + +inline LLVector4::LLVector4(const LLVector3 &vec, F32 w) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = w; +} + +inline LLVector4::LLVector4(const LLSD &sd) +{ + setValue(sd); +} + + +inline bool LLVector4::isFinite() const +{ + return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]) && llfinite(mV[VW])); +} + +// Clear and Assignment Functions + +inline void LLVector4::clear(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; + mV[VZ] = 0.f; + mV[VW] = 1.f; +} + +// deprecated +inline void LLVector4::clearVec(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; + mV[VZ] = 0.f; + mV[VW] = 1.f; +} + +// deprecated +inline void LLVector4::zeroVec(void) +{ + mV[VX] = 0.f; + mV[VY] = 0.f; + mV[VZ] = 0.f; + mV[VW] = 0.f; +} + +inline void LLVector4::set(F32 x, F32 y, F32 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + mV[VW] = 1.f; +} + +inline void LLVector4::set(F32 x, F32 y, F32 z, F32 w) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + mV[VW] = w; +} + +inline void LLVector4::set(const LLVector4 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = vec.mV[VW]; +} + +inline void LLVector4::set(const LLVector3 &vec, F32 w) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = w; +} + +inline void LLVector4::set(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; + mV[VW] = vec[VW]; +} + + +// deprecated +inline void LLVector4::setVec(F32 x, F32 y, F32 z) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + mV[VW] = 1.f; +} + +// deprecated +inline void LLVector4::setVec(F32 x, F32 y, F32 z, F32 w) +{ + mV[VX] = x; + mV[VY] = y; + mV[VZ] = z; + mV[VW] = w; +} + +// deprecated +inline void LLVector4::setVec(const LLVector4 &vec) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = vec.mV[VW]; +} + +// deprecated +inline void LLVector4::setVec(const LLVector3 &vec, F32 w) +{ + mV[VX] = vec.mV[VX]; + mV[VY] = vec.mV[VY]; + mV[VZ] = vec.mV[VZ]; + mV[VW] = w; +} + +// deprecated +inline void LLVector4::setVec(const F32 *vec) +{ + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; + mV[VW] = vec[VW]; +} + +// LLVector4 Magnitude and Normalization Functions + +inline F32 LLVector4::length(void) const +{ + return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); +} + +inline F32 LLVector4::lengthSquared(void) const +{ + return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]; +} + +inline F32 LLVector4::magVec(void) const +{ + return (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); +} + +inline F32 LLVector4::magVecSquared(void) const +{ + return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]; +} + +// LLVector4 Operators + +inline LLVector4 operator+(const LLVector4 &a, const LLVector4 &b) +{ + LLVector4 c(a); + return c += b; +} + +inline LLVector4 operator-(const LLVector4 &a, const LLVector4 &b) +{ + LLVector4 c(a); + return c -= b; +} + +inline F32 operator*(const LLVector4 &a, const LLVector4 &b) +{ + return (a.mV[VX]*b.mV[VX] + a.mV[VY]*b.mV[VY] + a.mV[VZ]*b.mV[VZ]); +} + +inline LLVector4 operator%(const LLVector4 &a, const LLVector4 &b) +{ + return LLVector4(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]); +} + +inline LLVector4 operator/(const LLVector4 &a, F32 k) +{ + F32 t = 1.f / k; + return LLVector4( a.mV[VX] * t, a.mV[VY] * t, a.mV[VZ] * t ); +} + + +inline LLVector4 operator*(const LLVector4 &a, F32 k) +{ + return LLVector4( a.mV[VX] * k, a.mV[VY] * k, a.mV[VZ] * k ); +} + +inline LLVector4 operator*(F32 k, const LLVector4 &a) +{ + return LLVector4( a.mV[VX] * k, a.mV[VY] * k, a.mV[VZ] * k ); +} + +inline bool operator==(const LLVector4 &a, const LLVector4 &b) +{ + return ( (a.mV[VX] == b.mV[VX]) + &&(a.mV[VY] == b.mV[VY]) + &&(a.mV[VZ] == b.mV[VZ])); +} + +inline bool operator!=(const LLVector4 &a, const LLVector4 &b) +{ + return ( (a.mV[VX] != b.mV[VX]) + ||(a.mV[VY] != b.mV[VY]) + ||(a.mV[VZ] != b.mV[VZ]) + ||(a.mV[VW] != b.mV[VW]) ); +} + +inline const LLVector4& operator+=(LLVector4 &a, const LLVector4 &b) +{ + a.mV[VX] += b.mV[VX]; + a.mV[VY] += b.mV[VY]; + a.mV[VZ] += b.mV[VZ]; + return a; +} + +inline const LLVector4& operator-=(LLVector4 &a, const LLVector4 &b) +{ + a.mV[VX] -= b.mV[VX]; + a.mV[VY] -= b.mV[VY]; + a.mV[VZ] -= b.mV[VZ]; + return a; +} + +inline const LLVector4& operator%=(LLVector4 &a, const LLVector4 &b) +{ + LLVector4 ret(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]); + a = ret; + return a; +} + +inline const LLVector4& operator*=(LLVector4 &a, F32 k) +{ + a.mV[VX] *= k; + a.mV[VY] *= k; + a.mV[VZ] *= k; + return a; +} + +inline const LLVector4& operator/=(LLVector4 &a, F32 k) +{ + F32 t = 1.f / k; + a.mV[VX] *= t; + a.mV[VY] *= t; + a.mV[VZ] *= t; + return a; +} + +inline LLVector4 operator-(const LLVector4 &a) +{ + return LLVector4( -a.mV[VX], -a.mV[VY], -a.mV[VZ] ); +} + +inline F32 dist_vec(const LLVector4 &a, const LLVector4 &b) +{ + LLVector4 vec = a - b; + return (vec.length()); +} + +inline F32 dist_vec_squared(const LLVector4 &a, const LLVector4 &b) +{ + LLVector4 vec = a - b; + return (vec.lengthSquared()); +} + +inline LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u) +{ + return LLVector4( + a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u, + a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u, + a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u, + a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u); +} + +inline F32 LLVector4::normalize(void) +{ + F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); + F32 oomag; + + if (mag > FP_MAG_THRESHOLD) + { + oomag = 1.f/mag; + mV[VX] *= oomag; + mV[VY] *= oomag; + mV[VZ] *= oomag; + } + else + { + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; + mag = 0; + } + return (mag); +} + +// deprecated +inline F32 LLVector4::normVec(void) +{ + F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); + F32 oomag; + + if (mag > FP_MAG_THRESHOLD) + { + oomag = 1.f/mag; + mV[VX] *= oomag; + mV[VY] *= oomag; + mV[VZ] *= oomag; + } + else + { + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; + mag = 0; + } + return (mag); +} + +// Because apparently some parts of the viewer use this for color info. +inline const LLVector4 srgbVector4(const LLVector4 &a) { + LLVector4 srgbColor; + + srgbColor.mV[0] = linearTosRGB(a.mV[0]); + srgbColor.mV[1] = linearTosRGB(a.mV[1]); + srgbColor.mV[2] = linearTosRGB(a.mV[2]); + srgbColor.mV[3] = a.mV[3]; + + return srgbColor; +} + + +#endif + diff --git a/indra/llmath/xform.cpp b/indra/llmath/xform.cpp index 239c1ca195..39bbb94c9f 100644 --- a/indra/llmath/xform.cpp +++ b/indra/llmath/xform.cpp @@ -1,119 +1,119 @@ -/**
- * @file xform.cpp
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "xform.h"
-
-LLXform::LLXform()
-{
- init();
-}
-
-LLXform::~LLXform()
-{
-}
-
-// Link optimization - don't inline these LL_WARNS()
-void LLXform::warn(const char* const msg)
-{
- LL_WARNS() << msg << LL_ENDL;
-}
-
-LLXform* LLXform::getRoot() const
-{
- const LLXform* root = this;
- while(root->mParent)
- {
- root = root->mParent;
- }
- return (LLXform*)root;
-}
-
-bool LLXform::isRoot() const
-{
- return (!mParent);
-}
-
-bool LLXform::isRootEdit() const
-{
- return (!mParent);
-}
-
-LLXformMatrix::~LLXformMatrix()
-{
-}
-
-void LLXformMatrix::update()
-{
- if (mParent)
- {
- mWorldPosition = mPosition;
- if (mParent->getScaleChildOffset())
- {
- mWorldPosition.scaleVec(mParent->getScale());
- }
- mWorldPosition *= mParent->getWorldRotation();
- mWorldPosition += mParent->getWorldPosition();
- mWorldRotation = mRotation * mParent->getWorldRotation();
- }
- else
- {
- mWorldPosition = mPosition;
- mWorldRotation = mRotation;
- }
-}
-
-void LLXformMatrix::updateMatrix(bool update_bounds)
-{
- update();
-
- mWorldMatrix.initAll(mScale, mWorldRotation, mWorldPosition);
-
- if (update_bounds && (mChanged & MOVED))
- {
- mMin.mV[0] = mMax.mV[0] = mWorldMatrix.mMatrix[3][0];
- mMin.mV[1] = mMax.mV[1] = mWorldMatrix.mMatrix[3][1];
- mMin.mV[2] = mMax.mV[2] = mWorldMatrix.mMatrix[3][2];
-
- F32 f0 = (fabs(mWorldMatrix.mMatrix[0][0])+fabs(mWorldMatrix.mMatrix[1][0])+fabs(mWorldMatrix.mMatrix[2][0])) * 0.5f;
- F32 f1 = (fabs(mWorldMatrix.mMatrix[0][1])+fabs(mWorldMatrix.mMatrix[1][1])+fabs(mWorldMatrix.mMatrix[2][1])) * 0.5f;
- F32 f2 = (fabs(mWorldMatrix.mMatrix[0][2])+fabs(mWorldMatrix.mMatrix[1][2])+fabs(mWorldMatrix.mMatrix[2][2])) * 0.5f;
-
- mMin.mV[0] -= f0;
- mMin.mV[1] -= f1;
- mMin.mV[2] -= f2;
-
- mMax.mV[0] += f0;
- mMax.mV[1] += f1;
- mMax.mV[2] += f2;
- }
-}
-
-void LLXformMatrix::getMinMax(LLVector3& min, LLVector3& max) const
-{
- min = mMin;
- max = mMax;
-}
+/** + * @file xform.cpp + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "xform.h" + +LLXform::LLXform() +{ + init(); +} + +LLXform::~LLXform() +{ +} + +// Link optimization - don't inline these LL_WARNS() +void LLXform::warn(const char* const msg) +{ + LL_WARNS() << msg << LL_ENDL; +} + +LLXform* LLXform::getRoot() const +{ + const LLXform* root = this; + while(root->mParent) + { + root = root->mParent; + } + return (LLXform*)root; +} + +bool LLXform::isRoot() const +{ + return (!mParent); +} + +bool LLXform::isRootEdit() const +{ + return (!mParent); +} + +LLXformMatrix::~LLXformMatrix() +{ +} + +void LLXformMatrix::update() +{ + if (mParent) + { + mWorldPosition = mPosition; + if (mParent->getScaleChildOffset()) + { + mWorldPosition.scaleVec(mParent->getScale()); + } + mWorldPosition *= mParent->getWorldRotation(); + mWorldPosition += mParent->getWorldPosition(); + mWorldRotation = mRotation * mParent->getWorldRotation(); + } + else + { + mWorldPosition = mPosition; + mWorldRotation = mRotation; + } +} + +void LLXformMatrix::updateMatrix(bool update_bounds) +{ + update(); + + mWorldMatrix.initAll(mScale, mWorldRotation, mWorldPosition); + + if (update_bounds && (mChanged & MOVED)) + { + mMin.mV[0] = mMax.mV[0] = mWorldMatrix.mMatrix[3][0]; + mMin.mV[1] = mMax.mV[1] = mWorldMatrix.mMatrix[3][1]; + mMin.mV[2] = mMax.mV[2] = mWorldMatrix.mMatrix[3][2]; + + F32 f0 = (fabs(mWorldMatrix.mMatrix[0][0])+fabs(mWorldMatrix.mMatrix[1][0])+fabs(mWorldMatrix.mMatrix[2][0])) * 0.5f; + F32 f1 = (fabs(mWorldMatrix.mMatrix[0][1])+fabs(mWorldMatrix.mMatrix[1][1])+fabs(mWorldMatrix.mMatrix[2][1])) * 0.5f; + F32 f2 = (fabs(mWorldMatrix.mMatrix[0][2])+fabs(mWorldMatrix.mMatrix[1][2])+fabs(mWorldMatrix.mMatrix[2][2])) * 0.5f; + + mMin.mV[0] -= f0; + mMin.mV[1] -= f1; + mMin.mV[2] -= f2; + + mMax.mV[0] += f0; + mMax.mV[1] += f1; + mMax.mV[2] += f2; + } +} + +void LLXformMatrix::getMinMax(LLVector3& min, LLVector3& max) const +{ + min = mMin; + max = mMax; +} diff --git a/indra/llmath/xform.h b/indra/llmath/xform.h index 0b583ce189..7434301670 100644 --- a/indra/llmath/xform.h +++ b/indra/llmath/xform.h @@ -1,315 +1,315 @@ -/**
- * @file xform.h
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_XFORM_H
-#define LL_XFORM_H
-
-#include "v3math.h"
-#include "m4math.h"
-#include "llquaternion.h"
-
-constexpr F32 MAX_OBJECT_Z = 4096.f; // should match REGION_HEIGHT_METERS, Pre-havok4: 768.f
-constexpr F32 MIN_OBJECT_Z = -256.f;
-constexpr F32 DEFAULT_MAX_PRIM_SCALE = 64.f;
-constexpr F32 DEFAULT_MAX_PRIM_SCALE_NO_MESH = 10.f;
-constexpr F32 MIN_PRIM_SCALE = 0.01f;
-constexpr F32 MAX_PRIM_SCALE = 65536.f; // something very high but not near FLT_MAX
-
-class LLXform
-{
-protected:
- LLVector3 mPosition;
- LLQuaternion mRotation;
- LLVector3 mScale;
-
- //RN: TODO: move these world transform members to LLXformMatrix
- // as they are *never* updated or accessed in the base class
- LLVector3 mWorldPosition;
- LLQuaternion mWorldRotation;
-
- LLXform* mParent;
- U32 mChanged;
-
- bool mScaleChildOffset;
-
-public:
- typedef enum e_changed_flags
- {
- UNCHANGED = 0x00,
- TRANSLATED = 0x01,
- ROTATED = 0x02,
- SCALED = 0x04,
- SHIFTED = 0x08,
- GEOMETRY = 0x10,
- TEXTURE = 0x20,
- MOVED = TRANSLATED|ROTATED|SCALED,
- SILHOUETTE = 0x40,
- ALL_CHANGED = 0x7f
- }EChangedFlags;
-
- void init()
- {
- mParent = NULL;
- mChanged = UNCHANGED;
- mPosition.setVec(0,0,0);
- mRotation.loadIdentity();
- mScale. setVec(1,1,1);
- mWorldPosition.clearVec();
- mWorldRotation.loadIdentity();
- mScaleChildOffset = false;
- }
-
- LLXform();
- virtual ~LLXform();
-
- void getLocalMat4(LLMatrix4 &mat) const { mat.initAll(mScale, mRotation, mPosition); }
-
- inline bool setParent(LLXform *parent);
-
- inline void setPosition(const LLVector3& pos);
- inline void setPosition(const F32 x, const F32 y, const F32 z);
- inline void setPositionX(const F32 x);
- inline void setPositionY(const F32 y);
- inline void setPositionZ(const F32 z);
- inline void addPosition(const LLVector3& pos);
-
-
- inline void setScale(const LLVector3& scale);
- inline void setScale(const F32 x, const F32 y, const F32 z);
- inline void setRotation(const LLQuaternion& rot);
- inline void setRotation(const F32 x, const F32 y, const F32 z);
- inline void setRotation(const F32 x, const F32 y, const F32 z, const F32 s);
-
- // Above functions must be inline for speed, but also
- // need to emit warnings. LL_WARNS() causes inline LLError::CallSite
- // static objects that make more work for the linker.
- // Avoid inline LL_WARNS() by calling this function.
- void warn(const char* const msg);
-
- void setChanged(const U32 bits) { mChanged |= bits; }
- bool isChanged() const { return mChanged; }
- bool isChanged(const U32 bits) const { return mChanged & bits; }
- void clearChanged() { mChanged = 0; }
- void clearChanged(U32 bits) { mChanged &= ~bits; }
-
- void setScaleChildOffset(bool scale) { mScaleChildOffset = scale; }
- bool getScaleChildOffset() { return mScaleChildOffset; }
-
- LLXform* getParent() const { return mParent; }
- LLXform* getRoot() const;
- virtual bool isRoot() const;
- virtual bool isRootEdit() const;
-
- const LLVector3& getPosition() const { return mPosition; }
- const LLVector3& getScale() const { return mScale; }
- const LLQuaternion& getRotation() const { return mRotation; }
- const LLVector3& getPositionW() const { return mWorldPosition; }
- const LLQuaternion& getWorldRotation() const { return mWorldRotation; }
- const LLVector3& getWorldPosition() const { return mWorldPosition; }
-};
-
-class LLXformMatrix : public LLXform
-{
-public:
- LLXformMatrix() : LLXform() {};
- virtual ~LLXformMatrix();
-
- const LLMatrix4& getWorldMatrix() const { return mWorldMatrix; }
- void setWorldMatrix (const LLMatrix4& mat) { mWorldMatrix = mat; }
-
- void init()
- {
- mWorldMatrix.setIdentity();
- mMin.clearVec();
- mMax.clearVec();
-
- LLXform::init();
- }
-
- void update();
- void updateMatrix(bool update_bounds = true);
- void getMinMax(LLVector3& min,LLVector3& max) const;
-
-protected:
- LLMatrix4 mWorldMatrix;
- LLVector3 mMin;
- LLVector3 mMax;
-
-};
-
-bool LLXform::setParent(LLXform* parent)
-{
- // Validate and make sure we're not creating a loop
- if (parent == mParent)
- {
- return true;
- }
- if (parent)
- {
- LLXform *cur_par = parent->mParent;
- while (cur_par)
- {
- if (cur_par == this)
- {
- //warn("LLXform::setParent Creating loop when setting parent!");
- return false;
- }
- cur_par = cur_par->mParent;
- }
- }
- mParent = parent;
- return true;
-}
-
-void LLXform::setPosition(const LLVector3& pos)
-{
- setChanged(TRANSLATED);
- if (pos.isFinite())
- mPosition = pos;
- else
- {
- mPosition.clearVec();
- warn("Non Finite in LLXform::setPosition(LLVector3)");
- }
-}
-
-void LLXform::setPosition(const F32 x, const F32 y, const F32 z)
-{
- setChanged(TRANSLATED);
- if (llfinite(x) && llfinite(y) && llfinite(z))
- mPosition.setVec(x,y,z);
- else
- {
- mPosition.clearVec();
- warn("Non Finite in LLXform::setPosition(F32,F32,F32)");
- }
-}
-
-void LLXform::setPositionX(const F32 x)
-{
- setChanged(TRANSLATED);
- if (llfinite(x))
- mPosition.mV[VX] = x;
- else
- {
- mPosition.mV[VX] = 0.f;
- warn("Non Finite in LLXform::setPositionX");
- }
-}
-
-void LLXform::setPositionY(const F32 y)
-{
- setChanged(TRANSLATED);
- if (llfinite(y))
- mPosition.mV[VY] = y;
- else
- {
- mPosition.mV[VY] = 0.f;
- warn("Non Finite in LLXform::setPositionY");
- }
-}
-
-void LLXform::setPositionZ(const F32 z)
-{
- setChanged(TRANSLATED);
- if (llfinite(z))
- mPosition.mV[VZ] = z;
- else
- {
- mPosition.mV[VZ] = 0.f;
- warn("Non Finite in LLXform::setPositionZ");
- }
-}
-
-void LLXform::addPosition(const LLVector3& pos)
-{
- setChanged(TRANSLATED);
- if (pos.isFinite())
- mPosition += pos;
- else
- warn("Non Finite in LLXform::addPosition");
-}
-
-void LLXform::setScale(const LLVector3& scale)
-{
- setChanged(SCALED);
- if (scale.isFinite())
- mScale = scale;
- else
- {
- mScale.setVec(1.f, 1.f, 1.f);
- warn("Non Finite in LLXform::setScale");
- }
-}
-void LLXform::setScale(const F32 x, const F32 y, const F32 z)
-{
- setChanged(SCALED);
- if (llfinite(x) && llfinite(y) && llfinite(z))
- mScale.setVec(x,y,z);
- else
- {
- mScale.setVec(1.f, 1.f, 1.f);
- warn("Non Finite in LLXform::setScale");
- }
-}
-void LLXform::setRotation(const LLQuaternion& rot)
-{
- setChanged(ROTATED);
- if (rot.isFinite())
- mRotation = rot;
- else
- {
- mRotation.loadIdentity();
- warn("Non Finite in LLXform::setRotation");
- }
-}
-void LLXform::setRotation(const F32 x, const F32 y, const F32 z)
-{
- setChanged(ROTATED);
- if (llfinite(x) && llfinite(y) && llfinite(z))
- {
- mRotation.setQuat(x,y,z);
- }
- else
- {
- mRotation.loadIdentity();
- warn("Non Finite in LLXform::setRotation");
- }
-}
-void LLXform::setRotation(const F32 x, const F32 y, const F32 z, const F32 s)
-{
- setChanged(ROTATED);
- if (llfinite(x) && llfinite(y) && llfinite(z) && llfinite(s))
- {
- mRotation.mQ[VX] = x; mRotation.mQ[VY] = y; mRotation.mQ[VZ] = z; mRotation.mQ[VS] = s;
- }
- else
- {
- mRotation.loadIdentity();
- warn("Non Finite in LLXform::setRotation");
- }
-}
-
-#endif
+/** + * @file xform.h + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_XFORM_H +#define LL_XFORM_H + +#include "v3math.h" +#include "m4math.h" +#include "llquaternion.h" + +constexpr F32 MAX_OBJECT_Z = 4096.f; // should match REGION_HEIGHT_METERS, Pre-havok4: 768.f +constexpr F32 MIN_OBJECT_Z = -256.f; +constexpr F32 DEFAULT_MAX_PRIM_SCALE = 64.f; +constexpr F32 DEFAULT_MAX_PRIM_SCALE_NO_MESH = 10.f; +constexpr F32 MIN_PRIM_SCALE = 0.01f; +constexpr F32 MAX_PRIM_SCALE = 65536.f; // something very high but not near FLT_MAX + +class LLXform +{ +protected: + LLVector3 mPosition; + LLQuaternion mRotation; + LLVector3 mScale; + + //RN: TODO: move these world transform members to LLXformMatrix + // as they are *never* updated or accessed in the base class + LLVector3 mWorldPosition; + LLQuaternion mWorldRotation; + + LLXform* mParent; + U32 mChanged; + + bool mScaleChildOffset; + +public: + typedef enum e_changed_flags + { + UNCHANGED = 0x00, + TRANSLATED = 0x01, + ROTATED = 0x02, + SCALED = 0x04, + SHIFTED = 0x08, + GEOMETRY = 0x10, + TEXTURE = 0x20, + MOVED = TRANSLATED|ROTATED|SCALED, + SILHOUETTE = 0x40, + ALL_CHANGED = 0x7f + }EChangedFlags; + + void init() + { + mParent = NULL; + mChanged = UNCHANGED; + mPosition.setVec(0,0,0); + mRotation.loadIdentity(); + mScale. setVec(1,1,1); + mWorldPosition.clearVec(); + mWorldRotation.loadIdentity(); + mScaleChildOffset = false; + } + + LLXform(); + virtual ~LLXform(); + + void getLocalMat4(LLMatrix4 &mat) const { mat.initAll(mScale, mRotation, mPosition); } + + inline bool setParent(LLXform *parent); + + inline void setPosition(const LLVector3& pos); + inline void setPosition(const F32 x, const F32 y, const F32 z); + inline void setPositionX(const F32 x); + inline void setPositionY(const F32 y); + inline void setPositionZ(const F32 z); + inline void addPosition(const LLVector3& pos); + + + inline void setScale(const LLVector3& scale); + inline void setScale(const F32 x, const F32 y, const F32 z); + inline void setRotation(const LLQuaternion& rot); + inline void setRotation(const F32 x, const F32 y, const F32 z); + inline void setRotation(const F32 x, const F32 y, const F32 z, const F32 s); + + // Above functions must be inline for speed, but also + // need to emit warnings. LL_WARNS() causes inline LLError::CallSite + // static objects that make more work for the linker. + // Avoid inline LL_WARNS() by calling this function. + void warn(const char* const msg); + + void setChanged(const U32 bits) { mChanged |= bits; } + bool isChanged() const { return mChanged; } + bool isChanged(const U32 bits) const { return mChanged & bits; } + void clearChanged() { mChanged = 0; } + void clearChanged(U32 bits) { mChanged &= ~bits; } + + void setScaleChildOffset(bool scale) { mScaleChildOffset = scale; } + bool getScaleChildOffset() { return mScaleChildOffset; } + + LLXform* getParent() const { return mParent; } + LLXform* getRoot() const; + virtual bool isRoot() const; + virtual bool isRootEdit() const; + + const LLVector3& getPosition() const { return mPosition; } + const LLVector3& getScale() const { return mScale; } + const LLQuaternion& getRotation() const { return mRotation; } + const LLVector3& getPositionW() const { return mWorldPosition; } + const LLQuaternion& getWorldRotation() const { return mWorldRotation; } + const LLVector3& getWorldPosition() const { return mWorldPosition; } +}; + +class LLXformMatrix : public LLXform +{ +public: + LLXformMatrix() : LLXform() {}; + virtual ~LLXformMatrix(); + + const LLMatrix4& getWorldMatrix() const { return mWorldMatrix; } + void setWorldMatrix (const LLMatrix4& mat) { mWorldMatrix = mat; } + + void init() + { + mWorldMatrix.setIdentity(); + mMin.clearVec(); + mMax.clearVec(); + + LLXform::init(); + } + + void update(); + void updateMatrix(bool update_bounds = true); + void getMinMax(LLVector3& min,LLVector3& max) const; + +protected: + LLMatrix4 mWorldMatrix; + LLVector3 mMin; + LLVector3 mMax; + +}; + +bool LLXform::setParent(LLXform* parent) +{ + // Validate and make sure we're not creating a loop + if (parent == mParent) + { + return true; + } + if (parent) + { + LLXform *cur_par = parent->mParent; + while (cur_par) + { + if (cur_par == this) + { + //warn("LLXform::setParent Creating loop when setting parent!"); + return false; + } + cur_par = cur_par->mParent; + } + } + mParent = parent; + return true; +} + +void LLXform::setPosition(const LLVector3& pos) +{ + setChanged(TRANSLATED); + if (pos.isFinite()) + mPosition = pos; + else + { + mPosition.clearVec(); + warn("Non Finite in LLXform::setPosition(LLVector3)"); + } +} + +void LLXform::setPosition(const F32 x, const F32 y, const F32 z) +{ + setChanged(TRANSLATED); + if (llfinite(x) && llfinite(y) && llfinite(z)) + mPosition.setVec(x,y,z); + else + { + mPosition.clearVec(); + warn("Non Finite in LLXform::setPosition(F32,F32,F32)"); + } +} + +void LLXform::setPositionX(const F32 x) +{ + setChanged(TRANSLATED); + if (llfinite(x)) + mPosition.mV[VX] = x; + else + { + mPosition.mV[VX] = 0.f; + warn("Non Finite in LLXform::setPositionX"); + } +} + +void LLXform::setPositionY(const F32 y) +{ + setChanged(TRANSLATED); + if (llfinite(y)) + mPosition.mV[VY] = y; + else + { + mPosition.mV[VY] = 0.f; + warn("Non Finite in LLXform::setPositionY"); + } +} + +void LLXform::setPositionZ(const F32 z) +{ + setChanged(TRANSLATED); + if (llfinite(z)) + mPosition.mV[VZ] = z; + else + { + mPosition.mV[VZ] = 0.f; + warn("Non Finite in LLXform::setPositionZ"); + } +} + +void LLXform::addPosition(const LLVector3& pos) +{ + setChanged(TRANSLATED); + if (pos.isFinite()) + mPosition += pos; + else + warn("Non Finite in LLXform::addPosition"); +} + +void LLXform::setScale(const LLVector3& scale) +{ + setChanged(SCALED); + if (scale.isFinite()) + mScale = scale; + else + { + mScale.setVec(1.f, 1.f, 1.f); + warn("Non Finite in LLXform::setScale"); + } +} +void LLXform::setScale(const F32 x, const F32 y, const F32 z) +{ + setChanged(SCALED); + if (llfinite(x) && llfinite(y) && llfinite(z)) + mScale.setVec(x,y,z); + else + { + mScale.setVec(1.f, 1.f, 1.f); + warn("Non Finite in LLXform::setScale"); + } +} +void LLXform::setRotation(const LLQuaternion& rot) +{ + setChanged(ROTATED); + if (rot.isFinite()) + mRotation = rot; + else + { + mRotation.loadIdentity(); + warn("Non Finite in LLXform::setRotation"); + } +} +void LLXform::setRotation(const F32 x, const F32 y, const F32 z) +{ + setChanged(ROTATED); + if (llfinite(x) && llfinite(y) && llfinite(z)) + { + mRotation.setQuat(x,y,z); + } + else + { + mRotation.loadIdentity(); + warn("Non Finite in LLXform::setRotation"); + } +} +void LLXform::setRotation(const F32 x, const F32 y, const F32 z, const F32 s) +{ + setChanged(ROTATED); + if (llfinite(x) && llfinite(y) && llfinite(z) && llfinite(s)) + { + mRotation.mQ[VX] = x; mRotation.mQ[VY] = y; mRotation.mQ[VZ] = z; mRotation.mQ[VS] = s; + } + else + { + mRotation.loadIdentity(); + warn("Non Finite in LLXform::setRotation"); + } +} + +#endif |