summaryrefslogtreecommitdiff
path: root/indra/llmath
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 21:25:21 +0200
committerAndrey Lihatskiy <alihatskiy@productengine.com>2024-05-22 22:40:26 +0300
commite2e37cced861b98de8c1a7c9c0d3a50d2d90e433 (patch)
tree1bb897489ce524986f6196201c10ac0d8861aa5f /indra/llmath
parent069ea06848f766466f1a281144c82a0f2bd79f3a (diff)
Fix line endlings
Diffstat (limited to 'indra/llmath')
-rw-r--r--indra/llmath/llbbox.cpp356
-rw-r--r--indra/llmath/llbbox.h202
-rw-r--r--indra/llmath/llcoordframe.h348
-rw-r--r--indra/llmath/llinterp.h850
-rw-r--r--indra/llmath/llmath.h1138
-rw-r--r--indra/llmath/lloctree.h1716
-rw-r--r--indra/llmath/llquaternion.cpp1962
-rw-r--r--indra/llmath/llquaternion.h1234
-rw-r--r--indra/llmath/llrect.h596
-rw-r--r--indra/llmath/llsphere.cpp740
-rw-r--r--indra/llmath/llsphere.h154
-rw-r--r--indra/llmath/lltreenode.h250
-rw-r--r--indra/llmath/llvolume.cpp14608
-rw-r--r--indra/llmath/llvolume.h2304
-rw-r--r--indra/llmath/llvolumemgr.cpp816
-rw-r--r--indra/llmath/llvolumemgr.h224
-rw-r--r--indra/llmath/llvolumeoctree.cpp546
-rw-r--r--indra/llmath/m3math.cpp1180
-rw-r--r--indra/llmath/m4math.cpp1746
-rw-r--r--indra/llmath/raytrace.cpp2538
-rw-r--r--indra/llmath/raytrace.h458
-rw-r--r--indra/llmath/tests/llbbox_test.cpp734
-rw-r--r--indra/llmath/tests/llrect_test.cpp1052
-rw-r--r--indra/llmath/tests/mathmisc_test.cpp1446
-rw-r--r--indra/llmath/tests/v2math_test.cpp896
-rw-r--r--indra/llmath/tests/v3color_test.cpp618
-rw-r--r--indra/llmath/tests/v3dmath_test.cpp1062
-rw-r--r--indra/llmath/tests/v3math_test.cpp1170
-rw-r--r--indra/llmath/tests/v4coloru_test.cpp672
-rw-r--r--indra/llmath/tests/v4math_test.cpp766
-rw-r--r--indra/llmath/tests/xform_test.cpp490
-rw-r--r--indra/llmath/v2math.cpp252
-rw-r--r--indra/llmath/v2math.h880
-rw-r--r--indra/llmath/v3dmath.cpp294
-rw-r--r--indra/llmath/v3dmath.h1060
-rw-r--r--indra/llmath/v3math.cpp826
-rw-r--r--indra/llmath/v3math.h1224
-rw-r--r--indra/llmath/v4color.cpp1476
-rw-r--r--indra/llmath/v4color.h1446
-rw-r--r--indra/llmath/v4coloru.cpp240
-rw-r--r--indra/llmath/v4coloru.h1170
-rw-r--r--indra/llmath/v4math.cpp240
-rw-r--r--indra/llmath/v4math.h1098
-rw-r--r--indra/llmath/xform.cpp238
-rw-r--r--indra/llmath/xform.h630
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 &center, 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 &center, 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 &params)
-{
- 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 &params)
-{
- 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 &params, 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 &params) const
-{
- return ( (getPathParams() == params.getPathParams()) &&
- (getProfileParams() == params.getProfileParams()) &&
- (mSculptID == params.mSculptID) &&
- (mSculptType == params.mSculptType) );
-}
-
-bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
-{
- return ( (getPathParams() != params.getPathParams()) ||
- (getProfileParams() != params.getProfileParams()) ||
- (mSculptID != params.mSculptID) ||
- (mSculptType != params.mSculptType) );
-}
-
-bool LLVolumeParams::operator<(const LLVolumeParams &params) 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 &params)
-{
- 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 &params)
+{
+ 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 &params)
+{
+ 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 &params, 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 &params) const
+{
+ return ( (getPathParams() == params.getPathParams()) &&
+ (getProfileParams() == params.getProfileParams()) &&
+ (mSculptID == params.mSculptID) &&
+ (mSculptType == params.mSculptType) );
+}
+
+bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
+{
+ return ( (getPathParams() != params.getPathParams()) ||
+ (getProfileParams() != params.getProfileParams()) ||
+ (mSculptID != params.mSculptID) ||
+ (mSculptType != params.mSculptType) );
+}
+
+bool LLVolumeParams::operator<(const LLVolumeParams &params) 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 &params)
+{
+ 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 &params) const;
- bool operator!=(const LLProfileParams &params) const;
- bool operator<(const LLProfileParams &params) const;
-
- void copyParams(const LLProfileParams &params);
-
- 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 &params) const
-{
- return
- (getCurveType() == params.getCurveType()) &&
- (getBegin() == params.getBegin()) &&
- (getEnd() == params.getEnd()) &&
- (getHollow() == params.getHollow());
-}
-
-inline bool LLProfileParams::operator!=(const LLProfileParams &params) const
-{
- return
- (getCurveType() != params.getCurveType()) ||
- (getBegin() != params.getBegin()) ||
- (getEnd() != params.getEnd()) ||
- (getHollow() != params.getHollow());
-}
-
-
-inline bool LLProfileParams::operator<(const LLProfileParams &params) 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 &params) const;
- bool operator!=(const LLPathParams &params) const;
- bool operator<(const LLPathParams &params) const;
-
- void copyParams(const LLPathParams &params);
-
- 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 &params) 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 &params) 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 &params) 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 &params) const;
- bool operator!=(const LLVolumeParams &params) const;
- bool operator<(const LLVolumeParams &params) const;
-
-
- void copyParams(const LLVolumeParams &params);
-
- 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 &params, 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 &params) const;
+ bool operator!=(const LLProfileParams &params) const;
+ bool operator<(const LLProfileParams &params) const;
+
+ void copyParams(const LLProfileParams &params);
+
+ 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 &params) const
+{
+ return
+ (getCurveType() == params.getCurveType()) &&
+ (getBegin() == params.getBegin()) &&
+ (getEnd() == params.getEnd()) &&
+ (getHollow() == params.getHollow());
+}
+
+inline bool LLProfileParams::operator!=(const LLProfileParams &params) const
+{
+ return
+ (getCurveType() != params.getCurveType()) ||
+ (getBegin() != params.getBegin()) ||
+ (getEnd() != params.getEnd()) ||
+ (getHollow() != params.getHollow());
+}
+
+
+inline bool LLProfileParams::operator<(const LLProfileParams &params) 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 &params) const;
+ bool operator!=(const LLPathParams &params) const;
+ bool operator<(const LLPathParams &params) const;
+
+ void copyParams(const LLPathParams &params);
+
+ 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 &params) 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 &params) 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 &params) 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 &params) const;
+ bool operator!=(const LLVolumeParams &params) const;
+ bool operator<(const LLVolumeParams &params) const;
+
+
+ void copyParams(const LLVolumeParams &params);
+
+ 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 &params, 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 &params)
- : 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 &params)
+ : 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 &params);
- ~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 &params);
+ ~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 &copy)
-{
- 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 &copy)
+{
+ 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 &copy)
-{
- 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 &copy)
+{
+ 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