diff options
Diffstat (limited to 'indra/llmath')
97 files changed, 32388 insertions, 32388 deletions
diff --git a/indra/llmath/camera.h b/indra/llmath/camera.h index 26f3c3d19f..14a08c5985 100644 --- a/indra/llmath/camera.h +++ b/indra/llmath/camera.h @@ -1,25 +1,25 @@ -/** +/** * @file camera.h * @brief Legacy wrapper header. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llmath/coordframe.h b/indra/llmath/coordframe.h index 271bcb433c..e7395b1212 100644 --- a/indra/llmath/coordframe.h +++ b/indra/llmath/coordframe.h @@ -1,25 +1,25 @@ -/** +/** * @file coordframe.h * @brief Legacy wrapper header. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llmath/llbbox.cpp b/indra/llmath/llbbox.cpp index 395992e68f..728b222d09 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 8ae5f221f7..988b95c54d 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/llbboxlocal.cpp b/indra/llmath/llbboxlocal.cpp index bf0c1a7b93..cbb7a1b481 100644 --- a/indra/llmath/llbboxlocal.cpp +++ b/indra/llmath/llbboxlocal.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llbboxlocal.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$ */ @@ -31,25 +31,25 @@ void LLBBoxLocal::addPoint(const LLVector3& p) { - mMin.mV[VX] = llmin( p.mV[VX], mMin.mV[VX] ); - mMin.mV[VY] = llmin( p.mV[VY], mMin.mV[VY] ); - mMin.mV[VZ] = llmin( p.mV[VZ], mMin.mV[VZ] ); - mMax.mV[VX] = llmax( p.mV[VX], mMax.mV[VX] ); - mMax.mV[VY] = llmax( p.mV[VY], mMax.mV[VY] ); - mMax.mV[VZ] = llmax( p.mV[VZ], mMax.mV[VZ] ); + mMin.mV[VX] = llmin( p.mV[VX], mMin.mV[VX] ); + mMin.mV[VY] = llmin( p.mV[VY], mMin.mV[VY] ); + mMin.mV[VZ] = llmin( p.mV[VZ], mMin.mV[VZ] ); + mMax.mV[VX] = llmax( p.mV[VX], mMax.mV[VX] ); + mMax.mV[VY] = llmax( p.mV[VY], mMax.mV[VY] ); + mMax.mV[VZ] = llmax( p.mV[VZ], mMax.mV[VZ] ); } void LLBBoxLocal::expand( F32 delta ) { - mMin.mV[VX] -= delta; - mMin.mV[VY] -= delta; - mMin.mV[VZ] -= delta; - mMax.mV[VX] += delta; - mMax.mV[VY] += delta; - mMax.mV[VZ] += delta; + mMin.mV[VX] -= delta; + mMin.mV[VY] -= delta; + mMin.mV[VZ] -= delta; + mMax.mV[VX] += delta; + mMax.mV[VY] += delta; + mMax.mV[VZ] += delta; } LLBBoxLocal operator*(const LLBBoxLocal &a, const LLMatrix4 &b) { - return LLBBoxLocal( a.mMin * b, a.mMax * b ); + return LLBBoxLocal( a.mMin * b, a.mMax * b ); } diff --git a/indra/llmath/llbboxlocal.h b/indra/llmath/llbboxlocal.h index defb899248..e215e55460 100644 --- a/indra/llmath/llbboxlocal.h +++ b/indra/llmath/llbboxlocal.h @@ -1,25 +1,25 @@ -/** +/** * @file llbboxlocal.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$ */ @@ -34,29 +34,29 @@ class LLMatrix4; class LLBBoxLocal { public: - LLBBoxLocal() {} - LLBBoxLocal( const LLVector3& min, const LLVector3& max ) : mMin( min ), mMax( max ) {} - // Default copy constructor is OK. + LLBBoxLocal() {} + LLBBoxLocal( const LLVector3& min, const LLVector3& max ) : mMin( min ), mMax( max ) {} + // Default copy constructor is OK. - const LLVector3& getMin() const { return mMin; } - void setMin( const LLVector3& min ) { mMin = min; } + const LLVector3& getMin() const { return mMin; } + void setMin( const LLVector3& min ) { mMin = min; } - const LLVector3& getMax() const { return mMax; } - void setMax( const LLVector3& max ) { mMax = max; } + const LLVector3& getMax() const { return mMax; } + void setMax( const LLVector3& max ) { mMax = max; } - LLVector3 getCenter() const { return (mMax - mMin) * 0.5f + mMin; } - LLVector3 getExtent() const { return mMax - mMin; } + LLVector3 getCenter() const { return (mMax - mMin) * 0.5f + mMin; } + LLVector3 getExtent() const { return mMax - mMin; } - void addPoint(const LLVector3& p); - void addBBox(const LLBBoxLocal& b) { addPoint( b.mMin ); addPoint( b.mMax ); } + void addPoint(const LLVector3& p); + void addBBox(const LLBBoxLocal& b) { addPoint( b.mMin ); addPoint( b.mMax ); } - void expand( F32 delta ); + void expand( F32 delta ); - friend LLBBoxLocal operator*(const LLBBoxLocal& a, const LLMatrix4& b); + friend LLBBoxLocal operator*(const LLBBoxLocal& a, const LLMatrix4& b); private: - LLVector3 mMin; - LLVector3 mMax; + LLVector3 mMin; + LLVector3 mMax; }; LLBBoxLocal operator*(const LLBBoxLocal &a, const LLMatrix4 &b); diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp index edc6986cc9..e060b5df58 100644 --- a/indra/llmath/llcalc.cpp +++ b/indra/llmath/llcalc.cpp @@ -4,21 +4,21 @@ * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2008, 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$ * @@ -71,16 +71,16 @@ LLCalc* LLCalc::sInstance = NULL; LLCalc::LLCalc() : mLastErrorPos(0) { - // Init table of constants - mConstants["PI"] = F_PI; - mConstants["TWO_PI"] = F_TWO_PI; - mConstants["PI_BY_TWO"] = F_PI_BY_TWO; - mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI; - mConstants["SQRT2"] = F_SQRT2; - mConstants["SQRT3"] = F_SQRT3; - mConstants["DEG_TO_RAD"] = DEG_TO_RAD; - mConstants["RAD_TO_DEG"] = RAD_TO_DEG; - mConstants["GRAVITY"] = GRAVITY; + // Init table of constants + mConstants["PI"] = F_PI; + mConstants["TWO_PI"] = F_TWO_PI; + mConstants["PI_BY_TWO"] = F_PI_BY_TWO; + mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI; + mConstants["SQRT2"] = F_SQRT2; + mConstants["SQRT3"] = F_SQRT3; + mConstants["DEG_TO_RAD"] = DEG_TO_RAD; + mConstants["RAD_TO_DEG"] = RAD_TO_DEG; + mConstants["GRAVITY"] = GRAVITY; } LLCalc::~LLCalc() @@ -90,73 +90,73 @@ LLCalc::~LLCalc() //static void LLCalc::cleanUp() { - delete sInstance; - sInstance = NULL; + delete sInstance; + sInstance = NULL; } //static LLCalc* LLCalc::getInstance() { - if (!sInstance) sInstance = new LLCalc(); - return sInstance; + if (!sInstance) sInstance = new LLCalc(); + return sInstance; } void LLCalc::setVar(const std::string& name, const F32& value) { - mVariables[name] = value; + mVariables[name] = value; } void LLCalc::clearVar(const std::string& name) { - mVariables.erase(name); + mVariables.erase(name); } void LLCalc::clearAllVariables() { - mVariables.clear(); + mVariables.clear(); } /* void LLCalc::updateVariables(LLSD& vars) { - LLSD::map_iterator cIt = vars.beginMap(); - for(; cIt != vars.endMap(); cIt++) - { - setVar(cIt->first, (F32)(LLSD::Real)cIt->second); - } + LLSD::map_iterator cIt = vars.beginMap(); + for(; cIt != vars.endMap(); cIt++) + { + setVar(cIt->first, (F32)(LLSD::Real)cIt->second); + } } */ bool LLCalc::evalString(const std::string& expression, F32& result) { - std::string expr_upper = expression; - LLStringUtil::toUpper(expr_upper); - - LLCalcParser calc(result, &mConstants, &mVariables); - - mLastErrorPos = 0; - std::string::iterator start = expr_upper.begin(); - parse_info<std::string::iterator> info; - - try - { - info = parse(start, expr_upper.end(), calc, space_p); - LL_DEBUGS() << "Math expression: " << expression << " = " << result << LL_ENDL; - } - catch(parser_error<std::string, std::string::iterator> &e) - { - mLastErrorPos = e.where - expr_upper.begin(); - - LL_INFOS() << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << LL_ENDL; - return false; - } - - if (!info.full) - { - mLastErrorPos = info.stop - expr_upper.begin(); - LL_INFOS() << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << LL_ENDL; - return false; - } - - return true; + std::string expr_upper = expression; + LLStringUtil::toUpper(expr_upper); + + LLCalcParser calc(result, &mConstants, &mVariables); + + mLastErrorPos = 0; + std::string::iterator start = expr_upper.begin(); + parse_info<std::string::iterator> info; + + try + { + info = parse(start, expr_upper.end(), calc, space_p); + LL_DEBUGS() << "Math expression: " << expression << " = " << result << LL_ENDL; + } + catch(parser_error<std::string, std::string::iterator> &e) + { + mLastErrorPos = e.where - expr_upper.begin(); + + LL_INFOS() << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << LL_ENDL; + return false; + } + + if (!info.full) + { + mLastErrorPos = info.stop - expr_upper.begin(); + LL_INFOS() << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << LL_ENDL; + return false; + } + + return true; } diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h index ceb9dce585..09672eb13b 100644 --- a/indra/llmath/llcalc.h +++ b/indra/llmath/llcalc.h @@ -4,21 +4,21 @@ * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2008, 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$ * @@ -33,68 +33,68 @@ class LLCalc { public: - LLCalc(); - ~LLCalc(); + LLCalc(); + ~LLCalc(); + + // Variable name constants + static const char* X_POS; + static const char* Y_POS; + static const char* Z_POS; + static const char* X_SCALE; + static const char* Y_SCALE; + static const char* Z_SCALE; + static const char* X_ROT; + static const char* Y_ROT; + static const char* Z_ROT; + static const char* HOLLOW; + static const char* CUT_BEGIN; + static const char* CUT_END; + static const char* PATH_BEGIN; + static const char* PATH_END; + static const char* TWIST_BEGIN; + static const char* TWIST_END; + static const char* X_SHEAR; + static const char* Y_SHEAR; + static const char* X_TAPER; + static const char* Y_TAPER; + static const char* RADIUS_OFFSET; + static const char* REVOLUTIONS; + static const char* SKEW; + static const char* X_HOLE; + static const char* Y_HOLE; + static const char* TEX_U_SCALE; + static const char* TEX_V_SCALE; + static const char* TEX_U_OFFSET; + static const char* TEX_V_OFFSET; + static const char* TEX_ROTATION; + static const char* TEX_TRANSPARENCY; + static const char* TEX_GLOW; - // Variable name constants - static const char* X_POS; - static const char* Y_POS; - static const char* Z_POS; - static const char* X_SCALE; - static const char* Y_SCALE; - static const char* Z_SCALE; - static const char* X_ROT; - static const char* Y_ROT; - static const char* Z_ROT; - static const char* HOLLOW; - static const char* CUT_BEGIN; - static const char* CUT_END; - static const char* PATH_BEGIN; - static const char* PATH_END; - static const char* TWIST_BEGIN; - static const char* TWIST_END; - static const char* X_SHEAR; - static const char* Y_SHEAR; - static const char* X_TAPER; - static const char* Y_TAPER; - static const char* RADIUS_OFFSET; - static const char* REVOLUTIONS; - static const char* SKEW; - static const char* X_HOLE; - static const char* Y_HOLE; - static const char* TEX_U_SCALE; - static const char* TEX_V_SCALE; - static const char* TEX_U_OFFSET; - static const char* TEX_V_OFFSET; - static const char* TEX_ROTATION; - static const char* TEX_TRANSPARENCY; - static const char* TEX_GLOW; + void setVar(const std::string& name, const F32& value); + void clearVar(const std::string& name); + void clearAllVariables(); +// void updateVariables(LLSD& vars); - void setVar(const std::string& name, const F32& value); - void clearVar(const std::string& name); - void clearAllVariables(); -// void updateVariables(LLSD& vars); + bool evalString(const std::string& expression, F32& result); + std::string::size_type getLastErrorPos() { return mLastErrorPos; } - bool evalString(const std::string& expression, F32& result); - std::string::size_type getLastErrorPos() { return mLastErrorPos; } - - static LLCalc* getInstance(); - static void cleanUp(); + static LLCalc* getInstance(); + static void cleanUp(); + + typedef std::map<std::string, F32> calc_map_t; - typedef std::map<std::string, F32> calc_map_t; - private: - std::string::size_type mLastErrorPos; - - calc_map_t mConstants; - calc_map_t mVariables; - - // *TODO: Add support for storing user defined variables, and stored functions. - // Will need UI work, and a means to save them between sessions. -// calc_map_t mUserVariables; - - // "There shall be only one" - static LLCalc* sInstance; + std::string::size_type mLastErrorPos; + + calc_map_t mConstants; + calc_map_t mVariables; + + // *TODO: Add support for storing user defined variables, and stored functions. + // Will need UI work, and a means to save them between sessions. +// calc_map_t mUserVariables; + + // "There shall be only one" + static LLCalc* sInstance; }; #endif // LL_CALC_H diff --git a/indra/llmath/llcalcparser.cpp b/indra/llmath/llcalcparser.cpp index b4ca320659..7399c368f7 100644 --- a/indra/llmath/llcalcparser.cpp +++ b/indra/llmath/llcalcparser.cpp @@ -4,21 +4,21 @@ * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2008, 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$ * @@ -31,33 +31,33 @@ using namespace boost::spirit::classic; F32 LLCalcParser::lookup(const std::string::iterator& start, const std::string::iterator& end) const { - LLCalc::calc_map_t::iterator iter; + LLCalc::calc_map_t::iterator iter; + + std::string name(start, end); + + if (mConstants) + { + iter = mConstants->find(name); + if (iter != mConstants->end()) + { + return (*iter).second; + } + } + else + { + // This should never happen! + throw_(end, std::string("Missing constants table")); + } + + if (mVariables) + { + iter = mVariables->find(name); + if (iter != mVariables->end()) + { + return (*iter).second; + } + } - std::string name(start, end); - - if (mConstants) - { - iter = mConstants->find(name); - if (iter != mConstants->end()) - { - return (*iter).second; - } - } - else - { - // This should never happen! - throw_(end, std::string("Missing constants table")); - } - - if (mVariables) - { - iter = mVariables->find(name); - if (iter != mVariables->end()) - { - return (*iter).second; - } - } - - throw_(end, std::string("Unknown symbol " + name)); - return 0.f; + throw_(end, std::string("Unknown symbol " + name)); + return 0.f; } diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index dff5bf3af3..e8fdcc9ae3 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -4,21 +4,21 @@ * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2008, 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$ * @@ -40,149 +40,149 @@ using namespace boost::spirit::classic; struct LLCalcParser : grammar<LLCalcParser> { - LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) : - mResult(result), mConstants(constants), mVariables(vars) {}; - - struct value_closure : closure<value_closure, F32> - { - member1 value; - }; - - template <typename ScannerT> - struct definition - { - // Rule declarations - rule<ScannerT> statement, identifier; - rule<ScannerT, value_closure::context_t> expression, term, - power, - unary_expr, - factor, - unary_func, - binary_func, - group; - - // start() should return the starting symbol - rule<ScannerT> const& start() const { return statement; } - - definition(LLCalcParser const& self) - { - using namespace phoenix; - - assertion<std::string> assert_domain("Domain error"); -// assertion<std::string> assert_symbol("Unknown symbol"); - assertion<std::string> assert_syntax("Syntax error"); - - identifier = - lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')] - ; - - group = - '(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')')) - ; - - unary_func = - ((str_p("SIN") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_sin)(self,arg1)]) | - (str_p("COS") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_cos)(self,arg1)]) | - (str_p("TAN") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_tan)(self,arg1)]) | - (str_p("ASIN") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_asin)(self,arg1)]) | - (str_p("ACOS") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_acos)(self,arg1)]) | - (str_p("ATAN") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_atan)(self,arg1)]) | - (str_p("SQRT") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_sqrt)(self,arg1)]) | - (str_p("LOG") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_log)(self,arg1)]) | - (str_p("EXP") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_exp)(self,arg1)]) | - (str_p("ABS") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_fabs)(self,arg1)]) | - (str_p("FLR") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_floor)(self,arg1)]) | - (str_p("CEIL") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_ceil)(self,arg1)]) - ) >> assert_syntax(ch_p(')')) - ; - - binary_func = - ((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >> - expression[binary_func.value = phoenix::bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) | - (str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >> - expression[binary_func.value = phoenix::bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) | - (str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >> - expression[binary_func.value = phoenix::bind(&LLCalcParser::_max)(self, binary_func.value, arg1)]) - ) >> assert_syntax(ch_p(')')) - ; - - // *TODO: Localisation of the decimal point? - // Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate - // for the current locale. However to do that here could clash with using - // the comma as a separator when passing arguments to functions. - factor = - (ureal_p[factor.value = arg1] | - group[factor.value = arg1] | - unary_func[factor.value = arg1] | - binary_func[factor.value = arg1] | - // Lookup throws an Unknown Symbol error if it is unknown, while this works fine, - // would be "neater" to handle symbol lookup from here with an assertive parser. -// constants_p[factor.value = arg1]| - identifier[factor.value = phoenix::bind(&LLCalcParser::lookup)(self, arg1, arg2)] - ) >> - // Detect and throw math errors. - assert_domain(eps_p(phoenix::bind(&LLCalcParser::checkNaN)(self, factor.value))) - ; - - unary_expr = - !ch_p('+') >> factor[unary_expr.value = arg1] | - '-' >> factor[unary_expr.value = -arg1] - ; - - power = - unary_expr[power.value = arg1] >> - *('^' >> assert_syntax(unary_expr[power.value = phoenix::bind(&powf)(power.value, arg1)])) - ; - - term = - power[term.value = arg1] >> - *(('*' >> assert_syntax(power[term.value *= arg1])) | - ('/' >> assert_syntax(power[term.value /= arg1])) | - ('%' >> assert_syntax(power[term.value = phoenix::bind(&fmodf)(term.value, arg1)])) - ) - ; - - expression = - assert_syntax(term[expression.value = arg1]) >> - *(('+' >> assert_syntax(term[expression.value += arg1])) | - ('-' >> assert_syntax(term[expression.value -= arg1])) - ) - ; - - statement = - !ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p) - ; - } - }; - + LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) : + mResult(result), mConstants(constants), mVariables(vars) {}; + + struct value_closure : closure<value_closure, F32> + { + member1 value; + }; + + template <typename ScannerT> + struct definition + { + // Rule declarations + rule<ScannerT> statement, identifier; + rule<ScannerT, value_closure::context_t> expression, term, + power, + unary_expr, + factor, + unary_func, + binary_func, + group; + + // start() should return the starting symbol + rule<ScannerT> const& start() const { return statement; } + + definition(LLCalcParser const& self) + { + using namespace phoenix; + + assertion<std::string> assert_domain("Domain error"); +// assertion<std::string> assert_symbol("Unknown symbol"); + assertion<std::string> assert_syntax("Syntax error"); + + identifier = + lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')] + ; + + group = + '(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')')) + ; + + unary_func = + ((str_p("SIN") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_sin)(self,arg1)]) | + (str_p("COS") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_cos)(self,arg1)]) | + (str_p("TAN") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_tan)(self,arg1)]) | + (str_p("ASIN") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_asin)(self,arg1)]) | + (str_p("ACOS") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_acos)(self,arg1)]) | + (str_p("ATAN") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_atan)(self,arg1)]) | + (str_p("SQRT") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_sqrt)(self,arg1)]) | + (str_p("LOG") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_log)(self,arg1)]) | + (str_p("EXP") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_exp)(self,arg1)]) | + (str_p("ABS") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_fabs)(self,arg1)]) | + (str_p("FLR") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_floor)(self,arg1)]) | + (str_p("CEIL") >> '(' >> expression[unary_func.value = phoenix::bind(&LLCalcParser::_ceil)(self,arg1)]) + ) >> assert_syntax(ch_p(')')) + ; + + binary_func = + ((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >> + expression[binary_func.value = phoenix::bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) | + (str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >> + expression[binary_func.value = phoenix::bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) | + (str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >> + expression[binary_func.value = phoenix::bind(&LLCalcParser::_max)(self, binary_func.value, arg1)]) + ) >> assert_syntax(ch_p(')')) + ; + + // *TODO: Localisation of the decimal point? + // Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate + // for the current locale. However to do that here could clash with using + // the comma as a separator when passing arguments to functions. + factor = + (ureal_p[factor.value = arg1] | + group[factor.value = arg1] | + unary_func[factor.value = arg1] | + binary_func[factor.value = arg1] | + // Lookup throws an Unknown Symbol error if it is unknown, while this works fine, + // would be "neater" to handle symbol lookup from here with an assertive parser. +// constants_p[factor.value = arg1]| + identifier[factor.value = phoenix::bind(&LLCalcParser::lookup)(self, arg1, arg2)] + ) >> + // Detect and throw math errors. + assert_domain(eps_p(phoenix::bind(&LLCalcParser::checkNaN)(self, factor.value))) + ; + + unary_expr = + !ch_p('+') >> factor[unary_expr.value = arg1] | + '-' >> factor[unary_expr.value = -arg1] + ; + + power = + unary_expr[power.value = arg1] >> + *('^' >> assert_syntax(unary_expr[power.value = phoenix::bind(&powf)(power.value, arg1)])) + ; + + term = + power[term.value = arg1] >> + *(('*' >> assert_syntax(power[term.value *= arg1])) | + ('/' >> assert_syntax(power[term.value /= arg1])) | + ('%' >> assert_syntax(power[term.value = phoenix::bind(&fmodf)(term.value, arg1)])) + ) + ; + + expression = + assert_syntax(term[expression.value = arg1]) >> + *(('+' >> assert_syntax(term[expression.value += arg1])) | + ('-' >> assert_syntax(term[expression.value -= arg1])) + ) + ; + + statement = + !ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p) + ; + } + }; + private: - // Member functions for semantic actions - F32 lookup(const std::string::iterator&, const std::string::iterator&) const; - F32 _min(const F32& a, const F32& b) const { return llmin(a, b); } - F32 _max(const F32& a, const F32& b) const { return llmax(a, b); } - - bool checkNaN(const F32& a) const { return !llisnan(a); } - - //FIX* non ambiguous function fix making SIN() work for calc -Cryogenic Blitz - F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); } - F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); } - F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); } - F32 _asin(const F32& a) const { return asin(a) * RAD_TO_DEG; } - F32 _acos(const F32& a) const { return acos(a) * RAD_TO_DEG; } - F32 _atan(const F32& a) const { return atan(a) * RAD_TO_DEG; } - F32 _sqrt(const F32& a) const { return sqrt(a); } - F32 _log(const F32& a) const { return log(a); } - F32 _exp(const F32& a) const { return exp(a); } - F32 _fabs(const F32& a) const { return fabs(a); } - F32 _floor(const F32& a) const { return (F32)llfloor(a); } - F32 _ceil(const F32& a) const { return llceil(a); } - F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); } - - LLCalc::calc_map_t* mConstants; - LLCalc::calc_map_t* mVariables; -// LLCalc::calc_map_t* mUserVariables; - - F32& mResult; + // Member functions for semantic actions + F32 lookup(const std::string::iterator&, const std::string::iterator&) const; + F32 _min(const F32& a, const F32& b) const { return llmin(a, b); } + F32 _max(const F32& a, const F32& b) const { return llmax(a, b); } + + bool checkNaN(const F32& a) const { return !llisnan(a); } + + //FIX* non ambiguous function fix making SIN() work for calc -Cryogenic Blitz + F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); } + F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); } + F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); } + F32 _asin(const F32& a) const { return asin(a) * RAD_TO_DEG; } + F32 _acos(const F32& a) const { return acos(a) * RAD_TO_DEG; } + F32 _atan(const F32& a) const { return atan(a) * RAD_TO_DEG; } + F32 _sqrt(const F32& a) const { return sqrt(a); } + F32 _log(const F32& a) const { return log(a); } + F32 _exp(const F32& a) const { return exp(a); } + F32 _fabs(const F32& a) const { return fabs(a); } + F32 _floor(const F32& a) const { return (F32)llfloor(a); } + F32 _ceil(const F32& a) const { return llceil(a); } + F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); } + + LLCalc::calc_map_t* mConstants; + LLCalc::calc_map_t* mVariables; +// LLCalc::calc_map_t* mUserVariables; + + F32& mResult; }; #endif // LL_CALCPARSER_H diff --git a/indra/llmath/llcamera.cpp b/indra/llmath/llcamera.cpp index 18d704dd0f..3daed2ac31 100644 --- a/indra/llmath/llcamera.cpp +++ b/indra/llmath/llcamera.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llcamera.cpp * @brief Implementation of the LLCamera class. * * $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$ */ @@ -32,43 +32,43 @@ // ---------------- Constructors and destructors ---------------- LLCamera::LLCamera() : - LLCoordFrame(), - mView(DEFAULT_FIELD_OF_VIEW), - mAspect(DEFAULT_ASPECT_RATIO), - mViewHeightInPixels( -1 ), // invalid height - mNearPlane(DEFAULT_NEAR_PLANE), - mFarPlane(DEFAULT_FAR_PLANE), - mFixedDistance(-1.f), - mPlaneCount(6), - mFrustumCornerDist(0.f) -{ - for (U32 i = 0; i < PLANE_MASK_NUM; i++) - { - mPlaneMask[i] = PLANE_MASK_NONE; - } - - calculateFrustumPlanes(); -} + LLCoordFrame(), + mView(DEFAULT_FIELD_OF_VIEW), + mAspect(DEFAULT_ASPECT_RATIO), + mViewHeightInPixels( -1 ), // invalid height + mNearPlane(DEFAULT_NEAR_PLANE), + mFarPlane(DEFAULT_FAR_PLANE), + mFixedDistance(-1.f), + mPlaneCount(6), + mFrustumCornerDist(0.f) +{ + for (U32 i = 0; i < PLANE_MASK_NUM; i++) + { + mPlaneMask[i] = PLANE_MASK_NONE; + } + + calculateFrustumPlanes(); +} LLCamera::LLCamera(F32 vertical_fov_rads, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane) : - LLCoordFrame(), - mViewHeightInPixels(view_height_in_pixels), - mFixedDistance(-1.f), - mPlaneCount(6), - mFrustumCornerDist(0.f) -{ - for (U32 i = 0; i < PLANE_MASK_NUM; i++) - { - mPlaneMask[i] = PLANE_MASK_NONE; - } - - mAspect = llclamp(aspect_ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO); - mNearPlane = llclamp(near_plane, MIN_NEAR_PLANE, MAX_NEAR_PLANE); - if(far_plane < 0) far_plane = DEFAULT_FAR_PLANE; - mFarPlane = llclamp(far_plane, MIN_FAR_PLANE, MAX_FAR_PLANE); - - setView(vertical_fov_rads); -} + LLCoordFrame(), + mViewHeightInPixels(view_height_in_pixels), + mFixedDistance(-1.f), + mPlaneCount(6), + mFrustumCornerDist(0.f) +{ + for (U32 i = 0; i < PLANE_MASK_NUM; i++) + { + mPlaneMask[i] = PLANE_MASK_NONE; + } + + mAspect = llclamp(aspect_ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO); + mNearPlane = llclamp(near_plane, MIN_NEAR_PLANE, MAX_NEAR_PLANE); + if(far_plane < 0) far_plane = DEFAULT_FAR_PLANE; + mFarPlane = llclamp(far_plane, MIN_FAR_PLANE, MAX_FAR_PLANE); + + setView(vertical_fov_rads); +} LLCamera::~LLCamera() { @@ -77,20 +77,20 @@ LLCamera::~LLCamera() // ---------------- LLCamera::getFoo() member functions ---------------- -F32 LLCamera::getMinView() const +F32 LLCamera::getMinView() const { - // minimum vertical fov needs to be constrained in narrow windows. - return mAspect > 1 - ? MIN_FIELD_OF_VIEW // wide views - : MIN_FIELD_OF_VIEW * 1/mAspect; // clamps minimum width in narrow views + // minimum vertical fov needs to be constrained in narrow windows. + return mAspect > 1 + ? MIN_FIELD_OF_VIEW // wide views + : MIN_FIELD_OF_VIEW * 1/mAspect; // clamps minimum width in narrow views } -F32 LLCamera::getMaxView() const +F32 LLCamera::getMaxView() const { - // maximum vertical fov needs to be constrained in wide windows. - return mAspect > 1 - ? MAX_FIELD_OF_VIEW / mAspect // clamps maximum width in wide views - : MAX_FIELD_OF_VIEW; // narrow views + // maximum vertical fov needs to be constrained in wide windows. + return mAspect > 1 + ? MAX_FIELD_OF_VIEW / mAspect // clamps maximum width in wide views + : MAX_FIELD_OF_VIEW; // narrow views } LLPlane LLCamera::getUserClipPlane() @@ -102,437 +102,437 @@ LLPlane LLCamera::getUserClipPlane() void LLCamera::setUserClipPlane(LLPlane& plane) { - mPlaneCount = AGENT_PLANE_USER_CLIP_NUM; - mAgentPlanes[AGENT_PLANE_USER_CLIP] = plane; - mPlaneMask[AGENT_PLANE_USER_CLIP] = plane.calcPlaneMask(); + mPlaneCount = AGENT_PLANE_USER_CLIP_NUM; + mAgentPlanes[AGENT_PLANE_USER_CLIP] = plane; + mPlaneMask[AGENT_PLANE_USER_CLIP] = plane.calcPlaneMask(); } void LLCamera::disableUserClipPlane() { - mPlaneCount = AGENT_PLANE_NO_USER_CLIP_NUM; + mPlaneCount = AGENT_PLANE_NO_USER_CLIP_NUM; } -void LLCamera::setView(F32 vertical_fov_rads) +void LLCamera::setView(F32 vertical_fov_rads) { - mView = llclamp(vertical_fov_rads, MIN_FIELD_OF_VIEW, MAX_FIELD_OF_VIEW); - calculateFrustumPlanes(); + mView = llclamp(vertical_fov_rads, MIN_FIELD_OF_VIEW, MAX_FIELD_OF_VIEW); + calculateFrustumPlanes(); } void LLCamera::setViewHeightInPixels(S32 height) { - mViewHeightInPixels = height; + mViewHeightInPixels = height; - // Don't really need to do this, but update the pixel meter ratio with it. - calculateFrustumPlanes(); + // Don't really need to do this, but update the pixel meter ratio with it. + calculateFrustumPlanes(); } -void LLCamera::setAspect(F32 aspect_ratio) +void LLCamera::setAspect(F32 aspect_ratio) { - mAspect = llclamp(aspect_ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO); - calculateFrustumPlanes(); + mAspect = llclamp(aspect_ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO); + calculateFrustumPlanes(); } -void LLCamera::setNear(F32 near_plane) +void LLCamera::setNear(F32 near_plane) { - mNearPlane = llclamp(near_plane, MIN_NEAR_PLANE, MAX_NEAR_PLANE); - calculateFrustumPlanes(); + mNearPlane = llclamp(near_plane, MIN_NEAR_PLANE, MAX_NEAR_PLANE); + calculateFrustumPlanes(); } -void LLCamera::setFar(F32 far_plane) +void LLCamera::setFar(F32 far_plane) { - mFarPlane = llclamp(far_plane, MIN_FAR_PLANE, MAX_FAR_PLANE); - calculateFrustumPlanes(); + mFarPlane = llclamp(far_plane, MIN_FAR_PLANE, MAX_FAR_PLANE); + calculateFrustumPlanes(); } -// ---------------- read/write to buffer ---------------- +// ---------------- read/write to buffer ---------------- size_t LLCamera::writeFrustumToBuffer(char *buffer) const { - memcpy(buffer, &mView, sizeof(F32)); /* Flawfinder: ignore */ - buffer += sizeof(F32); - memcpy(buffer, &mAspect, sizeof(F32)); /* Flawfinder: ignore */ - buffer += sizeof(F32); - memcpy(buffer, &mNearPlane, sizeof(F32)); /* Flawfinder: ignore */ - buffer += sizeof(F32); - memcpy(buffer, &mFarPlane, sizeof(F32)); /* Flawfinder: ignore */ - return 4*sizeof(F32); + memcpy(buffer, &mView, sizeof(F32)); /* Flawfinder: ignore */ + buffer += sizeof(F32); + memcpy(buffer, &mAspect, sizeof(F32)); /* Flawfinder: ignore */ + buffer += sizeof(F32); + memcpy(buffer, &mNearPlane, sizeof(F32)); /* Flawfinder: ignore */ + buffer += sizeof(F32); + memcpy(buffer, &mFarPlane, sizeof(F32)); /* Flawfinder: ignore */ + return 4*sizeof(F32); } size_t LLCamera::readFrustumFromBuffer(const char *buffer) { - memcpy(&mView, buffer, sizeof(F32)); /* Flawfinder: ignore */ - buffer += sizeof(F32); - memcpy(&mAspect, buffer, sizeof(F32)); /* Flawfinder: ignore */ - buffer += sizeof(F32); - memcpy(&mNearPlane, buffer, sizeof(F32)); /* Flawfinder: ignore */ - buffer += sizeof(F32); - memcpy(&mFarPlane, buffer, sizeof(F32)); /* Flawfinder: ignore */ - return 4*sizeof(F32); + memcpy(&mView, buffer, sizeof(F32)); /* Flawfinder: ignore */ + buffer += sizeof(F32); + memcpy(&mAspect, buffer, sizeof(F32)); /* Flawfinder: ignore */ + buffer += sizeof(F32); + memcpy(&mNearPlane, buffer, sizeof(F32)); /* Flawfinder: ignore */ + buffer += sizeof(F32); + memcpy(&mFarPlane, buffer, sizeof(F32)); /* Flawfinder: ignore */ + return 4*sizeof(F32); } -// ---------------- test methods ---------------- +// ---------------- test methods ---------------- -static const LLVector4a sFrustumScaler[] = +static const LLVector4a sFrustumScaler[] = { - LLVector4a(-1,-1,-1), - LLVector4a( 1,-1,-1), - LLVector4a(-1, 1,-1), - LLVector4a( 1, 1,-1), - LLVector4a(-1,-1, 1), - LLVector4a( 1,-1, 1), - LLVector4a(-1, 1, 1), - LLVector4a( 1, 1, 1) // 8 entries + LLVector4a(-1,-1,-1), + LLVector4a( 1,-1,-1), + LLVector4a(-1, 1,-1), + LLVector4a( 1, 1,-1), + LLVector4a(-1,-1, 1), + LLVector4a( 1,-1, 1), + LLVector4a(-1, 1, 1), + LLVector4a( 1, 1, 1) // 8 entries }; bool LLCamera::isChanged() { - bool changed = false; - for (U32 i = 0; i < mPlaneCount; i++) - { - U8 mask = mPlaneMask[i]; - if (mask != 0xff && !changed) - { - changed = !mAgentPlanes[i].equal(mLastAgentPlanes[i]); - } - mLastAgentPlanes[i].set(mAgentPlanes[i]); - } - - return changed; + bool changed = false; + for (U32 i = 0; i < mPlaneCount; i++) + { + U8 mask = mPlaneMask[i]; + if (mask != 0xff && !changed) + { + changed = !mAgentPlanes[i].equal(mLastAgentPlanes[i]); + } + mLastAgentPlanes[i].set(mAgentPlanes[i]); + } + + return changed; } -S32 LLCamera::AABBInFrustum(const LLVector4a ¢er, const LLVector4a& radius, const LLPlane* planes) -{ - if(!planes) - { - //use agent space - planes = mAgentPlanes; - } - - U8 mask = 0; - bool result = false; - LLVector4a rscale, maxp, minp; - LLSimdScalar d; - U32 max_planes = llmin(mPlaneCount, (U32) AGENT_PLANE_USER_CLIP_NUM); // mAgentPlanes[] size is 7 - for (U32 i = 0; i < max_planes; i++) - { - mask = mPlaneMask[i]; - if (mask < PLANE_MASK_NUM) - { - const LLPlane& p(planes[i]); - p.getAt<3>(d); - rscale.setMul(radius, sFrustumScaler[mask]); - minp.setSub(center, rscale); - d = -d; - if (p.dot3(minp).getF32() > d) - { - return 0; - } - - if(!result) - { - maxp.setAdd(center, rscale); - result = (p.dot3(maxp).getF32() > d); - } - } - } - - return result?1:2; +S32 LLCamera::AABBInFrustum(const LLVector4a ¢er, const LLVector4a& radius, const LLPlane* planes) +{ + if(!planes) + { + //use agent space + planes = mAgentPlanes; + } + + U8 mask = 0; + bool result = false; + LLVector4a rscale, maxp, minp; + LLSimdScalar d; + U32 max_planes = llmin(mPlaneCount, (U32) AGENT_PLANE_USER_CLIP_NUM); // mAgentPlanes[] size is 7 + for (U32 i = 0; i < max_planes; i++) + { + mask = mPlaneMask[i]; + if (mask < PLANE_MASK_NUM) + { + const LLPlane& p(planes[i]); + p.getAt<3>(d); + rscale.setMul(radius, sFrustumScaler[mask]); + minp.setSub(center, rscale); + d = -d; + if (p.dot3(minp).getF32() > d) + { + return 0; + } + + if(!result) + { + maxp.setAdd(center, rscale); + result = (p.dot3(maxp).getF32() > d); + } + } + } + + return result?1:2; } //exactly same as the function AABBInFrustum(...) //except uses mRegionPlanes instead of mAgentPlanes. -S32 LLCamera::AABBInRegionFrustum(const LLVector4a& center, const LLVector4a& radius) +S32 LLCamera::AABBInRegionFrustum(const LLVector4a& center, const LLVector4a& radius) { - return AABBInFrustum(center, radius, mRegionPlanes); + return AABBInFrustum(center, radius, mRegionPlanes); } -S32 LLCamera::AABBInFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius, const LLPlane* planes) -{ - if(!planes) - { - //use agent space - planes = mAgentPlanes; - } - - U8 mask = 0; - bool result = false; - LLVector4a rscale, maxp, minp; - LLSimdScalar d; - U32 max_planes = llmin(mPlaneCount, (U32) AGENT_PLANE_USER_CLIP_NUM); // mAgentPlanes[] size is 7 - for (U32 i = 0; i < max_planes; i++) - { - mask = mPlaneMask[i]; - if ((i != 5) && (mask < PLANE_MASK_NUM)) - { - const LLPlane& p(planes[i]); - p.getAt<3>(d); - rscale.setMul(radius, sFrustumScaler[mask]); - minp.setSub(center, rscale); - d = -d; - if (p.dot3(minp).getF32() > d) - { - return 0; - } - - if(!result) - { - maxp.setAdd(center, rscale); - result = (p.dot3(maxp).getF32() > d); - } - } - } - - return result?1:2; +S32 LLCamera::AABBInFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius, const LLPlane* planes) +{ + if(!planes) + { + //use agent space + planes = mAgentPlanes; + } + + U8 mask = 0; + bool result = false; + LLVector4a rscale, maxp, minp; + LLSimdScalar d; + U32 max_planes = llmin(mPlaneCount, (U32) AGENT_PLANE_USER_CLIP_NUM); // mAgentPlanes[] size is 7 + for (U32 i = 0; i < max_planes; i++) + { + mask = mPlaneMask[i]; + if ((i != 5) && (mask < PLANE_MASK_NUM)) + { + const LLPlane& p(planes[i]); + p.getAt<3>(d); + rscale.setMul(radius, sFrustumScaler[mask]); + minp.setSub(center, rscale); + d = -d; + if (p.dot3(minp).getF32() > d) + { + return 0; + } + + if(!result) + { + maxp.setAdd(center, rscale); + result = (p.dot3(maxp).getF32() > d); + } + } + } + + return result?1:2; } //exactly same as the function AABBInFrustumNoFarClip(...) //except uses mRegionPlanes instead of mAgentPlanes. -S32 LLCamera::AABBInRegionFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius) +S32 LLCamera::AABBInRegionFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius) { - return AABBInFrustumNoFarClip(center, radius, mRegionPlanes); + return AABBInFrustumNoFarClip(center, radius, mRegionPlanes); } -int LLCamera::sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius) +int LLCamera::sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius) { - LLVector3 dist = sphere_center-mFrustCenter; - float dsq = dist * dist; - float rsq = mFarPlane*0.5f + radius; - rsq *= rsq; + LLVector3 dist = sphere_center-mFrustCenter; + float dsq = dist * dist; + float rsq = mFarPlane*0.5f + radius; + rsq *= rsq; - if (dsq < rsq) - { - return 1; - } - - return 0; + if (dsq < rsq) + { + return 1; + } + + return 0; } // Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0. // NOTE: 'center' is in absolute frame. -int LLCamera::sphereInFrustum(const LLVector3 &sphere_center, const F32 radius) const -{ - // Returns 1 if sphere is in frustum, 0 if not. - bool res = false; - for (int i = 0; i < 6; i++) - { - if (mPlaneMask[i] != PLANE_MASK_NONE) - { - float d = mAgentPlanes[i].dist(sphere_center); - - if (d > radius) - { - return 0; - } - res = res || (d > -radius); - } - } - - return res?1:2; +int LLCamera::sphereInFrustum(const LLVector3 &sphere_center, const F32 radius) const +{ + // Returns 1 if sphere is in frustum, 0 if not. + bool res = false; + for (int i = 0; i < 6; i++) + { + if (mPlaneMask[i] != PLANE_MASK_NONE) + { + float d = mAgentPlanes[i].dist(sphere_center); + + if (d > radius) + { + return 0; + } + res = res || (d > -radius); + } + } + + return res?1:2; } // return height of a sphere of given radius, located at center, in pixels F32 LLCamera::heightInPixels(const LLVector3 ¢er, F32 radius ) const { - if (radius == 0.f) return 0.f; + if (radius == 0.f) return 0.f; - // If height initialized - if (mViewHeightInPixels > -1) - { - // Convert sphere to coord system with 0,0,0 at camera - LLVector3 vec = center - mOrigin; + // If height initialized + if (mViewHeightInPixels > -1) + { + // Convert sphere to coord system with 0,0,0 at camera + LLVector3 vec = center - mOrigin; - // Compute distance to sphere - F32 dist = vec.magVec(); + // Compute distance to sphere + F32 dist = vec.magVec(); - // Calculate angle of whole object - F32 angle = 2.0f * (F32) atan2(radius, dist); + // Calculate angle of whole object + F32 angle = 2.0f * (F32) atan2(radius, dist); - // Calculate fraction of field of view - F32 fraction_of_fov = angle / mView; + // Calculate fraction of field of view + F32 fraction_of_fov = angle / mView; - // Compute number of pixels tall, based on vertical field of view - return (fraction_of_fov * mViewHeightInPixels); - } - else - { - // return invalid height - return -1.0f; - } + // Compute number of pixels tall, based on vertical field of view + return (fraction_of_fov * mViewHeightInPixels); + } + else + { + // return invalid height + return -1.0f; + } } -// ---------------- friends and operators ---------------- +// ---------------- friends and operators ---------------- -std::ostream& operator<<(std::ostream &s, const LLCamera &C) +std::ostream& operator<<(std::ostream &s, const LLCamera &C) { - s << "{ \n"; - s << " Center = " << C.getOrigin() << "\n"; - s << " AtAxis = " << C.getXAxis() << "\n"; - s << " LeftAxis = " << C.getYAxis() << "\n"; - s << " UpAxis = " << C.getZAxis() << "\n"; - s << " View = " << C.getView() << "\n"; - s << " Aspect = " << C.getAspect() << "\n"; - s << " NearPlane = " << C.mNearPlane << "\n"; - s << " FarPlane = " << C.mFarPlane << "\n"; - s << "}"; - return s; + s << "{ \n"; + s << " Center = " << C.getOrigin() << "\n"; + s << " AtAxis = " << C.getXAxis() << "\n"; + s << " LeftAxis = " << C.getYAxis() << "\n"; + s << " UpAxis = " << C.getZAxis() << "\n"; + s << " View = " << C.getView() << "\n"; + s << " Aspect = " << C.getAspect() << "\n"; + s << " NearPlane = " << C.mNearPlane << "\n"; + s << " FarPlane = " << C.mFarPlane << "\n"; + s << "}"; + return s; } // ---------------- private member functions ---------------- -void LLCamera::calculateFrustumPlanes() +void LLCamera::calculateFrustumPlanes() { - // The planes only change when any of the frustum descriptions change. - // They are not affected by changes of the position of the Frustum - // because they are known in the view frame and the position merely - // provides information on how to get from the absolute frame to the - // view frame. + // The planes only change when any of the frustum descriptions change. + // They are not affected by changes of the position of the Frustum + // because they are known in the view frame and the position merely + // provides information on how to get from the absolute frame to the + // view frame. - F32 left,right,top,bottom; - top = mFarPlane * (F32)tanf(0.5f * mView); - bottom = -top; - left = top * mAspect; - right = -left; + F32 left,right,top,bottom; + top = mFarPlane * (F32)tanf(0.5f * mView); + bottom = -top; + left = top * mAspect; + right = -left; - calculateFrustumPlanes(left, right, top, bottom); + calculateFrustumPlanes(left, right, top, bottom); } LLPlane planeFromPoints(LLVector3 p1, LLVector3 p2, LLVector3 p3) { - LLVector3 n = ((p2-p1)%(p3-p1)); - n.normVec(); + LLVector3 n = ((p2-p1)%(p3-p1)); + n.normVec(); - return LLPlane(p1, n); + return LLPlane(p1, n); } void LLCamera::ignoreAgentFrustumPlane(S32 idx) { - if (idx < 0 || idx > (S32) mPlaneCount) - { - return; - } + if (idx < 0 || idx > (S32) mPlaneCount) + { + return; + } - mPlaneMask[idx] = PLANE_MASK_NONE; - mAgentPlanes[idx].clear(); + mPlaneMask[idx] = PLANE_MASK_NONE; + mAgentPlanes[idx].clear(); } void LLCamera::calcAgentFrustumPlanes(LLVector3* frust) { - - for (int i = 0; i < AGENT_FRUSTRUM_NUM; i++) - { - mAgentFrustum[i] = frust[i]; - } - mFrustumCornerDist = (frust[5] - getOrigin()).magVec(); + for (int i = 0; i < AGENT_FRUSTRUM_NUM; i++) + { + mAgentFrustum[i] = frust[i]; + } + + mFrustumCornerDist = (frust[5] - getOrigin()).magVec(); - //frust contains the 8 points of the frustum, calculate 6 planes + //frust contains the 8 points of the frustum, calculate 6 planes - //order of planes is important, keep most likely to fail in the front of the list + //order of planes is important, keep most likely to fail in the front of the list - //near - frust[0], frust[1], frust[2] - mAgentPlanes[AGENT_PLANE_NEAR] = planeFromPoints(frust[0], frust[1], frust[2]); + //near - frust[0], frust[1], frust[2] + mAgentPlanes[AGENT_PLANE_NEAR] = planeFromPoints(frust[0], frust[1], frust[2]); - //far - mAgentPlanes[AGENT_PLANE_FAR] = planeFromPoints(frust[5], frust[4], frust[6]); + //far + mAgentPlanes[AGENT_PLANE_FAR] = planeFromPoints(frust[5], frust[4], frust[6]); - //left - mAgentPlanes[AGENT_PLANE_LEFT] = planeFromPoints(frust[4], frust[0], frust[7]); + //left + mAgentPlanes[AGENT_PLANE_LEFT] = planeFromPoints(frust[4], frust[0], frust[7]); - //right - mAgentPlanes[AGENT_PLANE_RIGHT] = planeFromPoints(frust[1], frust[5], frust[6]); + //right + mAgentPlanes[AGENT_PLANE_RIGHT] = planeFromPoints(frust[1], frust[5], frust[6]); - //top - mAgentPlanes[AGENT_PLANE_TOP] = planeFromPoints(frust[3], frust[2], frust[6]); + //top + mAgentPlanes[AGENT_PLANE_TOP] = planeFromPoints(frust[3], frust[2], frust[6]); - //bottom - mAgentPlanes[AGENT_PLANE_BOTTOM] = planeFromPoints(frust[1], frust[0], frust[4]); + //bottom + mAgentPlanes[AGENT_PLANE_BOTTOM] = planeFromPoints(frust[1], frust[0], frust[4]); - //cache plane octant facing mask for use in AABBInFrustum - for (U32 i = 0; i < mPlaneCount; i++) - { - mPlaneMask[i] = mAgentPlanes[i].calcPlaneMask(); - } + //cache plane octant facing mask for use in AABBInFrustum + for (U32 i = 0; i < mPlaneCount; i++) + { + mPlaneMask[i] = mAgentPlanes[i].calcPlaneMask(); + } } //calculate regional planes from mAgentPlanes. //vector "shift" is the vector of the region origin in the agent space. -void LLCamera::calcRegionFrustumPlanes(const LLVector3& shift, F32 far_clip_distance) -{ - F32 far_w; - { - LLVector3 p = getOrigin(); - LLVector3 n(mAgentPlanes[5][0], mAgentPlanes[5][1], mAgentPlanes[5][2]); - F32 dd = n * p; - if(dd + mAgentPlanes[5][3] < 0) //signed distance - { - far_w = -far_clip_distance - dd; - } - else - { - far_w = far_clip_distance - dd; - } - far_w += n * shift; - } - - F32 d; - LLVector3 n; - for(S32 i = 0 ; i < 7; i++) - { - if (mPlaneMask[i] != 0xff) - { - n.setVec(mAgentPlanes[i][0], mAgentPlanes[i][1], mAgentPlanes[i][2]); - - if(i != 5) - { - d = mAgentPlanes[i][3] + n * shift; - } - else - { - d = far_w; - } - mRegionPlanes[i].setVec(n, d); - } - } +void LLCamera::calcRegionFrustumPlanes(const LLVector3& shift, F32 far_clip_distance) +{ + F32 far_w; + { + LLVector3 p = getOrigin(); + LLVector3 n(mAgentPlanes[5][0], mAgentPlanes[5][1], mAgentPlanes[5][2]); + F32 dd = n * p; + if(dd + mAgentPlanes[5][3] < 0) //signed distance + { + far_w = -far_clip_distance - dd; + } + else + { + far_w = far_clip_distance - dd; + } + far_w += n * shift; + } + + F32 d; + LLVector3 n; + for(S32 i = 0 ; i < 7; i++) + { + if (mPlaneMask[i] != 0xff) + { + n.setVec(mAgentPlanes[i][0], mAgentPlanes[i][1], mAgentPlanes[i][2]); + + if(i != 5) + { + d = mAgentPlanes[i][3] + n * shift; + } + else + { + d = far_w; + } + mRegionPlanes[i].setVec(n, d); + } + } } void LLCamera::calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom) { - //calculate center and radius squared of frustum in world absolute coordinates - static LLVector3 const X_AXIS(1.f, 0.f, 0.f); - mFrustCenter = X_AXIS*mFarPlane*0.5f; - mFrustCenter = transformToAbsolute(mFrustCenter); - mFrustRadiusSquared = mFarPlane*0.5f; - mFrustRadiusSquared *= mFrustRadiusSquared * 1.05f; //pad radius squared by 5% + //calculate center and radius squared of frustum in world absolute coordinates + static LLVector3 const X_AXIS(1.f, 0.f, 0.f); + mFrustCenter = X_AXIS*mFarPlane*0.5f; + mFrustCenter = transformToAbsolute(mFrustCenter); + mFrustRadiusSquared = mFarPlane*0.5f; + mFrustRadiusSquared *= mFrustRadiusSquared * 1.05f; //pad radius squared by 5% } // x and y are in WINDOW space, so x = Y-Axis (left/right), y= Z-Axis(Up/Down) void LLCamera::calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2) { - F32 bottom, top, left, right; - F32 view_height = (F32)tanf(0.5f * mView) * mFarPlane; - F32 view_width = view_height * mAspect; - - left = x1 * -2.f * view_width; - right = x2 * -2.f * view_width; - bottom = y1 * 2.f * view_height; - top = y2 * 2.f * view_height; + F32 bottom, top, left, right; + F32 view_height = (F32)tanf(0.5f * mView) * mFarPlane; + F32 view_width = view_height * mAspect; + + left = x1 * -2.f * view_width; + right = x2 * -2.f * view_width; + bottom = y1 * 2.f * view_height; + top = y2 * 2.f * view_height; - calculateFrustumPlanes(left, right, top, bottom); + calculateFrustumPlanes(left, right, top, bottom); } -// NOTE: this is the OpenGL matrix that will transform the default OpenGL view +// NOTE: this is the OpenGL matrix that will transform the default OpenGL view // (-Z=at, Y=up) to the default view of the LLCamera class (X=at, Z=up): -// +// // F32 cfr_transform = { 0.f, 0.f, -1.f, 0.f, // -Z becomes X -// -1.f, 0.f, 0.f, 0.f, // -X becomes Y -// 0.f, 1.f, 0.f, 0.f, // Y becomes Z -// 0.f, 0.f, 0.f, 1.f }; +// -1.f, 0.f, 0.f, 0.f, // -X becomes Y +// 0.f, 1.f, 0.f, 0.f, // Y becomes Z +// 0.f, 0.f, 0.f, 1.f }; diff --git a/indra/llmath/llcamera.h b/indra/llmath/llcamera.h index c4d04f5d02..3b52810855 100644 --- a/indra/llmath/llcamera.h +++ b/indra/llmath/llcamera.h @@ -1,25 +1,25 @@ -/** +/** * @file llcamera.h * @brief Header file for the LLCamera class. * * $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$ */ @@ -33,26 +33,26 @@ #include "llplane.h" #include "llvector4a.h" -const F32 DEFAULT_FIELD_OF_VIEW = 60.f * DEG_TO_RAD; -const F32 DEFAULT_ASPECT_RATIO = 640.f / 480.f; -const F32 DEFAULT_NEAR_PLANE = 0.25f; -const F32 DEFAULT_FAR_PLANE = 64.f; // far reaches across two horizontal, not diagonal, regions +const F32 DEFAULT_FIELD_OF_VIEW = 60.f * DEG_TO_RAD; +const F32 DEFAULT_ASPECT_RATIO = 640.f / 480.f; +const F32 DEFAULT_NEAR_PLANE = 0.25f; +const F32 DEFAULT_FAR_PLANE = 64.f; // far reaches across two horizontal, not diagonal, regions -const F32 MAX_ASPECT_RATIO = 50.0f; -const F32 MAX_NEAR_PLANE = 1023.9f; // Clamp the near plane just before the skybox ends -const F32 MAX_FAR_PLANE = 100000.0f; //1000000.0f; // Max allowed. Not good Z precision though. -const F32 MAX_FAR_CLIP = 512.0f; +const F32 MAX_ASPECT_RATIO = 50.0f; +const F32 MAX_NEAR_PLANE = 1023.9f; // Clamp the near plane just before the skybox ends +const F32 MAX_FAR_PLANE = 100000.0f; //1000000.0f; // Max allowed. Not good Z precision though. +const F32 MAX_FAR_CLIP = 512.0f; -const F32 MIN_ASPECT_RATIO = 0.02f; -const F32 MIN_NEAR_PLANE = 0.1f; -const F32 MIN_FAR_PLANE = 0.2f; +const F32 MIN_ASPECT_RATIO = 0.02f; +const F32 MIN_NEAR_PLANE = 0.1f; +const F32 MIN_FAR_PLANE = 0.2f; // Min/Max FOV values for square views. Call getMin/MaxView to get extremes based on current aspect ratio. static const F32 MIN_FIELD_OF_VIEW = 5.0f * DEG_TO_RAD; static const F32 MAX_FIELD_OF_VIEW = 175.f * DEG_TO_RAD; // An LLCamera is an LLCoorFrame with a view frustum. -// This means that it has several methods for moving it around +// This means that it has several methods for moving it around // that are inherited from the LLCoordFrame() class : // // setOrigin(), setAxes() @@ -62,163 +62,163 @@ static const F32 MAX_FIELD_OF_VIEW = 175.f * DEG_TO_RAD; LL_ALIGN_PREFIX(16) class LLCamera -: public LLCoordFrame +: public LLCoordFrame { public: - - LLCamera(const LLCamera& rhs) - { - *this = rhs; - } - - enum { - PLANE_LEFT = 0, - PLANE_RIGHT = 1, - PLANE_BOTTOM = 2, - PLANE_TOP = 3, - PLANE_NUM = 4, - PLANE_MASK_NONE = 0xff // Disable this plane - }; - enum { - PLANE_LEFT_MASK = (1<<PLANE_LEFT), - PLANE_RIGHT_MASK = (1<<PLANE_RIGHT), - PLANE_BOTTOM_MASK = (1<<PLANE_BOTTOM), - PLANE_TOP_MASK = (1<<PLANE_TOP), - PLANE_ALL_MASK = 0xf, - }; - - enum - { // Indexes to mAgentPlanes[] and mPlaneMask[] - AGENT_PLANE_LEFT = 0, - AGENT_PLANE_RIGHT = 1, - AGENT_PLANE_NEAR = 2, - AGENT_PLANE_BOTTOM = 3, - AGENT_PLANE_TOP = 4, - AGENT_PLANE_FAR = 5, - AGENT_PLANE_USER_CLIP = 6 - }; - enum - { // Sizes for mAgentPlanes[]. 7th entry is special case for user clip - AGENT_PLANE_NO_USER_CLIP_NUM = 6, - AGENT_PLANE_USER_CLIP_NUM = 7, - PLANE_MASK_NUM = 8 // 7 actually used, 8 is for alignment - }; - - enum - { - AGENT_FRUSTRUM_NUM = 8 - }; - - enum { - HORIZ_PLANE_LEFT = 0, - HORIZ_PLANE_RIGHT = 1, - HORIZ_PLANE_NUM = 2 - }; - enum { - HORIZ_PLANE_LEFT_MASK = (1<<HORIZ_PLANE_LEFT), - HORIZ_PLANE_RIGHT_MASK = (1<<HORIZ_PLANE_RIGHT), - HORIZ_PLANE_ALL_MASK = 0x3 - }; + + LLCamera(const LLCamera& rhs) + { + *this = rhs; + } + + enum { + PLANE_LEFT = 0, + PLANE_RIGHT = 1, + PLANE_BOTTOM = 2, + PLANE_TOP = 3, + PLANE_NUM = 4, + PLANE_MASK_NONE = 0xff // Disable this plane + }; + enum { + PLANE_LEFT_MASK = (1<<PLANE_LEFT), + PLANE_RIGHT_MASK = (1<<PLANE_RIGHT), + PLANE_BOTTOM_MASK = (1<<PLANE_BOTTOM), + PLANE_TOP_MASK = (1<<PLANE_TOP), + PLANE_ALL_MASK = 0xf, + }; + + enum + { // Indexes to mAgentPlanes[] and mPlaneMask[] + AGENT_PLANE_LEFT = 0, + AGENT_PLANE_RIGHT = 1, + AGENT_PLANE_NEAR = 2, + AGENT_PLANE_BOTTOM = 3, + AGENT_PLANE_TOP = 4, + AGENT_PLANE_FAR = 5, + AGENT_PLANE_USER_CLIP = 6 + }; + enum + { // Sizes for mAgentPlanes[]. 7th entry is special case for user clip + AGENT_PLANE_NO_USER_CLIP_NUM = 6, + AGENT_PLANE_USER_CLIP_NUM = 7, + PLANE_MASK_NUM = 8 // 7 actually used, 8 is for alignment + }; + + enum + { + AGENT_FRUSTRUM_NUM = 8 + }; + + enum { + HORIZ_PLANE_LEFT = 0, + HORIZ_PLANE_RIGHT = 1, + HORIZ_PLANE_NUM = 2 + }; + enum { + HORIZ_PLANE_LEFT_MASK = (1<<HORIZ_PLANE_LEFT), + HORIZ_PLANE_RIGHT_MASK = (1<<HORIZ_PLANE_RIGHT), + HORIZ_PLANE_ALL_MASK = 0x3 + }; private: - LL_ALIGN_16(LLPlane mAgentPlanes[AGENT_PLANE_USER_CLIP_NUM]); //frustum planes in agent space a la gluUnproject (I'm a bastard, I know) - DaveP - LL_ALIGN_16(LLPlane mRegionPlanes[AGENT_PLANE_USER_CLIP_NUM]); //frustum planes in a local region space, derived from mAgentPlanes - LL_ALIGN_16(LLPlane mLastAgentPlanes[AGENT_PLANE_USER_CLIP_NUM]); - U8 mPlaneMask[PLANE_MASK_NUM]; // 8 for alignment - - F32 mView; // angle between top and bottom frustum planes in radians. - F32 mAspect; // width/height - S32 mViewHeightInPixels; // for ViewHeightInPixels() only - F32 mNearPlane; - F32 mFarPlane; - F32 mFixedDistance; // Always return this distance, unless < 0 - LLVector3 mFrustCenter; // center of frustum and radius squared for ultra-quick exclusion test - F32 mFrustRadiusSquared; - - U32 mPlaneCount; //defaults to 6, if setUserClipPlane is called, uses user supplied clip plane in - - LLVector3 mWorldPlanePos; // Position of World Planes (may be offset from camera) + LL_ALIGN_16(LLPlane mAgentPlanes[AGENT_PLANE_USER_CLIP_NUM]); //frustum planes in agent space a la gluUnproject (I'm a bastard, I know) - DaveP + LL_ALIGN_16(LLPlane mRegionPlanes[AGENT_PLANE_USER_CLIP_NUM]); //frustum planes in a local region space, derived from mAgentPlanes + LL_ALIGN_16(LLPlane mLastAgentPlanes[AGENT_PLANE_USER_CLIP_NUM]); + U8 mPlaneMask[PLANE_MASK_NUM]; // 8 for alignment + + F32 mView; // angle between top and bottom frustum planes in radians. + F32 mAspect; // width/height + S32 mViewHeightInPixels; // for ViewHeightInPixels() only + F32 mNearPlane; + F32 mFarPlane; + F32 mFixedDistance; // Always return this distance, unless < 0 + LLVector3 mFrustCenter; // center of frustum and radius squared for ultra-quick exclusion test + F32 mFrustRadiusSquared; + + U32 mPlaneCount; //defaults to 6, if setUserClipPlane is called, uses user supplied clip plane in + + LLVector3 mWorldPlanePos; // Position of World Planes (may be offset from camera) public: - LLVector3 mAgentFrustum[AGENT_FRUSTRUM_NUM]; //8 corners of 6-plane frustum - F32 mFrustumCornerDist; //distance to corner of frustum against far clip plane - LLPlane& getAgentPlane(U32 idx) { return mAgentPlanes[idx]; } + LLVector3 mAgentFrustum[AGENT_FRUSTRUM_NUM]; //8 corners of 6-plane frustum + F32 mFrustumCornerDist; //distance to corner of frustum against far clip plane + LLPlane& getAgentPlane(U32 idx) { return mAgentPlanes[idx]; } public: - LLCamera(); - LLCamera(F32 vertical_fov_rads, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane); - virtual ~LLCamera(); - - bool isChanged(); //check if mAgentPlanes changed since last frame. + LLCamera(); + LLCamera(F32 vertical_fov_rads, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane); + virtual ~LLCamera(); + + bool isChanged(); //check if mAgentPlanes changed since last frame. LLPlane getUserClipPlane(); - void setUserClipPlane(LLPlane& plane); - void disableUserClipPlane(); - virtual void setView(F32 vertical_fov_rads); - void setViewHeightInPixels(S32 height); - void setAspect(F32 new_aspect); - void setNear(F32 new_near); - void setFar(F32 new_far); - - F32 getView() const { return mView; } // vertical FOV in radians - S32 getViewHeightInPixels() const { return mViewHeightInPixels; } - F32 getAspect() const { return mAspect; } // width / height - F32 getNear() const { return mNearPlane; } // meters - F32 getFar() const { return mFarPlane; } // meters - - // The values returned by the min/max view getters depend upon the aspect ratio - // at the time they are called and therefore should not be cached. - F32 getMinView() const; - F32 getMaxView() const; - - F32 getYaw() const - { - return atan2f(mXAxis[VY], mXAxis[VX]); - } - F32 getPitch() const - { - F32 xylen = sqrtf(mXAxis[VX]*mXAxis[VX] + mXAxis[VY]*mXAxis[VY]); - return atan2f(mXAxis[VZ], xylen); - } - - const LLVector3& getWorldPlanePos() const { return mWorldPlanePos; } - - // Copy mView, mAspect, mNearPlane, and mFarPlane to buffer. - // Return number of bytes copied. - size_t writeFrustumToBuffer(char *buffer) const; - - // Copy mView, mAspect, mNearPlane, and mFarPlane from buffer. - // Return number of bytes copied. - size_t readFrustumFromBuffer(const char *buffer); - void calcAgentFrustumPlanes(LLVector3* frust); - void calcRegionFrustumPlanes(const LLVector3& shift, F32 far_clip_distance); //calculate regional planes from mAgentPlanes. - void ignoreAgentFrustumPlane(S32 idx); - - // Returns 1 if partly in, 2 if fully in. - // NOTE: 'center' is in absolute frame. - S32 sphereInFrustum(const LLVector3 ¢er, const F32 radius) const; - S32 pointInFrustum(const LLVector3 &point) const { return sphereInFrustum(point, 0.0f); } - S32 sphereInFrustumFull(const LLVector3 ¢er, const F32 radius) const { return sphereInFrustum(center, radius); } - S32 AABBInFrustum(const LLVector4a& center, const LLVector4a& radius, const LLPlane* planes = NULL); - S32 AABBInRegionFrustum(const LLVector4a& center, const LLVector4a& radius); - S32 AABBInFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius, const LLPlane* planes = NULL); - S32 AABBInRegionFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius); - - //does a quick 'n dirty sphere-sphere check - S32 sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius); - - // Returns height of object in pixels (must be height because field of view - // is based on window height). - F32 heightInPixels(const LLVector3 ¢er, F32 radius ) const; - - // return the distance from pos to camera if visible (-distance if not visible) - void setFixedDistance(F32 distance) { mFixedDistance = distance; } - - friend std::ostream& operator<<(std::ostream &s, const LLCamera &C); + void setUserClipPlane(LLPlane& plane); + void disableUserClipPlane(); + virtual void setView(F32 vertical_fov_rads); + void setViewHeightInPixels(S32 height); + void setAspect(F32 new_aspect); + void setNear(F32 new_near); + void setFar(F32 new_far); + + F32 getView() const { return mView; } // vertical FOV in radians + S32 getViewHeightInPixels() const { return mViewHeightInPixels; } + F32 getAspect() const { return mAspect; } // width / height + F32 getNear() const { return mNearPlane; } // meters + F32 getFar() const { return mFarPlane; } // meters + + // The values returned by the min/max view getters depend upon the aspect ratio + // at the time they are called and therefore should not be cached. + F32 getMinView() const; + F32 getMaxView() const; + + F32 getYaw() const + { + return atan2f(mXAxis[VY], mXAxis[VX]); + } + F32 getPitch() const + { + F32 xylen = sqrtf(mXAxis[VX]*mXAxis[VX] + mXAxis[VY]*mXAxis[VY]); + return atan2f(mXAxis[VZ], xylen); + } + + const LLVector3& getWorldPlanePos() const { return mWorldPlanePos; } + + // Copy mView, mAspect, mNearPlane, and mFarPlane to buffer. + // Return number of bytes copied. + size_t writeFrustumToBuffer(char *buffer) const; + + // Copy mView, mAspect, mNearPlane, and mFarPlane from buffer. + // Return number of bytes copied. + size_t readFrustumFromBuffer(const char *buffer); + void calcAgentFrustumPlanes(LLVector3* frust); + void calcRegionFrustumPlanes(const LLVector3& shift, F32 far_clip_distance); //calculate regional planes from mAgentPlanes. + void ignoreAgentFrustumPlane(S32 idx); + + // Returns 1 if partly in, 2 if fully in. + // NOTE: 'center' is in absolute frame. + S32 sphereInFrustum(const LLVector3 ¢er, const F32 radius) const; + S32 pointInFrustum(const LLVector3 &point) const { return sphereInFrustum(point, 0.0f); } + S32 sphereInFrustumFull(const LLVector3 ¢er, const F32 radius) const { return sphereInFrustum(center, radius); } + S32 AABBInFrustum(const LLVector4a& center, const LLVector4a& radius, const LLPlane* planes = NULL); + S32 AABBInRegionFrustum(const LLVector4a& center, const LLVector4a& radius); + S32 AABBInFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius, const LLPlane* planes = NULL); + S32 AABBInRegionFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius); + + //does a quick 'n dirty sphere-sphere check + S32 sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius); + + // Returns height of object in pixels (must be height because field of view + // is based on window height). + F32 heightInPixels(const LLVector3 ¢er, F32 radius ) const; + + // return the distance from pos to camera if visible (-distance if not visible) + void setFixedDistance(F32 distance) { mFixedDistance = distance; } + + friend std::ostream& operator<<(std::ostream &s, const LLCamera &C); protected: - void calculateFrustumPlanes(); - void calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom); - void calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2); + void calculateFrustumPlanes(); + void calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom); + void calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2); } LL_ALIGN_POSTFIX(16); diff --git a/indra/llmath/llcoord.h b/indra/llmath/llcoord.h index 9b76268afd..120556c9fe 100644 --- a/indra/llmath/llcoord.h +++ b/indra/llmath/llcoord.h @@ -1,24 +1,24 @@ -/** +/** * @file llcoord.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$ */ @@ -37,10 +37,10 @@ typedef LLCoord<LL_COORD_TYPE_SCREEN> LLCoordScreen; struct LLCoordCommon { - LLCoordCommon(S32 x, S32 y) : mX(x), mY(y) {} - LLCoordCommon() : mX(0), mY(0) {} - S32 mX; - S32 mY; + LLCoordCommon(S32 x, S32 y) : mX(x), mY(y) {} + LLCoordCommon() : mX(0), mY(0) {} + S32 mX; + S32 mY; }; // A two-dimensional pixel value @@ -48,65 +48,65 @@ template<typename COORD_FRAME> class LLCoord : protected COORD_FRAME { public: - typedef LLCoord<COORD_FRAME> self_t; - typename COORD_FRAME::value_t mX; - typename COORD_FRAME::value_t mY; - - LLCoord(): mX(0), mY(0) - {} - LLCoord(typename COORD_FRAME::value_t x, typename COORD_FRAME::value_t y): mX(x), mY(y) - {} - - LLCoord(const LLCoordCommon& other) - { - COORD_FRAME::convertFromCommon(other); - } - - LLCoordCommon convert() const - { - return COORD_FRAME::convertToCommon(); - } - - void set(typename COORD_FRAME::value_t x, typename COORD_FRAME::value_t y) { mX = x; mY = y;} - bool operator==(const self_t& other) const { return mX == other.mX && mY == other.mY; } - bool operator!=(const self_t& other) const { return !(*this == other); } - - static const self_t& getTypedCoords(const COORD_FRAME& self) { return static_cast<const self_t&>(self); } - static self_t& getTypedCoords(COORD_FRAME& self) { return static_cast<self_t&>(self); } + typedef LLCoord<COORD_FRAME> self_t; + typename COORD_FRAME::value_t mX; + typename COORD_FRAME::value_t mY; + + LLCoord(): mX(0), mY(0) + {} + LLCoord(typename COORD_FRAME::value_t x, typename COORD_FRAME::value_t y): mX(x), mY(y) + {} + + LLCoord(const LLCoordCommon& other) + { + COORD_FRAME::convertFromCommon(other); + } + + LLCoordCommon convert() const + { + return COORD_FRAME::convertToCommon(); + } + + void set(typename COORD_FRAME::value_t x, typename COORD_FRAME::value_t y) { mX = x; mY = y;} + bool operator==(const self_t& other) const { return mX == other.mX && mY == other.mY; } + bool operator!=(const self_t& other) const { return !(*this == other); } + + static const self_t& getTypedCoords(const COORD_FRAME& self) { return static_cast<const self_t&>(self); } + static self_t& getTypedCoords(COORD_FRAME& self) { return static_cast<self_t&>(self); } }; -struct LL_COORD_TYPE_GL +struct LL_COORD_TYPE_GL { - typedef S32 value_t; - - LLCoordCommon convertToCommon() const - { - const LLCoordGL& self = LLCoordGL::getTypedCoords(*this); - return LLCoordCommon(self.mX, self.mY); - } - - void convertFromCommon(const LLCoordCommon& from) - { - LLCoordGL& self = LLCoordGL::getTypedCoords(*this); - self.mX = from.mX; - self.mY = from.mY; - } + typedef S32 value_t; + + LLCoordCommon convertToCommon() const + { + const LLCoordGL& self = LLCoordGL::getTypedCoords(*this); + return LLCoordCommon(self.mX, self.mY); + } + + void convertFromCommon(const LLCoordCommon& from) + { + LLCoordGL& self = LLCoordGL::getTypedCoords(*this); + self.mX = from.mX; + self.mY = from.mY; + } }; -struct LL_COORD_TYPE_WINDOW +struct LL_COORD_TYPE_WINDOW { - typedef S32 value_t; + typedef S32 value_t; - LLCoordCommon convertToCommon() const; - void convertFromCommon(const LLCoordCommon& from); + LLCoordCommon convertToCommon() const; + void convertFromCommon(const LLCoordCommon& from); }; -struct LL_COORD_TYPE_SCREEN +struct LL_COORD_TYPE_SCREEN { - typedef S32 value_t; + typedef S32 value_t; - LLCoordCommon convertToCommon() const; - void convertFromCommon(const LLCoordCommon& from); + LLCoordCommon convertToCommon() const; + void convertFromCommon(const LLCoordCommon& from); }; #endif diff --git a/indra/llmath/llcoordframe.cpp b/indra/llmath/llcoordframe.cpp index b25fd948f5..4d6276b2cd 100644 --- a/indra/llmath/llcoordframe.cpp +++ b/indra/llmath/llcoordframe.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llcoordframe.cpp * @brief LLCoordFrame 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$ */ @@ -49,168 +49,168 @@ } #ifndef X_AXIS - #define X_AXIS 1.0f,0.0f,0.0f - #define Y_AXIS 0.0f,1.0f,0.0f - #define Z_AXIS 0.0f,0.0f,1.0f + #define X_AXIS 1.0f,0.0f,0.0f + #define Y_AXIS 0.0f,1.0f,0.0f + #define Z_AXIS 0.0f,0.0f,1.0f #endif // Constructors LLCoordFrame::LLCoordFrame() : - mOrigin(0.f, 0.f, 0.f), - mXAxis(X_AXIS), - mYAxis(Y_AXIS), - mZAxis(Z_AXIS) + mOrigin(0.f, 0.f, 0.f), + mXAxis(X_AXIS), + mYAxis(Y_AXIS), + mZAxis(Z_AXIS) { } LLCoordFrame::LLCoordFrame(const LLVector3 &origin) : - mOrigin(origin), - mXAxis(X_AXIS), - mYAxis(Y_AXIS), - mZAxis(Z_AXIS) + mOrigin(origin), + mXAxis(X_AXIS), + mYAxis(Y_AXIS), + mZAxis(Z_AXIS) { CHECK_FINITE(mOrigin); } LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLVector3 &direction) : - mOrigin(origin) + mOrigin(origin) { - lookDir(direction); - + lookDir(direction); + CHECK_FINITE_OBJ(); } LLCoordFrame::LLCoordFrame(const LLVector3 &x_axis, - const LLVector3 &y_axis, - const LLVector3 &z_axis) : - mOrigin(0.f, 0.f, 0.f), - mXAxis(x_axis), - mYAxis(y_axis), - mZAxis(z_axis) + const LLVector3 &y_axis, + const LLVector3 &z_axis) : + mOrigin(0.f, 0.f, 0.f), + mXAxis(x_axis), + mYAxis(y_axis), + mZAxis(z_axis) { - CHECK_FINITE_OBJ(); + CHECK_FINITE_OBJ(); } LLCoordFrame::LLCoordFrame(const LLVector3 &origin, - const LLVector3 &x_axis, - const LLVector3 &y_axis, - const LLVector3 &z_axis) : - mOrigin(origin), - mXAxis(x_axis), - mYAxis(y_axis), - mZAxis(z_axis) + const LLVector3 &x_axis, + const LLVector3 &y_axis, + const LLVector3 &z_axis) : + mOrigin(origin), + mXAxis(x_axis), + mYAxis(y_axis), + mZAxis(z_axis) { - CHECK_FINITE_OBJ(); + CHECK_FINITE_OBJ(); } -LLCoordFrame::LLCoordFrame(const LLVector3 &origin, - const LLMatrix3 &rotation) : - mOrigin(origin), - mXAxis(rotation.mMatrix[VX]), - mYAxis(rotation.mMatrix[VY]), - mZAxis(rotation.mMatrix[VZ]) +LLCoordFrame::LLCoordFrame(const LLVector3 &origin, + const LLMatrix3 &rotation) : + mOrigin(origin), + mXAxis(rotation.mMatrix[VX]), + mYAxis(rotation.mMatrix[VY]), + mZAxis(rotation.mMatrix[VZ]) { - CHECK_FINITE_OBJ(); + CHECK_FINITE_OBJ(); } LLCoordFrame::LLCoordFrame(const LLQuaternion &q) : - mOrigin(0.f, 0.f, 0.f) + mOrigin(0.f, 0.f, 0.f) { - LLMatrix3 rotation_matrix(q); - mXAxis.setVec(rotation_matrix.mMatrix[VX]); - mYAxis.setVec(rotation_matrix.mMatrix[VY]); - mZAxis.setVec(rotation_matrix.mMatrix[VZ]); + LLMatrix3 rotation_matrix(q); + mXAxis.setVec(rotation_matrix.mMatrix[VX]); + mYAxis.setVec(rotation_matrix.mMatrix[VY]); + mZAxis.setVec(rotation_matrix.mMatrix[VZ]); - CHECK_FINITE_OBJ(); + CHECK_FINITE_OBJ(); } LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLQuaternion &q) : - mOrigin(origin) + mOrigin(origin) { - LLMatrix3 rotation_matrix(q); - mXAxis.setVec(rotation_matrix.mMatrix[VX]); - mYAxis.setVec(rotation_matrix.mMatrix[VY]); - mZAxis.setVec(rotation_matrix.mMatrix[VZ]); + LLMatrix3 rotation_matrix(q); + mXAxis.setVec(rotation_matrix.mMatrix[VX]); + mYAxis.setVec(rotation_matrix.mMatrix[VY]); + mZAxis.setVec(rotation_matrix.mMatrix[VZ]); - CHECK_FINITE_OBJ(); + CHECK_FINITE_OBJ(); } LLCoordFrame::LLCoordFrame(const LLMatrix4 &mat) : - mOrigin(mat.mMatrix[VW]), - mXAxis(mat.mMatrix[VX]), - mYAxis(mat.mMatrix[VY]), - mZAxis(mat.mMatrix[VZ]) + mOrigin(mat.mMatrix[VW]), + mXAxis(mat.mMatrix[VX]), + mYAxis(mat.mMatrix[VY]), + mZAxis(mat.mMatrix[VZ]) { - CHECK_FINITE_OBJ(); + CHECK_FINITE_OBJ(); } // The folowing two constructors are dangerous due to implicit casting and have been disabled - SJB /* LLCoordFrame::LLCoordFrame(const F32 *origin, const F32 *rotation) : - mOrigin(origin), - mXAxis(rotation+3*VX), - mYAxis(rotation+3*VY), - mZAxis(rotation+3*VZ) + mOrigin(origin), + mXAxis(rotation+3*VX), + mYAxis(rotation+3*VY), + mZAxis(rotation+3*VZ) { - CHECK_FINITE_OBJ(); + CHECK_FINITE_OBJ(); } */ /* LLCoordFrame::LLCoordFrame(const F32 *origin_and_rotation) : - mOrigin(origin_and_rotation), - mXAxis(origin_and_rotation + 3*(VX+1)), - mYAxis(origin_and_rotation + 3*(VY+1)), - mZAxis(origin_and_rotation + 3*(VZ+1)) + mOrigin(origin_and_rotation), + mXAxis(origin_and_rotation + 3*(VX+1)), + mYAxis(origin_and_rotation + 3*(VY+1)), + mZAxis(origin_and_rotation + 3*(VZ+1)) { - CHECK_FINITE_OBJ(); + CHECK_FINITE_OBJ(); } */ -void LLCoordFrame::reset() +void LLCoordFrame::reset() { - mOrigin.setVec(0.0f, 0.0f, 0.0f); - resetAxes(); + mOrigin.setVec(0.0f, 0.0f, 0.0f); + resetAxes(); } void LLCoordFrame::resetAxes() { - mXAxis.setVec(1.0f, 0.0f, 0.0f); - mYAxis.setVec(0.0f, 1.0f, 0.0f); - mZAxis.setVec(0.0f, 0.0f, 1.0f); + mXAxis.setVec(1.0f, 0.0f, 0.0f); + mYAxis.setVec(0.0f, 1.0f, 0.0f); + mZAxis.setVec(0.0f, 0.0f, 1.0f); } // setOrigin() member functions set mOrigin -void LLCoordFrame::setOrigin(F32 x, F32 y, F32 z) +void LLCoordFrame::setOrigin(F32 x, F32 y, F32 z) { - mOrigin.setVec(x, y, z); + mOrigin.setVec(x, y, z); CHECK_FINITE(mOrigin); } void LLCoordFrame::setOrigin(const LLVector3 &new_origin) { - mOrigin = new_origin; - CHECK_FINITE(mOrigin); + mOrigin = new_origin; + CHECK_FINITE(mOrigin); } void LLCoordFrame::setOrigin(const F32 *origin) { - mOrigin.mV[VX] = *(origin + VX); - mOrigin.mV[VY] = *(origin + VY); - mOrigin.mV[VZ] = *(origin + VZ); + mOrigin.mV[VX] = *(origin + VX); + mOrigin.mV[VY] = *(origin + VY); + mOrigin.mV[VZ] = *(origin + VZ); CHECK_FINITE(mOrigin); } void LLCoordFrame::setOrigin(const LLCoordFrame &frame) { - mOrigin = frame.getOrigin(); + mOrigin = frame.getOrigin(); CHECK_FINITE(mOrigin); } @@ -218,79 +218,79 @@ void LLCoordFrame::setOrigin(const LLCoordFrame &frame) // the arguments are orthogonal and normalized. void LLCoordFrame::setAxes(const LLVector3 &x_axis, - const LLVector3 &y_axis, - const LLVector3 &z_axis) + const LLVector3 &y_axis, + const LLVector3 &z_axis) { - mXAxis = x_axis; - mYAxis = y_axis; - mZAxis = z_axis; - CHECK_FINITE_OBJ(); + mXAxis = x_axis; + mYAxis = y_axis; + mZAxis = z_axis; + CHECK_FINITE_OBJ(); } void LLCoordFrame::setAxes(const LLMatrix3 &rotation_matrix) { - mXAxis.setVec(rotation_matrix.mMatrix[VX]); - mYAxis.setVec(rotation_matrix.mMatrix[VY]); - mZAxis.setVec(rotation_matrix.mMatrix[VZ]); - CHECK_FINITE_OBJ(); + mXAxis.setVec(rotation_matrix.mMatrix[VX]); + mYAxis.setVec(rotation_matrix.mMatrix[VY]); + mZAxis.setVec(rotation_matrix.mMatrix[VZ]); + CHECK_FINITE_OBJ(); } void LLCoordFrame::setAxes(const LLQuaternion &q ) { - LLMatrix3 rotation_matrix(q); - setAxes(rotation_matrix); - CHECK_FINITE_OBJ(); + LLMatrix3 rotation_matrix(q); + setAxes(rotation_matrix); + CHECK_FINITE_OBJ(); } -void LLCoordFrame::setAxes( const F32 *rotation_matrix ) +void LLCoordFrame::setAxes( const F32 *rotation_matrix ) { - mXAxis.mV[VX] = *(rotation_matrix + 3*VX + VX); - mXAxis.mV[VY] = *(rotation_matrix + 3*VX + VY); - mXAxis.mV[VZ] = *(rotation_matrix + 3*VX + VZ); - mYAxis.mV[VX] = *(rotation_matrix + 3*VY + VX); - mYAxis.mV[VY] = *(rotation_matrix + 3*VY + VY); - mYAxis.mV[VZ] = *(rotation_matrix + 3*VY + VZ); - mZAxis.mV[VX] = *(rotation_matrix + 3*VZ + VX); - mZAxis.mV[VY] = *(rotation_matrix + 3*VZ + VY); - mZAxis.mV[VZ] = *(rotation_matrix + 3*VZ + VZ); + mXAxis.mV[VX] = *(rotation_matrix + 3*VX + VX); + mXAxis.mV[VY] = *(rotation_matrix + 3*VX + VY); + mXAxis.mV[VZ] = *(rotation_matrix + 3*VX + VZ); + mYAxis.mV[VX] = *(rotation_matrix + 3*VY + VX); + mYAxis.mV[VY] = *(rotation_matrix + 3*VY + VY); + mYAxis.mV[VZ] = *(rotation_matrix + 3*VY + VZ); + mZAxis.mV[VX] = *(rotation_matrix + 3*VZ + VX); + mZAxis.mV[VY] = *(rotation_matrix + 3*VZ + VY); + mZAxis.mV[VZ] = *(rotation_matrix + 3*VZ + VZ); - CHECK_FINITE_OBJ(); + CHECK_FINITE_OBJ(); } void LLCoordFrame::setAxes(const LLCoordFrame &frame) { - mXAxis = frame.getXAxis(); - mYAxis = frame.getYAxis(); - mZAxis = frame.getZAxis(); - CHECK_FINITE_OBJ(); + mXAxis = frame.getXAxis(); + mYAxis = frame.getYAxis(); + mZAxis = frame.getZAxis(); + CHECK_FINITE_OBJ(); } // translate() member functions move mOrigin to a relative position void LLCoordFrame::translate(F32 x, F32 y, F32 z) { - mOrigin.mV[VX] += x; - mOrigin.mV[VY] += y; - mOrigin.mV[VZ] += z; + mOrigin.mV[VX] += x; + mOrigin.mV[VY] += y; + mOrigin.mV[VZ] += z; CHECK_FINITE(mOrigin); } void LLCoordFrame::translate(const LLVector3 &v) { - mOrigin += v; + mOrigin += v; CHECK_FINITE(mOrigin); } void LLCoordFrame::translate(const F32 *origin) { - mOrigin.mV[VX] += *(origin + VX); - mOrigin.mV[VY] += *(origin + VY); - mOrigin.mV[VZ] += *(origin + VZ); - CHECK_FINITE(mOrigin); + mOrigin.mV[VX] += *(origin + VX); + mOrigin.mV[VY] += *(origin + VY); + mOrigin.mV[VZ] += *(origin + VZ); + CHECK_FINITE(mOrigin); } @@ -298,58 +298,58 @@ void LLCoordFrame::translate(const F32 *origin) void LLCoordFrame::rotate(F32 angle, F32 x, F32 y, F32 z) { - LLQuaternion q(angle, LLVector3(x,y,z)); - rotate(q); + LLQuaternion q(angle, LLVector3(x,y,z)); + rotate(q); CHECK_FINITE_OBJ(); } void LLCoordFrame::rotate(F32 angle, const LLVector3 &rotation_axis) { - LLQuaternion q(angle, rotation_axis); - rotate(q); + LLQuaternion q(angle, rotation_axis); + rotate(q); CHECK_FINITE_OBJ(); } void LLCoordFrame::rotate(const LLQuaternion &q) { - LLMatrix3 rotation_matrix(q); - rotate(rotation_matrix); + LLMatrix3 rotation_matrix(q); + rotate(rotation_matrix); CHECK_FINITE_OBJ(); } void LLCoordFrame::rotate(const LLMatrix3 &rotation_matrix) { - mXAxis.rotVec(rotation_matrix); - mYAxis.rotVec(rotation_matrix); - orthonormalize(); + mXAxis.rotVec(rotation_matrix); + mYAxis.rotVec(rotation_matrix); + orthonormalize(); CHECK_FINITE_OBJ(); } void LLCoordFrame::roll(F32 angle) { - LLQuaternion q(angle, mXAxis); - LLMatrix3 rotation_matrix(q); - rotate(rotation_matrix); + LLQuaternion q(angle, mXAxis); + LLMatrix3 rotation_matrix(q); + rotate(rotation_matrix); CHECK_FINITE_OBJ(); } void LLCoordFrame::pitch(F32 angle) { - LLQuaternion q(angle, mYAxis); - LLMatrix3 rotation_matrix(q); - rotate(rotation_matrix); - CHECK_FINITE_OBJ(); + LLQuaternion q(angle, mYAxis); + LLMatrix3 rotation_matrix(q); + rotate(rotation_matrix); + CHECK_FINITE_OBJ(); } void LLCoordFrame::yaw(F32 angle) { - LLQuaternion q(angle, mZAxis); - LLMatrix3 rotation_matrix(q); - rotate(rotation_matrix); + LLQuaternion q(angle, mZAxis); + LLMatrix3 rotation_matrix(q); + rotate(rotation_matrix); CHECK_FINITE_OBJ(); } @@ -358,61 +358,61 @@ void LLCoordFrame::yaw(F32 angle) LLQuaternion LLCoordFrame::getQuaternion() const { - LLQuaternion quat(mXAxis, mYAxis, mZAxis); - return quat; + LLQuaternion quat(mXAxis, mYAxis, mZAxis); + return quat; } void LLCoordFrame::getMatrixToLocal(LLMatrix4& mat) const { - mat.setFwdCol(mXAxis); - mat.setLeftCol(mYAxis); - mat.setUpCol(mZAxis); + mat.setFwdCol(mXAxis); + mat.setLeftCol(mYAxis); + mat.setUpCol(mZAxis); - mat.mMatrix[3][0] = -(mOrigin * LLVector3(mat.mMatrix[0][0], mat.mMatrix[1][0], mat.mMatrix[2][0])); - mat.mMatrix[3][1] = -(mOrigin * LLVector3(mat.mMatrix[0][1], mat.mMatrix[1][1], mat.mMatrix[2][1])); - mat.mMatrix[3][2] = -(mOrigin * LLVector3(mat.mMatrix[0][2], mat.mMatrix[1][2], mat.mMatrix[2][2])); + mat.mMatrix[3][0] = -(mOrigin * LLVector3(mat.mMatrix[0][0], mat.mMatrix[1][0], mat.mMatrix[2][0])); + mat.mMatrix[3][1] = -(mOrigin * LLVector3(mat.mMatrix[0][1], mat.mMatrix[1][1], mat.mMatrix[2][1])); + mat.mMatrix[3][2] = -(mOrigin * LLVector3(mat.mMatrix[0][2], mat.mMatrix[1][2], mat.mMatrix[2][2])); } void LLCoordFrame::getRotMatrixToParent(LLMatrix4& mat) const { - // Note: moves into CFR - mat.setFwdRow( -mYAxis ); - mat.setLeftRow( mZAxis ); - mat.setUpRow( -mXAxis ); + // Note: moves into CFR + mat.setFwdRow( -mYAxis ); + mat.setLeftRow( mZAxis ); + mat.setUpRow( -mXAxis ); } size_t LLCoordFrame::writeOrientation(char *buffer) const { - memcpy(buffer, mOrigin.mV, 3*sizeof(F32)); /*Flawfinder: ignore */ - buffer += 3*sizeof(F32); - memcpy(buffer, mXAxis.mV, 3*sizeof(F32)); /*Flawfinder: ignore */ - buffer += 3*sizeof(F32); - memcpy(buffer, mYAxis.mV, 3*sizeof(F32));/*Flawfinder: ignore */ - buffer += 3*sizeof(F32); - memcpy(buffer, mZAxis.mV, 3*sizeof(F32)); /*Flawfinder: ignore */ - return 12*sizeof(F32); + memcpy(buffer, mOrigin.mV, 3*sizeof(F32)); /*Flawfinder: ignore */ + buffer += 3*sizeof(F32); + memcpy(buffer, mXAxis.mV, 3*sizeof(F32)); /*Flawfinder: ignore */ + buffer += 3*sizeof(F32); + memcpy(buffer, mYAxis.mV, 3*sizeof(F32));/*Flawfinder: ignore */ + buffer += 3*sizeof(F32); + memcpy(buffer, mZAxis.mV, 3*sizeof(F32)); /*Flawfinder: ignore */ + return 12*sizeof(F32); } size_t LLCoordFrame::readOrientation(const char *buffer) { - memcpy(mOrigin.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ - buffer += 3*sizeof(F32); - memcpy(mXAxis.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ - buffer += 3*sizeof(F32); - memcpy(mYAxis.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ - buffer += 3*sizeof(F32); - memcpy(mZAxis.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ - - if( !isFinite() ) - { - reset(); - LL_WARNS() << "Non Finite in LLCoordFrame::readOrientation()" << LL_ENDL; - } + memcpy(mOrigin.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ + buffer += 3*sizeof(F32); + memcpy(mXAxis.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ + buffer += 3*sizeof(F32); + memcpy(mYAxis.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ + buffer += 3*sizeof(F32); + memcpy(mZAxis.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ + + if( !isFinite() ) + { + reset(); + LL_WARNS() << "Non Finite in LLCoordFrame::readOrientation()" << LL_ENDL; + } - return 12*sizeof(F32); + return 12*sizeof(F32); } @@ -420,107 +420,107 @@ size_t LLCoordFrame::readOrientation(const char *buffer) LLVector3 LLCoordFrame::rotateToLocal(const LLVector3 &absolute_vector) const { - LLVector3 local_vector(mXAxis * absolute_vector, - mYAxis * absolute_vector, - mZAxis * absolute_vector); - return local_vector; + LLVector3 local_vector(mXAxis * absolute_vector, + mYAxis * absolute_vector, + mZAxis * absolute_vector); + return local_vector; } LLVector4 LLCoordFrame::rotateToLocal(const LLVector4 &absolute_vector) const { - LLVector4 local_vector; - local_vector.mV[VX] = mXAxis.mV[VX] * absolute_vector.mV[VX] + - mXAxis.mV[VY] * absolute_vector.mV[VY] + - mXAxis.mV[VZ] * absolute_vector.mV[VZ]; - local_vector.mV[VY] = mYAxis.mV[VX] * absolute_vector.mV[VX] + - mYAxis.mV[VY] * absolute_vector.mV[VY] + - mYAxis.mV[VZ] * absolute_vector.mV[VZ]; - local_vector.mV[VZ] = mZAxis.mV[VX] * absolute_vector.mV[VX] + - mZAxis.mV[VY] * absolute_vector.mV[VY] + - mZAxis.mV[VZ] * absolute_vector.mV[VZ]; - local_vector.mV[VW] = absolute_vector.mV[VW]; - return local_vector; + LLVector4 local_vector; + local_vector.mV[VX] = mXAxis.mV[VX] * absolute_vector.mV[VX] + + mXAxis.mV[VY] * absolute_vector.mV[VY] + + mXAxis.mV[VZ] * absolute_vector.mV[VZ]; + local_vector.mV[VY] = mYAxis.mV[VX] * absolute_vector.mV[VX] + + mYAxis.mV[VY] * absolute_vector.mV[VY] + + mYAxis.mV[VZ] * absolute_vector.mV[VZ]; + local_vector.mV[VZ] = mZAxis.mV[VX] * absolute_vector.mV[VX] + + mZAxis.mV[VY] * absolute_vector.mV[VY] + + mZAxis.mV[VZ] * absolute_vector.mV[VZ]; + local_vector.mV[VW] = absolute_vector.mV[VW]; + return local_vector; } LLVector3 LLCoordFrame::rotateToAbsolute(const LLVector3 &local_vector) const { - LLVector3 absolute_vector; - absolute_vector.mV[VX] = mXAxis.mV[VX] * local_vector.mV[VX] + - mYAxis.mV[VX] * local_vector.mV[VY] + - mZAxis.mV[VX] * local_vector.mV[VZ]; - absolute_vector.mV[VY] = mXAxis.mV[VY] * local_vector.mV[VX] + - mYAxis.mV[VY] * local_vector.mV[VY] + - mZAxis.mV[VY] * local_vector.mV[VZ]; - absolute_vector.mV[VZ] = mXAxis.mV[VZ] * local_vector.mV[VX] + - mYAxis.mV[VZ] * local_vector.mV[VY] + - mZAxis.mV[VZ] * local_vector.mV[VZ]; - return absolute_vector; + LLVector3 absolute_vector; + absolute_vector.mV[VX] = mXAxis.mV[VX] * local_vector.mV[VX] + + mYAxis.mV[VX] * local_vector.mV[VY] + + mZAxis.mV[VX] * local_vector.mV[VZ]; + absolute_vector.mV[VY] = mXAxis.mV[VY] * local_vector.mV[VX] + + mYAxis.mV[VY] * local_vector.mV[VY] + + mZAxis.mV[VY] * local_vector.mV[VZ]; + absolute_vector.mV[VZ] = mXAxis.mV[VZ] * local_vector.mV[VX] + + mYAxis.mV[VZ] * local_vector.mV[VY] + + mZAxis.mV[VZ] * local_vector.mV[VZ]; + return absolute_vector; } LLVector4 LLCoordFrame::rotateToAbsolute(const LLVector4 &local_vector) const { - LLVector4 absolute_vector; - absolute_vector.mV[VX] = mXAxis.mV[VX] * local_vector.mV[VX] + - mYAxis.mV[VX] * local_vector.mV[VY] + - mZAxis.mV[VX] * local_vector.mV[VZ]; - absolute_vector.mV[VY] = mXAxis.mV[VY] * local_vector.mV[VX] + - mYAxis.mV[VY] * local_vector.mV[VY] + - mZAxis.mV[VY] * local_vector.mV[VZ]; - absolute_vector.mV[VZ] = mXAxis.mV[VZ] * local_vector.mV[VX] + - mYAxis.mV[VZ] * local_vector.mV[VY] + - mZAxis.mV[VZ] * local_vector.mV[VZ]; - absolute_vector.mV[VW] = local_vector[VW]; - return absolute_vector; + LLVector4 absolute_vector; + absolute_vector.mV[VX] = mXAxis.mV[VX] * local_vector.mV[VX] + + mYAxis.mV[VX] * local_vector.mV[VY] + + mZAxis.mV[VX] * local_vector.mV[VZ]; + absolute_vector.mV[VY] = mXAxis.mV[VY] * local_vector.mV[VX] + + mYAxis.mV[VY] * local_vector.mV[VY] + + mZAxis.mV[VY] * local_vector.mV[VZ]; + absolute_vector.mV[VZ] = mXAxis.mV[VZ] * local_vector.mV[VX] + + mYAxis.mV[VZ] * local_vector.mV[VY] + + mZAxis.mV[VZ] * local_vector.mV[VZ]; + absolute_vector.mV[VW] = local_vector[VW]; + return absolute_vector; } void LLCoordFrame::orthonormalize() // Makes sure the axes are orthogonal and normalized. { - mXAxis.normVec(); // X is renormalized - mYAxis -= mXAxis * (mXAxis * mYAxis); // Y remains in X-Y plane - mYAxis.normVec(); // Y is normalized - mZAxis = mXAxis % mYAxis; // Z = X cross Y + mXAxis.normVec(); // X is renormalized + mYAxis -= mXAxis * (mXAxis * mYAxis); // Y remains in X-Y plane + mYAxis.normVec(); // Y is normalized + mZAxis = mXAxis % mYAxis; // Z = X cross Y } LLVector3 LLCoordFrame::transformToLocal(const LLVector3 &absolute_vector) const { - return rotateToLocal(absolute_vector - mOrigin); + return rotateToLocal(absolute_vector - mOrigin); } LLVector4 LLCoordFrame::transformToLocal(const LLVector4 &absolute_vector) const { - LLVector4 local_vector(absolute_vector); - local_vector.mV[VX] -= mOrigin.mV[VX]; - local_vector.mV[VY] -= mOrigin.mV[VY]; - local_vector.mV[VZ] -= mOrigin.mV[VZ]; - return rotateToLocal(local_vector); + LLVector4 local_vector(absolute_vector); + local_vector.mV[VX] -= mOrigin.mV[VX]; + local_vector.mV[VY] -= mOrigin.mV[VY]; + local_vector.mV[VZ] -= mOrigin.mV[VZ]; + return rotateToLocal(local_vector); } LLVector3 LLCoordFrame::transformToAbsolute(const LLVector3 &local_vector) const { - return (rotateToAbsolute(local_vector) + mOrigin); + return (rotateToAbsolute(local_vector) + mOrigin); } LLVector4 LLCoordFrame::transformToAbsolute(const LLVector4 &local_vector) const { - LLVector4 absolute_vector; - absolute_vector = rotateToAbsolute(local_vector); - absolute_vector.mV[VX] += mOrigin.mV[VX]; - absolute_vector.mV[VY] += mOrigin.mV[VY]; - absolute_vector.mV[VZ] += mOrigin.mV[VZ]; - return absolute_vector; + LLVector4 absolute_vector; + absolute_vector = rotateToAbsolute(local_vector); + absolute_vector.mV[VX] += mOrigin.mV[VX]; + absolute_vector.mV[VY] += mOrigin.mV[VY]; + absolute_vector.mV[VZ] += mOrigin.mV[VZ]; + return absolute_vector; } -// This is how you combine a translation and rotation of a +// This is how you combine a translation and rotation of a // coordinate frame to get an OpenGL transformation matrix: // // translation * rotation = transformation matrix @@ -531,129 +531,129 @@ LLVector4 LLCoordFrame::transformToAbsolute(const LLVector4 &local_vector) const // V | 0 0 1 0 | | c f i 0 | | c f i 0 | // |-x -y -z 1 | | 0 0 0 1 | |-(ax+by+cz) -(dx+ey+fz) -(gx+hy+iz) 1 | // -// where {a,b,c} = x-axis -// {d,e,f} = y-axis -// {g,h,i} = z-axis +// where {a,b,c} = x-axis +// {d,e,f} = y-axis +// {g,h,i} = z-axis // {x,y,z} = origin void LLCoordFrame::getOpenGLTranslation(F32 *ogl_matrix) const { - *(ogl_matrix + 0) = 1.0f; - *(ogl_matrix + 1) = 0.0f; - *(ogl_matrix + 2) = 0.0f; - *(ogl_matrix + 3) = 0.0f; + *(ogl_matrix + 0) = 1.0f; + *(ogl_matrix + 1) = 0.0f; + *(ogl_matrix + 2) = 0.0f; + *(ogl_matrix + 3) = 0.0f; - *(ogl_matrix + 4) = 0.0f; - *(ogl_matrix + 5) = 1.0f; - *(ogl_matrix + 6) = 0.0f; - *(ogl_matrix + 7) = 0.0f; + *(ogl_matrix + 4) = 0.0f; + *(ogl_matrix + 5) = 1.0f; + *(ogl_matrix + 6) = 0.0f; + *(ogl_matrix + 7) = 0.0f; - *(ogl_matrix + 8) = 0.0f; - *(ogl_matrix + 9) = 0.0f; - *(ogl_matrix + 10) = 1.0f; - *(ogl_matrix + 11) = 0.0f; + *(ogl_matrix + 8) = 0.0f; + *(ogl_matrix + 9) = 0.0f; + *(ogl_matrix + 10) = 1.0f; + *(ogl_matrix + 11) = 0.0f; - *(ogl_matrix + 12) = -mOrigin.mV[VX]; - *(ogl_matrix + 13) = -mOrigin.mV[VY]; - *(ogl_matrix + 14) = -mOrigin.mV[VZ]; - *(ogl_matrix + 15) = 1.0f; + *(ogl_matrix + 12) = -mOrigin.mV[VX]; + *(ogl_matrix + 13) = -mOrigin.mV[VY]; + *(ogl_matrix + 14) = -mOrigin.mV[VZ]; + *(ogl_matrix + 15) = 1.0f; } void LLCoordFrame::getOpenGLRotation(F32 *ogl_matrix) const { - *(ogl_matrix + 0) = mXAxis.mV[VX]; - *(ogl_matrix + 4) = mXAxis.mV[VY]; - *(ogl_matrix + 8) = mXAxis.mV[VZ]; + *(ogl_matrix + 0) = mXAxis.mV[VX]; + *(ogl_matrix + 4) = mXAxis.mV[VY]; + *(ogl_matrix + 8) = mXAxis.mV[VZ]; - *(ogl_matrix + 1) = mYAxis.mV[VX]; - *(ogl_matrix + 5) = mYAxis.mV[VY]; - *(ogl_matrix + 9) = mYAxis.mV[VZ]; + *(ogl_matrix + 1) = mYAxis.mV[VX]; + *(ogl_matrix + 5) = mYAxis.mV[VY]; + *(ogl_matrix + 9) = mYAxis.mV[VZ]; - *(ogl_matrix + 2) = mZAxis.mV[VX]; - *(ogl_matrix + 6) = mZAxis.mV[VY]; - *(ogl_matrix + 10) = mZAxis.mV[VZ]; + *(ogl_matrix + 2) = mZAxis.mV[VX]; + *(ogl_matrix + 6) = mZAxis.mV[VY]; + *(ogl_matrix + 10) = mZAxis.mV[VZ]; - *(ogl_matrix + 3) = 0.0f; - *(ogl_matrix + 7) = 0.0f; - *(ogl_matrix + 11) = 0.0f; + *(ogl_matrix + 3) = 0.0f; + *(ogl_matrix + 7) = 0.0f; + *(ogl_matrix + 11) = 0.0f; - *(ogl_matrix + 12) = 0.0f; - *(ogl_matrix + 13) = 0.0f; - *(ogl_matrix + 14) = 0.0f; - *(ogl_matrix + 15) = 1.0f; + *(ogl_matrix + 12) = 0.0f; + *(ogl_matrix + 13) = 0.0f; + *(ogl_matrix + 14) = 0.0f; + *(ogl_matrix + 15) = 1.0f; } void LLCoordFrame::getOpenGLTransform(F32 *ogl_matrix) const { - *(ogl_matrix + 0) = mXAxis.mV[VX]; - *(ogl_matrix + 4) = mXAxis.mV[VY]; - *(ogl_matrix + 8) = mXAxis.mV[VZ]; - *(ogl_matrix + 12) = -mOrigin * mXAxis; + *(ogl_matrix + 0) = mXAxis.mV[VX]; + *(ogl_matrix + 4) = mXAxis.mV[VY]; + *(ogl_matrix + 8) = mXAxis.mV[VZ]; + *(ogl_matrix + 12) = -mOrigin * mXAxis; - *(ogl_matrix + 1) = mYAxis.mV[VX]; - *(ogl_matrix + 5) = mYAxis.mV[VY]; - *(ogl_matrix + 9) = mYAxis.mV[VZ]; - *(ogl_matrix + 13) = -mOrigin * mYAxis; + *(ogl_matrix + 1) = mYAxis.mV[VX]; + *(ogl_matrix + 5) = mYAxis.mV[VY]; + *(ogl_matrix + 9) = mYAxis.mV[VZ]; + *(ogl_matrix + 13) = -mOrigin * mYAxis; - *(ogl_matrix + 2) = mZAxis.mV[VX]; - *(ogl_matrix + 6) = mZAxis.mV[VY]; - *(ogl_matrix + 10) = mZAxis.mV[VZ]; - *(ogl_matrix + 14) = -mOrigin * mZAxis; + *(ogl_matrix + 2) = mZAxis.mV[VX]; + *(ogl_matrix + 6) = mZAxis.mV[VY]; + *(ogl_matrix + 10) = mZAxis.mV[VZ]; + *(ogl_matrix + 14) = -mOrigin * mZAxis; - *(ogl_matrix + 3) = 0.0f; - *(ogl_matrix + 7) = 0.0f; - *(ogl_matrix + 11) = 0.0f; - *(ogl_matrix + 15) = 1.0f; + *(ogl_matrix + 3) = 0.0f; + *(ogl_matrix + 7) = 0.0f; + *(ogl_matrix + 11) = 0.0f; + *(ogl_matrix + 15) = 1.0f; } // at and up_direction are presumed to be normalized void LLCoordFrame::lookDir(const LLVector3 &at, const LLVector3 &up_direction) { - // Make sure 'at' and 'up_direction' are not parallel - // and that neither are zero-length vectors - LLVector3 left(up_direction % at); - if (left.isNull()) - { - //tweak lookat pos so we don't get a degenerate matrix - LLVector3 tempat(at[VX] + 0.01f, at[VY], at[VZ]); - tempat.normVec(); - left = (up_direction % tempat); - } - left.normVec(); + // Make sure 'at' and 'up_direction' are not parallel + // and that neither are zero-length vectors + LLVector3 left(up_direction % at); + if (left.isNull()) + { + //tweak lookat pos so we don't get a degenerate matrix + LLVector3 tempat(at[VX] + 0.01f, at[VY], at[VZ]); + tempat.normVec(); + left = (up_direction % tempat); + } + left.normVec(); - LLVector3 up = at % left; + LLVector3 up = at % left; - if (at.isFinite() && left.isFinite() && up.isFinite()) - { - setAxes(at, left, up); - } + if (at.isFinite() && left.isFinite() && up.isFinite()) + { + setAxes(at, left, up); + } } void LLCoordFrame::lookDir(const LLVector3 &xuv) { - static LLVector3 up_direction(0.0f, 0.0f, 1.0f); - lookDir(xuv, up_direction); + static LLVector3 up_direction(0.0f, 0.0f, 1.0f); + lookDir(xuv, up_direction); } void LLCoordFrame::lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest, const LLVector3 &up_direction) { - setOrigin(origin); - LLVector3 at(point_of_interest - origin); - at.normVec(); - lookDir(at, up_direction); + setOrigin(origin); + LLVector3 at(point_of_interest - origin); + at.normVec(); + lookDir(at, up_direction); } void LLCoordFrame::lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest) { - static LLVector3 up_direction(0.0f, 0.0f, 1.0f); + static LLVector3 up_direction(0.0f, 0.0f, 1.0f); - setOrigin(origin); - LLVector3 at(point_of_interest - origin); - at.normVec(); - lookDir(at, up_direction); + setOrigin(origin); + LLVector3 at(point_of_interest - origin); + at.normVec(); + lookDir(at, up_direction); } @@ -661,13 +661,13 @@ void LLCoordFrame::lookAt(const LLVector3 &origin, const LLVector3 &point_of_int std::ostream& operator<<(std::ostream &s, const LLCoordFrame &C) { - s << "{ " - << " origin = " << C.mOrigin - << " x_axis = " << C.mXAxis - << " y_axis = " << C.mYAxis - << " z_axis = " << C.mZAxis - << " }"; - return s; + s << "{ " + << " origin = " << C.mOrigin + << " x_axis = " << C.mXAxis + << " y_axis = " << C.mYAxis + << " z_axis = " << C.mZAxis + << " }"; + return s; } diff --git a/indra/llmath/llcoordframe.h b/indra/llmath/llcoordframe.h index 1d3f4f7e3e..a21d39bbc0 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 a107d301d8..2df3ae8561 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/llline.cpp b/indra/llmath/llline.cpp index cfee315b55..70c68dd42f 100644 --- a/indra/llmath/llline.cpp +++ b/indra/llmath/llline.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llline.cpp * @author Andrew Meadows * @brief Simple line class that can compute nearest approach between two lines @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2006&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$ */ @@ -33,87 +33,87 @@ const F32 SOME_VERY_SMALL_NUMBER = 1.0e-8f; LLLine::LLLine() -: mPoint(0.f, 0.f, 0.f), - mDirection(1.f, 0.f, 0.f) +: mPoint(0.f, 0.f, 0.f), + mDirection(1.f, 0.f, 0.f) { } LLLine::LLLine( const LLVector3& first_point, const LLVector3& second_point ) { - setPoints(first_point, second_point); + setPoints(first_point, second_point); } void LLLine::setPoints( const LLVector3& first_point, const LLVector3& second_point ) { - mPoint = first_point; - mDirection = second_point - first_point; - mDirection.normalize(); + mPoint = first_point; + mDirection = second_point - first_point; + mDirection.normalize(); } void LLLine::setPointDirection( const LLVector3& first_point, const LLVector3& second_point ) { - setPoints(first_point, first_point + second_point); + setPoints(first_point, first_point + second_point); } bool LLLine::intersects( const LLVector3& point, F32 radius ) const { - LLVector3 other_direction = point - mPoint; - LLVector3 nearest_point = mPoint + mDirection * (other_direction * mDirection); - F32 nearest_approach = (nearest_point - point).length(); - return (nearest_approach <= radius); + LLVector3 other_direction = point - mPoint; + LLVector3 nearest_point = mPoint + mDirection * (other_direction * mDirection); + F32 nearest_approach = (nearest_point - point).length(); + return (nearest_approach <= radius); } // returns the point on this line that is closest to some_point LLVector3 LLLine::nearestApproach( const LLVector3& some_point ) const { - return (mPoint + mDirection * ((some_point - mPoint) * mDirection)); + return (mPoint + mDirection * ((some_point - mPoint) * mDirection)); } // the accuracy of this method sucks when you give it two nearly // parallel lines, so you should probably check for parallelism // before you call this -// +// // returns the point on this line that is closest to other_line LLVector3 LLLine::nearestApproach( const LLLine& other_line ) const { - LLVector3 between_points = other_line.mPoint - mPoint; - F32 dir_dot_dir = mDirection * other_line.mDirection; - F32 one_minus_dir_dot_dir = 1.0f - fabs(dir_dot_dir); - if ( one_minus_dir_dot_dir < SOME_VERY_SMALL_NUMBER ) - { + LLVector3 between_points = other_line.mPoint - mPoint; + F32 dir_dot_dir = mDirection * other_line.mDirection; + F32 one_minus_dir_dot_dir = 1.0f - fabs(dir_dot_dir); + if ( one_minus_dir_dot_dir < SOME_VERY_SMALL_NUMBER ) + { #ifdef LL_DEBUG - LL_WARNS() << "LLLine::nearestApproach() was given two very " - << "nearly parallel lines dir1 = " << mDirection - << " dir2 = " << other_line.mDirection << " with 1-dot_product = " - << one_minus_dir_dot_dir << LL_ENDL; + LL_WARNS() << "LLLine::nearestApproach() was given two very " + << "nearly parallel lines dir1 = " << mDirection + << " dir2 = " << other_line.mDirection << " with 1-dot_product = " + << one_minus_dir_dot_dir << LL_ENDL; #endif - // the lines are approximately parallel - // We shouldn't fall in here because this check should have been made - // BEFORE this function was called. We dare not continue with the - // computations for fear of division by zero, but we have to return - // something so we return a bogus point -- caller beware. - return 0.5f * (mPoint + other_line.mPoint); - } - - F32 odir_dot_bp = other_line.mDirection * between_points; - - F32 numerator = 0; - F32 denominator = 0; - for (S32 i=0; i<3; i++) - { - F32 factor = dir_dot_dir * other_line.mDirection.mV[i] - mDirection.mV[i]; - numerator += ( between_points.mV[i] - odir_dot_bp * other_line.mDirection.mV[i] ) * factor; - denominator -= factor * factor; - } - - F32 length_to_nearest_approach = numerator / denominator; - - return mPoint + length_to_nearest_approach * mDirection; + // the lines are approximately parallel + // We shouldn't fall in here because this check should have been made + // BEFORE this function was called. We dare not continue with the + // computations for fear of division by zero, but we have to return + // something so we return a bogus point -- caller beware. + return 0.5f * (mPoint + other_line.mPoint); + } + + F32 odir_dot_bp = other_line.mDirection * between_points; + + F32 numerator = 0; + F32 denominator = 0; + for (S32 i=0; i<3; i++) + { + F32 factor = dir_dot_dir * other_line.mDirection.mV[i] - mDirection.mV[i]; + numerator += ( between_points.mV[i] - odir_dot_bp * other_line.mDirection.mV[i] ) * factor; + denominator -= factor * factor; + } + + F32 length_to_nearest_approach = numerator / denominator; + + return mPoint + length_to_nearest_approach * mDirection; } std::ostream& operator<<( std::ostream& output_stream, const LLLine& line ) { - output_stream << "{point=" << line.mPoint << "," << "dir=" << line.mDirection << "}"; - return output_stream; + output_stream << "{point=" << line.mPoint << "," << "dir=" << line.mDirection << "}"; + return output_stream; } @@ -124,72 +124,72 @@ F32 TOO_SMALL_FOR_DIVISION = 0.0001f; // on success stores the intersection point in 'result' bool LLLine::intersectsPlane( LLVector3& result, const LLLine& plane ) const { - // p = P + l * d equation for a line - // - // N * p = D equation for a point - // - // N * (P + l * d) = D - // N*P + l * (N*d) = D - // l * (N*d) = D - N*P - // l = ( D - N*P ) / ( N*d ) - // - - F32 dot = plane.mDirection * mDirection; - if (fabs(dot) < TOO_SMALL_FOR_DIVISION) - { - return false; - } - - F32 plane_dot = plane.mDirection * plane.mPoint; - F32 length = ( plane_dot - (plane.mDirection * mPoint) ) / dot; - result = mPoint + length * mDirection; - return true; + // p = P + l * d equation for a line + // + // N * p = D equation for a point + // + // N * (P + l * d) = D + // N*P + l * (N*d) = D + // l * (N*d) = D - N*P + // l = ( D - N*P ) / ( N*d ) + // + + F32 dot = plane.mDirection * mDirection; + if (fabs(dot) < TOO_SMALL_FOR_DIVISION) + { + return false; + } + + F32 plane_dot = plane.mDirection * plane.mPoint; + F32 length = ( plane_dot - (plane.mDirection * mPoint) ) / dot; + result = mPoint + length * mDirection; + return true; } -//static -// returns 'true' if planes intersect, and stores the result +//static +// returns 'true' if planes intersect, and stores the result // the second and third arguments are treated as planes // where mPoint is on the plane and mDirection is the normal -// result.mPoint will be the intersection line's closest approach +// result.mPoint will be the intersection line's closest approach // to first_plane.mPoint bool LLLine::getIntersectionBetweenTwoPlanes( LLLine& result, const LLLine& first_plane, const LLLine& second_plane ) { - // TODO -- if we ever get some generic matrix solving code in our libs - // then we should just use that, since this problem is really just - // linear algebra. - - F32 dot = fabs(first_plane.mDirection * second_plane.mDirection); - if (dot > ALMOST_PARALLEL) - { - // the planes are nearly parallel - return false; - } - - LLVector3 direction = first_plane.mDirection % second_plane.mDirection; - direction.normalize(); - - LLVector3 first_intersection; - { - LLLine intersection_line(first_plane); - intersection_line.mDirection = direction % first_plane.mDirection; - intersection_line.mDirection.normalize(); - intersection_line.intersectsPlane(first_intersection, second_plane); - } - - /* - LLVector3 second_intersection; - { - LLLine intersection_line(second_plane); - intersection_line.mDirection = direction % second_plane.mDirection; - intersection_line.mDirection.normalize(); - intersection_line.intersectsPlane(second_intersection, first_plane); - } - */ - - result.mPoint = first_intersection; - result.mDirection = direction; - - return true; + // TODO -- if we ever get some generic matrix solving code in our libs + // then we should just use that, since this problem is really just + // linear algebra. + + F32 dot = fabs(first_plane.mDirection * second_plane.mDirection); + if (dot > ALMOST_PARALLEL) + { + // the planes are nearly parallel + return false; + } + + LLVector3 direction = first_plane.mDirection % second_plane.mDirection; + direction.normalize(); + + LLVector3 first_intersection; + { + LLLine intersection_line(first_plane); + intersection_line.mDirection = direction % first_plane.mDirection; + intersection_line.mDirection.normalize(); + intersection_line.intersectsPlane(first_intersection, second_plane); + } + + /* + LLVector3 second_intersection; + { + LLLine intersection_line(second_plane); + intersection_line.mDirection = direction % second_plane.mDirection; + intersection_line.mDirection.normalize(); + intersection_line.intersectsPlane(second_intersection, first_plane); + } + */ + + result.mPoint = first_intersection; + result.mDirection = direction; + + return true; } diff --git a/indra/llmath/llline.h b/indra/llmath/llline.h index e1cbc1323e..33c1eb61a4 100644 --- a/indra/llmath/llline.h +++ b/indra/llmath/llline.h @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2006&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$ */ @@ -38,42 +38,42 @@ const F32 DEFAULT_INTERSECTION_ERROR = 0.000001f; class LLLine { public: - LLLine(); - LLLine( const LLVector3& first_point, const LLVector3& second_point ); - virtual ~LLLine() {}; + LLLine(); + LLLine( const LLVector3& first_point, const LLVector3& second_point ); + virtual ~LLLine() {}; - void setPointDirection( const LLVector3& first_point, const LLVector3& second_point ); - void setPoints( const LLVector3& first_point, const LLVector3& second_point ); + void setPointDirection( const LLVector3& first_point, const LLVector3& second_point ); + void setPoints( const LLVector3& first_point, const LLVector3& second_point ); - bool intersects( const LLVector3& point, F32 radius = DEFAULT_INTERSECTION_ERROR ) const; + bool intersects( const LLVector3& point, F32 radius = DEFAULT_INTERSECTION_ERROR ) const; - // returns the point on this line that is closest to some_point - LLVector3 nearestApproach( const LLVector3& some_point ) const; + // returns the point on this line that is closest to some_point + LLVector3 nearestApproach( const LLVector3& some_point ) const; - // returns the point on this line that is closest to other_line - LLVector3 nearestApproach( const LLLine& other_line ) const; + // returns the point on this line that is closest to other_line + LLVector3 nearestApproach( const LLLine& other_line ) const; - friend std::ostream& operator<<( std::ostream& output_stream, const LLLine& line ); + friend std::ostream& operator<<( std::ostream& output_stream, const LLLine& line ); - // returns 'true' if this line intersects the plane - // on success stores the intersection point in 'result' - bool intersectsPlane( LLVector3& result, const LLLine& plane ) const; + // returns 'true' if this line intersects the plane + // on success stores the intersection point in 'result' + bool intersectsPlane( LLVector3& result, const LLLine& plane ) const; - // returns 'true' if planes intersect, and stores the result - // the second and third arguments are treated as planes - // where mPoint is on the plane and mDirection is the normal - // result.mPoint will be the intersection line's closest approach - // to first_plane.mPoint - static bool getIntersectionBetweenTwoPlanes( LLLine& result, const LLLine& first_plane, const LLLine& second_plane ); + // returns 'true' if planes intersect, and stores the result + // the second and third arguments are treated as planes + // where mPoint is on the plane and mDirection is the normal + // result.mPoint will be the intersection line's closest approach + // to first_plane.mPoint + static bool getIntersectionBetweenTwoPlanes( LLLine& result, const LLLine& first_plane, const LLLine& second_plane ); - const LLVector3& getPoint() const { return mPoint; } - const LLVector3& getDirection() const { return mDirection; } + const LLVector3& getPoint() const { return mPoint; } + const LLVector3& getDirection() const { return mDirection; } protected: - // these are protected because some code assumes that the normal is - // always correct and properly normalized. - LLVector3 mPoint; - LLVector3 mDirection; + // these are protected because some code assumes that the normal is + // always correct and properly normalized. + LLVector3 mPoint; + LLVector3 mDirection; }; diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index efcc3882fc..c60b90f301 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/llmatrix3a.cpp b/indra/llmath/llmatrix3a.cpp index ab077abcb0..48a72e71e1 100644 --- a/indra/llmath/llmatrix3a.cpp +++ b/indra/llmath/llmatrix3a.cpp @@ -1,134 +1,134 @@ -/** +/** * @file llvector4a.cpp * @brief SIMD vector implementation * * $LicenseInfo:firstyear=2010&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 "llmath.h" -static LL_ALIGN_16(const F32 M_IDENT_3A[12]) = - { 1.f, 0.f, 0.f, 0.f, // Column 1 - 0.f, 1.f, 0.f, 0.f, // Column 2 - 0.f, 0.f, 1.f, 0.f }; // Column 3 +static LL_ALIGN_16(const F32 M_IDENT_3A[12]) = + { 1.f, 0.f, 0.f, 0.f, // Column 1 + 0.f, 1.f, 0.f, 0.f, // Column 2 + 0.f, 0.f, 1.f, 0.f }; // Column 3 extern const LLMatrix3a LL_M3A_IDENTITY = *reinterpret_cast<const LLMatrix3a*> (M_IDENT_3A); void LLMatrix3a::setMul( const LLMatrix3a& lhs, const LLMatrix3a& rhs ) { - const LLVector4a col0 = lhs.getColumn(0); - const LLVector4a col1 = lhs.getColumn(1); - const LLVector4a col2 = lhs.getColumn(2); - - for ( int i = 0; i < 3; i++ ) - { - LLVector4a xxxx = _mm_load_ss( rhs.mColumns[i].getF32ptr() ); - xxxx.splat<0>( xxxx ); - xxxx.mul( col0 ); - - { - LLVector4a yyyy = _mm_load_ss( rhs.mColumns[i].getF32ptr() + 1 ); - yyyy.splat<0>( yyyy ); - yyyy.mul( col1 ); - xxxx.add( yyyy ); - } - - { - LLVector4a zzzz = _mm_load_ss( rhs.mColumns[i].getF32ptr() + 2 ); - zzzz.splat<0>( zzzz ); - zzzz.mul( col2 ); - xxxx.add( zzzz ); - } - - xxxx.store4a( mColumns[i].getF32ptr() ); - } - + const LLVector4a col0 = lhs.getColumn(0); + const LLVector4a col1 = lhs.getColumn(1); + const LLVector4a col2 = lhs.getColumn(2); + + for ( int i = 0; i < 3; i++ ) + { + LLVector4a xxxx = _mm_load_ss( rhs.mColumns[i].getF32ptr() ); + xxxx.splat<0>( xxxx ); + xxxx.mul( col0 ); + + { + LLVector4a yyyy = _mm_load_ss( rhs.mColumns[i].getF32ptr() + 1 ); + yyyy.splat<0>( yyyy ); + yyyy.mul( col1 ); + xxxx.add( yyyy ); + } + + { + LLVector4a zzzz = _mm_load_ss( rhs.mColumns[i].getF32ptr() + 2 ); + zzzz.splat<0>( zzzz ); + zzzz.mul( col2 ); + xxxx.add( zzzz ); + } + + xxxx.store4a( mColumns[i].getF32ptr() ); + } + } /*static */void LLMatrix3a::batchTransform( const LLMatrix3a& xform, const LLVector4a* src, int numVectors, LLVector4a* dst ) { - const LLVector4a col0 = xform.getColumn(0); - const LLVector4a col1 = xform.getColumn(1); - const LLVector4a col2 = xform.getColumn(2); - const LLVector4a* maxAddr = src + numVectors; - - if ( numVectors & 0x1 ) - { - LLVector4a xxxx = _mm_load_ss( (const F32*)src ); - LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 ); - LLVector4a zzzz = _mm_load_ss( (const F32*)src + 2 ); - xxxx.splat<0>( xxxx ); - yyyy.splat<0>( yyyy ); - zzzz.splat<0>( zzzz ); - xxxx.mul( col0 ); - yyyy.mul( col1 ); - zzzz.mul( col2 ); - xxxx.add( yyyy ); - xxxx.add( zzzz ); - xxxx.store4a( (F32*)dst ); - src++; - dst++; - } - - - numVectors >>= 1; - while ( src < maxAddr ) - { - _mm_prefetch( (const char*)(src + 32 ), _MM_HINT_NTA ); - _mm_prefetch( (const char*)(dst + 32), _MM_HINT_NTA ); - LLVector4a xxxx = _mm_load_ss( (const F32*)src ); - LLVector4a xxxx1= _mm_load_ss( (const F32*)(src + 1) ); - - xxxx.splat<0>( xxxx ); - xxxx1.splat<0>( xxxx1 ); - xxxx.mul( col0 ); - xxxx1.mul( col0 ); - - { - LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 ); - LLVector4a yyyy1 = _mm_load_ss( (const F32*)(src + 1) + 1); - yyyy.splat<0>( yyyy ); - yyyy1.splat<0>( yyyy1 ); - yyyy.mul( col1 ); - yyyy1.mul( col1 ); - xxxx.add( yyyy ); - xxxx1.add( yyyy1 ); - } - - { - LLVector4a zzzz = _mm_load_ss( (const F32*)(src) + 2 ); - LLVector4a zzzz1 = _mm_load_ss( (const F32*)(++src) + 2 ); - zzzz.splat<0>( zzzz ); - zzzz1.splat<0>( zzzz1 ); - zzzz.mul( col2 ); - zzzz1.mul( col2 ); - xxxx.add( zzzz ); - xxxx1.add( zzzz1 ); - } - - xxxx.store4a(dst->getF32ptr()); - src++; - dst++; - - xxxx1.store4a((F32*)dst++); - } + const LLVector4a col0 = xform.getColumn(0); + const LLVector4a col1 = xform.getColumn(1); + const LLVector4a col2 = xform.getColumn(2); + const LLVector4a* maxAddr = src + numVectors; + + if ( numVectors & 0x1 ) + { + LLVector4a xxxx = _mm_load_ss( (const F32*)src ); + LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 ); + LLVector4a zzzz = _mm_load_ss( (const F32*)src + 2 ); + xxxx.splat<0>( xxxx ); + yyyy.splat<0>( yyyy ); + zzzz.splat<0>( zzzz ); + xxxx.mul( col0 ); + yyyy.mul( col1 ); + zzzz.mul( col2 ); + xxxx.add( yyyy ); + xxxx.add( zzzz ); + xxxx.store4a( (F32*)dst ); + src++; + dst++; + } + + + numVectors >>= 1; + while ( src < maxAddr ) + { + _mm_prefetch( (const char*)(src + 32 ), _MM_HINT_NTA ); + _mm_prefetch( (const char*)(dst + 32), _MM_HINT_NTA ); + LLVector4a xxxx = _mm_load_ss( (const F32*)src ); + LLVector4a xxxx1= _mm_load_ss( (const F32*)(src + 1) ); + + xxxx.splat<0>( xxxx ); + xxxx1.splat<0>( xxxx1 ); + xxxx.mul( col0 ); + xxxx1.mul( col0 ); + + { + LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 ); + LLVector4a yyyy1 = _mm_load_ss( (const F32*)(src + 1) + 1); + yyyy.splat<0>( yyyy ); + yyyy1.splat<0>( yyyy1 ); + yyyy.mul( col1 ); + yyyy1.mul( col1 ); + xxxx.add( yyyy ); + xxxx1.add( yyyy1 ); + } + + { + LLVector4a zzzz = _mm_load_ss( (const F32*)(src) + 2 ); + LLVector4a zzzz1 = _mm_load_ss( (const F32*)(++src) + 2 ); + zzzz.splat<0>( zzzz ); + zzzz1.splat<0>( zzzz1 ); + zzzz.mul( col2 ); + zzzz1.mul( col2 ); + xxxx.add( zzzz ); + xxxx1.add( zzzz1 ); + } + + xxxx.store4a(dst->getF32ptr()); + src++; + dst++; + + xxxx1.store4a((F32*)dst++); + } } diff --git a/indra/llmath/llmatrix3a.h b/indra/llmath/llmatrix3a.h index 9916cfd2da..dff6604ae5 100644 --- a/indra/llmath/llmatrix3a.h +++ b/indra/llmath/llmatrix3a.h @@ -1,31 +1,31 @@ -/** +/** * @file llmatrix3a.h * @brief LLMatrix3a class header file - memory aligned and vectorized 3x3 matrix * * $LicenseInfo:firstyear=2010&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_LLMATRIX3A_H -#define LL_LLMATRIX3A_H +#ifndef LL_LLMATRIX3A_H +#define LL_LLMATRIX3A_H ///////////////////////////// // LLMatrix3a, LLRotation @@ -38,91 +38,91 @@ // Huseby). ///////////////////////////// -// LLMatrix3a is the base class for LLRotation, which should be used instead any time you're dealing with a +// LLMatrix3a is the base class for LLRotation, which should be used instead any time you're dealing with a // rotation matrix. class LLMatrix3a { public: - // Utility function for quickly transforming an array of LLVector4a's - // For transforming a single LLVector4a, see LLVector4a::setRotated - static void batchTransform( const LLMatrix3a& xform, const LLVector4a* src, int numVectors, LLVector4a* dst ); + // Utility function for quickly transforming an array of LLVector4a's + // For transforming a single LLVector4a, see LLVector4a::setRotated + static void batchTransform( const LLMatrix3a& xform, const LLVector4a* src, int numVectors, LLVector4a* dst ); + + // Utility function to obtain the identity matrix + static inline const LLMatrix3a& getIdentity(); - // Utility function to obtain the identity matrix - static inline const LLMatrix3a& getIdentity(); + ////////////////////////// + // Ctors + ////////////////////////// - ////////////////////////// - // Ctors - ////////////////////////// - - // Ctor - LLMatrix3a() {} + // Ctor + LLMatrix3a() {} - // Ctor for setting by columns - inline LLMatrix3a( const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2 ); + // Ctor for setting by columns + inline LLMatrix3a( const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2 ); - ////////////////////////// - // Get/Set - ////////////////////////// + ////////////////////////// + // Get/Set + ////////////////////////// - // Loads from an LLMatrix3 - inline void loadu(const LLMatrix3& src); - - // Set rows - inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2); - - // Set columns - inline void setColumns(const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2); + // Loads from an LLMatrix3 + inline void loadu(const LLMatrix3& src); - // Get the read-only access to a specified column. Valid columns are 0-2, but the - // function is unchecked. You've been warned. - inline const LLVector4a& getColumn(const U32 column) const; + // Set rows + inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2); - ///////////////////////// - // Matrix modification - ///////////////////////// - - // Set this matrix to the product of lhs and rhs ( this = lhs * rhs ) - void setMul( const LLMatrix3a& lhs, const LLMatrix3a& rhs ); + // Set columns + inline void setColumns(const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2); - // Set this matrix to the transpose of src - inline void setTranspose(const LLMatrix3a& src); + // Get the read-only access to a specified column. Valid columns are 0-2, but the + // function is unchecked. You've been warned. + inline const LLVector4a& getColumn(const U32 column) const; - // Set this matrix to a*w + b*(1-w) - inline void setLerp(const LLMatrix3a& a, const LLMatrix3a& b, F32 w); + ///////////////////////// + // Matrix modification + ///////////////////////// - ///////////////////////// - // Matrix inspection - ///////////////////////// + // Set this matrix to the product of lhs and rhs ( this = lhs * rhs ) + void setMul( const LLMatrix3a& lhs, const LLMatrix3a& rhs ); - // Sets all 4 elements in 'dest' to the determinant of this matrix. - // If you will be using the determinant in subsequent ops with LLVector4a, use this version - inline void getDeterminant( LLVector4a& dest ) const; + // Set this matrix to the transpose of src + inline void setTranspose(const LLMatrix3a& src); - // Returns the determinant as an LLSimdScalar. Use this if you will be using the determinant - // primary for scalar operations. - inline LLSimdScalar getDeterminant() const; + // Set this matrix to a*w + b*(1-w) + inline void setLerp(const LLMatrix3a& a, const LLMatrix3a& b, F32 w); - // Returns nonzero if rows 0-2 and colums 0-2 contain no NaN or INF values. Row 3 is ignored - inline LLBool32 isFinite() const; + ///////////////////////// + // Matrix inspection + ///////////////////////// - // Returns true if this matrix is equal to 'rhs' up to 'tolerance' - inline bool isApproximatelyEqual( const LLMatrix3a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; + // Sets all 4 elements in 'dest' to the determinant of this matrix. + // If you will be using the determinant in subsequent ops with LLVector4a, use this version + inline void getDeterminant( LLVector4a& dest ) const; + + // Returns the determinant as an LLSimdScalar. Use this if you will be using the determinant + // primary for scalar operations. + inline LLSimdScalar getDeterminant() const; + + // Returns nonzero if rows 0-2 and colums 0-2 contain no NaN or INF values. Row 3 is ignored + inline LLBool32 isFinite() const; + + // Returns true if this matrix is equal to 'rhs' up to 'tolerance' + inline bool isApproximatelyEqual( const LLMatrix3a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; protected: - LL_ALIGN_16(LLVector4a mColumns[3]); + LL_ALIGN_16(LLVector4a mColumns[3]); }; class LLRotation : public LLMatrix3a { public: - - LLRotation() {} - - // Returns true if this rotation is orthonormal with det ~= 1 - inline bool isOkRotation() const; + + LLRotation() {} + + // Returns true if this rotation is orthonormal with det ~= 1 + inline bool isOkRotation() const; }; #endif diff --git a/indra/llmath/llmatrix3a.inl b/indra/llmath/llmatrix3a.inl index 37819fea3c..262810746e 100644 --- a/indra/llmath/llmatrix3a.inl +++ b/indra/llmath/llmatrix3a.inl @@ -1,25 +1,25 @@ -/** +/** * @file llmatrix3a.inl * @brief LLMatrix3a inline definitions * * $LicenseInfo:firstyear=2010&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$ */ @@ -29,91 +29,91 @@ inline LLMatrix3a::LLMatrix3a( const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2 ) { - setColumns( c0, c1, c2 ); + setColumns( c0, c1, c2 ); } inline void LLMatrix3a::loadu(const LLMatrix3& src) { - mColumns[0].load3(src.mMatrix[0]); - mColumns[1].load3(src.mMatrix[1]); - mColumns[2].load3(src.mMatrix[2]); + mColumns[0].load3(src.mMatrix[0]); + mColumns[1].load3(src.mMatrix[1]); + mColumns[2].load3(src.mMatrix[2]); } inline void LLMatrix3a::setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2) { - mColumns[0] = r0; - mColumns[1] = r1; - mColumns[2] = r2; - setTranspose( *this ); + mColumns[0] = r0; + mColumns[1] = r1; + mColumns[2] = r2; + setTranspose( *this ); } inline void LLMatrix3a::setColumns(const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2) { - mColumns[0] = c0; - mColumns[1] = c1; - mColumns[2] = c2; + mColumns[0] = c0; + mColumns[1] = c1; + mColumns[2] = c2; } inline void LLMatrix3a::setTranspose(const LLMatrix3a& src) { - const LLQuad srcCol0 = src.mColumns[0]; - const LLQuad srcCol1 = src.mColumns[1]; - const LLQuad unpacklo = _mm_unpacklo_ps( srcCol0, srcCol1 ); - mColumns[0] = _mm_movelh_ps( unpacklo, src.mColumns[2] ); - mColumns[1] = _mm_shuffle_ps( _mm_movehl_ps( srcCol0, unpacklo ), src.mColumns[2], _MM_SHUFFLE(0, 1, 1, 0) ); - mColumns[2] = _mm_shuffle_ps( _mm_unpackhi_ps( srcCol0, srcCol1 ), src.mColumns[2], _MM_SHUFFLE(0, 2, 1, 0) ); + const LLQuad srcCol0 = src.mColumns[0]; + const LLQuad srcCol1 = src.mColumns[1]; + const LLQuad unpacklo = _mm_unpacklo_ps( srcCol0, srcCol1 ); + mColumns[0] = _mm_movelh_ps( unpacklo, src.mColumns[2] ); + mColumns[1] = _mm_shuffle_ps( _mm_movehl_ps( srcCol0, unpacklo ), src.mColumns[2], _MM_SHUFFLE(0, 1, 1, 0) ); + mColumns[2] = _mm_shuffle_ps( _mm_unpackhi_ps( srcCol0, srcCol1 ), src.mColumns[2], _MM_SHUFFLE(0, 2, 1, 0) ); } inline const LLVector4a& LLMatrix3a::getColumn(const U32 column) const { - llassert( column < 3 ); - return mColumns[column]; + llassert( column < 3 ); + return mColumns[column]; } inline void LLMatrix3a::setLerp(const LLMatrix3a& a, const LLMatrix3a& b, F32 w) { - mColumns[0].setLerp( a.mColumns[0], b.mColumns[0], w ); - mColumns[1].setLerp( a.mColumns[1], b.mColumns[1], w ); - mColumns[2].setLerp( a.mColumns[2], b.mColumns[2], w ); + mColumns[0].setLerp( a.mColumns[0], b.mColumns[0], w ); + mColumns[1].setLerp( a.mColumns[1], b.mColumns[1], w ); + mColumns[2].setLerp( a.mColumns[2], b.mColumns[2], w ); } inline LLBool32 LLMatrix3a::isFinite() const { - return mColumns[0].isFinite3() && mColumns[1].isFinite3() && mColumns[2].isFinite3(); + return mColumns[0].isFinite3() && mColumns[1].isFinite3() && mColumns[2].isFinite3(); } inline void LLMatrix3a::getDeterminant( LLVector4a& dest ) const { - LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] ); - dest.setAllDot3( col1xcol2, mColumns[0] ); + LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] ); + dest.setAllDot3( col1xcol2, mColumns[0] ); } inline LLSimdScalar LLMatrix3a::getDeterminant() const { - LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] ); - return col1xcol2.dot3( mColumns[0] ); + LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] ); + return col1xcol2.dot3( mColumns[0] ); } inline bool LLMatrix3a::isApproximatelyEqual( const LLMatrix3a& rhs, F32 tolerance /*= F_APPROXIMATELY_ZERO*/ ) const { - return rhs.getColumn(0).equals3(mColumns[0], tolerance) - && rhs.getColumn(1).equals3(mColumns[1], tolerance) - && rhs.getColumn(2).equals3(mColumns[2], tolerance); + return rhs.getColumn(0).equals3(mColumns[0], tolerance) + && rhs.getColumn(1).equals3(mColumns[1], tolerance) + && rhs.getColumn(2).equals3(mColumns[2], tolerance); } inline const LLMatrix3a& LLMatrix3a::getIdentity() { - extern const LLMatrix3a LL_M3A_IDENTITY; - return LL_M3A_IDENTITY; + extern const LLMatrix3a LL_M3A_IDENTITY; + return LL_M3A_IDENTITY; } inline bool LLRotation::isOkRotation() const { - LLMatrix3a transpose; transpose.setTranspose( *this ); - LLMatrix3a product; product.setMul( *this, transpose ); + LLMatrix3a transpose; transpose.setTranspose( *this ); + LLMatrix3a product; product.setMul( *this, transpose ); - LLSimdScalar detMinusOne = getDeterminant() - 1.f; + LLSimdScalar detMinusOne = getDeterminant() - 1.f; - return product.isApproximatelyEqual( LLMatrix3a::getIdentity() ) && (detMinusOne.getAbs() < F_APPROXIMATELY_ZERO); + return product.isApproximatelyEqual( LLMatrix3a::getIdentity() ) && (detMinusOne.getAbs() < F_APPROXIMATELY_ZERO); } diff --git a/indra/llmath/llmatrix4a.cpp b/indra/llmath/llmatrix4a.cpp index fe8f0b98f3..00e30a248b 100644 --- a/indra/llmath/llmatrix4a.cpp +++ b/indra/llmath/llmatrix4a.cpp @@ -33,48 +33,48 @@ // necessarily the fastest way to implement. void matMulBoundBox(const LLMatrix4a &mat, const LLVector4a *in_extents, LLVector4a *out_extents) { - //get 8 corners of bounding box - LLVector4Logical mask[6]; + //get 8 corners of bounding box + LLVector4Logical mask[6]; - for (U32 i = 0; i < 6; ++i) - { - mask[i].clear(); - } + for (U32 i = 0; i < 6; ++i) + { + mask[i].clear(); + } - mask[0].setElement<2>(); //001 - mask[1].setElement<1>(); //010 - mask[2].setElement<1>(); //011 - mask[2].setElement<2>(); - mask[3].setElement<0>(); //100 - mask[4].setElement<0>(); //101 - mask[4].setElement<2>(); - mask[5].setElement<0>(); //110 - mask[5].setElement<1>(); + mask[0].setElement<2>(); //001 + mask[1].setElement<1>(); //010 + mask[2].setElement<1>(); //011 + mask[2].setElement<2>(); + mask[3].setElement<0>(); //100 + mask[4].setElement<0>(); //101 + mask[4].setElement<2>(); + mask[5].setElement<0>(); //110 + mask[5].setElement<1>(); - LLVector4a v[8]; + LLVector4a v[8]; - v[6] = in_extents[0]; - v[7] = in_extents[1]; + v[6] = in_extents[0]; + v[7] = in_extents[1]; - for (U32 i = 0; i < 6; ++i) - { - v[i].setSelectWithMask(mask[i], in_extents[0], in_extents[1]); - } + for (U32 i = 0; i < 6; ++i) + { + v[i].setSelectWithMask(mask[i], in_extents[0], in_extents[1]); + } - LLVector4a tv[8]; + LLVector4a tv[8]; - //transform bounding box into drawable space - for (U32 i = 0; i < 8; ++i) - { - mat.affineTransform(v[i], tv[i]); - } - - //find bounding box - out_extents[0] = out_extents[1] = tv[0]; + //transform bounding box into drawable space + for (U32 i = 0; i < 8; ++i) + { + mat.affineTransform(v[i], tv[i]); + } - for (U32 i = 1; i < 8; ++i) - { - out_extents[0].setMin(out_extents[0], tv[i]); - out_extents[1].setMax(out_extents[1], tv[i]); - } + //find bounding box + out_extents[0] = out_extents[1] = tv[0]; + + for (U32 i = 1; i < 8; ++i) + { + out_extents[0].setMin(out_extents[0], tv[i]); + out_extents[1].setMax(out_extents[1], tv[i]); + } } diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h index 2cf50e9cd2..348feba27e 100644 --- a/indra/llmath/llmatrix4a.h +++ b/indra/llmath/llmatrix4a.h @@ -1,31 +1,31 @@ -/** +/** * @file llmatrix4a.h * @brief LLMatrix4a class header file - memory aligned and vectorized 4x4 matrix * * $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_LLMATRIX4A_H -#define LL_LLMATRIX4A_H +#ifndef LL_LLMATRIX4A_H +#define LL_LLMATRIX4A_H #include "llvector4a.h" #include "m4math.h" @@ -34,7 +34,7 @@ class LLMatrix4a { public: - LL_ALIGN_16(LLVector4a mMatrix[4]); + LL_ALIGN_16(LLVector4a mMatrix[4]); LLMatrix4a() { @@ -56,13 +56,13 @@ public: return (F32*)&mMatrix; } - inline void clear() - { - mMatrix[0].clear(); - mMatrix[1].clear(); - mMatrix[2].clear(); - mMatrix[3].clear(); - } + inline void clear() + { + mMatrix[0].clear(); + mMatrix[1].clear(); + mMatrix[2].clear(); + mMatrix[3].clear(); + } inline void setIdentity() { @@ -72,14 +72,14 @@ public: mMatrix[3].set(0.f, 0.f, 0.f, 1.f); } - inline void loadu(const LLMatrix4& src) - { - mMatrix[0] = _mm_loadu_ps(src.mMatrix[0]); - mMatrix[1] = _mm_loadu_ps(src.mMatrix[1]); - mMatrix[2] = _mm_loadu_ps(src.mMatrix[2]); - mMatrix[3] = _mm_loadu_ps(src.mMatrix[3]); - } - + inline void loadu(const LLMatrix4& src) + { + mMatrix[0] = _mm_loadu_ps(src.mMatrix[0]); + mMatrix[1] = _mm_loadu_ps(src.mMatrix[1]); + mMatrix[2] = _mm_loadu_ps(src.mMatrix[2]); + mMatrix[3] = _mm_loadu_ps(src.mMatrix[3]); + } + inline void loadu(const F32* src) { mMatrix[0] = _mm_loadu_ps(src); @@ -88,90 +88,90 @@ public: mMatrix[3] = _mm_loadu_ps(src+12); } - inline void loadu(const LLMatrix3& src) - { - mMatrix[0].load3(src.mMatrix[0]); - mMatrix[1].load3(src.mMatrix[1]); - mMatrix[2].load3(src.mMatrix[2]); - mMatrix[3].set(0,0,0,1.f); - } - - inline void add(const LLMatrix4a& rhs) - { - mMatrix[0].add(rhs.mMatrix[0]); - mMatrix[1].add(rhs.mMatrix[1]); - mMatrix[2].add(rhs.mMatrix[2]); - mMatrix[3].add(rhs.mMatrix[3]); - } - - inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2) - { - mMatrix[0] = r0; - mMatrix[1] = r1; - mMatrix[2] = r2; - } - - inline void setMul(const LLMatrix4a& m, const F32 s) - { - mMatrix[0].setMul(m.mMatrix[0], s); - mMatrix[1].setMul(m.mMatrix[1], s); - mMatrix[2].setMul(m.mMatrix[2], s); - mMatrix[3].setMul(m.mMatrix[3], s); - } - - inline void setLerp(const LLMatrix4a& a, const LLMatrix4a& b, F32 w) - { - LLVector4a d0,d1,d2,d3; - d0.setSub(b.mMatrix[0], a.mMatrix[0]); - d1.setSub(b.mMatrix[1], a.mMatrix[1]); - d2.setSub(b.mMatrix[2], a.mMatrix[2]); - d3.setSub(b.mMatrix[3], a.mMatrix[3]); - - // this = a + d*w - - d0.mul(w); - d1.mul(w); - d2.mul(w); - d3.mul(w); - - mMatrix[0].setAdd(a.mMatrix[0],d0); - mMatrix[1].setAdd(a.mMatrix[1],d1); - mMatrix[2].setAdd(a.mMatrix[2],d2); - mMatrix[3].setAdd(a.mMatrix[3],d3); - } - - inline void rotate(const LLVector4a& v, LLVector4a& res) const - { - LLVector4a y,z; - - res = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0)); - y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1)); - z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2)); - - res.mul(mMatrix[0]); - y.mul(mMatrix[1]); - z.mul(mMatrix[2]); - - res.add(y); - res.add(z); - } - - inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res) const - { - LLVector4a x,y,z; - - x = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0)); - y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1)); - z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2)); - - x.mul(mMatrix[0]); - y.mul(mMatrix[1]); - z.mul(mMatrix[2]); - - x.add(y); - z.add(mMatrix[3]); - res.setAdd(x,z); - } + inline void loadu(const LLMatrix3& src) + { + mMatrix[0].load3(src.mMatrix[0]); + mMatrix[1].load3(src.mMatrix[1]); + mMatrix[2].load3(src.mMatrix[2]); + mMatrix[3].set(0,0,0,1.f); + } + + inline void add(const LLMatrix4a& rhs) + { + mMatrix[0].add(rhs.mMatrix[0]); + mMatrix[1].add(rhs.mMatrix[1]); + mMatrix[2].add(rhs.mMatrix[2]); + mMatrix[3].add(rhs.mMatrix[3]); + } + + inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2) + { + mMatrix[0] = r0; + mMatrix[1] = r1; + mMatrix[2] = r2; + } + + inline void setMul(const LLMatrix4a& m, const F32 s) + { + mMatrix[0].setMul(m.mMatrix[0], s); + mMatrix[1].setMul(m.mMatrix[1], s); + mMatrix[2].setMul(m.mMatrix[2], s); + mMatrix[3].setMul(m.mMatrix[3], s); + } + + inline void setLerp(const LLMatrix4a& a, const LLMatrix4a& b, F32 w) + { + LLVector4a d0,d1,d2,d3; + d0.setSub(b.mMatrix[0], a.mMatrix[0]); + d1.setSub(b.mMatrix[1], a.mMatrix[1]); + d2.setSub(b.mMatrix[2], a.mMatrix[2]); + d3.setSub(b.mMatrix[3], a.mMatrix[3]); + + // this = a + d*w + + d0.mul(w); + d1.mul(w); + d2.mul(w); + d3.mul(w); + + mMatrix[0].setAdd(a.mMatrix[0],d0); + mMatrix[1].setAdd(a.mMatrix[1],d1); + mMatrix[2].setAdd(a.mMatrix[2],d2); + mMatrix[3].setAdd(a.mMatrix[3],d3); + } + + inline void rotate(const LLVector4a& v, LLVector4a& res) const + { + LLVector4a y,z; + + res = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0)); + y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1)); + z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2)); + + res.mul(mMatrix[0]); + y.mul(mMatrix[1]); + z.mul(mMatrix[2]); + + res.add(y); + res.add(z); + } + + inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res) const + { + LLVector4a x,y,z; + + x = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0)); + y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1)); + z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2)); + + x.mul(mMatrix[0]); + y.mul(mMatrix[1]); + z.mul(mMatrix[2]); + + x.add(y); + z.add(mMatrix[3]); + res.setAdd(x,z); + } inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res) const { @@ -182,7 +182,7 @@ public: res.set(x,y,z,w); } - inline void affineTransform(const LLVector4a& v, LLVector4a& res) const + inline void affineTransform(const LLVector4a& v, LLVector4a& res) const { affineTransformSSE(v,res); } @@ -226,7 +226,7 @@ inline std::ostream& operator<<(std::ostream& s, const LLMatrix4a& m) { s << "[" << m.mMatrix[0] << ", " << m.mMatrix[1] << ", " << m.mMatrix[2] << ", " << m.mMatrix[3] << "]"; return s; -} +} void matMulBoundBox(const LLMatrix4a &a, const LLVector4a *in_extents, LLVector4a *out_extents); diff --git a/indra/llmath/llmodularmath.cpp b/indra/llmath/llmodularmath.cpp index cdc20028bf..7a22866b5e 100644 --- a/indra/llmath/llmodularmath.cpp +++ b/indra/llmath/llmodularmath.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llmodularmath.cpp * @brief LLModularMath 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$ */ diff --git a/indra/llmath/llmodularmath.h b/indra/llmath/llmodularmath.h index 0d4d28fadc..e0ae71417a 100644 --- a/indra/llmath/llmodularmath.h +++ b/indra/llmath/llmodularmath.h @@ -1,25 +1,25 @@ -/** +/** * @file llmodularmath.h * @brief Useful modular math functions. * * $LicenseInfo:firstyear=2008&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$ */ @@ -32,21 +32,21 @@ namespace LLModularMath // Return difference between lhs and rhs // treating the U32 operands and result // as unsigned values of given width. - template<int width> - inline U32 subtract(U32 lhs, U32 rhs) - { - // Generate a bit mask which will truncate - // unsigned values to given width at compile time. - const U32 mask = (1 << width) - 1; - - // Operands are unsigned, so modular - // arithmetic applies. If lhs < rhs, - // difference will wrap in to lower - // bits of result, which is then masked - // to give a value that can be represented - // by an unsigned value of width bits. - return mask & (lhs - rhs); - } + template<int width> + inline U32 subtract(U32 lhs, U32 rhs) + { + // Generate a bit mask which will truncate + // unsigned values to given width at compile time. + const U32 mask = (1 << width) - 1; + + // Operands are unsigned, so modular + // arithmetic applies. If lhs < rhs, + // difference will wrap in to lower + // bits of result, which is then masked + // to give a value that can be represented + // by an unsigned value of width bits. + return mask & (lhs - rhs); + } } #endif diff --git a/indra/llmath/lloctree.cpp b/indra/llmath/lloctree.cpp index 3fcb3a27d7..1d87ca797f 100644 --- a/indra/llmath/lloctree.cpp +++ b/indra/llmath/lloctree.cpp @@ -1,24 +1,24 @@ -/** +/** * @file lloctree.cpp * * $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$ */ diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h index 2e9625fff7..6e43646b60 100644 --- a/indra/llmath/lloctree.h +++ b/indra/llmath/lloctree.h @@ -1,858 +1,858 @@ -/** - * @file lloctree.h - * @brief Octree declaration. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLOCTREE_H -#define LL_LLOCTREE_H - -#include "lltreenode.h" -#include "v3math.h" -#include "llvector4a.h" -#include <vector> - -#define OCT_ERRS LL_WARNS("OctreeErrors") - -#define OCTREE_DEBUG_COLOR_REMOVE 0x0000FF // r -#define OCTREE_DEBUG_COLOR_INSERT 0x00FF00 // g -#define OCTREE_DEBUG_COLOR_BALANCE 0xFF0000 // b - -extern U32 gOctreeMaxCapacity; -extern float gOctreeMinSize; - -/*#define LL_OCTREE_PARANOIA_CHECK 0 -#if LL_DARWIN -#define LL_OCTREE_MAX_CAPACITY 32 -#else -#define LL_OCTREE_MAX_CAPACITY 128 -#endif*/ - -// T is the type of the element referenced by the octree node. -// T_PTR determines how pointers to elements are stored internally. -// LLOctreeNode<T, LLPointer<T>> assumes ownership of inserted elements and -// deletes elements removed from the tree. -// LLOctreeNode<T, T*> doesn't take ownership of inserted elements, so the API -// user is responsible for managing the storage lifecycle of elements added to -// the tree. -template <class T, typename T_PTR> class LLOctreeNode; - -template <class T, typename T_PTR> -class LLOctreeListener: public LLTreeListener<T> -{ -public: - typedef LLTreeListener<T> BaseType; - typedef LLOctreeNode<T, T_PTR> oct_node; - - virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0; - virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0; -}; - -template <class T, typename T_PTR> -class LLOctreeTraveler -{ -public: - virtual void traverse(const LLOctreeNode<T, T_PTR>* node); - virtual void visit(const LLOctreeNode<T, T_PTR>* branch) = 0; -}; - -template <class T, typename T_PTR> -class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T, T_PTR> -{ -public: - virtual void traverse(const LLOctreeNode<T, T_PTR>* node) override; -}; - -template <class T, typename T_PTR> -class alignas(16) LLOctreeNode : public LLTreeNode<T> -{ - LL_ALIGN_NEW -public: - - typedef LLOctreeTraveler<T, T_PTR> oct_traveler; - typedef LLTreeTraveler<T> tree_traveler; - typedef std::vector<T_PTR> element_list; - typedef typename element_list::iterator element_iter; - typedef typename element_list::const_iterator const_element_iter; - typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter; - typedef LLOctreeNode<T, T_PTR>** child_list; - typedef LLOctreeNode<T, T_PTR>** child_iter; - - typedef LLTreeNode<T> BaseType; - typedef LLOctreeNode<T, T_PTR> oct_node; - typedef LLOctreeListener<T, T_PTR> oct_listener; - - enum - { - NO_CHILD_NODES = 255 // Note: This is an U8 to match the max value in mChildMap[] - }; - - LLOctreeNode( const LLVector4a& center, - const LLVector4a& size, - BaseType* parent, - U8 octant = NO_CHILD_NODES) - : mParent((oct_node*)parent), - mOctant(octant) - { - llassert(size[0] >= gOctreeMinSize*0.5f); - - mCenter = center; - mSize = size; - - updateMinMax(); - if ((mOctant == NO_CHILD_NODES) && mParent) - { - mOctant = ((oct_node*) mParent)->getOctant(mCenter); - } - - clearChildren(); - } - - virtual ~LLOctreeNode() - { - BaseType::destroyListeners(); - - const U32 element_count = getElementCount(); - for (U32 i = 0; i < element_count; ++i) - { - mData[i]->setBinIndex(-1); - mData[i] = NULL; - } - - mData.clear(); - - for (U32 i = 0; i < getChildCount(); i++) - { - delete getChild(i); - } - } - - inline const BaseType* getParent() const { return mParent; } - inline void setParent(BaseType* parent) { mParent = (oct_node*) parent; } - inline const LLVector4a& getCenter() const { return mCenter; } - inline const LLVector4a& getSize() const { return mSize; } - inline void setCenter(const LLVector4a& center) { mCenter = center; } - inline void setSize(const LLVector4a& size) { mSize = size; } - inline oct_node* getNodeAt(T* data) { return getNodeAt(data->getPositionGroup(), data->getBinRadius()); } - inline U8 getOctant() const { return mOctant; } - inline const oct_node* getOctParent() const { return (const oct_node*) getParent(); } - inline oct_node* getOctParent() { return (oct_node*) getParent(); } - - U8 getOctant(const LLVector4a& pos) const //get the octant pos is in - { - return (U8) (pos.greaterThan(mCenter).getGatheredBits() & 0x7); - } - - inline bool isInside(const LLVector4a& pos, const F32& rad) const - { - return rad <= mSize[0]*2.f && isInside(pos); - } - - inline bool isInside(T* data) const - { - return isInside(data->getPositionGroup(), data->getBinRadius()); - } - - bool isInside(const LLVector4a& pos) const - { - S32 gt = pos.greaterThan(mMax).getGatheredBits() & 0x7; - if (gt) - { - return false; - } - - S32 lt = pos.lessEqual(mMin).getGatheredBits() & 0x7; - if (lt) - { - return false; - } - - return true; - } - - void updateMinMax() - { - mMax.setAdd(mCenter, mSize); - mMin.setSub(mCenter, mSize); - } - - inline oct_listener* getOctListener(U32 index) - { - return (oct_listener*) BaseType::getListener(index); - } - - inline bool contains(T* xform) - { - return contains(xform->getBinRadius()); - } - - bool contains(F32 radius) - { - if (mParent == NULL) - { //root node contains nothing - return false; - } - - F32 size = mSize[0]; - F32 p_size = size * 2.f; - - return (radius <= gOctreeMinSize && size <= gOctreeMinSize) || - (radius <= p_size && radius > size); - } - - static void pushCenter(LLVector4a ¢er, const LLVector4a &size, const T* data) - { - const LLVector4a& pos = data->getPositionGroup(); - - LLVector4Logical gt = pos.greaterThan(center); - - LLVector4a up; - up = _mm_and_ps(size, gt); - - LLVector4a down; - down = _mm_andnot_ps(gt, size); - - center.add(up); - center.sub(down); - } - - void accept(oct_traveler* visitor) { visitor->visit(this); } - virtual bool isLeaf() const { return mChildCount == 0; } - - U32 getElementCount() const { return (U32)mData.size(); } - bool isEmpty() const { return mData.empty(); } - element_iter getDataBegin() { return mData.begin(); } - element_iter getDataEnd() { return mData.end(); } - const_element_iter getDataBegin() const { return mData.cbegin(); } - const_element_iter getDataEnd() const { return mData.cend(); } - - U32 getChildCount() const { return mChildCount; } - oct_node* getChild(U32 index) { return mChild[index]; } - const oct_node* getChild(U32 index) const { return mChild[index]; } - child_list& getChildren() { return mChild; } - const child_list& getChildren() const { return mChild; } - - void accept(tree_traveler* visitor) const { visitor->visit(this); } - void accept(oct_traveler* visitor) const { visitor->visit(this); } - - void validateChildMap() - { - for (U32 i = 0; i < 8; i++) - { - U8 idx = mChildMap[i]; - if (idx != NO_CHILD_NODES) - { - oct_node* child = mChild[idx]; - - if (child->getOctant() != i) - { - LL_ERRS() << "Invalid child map, bad octant data." << LL_ENDL; - } - - if (getOctant(child->getCenter()) != child->getOctant()) - { - LL_ERRS() << "Invalid child octant compared to position data." << LL_ENDL; - } - } - } - } - - - oct_node* getNodeAt(const LLVector4a& pos, const F32& rad) - { - oct_node* node = this; - - if (node->isInside(pos, rad)) - { - //do a quick search by octant - U8 octant = node->getOctant(pos); - - //traverse the tree until we find a node that has no node - //at the appropriate octant or is smaller than the object. - //by definition, that node is the smallest node that contains - // the data - U8 next_node = node->mChildMap[octant]; - - while (next_node != NO_CHILD_NODES && node->getSize()[0] >= rad) - { - node = node->getChild(next_node); - octant = node->getOctant(pos); - next_node = node->mChildMap[octant]; - } - } - else if (!node->contains(rad) && node->getParent()) - { //if we got here, data does not exist in this node - return ((oct_node*) node->getParent())->getNodeAt(pos, rad); - } - - return node; - } - - virtual bool insert(T* data) - { - //LL_PROFILE_ZONE_NAMED_COLOR("Octree::insert()",OCTREE_DEBUG_COLOR_INSERT); - - if (data == NULL || data->getBinIndex() != -1) - { - OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL; - return false; - } - oct_node* parent = getOctParent(); - - //is it here? - if (isInside(data->getPositionGroup())) - { - if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) || - (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity))) - { //it belongs here - mData.push_back(data); - data->setBinIndex(getElementCount() - 1); - BaseType::insert(data); - return true; - } - else - { - //find a child to give it to - oct_node* child = NULL; - for (U32 i = 0; i < getChildCount(); i++) - { - child = getChild(i); - if (child->isInside(data->getPositionGroup())) - { - child->insert(data); - return false; - } - } - - //it's here, but no kids are in the right place, make a new kid - LLVector4a center = getCenter(); - LLVector4a size = getSize(); - size.mul(0.5f); - - //push center in direction of data - oct_node::pushCenter(center, size, data); - - // handle case where floating point number gets too small - LLVector4a val; - val.setSub(center, getCenter()); - val.setAbs(val); - LLVector4a min_diff(gOctreeMinSize); - - S32 lt = val.lessThan(min_diff).getGatheredBits() & 0x7; - - if( lt == 0x7 ) - { - mData.push_back(data); - data->setBinIndex(getElementCount() - 1); - BaseType::insert(data); - return true; - } - -#if LL_OCTREE_PARANOIA_CHECK - if (getChildCount() == 8) - { - //this really isn't possible, something bad has happened - OCT_ERRS << "Octree detected floating point error and gave up." << LL_ENDL; - return false; - } - - //make sure no existing node matches this position - for (U32 i = 0; i < getChildCount(); i++) - { - if (mChild[i]->getCenter().equals3(center)) - { - OCT_ERRS << "Octree detected duplicate child center and gave up." << LL_ENDL; - return false; - } - } -#endif - - llassert(size[0] >= gOctreeMinSize*0.5f); - //make the new kid - child = new oct_node(center, size, this); - addChild(child); - - child->insert(data); - } - } - else if (parent) - { - //it's not in here, give it to the root - OCT_ERRS << "Octree insertion failed, starting over from root!" << LL_ENDL; - - oct_node* node = this; - - while (parent) - { - node = parent; - parent = node->getOctParent(); - } - - node->insert(data); - } - else - { - // It's not in here, and we are root. - // LLOctreeRoot::insert() should have expanded - // root by now, something is wrong - OCT_ERRS << "Octree insertion failed! Root expansion failed." << LL_ENDL; - } - - return false; - } - - void _remove(T* data, S32 i) - { //precondition -- getElementCount() > 0, idx is in range [0, getElementCount()) - - data->setBinIndex(-1); - - const U32 new_element_count = getElementCount() - 1; - if (new_element_count > 0) - { - if (new_element_count != i) - { - mData[i] = mData[new_element_count]; //might unref data, do not access data after this point - mData[i]->setBinIndex(i); - } - - mData[new_element_count] = NULL; - mData.pop_back(); - } - else - { - mData.clear(); - } - - this->notifyRemoval(data); - checkAlive(); - } - - bool remove(T* data) - { - //LL_PROFILE_ZONE_NAMED_COLOR("Octree::remove()", OCTREE_DEBUG_COLOR_REMOVE); - - S32 i = data->getBinIndex(); - - if (i >= 0 && i < getElementCount()) - { - if (mData[i] == data) - { //found it - _remove(data, i); - llassert(data->getBinIndex() == -1); - return true; - } - } - - if (isInside(data)) - { - oct_node* dest = getNodeAt(data); - - if (dest != this) - { - bool ret = dest->remove(data); - llassert(data->getBinIndex() == -1); - return ret; - } - } - - //SHE'S GONE MISSING... - //none of the children have it, let's just brute force this bastard out - //starting with the root node (UGLY CODE COMETH!) - oct_node* parent = getOctParent(); - oct_node* node = this; - - while (parent != NULL) - { - node = parent; - parent = node->getOctParent(); - } - - //node is now root - LL_WARNS() << "!!! OCTREE REMOVING ELEMENT BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << LL_ENDL; - node->removeByAddress(data); - llassert(data->getBinIndex() == -1); - return true; - } - - void removeByAddress(T* data) - { - const U32 element_count = getElementCount(); - for (U32 i = 0; i < element_count; ++i) - { - if (mData[i] == data) - { //we have data - _remove(data, i); - LL_WARNS() << "FOUND!" << LL_ENDL; - return; - } - } - - for (U32 i = 0; i < getChildCount(); i++) - { //we don't contain data, so pass this guy down - oct_node* child = (oct_node*) getChild(i); - child->removeByAddress(data); - } - } - - void clearChildren() - { - mChildCount = 0; - memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap)); - } - - void validate() - { -#if LL_OCTREE_PARANOIA_CHECK - for (U32 i = 0; i < getChildCount(); i++) - { - mChild[i]->validate(); - if (mChild[i]->getParent() != this) - { - LL_ERRS() << "Octree child has invalid parent." << LL_ENDL; - } - } -#endif - } - - virtual bool balance() - { - return false; - } - - void destroy() - { - for (U32 i = 0; i < getChildCount(); i++) - { - mChild[i]->destroy(); - delete mChild[i]; - } - } - - void addChild(oct_node* child, bool silent = false) - { -#if LL_OCTREE_PARANOIA_CHECK - - if (child->getSize().equals3(getSize())) - { - OCT_ERRS << "Child size is same as parent size!" << LL_ENDL; - } - - for (U32 i = 0; i < getChildCount(); i++) - { - if(!mChild[i]->getSize().equals3(child->getSize())) - { - OCT_ERRS <<"Invalid octree child size." << LL_ENDL; - } - if (mChild[i]->getCenter().equals3(child->getCenter())) - { - OCT_ERRS <<"Duplicate octree child position." << LL_ENDL; - } - } - - if (mChild.size() >= 8) - { - OCT_ERRS <<"Octree node has too many children... why?" << LL_ENDL; - } -#endif - - mChildMap[child->getOctant()] = mChildCount; - - mChild[mChildCount] = child; - ++mChildCount; - child->setParent(this); - - if (!silent) - { - for (U32 i = 0; i < this->getListenerCount(); i++) - { - oct_listener* listener = getOctListener(i); - listener->handleChildAddition(this, child); - } - } - } - - void removeChild(S32 index, bool destroy = false) - { - for (U32 i = 0; i < this->getListenerCount(); i++) - { - oct_listener* listener = getOctListener(i); - listener->handleChildRemoval(this, getChild(index)); - } - - if (destroy) - { - mChild[index]->destroy(); - delete mChild[index]; - } - - --mChildCount; - - mChild[index] = mChild[mChildCount]; - - //rebuild child map - memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap)); - - for (U32 i = 0; i < mChildCount; ++i) - { - mChildMap[mChild[i]->getOctant()] = i; - } - - checkAlive(); - } - - void checkAlive() - { - if (getChildCount() == 0 && getElementCount() == 0) - { - oct_node* parent = getOctParent(); - if (parent) - { - parent->deleteChild(this); - } - } - } - - void deleteChild(oct_node* node) - { - for (U32 i = 0; i < getChildCount(); i++) - { - if (getChild(i) == node) - { - removeChild(i, true); - return; - } - } - - OCT_ERRS << "Octree failed to delete requested child." << LL_ENDL; - } - -protected: - typedef enum - { - CENTER = 0, - SIZE = 1, - MAX = 2, - MIN = 3 - } eDName; - - LLVector4a mCenter; - LLVector4a mSize; - LLVector4a mMax; - LLVector4a mMin; - - oct_node* mParent; - U8 mOctant; - - oct_node* mChild[8]; - U8 mChildMap[8]; - U32 mChildCount; - - element_list mData; -}; - -//just like a regular node, except it might expand on insert and compress on balance -template <class T, typename T_PTR> -class LLOctreeRoot : public LLOctreeNode<T, T_PTR> -{ -public: - typedef LLOctreeNode<T, T_PTR> BaseType; - typedef LLOctreeNode<T, T_PTR> oct_node; - - LLOctreeRoot(const LLVector4a& center, - const LLVector4a& size, - BaseType* parent) - : BaseType(center, size, parent) - { - } - - bool balance() override - { - //LL_PROFILE_ZONE_NAMED_COLOR("Octree::balance()",OCTREE_DEBUG_COLOR_BALANCE); - - if (this->getChildCount() == 1 && - !(this->mChild[0]->isLeaf()) && - this->mChild[0]->getElementCount() == 0) - { //if we have only one child and that child is an empty branch, make that child the root - oct_node* child = this->mChild[0]; - - //make the root node look like the child - this->setCenter(this->mChild[0]->getCenter()); - this->setSize(this->mChild[0]->getSize()); - this->updateMinMax(); - - //reset root node child list - this->clearChildren(); - - //copy the child's children into the root node silently - //(don't notify listeners of addition) - for (U32 i = 0; i < child->getChildCount(); i++) - { - this->addChild(child->getChild(i), true); - } - - //destroy child - child->clearChildren(); - delete child; - - return false; - } - - return true; - } - - // LLOctreeRoot::insert - bool insert(T* data) override - { - if (data == nullptr) - { - OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << LL_ENDL; - return false; - } - - if (data->getBinRadius() > 4096.0) - { - OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << LL_ENDL; - return false; - } - - LLVector4a MAX_MAG; - MAX_MAG.splat(1024.f*1024.f); - - const LLVector4a& v = data->getPositionGroup(); - - LLVector4a val; - val.setSub(v, BaseType::mCenter); - val.setAbs(val); - S32 lt = val.lessThan(MAX_MAG).getGatheredBits() & 0x7; - - if (lt != 0x7) - { - //OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << LL_ENDL; - return false; - } - - if (this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup())) - { - //we got it, just act like a branch - oct_node* node = this->getNodeAt(data); - if (node == this) - { - oct_node::insert(data); - } - else if (node->isInside(data->getPositionGroup())) - { - node->insert(data); - } - else - { - // calling node->insert(data) will return us to root - OCT_ERRS << "Failed to insert data at child node" << LL_ENDL; - } - } - else if (this->getChildCount() == 0) - { - //first object being added, just wrap it up - while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup()))) - { - LLVector4a center, size; - center = this->getCenter(); - size = this->getSize(); - oct_node::pushCenter(center, size, data); - this->setCenter(center); - size.mul(2.f); - this->setSize(size); - this->updateMinMax(); - } - oct_node::insert(data); - } - else - { - while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup()))) - { - //the data is outside the root node, we need to grow - LLVector4a center(this->getCenter()); - LLVector4a size(this->getSize()); - - //expand this node - LLVector4a newcenter(center); - oct_node::pushCenter(newcenter, size, data); - this->setCenter(newcenter); - LLVector4a size2 = size; - size2.mul(2.f); - this->setSize(size2); - this->updateMinMax(); - - llassert(size[0] >= gOctreeMinSize); - - //copy our children to a new branch - oct_node* newnode = new oct_node(center, size, this); - - for (U32 i = 0; i < this->getChildCount(); i++) - { - oct_node* child = this->getChild(i); - newnode->addChild(child); - } - - //clear our children and add the root copy - this->clearChildren(); - this->addChild(newnode); - } - - //insert the data - insert(data); - } - - return false; - } - - bool isLeaf() const override - { - // root can't be a leaf - return false; - } -}; - -//======================== -// LLOctreeTraveler -//======================== -template <class T, typename T_PTR> -void LLOctreeTraveler<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node) -{ - node->accept(this); - for (U32 i = 0; i < node->getChildCount(); i++) - { - traverse(node->getChild(i)); - } -} - -template <class T, typename T_PTR> -void LLOctreeTravelerDepthFirst<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node) -{ - for (U32 i = 0; i < node->getChildCount(); i++) - { - traverse(node->getChild(i)); - } - node->accept(this); -} - -#endif +/**
+ * @file lloctree.h
+ * @brief Octree declaration.
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLOCTREE_H
+#define LL_LLOCTREE_H
+
+#include "lltreenode.h"
+#include "v3math.h"
+#include "llvector4a.h"
+#include <vector>
+
+#define OCT_ERRS LL_WARNS("OctreeErrors")
+
+#define OCTREE_DEBUG_COLOR_REMOVE 0x0000FF // r
+#define OCTREE_DEBUG_COLOR_INSERT 0x00FF00 // g
+#define OCTREE_DEBUG_COLOR_BALANCE 0xFF0000 // b
+
+extern U32 gOctreeMaxCapacity;
+extern float gOctreeMinSize;
+
+/*#define LL_OCTREE_PARANOIA_CHECK 0
+#if LL_DARWIN
+#define LL_OCTREE_MAX_CAPACITY 32
+#else
+#define LL_OCTREE_MAX_CAPACITY 128
+#endif*/
+
+// T is the type of the element referenced by the octree node.
+// T_PTR determines how pointers to elements are stored internally.
+// LLOctreeNode<T, LLPointer<T>> assumes ownership of inserted elements and
+// deletes elements removed from the tree.
+// LLOctreeNode<T, T*> doesn't take ownership of inserted elements, so the API
+// user is responsible for managing the storage lifecycle of elements added to
+// the tree.
+template <class T, typename T_PTR> class LLOctreeNode;
+
+template <class T, typename T_PTR>
+class LLOctreeListener: public LLTreeListener<T>
+{
+public:
+ typedef LLTreeListener<T> BaseType;
+ typedef LLOctreeNode<T, T_PTR> oct_node;
+
+ virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0;
+ virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0;
+};
+
+template <class T, typename T_PTR>
+class LLOctreeTraveler
+{
+public:
+ virtual void traverse(const LLOctreeNode<T, T_PTR>* node);
+ virtual void visit(const LLOctreeNode<T, T_PTR>* branch) = 0;
+};
+
+template <class T, typename T_PTR>
+class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T, T_PTR>
+{
+public:
+ virtual void traverse(const LLOctreeNode<T, T_PTR>* node) override;
+};
+
+template <class T, typename T_PTR>
+class alignas(16) LLOctreeNode : public LLTreeNode<T>
+{
+ LL_ALIGN_NEW
+public:
+
+ typedef LLOctreeTraveler<T, T_PTR> oct_traveler;
+ typedef LLTreeTraveler<T> tree_traveler;
+ typedef std::vector<T_PTR> element_list;
+ typedef typename element_list::iterator element_iter;
+ typedef typename element_list::const_iterator const_element_iter;
+ typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter;
+ typedef LLOctreeNode<T, T_PTR>** child_list;
+ typedef LLOctreeNode<T, T_PTR>** child_iter;
+
+ typedef LLTreeNode<T> BaseType;
+ typedef LLOctreeNode<T, T_PTR> oct_node;
+ typedef LLOctreeListener<T, T_PTR> oct_listener;
+
+ enum
+ {
+ NO_CHILD_NODES = 255 // Note: This is an U8 to match the max value in mChildMap[]
+ };
+
+ LLOctreeNode( const LLVector4a& center,
+ const LLVector4a& size,
+ BaseType* parent,
+ U8 octant = NO_CHILD_NODES)
+ : mParent((oct_node*)parent),
+ mOctant(octant)
+ {
+ llassert(size[0] >= gOctreeMinSize*0.5f);
+
+ mCenter = center;
+ mSize = size;
+
+ updateMinMax();
+ if ((mOctant == NO_CHILD_NODES) && mParent)
+ {
+ mOctant = ((oct_node*) mParent)->getOctant(mCenter);
+ }
+
+ clearChildren();
+ }
+
+ virtual ~LLOctreeNode()
+ {
+ BaseType::destroyListeners();
+
+ const U32 element_count = getElementCount();
+ for (U32 i = 0; i < element_count; ++i)
+ {
+ mData[i]->setBinIndex(-1);
+ mData[i] = NULL;
+ }
+
+ mData.clear();
+
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ delete getChild(i);
+ }
+ }
+
+ inline const BaseType* getParent() const { return mParent; }
+ inline void setParent(BaseType* parent) { mParent = (oct_node*) parent; }
+ inline const LLVector4a& getCenter() const { return mCenter; }
+ inline const LLVector4a& getSize() const { return mSize; }
+ inline void setCenter(const LLVector4a& center) { mCenter = center; }
+ inline void setSize(const LLVector4a& size) { mSize = size; }
+ inline oct_node* getNodeAt(T* data) { return getNodeAt(data->getPositionGroup(), data->getBinRadius()); }
+ inline U8 getOctant() const { return mOctant; }
+ inline const oct_node* getOctParent() const { return (const oct_node*) getParent(); }
+ inline oct_node* getOctParent() { return (oct_node*) getParent(); }
+
+ U8 getOctant(const LLVector4a& pos) const //get the octant pos is in
+ {
+ return (U8) (pos.greaterThan(mCenter).getGatheredBits() & 0x7);
+ }
+
+ inline bool isInside(const LLVector4a& pos, const F32& rad) const
+ {
+ return rad <= mSize[0]*2.f && isInside(pos);
+ }
+
+ inline bool isInside(T* data) const
+ {
+ return isInside(data->getPositionGroup(), data->getBinRadius());
+ }
+
+ bool isInside(const LLVector4a& pos) const
+ {
+ S32 gt = pos.greaterThan(mMax).getGatheredBits() & 0x7;
+ if (gt)
+ {
+ return false;
+ }
+
+ S32 lt = pos.lessEqual(mMin).getGatheredBits() & 0x7;
+ if (lt)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ void updateMinMax()
+ {
+ mMax.setAdd(mCenter, mSize);
+ mMin.setSub(mCenter, mSize);
+ }
+
+ inline oct_listener* getOctListener(U32 index)
+ {
+ return (oct_listener*) BaseType::getListener(index);
+ }
+
+ inline bool contains(T* xform)
+ {
+ return contains(xform->getBinRadius());
+ }
+
+ bool contains(F32 radius)
+ {
+ if (mParent == NULL)
+ { //root node contains nothing
+ return false;
+ }
+
+ F32 size = mSize[0];
+ F32 p_size = size * 2.f;
+
+ return (radius <= gOctreeMinSize && size <= gOctreeMinSize) ||
+ (radius <= p_size && radius > size);
+ }
+
+ static void pushCenter(LLVector4a ¢er, const LLVector4a &size, const T* data)
+ {
+ const LLVector4a& pos = data->getPositionGroup();
+
+ LLVector4Logical gt = pos.greaterThan(center);
+
+ LLVector4a up;
+ up = _mm_and_ps(size, gt);
+
+ LLVector4a down;
+ down = _mm_andnot_ps(gt, size);
+
+ center.add(up);
+ center.sub(down);
+ }
+
+ void accept(oct_traveler* visitor) { visitor->visit(this); }
+ virtual bool isLeaf() const { return mChildCount == 0; }
+
+ U32 getElementCount() const { return (U32)mData.size(); }
+ bool isEmpty() const { return mData.empty(); }
+ element_iter getDataBegin() { return mData.begin(); }
+ element_iter getDataEnd() { return mData.end(); }
+ const_element_iter getDataBegin() const { return mData.cbegin(); }
+ const_element_iter getDataEnd() const { return mData.cend(); }
+
+ U32 getChildCount() const { return mChildCount; }
+ oct_node* getChild(U32 index) { return mChild[index]; }
+ const oct_node* getChild(U32 index) const { return mChild[index]; }
+ child_list& getChildren() { return mChild; }
+ const child_list& getChildren() const { return mChild; }
+
+ void accept(tree_traveler* visitor) const { visitor->visit(this); }
+ void accept(oct_traveler* visitor) const { visitor->visit(this); }
+
+ void validateChildMap()
+ {
+ for (U32 i = 0; i < 8; i++)
+ {
+ U8 idx = mChildMap[i];
+ if (idx != NO_CHILD_NODES)
+ {
+ oct_node* child = mChild[idx];
+
+ if (child->getOctant() != i)
+ {
+ LL_ERRS() << "Invalid child map, bad octant data." << LL_ENDL;
+ }
+
+ if (getOctant(child->getCenter()) != child->getOctant())
+ {
+ LL_ERRS() << "Invalid child octant compared to position data." << LL_ENDL;
+ }
+ }
+ }
+ }
+
+
+ oct_node* getNodeAt(const LLVector4a& pos, const F32& rad)
+ {
+ oct_node* node = this;
+
+ if (node->isInside(pos, rad))
+ {
+ //do a quick search by octant
+ U8 octant = node->getOctant(pos);
+
+ //traverse the tree until we find a node that has no node
+ //at the appropriate octant or is smaller than the object.
+ //by definition, that node is the smallest node that contains
+ // the data
+ U8 next_node = node->mChildMap[octant];
+
+ while (next_node != NO_CHILD_NODES && node->getSize()[0] >= rad)
+ {
+ node = node->getChild(next_node);
+ octant = node->getOctant(pos);
+ next_node = node->mChildMap[octant];
+ }
+ }
+ else if (!node->contains(rad) && node->getParent())
+ { //if we got here, data does not exist in this node
+ return ((oct_node*) node->getParent())->getNodeAt(pos, rad);
+ }
+
+ return node;
+ }
+
+ virtual bool insert(T* data)
+ {
+ //LL_PROFILE_ZONE_NAMED_COLOR("Octree::insert()",OCTREE_DEBUG_COLOR_INSERT);
+
+ if (data == NULL || data->getBinIndex() != -1)
+ {
+ OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL;
+ return false;
+ }
+ oct_node* parent = getOctParent();
+
+ //is it here?
+ if (isInside(data->getPositionGroup()))
+ {
+ if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) ||
+ (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity)))
+ { //it belongs here
+ mData.push_back(data);
+ data->setBinIndex(getElementCount() - 1);
+ BaseType::insert(data);
+ return true;
+ }
+ else
+ {
+ //find a child to give it to
+ oct_node* child = NULL;
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ child = getChild(i);
+ if (child->isInside(data->getPositionGroup()))
+ {
+ child->insert(data);
+ return false;
+ }
+ }
+
+ //it's here, but no kids are in the right place, make a new kid
+ LLVector4a center = getCenter();
+ LLVector4a size = getSize();
+ size.mul(0.5f);
+
+ //push center in direction of data
+ oct_node::pushCenter(center, size, data);
+
+ // handle case where floating point number gets too small
+ LLVector4a val;
+ val.setSub(center, getCenter());
+ val.setAbs(val);
+ LLVector4a min_diff(gOctreeMinSize);
+
+ S32 lt = val.lessThan(min_diff).getGatheredBits() & 0x7;
+
+ if( lt == 0x7 )
+ {
+ mData.push_back(data);
+ data->setBinIndex(getElementCount() - 1);
+ BaseType::insert(data);
+ return true;
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ if (getChildCount() == 8)
+ {
+ //this really isn't possible, something bad has happened
+ OCT_ERRS << "Octree detected floating point error and gave up." << LL_ENDL;
+ return false;
+ }
+
+ //make sure no existing node matches this position
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ if (mChild[i]->getCenter().equals3(center))
+ {
+ OCT_ERRS << "Octree detected duplicate child center and gave up." << LL_ENDL;
+ return false;
+ }
+ }
+#endif
+
+ llassert(size[0] >= gOctreeMinSize*0.5f);
+ //make the new kid
+ child = new oct_node(center, size, this);
+ addChild(child);
+
+ child->insert(data);
+ }
+ }
+ else if (parent)
+ {
+ //it's not in here, give it to the root
+ OCT_ERRS << "Octree insertion failed, starting over from root!" << LL_ENDL;
+
+ oct_node* node = this;
+
+ while (parent)
+ {
+ node = parent;
+ parent = node->getOctParent();
+ }
+
+ node->insert(data);
+ }
+ else
+ {
+ // It's not in here, and we are root.
+ // LLOctreeRoot::insert() should have expanded
+ // root by now, something is wrong
+ OCT_ERRS << "Octree insertion failed! Root expansion failed." << LL_ENDL;
+ }
+
+ return false;
+ }
+
+ void _remove(T* data, S32 i)
+ { //precondition -- getElementCount() > 0, idx is in range [0, getElementCount())
+
+ data->setBinIndex(-1);
+
+ const U32 new_element_count = getElementCount() - 1;
+ if (new_element_count > 0)
+ {
+ if (new_element_count != i)
+ {
+ mData[i] = mData[new_element_count]; //might unref data, do not access data after this point
+ mData[i]->setBinIndex(i);
+ }
+
+ mData[new_element_count] = NULL;
+ mData.pop_back();
+ }
+ else
+ {
+ mData.clear();
+ }
+
+ this->notifyRemoval(data);
+ checkAlive();
+ }
+
+ bool remove(T* data)
+ {
+ //LL_PROFILE_ZONE_NAMED_COLOR("Octree::remove()", OCTREE_DEBUG_COLOR_REMOVE);
+
+ S32 i = data->getBinIndex();
+
+ if (i >= 0 && i < getElementCount())
+ {
+ if (mData[i] == data)
+ { //found it
+ _remove(data, i);
+ llassert(data->getBinIndex() == -1);
+ return true;
+ }
+ }
+
+ if (isInside(data))
+ {
+ oct_node* dest = getNodeAt(data);
+
+ if (dest != this)
+ {
+ bool ret = dest->remove(data);
+ llassert(data->getBinIndex() == -1);
+ return ret;
+ }
+ }
+
+ //SHE'S GONE MISSING...
+ //none of the children have it, let's just brute force this bastard out
+ //starting with the root node (UGLY CODE COMETH!)
+ oct_node* parent = getOctParent();
+ oct_node* node = this;
+
+ while (parent != NULL)
+ {
+ node = parent;
+ parent = node->getOctParent();
+ }
+
+ //node is now root
+ LL_WARNS() << "!!! OCTREE REMOVING ELEMENT BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << LL_ENDL;
+ node->removeByAddress(data);
+ llassert(data->getBinIndex() == -1);
+ return true;
+ }
+
+ void removeByAddress(T* data)
+ {
+ const U32 element_count = getElementCount();
+ for (U32 i = 0; i < element_count; ++i)
+ {
+ if (mData[i] == data)
+ { //we have data
+ _remove(data, i);
+ LL_WARNS() << "FOUND!" << LL_ENDL;
+ return;
+ }
+ }
+
+ for (U32 i = 0; i < getChildCount(); i++)
+ { //we don't contain data, so pass this guy down
+ oct_node* child = (oct_node*) getChild(i);
+ child->removeByAddress(data);
+ }
+ }
+
+ void clearChildren()
+ {
+ mChildCount = 0;
+ memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap));
+ }
+
+ void validate()
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ mChild[i]->validate();
+ if (mChild[i]->getParent() != this)
+ {
+ LL_ERRS() << "Octree child has invalid parent." << LL_ENDL;
+ }
+ }
+#endif
+ }
+
+ virtual bool balance()
+ {
+ return false;
+ }
+
+ void destroy()
+ {
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ mChild[i]->destroy();
+ delete mChild[i];
+ }
+ }
+
+ void addChild(oct_node* child, bool silent = false)
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+
+ if (child->getSize().equals3(getSize()))
+ {
+ OCT_ERRS << "Child size is same as parent size!" << LL_ENDL;
+ }
+
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ if(!mChild[i]->getSize().equals3(child->getSize()))
+ {
+ OCT_ERRS <<"Invalid octree child size." << LL_ENDL;
+ }
+ if (mChild[i]->getCenter().equals3(child->getCenter()))
+ {
+ OCT_ERRS <<"Duplicate octree child position." << LL_ENDL;
+ }
+ }
+
+ if (mChild.size() >= 8)
+ {
+ OCT_ERRS <<"Octree node has too many children... why?" << LL_ENDL;
+ }
+#endif
+
+ mChildMap[child->getOctant()] = mChildCount;
+
+ mChild[mChildCount] = child;
+ ++mChildCount;
+ child->setParent(this);
+
+ if (!silent)
+ {
+ for (U32 i = 0; i < this->getListenerCount(); i++)
+ {
+ oct_listener* listener = getOctListener(i);
+ listener->handleChildAddition(this, child);
+ }
+ }
+ }
+
+ void removeChild(S32 index, bool destroy = false)
+ {
+ for (U32 i = 0; i < this->getListenerCount(); i++)
+ {
+ oct_listener* listener = getOctListener(i);
+ listener->handleChildRemoval(this, getChild(index));
+ }
+
+ if (destroy)
+ {
+ mChild[index]->destroy();
+ delete mChild[index];
+ }
+
+ --mChildCount;
+
+ mChild[index] = mChild[mChildCount];
+
+ //rebuild child map
+ memset(mChildMap, NO_CHILD_NODES, sizeof(mChildMap));
+
+ for (U32 i = 0; i < mChildCount; ++i)
+ {
+ mChildMap[mChild[i]->getOctant()] = i;
+ }
+
+ checkAlive();
+ }
+
+ void checkAlive()
+ {
+ if (getChildCount() == 0 && getElementCount() == 0)
+ {
+ oct_node* parent = getOctParent();
+ if (parent)
+ {
+ parent->deleteChild(this);
+ }
+ }
+ }
+
+ void deleteChild(oct_node* node)
+ {
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ if (getChild(i) == node)
+ {
+ removeChild(i, true);
+ return;
+ }
+ }
+
+ OCT_ERRS << "Octree failed to delete requested child." << LL_ENDL;
+ }
+
+protected:
+ typedef enum
+ {
+ CENTER = 0,
+ SIZE = 1,
+ MAX = 2,
+ MIN = 3
+ } eDName;
+
+ LLVector4a mCenter;
+ LLVector4a mSize;
+ LLVector4a mMax;
+ LLVector4a mMin;
+
+ oct_node* mParent;
+ U8 mOctant;
+
+ oct_node* mChild[8];
+ U8 mChildMap[8];
+ U32 mChildCount;
+
+ element_list mData;
+};
+
+//just like a regular node, except it might expand on insert and compress on balance
+template <class T, typename T_PTR>
+class LLOctreeRoot : public LLOctreeNode<T, T_PTR>
+{
+public:
+ typedef LLOctreeNode<T, T_PTR> BaseType;
+ typedef LLOctreeNode<T, T_PTR> oct_node;
+
+ LLOctreeRoot(const LLVector4a& center,
+ const LLVector4a& size,
+ BaseType* parent)
+ : BaseType(center, size, parent)
+ {
+ }
+
+ bool balance() override
+ {
+ //LL_PROFILE_ZONE_NAMED_COLOR("Octree::balance()",OCTREE_DEBUG_COLOR_BALANCE);
+
+ if (this->getChildCount() == 1 &&
+ !(this->mChild[0]->isLeaf()) &&
+ this->mChild[0]->getElementCount() == 0)
+ { //if we have only one child and that child is an empty branch, make that child the root
+ oct_node* child = this->mChild[0];
+
+ //make the root node look like the child
+ this->setCenter(this->mChild[0]->getCenter());
+ this->setSize(this->mChild[0]->getSize());
+ this->updateMinMax();
+
+ //reset root node child list
+ this->clearChildren();
+
+ //copy the child's children into the root node silently
+ //(don't notify listeners of addition)
+ for (U32 i = 0; i < child->getChildCount(); i++)
+ {
+ this->addChild(child->getChild(i), true);
+ }
+
+ //destroy child
+ child->clearChildren();
+ delete child;
+
+ return false;
+ }
+
+ return true;
+ }
+
+ // LLOctreeRoot::insert
+ bool insert(T* data) override
+ {
+ if (data == nullptr)
+ {
+ OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << LL_ENDL;
+ return false;
+ }
+
+ if (data->getBinRadius() > 4096.0)
+ {
+ OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << LL_ENDL;
+ return false;
+ }
+
+ LLVector4a MAX_MAG;
+ MAX_MAG.splat(1024.f*1024.f);
+
+ const LLVector4a& v = data->getPositionGroup();
+
+ LLVector4a val;
+ val.setSub(v, BaseType::mCenter);
+ val.setAbs(val);
+ S32 lt = val.lessThan(MAX_MAG).getGatheredBits() & 0x7;
+
+ if (lt != 0x7)
+ {
+ //OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << LL_ENDL;
+ return false;
+ }
+
+ if (this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup()))
+ {
+ //we got it, just act like a branch
+ oct_node* node = this->getNodeAt(data);
+ if (node == this)
+ {
+ oct_node::insert(data);
+ }
+ else if (node->isInside(data->getPositionGroup()))
+ {
+ node->insert(data);
+ }
+ else
+ {
+ // calling node->insert(data) will return us to root
+ OCT_ERRS << "Failed to insert data at child node" << LL_ENDL;
+ }
+ }
+ else if (this->getChildCount() == 0)
+ {
+ //first object being added, just wrap it up
+ while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup())))
+ {
+ LLVector4a center, size;
+ center = this->getCenter();
+ size = this->getSize();
+ oct_node::pushCenter(center, size, data);
+ this->setCenter(center);
+ size.mul(2.f);
+ this->setSize(size);
+ this->updateMinMax();
+ }
+ oct_node::insert(data);
+ }
+ else
+ {
+ while (!(this->getSize()[0] > data->getBinRadius() && this->isInside(data->getPositionGroup())))
+ {
+ //the data is outside the root node, we need to grow
+ LLVector4a center(this->getCenter());
+ LLVector4a size(this->getSize());
+
+ //expand this node
+ LLVector4a newcenter(center);
+ oct_node::pushCenter(newcenter, size, data);
+ this->setCenter(newcenter);
+ LLVector4a size2 = size;
+ size2.mul(2.f);
+ this->setSize(size2);
+ this->updateMinMax();
+
+ llassert(size[0] >= gOctreeMinSize);
+
+ //copy our children to a new branch
+ oct_node* newnode = new oct_node(center, size, this);
+
+ for (U32 i = 0; i < this->getChildCount(); i++)
+ {
+ oct_node* child = this->getChild(i);
+ newnode->addChild(child);
+ }
+
+ //clear our children and add the root copy
+ this->clearChildren();
+ this->addChild(newnode);
+ }
+
+ //insert the data
+ insert(data);
+ }
+
+ return false;
+ }
+
+ bool isLeaf() const override
+ {
+ // root can't be a leaf
+ return false;
+ }
+};
+
+//========================
+// LLOctreeTraveler
+//========================
+template <class T, typename T_PTR>
+void LLOctreeTraveler<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
+{
+ node->accept(this);
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ traverse(node->getChild(i));
+ }
+}
+
+template <class T, typename T_PTR>
+void LLOctreeTravelerDepthFirst<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
+{
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ traverse(node->getChild(i));
+ }
+ node->accept(this);
+}
+
+#endif
diff --git a/indra/llmath/llperlin.cpp b/indra/llmath/llperlin.cpp index e1da2bf92b..1fcad07f49 100644 --- a/indra/llmath/llperlin.cpp +++ b/indra/llmath/llperlin.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llperlin.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$ */ @@ -44,69 +44,69 @@ bool LLPerlinNoise::sInitialized = 0; static void normalize2(F32 v[2]) { - F32 s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1]); - v[0] = v[0] * s; - v[1] = v[1] * s; + F32 s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1]); + v[0] = v[0] * s; + v[1] = v[1] * s; } static void normalize3(F32 v[3]) { - F32 s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - v[0] = v[0] * s; - v[1] = v[1] * s; - v[2] = v[2] * s; + F32 s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + v[0] = v[0] * s; + v[1] = v[1] * s; + v[2] = v[2] * s; } static void fast_setup(F32 vec, U8 &b0, U8 &b1, F32 &r0, F32 &r1) { - S32 t_S32; - - r1 = vec + NF32; - t_S32 = lltrunc(r1); - b0 = (U8)t_S32; - b1 = b0 + 1; - r0 = r1 - t_S32; - r1 = r0 - 1.f; + S32 t_S32; + + r1 = vec + NF32; + t_S32 = lltrunc(r1); + b0 = (U8)t_S32; + b1 = b0 + 1; + r0 = r1 - t_S32; + r1 = r0 - 1.f; } void LLPerlinNoise::init(void) { - int i, j, k; - - for (i = 0 ; i < B ; i++) - { - p[i] = i; - - g1[i] = (F32)((rand() % (B + B)) - B) / B; - - for (j = 0 ; j < 2 ; j++) - g2[i][j] = (F32)((rand() % (B + B)) - B) / B; - normalize2(g2[i]); - - for (j = 0 ; j < 3 ; j++) - g3[i][j] = (F32)((rand() % (B + B)) - B) / B; - normalize3(g3[i]); - } - - while (--i) - { - k = p[i]; - p[i] = p[j = rand() % B]; - p[j] = k; - } - - for (i = 0 ; i < B + 2 ; i++) - { - p[B + i] = p[i]; - g1[B + i] = g1[i]; - for (j = 0 ; j < 2 ; j++) - g2[B + i][j] = g2[i][j]; - for (j = 0 ; j < 3 ; j++) - g3[B + i][j] = g3[i][j]; - } - - sInitialized = true; + int i, j, k; + + for (i = 0 ; i < B ; i++) + { + p[i] = i; + + g1[i] = (F32)((rand() % (B + B)) - B) / B; + + for (j = 0 ; j < 2 ; j++) + g2[i][j] = (F32)((rand() % (B + B)) - B) / B; + normalize2(g2[i]); + + for (j = 0 ; j < 3 ; j++) + g3[i][j] = (F32)((rand() % (B + B)) - B) / B; + normalize3(g3[i]); + } + + while (--i) + { + k = p[i]; + p[i] = p[j = rand() % B]; + p[j] = k; + } + + for (i = 0 ; i < B + 2 ; i++) + { + p[B + i] = p[i]; + g1[B + i] = g1[i]; + for (j = 0 ; j < 2 ; j++) + g2[B + i][j] = g2[i][j]; + for (j = 0 ; j < 3 ; j++) + g3[B + i][j] = g3[i][j]; + } + + sInitialized = true; } @@ -119,176 +119,176 @@ void LLPerlinNoise::init(void) F32 LLPerlinNoise::noise1(F32 x) { - int bx0, bx1; - F32 rx0, rx1, sx, t, u, v; + int bx0, bx1; + F32 rx0, rx1, sx, t, u, v; - if (!sInitialized) - init(); + if (!sInitialized) + init(); - t = x + N; - bx0 = (lltrunc(t)) & BM; - bx1 = (bx0+1) & BM; - rx0 = t - lltrunc(t); - rx1 = rx0 - 1.f; + t = x + N; + bx0 = (lltrunc(t)) & BM; + bx1 = (bx0+1) & BM; + rx0 = t - lltrunc(t); + rx1 = rx0 - 1.f; - sx = s_curve(rx0); + sx = s_curve(rx0); - u = rx0 * g1[ p[ bx0 ] ]; - v = rx1 * g1[ p[ bx1 ] ]; + u = rx0 * g1[ p[ bx0 ] ]; + v = rx1 * g1[ p[ bx1 ] ]; - return lerp_m(sx, u, v); + return lerp_m(sx, u, v); } static F32 fast_at2(F32 rx, F32 ry, F32 *q) { - return rx * q[0] + ry * q[1]; + return rx * q[0] + ry * q[1]; } F32 LLPerlinNoise::noise2(F32 x, F32 y) { - U8 bx0, bx1, by0, by1; - U32 b00, b10, b01, b11; - F32 rx0, rx1, ry0, ry1, *q, sx, sy, a, b, u, v; - S32 i, j; + U8 bx0, bx1, by0, by1; + U32 b00, b10, b01, b11; + F32 rx0, rx1, ry0, ry1, *q, sx, sy, a, b, u, v; + S32 i, j; - if (!sInitialized) - init(); + if (!sInitialized) + init(); - fast_setup(x, bx0, bx1, rx0, rx1); - fast_setup(y, by0, by1, ry0, ry1); + fast_setup(x, bx0, bx1, rx0, rx1); + fast_setup(y, by0, by1, ry0, ry1); - i = *(p + bx0); - j = *(p + bx1); + i = *(p + bx0); + j = *(p + bx1); - b00 = *(p + i + by0); - b10 = *(p + j + by0); - b01 = *(p + i + by1); - b11 = *(p + j + by1); + b00 = *(p + i + by0); + b10 = *(p + j + by0); + b01 = *(p + i + by1); + b11 = *(p + j + by1); - sx = s_curve(rx0); - sy = s_curve(ry0); + sx = s_curve(rx0); + sy = s_curve(ry0); - q = *(g2 + b00); - u = fast_at2(rx0, ry0, q); - q = *(g2 + b10); - v = fast_at2(rx1, ry0, q); - a = lerp_m(sx, u, v); + q = *(g2 + b00); + u = fast_at2(rx0, ry0, q); + q = *(g2 + b10); + v = fast_at2(rx1, ry0, q); + a = lerp_m(sx, u, v); - q = *(g2 + b01); - u = fast_at2(rx0,ry1,q); - q = *(g2 + b11); - v = fast_at2(rx1,ry1,q); - b = lerp_m(sx, u, v); + q = *(g2 + b01); + u = fast_at2(rx0,ry1,q); + q = *(g2 + b11); + v = fast_at2(rx1,ry1,q); + b = lerp_m(sx, u, v); - return lerp_m(sy, a, b); + return lerp_m(sy, a, b); } static F32 fast_at3(F32 rx, F32 ry, F32 rz, F32 *q) { - return rx * q[0] + ry * q[1] + rz * q[2]; + return rx * q[0] + ry * q[1] + rz * q[2]; } F32 LLPerlinNoise::noise3(F32 x, F32 y, F32 z) { - U8 bx0, bx1, by0, by1, bz0, bz1; - S32 b00, b10, b01, b11; - F32 rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v; - S32 i, j; - - if (!sInitialized) - init(); - - fast_setup(x, bx0,bx1, rx0,rx1); - fast_setup(y, by0,by1, ry0,ry1); - fast_setup(z, bz0,bz1, rz0,rz1); - - i = p[ bx0 ]; - j = p[ bx1 ]; - - b00 = p[ i + by0 ]; - b10 = p[ j + by0 ]; - b01 = p[ i + by1 ]; - b11 = p[ j + by1 ]; - - t = s_curve(rx0); - sy = s_curve(ry0); - sz = s_curve(rz0); - - q = g3[ b00 + bz0 ]; - u = fast_at3(rx0,ry0,rz0,q); - q = g3[ b10 + bz0 ]; - v = fast_at3(rx1,ry0,rz0,q); - a = lerp_m(t, u, v); - - q = g3[ b01 + bz0 ]; - u = fast_at3(rx0,ry1,rz0,q); - q = g3[ b11 + bz0 ]; - v = fast_at3(rx1,ry1,rz0,q); - b = lerp_m(t, u, v); - - c = lerp_m(sy, a, b); - - q = g3[ b00 + bz1 ]; - u = fast_at3(rx0,ry0,rz1,q); - q = g3[ b10 + bz1 ]; - v = fast_at3(rx1,ry0,rz1,q); - a = lerp_m(t, u, v); - - q = g3[ b01 + bz1 ]; - u = fast_at3(rx0,ry1,rz1,q); - q = g3[ b11 + bz1 ]; - v = fast_at3(rx1,ry1,rz1,q); - b = lerp_m(t, u, v); - - d = lerp_m(sy, a, b); - - return lerp_m(sz, c, d); + U8 bx0, bx1, by0, by1, bz0, bz1; + S32 b00, b10, b01, b11; + F32 rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v; + S32 i, j; + + if (!sInitialized) + init(); + + fast_setup(x, bx0,bx1, rx0,rx1); + fast_setup(y, by0,by1, ry0,ry1); + fast_setup(z, bz0,bz1, rz0,rz1); + + i = p[ bx0 ]; + j = p[ bx1 ]; + + b00 = p[ i + by0 ]; + b10 = p[ j + by0 ]; + b01 = p[ i + by1 ]; + b11 = p[ j + by1 ]; + + t = s_curve(rx0); + sy = s_curve(ry0); + sz = s_curve(rz0); + + q = g3[ b00 + bz0 ]; + u = fast_at3(rx0,ry0,rz0,q); + q = g3[ b10 + bz0 ]; + v = fast_at3(rx1,ry0,rz0,q); + a = lerp_m(t, u, v); + + q = g3[ b01 + bz0 ]; + u = fast_at3(rx0,ry1,rz0,q); + q = g3[ b11 + bz0 ]; + v = fast_at3(rx1,ry1,rz0,q); + b = lerp_m(t, u, v); + + c = lerp_m(sy, a, b); + + q = g3[ b00 + bz1 ]; + u = fast_at3(rx0,ry0,rz1,q); + q = g3[ b10 + bz1 ]; + v = fast_at3(rx1,ry0,rz1,q); + a = lerp_m(t, u, v); + + q = g3[ b01 + bz1 ]; + u = fast_at3(rx0,ry1,rz1,q); + q = g3[ b11 + bz1 ]; + v = fast_at3(rx1,ry1,rz1,q); + b = lerp_m(t, u, v); + + d = lerp_m(sy, a, b); + + return lerp_m(sz, c, d); } F32 LLPerlinNoise::turbulence2(F32 x, F32 y, F32 freq) { - F32 t, lx, ly; - - for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) - { - lx = freq * x; - ly = freq * y; - t += noise2(lx, ly)/freq; - } - return t; + F32 t, lx, ly; + + for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) + { + lx = freq * x; + ly = freq * y; + t += noise2(lx, ly)/freq; + } + return t; } F32 LLPerlinNoise::turbulence3(F32 x, F32 y, F32 z, F32 freq) { - F32 t, lx, ly, lz; - - for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) - { - lx = freq * x; - ly = freq * y; - lz = freq * z; - t += noise3(lx,ly,lz)/freq; -// t += fabs(noise3(lx,ly,lz)) / freq; // Like snow - bubbly at low frequencies -// t += sqrt(fabs(noise3(lx,ly,lz))) / freq; // Better at low freq -// t += (noise3(lx,ly,lz)*noise3(lx,ly,lz)) / freq; - } - return t; + F32 t, lx, ly, lz; + + for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) + { + lx = freq * x; + ly = freq * y; + lz = freq * z; + t += noise3(lx,ly,lz)/freq; +// t += fabs(noise3(lx,ly,lz)) / freq; // Like snow - bubbly at low frequencies +// t += sqrt(fabs(noise3(lx,ly,lz))) / freq; // Better at low freq +// t += (noise3(lx,ly,lz)*noise3(lx,ly,lz)) / freq; + } + return t; } F32 LLPerlinNoise::clouds3(F32 x, F32 y, F32 z, F32 freq) { - F32 t, lx, ly, lz; - - for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) - { - lx = freq * x; - ly = freq * y; - lz = freq * z; -// t += noise3(lx,ly,lz)/freq; -// t += fabs(noise3(lx,ly,lz)) / freq; // Like snow - bubbly at low frequencies -// t += sqrt(fabs(noise3(lx,ly,lz))) / freq; // Better at low freq - t += (noise3(lx,ly,lz)*noise3(lx,ly,lz)) / freq; - } - return t; + F32 t, lx, ly, lz; + + for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) + { + lx = freq * x; + ly = freq * y; + lz = freq * z; +// t += noise3(lx,ly,lz)/freq; +// t += fabs(noise3(lx,ly,lz)) / freq; // Like snow - bubbly at low frequencies +// t += sqrt(fabs(noise3(lx,ly,lz))) / freq; // Better at low freq + t += (noise3(lx,ly,lz)*noise3(lx,ly,lz)) / freq; + } + return t; } diff --git a/indra/llmath/llperlin.h b/indra/llmath/llperlin.h index 40cf19d1ec..2b001ba951 100644 --- a/indra/llmath/llperlin.h +++ b/indra/llmath/llperlin.h @@ -1,24 +1,24 @@ -/** +/** * @file llperlin.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$ */ @@ -32,15 +32,15 @@ class LLPerlinNoise { public: - static F32 noise1(F32 x); - static F32 noise2(F32 x, F32 y); - static F32 noise3(F32 x, F32 y, F32 z); - static F32 turbulence2(F32 x, F32 y, F32 freq); - static F32 turbulence3(F32 x, F32 y, F32 z, F32 freq); - static F32 clouds3(F32 x, F32 y, F32 z, F32 freq); + static F32 noise1(F32 x); + static F32 noise2(F32 x, F32 y); + static F32 noise3(F32 x, F32 y, F32 z); + static F32 turbulence2(F32 x, F32 y, F32 freq); + static F32 turbulence3(F32 x, F32 y, F32 z, F32 freq); + static F32 clouds3(F32 x, F32 y, F32 z, F32 freq); private: - static bool sInitialized; - static void init(void); + static bool sInitialized; + static void init(void); }; #endif // LL_PERLIN_ diff --git a/indra/llmath/llplane.h b/indra/llmath/llplane.h index 64a3eed0e5..4e8546e32b 100644 --- a/indra/llmath/llplane.h +++ b/indra/llmath/llplane.h @@ -1,24 +1,24 @@ -/** +/** * @file llplane.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$ */ @@ -31,7 +31,7 @@ // A simple way to specify a plane is to give its normal, // and it's nearest approach to the origin. -// +// // Given the equation for a plane : A*x + B*y + C*z + D = 0 // The plane normal = [A, B, C] // The closest approach = D / sqrt(A*A + B*B + C*C) @@ -41,67 +41,67 @@ LL_ALIGN_PREFIX(16) class LLPlane { public: - - // Constructors - LLPlane() {}; // no default constructor - LLPlane(const LLVector3 &p0, F32 d) { setVec(p0, d); } - LLPlane(const LLVector3 &p0, const LLVector3 &n) { setVec(p0, n); } - inline void setVec(const LLVector3 &p0, F32 d) { mV.set(p0[0], p0[1], p0[2], d); } - - // Set - inline void setVec(const LLVector3 &p0, const LLVector3 &n) - { - F32 d = -(p0 * n); - setVec(n, d); - } - inline void setVec(const LLVector3 &p0, const LLVector3 &p1, const LLVector3 &p2) - { - LLVector3 u, v, w; - u = p1 - p0; - v = p2 - p0; - w = u % v; - w.normVec(); - F32 d = -(w * p0); - setVec(w, d); - } - - inline LLPlane& operator=(const LLVector4& v2) { mV.set(v2[0],v2[1],v2[2],v2[3]); return *this;} - - inline LLPlane& operator=(const LLVector4a& v2) { mV.set(v2[0],v2[1],v2[2],v2[3]); return *this;} - - inline void set(const LLPlane& p2) { mV = p2.mV; } - - // - F32 dist(const LLVector3 &v2) const { return mV[0]*v2[0] + mV[1]*v2[1] + mV[2]*v2[2] + mV[3]; } - - inline LLSimdScalar dot3(const LLVector4a& b) const { return mV.dot3(b); } - - // Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates - // the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead - inline F32 operator[](const S32 idx) const { return mV[idx]; } - - // preferable when index is known at compile time - template <int N> LL_FORCE_INLINE void getAt(LLSimdScalar& v) const { v = mV.getScalarAt<N>(); } - - // reset the vector to 0, 0, 0, 1 - inline void clear() { mV.set(0, 0, 0, 1); } - - inline void getVector3(LLVector3& vec) const { vec.set(mV[0], mV[1], mV[2]); } - - // Retrieve the mask indicating which of the x, y, or z axis are greater or equal to zero. - inline U8 calcPlaneMask() - { - return mV.greaterEqual(LLVector4a::getZero()).getGatheredBits() & LLVector4Logical::MASK_XYZ; - } - - //check if two planes are nearly same - bool equal(const LLPlane& p) const - { - return mV.equals4(p.mV); - } + + // Constructors + LLPlane() {}; // no default constructor + LLPlane(const LLVector3 &p0, F32 d) { setVec(p0, d); } + LLPlane(const LLVector3 &p0, const LLVector3 &n) { setVec(p0, n); } + inline void setVec(const LLVector3 &p0, F32 d) { mV.set(p0[0], p0[1], p0[2], d); } + + // Set + inline void setVec(const LLVector3 &p0, const LLVector3 &n) + { + F32 d = -(p0 * n); + setVec(n, d); + } + inline void setVec(const LLVector3 &p0, const LLVector3 &p1, const LLVector3 &p2) + { + LLVector3 u, v, w; + u = p1 - p0; + v = p2 - p0; + w = u % v; + w.normVec(); + F32 d = -(w * p0); + setVec(w, d); + } + + inline LLPlane& operator=(const LLVector4& v2) { mV.set(v2[0],v2[1],v2[2],v2[3]); return *this;} + + inline LLPlane& operator=(const LLVector4a& v2) { mV.set(v2[0],v2[1],v2[2],v2[3]); return *this;} + + inline void set(const LLPlane& p2) { mV = p2.mV; } + + // + F32 dist(const LLVector3 &v2) const { return mV[0]*v2[0] + mV[1]*v2[1] + mV[2]*v2[2] + mV[3]; } + + inline LLSimdScalar dot3(const LLVector4a& b) const { return mV.dot3(b); } + + // Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates + // the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead + inline F32 operator[](const S32 idx) const { return mV[idx]; } + + // preferable when index is known at compile time + template <int N> LL_FORCE_INLINE void getAt(LLSimdScalar& v) const { v = mV.getScalarAt<N>(); } + + // reset the vector to 0, 0, 0, 1 + inline void clear() { mV.set(0, 0, 0, 1); } + + inline void getVector3(LLVector3& vec) const { vec.set(mV[0], mV[1], mV[2]); } + + // Retrieve the mask indicating which of the x, y, or z axis are greater or equal to zero. + inline U8 calcPlaneMask() + { + return mV.greaterEqual(LLVector4a::getZero()).getGatheredBits() & LLVector4Logical::MASK_XYZ; + } + + //check if two planes are nearly same + bool equal(const LLPlane& p) const + { + return mV.equals4(p.mV); + } private: - LLVector4a mV; + LLVector4a mV; } LL_ALIGN_POSTFIX(16); diff --git a/indra/llmath/llquantize.h b/indra/llmath/llquantize.h index 10c950abbb..e8d880122e 100644 --- a/indra/llmath/llquantize.h +++ b/indra/llmath/llquantize.h @@ -1,4 +1,4 @@ -/** +/** * @file llquantize.h * @brief useful routines for quantizing floats to various length ints * and back out again @@ -6,21 +6,21 @@ * $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$ */ @@ -41,112 +41,112 @@ const F32 OOU8MAX = 1.f/(F32)(U8MAX); LL_ALIGN_16( const F32 F_OOU8MAX_4A[4] ) = { OOU8MAX, OOU8MAX, OOU8MAX, OOU8MAX }; const U8 FIRSTVALIDCHAR = 54; -const U8 MAXSTRINGVAL = U8MAX - FIRSTVALIDCHAR; //we don't allow newline or null +const U8 MAXSTRINGVAL = U8MAX - FIRSTVALIDCHAR; //we don't allow newline or null inline U16 F32_to_U16_ROUND(F32 val, F32 lower, F32 upper) { - val = llclamp(val, lower, upper); - // make sure that the value is positive and normalized to <0, 1> - val -= lower; - val /= (upper - lower); + val = llclamp(val, lower, upper); + // make sure that the value is positive and normalized to <0, 1> + val -= lower; + val /= (upper - lower); - // round the value. Sreturn the U16 - return (U16)(ll_round(val*U16MAX)); + // round the value. Sreturn the U16 + return (U16)(ll_round(val*U16MAX)); } inline U16 F32_to_U16(F32 val, F32 lower, F32 upper) { - val = llclamp(val, lower, upper); - // make sure that the value is positive and normalized to <0, 1> - val -= lower; - val /= (upper - lower); + val = llclamp(val, lower, upper); + // make sure that the value is positive and normalized to <0, 1> + val -= lower; + val /= (upper - lower); - // return the U16 - return (U16)(llfloor(val*U16MAX)); + // return the U16 + return (U16)(llfloor(val*U16MAX)); } inline F32 U16_to_F32(U16 ival, F32 lower, F32 upper) { - F32 val = ival*OOU16MAX; - F32 delta = (upper - lower); - val *= delta; - val += lower; + F32 val = ival*OOU16MAX; + F32 delta = (upper - lower); + val *= delta; + val += lower; - F32 max_error = delta*OOU16MAX; + F32 max_error = delta*OOU16MAX; - // make sure that zero's come through as zero - if (fabsf(val) < max_error) - val = 0.f; + // make sure that zero's come through as zero + if (fabsf(val) < max_error) + val = 0.f; - return val; + return val; } inline U8 F32_to_U8_ROUND(F32 val, F32 lower, F32 upper) { - val = llclamp(val, lower, upper); - // make sure that the value is positive and normalized to <0, 1> - val -= lower; - val /= (upper - lower); + val = llclamp(val, lower, upper); + // make sure that the value is positive and normalized to <0, 1> + val -= lower; + val /= (upper - lower); - // return the rounded U8 - return (U8)(ll_round(val*U8MAX)); + // return the rounded U8 + return (U8)(ll_round(val*U8MAX)); } inline U8 F32_to_U8(F32 val, F32 lower, F32 upper) { - val = llclamp(val, lower, upper); - // make sure that the value is positive and normalized to <0, 1> - val -= lower; - val /= (upper - lower); + val = llclamp(val, lower, upper); + // make sure that the value is positive and normalized to <0, 1> + val -= lower; + val /= (upper - lower); - // return the U8 - return (U8)(llfloor(val*U8MAX)); + // return the U8 + return (U8)(llfloor(val*U8MAX)); } inline F32 U8_to_F32(U8 ival, F32 lower, F32 upper) { - F32 val = ival*OOU8MAX; - F32 delta = (upper - lower); - val *= delta; - val += lower; + F32 val = ival*OOU8MAX; + F32 delta = (upper - lower); + val *= delta; + val += lower; - F32 max_error = delta*OOU8MAX; + F32 max_error = delta*OOU8MAX; - // make sure that zero's come through as zero - if (fabsf(val) < max_error) - val = 0.f; + // make sure that zero's come through as zero + if (fabsf(val) < max_error) + val = 0.f; - return val; + return val; } inline U8 F32_TO_STRING(F32 val, F32 lower, F32 upper) { - val = llclamp(val, lower, upper); //[lower, upper] - // make sure that the value is positive and normalized to <0, 1> - val -= lower; //[0, upper-lower] - val /= (upper - lower); //[0,1] - val = val * MAXSTRINGVAL; //[0, MAXSTRINGVAL] - val = floor(val + 0.5f); //[0, MAXSTRINGVAL] - - U8 stringVal = (U8)(val) + FIRSTVALIDCHAR; //[FIRSTVALIDCHAR, MAXSTRINGVAL + FIRSTVALIDCHAR] - return stringVal; + val = llclamp(val, lower, upper); //[lower, upper] + // make sure that the value is positive and normalized to <0, 1> + val -= lower; //[0, upper-lower] + val /= (upper - lower); //[0,1] + val = val * MAXSTRINGVAL; //[0, MAXSTRINGVAL] + val = floor(val + 0.5f); //[0, MAXSTRINGVAL] + + U8 stringVal = (U8)(val) + FIRSTVALIDCHAR; //[FIRSTVALIDCHAR, MAXSTRINGVAL + FIRSTVALIDCHAR] + return stringVal; } inline F32 STRING_TO_F32(U8 ival, F32 lower, F32 upper) { - // remove empty space left for NULL, newline, etc. - ival -= FIRSTVALIDCHAR; //[0, MAXSTRINGVAL] + // remove empty space left for NULL, newline, etc. + ival -= FIRSTVALIDCHAR; //[0, MAXSTRINGVAL] - F32 val = (F32)ival * (1.f / (F32)MAXSTRINGVAL); //[0, 1] - F32 delta = (upper - lower); - val *= delta; //[0, upper - lower] - val += lower; //[lower, upper] + F32 val = (F32)ival * (1.f / (F32)MAXSTRINGVAL); //[0, 1] + F32 delta = (upper - lower); + val *= delta; //[0, upper - lower] + val += lower; //[lower, upper] - return val; + return val; } #endif diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp index dd3d552832..90d908c07c 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 1c9da7c342..7a245475c1 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/llquaternion2.h b/indra/llmath/llquaternion2.h index fd9c0cf3ab..902bfb7134 100644 --- a/indra/llmath/llquaternion2.h +++ b/indra/llmath/llquaternion2.h @@ -1,31 +1,31 @@ -/** +/** * @file llquaternion2.h * @brief LLQuaternion2 class header file - SIMD-enabled quaternion class * * $LicenseInfo:firstyear=2010&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_QUATERNION2_H -#define LL_QUATERNION2_H +#ifndef LL_QUATERNION2_H +#define LL_QUATERNION2_H ///////////////////////////// // LLQuaternion2 @@ -44,61 +44,61 @@ class LLQuaternion2 { public: - ////////////////////////// - // Ctors - ////////////////////////// - - // Ctor - LLQuaternion2() {} + ////////////////////////// + // Ctors + ////////////////////////// + + // Ctor + LLQuaternion2() {} + + // Ctor from LLQuaternion + explicit LLQuaternion2( const class LLQuaternion& quat ); - // Ctor from LLQuaternion - explicit LLQuaternion2( const class LLQuaternion& quat ); + ////////////////////////// + // Get/Set + ////////////////////////// - ////////////////////////// - // Get/Set - ////////////////////////// + // Load from an LLQuaternion + inline void operator=( const LLQuaternion& quat ) + { + mQ.loadua( quat.mQ ); + } - // Load from an LLQuaternion - inline void operator=( const LLQuaternion& quat ) - { - mQ.loadua( quat.mQ ); - } + // Return the internal LLVector4a representation of the quaternion + inline const LLVector4a& getVector4a() const; + inline LLVector4a& getVector4aRw(); - // Return the internal LLVector4a representation of the quaternion - inline const LLVector4a& getVector4a() const; - inline LLVector4a& getVector4aRw(); + ///////////////////////// + // Quaternion modification + ///////////////////////// - ///////////////////////// - // Quaternion modification - ///////////////////////// - - // Set this quaternion to the conjugate of src - inline void setConjugate(const LLQuaternion2& src); + // Set this quaternion to the conjugate of src + inline void setConjugate(const LLQuaternion2& src); - // Renormalizes the quaternion. Assumes it has nonzero length. - inline void normalize(); + // Renormalizes the quaternion. Assumes it has nonzero length. + inline void normalize(); - // Quantize this quaternion to 8 bit precision - inline void quantize8(); + // Quantize this quaternion to 8 bit precision + inline void quantize8(); - // Quantize this quaternion to 16 bit precision - inline void quantize16(); + // Quantize this quaternion to 16 bit precision + inline void quantize16(); - ///////////////////////// - // Quaternion inspection - ///////////////////////// + ///////////////////////// + // Quaternion inspection + ///////////////////////// - // Return true if this quaternion is equal to 'rhs'. - // Note! Quaternions exhibit "double-cover", so any rotation has two equally valid - // quaternion representations and they will NOT compare equal. - inline bool equals(const LLQuaternion2& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; + // Return true if this quaternion is equal to 'rhs'. + // Note! Quaternions exhibit "double-cover", so any rotation has two equally valid + // quaternion representations and they will NOT compare equal. + inline bool equals(const LLQuaternion2& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; - // Return true if all components are finite and the quaternion is normalized - inline bool isOkRotation() const; + // Return true if all components are finite and the quaternion is normalized + inline bool isOkRotation() const; protected: - LLVector4a mQ; + LLVector4a mQ; }; diff --git a/indra/llmath/llquaternion2.inl b/indra/llmath/llquaternion2.inl index 2a6987552d..ce5ed73926 100644 --- a/indra/llmath/llquaternion2.inl +++ b/indra/llmath/llquaternion2.inl @@ -1,25 +1,25 @@ -/** +/** * @file llquaternion2.inl * @brief LLQuaternion2 inline definitions * * $LicenseInfo:firstyear=2010&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$ */ @@ -32,7 +32,7 @@ static const LLQuad LL_V4A_MINUS_ONE = {-1.f, -1.f, -1.f, -1.f}; // Ctor from LLQuaternion inline LLQuaternion2::LLQuaternion2( const LLQuaternion& quat ) { - mQ.set(quat.mQ[VX], quat.mQ[VY], quat.mQ[VZ], quat.mQ[VW]); + mQ.set(quat.mQ[VX], quat.mQ[VY], quat.mQ[VZ], quat.mQ[VW]); } ////////////////////////// @@ -42,12 +42,12 @@ inline LLQuaternion2::LLQuaternion2( const LLQuaternion& quat ) // Return the internal LLVector4a representation of the quaternion inline const LLVector4a& LLQuaternion2::getVector4a() const { - return mQ; + return mQ; } inline LLVector4a& LLQuaternion2::getVector4aRw() { - return mQ; + return mQ; } ///////////////////////// @@ -57,28 +57,28 @@ inline LLVector4a& LLQuaternion2::getVector4aRw() // Set this quaternion to the conjugate of src inline void LLQuaternion2::setConjugate(const LLQuaternion2& src) { - static LL_ALIGN_16( const U32 F_QUAT_INV_MASK_4A[4] ) = { 0x80000000, 0x80000000, 0x80000000, 0x00000000 }; - mQ = _mm_xor_ps(src.mQ, *reinterpret_cast<const LLQuad*>(&F_QUAT_INV_MASK_4A)); + static LL_ALIGN_16( const U32 F_QUAT_INV_MASK_4A[4] ) = { 0x80000000, 0x80000000, 0x80000000, 0x00000000 }; + mQ = _mm_xor_ps(src.mQ, *reinterpret_cast<const LLQuad*>(&F_QUAT_INV_MASK_4A)); } // Renormalizes the quaternion. Assumes it has nonzero length. inline void LLQuaternion2::normalize() { - mQ.normalize4(); + mQ.normalize4(); } // Quantize this quaternion to 8 bit precision inline void LLQuaternion2::quantize8() { - mQ.quantize8( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE ); - normalize(); + mQ.quantize8( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE ); + normalize(); } // Quantize this quaternion to 16 bit precision inline void LLQuaternion2::quantize16() { - mQ.quantize16( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE ); - normalize(); + mQ.quantize16( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE ); + normalize(); } @@ -86,17 +86,17 @@ inline void LLQuaternion2::quantize16() // Quaternion inspection ///////////////////////// -// Return true if this quaternion is equal to 'rhs'. +// Return true if this quaternion is equal to 'rhs'. // Note! Quaternions exhibit "double-cover", so any rotation has two equally valid // quaternion representations and they will NOT compare equal. inline bool LLQuaternion2::equals(const LLQuaternion2 &rhs, F32 tolerance/* = F_APPROXIMATELY_ZERO*/) const { - return mQ.equals4(rhs.mQ, tolerance); + return mQ.equals4(rhs.mQ, tolerance); } // Return true if all components are finite and the quaternion is normalized inline bool LLQuaternion2::isOkRotation() const { - return mQ.isFinite4() && mQ.isNormalized4(); + return mQ.isFinite4() && mQ.isNormalized4(); } diff --git a/indra/llmath/llrect.cpp b/indra/llmath/llrect.cpp index 4083c99768..2a40dc69dd 100644 --- a/indra/llmath/llrect.cpp +++ b/indra/llmath/llrect.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llrect.cpp * @brief LLRect 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$ */ diff --git a/indra/llmath/llrect.h b/indra/llmath/llrect.h index 25af04be06..5ec3ca0dff 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/llrigginginfo.cpp b/indra/llmath/llrigginginfo.cpp index 0de07950c1..23dbddd78e 100644 --- a/indra/llmath/llrigginginfo.cpp +++ b/indra/llmath/llrigginginfo.cpp @@ -46,7 +46,7 @@ void LLJointRiggingInfo::setIsRiggedTo(bool val) { mIsRiggedTo = val; } - + LLVector4a *LLJointRiggingInfo::getRiggedExtents() { return mRiggedExtents; @@ -120,8 +120,8 @@ void LLJointRiggingInfoTab::clear() void showDetails(const LLJointRiggingInfoTab& src, const std::string& str) { - S32 count_rigged = 0; - S32 count_box = 0; + S32 count_rigged = 0; + S32 count_box = 0; LLVector4a zero_vec; zero_vec.clear(); for (S32 i=0; i<src.size(); i++) diff --git a/indra/llmath/llrigginginfo.h b/indra/llmath/llrigginginfo.h index 059c6ae082..fb550d013f 100644 --- a/indra/llmath/llrigginginfo.h +++ b/indra/llmath/llrigginginfo.h @@ -27,8 +27,8 @@ // Stores information related to associated rigged mesh vertices // This lives in llmath because llvolume lives in llmath. -#ifndef LL_LLRIGGINGINFO_H -#define LL_LLRIGGINGINFO_H +#ifndef LL_LLRIGGINGINFO_H +#define LL_LLRIGGINGINFO_H #include "llvector4a.h" @@ -46,7 +46,7 @@ public: void merge(const LLJointRiggingInfo& other); private: - LLVector4a mRiggedExtents[2]; + LLVector4a mRiggedExtents[2]; bool mIsRiggedTo; }; diff --git a/indra/llmath/llsdutil_math.cpp b/indra/llmath/llsdutil_math.cpp index 51e5e3764f..0ea1a9c77a 100644 --- a/indra/llmath/llsdutil_math.cpp +++ b/indra/llmath/llsdutil_math.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llsdutil_math.cpp * @author Phoenix * @date 2006-05-24 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2006&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$ */ @@ -38,12 +38,12 @@ #include "v4color.h" #if LL_WINDOWS -# define WIN32_LEAN_AND_MEAN -# include <winsock2.h> // for htonl +# define WIN32_LEAN_AND_MEAN +# include <winsock2.h> // for htonl #elif LL_LINUX -# include <netinet/in.h> +# include <netinet/in.h> #elif LL_DARWIN -# include <arpa/inet.h> +# include <arpa/inet.h> #endif #include "llsdserialize.h" @@ -51,114 +51,114 @@ // vector3 LLSD ll_sd_from_vector3(const LLVector3& vec) { - LLSD rv; - rv.append((F64)vec.mV[VX]); - rv.append((F64)vec.mV[VY]); - rv.append((F64)vec.mV[VZ]); - return rv; + LLSD rv; + rv.append((F64)vec.mV[VX]); + rv.append((F64)vec.mV[VY]); + rv.append((F64)vec.mV[VZ]); + return rv; } LLVector3 ll_vector3_from_sd(const LLSD& sd, S32 start_index) { - LLVector3 rv; - rv.mV[VX] = (F32)sd[start_index].asReal(); - rv.mV[VY] = (F32)sd[++start_index].asReal(); - rv.mV[VZ] = (F32)sd[++start_index].asReal(); - return rv; + LLVector3 rv; + rv.mV[VX] = (F32)sd[start_index].asReal(); + rv.mV[VY] = (F32)sd[++start_index].asReal(); + rv.mV[VZ] = (F32)sd[++start_index].asReal(); + return rv; } // vector4 LLSD ll_sd_from_vector4(const LLVector4& vec) { - LLSD rv; - rv.append((F64)vec.mV[VX]); - rv.append((F64)vec.mV[VY]); - rv.append((F64)vec.mV[VZ]); - rv.append((F64)vec.mV[VW]); - return rv; + LLSD rv; + rv.append((F64)vec.mV[VX]); + rv.append((F64)vec.mV[VY]); + rv.append((F64)vec.mV[VZ]); + rv.append((F64)vec.mV[VW]); + return rv; } LLVector4 ll_vector4_from_sd(const LLSD& sd, S32 start_index) { - LLVector4 rv; - rv.mV[VX] = (F32)sd[start_index].asReal(); - rv.mV[VY] = (F32)sd[++start_index].asReal(); - rv.mV[VZ] = (F32)sd[++start_index].asReal(); - rv.mV[VW] = (F32)sd[++start_index].asReal(); - return rv; + LLVector4 rv; + rv.mV[VX] = (F32)sd[start_index].asReal(); + rv.mV[VY] = (F32)sd[++start_index].asReal(); + rv.mV[VZ] = (F32)sd[++start_index].asReal(); + rv.mV[VW] = (F32)sd[++start_index].asReal(); + return rv; } // vector3d LLSD ll_sd_from_vector3d(const LLVector3d& vec) { - LLSD rv; - rv.append(vec.mdV[VX]); - rv.append(vec.mdV[VY]); - rv.append(vec.mdV[VZ]); - return rv; + LLSD rv; + rv.append(vec.mdV[VX]); + rv.append(vec.mdV[VY]); + rv.append(vec.mdV[VZ]); + return rv; } LLVector3d ll_vector3d_from_sd(const LLSD& sd, S32 start_index) { - LLVector3d rv; - rv.mdV[VX] = sd[start_index].asReal(); - rv.mdV[VY] = sd[++start_index].asReal(); - rv.mdV[VZ] = sd[++start_index].asReal(); - return rv; + LLVector3d rv; + rv.mdV[VX] = sd[start_index].asReal(); + rv.mdV[VY] = sd[++start_index].asReal(); + rv.mdV[VZ] = sd[++start_index].asReal(); + return rv; } //vector2 LLSD ll_sd_from_vector2(const LLVector2& vec) { - LLSD rv; - rv.append((F64)vec.mV[VX]); - rv.append((F64)vec.mV[VY]); - return rv; + LLSD rv; + rv.append((F64)vec.mV[VX]); + rv.append((F64)vec.mV[VY]); + return rv; } LLVector2 ll_vector2_from_sd(const LLSD& sd) { - LLVector2 rv; - rv.mV[VX] = (F32)sd[0].asReal(); - rv.mV[VY] = (F32)sd[1].asReal(); - return rv; + LLVector2 rv; + rv.mV[VX] = (F32)sd[0].asReal(); + rv.mV[VY] = (F32)sd[1].asReal(); + return rv; } // Quaternion LLSD ll_sd_from_quaternion(const LLQuaternion& quat) { - LLSD rv; - rv.append((F64)quat.mQ[VX]); - rv.append((F64)quat.mQ[VY]); - rv.append((F64)quat.mQ[VZ]); - rv.append((F64)quat.mQ[VW]); - return rv; + LLSD rv; + rv.append((F64)quat.mQ[VX]); + rv.append((F64)quat.mQ[VY]); + rv.append((F64)quat.mQ[VZ]); + rv.append((F64)quat.mQ[VW]); + return rv; } LLQuaternion ll_quaternion_from_sd(const LLSD& sd) { - LLQuaternion quat; - quat.mQ[VX] = (F32)sd[0].asReal(); - quat.mQ[VY] = (F32)sd[1].asReal(); - quat.mQ[VZ] = (F32)sd[2].asReal(); - quat.mQ[VW] = (F32)sd[3].asReal(); - return quat; + LLQuaternion quat; + quat.mQ[VX] = (F32)sd[0].asReal(); + quat.mQ[VY] = (F32)sd[1].asReal(); + quat.mQ[VZ] = (F32)sd[2].asReal(); + quat.mQ[VW] = (F32)sd[3].asReal(); + return quat; } // color4 LLSD ll_sd_from_color4(const LLColor4& c) { - LLSD rv; - rv.append(c.mV[0]); - rv.append(c.mV[1]); - rv.append(c.mV[2]); - rv.append(c.mV[3]); - return rv; + LLSD rv; + rv.append(c.mV[0]); + rv.append(c.mV[1]); + rv.append(c.mV[2]); + rv.append(c.mV[3]); + return rv; } LLColor4 ll_color4_from_sd(const LLSD& sd) { - LLColor4 c; - c.setValue(sd); - return c; + LLColor4 c; + c.setValue(sd); + return c; } diff --git a/indra/llmath/llsdutil_math.h b/indra/llmath/llsdutil_math.h index 0ea78cd231..8f8852f7c3 100644 --- a/indra/llmath/llsdutil_math.h +++ b/indra/llmath/llsdutil_math.h @@ -1,4 +1,4 @@ -/** +/** * @file llsdutil_math.h * @author Brad * @date 2009-05-19 @@ -7,21 +7,21 @@ * $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$ */ diff --git a/indra/llmath/llsimdmath.h b/indra/llmath/llsimdmath.h index 54a275633f..40953dc2e8 100644 --- a/indra/llmath/llsimdmath.h +++ b/indra/llmath/llsimdmath.h @@ -1,31 +1,31 @@ -/** +/** * @file llsimdmath.h * @brief Common header for SIMD-based math library (llvector4a, llmatrix3a, etc.) * * $LicenseInfo:firstyear=2010&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_SIMD_MATH_H -#define LL_SIMD_MATH_H +#ifndef LL_SIMD_MATH_H +#define LL_SIMD_MATH_H #ifndef LLMATH_H #error "Please include llmath.h before this file." diff --git a/indra/llmath/llsimdtypes.h b/indra/llmath/llsimdtypes.h index bd991d0e71..9db152adf8 100644 --- a/indra/llmath/llsimdtypes.h +++ b/indra/llmath/llsimdtypes.h @@ -1,25 +1,25 @@ -/** +/** * @file llsimdtypes.h * @brief Declaration of basic SIMD math related types * * $LicenseInfo:firstyear=2010&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$ */ @@ -31,7 +31,7 @@ #error "Please include llmath.h before this file." #endif -typedef __m128 LLQuad; +typedef __m128 LLQuad; #if LL_WINDOWS @@ -50,17 +50,17 @@ __forceinline const __m128i _mm_castps_si128( const __m128 a ) { return reinterp class LLBool32 { public: - inline LLBool32() {} - inline LLBool32(int rhs) : m_bool(rhs) {} - inline LLBool32(unsigned int rhs) : m_bool(rhs) {} - inline LLBool32(bool rhs) { m_bool = static_cast<const int>(rhs); } - inline LLBool32& operator= (bool rhs) { m_bool = (int)rhs; return *this; } - inline bool operator== (bool rhs) const { return static_cast<const bool&>(m_bool) == rhs; } - inline bool operator!= (bool rhs) const { return !operator==(rhs); } - inline operator bool() const { return static_cast<const bool&>(m_bool); } + inline LLBool32() {} + inline LLBool32(int rhs) : m_bool(rhs) {} + inline LLBool32(unsigned int rhs) : m_bool(rhs) {} + inline LLBool32(bool rhs) { m_bool = static_cast<const int>(rhs); } + inline LLBool32& operator= (bool rhs) { m_bool = (int)rhs; return *this; } + inline bool operator== (bool rhs) const { return static_cast<const bool&>(m_bool) == rhs; } + inline bool operator!= (bool rhs) const { return !operator==(rhs); } + inline operator bool() const { return static_cast<const bool&>(m_bool); } private: - int m_bool; + int m_bool; }; #if LL_WINDOWS @@ -70,55 +70,55 @@ private: class LLSimdScalar { public: - inline LLSimdScalar() {} - inline LLSimdScalar(LLQuad q) - { - mQ = q; - } + inline LLSimdScalar() {} + inline LLSimdScalar(LLQuad q) + { + mQ = q; + } + + inline LLSimdScalar(F32 f) + { + mQ = _mm_set_ss(f); + } + + static inline const LLSimdScalar& getZero() + { + extern const LLQuad F_ZERO_4A; + return reinterpret_cast<const LLSimdScalar&>(F_ZERO_4A); + } - inline LLSimdScalar(F32 f) - { - mQ = _mm_set_ss(f); - } + inline F32 getF32() const; - static inline const LLSimdScalar& getZero() - { - extern const LLQuad F_ZERO_4A; - return reinterpret_cast<const LLSimdScalar&>(F_ZERO_4A); - } + inline LLBool32 isApproximatelyEqual(const LLSimdScalar& rhs, F32 tolerance = F_APPROXIMATELY_ZERO) const; - inline F32 getF32() const; + inline LLSimdScalar getAbs() const; - inline LLBool32 isApproximatelyEqual(const LLSimdScalar& rhs, F32 tolerance = F_APPROXIMATELY_ZERO) const; + inline void setMax( const LLSimdScalar& a, const LLSimdScalar& b ); - inline LLSimdScalar getAbs() const; + inline void setMin( const LLSimdScalar& a, const LLSimdScalar& b ); - inline void setMax( const LLSimdScalar& a, const LLSimdScalar& b ); - - inline void setMin( const LLSimdScalar& a, const LLSimdScalar& b ); + inline LLSimdScalar& operator=(F32 rhs); - inline LLSimdScalar& operator=(F32 rhs); + inline LLSimdScalar& operator+=(const LLSimdScalar& rhs); - inline LLSimdScalar& operator+=(const LLSimdScalar& rhs); + inline LLSimdScalar& operator-=(const LLSimdScalar& rhs); - inline LLSimdScalar& operator-=(const LLSimdScalar& rhs); + inline LLSimdScalar& operator*=(const LLSimdScalar& rhs); - inline LLSimdScalar& operator*=(const LLSimdScalar& rhs); + inline LLSimdScalar& operator/=(const LLSimdScalar& rhs); - inline LLSimdScalar& operator/=(const LLSimdScalar& rhs); + inline operator LLQuad() const + { + return mQ; + } - inline operator LLQuad() const - { - return mQ; - } - - inline const LLQuad& getQuad() const - { - return mQ; - } + inline const LLQuad& getQuad() const + { + return mQ; + } private: - LLQuad mQ; + LLQuad mQ; }; #endif //LL_SIMD_TYPES_H diff --git a/indra/llmath/llsimdtypes.inl b/indra/llmath/llsimdtypes.inl index e905c84954..125f4b9df5 100644 --- a/indra/llmath/llsimdtypes.inl +++ b/indra/llmath/llsimdtypes.inl @@ -1,25 +1,25 @@ -/** +/** * @file llsimdtypes.inl * @brief Inlined definitions of basic SIMD math related types * * $LicenseInfo:firstyear=2010&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$ */ @@ -33,127 +33,127 @@ inline LLSimdScalar operator+(const LLSimdScalar& a, const LLSimdScalar& b) { - LLSimdScalar t(a); - t += b; - return t; + LLSimdScalar t(a); + t += b; + return t; } inline LLSimdScalar operator-(const LLSimdScalar& a, const LLSimdScalar& b) { - LLSimdScalar t(a); - t -= b; - return t; + LLSimdScalar t(a); + t -= b; + return t; } inline LLSimdScalar operator*(const LLSimdScalar& a, const LLSimdScalar& b) { - LLSimdScalar t(a); - t *= b; - return t; + LLSimdScalar t(a); + t *= b; + return t; } inline LLSimdScalar operator/(const LLSimdScalar& a, const LLSimdScalar& b) { - LLSimdScalar t(a); - t /= b; - return t; + LLSimdScalar t(a); + t /= b; + return t; } inline LLSimdScalar operator-(const LLSimdScalar& a) { - static LL_ALIGN_16(const U32 signMask[4]) = {0x80000000, 0x80000000, 0x80000000, 0x80000000 }; - ll_assert_aligned(signMask,16); - return _mm_xor_ps(*reinterpret_cast<const LLQuad*>(signMask), a); + static LL_ALIGN_16(const U32 signMask[4]) = {0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + ll_assert_aligned(signMask,16); + return _mm_xor_ps(*reinterpret_cast<const LLQuad*>(signMask), a); } inline LLBool32 operator==(const LLSimdScalar& a, const LLSimdScalar& b) { - return _mm_comieq_ss(a, b); + return _mm_comieq_ss(a, b); } inline LLBool32 operator!=(const LLSimdScalar& a, const LLSimdScalar& b) { - return _mm_comineq_ss(a, b); + return _mm_comineq_ss(a, b); } inline LLBool32 operator<(const LLSimdScalar& a, const LLSimdScalar& b) { - return _mm_comilt_ss(a, b); + return _mm_comilt_ss(a, b); } inline LLBool32 operator<=(const LLSimdScalar& a, const LLSimdScalar& b) { - return _mm_comile_ss(a, b); + return _mm_comile_ss(a, b); } inline LLBool32 operator>(const LLSimdScalar& a, const LLSimdScalar& b) { - return _mm_comigt_ss(a, b); + return _mm_comigt_ss(a, b); } inline LLBool32 operator>=(const LLSimdScalar& a, const LLSimdScalar& b) { - return _mm_comige_ss(a, b); + return _mm_comige_ss(a, b); } inline LLBool32 LLSimdScalar::isApproximatelyEqual(const LLSimdScalar& rhs, F32 tolerance /* = F_APPROXIMATELY_ZERO */) const { - const LLSimdScalar tol( tolerance ); - const LLSimdScalar diff = _mm_sub_ss( mQ, rhs.mQ ); - const LLSimdScalar absDiff = diff.getAbs(); - return absDiff <= tol; + const LLSimdScalar tol( tolerance ); + const LLSimdScalar diff = _mm_sub_ss( mQ, rhs.mQ ); + const LLSimdScalar absDiff = diff.getAbs(); + return absDiff <= tol; } inline void LLSimdScalar::setMax( const LLSimdScalar& a, const LLSimdScalar& b ) { - mQ = _mm_max_ss( a, b ); + mQ = _mm_max_ss( a, b ); } inline void LLSimdScalar::setMin( const LLSimdScalar& a, const LLSimdScalar& b ) { - mQ = _mm_min_ss( a, b ); + mQ = _mm_min_ss( a, b ); } -inline LLSimdScalar& LLSimdScalar::operator=(F32 rhs) -{ - mQ = _mm_set_ss(rhs); - return *this; +inline LLSimdScalar& LLSimdScalar::operator=(F32 rhs) +{ + mQ = _mm_set_ss(rhs); + return *this; } -inline LLSimdScalar& LLSimdScalar::operator+=(const LLSimdScalar& rhs) +inline LLSimdScalar& LLSimdScalar::operator+=(const LLSimdScalar& rhs) { - mQ = _mm_add_ss( mQ, rhs ); - return *this; + mQ = _mm_add_ss( mQ, rhs ); + return *this; } inline LLSimdScalar& LLSimdScalar::operator-=(const LLSimdScalar& rhs) { - mQ = _mm_sub_ss( mQ, rhs ); - return *this; + mQ = _mm_sub_ss( mQ, rhs ); + return *this; } inline LLSimdScalar& LLSimdScalar::operator*=(const LLSimdScalar& rhs) { - mQ = _mm_mul_ss( mQ, rhs ); - return *this; + mQ = _mm_mul_ss( mQ, rhs ); + return *this; } inline LLSimdScalar& LLSimdScalar::operator/=(const LLSimdScalar& rhs) { - mQ = _mm_div_ss( mQ, rhs ); - return *this; + mQ = _mm_div_ss( mQ, rhs ); + return *this; } inline LLSimdScalar LLSimdScalar::getAbs() const { - static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }; - ll_assert_aligned(F_ABS_MASK_4A,16); - return _mm_and_ps( mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A)); + static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }; + ll_assert_aligned(F_ABS_MASK_4A,16); + return _mm_and_ps( mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A)); } inline F32 LLSimdScalar::getF32() const -{ - F32 ret; - _mm_store_ss(&ret, mQ); - return ret; +{ + F32 ret; + _mm_store_ss(&ret, mQ); + return ret; } diff --git a/indra/llmath/llsphere.cpp b/indra/llmath/llsphere.cpp index 3f04ca704c..b68743ea6e 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 ba8e437ecf..413a307543 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 ce104b88a0..5875e9e8e0 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/llvector4a.cpp b/indra/llmath/llvector4a.cpp index 570fa41a43..0ac91366b6 100644 --- a/indra/llmath/llvector4a.cpp +++ b/indra/llmath/llvector4a.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llvector4a.cpp * @brief SIMD vector implementation * * $LicenseInfo:firstyear=2010&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$ */ @@ -28,12 +28,12 @@ #include "llmath.h" #include "llquantize.h" -extern const LLQuad F_ZERO_4A = { 0, 0, 0, 0 }; -extern const LLQuad F_APPROXIMATELY_ZERO_4A = { - F_APPROXIMATELY_ZERO, - F_APPROXIMATELY_ZERO, - F_APPROXIMATELY_ZERO, - F_APPROXIMATELY_ZERO +extern const LLQuad F_ZERO_4A = { 0, 0, 0, 0 }; +extern const LLQuad F_APPROXIMATELY_ZERO_4A = { + F_APPROXIMATELY_ZERO, + F_APPROXIMATELY_ZERO, + F_APPROXIMATELY_ZERO, + F_APPROXIMATELY_ZERO }; extern const LLVector4a LL_V4A_ZERO = reinterpret_cast<const LLVector4a&> ( F_ZERO_4A ); @@ -46,135 +46,135 @@ extern const LLVector4a LL_V4A_EPSILON = reinterpret_cast<const LLVector4a&> ( F void LLVector4a::setRotated( const LLRotation& rot, const LLVector4a& vec ) { - const LLVector4a col0 = rot.getColumn(0); - const LLVector4a col1 = rot.getColumn(1); - const LLVector4a col2 = rot.getColumn(2); - - LLVector4a result = _mm_load_ss( vec.getF32ptr() ); - result.splat<0>( result ); - result.mul( col0 ); - - { - LLVector4a yyyy = _mm_load_ss( vec.getF32ptr() + 1 ); - yyyy.splat<0>( yyyy ); - yyyy.mul( col1 ); - result.add( yyyy ); - } - - { - LLVector4a zzzz = _mm_load_ss( vec.getF32ptr() + 2 ); - zzzz.splat<0>( zzzz ); - zzzz.mul( col2 ); - result.add( zzzz ); - } - - *this = result; + const LLVector4a col0 = rot.getColumn(0); + const LLVector4a col1 = rot.getColumn(1); + const LLVector4a col2 = rot.getColumn(2); + + LLVector4a result = _mm_load_ss( vec.getF32ptr() ); + result.splat<0>( result ); + result.mul( col0 ); + + { + LLVector4a yyyy = _mm_load_ss( vec.getF32ptr() + 1 ); + yyyy.splat<0>( yyyy ); + yyyy.mul( col1 ); + result.add( yyyy ); + } + + { + LLVector4a zzzz = _mm_load_ss( vec.getF32ptr() + 2 ); + zzzz.splat<0>( zzzz ); + zzzz.mul( col2 ); + result.add( zzzz ); + } + + *this = result; } void LLVector4a::setRotated( const LLQuaternion2& quat, const LLVector4a& vec ) { - const LLVector4a& quatVec = quat.getVector4a(); - LLVector4a temp; temp.setCross3(quatVec, vec); - temp.add( temp ); - - const LLVector4a realPart( quatVec.getScalarAt<3>() ); - LLVector4a tempTimesReal; tempTimesReal.setMul( temp, realPart ); - - mQ = vec; - add( tempTimesReal ); - - LLVector4a imagCrossTemp; imagCrossTemp.setCross3( quatVec, temp ); - add(imagCrossTemp); + const LLVector4a& quatVec = quat.getVector4a(); + LLVector4a temp; temp.setCross3(quatVec, vec); + temp.add( temp ); + + const LLVector4a realPart( quatVec.getScalarAt<3>() ); + LLVector4a tempTimesReal; tempTimesReal.setMul( temp, realPart ); + + mQ = vec; + add( tempTimesReal ); + + LLVector4a imagCrossTemp; imagCrossTemp.setCross3( quatVec, temp ); + add(imagCrossTemp); } void LLVector4a::quantize8( const LLVector4a& low, const LLVector4a& high ) { - LLVector4a val(mQ); - LLVector4a delta; delta.setSub( high, low ); - - { - val.clamp(low, high); - val.sub(low); - - // 8-bit quantization means we can do with just 12 bits of reciprocal accuracy - const LLVector4a oneOverDelta = _mm_rcp_ps(delta.mQ); -// { -// static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f }; -// LLVector4a two; two.load4a( F_TWO_4A ); -// -// // Here we use _mm_rcp_ps plus one round of newton-raphson -// // We wish to find 'x' such that x = 1/delta -// // As a first approximation, we take x0 = _mm_rcp_ps(delta) -// // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 ) -// // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf -// const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ); -// oneOverDelta.setMul( delta, recipApprox ); -// oneOverDelta.setSub( two, oneOverDelta ); -// oneOverDelta.mul( recipApprox ); -// } - - val.mul(oneOverDelta); - val.mul(*reinterpret_cast<const LLVector4a*>(F_U8MAX_4A)); - } - - val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ )); - - { - val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A)); - val.mul(delta); - val.add(low); - } - - { - LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A)); - LLVector4a absVal; absVal.setAbs( val ); - setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val ); - } + LLVector4a val(mQ); + LLVector4a delta; delta.setSub( high, low ); + + { + val.clamp(low, high); + val.sub(low); + + // 8-bit quantization means we can do with just 12 bits of reciprocal accuracy + const LLVector4a oneOverDelta = _mm_rcp_ps(delta.mQ); +// { +// static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f }; +// LLVector4a two; two.load4a( F_TWO_4A ); +// +// // Here we use _mm_rcp_ps plus one round of newton-raphson +// // We wish to find 'x' such that x = 1/delta +// // As a first approximation, we take x0 = _mm_rcp_ps(delta) +// // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 ) +// // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf +// const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ); +// oneOverDelta.setMul( delta, recipApprox ); +// oneOverDelta.setSub( two, oneOverDelta ); +// oneOverDelta.mul( recipApprox ); +// } + + val.mul(oneOverDelta); + val.mul(*reinterpret_cast<const LLVector4a*>(F_U8MAX_4A)); + } + + val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ )); + + { + val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A)); + val.mul(delta); + val.add(low); + } + + { + LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A)); + LLVector4a absVal; absVal.setAbs( val ); + setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val ); + } } void LLVector4a::quantize16( const LLVector4a& low, const LLVector4a& high ) { - LLVector4a val(mQ); - LLVector4a delta; delta.setSub( high, low ); - - { - val.clamp(low, high); - val.sub(low); - - // 16-bit quantization means we need a round of Newton-Raphson - LLVector4a oneOverDelta; - { - static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f }; - ll_assert_aligned(F_TWO_4A,16); - - LLVector4a two; two.load4a( F_TWO_4A ); - - // Here we use _mm_rcp_ps plus one round of newton-raphson - // We wish to find 'x' such that x = 1/delta - // As a first approximation, we take x0 = _mm_rcp_ps(delta) - // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 ) - // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf - const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ); - oneOverDelta.setMul( delta, recipApprox ); - oneOverDelta.setSub( two, oneOverDelta ); - oneOverDelta.mul( recipApprox ); - } - - val.mul(oneOverDelta); - val.mul(*reinterpret_cast<const LLVector4a*>(F_U16MAX_4A)); - } - - val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ )); - - { - val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A)); - val.mul(delta); - val.add(low); - } - - { - LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A)); - LLVector4a absVal; absVal.setAbs( val ); - setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val ); - } + LLVector4a val(mQ); + LLVector4a delta; delta.setSub( high, low ); + + { + val.clamp(low, high); + val.sub(low); + + // 16-bit quantization means we need a round of Newton-Raphson + LLVector4a oneOverDelta; + { + static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f }; + ll_assert_aligned(F_TWO_4A,16); + + LLVector4a two; two.load4a( F_TWO_4A ); + + // Here we use _mm_rcp_ps plus one round of newton-raphson + // We wish to find 'x' such that x = 1/delta + // As a first approximation, we take x0 = _mm_rcp_ps(delta) + // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 ) + // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf + const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ); + oneOverDelta.setMul( delta, recipApprox ); + oneOverDelta.setSub( two, oneOverDelta ); + oneOverDelta.mul( recipApprox ); + } + + val.mul(oneOverDelta); + val.mul(*reinterpret_cast<const LLVector4a*>(F_U16MAX_4A)); + } + + val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ )); + + { + val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A)); + val.mul(delta); + val.add(low); + } + + { + LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A)); + LLVector4a absVal; absVal.setAbs( val ); + setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val ); + } } diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h index 53c8f604f6..8f0ee4b739 100644 --- a/indra/llmath/llvector4a.h +++ b/indra/llmath/llvector4a.h @@ -1,31 +1,31 @@ -/** +/** * @file llvector4a.h * @brief LLVector4a class header file - memory aligned and vectorized 4 component vector * * $LicenseInfo:firstyear=2010&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_LLVECTOR4A_H -#define LL_LLVECTOR4A_H +#ifndef LL_LLVECTOR4A_H +#define LL_LLVECTOR4A_H class LLRotation; @@ -40,11 +40,11 @@ class LLRotation; // This is just the beginning of LLVector4a. There are many more useful functions // yet to be implemented. For example, setNeg to negate a vector, rotate() to apply // a matrix rotation, various functions to manipulate only the X, Y, and Z elements -// and many others (including a whole variety of accessors). So if you don't see a -// function here that you need, please contact Falcon or someone else with SSE -// experience (Richard, I think, has some and davep has a little as of the time +// and many others (including a whole variety of accessors). So if you don't see a +// function here that you need, please contact Falcon or someone else with SSE +// experience (Richard, I think, has some and davep has a little as of the time // of this writing, July 08, 2010) about getting it implemented before you resort to -// LLVector3/LLVector4. +// LLVector3/LLVector4. ///////////////////////////////// class alignas(16) LLVector4a @@ -52,283 +52,283 @@ class alignas(16) LLVector4a LL_ALIGN_NEW public: - /////////////////////////////////// - // STATIC METHODS - /////////////////////////////////// - - // Call initClass() at startup to avoid 15,000+ cycle penalties from denormalized numbers - static void initClass() - { - _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); - _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); - } - - // Return a vector of all zeros - static inline const LLVector4a& getZero() - { - extern const LLVector4a LL_V4A_ZERO; - return LL_V4A_ZERO; - } - - // Return a vector of all epsilon, where epsilon is a small float suitable for approximate equality checks - static inline const LLVector4a& getEpsilon() - { - extern const LLVector4a LL_V4A_EPSILON; - return LL_V4A_EPSILON; - } - - // Copy 16 bytes from src to dst. Source and destination must be 16-byte aligned - static inline void copy4a(F32* dst, const F32* src) - { - _mm_store_ps(dst, _mm_load_ps(src)); - } - - // Copy words 16-byte blocks from src to dst. Source and destination must not overlap. - // Source and dest must be 16-byte aligned and size must be multiple of 16. - static void memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes); - - //////////////////////////////////// - // CONSTRUCTORS - //////////////////////////////////// - - //LLVector4a is plain data which should never have a default constructor or destructor(malloc&free won't trigger it) - LLVector4a() - { //DO NOT INITIALIZE -- The overhead is completely unnecessary - ll_assert_aligned(this,16); - } - - LLVector4a(F32 x, F32 y, F32 z, F32 w = 0.f) - { - set(x,y,z,w); - } - - LLVector4a(F32 x) - { - splat(x); - } - - LLVector4a(const LLSimdScalar& x) - { - splat(x); - } - - LLVector4a(LLQuad q) - { - mQ = q; - } - - //////////////////////////////////// - // LOAD/STORE - //////////////////////////////////// - - // Load from 16-byte aligned src array (preferred method of loading) - inline void load4a(const F32* src); - - // Load from unaligned src array (NB: Significantly slower than load4a) - inline void loadua(const F32* src); - - // Load only three floats beginning at address 'src'. Slowest method. - inline void load3(const F32* src); - - // Store to a 16-byte aligned memory address - inline void store4a(F32* dst) const; - - //////////////////////////////////// - // BASIC GET/SET - //////////////////////////////////// - - // Return a "this" as an F32 pointer. - inline F32* getF32ptr(); - - // Return a "this" as a const F32 pointer. - inline const F32* const getF32ptr() const; - - // Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates - // the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead - inline F32 operator[](const S32 idx) const; - - // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. - inline LLSimdScalar getScalarAt(const S32 idx) const; - - // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. - template <int N> LL_FORCE_INLINE LLSimdScalar getScalarAt() const; - - // Set to an x, y, z and optional w provided - inline void set(F32 x, F32 y, F32 z, F32 w = 0.f); - - // Set to all zeros. This is preferred to using ::getZero() - inline void clear(); - - // Set all elements to 'x' - inline void splat(const F32 x); - - // Set all elements to 'x' - inline void splat(const LLSimdScalar& x); - - // Set all 4 elements to element N of src, with N known at compile time - template <int N> void splat(const LLVector4a& src); - - // Set all 4 elements to element i of v, with i NOT known at compile time - inline void splat(const LLVector4a& v, U32 i); - - // Select bits from sourceIfTrue and sourceIfFalse according to bits in mask - inline void setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse ); - - //////////////////////////////////// - // ALGEBRAIC - //////////////////////////////////// - - // Set this to the element-wise (a + b) - inline void setAdd(const LLVector4a& a, const LLVector4a& b); - - // Set this to element-wise (a - b) - inline void setSub(const LLVector4a& a, const LLVector4a& b); - - // Set this to element-wise multiply (a * b) - inline void setMul(const LLVector4a& a, const LLVector4a& b); - - // Set this to element-wise quotient (a / b) - inline void setDiv(const LLVector4a& a, const LLVector4a& b); - - // Set this to the element-wise absolute value of src - inline void setAbs(const LLVector4a& src); - - // Add to each component in this vector the corresponding component in rhs - inline void add(const LLVector4a& rhs); - - // Subtract from each component in this vector the corresponding component in rhs - inline void sub(const LLVector4a& rhs); - - // Multiply each component in this vector by the corresponding component in rhs - inline void mul(const LLVector4a& rhs); - - // Divide each component in this vector by the corresponding component in rhs - inline void div(const LLVector4a& rhs); - - // Multiply this vector by x in a scalar fashion - inline void mul(const F32 x); - - // Set this to (a x b) (geometric cross-product) - inline void setCross3(const LLVector4a& a, const LLVector4a& b); - - // Set all elements to the dot product of the x, y, and z elements in a and b - inline void setAllDot3(const LLVector4a& a, const LLVector4a& b); - - // Set all elements to the dot product of the x, y, z, and w elements in a and b - inline void setAllDot4(const LLVector4a& a, const LLVector4a& b); - - // Return the 3D dot product of this vector and b - inline LLSimdScalar dot3(const LLVector4a& b) const; - - // Return the 4D dot product of this vector and b - inline LLSimdScalar dot4(const LLVector4a& b) const; - - // Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed - // Note that this does not consider zero length vectors! - inline void normalize3(); - - // Same as normalize3() but with respect to all 4 components - inline void normalize4(); - - // Same as normalize3(), but returns length as a SIMD scalar - inline LLSimdScalar normalize3withLength(); - - // Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed - // Note that this does not consider zero length vectors! - inline void normalize3fast(); - - // Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed - // Same as above except substitutes default vector contents if the vector is non-finite or degenerate due to zero length. - // - inline void normalize3fast_checked(LLVector4a* d = 0); - - // Return true if this vector is normalized with respect to x,y,z up to tolerance - inline LLBool32 isNormalized3( F32 tolerance = 1e-3 ) const; - - // Return true if this vector is normalized with respect to all components up to tolerance - inline LLBool32 isNormalized4( F32 tolerance = 1e-3 ) const; - - // Set all elements to the length of vector 'v' - inline void setAllLength3( const LLVector4a& v ); - - // Get this vector's length - inline LLSimdScalar getLength3() const; - - // Set the components of this vector to the minimum of the corresponding components of lhs and rhs - inline void setMin(const LLVector4a& lhs, const LLVector4a& rhs); - - // Set the components of this vector to the maximum of the corresponding components of lhs and rhs - inline void setMax(const LLVector4a& lhs, const LLVector4a& rhs); - - // Clamps this vector to be within the component-wise range low to high (inclusive) - inline void clamp( const LLVector4a& low, const LLVector4a& high ); - - // Set this to (c * lhs) + rhs * ( 1 - c) - inline void setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c); - - // Return true (nonzero) if x, y, z (and w for Finite4) are all finite floats - inline LLBool32 isFinite3() const; - inline LLBool32 isFinite4() const; - - // Set this vector to 'vec' rotated by the LLRotation or LLQuaternion2 provided - void setRotated( const LLRotation& rot, const LLVector4a& vec ); - void setRotated( const class LLQuaternion2& quat, const LLVector4a& vec ); - - // Set this vector to 'vec' rotated by the INVERSE of the LLRotation or LLQuaternion2 provided - inline void setRotatedInv( const LLRotation& rot, const LLVector4a& vec ); - inline void setRotatedInv( const class LLQuaternion2& quat, const LLVector4a& vec ); - - // Quantize this vector to 8 or 16 bit precision - void quantize8( const LLVector4a& low, const LLVector4a& high ); - void quantize16( const LLVector4a& low, const LLVector4a& high ); - - //////////////////////////////////// - // LOGICAL - //////////////////////////////////// - // The functions in this section will compare the elements in this vector - // to those in rhs and return an LLVector4Logical with all bits set in elements - // where the comparison was true and all bits unset in elements where the comparison - // was false. See llvector4logica.h - //////////////////////////////////// - // WARNING: Other than equals3 and equals4, these functions do NOT account - // for floating point tolerance. You should include the appropriate tolerance - // in the inputs. - //////////////////////////////////// - - inline LLVector4Logical greaterThan(const LLVector4a& rhs) const; - - inline LLVector4Logical lessThan(const LLVector4a& rhs) const; - - inline LLVector4Logical greaterEqual(const LLVector4a& rhs) const; - - inline LLVector4Logical lessEqual(const LLVector4a& rhs) const; - - inline LLVector4Logical equal(const LLVector4a& rhs) const; - - // Returns true if this and rhs are componentwise equal up to the specified absolute tolerance - inline bool equals4(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; - - inline bool equals3(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; - - //////////////////////////////////// - // OPERATORS - //////////////////////////////////// - - // Do NOT add aditional operators without consulting someone with SSE experience - inline const LLVector4a& operator= ( const LLVector4a& rhs ); - - inline const LLVector4a& operator= ( const LLQuad& rhs ); - - inline operator LLQuad() const; - + /////////////////////////////////// + // STATIC METHODS + /////////////////////////////////// + + // Call initClass() at startup to avoid 15,000+ cycle penalties from denormalized numbers + static void initClass() + { + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); + } + + // Return a vector of all zeros + static inline const LLVector4a& getZero() + { + extern const LLVector4a LL_V4A_ZERO; + return LL_V4A_ZERO; + } + + // Return a vector of all epsilon, where epsilon is a small float suitable for approximate equality checks + static inline const LLVector4a& getEpsilon() + { + extern const LLVector4a LL_V4A_EPSILON; + return LL_V4A_EPSILON; + } + + // Copy 16 bytes from src to dst. Source and destination must be 16-byte aligned + static inline void copy4a(F32* dst, const F32* src) + { + _mm_store_ps(dst, _mm_load_ps(src)); + } + + // Copy words 16-byte blocks from src to dst. Source and destination must not overlap. + // Source and dest must be 16-byte aligned and size must be multiple of 16. + static void memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes); + + //////////////////////////////////// + // CONSTRUCTORS + //////////////////////////////////// + + //LLVector4a is plain data which should never have a default constructor or destructor(malloc&free won't trigger it) + LLVector4a() + { //DO NOT INITIALIZE -- The overhead is completely unnecessary + ll_assert_aligned(this,16); + } + + LLVector4a(F32 x, F32 y, F32 z, F32 w = 0.f) + { + set(x,y,z,w); + } + + LLVector4a(F32 x) + { + splat(x); + } + + LLVector4a(const LLSimdScalar& x) + { + splat(x); + } + + LLVector4a(LLQuad q) + { + mQ = q; + } + + //////////////////////////////////// + // LOAD/STORE + //////////////////////////////////// + + // Load from 16-byte aligned src array (preferred method of loading) + inline void load4a(const F32* src); + + // Load from unaligned src array (NB: Significantly slower than load4a) + inline void loadua(const F32* src); + + // Load only three floats beginning at address 'src'. Slowest method. + inline void load3(const F32* src); + + // Store to a 16-byte aligned memory address + inline void store4a(F32* dst) const; + + //////////////////////////////////// + // BASIC GET/SET + //////////////////////////////////// + + // Return a "this" as an F32 pointer. + inline F32* getF32ptr(); + + // Return a "this" as a const F32 pointer. + inline const F32* const getF32ptr() const; + + // Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates + // the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead + inline F32 operator[](const S32 idx) const; + + // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. + inline LLSimdScalar getScalarAt(const S32 idx) const; + + // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. + template <int N> LL_FORCE_INLINE LLSimdScalar getScalarAt() const; + + // Set to an x, y, z and optional w provided + inline void set(F32 x, F32 y, F32 z, F32 w = 0.f); + + // Set to all zeros. This is preferred to using ::getZero() + inline void clear(); + + // Set all elements to 'x' + inline void splat(const F32 x); + + // Set all elements to 'x' + inline void splat(const LLSimdScalar& x); + + // Set all 4 elements to element N of src, with N known at compile time + template <int N> void splat(const LLVector4a& src); + + // Set all 4 elements to element i of v, with i NOT known at compile time + inline void splat(const LLVector4a& v, U32 i); + + // Select bits from sourceIfTrue and sourceIfFalse according to bits in mask + inline void setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse ); + + //////////////////////////////////// + // ALGEBRAIC + //////////////////////////////////// + + // Set this to the element-wise (a + b) + inline void setAdd(const LLVector4a& a, const LLVector4a& b); + + // Set this to element-wise (a - b) + inline void setSub(const LLVector4a& a, const LLVector4a& b); + + // Set this to element-wise multiply (a * b) + inline void setMul(const LLVector4a& a, const LLVector4a& b); + + // Set this to element-wise quotient (a / b) + inline void setDiv(const LLVector4a& a, const LLVector4a& b); + + // Set this to the element-wise absolute value of src + inline void setAbs(const LLVector4a& src); + + // Add to each component in this vector the corresponding component in rhs + inline void add(const LLVector4a& rhs); + + // Subtract from each component in this vector the corresponding component in rhs + inline void sub(const LLVector4a& rhs); + + // Multiply each component in this vector by the corresponding component in rhs + inline void mul(const LLVector4a& rhs); + + // Divide each component in this vector by the corresponding component in rhs + inline void div(const LLVector4a& rhs); + + // Multiply this vector by x in a scalar fashion + inline void mul(const F32 x); + + // Set this to (a x b) (geometric cross-product) + inline void setCross3(const LLVector4a& a, const LLVector4a& b); + + // Set all elements to the dot product of the x, y, and z elements in a and b + inline void setAllDot3(const LLVector4a& a, const LLVector4a& b); + + // Set all elements to the dot product of the x, y, z, and w elements in a and b + inline void setAllDot4(const LLVector4a& a, const LLVector4a& b); + + // Return the 3D dot product of this vector and b + inline LLSimdScalar dot3(const LLVector4a& b) const; + + // Return the 4D dot product of this vector and b + inline LLSimdScalar dot4(const LLVector4a& b) const; + + // Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed + // Note that this does not consider zero length vectors! + inline void normalize3(); + + // Same as normalize3() but with respect to all 4 components + inline void normalize4(); + + // Same as normalize3(), but returns length as a SIMD scalar + inline LLSimdScalar normalize3withLength(); + + // Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed + // Note that this does not consider zero length vectors! + inline void normalize3fast(); + + // Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed + // Same as above except substitutes default vector contents if the vector is non-finite or degenerate due to zero length. + // + inline void normalize3fast_checked(LLVector4a* d = 0); + + // Return true if this vector is normalized with respect to x,y,z up to tolerance + inline LLBool32 isNormalized3( F32 tolerance = 1e-3 ) const; + + // Return true if this vector is normalized with respect to all components up to tolerance + inline LLBool32 isNormalized4( F32 tolerance = 1e-3 ) const; + + // Set all elements to the length of vector 'v' + inline void setAllLength3( const LLVector4a& v ); + + // Get this vector's length + inline LLSimdScalar getLength3() const; + + // Set the components of this vector to the minimum of the corresponding components of lhs and rhs + inline void setMin(const LLVector4a& lhs, const LLVector4a& rhs); + + // Set the components of this vector to the maximum of the corresponding components of lhs and rhs + inline void setMax(const LLVector4a& lhs, const LLVector4a& rhs); + + // Clamps this vector to be within the component-wise range low to high (inclusive) + inline void clamp( const LLVector4a& low, const LLVector4a& high ); + + // Set this to (c * lhs) + rhs * ( 1 - c) + inline void setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c); + + // Return true (nonzero) if x, y, z (and w for Finite4) are all finite floats + inline LLBool32 isFinite3() const; + inline LLBool32 isFinite4() const; + + // Set this vector to 'vec' rotated by the LLRotation or LLQuaternion2 provided + void setRotated( const LLRotation& rot, const LLVector4a& vec ); + void setRotated( const class LLQuaternion2& quat, const LLVector4a& vec ); + + // Set this vector to 'vec' rotated by the INVERSE of the LLRotation or LLQuaternion2 provided + inline void setRotatedInv( const LLRotation& rot, const LLVector4a& vec ); + inline void setRotatedInv( const class LLQuaternion2& quat, const LLVector4a& vec ); + + // Quantize this vector to 8 or 16 bit precision + void quantize8( const LLVector4a& low, const LLVector4a& high ); + void quantize16( const LLVector4a& low, const LLVector4a& high ); + + //////////////////////////////////// + // LOGICAL + //////////////////////////////////// + // The functions in this section will compare the elements in this vector + // to those in rhs and return an LLVector4Logical with all bits set in elements + // where the comparison was true and all bits unset in elements where the comparison + // was false. See llvector4logica.h + //////////////////////////////////// + // WARNING: Other than equals3 and equals4, these functions do NOT account + // for floating point tolerance. You should include the appropriate tolerance + // in the inputs. + //////////////////////////////////// + + inline LLVector4Logical greaterThan(const LLVector4a& rhs) const; + + inline LLVector4Logical lessThan(const LLVector4a& rhs) const; + + inline LLVector4Logical greaterEqual(const LLVector4a& rhs) const; + + inline LLVector4Logical lessEqual(const LLVector4a& rhs) const; + + inline LLVector4Logical equal(const LLVector4a& rhs) const; + + // Returns true if this and rhs are componentwise equal up to the specified absolute tolerance + inline bool equals4(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; + + inline bool equals3(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; + + //////////////////////////////////// + // OPERATORS + //////////////////////////////////// + + // Do NOT add aditional operators without consulting someone with SSE experience + inline const LLVector4a& operator= ( const LLVector4a& rhs ); + + inline const LLVector4a& operator= ( const LLQuad& rhs ); + + inline operator LLQuad() const; + private: - LLQuad mQ; + LLQuad mQ; }; inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p) { - min.setMin(min, p); - max.setMax(max, p); + min.setMin(min, p); + max.setMax(max, p); } inline std::ostream& operator<<(std::ostream& s, const LLVector4a& v) diff --git a/indra/llmath/llvector4a.inl b/indra/llmath/llvector4a.inl index 8be1c1b114..36dbec078c 100644 --- a/indra/llmath/llvector4a.inl +++ b/indra/llmath/llvector4a.inl @@ -1,25 +1,25 @@ -/** +/** * @file llvector4a.inl * @brief LLVector4a inline function implementations * * $LicenseInfo:firstyear=2010&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$ */ @@ -31,138 +31,138 @@ // Load from 16-byte aligned src array (preferred method of loading) inline void LLVector4a::load4a(const F32* src) { - mQ = _mm_load_ps(src); + mQ = _mm_load_ps(src); } // Load from unaligned src array (NB: Significantly slower than load4a) inline void LLVector4a::loadua(const F32* src) { - mQ = _mm_loadu_ps(src); + mQ = _mm_loadu_ps(src); } // Load only three floats beginning at address 'src'. Slowest method. inline void LLVector4a::load3(const F32* src) { - // mQ = { 0.f, src[2], src[1], src[0] } = { W, Z, Y, X } - // NB: This differs from the convention of { Z, Y, X, W } - mQ = _mm_set_ps(0.f, src[2], src[1], src[0]); -} + // mQ = { 0.f, src[2], src[1], src[0] } = { W, Z, Y, X } + // NB: This differs from the convention of { Z, Y, X, W } + mQ = _mm_set_ps(0.f, src[2], src[1], src[0]); +} // Store to a 16-byte aligned memory address inline void LLVector4a::store4a(F32* dst) const { - _mm_store_ps(dst, mQ); + _mm_store_ps(dst, mQ); } //////////////////////////////////// -// BASIC GET/SET +// BASIC GET/SET //////////////////////////////////// // Return a "this" as an F32 pointer. F32* LLVector4a::getF32ptr() { - return (F32*) &mQ; + return (F32*) &mQ; } // Return a "this" as a const F32 pointer. const F32* const LLVector4a::getF32ptr() const { - return (const F32* const) &mQ; + return (const F32* const) &mQ; } // Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates // the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead inline F32 LLVector4a::operator[](const S32 idx) const { - return ((F32*)&mQ)[idx]; -} + return ((F32*)&mQ)[idx]; +} // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. inline LLSimdScalar LLVector4a::getScalarAt(const S32 idx) const { - // Return appropriate LLQuad. It will be cast to LLSimdScalar automatically (should be effectively a nop) - switch (idx) - { - case 0: - return mQ; - case 1: - return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(1, 1, 1, 1)); - case 2: - return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(2, 2, 2, 2)); - case 3: - default: - return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(3, 3, 3, 3)); - } + // Return appropriate LLQuad. It will be cast to LLSimdScalar automatically (should be effectively a nop) + switch (idx) + { + case 0: + return mQ; + case 1: + return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(1, 1, 1, 1)); + case 2: + return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(2, 2, 2, 2)); + case 3: + default: + return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(3, 3, 3, 3)); + } } // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. template <int N> LL_FORCE_INLINE LLSimdScalar LLVector4a::getScalarAt() const { - return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(N, N, N, N)); + return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(N, N, N, N)); } template<> LL_FORCE_INLINE LLSimdScalar LLVector4a::getScalarAt<0>() const { - return mQ; + return mQ; } // Set to an x, y, z and optional w provided inline void LLVector4a::set(F32 x, F32 y, F32 z, F32 w) { - mQ = _mm_set_ps(w, z, y, x); + mQ = _mm_set_ps(w, z, y, x); } // Set to all zeros inline void LLVector4a::clear() { - mQ = LLVector4a::getZero().mQ; + mQ = LLVector4a::getZero().mQ; } inline void LLVector4a::splat(const F32 x) { - mQ = _mm_set1_ps(x); + mQ = _mm_set1_ps(x); } inline void LLVector4a::splat(const LLSimdScalar& x) { - mQ = _mm_shuffle_ps( x.getQuad(), x.getQuad(), _MM_SHUFFLE(0,0,0,0) ); + mQ = _mm_shuffle_ps( x.getQuad(), x.getQuad(), _MM_SHUFFLE(0,0,0,0) ); } // Set all 4 elements to element N of src, with N known at compile time template <int N> void LLVector4a::splat(const LLVector4a& src) { - mQ = _mm_shuffle_ps(src.mQ, src.mQ, _MM_SHUFFLE(N, N, N, N) ); + mQ = _mm_shuffle_ps(src.mQ, src.mQ, _MM_SHUFFLE(N, N, N, N) ); } // Set all 4 elements to element i of v, with i NOT known at compile time inline void LLVector4a::splat(const LLVector4a& v, U32 i) { - switch (i) - { - case 0: - mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(0, 0, 0, 0)); - break; - case 1: - mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(1, 1, 1, 1)); - break; - case 2: - mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(2, 2, 2, 2)); - break; - case 3: - mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(3, 3, 3, 3)); - break; - } + switch (i) + { + case 0: + mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(0, 0, 0, 0)); + break; + case 1: + mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(1, 1, 1, 1)); + break; + case 2: + mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(2, 2, 2, 2)); + break; + case 3: + mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(3, 3, 3, 3)); + break; + } } // Select bits from sourceIfTrue and sourceIfFalse according to bits in mask inline void LLVector4a::setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse ) { - // ((( sourceIfTrue ^ sourceIfFalse ) & mask) ^ sourceIfFalse ) - // E.g., sourceIfFalse = 1010b, sourceIfTrue = 0101b, mask = 1100b - // (sourceIfTrue ^ sourceIfFalse) = 1111b --> & mask = 1100b --> ^ sourceIfFalse = 0110b, - // as expected (01 from sourceIfTrue, 10 from sourceIfFalse) - // Courtesy of Mark++, http://markplusplus.wordpress.com/2007/03/14/fast-sse-select-operation/ - mQ = _mm_xor_ps( sourceIfFalse, _mm_and_ps( mask, _mm_xor_ps( sourceIfTrue, sourceIfFalse ) ) ); + // ((( sourceIfTrue ^ sourceIfFalse ) & mask) ^ sourceIfFalse ) + // E.g., sourceIfFalse = 1010b, sourceIfTrue = 0101b, mask = 1100b + // (sourceIfTrue ^ sourceIfFalse) = 1111b --> & mask = 1100b --> ^ sourceIfFalse = 0110b, + // as expected (01 from sourceIfTrue, 10 from sourceIfFalse) + // Courtesy of Mark++, http://markplusplus.wordpress.com/2007/03/14/fast-sse-select-operation/ + mQ = _mm_xor_ps( sourceIfFalse, _mm_and_ps( mask, _mm_xor_ps( sourceIfTrue, sourceIfFalse ) ) ); } //////////////////////////////////// @@ -172,84 +172,84 @@ inline void LLVector4a::setSelectWithMask( const LLVector4Logical& mask, const L // Set this to the element-wise (a + b) inline void LLVector4a::setAdd(const LLVector4a& a, const LLVector4a& b) { - mQ = _mm_add_ps(a.mQ, b.mQ); + mQ = _mm_add_ps(a.mQ, b.mQ); } // Set this to element-wise (a - b) inline void LLVector4a::setSub(const LLVector4a& a, const LLVector4a& b) { - mQ = _mm_sub_ps(a.mQ, b.mQ); + mQ = _mm_sub_ps(a.mQ, b.mQ); } // Set this to element-wise multiply (a * b) inline void LLVector4a::setMul(const LLVector4a& a, const LLVector4a& b) { - mQ = _mm_mul_ps(a.mQ, b.mQ); + mQ = _mm_mul_ps(a.mQ, b.mQ); } // Set this to element-wise quotient (a / b) inline void LLVector4a::setDiv(const LLVector4a& a, const LLVector4a& b) { - mQ = _mm_div_ps( a.mQ, b.mQ ); + mQ = _mm_div_ps( a.mQ, b.mQ ); } // Set this to the element-wise absolute value of src inline void LLVector4a::setAbs(const LLVector4a& src) { - static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }; - mQ = _mm_and_ps(src.mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A)); + static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }; + mQ = _mm_and_ps(src.mQ, *reinterpret_cast<const LLQuad*>(F_ABS_MASK_4A)); } // Add to each component in this vector the corresponding component in rhs inline void LLVector4a::add(const LLVector4a& rhs) { - mQ = _mm_add_ps(mQ, rhs.mQ); + mQ = _mm_add_ps(mQ, rhs.mQ); } // Subtract from each component in this vector the corresponding component in rhs inline void LLVector4a::sub(const LLVector4a& rhs) { - mQ = _mm_sub_ps(mQ, rhs.mQ); + mQ = _mm_sub_ps(mQ, rhs.mQ); } // Multiply each component in this vector by the corresponding component in rhs inline void LLVector4a::mul(const LLVector4a& rhs) { - mQ = _mm_mul_ps(mQ, rhs.mQ); + mQ = _mm_mul_ps(mQ, rhs.mQ); } // Divide each component in this vector by the corresponding component in rhs inline void LLVector4a::div(const LLVector4a& rhs) { - // TODO: Check accuracy, maybe add divFast - mQ = _mm_div_ps(mQ, rhs.mQ); + // TODO: Check accuracy, maybe add divFast + mQ = _mm_div_ps(mQ, rhs.mQ); } // Multiply this vector by x in a scalar fashion -inline void LLVector4a::mul(const F32 x) +inline void LLVector4a::mul(const F32 x) { - LLVector4a t; - t.splat(x); - - mQ = _mm_mul_ps(mQ, t.mQ); + LLVector4a t; + t.splat(x); + + mQ = _mm_mul_ps(mQ, t.mQ); } // Set this to (a x b) (geometric cross-product) inline void LLVector4a::setCross3(const LLVector4a& a, const LLVector4a& b) { - // Vectors are stored in memory in w, z, y, x order from high to low - // Set vector1 = { a[W], a[X], a[Z], a[Y] } - const LLQuad vector1 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 0, 2, 1 )); - // Set vector2 = { b[W], b[Y], b[X], b[Z] } - const LLQuad vector2 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 1, 0, 2 )); - // mQ = { a[W]*b[W], a[X]*b[Y], a[Z]*b[X], a[Y]*b[Z] } - mQ = _mm_mul_ps( vector1, vector2 ); - // vector3 = { a[W], a[Y], a[X], a[Z] } - const LLQuad vector3 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 1, 0, 2 )); - // vector4 = { b[W], b[X], b[Z], b[Y] } - const LLQuad vector4 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 0, 2, 1 )); - // mQ = { 0, a[X]*b[Y] - a[Y]*b[X], a[Z]*b[X] - a[X]*b[Z], a[Y]*b[Z] - a[Z]*b[Y] } - mQ = _mm_sub_ps( mQ, _mm_mul_ps( vector3, vector4 )); + // Vectors are stored in memory in w, z, y, x order from high to low + // Set vector1 = { a[W], a[X], a[Z], a[Y] } + const LLQuad vector1 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 0, 2, 1 )); + // Set vector2 = { b[W], b[Y], b[X], b[Z] } + const LLQuad vector2 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 1, 0, 2 )); + // mQ = { a[W]*b[W], a[X]*b[Y], a[Z]*b[X], a[Y]*b[Z] } + mQ = _mm_mul_ps( vector1, vector2 ); + // vector3 = { a[W], a[Y], a[X], a[Z] } + const LLQuad vector3 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 1, 0, 2 )); + // vector4 = { b[W], b[X], b[Z], b[Y] } + const LLQuad vector4 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 0, 2, 1 )); + // mQ = { 0, a[X]*b[Y] - a[Y]*b[X], a[Z]*b[X] - a[X]*b[Z], a[Y]*b[Z] - a[Z]*b[Y] } + mQ = _mm_sub_ps( mQ, _mm_mul_ps( vector3, vector4 )); } /* This function works, but may be slightly slower than the one below on older machines @@ -261,7 +261,7 @@ inline void LLVector4a::setCross3(const LLVector4a& a, const LLVector4a& b) const LLQuad wzxy = _mm_shuffle_ps( ab, ab, _MM_SHUFFLE(3, 2, 0, 1 )); // xPlusY = { 2*a[W]*b[W], 2 * a[Z] * b[Z], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } const LLQuad xPlusY = _mm_add_ps(ab, wzxy); - // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } const LLQuad xPlusYSplat = _mm_movelh_ps(xPlusY, xPlusY); // zSplat = { a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z] } const LLQuad zSplat = _mm_shuffle_ps( ab, ab, _MM_SHUFFLE( 2, 2, 2, 2 )); @@ -272,267 +272,267 @@ inline void LLVector4a::setCross3(const LLVector4a& a, const LLVector4a& b) // Set all elements to the dot product of the x, y, and z elements in a and b inline void LLVector4a::setAllDot3(const LLVector4a& a, const LLVector4a& b) { - // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] } - const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ ); - // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] } - const __m128i wzxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(3, 2, 0, 1 )); - // xPlusY = { 2*a[W]*b[W], 2 * a[Z] * b[Z], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } - const LLQuad xPlusY = _mm_add_ps(ab, _mm_castsi128_ps(wzxy)); - // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } - const LLQuad xPlusYSplat = _mm_movelh_ps(xPlusY, xPlusY); - // zSplat = { a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z] } - const __m128i zSplat = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE( 2, 2, 2, 2 )); - // mQ = { a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same } - mQ = _mm_add_ps(_mm_castsi128_ps(zSplat), xPlusYSplat); + // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] } + const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ ); + // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] } + const __m128i wzxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(3, 2, 0, 1 )); + // xPlusY = { 2*a[W]*b[W], 2 * a[Z] * b[Z], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad xPlusY = _mm_add_ps(ab, _mm_castsi128_ps(wzxy)); + // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad xPlusYSplat = _mm_movelh_ps(xPlusY, xPlusY); + // zSplat = { a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z] } + const __m128i zSplat = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE( 2, 2, 2, 2 )); + // mQ = { a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same } + mQ = _mm_add_ps(_mm_castsi128_ps(zSplat), xPlusYSplat); } // Set all elements to the dot product of the x, y, z, and w elements in a and b inline void LLVector4a::setAllDot4(const LLVector4a& a, const LLVector4a& b) { - // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] } - const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ ); - // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] } - const __m128i zwxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(2, 3, 0, 1 )); - // zPlusWandXplusY = { a[W]*b[W] + a[Z]*b[Z], a[Z] * b[Z] + a[W]*b[W], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } - const LLQuad zPlusWandXplusY = _mm_add_ps(ab, _mm_castsi128_ps(zwxy)); - // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } - const LLQuad xPlusYSplat = _mm_movelh_ps(zPlusWandXplusY, zPlusWandXplusY); - const LLQuad zPlusWSplat = _mm_movehl_ps(zPlusWandXplusY, zPlusWandXplusY); + // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] } + const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ ); + // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] } + const __m128i zwxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(2, 3, 0, 1 )); + // zPlusWandXplusY = { a[W]*b[W] + a[Z]*b[Z], a[Z] * b[Z] + a[W]*b[W], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad zPlusWandXplusY = _mm_add_ps(ab, _mm_castsi128_ps(zwxy)); + // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad xPlusYSplat = _mm_movelh_ps(zPlusWandXplusY, zPlusWandXplusY); + const LLQuad zPlusWSplat = _mm_movehl_ps(zPlusWandXplusY, zPlusWandXplusY); - // mQ = { a[W]*b[W] + a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same } - mQ = _mm_add_ps(xPlusYSplat, zPlusWSplat); + // mQ = { a[W]*b[W] + a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same } + mQ = _mm_add_ps(xPlusYSplat, zPlusWSplat); } // Return the 3D dot product of this vector and b inline LLSimdScalar LLVector4a::dot3(const LLVector4a& b) const { - const LLQuad ab = _mm_mul_ps( mQ, b.mQ ); - const LLQuad splatY = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(1, 1, 1, 1) ) ); - const LLQuad splatZ = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(2, 2, 2, 2) ) ); - const LLQuad xPlusY = _mm_add_ps( ab, splatY ); - return _mm_add_ps( xPlusY, splatZ ); + const LLQuad ab = _mm_mul_ps( mQ, b.mQ ); + const LLQuad splatY = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(1, 1, 1, 1) ) ); + const LLQuad splatZ = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(2, 2, 2, 2) ) ); + const LLQuad xPlusY = _mm_add_ps( ab, splatY ); + return _mm_add_ps( xPlusY, splatZ ); } // Return the 4D dot product of this vector and b inline LLSimdScalar LLVector4a::dot4(const LLVector4a& b) const { - // ab = { w, z, y, x } - const LLQuad ab = _mm_mul_ps( mQ, b.mQ ); - // upperProdsInLowerElems = { y, x, y, x } - const LLQuad upperProdsInLowerElems = _mm_movehl_ps( ab, ab ); - // sumOfPairs = { w+y, z+x, 2y, 2x } - const LLQuad sumOfPairs = _mm_add_ps( upperProdsInLowerElems, ab ); - // shuffled = { z+x, z+x, z+x, z+x } - const LLQuad shuffled = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( sumOfPairs ), _MM_SHUFFLE(1, 1, 1, 1) ) ); - return _mm_add_ss( sumOfPairs, shuffled ); + // ab = { w, z, y, x } + const LLQuad ab = _mm_mul_ps( mQ, b.mQ ); + // upperProdsInLowerElems = { y, x, y, x } + const LLQuad upperProdsInLowerElems = _mm_movehl_ps( ab, ab ); + // sumOfPairs = { w+y, z+x, 2y, 2x } + const LLQuad sumOfPairs = _mm_add_ps( upperProdsInLowerElems, ab ); + // shuffled = { z+x, z+x, z+x, z+x } + const LLQuad shuffled = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( sumOfPairs ), _MM_SHUFFLE(1, 1, 1, 1) ) ); + return _mm_add_ss( sumOfPairs, shuffled ); } // Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed // Note that this does not consider zero length vectors! inline void LLVector4a::normalize3() { - // lenSqrd = a dot a - LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); - // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 } - const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ); - static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f }; - static const LLQuad three = {3.f, 3.f, 3.f, 3.f }; - // Now we do one round of Newton-Raphson approximation to get full accuracy - // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a)) - // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3)) - // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3 - // = 0.5 * w * (3 - a*w^2) - // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula - // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)] - const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt ); - const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt ); - const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt ); - const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt)); - mQ = _mm_mul_ps( mQ, nrApprox ); + // lenSqrd = a dot a + LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); + // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 } + const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ); + static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f }; + static const LLQuad three = {3.f, 3.f, 3.f, 3.f }; + // Now we do one round of Newton-Raphson approximation to get full accuracy + // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a)) + // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3)) + // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3 + // = 0.5 * w * (3 - a*w^2) + // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula + // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)] + const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt ); + const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt ); + const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt ); + const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt)); + mQ = _mm_mul_ps( mQ, nrApprox ); } // Normalize this vector with respect to all components. Accurate to 22 bites of precision. // Note that this does not consider zero length vectors! inline void LLVector4a::normalize4() { - // lenSqrd = a dot a - LLVector4a lenSqrd; lenSqrd.setAllDot4( *this, *this ); - // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 } - const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ); - static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f }; - static const LLQuad three = {3.f, 3.f, 3.f, 3.f }; - // Now we do one round of Newton-Raphson approximation to get full accuracy - // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a)) - // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3)) - // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3 - // = 0.5 * w * (3 - a*w^2) - // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula - // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)] - const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt ); - const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt ); - const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt ); - const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt)); - mQ = _mm_mul_ps( mQ, nrApprox ); + // lenSqrd = a dot a + LLVector4a lenSqrd; lenSqrd.setAllDot4( *this, *this ); + // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 } + const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ); + static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f }; + static const LLQuad three = {3.f, 3.f, 3.f, 3.f }; + // Now we do one round of Newton-Raphson approximation to get full accuracy + // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a)) + // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3)) + // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3 + // = 0.5 * w * (3 - a*w^2) + // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula + // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)] + const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt ); + const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt ); + const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt ); + const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt)); + mQ = _mm_mul_ps( mQ, nrApprox ); } // Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed // Note that this does not consider zero length vectors! inline LLSimdScalar LLVector4a::normalize3withLength() { - // lenSqrd = a dot a - LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); - // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 } - const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ); - static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f }; - static const LLQuad three = {3.f, 3.f, 3.f, 3.f }; - // Now we do one round of Newton-Raphson approximation to get full accuracy - // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a)) - // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3)) - // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3 - // = 0.5 * w * (3 - a*w^2) - // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula - // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)] - const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt ); - const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt ); - const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt ); - const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt)); - mQ = _mm_mul_ps( mQ, nrApprox ); - return _mm_sqrt_ss(lenSqrd); + // lenSqrd = a dot a + LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); + // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 } + const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ); + static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f }; + static const LLQuad three = {3.f, 3.f, 3.f, 3.f }; + // Now we do one round of Newton-Raphson approximation to get full accuracy + // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a)) + // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3)) + // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3 + // = 0.5 * w * (3 - a*w^2) + // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula + // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)] + const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt ); + const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt ); + const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt ); + const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt)); + mQ = _mm_mul_ps( mQ, nrApprox ); + return _mm_sqrt_ss(lenSqrd); } // Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed // Note that this does not consider zero length vectors! inline void LLVector4a::normalize3fast() { - LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); - const LLQuad approxRsqrt = _mm_rsqrt_ps(lenSqrd.mQ); - mQ = _mm_mul_ps( mQ, approxRsqrt ); + LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); + const LLQuad approxRsqrt = _mm_rsqrt_ps(lenSqrd.mQ); + mQ = _mm_mul_ps( mQ, approxRsqrt ); } inline void LLVector4a::normalize3fast_checked(LLVector4a* d) { - if (!isFinite3()) - { - *this = d ? *d : LLVector4a(0,1,0,1); - return; - } + if (!isFinite3()) + { + *this = d ? *d : LLVector4a(0,1,0,1); + return; + } - LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); + LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); - if (lenSqrd.getF32ptr()[0] <= FLT_EPSILON) - { - *this = d ? *d : LLVector4a(0,1,0,1); - return; - } + if (lenSqrd.getF32ptr()[0] <= FLT_EPSILON) + { + *this = d ? *d : LLVector4a(0,1,0,1); + return; + } - const LLQuad approxRsqrt = _mm_rsqrt_ps(lenSqrd.mQ); - mQ = _mm_mul_ps( mQ, approxRsqrt ); + const LLQuad approxRsqrt = _mm_rsqrt_ps(lenSqrd.mQ); + mQ = _mm_mul_ps( mQ, approxRsqrt ); } // Return true if this vector is normalized with respect to x,y,z up to tolerance inline LLBool32 LLVector4a::isNormalized3( F32 tolerance ) const { - static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 }; - LLSimdScalar tol = _mm_load_ss( &tolerance ); - tol = _mm_mul_ss( tol, tol ); - LLVector4a lenSquared; lenSquared.setAllDot3( *this, *this ); - lenSquared.sub( *reinterpret_cast<const LLVector4a*>(ones) ); - lenSquared.setAbs(lenSquared); - return _mm_comile_ss( lenSquared, tol ); + static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 }; + LLSimdScalar tol = _mm_load_ss( &tolerance ); + tol = _mm_mul_ss( tol, tol ); + LLVector4a lenSquared; lenSquared.setAllDot3( *this, *this ); + lenSquared.sub( *reinterpret_cast<const LLVector4a*>(ones) ); + lenSquared.setAbs(lenSquared); + return _mm_comile_ss( lenSquared, tol ); } // Return true if this vector is normalized with respect to all components up to tolerance inline LLBool32 LLVector4a::isNormalized4( F32 tolerance ) const { - static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 }; - LLSimdScalar tol = _mm_load_ss( &tolerance ); - tol = _mm_mul_ss( tol, tol ); - LLVector4a lenSquared; lenSquared.setAllDot4( *this, *this ); - lenSquared.sub( *reinterpret_cast<const LLVector4a*>(ones) ); - lenSquared.setAbs(lenSquared); - return _mm_comile_ss( lenSquared, tol ); + static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 }; + LLSimdScalar tol = _mm_load_ss( &tolerance ); + tol = _mm_mul_ss( tol, tol ); + LLVector4a lenSquared; lenSquared.setAllDot4( *this, *this ); + lenSquared.sub( *reinterpret_cast<const LLVector4a*>(ones) ); + lenSquared.setAbs(lenSquared); + return _mm_comile_ss( lenSquared, tol ); } -// Set all elements to the length of vector 'v' +// Set all elements to the length of vector 'v' inline void LLVector4a::setAllLength3( const LLVector4a& v ) { - LLVector4a lenSqrd; - lenSqrd.setAllDot3(v, v); - - mQ = _mm_sqrt_ps(lenSqrd.mQ); + LLVector4a lenSqrd; + lenSqrd.setAllDot3(v, v); + + mQ = _mm_sqrt_ps(lenSqrd.mQ); } // Get this vector's length inline LLSimdScalar LLVector4a::getLength3() const { - return _mm_sqrt_ss( dot3( (const LLVector4a)mQ ) ); + return _mm_sqrt_ss( dot3( (const LLVector4a)mQ ) ); } // Set the components of this vector to the minimum of the corresponding components of lhs and rhs inline void LLVector4a::setMin(const LLVector4a& lhs, const LLVector4a& rhs) { - mQ = _mm_min_ps(lhs.mQ, rhs.mQ); + mQ = _mm_min_ps(lhs.mQ, rhs.mQ); } // Set the components of this vector to the maximum of the corresponding components of lhs and rhs inline void LLVector4a::setMax(const LLVector4a& lhs, const LLVector4a& rhs) { - mQ = _mm_max_ps(lhs.mQ, rhs.mQ); + mQ = _mm_max_ps(lhs.mQ, rhs.mQ); } // Set this to lhs + (rhs-lhs)*c inline void LLVector4a::setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c) { - LLVector4a t; - t.setSub(rhs,lhs); - t.mul(c); - setAdd(lhs, t); + LLVector4a t; + t.setSub(rhs,lhs); + t.mul(c); + setAdd(lhs, t); } inline LLBool32 LLVector4a::isFinite3() const { - static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 }; - ll_assert_aligned(nanOrInfMask,16); - const __m128i nanOrInfMaskV = *reinterpret_cast<const __m128i*> (nanOrInfMask); - const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV ); - const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV )); - return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZ ); + static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 }; + ll_assert_aligned(nanOrInfMask,16); + const __m128i nanOrInfMaskV = *reinterpret_cast<const __m128i*> (nanOrInfMask); + const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV ); + const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV )); + return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZ ); } - + inline LLBool32 LLVector4a::isFinite4() const { - static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 }; - const __m128i nanOrInfMaskV = *reinterpret_cast<const __m128i*> (nanOrInfMask); - const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV ); - const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV )); - return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZW ); + static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 }; + const __m128i nanOrInfMaskV = *reinterpret_cast<const __m128i*> (nanOrInfMask); + const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV ); + const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV )); + return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZW ); } inline void LLVector4a::setRotatedInv( const LLRotation& rot, const LLVector4a& vec ) { - LLRotation inv; inv.setTranspose( rot ); - setRotated( inv, vec ); + LLRotation inv; inv.setTranspose( rot ); + setRotated( inv, vec ); } inline void LLVector4a::setRotatedInv( const LLQuaternion2& quat, const LLVector4a& vec ) { - LLQuaternion2 invRot; invRot.setConjugate( quat ); - setRotated(invRot, vec); + LLQuaternion2 invRot; invRot.setConjugate( quat ); + setRotated(invRot, vec); } inline void LLVector4a::clamp( const LLVector4a& low, const LLVector4a& high ) { - const LLVector4Logical highMask = greaterThan( high ); - const LLVector4Logical lowMask = lessThan( low ); + const LLVector4Logical highMask = greaterThan( high ); + const LLVector4Logical lowMask = lessThan( low ); - setSelectWithMask( highMask, high, *this ); - setSelectWithMask( lowMask, low, *this ); + setSelectWithMask( highMask, high, *this ); + setSelectWithMask( lowMask, low, *this ); } //////////////////////////////////// // LOGICAL -//////////////////////////////////// +//////////////////////////////////// // The functions in this section will compare the elements in this vector // to those in rhs and return an LLVector4Logical with all bits set in elements // where the comparison was true and all bits unset in elements where the comparison @@ -544,68 +544,68 @@ inline void LLVector4a::clamp( const LLVector4a& low, const LLVector4a& high ) //////////////////////////////////// inline LLVector4Logical LLVector4a::greaterThan(const LLVector4a& rhs) const -{ - return _mm_cmpgt_ps(mQ, rhs.mQ); +{ + return _mm_cmpgt_ps(mQ, rhs.mQ); } inline LLVector4Logical LLVector4a::lessThan(const LLVector4a& rhs) const { - return _mm_cmplt_ps(mQ, rhs.mQ); + return _mm_cmplt_ps(mQ, rhs.mQ); } inline LLVector4Logical LLVector4a::greaterEqual(const LLVector4a& rhs) const { - return _mm_cmpge_ps(mQ, rhs.mQ); + return _mm_cmpge_ps(mQ, rhs.mQ); } inline LLVector4Logical LLVector4a::lessEqual(const LLVector4a& rhs) const { - return _mm_cmple_ps(mQ, rhs.mQ); + return _mm_cmple_ps(mQ, rhs.mQ); } inline LLVector4Logical LLVector4a::equal(const LLVector4a& rhs) const { - return _mm_cmpeq_ps(mQ, rhs.mQ); + return _mm_cmpeq_ps(mQ, rhs.mQ); } // Returns true if this and rhs are componentwise equal up to the specified absolute tolerance inline bool LLVector4a::equals4(const LLVector4a& rhs, F32 tolerance ) const { - LLVector4a diff; diff.setSub( *this, rhs ); - diff.setAbs( diff ); - const LLQuad tol = _mm_set1_ps( tolerance ); - const LLQuad cmp = _mm_cmplt_ps( diff, tol ); - return (_mm_movemask_ps( cmp ) & LLVector4Logical::MASK_XYZW) == LLVector4Logical::MASK_XYZW; + LLVector4a diff; diff.setSub( *this, rhs ); + diff.setAbs( diff ); + const LLQuad tol = _mm_set1_ps( tolerance ); + const LLQuad cmp = _mm_cmplt_ps( diff, tol ); + return (_mm_movemask_ps( cmp ) & LLVector4Logical::MASK_XYZW) == LLVector4Logical::MASK_XYZW; } inline bool LLVector4a::equals3(const LLVector4a& rhs, F32 tolerance ) const { - LLVector4a diff; diff.setSub( *this, rhs ); - diff.setAbs( diff ); - const LLQuad tol = _mm_set1_ps( tolerance ); - const LLQuad t = _mm_cmplt_ps( diff, tol ); - return (_mm_movemask_ps( t ) & LLVector4Logical::MASK_XYZ) == LLVector4Logical::MASK_XYZ; - + LLVector4a diff; diff.setSub( *this, rhs ); + diff.setAbs( diff ); + const LLQuad tol = _mm_set1_ps( tolerance ); + const LLQuad t = _mm_cmplt_ps( diff, tol ); + return (_mm_movemask_ps( t ) & LLVector4Logical::MASK_XYZ) == LLVector4Logical::MASK_XYZ; + } //////////////////////////////////// // OPERATORS -//////////////////////////////////// +//////////////////////////////////// // Do NOT add aditional operators without consulting someone with SSE experience inline const LLVector4a& LLVector4a::operator= ( const LLVector4a& rhs ) { - mQ = rhs.mQ; - return *this; + mQ = rhs.mQ; + return *this; } inline const LLVector4a& LLVector4a::operator= ( const LLQuad& rhs ) { - mQ = rhs; - return *this; + mQ = rhs; + return *this; } inline LLVector4a::operator LLQuad() const { - return mQ; + return mQ; } diff --git a/indra/llmath/llvector4logical.h b/indra/llmath/llvector4logical.h index c5698f7cea..d08b5513d9 100644 --- a/indra/llmath/llvector4logical.h +++ b/indra/llmath/llvector4logical.h @@ -1,31 +1,31 @@ -/** +/** * @file llvector4logical.h * @brief LLVector4Logical class header file - Companion class to LLVector4a for logical and bit-twiddling operations * * $LicenseInfo:firstyear=2010&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_VECTOR4LOGICAL_H -#define LL_VECTOR4LOGICAL_H +#ifndef LL_VECTOR4LOGICAL_H +#define LL_VECTOR4LOGICAL_H #include "llmemory.h" @@ -41,86 +41,86 @@ static LL_ALIGN_16(const U32 S_V4LOGICAL_MASK_TABLE[4*4]) = { - 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF }; class LLVector4Logical { public: - - enum { - MASK_X = 1, - MASK_Y = 1 << 1, - MASK_Z = 1 << 2, - MASK_W = 1 << 3, - MASK_XYZ = MASK_X | MASK_Y | MASK_Z, - MASK_XYZW = MASK_XYZ | MASK_W - }; - - // Empty default ctor - LLVector4Logical() {} - - LLVector4Logical( const LLQuad& quad ) - { - mQ = quad; - } - - // Create and return a mask consisting of the lowest order bit of each element - inline U32 getGatheredBits() const - { - return _mm_movemask_ps(mQ); - }; - - // Invert this mask - inline LLVector4Logical& invert() - { - static const LL_ALIGN_16(U32 allOnes[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; - ll_assert_aligned(allOnes,16); - mQ = _mm_andnot_ps( mQ, *(LLQuad*)(allOnes) ); - return *this; - } - - inline LLBool32 areAllSet( U32 mask ) const - { - return ( getGatheredBits() & mask) == mask; - } - - inline LLBool32 areAllSet() const - { - return areAllSet( MASK_XYZW ); - } - - inline LLBool32 areAnySet( U32 mask ) const - { - return getGatheredBits() & mask; - } - - inline LLBool32 areAnySet() const - { - return areAnySet( MASK_XYZW ); - } - - inline operator LLQuad() const - { - return mQ; - } - - inline void clear() - { - mQ = _mm_setzero_ps(); - } - - template<int N> void setElement() - { - mQ = _mm_or_ps( mQ, *reinterpret_cast<const LLQuad*>(S_V4LOGICAL_MASK_TABLE + 4*N) ); - } - + + enum { + MASK_X = 1, + MASK_Y = 1 << 1, + MASK_Z = 1 << 2, + MASK_W = 1 << 3, + MASK_XYZ = MASK_X | MASK_Y | MASK_Z, + MASK_XYZW = MASK_XYZ | MASK_W + }; + + // Empty default ctor + LLVector4Logical() {} + + LLVector4Logical( const LLQuad& quad ) + { + mQ = quad; + } + + // Create and return a mask consisting of the lowest order bit of each element + inline U32 getGatheredBits() const + { + return _mm_movemask_ps(mQ); + }; + + // Invert this mask + inline LLVector4Logical& invert() + { + static const LL_ALIGN_16(U32 allOnes[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + ll_assert_aligned(allOnes,16); + mQ = _mm_andnot_ps( mQ, *(LLQuad*)(allOnes) ); + return *this; + } + + inline LLBool32 areAllSet( U32 mask ) const + { + return ( getGatheredBits() & mask) == mask; + } + + inline LLBool32 areAllSet() const + { + return areAllSet( MASK_XYZW ); + } + + inline LLBool32 areAnySet( U32 mask ) const + { + return getGatheredBits() & mask; + } + + inline LLBool32 areAnySet() const + { + return areAnySet( MASK_XYZW ); + } + + inline operator LLQuad() const + { + return mQ; + } + + inline void clear() + { + mQ = _mm_setzero_ps(); + } + + template<int N> void setElement() + { + mQ = _mm_or_ps( mQ, *reinterpret_cast<const LLQuad*>(S_V4LOGICAL_MASK_TABLE + 4*N) ); + } + private: - - LLQuad mQ; + + LLQuad mQ; }; #endif //LL_VECTOR4ALOGICAL_H diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index a4b039fd55..f9f4aea77b 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1,7304 +1,7304 @@ -/** - * @file llvolume.cpp - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llmemory.h" -#include "llmath.h" - -#include <set> -#if !LL_WINDOWS -#include <stdint.h> -#endif -#include <cmath> -#include <unordered_map> - -#include "llerror.h" - -#include "llvolumemgr.h" -#include "v2math.h" -#include "v3math.h" -#include "v4math.h" -#include "m4math.h" -#include "m3math.h" -#include "llmatrix3a.h" -#include "lloctree.h" -#include "llvolume.h" -#include "llvolumeoctree.h" -#include "llstl.h" -#include "llsdserialize.h" -#include "llvector4a.h" -#include "llmatrix4a.h" -#include "llmeshoptimizer.h" -#include "lltimer.h" - -#include "mikktspace/mikktspace.h" -#include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file - -#include "meshoptimizer/meshoptimizer.h" - -#define DEBUG_SILHOUETTE_BINORMALS 0 -#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette -#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette - -constexpr F32 MIN_CUT_DELTA = 0.02f; - -constexpr F32 HOLLOW_MIN = 0.f; -constexpr F32 HOLLOW_MAX = 0.95f; -constexpr F32 HOLLOW_MAX_SQUARE = 0.7f; - -constexpr F32 TWIST_MIN = -1.f; -constexpr F32 TWIST_MAX = 1.f; - -constexpr F32 RATIO_MIN = 0.f; -constexpr F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper - -constexpr F32 HOLE_X_MIN= 0.05f; -constexpr F32 HOLE_X_MAX= 1.0f; - -constexpr F32 HOLE_Y_MIN= 0.05f; -constexpr F32 HOLE_Y_MAX= 0.5f; - -constexpr F32 SHEAR_MIN = -0.5f; -constexpr F32 SHEAR_MAX = 0.5f; - -constexpr F32 REV_MIN = 1.f; -constexpr F32 REV_MAX = 4.f; - -constexpr F32 TAPER_MIN = -1.f; -constexpr F32 TAPER_MAX = 1.f; - -constexpr F32 SKEW_MIN = -0.95f; -constexpr F32 SKEW_MAX = 0.95f; - -constexpr F32 SCULPT_MIN_AREA = 0.002f; -constexpr S32 SCULPT_MIN_AREA_DETAIL = 1; - -bool gDebugGL = false; // See settings.xml "RenderDebugGL" - -bool check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm) -{ - LLVector3 test = (pt2-pt1)%(pt3-pt2); - - //answer - if(test * norm < 0) - { - return false; - } - else - { - return true; - } -} - -bool LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size) -{ - return LLLineSegmentBoxIntersect(start.mV, end.mV, center.mV, size.mV); -} - -bool LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size) -{ - F32 fAWdU[3]{}; - F32 dir[3]{}; - F32 diff[3]{}; - - for (U32 i = 0; i < 3; i++) - { - dir[i] = 0.5f * (end[i] - start[i]); - diff[i] = (0.5f * (end[i] + start[i])) - center[i]; - fAWdU[i] = fabsf(dir[i]); - if(fabsf(diff[i])>size[i] + fAWdU[i]) return false; - } - - float f; - f = dir[1] * diff[2] - dir[2] * diff[1]; if(fabsf(f)>size[1]*fAWdU[2] + size[2]*fAWdU[1]) return false; - f = dir[2] * diff[0] - dir[0] * diff[2]; if(fabsf(f)>size[0]*fAWdU[2] + size[2]*fAWdU[0]) return false; - f = dir[0] * diff[1] - dir[1] * diff[0]; if(fabsf(f)>size[0]*fAWdU[1] + size[1]*fAWdU[0]) return false; - - return true; -} - -// Finds tangent vec based on three vertices with texture coordinates. -// Fills in dummy values if the triangle has degenerate texture coordinates. -void calc_tangent_from_triangle( - LLVector4a& normal, - LLVector4a& tangent_out, - const LLVector4a& v1, - const LLVector2& w1, - const LLVector4a& v2, - const LLVector2& w2, - const LLVector4a& v3, - const LLVector2& w3) -{ - const F32* v1ptr = v1.getF32ptr(); - const F32* v2ptr = v2.getF32ptr(); - const F32* v3ptr = v3.getF32ptr(); - - float x1 = v2ptr[0] - v1ptr[0]; - float x2 = v3ptr[0] - v1ptr[0]; - float y1 = v2ptr[1] - v1ptr[1]; - float y2 = v3ptr[1] - v1ptr[1]; - float z1 = v2ptr[2] - v1ptr[2]; - float z2 = v3ptr[2] - v1ptr[2]; - - float s1 = w2.mV[0] - w1.mV[0]; - float s2 = w3.mV[0] - w1.mV[0]; - float t1 = w2.mV[1] - w1.mV[1]; - float t2 = w3.mV[1] - w1.mV[1]; - - F32 rd = s1*t2-s2*t1; - - float r = ((rd*rd) > FLT_EPSILON) ? (1.0f / rd) - : ((rd > 0.0f) ? 1024.f : -1024.f); //some made up large ratio for division by zero - - llassert(llfinite(r)); - llassert(!llisnan(r)); - - LLVector4a sdir( - (t2 * x1 - t1 * x2) * r, - (t2 * y1 - t1 * y2) * r, - (t2 * z1 - t1 * z2) * r); - - LLVector4a tdir( - (s1 * x2 - s2 * x1) * r, - (s1 * y2 - s2 * y1) * r, - (s1 * z2 - s2 * z1) * r); - - LLVector4a n = normal; - LLVector4a t = sdir; - - LLVector4a ncrosst; - ncrosst.setCross3(n,t); - - // Gram-Schmidt orthogonalize - n.mul(n.dot3(t).getF32()); - - LLVector4a tsubn; - tsubn.setSub(t,n); - - if (tsubn.dot3(tsubn).getF32() > F_APPROXIMATELY_ZERO) - { - tsubn.normalize3fast_checked(); - - // Calculate handedness - F32 handedness = ncrosst.dot3(tdir).getF32() < 0.f ? -1.f : 1.f; - - tsubn.getF32ptr()[3] = handedness; - - tangent_out = tsubn; - } - else - { - // degenerate, make up a value - // - tangent_out.set(0,0,1,1); - } - -} - - -// intersect test between triangle vert0, vert1, vert2 and a ray from orig in direction dir. -// returns true if intersecting and returns barycentric coordinates in intersection_a, intersection_b, -// and returns the intersection point along dir in intersection_t. - -// Moller-Trumbore algorithm -bool LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, - F32& intersection_a, F32& intersection_b, F32& intersection_t) -{ - - /* find vectors for two edges sharing vert0 */ - LLVector4a edge1; - edge1.setSub(vert1, vert0); - - LLVector4a edge2; - edge2.setSub(vert2, vert0); - - /* begin calculating determinant - also used to calculate U parameter */ - LLVector4a pvec; - pvec.setCross3(dir, edge2); - - /* if determinant is near zero, ray lies in plane of triangle */ - LLVector4a det; - det.setAllDot3(edge1, pvec); - - if (det.greaterEqual(LLVector4a::getEpsilon()).getGatheredBits() & 0x7) - { - /* calculate distance from vert0 to ray origin */ - LLVector4a tvec; - tvec.setSub(orig, vert0); - - /* calculate U parameter and test bounds */ - LLVector4a u; - u.setAllDot3(tvec,pvec); - - if ((u.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7) && - (u.lessEqual(det).getGatheredBits() & 0x7)) - { - /* prepare to test V parameter */ - LLVector4a qvec; - qvec.setCross3(tvec, edge1); - - /* calculate V parameter and test bounds */ - LLVector4a v; - v.setAllDot3(dir, qvec); - - - //if (!(v < 0.f || u + v > det)) - - LLVector4a sum_uv; - sum_uv.setAdd(u, v); - - S32 v_gequal = v.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7; - S32 sum_lequal = sum_uv.lessEqual(det).getGatheredBits() & 0x7; - - if (v_gequal && sum_lequal) - { - /* calculate t, scale parameters, ray intersects triangle */ - LLVector4a t; - t.setAllDot3(edge2,qvec); - - t.div(det); - u.div(det); - v.div(det); - - intersection_a = u[0]; - intersection_b = v[0]; - intersection_t = t[0]; - return true; - } - } - } - - return false; -} - -bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, - F32& intersection_a, F32& intersection_b, F32& intersection_t) -{ - F32 u, v, t; - - /* find vectors for two edges sharing vert0 */ - LLVector4a edge1; - edge1.setSub(vert1, vert0); - - - LLVector4a edge2; - edge2.setSub(vert2, vert0); - - /* begin calculating determinant - also used to calculate U parameter */ - LLVector4a pvec; - pvec.setCross3(dir, edge2); - - /* if determinant is near zero, ray lies in plane of triangle */ - F32 det = edge1.dot3(pvec).getF32(); - - - if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO) - { - return false; - } - - F32 inv_det = 1.f / det; - - /* calculate distance from vert0 to ray origin */ - LLVector4a tvec; - tvec.setSub(orig, vert0); - - /* calculate U parameter and test bounds */ - u = (tvec.dot3(pvec).getF32()) * inv_det; - if (u < 0.f || u > 1.f) - { - return false; - } - - /* prepare to test V parameter */ - tvec.sub(edge1); - - /* calculate V parameter and test bounds */ - v = (dir.dot3(tvec).getF32()) * inv_det; - - if (v < 0.f || u + v > 1.f) - { - return false; - } - - /* calculate t, ray intersects triangle */ - t = (edge2.dot3(tvec).getF32()) * inv_det; - - intersection_a = u; - intersection_b = v; - intersection_t = t; - - - return true; -} - -class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*> -{ -public: - const LLVolumeFace* mFace; - - LLVolumeOctreeRebound(const LLVolumeFace* face) - { - mFace = face; - } - - virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch) - { //this is a depth first traversal, so it's safe to assum all children have complete - //bounding data - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0); - - LLVector4a& min = node->mExtents[0]; - LLVector4a& max = node->mExtents[1]; - - if (!branch->isEmpty()) - { //node has data, find AABB that binds data set - const LLVolumeTriangle* tri = *(branch->getDataBegin()); - - //initialize min/max to first available vertex - min = *(tri->mV[0]); - max = *(tri->mV[0]); - - for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) - { //for each triangle in node - - //stretch by triangles in node - tri = *iter; - - min.setMin(min, *tri->mV[0]); - min.setMin(min, *tri->mV[1]); - min.setMin(min, *tri->mV[2]); - - max.setMax(max, *tri->mV[0]); - max.setMax(max, *tri->mV[1]); - max.setMax(max, *tri->mV[2]); - } - } - else if (branch->getChildCount() > 0) - { //no data, but child nodes exist - LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0); - - //initialize min/max to extents of first child - min = child->mExtents[0]; - max = child->mExtents[1]; - } - else - { - llassert(!branch->isLeaf()); // Empty leaf - } - - for (S32 i = 0; i < branch->getChildCount(); ++i) - { //stretch by child extents - LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0); - min.setMin(min, child->mExtents[0]); - max.setMax(max, child->mExtents[1]); - } - - node->mBounds[0].setAdd(min, max); - node->mBounds[0].mul(0.5f); - - node->mBounds[1].setSub(max,min); - node->mBounds[1].mul(0.5f); - } -}; - -//------------------------------------------------------------------- -// statics -//------------------------------------------------------------------- - - -//---------------------------------------------------- - -LLProfile::Face* LLProfile::addCap(S16 faceID) -{ - Face *face = vector_append(mFaces, 1); - - face->mIndex = 0; - face->mCount = mTotal; - face->mScaleU= 1.0f; - face->mCap = true; - face->mFaceID = faceID; - return face; -} - -LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, bool flat) -{ - Face *face = vector_append(mFaces, 1); - - face->mIndex = i; - face->mCount = count; - face->mScaleU= scaleU; - - face->mFlat = flat; - face->mCap = false; - face->mFaceID = faceID; - return face; -} - -//static -S32 LLProfile::getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split) -{ // this is basically LLProfile::genNGon stripped down to only the operations that influence the number of points - S32 np = 0; - - // Generate an n-sided "circular" path. - // 0 is (1,0), and we go counter-clockwise along a circular path from there. - F32 t, t_step, t_first, t_fraction; - - F32 begin = params.getBegin(); - F32 end = params.getEnd(); - - t_step = 1.0f / sides; - - t_first = floor(begin * sides) / (F32)sides; - - // pt1 is the first point on the fractional face. - // Starting t and ang values for the first face - t = t_first; - - // Increment to the next point. - // pt2 is the end point on the fractional face - t += t_step; - - t_fraction = (begin - t_first)*sides; - - // Only use if it's not almost exactly on an edge. - if (t_fraction < 0.9999f) - { - np++; - } - - // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02 - while (t < end) - { - // Iterate through all the integer steps of t. - np++; - - t += t_step; - } - - t_fraction = (end - (t - t_step))*sides; - - // Find the fraction that we need to add to the end point. - t_fraction = (end - (t - t_step))*sides; - if (t_fraction > 0.0001f) - { - np++; - } - - // If we're sliced, the profile is open. - if ((end - begin)*ang_scale < 0.99f) - { - if (params.getHollow() <= 0) - { - // put center point if not hollow. - np++; - } - } - - return np; -} - -// What is the bevel parameter used for? - DJS 04/05/02 -// Bevel parameter is currently unused but presumedly would support -// filleted and chamfered corners -void LLProfile::genNGon(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split) -{ - // Generate an n-sided "circular" path. - // 0 is (1,0), and we go counter-clockwise along a circular path from there. - constexpr F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f }; - F32 scale = 0.5f; - F32 t, t_step, t_first, t_fraction, ang, ang_step; - LLVector4a pt1,pt2; - - F32 begin = params.getBegin(); - F32 end = params.getEnd(); - - t_step = 1.0f / sides; - ang_step = 2.0f*F_PI*t_step*ang_scale; - - // Scale to have size "match" scale. Compensates to get object to generally fill bounding box. - - S32 total_sides = ll_round(sides / ang_scale); // Total number of sides all around - - if (total_sides < 8) - { - scale = tableScale[total_sides]; - } - - t_first = floor(begin * sides) / (F32)sides; - - // pt1 is the first point on the fractional face. - // Starting t and ang values for the first face - t = t_first; - ang = 2.0f*F_PI*(t*ang_scale + offset); - pt1.set(cos(ang)*scale,sin(ang)*scale, t); - - // Increment to the next point. - // pt2 is the end point on the fractional face - t += t_step; - ang += ang_step; - pt2.set(cos(ang)*scale,sin(ang)*scale,t); - - t_fraction = (begin - t_first)*sides; - - // Only use if it's not almost exactly on an edge. - if (t_fraction < 0.9999f) - { - LLVector4a new_pt; - new_pt.setLerp(pt1, pt2, t_fraction); - mProfile.push_back(new_pt); - } - - // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02 - while (t < end) - { - // Iterate through all the integer steps of t. - pt1.set(cos(ang)*scale,sin(ang)*scale,t); - - if (mProfile.size() > 0) { - LLVector4a p = mProfile[mProfile.size()-1]; - for (S32 i = 0; i < split && mProfile.size() > 0; i++) { - //mProfile.push_back(p+(pt1-p) * 1.0f/(float)(split+1) * (float)(i+1)); - LLVector4a new_pt; - new_pt.setSub(pt1, p); - new_pt.mul(1.0f/(float)(split+1) * (float)(i+1)); - new_pt.add(p); - mProfile.push_back(new_pt); - } - } - mProfile.push_back(pt1); - - t += t_step; - ang += ang_step; - } - - t_fraction = (end - (t - t_step))*sides; - - // pt1 is the first point on the fractional face - // pt2 is the end point on the fractional face - pt2.set(cos(ang)*scale,sin(ang)*scale,t); - - // Find the fraction that we need to add to the end point. - t_fraction = (end - (t - t_step))*sides; - if (t_fraction > 0.0001f) - { - LLVector4a new_pt; - new_pt.setLerp(pt1, pt2, t_fraction); - - if (mProfile.size() > 0) { - LLVector4a p = mProfile[mProfile.size()-1]; - for (S32 i = 0; i < split && mProfile.size() > 0; i++) { - //mProfile.push_back(p+(new_pt-p) * 1.0f/(float)(split+1) * (float)(i+1)); - - LLVector4a pt1; - pt1.setSub(new_pt, p); - pt1.mul(1.0f/(float)(split+1) * (float)(i+1)); - pt1.add(p); - mProfile.push_back(pt1); - } - } - mProfile.push_back(new_pt); - } - - // If we're sliced, the profile is open. - if ((end - begin)*ang_scale < 0.99f) - { - if ((end - begin)*ang_scale > 0.5f) - { - mConcave = true; - } - else - { - mConcave = false; - } - mOpen = true; - if (params.getHollow() <= 0) - { - // put center point if not hollow. - mProfile.push_back(LLVector4a(0,0,0)); - } - } - else - { - // The profile isn't open. - mOpen = false; - mConcave = false; - } - - mTotal = mProfile.size(); -} - -// Hollow is percent of the original bounding box, not of this particular -// profile's geometry. Thus, a swept triangle needs lower hollow values than -// a swept square. -LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, bool flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split) -{ - // Note that addHole will NOT work for non-"circular" profiles, if we ever decide to use them. - - // Total add has number of vertices on outside. - mTotalOut = mTotal; - - // Why is the "bevel" parameter -1? DJS 04/05/02 - genNGon(params, llfloor(sides),offset,-1, ang_scale, split); - - Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat); - - static thread_local LLAlignedArray<LLVector4a,64> pt; - pt.resize(mTotal) ; - - for (S32 i=mTotalOut;i<mTotal;i++) - { - pt[i] = mProfile[i]; - pt[i].mul(box_hollow); - } - - S32 j=mTotal-1; - for (S32 i=mTotalOut;i<mTotal;i++) - { - mProfile[i] = pt[j--]; - } - - for (S32 i=0;i<(S32)mFaces.size();i++) - { - if (mFaces[i].mCap) - { - mFaces[i].mCount *= 2; - } - } - - return face; -} - -//static -S32 LLProfile::getNumPoints(const LLProfileParams& params, bool path_open,F32 detail, S32 split, - bool is_sculpted, S32 sculpt_size) -{ // this is basically LLProfile::generate stripped down to only operations that influence the number of points - if (detail < MIN_LOD) - { - detail = MIN_LOD; - } - - // Generate the face data - F32 hollow = params.getHollow(); - - S32 np = 0; - - switch (params.getCurveType() & LL_PCODE_PROFILE_MASK) - { - case LL_PCODE_PROFILE_SQUARE: - { - np = getNumNGonPoints(params, 4,-0.375, 0, 1, split); - - if (hollow) - { - np *= 2; - } - } - break; - case LL_PCODE_PROFILE_ISOTRI: - case LL_PCODE_PROFILE_RIGHTTRI: - case LL_PCODE_PROFILE_EQUALTRI: - { - np = getNumNGonPoints(params, 3,0, 0, 1, split); - - if (hollow) - { - np *= 2; - } - } - break; - case LL_PCODE_PROFILE_CIRCLE: - { - // If this has a square hollow, we should adjust the - // number of faces a bit so that the geometry lines up. - U8 hole_type=0; - F32 circle_detail = MIN_DETAIL_FACES * detail; - if (hollow) - { - hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK; - if (hole_type == LL_PCODE_HOLE_SQUARE) - { - // Snap to the next multiple of four sides, - // so that corners line up. - circle_detail = llceil(circle_detail / 4.0f) * 4.0f; - } - } - - S32 sides = (S32)circle_detail; - - if (is_sculpted) - sides = sculpt_size; - - np = getNumNGonPoints(params, sides); - - if (hollow) - { - np *= 2; - } - } - break; - case LL_PCODE_PROFILE_CIRCLE_HALF: - { - // If this has a square hollow, we should adjust the - // number of faces a bit so that the geometry lines up. - U8 hole_type=0; - // Number of faces is cut in half because it's only a half-circle. - F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f; - if (hollow) - { - hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK; - if (hole_type == LL_PCODE_HOLE_SQUARE) - { - // Snap to the next multiple of four sides (div 2), - // so that corners line up. - circle_detail = llceil(circle_detail / 2.0f) * 2.0f; - } - } - np = getNumNGonPoints(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f); - - if (hollow) - { - np *= 2; - } - - // Special case for openness of sphere - if ((params.getEnd() - params.getBegin()) < 1.f) - { - } - else if (!hollow) - { - np++; - } - } - break; - default: - break; - }; - - - return np; -} - - -bool LLProfile::generate(const LLProfileParams& params, bool path_open,F32 detail, S32 split, - bool is_sculpted, S32 sculpt_size) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - if ((!mDirty) && (!is_sculpted)) - { - return false; - } - mDirty = false; - - if (detail < MIN_LOD) - { - LL_INFOS() << "Generating profile with LOD < MIN_LOD. CLAMPING" << LL_ENDL; - detail = MIN_LOD; - } - - mProfile.resize(0); - mFaces.resize(0); - - // Generate the face data - S32 i; - F32 begin = params.getBegin(); - F32 end = params.getEnd(); - F32 hollow = params.getHollow(); - - // Quick validation to eliminate some server crashes. - if (begin > end - 0.01f) - { - LL_WARNS() << "LLProfile::generate() assertion failed (begin >= end)" << LL_ENDL; - return false; - } - - S32 face_num = 0; - - switch (params.getCurveType() & LL_PCODE_PROFILE_MASK) - { - case LL_PCODE_PROFILE_SQUARE: - { - genNGon(params, 4,-0.375, 0, 1, split); - if (path_open) - { - addCap (LL_FACE_PATH_BEGIN); - } - - for (i = llfloor(begin * 4.f); i < llfloor(end * 4.f + .999f); i++) - { - addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, true); - } - - LLVector4a scale(1,1,4,1); - - for (i = 0; i <(S32) mProfile.size(); i++) - { - // Scale by 4 to generate proper tex coords. - mProfile[i].mul(scale); - llassert(mProfile[i].isFinite3()); - } - - if (hollow) - { - switch (params.getCurveType() & LL_PCODE_HOLE_MASK) - { - case LL_PCODE_HOLE_TRIANGLE: - // This offset is not correct, but we can't change it now... DK 11/17/04 - addHole(params, true, 3, -0.375f, hollow, 1.f, split); - break; - case LL_PCODE_HOLE_CIRCLE: - // TODO: Compute actual detail levels for cubes - addHole(params, false, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f); - break; - case LL_PCODE_HOLE_SAME: - case LL_PCODE_HOLE_SQUARE: - default: - addHole(params, true, 4, -0.375f, hollow, 1.f, split); - break; - } - } - - if (path_open) { - mFaces[0].mCount = mTotal; - } - } - break; - case LL_PCODE_PROFILE_ISOTRI: - case LL_PCODE_PROFILE_RIGHTTRI: - case LL_PCODE_PROFILE_EQUALTRI: - { - genNGon(params, 3,0, 0, 1, split); - LLVector4a scale(1,1,3,1); - for (i = 0; i <(S32) mProfile.size(); i++) - { - // Scale by 3 to generate proper tex coords. - mProfile[i].mul(scale); - llassert(mProfile[i].isFinite3()); - } - - if (path_open) - { - addCap(LL_FACE_PATH_BEGIN); - } - - for (i = llfloor(begin * 3.f); i < llfloor(end * 3.f + .999f); i++) - { - addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, true); - } - if (hollow) - { - // Swept triangles need smaller hollowness values, - // because the triangle doesn't fill the bounding box. - F32 triangle_hollow = hollow / 2.f; - - switch (params.getCurveType() & LL_PCODE_HOLE_MASK) - { - case LL_PCODE_HOLE_CIRCLE: - // TODO: Actually generate level of detail for triangles - addHole(params, false, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f); - break; - case LL_PCODE_HOLE_SQUARE: - addHole(params, true, 4, 0, triangle_hollow, 1.f, split); - break; - case LL_PCODE_HOLE_SAME: - case LL_PCODE_HOLE_TRIANGLE: - default: - addHole(params, true, 3, 0, triangle_hollow, 1.f, split); - break; - } - } - } - break; - case LL_PCODE_PROFILE_CIRCLE: - { - // If this has a square hollow, we should adjust the - // number of faces a bit so that the geometry lines up. - U8 hole_type=0; - F32 circle_detail = MIN_DETAIL_FACES * detail; - if (hollow) - { - hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK; - if (hole_type == LL_PCODE_HOLE_SQUARE) - { - // Snap to the next multiple of four sides, - // so that corners line up. - circle_detail = llceil(circle_detail / 4.0f) * 4.0f; - } - } - - S32 sides = (S32)circle_detail; - - if (is_sculpted) - sides = sculpt_size; - - genNGon(params, sides); - - if (path_open) - { - addCap (LL_FACE_PATH_BEGIN); - } - - if (mOpen && !hollow) - { - addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, false); - } - else - { - addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, false); - } - - if (hollow) - { - switch (hole_type) - { - case LL_PCODE_HOLE_SQUARE: - addHole(params, true, 4, 0, hollow, 1.f, split); - break; - case LL_PCODE_HOLE_TRIANGLE: - addHole(params, true, 3, 0, hollow, 1.f, split); - break; - case LL_PCODE_HOLE_CIRCLE: - case LL_PCODE_HOLE_SAME: - default: - addHole(params, true, circle_detail, 0, hollow, 1.f); - break; - } - } - } - break; - case LL_PCODE_PROFILE_CIRCLE_HALF: - { - // If this has a square hollow, we should adjust the - // number of faces a bit so that the geometry lines up. - U8 hole_type=0; - // Number of faces is cut in half because it's only a half-circle. - F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f; - if (hollow) - { - hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK; - if (hole_type == LL_PCODE_HOLE_SQUARE) - { - // Snap to the next multiple of four sides (div 2), - // so that corners line up. - circle_detail = llceil(circle_detail / 2.0f) * 2.0f; - } - } - genNGon(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f); - if (path_open) - { - addCap(LL_FACE_PATH_BEGIN); - } - if (mOpen && !params.getHollow()) - { - addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, false); - } - else - { - addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, false); - } - - if (hollow) - { - switch (hole_type) - { - case LL_PCODE_HOLE_SQUARE: - addHole(params, true, 2, 0.5f, hollow, 0.5f, split); - break; - case LL_PCODE_HOLE_TRIANGLE: - addHole(params, true, 3, 0.5f, hollow, 0.5f, split); - break; - case LL_PCODE_HOLE_CIRCLE: - case LL_PCODE_HOLE_SAME: - default: - addHole(params, false, circle_detail, 0.5f, hollow, 0.5f); - break; - } - } - - // Special case for openness of sphere - if ((params.getEnd() - params.getBegin()) < 1.f) - { - mOpen = true; - } - else if (!hollow) - { - mOpen = false; - mProfile.push_back(mProfile[0]); - mTotal++; - } - } - break; - default: - LL_ERRS() << "Unknown profile: getCurveType()=" << params.getCurveType() << LL_ENDL; - break; - }; - - if (path_open) - { - addCap(LL_FACE_PATH_END); // bottom - } - - if ( mOpen) // interior edge caps - { - addFace(mTotal-1, 2,0.5,LL_FACE_PROFILE_BEGIN, true); - - if (hollow) - { - addFace(mTotalOut-1, 2,0.5,LL_FACE_PROFILE_END, true); - } - else - { - addFace(mTotal-2, 2,0.5,LL_FACE_PROFILE_END, true); - } - } - - return true; -} - - - -bool LLProfileParams::importFile(LLFILE *fp) -{ - const S32 BUFSIZE = 16384; - char buffer[BUFSIZE]; /* Flawfinder: ignore */ - // *NOTE: changing the size or type of these buffers will require - // changing the sscanf below. - char keyword[256]; /* Flawfinder: ignore */ - char valuestr[256]; /* Flawfinder: ignore */ - keyword[0] = 0; - valuestr[0] = 0; - F32 tempF32; - U32 tempU32; - - while (!feof(fp)) - { - if (fgets(buffer, BUFSIZE, fp) == NULL) - { - buffer[0] = '\0'; - } - - sscanf( /* Flawfinder: ignore */ - buffer, - " %255s %255s", - keyword, valuestr); - if (!strcmp("{", keyword)) - { - continue; - } - if (!strcmp("}",keyword)) - { - break; - } - else if (!strcmp("curve", keyword)) - { - sscanf(valuestr,"%d",&tempU32); - setCurveType((U8) tempU32); - } - else if (!strcmp("begin",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setBegin(tempF32); - } - else if (!strcmp("end",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setEnd(tempF32); - } - else if (!strcmp("hollow",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setHollow(tempF32); - } - else - { - LL_WARNS() << "unknown keyword " << keyword << " in profile import" << LL_ENDL; - } - } - - return true; -} - - -bool LLProfileParams::exportFile(LLFILE *fp) const -{ - fprintf(fp,"\t\tprofile 0\n"); - fprintf(fp,"\t\t{\n"); - fprintf(fp,"\t\t\tcurve\t%d\n", getCurveType()); - fprintf(fp,"\t\t\tbegin\t%g\n", getBegin()); - fprintf(fp,"\t\t\tend\t%g\n", getEnd()); - fprintf(fp,"\t\t\thollow\t%g\n", getHollow()); - fprintf(fp, "\t\t}\n"); - return true; -} - - -bool LLProfileParams::importLegacyStream(std::istream& input_stream) -{ - const S32 BUFSIZE = 16384; - char buffer[BUFSIZE]; /* Flawfinder: ignore */ - // *NOTE: changing the size or type of these buffers will require - // changing the sscanf below. - char keyword[256]; /* Flawfinder: ignore */ - char valuestr[256]; /* Flawfinder: ignore */ - keyword[0] = 0; - valuestr[0] = 0; - F32 tempF32; - U32 tempU32; - - while (input_stream.good()) - { - input_stream.getline(buffer, BUFSIZE); - sscanf( /* Flawfinder: ignore */ - buffer, - " %255s %255s", - keyword, - valuestr); - if (!strcmp("{", keyword)) - { - continue; - } - if (!strcmp("}",keyword)) - { - break; - } - else if (!strcmp("curve", keyword)) - { - sscanf(valuestr,"%d",&tempU32); - setCurveType((U8) tempU32); - } - else if (!strcmp("begin",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setBegin(tempF32); - } - else if (!strcmp("end",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setEnd(tempF32); - } - else if (!strcmp("hollow",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setHollow(tempF32); - } - else - { - LL_WARNS() << "unknown keyword " << keyword << " in profile import" << LL_ENDL; - } - } - - return true; -} - - -bool LLProfileParams::exportLegacyStream(std::ostream& output_stream) const -{ - output_stream <<"\t\tprofile 0\n"; - output_stream <<"\t\t{\n"; - output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n"; - output_stream <<"\t\t\tbegin\t" << getBegin() << "\n"; - output_stream <<"\t\t\tend\t" << getEnd() << "\n"; - output_stream <<"\t\t\thollow\t" << getHollow() << "\n"; - output_stream << "\t\t}\n"; - return true; -} - -LLSD LLProfileParams::asLLSD() const -{ - LLSD sd; - - sd["curve"] = getCurveType(); - sd["begin"] = getBegin(); - sd["end"] = getEnd(); - sd["hollow"] = getHollow(); - return sd; -} - -bool LLProfileParams::fromLLSD(LLSD& sd) -{ - setCurveType(sd["curve"].asInteger()); - setBegin((F32)sd["begin"].asReal()); - setEnd((F32)sd["end"].asReal()); - setHollow((F32)sd["hollow"].asReal()); - return true; -} - -void LLProfileParams::copyParams(const LLProfileParams ¶ms) -{ - setCurveType(params.getCurveType()); - setBegin(params.getBegin()); - setEnd(params.getEnd()); - setHollow(params.getHollow()); -} - - -LLPath::~LLPath() -{ -} - -S32 LLPath::getNumNGonPoints(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale) -{ //this is basically LLPath::genNGon stripped down to only operations that influence the number of points added - S32 ret = 0; - - F32 step= 1.0f / sides; - F32 t = params.getBegin(); - ret = 1; - - t+=step; - - // Snap to a quantized parameter, so that cut does not - // affect most sample points. - t = ((S32)(t * sides)) / (F32)sides; - - // Run through the non-cut dependent points. - while (t < params.getEnd()) - { - ret++; - t+=step; - } - - ret++; - - return ret; -} - -void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - // Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane. - constexpr F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f }; - - F32 revolutions = params.getRevolutions(); - F32 skew = params.getSkew(); - F32 skew_mag = fabs(skew); - F32 hole_x = params.getScaleX() * (1.0f - skew_mag); - F32 hole_y = params.getScaleY(); - - // Calculate taper begin/end for x,y (Negative means taper the beginning) - F32 taper_x_begin = 1.0f; - F32 taper_x_end = 1.0f - params.getTaperX(); - F32 taper_y_begin = 1.0f; - F32 taper_y_end = 1.0f - params.getTaperY(); - - if ( taper_x_end > 1.0f ) - { - // Flip tapering. - taper_x_begin = 2.0f - taper_x_end; - taper_x_end = 1.0f; - } - if ( taper_y_end > 1.0f ) - { - // Flip tapering. - taper_y_begin = 2.0f - taper_y_end; - taper_y_end = 1.0f; - } - - // For spheres, the radius is usually zero. - F32 radius_start = 0.5f; - if (sides < 8) - { - radius_start = tableScale[sides]; - } - - // Scale the radius to take the hole size into account. - radius_start *= 1.0f - hole_y; - - // Now check the radius offset to calculate the start,end radius. (Negative means - // decrease the start radius instead). - F32 radius_end = radius_start; - F32 radius_offset = params.getRadiusOffset(); - if (radius_offset < 0.f) - { - radius_start *= 1.f + radius_offset; - } - else - { - radius_end *= 1.f - radius_offset; - } - - // Is the path NOT a closed loop? - mOpen = ( (params.getEnd()*end_scale - params.getBegin() < 1.0f) || - (skew_mag > 0.001f) || - (fabs(taper_x_end - taper_x_begin) > 0.001f) || - (fabs(taper_y_end - taper_y_begin) > 0.001f) || - (fabs(radius_end - radius_start) > 0.001f) ); - - F32 ang, c, s; - LLQuaternion twist, qang; - PathPt *pt; - LLVector3 path_axis (1.f, 0.f, 0.f); - //LLVector3 twist_axis(0.f, 0.f, 1.f); - F32 twist_begin = params.getTwistBegin() * twist_scale; - F32 twist_end = params.getTwist() * twist_scale; - - // We run through this once before the main loop, to make sure - // the path begins at the correct cut. - F32 step= 1.0f / sides; - F32 t = params.getBegin(); - pt = mPath.append(1); - ang = 2.0f*F_PI*revolutions * t; - s = sin(ang)*lerp(radius_start, radius_end, t); - c = cos(ang)*lerp(radius_start, radius_end, t); - - - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) - + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), - s); - pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), - hole_y * lerp(taper_y_begin, taper_y_end, t), - 0,1); - pt->mTexT = t; - - // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02 - twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1); - // Rotate the point around the circle's center. - qang.setQuat (ang,path_axis); - - LLMatrix3 rot(twist * qang); - - pt->mRot.loadu(rot); - - t+=step; - - // Snap to a quantized parameter, so that cut does not - // affect most sample points. - t = ((S32)(t * sides)) / (F32)sides; - - // Run through the non-cut dependent points. - while (t < params.getEnd()) - { - pt = mPath.append(1); - - ang = 2.0f*F_PI*revolutions * t; - c = cos(ang)*lerp(radius_start, radius_end, t); - s = sin(ang)*lerp(radius_start, radius_end, t); - - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) - + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), - s); - - pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), - hole_y * lerp(taper_y_begin, taper_y_end, t), - 0,1); - pt->mTexT = t; - - // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02 - twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1); - // Rotate the point around the circle's center. - qang.setQuat (ang,path_axis); - LLMatrix3 tmp(twist*qang); - pt->mRot.loadu(tmp); - - t+=step; - } - - // Make one final pass for the end cut. - t = params.getEnd(); - pt = mPath.append(1); - ang = 2.0f*F_PI*revolutions * t; - c = cos(ang)*lerp(radius_start, radius_end, t); - s = sin(ang)*lerp(radius_start, radius_end, t); - - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) - + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), - s); - pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), - hole_y * lerp(taper_y_begin, taper_y_end, t), - 0,1); - pt->mTexT = t; - - // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02 - twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1); - // Rotate the point around the circle's center. - qang.setQuat (ang,path_axis); - LLMatrix3 tmp(twist*qang); - pt->mRot.loadu(tmp); - - mTotal = mPath.size(); -} - -const LLVector2 LLPathParams::getBeginScale() const -{ - LLVector2 begin_scale(1.f, 1.f); - if (getScaleX() > 1) - { - begin_scale.mV[0] = 2-getScaleX(); - } - if (getScaleY() > 1) - { - begin_scale.mV[1] = 2-getScaleY(); - } - return begin_scale; -} - -const LLVector2 LLPathParams::getEndScale() const -{ - LLVector2 end_scale(1.f, 1.f); - if (getScaleX() < 1) - { - end_scale.mV[0] = getScaleX(); - } - if (getScaleY() < 1) - { - end_scale.mV[1] = getScaleY(); - } - return end_scale; -} - -S32 LLPath::getNumPoints(const LLPathParams& params, F32 detail) -{ // this is basically LLPath::generate stripped down to only the operations that influence the number of points - if (detail < MIN_LOD) - { - detail = MIN_LOD; - } - - S32 np = 2; // hardcode for line - - // Is this 0xf0 mask really necessary? DK 03/02/05 - - switch (params.getCurveType() & 0xf0) - { - default: - case LL_PCODE_PATH_LINE: - { - // Take the begin/end twist into account for detail. - np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2; - } - break; - - case LL_PCODE_PATH_CIRCLE: - { - // Increase the detail as the revolutions and twist increase. - F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist()); - - S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions()); - - np = sides; - } - break; - - case LL_PCODE_PATH_CIRCLE2: - { - //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f); - np = getNumNGonPoints(params, llfloor(MIN_DETAIL_FACES * detail)); - } - break; - - case LL_PCODE_PATH_TEST: - - np = 5; - break; - }; - - return np; -} - -bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split, - bool is_sculpted, S32 sculpt_size) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - if ((!mDirty) && (!is_sculpted)) - { - return false; - } - - if (detail < MIN_LOD) - { - LL_INFOS() << "Generating path with LOD < MIN! Clamping to 1" << LL_ENDL; - detail = MIN_LOD; - } - - mDirty = false; - S32 np = 2; // hardcode for line - - mPath.resize(0); - mOpen = true; - - // Is this 0xf0 mask really necessary? DK 03/02/05 - switch (params.getCurveType() & 0xf0) - { - default: - case LL_PCODE_PATH_LINE: - { - // Take the begin/end twist into account for detail. - np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2; - if (np < split+2) - { - np = split+2; - } - - mStep = 1.0f / (np-1); - - mPath.resize(np); - - LLVector2 start_scale = params.getBeginScale(); - LLVector2 end_scale = params.getEndScale(); - - for (S32 i=0;i<np;i++) - { - F32 t = lerp(params.getBegin(),params.getEnd(),(F32)i * mStep); - mPath[i].mPos.set(lerp(0,params.getShear().mV[0],t), - lerp(0,params.getShear().mV[1],t), - t - 0.5f); - LLQuaternion quat; - quat.setQuat(lerp(F_PI * params.getTwistBegin(),F_PI * params.getTwist(),t),0,0,1); - LLMatrix3 tmp(quat); - mPath[i].mRot.loadu(tmp); - mPath[i].mScale.set(lerp(start_scale.mV[0],end_scale.mV[0],t), - lerp(start_scale.mV[1],end_scale.mV[1],t), - 0,1); - mPath[i].mTexT = t; - } - } - break; - - case LL_PCODE_PATH_CIRCLE: - { - // Increase the detail as the revolutions and twist increase. - F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist()); - - S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions()); - - if (is_sculpted) - sides = llmax(sculpt_size, 1); - - if (0 < sides) - genNGon(params, sides); - } - break; - - case LL_PCODE_PATH_CIRCLE2: - { - if (params.getEnd() - params.getBegin() >= 0.99f && - params.getScaleX() >= .99f) - { - mOpen = false; - } - - //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f); - genNGon(params, llfloor(MIN_DETAIL_FACES * detail)); - - F32 toggle = 0.5f; - for (S32 i=0;i<(S32)mPath.size();i++) - { - mPath[i].mPos.getF32ptr()[0] = toggle; - if (toggle == 0.5f) - toggle = -0.5f; - else - toggle = 0.5f; - } - } - - break; - - case LL_PCODE_PATH_TEST: - - np = 5; - mStep = 1.0f / (np-1); - - mPath.resize(np); - - for (S32 i=0;i<np;i++) - { - F32 t = (F32)i * mStep; - mPath[i].mPos.set(0, - lerp(0, -sin(F_PI*params.getTwist()*t)*0.5f,t), - lerp(-0.5f, cos(F_PI*params.getTwist()*t)*0.5f,t)); - mPath[i].mScale.set(lerp(1,params.getScale().mV[0],t), - lerp(1,params.getScale().mV[1],t), 0,1); - mPath[i].mTexT = t; - LLQuaternion quat; - quat.setQuat(F_PI * params.getTwist() * t,1,0,0); - LLMatrix3 tmp(quat); - mPath[i].mRot.loadu(tmp); - } - - break; - }; - - if (params.getTwist() != params.getTwistBegin()) mOpen = true; - - //if ((int(fabsf(params.getTwist() - params.getTwistBegin())*100))%100 != 0) { - // mOpen = true; - //} - - return true; -} - -bool LLDynamicPath::generate(const LLPathParams& params, F32 detail, S32 split, - bool is_sculpted, S32 sculpt_size) -{ - mOpen = true; // Draw end caps - if (getPathLength() == 0) - { - // Path hasn't been generated yet. - // Some algorithms later assume at least TWO path points. - resizePath(2); - LLQuaternion quat; - quat.setQuat(0,0,0); - LLMatrix3 tmp(quat); - - for (U32 i = 0; i < 2; i++) - { - mPath[i].mPos.set(0, 0, 0); - mPath[i].mRot.loadu(tmp); - mPath[i].mScale.set(1, 1, 0, 1); - mPath[i].mTexT = 0; - } - } - - return true; -} - - -bool LLPathParams::importFile(LLFILE *fp) -{ - const S32 BUFSIZE = 16384; - char buffer[BUFSIZE]; /* Flawfinder: ignore */ - // *NOTE: changing the size or type of these buffers will require - // changing the sscanf below. - char keyword[256]; /* Flawfinder: ignore */ - char valuestr[256]; /* Flawfinder: ignore */ - keyword[0] = 0; - valuestr[0] = 0; - - F32 tempF32; - F32 x, y; - U32 tempU32; - - while (!feof(fp)) - { - if (fgets(buffer, BUFSIZE, fp) == NULL) - { - buffer[0] = '\0'; - } - - sscanf( /* Flawfinder: ignore */ - buffer, - " %255s %255s", - keyword, valuestr); - if (!strcmp("{", keyword)) - { - continue; - } - if (!strcmp("}",keyword)) - { - break; - } - else if (!strcmp("curve", keyword)) - { - sscanf(valuestr,"%d",&tempU32); - setCurveType((U8) tempU32); - } - else if (!strcmp("begin",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setBegin(tempF32); - } - else if (!strcmp("end",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setEnd(tempF32); - } - else if (!strcmp("scale",keyword)) - { - // Legacy for one dimensional scale per path - sscanf(valuestr,"%g",&tempF32); - setScale(tempF32, tempF32); - } - else if (!strcmp("scale_x", keyword)) - { - sscanf(valuestr, "%g", &x); - setScaleX(x); - } - else if (!strcmp("scale_y", keyword)) - { - sscanf(valuestr, "%g", &y); - setScaleY(y); - } - else if (!strcmp("shear_x", keyword)) - { - sscanf(valuestr, "%g", &x); - setShearX(x); - } - else if (!strcmp("shear_y", keyword)) - { - sscanf(valuestr, "%g", &y); - setShearY(y); - } - else if (!strcmp("twist",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setTwist(tempF32); - } - else if (!strcmp("twist_begin", keyword)) - { - sscanf(valuestr, "%g", &y); - setTwistBegin(y); - } - else if (!strcmp("radius_offset", keyword)) - { - sscanf(valuestr, "%g", &y); - setRadiusOffset(y); - } - else if (!strcmp("taper_x", keyword)) - { - sscanf(valuestr, "%g", &y); - setTaperX(y); - } - else if (!strcmp("taper_y", keyword)) - { - sscanf(valuestr, "%g", &y); - setTaperY(y); - } - else if (!strcmp("revolutions", keyword)) - { - sscanf(valuestr, "%g", &y); - setRevolutions(y); - } - else if (!strcmp("skew", keyword)) - { - sscanf(valuestr, "%g", &y); - setSkew(y); - } - else - { - LL_WARNS() << "unknown keyword " << " in path import" << LL_ENDL; - } - } - return true; -} - - -bool LLPathParams::exportFile(LLFILE *fp) const -{ - fprintf(fp, "\t\tpath 0\n"); - fprintf(fp, "\t\t{\n"); - fprintf(fp, "\t\t\tcurve\t%d\n", getCurveType()); - fprintf(fp, "\t\t\tbegin\t%g\n", getBegin()); - fprintf(fp, "\t\t\tend\t%g\n", getEnd()); - fprintf(fp, "\t\t\tscale_x\t%g\n", getScaleX() ); - fprintf(fp, "\t\t\tscale_y\t%g\n", getScaleY() ); - fprintf(fp, "\t\t\tshear_x\t%g\n", getShearX() ); - fprintf(fp, "\t\t\tshear_y\t%g\n", getShearY() ); - fprintf(fp,"\t\t\ttwist\t%g\n", getTwist()); - - fprintf(fp,"\t\t\ttwist_begin\t%g\n", getTwistBegin()); - fprintf(fp,"\t\t\tradius_offset\t%g\n", getRadiusOffset()); - fprintf(fp,"\t\t\ttaper_x\t%g\n", getTaperX()); - fprintf(fp,"\t\t\ttaper_y\t%g\n", getTaperY()); - fprintf(fp,"\t\t\trevolutions\t%g\n", getRevolutions()); - fprintf(fp,"\t\t\tskew\t%g\n", getSkew()); - - fprintf(fp, "\t\t}\n"); - return true; -} - - -bool LLPathParams::importLegacyStream(std::istream& input_stream) -{ - const S32 BUFSIZE = 16384; - char buffer[BUFSIZE]; /* Flawfinder: ignore */ - // *NOTE: changing the size or type of these buffers will require - // changing the sscanf below. - char keyword[256]; /* Flawfinder: ignore */ - char valuestr[256]; /* Flawfinder: ignore */ - keyword[0] = 0; - valuestr[0] = 0; - - F32 tempF32; - F32 x, y; - U32 tempU32; - - while (input_stream.good()) - { - input_stream.getline(buffer, BUFSIZE); - sscanf( /* Flawfinder: ignore */ - buffer, - " %255s %255s", - keyword, valuestr); - if (!strcmp("{", keyword)) - { - continue; - } - if (!strcmp("}",keyword)) - { - break; - } - else if (!strcmp("curve", keyword)) - { - sscanf(valuestr,"%d",&tempU32); - setCurveType((U8) tempU32); - } - else if (!strcmp("begin",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setBegin(tempF32); - } - else if (!strcmp("end",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setEnd(tempF32); - } - else if (!strcmp("scale",keyword)) - { - // Legacy for one dimensional scale per path - sscanf(valuestr,"%g",&tempF32); - setScale(tempF32, tempF32); - } - else if (!strcmp("scale_x", keyword)) - { - sscanf(valuestr, "%g", &x); - setScaleX(x); - } - else if (!strcmp("scale_y", keyword)) - { - sscanf(valuestr, "%g", &y); - setScaleY(y); - } - else if (!strcmp("shear_x", keyword)) - { - sscanf(valuestr, "%g", &x); - setShearX(x); - } - else if (!strcmp("shear_y", keyword)) - { - sscanf(valuestr, "%g", &y); - setShearY(y); - } - else if (!strcmp("twist",keyword)) - { - sscanf(valuestr,"%g",&tempF32); - setTwist(tempF32); - } - else if (!strcmp("twist_begin", keyword)) - { - sscanf(valuestr, "%g", &y); - setTwistBegin(y); - } - else if (!strcmp("radius_offset", keyword)) - { - sscanf(valuestr, "%g", &y); - setRadiusOffset(y); - } - else if (!strcmp("taper_x", keyword)) - { - sscanf(valuestr, "%g", &y); - setTaperX(y); - } - else if (!strcmp("taper_y", keyword)) - { - sscanf(valuestr, "%g", &y); - setTaperY(y); - } - else if (!strcmp("revolutions", keyword)) - { - sscanf(valuestr, "%g", &y); - setRevolutions(y); - } - else if (!strcmp("skew", keyword)) - { - sscanf(valuestr, "%g", &y); - setSkew(y); - } - else - { - LL_WARNS() << "unknown keyword " << " in path import" << LL_ENDL; - } - } - return true; -} - - -bool LLPathParams::exportLegacyStream(std::ostream& output_stream) const -{ - output_stream << "\t\tpath 0\n"; - output_stream << "\t\t{\n"; - output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n"; - output_stream << "\t\t\tbegin\t" << getBegin() << "\n"; - output_stream << "\t\t\tend\t" << getEnd() << "\n"; - output_stream << "\t\t\tscale_x\t" << getScaleX() << "\n"; - output_stream << "\t\t\tscale_y\t" << getScaleY() << "\n"; - output_stream << "\t\t\tshear_x\t" << getShearX() << "\n"; - output_stream << "\t\t\tshear_y\t" << getShearY() << "\n"; - output_stream <<"\t\t\ttwist\t" << getTwist() << "\n"; - - output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n"; - output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n"; - output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n"; - output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n"; - output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n"; - output_stream <<"\t\t\tskew\t" << getSkew() << "\n"; - - output_stream << "\t\t}\n"; - return true; -} - -LLSD LLPathParams::asLLSD() const -{ - LLSD sd = LLSD(); - sd["curve"] = getCurveType(); - sd["begin"] = getBegin(); - sd["end"] = getEnd(); - sd["scale_x"] = getScaleX(); - sd["scale_y"] = getScaleY(); - sd["shear_x"] = getShearX(); - sd["shear_y"] = getShearY(); - sd["twist"] = getTwist(); - sd["twist_begin"] = getTwistBegin(); - sd["radius_offset"] = getRadiusOffset(); - sd["taper_x"] = getTaperX(); - sd["taper_y"] = getTaperY(); - sd["revolutions"] = getRevolutions(); - sd["skew"] = getSkew(); - - return sd; -} - -bool LLPathParams::fromLLSD(LLSD& sd) -{ - setCurveType(sd["curve"].asInteger()); - setBegin((F32)sd["begin"].asReal()); - setEnd((F32)sd["end"].asReal()); - setScaleX((F32)sd["scale_x"].asReal()); - setScaleY((F32)sd["scale_y"].asReal()); - setShearX((F32)sd["shear_x"].asReal()); - setShearY((F32)sd["shear_y"].asReal()); - setTwist((F32)sd["twist"].asReal()); - setTwistBegin((F32)sd["twist_begin"].asReal()); - setRadiusOffset((F32)sd["radius_offset"].asReal()); - setTaperX((F32)sd["taper_x"].asReal()); - setTaperY((F32)sd["taper_y"].asReal()); - setRevolutions((F32)sd["revolutions"].asReal()); - setSkew((F32)sd["skew"].asReal()); - return true; -} - -void LLPathParams::copyParams(const LLPathParams ¶ms) -{ - setCurveType(params.getCurveType()); - setBegin(params.getBegin()); - setEnd(params.getEnd()); - setScale(params.getScaleX(), params.getScaleY() ); - setShear(params.getShearX(), params.getShearY() ); - setTwist(params.getTwist()); - setTwistBegin(params.getTwistBegin()); - setRadiusOffset(params.getRadiusOffset()); - setTaper( params.getTaperX(), params.getTaperY() ); - setRevolutions(params.getRevolutions()); - setSkew(params.getSkew()); -} - -LLProfile::~LLProfile() -{ -} - - -S32 LLVolume::sNumMeshPoints = 0; - -LLVolume::LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool generate_single_face, const bool is_unique) - : mParams(params) -{ - mUnique = is_unique; - mFaceMask = 0x0; - mDetail = detail; - mSculptLevel = -2; - mSurfaceArea = 1.f; //only calculated for sculpts, defaults to 1 for all other prims - mIsMeshAssetLoaded = false; - mIsMeshAssetUnavaliable = false; - mLODScaleBias.setVec(1,1,1); - mHullPoints = nullptr; - mHullIndices = nullptr; - mNumHullPoints = 0; - mNumHullIndices = 0; - - // set defaults - if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE) - { - mPathp = new LLDynamicPath(); - } - else - { - mPathp = new LLPath(); - } - mProfilep = new LLProfile(); - - mGenerateSingleFace = generate_single_face; - - generate(); - - if ((mParams.getSculptID().isNull() && mParams.getSculptType() == LL_SCULPT_TYPE_NONE) || mParams.getSculptType() == LL_SCULPT_TYPE_MESH) - { - createVolumeFaces(); - } -} - -void LLVolume::resizePath(S32 length) -{ - mPathp->resizePath(length); - mVolumeFaces.clear(); - setDirty(); -} - -void LLVolume::regen() -{ - generate(); - createVolumeFaces(); -} - -void LLVolume::genTangents(S32 face) -{ - // generate legacy tangents for the specified face - llassert(!isMeshAssetLoaded() || mVolumeFaces[face].mTangents != nullptr); // if this is a complete mesh asset, we should already have tangents - mVolumeFaces[face].createTangents(); -} - -LLVolume::~LLVolume() -{ - sNumMeshPoints -= mMesh.size(); - delete mPathp; - - delete mProfilep; - - mPathp = NULL; - mProfilep = NULL; - mVolumeFaces.clear(); - - ll_aligned_free_16(mHullPoints); - mHullPoints = NULL; - ll_aligned_free_16(mHullIndices); - mHullIndices = NULL; -} - -bool LLVolume::generate() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - LL_CHECK_MEMORY - llassert_always(mProfilep); - - //Added 10.03.05 Dave Parks - // Split is a parameter to LLProfile::generate that tesselates edges on the profile - // to prevent lighting and texture interpolation errors on triangles that are - // stretched due to twisting or scaling on the path. - S32 split = (S32) ((mDetail)*0.66f); - - if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_LINE && - (mParams.getPathParams().getScale().mV[0] != 1.0f || - mParams.getPathParams().getScale().mV[1] != 1.0f) && - (mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_SQUARE || - mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_ISOTRI || - mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_EQUALTRI || - mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_RIGHTTRI)) - { - split = 0; - } - - mLODScaleBias.setVec(0.5f, 0.5f, 0.5f); - - F32 profile_detail = mDetail; - F32 path_detail = mDetail; - - if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH) - { - U8 path_type = mParams.getPathParams().getCurveType(); - U8 profile_type = mParams.getProfileParams().getCurveType(); - if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE) - { - //cylinders don't care about Z-Axis - mLODScaleBias.setVec(0.6f, 0.6f, 0.0f); - } - else if (path_type == LL_PCODE_PATH_CIRCLE) - { - mLODScaleBias.setVec(0.6f, 0.6f, 0.6f); - } - } - - bool regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split); - bool regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split); - - if (regenPath || regenProf ) - { - S32 sizeS = mPathp->mPath.size(); - S32 sizeT = mProfilep->mProfile.size(); - - sNumMeshPoints -= mMesh.size(); - mMesh.resize(sizeT * sizeS); - sNumMeshPoints += mMesh.size(); - - //generate vertex positions - - // Run along the path. - LLVector4a* dst = mMesh.mArray; - - for (S32 s = 0; s < sizeS; ++s) - { - F32* scale = mPathp->mPath[s].mScale.getF32ptr(); - - F32 sc [] = - { scale[0], 0, 0, 0, - 0, scale[1], 0, 0, - 0, 0, scale[2], 0, - 0, 0, 0, 1 }; - - LLMatrix4 rot((F32*) mPathp->mPath[s].mRot.mMatrix); - LLMatrix4 scale_mat(sc); - - scale_mat *= rot; - - LLMatrix4a rot_mat; - rot_mat.loadu(scale_mat); - - LLVector4a* profile = mProfilep->mProfile.mArray; - LLVector4a* end_profile = profile+sizeT; - LLVector4a offset = mPathp->mPath[s].mPos; - - // hack to work around MAINT-5660 for debug until we can suss out - // what is wrong with the path generated that inserts NaNs... - if (!offset.isFinite3()) - { - offset.clear(); - } - - LLVector4a tmp; - - // Run along the profile. - while (profile < end_profile) - { - rot_mat.rotate(*profile++, tmp); - dst->setAdd(tmp,offset); - ++dst; - } - } - - for (std::vector<LLProfile::Face>::iterator iter = mProfilep->mFaces.begin(); - iter != mProfilep->mFaces.end(); ++iter) - { - LLFaceID id = iter->mFaceID; - mFaceMask |= id; - } - LL_CHECK_MEMORY - return true; - } - - LL_CHECK_MEMORY - return false; -} - -void LLVolumeFace::VertexData::init() -{ - if (!mData) - { - mData = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*2); - } -} - -LLVolumeFace::VertexData::VertexData() -{ - mData = NULL; - init(); -} - -LLVolumeFace::VertexData::VertexData(const VertexData& rhs) -{ - mData = NULL; - *this = rhs; -} - -const LLVolumeFace::VertexData& LLVolumeFace::VertexData::operator=(const LLVolumeFace::VertexData& rhs) -{ - if (this != &rhs) - { - init(); - LLVector4a::memcpyNonAliased16((F32*) mData, (F32*) rhs.mData, 2*sizeof(LLVector4a)); - mTexCoord = rhs.mTexCoord; - } - return *this; -} - -LLVolumeFace::VertexData::~VertexData() -{ - ll_aligned_free_16(mData); - mData = NULL; -} - -LLVector4a& LLVolumeFace::VertexData::getPosition() -{ - return mData[POSITION]; -} - -LLVector4a& LLVolumeFace::VertexData::getNormal() -{ - return mData[NORMAL]; -} - -const LLVector4a& LLVolumeFace::VertexData::getPosition() const -{ - return mData[POSITION]; -} - -const LLVector4a& LLVolumeFace::VertexData::getNormal() const -{ - return mData[NORMAL]; -} - - -void LLVolumeFace::VertexData::setPosition(const LLVector4a& pos) -{ - mData[POSITION] = pos; -} - -void LLVolumeFace::VertexData::setNormal(const LLVector4a& norm) -{ - mData[NORMAL] = norm; -} - -bool LLVolumeFace::VertexData::operator<(const LLVolumeFace::VertexData& rhs)const -{ - const F32* lp = this->getPosition().getF32ptr(); - const F32* rp = rhs.getPosition().getF32ptr(); - - if (lp[0] != rp[0]) - { - return lp[0] < rp[0]; - } - - if (rp[1] != lp[1]) - { - return lp[1] < rp[1]; - } - - if (rp[2] != lp[2]) - { - return lp[2] < rp[2]; - } - - lp = getNormal().getF32ptr(); - rp = rhs.getNormal().getF32ptr(); - - if (lp[0] != rp[0]) - { - return lp[0] < rp[0]; - } - - if (rp[1] != lp[1]) - { - return lp[1] < rp[1]; - } - - if (rp[2] != lp[2]) - { - return lp[2] < rp[2]; - } - - if (mTexCoord.mV[0] != rhs.mTexCoord.mV[0]) - { - return mTexCoord.mV[0] < rhs.mTexCoord.mV[0]; - } - - return mTexCoord.mV[1] < rhs.mTexCoord.mV[1]; -} - -bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)const -{ - return mData[POSITION].equals3(rhs.getPosition()) && - mData[NORMAL].equals3(rhs.getNormal()) && - mTexCoord == rhs.mTexCoord; -} - -bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const -{ - bool retval = false; - - const F32 epsilon = 0.00001f; - - if (rhs.mData[POSITION].equals3(mData[POSITION], epsilon) && - fabs(rhs.mTexCoord[0]-mTexCoord[0]) < epsilon && - fabs(rhs.mTexCoord[1]-mTexCoord[1]) < epsilon) - { - if (angle_cutoff > 1.f) - { - retval = (mData[NORMAL].equals3(rhs.mData[NORMAL], epsilon)); - } - else - { - F32 cur_angle = rhs.mData[NORMAL].dot3(mData[NORMAL]).getF32(); - retval = cur_angle > angle_cutoff; - } - } - - return retval; -} - -bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - //input stream is now pointing at a zlib compressed block of LLSD - //decompress block - LLSD mdl; - U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, is, size); - if (uzip_result != LLUZipHelper::ZR_OK) - { - LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL; - return false; - } - return unpackVolumeFacesInternal(mdl); -} - -bool LLVolume::unpackVolumeFaces(U8* in_data, S32 size) -{ - //input data is now pointing at a zlib compressed block of LLSD - //decompress block - LLSD mdl; - U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, in_data, size); - if (uzip_result != LLUZipHelper::ZR_OK) - { - LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL; - return false; - } - return unpackVolumeFacesInternal(mdl); -} - -bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) -{ - { - U32 face_count = mdl.size(); - - if (face_count == 0) - { //no faces unpacked, treat as failed decode - LL_WARNS() << "found no faces!" << LL_ENDL; - return false; - } - - mVolumeFaces.resize(face_count); - - for (size_t i = 0; i < face_count; ++i) - { - LLVolumeFace& face = mVolumeFaces[i]; - - if (mdl[i].has("NoGeometry")) - { //face has no geometry, continue - face.resizeIndices(3); - face.resizeVertices(1); - face.mPositions->clear(); - face.mNormals->clear(); - face.mTexCoords->setZero(); - memset(face.mIndices, 0, sizeof(U16)*3); - continue; - } - - LLSD::Binary pos = mdl[i]["Position"]; - LLSD::Binary norm = mdl[i]["Normal"]; - LLSD::Binary tangent = mdl[i]["Tangent"]; - LLSD::Binary tc = mdl[i]["TexCoord0"]; - LLSD::Binary idx = mdl[i]["TriangleList"]; - - //copy out indices - S32 num_indices = idx.size() / 2; - const S32 indices_to_discard = num_indices % 3; - if (indices_to_discard > 0) - { - // Invalid number of triangle indices - LL_WARNS() << "Incomplete triangle discarded from face! Indices count " << num_indices << " was not divisible by 3. face index: " << i << " Total: " << face_count << LL_ENDL; - num_indices -= indices_to_discard; - } - face.resizeIndices(num_indices); - - if (num_indices > 2 && !face.mIndices) - { - LL_WARNS() << "Failed to allocate " << num_indices << " indices for face index: " << i << " Total: " << face_count << LL_ENDL; - continue; - } - - if (idx.empty() || face.mNumIndices < 3) - { //why is there an empty index list? - LL_WARNS() << "Empty face present! Face index: " << i << " Total: " << face_count << LL_ENDL; - continue; - } - - U16* indices = (U16*) &(idx[0]); - for (U32 j = 0; j < num_indices; ++j) - { - face.mIndices[j] = indices[j]; - } - - //copy out vertices - U32 num_verts = pos.size()/(3*2); - face.resizeVertices(num_verts); - - if (num_verts > 0 && !face.mPositions) - { - LL_WARNS() << "Failed to allocate " << num_verts << " vertices for face index: " << i << " Total: " << face_count << LL_ENDL; - face.resizeIndices(0); - continue; - } - - LLVector3 minp; - LLVector3 maxp; - LLVector2 min_tc; - LLVector2 max_tc; - - minp.setValue(mdl[i]["PositionDomain"]["Min"]); - maxp.setValue(mdl[i]["PositionDomain"]["Max"]); - LLVector4a min_pos, max_pos; - min_pos.load3(minp.mV); - max_pos.load3(maxp.mV); - - min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]); - max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]); - - //unpack normalized scale/translation - if (mdl[i].has("NormalizedScale")) - { - face.mNormalizedScale.setValue(mdl[i]["NormalizedScale"]); - } - else - { - face.mNormalizedScale.set(1, 1, 1); - } - - LLVector4a pos_range; - pos_range.setSub(max_pos, min_pos); - LLVector2 tc_range2 = max_tc - min_tc; - - LLVector4a tc_range; - tc_range.set(tc_range2[0], tc_range2[1], tc_range2[0], tc_range2[1]); - LLVector4a min_tc4(min_tc[0], min_tc[1], min_tc[0], min_tc[1]); - - LLVector4a* pos_out = face.mPositions; - LLVector4a* norm_out = face.mNormals; - LLVector4a* tc_out = (LLVector4a*) face.mTexCoords; - - { - U16* v = (U16*) &(pos[0]); - for (U32 j = 0; j < num_verts; ++j) - { - pos_out->set((F32) v[0], (F32) v[1], (F32) v[2]); - pos_out->div(65535.f); - pos_out->mul(pos_range); - pos_out->add(min_pos); - pos_out++; - v += 3; - } - - } - - { - if (!norm.empty()) - { - U16* n = (U16*) &(norm[0]); - for (U32 j = 0; j < num_verts; ++j) - { - norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]); - norm_out->div(65535.f); - norm_out->mul(2.f); - norm_out->sub(1.f); - norm_out++; - n += 3; - } - } - else - { - for (U32 j = 0; j < num_verts; ++j) - { - norm_out->clear(); - norm_out++; // or just norm_out[j].clear(); - } - } - } - -#if 0 // keep this code for now in case we decide to add support for on-the-wire tangents - { - if (!tangent.empty()) - { - face.allocateTangents(face.mNumVertices); - U16* t = (U16*)&(tangent[0]); - - // NOTE: tangents coming from the asset may not be mikkt space, but they should always be used by the GLTF shaders to - // maintain compliance with the GLTF spec - LLVector4a* t_out = face.mTangents; - - for (U32 j = 0; j < num_verts; ++j) - { - t_out->set((F32)t[0], (F32)t[1], (F32)t[2], (F32) t[3]); - t_out->div(65535.f); - t_out->mul(2.f); - t_out->sub(1.f); - - F32* tp = t_out->getF32ptr(); - tp[3] = tp[3] < 0.f ? -1.f : 1.f; - - t_out++; - t += 4; - } - } - } -#endif - - { - if (!tc.empty()) - { - U16* t = (U16*) &(tc[0]); - for (U32 j = 0; j < num_verts; j+=2) - { - if (j < num_verts-1) - { - tc_out->set((F32) t[0], (F32) t[1], (F32) t[2], (F32) t[3]); - } - else - { - tc_out->set((F32) t[0], (F32) t[1], 0.f, 0.f); - } - - t += 4; - - tc_out->div(65535.f); - tc_out->mul(tc_range); - tc_out->add(min_tc4); - - tc_out++; - } - } - else - { - for (U32 j = 0; j < num_verts; j += 2) - { - tc_out->clear(); - tc_out++; - } - } - } - - if (mdl[i].has("Weights")) - { - face.allocateWeights(num_verts); - if (!face.mWeights && num_verts) - { - LL_WARNS() << "Failed to allocate " << num_verts << " weights for face index: " << i << " Total: " << face_count << LL_ENDL; - face.resizeIndices(0); - face.resizeVertices(0); - continue; - } - - LLSD::Binary weights = mdl[i]["Weights"]; - - U32 idx = 0; - - U32 cur_vertex = 0; - while (idx < weights.size() && cur_vertex < num_verts) - { - const U8 END_INFLUENCES = 0xFF; - U8 joint = weights[idx++]; - - U32 cur_influence = 0; - LLVector4 wght(0,0,0,0); - U32 joints[4] = {0,0,0,0}; - LLVector4 joints_with_weights(0,0,0,0); - - while (joint != END_INFLUENCES && idx < weights.size()) - { - U16 influence = weights[idx++]; - influence |= ((U16) weights[idx++] << 8); - - F32 w = llclamp((F32) influence / 65535.f, 0.001f, 0.999f); - wght.mV[cur_influence] = w; - joints[cur_influence] = joint; - cur_influence++; - - if (cur_influence >= 4) - { - joint = END_INFLUENCES; - } - else - { - joint = weights[idx++]; - } - } - F32 wsum = wght.mV[VX] + wght.mV[VY] + wght.mV[VZ] + wght.mV[VW]; - if (wsum <= 0.f) - { - wght = LLVector4(0.999f,0.f,0.f,0.f); - } - for (U32 k=0; k<4; k++) - { - F32 f_combined = (F32) joints[k] + wght[k]; - joints_with_weights[k] = f_combined; - // Any weights we added above should wind up non-zero and applied to a specific bone. - // A failure here would indicate a floating point precision error in the math. - llassert((k >= cur_influence) || (f_combined - S32(f_combined) > 0.0f)); - } - face.mWeights[cur_vertex].loadua(joints_with_weights.mV); - - cur_vertex++; - } - - if (cur_vertex != num_verts || idx != weights.size()) - { - LL_WARNS() << "Vertex weight count does not match vertex count!" << LL_ENDL; - } - - } - - // modifier flags? - bool do_mirror = (mParams.getSculptType() & LL_SCULPT_FLAG_MIRROR); - bool do_invert = (mParams.getSculptType() &LL_SCULPT_FLAG_INVERT); - - - // translate to actions: - bool do_reflect_x = false; - bool do_reverse_triangles = false; - bool do_invert_normals = false; - - if (do_mirror) - { - do_reflect_x = true; - do_reverse_triangles = !do_reverse_triangles; - } - - if (do_invert) - { - do_invert_normals = true; - do_reverse_triangles = !do_reverse_triangles; - } - - // now do the work - - if (do_reflect_x) - { - LLVector4a* p = (LLVector4a*) face.mPositions; - LLVector4a* n = (LLVector4a*) face.mNormals; - - for (S32 i = 0; i < face.mNumVertices; i++) - { - p[i].mul(-1.0f); - n[i].mul(-1.0f); - } - } - - if (do_invert_normals) - { - LLVector4a* n = (LLVector4a*) face.mNormals; - - for (S32 i = 0; i < face.mNumVertices; i++) - { - n[i].mul(-1.0f); - } - } - - if (do_reverse_triangles) - { - for (U32 j = 0; j < face.mNumIndices; j += 3) - { - // swap the 2nd and 3rd index - S32 swap = face.mIndices[j+1]; - face.mIndices[j+1] = face.mIndices[j+2]; - face.mIndices[j+2] = swap; - } - } - - //calculate bounding box - // VFExtents change - LLVector4a& min = face.mExtents[0]; - LLVector4a& max = face.mExtents[1]; - - if (face.mNumVertices < 3) - { //empty face, use a dummy 1cm (at 1m scale) bounding box - min.splat(-0.005f); - max.splat(0.005f); - } - else - { - min = max = face.mPositions[0]; - - for (S32 i = 1; i < face.mNumVertices; ++i) - { - min.setMin(min, face.mPositions[i]); - max.setMax(max, face.mPositions[i]); - } - - if (face.mTexCoords) - { - LLVector2& min_tc = face.mTexCoordExtents[0]; - LLVector2& max_tc = face.mTexCoordExtents[1]; - - min_tc = face.mTexCoords[0]; - max_tc = face.mTexCoords[0]; - - for (U32 j = 1; j < face.mNumVertices; ++j) - { - update_min_max(min_tc, max_tc, face.mTexCoords[j]); - } - } - else - { - face.mTexCoordExtents[0].set(0,0); - face.mTexCoordExtents[1].set(1,1); - } - } - } - } - - if (!cacheOptimize(true)) - { - // Out of memory? - LL_WARNS() << "Failed to optimize!" << LL_ENDL; - mVolumeFaces.clear(); - return false; - } - - mSculptLevel = 0; // success! - - return true; -} - - -bool LLVolume::isMeshAssetLoaded() -{ - return mIsMeshAssetLoaded; -} - -void LLVolume::setMeshAssetLoaded(bool loaded) -{ - mIsMeshAssetLoaded = loaded; - if (loaded) - { - mIsMeshAssetUnavaliable = false; - } -} - -void LLVolume::setMeshAssetUnavaliable(bool unavaliable) -{ - // Don't set it if at least one lod loaded - if (!mIsMeshAssetLoaded) - { - mIsMeshAssetUnavaliable = unavaliable; - } -} - -bool LLVolume::isMeshAssetUnavaliable() -{ - return mIsMeshAssetUnavaliable; -} - -void LLVolume::copyFacesTo(std::vector<LLVolumeFace> &faces) const -{ - faces = mVolumeFaces; -} - -void LLVolume::copyFacesFrom(const std::vector<LLVolumeFace> &faces) -{ - mVolumeFaces = faces; - mSculptLevel = 0; -} - -void LLVolume::copyVolumeFaces(const LLVolume* volume) -{ - mVolumeFaces = volume->mVolumeFaces; - mSculptLevel = 0; -} - -bool LLVolume::cacheOptimize(bool gen_tangents) -{ - for (S32 i = 0; i < mVolumeFaces.size(); ++i) - { - if (!mVolumeFaces[i].cacheOptimize(gen_tangents)) - { - return false; - } - } - return true; -} - - -S32 LLVolume::getNumFaces() const -{ - return mIsMeshAssetLoaded ? getNumVolumeFaces() : (S32)mProfilep->mFaces.size(); -} - - -void LLVolume::createVolumeFaces() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - if (mGenerateSingleFace) - { - // do nothing - } - else - { - S32 num_faces = getNumFaces(); - bool partial_build = true; - if (num_faces != mVolumeFaces.size()) - { - partial_build = false; - mVolumeFaces.resize(num_faces); - } - // Initialize volume faces with parameter data - for (S32 i = 0; i < (S32)mVolumeFaces.size(); i++) - { - LLVolumeFace& vf = mVolumeFaces[i]; - LLProfile::Face& face = mProfilep->mFaces[i]; - vf.mBeginS = face.mIndex; - vf.mNumS = face.mCount; - if (vf.mNumS < 0) - { - LL_ERRS() << "Volume face corruption detected." << LL_ENDL; - } - - vf.mBeginT = 0; - vf.mNumT= getPath().mPath.size(); - vf.mID = i; - - // Set the type mask bits correctly - if (mParams.getProfileParams().getHollow() > 0) - { - vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK; - } - if (mProfilep->isOpen()) - { - vf.mTypeMask |= LLVolumeFace::OPEN_MASK; - } - if (face.mCap) - { - vf.mTypeMask |= LLVolumeFace::CAP_MASK; - if (face.mFaceID == LL_FACE_PATH_BEGIN) - { - vf.mTypeMask |= LLVolumeFace::TOP_MASK; - } - else - { - llassert(face.mFaceID == LL_FACE_PATH_END); - vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK; - } - } - else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END)) - { - vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK; - } - else - { - vf.mTypeMask |= LLVolumeFace::SIDE_MASK; - if (face.mFlat) - { - vf.mTypeMask |= LLVolumeFace::FLAT_MASK; - } - if (face.mFaceID & LL_FACE_INNER_SIDE) - { - vf.mTypeMask |= LLVolumeFace::INNER_MASK; - if (face.mFlat && vf.mNumS > 2) - { //flat inner faces have to copy vert normals - vf.mNumS = vf.mNumS*2; - if (vf.mNumS < 0) - { - LL_ERRS() << "Volume face corruption detected." << LL_ENDL; - } - } - } - else - { - vf.mTypeMask |= LLVolumeFace::OUTER_MASK; - } - } - } - - for (face_list_t::iterator iter = mVolumeFaces.begin(); - iter != mVolumeFaces.end(); ++iter) - { - (*iter).create(this, partial_build); - } - } -} - - -inline LLVector4a sculpt_rgb_to_vector(U8 r, U8 g, U8 b) -{ - // maps RGB values to vector values [0..255] -> [-0.5..0.5] - LLVector4a value; - LLVector4a sub(0.5f, 0.5f, 0.5f); - - value.set(r,g,b); - value.mul(1.f/255.f); - value.sub(sub); - - return value; -} - -inline U32 sculpt_xy_to_index(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components) -{ - U32 index = (x + y * sculpt_width) * sculpt_components; - return index; -} - - -inline U32 sculpt_st_to_index(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components) -{ - U32 x = (U32) ((F32)s/(size_s) * (F32) sculpt_width); - U32 y = (U32) ((F32)t/(size_t) * (F32) sculpt_height); - - return sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components); -} - - -inline LLVector4a sculpt_index_to_vector(U32 index, const U8* sculpt_data) -{ - LLVector4a v = sculpt_rgb_to_vector(sculpt_data[index], sculpt_data[index+1], sculpt_data[index+2]); - - return v; -} - -inline LLVector4a sculpt_st_to_vector(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data) -{ - U32 index = sculpt_st_to_index(s, t, size_s, size_t, sculpt_width, sculpt_height, sculpt_components); - - return sculpt_index_to_vector(index, sculpt_data); -} - -inline LLVector4a sculpt_xy_to_vector(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data) -{ - U32 index = sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components); - - return sculpt_index_to_vector(index, sculpt_data); -} - - -F32 LLVolume::sculptGetSurfaceArea() -{ - // test to see if image has enough variation to create non-degenerate geometry - - F32 area = 0; - - S32 sizeS = mPathp->mPath.size(); - S32 sizeT = mProfilep->mProfile.size(); - - for (S32 s = 0; s < sizeS-1; s++) - { - for (S32 t = 0; t < sizeT-1; t++) - { - // get four corners of quad - LLVector4a& p1 = mMesh[(s )*sizeT + (t )]; - LLVector4a& p2 = mMesh[(s+1)*sizeT + (t )]; - LLVector4a& p3 = mMesh[(s )*sizeT + (t+1)]; - LLVector4a& p4 = mMesh[(s+1)*sizeT + (t+1)]; - - // compute the area of the quad by taking the length of the cross product of the two triangles - LLVector4a v0,v1,v2,v3; - v0.setSub(p1,p2); - v1.setSub(p1,p3); - v2.setSub(p4,p2); - v3.setSub(p4,p3); - - LLVector4a cross1, cross2; - cross1.setCross3(v0,v1); - cross2.setCross3(v2,v3); - - //LLVector3 cross1 = (p1 - p2) % (p1 - p3); - //LLVector3 cross2 = (p4 - p2) % (p4 - p3); - - area += (cross1.getLength3() + cross2.getLength3()).getF32() / 2.f; - } - } - - return area; -} - -// create empty placeholder shape -void LLVolume::sculptGenerateEmptyPlaceholder() -{ - S32 sizeS = mPathp->mPath.size(); - S32 sizeT = mProfilep->mProfile.size(); - - S32 line = 0; - - for (S32 s = 0; s < sizeS; s++) - { - for (S32 t = 0; t < sizeT; t++) - { - S32 i = t + line; - LLVector4a& pt = mMesh[i]; - - F32* p = pt.getF32ptr(); - - p[0] = 0; - p[1] = 0; - p[2] = 0; - - llassert(pt.isFinite3()); - } - line += sizeT; - } -} - -// create sphere placeholder shape -void LLVolume::sculptGenerateSpherePlaceholder() -{ - S32 sizeS = mPathp->mPath.size(); - S32 sizeT = mProfilep->mProfile.size(); - - S32 line = 0; - - for (S32 s = 0; s < sizeS; s++) - { - for (S32 t = 0; t < sizeT; t++) - { - S32 i = t + line; - LLVector4a& pt = mMesh[i]; - - - F32 u = (F32)s / (sizeS - 1); - F32 v = (F32)t / (sizeT - 1); - - const F32 RADIUS = (F32) 0.3; - - F32* p = pt.getF32ptr(); - - p[0] = (F32)(sin(F_PI * v) * cos(2.0 * F_PI * u) * RADIUS); - p[1] = (F32)(sin(F_PI * v) * sin(2.0 * F_PI * u) * RADIUS); - p[2] = (F32)(cos(F_PI * v) * RADIUS); - - llassert(pt.isFinite3()); - } - line += sizeT; - } -} - -// create the vertices from the map -void LLVolume::sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type) -{ - U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK; - bool sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT; - bool sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR; - bool reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR - - S32 sizeS = mPathp->mPath.size(); - S32 sizeT = mProfilep->mProfile.size(); - - S32 line = 0; - for (S32 s = 0; s < sizeS; s++) - { - // Run along the profile. - for (S32 t = 0; t < sizeT; t++) - { - S32 i = t + line; - LLVector4a& pt = mMesh[i]; - - S32 reversed_t = t; - - if (reverse_horizontal) - { - reversed_t = sizeT - t - 1; - } - - U32 x = (U32) ((F32)reversed_t/(sizeT-1) * (F32) sculpt_width); - U32 y = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_height); - - - if (y == 0) // top row stitching - { - // pinch? - if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE) - { - x = sculpt_width / 2; - } - } - - if (y == sculpt_height) // bottom row stitching - { - // wrap? - if (sculpt_stitching == LL_SCULPT_TYPE_TORUS) - { - y = 0; - } - else - { - y = sculpt_height - 1; - } - - // pinch? - if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE) - { - x = sculpt_width / 2; - } - } - - if (x == sculpt_width) // side stitching - { - // wrap? - if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) || - (sculpt_stitching == LL_SCULPT_TYPE_TORUS) || - (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER)) - { - x = 0; - } - - else - { - x = sculpt_width - 1; - } - } - - pt = sculpt_xy_to_vector(x, y, sculpt_width, sculpt_height, sculpt_components, sculpt_data); - - if (sculpt_mirror) - { - LLVector4a scale(-1.f,1,1,1); - pt.mul(scale); - } - - llassert(pt.isFinite3()); - } - - line += sizeT; - } -} - - -constexpr S32 SCULPT_REZ_1 = 6; // changed from 4 to 6 - 6 looks round whereas 4 looks square -constexpr S32 SCULPT_REZ_2 = 8; -constexpr S32 SCULPT_REZ_3 = 16; -constexpr S32 SCULPT_REZ_4 = 32; - -S32 sculpt_sides(F32 detail) -{ - // detail is usually one of: 1, 1.5, 2.5, 4.0. - - if (detail <= 1.0) - { - return SCULPT_REZ_1; - } - if (detail <= 2.0) - { - return SCULPT_REZ_2; - } - if (detail <= 3.0) - { - return SCULPT_REZ_3; - } - else - { - return SCULPT_REZ_4; - } -} - - - -// determine the number of vertices in both s and t direction for this sculpt -void sculpt_calc_mesh_resolution(U16 width, U16 height, U8 type, F32 detail, S32& s, S32& t) -{ - // this code has the following properties: - // 1) the aspect ratio of the mesh is as close as possible to the ratio of the map - // while still using all available verts - // 2) the mesh cannot have more verts than is allowed by LOD - // 3) the mesh cannot have more verts than is allowed by the map - - S32 max_vertices_lod = (S32)pow((double)sculpt_sides(detail), 2.0); - S32 max_vertices_map = width * height / 4; - - S32 vertices; - if (max_vertices_map > 0) - vertices = llmin(max_vertices_lod, max_vertices_map); - else - vertices = max_vertices_lod; - - - F32 ratio; - if ((width == 0) || (height == 0)) - ratio = 1.f; - else - ratio = (F32) width / (F32) height; - - - s = (S32)(F32) sqrt(((F32)vertices / ratio)); - - s = llmax(s, 4); // no degenerate sizes, please - t = vertices / s; - - t = llmax(t, 4); // no degenerate sizes, please - s = vertices / t; -} - -// sculpt replaces generate() for sculpted surfaces -void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level, bool visible_placeholder) -{ - U8 sculpt_type = mParams.getSculptType(); - - bool data_is_empty = false; - - if (sculpt_width == 0 || sculpt_height == 0 || sculpt_components < 3 || sculpt_data == NULL) - { - sculpt_level = -1; - data_is_empty = true; - } - - S32 requested_sizeS = 0; - S32 requested_sizeT = 0; - - sculpt_calc_mesh_resolution(sculpt_width, sculpt_height, sculpt_type, mDetail, requested_sizeS, requested_sizeT); - - mPathp->generate(mParams.getPathParams(), mDetail, 0, true, requested_sizeS); - mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(), mDetail, 0, true, requested_sizeT); - - S32 sizeS = mPathp->mPath.size(); // we requested a specific size, now see what we really got - S32 sizeT = mProfilep->mProfile.size(); // we requested a specific size, now see what we really got - - // weird crash bug - DEV-11158 - trying to collect more data: - if ((sizeS == 0) || (sizeT == 0)) - { - LL_WARNS() << "sculpt bad mesh size " << sizeS << " " << sizeT << LL_ENDL; - } - - sNumMeshPoints -= mMesh.size(); - mMesh.resize(sizeS * sizeT); - sNumMeshPoints += mMesh.size(); - - //generate vertex positions - if (!data_is_empty) - { - sculptGenerateMapVertices(sculpt_width, sculpt_height, sculpt_components, sculpt_data, sculpt_type); - - // don't test lowest LOD to support legacy content DEV-33670 - if (mDetail > SCULPT_MIN_AREA_DETAIL) - { - F32 area = sculptGetSurfaceArea(); - - mSurfaceArea = area; - - const F32 SCULPT_MAX_AREA = 384.f; - - if (area < SCULPT_MIN_AREA || area > SCULPT_MAX_AREA) - { - data_is_empty = true; - visible_placeholder = true; - } - } - } - - if (data_is_empty) - { - if (visible_placeholder) - { - // Object should be visible since there will be nothing else to display - sculptGenerateSpherePlaceholder(); - } - else - { - sculptGenerateEmptyPlaceholder(); - } - } - - for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++) - { - mFaceMask |= mProfilep->mFaces[i].mFaceID; - } - - mSculptLevel = sculpt_level; - - // Delete any existing faces so that they get regenerated - mVolumeFaces.clear(); - - createVolumeFaces(); -} - - - - -bool LLVolume::isCap(S32 face) -{ - return mProfilep->mFaces[face].mCap; -} - -bool LLVolume::isFlat(S32 face) -{ - return mProfilep->mFaces[face].mFlat; -} - - -bool LLVolumeParams::isSculpt() const -{ - return (mSculptType & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_NONE; -} - -bool LLVolumeParams::isMeshSculpt() const -{ - return (mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH; -} - -bool LLVolumeParams::operator==(const LLVolumeParams ¶ms) const -{ - return ( (getPathParams() == params.getPathParams()) && - (getProfileParams() == params.getProfileParams()) && - (mSculptID == params.mSculptID) && - (mSculptType == params.mSculptType) ); -} - -bool LLVolumeParams::operator!=(const LLVolumeParams ¶ms) const -{ - return ( (getPathParams() != params.getPathParams()) || - (getProfileParams() != params.getProfileParams()) || - (mSculptID != params.mSculptID) || - (mSculptType != params.mSculptType) ); -} - -bool LLVolumeParams::operator<(const LLVolumeParams ¶ms) const -{ - if( getPathParams() != params.getPathParams() ) - { - return getPathParams() < params.getPathParams(); - } - - if (getProfileParams() != params.getProfileParams()) - { - return getProfileParams() < params.getProfileParams(); - } - - if (mSculptID != params.mSculptID) - { - return mSculptID < params.mSculptID; - } - - return mSculptType < params.mSculptType; - - -} - -void LLVolumeParams::copyParams(const LLVolumeParams ¶ms) -{ - mProfileParams.copyParams(params.mProfileParams); - mPathParams.copyParams(params.mPathParams); - mSculptID = params.getSculptID(); - mSculptType = params.getSculptType(); -} - -// Less restricitve approx 0 for volumes -constexpr F32 APPROXIMATELY_ZERO = 0.001f; -bool approx_zero( F32 f, F32 tolerance = APPROXIMATELY_ZERO) -{ - return (f >= -tolerance) && (f <= tolerance); -} - -// return true if in range (or nearly so) -static bool limit_range(F32& v, F32 min, F32 max, F32 tolerance = APPROXIMATELY_ZERO) -{ - F32 min_delta = v - min; - if (min_delta < 0.f) - { - v = min; - if (!approx_zero(min_delta, tolerance)) - return false; - } - F32 max_delta = max - v; - if (max_delta < 0.f) - { - v = max; - if (!approx_zero(max_delta, tolerance)) - return false; - } - return true; -} - -bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e) -{ - bool valid = true; - - // First, clamp to valid ranges. - F32 begin = b; - valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA); - - F32 end = e; - if (end >= .0149f && end < MIN_CUT_DELTA) end = MIN_CUT_DELTA; // eliminate warning for common rounding error - valid &= limit_range(end, MIN_CUT_DELTA, 1.f); - - valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f); - - // Now set them. - mProfileParams.setBegin(begin); - mProfileParams.setEnd(end); - - return valid; -} - -bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e) -{ - bool valid = true; - - // First, clamp to valid ranges. - F32 begin = b; - valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA); - - F32 end = e; - valid &= limit_range(end, MIN_CUT_DELTA, 1.f); - - valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f); - - // Now set them. - mPathParams.setBegin(begin); - mPathParams.setEnd(end); - - return valid; -} - -bool LLVolumeParams::setHollow(const F32 h) -{ - // Validate the hollow based on path and profile. - U8 profile = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK; - U8 hole_type = mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK; - - F32 max_hollow = HOLLOW_MAX; - - // Only square holes have trouble. - if (LL_PCODE_HOLE_SQUARE == hole_type) - { - switch(profile) - { - case LL_PCODE_PROFILE_CIRCLE: - case LL_PCODE_PROFILE_CIRCLE_HALF: - case LL_PCODE_PROFILE_EQUALTRI: - max_hollow = HOLLOW_MAX_SQUARE; - } - } - - F32 hollow = h; - bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow); - mProfileParams.setHollow(hollow); - - return valid; -} - -bool LLVolumeParams::setTwistBegin(const F32 b) -{ - F32 twist_begin = b; - bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX); - mPathParams.setTwistBegin(twist_begin); - return valid; -} - -bool LLVolumeParams::setTwistEnd(const F32 e) -{ - F32 twist_end = e; - bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX); - mPathParams.setTwistEnd(twist_end); - return valid; -} - -bool LLVolumeParams::setRatio(const F32 x, const F32 y) -{ - F32 min_x = RATIO_MIN; - F32 max_x = RATIO_MAX; - F32 min_y = RATIO_MIN; - F32 max_y = RATIO_MAX; - // If this is a circular path (and not a sphere) then 'ratio' is actually hole size. - U8 path_type = mPathParams.getCurveType(); - U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK; - if ( LL_PCODE_PATH_CIRCLE == path_type && - LL_PCODE_PROFILE_CIRCLE_HALF != profile_type) - { - // Holes are more restricted... - min_x = HOLE_X_MIN; - max_x = HOLE_X_MAX; - min_y = HOLE_Y_MIN; - max_y = HOLE_Y_MAX; - } - - F32 ratio_x = x; - bool valid = limit_range(ratio_x, min_x, max_x); - F32 ratio_y = y; - valid &= limit_range(ratio_y, min_y, max_y); - - mPathParams.setScale(ratio_x, ratio_y); - - return valid; -} - -bool LLVolumeParams::setShear(const F32 x, const F32 y) -{ - F32 shear_x = x; - bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX); - F32 shear_y = y; - valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX); - mPathParams.setShear(shear_x, shear_y); - return valid; -} - -bool LLVolumeParams::setTaperX(const F32 v) -{ - F32 taper = v; - bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX); - mPathParams.setTaperX(taper); - return valid; -} - -bool LLVolumeParams::setTaperY(const F32 v) -{ - F32 taper = v; - bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX); - mPathParams.setTaperY(taper); - return valid; -} - -bool LLVolumeParams::setRevolutions(const F32 r) -{ - F32 revolutions = r; - bool valid = limit_range(revolutions, REV_MIN, REV_MAX); - mPathParams.setRevolutions(revolutions); - return valid; -} - -bool LLVolumeParams::setRadiusOffset(const F32 offset) -{ - bool valid = true; - - // If this is a sphere, just set it to 0 and get out. - U8 path_type = mPathParams.getCurveType(); - U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK; - if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type || - LL_PCODE_PATH_CIRCLE != path_type ) - { - mPathParams.setRadiusOffset(0.f); - return true; - } - - // Limit radius offset, based on taper and hole size y. - F32 radius_offset = offset; - F32 taper_y = getTaperY(); - F32 radius_mag = fabs(radius_offset); - F32 hole_y_mag = fabs(getRatioY()); - F32 taper_y_mag = fabs(taper_y); - // Check to see if the taper effects us. - if ( (radius_offset > 0.f && taper_y < 0.f) || - (radius_offset < 0.f && taper_y > 0.f) ) - { - // The taper does not help increase the radius offset range. - taper_y_mag = 0.f; - } - F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag); - - // Enforce the maximum magnitude. - F32 delta = max_radius_mag - radius_mag; - if (delta < 0.f) - { - // Check radius offset sign. - if (radius_offset < 0.f) - { - radius_offset = -max_radius_mag; - } - else - { - radius_offset = max_radius_mag; - } - valid = approx_zero(delta, .1f); - } - - mPathParams.setRadiusOffset(radius_offset); - return valid; -} - -bool LLVolumeParams::setSkew(const F32 skew_value) -{ - bool valid = true; - - // Check the skew value against the revolutions. - F32 skew = llclamp(skew_value, SKEW_MIN, SKEW_MAX); - F32 skew_mag = fabs(skew); - F32 revolutions = getRevolutions(); - F32 scale_x = getRatioX(); - F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f); - // Discontinuity; A revolution of 1 allows skews below 0.5. - if ( fabs(revolutions - 1.0f) < 0.001) - min_skew_mag = 0.0f; - - // Clip skew. - F32 delta = skew_mag - min_skew_mag; - if (delta < 0.f) - { - // Check skew sign. - if (skew < 0.0f) - { - skew = -min_skew_mag; - } - else - { - skew = min_skew_mag; - } - valid = approx_zero(delta, .01f); - } - - mPathParams.setSkew(skew); - return valid; -} - -bool LLVolumeParams::setSculptID(const LLUUID& sculpt_id, U8 sculpt_type) -{ - mSculptID = sculpt_id; - mSculptType = sculpt_type; - return true; -} - -bool LLVolumeParams::setType(U8 profile, U8 path) -{ - bool result = true; - // First, check profile and path for validity. - U8 profile_type = profile & LL_PCODE_PROFILE_MASK; - U8 hole_type = (profile & LL_PCODE_HOLE_MASK) >> 4; - U8 path_type = path >> 4; - - if (profile_type > LL_PCODE_PROFILE_MAX) - { - // Bad profile. Make it square. - profile = LL_PCODE_PROFILE_SQUARE; - result = false; - LL_WARNS() << "LLVolumeParams::setType changing bad profile type (" << profile_type - << ") to be LL_PCODE_PROFILE_SQUARE" << LL_ENDL; - } - else if (hole_type > LL_PCODE_HOLE_MAX) - { - // Bad hole. Make it the same. - profile = profile_type; - result = false; - LL_WARNS() << "LLVolumeParams::setType changing bad hole type (" << hole_type - << ") to be LL_PCODE_HOLE_SAME" << LL_ENDL; - } - - if (path_type < LL_PCODE_PATH_MIN || - path_type > LL_PCODE_PATH_MAX) - { - // Bad path. Make it linear. - result = false; - LL_WARNS() << "LLVolumeParams::setType changing bad path (" << path - << ") to be LL_PCODE_PATH_LINE" << LL_ENDL; - path = LL_PCODE_PATH_LINE; - } - - mProfileParams.setCurveType(profile); - mPathParams.setCurveType(path); - return result; -} - -// static -bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow, - U8 path_curve, F32 path_begin, F32 path_end, - F32 scx, F32 scy, F32 shx, F32 shy, - F32 twistend, F32 twistbegin, F32 radiusoffset, - F32 tx, F32 ty, F32 revolutions, F32 skew) -{ - LLVolumeParams test_params; - if (!test_params.setType (prof_curve, path_curve)) - { - return false; - } - if (!test_params.setBeginAndEndS (prof_begin, prof_end)) - { - return false; - } - if (!test_params.setBeginAndEndT (path_begin, path_end)) - { - return false; - } - if (!test_params.setHollow (hollow)) - { - return false; - } - if (!test_params.setTwistBegin (twistbegin)) - { - return false; - } - if (!test_params.setTwistEnd (twistend)) - { - return false; - } - if (!test_params.setRatio (scx, scy)) - { - return false; - } - if (!test_params.setShear (shx, shy)) - { - return false; - } - if (!test_params.setTaper (tx, ty)) - { - return false; - } - if (!test_params.setRevolutions (revolutions)) - { - return false; - } - if (!test_params.setRadiusOffset (radiusoffset)) - { - return false; - } - if (!test_params.setSkew (skew)) - { - return false; - } - return true; -} - -void LLVolume::getLoDTriangleCounts(const LLVolumeParams& params, S32* counts) -{ //attempt to approximate the number of triangles that will result from generating a volume LoD set for the - //supplied LLVolumeParams -- inaccurate, but a close enough approximation for determining streaming cost - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; - F32 detail[] = {1.f, 1.5f, 2.5f, 4.f}; - for (S32 i = 0; i < 4; i++) - { - S32 count = 0; - S32 path_points = LLPath::getNumPoints(params.getPathParams(), detail[i]); - S32 profile_points = LLProfile::getNumPoints(params.getProfileParams(), false, detail[i]); - - count = (profile_points-1)*2*(path_points-1); - count += profile_points*2; - - counts[i] = count; - } -} - - -S32 LLVolume::getNumTriangles(S32* vcount) const -{ - U32 triangle_count = 0; - U32 vertex_count = 0; - - for (S32 i = 0; i < getNumVolumeFaces(); ++i) - { - const LLVolumeFace& face = getVolumeFace(i); - triangle_count += face.mNumIndices/3; - - vertex_count += face.mNumVertices; - } - - - if (vcount) - { - *vcount = vertex_count; - } - - return triangle_count; -} - - -//----------------------------------------------------------------------------- -// generateSilhouetteVertices() -//----------------------------------------------------------------------------- -void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices, - std::vector<LLVector3> &normals, - const LLVector3& obj_cam_vec_in, - const LLMatrix4& mat_in, - const LLMatrix3& norm_mat_in, - S32 face_mask) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - LLMatrix4a mat; - mat.loadu(mat_in); - - LLMatrix4a norm_mat; - norm_mat.loadu(norm_mat_in); - - LLVector4a obj_cam_vec; - obj_cam_vec.load3(obj_cam_vec_in.mV); - - vertices.clear(); - normals.clear(); - - if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) - { - return; - } - - S32 cur_index = 0; - //for each face - for (face_list_t::iterator iter = mVolumeFaces.begin(); - iter != mVolumeFaces.end(); ++iter) - { - LLVolumeFace& face = *iter; - - if (!(face_mask & (0x1 << cur_index++)) || - face.mNumIndices == 0 || face.mEdge.empty()) - { - continue; - } - - if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) - { - LLVector4a* v = (LLVector4a*)face.mPositions; - LLVector4a* n = (LLVector4a*)face.mNormals; - - for (U32 j = 0; j < face.mNumIndices / 3; j++) - { - for (S32 k = 0; k < 3; k++) - { - S32 index = face.mEdge[j * 3 + k]; - - if (index == -1) - { - // silhouette edge, currently only cubes, so no other conditions - - S32 v1 = face.mIndices[j * 3 + k]; - S32 v2 = face.mIndices[j * 3 + ((k + 1) % 3)]; - - LLVector4a t; - mat.affineTransform(v[v1], t); - vertices.push_back(LLVector3(t[0], t[1], t[2])); - - norm_mat.rotate(n[v1], t); - - t.normalize3fast(); - normals.push_back(LLVector3(t[0], t[1], t[2])); - - mat.affineTransform(v[v2], t); - vertices.push_back(LLVector3(t[0], t[1], t[2])); - - norm_mat.rotate(n[v2], t); - t.normalize3fast(); - normals.push_back(LLVector3(t[0], t[1], t[2])); - } - } - } - - } - else - { - //============================================== - //DEBUG draw edge map instead of silhouette edge - //============================================== - -#if DEBUG_SILHOUETTE_EDGE_MAP - - //for each triangle - U32 tri_count = face.mNumIndices / 3; - for (U32 j = 0; j < tri_count; j++) { - //get vertices - S32 v1 = face.mIndices[j*3+0]; - S32 v2 = face.mIndices[j*3+1]; - S32 v3 = face.mIndices[j*3+2]; - - //get current face center - LLVector3 cCenter = (face.mVertices[v1].getPosition() + - face.mVertices[v2].getPosition() + - face.mVertices[v3].getPosition()) / 3.0f; - - //for each edge - for (S32 k = 0; k < 3; k++) { - S32 nIndex = face.mEdge[j*3+k]; - if (nIndex <= -1) { - continue; - } - - if (nIndex >= (S32)tri_count) { - continue; - } - //get neighbor vertices - v1 = face.mIndices[nIndex*3+0]; - v2 = face.mIndices[nIndex*3+1]; - v3 = face.mIndices[nIndex*3+2]; - - //get neighbor face center - LLVector3 nCenter = (face.mVertices[v1].getPosition() + - face.mVertices[v2].getPosition() + - face.mVertices[v3].getPosition()) / 3.0f; - - //draw line - vertices.push_back(cCenter); - vertices.push_back(nCenter); - normals.push_back(LLVector3(1,1,1)); - normals.push_back(LLVector3(1,1,1)); - segments.push_back(vertices.size()); - } - } - - continue; - - //============================================== - //DEBUG - //============================================== - - //============================================== - //DEBUG draw normals instead of silhouette edge - //============================================== -#elif DEBUG_SILHOUETTE_NORMALS - - //for each vertex - for (U32 j = 0; j < face.mNumVertices; j++) { - vertices.push_back(face.mVertices[j].getPosition()); - vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].getNormal()*0.1f); - normals.push_back(LLVector3(0,0,1)); - normals.push_back(LLVector3(0,0,1)); - segments.push_back(vertices.size()); -#if DEBUG_SILHOUETTE_BINORMALS - vertices.push_back(face.mVertices[j].getPosition()); - vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].mTangent*0.1f); - normals.push_back(LLVector3(0,0,1)); - normals.push_back(LLVector3(0,0,1)); - segments.push_back(vertices.size()); -#endif - } - - continue; -#else - //============================================== - //DEBUG - //============================================== - - constexpr U8 AWAY = 0x01, - TOWARDS = 0x02; - - //for each triangle - std::vector<U8> fFacing; - vector_append(fFacing, face.mNumIndices/3); - - LLVector4a* v = (LLVector4a*) face.mPositions; - LLVector4a* n = (LLVector4a*) face.mNormals; - - for (U32 j = 0; j < face.mNumIndices/3; j++) - { - //approximate normal - S32 v1 = face.mIndices[j*3+0]; - S32 v2 = face.mIndices[j*3+1]; - S32 v3 = face.mIndices[j*3+2]; - - LLVector4a c1,c2; - c1.setSub(v[v1], v[v2]); - c2.setSub(v[v2], v[v3]); - - LLVector4a norm; - - norm.setCross3(c1, c2); - - if (norm.dot3(norm) < 0.00000001f) - { - fFacing[j] = AWAY | TOWARDS; - } - else - { - //get view vector - LLVector4a view; - view.setSub(obj_cam_vec, v[v1]); - bool away = view.dot3(norm) > 0.0f; - if (away) - { - fFacing[j] = AWAY; - } - else - { - fFacing[j] = TOWARDS; - } - } - } - - //for each triangle - for (U32 j = 0; j < face.mNumIndices/3; j++) - { - if (fFacing[j] == (AWAY | TOWARDS)) - { //this is a degenerate triangle - //take neighbor facing (degenerate faces get facing of one of their neighbors) - // *FIX IF NEEDED: this does not deal with neighboring degenerate faces - for (S32 k = 0; k < 3; k++) - { - S32 index = face.mEdge[j*3+k]; - if (index != -1) - { - fFacing[j] = fFacing[index]; - break; - } - } - continue; //skip degenerate face - } - - //for each edge - for (S32 k = 0; k < 3; k++) { - S32 index = face.mEdge[j*3+k]; - if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) { - //our neighbor is degenerate, make him face our direction - fFacing[face.mEdge[j*3+k]] = fFacing[j]; - continue; - } - - if (index == -1 || //edge has no neighbor, MUST be a silhouette edge - (fFacing[index] & fFacing[j]) == 0) { //we found a silhouette edge - - S32 v1 = face.mIndices[j*3+k]; - S32 v2 = face.mIndices[j*3+((k+1)%3)]; - - LLVector4a t; - mat.affineTransform(v[v1], t); - vertices.push_back(LLVector3(t[0], t[1], t[2])); - - norm_mat.rotate(n[v1], t); - - t.normalize3fast(); - normals.push_back(LLVector3(t[0], t[1], t[2])); - - mat.affineTransform(v[v2], t); - vertices.push_back(LLVector3(t[0], t[1], t[2])); - - norm_mat.rotate(n[v2], t); - t.normalize3fast(); - normals.push_back(LLVector3(t[0], t[1], t[2])); - } - } - } -#endif - } - } -} - -S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, - S32 face, - LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent_out) -{ - S32 hit_face = -1; - - S32 start_face; - S32 end_face; - - if (face == -1) // ALL_SIDES - { - start_face = 0; - end_face = getNumVolumeFaces() - 1; - } - else - { - start_face = face; - end_face = face; - } - - LLVector4a dir; - dir.setSub(end, start); - - F32 closest_t = 2.f; // must be larger than 1 - - end_face = llmin(end_face, getNumVolumeFaces()-1); - - for (S32 i = start_face; i <= end_face; i++) - { - LLVolumeFace &face = mVolumeFaces[i]; - - LLVector4a box_center; - box_center.setAdd(face.mExtents[0], face.mExtents[1]); - box_center.mul(0.5f); - - LLVector4a box_size; - box_size.setSub(face.mExtents[1], face.mExtents[0]); - - if (LLLineSegmentBoxIntersect(start, end, box_center, box_size)) - { - if (tangent_out != NULL) // if the caller wants tangents, we may need to generate them - { - genTangents(i); - } - - if (isUnique()) - { //don't bother with an octree for flexi volumes - U32 tri_count = face.mNumIndices/3; - - for (U32 j = 0; j < tri_count; ++j) - { - U16 idx0 = face.mIndices[j*3+0]; - U16 idx1 = face.mIndices[j*3+1]; - U16 idx2 = face.mIndices[j*3+2]; - - const LLVector4a& v0 = face.mPositions[idx0]; - const LLVector4a& v1 = face.mPositions[idx1]; - const LLVector4a& v2 = face.mPositions[idx2]; - - F32 a,b,t; - - if (LLTriangleRayIntersect(v0, v1, v2, - start, dir, a, b, t)) - { - if ((t >= 0.f) && // if hit is after start - (t <= 1.f) && // and before end - (t < closest_t)) // and this hit is closer - { - closest_t = t; - hit_face = i; - - if (intersection != NULL) - { - LLVector4a intersect = dir; - intersect.mul(closest_t); - intersect.add(start); - *intersection = intersect; - } - - - if (tex_coord != NULL) - { - LLVector2* tc = (LLVector2*) face.mTexCoords; - *tex_coord = ((1.f - a - b) * tc[idx0] + - a * tc[idx1] + - b * tc[idx2]); - - } - - if (normal!= NULL) - { - LLVector4a* norm = face.mNormals; - - LLVector4a n1,n2,n3; - n1 = norm[idx0]; - n1.mul(1.f-a-b); - - n2 = norm[idx1]; - n2.mul(a); - - n3 = norm[idx2]; - n3.mul(b); - - n1.add(n2); - n1.add(n3); - - *normal = n1; - } - - if (tangent_out != NULL) - { - LLVector4a* tangents = face.mTangents; - - LLVector4a t1,t2,t3; - t1 = tangents[idx0]; - t1.mul(1.f-a-b); - - t2 = tangents[idx1]; - t2.mul(a); - - t3 = tangents[idx2]; - t3.mul(b); - - t1.add(t2); - t1.add(t3); - - *tangent_out = t1; - } - } - } - } - } - else - { - if (!face.getOctree()) - { - face.createOctree(); - } - - LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out); - intersect.traverse(face.getOctree()); - if (intersect.mHitFace) - { - hit_face = i; - } - } - } - } - - - return hit_face; -} - -class LLVertexIndexPair -{ -public: - LLVertexIndexPair(const LLVector3 &vertex, const S32 index); - - LLVector3 mVertex; - S32 mIndex; -}; - -LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index) -{ - mVertex = vertex; - mIndex = index; -} - -constexpr F32 VERTEX_SLOP = 0.00001f; - -struct lessVertex -{ - bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b) - { - const F32 slop = VERTEX_SLOP; - - if (a->mVertex.mV[0] + slop < b->mVertex.mV[0]) - { - return true; - } - else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0]) - { - return false; - } - - if (a->mVertex.mV[1] + slop < b->mVertex.mV[1]) - { - return true; - } - else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1]) - { - return false; - } - - if (a->mVertex.mV[2] + slop < b->mVertex.mV[2]) - { - return true; - } - else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2]) - { - return false; - } - - return false; - } -}; - -struct lessTriangle -{ - bool operator()(const S32 *a, const S32 *b) - { - if (*a < *b) - { - return true; - } - else if (*a > *b) - { - return false; - } - - if (*(a+1) < *(b+1)) - { - return true; - } - else if (*(a+1) > *(b+1)) - { - return false; - } - - if (*(a+2) < *(b+2)) - { - return true; - } - else if (*(a+2) > *(b+2)) - { - return false; - } - - return false; - } -}; - -bool equalTriangle(const S32 *a, const S32 *b) -{ - if ((*a == *b) && (*(a+1) == *(b+1)) && (*(a+2) == *(b+2))) - { - return true; - } - return false; -} - -bool LLVolumeParams::importFile(LLFILE *fp) -{ - //LL_INFOS() << "importing volume" << LL_ENDL; - const S32 BUFSIZE = 16384; - char buffer[BUFSIZE]; /* Flawfinder: ignore */ - // *NOTE: changing the size or type of this buffer will require - // changing the sscanf below. - char keyword[256]; /* Flawfinder: ignore */ - keyword[0] = 0; - - while (!feof(fp)) - { - if (fgets(buffer, BUFSIZE, fp) == NULL) - { - buffer[0] = '\0'; - } - - sscanf(buffer, " %255s", keyword); /* Flawfinder: ignore */ - if (!strcmp("{", keyword)) - { - continue; - } - if (!strcmp("}",keyword)) - { - break; - } - else if (!strcmp("profile", keyword)) - { - mProfileParams.importFile(fp); - } - else if (!strcmp("path",keyword)) - { - mPathParams.importFile(fp); - } - else - { - LL_WARNS() << "unknown keyword " << keyword << " in volume import" << LL_ENDL; - } - } - - return true; -} - -bool LLVolumeParams::exportFile(LLFILE *fp) const -{ - fprintf(fp,"\tshape 0\n"); - fprintf(fp,"\t{\n"); - mPathParams.exportFile(fp); - mProfileParams.exportFile(fp); - fprintf(fp, "\t}\n"); - return true; -} - - -bool LLVolumeParams::importLegacyStream(std::istream& input_stream) -{ - //LL_INFOS() << "importing volume" << LL_ENDL; - const S32 BUFSIZE = 16384; - // *NOTE: changing the size or type of this buffer will require - // changing the sscanf below. - char buffer[BUFSIZE]; /* Flawfinder: ignore */ - char keyword[256]; /* Flawfinder: ignore */ - keyword[0] = 0; - - while (input_stream.good()) - { - input_stream.getline(buffer, BUFSIZE); - sscanf(buffer, " %255s", keyword); - if (!strcmp("{", keyword)) - { - continue; - } - if (!strcmp("}",keyword)) - { - break; - } - else if (!strcmp("profile", keyword)) - { - mProfileParams.importLegacyStream(input_stream); - } - else if (!strcmp("path",keyword)) - { - mPathParams.importLegacyStream(input_stream); - } - else - { - LL_WARNS() << "unknown keyword " << keyword << " in volume import" << LL_ENDL; - } - } - - return true; -} - -bool LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const -{ - output_stream <<"\tshape 0\n"; - output_stream <<"\t{\n"; - mPathParams.exportLegacyStream(output_stream); - mProfileParams.exportLegacyStream(output_stream); - output_stream << "\t}\n"; - return true; -} - -LLSD LLVolumeParams::sculptAsLLSD() const -{ - LLSD sd = LLSD(); - sd["id"] = getSculptID(); - sd["type"] = getSculptType(); - - return sd; -} - -bool LLVolumeParams::sculptFromLLSD(LLSD& sd) -{ - setSculptID(sd["id"].asUUID(), (U8)sd["type"].asInteger()); - return true; -} - -LLSD LLVolumeParams::asLLSD() const -{ - LLSD sd = LLSD(); - sd["path"] = mPathParams; - sd["profile"] = mProfileParams; - sd["sculpt"] = sculptAsLLSD(); - - return sd; -} - -bool LLVolumeParams::fromLLSD(LLSD& sd) -{ - mPathParams.fromLLSD(sd["path"]); - mProfileParams.fromLLSD(sd["profile"]); - sculptFromLLSD(sd["sculpt"]); - - return true; -} - -void LLVolumeParams::reduceS(F32 begin, F32 end) -{ - begin = llclampf(begin); - end = llclampf(end); - if (begin > end) - { - F32 temp = begin; - begin = end; - end = temp; - } - F32 a = mProfileParams.getBegin(); - F32 b = mProfileParams.getEnd(); - mProfileParams.setBegin(a + begin * (b - a)); - mProfileParams.setEnd(a + end * (b - a)); -} - -void LLVolumeParams::reduceT(F32 begin, F32 end) -{ - begin = llclampf(begin); - end = llclampf(end); - if (begin > end) - { - F32 temp = begin; - begin = end; - end = temp; - } - F32 a = mPathParams.getBegin(); - F32 b = mPathParams.getEnd(); - mPathParams.setBegin(a + begin * (b - a)); - mPathParams.setEnd(a + end * (b - a)); -} - -const F32 MIN_CONCAVE_PROFILE_WEDGE = 0.125f; // 1/8 unity -const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f; // 1/9 unity - -// returns true if the shape can be approximated with a convex shape -// for collison purposes -bool LLVolumeParams::isConvex() const -{ - if (!getSculptID().isNull()) - { - // can't determine, be safe and say no: - return false; - } - - F32 path_length = mPathParams.getEnd() - mPathParams.getBegin(); - F32 hollow = mProfileParams.getHollow(); - - U8 path_type = mPathParams.getCurveType(); - if ( path_length > MIN_CONCAVE_PATH_WEDGE - && ( mPathParams.getTwist() != mPathParams.getTwistBegin() - || (hollow > 0.f - && LL_PCODE_PATH_LINE != path_type) ) ) - { - // twist along a "not too short" path is concave - return false; - } - - F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin(); - bool same_hole = hollow == 0.f - || (mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK) == LL_PCODE_HOLE_SAME; - - F32 min_profile_wedge = MIN_CONCAVE_PROFILE_WEDGE; - U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK; - if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ) - { - // it is a sphere and spheres get twice the minimum profile wedge - min_profile_wedge = 2.f * MIN_CONCAVE_PROFILE_WEDGE; - } - - bool convex_profile = ( ( profile_length == 1.f - || profile_length <= 0.5f ) - && hollow == 0.f ) // trivially convex - || ( profile_length <= min_profile_wedge - && same_hole ); // effectvely convex (even when hollow) - - if (!convex_profile) - { - // profile is concave - return false; - } - - if ( LL_PCODE_PATH_LINE == path_type ) - { - // straight paths with convex profile - return true; - } - - bool concave_path = (path_length < 1.0f) && (path_length > 0.5f); - if (concave_path) - { - return false; - } - - // we're left with spheres, toroids and tubes - if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ) - { - // at this stage all spheres must be convex - return true; - } - - // it's a toroid or tube - if ( path_length <= MIN_CONCAVE_PATH_WEDGE ) - { - // effectively convex - return true; - } - - return false; -} - -// debug -void LLVolumeParams::setCube() -{ - mProfileParams.setCurveType(LL_PCODE_PROFILE_SQUARE); - mProfileParams.setBegin(0.f); - mProfileParams.setEnd(1.f); - mProfileParams.setHollow(0.f); - - mPathParams.setBegin(0.f); - mPathParams.setEnd(1.f); - mPathParams.setScale(1.f, 1.f); - mPathParams.setShear(0.f, 0.f); - mPathParams.setCurveType(LL_PCODE_PATH_LINE); - mPathParams.setTwistBegin(0.f); - mPathParams.setTwistEnd(0.f); - mPathParams.setRadiusOffset(0.f); - mPathParams.setTaper(0.f, 0.f); - mPathParams.setRevolutions(0.f); - mPathParams.setSkew(0.f); -} - -LLFaceID LLVolume::generateFaceMask() -{ - LLFaceID new_mask = 0x0000; - - switch(mParams.getProfileParams().getCurveType() & LL_PCODE_PROFILE_MASK) - { - case LL_PCODE_PROFILE_CIRCLE: - case LL_PCODE_PROFILE_CIRCLE_HALF: - new_mask |= LL_FACE_OUTER_SIDE_0; - break; - case LL_PCODE_PROFILE_SQUARE: - { - for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 4.f); side < llceil(mParams.getProfileParams().getEnd() * 4.f); side++) - { - new_mask |= LL_FACE_OUTER_SIDE_0 << side; - } - } - break; - case LL_PCODE_PROFILE_ISOTRI: - case LL_PCODE_PROFILE_EQUALTRI: - case LL_PCODE_PROFILE_RIGHTTRI: - { - for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 3.f); side < llceil(mParams.getProfileParams().getEnd() * 3.f); side++) - { - new_mask |= LL_FACE_OUTER_SIDE_0 << side; - } - } - break; - default: - LL_ERRS() << "Unknown profile!" << LL_ENDL; - break; - } - - // handle hollow objects - if (mParams.getProfileParams().getHollow() > 0) - { - new_mask |= LL_FACE_INNER_SIDE; - } - - // handle open profile curves - if (mProfilep->isOpen()) - { - new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END; - } - - // handle open path curves - if (mPathp->isOpen()) - { - new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END; - } - - return new_mask; -} - -bool LLVolume::isFaceMaskValid(LLFaceID face_mask) -{ - LLFaceID test_mask = 0; - for(S32 i = 0; i < getNumFaces(); i++) - { - test_mask |= mProfilep->mFaces[i].mFaceID; - } - - return test_mask == face_mask; -} - -bool LLVolume::isConvex() const -{ - // mParams.isConvex() may return false even though the final - // geometry is actually convex due to LOD approximations. - // TODO -- provide LLPath and LLProfile with isConvex() methods - // that correctly determine convexity. -- Leviathan - return mParams.isConvex(); -} - - -std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params) -{ - s << "{type=" << (U32) profile_params.mCurveType; - s << ", begin=" << profile_params.mBegin; - s << ", end=" << profile_params.mEnd; - s << ", hollow=" << profile_params.mHollow; - s << "}"; - return s; -} - - -std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params) -{ - s << "{type=" << (U32) path_params.mCurveType; - s << ", begin=" << path_params.mBegin; - s << ", end=" << path_params.mEnd; - s << ", twist=" << path_params.mTwistEnd; - s << ", scale=" << path_params.mScale; - s << ", shear=" << path_params.mShear; - s << ", twist_begin=" << path_params.mTwistBegin; - s << ", radius_offset=" << path_params.mRadiusOffset; - s << ", taper=" << path_params.mTaper; - s << ", revolutions=" << path_params.mRevolutions; - s << ", skew=" << path_params.mSkew; - s << "}"; - return s; -} - - -std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params) -{ - s << "{profileparams = " << volume_params.mProfileParams; - s << ", pathparams = " << volume_params.mPathParams; - s << "}"; - return s; -} - - -std::ostream& operator<<(std::ostream &s, const LLProfile &profile) -{ - s << " {open=" << (U32) profile.mOpen; - s << ", dirty=" << profile.mDirty; - s << ", totalout=" << profile.mTotalOut; - s << ", total=" << profile.mTotal; - s << "}"; - return s; -} - - -std::ostream& operator<<(std::ostream &s, const LLPath &path) -{ - s << "{open=" << (U32) path.mOpen; - s << ", dirty=" << path.mDirty; - s << ", step=" << path.mStep; - s << ", total=" << path.mTotal; - s << "}"; - return s; -} - -std::ostream& operator<<(std::ostream &s, const LLVolume &volume) -{ - s << "{params = " << volume.getParams(); - s << ", path = " << *volume.mPathp; - s << ", profile = " << *volume.mProfilep; - s << "}"; - return s; -} - - -std::ostream& operator<<(std::ostream &s, const LLVolume *volumep) -{ - s << "{params = " << volumep->getParams(); - s << ", path = " << *(volumep->mPathp); - s << ", profile = " << *(volumep->mProfilep); - s << "}"; - return s; -} - -LLVolumeFace::LLVolumeFace() : - mID(0), - mTypeMask(0), - mBeginS(0), - mBeginT(0), - mNumS(0), - mNumT(0), - mNumVertices(0), - mNumAllocatedVertices(0), - mNumIndices(0), - mPositions(NULL), - mNormals(NULL), - mTangents(NULL), - mTexCoords(NULL), - mIndices(NULL), - mWeights(NULL), -#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS - mJustWeights(NULL), - mJointIndices(NULL), -#endif - mWeightsScrubbed(false), - mOctree(NULL), - mOctreeTriangles(NULL), - mOptimized(false) -{ - mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); - mExtents[0].splat(-0.5f); - mExtents[1].splat(0.5f); - mCenter = mExtents+2; -} - -LLVolumeFace::LLVolumeFace(const LLVolumeFace& src) -: mID(0), - mTypeMask(0), - mBeginS(0), - mBeginT(0), - mNumS(0), - mNumT(0), - mNumVertices(0), - mNumAllocatedVertices(0), - mNumIndices(0), - mPositions(NULL), - mNormals(NULL), - mTangents(NULL), - mTexCoords(NULL), - mIndices(NULL), - mWeights(NULL), -#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS - mJustWeights(NULL), - mJointIndices(NULL), -#endif - mWeightsScrubbed(false), - mOctree(NULL), - mOctreeTriangles(NULL) -{ - mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); - mCenter = mExtents+2; - *this = src; -} - -LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) -{ - if (&src == this) - { //self assignment, do nothing - return *this; - } - - mID = src.mID; - mTypeMask = src.mTypeMask; - mBeginS = src.mBeginS; - mBeginT = src.mBeginT; - mNumS = src.mNumS; - mNumT = src.mNumT; - - mExtents[0] = src.mExtents[0]; - mExtents[1] = src.mExtents[1]; - *mCenter = *src.mCenter; - - mNumVertices = 0; - mNumIndices = 0; - - freeData(); - - resizeVertices(src.mNumVertices); - resizeIndices(src.mNumIndices); - - if (mNumVertices) - { - S32 vert_size = mNumVertices*sizeof(LLVector4a); - S32 tc_size = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF; - - LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) src.mPositions, vert_size); - - if (src.mNormals) - { - LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) src.mNormals, vert_size); - } - - if(src.mTexCoords) - { - LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size); - } - - if (src.mTangents) - { - allocateTangents(src.mNumVertices); - LLVector4a::memcpyNonAliased16((F32*) mTangents, (F32*) src.mTangents, vert_size); - } - else - { - ll_aligned_free_16(mTangents); - mTangents = NULL; - } - - if (src.mWeights) - { - llassert(!mWeights); // don't orphan an old alloc here accidentally - allocateWeights(src.mNumVertices); - LLVector4a::memcpyNonAliased16((F32*) mWeights, (F32*) src.mWeights, vert_size); - mWeightsScrubbed = src.mWeightsScrubbed; - } - else - { - ll_aligned_free_16(mWeights); - mWeights = NULL; - mWeightsScrubbed = false; - } - - #if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS - if (src.mJointIndices) - { - llassert(!mJointIndices); // don't orphan an old alloc here accidentally - allocateJointIndices(src.mNumVertices); - LLVector4a::memcpyNonAliased16((F32*) mJointIndices, (F32*) src.mJointIndices, src.mNumVertices * sizeof(U8) * 4); - } - else*/ - { - ll_aligned_free_16(mJointIndices); - mJointIndices = NULL; - } - #endif - - } - - if (mNumIndices) - { - S32 idx_size = (mNumIndices*sizeof(U16)+0xF) & ~0xF; - - LLVector4a::memcpyNonAliased16((F32*) mIndices, (F32*) src.mIndices, idx_size); - } - else - { - ll_aligned_free_16(mIndices); - mIndices = NULL; - } - - mOptimized = src.mOptimized; - mNormalizedScale = src.mNormalizedScale; - - //delete - return *this; -} - -LLVolumeFace::~LLVolumeFace() -{ - ll_aligned_free_16(mExtents); - mExtents = NULL; - mCenter = NULL; - - freeData(); -} - -void LLVolumeFace::freeData() -{ - ll_aligned_free<64>(mPositions); - mPositions = NULL; - - //normals and texture coordinates are part of the same buffer as mPositions, do not free them separately - mNormals = NULL; - mTexCoords = NULL; - - ll_aligned_free_16(mIndices); - mIndices = NULL; - ll_aligned_free_16(mTangents); - mTangents = NULL; - ll_aligned_free_16(mWeights); - mWeights = NULL; - -#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS - ll_aligned_free_16(mJointIndices); - mJointIndices = NULL; - ll_aligned_free_16(mJustWeights); - mJustWeights = NULL; -#endif - - destroyOctree(); -} - -bool LLVolumeFace::create(LLVolume* volume, bool partial_build) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - //tree for this face is no longer valid - destroyOctree(); - - LL_CHECK_MEMORY - bool ret = false ; - if (mTypeMask & CAP_MASK) - { - ret = createCap(volume, partial_build); - LL_CHECK_MEMORY - } - else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK)) - { - ret = createSide(volume, partial_build); - LL_CHECK_MEMORY - } - else - { - LL_ERRS() << "Unknown/uninitialized face type!" << LL_ENDL; - } - - return ret ; -} - -void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv) -{ - cv.setPosition(mPositions[index]); - if (mNormals) - { - cv.setNormal(mNormals[index]); - } - else - { - cv.getNormal().clear(); - } - - if (mTexCoords) - { - cv.mTexCoord = mTexCoords[index]; - } - else - { - cv.mTexCoord.clear(); - } -} - -bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const -{ - return getPosition().equals3(rhs.getPosition()) && - mTexCoord == rhs.mTexCoord && - getNormal().equals3(rhs.getNormal()); -} - -bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a, const LLVector3& b) const -{ - if (a.mV[0] != b.mV[0]) - { - return a.mV[0] < b.mV[0]; - } - - if (a.mV[1] != b.mV[1]) - { - return a.mV[1] < b.mV[1]; - } - - return a.mV[2] < b.mV[2]; -} - -void LLVolumeFace::remap() -{ - // Generate a remap buffer - std::vector<unsigned int> remap(mNumVertices); - S32 remap_vertices_count = LLMeshOptimizer::generateRemapMultiU16(&remap[0], - mIndices, - mNumIndices, - mPositions, - mNormals, - mTexCoords, - mNumVertices); - - // Allocate new buffers - S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF; - U16* remap_indices = (U16*)ll_aligned_malloc_16(size); - - S32 tc_bytes_size = ((remap_vertices_count * sizeof(LLVector2)) + 0xF) & ~0xF; - LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * remap_vertices_count + tc_bytes_size); - LLVector4a* remap_normals = remap_positions + remap_vertices_count; - LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count); - - // Fill the buffers - LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]); - LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]); - LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]); - LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]); - - // Free unused buffers - ll_aligned_free_16(mIndices); - ll_aligned_free<64>(mPositions); - - // Tangets are now invalid - ll_aligned_free_16(mTangents); - mTangents = NULL; - - // Assign new values - mIndices = remap_indices; - mPositions = remap_positions; - mNormals = remap_normals; - mTexCoords = remap_tex_coords; - mNumVertices = remap_vertices_count; - mNumAllocatedVertices = remap_vertices_count; -} - -void LLVolumeFace::optimize(F32 angle_cutoff) -{ - LLVolumeFace new_face; - - //map of points to vector of vertices at that point - std::map<U64, std::vector<VertexMapData> > point_map; - - LLVector4a range; - range.setSub(mExtents[1],mExtents[0]); - - //remove redundant vertices - for (U32 i = 0; i < mNumIndices; ++i) - { - U16 index = mIndices[i]; - - if (index >= mNumVertices) - { - // invalid index - // replace with a valid index to avoid crashes - index = mNumVertices - 1; - mIndices[i] = index; - - // Needs better logging - LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL; - } - - LLVolumeFace::VertexData cv; - getVertexData(index, cv); - - bool found = false; - - LLVector4a pos; - pos.setSub(mPositions[index], mExtents[0]); - pos.div(range); - - U64 pos64 = 0; - - pos64 = (U16) (pos[0]*65535); - pos64 = pos64 | (((U64) (pos[1]*65535)) << 16); - pos64 = pos64 | (((U64) (pos[2]*65535)) << 32); - - std::map<U64, std::vector<VertexMapData> >::iterator point_iter = point_map.find(pos64); - - if (point_iter != point_map.end()) - { //duplicate point might exist - for (U32 j = 0; j < point_iter->second.size(); ++j) - { - LLVolumeFace::VertexData& tv = (point_iter->second)[j]; - if (tv.compareNormal(cv, angle_cutoff)) - { - found = true; - new_face.pushIndex((point_iter->second)[j].mIndex); - break; - } - } - } - - if (!found) - { - new_face.pushVertex(cv); - U16 index = (U16) new_face.mNumVertices-1; - new_face.pushIndex(index); - - VertexMapData d; - d.setPosition(cv.getPosition()); - d.mTexCoord = cv.mTexCoord; - d.setNormal(cv.getNormal()); - d.mIndex = index; - if (point_iter != point_map.end()) - { - point_iter->second.push_back(d); - } - else - { - point_map[pos64].push_back(d); - } - } - } - - - if (angle_cutoff > 1.f && !mNormals) - { - // Now alloc'd with positions - //ll_aligned_free_16(new_face.mNormals); - new_face.mNormals = NULL; - } - - if (!mTexCoords) - { - // Now alloc'd with positions - //ll_aligned_free_16(new_face.mTexCoords); - new_face.mTexCoords = NULL; - } - - // Only swap data if we've actually optimized the mesh - // - if (new_face.mNumVertices <= mNumVertices) - { - llassert(new_face.mNumIndices == mNumIndices); - swapData(new_face); - } - -} - -class LLVCacheTriangleData; - -class LLVCacheVertexData -{ -public: - S32 mIdx; - S32 mCacheTag; - F64 mScore; - U32 mActiveTriangles; - std::vector<LLVCacheTriangleData*> mTriangles; - - LLVCacheVertexData() - { - mCacheTag = -1; - mScore = 0.0; - mActiveTriangles = 0; - mIdx = -1; - } -}; - -class LLVCacheTriangleData -{ -public: - bool mActive; - F64 mScore; - LLVCacheVertexData* mVertex[3]; - - LLVCacheTriangleData() - { - mActive = true; - mScore = 0.0; - mVertex[0] = mVertex[1] = mVertex[2] = NULL; - } - - void complete() - { - mActive = false; - for (S32 i = 0; i < 3; ++i) - { - if (mVertex[i]) - { - llassert(mVertex[i]->mActiveTriangles > 0); - mVertex[i]->mActiveTriangles--; - } - } - } - - bool operator<(const LLVCacheTriangleData& rhs) const - { //highest score first - return rhs.mScore < mScore; - } -}; - -constexpr F64 FindVertexScore_CacheDecayPower = 1.5; -constexpr F64 FindVertexScore_LastTriScore = 0.75; -constexpr F64 FindVertexScore_ValenceBoostScale = 2.0; -constexpr F64 FindVertexScore_ValenceBoostPower = 0.5; -constexpr U32 MaxSizeVertexCache = 32; -constexpr F64 FindVertexScore_Scaler = 1.0/(MaxSizeVertexCache-3); - -F64 find_vertex_score(LLVCacheVertexData& data) -{ - F64 score = -1.0; - - score = 0.0; - - S32 cache_idx = data.mCacheTag; - - if (cache_idx < 0) - { - //not in cache - } - else - { - if (cache_idx < 3) - { //vertex was in the last triangle - score = FindVertexScore_LastTriScore; - } - else - { //more points for being higher in the cache - score = 1.0-((cache_idx-3)*FindVertexScore_Scaler); - score = pow(score, FindVertexScore_CacheDecayPower); - } - } - - //bonus points for having low valence - F64 valence_boost = pow((F64)data.mActiveTriangles, -FindVertexScore_ValenceBoostPower); - score += FindVertexScore_ValenceBoostScale * valence_boost; - - return score; -} - -class LLVCacheFIFO -{ -public: - LLVCacheVertexData* mCache[MaxSizeVertexCache]; - U32 mMisses; - - LLVCacheFIFO() - { - mMisses = 0; - for (U32 i = 0; i < MaxSizeVertexCache; ++i) - { - mCache[i] = NULL; - } - } - - void addVertex(LLVCacheVertexData* data) - { - if (data->mCacheTag == -1) - { - mMisses++; - - S32 end = MaxSizeVertexCache-1; - - if (mCache[end]) - { - mCache[end]->mCacheTag = -1; - } - - for (S32 i = end; i > 0; --i) - { - mCache[i] = mCache[i-1]; - if (mCache[i]) - { - mCache[i]->mCacheTag = i; - } - } - - mCache[0] = data; - data->mCacheTag = 0; - } - } -}; - -class LLVCacheLRU -{ -public: - LLVCacheVertexData* mCache[MaxSizeVertexCache+3]; - - LLVCacheTriangleData* mBestTriangle; - - U32 mMisses; - - LLVCacheLRU() - { - for (U32 i = 0; i < MaxSizeVertexCache+3; ++i) - { - mCache[i] = NULL; - } - - mBestTriangle = NULL; - mMisses = 0; - } - - void addVertex(LLVCacheVertexData* data) - { - S32 end = MaxSizeVertexCache+2; - if (data->mCacheTag != -1) - { //just moving a vertex to the front of the cache - end = data->mCacheTag; - } - else - { - mMisses++; - if (mCache[end]) - { //adding a new vertex, vertex at end of cache falls off - mCache[end]->mCacheTag = -1; - } - } - - for (S32 i = end; i > 0; --i) - { //adjust cache pointers and tags - mCache[i] = mCache[i-1]; - - if (mCache[i]) - { - mCache[i]->mCacheTag = i; - } - } - - mCache[0] = data; - mCache[0]->mCacheTag = 0; - } - - void addTriangle(LLVCacheTriangleData* data) - { - addVertex(data->mVertex[0]); - addVertex(data->mVertex[1]); - addVertex(data->mVertex[2]); - } - - void updateScores() - { - LLVCacheVertexData** data_iter = mCache+MaxSizeVertexCache; - LLVCacheVertexData** end_data = mCache+MaxSizeVertexCache+3; - - while(data_iter != end_data) - { - LLVCacheVertexData* data = *data_iter++; - //trailing 3 vertices aren't actually in the cache for scoring purposes - if (data) - { - data->mCacheTag = -1; - } - } - - data_iter = mCache; - end_data = mCache+MaxSizeVertexCache; - - while (data_iter != end_data) - { //update scores of vertices in cache - LLVCacheVertexData* data = *data_iter++; - if (data) - { - data->mScore = find_vertex_score(*data); - } - } - - mBestTriangle = NULL; - //update triangle scores - data_iter = mCache; - end_data = mCache+MaxSizeVertexCache+3; - - while (data_iter != end_data) - { - LLVCacheVertexData* data = *data_iter++; - if (data) - { - for (std::vector<LLVCacheTriangleData*>::iterator iter = data->mTriangles.begin(), end_iter = data->mTriangles.end(); iter != end_iter; ++iter) - { - LLVCacheTriangleData* tri = *iter; - if (tri->mActive) - { - tri->mScore = tri->mVertex[0] ? tri->mVertex[0]->mScore : 0; - tri->mScore += tri->mVertex[1] ? tri->mVertex[1]->mScore : 0; - tri->mScore += tri->mVertex[2] ? tri->mVertex[2]->mScore : 0; - - if (!mBestTriangle || mBestTriangle->mScore < tri->mScore) - { - mBestTriangle = tri; - } - } - } - } - } - - //knock trailing 3 vertices off the cache - data_iter = mCache+MaxSizeVertexCache; - end_data = mCache+MaxSizeVertexCache+3; - while (data_iter != end_data) - { - LLVCacheVertexData* data = *data_iter; - if (data) - { - llassert(data->mCacheTag == -1); - *data_iter = NULL; - } - ++data_iter; - } - } -}; - -// data structures for tangent generation - -struct MikktData -{ - LLVolumeFace* face; - std::vector<LLVector3> p; - std::vector<LLVector3> n; - std::vector<LLVector2> tc; - std::vector<LLVector4> w; - std::vector<LLVector4> t; - - MikktData(LLVolumeFace* f) - : face(f) - { - U32 count = face->mNumIndices; - - p.resize(count); - n.resize(count); - tc.resize(count); - t.resize(count); - - if (face->mWeights) - { - w.resize(count); - } - - - LLVector3 inv_scale(1.f / face->mNormalizedScale.mV[0], 1.f / face->mNormalizedScale.mV[1], 1.f / face->mNormalizedScale.mV[2]); - - - for (int i = 0; i < face->mNumIndices; ++i) - { - U32 idx = face->mIndices[i]; - - p[i].set(face->mPositions[idx].getF32ptr()); - p[i].scaleVec(face->mNormalizedScale); //put mesh in original coordinate frame when reconstructing tangents - n[i].set(face->mNormals[idx].getF32ptr()); - n[i].scaleVec(inv_scale); - n[i].normalize(); - tc[i].set(face->mTexCoords[idx]); - - if (idx >= face->mNumVertices) - { - // invalid index - // replace with a valid index to avoid crashes - idx = face->mNumVertices - 1; - face->mIndices[i] = idx; - - // Needs better logging - LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL; - } - - if (face->mWeights) - { - w[i].set(face->mWeights[idx].getF32ptr()); - } - } - } -}; - - -bool LLVolumeFace::cacheOptimize(bool gen_tangents) -{ //optimize for vertex cache according to Forsyth method: - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; - llassert(!mOptimized); - mOptimized = true; - - if (gen_tangents && mNormals && mTexCoords) - { // generate mikkt space tangents before cache optimizing since the index buffer may change - // a bit of a hack to do this here, but this function gets called exactly once for the lifetime of a mesh - // and is executed on a background thread - SMikkTSpaceInterface ms; - - ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - return face->mNumIndices / 3; - }; - - ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) - { - return 3; - }; - - ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - F32* v = data->p[iFace * 3 + iVert].mV; - fvPosOut[0] = v[0]; - fvPosOut[1] = v[1]; - fvPosOut[2] = v[2]; - }; - - ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - F32* n = data->n[iFace * 3 + iVert].mV; - fvNormOut[0] = n[0]; - fvNormOut[1] = n[1]; - fvNormOut[2] = n[2]; - }; - - ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - F32* tc = data->tc[iFace * 3 + iVert].mV; - fvTexcOut[0] = tc[0]; - fvTexcOut[1] = tc[1]; - }; - - ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - S32 i = iFace * 3 + iVert; - - data->t[i].set(fvTangent); - data->t[i].mV[3] = fSign; - }; - - ms.m_setTSpace = nullptr; - - MikktData data(this); - - SMikkTSpaceContext ctx = { &ms, &data }; - - genTangSpaceDefault(&ctx); - - //re-weld - meshopt_Stream mos[] = - { - { &data.p[0], sizeof(LLVector3), sizeof(LLVector3) }, - { &data.n[0], sizeof(LLVector3), sizeof(LLVector3) }, - { &data.t[0], sizeof(LLVector4), sizeof(LLVector4) }, - { &data.tc[0], sizeof(LLVector2), sizeof(LLVector2) }, - { data.w.empty() ? nullptr : &data.w[0], sizeof(LLVector4), sizeof(LLVector4) } - }; - - std::vector<U32> remap; - remap.resize(data.p.size()); - - U32 stream_count = data.w.empty() ? 4 : 5; - - size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count); - - if (vert_count < 65535 && vert_count != 0) - { - std::vector<U32> indices; - indices.resize(mNumIndices); - - //copy results back into volume - resizeVertices(vert_count); - - if (!data.w.empty()) - { - allocateWeights(vert_count); - } - - allocateTangents(mNumVertices); - - for (int i = 0; i < mNumIndices; ++i) - { - U32 src_idx = i; - U32 dst_idx = remap[i]; - if (dst_idx >= mNumVertices) - { - dst_idx = mNumVertices - 1; - // Shouldn't happen, figure out what gets returned in remap and why. - llassert(false); - LL_DEBUGS_ONCE("LLVOLUME") << "Invalid destination index, substituting" << LL_ENDL; - } - mIndices[i] = dst_idx; - - mPositions[dst_idx].load3(data.p[src_idx].mV); - mNormals[dst_idx].load3(data.n[src_idx].mV); - mTexCoords[dst_idx] = data.tc[src_idx]; - - mTangents[dst_idx].loadua(data.t[src_idx].mV); - - if (mWeights) - { - mWeights[dst_idx].loadua(data.w[src_idx].mV); - } - } - - // put back in normalized coordinate frame - LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]); - LLVector4a scale; - scale.load3(mNormalizedScale.mV); - scale.getF32ptr()[3] = 1.f; - - for (int i = 0; i < mNumVertices; ++i) - { - mPositions[i].mul(inv_scale); - mNormals[i].mul(scale); - mNormals[i].normalize3(); - F32 w = mTangents[i].getF32ptr()[3]; - mTangents[i].mul(scale); - mTangents[i].normalize3(); - mTangents[i].getF32ptr()[3] = w; - } - } - else - { - if (vert_count == 0) - { - LL_WARNS_ONCE("LLVOLUME") << "meshopt_generateVertexRemapMulti failed to process a model or model was invalid" << LL_ENDL; - } - // blew past the max vertex size limit, use legacy tangent generation which never adds verts - createTangents(); - } - } - - // cache optimize index buffer - - // meshopt needs scratch space, do some pointer shuffling to avoid an extra index buffer copy - U16* src_indices = mIndices; - mIndices = nullptr; - resizeIndices(mNumIndices); - - meshopt_optimizeVertexCache<U16>(mIndices, src_indices, mNumIndices, mNumVertices); - - ll_aligned_free_16(src_indices); - - return true; -} - -void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVector4a& size) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - if (getOctree()) - { - return; - } - - llassert(mNumIndices % 3 == 0); - - mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL); - new LLVolumeOctreeListener(mOctree); - const U32 num_triangles = mNumIndices / 3; - // Initialize all the triangles we need - mOctreeTriangles = new LLVolumeTriangle[num_triangles]; - - for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index) - { //for each triangle - const U32 index = triangle_index * 3; - LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index]; - - const LLVector4a& v0 = mPositions[mIndices[index]]; - const LLVector4a& v1 = mPositions[mIndices[index + 1]]; - const LLVector4a& v2 = mPositions[mIndices[index + 2]]; - - //store pointers to vertex data - tri->mV[0] = &v0; - tri->mV[1] = &v1; - tri->mV[2] = &v2; - - //store indices - tri->mIndex[0] = mIndices[index]; - tri->mIndex[1] = mIndices[index + 1]; - tri->mIndex[2] = mIndices[index + 2]; - - //get minimum point - LLVector4a min = v0; - min.setMin(min, v1); - min.setMin(min, v2); - - //get maximum point - LLVector4a max = v0; - max.setMax(max, v1); - max.setMax(max, v2); - - //compute center - LLVector4a center; - center.setAdd(min, max); - center.mul(0.5f); - - tri->mPositionGroup = center; - - //compute "radius" - LLVector4a size; - size.setSub(max,min); - - tri->mRadius = size.getLength3().getF32() * scaler; - - //insert - mOctree->insert(tri); - } - - //remove unneeded octree layers - while (!mOctree->balance()) { } - - //calculate AABB for each node - LLVolumeOctreeRebound rebound(this); - rebound.traverse(mOctree); - - if (gDebugGL) - { - LLVolumeOctreeValidate validate; - validate.traverse(mOctree); - } -} - -void LLVolumeFace::destroyOctree() -{ - delete mOctree; - mOctree = NULL; - delete[] mOctreeTriangles; - mOctreeTriangles = NULL; -} - -const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const -{ - return mOctree; -} - - -void LLVolumeFace::swapData(LLVolumeFace& rhs) -{ - llswap(rhs.mPositions, mPositions); - llswap(rhs.mNormals, mNormals); - llswap(rhs.mTangents, mTangents); - llswap(rhs.mTexCoords, mTexCoords); - llswap(rhs.mIndices,mIndices); - llswap(rhs.mNumVertices, mNumVertices); - llswap(rhs.mNumIndices, mNumIndices); -} - -void LerpPlanarVertex(LLVolumeFace::VertexData& v0, - LLVolumeFace::VertexData& v1, - LLVolumeFace::VertexData& v2, - LLVolumeFace::VertexData& vout, - F32 coef01, - F32 coef02) -{ - - LLVector4a lhs; - lhs.setSub(v1.getPosition(), v0.getPosition()); - lhs.mul(coef01); - LLVector4a rhs; - rhs.setSub(v2.getPosition(), v0.getPosition()); - rhs.mul(coef02); - - rhs.add(lhs); - rhs.add(v0.getPosition()); - - vout.setPosition(rhs); - - vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02); - vout.setNormal(v0.getNormal()); -} - -bool LLVolumeFace::createUnCutCubeCap(LLVolume* volume, bool partial_build) -{ - LL_CHECK_MEMORY - - const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh(); - const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile; - S32 max_s = volume->getProfile().getTotal(); - S32 max_t = volume->getPath().mPath.size(); - - // S32 i; - S32 grid_size = (profile.size()-1)/4; - // VFExtents change - LLVector4a& min = mExtents[0]; - LLVector4a& max = mExtents[1]; - - S32 offset = 0; - if (mTypeMask & TOP_MASK) - { - offset = (max_t-1) * max_s; - } - else - { - offset = mBeginS; - } - - { - VertexData corners[4]; - VertexData baseVert; - for(S32 t = 0; t < 4; t++) - { - corners[t].getPosition().load4a(mesh[offset + (grid_size*t)].getF32ptr()); - corners[t].mTexCoord.mV[0] = profile[grid_size*t][0]+0.5f; - corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t][1]; - } - - { - LLVector4a lhs; - lhs.setSub(corners[1].getPosition(), corners[0].getPosition()); - LLVector4a rhs; - rhs.setSub(corners[2].getPosition(), corners[1].getPosition()); - baseVert.getNormal().setCross3(lhs, rhs); - baseVert.getNormal().normalize3fast(); - } - - if(!(mTypeMask & TOP_MASK)) - { - baseVert.getNormal().mul(-1.0f); - } - else - { - //Swap the UVs on the U(X) axis for top face - LLVector2 swap; - swap = corners[0].mTexCoord; - corners[0].mTexCoord=corners[3].mTexCoord; - corners[3].mTexCoord=swap; - swap = corners[1].mTexCoord; - corners[1].mTexCoord=corners[2].mTexCoord; - corners[2].mTexCoord=swap; - } - - S32 size = (grid_size+1)*(grid_size+1); - resizeVertices(size); - - LLVector4a* pos = (LLVector4a*) mPositions; - LLVector4a* norm = (LLVector4a*) mNormals; - LLVector2* tc = (LLVector2*) mTexCoords; - - for(int gx = 0;gx<grid_size+1;gx++) - { - for(int gy = 0;gy<grid_size+1;gy++) - { - VertexData newVert; - LerpPlanarVertex( - corners[0], - corners[1], - corners[3], - newVert, - (F32)gx/(F32)grid_size, - (F32)gy/(F32)grid_size); - - *pos++ = newVert.getPosition(); - *norm++ = baseVert.getNormal(); - *tc++ = newVert.mTexCoord; - - if (gx == 0 && gy == 0) - { - min = newVert.getPosition(); - max = min; - } - else - { - min.setMin(min, newVert.getPosition()); - max.setMax(max, newVert.getPosition()); - } - } - } - - mCenter->setAdd(min, max); - mCenter->mul(0.5f); - } - - if (!partial_build) - { - resizeIndices(grid_size*grid_size*6); - if (!volume->isMeshAssetLoaded()) - { - S32 size = grid_size * grid_size * 6; - try - { - mEdge.resize(size); - } - catch (std::bad_alloc&) - { - LL_WARNS("LLVOLUME") << "Resize of mEdge to " << size << " failed" << LL_ENDL; - return false; - } - } - - U16* out = mIndices; - - S32 idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0}; - - int cur_edge = 0; - - for(S32 gx = 0;gx<grid_size;gx++) - { - - for(S32 gy = 0;gy<grid_size;gy++) - { - if (mTypeMask & TOP_MASK) - { - for(S32 i=5;i>=0;i--) - { - *out++ = ((gy*(grid_size+1))+gx+idxs[i]); - } - - S32 edge_value = grid_size * 2 * gy + gx * 2; - - if (gx > 0) - { - mEdge[cur_edge++] = edge_value; - } - else - { - mEdge[cur_edge++] = -1; // Mark face to higlight it - } - - if (gy < grid_size - 1) - { - mEdge[cur_edge++] = edge_value; - } - else - { - mEdge[cur_edge++] = -1; - } - - mEdge[cur_edge++] = edge_value; - - if (gx < grid_size - 1) - { - mEdge[cur_edge++] = edge_value; - } - else - { - mEdge[cur_edge++] = -1; - } - - if (gy > 0) - { - mEdge[cur_edge++] = edge_value; - } - else - { - mEdge[cur_edge++] = -1; - } - - mEdge[cur_edge++] = edge_value; - } - else - { - for(S32 i=0;i<6;i++) - { - *out++ = ((gy*(grid_size+1))+gx+idxs[i]); - } - - S32 edge_value = grid_size * 2 * gy + gx * 2; - - if (gy > 0) - { - mEdge[cur_edge++] = edge_value; - } - else - { - mEdge[cur_edge++] = -1; - } - - if (gx < grid_size - 1) - { - mEdge[cur_edge++] = edge_value; - } - else - { - mEdge[cur_edge++] = -1; - } - - mEdge[cur_edge++] = edge_value; - - if (gy < grid_size - 1) - { - mEdge[cur_edge++] = edge_value; - } - else - { - mEdge[cur_edge++] = -1; - } - - if (gx > 0) - { - mEdge[cur_edge++] = edge_value; - } - else - { - mEdge[cur_edge++] = -1; - } - - mEdge[cur_edge++] = edge_value; - } - } - } - } - - LL_CHECK_MEMORY - return true; -} - - -bool LLVolumeFace::createCap(LLVolume* volume, bool partial_build) -{ - if (!(mTypeMask & HOLLOW_MASK) && - !(mTypeMask & OPEN_MASK) && - ((volume->getParams().getPathParams().getBegin()==0.0f)&& - (volume->getParams().getPathParams().getEnd()==1.0f))&& - (volume->getParams().getProfileParams().getCurveType()==LL_PCODE_PROFILE_SQUARE && - volume->getParams().getPathParams().getCurveType()==LL_PCODE_PATH_LINE) - ){ - return createUnCutCubeCap(volume, partial_build); - } - - S32 num_vertices = 0, num_indices = 0; - - const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh(); - const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile; - - // All types of caps have the same number of vertices and indices - num_vertices = profile.size(); - num_indices = (profile.size() - 2)*3; - - if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK)) - { - resizeVertices(num_vertices+1); - - //if (!partial_build) - { - resizeIndices(num_indices+3); - } - } - else - { - resizeVertices(num_vertices); - //if (!partial_build) - { - resizeIndices(num_indices); - } - } - - LL_CHECK_MEMORY; - - S32 max_s = volume->getProfile().getTotal(); - S32 max_t = volume->getPath().mPath.size(); - - mCenter->clear(); - - S32 offset = 0; - if (mTypeMask & TOP_MASK) - { - offset = (max_t-1) * max_s; - } - else - { - offset = mBeginS; - } - - // Figure out the normal, assume all caps are flat faces. - // Cross product to get normals. - - LLVector2 cuv; - LLVector2 min_uv, max_uv; - // VFExtents change - LLVector4a& min = mExtents[0]; - LLVector4a& max = mExtents[1]; - - LLVector2* tc = (LLVector2*) mTexCoords; - LLVector4a* pos = (LLVector4a*) mPositions; - LLVector4a* norm = (LLVector4a*) mNormals; - - // Copy the vertices into the array - - const LLVector4a* src = mesh.mArray+offset; - const LLVector4a* end = src+num_vertices; - - min = *src; - max = min; - - - const LLVector4a* p = profile.mArray; - - if (mTypeMask & TOP_MASK) - { - min_uv.set((*p)[0]+0.5f, - (*p)[1]+0.5f); - - max_uv = min_uv; - - while(src < end) - { - tc->mV[0] = (*p)[0]+0.5f; - tc->mV[1] = (*p)[1]+0.5f; - - llassert(src->isFinite3()); // MAINT-5660; don't know why this happens, does not affect Release builds - update_min_max(min,max,*src); - update_min_max(min_uv, max_uv, *tc); - - *pos = *src; - - llassert(pos->isFinite3()); - - ++p; - ++tc; - ++src; - ++pos; - } - } - else - { - - min_uv.set((*p)[0]+0.5f, - 0.5f - (*p)[1]); - max_uv = min_uv; - - while(src < end) - { - // Mirror for underside. - tc->mV[0] = (*p)[0]+0.5f; - tc->mV[1] = 0.5f - (*p)[1]; - - llassert(src->isFinite3()); - update_min_max(min,max,*src); - update_min_max(min_uv, max_uv, *tc); - - *pos = *src; - - llassert(pos->isFinite3()); - - ++p; - ++tc; - ++src; - ++pos; - } - } - - LL_CHECK_MEMORY - - mCenter->setAdd(min, max); - mCenter->mul(0.5f); - - cuv = (min_uv + max_uv)*0.5f; - - - VertexData vd; - vd.setPosition(*mCenter); - vd.mTexCoord = cuv; - - if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK)) - { - *pos++ = *mCenter; - *tc++ = cuv; - num_vertices++; - } - - LL_CHECK_MEMORY - - //if (partial_build) - //{ - // return true; - //} - - if (mTypeMask & HOLLOW_MASK) - { - if (mTypeMask & TOP_MASK) - { - // HOLLOW TOP - // Does it matter if it's open or closed? - djs - - S32 pt1 = 0, pt2 = num_vertices - 1; - S32 i = 0; - while (pt2 - pt1 > 1) - { - // Use the profile points instead of the mesh, since you want - // the un-transformed profile distances. - const LLVector4a& p1 = profile[pt1]; - const LLVector4a& p2 = profile[pt2]; - const LLVector4a& pa = profile[pt1+1]; - const LLVector4a& pb = profile[pt2-1]; - - const F32* p1V = p1.getF32ptr(); - const F32* p2V = p2.getF32ptr(); - const F32* paV = pa.getF32ptr(); - const F32* pbV = pb.getF32ptr(); - - //p1.mV[VZ] = 0.f; - //p2.mV[VZ] = 0.f; - //pa.mV[VZ] = 0.f; - //pb.mV[VZ] = 0.f; - - // Use area of triangle to determine backfacing - F32 area_1a2, area_1ba, area_21b, area_2ab; - area_1a2 = (p1V[0]*paV[1] - paV[0]*p1V[1]) + - (paV[0]*p2V[1] - p2V[0]*paV[1]) + - (p2V[0]*p1V[1] - p1V[0]*p2V[1]); - - area_1ba = (p1V[0]*pbV[1] - pbV[0]*p1V[1]) + - (pbV[0]*paV[1] - paV[0]*pbV[1]) + - (paV[0]*p1V[1] - p1V[0]*paV[1]); - - area_21b = (p2V[0]*p1V[1] - p1V[0]*p2V[1]) + - (p1V[0]*pbV[1] - pbV[0]*p1V[1]) + - (pbV[0]*p2V[1] - p2V[0]*pbV[1]); - - area_2ab = (p2V[0]*paV[1] - paV[0]*p2V[1]) + - (paV[0]*pbV[1] - pbV[0]*paV[1]) + - (pbV[0]*p2V[1] - p2V[0]*pbV[1]); - - bool use_tri1a2 = true; - bool tri_1a2 = true; - bool tri_21b = true; - - if (area_1a2 < 0) - { - tri_1a2 = false; - } - if (area_2ab < 0) - { - // Can't use, because it contains point b - tri_1a2 = false; - } - if (area_21b < 0) - { - tri_21b = false; - } - if (area_1ba < 0) - { - // Can't use, because it contains point b - tri_21b = false; - } - - if (!tri_1a2) - { - use_tri1a2 = false; - } - else if (!tri_21b) - { - use_tri1a2 = true; - } - else - { - LLVector4a d1; - d1.setSub(p1, pa); - - LLVector4a d2; - d2.setSub(p2, pb); - - if (d1.dot3(d1) < d2.dot3(d2)) - { - use_tri1a2 = true; - } - else - { - use_tri1a2 = false; - } - } - - if (use_tri1a2) - { - mIndices[i++] = pt1; - mIndices[i++] = pt1 + 1; - mIndices[i++] = pt2; - pt1++; - } - else - { - mIndices[i++] = pt1; - mIndices[i++] = pt2 - 1; - mIndices[i++] = pt2; - pt2--; - } - } - } - else - { - // HOLLOW BOTTOM - // Does it matter if it's open or closed? - djs - - llassert(mTypeMask & BOTTOM_MASK); - S32 pt1 = 0, pt2 = num_vertices - 1; - - S32 i = 0; - while (pt2 - pt1 > 1) - { - // Use the profile points instead of the mesh, since you want - // the un-transformed profile distances. - const LLVector4a& p1 = profile[pt1]; - const LLVector4a& p2 = profile[pt2]; - const LLVector4a& pa = profile[pt1+1]; - const LLVector4a& pb = profile[pt2-1]; - - const F32* p1V = p1.getF32ptr(); - const F32* p2V = p2.getF32ptr(); - const F32* paV = pa.getF32ptr(); - const F32* pbV = pb.getF32ptr(); - - // Use area of triangle to determine backfacing - F32 area_1a2, area_1ba, area_21b, area_2ab; - area_1a2 = (p1V[0]*paV[1] - paV[0]*p1V[1]) + - (paV[0]*p2V[1] - p2V[0]*paV[1]) + - (p2V[0]*p1V[1] - p1V[0]*p2V[1]); - - area_1ba = (p1V[0]*pbV[1] - pbV[0]*p1V[1]) + - (pbV[0]*paV[1] - paV[0]*pbV[1]) + - (paV[0]*p1V[1] - p1V[0]*paV[1]); - - area_21b = (p2V[0]*p1V[1] - p1V[0]*p2V[1]) + - (p1V[0]*pbV[1] - pbV[0]*p1V[1]) + - (pbV[0]*p2V[1] - p2V[0]*pbV[1]); - - area_2ab = (p2V[0]*paV[1] - paV[0]*p2V[1]) + - (paV[0]*pbV[1] - pbV[0]*paV[1]) + - (pbV[0]*p2V[1] - p2V[0]*pbV[1]); - - bool use_tri1a2 = true; - bool tri_1a2 = true; - bool tri_21b = true; - - if (area_1a2 < 0) - { - tri_1a2 = false; - } - if (area_2ab < 0) - { - // Can't use, because it contains point b - tri_1a2 = false; - } - if (area_21b < 0) - { - tri_21b = false; - } - if (area_1ba < 0) - { - // Can't use, because it contains point b - tri_21b = false; - } - - if (!tri_1a2) - { - use_tri1a2 = false; - } - else if (!tri_21b) - { - use_tri1a2 = true; - } - else - { - LLVector4a d1; - d1.setSub(p1,pa); - LLVector4a d2; - d2.setSub(p2,pb); - - if (d1.dot3(d1) < d2.dot3(d2)) - { - use_tri1a2 = true; - } - else - { - use_tri1a2 = false; - } - } - - // Flipped backfacing from top - if (use_tri1a2) - { - mIndices[i++] = pt1; - mIndices[i++] = pt2; - mIndices[i++] = pt1 + 1; - pt1++; - } - else - { - mIndices[i++] = pt1; - mIndices[i++] = pt2; - mIndices[i++] = pt2 - 1; - pt2--; - } - } - } - } - else - { - // Not hollow, generate the triangle fan. - U16 v1 = 2; - U16 v2 = 1; - - if (mTypeMask & TOP_MASK) - { - v1 = 1; - v2 = 2; - } - - for (S32 i = 0; i < (num_vertices - 2); i++) - { - mIndices[3*i] = num_vertices - 1; - mIndices[3*i+v1] = i; - mIndices[3*i+v2] = i + 1; - } - - - } - - LLVector4a d0,d1; - LL_CHECK_MEMORY - - - d0.setSub(mPositions[mIndices[1]], mPositions[mIndices[0]]); - d1.setSub(mPositions[mIndices[2]], mPositions[mIndices[0]]); - - LLVector4a normal; - normal.setCross3(d0,d1); - - if (normal.dot3(normal).getF32() > F_APPROXIMATELY_ZERO) - { - normal.normalize3fast(); - } - else - { //degenerate, make up a value - if(normal.getF32ptr()[2] >= 0) - normal.set(0.f,0.f,1.f); - else - normal.set(0.f,0.f,-1.f); - } - - llassert(llfinite(normal.getF32ptr()[0])); - llassert(llfinite(normal.getF32ptr()[1])); - llassert(llfinite(normal.getF32ptr()[2])); - - llassert(!llisnan(normal.getF32ptr()[0])); - llassert(!llisnan(normal.getF32ptr()[1])); - llassert(!llisnan(normal.getF32ptr()[2])); - - for (S32 i = 0; i < num_vertices; i++) - { - norm[i].load4a(normal.getF32ptr()); - } - - return true; -} - -void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, - const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent); - -void LLVolumeFace::createTangents() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; - - if (!mTangents) - { - allocateTangents(mNumVertices); - - //generate tangents - LLVector4a* ptr = (LLVector4a*)mTangents; - - LLVector4a* end = mTangents + mNumVertices; - while (ptr < end) - { - (*ptr++).clear(); - } - - CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents); - - //normalize normals - for (U32 i = 0; i < mNumVertices; i++) - { - //bump map/planar projection code requires normals to be normalized - mNormals[i].normalize3fast(); - } - } - -} - -void LLVolumeFace::resizeVertices(S32 num_verts) -{ - ll_aligned_free<64>(mPositions); - //DO NOT free mNormals and mTexCoords as they are part of mPositions buffer - ll_aligned_free_16(mTangents); - - mTangents = NULL; - - if (num_verts) - { - //pad texture coordinate block end to allow for QWORD reads - S32 tc_size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF; - - mPositions = (LLVector4a*) ll_aligned_malloc<64>(sizeof(LLVector4a)*2*num_verts+tc_size); - mNormals = mPositions+num_verts; - mTexCoords = (LLVector2*) (mNormals+num_verts); - - ll_assert_aligned(mPositions, 64); - } - else - { - mPositions = NULL; - mNormals = NULL; - mTexCoords = NULL; - } - - - if (mPositions) - { - mNumVertices = num_verts; - mNumAllocatedVertices = num_verts; - } - else - { - // Either num_verts is zero or allocation failure - mNumVertices = 0; - mNumAllocatedVertices = 0; - } - - // Force update - mJointRiggingInfoTab.clear(); -} - -void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv) -{ - pushVertex(cv.getPosition(), cv.getNormal(), cv.mTexCoord); -} - -void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc) -{ - S32 new_verts = mNumVertices+1; - - if (new_verts > mNumAllocatedVertices) - { - // double buffer size on expansion - new_verts *= 2; - - S32 new_tc_size = ((new_verts*8)+0xF) & ~0xF; - S32 old_tc_size = ((mNumVertices*8)+0xF) & ~0xF; - - S32 old_vsize = mNumVertices*16; - - S32 new_size = new_verts*16*2+new_tc_size; - - LLVector4a* old_buf = mPositions; - - mPositions = (LLVector4a*) ll_aligned_malloc<64>(new_size); - mNormals = mPositions+new_verts; - mTexCoords = (LLVector2*) (mNormals+new_verts); - - if (old_buf != NULL) - { - // copy old positions into new buffer - LLVector4a::memcpyNonAliased16((F32*)mPositions, (F32*)old_buf, old_vsize); - - // normals - LLVector4a::memcpyNonAliased16((F32*)mNormals, (F32*)(old_buf + mNumVertices), old_vsize); - - // tex coords - LLVector4a::memcpyNonAliased16((F32*)mTexCoords, (F32*)(old_buf + mNumVertices * 2), old_tc_size); - } - - // just clear tangents - ll_aligned_free_16(mTangents); - mTangents = NULL; - ll_aligned_free<64>(old_buf); - - mNumAllocatedVertices = new_verts; - - } - - mPositions[mNumVertices] = pos; - mNormals[mNumVertices] = norm; - mTexCoords[mNumVertices] = tc; - - mNumVertices++; -} - -void LLVolumeFace::allocateTangents(S32 num_verts) -{ - ll_aligned_free_16(mTangents); - mTangents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); -} - -void LLVolumeFace::allocateWeights(S32 num_verts) -{ - ll_aligned_free_16(mWeights); - mWeights = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); - -} - -void LLVolumeFace::allocateJointIndices(S32 num_verts) -{ -#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS - ll_aligned_free_16(mJointIndices); - ll_aligned_free_16(mJustWeights); - - mJointIndices = (U8*)ll_aligned_malloc_16(sizeof(U8) * 4 * num_verts); - mJustWeights = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a) * num_verts); -#endif -} - -void LLVolumeFace::resizeIndices(S32 num_indices) -{ - ll_aligned_free_16(mIndices); - llassert(num_indices % 3 == 0); - - if (num_indices) - { - //pad index block end to allow for QWORD reads - S32 size = ((num_indices*sizeof(U16)) + 0xF) & ~0xF; - - mIndices = (U16*) ll_aligned_malloc_16(size); - } - else - { - mIndices = NULL; - } - - if (mIndices) - { - mNumIndices = num_indices; - } - else - { - // Either num_indices is zero or allocation failure - mNumIndices = 0; - } -} - -void LLVolumeFace::pushIndex(const U16& idx) -{ - S32 new_count = mNumIndices + 1; - S32 new_size = ((new_count*2)+0xF) & ~0xF; - - S32 old_size = ((mNumIndices*2)+0xF) & ~0xF; - if (new_size != old_size) - { - mIndices = (U16*) ll_aligned_realloc_16(mIndices, new_size, old_size); - ll_assert_aligned(mIndices,16); - } - - mIndices[mNumIndices++] = idx; -} - -void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx) -{ - resizeVertices(v.size()); - resizeIndices(idx.size()); - - for (U32 i = 0; i < v.size(); ++i) - { - mPositions[i] = v[i].getPosition(); - mNormals[i] = v[i].getNormal(); - mTexCoords[i] = v[i].mTexCoord; - } - - for (U32 i = 0; i < idx.size(); ++i) - { - mIndices[i] = idx[i]; - } -} - -bool LLVolumeFace::createSide(LLVolume* volume, bool partial_build) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - LL_CHECK_MEMORY - bool flat = mTypeMask & FLAT_MASK; - - U8 sculpt_type = volume->getParams().getSculptType(); - U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK; - bool sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT; - bool sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR; - bool sculpt_reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR - - S32 num_vertices, num_indices; - - const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh(); - const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile; - const LLAlignedArray<LLPath::PathPt,64>& path_data = volume->getPath().mPath; - - S32 max_s = volume->getProfile().getTotal(); - - S32 s, t, i; - F32 ss, tt; - - num_vertices = mNumS*mNumT; - num_indices = (mNumS-1)*(mNumT-1)*6; - - partial_build = (num_vertices > mNumVertices || num_indices > mNumIndices) ? false : partial_build; - - if (!partial_build) - { - resizeVertices(num_vertices); - resizeIndices(num_indices); - - if (!volume->isMeshAssetLoaded()) - { - try - { - mEdge.resize(num_indices); - } - catch (std::bad_alloc&) - { - LL_WARNS("LLVOLUME") << "Resize of mEdge to " << num_indices << " failed" << LL_ENDL; - return false; - } - } - } - - LL_CHECK_MEMORY - - LLVector4a* pos = (LLVector4a*) mPositions; - LLVector2* tc = (LLVector2*) mTexCoords; - F32 begin_stex = floorf(profile[mBeginS][2]); - S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS; - - S32 cur_vertex = 0; - S32 end_t = mBeginT+mNumT; - bool test = (mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2; - - // Copy the vertices into the array - for (t = mBeginT; t < end_t; t++) - { - tt = path_data[t].mTexT; - for (s = 0; s < num_s; s++) - { - if (mTypeMask & END_MASK) - { - if (s) - { - ss = 1.f; - } - else - { - ss = 0.f; - } - } - else - { - // Get s value for tex-coord. - S32 index = mBeginS + s; - if (index >= profile.size()) - { - // edge? - ss = flat ? 1.f - begin_stex : 1.f; - } - else if (!flat) - { - ss = profile[index][2]; - } - else - { - ss = profile[index][2] - begin_stex; - } - } - - if (sculpt_reverse_horizontal) - { - ss = 1.f - ss; - } - - // Check to see if this triangle wraps around the array. - if (mBeginS + s >= max_s) - { - // We're wrapping - i = mBeginS + s + max_s*(t-1); - } - else - { - i = mBeginS + s + max_s*t; - } - - mesh[i].store4a((F32*)(pos+cur_vertex)); - tc[cur_vertex].set(ss,tt); - - cur_vertex++; - - if (test && s > 0) - { - mesh[i].store4a((F32*)(pos+cur_vertex)); - tc[cur_vertex].set(ss,tt); - cur_vertex++; - } - } - - if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) - { - if (mTypeMask & OPEN_MASK) - { - s = num_s-1; - } - else - { - s = 0; - } - - i = mBeginS + s + max_s*t; - ss = profile[mBeginS + s][2] - begin_stex; - - mesh[i].store4a((F32*)(pos+cur_vertex)); - tc[cur_vertex].set(ss,tt); - - cur_vertex++; - } - } - LL_CHECK_MEMORY - - mCenter->clear(); - - LLVector4a* cur_pos = pos; - LLVector4a* end_pos = pos + mNumVertices; - - //get bounding box for this side - LLVector4a face_min; - LLVector4a face_max; - - face_min = face_max = *cur_pos++; - - while (cur_pos < end_pos) - { - update_min_max(face_min, face_max, *cur_pos++); - } - // VFExtents change - mExtents[0] = face_min; - mExtents[1] = face_max; - - U32 tc_count = mNumVertices; - if (tc_count%2 == 1) - { //odd number of texture coordinates, duplicate last entry to padded end of array - tc_count++; - mTexCoords[mNumVertices] = mTexCoords[mNumVertices-1]; - } - - LLVector4a* cur_tc = (LLVector4a*) mTexCoords; - LLVector4a* end_tc = (LLVector4a*) (mTexCoords+tc_count); - - LLVector4a tc_min; - LLVector4a tc_max; - - tc_min = tc_max = *cur_tc++; - - while (cur_tc < end_tc) - { - update_min_max(tc_min, tc_max, *cur_tc++); - } - - F32* minp = tc_min.getF32ptr(); - F32* maxp = tc_max.getF32ptr(); - - mTexCoordExtents[0].mV[0] = llmin(minp[0], minp[2]); - mTexCoordExtents[0].mV[1] = llmin(minp[1], minp[3]); - mTexCoordExtents[1].mV[0] = llmax(maxp[0], maxp[2]); - mTexCoordExtents[1].mV[1] = llmax(maxp[1], maxp[3]); - - mCenter->setAdd(face_min, face_max); - mCenter->mul(0.5f); - - S32 cur_index = 0; - S32 cur_edge = 0; - bool flat_face = mTypeMask & FLAT_MASK; - - if (!partial_build) - { - // Now we generate the indices. - for (t = 0; t < (mNumT-1); t++) - { - for (s = 0; s < (mNumS-1); s++) - { - mIndices[cur_index++] = s + mNumS*t; //bottom left - mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right - mIndices[cur_index++] = s + mNumS*(t+1); //top left - mIndices[cur_index++] = s + mNumS*t; //bottom left - mIndices[cur_index++] = s+1 + mNumS*t; //bottom right - mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right - - // bottom left/top right neighbor face - mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1; - - if (t < mNumT-2) - { // top right/top left neighbor face - mEdge[cur_edge++] = (mNumS-1)*2*(t+1)+s*2+1; - } - else if (mNumT <= 3 || volume->getPath().isOpen()) - { // no neighbor - mEdge[cur_edge++] = -1; - } - else - { // wrap on T - mEdge[cur_edge++] = s*2+1; - } - - if (s > 0) - { // top left/bottom left neighbor face - mEdge[cur_edge++] = (mNumS-1)*2*t+s*2-1; - } - else if (flat_face || volume->getProfile().isOpen()) - { // no neighbor - mEdge[cur_edge++] = -1; - } - else - { // wrap on S - mEdge[cur_edge++] = (mNumS-1)*2*t+(mNumS-2)*2+1; - } - - if (t > 0) - { // bottom left/bottom right neighbor face - mEdge[cur_edge++] = (mNumS-1)*2*(t-1)+s*2; - } - else if (mNumT <= 3 || volume->getPath().isOpen()) - { // no neighbor - mEdge[cur_edge++] = -1; - } - else - { // wrap on T - mEdge[cur_edge++] = (mNumS-1)*2*(mNumT-2)+s*2; - } - - if (s < mNumS-2) - { // bottom right/top right neighbor face - mEdge[cur_edge++] = (mNumS-1)*2*t+(s+1)*2; - } - else if (flat_face || volume->getProfile().isOpen()) - { // no neighbor - mEdge[cur_edge++] = -1; - } - else - { // wrap on S - mEdge[cur_edge++] = (mNumS-1)*2*t; - } - - // top right/bottom left neighbor face - mEdge[cur_edge++] = (mNumS-1)*2*t+s*2; - } - } - } - - LL_CHECK_MEMORY - - //clear normals - F32* dst = (F32*) mNormals; - F32* end = (F32*) (mNormals+mNumVertices); - LLVector4a zero = LLVector4a::getZero(); - - while (dst < end) - { - zero.store4a(dst); - dst += 4; - } - - LL_CHECK_MEMORY - - //generate normals - U32 count = mNumIndices/3; - - LLVector4a* norm = mNormals; - - static thread_local LLAlignedArray<LLVector4a, 64> triangle_normals; - try - { - triangle_normals.resize(count); - } - catch (std::bad_alloc&) - { - LL_WARNS("LLVOLUME") << "Resize of triangle_normals to " << count << " failed" << LL_ENDL; - return false; - } - LLVector4a* output = triangle_normals.mArray; - LLVector4a* end_output = output+count; - - U16* idx = mIndices; - - while (output < end_output) - { - LLVector4a b,v1,v2; - b.load4a((F32*) (pos+idx[0])); - v1.load4a((F32*) (pos+idx[1])); - v2.load4a((F32*) (pos+idx[2])); - - //calculate triangle normal - LLVector4a a; - - a.setSub(b, v1); - b.sub(v2); - - - LLQuad& vector1 = *((LLQuad*) &v1); - LLQuad& vector2 = *((LLQuad*) &v2); - - LLQuad& amQ = *((LLQuad*) &a); - LLQuad& bmQ = *((LLQuad*) &b); - - //v1.setCross3(t,v0); - //setCross3(const LLVector4a& a, const LLVector4a& b) - // Vectors are stored in memory in w, z, y, x order from high to low - // Set vector1 = { a[W], a[X], a[Z], a[Y] } - vector1 = _mm_shuffle_ps( amQ, amQ, _MM_SHUFFLE( 3, 0, 2, 1 )); - // Set vector2 = { b[W], b[Y], b[X], b[Z] } - vector2 = _mm_shuffle_ps( bmQ, bmQ, _MM_SHUFFLE( 3, 1, 0, 2 )); - // mQ = { a[W]*b[W], a[X]*b[Y], a[Z]*b[X], a[Y]*b[Z] } - vector2 = _mm_mul_ps( vector1, vector2 ); - // vector3 = { a[W], a[Y], a[X], a[Z] } - amQ = _mm_shuffle_ps( amQ, amQ, _MM_SHUFFLE( 3, 1, 0, 2 )); - // vector4 = { b[W], b[X], b[Z], b[Y] } - bmQ = _mm_shuffle_ps( bmQ, bmQ, _MM_SHUFFLE( 3, 0, 2, 1 )); - // mQ = { 0, a[X]*b[Y] - a[Y]*b[X], a[Z]*b[X] - a[X]*b[Z], a[Y]*b[Z] - a[Z]*b[Y] } - vector1 = _mm_sub_ps( vector2, _mm_mul_ps( amQ, bmQ )); - - llassert(v1.isFinite3()); - - v1.store4a((F32*) output); - - - output++; - idx += 3; - } - - idx = mIndices; - - LLVector4a* src = triangle_normals.mArray; - - for (U32 i = 0; i < count; i++) //for each triangle - { - LLVector4a c; - c.load4a((F32*) (src++)); - - LLVector4a* n0p = norm+idx[0]; - LLVector4a* n1p = norm+idx[1]; - LLVector4a* n2p = norm+idx[2]; - - idx += 3; - - LLVector4a n0,n1,n2; - n0.load4a((F32*) n0p); - n1.load4a((F32*) n1p); - n2.load4a((F32*) n2p); - - n0.add(c); - n1.add(c); - n2.add(c); - - llassert(c.isFinite3()); - - //even out quad contributions - switch (i%2+1) - { - case 0: n0.add(c); break; - case 1: n1.add(c); break; - case 2: n2.add(c); break; - }; - - n0.store4a((F32*) n0p); - n1.store4a((F32*) n1p); - n2.store4a((F32*) n2p); - } - - LL_CHECK_MEMORY - - // adjust normals based on wrapping and stitching - - LLVector4a top; - top.setSub(pos[0], pos[mNumS*(mNumT-2)]); - bool s_bottom_converges = (top.dot3(top) < 0.000001f); - - top.setSub(pos[mNumS-1], pos[mNumS*(mNumT-2)+mNumS-1]); - bool s_top_converges = (top.dot3(top) < 0.000001f); - - if (sculpt_stitching == LL_SCULPT_TYPE_NONE) // logic for non-sculpt volumes - { - if (!volume->getPath().isOpen()) - { //wrap normals on T - for (S32 i = 0; i < mNumS; i++) - { - LLVector4a n; - n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]); - norm[i] = n; - norm[mNumS*(mNumT-1)+i] = n; - } - } - - if (!volume->getProfile().isOpen() && !s_bottom_converges) - { //wrap normals on S - for (S32 i = 0; i < mNumT; i++) - { - LLVector4a n; - n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]); - norm[mNumS * i] = n; - norm[mNumS * i+mNumS-1] = n; - } - } - - if (volume->getPathType() == LL_PCODE_PATH_CIRCLE && - ((volume->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF)) - { - if (s_bottom_converges) - { //all lower S have same normal - for (S32 i = 0; i < mNumT; i++) - { - norm[mNumS*i].set(1,0,0); - } - } - - if (s_top_converges) - { //all upper S have same normal - for (S32 i = 0; i < mNumT; i++) - { - norm[mNumS*i+mNumS-1].set(-1,0,0); - } - } - } - } - else // logic for sculpt volumes - { - bool average_poles = false; - bool wrap_s = false; - bool wrap_t = false; - - if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE) - average_poles = true; - - if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) || - (sculpt_stitching == LL_SCULPT_TYPE_TORUS) || - (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER)) - wrap_s = true; - - if (sculpt_stitching == LL_SCULPT_TYPE_TORUS) - wrap_t = true; - - - if (average_poles) - { - // average normals for north pole - - LLVector4a average; - average.clear(); - - for (S32 i = 0; i < mNumS; i++) - { - average.add(norm[i]); - } - - // set average - for (S32 i = 0; i < mNumS; i++) - { - norm[i] = average; - } - - // average normals for south pole - - average.clear(); - - for (S32 i = 0; i < mNumS; i++) - { - average.add(norm[i + mNumS * (mNumT - 1)]); - } - - // set average - for (S32 i = 0; i < mNumS; i++) - { - norm[i + mNumS * (mNumT - 1)] = average; - } - - } - - - if (wrap_s) - { - for (S32 i = 0; i < mNumT; i++) - { - LLVector4a n; - n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]); - norm[mNumS * i] = n; - norm[mNumS * i+mNumS-1] = n; - } - } - - if (wrap_t) - { - for (S32 i = 0; i < mNumS; i++) - { - LLVector4a n; - n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]); - norm[i] = n; - norm[mNumS*(mNumT-1)+i] = n; - } - } - - } - - LL_CHECK_MEMORY - - return true; -} - -//adapted from Lengyel, Eric. "Computing Tangent Space Basis Vectors for an Arbitrary Mesh". Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html -void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, - const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - //LLVector4a *tan1 = new LLVector4a[vertexCount * 2]; - LLVector4a* tan1 = (LLVector4a*) ll_aligned_malloc_16(vertexCount*2*sizeof(LLVector4a)); - // new(tan1) LLVector4a; - - LLVector4a* tan2 = tan1 + vertexCount; - - U32 count = vertexCount * 2; - for (U32 i = 0; i < count; i++) - { - tan1[i].clear(); - } - - for (U32 a = 0; a < triangleCount; a++) - { - U32 i1 = *index_array++; - U32 i2 = *index_array++; - U32 i3 = *index_array++; - - const LLVector4a& v1 = vertex[i1]; - const LLVector4a& v2 = vertex[i2]; - const LLVector4a& v3 = vertex[i3]; - - const LLVector2& w1 = texcoord[i1]; - const LLVector2& w2 = texcoord[i2]; - const LLVector2& w3 = texcoord[i3]; - - const F32* v1ptr = v1.getF32ptr(); - const F32* v2ptr = v2.getF32ptr(); - const F32* v3ptr = v3.getF32ptr(); - - float x1 = v2ptr[0] - v1ptr[0]; - float x2 = v3ptr[0] - v1ptr[0]; - float y1 = v2ptr[1] - v1ptr[1]; - float y2 = v3ptr[1] - v1ptr[1]; - float z1 = v2ptr[2] - v1ptr[2]; - float z2 = v3ptr[2] - v1ptr[2]; - - float s1 = w2.mV[0] - w1.mV[0]; - float s2 = w3.mV[0] - w1.mV[0]; - float t1 = w2.mV[1] - w1.mV[1]; - float t2 = w3.mV[1] - w1.mV[1]; - - F32 rd = s1*t2-s2*t1; - - float r = ((rd*rd) > FLT_EPSILON) ? (1.0f / rd) - : ((rd > 0.0f) ? 1024.f : -1024.f); //some made up large ratio for division by zero - - llassert(llfinite(r)); - llassert(!llisnan(r)); - - LLVector4a sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, - (t2 * z1 - t1 * z2) * r); - LLVector4a tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, - (s1 * z2 - s2 * z1) * r); - - tan1[i1].add(sdir); - tan1[i2].add(sdir); - tan1[i3].add(sdir); - - tan2[i1].add(tdir); - tan2[i2].add(tdir); - tan2[i3].add(tdir); - } - - for (U32 a = 0; a < vertexCount; a++) - { - LLVector4a n = normal[a]; - - const LLVector4a& t = tan1[a]; - - LLVector4a ncrosst; - ncrosst.setCross3(n,t); - - // Gram-Schmidt orthogonalize - n.mul(n.dot3(t).getF32()); - - LLVector4a tsubn; - tsubn.setSub(t,n); - - if (tsubn.dot3(tsubn).getF32() > F_APPROXIMATELY_ZERO) - { - tsubn.normalize3fast(); - - // Calculate handedness - F32 handedness = ncrosst.dot3(tan2[a]).getF32() < 0.f ? -1.f : 1.f; - - tsubn.getF32ptr()[3] = handedness; - - tangent[a] = tsubn; - } - else - { //degenerate, make up a value - tangent[a].set(0,0,1,1); - } - } - - ll_aligned_free_16(tan1); -} - - +/**
+ * @file llvolume.cpp
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llmemory.h"
+#include "llmath.h"
+
+#include <set>
+#if !LL_WINDOWS
+#include <stdint.h>
+#endif
+#include <cmath>
+#include <unordered_map>
+
+#include "llerror.h"
+
+#include "llvolumemgr.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llmatrix3a.h"
+#include "lloctree.h"
+#include "llvolume.h"
+#include "llvolumeoctree.h"
+#include "llstl.h"
+#include "llsdserialize.h"
+#include "llvector4a.h"
+#include "llmatrix4a.h"
+#include "llmeshoptimizer.h"
+#include "lltimer.h"
+
+#include "mikktspace/mikktspace.h"
+#include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file
+
+#include "meshoptimizer/meshoptimizer.h"
+
+#define DEBUG_SILHOUETTE_BINORMALS 0
+#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
+#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette
+
+constexpr F32 MIN_CUT_DELTA = 0.02f;
+
+constexpr F32 HOLLOW_MIN = 0.f;
+constexpr F32 HOLLOW_MAX = 0.95f;
+constexpr F32 HOLLOW_MAX_SQUARE = 0.7f;
+
+constexpr F32 TWIST_MIN = -1.f;
+constexpr F32 TWIST_MAX = 1.f;
+
+constexpr F32 RATIO_MIN = 0.f;
+constexpr F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper
+
+constexpr F32 HOLE_X_MIN= 0.05f;
+constexpr F32 HOLE_X_MAX= 1.0f;
+
+constexpr F32 HOLE_Y_MIN= 0.05f;
+constexpr F32 HOLE_Y_MAX= 0.5f;
+
+constexpr F32 SHEAR_MIN = -0.5f;
+constexpr F32 SHEAR_MAX = 0.5f;
+
+constexpr F32 REV_MIN = 1.f;
+constexpr F32 REV_MAX = 4.f;
+
+constexpr F32 TAPER_MIN = -1.f;
+constexpr F32 TAPER_MAX = 1.f;
+
+constexpr F32 SKEW_MIN = -0.95f;
+constexpr F32 SKEW_MAX = 0.95f;
+
+constexpr F32 SCULPT_MIN_AREA = 0.002f;
+constexpr S32 SCULPT_MIN_AREA_DETAIL = 1;
+
+bool gDebugGL = false; // See settings.xml "RenderDebugGL"
+
+bool check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)
+{
+ LLVector3 test = (pt2-pt1)%(pt3-pt2);
+
+ //answer
+ if(test * norm < 0)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+bool LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
+{
+ return LLLineSegmentBoxIntersect(start.mV, end.mV, center.mV, size.mV);
+}
+
+bool LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size)
+{
+ F32 fAWdU[3]{};
+ F32 dir[3]{};
+ F32 diff[3]{};
+
+ for (U32 i = 0; i < 3; i++)
+ {
+ dir[i] = 0.5f * (end[i] - start[i]);
+ diff[i] = (0.5f * (end[i] + start[i])) - center[i];
+ fAWdU[i] = fabsf(dir[i]);
+ if(fabsf(diff[i])>size[i] + fAWdU[i]) return false;
+ }
+
+ float f;
+ f = dir[1] * diff[2] - dir[2] * diff[1]; if(fabsf(f)>size[1]*fAWdU[2] + size[2]*fAWdU[1]) return false;
+ f = dir[2] * diff[0] - dir[0] * diff[2]; if(fabsf(f)>size[0]*fAWdU[2] + size[2]*fAWdU[0]) return false;
+ f = dir[0] * diff[1] - dir[1] * diff[0]; if(fabsf(f)>size[0]*fAWdU[1] + size[1]*fAWdU[0]) return false;
+
+ return true;
+}
+
+// Finds tangent vec based on three vertices with texture coordinates.
+// Fills in dummy values if the triangle has degenerate texture coordinates.
+void calc_tangent_from_triangle(
+ LLVector4a& normal,
+ LLVector4a& tangent_out,
+ const LLVector4a& v1,
+ const LLVector2& w1,
+ const LLVector4a& v2,
+ const LLVector2& w2,
+ const LLVector4a& v3,
+ const LLVector2& w3)
+{
+ const F32* v1ptr = v1.getF32ptr();
+ const F32* v2ptr = v2.getF32ptr();
+ const F32* v3ptr = v3.getF32ptr();
+
+ float x1 = v2ptr[0] - v1ptr[0];
+ float x2 = v3ptr[0] - v1ptr[0];
+ float y1 = v2ptr[1] - v1ptr[1];
+ float y2 = v3ptr[1] - v1ptr[1];
+ float z1 = v2ptr[2] - v1ptr[2];
+ float z2 = v3ptr[2] - v1ptr[2];
+
+ float s1 = w2.mV[0] - w1.mV[0];
+ float s2 = w3.mV[0] - w1.mV[0];
+ float t1 = w2.mV[1] - w1.mV[1];
+ float t2 = w3.mV[1] - w1.mV[1];
+
+ F32 rd = s1*t2-s2*t1;
+
+ float r = ((rd*rd) > FLT_EPSILON) ? (1.0f / rd)
+ : ((rd > 0.0f) ? 1024.f : -1024.f); //some made up large ratio for division by zero
+
+ llassert(llfinite(r));
+ llassert(!llisnan(r));
+
+ LLVector4a sdir(
+ (t2 * x1 - t1 * x2) * r,
+ (t2 * y1 - t1 * y2) * r,
+ (t2 * z1 - t1 * z2) * r);
+
+ LLVector4a tdir(
+ (s1 * x2 - s2 * x1) * r,
+ (s1 * y2 - s2 * y1) * r,
+ (s1 * z2 - s2 * z1) * r);
+
+ LLVector4a n = normal;
+ LLVector4a t = sdir;
+
+ LLVector4a ncrosst;
+ ncrosst.setCross3(n,t);
+
+ // Gram-Schmidt orthogonalize
+ n.mul(n.dot3(t).getF32());
+
+ LLVector4a tsubn;
+ tsubn.setSub(t,n);
+
+ if (tsubn.dot3(tsubn).getF32() > F_APPROXIMATELY_ZERO)
+ {
+ tsubn.normalize3fast_checked();
+
+ // Calculate handedness
+ F32 handedness = ncrosst.dot3(tdir).getF32() < 0.f ? -1.f : 1.f;
+
+ tsubn.getF32ptr()[3] = handedness;
+
+ tangent_out = tsubn;
+ }
+ else
+ {
+ // degenerate, make up a value
+ //
+ tangent_out.set(0,0,1,1);
+ }
+
+}
+
+
+// intersect test between triangle vert0, vert1, vert2 and a ray from orig in direction dir.
+// returns true if intersecting and returns barycentric coordinates in intersection_a, intersection_b,
+// and returns the intersection point along dir in intersection_t.
+
+// Moller-Trumbore algorithm
+bool LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+ F32& intersection_a, F32& intersection_b, F32& intersection_t)
+{
+
+ /* find vectors for two edges sharing vert0 */
+ LLVector4a edge1;
+ edge1.setSub(vert1, vert0);
+
+ LLVector4a edge2;
+ edge2.setSub(vert2, vert0);
+
+ /* begin calculating determinant - also used to calculate U parameter */
+ LLVector4a pvec;
+ pvec.setCross3(dir, edge2);
+
+ /* if determinant is near zero, ray lies in plane of triangle */
+ LLVector4a det;
+ det.setAllDot3(edge1, pvec);
+
+ if (det.greaterEqual(LLVector4a::getEpsilon()).getGatheredBits() & 0x7)
+ {
+ /* calculate distance from vert0 to ray origin */
+ LLVector4a tvec;
+ tvec.setSub(orig, vert0);
+
+ /* calculate U parameter and test bounds */
+ LLVector4a u;
+ u.setAllDot3(tvec,pvec);
+
+ if ((u.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7) &&
+ (u.lessEqual(det).getGatheredBits() & 0x7))
+ {
+ /* prepare to test V parameter */
+ LLVector4a qvec;
+ qvec.setCross3(tvec, edge1);
+
+ /* calculate V parameter and test bounds */
+ LLVector4a v;
+ v.setAllDot3(dir, qvec);
+
+
+ //if (!(v < 0.f || u + v > det))
+
+ LLVector4a sum_uv;
+ sum_uv.setAdd(u, v);
+
+ S32 v_gequal = v.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7;
+ S32 sum_lequal = sum_uv.lessEqual(det).getGatheredBits() & 0x7;
+
+ if (v_gequal && sum_lequal)
+ {
+ /* calculate t, scale parameters, ray intersects triangle */
+ LLVector4a t;
+ t.setAllDot3(edge2,qvec);
+
+ t.div(det);
+ u.div(det);
+ v.div(det);
+
+ intersection_a = u[0];
+ intersection_b = v[0];
+ intersection_t = t[0];
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+ F32& intersection_a, F32& intersection_b, F32& intersection_t)
+{
+ F32 u, v, t;
+
+ /* find vectors for two edges sharing vert0 */
+ LLVector4a edge1;
+ edge1.setSub(vert1, vert0);
+
+
+ LLVector4a edge2;
+ edge2.setSub(vert2, vert0);
+
+ /* begin calculating determinant - also used to calculate U parameter */
+ LLVector4a pvec;
+ pvec.setCross3(dir, edge2);
+
+ /* if determinant is near zero, ray lies in plane of triangle */
+ F32 det = edge1.dot3(pvec).getF32();
+
+
+ if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO)
+ {
+ return false;
+ }
+
+ F32 inv_det = 1.f / det;
+
+ /* calculate distance from vert0 to ray origin */
+ LLVector4a tvec;
+ tvec.setSub(orig, vert0);
+
+ /* calculate U parameter and test bounds */
+ u = (tvec.dot3(pvec).getF32()) * inv_det;
+ if (u < 0.f || u > 1.f)
+ {
+ return false;
+ }
+
+ /* prepare to test V parameter */
+ tvec.sub(edge1);
+
+ /* calculate V parameter and test bounds */
+ v = (dir.dot3(tvec).getF32()) * inv_det;
+
+ if (v < 0.f || u + v > 1.f)
+ {
+ return false;
+ }
+
+ /* calculate t, ray intersects triangle */
+ t = (edge2.dot3(tvec).getF32()) * inv_det;
+
+ intersection_a = u;
+ intersection_b = v;
+ intersection_t = t;
+
+
+ return true;
+}
+
+class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*>
+{
+public:
+ const LLVolumeFace* mFace;
+
+ LLVolumeOctreeRebound(const LLVolumeFace* face)
+ {
+ mFace = face;
+ }
+
+ virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
+ { //this is a depth first traversal, so it's safe to assum all children have complete
+ //bounding data
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
+
+ LLVector4a& min = node->mExtents[0];
+ LLVector4a& max = node->mExtents[1];
+
+ if (!branch->isEmpty())
+ { //node has data, find AABB that binds data set
+ const LLVolumeTriangle* tri = *(branch->getDataBegin());
+
+ //initialize min/max to first available vertex
+ min = *(tri->mV[0]);
+ max = *(tri->mV[0]);
+
+ for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter)
+ { //for each triangle in node
+
+ //stretch by triangles in node
+ tri = *iter;
+
+ min.setMin(min, *tri->mV[0]);
+ min.setMin(min, *tri->mV[1]);
+ min.setMin(min, *tri->mV[2]);
+
+ max.setMax(max, *tri->mV[0]);
+ max.setMax(max, *tri->mV[1]);
+ max.setMax(max, *tri->mV[2]);
+ }
+ }
+ else if (branch->getChildCount() > 0)
+ { //no data, but child nodes exist
+ LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0);
+
+ //initialize min/max to extents of first child
+ min = child->mExtents[0];
+ max = child->mExtents[1];
+ }
+ else
+ {
+ llassert(!branch->isLeaf()); // Empty leaf
+ }
+
+ for (S32 i = 0; i < branch->getChildCount(); ++i)
+ { //stretch by child extents
+ LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0);
+ min.setMin(min, child->mExtents[0]);
+ max.setMax(max, child->mExtents[1]);
+ }
+
+ node->mBounds[0].setAdd(min, max);
+ node->mBounds[0].mul(0.5f);
+
+ node->mBounds[1].setSub(max,min);
+ node->mBounds[1].mul(0.5f);
+ }
+};
+
+//-------------------------------------------------------------------
+// statics
+//-------------------------------------------------------------------
+
+
+//----------------------------------------------------
+
+LLProfile::Face* LLProfile::addCap(S16 faceID)
+{
+ Face *face = vector_append(mFaces, 1);
+
+ face->mIndex = 0;
+ face->mCount = mTotal;
+ face->mScaleU= 1.0f;
+ face->mCap = true;
+ face->mFaceID = faceID;
+ return face;
+}
+
+LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, bool flat)
+{
+ Face *face = vector_append(mFaces, 1);
+
+ face->mIndex = i;
+ face->mCount = count;
+ face->mScaleU= scaleU;
+
+ face->mFlat = flat;
+ face->mCap = false;
+ face->mFaceID = faceID;
+ return face;
+}
+
+//static
+S32 LLProfile::getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
+{ // this is basically LLProfile::genNGon stripped down to only the operations that influence the number of points
+ S32 np = 0;
+
+ // Generate an n-sided "circular" path.
+ // 0 is (1,0), and we go counter-clockwise along a circular path from there.
+ F32 t, t_step, t_first, t_fraction;
+
+ F32 begin = params.getBegin();
+ F32 end = params.getEnd();
+
+ t_step = 1.0f / sides;
+
+ t_first = floor(begin * sides) / (F32)sides;
+
+ // pt1 is the first point on the fractional face.
+ // Starting t and ang values for the first face
+ t = t_first;
+
+ // Increment to the next point.
+ // pt2 is the end point on the fractional face
+ t += t_step;
+
+ t_fraction = (begin - t_first)*sides;
+
+ // Only use if it's not almost exactly on an edge.
+ if (t_fraction < 0.9999f)
+ {
+ np++;
+ }
+
+ // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
+ while (t < end)
+ {
+ // Iterate through all the integer steps of t.
+ np++;
+
+ t += t_step;
+ }
+
+ t_fraction = (end - (t - t_step))*sides;
+
+ // Find the fraction that we need to add to the end point.
+ t_fraction = (end - (t - t_step))*sides;
+ if (t_fraction > 0.0001f)
+ {
+ np++;
+ }
+
+ // If we're sliced, the profile is open.
+ if ((end - begin)*ang_scale < 0.99f)
+ {
+ if (params.getHollow() <= 0)
+ {
+ // put center point if not hollow.
+ np++;
+ }
+ }
+
+ return np;
+}
+
+// What is the bevel parameter used for? - DJS 04/05/02
+// Bevel parameter is currently unused but presumedly would support
+// filleted and chamfered corners
+void LLProfile::genNGon(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
+{
+ // Generate an n-sided "circular" path.
+ // 0 is (1,0), and we go counter-clockwise along a circular path from there.
+ constexpr F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
+ F32 scale = 0.5f;
+ F32 t, t_step, t_first, t_fraction, ang, ang_step;
+ LLVector4a pt1,pt2;
+
+ F32 begin = params.getBegin();
+ F32 end = params.getEnd();
+
+ t_step = 1.0f / sides;
+ ang_step = 2.0f*F_PI*t_step*ang_scale;
+
+ // Scale to have size "match" scale. Compensates to get object to generally fill bounding box.
+
+ S32 total_sides = ll_round(sides / ang_scale); // Total number of sides all around
+
+ if (total_sides < 8)
+ {
+ scale = tableScale[total_sides];
+ }
+
+ t_first = floor(begin * sides) / (F32)sides;
+
+ // pt1 is the first point on the fractional face.
+ // Starting t and ang values for the first face
+ t = t_first;
+ ang = 2.0f*F_PI*(t*ang_scale + offset);
+ pt1.set(cos(ang)*scale,sin(ang)*scale, t);
+
+ // Increment to the next point.
+ // pt2 is the end point on the fractional face
+ t += t_step;
+ ang += ang_step;
+ pt2.set(cos(ang)*scale,sin(ang)*scale,t);
+
+ t_fraction = (begin - t_first)*sides;
+
+ // Only use if it's not almost exactly on an edge.
+ if (t_fraction < 0.9999f)
+ {
+ LLVector4a new_pt;
+ new_pt.setLerp(pt1, pt2, t_fraction);
+ mProfile.push_back(new_pt);
+ }
+
+ // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
+ while (t < end)
+ {
+ // Iterate through all the integer steps of t.
+ pt1.set(cos(ang)*scale,sin(ang)*scale,t);
+
+ if (mProfile.size() > 0) {
+ LLVector4a p = mProfile[mProfile.size()-1];
+ for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
+ //mProfile.push_back(p+(pt1-p) * 1.0f/(float)(split+1) * (float)(i+1));
+ LLVector4a new_pt;
+ new_pt.setSub(pt1, p);
+ new_pt.mul(1.0f/(float)(split+1) * (float)(i+1));
+ new_pt.add(p);
+ mProfile.push_back(new_pt);
+ }
+ }
+ mProfile.push_back(pt1);
+
+ t += t_step;
+ ang += ang_step;
+ }
+
+ t_fraction = (end - (t - t_step))*sides;
+
+ // pt1 is the first point on the fractional face
+ // pt2 is the end point on the fractional face
+ pt2.set(cos(ang)*scale,sin(ang)*scale,t);
+
+ // Find the fraction that we need to add to the end point.
+ t_fraction = (end - (t - t_step))*sides;
+ if (t_fraction > 0.0001f)
+ {
+ LLVector4a new_pt;
+ new_pt.setLerp(pt1, pt2, t_fraction);
+
+ if (mProfile.size() > 0) {
+ LLVector4a p = mProfile[mProfile.size()-1];
+ for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
+ //mProfile.push_back(p+(new_pt-p) * 1.0f/(float)(split+1) * (float)(i+1));
+
+ LLVector4a pt1;
+ pt1.setSub(new_pt, p);
+ pt1.mul(1.0f/(float)(split+1) * (float)(i+1));
+ pt1.add(p);
+ mProfile.push_back(pt1);
+ }
+ }
+ mProfile.push_back(new_pt);
+ }
+
+ // If we're sliced, the profile is open.
+ if ((end - begin)*ang_scale < 0.99f)
+ {
+ if ((end - begin)*ang_scale > 0.5f)
+ {
+ mConcave = true;
+ }
+ else
+ {
+ mConcave = false;
+ }
+ mOpen = true;
+ if (params.getHollow() <= 0)
+ {
+ // put center point if not hollow.
+ mProfile.push_back(LLVector4a(0,0,0));
+ }
+ }
+ else
+ {
+ // The profile isn't open.
+ mOpen = false;
+ mConcave = false;
+ }
+
+ mTotal = mProfile.size();
+}
+
+// Hollow is percent of the original bounding box, not of this particular
+// profile's geometry. Thus, a swept triangle needs lower hollow values than
+// a swept square.
+LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, bool flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split)
+{
+ // Note that addHole will NOT work for non-"circular" profiles, if we ever decide to use them.
+
+ // Total add has number of vertices on outside.
+ mTotalOut = mTotal;
+
+ // Why is the "bevel" parameter -1? DJS 04/05/02
+ genNGon(params, llfloor(sides),offset,-1, ang_scale, split);
+
+ Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
+
+ static thread_local LLAlignedArray<LLVector4a,64> pt;
+ pt.resize(mTotal) ;
+
+ for (S32 i=mTotalOut;i<mTotal;i++)
+ {
+ pt[i] = mProfile[i];
+ pt[i].mul(box_hollow);
+ }
+
+ S32 j=mTotal-1;
+ for (S32 i=mTotalOut;i<mTotal;i++)
+ {
+ mProfile[i] = pt[j--];
+ }
+
+ for (S32 i=0;i<(S32)mFaces.size();i++)
+ {
+ if (mFaces[i].mCap)
+ {
+ mFaces[i].mCount *= 2;
+ }
+ }
+
+ return face;
+}
+
+//static
+S32 LLProfile::getNumPoints(const LLProfileParams& params, bool path_open,F32 detail, S32 split,
+ bool is_sculpted, S32 sculpt_size)
+{ // this is basically LLProfile::generate stripped down to only operations that influence the number of points
+ if (detail < MIN_LOD)
+ {
+ detail = MIN_LOD;
+ }
+
+ // Generate the face data
+ F32 hollow = params.getHollow();
+
+ S32 np = 0;
+
+ switch (params.getCurveType() & LL_PCODE_PROFILE_MASK)
+ {
+ case LL_PCODE_PROFILE_SQUARE:
+ {
+ np = getNumNGonPoints(params, 4,-0.375, 0, 1, split);
+
+ if (hollow)
+ {
+ np *= 2;
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_ISOTRI:
+ case LL_PCODE_PROFILE_RIGHTTRI:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ {
+ np = getNumNGonPoints(params, 3,0, 0, 1, split);
+
+ if (hollow)
+ {
+ np *= 2;
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_CIRCLE:
+ {
+ // If this has a square hollow, we should adjust the
+ // number of faces a bit so that the geometry lines up.
+ U8 hole_type=0;
+ F32 circle_detail = MIN_DETAIL_FACES * detail;
+ if (hollow)
+ {
+ hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
+ if (hole_type == LL_PCODE_HOLE_SQUARE)
+ {
+ // Snap to the next multiple of four sides,
+ // so that corners line up.
+ circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
+ }
+ }
+
+ S32 sides = (S32)circle_detail;
+
+ if (is_sculpted)
+ sides = sculpt_size;
+
+ np = getNumNGonPoints(params, sides);
+
+ if (hollow)
+ {
+ np *= 2;
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ {
+ // If this has a square hollow, we should adjust the
+ // number of faces a bit so that the geometry lines up.
+ U8 hole_type=0;
+ // Number of faces is cut in half because it's only a half-circle.
+ F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
+ if (hollow)
+ {
+ hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
+ if (hole_type == LL_PCODE_HOLE_SQUARE)
+ {
+ // Snap to the next multiple of four sides (div 2),
+ // so that corners line up.
+ circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
+ }
+ }
+ np = getNumNGonPoints(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f);
+
+ if (hollow)
+ {
+ np *= 2;
+ }
+
+ // Special case for openness of sphere
+ if ((params.getEnd() - params.getBegin()) < 1.f)
+ {
+ }
+ else if (!hollow)
+ {
+ np++;
+ }
+ }
+ break;
+ default:
+ break;
+ };
+
+
+ return np;
+}
+
+
+bool LLProfile::generate(const LLProfileParams& params, bool path_open,F32 detail, S32 split,
+ bool is_sculpted, S32 sculpt_size)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ if ((!mDirty) && (!is_sculpted))
+ {
+ return false;
+ }
+ mDirty = false;
+
+ if (detail < MIN_LOD)
+ {
+ LL_INFOS() << "Generating profile with LOD < MIN_LOD. CLAMPING" << LL_ENDL;
+ detail = MIN_LOD;
+ }
+
+ mProfile.resize(0);
+ mFaces.resize(0);
+
+ // Generate the face data
+ S32 i;
+ F32 begin = params.getBegin();
+ F32 end = params.getEnd();
+ F32 hollow = params.getHollow();
+
+ // Quick validation to eliminate some server crashes.
+ if (begin > end - 0.01f)
+ {
+ LL_WARNS() << "LLProfile::generate() assertion failed (begin >= end)" << LL_ENDL;
+ return false;
+ }
+
+ S32 face_num = 0;
+
+ switch (params.getCurveType() & LL_PCODE_PROFILE_MASK)
+ {
+ case LL_PCODE_PROFILE_SQUARE:
+ {
+ genNGon(params, 4,-0.375, 0, 1, split);
+ if (path_open)
+ {
+ addCap (LL_FACE_PATH_BEGIN);
+ }
+
+ for (i = llfloor(begin * 4.f); i < llfloor(end * 4.f + .999f); i++)
+ {
+ addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, true);
+ }
+
+ LLVector4a scale(1,1,4,1);
+
+ for (i = 0; i <(S32) mProfile.size(); i++)
+ {
+ // Scale by 4 to generate proper tex coords.
+ mProfile[i].mul(scale);
+ llassert(mProfile[i].isFinite3());
+ }
+
+ if (hollow)
+ {
+ switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
+ {
+ case LL_PCODE_HOLE_TRIANGLE:
+ // This offset is not correct, but we can't change it now... DK 11/17/04
+ addHole(params, true, 3, -0.375f, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ // TODO: Compute actual detail levels for cubes
+ addHole(params, false, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f);
+ break;
+ case LL_PCODE_HOLE_SAME:
+ case LL_PCODE_HOLE_SQUARE:
+ default:
+ addHole(params, true, 4, -0.375f, hollow, 1.f, split);
+ break;
+ }
+ }
+
+ if (path_open) {
+ mFaces[0].mCount = mTotal;
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_ISOTRI:
+ case LL_PCODE_PROFILE_RIGHTTRI:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ {
+ genNGon(params, 3,0, 0, 1, split);
+ LLVector4a scale(1,1,3,1);
+ for (i = 0; i <(S32) mProfile.size(); i++)
+ {
+ // Scale by 3 to generate proper tex coords.
+ mProfile[i].mul(scale);
+ llassert(mProfile[i].isFinite3());
+ }
+
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_BEGIN);
+ }
+
+ for (i = llfloor(begin * 3.f); i < llfloor(end * 3.f + .999f); i++)
+ {
+ addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, true);
+ }
+ if (hollow)
+ {
+ // Swept triangles need smaller hollowness values,
+ // because the triangle doesn't fill the bounding box.
+ F32 triangle_hollow = hollow / 2.f;
+
+ switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
+ {
+ case LL_PCODE_HOLE_CIRCLE:
+ // TODO: Actually generate level of detail for triangles
+ addHole(params, false, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f);
+ break;
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(params, true, 4, 0, triangle_hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_SAME:
+ case LL_PCODE_HOLE_TRIANGLE:
+ default:
+ addHole(params, true, 3, 0, triangle_hollow, 1.f, split);
+ break;
+ }
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_CIRCLE:
+ {
+ // If this has a square hollow, we should adjust the
+ // number of faces a bit so that the geometry lines up.
+ U8 hole_type=0;
+ F32 circle_detail = MIN_DETAIL_FACES * detail;
+ if (hollow)
+ {
+ hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
+ if (hole_type == LL_PCODE_HOLE_SQUARE)
+ {
+ // Snap to the next multiple of four sides,
+ // so that corners line up.
+ circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
+ }
+ }
+
+ S32 sides = (S32)circle_detail;
+
+ if (is_sculpted)
+ sides = sculpt_size;
+
+ genNGon(params, sides);
+
+ if (path_open)
+ {
+ addCap (LL_FACE_PATH_BEGIN);
+ }
+
+ if (mOpen && !hollow)
+ {
+ addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, false);
+ }
+ else
+ {
+ addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, false);
+ }
+
+ if (hollow)
+ {
+ switch (hole_type)
+ {
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(params, true, 4, 0, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ addHole(params, true, 3, 0, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ case LL_PCODE_HOLE_SAME:
+ default:
+ addHole(params, true, circle_detail, 0, hollow, 1.f);
+ break;
+ }
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ {
+ // If this has a square hollow, we should adjust the
+ // number of faces a bit so that the geometry lines up.
+ U8 hole_type=0;
+ // Number of faces is cut in half because it's only a half-circle.
+ F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
+ if (hollow)
+ {
+ hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
+ if (hole_type == LL_PCODE_HOLE_SQUARE)
+ {
+ // Snap to the next multiple of four sides (div 2),
+ // so that corners line up.
+ circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
+ }
+ }
+ genNGon(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f);
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_BEGIN);
+ }
+ if (mOpen && !params.getHollow())
+ {
+ addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, false);
+ }
+ else
+ {
+ addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, false);
+ }
+
+ if (hollow)
+ {
+ switch (hole_type)
+ {
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(params, true, 2, 0.5f, hollow, 0.5f, split);
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ addHole(params, true, 3, 0.5f, hollow, 0.5f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ case LL_PCODE_HOLE_SAME:
+ default:
+ addHole(params, false, circle_detail, 0.5f, hollow, 0.5f);
+ break;
+ }
+ }
+
+ // Special case for openness of sphere
+ if ((params.getEnd() - params.getBegin()) < 1.f)
+ {
+ mOpen = true;
+ }
+ else if (!hollow)
+ {
+ mOpen = false;
+ mProfile.push_back(mProfile[0]);
+ mTotal++;
+ }
+ }
+ break;
+ default:
+ LL_ERRS() << "Unknown profile: getCurveType()=" << params.getCurveType() << LL_ENDL;
+ break;
+ };
+
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_END); // bottom
+ }
+
+ if ( mOpen) // interior edge caps
+ {
+ addFace(mTotal-1, 2,0.5,LL_FACE_PROFILE_BEGIN, true);
+
+ if (hollow)
+ {
+ addFace(mTotalOut-1, 2,0.5,LL_FACE_PROFILE_END, true);
+ }
+ else
+ {
+ addFace(mTotal-2, 2,0.5,LL_FACE_PROFILE_END, true);
+ }
+ }
+
+ return true;
+}
+
+
+
+bool LLProfileParams::importFile(LLFILE *fp)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of these buffers will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ char valuestr[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+ valuestr[0] = 0;
+ F32 tempF32;
+ U32 tempU32;
+
+ while (!feof(fp))
+ {
+ if (fgets(buffer, BUFSIZE, fp) == NULL)
+ {
+ buffer[0] = '\0';
+ }
+
+ sscanf( /* Flawfinder: ignore */
+ buffer,
+ " %255s %255s",
+ keyword, valuestr);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("hollow",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setHollow(tempF32);
+ }
+ else
+ {
+ LL_WARNS() << "unknown keyword " << keyword << " in profile import" << LL_ENDL;
+ }
+ }
+
+ return true;
+}
+
+
+bool LLProfileParams::exportFile(LLFILE *fp) const
+{
+ fprintf(fp,"\t\tprofile 0\n");
+ fprintf(fp,"\t\t{\n");
+ fprintf(fp,"\t\t\tcurve\t%d\n", getCurveType());
+ fprintf(fp,"\t\t\tbegin\t%g\n", getBegin());
+ fprintf(fp,"\t\t\tend\t%g\n", getEnd());
+ fprintf(fp,"\t\t\thollow\t%g\n", getHollow());
+ fprintf(fp, "\t\t}\n");
+ return true;
+}
+
+
+bool LLProfileParams::importLegacyStream(std::istream& input_stream)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of these buffers will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ char valuestr[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+ valuestr[0] = 0;
+ F32 tempF32;
+ U32 tempU32;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf( /* Flawfinder: ignore */
+ buffer,
+ " %255s %255s",
+ keyword,
+ valuestr);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("hollow",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setHollow(tempF32);
+ }
+ else
+ {
+ LL_WARNS() << "unknown keyword " << keyword << " in profile import" << LL_ENDL;
+ }
+ }
+
+ return true;
+}
+
+
+bool LLProfileParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream <<"\t\tprofile 0\n";
+ output_stream <<"\t\t{\n";
+ output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n";
+ output_stream <<"\t\t\tbegin\t" << getBegin() << "\n";
+ output_stream <<"\t\t\tend\t" << getEnd() << "\n";
+ output_stream <<"\t\t\thollow\t" << getHollow() << "\n";
+ output_stream << "\t\t}\n";
+ return true;
+}
+
+LLSD LLProfileParams::asLLSD() const
+{
+ LLSD sd;
+
+ sd["curve"] = getCurveType();
+ sd["begin"] = getBegin();
+ sd["end"] = getEnd();
+ sd["hollow"] = getHollow();
+ return sd;
+}
+
+bool LLProfileParams::fromLLSD(LLSD& sd)
+{
+ setCurveType(sd["curve"].asInteger());
+ setBegin((F32)sd["begin"].asReal());
+ setEnd((F32)sd["end"].asReal());
+ setHollow((F32)sd["hollow"].asReal());
+ return true;
+}
+
+void LLProfileParams::copyParams(const LLProfileParams ¶ms)
+{
+ setCurveType(params.getCurveType());
+ setBegin(params.getBegin());
+ setEnd(params.getEnd());
+ setHollow(params.getHollow());
+}
+
+
+LLPath::~LLPath()
+{
+}
+
+S32 LLPath::getNumNGonPoints(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
+{ //this is basically LLPath::genNGon stripped down to only operations that influence the number of points added
+ S32 ret = 0;
+
+ F32 step= 1.0f / sides;
+ F32 t = params.getBegin();
+ ret = 1;
+
+ t+=step;
+
+ // Snap to a quantized parameter, so that cut does not
+ // affect most sample points.
+ t = ((S32)(t * sides)) / (F32)sides;
+
+ // Run through the non-cut dependent points.
+ while (t < params.getEnd())
+ {
+ ret++;
+ t+=step;
+ }
+
+ ret++;
+
+ return ret;
+}
+
+void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ // Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane.
+ constexpr F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
+
+ F32 revolutions = params.getRevolutions();
+ F32 skew = params.getSkew();
+ F32 skew_mag = fabs(skew);
+ F32 hole_x = params.getScaleX() * (1.0f - skew_mag);
+ F32 hole_y = params.getScaleY();
+
+ // Calculate taper begin/end for x,y (Negative means taper the beginning)
+ F32 taper_x_begin = 1.0f;
+ F32 taper_x_end = 1.0f - params.getTaperX();
+ F32 taper_y_begin = 1.0f;
+ F32 taper_y_end = 1.0f - params.getTaperY();
+
+ if ( taper_x_end > 1.0f )
+ {
+ // Flip tapering.
+ taper_x_begin = 2.0f - taper_x_end;
+ taper_x_end = 1.0f;
+ }
+ if ( taper_y_end > 1.0f )
+ {
+ // Flip tapering.
+ taper_y_begin = 2.0f - taper_y_end;
+ taper_y_end = 1.0f;
+ }
+
+ // For spheres, the radius is usually zero.
+ F32 radius_start = 0.5f;
+ if (sides < 8)
+ {
+ radius_start = tableScale[sides];
+ }
+
+ // Scale the radius to take the hole size into account.
+ radius_start *= 1.0f - hole_y;
+
+ // Now check the radius offset to calculate the start,end radius. (Negative means
+ // decrease the start radius instead).
+ F32 radius_end = radius_start;
+ F32 radius_offset = params.getRadiusOffset();
+ if (radius_offset < 0.f)
+ {
+ radius_start *= 1.f + radius_offset;
+ }
+ else
+ {
+ radius_end *= 1.f - radius_offset;
+ }
+
+ // Is the path NOT a closed loop?
+ mOpen = ( (params.getEnd()*end_scale - params.getBegin() < 1.0f) ||
+ (skew_mag > 0.001f) ||
+ (fabs(taper_x_end - taper_x_begin) > 0.001f) ||
+ (fabs(taper_y_end - taper_y_begin) > 0.001f) ||
+ (fabs(radius_end - radius_start) > 0.001f) );
+
+ F32 ang, c, s;
+ LLQuaternion twist, qang;
+ PathPt *pt;
+ LLVector3 path_axis (1.f, 0.f, 0.f);
+ //LLVector3 twist_axis(0.f, 0.f, 1.f);
+ F32 twist_begin = params.getTwistBegin() * twist_scale;
+ F32 twist_end = params.getTwist() * twist_scale;
+
+ // We run through this once before the main loop, to make sure
+ // the path begins at the correct cut.
+ F32 step= 1.0f / sides;
+ F32 t = params.getBegin();
+ pt = mPath.append(1);
+ ang = 2.0f*F_PI*revolutions * t;
+ s = sin(ang)*lerp(radius_start, radius_end, t);
+ c = cos(ang)*lerp(radius_start, radius_end, t);
+
+
+ pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,params.getShear().mV[1],s),
+ s);
+ pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t),
+ hole_y * lerp(taper_y_begin, taper_y_end, t),
+ 0,1);
+ pt->mTexT = t;
+
+ // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
+ twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
+ // Rotate the point around the circle's center.
+ qang.setQuat (ang,path_axis);
+
+ LLMatrix3 rot(twist * qang);
+
+ pt->mRot.loadu(rot);
+
+ t+=step;
+
+ // Snap to a quantized parameter, so that cut does not
+ // affect most sample points.
+ t = ((S32)(t * sides)) / (F32)sides;
+
+ // Run through the non-cut dependent points.
+ while (t < params.getEnd())
+ {
+ pt = mPath.append(1);
+
+ ang = 2.0f*F_PI*revolutions * t;
+ c = cos(ang)*lerp(radius_start, radius_end, t);
+ s = sin(ang)*lerp(radius_start, radius_end, t);
+
+ pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,params.getShear().mV[1],s),
+ s);
+
+ pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t),
+ hole_y * lerp(taper_y_begin, taper_y_end, t),
+ 0,1);
+ pt->mTexT = t;
+
+ // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
+ twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
+ // Rotate the point around the circle's center.
+ qang.setQuat (ang,path_axis);
+ LLMatrix3 tmp(twist*qang);
+ pt->mRot.loadu(tmp);
+
+ t+=step;
+ }
+
+ // Make one final pass for the end cut.
+ t = params.getEnd();
+ pt = mPath.append(1);
+ ang = 2.0f*F_PI*revolutions * t;
+ c = cos(ang)*lerp(radius_start, radius_end, t);
+ s = sin(ang)*lerp(radius_start, radius_end, t);
+
+ pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,params.getShear().mV[1],s),
+ s);
+ pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t),
+ hole_y * lerp(taper_y_begin, taper_y_end, t),
+ 0,1);
+ pt->mTexT = t;
+
+ // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
+ twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
+ // Rotate the point around the circle's center.
+ qang.setQuat (ang,path_axis);
+ LLMatrix3 tmp(twist*qang);
+ pt->mRot.loadu(tmp);
+
+ mTotal = mPath.size();
+}
+
+const LLVector2 LLPathParams::getBeginScale() const
+{
+ LLVector2 begin_scale(1.f, 1.f);
+ if (getScaleX() > 1)
+ {
+ begin_scale.mV[0] = 2-getScaleX();
+ }
+ if (getScaleY() > 1)
+ {
+ begin_scale.mV[1] = 2-getScaleY();
+ }
+ return begin_scale;
+}
+
+const LLVector2 LLPathParams::getEndScale() const
+{
+ LLVector2 end_scale(1.f, 1.f);
+ if (getScaleX() < 1)
+ {
+ end_scale.mV[0] = getScaleX();
+ }
+ if (getScaleY() < 1)
+ {
+ end_scale.mV[1] = getScaleY();
+ }
+ return end_scale;
+}
+
+S32 LLPath::getNumPoints(const LLPathParams& params, F32 detail)
+{ // this is basically LLPath::generate stripped down to only the operations that influence the number of points
+ if (detail < MIN_LOD)
+ {
+ detail = MIN_LOD;
+ }
+
+ S32 np = 2; // hardcode for line
+
+ // Is this 0xf0 mask really necessary? DK 03/02/05
+
+ switch (params.getCurveType() & 0xf0)
+ {
+ default:
+ case LL_PCODE_PATH_LINE:
+ {
+ // Take the begin/end twist into account for detail.
+ np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2;
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE:
+ {
+ // Increase the detail as the revolutions and twist increase.
+ F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist());
+
+ S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions());
+
+ np = sides;
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE2:
+ {
+ //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
+ np = getNumNGonPoints(params, llfloor(MIN_DETAIL_FACES * detail));
+ }
+ break;
+
+ case LL_PCODE_PATH_TEST:
+
+ np = 5;
+ break;
+ };
+
+ return np;
+}
+
+bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
+ bool is_sculpted, S32 sculpt_size)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ if ((!mDirty) && (!is_sculpted))
+ {
+ return false;
+ }
+
+ if (detail < MIN_LOD)
+ {
+ LL_INFOS() << "Generating path with LOD < MIN! Clamping to 1" << LL_ENDL;
+ detail = MIN_LOD;
+ }
+
+ mDirty = false;
+ S32 np = 2; // hardcode for line
+
+ mPath.resize(0);
+ mOpen = true;
+
+ // Is this 0xf0 mask really necessary? DK 03/02/05
+ switch (params.getCurveType() & 0xf0)
+ {
+ default:
+ case LL_PCODE_PATH_LINE:
+ {
+ // Take the begin/end twist into account for detail.
+ np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2;
+ if (np < split+2)
+ {
+ np = split+2;
+ }
+
+ mStep = 1.0f / (np-1);
+
+ mPath.resize(np);
+
+ LLVector2 start_scale = params.getBeginScale();
+ LLVector2 end_scale = params.getEndScale();
+
+ for (S32 i=0;i<np;i++)
+ {
+ F32 t = lerp(params.getBegin(),params.getEnd(),(F32)i * mStep);
+ mPath[i].mPos.set(lerp(0,params.getShear().mV[0],t),
+ lerp(0,params.getShear().mV[1],t),
+ t - 0.5f);
+ LLQuaternion quat;
+ quat.setQuat(lerp(F_PI * params.getTwistBegin(),F_PI * params.getTwist(),t),0,0,1);
+ LLMatrix3 tmp(quat);
+ mPath[i].mRot.loadu(tmp);
+ mPath[i].mScale.set(lerp(start_scale.mV[0],end_scale.mV[0],t),
+ lerp(start_scale.mV[1],end_scale.mV[1],t),
+ 0,1);
+ mPath[i].mTexT = t;
+ }
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE:
+ {
+ // Increase the detail as the revolutions and twist increase.
+ F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist());
+
+ S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions());
+
+ if (is_sculpted)
+ sides = llmax(sculpt_size, 1);
+
+ if (0 < sides)
+ genNGon(params, sides);
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE2:
+ {
+ if (params.getEnd() - params.getBegin() >= 0.99f &&
+ params.getScaleX() >= .99f)
+ {
+ mOpen = false;
+ }
+
+ //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
+ genNGon(params, llfloor(MIN_DETAIL_FACES * detail));
+
+ F32 toggle = 0.5f;
+ for (S32 i=0;i<(S32)mPath.size();i++)
+ {
+ mPath[i].mPos.getF32ptr()[0] = toggle;
+ if (toggle == 0.5f)
+ toggle = -0.5f;
+ else
+ toggle = 0.5f;
+ }
+ }
+
+ break;
+
+ case LL_PCODE_PATH_TEST:
+
+ np = 5;
+ mStep = 1.0f / (np-1);
+
+ mPath.resize(np);
+
+ for (S32 i=0;i<np;i++)
+ {
+ F32 t = (F32)i * mStep;
+ mPath[i].mPos.set(0,
+ lerp(0, -sin(F_PI*params.getTwist()*t)*0.5f,t),
+ lerp(-0.5f, cos(F_PI*params.getTwist()*t)*0.5f,t));
+ mPath[i].mScale.set(lerp(1,params.getScale().mV[0],t),
+ lerp(1,params.getScale().mV[1],t), 0,1);
+ mPath[i].mTexT = t;
+ LLQuaternion quat;
+ quat.setQuat(F_PI * params.getTwist() * t,1,0,0);
+ LLMatrix3 tmp(quat);
+ mPath[i].mRot.loadu(tmp);
+ }
+
+ break;
+ };
+
+ if (params.getTwist() != params.getTwistBegin()) mOpen = true;
+
+ //if ((int(fabsf(params.getTwist() - params.getTwistBegin())*100))%100 != 0) {
+ // mOpen = true;
+ //}
+
+ return true;
+}
+
+bool LLDynamicPath::generate(const LLPathParams& params, F32 detail, S32 split,
+ bool is_sculpted, S32 sculpt_size)
+{
+ mOpen = true; // Draw end caps
+ if (getPathLength() == 0)
+ {
+ // Path hasn't been generated yet.
+ // Some algorithms later assume at least TWO path points.
+ resizePath(2);
+ LLQuaternion quat;
+ quat.setQuat(0,0,0);
+ LLMatrix3 tmp(quat);
+
+ for (U32 i = 0; i < 2; i++)
+ {
+ mPath[i].mPos.set(0, 0, 0);
+ mPath[i].mRot.loadu(tmp);
+ mPath[i].mScale.set(1, 1, 0, 1);
+ mPath[i].mTexT = 0;
+ }
+ }
+
+ return true;
+}
+
+
+bool LLPathParams::importFile(LLFILE *fp)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of these buffers will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ char valuestr[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+ valuestr[0] = 0;
+
+ F32 tempF32;
+ F32 x, y;
+ U32 tempU32;
+
+ while (!feof(fp))
+ {
+ if (fgets(buffer, BUFSIZE, fp) == NULL)
+ {
+ buffer[0] = '\0';
+ }
+
+ sscanf( /* Flawfinder: ignore */
+ buffer,
+ " %255s %255s",
+ keyword, valuestr);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("scale",keyword))
+ {
+ // Legacy for one dimensional scale per path
+ sscanf(valuestr,"%g",&tempF32);
+ setScale(tempF32, tempF32);
+ }
+ else if (!strcmp("scale_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setScaleX(x);
+ }
+ else if (!strcmp("scale_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setScaleY(y);
+ }
+ else if (!strcmp("shear_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setShearX(x);
+ }
+ else if (!strcmp("shear_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setShearY(y);
+ }
+ else if (!strcmp("twist",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setTwist(tempF32);
+ }
+ else if (!strcmp("twist_begin", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTwistBegin(y);
+ }
+ else if (!strcmp("radius_offset", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRadiusOffset(y);
+ }
+ else if (!strcmp("taper_x", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperX(y);
+ }
+ else if (!strcmp("taper_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperY(y);
+ }
+ else if (!strcmp("revolutions", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRevolutions(y);
+ }
+ else if (!strcmp("skew", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setSkew(y);
+ }
+ else
+ {
+ LL_WARNS() << "unknown keyword " << " in path import" << LL_ENDL;
+ }
+ }
+ return true;
+}
+
+
+bool LLPathParams::exportFile(LLFILE *fp) const
+{
+ fprintf(fp, "\t\tpath 0\n");
+ fprintf(fp, "\t\t{\n");
+ fprintf(fp, "\t\t\tcurve\t%d\n", getCurveType());
+ fprintf(fp, "\t\t\tbegin\t%g\n", getBegin());
+ fprintf(fp, "\t\t\tend\t%g\n", getEnd());
+ fprintf(fp, "\t\t\tscale_x\t%g\n", getScaleX() );
+ fprintf(fp, "\t\t\tscale_y\t%g\n", getScaleY() );
+ fprintf(fp, "\t\t\tshear_x\t%g\n", getShearX() );
+ fprintf(fp, "\t\t\tshear_y\t%g\n", getShearY() );
+ fprintf(fp,"\t\t\ttwist\t%g\n", getTwist());
+
+ fprintf(fp,"\t\t\ttwist_begin\t%g\n", getTwistBegin());
+ fprintf(fp,"\t\t\tradius_offset\t%g\n", getRadiusOffset());
+ fprintf(fp,"\t\t\ttaper_x\t%g\n", getTaperX());
+ fprintf(fp,"\t\t\ttaper_y\t%g\n", getTaperY());
+ fprintf(fp,"\t\t\trevolutions\t%g\n", getRevolutions());
+ fprintf(fp,"\t\t\tskew\t%g\n", getSkew());
+
+ fprintf(fp, "\t\t}\n");
+ return true;
+}
+
+
+bool LLPathParams::importLegacyStream(std::istream& input_stream)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of these buffers will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ char valuestr[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+ valuestr[0] = 0;
+
+ F32 tempF32;
+ F32 x, y;
+ U32 tempU32;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf( /* Flawfinder: ignore */
+ buffer,
+ " %255s %255s",
+ keyword, valuestr);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("scale",keyword))
+ {
+ // Legacy for one dimensional scale per path
+ sscanf(valuestr,"%g",&tempF32);
+ setScale(tempF32, tempF32);
+ }
+ else if (!strcmp("scale_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setScaleX(x);
+ }
+ else if (!strcmp("scale_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setScaleY(y);
+ }
+ else if (!strcmp("shear_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setShearX(x);
+ }
+ else if (!strcmp("shear_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setShearY(y);
+ }
+ else if (!strcmp("twist",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setTwist(tempF32);
+ }
+ else if (!strcmp("twist_begin", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTwistBegin(y);
+ }
+ else if (!strcmp("radius_offset", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRadiusOffset(y);
+ }
+ else if (!strcmp("taper_x", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperX(y);
+ }
+ else if (!strcmp("taper_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperY(y);
+ }
+ else if (!strcmp("revolutions", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRevolutions(y);
+ }
+ else if (!strcmp("skew", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setSkew(y);
+ }
+ else
+ {
+ LL_WARNS() << "unknown keyword " << " in path import" << LL_ENDL;
+ }
+ }
+ return true;
+}
+
+
+bool LLPathParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream << "\t\tpath 0\n";
+ output_stream << "\t\t{\n";
+ output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n";
+ output_stream << "\t\t\tbegin\t" << getBegin() << "\n";
+ output_stream << "\t\t\tend\t" << getEnd() << "\n";
+ output_stream << "\t\t\tscale_x\t" << getScaleX() << "\n";
+ output_stream << "\t\t\tscale_y\t" << getScaleY() << "\n";
+ output_stream << "\t\t\tshear_x\t" << getShearX() << "\n";
+ output_stream << "\t\t\tshear_y\t" << getShearY() << "\n";
+ output_stream <<"\t\t\ttwist\t" << getTwist() << "\n";
+
+ output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n";
+ output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n";
+ output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n";
+ output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n";
+ output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n";
+ output_stream <<"\t\t\tskew\t" << getSkew() << "\n";
+
+ output_stream << "\t\t}\n";
+ return true;
+}
+
+LLSD LLPathParams::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["curve"] = getCurveType();
+ sd["begin"] = getBegin();
+ sd["end"] = getEnd();
+ sd["scale_x"] = getScaleX();
+ sd["scale_y"] = getScaleY();
+ sd["shear_x"] = getShearX();
+ sd["shear_y"] = getShearY();
+ sd["twist"] = getTwist();
+ sd["twist_begin"] = getTwistBegin();
+ sd["radius_offset"] = getRadiusOffset();
+ sd["taper_x"] = getTaperX();
+ sd["taper_y"] = getTaperY();
+ sd["revolutions"] = getRevolutions();
+ sd["skew"] = getSkew();
+
+ return sd;
+}
+
+bool LLPathParams::fromLLSD(LLSD& sd)
+{
+ setCurveType(sd["curve"].asInteger());
+ setBegin((F32)sd["begin"].asReal());
+ setEnd((F32)sd["end"].asReal());
+ setScaleX((F32)sd["scale_x"].asReal());
+ setScaleY((F32)sd["scale_y"].asReal());
+ setShearX((F32)sd["shear_x"].asReal());
+ setShearY((F32)sd["shear_y"].asReal());
+ setTwist((F32)sd["twist"].asReal());
+ setTwistBegin((F32)sd["twist_begin"].asReal());
+ setRadiusOffset((F32)sd["radius_offset"].asReal());
+ setTaperX((F32)sd["taper_x"].asReal());
+ setTaperY((F32)sd["taper_y"].asReal());
+ setRevolutions((F32)sd["revolutions"].asReal());
+ setSkew((F32)sd["skew"].asReal());
+ return true;
+}
+
+void LLPathParams::copyParams(const LLPathParams ¶ms)
+{
+ setCurveType(params.getCurveType());
+ setBegin(params.getBegin());
+ setEnd(params.getEnd());
+ setScale(params.getScaleX(), params.getScaleY() );
+ setShear(params.getShearX(), params.getShearY() );
+ setTwist(params.getTwist());
+ setTwistBegin(params.getTwistBegin());
+ setRadiusOffset(params.getRadiusOffset());
+ setTaper( params.getTaperX(), params.getTaperY() );
+ setRevolutions(params.getRevolutions());
+ setSkew(params.getSkew());
+}
+
+LLProfile::~LLProfile()
+{
+}
+
+
+S32 LLVolume::sNumMeshPoints = 0;
+
+LLVolume::LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool generate_single_face, const bool is_unique)
+ : mParams(params)
+{
+ mUnique = is_unique;
+ mFaceMask = 0x0;
+ mDetail = detail;
+ mSculptLevel = -2;
+ mSurfaceArea = 1.f; //only calculated for sculpts, defaults to 1 for all other prims
+ mIsMeshAssetLoaded = false;
+ mIsMeshAssetUnavaliable = false;
+ mLODScaleBias.setVec(1,1,1);
+ mHullPoints = nullptr;
+ mHullIndices = nullptr;
+ mNumHullPoints = 0;
+ mNumHullIndices = 0;
+
+ // set defaults
+ if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
+ {
+ mPathp = new LLDynamicPath();
+ }
+ else
+ {
+ mPathp = new LLPath();
+ }
+ mProfilep = new LLProfile();
+
+ mGenerateSingleFace = generate_single_face;
+
+ generate();
+
+ if ((mParams.getSculptID().isNull() && mParams.getSculptType() == LL_SCULPT_TYPE_NONE) || mParams.getSculptType() == LL_SCULPT_TYPE_MESH)
+ {
+ createVolumeFaces();
+ }
+}
+
+void LLVolume::resizePath(S32 length)
+{
+ mPathp->resizePath(length);
+ mVolumeFaces.clear();
+ setDirty();
+}
+
+void LLVolume::regen()
+{
+ generate();
+ createVolumeFaces();
+}
+
+void LLVolume::genTangents(S32 face)
+{
+ // generate legacy tangents for the specified face
+ llassert(!isMeshAssetLoaded() || mVolumeFaces[face].mTangents != nullptr); // if this is a complete mesh asset, we should already have tangents
+ mVolumeFaces[face].createTangents();
+}
+
+LLVolume::~LLVolume()
+{
+ sNumMeshPoints -= mMesh.size();
+ delete mPathp;
+
+ delete mProfilep;
+
+ mPathp = NULL;
+ mProfilep = NULL;
+ mVolumeFaces.clear();
+
+ ll_aligned_free_16(mHullPoints);
+ mHullPoints = NULL;
+ ll_aligned_free_16(mHullIndices);
+ mHullIndices = NULL;
+}
+
+bool LLVolume::generate()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ LL_CHECK_MEMORY
+ llassert_always(mProfilep);
+
+ //Added 10.03.05 Dave Parks
+ // Split is a parameter to LLProfile::generate that tesselates edges on the profile
+ // to prevent lighting and texture interpolation errors on triangles that are
+ // stretched due to twisting or scaling on the path.
+ S32 split = (S32) ((mDetail)*0.66f);
+
+ if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_LINE &&
+ (mParams.getPathParams().getScale().mV[0] != 1.0f ||
+ mParams.getPathParams().getScale().mV[1] != 1.0f) &&
+ (mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_SQUARE ||
+ mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_ISOTRI ||
+ mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_EQUALTRI ||
+ mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_RIGHTTRI))
+ {
+ split = 0;
+ }
+
+ mLODScaleBias.setVec(0.5f, 0.5f, 0.5f);
+
+ F32 profile_detail = mDetail;
+ F32 path_detail = mDetail;
+
+ if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
+ {
+ U8 path_type = mParams.getPathParams().getCurveType();
+ U8 profile_type = mParams.getProfileParams().getCurveType();
+ if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE)
+ {
+ //cylinders don't care about Z-Axis
+ mLODScaleBias.setVec(0.6f, 0.6f, 0.0f);
+ }
+ else if (path_type == LL_PCODE_PATH_CIRCLE)
+ {
+ mLODScaleBias.setVec(0.6f, 0.6f, 0.6f);
+ }
+ }
+
+ bool regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split);
+ bool regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split);
+
+ if (regenPath || regenProf )
+ {
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ sNumMeshPoints -= mMesh.size();
+ mMesh.resize(sizeT * sizeS);
+ sNumMeshPoints += mMesh.size();
+
+ //generate vertex positions
+
+ // Run along the path.
+ LLVector4a* dst = mMesh.mArray;
+
+ for (S32 s = 0; s < sizeS; ++s)
+ {
+ F32* scale = mPathp->mPath[s].mScale.getF32ptr();
+
+ F32 sc [] =
+ { scale[0], 0, 0, 0,
+ 0, scale[1], 0, 0,
+ 0, 0, scale[2], 0,
+ 0, 0, 0, 1 };
+
+ LLMatrix4 rot((F32*) mPathp->mPath[s].mRot.mMatrix);
+ LLMatrix4 scale_mat(sc);
+
+ scale_mat *= rot;
+
+ LLMatrix4a rot_mat;
+ rot_mat.loadu(scale_mat);
+
+ LLVector4a* profile = mProfilep->mProfile.mArray;
+ LLVector4a* end_profile = profile+sizeT;
+ LLVector4a offset = mPathp->mPath[s].mPos;
+
+ // hack to work around MAINT-5660 for debug until we can suss out
+ // what is wrong with the path generated that inserts NaNs...
+ if (!offset.isFinite3())
+ {
+ offset.clear();
+ }
+
+ LLVector4a tmp;
+
+ // Run along the profile.
+ while (profile < end_profile)
+ {
+ rot_mat.rotate(*profile++, tmp);
+ dst->setAdd(tmp,offset);
+ ++dst;
+ }
+ }
+
+ for (std::vector<LLProfile::Face>::iterator iter = mProfilep->mFaces.begin();
+ iter != mProfilep->mFaces.end(); ++iter)
+ {
+ LLFaceID id = iter->mFaceID;
+ mFaceMask |= id;
+ }
+ LL_CHECK_MEMORY
+ return true;
+ }
+
+ LL_CHECK_MEMORY
+ return false;
+}
+
+void LLVolumeFace::VertexData::init()
+{
+ if (!mData)
+ {
+ mData = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*2);
+ }
+}
+
+LLVolumeFace::VertexData::VertexData()
+{
+ mData = NULL;
+ init();
+}
+
+LLVolumeFace::VertexData::VertexData(const VertexData& rhs)
+{
+ mData = NULL;
+ *this = rhs;
+}
+
+const LLVolumeFace::VertexData& LLVolumeFace::VertexData::operator=(const LLVolumeFace::VertexData& rhs)
+{
+ if (this != &rhs)
+ {
+ init();
+ LLVector4a::memcpyNonAliased16((F32*) mData, (F32*) rhs.mData, 2*sizeof(LLVector4a));
+ mTexCoord = rhs.mTexCoord;
+ }
+ return *this;
+}
+
+LLVolumeFace::VertexData::~VertexData()
+{
+ ll_aligned_free_16(mData);
+ mData = NULL;
+}
+
+LLVector4a& LLVolumeFace::VertexData::getPosition()
+{
+ return mData[POSITION];
+}
+
+LLVector4a& LLVolumeFace::VertexData::getNormal()
+{
+ return mData[NORMAL];
+}
+
+const LLVector4a& LLVolumeFace::VertexData::getPosition() const
+{
+ return mData[POSITION];
+}
+
+const LLVector4a& LLVolumeFace::VertexData::getNormal() const
+{
+ return mData[NORMAL];
+}
+
+
+void LLVolumeFace::VertexData::setPosition(const LLVector4a& pos)
+{
+ mData[POSITION] = pos;
+}
+
+void LLVolumeFace::VertexData::setNormal(const LLVector4a& norm)
+{
+ mData[NORMAL] = norm;
+}
+
+bool LLVolumeFace::VertexData::operator<(const LLVolumeFace::VertexData& rhs)const
+{
+ const F32* lp = this->getPosition().getF32ptr();
+ const F32* rp = rhs.getPosition().getF32ptr();
+
+ if (lp[0] != rp[0])
+ {
+ return lp[0] < rp[0];
+ }
+
+ if (rp[1] != lp[1])
+ {
+ return lp[1] < rp[1];
+ }
+
+ if (rp[2] != lp[2])
+ {
+ return lp[2] < rp[2];
+ }
+
+ lp = getNormal().getF32ptr();
+ rp = rhs.getNormal().getF32ptr();
+
+ if (lp[0] != rp[0])
+ {
+ return lp[0] < rp[0];
+ }
+
+ if (rp[1] != lp[1])
+ {
+ return lp[1] < rp[1];
+ }
+
+ if (rp[2] != lp[2])
+ {
+ return lp[2] < rp[2];
+ }
+
+ if (mTexCoord.mV[0] != rhs.mTexCoord.mV[0])
+ {
+ return mTexCoord.mV[0] < rhs.mTexCoord.mV[0];
+ }
+
+ return mTexCoord.mV[1] < rhs.mTexCoord.mV[1];
+}
+
+bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)const
+{
+ return mData[POSITION].equals3(rhs.getPosition()) &&
+ mData[NORMAL].equals3(rhs.getNormal()) &&
+ mTexCoord == rhs.mTexCoord;
+}
+
+bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const
+{
+ bool retval = false;
+
+ const F32 epsilon = 0.00001f;
+
+ if (rhs.mData[POSITION].equals3(mData[POSITION], epsilon) &&
+ fabs(rhs.mTexCoord[0]-mTexCoord[0]) < epsilon &&
+ fabs(rhs.mTexCoord[1]-mTexCoord[1]) < epsilon)
+ {
+ if (angle_cutoff > 1.f)
+ {
+ retval = (mData[NORMAL].equals3(rhs.mData[NORMAL], epsilon));
+ }
+ else
+ {
+ F32 cur_angle = rhs.mData[NORMAL].dot3(mData[NORMAL]).getF32();
+ retval = cur_angle > angle_cutoff;
+ }
+ }
+
+ return retval;
+}
+
+bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ //input stream is now pointing at a zlib compressed block of LLSD
+ //decompress block
+ LLSD mdl;
+ U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, is, size);
+ if (uzip_result != LLUZipHelper::ZR_OK)
+ {
+ LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL;
+ return false;
+ }
+ return unpackVolumeFacesInternal(mdl);
+}
+
+bool LLVolume::unpackVolumeFaces(U8* in_data, S32 size)
+{
+ //input data is now pointing at a zlib compressed block of LLSD
+ //decompress block
+ LLSD mdl;
+ U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, in_data, size);
+ if (uzip_result != LLUZipHelper::ZR_OK)
+ {
+ LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL;
+ return false;
+ }
+ return unpackVolumeFacesInternal(mdl);
+}
+
+bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
+{
+ {
+ U32 face_count = mdl.size();
+
+ if (face_count == 0)
+ { //no faces unpacked, treat as failed decode
+ LL_WARNS() << "found no faces!" << LL_ENDL;
+ return false;
+ }
+
+ mVolumeFaces.resize(face_count);
+
+ for (size_t i = 0; i < face_count; ++i)
+ {
+ LLVolumeFace& face = mVolumeFaces[i];
+
+ if (mdl[i].has("NoGeometry"))
+ { //face has no geometry, continue
+ face.resizeIndices(3);
+ face.resizeVertices(1);
+ face.mPositions->clear();
+ face.mNormals->clear();
+ face.mTexCoords->setZero();
+ memset(face.mIndices, 0, sizeof(U16)*3);
+ continue;
+ }
+
+ LLSD::Binary pos = mdl[i]["Position"];
+ LLSD::Binary norm = mdl[i]["Normal"];
+ LLSD::Binary tangent = mdl[i]["Tangent"];
+ LLSD::Binary tc = mdl[i]["TexCoord0"];
+ LLSD::Binary idx = mdl[i]["TriangleList"];
+
+ //copy out indices
+ S32 num_indices = idx.size() / 2;
+ const S32 indices_to_discard = num_indices % 3;
+ if (indices_to_discard > 0)
+ {
+ // Invalid number of triangle indices
+ LL_WARNS() << "Incomplete triangle discarded from face! Indices count " << num_indices << " was not divisible by 3. face index: " << i << " Total: " << face_count << LL_ENDL;
+ num_indices -= indices_to_discard;
+ }
+ face.resizeIndices(num_indices);
+
+ if (num_indices > 2 && !face.mIndices)
+ {
+ LL_WARNS() << "Failed to allocate " << num_indices << " indices for face index: " << i << " Total: " << face_count << LL_ENDL;
+ continue;
+ }
+
+ if (idx.empty() || face.mNumIndices < 3)
+ { //why is there an empty index list?
+ LL_WARNS() << "Empty face present! Face index: " << i << " Total: " << face_count << LL_ENDL;
+ continue;
+ }
+
+ U16* indices = (U16*) &(idx[0]);
+ for (U32 j = 0; j < num_indices; ++j)
+ {
+ face.mIndices[j] = indices[j];
+ }
+
+ //copy out vertices
+ U32 num_verts = pos.size()/(3*2);
+ face.resizeVertices(num_verts);
+
+ if (num_verts > 0 && !face.mPositions)
+ {
+ LL_WARNS() << "Failed to allocate " << num_verts << " vertices for face index: " << i << " Total: " << face_count << LL_ENDL;
+ face.resizeIndices(0);
+ continue;
+ }
+
+ LLVector3 minp;
+ LLVector3 maxp;
+ LLVector2 min_tc;
+ LLVector2 max_tc;
+
+ minp.setValue(mdl[i]["PositionDomain"]["Min"]);
+ maxp.setValue(mdl[i]["PositionDomain"]["Max"]);
+ LLVector4a min_pos, max_pos;
+ min_pos.load3(minp.mV);
+ max_pos.load3(maxp.mV);
+
+ min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
+ max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
+
+ //unpack normalized scale/translation
+ if (mdl[i].has("NormalizedScale"))
+ {
+ face.mNormalizedScale.setValue(mdl[i]["NormalizedScale"]);
+ }
+ else
+ {
+ face.mNormalizedScale.set(1, 1, 1);
+ }
+
+ LLVector4a pos_range;
+ pos_range.setSub(max_pos, min_pos);
+ LLVector2 tc_range2 = max_tc - min_tc;
+
+ LLVector4a tc_range;
+ tc_range.set(tc_range2[0], tc_range2[1], tc_range2[0], tc_range2[1]);
+ LLVector4a min_tc4(min_tc[0], min_tc[1], min_tc[0], min_tc[1]);
+
+ LLVector4a* pos_out = face.mPositions;
+ LLVector4a* norm_out = face.mNormals;
+ LLVector4a* tc_out = (LLVector4a*) face.mTexCoords;
+
+ {
+ U16* v = (U16*) &(pos[0]);
+ for (U32 j = 0; j < num_verts; ++j)
+ {
+ pos_out->set((F32) v[0], (F32) v[1], (F32) v[2]);
+ pos_out->div(65535.f);
+ pos_out->mul(pos_range);
+ pos_out->add(min_pos);
+ pos_out++;
+ v += 3;
+ }
+
+ }
+
+ {
+ if (!norm.empty())
+ {
+ U16* n = (U16*) &(norm[0]);
+ for (U32 j = 0; j < num_verts; ++j)
+ {
+ norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]);
+ norm_out->div(65535.f);
+ norm_out->mul(2.f);
+ norm_out->sub(1.f);
+ norm_out++;
+ n += 3;
+ }
+ }
+ else
+ {
+ for (U32 j = 0; j < num_verts; ++j)
+ {
+ norm_out->clear();
+ norm_out++; // or just norm_out[j].clear();
+ }
+ }
+ }
+
+#if 0 // keep this code for now in case we decide to add support for on-the-wire tangents
+ {
+ if (!tangent.empty())
+ {
+ face.allocateTangents(face.mNumVertices);
+ U16* t = (U16*)&(tangent[0]);
+
+ // NOTE: tangents coming from the asset may not be mikkt space, but they should always be used by the GLTF shaders to
+ // maintain compliance with the GLTF spec
+ LLVector4a* t_out = face.mTangents;
+
+ for (U32 j = 0; j < num_verts; ++j)
+ {
+ t_out->set((F32)t[0], (F32)t[1], (F32)t[2], (F32) t[3]);
+ t_out->div(65535.f);
+ t_out->mul(2.f);
+ t_out->sub(1.f);
+
+ F32* tp = t_out->getF32ptr();
+ tp[3] = tp[3] < 0.f ? -1.f : 1.f;
+
+ t_out++;
+ t += 4;
+ }
+ }
+ }
+#endif
+
+ {
+ if (!tc.empty())
+ {
+ U16* t = (U16*) &(tc[0]);
+ for (U32 j = 0; j < num_verts; j+=2)
+ {
+ if (j < num_verts-1)
+ {
+ tc_out->set((F32) t[0], (F32) t[1], (F32) t[2], (F32) t[3]);
+ }
+ else
+ {
+ tc_out->set((F32) t[0], (F32) t[1], 0.f, 0.f);
+ }
+
+ t += 4;
+
+ tc_out->div(65535.f);
+ tc_out->mul(tc_range);
+ tc_out->add(min_tc4);
+
+ tc_out++;
+ }
+ }
+ else
+ {
+ for (U32 j = 0; j < num_verts; j += 2)
+ {
+ tc_out->clear();
+ tc_out++;
+ }
+ }
+ }
+
+ if (mdl[i].has("Weights"))
+ {
+ face.allocateWeights(num_verts);
+ if (!face.mWeights && num_verts)
+ {
+ LL_WARNS() << "Failed to allocate " << num_verts << " weights for face index: " << i << " Total: " << face_count << LL_ENDL;
+ face.resizeIndices(0);
+ face.resizeVertices(0);
+ continue;
+ }
+
+ LLSD::Binary weights = mdl[i]["Weights"];
+
+ U32 idx = 0;
+
+ U32 cur_vertex = 0;
+ while (idx < weights.size() && cur_vertex < num_verts)
+ {
+ const U8 END_INFLUENCES = 0xFF;
+ U8 joint = weights[idx++];
+
+ U32 cur_influence = 0;
+ LLVector4 wght(0,0,0,0);
+ U32 joints[4] = {0,0,0,0};
+ LLVector4 joints_with_weights(0,0,0,0);
+
+ while (joint != END_INFLUENCES && idx < weights.size())
+ {
+ U16 influence = weights[idx++];
+ influence |= ((U16) weights[idx++] << 8);
+
+ F32 w = llclamp((F32) influence / 65535.f, 0.001f, 0.999f);
+ wght.mV[cur_influence] = w;
+ joints[cur_influence] = joint;
+ cur_influence++;
+
+ if (cur_influence >= 4)
+ {
+ joint = END_INFLUENCES;
+ }
+ else
+ {
+ joint = weights[idx++];
+ }
+ }
+ F32 wsum = wght.mV[VX] + wght.mV[VY] + wght.mV[VZ] + wght.mV[VW];
+ if (wsum <= 0.f)
+ {
+ wght = LLVector4(0.999f,0.f,0.f,0.f);
+ }
+ for (U32 k=0; k<4; k++)
+ {
+ F32 f_combined = (F32) joints[k] + wght[k];
+ joints_with_weights[k] = f_combined;
+ // Any weights we added above should wind up non-zero and applied to a specific bone.
+ // A failure here would indicate a floating point precision error in the math.
+ llassert((k >= cur_influence) || (f_combined - S32(f_combined) > 0.0f));
+ }
+ face.mWeights[cur_vertex].loadua(joints_with_weights.mV);
+
+ cur_vertex++;
+ }
+
+ if (cur_vertex != num_verts || idx != weights.size())
+ {
+ LL_WARNS() << "Vertex weight count does not match vertex count!" << LL_ENDL;
+ }
+
+ }
+
+ // modifier flags?
+ bool do_mirror = (mParams.getSculptType() & LL_SCULPT_FLAG_MIRROR);
+ bool do_invert = (mParams.getSculptType() &LL_SCULPT_FLAG_INVERT);
+
+
+ // translate to actions:
+ bool do_reflect_x = false;
+ bool do_reverse_triangles = false;
+ bool do_invert_normals = false;
+
+ if (do_mirror)
+ {
+ do_reflect_x = true;
+ do_reverse_triangles = !do_reverse_triangles;
+ }
+
+ if (do_invert)
+ {
+ do_invert_normals = true;
+ do_reverse_triangles = !do_reverse_triangles;
+ }
+
+ // now do the work
+
+ if (do_reflect_x)
+ {
+ LLVector4a* p = (LLVector4a*) face.mPositions;
+ LLVector4a* n = (LLVector4a*) face.mNormals;
+
+ for (S32 i = 0; i < face.mNumVertices; i++)
+ {
+ p[i].mul(-1.0f);
+ n[i].mul(-1.0f);
+ }
+ }
+
+ if (do_invert_normals)
+ {
+ LLVector4a* n = (LLVector4a*) face.mNormals;
+
+ for (S32 i = 0; i < face.mNumVertices; i++)
+ {
+ n[i].mul(-1.0f);
+ }
+ }
+
+ if (do_reverse_triangles)
+ {
+ for (U32 j = 0; j < face.mNumIndices; j += 3)
+ {
+ // swap the 2nd and 3rd index
+ S32 swap = face.mIndices[j+1];
+ face.mIndices[j+1] = face.mIndices[j+2];
+ face.mIndices[j+2] = swap;
+ }
+ }
+
+ //calculate bounding box
+ // VFExtents change
+ LLVector4a& min = face.mExtents[0];
+ LLVector4a& max = face.mExtents[1];
+
+ if (face.mNumVertices < 3)
+ { //empty face, use a dummy 1cm (at 1m scale) bounding box
+ min.splat(-0.005f);
+ max.splat(0.005f);
+ }
+ else
+ {
+ min = max = face.mPositions[0];
+
+ for (S32 i = 1; i < face.mNumVertices; ++i)
+ {
+ min.setMin(min, face.mPositions[i]);
+ max.setMax(max, face.mPositions[i]);
+ }
+
+ if (face.mTexCoords)
+ {
+ LLVector2& min_tc = face.mTexCoordExtents[0];
+ LLVector2& max_tc = face.mTexCoordExtents[1];
+
+ min_tc = face.mTexCoords[0];
+ max_tc = face.mTexCoords[0];
+
+ for (U32 j = 1; j < face.mNumVertices; ++j)
+ {
+ update_min_max(min_tc, max_tc, face.mTexCoords[j]);
+ }
+ }
+ else
+ {
+ face.mTexCoordExtents[0].set(0,0);
+ face.mTexCoordExtents[1].set(1,1);
+ }
+ }
+ }
+ }
+
+ if (!cacheOptimize(true))
+ {
+ // Out of memory?
+ LL_WARNS() << "Failed to optimize!" << LL_ENDL;
+ mVolumeFaces.clear();
+ return false;
+ }
+
+ mSculptLevel = 0; // success!
+
+ return true;
+}
+
+
+bool LLVolume::isMeshAssetLoaded()
+{
+ return mIsMeshAssetLoaded;
+}
+
+void LLVolume::setMeshAssetLoaded(bool loaded)
+{
+ mIsMeshAssetLoaded = loaded;
+ if (loaded)
+ {
+ mIsMeshAssetUnavaliable = false;
+ }
+}
+
+void LLVolume::setMeshAssetUnavaliable(bool unavaliable)
+{
+ // Don't set it if at least one lod loaded
+ if (!mIsMeshAssetLoaded)
+ {
+ mIsMeshAssetUnavaliable = unavaliable;
+ }
+}
+
+bool LLVolume::isMeshAssetUnavaliable()
+{
+ return mIsMeshAssetUnavaliable;
+}
+
+void LLVolume::copyFacesTo(std::vector<LLVolumeFace> &faces) const
+{
+ faces = mVolumeFaces;
+}
+
+void LLVolume::copyFacesFrom(const std::vector<LLVolumeFace> &faces)
+{
+ mVolumeFaces = faces;
+ mSculptLevel = 0;
+}
+
+void LLVolume::copyVolumeFaces(const LLVolume* volume)
+{
+ mVolumeFaces = volume->mVolumeFaces;
+ mSculptLevel = 0;
+}
+
+bool LLVolume::cacheOptimize(bool gen_tangents)
+{
+ for (S32 i = 0; i < mVolumeFaces.size(); ++i)
+ {
+ if (!mVolumeFaces[i].cacheOptimize(gen_tangents))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+S32 LLVolume::getNumFaces() const
+{
+ return mIsMeshAssetLoaded ? getNumVolumeFaces() : (S32)mProfilep->mFaces.size();
+}
+
+
+void LLVolume::createVolumeFaces()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ if (mGenerateSingleFace)
+ {
+ // do nothing
+ }
+ else
+ {
+ S32 num_faces = getNumFaces();
+ bool partial_build = true;
+ if (num_faces != mVolumeFaces.size())
+ {
+ partial_build = false;
+ mVolumeFaces.resize(num_faces);
+ }
+ // Initialize volume faces with parameter data
+ for (S32 i = 0; i < (S32)mVolumeFaces.size(); i++)
+ {
+ LLVolumeFace& vf = mVolumeFaces[i];
+ LLProfile::Face& face = mProfilep->mFaces[i];
+ vf.mBeginS = face.mIndex;
+ vf.mNumS = face.mCount;
+ if (vf.mNumS < 0)
+ {
+ LL_ERRS() << "Volume face corruption detected." << LL_ENDL;
+ }
+
+ vf.mBeginT = 0;
+ vf.mNumT= getPath().mPath.size();
+ vf.mID = i;
+
+ // Set the type mask bits correctly
+ if (mParams.getProfileParams().getHollow() > 0)
+ {
+ vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK;
+ }
+ if (mProfilep->isOpen())
+ {
+ vf.mTypeMask |= LLVolumeFace::OPEN_MASK;
+ }
+ if (face.mCap)
+ {
+ vf.mTypeMask |= LLVolumeFace::CAP_MASK;
+ if (face.mFaceID == LL_FACE_PATH_BEGIN)
+ {
+ vf.mTypeMask |= LLVolumeFace::TOP_MASK;
+ }
+ else
+ {
+ llassert(face.mFaceID == LL_FACE_PATH_END);
+ vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK;
+ }
+ }
+ else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
+ {
+ vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK;
+ }
+ else
+ {
+ vf.mTypeMask |= LLVolumeFace::SIDE_MASK;
+ if (face.mFlat)
+ {
+ vf.mTypeMask |= LLVolumeFace::FLAT_MASK;
+ }
+ if (face.mFaceID & LL_FACE_INNER_SIDE)
+ {
+ vf.mTypeMask |= LLVolumeFace::INNER_MASK;
+ if (face.mFlat && vf.mNumS > 2)
+ { //flat inner faces have to copy vert normals
+ vf.mNumS = vf.mNumS*2;
+ if (vf.mNumS < 0)
+ {
+ LL_ERRS() << "Volume face corruption detected." << LL_ENDL;
+ }
+ }
+ }
+ else
+ {
+ vf.mTypeMask |= LLVolumeFace::OUTER_MASK;
+ }
+ }
+ }
+
+ for (face_list_t::iterator iter = mVolumeFaces.begin();
+ iter != mVolumeFaces.end(); ++iter)
+ {
+ (*iter).create(this, partial_build);
+ }
+ }
+}
+
+
+inline LLVector4a sculpt_rgb_to_vector(U8 r, U8 g, U8 b)
+{
+ // maps RGB values to vector values [0..255] -> [-0.5..0.5]
+ LLVector4a value;
+ LLVector4a sub(0.5f, 0.5f, 0.5f);
+
+ value.set(r,g,b);
+ value.mul(1.f/255.f);
+ value.sub(sub);
+
+ return value;
+}
+
+inline U32 sculpt_xy_to_index(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
+{
+ U32 index = (x + y * sculpt_width) * sculpt_components;
+ return index;
+}
+
+
+inline U32 sculpt_st_to_index(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
+{
+ U32 x = (U32) ((F32)s/(size_s) * (F32) sculpt_width);
+ U32 y = (U32) ((F32)t/(size_t) * (F32) sculpt_height);
+
+ return sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
+}
+
+
+inline LLVector4a sculpt_index_to_vector(U32 index, const U8* sculpt_data)
+{
+ LLVector4a v = sculpt_rgb_to_vector(sculpt_data[index], sculpt_data[index+1], sculpt_data[index+2]);
+
+ return v;
+}
+
+inline LLVector4a sculpt_st_to_vector(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
+{
+ U32 index = sculpt_st_to_index(s, t, size_s, size_t, sculpt_width, sculpt_height, sculpt_components);
+
+ return sculpt_index_to_vector(index, sculpt_data);
+}
+
+inline LLVector4a sculpt_xy_to_vector(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
+{
+ U32 index = sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
+
+ return sculpt_index_to_vector(index, sculpt_data);
+}
+
+
+F32 LLVolume::sculptGetSurfaceArea()
+{
+ // test to see if image has enough variation to create non-degenerate geometry
+
+ F32 area = 0;
+
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ for (S32 s = 0; s < sizeS-1; s++)
+ {
+ for (S32 t = 0; t < sizeT-1; t++)
+ {
+ // get four corners of quad
+ LLVector4a& p1 = mMesh[(s )*sizeT + (t )];
+ LLVector4a& p2 = mMesh[(s+1)*sizeT + (t )];
+ LLVector4a& p3 = mMesh[(s )*sizeT + (t+1)];
+ LLVector4a& p4 = mMesh[(s+1)*sizeT + (t+1)];
+
+ // compute the area of the quad by taking the length of the cross product of the two triangles
+ LLVector4a v0,v1,v2,v3;
+ v0.setSub(p1,p2);
+ v1.setSub(p1,p3);
+ v2.setSub(p4,p2);
+ v3.setSub(p4,p3);
+
+ LLVector4a cross1, cross2;
+ cross1.setCross3(v0,v1);
+ cross2.setCross3(v2,v3);
+
+ //LLVector3 cross1 = (p1 - p2) % (p1 - p3);
+ //LLVector3 cross2 = (p4 - p2) % (p4 - p3);
+
+ area += (cross1.getLength3() + cross2.getLength3()).getF32() / 2.f;
+ }
+ }
+
+ return area;
+}
+
+// create empty placeholder shape
+void LLVolume::sculptGenerateEmptyPlaceholder()
+{
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ S32 line = 0;
+
+ for (S32 s = 0; s < sizeS; s++)
+ {
+ for (S32 t = 0; t < sizeT; t++)
+ {
+ S32 i = t + line;
+ LLVector4a& pt = mMesh[i];
+
+ F32* p = pt.getF32ptr();
+
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+
+ llassert(pt.isFinite3());
+ }
+ line += sizeT;
+ }
+}
+
+// create sphere placeholder shape
+void LLVolume::sculptGenerateSpherePlaceholder()
+{
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ S32 line = 0;
+
+ for (S32 s = 0; s < sizeS; s++)
+ {
+ for (S32 t = 0; t < sizeT; t++)
+ {
+ S32 i = t + line;
+ LLVector4a& pt = mMesh[i];
+
+
+ F32 u = (F32)s / (sizeS - 1);
+ F32 v = (F32)t / (sizeT - 1);
+
+ const F32 RADIUS = (F32) 0.3;
+
+ F32* p = pt.getF32ptr();
+
+ p[0] = (F32)(sin(F_PI * v) * cos(2.0 * F_PI * u) * RADIUS);
+ p[1] = (F32)(sin(F_PI * v) * sin(2.0 * F_PI * u) * RADIUS);
+ p[2] = (F32)(cos(F_PI * v) * RADIUS);
+
+ llassert(pt.isFinite3());
+ }
+ line += sizeT;
+ }
+}
+
+// create the vertices from the map
+void LLVolume::sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type)
+{
+ U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
+ bool sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
+ bool sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
+ bool reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR
+
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ S32 line = 0;
+ for (S32 s = 0; s < sizeS; s++)
+ {
+ // Run along the profile.
+ for (S32 t = 0; t < sizeT; t++)
+ {
+ S32 i = t + line;
+ LLVector4a& pt = mMesh[i];
+
+ S32 reversed_t = t;
+
+ if (reverse_horizontal)
+ {
+ reversed_t = sizeT - t - 1;
+ }
+
+ U32 x = (U32) ((F32)reversed_t/(sizeT-1) * (F32) sculpt_width);
+ U32 y = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_height);
+
+
+ if (y == 0) // top row stitching
+ {
+ // pinch?
+ if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+ {
+ x = sculpt_width / 2;
+ }
+ }
+
+ if (y == sculpt_height) // bottom row stitching
+ {
+ // wrap?
+ if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
+ {
+ y = 0;
+ }
+ else
+ {
+ y = sculpt_height - 1;
+ }
+
+ // pinch?
+ if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+ {
+ x = sculpt_width / 2;
+ }
+ }
+
+ if (x == sculpt_width) // side stitching
+ {
+ // wrap?
+ if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
+ (sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
+ (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
+ {
+ x = 0;
+ }
+
+ else
+ {
+ x = sculpt_width - 1;
+ }
+ }
+
+ pt = sculpt_xy_to_vector(x, y, sculpt_width, sculpt_height, sculpt_components, sculpt_data);
+
+ if (sculpt_mirror)
+ {
+ LLVector4a scale(-1.f,1,1,1);
+ pt.mul(scale);
+ }
+
+ llassert(pt.isFinite3());
+ }
+
+ line += sizeT;
+ }
+}
+
+
+constexpr S32 SCULPT_REZ_1 = 6; // changed from 4 to 6 - 6 looks round whereas 4 looks square
+constexpr S32 SCULPT_REZ_2 = 8;
+constexpr S32 SCULPT_REZ_3 = 16;
+constexpr S32 SCULPT_REZ_4 = 32;
+
+S32 sculpt_sides(F32 detail)
+{
+ // detail is usually one of: 1, 1.5, 2.5, 4.0.
+
+ if (detail <= 1.0)
+ {
+ return SCULPT_REZ_1;
+ }
+ if (detail <= 2.0)
+ {
+ return SCULPT_REZ_2;
+ }
+ if (detail <= 3.0)
+ {
+ return SCULPT_REZ_3;
+ }
+ else
+ {
+ return SCULPT_REZ_4;
+ }
+}
+
+
+
+// determine the number of vertices in both s and t direction for this sculpt
+void sculpt_calc_mesh_resolution(U16 width, U16 height, U8 type, F32 detail, S32& s, S32& t)
+{
+ // this code has the following properties:
+ // 1) the aspect ratio of the mesh is as close as possible to the ratio of the map
+ // while still using all available verts
+ // 2) the mesh cannot have more verts than is allowed by LOD
+ // 3) the mesh cannot have more verts than is allowed by the map
+
+ S32 max_vertices_lod = (S32)pow((double)sculpt_sides(detail), 2.0);
+ S32 max_vertices_map = width * height / 4;
+
+ S32 vertices;
+ if (max_vertices_map > 0)
+ vertices = llmin(max_vertices_lod, max_vertices_map);
+ else
+ vertices = max_vertices_lod;
+
+
+ F32 ratio;
+ if ((width == 0) || (height == 0))
+ ratio = 1.f;
+ else
+ ratio = (F32) width / (F32) height;
+
+
+ s = (S32)(F32) sqrt(((F32)vertices / ratio));
+
+ s = llmax(s, 4); // no degenerate sizes, please
+ t = vertices / s;
+
+ t = llmax(t, 4); // no degenerate sizes, please
+ s = vertices / t;
+}
+
+// sculpt replaces generate() for sculpted surfaces
+void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level, bool visible_placeholder)
+{
+ U8 sculpt_type = mParams.getSculptType();
+
+ bool data_is_empty = false;
+
+ if (sculpt_width == 0 || sculpt_height == 0 || sculpt_components < 3 || sculpt_data == NULL)
+ {
+ sculpt_level = -1;
+ data_is_empty = true;
+ }
+
+ S32 requested_sizeS = 0;
+ S32 requested_sizeT = 0;
+
+ sculpt_calc_mesh_resolution(sculpt_width, sculpt_height, sculpt_type, mDetail, requested_sizeS, requested_sizeT);
+
+ mPathp->generate(mParams.getPathParams(), mDetail, 0, true, requested_sizeS);
+ mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(), mDetail, 0, true, requested_sizeT);
+
+ S32 sizeS = mPathp->mPath.size(); // we requested a specific size, now see what we really got
+ S32 sizeT = mProfilep->mProfile.size(); // we requested a specific size, now see what we really got
+
+ // weird crash bug - DEV-11158 - trying to collect more data:
+ if ((sizeS == 0) || (sizeT == 0))
+ {
+ LL_WARNS() << "sculpt bad mesh size " << sizeS << " " << sizeT << LL_ENDL;
+ }
+
+ sNumMeshPoints -= mMesh.size();
+ mMesh.resize(sizeS * sizeT);
+ sNumMeshPoints += mMesh.size();
+
+ //generate vertex positions
+ if (!data_is_empty)
+ {
+ sculptGenerateMapVertices(sculpt_width, sculpt_height, sculpt_components, sculpt_data, sculpt_type);
+
+ // don't test lowest LOD to support legacy content DEV-33670
+ if (mDetail > SCULPT_MIN_AREA_DETAIL)
+ {
+ F32 area = sculptGetSurfaceArea();
+
+ mSurfaceArea = area;
+
+ const F32 SCULPT_MAX_AREA = 384.f;
+
+ if (area < SCULPT_MIN_AREA || area > SCULPT_MAX_AREA)
+ {
+ data_is_empty = true;
+ visible_placeholder = true;
+ }
+ }
+ }
+
+ if (data_is_empty)
+ {
+ if (visible_placeholder)
+ {
+ // Object should be visible since there will be nothing else to display
+ sculptGenerateSpherePlaceholder();
+ }
+ else
+ {
+ sculptGenerateEmptyPlaceholder();
+ }
+ }
+
+ for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
+ {
+ mFaceMask |= mProfilep->mFaces[i].mFaceID;
+ }
+
+ mSculptLevel = sculpt_level;
+
+ // Delete any existing faces so that they get regenerated
+ mVolumeFaces.clear();
+
+ createVolumeFaces();
+}
+
+
+
+
+bool LLVolume::isCap(S32 face)
+{
+ return mProfilep->mFaces[face].mCap;
+}
+
+bool LLVolume::isFlat(S32 face)
+{
+ return mProfilep->mFaces[face].mFlat;
+}
+
+
+bool LLVolumeParams::isSculpt() const
+{
+ return (mSculptType & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_NONE;
+}
+
+bool LLVolumeParams::isMeshSculpt() const
+{
+ return (mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH;
+}
+
+bool LLVolumeParams::operator==(const LLVolumeParams ¶ms) const
+{
+ return ( (getPathParams() == params.getPathParams()) &&
+ (getProfileParams() == params.getProfileParams()) &&
+ (mSculptID == params.mSculptID) &&
+ (mSculptType == params.mSculptType) );
+}
+
+bool LLVolumeParams::operator!=(const LLVolumeParams ¶ms) const
+{
+ return ( (getPathParams() != params.getPathParams()) ||
+ (getProfileParams() != params.getProfileParams()) ||
+ (mSculptID != params.mSculptID) ||
+ (mSculptType != params.mSculptType) );
+}
+
+bool LLVolumeParams::operator<(const LLVolumeParams ¶ms) const
+{
+ if( getPathParams() != params.getPathParams() )
+ {
+ return getPathParams() < params.getPathParams();
+ }
+
+ if (getProfileParams() != params.getProfileParams())
+ {
+ return getProfileParams() < params.getProfileParams();
+ }
+
+ if (mSculptID != params.mSculptID)
+ {
+ return mSculptID < params.mSculptID;
+ }
+
+ return mSculptType < params.mSculptType;
+
+
+}
+
+void LLVolumeParams::copyParams(const LLVolumeParams ¶ms)
+{
+ mProfileParams.copyParams(params.mProfileParams);
+ mPathParams.copyParams(params.mPathParams);
+ mSculptID = params.getSculptID();
+ mSculptType = params.getSculptType();
+}
+
+// Less restricitve approx 0 for volumes
+constexpr F32 APPROXIMATELY_ZERO = 0.001f;
+bool approx_zero( F32 f, F32 tolerance = APPROXIMATELY_ZERO)
+{
+ return (f >= -tolerance) && (f <= tolerance);
+}
+
+// return true if in range (or nearly so)
+static bool limit_range(F32& v, F32 min, F32 max, F32 tolerance = APPROXIMATELY_ZERO)
+{
+ F32 min_delta = v - min;
+ if (min_delta < 0.f)
+ {
+ v = min;
+ if (!approx_zero(min_delta, tolerance))
+ return false;
+ }
+ F32 max_delta = max - v;
+ if (max_delta < 0.f)
+ {
+ v = max;
+ if (!approx_zero(max_delta, tolerance))
+ return false;
+ }
+ return true;
+}
+
+bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e)
+{
+ bool valid = true;
+
+ // First, clamp to valid ranges.
+ F32 begin = b;
+ valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+
+ F32 end = e;
+ if (end >= .0149f && end < MIN_CUT_DELTA) end = MIN_CUT_DELTA; // eliminate warning for common rounding error
+ valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+
+ valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
+
+ // Now set them.
+ mProfileParams.setBegin(begin);
+ mProfileParams.setEnd(end);
+
+ return valid;
+}
+
+bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e)
+{
+ bool valid = true;
+
+ // First, clamp to valid ranges.
+ F32 begin = b;
+ valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+
+ F32 end = e;
+ valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+
+ valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
+
+ // Now set them.
+ mPathParams.setBegin(begin);
+ mPathParams.setEnd(end);
+
+ return valid;
+}
+
+bool LLVolumeParams::setHollow(const F32 h)
+{
+ // Validate the hollow based on path and profile.
+ U8 profile = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ U8 hole_type = mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK;
+
+ F32 max_hollow = HOLLOW_MAX;
+
+ // Only square holes have trouble.
+ if (LL_PCODE_HOLE_SQUARE == hole_type)
+ {
+ switch(profile)
+ {
+ case LL_PCODE_PROFILE_CIRCLE:
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ max_hollow = HOLLOW_MAX_SQUARE;
+ }
+ }
+
+ F32 hollow = h;
+ bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow);
+ mProfileParams.setHollow(hollow);
+
+ return valid;
+}
+
+bool LLVolumeParams::setTwistBegin(const F32 b)
+{
+ F32 twist_begin = b;
+ bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX);
+ mPathParams.setTwistBegin(twist_begin);
+ return valid;
+}
+
+bool LLVolumeParams::setTwistEnd(const F32 e)
+{
+ F32 twist_end = e;
+ bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX);
+ mPathParams.setTwistEnd(twist_end);
+ return valid;
+}
+
+bool LLVolumeParams::setRatio(const F32 x, const F32 y)
+{
+ F32 min_x = RATIO_MIN;
+ F32 max_x = RATIO_MAX;
+ F32 min_y = RATIO_MIN;
+ F32 max_y = RATIO_MAX;
+ // If this is a circular path (and not a sphere) then 'ratio' is actually hole size.
+ U8 path_type = mPathParams.getCurveType();
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PATH_CIRCLE == path_type &&
+ LL_PCODE_PROFILE_CIRCLE_HALF != profile_type)
+ {
+ // Holes are more restricted...
+ min_x = HOLE_X_MIN;
+ max_x = HOLE_X_MAX;
+ min_y = HOLE_Y_MIN;
+ max_y = HOLE_Y_MAX;
+ }
+
+ F32 ratio_x = x;
+ bool valid = limit_range(ratio_x, min_x, max_x);
+ F32 ratio_y = y;
+ valid &= limit_range(ratio_y, min_y, max_y);
+
+ mPathParams.setScale(ratio_x, ratio_y);
+
+ return valid;
+}
+
+bool LLVolumeParams::setShear(const F32 x, const F32 y)
+{
+ F32 shear_x = x;
+ bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX);
+ F32 shear_y = y;
+ valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX);
+ mPathParams.setShear(shear_x, shear_y);
+ return valid;
+}
+
+bool LLVolumeParams::setTaperX(const F32 v)
+{
+ F32 taper = v;
+ bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
+ mPathParams.setTaperX(taper);
+ return valid;
+}
+
+bool LLVolumeParams::setTaperY(const F32 v)
+{
+ F32 taper = v;
+ bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
+ mPathParams.setTaperY(taper);
+ return valid;
+}
+
+bool LLVolumeParams::setRevolutions(const F32 r)
+{
+ F32 revolutions = r;
+ bool valid = limit_range(revolutions, REV_MIN, REV_MAX);
+ mPathParams.setRevolutions(revolutions);
+ return valid;
+}
+
+bool LLVolumeParams::setRadiusOffset(const F32 offset)
+{
+ bool valid = true;
+
+ // If this is a sphere, just set it to 0 and get out.
+ U8 path_type = mPathParams.getCurveType();
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ||
+ LL_PCODE_PATH_CIRCLE != path_type )
+ {
+ mPathParams.setRadiusOffset(0.f);
+ return true;
+ }
+
+ // Limit radius offset, based on taper and hole size y.
+ F32 radius_offset = offset;
+ F32 taper_y = getTaperY();
+ F32 radius_mag = fabs(radius_offset);
+ F32 hole_y_mag = fabs(getRatioY());
+ F32 taper_y_mag = fabs(taper_y);
+ // Check to see if the taper effects us.
+ if ( (radius_offset > 0.f && taper_y < 0.f) ||
+ (radius_offset < 0.f && taper_y > 0.f) )
+ {
+ // The taper does not help increase the radius offset range.
+ taper_y_mag = 0.f;
+ }
+ F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
+
+ // Enforce the maximum magnitude.
+ F32 delta = max_radius_mag - radius_mag;
+ if (delta < 0.f)
+ {
+ // Check radius offset sign.
+ if (radius_offset < 0.f)
+ {
+ radius_offset = -max_radius_mag;
+ }
+ else
+ {
+ radius_offset = max_radius_mag;
+ }
+ valid = approx_zero(delta, .1f);
+ }
+
+ mPathParams.setRadiusOffset(radius_offset);
+ return valid;
+}
+
+bool LLVolumeParams::setSkew(const F32 skew_value)
+{
+ bool valid = true;
+
+ // Check the skew value against the revolutions.
+ F32 skew = llclamp(skew_value, SKEW_MIN, SKEW_MAX);
+ F32 skew_mag = fabs(skew);
+ F32 revolutions = getRevolutions();
+ F32 scale_x = getRatioX();
+ F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
+ // Discontinuity; A revolution of 1 allows skews below 0.5.
+ if ( fabs(revolutions - 1.0f) < 0.001)
+ min_skew_mag = 0.0f;
+
+ // Clip skew.
+ F32 delta = skew_mag - min_skew_mag;
+ if (delta < 0.f)
+ {
+ // Check skew sign.
+ if (skew < 0.0f)
+ {
+ skew = -min_skew_mag;
+ }
+ else
+ {
+ skew = min_skew_mag;
+ }
+ valid = approx_zero(delta, .01f);
+ }
+
+ mPathParams.setSkew(skew);
+ return valid;
+}
+
+bool LLVolumeParams::setSculptID(const LLUUID& sculpt_id, U8 sculpt_type)
+{
+ mSculptID = sculpt_id;
+ mSculptType = sculpt_type;
+ return true;
+}
+
+bool LLVolumeParams::setType(U8 profile, U8 path)
+{
+ bool result = true;
+ // First, check profile and path for validity.
+ U8 profile_type = profile & LL_PCODE_PROFILE_MASK;
+ U8 hole_type = (profile & LL_PCODE_HOLE_MASK) >> 4;
+ U8 path_type = path >> 4;
+
+ if (profile_type > LL_PCODE_PROFILE_MAX)
+ {
+ // Bad profile. Make it square.
+ profile = LL_PCODE_PROFILE_SQUARE;
+ result = false;
+ LL_WARNS() << "LLVolumeParams::setType changing bad profile type (" << profile_type
+ << ") to be LL_PCODE_PROFILE_SQUARE" << LL_ENDL;
+ }
+ else if (hole_type > LL_PCODE_HOLE_MAX)
+ {
+ // Bad hole. Make it the same.
+ profile = profile_type;
+ result = false;
+ LL_WARNS() << "LLVolumeParams::setType changing bad hole type (" << hole_type
+ << ") to be LL_PCODE_HOLE_SAME" << LL_ENDL;
+ }
+
+ if (path_type < LL_PCODE_PATH_MIN ||
+ path_type > LL_PCODE_PATH_MAX)
+ {
+ // Bad path. Make it linear.
+ result = false;
+ LL_WARNS() << "LLVolumeParams::setType changing bad path (" << path
+ << ") to be LL_PCODE_PATH_LINE" << LL_ENDL;
+ path = LL_PCODE_PATH_LINE;
+ }
+
+ mProfileParams.setCurveType(profile);
+ mPathParams.setCurveType(path);
+ return result;
+}
+
+// static
+bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
+ U8 path_curve, F32 path_begin, F32 path_end,
+ F32 scx, F32 scy, F32 shx, F32 shy,
+ F32 twistend, F32 twistbegin, F32 radiusoffset,
+ F32 tx, F32 ty, F32 revolutions, F32 skew)
+{
+ LLVolumeParams test_params;
+ if (!test_params.setType (prof_curve, path_curve))
+ {
+ return false;
+ }
+ if (!test_params.setBeginAndEndS (prof_begin, prof_end))
+ {
+ return false;
+ }
+ if (!test_params.setBeginAndEndT (path_begin, path_end))
+ {
+ return false;
+ }
+ if (!test_params.setHollow (hollow))
+ {
+ return false;
+ }
+ if (!test_params.setTwistBegin (twistbegin))
+ {
+ return false;
+ }
+ if (!test_params.setTwistEnd (twistend))
+ {
+ return false;
+ }
+ if (!test_params.setRatio (scx, scy))
+ {
+ return false;
+ }
+ if (!test_params.setShear (shx, shy))
+ {
+ return false;
+ }
+ if (!test_params.setTaper (tx, ty))
+ {
+ return false;
+ }
+ if (!test_params.setRevolutions (revolutions))
+ {
+ return false;
+ }
+ if (!test_params.setRadiusOffset (radiusoffset))
+ {
+ return false;
+ }
+ if (!test_params.setSkew (skew))
+ {
+ return false;
+ }
+ return true;
+}
+
+void LLVolume::getLoDTriangleCounts(const LLVolumeParams& params, S32* counts)
+{ //attempt to approximate the number of triangles that will result from generating a volume LoD set for the
+ //supplied LLVolumeParams -- inaccurate, but a close enough approximation for determining streaming cost
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
+ F32 detail[] = {1.f, 1.5f, 2.5f, 4.f};
+ for (S32 i = 0; i < 4; i++)
+ {
+ S32 count = 0;
+ S32 path_points = LLPath::getNumPoints(params.getPathParams(), detail[i]);
+ S32 profile_points = LLProfile::getNumPoints(params.getProfileParams(), false, detail[i]);
+
+ count = (profile_points-1)*2*(path_points-1);
+ count += profile_points*2;
+
+ counts[i] = count;
+ }
+}
+
+
+S32 LLVolume::getNumTriangles(S32* vcount) const
+{
+ U32 triangle_count = 0;
+ U32 vertex_count = 0;
+
+ for (S32 i = 0; i < getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = getVolumeFace(i);
+ triangle_count += face.mNumIndices/3;
+
+ vertex_count += face.mNumVertices;
+ }
+
+
+ if (vcount)
+ {
+ *vcount = vertex_count;
+ }
+
+ return triangle_count;
+}
+
+
+//-----------------------------------------------------------------------------
+// generateSilhouetteVertices()
+//-----------------------------------------------------------------------------
+void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
+ std::vector<LLVector3> &normals,
+ const LLVector3& obj_cam_vec_in,
+ const LLMatrix4& mat_in,
+ const LLMatrix3& norm_mat_in,
+ S32 face_mask)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ LLMatrix4a mat;
+ mat.loadu(mat_in);
+
+ LLMatrix4a norm_mat;
+ norm_mat.loadu(norm_mat_in);
+
+ LLVector4a obj_cam_vec;
+ obj_cam_vec.load3(obj_cam_vec_in.mV);
+
+ vertices.clear();
+ normals.clear();
+
+ if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+ {
+ return;
+ }
+
+ S32 cur_index = 0;
+ //for each face
+ for (face_list_t::iterator iter = mVolumeFaces.begin();
+ iter != mVolumeFaces.end(); ++iter)
+ {
+ LLVolumeFace& face = *iter;
+
+ if (!(face_mask & (0x1 << cur_index++)) ||
+ face.mNumIndices == 0 || face.mEdge.empty())
+ {
+ continue;
+ }
+
+ if (face.mTypeMask & (LLVolumeFace::CAP_MASK))
+ {
+ LLVector4a* v = (LLVector4a*)face.mPositions;
+ LLVector4a* n = (LLVector4a*)face.mNormals;
+
+ for (U32 j = 0; j < face.mNumIndices / 3; j++)
+ {
+ for (S32 k = 0; k < 3; k++)
+ {
+ S32 index = face.mEdge[j * 3 + k];
+
+ if (index == -1)
+ {
+ // silhouette edge, currently only cubes, so no other conditions
+
+ S32 v1 = face.mIndices[j * 3 + k];
+ S32 v2 = face.mIndices[j * 3 + ((k + 1) % 3)];
+
+ LLVector4a t;
+ mat.affineTransform(v[v1], t);
+ vertices.push_back(LLVector3(t[0], t[1], t[2]));
+
+ norm_mat.rotate(n[v1], t);
+
+ t.normalize3fast();
+ normals.push_back(LLVector3(t[0], t[1], t[2]));
+
+ mat.affineTransform(v[v2], t);
+ vertices.push_back(LLVector3(t[0], t[1], t[2]));
+
+ norm_mat.rotate(n[v2], t);
+ t.normalize3fast();
+ normals.push_back(LLVector3(t[0], t[1], t[2]));
+ }
+ }
+ }
+
+ }
+ else
+ {
+ //==============================================
+ //DEBUG draw edge map instead of silhouette edge
+ //==============================================
+
+#if DEBUG_SILHOUETTE_EDGE_MAP
+
+ //for each triangle
+ U32 tri_count = face.mNumIndices / 3;
+ for (U32 j = 0; j < tri_count; j++) {
+ //get vertices
+ S32 v1 = face.mIndices[j*3+0];
+ S32 v2 = face.mIndices[j*3+1];
+ S32 v3 = face.mIndices[j*3+2];
+
+ //get current face center
+ LLVector3 cCenter = (face.mVertices[v1].getPosition() +
+ face.mVertices[v2].getPosition() +
+ face.mVertices[v3].getPosition()) / 3.0f;
+
+ //for each edge
+ for (S32 k = 0; k < 3; k++) {
+ S32 nIndex = face.mEdge[j*3+k];
+ if (nIndex <= -1) {
+ continue;
+ }
+
+ if (nIndex >= (S32)tri_count) {
+ continue;
+ }
+ //get neighbor vertices
+ v1 = face.mIndices[nIndex*3+0];
+ v2 = face.mIndices[nIndex*3+1];
+ v3 = face.mIndices[nIndex*3+2];
+
+ //get neighbor face center
+ LLVector3 nCenter = (face.mVertices[v1].getPosition() +
+ face.mVertices[v2].getPosition() +
+ face.mVertices[v3].getPosition()) / 3.0f;
+
+ //draw line
+ vertices.push_back(cCenter);
+ vertices.push_back(nCenter);
+ normals.push_back(LLVector3(1,1,1));
+ normals.push_back(LLVector3(1,1,1));
+ segments.push_back(vertices.size());
+ }
+ }
+
+ continue;
+
+ //==============================================
+ //DEBUG
+ //==============================================
+
+ //==============================================
+ //DEBUG draw normals instead of silhouette edge
+ //==============================================
+#elif DEBUG_SILHOUETTE_NORMALS
+
+ //for each vertex
+ for (U32 j = 0; j < face.mNumVertices; j++) {
+ vertices.push_back(face.mVertices[j].getPosition());
+ vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].getNormal()*0.1f);
+ normals.push_back(LLVector3(0,0,1));
+ normals.push_back(LLVector3(0,0,1));
+ segments.push_back(vertices.size());
+#if DEBUG_SILHOUETTE_BINORMALS
+ vertices.push_back(face.mVertices[j].getPosition());
+ vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].mTangent*0.1f);
+ normals.push_back(LLVector3(0,0,1));
+ normals.push_back(LLVector3(0,0,1));
+ segments.push_back(vertices.size());
+#endif
+ }
+
+ continue;
+#else
+ //==============================================
+ //DEBUG
+ //==============================================
+
+ constexpr U8 AWAY = 0x01,
+ TOWARDS = 0x02;
+
+ //for each triangle
+ std::vector<U8> fFacing;
+ vector_append(fFacing, face.mNumIndices/3);
+
+ LLVector4a* v = (LLVector4a*) face.mPositions;
+ LLVector4a* n = (LLVector4a*) face.mNormals;
+
+ for (U32 j = 0; j < face.mNumIndices/3; j++)
+ {
+ //approximate normal
+ S32 v1 = face.mIndices[j*3+0];
+ S32 v2 = face.mIndices[j*3+1];
+ S32 v3 = face.mIndices[j*3+2];
+
+ LLVector4a c1,c2;
+ c1.setSub(v[v1], v[v2]);
+ c2.setSub(v[v2], v[v3]);
+
+ LLVector4a norm;
+
+ norm.setCross3(c1, c2);
+
+ if (norm.dot3(norm) < 0.00000001f)
+ {
+ fFacing[j] = AWAY | TOWARDS;
+ }
+ else
+ {
+ //get view vector
+ LLVector4a view;
+ view.setSub(obj_cam_vec, v[v1]);
+ bool away = view.dot3(norm) > 0.0f;
+ if (away)
+ {
+ fFacing[j] = AWAY;
+ }
+ else
+ {
+ fFacing[j] = TOWARDS;
+ }
+ }
+ }
+
+ //for each triangle
+ for (U32 j = 0; j < face.mNumIndices/3; j++)
+ {
+ if (fFacing[j] == (AWAY | TOWARDS))
+ { //this is a degenerate triangle
+ //take neighbor facing (degenerate faces get facing of one of their neighbors)
+ // *FIX IF NEEDED: this does not deal with neighboring degenerate faces
+ for (S32 k = 0; k < 3; k++)
+ {
+ S32 index = face.mEdge[j*3+k];
+ if (index != -1)
+ {
+ fFacing[j] = fFacing[index];
+ break;
+ }
+ }
+ continue; //skip degenerate face
+ }
+
+ //for each edge
+ for (S32 k = 0; k < 3; k++) {
+ S32 index = face.mEdge[j*3+k];
+ if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) {
+ //our neighbor is degenerate, make him face our direction
+ fFacing[face.mEdge[j*3+k]] = fFacing[j];
+ continue;
+ }
+
+ if (index == -1 || //edge has no neighbor, MUST be a silhouette edge
+ (fFacing[index] & fFacing[j]) == 0) { //we found a silhouette edge
+
+ S32 v1 = face.mIndices[j*3+k];
+ S32 v2 = face.mIndices[j*3+((k+1)%3)];
+
+ LLVector4a t;
+ mat.affineTransform(v[v1], t);
+ vertices.push_back(LLVector3(t[0], t[1], t[2]));
+
+ norm_mat.rotate(n[v1], t);
+
+ t.normalize3fast();
+ normals.push_back(LLVector3(t[0], t[1], t[2]));
+
+ mat.affineTransform(v[v2], t);
+ vertices.push_back(LLVector3(t[0], t[1], t[2]));
+
+ norm_mat.rotate(n[v2], t);
+ t.normalize3fast();
+ normals.push_back(LLVector3(t[0], t[1], t[2]));
+ }
+ }
+ }
+#endif
+ }
+ }
+}
+
+S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ S32 face,
+ LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent_out)
+{
+ S32 hit_face = -1;
+
+ S32 start_face;
+ S32 end_face;
+
+ if (face == -1) // ALL_SIDES
+ {
+ start_face = 0;
+ end_face = getNumVolumeFaces() - 1;
+ }
+ else
+ {
+ start_face = face;
+ end_face = face;
+ }
+
+ LLVector4a dir;
+ dir.setSub(end, start);
+
+ F32 closest_t = 2.f; // must be larger than 1
+
+ end_face = llmin(end_face, getNumVolumeFaces()-1);
+
+ for (S32 i = start_face; i <= end_face; i++)
+ {
+ LLVolumeFace &face = mVolumeFaces[i];
+
+ LLVector4a box_center;
+ box_center.setAdd(face.mExtents[0], face.mExtents[1]);
+ box_center.mul(0.5f);
+
+ LLVector4a box_size;
+ box_size.setSub(face.mExtents[1], face.mExtents[0]);
+
+ if (LLLineSegmentBoxIntersect(start, end, box_center, box_size))
+ {
+ if (tangent_out != NULL) // if the caller wants tangents, we may need to generate them
+ {
+ genTangents(i);
+ }
+
+ if (isUnique())
+ { //don't bother with an octree for flexi volumes
+ U32 tri_count = face.mNumIndices/3;
+
+ for (U32 j = 0; j < tri_count; ++j)
+ {
+ U16 idx0 = face.mIndices[j*3+0];
+ U16 idx1 = face.mIndices[j*3+1];
+ U16 idx2 = face.mIndices[j*3+2];
+
+ const LLVector4a& v0 = face.mPositions[idx0];
+ const LLVector4a& v1 = face.mPositions[idx1];
+ const LLVector4a& v2 = face.mPositions[idx2];
+
+ F32 a,b,t;
+
+ if (LLTriangleRayIntersect(v0, v1, v2,
+ start, dir, a, b, t))
+ {
+ if ((t >= 0.f) && // if hit is after start
+ (t <= 1.f) && // and before end
+ (t < closest_t)) // and this hit is closer
+ {
+ closest_t = t;
+ hit_face = i;
+
+ if (intersection != NULL)
+ {
+ LLVector4a intersect = dir;
+ intersect.mul(closest_t);
+ intersect.add(start);
+ *intersection = intersect;
+ }
+
+
+ if (tex_coord != NULL)
+ {
+ LLVector2* tc = (LLVector2*) face.mTexCoords;
+ *tex_coord = ((1.f - a - b) * tc[idx0] +
+ a * tc[idx1] +
+ b * tc[idx2]);
+
+ }
+
+ if (normal!= NULL)
+ {
+ LLVector4a* norm = face.mNormals;
+
+ LLVector4a n1,n2,n3;
+ n1 = norm[idx0];
+ n1.mul(1.f-a-b);
+
+ n2 = norm[idx1];
+ n2.mul(a);
+
+ n3 = norm[idx2];
+ n3.mul(b);
+
+ n1.add(n2);
+ n1.add(n3);
+
+ *normal = n1;
+ }
+
+ if (tangent_out != NULL)
+ {
+ LLVector4a* tangents = face.mTangents;
+
+ LLVector4a t1,t2,t3;
+ t1 = tangents[idx0];
+ t1.mul(1.f-a-b);
+
+ t2 = tangents[idx1];
+ t2.mul(a);
+
+ t3 = tangents[idx2];
+ t3.mul(b);
+
+ t1.add(t2);
+ t1.add(t3);
+
+ *tangent_out = t1;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!face.getOctree())
+ {
+ face.createOctree();
+ }
+
+ LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
+ intersect.traverse(face.getOctree());
+ if (intersect.mHitFace)
+ {
+ hit_face = i;
+ }
+ }
+ }
+ }
+
+
+ return hit_face;
+}
+
+class LLVertexIndexPair
+{
+public:
+ LLVertexIndexPair(const LLVector3 &vertex, const S32 index);
+
+ LLVector3 mVertex;
+ S32 mIndex;
+};
+
+LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index)
+{
+ mVertex = vertex;
+ mIndex = index;
+}
+
+constexpr F32 VERTEX_SLOP = 0.00001f;
+
+struct lessVertex
+{
+ bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b)
+ {
+ const F32 slop = VERTEX_SLOP;
+
+ if (a->mVertex.mV[0] + slop < b->mVertex.mV[0])
+ {
+ return true;
+ }
+ else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0])
+ {
+ return false;
+ }
+
+ if (a->mVertex.mV[1] + slop < b->mVertex.mV[1])
+ {
+ return true;
+ }
+ else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1])
+ {
+ return false;
+ }
+
+ if (a->mVertex.mV[2] + slop < b->mVertex.mV[2])
+ {
+ return true;
+ }
+ else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2])
+ {
+ return false;
+ }
+
+ return false;
+ }
+};
+
+struct lessTriangle
+{
+ bool operator()(const S32 *a, const S32 *b)
+ {
+ if (*a < *b)
+ {
+ return true;
+ }
+ else if (*a > *b)
+ {
+ return false;
+ }
+
+ if (*(a+1) < *(b+1))
+ {
+ return true;
+ }
+ else if (*(a+1) > *(b+1))
+ {
+ return false;
+ }
+
+ if (*(a+2) < *(b+2))
+ {
+ return true;
+ }
+ else if (*(a+2) > *(b+2))
+ {
+ return false;
+ }
+
+ return false;
+ }
+};
+
+bool equalTriangle(const S32 *a, const S32 *b)
+{
+ if ((*a == *b) && (*(a+1) == *(b+1)) && (*(a+2) == *(b+2)))
+ {
+ return true;
+ }
+ return false;
+}
+
+bool LLVolumeParams::importFile(LLFILE *fp)
+{
+ //LL_INFOS() << "importing volume" << LL_ENDL;
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of this buffer will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+
+ while (!feof(fp))
+ {
+ if (fgets(buffer, BUFSIZE, fp) == NULL)
+ {
+ buffer[0] = '\0';
+ }
+
+ sscanf(buffer, " %255s", keyword); /* Flawfinder: ignore */
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("profile", keyword))
+ {
+ mProfileParams.importFile(fp);
+ }
+ else if (!strcmp("path",keyword))
+ {
+ mPathParams.importFile(fp);
+ }
+ else
+ {
+ LL_WARNS() << "unknown keyword " << keyword << " in volume import" << LL_ENDL;
+ }
+ }
+
+ return true;
+}
+
+bool LLVolumeParams::exportFile(LLFILE *fp) const
+{
+ fprintf(fp,"\tshape 0\n");
+ fprintf(fp,"\t{\n");
+ mPathParams.exportFile(fp);
+ mProfileParams.exportFile(fp);
+ fprintf(fp, "\t}\n");
+ return true;
+}
+
+
+bool LLVolumeParams::importLegacyStream(std::istream& input_stream)
+{
+ //LL_INFOS() << "importing volume" << LL_ENDL;
+ const S32 BUFSIZE = 16384;
+ // *NOTE: changing the size or type of this buffer will require
+ // changing the sscanf below.
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ char keyword[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf(buffer, " %255s", keyword);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("profile", keyword))
+ {
+ mProfileParams.importLegacyStream(input_stream);
+ }
+ else if (!strcmp("path",keyword))
+ {
+ mPathParams.importLegacyStream(input_stream);
+ }
+ else
+ {
+ LL_WARNS() << "unknown keyword " << keyword << " in volume import" << LL_ENDL;
+ }
+ }
+
+ return true;
+}
+
+bool LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream <<"\tshape 0\n";
+ output_stream <<"\t{\n";
+ mPathParams.exportLegacyStream(output_stream);
+ mProfileParams.exportLegacyStream(output_stream);
+ output_stream << "\t}\n";
+ return true;
+}
+
+LLSD LLVolumeParams::sculptAsLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["id"] = getSculptID();
+ sd["type"] = getSculptType();
+
+ return sd;
+}
+
+bool LLVolumeParams::sculptFromLLSD(LLSD& sd)
+{
+ setSculptID(sd["id"].asUUID(), (U8)sd["type"].asInteger());
+ return true;
+}
+
+LLSD LLVolumeParams::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["path"] = mPathParams;
+ sd["profile"] = mProfileParams;
+ sd["sculpt"] = sculptAsLLSD();
+
+ return sd;
+}
+
+bool LLVolumeParams::fromLLSD(LLSD& sd)
+{
+ mPathParams.fromLLSD(sd["path"]);
+ mProfileParams.fromLLSD(sd["profile"]);
+ sculptFromLLSD(sd["sculpt"]);
+
+ return true;
+}
+
+void LLVolumeParams::reduceS(F32 begin, F32 end)
+{
+ begin = llclampf(begin);
+ end = llclampf(end);
+ if (begin > end)
+ {
+ F32 temp = begin;
+ begin = end;
+ end = temp;
+ }
+ F32 a = mProfileParams.getBegin();
+ F32 b = mProfileParams.getEnd();
+ mProfileParams.setBegin(a + begin * (b - a));
+ mProfileParams.setEnd(a + end * (b - a));
+}
+
+void LLVolumeParams::reduceT(F32 begin, F32 end)
+{
+ begin = llclampf(begin);
+ end = llclampf(end);
+ if (begin > end)
+ {
+ F32 temp = begin;
+ begin = end;
+ end = temp;
+ }
+ F32 a = mPathParams.getBegin();
+ F32 b = mPathParams.getEnd();
+ mPathParams.setBegin(a + begin * (b - a));
+ mPathParams.setEnd(a + end * (b - a));
+}
+
+const F32 MIN_CONCAVE_PROFILE_WEDGE = 0.125f; // 1/8 unity
+const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f; // 1/9 unity
+
+// returns true if the shape can be approximated with a convex shape
+// for collison purposes
+bool LLVolumeParams::isConvex() const
+{
+ if (!getSculptID().isNull())
+ {
+ // can't determine, be safe and say no:
+ return false;
+ }
+
+ F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
+ F32 hollow = mProfileParams.getHollow();
+
+ U8 path_type = mPathParams.getCurveType();
+ if ( path_length > MIN_CONCAVE_PATH_WEDGE
+ && ( mPathParams.getTwist() != mPathParams.getTwistBegin()
+ || (hollow > 0.f
+ && LL_PCODE_PATH_LINE != path_type) ) )
+ {
+ // twist along a "not too short" path is concave
+ return false;
+ }
+
+ F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
+ bool same_hole = hollow == 0.f
+ || (mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK) == LL_PCODE_HOLE_SAME;
+
+ F32 min_profile_wedge = MIN_CONCAVE_PROFILE_WEDGE;
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
+ {
+ // it is a sphere and spheres get twice the minimum profile wedge
+ min_profile_wedge = 2.f * MIN_CONCAVE_PROFILE_WEDGE;
+ }
+
+ bool convex_profile = ( ( profile_length == 1.f
+ || profile_length <= 0.5f )
+ && hollow == 0.f ) // trivially convex
+ || ( profile_length <= min_profile_wedge
+ && same_hole ); // effectvely convex (even when hollow)
+
+ if (!convex_profile)
+ {
+ // profile is concave
+ return false;
+ }
+
+ if ( LL_PCODE_PATH_LINE == path_type )
+ {
+ // straight paths with convex profile
+ return true;
+ }
+
+ bool concave_path = (path_length < 1.0f) && (path_length > 0.5f);
+ if (concave_path)
+ {
+ return false;
+ }
+
+ // we're left with spheres, toroids and tubes
+ if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
+ {
+ // at this stage all spheres must be convex
+ return true;
+ }
+
+ // it's a toroid or tube
+ if ( path_length <= MIN_CONCAVE_PATH_WEDGE )
+ {
+ // effectively convex
+ return true;
+ }
+
+ return false;
+}
+
+// debug
+void LLVolumeParams::setCube()
+{
+ mProfileParams.setCurveType(LL_PCODE_PROFILE_SQUARE);
+ mProfileParams.setBegin(0.f);
+ mProfileParams.setEnd(1.f);
+ mProfileParams.setHollow(0.f);
+
+ mPathParams.setBegin(0.f);
+ mPathParams.setEnd(1.f);
+ mPathParams.setScale(1.f, 1.f);
+ mPathParams.setShear(0.f, 0.f);
+ mPathParams.setCurveType(LL_PCODE_PATH_LINE);
+ mPathParams.setTwistBegin(0.f);
+ mPathParams.setTwistEnd(0.f);
+ mPathParams.setRadiusOffset(0.f);
+ mPathParams.setTaper(0.f, 0.f);
+ mPathParams.setRevolutions(0.f);
+ mPathParams.setSkew(0.f);
+}
+
+LLFaceID LLVolume::generateFaceMask()
+{
+ LLFaceID new_mask = 0x0000;
+
+ switch(mParams.getProfileParams().getCurveType() & LL_PCODE_PROFILE_MASK)
+ {
+ case LL_PCODE_PROFILE_CIRCLE:
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ new_mask |= LL_FACE_OUTER_SIDE_0;
+ break;
+ case LL_PCODE_PROFILE_SQUARE:
+ {
+ for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 4.f); side < llceil(mParams.getProfileParams().getEnd() * 4.f); side++)
+ {
+ new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_ISOTRI:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ case LL_PCODE_PROFILE_RIGHTTRI:
+ {
+ for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 3.f); side < llceil(mParams.getProfileParams().getEnd() * 3.f); side++)
+ {
+ new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+ }
+ }
+ break;
+ default:
+ LL_ERRS() << "Unknown profile!" << LL_ENDL;
+ break;
+ }
+
+ // handle hollow objects
+ if (mParams.getProfileParams().getHollow() > 0)
+ {
+ new_mask |= LL_FACE_INNER_SIDE;
+ }
+
+ // handle open profile curves
+ if (mProfilep->isOpen())
+ {
+ new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END;
+ }
+
+ // handle open path curves
+ if (mPathp->isOpen())
+ {
+ new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END;
+ }
+
+ return new_mask;
+}
+
+bool LLVolume::isFaceMaskValid(LLFaceID face_mask)
+{
+ LLFaceID test_mask = 0;
+ for(S32 i = 0; i < getNumFaces(); i++)
+ {
+ test_mask |= mProfilep->mFaces[i].mFaceID;
+ }
+
+ return test_mask == face_mask;
+}
+
+bool LLVolume::isConvex() const
+{
+ // mParams.isConvex() may return false even though the final
+ // geometry is actually convex due to LOD approximations.
+ // TODO -- provide LLPath and LLProfile with isConvex() methods
+ // that correctly determine convexity. -- Leviathan
+ return mParams.isConvex();
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params)
+{
+ s << "{type=" << (U32) profile_params.mCurveType;
+ s << ", begin=" << profile_params.mBegin;
+ s << ", end=" << profile_params.mEnd;
+ s << ", hollow=" << profile_params.mHollow;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params)
+{
+ s << "{type=" << (U32) path_params.mCurveType;
+ s << ", begin=" << path_params.mBegin;
+ s << ", end=" << path_params.mEnd;
+ s << ", twist=" << path_params.mTwistEnd;
+ s << ", scale=" << path_params.mScale;
+ s << ", shear=" << path_params.mShear;
+ s << ", twist_begin=" << path_params.mTwistBegin;
+ s << ", radius_offset=" << path_params.mRadiusOffset;
+ s << ", taper=" << path_params.mTaper;
+ s << ", revolutions=" << path_params.mRevolutions;
+ s << ", skew=" << path_params.mSkew;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params)
+{
+ s << "{profileparams = " << volume_params.mProfileParams;
+ s << ", pathparams = " << volume_params.mPathParams;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLProfile &profile)
+{
+ s << " {open=" << (U32) profile.mOpen;
+ s << ", dirty=" << profile.mDirty;
+ s << ", totalout=" << profile.mTotalOut;
+ s << ", total=" << profile.mTotal;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLPath &path)
+{
+ s << "{open=" << (U32) path.mOpen;
+ s << ", dirty=" << path.mDirty;
+ s << ", step=" << path.mStep;
+ s << ", total=" << path.mTotal;
+ s << "}";
+ return s;
+}
+
+std::ostream& operator<<(std::ostream &s, const LLVolume &volume)
+{
+ s << "{params = " << volume.getParams();
+ s << ", path = " << *volume.mPathp;
+ s << ", profile = " << *volume.mProfilep;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
+{
+ s << "{params = " << volumep->getParams();
+ s << ", path = " << *(volumep->mPathp);
+ s << ", profile = " << *(volumep->mProfilep);
+ s << "}";
+ return s;
+}
+
+LLVolumeFace::LLVolumeFace() :
+ mID(0),
+ mTypeMask(0),
+ mBeginS(0),
+ mBeginT(0),
+ mNumS(0),
+ mNumT(0),
+ mNumVertices(0),
+ mNumAllocatedVertices(0),
+ mNumIndices(0),
+ mPositions(NULL),
+ mNormals(NULL),
+ mTangents(NULL),
+ mTexCoords(NULL),
+ mIndices(NULL),
+ mWeights(NULL),
+#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
+ mJustWeights(NULL),
+ mJointIndices(NULL),
+#endif
+ mWeightsScrubbed(false),
+ mOctree(NULL),
+ mOctreeTriangles(NULL),
+ mOptimized(false)
+{
+ mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
+ mExtents[0].splat(-0.5f);
+ mExtents[1].splat(0.5f);
+ mCenter = mExtents+2;
+}
+
+LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
+: mID(0),
+ mTypeMask(0),
+ mBeginS(0),
+ mBeginT(0),
+ mNumS(0),
+ mNumT(0),
+ mNumVertices(0),
+ mNumAllocatedVertices(0),
+ mNumIndices(0),
+ mPositions(NULL),
+ mNormals(NULL),
+ mTangents(NULL),
+ mTexCoords(NULL),
+ mIndices(NULL),
+ mWeights(NULL),
+#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
+ mJustWeights(NULL),
+ mJointIndices(NULL),
+#endif
+ mWeightsScrubbed(false),
+ mOctree(NULL),
+ mOctreeTriangles(NULL)
+{
+ mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
+ mCenter = mExtents+2;
+ *this = src;
+}
+
+LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
+{
+ if (&src == this)
+ { //self assignment, do nothing
+ return *this;
+ }
+
+ mID = src.mID;
+ mTypeMask = src.mTypeMask;
+ mBeginS = src.mBeginS;
+ mBeginT = src.mBeginT;
+ mNumS = src.mNumS;
+ mNumT = src.mNumT;
+
+ mExtents[0] = src.mExtents[0];
+ mExtents[1] = src.mExtents[1];
+ *mCenter = *src.mCenter;
+
+ mNumVertices = 0;
+ mNumIndices = 0;
+
+ freeData();
+
+ resizeVertices(src.mNumVertices);
+ resizeIndices(src.mNumIndices);
+
+ if (mNumVertices)
+ {
+ S32 vert_size = mNumVertices*sizeof(LLVector4a);
+ S32 tc_size = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF;
+
+ LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) src.mPositions, vert_size);
+
+ if (src.mNormals)
+ {
+ LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) src.mNormals, vert_size);
+ }
+
+ if(src.mTexCoords)
+ {
+ LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size);
+ }
+
+ if (src.mTangents)
+ {
+ allocateTangents(src.mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mTangents, (F32*) src.mTangents, vert_size);
+ }
+ else
+ {
+ ll_aligned_free_16(mTangents);
+ mTangents = NULL;
+ }
+
+ if (src.mWeights)
+ {
+ llassert(!mWeights); // don't orphan an old alloc here accidentally
+ allocateWeights(src.mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mWeights, (F32*) src.mWeights, vert_size);
+ mWeightsScrubbed = src.mWeightsScrubbed;
+ }
+ else
+ {
+ ll_aligned_free_16(mWeights);
+ mWeights = NULL;
+ mWeightsScrubbed = false;
+ }
+
+ #if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
+ if (src.mJointIndices)
+ {
+ llassert(!mJointIndices); // don't orphan an old alloc here accidentally
+ allocateJointIndices(src.mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mJointIndices, (F32*) src.mJointIndices, src.mNumVertices * sizeof(U8) * 4);
+ }
+ else*/
+ {
+ ll_aligned_free_16(mJointIndices);
+ mJointIndices = NULL;
+ }
+ #endif
+
+ }
+
+ if (mNumIndices)
+ {
+ S32 idx_size = (mNumIndices*sizeof(U16)+0xF) & ~0xF;
+
+ LLVector4a::memcpyNonAliased16((F32*) mIndices, (F32*) src.mIndices, idx_size);
+ }
+ else
+ {
+ ll_aligned_free_16(mIndices);
+ mIndices = NULL;
+ }
+
+ mOptimized = src.mOptimized;
+ mNormalizedScale = src.mNormalizedScale;
+
+ //delete
+ return *this;
+}
+
+LLVolumeFace::~LLVolumeFace()
+{
+ ll_aligned_free_16(mExtents);
+ mExtents = NULL;
+ mCenter = NULL;
+
+ freeData();
+}
+
+void LLVolumeFace::freeData()
+{
+ ll_aligned_free<64>(mPositions);
+ mPositions = NULL;
+
+ //normals and texture coordinates are part of the same buffer as mPositions, do not free them separately
+ mNormals = NULL;
+ mTexCoords = NULL;
+
+ ll_aligned_free_16(mIndices);
+ mIndices = NULL;
+ ll_aligned_free_16(mTangents);
+ mTangents = NULL;
+ ll_aligned_free_16(mWeights);
+ mWeights = NULL;
+
+#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
+ ll_aligned_free_16(mJointIndices);
+ mJointIndices = NULL;
+ ll_aligned_free_16(mJustWeights);
+ mJustWeights = NULL;
+#endif
+
+ destroyOctree();
+}
+
+bool LLVolumeFace::create(LLVolume* volume, bool partial_build)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ //tree for this face is no longer valid
+ destroyOctree();
+
+ LL_CHECK_MEMORY
+ bool ret = false ;
+ if (mTypeMask & CAP_MASK)
+ {
+ ret = createCap(volume, partial_build);
+ LL_CHECK_MEMORY
+ }
+ else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
+ {
+ ret = createSide(volume, partial_build);
+ LL_CHECK_MEMORY
+ }
+ else
+ {
+ LL_ERRS() << "Unknown/uninitialized face type!" << LL_ENDL;
+ }
+
+ return ret ;
+}
+
+void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv)
+{
+ cv.setPosition(mPositions[index]);
+ if (mNormals)
+ {
+ cv.setNormal(mNormals[index]);
+ }
+ else
+ {
+ cv.getNormal().clear();
+ }
+
+ if (mTexCoords)
+ {
+ cv.mTexCoord = mTexCoords[index];
+ }
+ else
+ {
+ cv.mTexCoord.clear();
+ }
+}
+
+bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const
+{
+ return getPosition().equals3(rhs.getPosition()) &&
+ mTexCoord == rhs.mTexCoord &&
+ getNormal().equals3(rhs.getNormal());
+}
+
+bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a, const LLVector3& b) const
+{
+ if (a.mV[0] != b.mV[0])
+ {
+ return a.mV[0] < b.mV[0];
+ }
+
+ if (a.mV[1] != b.mV[1])
+ {
+ return a.mV[1] < b.mV[1];
+ }
+
+ return a.mV[2] < b.mV[2];
+}
+
+void LLVolumeFace::remap()
+{
+ // Generate a remap buffer
+ std::vector<unsigned int> remap(mNumVertices);
+ S32 remap_vertices_count = LLMeshOptimizer::generateRemapMultiU16(&remap[0],
+ mIndices,
+ mNumIndices,
+ mPositions,
+ mNormals,
+ mTexCoords,
+ mNumVertices);
+
+ // Allocate new buffers
+ S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF;
+ U16* remap_indices = (U16*)ll_aligned_malloc_16(size);
+
+ S32 tc_bytes_size = ((remap_vertices_count * sizeof(LLVector2)) + 0xF) & ~0xF;
+ LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * remap_vertices_count + tc_bytes_size);
+ LLVector4a* remap_normals = remap_positions + remap_vertices_count;
+ LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count);
+
+ // Fill the buffers
+ LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]);
+ LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]);
+ LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]);
+ LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]);
+
+ // Free unused buffers
+ ll_aligned_free_16(mIndices);
+ ll_aligned_free<64>(mPositions);
+
+ // Tangets are now invalid
+ ll_aligned_free_16(mTangents);
+ mTangents = NULL;
+
+ // Assign new values
+ mIndices = remap_indices;
+ mPositions = remap_positions;
+ mNormals = remap_normals;
+ mTexCoords = remap_tex_coords;
+ mNumVertices = remap_vertices_count;
+ mNumAllocatedVertices = remap_vertices_count;
+}
+
+void LLVolumeFace::optimize(F32 angle_cutoff)
+{
+ LLVolumeFace new_face;
+
+ //map of points to vector of vertices at that point
+ std::map<U64, std::vector<VertexMapData> > point_map;
+
+ LLVector4a range;
+ range.setSub(mExtents[1],mExtents[0]);
+
+ //remove redundant vertices
+ for (U32 i = 0; i < mNumIndices; ++i)
+ {
+ U16 index = mIndices[i];
+
+ if (index >= mNumVertices)
+ {
+ // invalid index
+ // replace with a valid index to avoid crashes
+ index = mNumVertices - 1;
+ mIndices[i] = index;
+
+ // Needs better logging
+ LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL;
+ }
+
+ LLVolumeFace::VertexData cv;
+ getVertexData(index, cv);
+
+ bool found = false;
+
+ LLVector4a pos;
+ pos.setSub(mPositions[index], mExtents[0]);
+ pos.div(range);
+
+ U64 pos64 = 0;
+
+ pos64 = (U16) (pos[0]*65535);
+ pos64 = pos64 | (((U64) (pos[1]*65535)) << 16);
+ pos64 = pos64 | (((U64) (pos[2]*65535)) << 32);
+
+ std::map<U64, std::vector<VertexMapData> >::iterator point_iter = point_map.find(pos64);
+
+ if (point_iter != point_map.end())
+ { //duplicate point might exist
+ for (U32 j = 0; j < point_iter->second.size(); ++j)
+ {
+ LLVolumeFace::VertexData& tv = (point_iter->second)[j];
+ if (tv.compareNormal(cv, angle_cutoff))
+ {
+ found = true;
+ new_face.pushIndex((point_iter->second)[j].mIndex);
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ new_face.pushVertex(cv);
+ U16 index = (U16) new_face.mNumVertices-1;
+ new_face.pushIndex(index);
+
+ VertexMapData d;
+ d.setPosition(cv.getPosition());
+ d.mTexCoord = cv.mTexCoord;
+ d.setNormal(cv.getNormal());
+ d.mIndex = index;
+ if (point_iter != point_map.end())
+ {
+ point_iter->second.push_back(d);
+ }
+ else
+ {
+ point_map[pos64].push_back(d);
+ }
+ }
+ }
+
+
+ if (angle_cutoff > 1.f && !mNormals)
+ {
+ // Now alloc'd with positions
+ //ll_aligned_free_16(new_face.mNormals);
+ new_face.mNormals = NULL;
+ }
+
+ if (!mTexCoords)
+ {
+ // Now alloc'd with positions
+ //ll_aligned_free_16(new_face.mTexCoords);
+ new_face.mTexCoords = NULL;
+ }
+
+ // Only swap data if we've actually optimized the mesh
+ //
+ if (new_face.mNumVertices <= mNumVertices)
+ {
+ llassert(new_face.mNumIndices == mNumIndices);
+ swapData(new_face);
+ }
+
+}
+
+class LLVCacheTriangleData;
+
+class LLVCacheVertexData
+{
+public:
+ S32 mIdx;
+ S32 mCacheTag;
+ F64 mScore;
+ U32 mActiveTriangles;
+ std::vector<LLVCacheTriangleData*> mTriangles;
+
+ LLVCacheVertexData()
+ {
+ mCacheTag = -1;
+ mScore = 0.0;
+ mActiveTriangles = 0;
+ mIdx = -1;
+ }
+};
+
+class LLVCacheTriangleData
+{
+public:
+ bool mActive;
+ F64 mScore;
+ LLVCacheVertexData* mVertex[3];
+
+ LLVCacheTriangleData()
+ {
+ mActive = true;
+ mScore = 0.0;
+ mVertex[0] = mVertex[1] = mVertex[2] = NULL;
+ }
+
+ void complete()
+ {
+ mActive = false;
+ for (S32 i = 0; i < 3; ++i)
+ {
+ if (mVertex[i])
+ {
+ llassert(mVertex[i]->mActiveTriangles > 0);
+ mVertex[i]->mActiveTriangles--;
+ }
+ }
+ }
+
+ bool operator<(const LLVCacheTriangleData& rhs) const
+ { //highest score first
+ return rhs.mScore < mScore;
+ }
+};
+
+constexpr F64 FindVertexScore_CacheDecayPower = 1.5;
+constexpr F64 FindVertexScore_LastTriScore = 0.75;
+constexpr F64 FindVertexScore_ValenceBoostScale = 2.0;
+constexpr F64 FindVertexScore_ValenceBoostPower = 0.5;
+constexpr U32 MaxSizeVertexCache = 32;
+constexpr F64 FindVertexScore_Scaler = 1.0/(MaxSizeVertexCache-3);
+
+F64 find_vertex_score(LLVCacheVertexData& data)
+{
+ F64 score = -1.0;
+
+ score = 0.0;
+
+ S32 cache_idx = data.mCacheTag;
+
+ if (cache_idx < 0)
+ {
+ //not in cache
+ }
+ else
+ {
+ if (cache_idx < 3)
+ { //vertex was in the last triangle
+ score = FindVertexScore_LastTriScore;
+ }
+ else
+ { //more points for being higher in the cache
+ score = 1.0-((cache_idx-3)*FindVertexScore_Scaler);
+ score = pow(score, FindVertexScore_CacheDecayPower);
+ }
+ }
+
+ //bonus points for having low valence
+ F64 valence_boost = pow((F64)data.mActiveTriangles, -FindVertexScore_ValenceBoostPower);
+ score += FindVertexScore_ValenceBoostScale * valence_boost;
+
+ return score;
+}
+
+class LLVCacheFIFO
+{
+public:
+ LLVCacheVertexData* mCache[MaxSizeVertexCache];
+ U32 mMisses;
+
+ LLVCacheFIFO()
+ {
+ mMisses = 0;
+ for (U32 i = 0; i < MaxSizeVertexCache; ++i)
+ {
+ mCache[i] = NULL;
+ }
+ }
+
+ void addVertex(LLVCacheVertexData* data)
+ {
+ if (data->mCacheTag == -1)
+ {
+ mMisses++;
+
+ S32 end = MaxSizeVertexCache-1;
+
+ if (mCache[end])
+ {
+ mCache[end]->mCacheTag = -1;
+ }
+
+ for (S32 i = end; i > 0; --i)
+ {
+ mCache[i] = mCache[i-1];
+ if (mCache[i])
+ {
+ mCache[i]->mCacheTag = i;
+ }
+ }
+
+ mCache[0] = data;
+ data->mCacheTag = 0;
+ }
+ }
+};
+
+class LLVCacheLRU
+{
+public:
+ LLVCacheVertexData* mCache[MaxSizeVertexCache+3];
+
+ LLVCacheTriangleData* mBestTriangle;
+
+ U32 mMisses;
+
+ LLVCacheLRU()
+ {
+ for (U32 i = 0; i < MaxSizeVertexCache+3; ++i)
+ {
+ mCache[i] = NULL;
+ }
+
+ mBestTriangle = NULL;
+ mMisses = 0;
+ }
+
+ void addVertex(LLVCacheVertexData* data)
+ {
+ S32 end = MaxSizeVertexCache+2;
+ if (data->mCacheTag != -1)
+ { //just moving a vertex to the front of the cache
+ end = data->mCacheTag;
+ }
+ else
+ {
+ mMisses++;
+ if (mCache[end])
+ { //adding a new vertex, vertex at end of cache falls off
+ mCache[end]->mCacheTag = -1;
+ }
+ }
+
+ for (S32 i = end; i > 0; --i)
+ { //adjust cache pointers and tags
+ mCache[i] = mCache[i-1];
+
+ if (mCache[i])
+ {
+ mCache[i]->mCacheTag = i;
+ }
+ }
+
+ mCache[0] = data;
+ mCache[0]->mCacheTag = 0;
+ }
+
+ void addTriangle(LLVCacheTriangleData* data)
+ {
+ addVertex(data->mVertex[0]);
+ addVertex(data->mVertex[1]);
+ addVertex(data->mVertex[2]);
+ }
+
+ void updateScores()
+ {
+ LLVCacheVertexData** data_iter = mCache+MaxSizeVertexCache;
+ LLVCacheVertexData** end_data = mCache+MaxSizeVertexCache+3;
+
+ while(data_iter != end_data)
+ {
+ LLVCacheVertexData* data = *data_iter++;
+ //trailing 3 vertices aren't actually in the cache for scoring purposes
+ if (data)
+ {
+ data->mCacheTag = -1;
+ }
+ }
+
+ data_iter = mCache;
+ end_data = mCache+MaxSizeVertexCache;
+
+ while (data_iter != end_data)
+ { //update scores of vertices in cache
+ LLVCacheVertexData* data = *data_iter++;
+ if (data)
+ {
+ data->mScore = find_vertex_score(*data);
+ }
+ }
+
+ mBestTriangle = NULL;
+ //update triangle scores
+ data_iter = mCache;
+ end_data = mCache+MaxSizeVertexCache+3;
+
+ while (data_iter != end_data)
+ {
+ LLVCacheVertexData* data = *data_iter++;
+ if (data)
+ {
+ for (std::vector<LLVCacheTriangleData*>::iterator iter = data->mTriangles.begin(), end_iter = data->mTriangles.end(); iter != end_iter; ++iter)
+ {
+ LLVCacheTriangleData* tri = *iter;
+ if (tri->mActive)
+ {
+ tri->mScore = tri->mVertex[0] ? tri->mVertex[0]->mScore : 0;
+ tri->mScore += tri->mVertex[1] ? tri->mVertex[1]->mScore : 0;
+ tri->mScore += tri->mVertex[2] ? tri->mVertex[2]->mScore : 0;
+
+ if (!mBestTriangle || mBestTriangle->mScore < tri->mScore)
+ {
+ mBestTriangle = tri;
+ }
+ }
+ }
+ }
+ }
+
+ //knock trailing 3 vertices off the cache
+ data_iter = mCache+MaxSizeVertexCache;
+ end_data = mCache+MaxSizeVertexCache+3;
+ while (data_iter != end_data)
+ {
+ LLVCacheVertexData* data = *data_iter;
+ if (data)
+ {
+ llassert(data->mCacheTag == -1);
+ *data_iter = NULL;
+ }
+ ++data_iter;
+ }
+ }
+};
+
+// data structures for tangent generation
+
+struct MikktData
+{
+ LLVolumeFace* face;
+ std::vector<LLVector3> p;
+ std::vector<LLVector3> n;
+ std::vector<LLVector2> tc;
+ std::vector<LLVector4> w;
+ std::vector<LLVector4> t;
+
+ MikktData(LLVolumeFace* f)
+ : face(f)
+ {
+ U32 count = face->mNumIndices;
+
+ p.resize(count);
+ n.resize(count);
+ tc.resize(count);
+ t.resize(count);
+
+ if (face->mWeights)
+ {
+ w.resize(count);
+ }
+
+
+ LLVector3 inv_scale(1.f / face->mNormalizedScale.mV[0], 1.f / face->mNormalizedScale.mV[1], 1.f / face->mNormalizedScale.mV[2]);
+
+
+ for (int i = 0; i < face->mNumIndices; ++i)
+ {
+ U32 idx = face->mIndices[i];
+
+ p[i].set(face->mPositions[idx].getF32ptr());
+ p[i].scaleVec(face->mNormalizedScale); //put mesh in original coordinate frame when reconstructing tangents
+ n[i].set(face->mNormals[idx].getF32ptr());
+ n[i].scaleVec(inv_scale);
+ n[i].normalize();
+ tc[i].set(face->mTexCoords[idx]);
+
+ if (idx >= face->mNumVertices)
+ {
+ // invalid index
+ // replace with a valid index to avoid crashes
+ idx = face->mNumVertices - 1;
+ face->mIndices[i] = idx;
+
+ // Needs better logging
+ LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL;
+ }
+
+ if (face->mWeights)
+ {
+ w[i].set(face->mWeights[idx].getF32ptr());
+ }
+ }
+ }
+};
+
+
+bool LLVolumeFace::cacheOptimize(bool gen_tangents)
+{ //optimize for vertex cache according to Forsyth method:
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
+ llassert(!mOptimized);
+ mOptimized = true;
+
+ if (gen_tangents && mNormals && mTexCoords)
+ { // generate mikkt space tangents before cache optimizing since the index buffer may change
+ // a bit of a hack to do this here, but this function gets called exactly once for the lifetime of a mesh
+ // and is executed on a background thread
+ SMikkTSpaceInterface ms;
+
+ ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ LLVolumeFace* face = data->face;
+ return face->mNumIndices / 3;
+ };
+
+ ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace)
+ {
+ return 3;
+ };
+
+ ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ F32* v = data->p[iFace * 3 + iVert].mV;
+ fvPosOut[0] = v[0];
+ fvPosOut[1] = v[1];
+ fvPosOut[2] = v[2];
+ };
+
+ ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ F32* n = data->n[iFace * 3 + iVert].mV;
+ fvNormOut[0] = n[0];
+ fvNormOut[1] = n[1];
+ fvNormOut[2] = n[2];
+ };
+
+ ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ F32* tc = data->tc[iFace * 3 + iVert].mV;
+ fvTexcOut[0] = tc[0];
+ fvTexcOut[1] = tc[1];
+ };
+
+ ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ S32 i = iFace * 3 + iVert;
+
+ data->t[i].set(fvTangent);
+ data->t[i].mV[3] = fSign;
+ };
+
+ ms.m_setTSpace = nullptr;
+
+ MikktData data(this);
+
+ SMikkTSpaceContext ctx = { &ms, &data };
+
+ genTangSpaceDefault(&ctx);
+
+ //re-weld
+ meshopt_Stream mos[] =
+ {
+ { &data.p[0], sizeof(LLVector3), sizeof(LLVector3) },
+ { &data.n[0], sizeof(LLVector3), sizeof(LLVector3) },
+ { &data.t[0], sizeof(LLVector4), sizeof(LLVector4) },
+ { &data.tc[0], sizeof(LLVector2), sizeof(LLVector2) },
+ { data.w.empty() ? nullptr : &data.w[0], sizeof(LLVector4), sizeof(LLVector4) }
+ };
+
+ std::vector<U32> remap;
+ remap.resize(data.p.size());
+
+ U32 stream_count = data.w.empty() ? 4 : 5;
+
+ size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count);
+
+ if (vert_count < 65535 && vert_count != 0)
+ {
+ std::vector<U32> indices;
+ indices.resize(mNumIndices);
+
+ //copy results back into volume
+ resizeVertices(vert_count);
+
+ if (!data.w.empty())
+ {
+ allocateWeights(vert_count);
+ }
+
+ allocateTangents(mNumVertices);
+
+ for (int i = 0; i < mNumIndices; ++i)
+ {
+ U32 src_idx = i;
+ U32 dst_idx = remap[i];
+ if (dst_idx >= mNumVertices)
+ {
+ dst_idx = mNumVertices - 1;
+ // Shouldn't happen, figure out what gets returned in remap and why.
+ llassert(false);
+ LL_DEBUGS_ONCE("LLVOLUME") << "Invalid destination index, substituting" << LL_ENDL;
+ }
+ mIndices[i] = dst_idx;
+
+ mPositions[dst_idx].load3(data.p[src_idx].mV);
+ mNormals[dst_idx].load3(data.n[src_idx].mV);
+ mTexCoords[dst_idx] = data.tc[src_idx];
+
+ mTangents[dst_idx].loadua(data.t[src_idx].mV);
+
+ if (mWeights)
+ {
+ mWeights[dst_idx].loadua(data.w[src_idx].mV);
+ }
+ }
+
+ // put back in normalized coordinate frame
+ LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]);
+ LLVector4a scale;
+ scale.load3(mNormalizedScale.mV);
+ scale.getF32ptr()[3] = 1.f;
+
+ for (int i = 0; i < mNumVertices; ++i)
+ {
+ mPositions[i].mul(inv_scale);
+ mNormals[i].mul(scale);
+ mNormals[i].normalize3();
+ F32 w = mTangents[i].getF32ptr()[3];
+ mTangents[i].mul(scale);
+ mTangents[i].normalize3();
+ mTangents[i].getF32ptr()[3] = w;
+ }
+ }
+ else
+ {
+ if (vert_count == 0)
+ {
+ LL_WARNS_ONCE("LLVOLUME") << "meshopt_generateVertexRemapMulti failed to process a model or model was invalid" << LL_ENDL;
+ }
+ // blew past the max vertex size limit, use legacy tangent generation which never adds verts
+ createTangents();
+ }
+ }
+
+ // cache optimize index buffer
+
+ // meshopt needs scratch space, do some pointer shuffling to avoid an extra index buffer copy
+ U16* src_indices = mIndices;
+ mIndices = nullptr;
+ resizeIndices(mNumIndices);
+
+ meshopt_optimizeVertexCache<U16>(mIndices, src_indices, mNumIndices, mNumVertices);
+
+ ll_aligned_free_16(src_indices);
+
+ return true;
+}
+
+void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVector4a& size)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ if (getOctree())
+ {
+ return;
+ }
+
+ llassert(mNumIndices % 3 == 0);
+
+ mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL);
+ new LLVolumeOctreeListener(mOctree);
+ const U32 num_triangles = mNumIndices / 3;
+ // Initialize all the triangles we need
+ mOctreeTriangles = new LLVolumeTriangle[num_triangles];
+
+ for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index)
+ { //for each triangle
+ const U32 index = triangle_index * 3;
+ LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index];
+
+ const LLVector4a& v0 = mPositions[mIndices[index]];
+ const LLVector4a& v1 = mPositions[mIndices[index + 1]];
+ const LLVector4a& v2 = mPositions[mIndices[index + 2]];
+
+ //store pointers to vertex data
+ tri->mV[0] = &v0;
+ tri->mV[1] = &v1;
+ tri->mV[2] = &v2;
+
+ //store indices
+ tri->mIndex[0] = mIndices[index];
+ tri->mIndex[1] = mIndices[index + 1];
+ tri->mIndex[2] = mIndices[index + 2];
+
+ //get minimum point
+ LLVector4a min = v0;
+ min.setMin(min, v1);
+ min.setMin(min, v2);
+
+ //get maximum point
+ LLVector4a max = v0;
+ max.setMax(max, v1);
+ max.setMax(max, v2);
+
+ //compute center
+ LLVector4a center;
+ center.setAdd(min, max);
+ center.mul(0.5f);
+
+ tri->mPositionGroup = center;
+
+ //compute "radius"
+ LLVector4a size;
+ size.setSub(max,min);
+
+ tri->mRadius = size.getLength3().getF32() * scaler;
+
+ //insert
+ mOctree->insert(tri);
+ }
+
+ //remove unneeded octree layers
+ while (!mOctree->balance()) { }
+
+ //calculate AABB for each node
+ LLVolumeOctreeRebound rebound(this);
+ rebound.traverse(mOctree);
+
+ if (gDebugGL)
+ {
+ LLVolumeOctreeValidate validate;
+ validate.traverse(mOctree);
+ }
+}
+
+void LLVolumeFace::destroyOctree()
+{
+ delete mOctree;
+ mOctree = NULL;
+ delete[] mOctreeTriangles;
+ mOctreeTriangles = NULL;
+}
+
+const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const
+{
+ return mOctree;
+}
+
+
+void LLVolumeFace::swapData(LLVolumeFace& rhs)
+{
+ llswap(rhs.mPositions, mPositions);
+ llswap(rhs.mNormals, mNormals);
+ llswap(rhs.mTangents, mTangents);
+ llswap(rhs.mTexCoords, mTexCoords);
+ llswap(rhs.mIndices,mIndices);
+ llswap(rhs.mNumVertices, mNumVertices);
+ llswap(rhs.mNumIndices, mNumIndices);
+}
+
+void LerpPlanarVertex(LLVolumeFace::VertexData& v0,
+ LLVolumeFace::VertexData& v1,
+ LLVolumeFace::VertexData& v2,
+ LLVolumeFace::VertexData& vout,
+ F32 coef01,
+ F32 coef02)
+{
+
+ LLVector4a lhs;
+ lhs.setSub(v1.getPosition(), v0.getPosition());
+ lhs.mul(coef01);
+ LLVector4a rhs;
+ rhs.setSub(v2.getPosition(), v0.getPosition());
+ rhs.mul(coef02);
+
+ rhs.add(lhs);
+ rhs.add(v0.getPosition());
+
+ vout.setPosition(rhs);
+
+ vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02);
+ vout.setNormal(v0.getNormal());
+}
+
+bool LLVolumeFace::createUnCutCubeCap(LLVolume* volume, bool partial_build)
+{
+ LL_CHECK_MEMORY
+
+ const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh();
+ const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile;
+ S32 max_s = volume->getProfile().getTotal();
+ S32 max_t = volume->getPath().mPath.size();
+
+ // S32 i;
+ S32 grid_size = (profile.size()-1)/4;
+ // VFExtents change
+ LLVector4a& min = mExtents[0];
+ LLVector4a& max = mExtents[1];
+
+ S32 offset = 0;
+ if (mTypeMask & TOP_MASK)
+ {
+ offset = (max_t-1) * max_s;
+ }
+ else
+ {
+ offset = mBeginS;
+ }
+
+ {
+ VertexData corners[4];
+ VertexData baseVert;
+ for(S32 t = 0; t < 4; t++)
+ {
+ corners[t].getPosition().load4a(mesh[offset + (grid_size*t)].getF32ptr());
+ corners[t].mTexCoord.mV[0] = profile[grid_size*t][0]+0.5f;
+ corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t][1];
+ }
+
+ {
+ LLVector4a lhs;
+ lhs.setSub(corners[1].getPosition(), corners[0].getPosition());
+ LLVector4a rhs;
+ rhs.setSub(corners[2].getPosition(), corners[1].getPosition());
+ baseVert.getNormal().setCross3(lhs, rhs);
+ baseVert.getNormal().normalize3fast();
+ }
+
+ if(!(mTypeMask & TOP_MASK))
+ {
+ baseVert.getNormal().mul(-1.0f);
+ }
+ else
+ {
+ //Swap the UVs on the U(X) axis for top face
+ LLVector2 swap;
+ swap = corners[0].mTexCoord;
+ corners[0].mTexCoord=corners[3].mTexCoord;
+ corners[3].mTexCoord=swap;
+ swap = corners[1].mTexCoord;
+ corners[1].mTexCoord=corners[2].mTexCoord;
+ corners[2].mTexCoord=swap;
+ }
+
+ S32 size = (grid_size+1)*(grid_size+1);
+ resizeVertices(size);
+
+ LLVector4a* pos = (LLVector4a*) mPositions;
+ LLVector4a* norm = (LLVector4a*) mNormals;
+ LLVector2* tc = (LLVector2*) mTexCoords;
+
+ for(int gx = 0;gx<grid_size+1;gx++)
+ {
+ for(int gy = 0;gy<grid_size+1;gy++)
+ {
+ VertexData newVert;
+ LerpPlanarVertex(
+ corners[0],
+ corners[1],
+ corners[3],
+ newVert,
+ (F32)gx/(F32)grid_size,
+ (F32)gy/(F32)grid_size);
+
+ *pos++ = newVert.getPosition();
+ *norm++ = baseVert.getNormal();
+ *tc++ = newVert.mTexCoord;
+
+ if (gx == 0 && gy == 0)
+ {
+ min = newVert.getPosition();
+ max = min;
+ }
+ else
+ {
+ min.setMin(min, newVert.getPosition());
+ max.setMax(max, newVert.getPosition());
+ }
+ }
+ }
+
+ mCenter->setAdd(min, max);
+ mCenter->mul(0.5f);
+ }
+
+ if (!partial_build)
+ {
+ resizeIndices(grid_size*grid_size*6);
+ if (!volume->isMeshAssetLoaded())
+ {
+ S32 size = grid_size * grid_size * 6;
+ try
+ {
+ mEdge.resize(size);
+ }
+ catch (std::bad_alloc&)
+ {
+ LL_WARNS("LLVOLUME") << "Resize of mEdge to " << size << " failed" << LL_ENDL;
+ return false;
+ }
+ }
+
+ U16* out = mIndices;
+
+ S32 idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
+
+ int cur_edge = 0;
+
+ for(S32 gx = 0;gx<grid_size;gx++)
+ {
+
+ for(S32 gy = 0;gy<grid_size;gy++)
+ {
+ if (mTypeMask & TOP_MASK)
+ {
+ for(S32 i=5;i>=0;i--)
+ {
+ *out++ = ((gy*(grid_size+1))+gx+idxs[i]);
+ }
+
+ S32 edge_value = grid_size * 2 * gy + gx * 2;
+
+ if (gx > 0)
+ {
+ mEdge[cur_edge++] = edge_value;
+ }
+ else
+ {
+ mEdge[cur_edge++] = -1; // Mark face to higlight it
+ }
+
+ if (gy < grid_size - 1)
+ {
+ mEdge[cur_edge++] = edge_value;
+ }
+ else
+ {
+ mEdge[cur_edge++] = -1;
+ }
+
+ mEdge[cur_edge++] = edge_value;
+
+ if (gx < grid_size - 1)
+ {
+ mEdge[cur_edge++] = edge_value;
+ }
+ else
+ {
+ mEdge[cur_edge++] = -1;
+ }
+
+ if (gy > 0)
+ {
+ mEdge[cur_edge++] = edge_value;
+ }
+ else
+ {
+ mEdge[cur_edge++] = -1;
+ }
+
+ mEdge[cur_edge++] = edge_value;
+ }
+ else
+ {
+ for(S32 i=0;i<6;i++)
+ {
+ *out++ = ((gy*(grid_size+1))+gx+idxs[i]);
+ }
+
+ S32 edge_value = grid_size * 2 * gy + gx * 2;
+
+ if (gy > 0)
+ {
+ mEdge[cur_edge++] = edge_value;
+ }
+ else
+ {
+ mEdge[cur_edge++] = -1;
+ }
+
+ if (gx < grid_size - 1)
+ {
+ mEdge[cur_edge++] = edge_value;
+ }
+ else
+ {
+ mEdge[cur_edge++] = -1;
+ }
+
+ mEdge[cur_edge++] = edge_value;
+
+ if (gy < grid_size - 1)
+ {
+ mEdge[cur_edge++] = edge_value;
+ }
+ else
+ {
+ mEdge[cur_edge++] = -1;
+ }
+
+ if (gx > 0)
+ {
+ mEdge[cur_edge++] = edge_value;
+ }
+ else
+ {
+ mEdge[cur_edge++] = -1;
+ }
+
+ mEdge[cur_edge++] = edge_value;
+ }
+ }
+ }
+ }
+
+ LL_CHECK_MEMORY
+ return true;
+}
+
+
+bool LLVolumeFace::createCap(LLVolume* volume, bool partial_build)
+{
+ if (!(mTypeMask & HOLLOW_MASK) &&
+ !(mTypeMask & OPEN_MASK) &&
+ ((volume->getParams().getPathParams().getBegin()==0.0f)&&
+ (volume->getParams().getPathParams().getEnd()==1.0f))&&
+ (volume->getParams().getProfileParams().getCurveType()==LL_PCODE_PROFILE_SQUARE &&
+ volume->getParams().getPathParams().getCurveType()==LL_PCODE_PATH_LINE)
+ ){
+ return createUnCutCubeCap(volume, partial_build);
+ }
+
+ S32 num_vertices = 0, num_indices = 0;
+
+ const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh();
+ const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile;
+
+ // All types of caps have the same number of vertices and indices
+ num_vertices = profile.size();
+ num_indices = (profile.size() - 2)*3;
+
+ if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
+ {
+ resizeVertices(num_vertices+1);
+
+ //if (!partial_build)
+ {
+ resizeIndices(num_indices+3);
+ }
+ }
+ else
+ {
+ resizeVertices(num_vertices);
+ //if (!partial_build)
+ {
+ resizeIndices(num_indices);
+ }
+ }
+
+ LL_CHECK_MEMORY;
+
+ S32 max_s = volume->getProfile().getTotal();
+ S32 max_t = volume->getPath().mPath.size();
+
+ mCenter->clear();
+
+ S32 offset = 0;
+ if (mTypeMask & TOP_MASK)
+ {
+ offset = (max_t-1) * max_s;
+ }
+ else
+ {
+ offset = mBeginS;
+ }
+
+ // Figure out the normal, assume all caps are flat faces.
+ // Cross product to get normals.
+
+ LLVector2 cuv;
+ LLVector2 min_uv, max_uv;
+ // VFExtents change
+ LLVector4a& min = mExtents[0];
+ LLVector4a& max = mExtents[1];
+
+ LLVector2* tc = (LLVector2*) mTexCoords;
+ LLVector4a* pos = (LLVector4a*) mPositions;
+ LLVector4a* norm = (LLVector4a*) mNormals;
+
+ // Copy the vertices into the array
+
+ const LLVector4a* src = mesh.mArray+offset;
+ const LLVector4a* end = src+num_vertices;
+
+ min = *src;
+ max = min;
+
+
+ const LLVector4a* p = profile.mArray;
+
+ if (mTypeMask & TOP_MASK)
+ {
+ min_uv.set((*p)[0]+0.5f,
+ (*p)[1]+0.5f);
+
+ max_uv = min_uv;
+
+ while(src < end)
+ {
+ tc->mV[0] = (*p)[0]+0.5f;
+ tc->mV[1] = (*p)[1]+0.5f;
+
+ llassert(src->isFinite3()); // MAINT-5660; don't know why this happens, does not affect Release builds
+ update_min_max(min,max,*src);
+ update_min_max(min_uv, max_uv, *tc);
+
+ *pos = *src;
+
+ llassert(pos->isFinite3());
+
+ ++p;
+ ++tc;
+ ++src;
+ ++pos;
+ }
+ }
+ else
+ {
+
+ min_uv.set((*p)[0]+0.5f,
+ 0.5f - (*p)[1]);
+ max_uv = min_uv;
+
+ while(src < end)
+ {
+ // Mirror for underside.
+ tc->mV[0] = (*p)[0]+0.5f;
+ tc->mV[1] = 0.5f - (*p)[1];
+
+ llassert(src->isFinite3());
+ update_min_max(min,max,*src);
+ update_min_max(min_uv, max_uv, *tc);
+
+ *pos = *src;
+
+ llassert(pos->isFinite3());
+
+ ++p;
+ ++tc;
+ ++src;
+ ++pos;
+ }
+ }
+
+ LL_CHECK_MEMORY
+
+ mCenter->setAdd(min, max);
+ mCenter->mul(0.5f);
+
+ cuv = (min_uv + max_uv)*0.5f;
+
+
+ VertexData vd;
+ vd.setPosition(*mCenter);
+ vd.mTexCoord = cuv;
+
+ if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
+ {
+ *pos++ = *mCenter;
+ *tc++ = cuv;
+ num_vertices++;
+ }
+
+ LL_CHECK_MEMORY
+
+ //if (partial_build)
+ //{
+ // return true;
+ //}
+
+ if (mTypeMask & HOLLOW_MASK)
+ {
+ if (mTypeMask & TOP_MASK)
+ {
+ // HOLLOW TOP
+ // Does it matter if it's open or closed? - djs
+
+ S32 pt1 = 0, pt2 = num_vertices - 1;
+ S32 i = 0;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ const LLVector4a& p1 = profile[pt1];
+ const LLVector4a& p2 = profile[pt2];
+ const LLVector4a& pa = profile[pt1+1];
+ const LLVector4a& pb = profile[pt2-1];
+
+ const F32* p1V = p1.getF32ptr();
+ const F32* p2V = p2.getF32ptr();
+ const F32* paV = pa.getF32ptr();
+ const F32* pbV = pb.getF32ptr();
+
+ //p1.mV[VZ] = 0.f;
+ //p2.mV[VZ] = 0.f;
+ //pa.mV[VZ] = 0.f;
+ //pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1V[0]*paV[1] - paV[0]*p1V[1]) +
+ (paV[0]*p2V[1] - p2V[0]*paV[1]) +
+ (p2V[0]*p1V[1] - p1V[0]*p2V[1]);
+
+ area_1ba = (p1V[0]*pbV[1] - pbV[0]*p1V[1]) +
+ (pbV[0]*paV[1] - paV[0]*pbV[1]) +
+ (paV[0]*p1V[1] - p1V[0]*paV[1]);
+
+ area_21b = (p2V[0]*p1V[1] - p1V[0]*p2V[1]) +
+ (p1V[0]*pbV[1] - pbV[0]*p1V[1]) +
+ (pbV[0]*p2V[1] - p2V[0]*pbV[1]);
+
+ area_2ab = (p2V[0]*paV[1] - paV[0]*p2V[1]) +
+ (paV[0]*pbV[1] - pbV[0]*paV[1]) +
+ (pbV[0]*p2V[1] - p2V[0]*pbV[1]);
+
+ bool use_tri1a2 = true;
+ bool tri_1a2 = true;
+ bool tri_21b = true;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = false;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = false;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = false;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = false;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = false;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = true;
+ }
+ else
+ {
+ LLVector4a d1;
+ d1.setSub(p1, pa);
+
+ LLVector4a d2;
+ d2.setSub(p2, pb);
+
+ if (d1.dot3(d1) < d2.dot3(d2))
+ {
+ use_tri1a2 = true;
+ }
+ else
+ {
+ use_tri1a2 = false;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt1 + 1;
+ mIndices[i++] = pt2;
+ pt1++;
+ }
+ else
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt2 - 1;
+ mIndices[i++] = pt2;
+ pt2--;
+ }
+ }
+ }
+ else
+ {
+ // HOLLOW BOTTOM
+ // Does it matter if it's open or closed? - djs
+
+ llassert(mTypeMask & BOTTOM_MASK);
+ S32 pt1 = 0, pt2 = num_vertices - 1;
+
+ S32 i = 0;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ const LLVector4a& p1 = profile[pt1];
+ const LLVector4a& p2 = profile[pt2];
+ const LLVector4a& pa = profile[pt1+1];
+ const LLVector4a& pb = profile[pt2-1];
+
+ const F32* p1V = p1.getF32ptr();
+ const F32* p2V = p2.getF32ptr();
+ const F32* paV = pa.getF32ptr();
+ const F32* pbV = pb.getF32ptr();
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1V[0]*paV[1] - paV[0]*p1V[1]) +
+ (paV[0]*p2V[1] - p2V[0]*paV[1]) +
+ (p2V[0]*p1V[1] - p1V[0]*p2V[1]);
+
+ area_1ba = (p1V[0]*pbV[1] - pbV[0]*p1V[1]) +
+ (pbV[0]*paV[1] - paV[0]*pbV[1]) +
+ (paV[0]*p1V[1] - p1V[0]*paV[1]);
+
+ area_21b = (p2V[0]*p1V[1] - p1V[0]*p2V[1]) +
+ (p1V[0]*pbV[1] - pbV[0]*p1V[1]) +
+ (pbV[0]*p2V[1] - p2V[0]*pbV[1]);
+
+ area_2ab = (p2V[0]*paV[1] - paV[0]*p2V[1]) +
+ (paV[0]*pbV[1] - pbV[0]*paV[1]) +
+ (pbV[0]*p2V[1] - p2V[0]*pbV[1]);
+
+ bool use_tri1a2 = true;
+ bool tri_1a2 = true;
+ bool tri_21b = true;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = false;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = false;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = false;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = false;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = false;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = true;
+ }
+ else
+ {
+ LLVector4a d1;
+ d1.setSub(p1,pa);
+ LLVector4a d2;
+ d2.setSub(p2,pb);
+
+ if (d1.dot3(d1) < d2.dot3(d2))
+ {
+ use_tri1a2 = true;
+ }
+ else
+ {
+ use_tri1a2 = false;
+ }
+ }
+
+ // Flipped backfacing from top
+ if (use_tri1a2)
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt2;
+ mIndices[i++] = pt1 + 1;
+ pt1++;
+ }
+ else
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt2;
+ mIndices[i++] = pt2 - 1;
+ pt2--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Not hollow, generate the triangle fan.
+ U16 v1 = 2;
+ U16 v2 = 1;
+
+ if (mTypeMask & TOP_MASK)
+ {
+ v1 = 1;
+ v2 = 2;
+ }
+
+ for (S32 i = 0; i < (num_vertices - 2); i++)
+ {
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+v1] = i;
+ mIndices[3*i+v2] = i + 1;
+ }
+
+
+ }
+
+ LLVector4a d0,d1;
+ LL_CHECK_MEMORY
+
+
+ d0.setSub(mPositions[mIndices[1]], mPositions[mIndices[0]]);
+ d1.setSub(mPositions[mIndices[2]], mPositions[mIndices[0]]);
+
+ LLVector4a normal;
+ normal.setCross3(d0,d1);
+
+ if (normal.dot3(normal).getF32() > F_APPROXIMATELY_ZERO)
+ {
+ normal.normalize3fast();
+ }
+ else
+ { //degenerate, make up a value
+ if(normal.getF32ptr()[2] >= 0)
+ normal.set(0.f,0.f,1.f);
+ else
+ normal.set(0.f,0.f,-1.f);
+ }
+
+ llassert(llfinite(normal.getF32ptr()[0]));
+ llassert(llfinite(normal.getF32ptr()[1]));
+ llassert(llfinite(normal.getF32ptr()[2]));
+
+ llassert(!llisnan(normal.getF32ptr()[0]));
+ llassert(!llisnan(normal.getF32ptr()[1]));
+ llassert(!llisnan(normal.getF32ptr()[2]));
+
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ norm[i].load4a(normal.getF32ptr());
+ }
+
+ return true;
+}
+
+void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
+ const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent);
+
+void LLVolumeFace::createTangents()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
+
+ if (!mTangents)
+ {
+ allocateTangents(mNumVertices);
+
+ //generate tangents
+ LLVector4a* ptr = (LLVector4a*)mTangents;
+
+ LLVector4a* end = mTangents + mNumVertices;
+ while (ptr < end)
+ {
+ (*ptr++).clear();
+ }
+
+ CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents);
+
+ //normalize normals
+ for (U32 i = 0; i < mNumVertices; i++)
+ {
+ //bump map/planar projection code requires normals to be normalized
+ mNormals[i].normalize3fast();
+ }
+ }
+
+}
+
+void LLVolumeFace::resizeVertices(S32 num_verts)
+{
+ ll_aligned_free<64>(mPositions);
+ //DO NOT free mNormals and mTexCoords as they are part of mPositions buffer
+ ll_aligned_free_16(mTangents);
+
+ mTangents = NULL;
+
+ if (num_verts)
+ {
+ //pad texture coordinate block end to allow for QWORD reads
+ S32 tc_size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
+
+ mPositions = (LLVector4a*) ll_aligned_malloc<64>(sizeof(LLVector4a)*2*num_verts+tc_size);
+ mNormals = mPositions+num_verts;
+ mTexCoords = (LLVector2*) (mNormals+num_verts);
+
+ ll_assert_aligned(mPositions, 64);
+ }
+ else
+ {
+ mPositions = NULL;
+ mNormals = NULL;
+ mTexCoords = NULL;
+ }
+
+
+ if (mPositions)
+ {
+ mNumVertices = num_verts;
+ mNumAllocatedVertices = num_verts;
+ }
+ else
+ {
+ // Either num_verts is zero or allocation failure
+ mNumVertices = 0;
+ mNumAllocatedVertices = 0;
+ }
+
+ // Force update
+ mJointRiggingInfoTab.clear();
+}
+
+void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv)
+{
+ pushVertex(cv.getPosition(), cv.getNormal(), cv.mTexCoord);
+}
+
+void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc)
+{
+ S32 new_verts = mNumVertices+1;
+
+ if (new_verts > mNumAllocatedVertices)
+ {
+ // double buffer size on expansion
+ new_verts *= 2;
+
+ S32 new_tc_size = ((new_verts*8)+0xF) & ~0xF;
+ S32 old_tc_size = ((mNumVertices*8)+0xF) & ~0xF;
+
+ S32 old_vsize = mNumVertices*16;
+
+ S32 new_size = new_verts*16*2+new_tc_size;
+
+ LLVector4a* old_buf = mPositions;
+
+ mPositions = (LLVector4a*) ll_aligned_malloc<64>(new_size);
+ mNormals = mPositions+new_verts;
+ mTexCoords = (LLVector2*) (mNormals+new_verts);
+
+ if (old_buf != NULL)
+ {
+ // copy old positions into new buffer
+ LLVector4a::memcpyNonAliased16((F32*)mPositions, (F32*)old_buf, old_vsize);
+
+ // normals
+ LLVector4a::memcpyNonAliased16((F32*)mNormals, (F32*)(old_buf + mNumVertices), old_vsize);
+
+ // tex coords
+ LLVector4a::memcpyNonAliased16((F32*)mTexCoords, (F32*)(old_buf + mNumVertices * 2), old_tc_size);
+ }
+
+ // just clear tangents
+ ll_aligned_free_16(mTangents);
+ mTangents = NULL;
+ ll_aligned_free<64>(old_buf);
+
+ mNumAllocatedVertices = new_verts;
+
+ }
+
+ mPositions[mNumVertices] = pos;
+ mNormals[mNumVertices] = norm;
+ mTexCoords[mNumVertices] = tc;
+
+ mNumVertices++;
+}
+
+void LLVolumeFace::allocateTangents(S32 num_verts)
+{
+ ll_aligned_free_16(mTangents);
+ mTangents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
+}
+
+void LLVolumeFace::allocateWeights(S32 num_verts)
+{
+ ll_aligned_free_16(mWeights);
+ mWeights = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
+
+}
+
+void LLVolumeFace::allocateJointIndices(S32 num_verts)
+{
+#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
+ ll_aligned_free_16(mJointIndices);
+ ll_aligned_free_16(mJustWeights);
+
+ mJointIndices = (U8*)ll_aligned_malloc_16(sizeof(U8) * 4 * num_verts);
+ mJustWeights = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a) * num_verts);
+#endif
+}
+
+void LLVolumeFace::resizeIndices(S32 num_indices)
+{
+ ll_aligned_free_16(mIndices);
+ llassert(num_indices % 3 == 0);
+
+ if (num_indices)
+ {
+ //pad index block end to allow for QWORD reads
+ S32 size = ((num_indices*sizeof(U16)) + 0xF) & ~0xF;
+
+ mIndices = (U16*) ll_aligned_malloc_16(size);
+ }
+ else
+ {
+ mIndices = NULL;
+ }
+
+ if (mIndices)
+ {
+ mNumIndices = num_indices;
+ }
+ else
+ {
+ // Either num_indices is zero or allocation failure
+ mNumIndices = 0;
+ }
+}
+
+void LLVolumeFace::pushIndex(const U16& idx)
+{
+ S32 new_count = mNumIndices + 1;
+ S32 new_size = ((new_count*2)+0xF) & ~0xF;
+
+ S32 old_size = ((mNumIndices*2)+0xF) & ~0xF;
+ if (new_size != old_size)
+ {
+ mIndices = (U16*) ll_aligned_realloc_16(mIndices, new_size, old_size);
+ ll_assert_aligned(mIndices,16);
+ }
+
+ mIndices[mNumIndices++] = idx;
+}
+
+void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx)
+{
+ resizeVertices(v.size());
+ resizeIndices(idx.size());
+
+ for (U32 i = 0; i < v.size(); ++i)
+ {
+ mPositions[i] = v[i].getPosition();
+ mNormals[i] = v[i].getNormal();
+ mTexCoords[i] = v[i].mTexCoord;
+ }
+
+ for (U32 i = 0; i < idx.size(); ++i)
+ {
+ mIndices[i] = idx[i];
+ }
+}
+
+bool LLVolumeFace::createSide(LLVolume* volume, bool partial_build)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ LL_CHECK_MEMORY
+ bool flat = mTypeMask & FLAT_MASK;
+
+ U8 sculpt_type = volume->getParams().getSculptType();
+ U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
+ bool sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
+ bool sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
+ bool sculpt_reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR
+
+ S32 num_vertices, num_indices;
+
+ const LLAlignedArray<LLVector4a,64>& mesh = volume->getMesh();
+ const LLAlignedArray<LLVector4a,64>& profile = volume->getProfile().mProfile;
+ const LLAlignedArray<LLPath::PathPt,64>& path_data = volume->getPath().mPath;
+
+ S32 max_s = volume->getProfile().getTotal();
+
+ S32 s, t, i;
+ F32 ss, tt;
+
+ num_vertices = mNumS*mNumT;
+ num_indices = (mNumS-1)*(mNumT-1)*6;
+
+ partial_build = (num_vertices > mNumVertices || num_indices > mNumIndices) ? false : partial_build;
+
+ if (!partial_build)
+ {
+ resizeVertices(num_vertices);
+ resizeIndices(num_indices);
+
+ if (!volume->isMeshAssetLoaded())
+ {
+ try
+ {
+ mEdge.resize(num_indices);
+ }
+ catch (std::bad_alloc&)
+ {
+ LL_WARNS("LLVOLUME") << "Resize of mEdge to " << num_indices << " failed" << LL_ENDL;
+ return false;
+ }
+ }
+ }
+
+ LL_CHECK_MEMORY
+
+ LLVector4a* pos = (LLVector4a*) mPositions;
+ LLVector2* tc = (LLVector2*) mTexCoords;
+ F32 begin_stex = floorf(profile[mBeginS][2]);
+ S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS;
+
+ S32 cur_vertex = 0;
+ S32 end_t = mBeginT+mNumT;
+ bool test = (mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2;
+
+ // Copy the vertices into the array
+ for (t = mBeginT; t < end_t; t++)
+ {
+ tt = path_data[t].mTexT;
+ for (s = 0; s < num_s; s++)
+ {
+ if (mTypeMask & END_MASK)
+ {
+ if (s)
+ {
+ ss = 1.f;
+ }
+ else
+ {
+ ss = 0.f;
+ }
+ }
+ else
+ {
+ // Get s value for tex-coord.
+ S32 index = mBeginS + s;
+ if (index >= profile.size())
+ {
+ // edge?
+ ss = flat ? 1.f - begin_stex : 1.f;
+ }
+ else if (!flat)
+ {
+ ss = profile[index][2];
+ }
+ else
+ {
+ ss = profile[index][2] - begin_stex;
+ }
+ }
+
+ if (sculpt_reverse_horizontal)
+ {
+ ss = 1.f - ss;
+ }
+
+ // Check to see if this triangle wraps around the array.
+ if (mBeginS + s >= max_s)
+ {
+ // We're wrapping
+ i = mBeginS + s + max_s*(t-1);
+ }
+ else
+ {
+ i = mBeginS + s + max_s*t;
+ }
+
+ mesh[i].store4a((F32*)(pos+cur_vertex));
+ tc[cur_vertex].set(ss,tt);
+
+ cur_vertex++;
+
+ if (test && s > 0)
+ {
+ mesh[i].store4a((F32*)(pos+cur_vertex));
+ tc[cur_vertex].set(ss,tt);
+ cur_vertex++;
+ }
+ }
+
+ if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2)
+ {
+ if (mTypeMask & OPEN_MASK)
+ {
+ s = num_s-1;
+ }
+ else
+ {
+ s = 0;
+ }
+
+ i = mBeginS + s + max_s*t;
+ ss = profile[mBeginS + s][2] - begin_stex;
+
+ mesh[i].store4a((F32*)(pos+cur_vertex));
+ tc[cur_vertex].set(ss,tt);
+
+ cur_vertex++;
+ }
+ }
+ LL_CHECK_MEMORY
+
+ mCenter->clear();
+
+ LLVector4a* cur_pos = pos;
+ LLVector4a* end_pos = pos + mNumVertices;
+
+ //get bounding box for this side
+ LLVector4a face_min;
+ LLVector4a face_max;
+
+ face_min = face_max = *cur_pos++;
+
+ while (cur_pos < end_pos)
+ {
+ update_min_max(face_min, face_max, *cur_pos++);
+ }
+ // VFExtents change
+ mExtents[0] = face_min;
+ mExtents[1] = face_max;
+
+ U32 tc_count = mNumVertices;
+ if (tc_count%2 == 1)
+ { //odd number of texture coordinates, duplicate last entry to padded end of array
+ tc_count++;
+ mTexCoords[mNumVertices] = mTexCoords[mNumVertices-1];
+ }
+
+ LLVector4a* cur_tc = (LLVector4a*) mTexCoords;
+ LLVector4a* end_tc = (LLVector4a*) (mTexCoords+tc_count);
+
+ LLVector4a tc_min;
+ LLVector4a tc_max;
+
+ tc_min = tc_max = *cur_tc++;
+
+ while (cur_tc < end_tc)
+ {
+ update_min_max(tc_min, tc_max, *cur_tc++);
+ }
+
+ F32* minp = tc_min.getF32ptr();
+ F32* maxp = tc_max.getF32ptr();
+
+ mTexCoordExtents[0].mV[0] = llmin(minp[0], minp[2]);
+ mTexCoordExtents[0].mV[1] = llmin(minp[1], minp[3]);
+ mTexCoordExtents[1].mV[0] = llmax(maxp[0], maxp[2]);
+ mTexCoordExtents[1].mV[1] = llmax(maxp[1], maxp[3]);
+
+ mCenter->setAdd(face_min, face_max);
+ mCenter->mul(0.5f);
+
+ S32 cur_index = 0;
+ S32 cur_edge = 0;
+ bool flat_face = mTypeMask & FLAT_MASK;
+
+ if (!partial_build)
+ {
+ // Now we generate the indices.
+ for (t = 0; t < (mNumT-1); t++)
+ {
+ for (s = 0; s < (mNumS-1); s++)
+ {
+ mIndices[cur_index++] = s + mNumS*t; //bottom left
+ mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
+ mIndices[cur_index++] = s + mNumS*(t+1); //top left
+ mIndices[cur_index++] = s + mNumS*t; //bottom left
+ mIndices[cur_index++] = s+1 + mNumS*t; //bottom right
+ mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
+
+ // bottom left/top right neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1;
+
+ if (t < mNumT-2)
+ { // top right/top left neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*(t+1)+s*2+1;
+ }
+ else if (mNumT <= 3 || volume->getPath().isOpen())
+ { // no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else
+ { // wrap on T
+ mEdge[cur_edge++] = s*2+1;
+ }
+
+ if (s > 0)
+ { // top left/bottom left neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2-1;
+ }
+ else if (flat_face || volume->getProfile().isOpen())
+ { // no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else
+ { // wrap on S
+ mEdge[cur_edge++] = (mNumS-1)*2*t+(mNumS-2)*2+1;
+ }
+
+ if (t > 0)
+ { // bottom left/bottom right neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*(t-1)+s*2;
+ }
+ else if (mNumT <= 3 || volume->getPath().isOpen())
+ { // no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else
+ { // wrap on T
+ mEdge[cur_edge++] = (mNumS-1)*2*(mNumT-2)+s*2;
+ }
+
+ if (s < mNumS-2)
+ { // bottom right/top right neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*t+(s+1)*2;
+ }
+ else if (flat_face || volume->getProfile().isOpen())
+ { // no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else
+ { // wrap on S
+ mEdge[cur_edge++] = (mNumS-1)*2*t;
+ }
+
+ // top right/bottom left neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2;
+ }
+ }
+ }
+
+ LL_CHECK_MEMORY
+
+ //clear normals
+ F32* dst = (F32*) mNormals;
+ F32* end = (F32*) (mNormals+mNumVertices);
+ LLVector4a zero = LLVector4a::getZero();
+
+ while (dst < end)
+ {
+ zero.store4a(dst);
+ dst += 4;
+ }
+
+ LL_CHECK_MEMORY
+
+ //generate normals
+ U32 count = mNumIndices/3;
+
+ LLVector4a* norm = mNormals;
+
+ static thread_local LLAlignedArray<LLVector4a, 64> triangle_normals;
+ try
+ {
+ triangle_normals.resize(count);
+ }
+ catch (std::bad_alloc&)
+ {
+ LL_WARNS("LLVOLUME") << "Resize of triangle_normals to " << count << " failed" << LL_ENDL;
+ return false;
+ }
+ LLVector4a* output = triangle_normals.mArray;
+ LLVector4a* end_output = output+count;
+
+ U16* idx = mIndices;
+
+ while (output < end_output)
+ {
+ LLVector4a b,v1,v2;
+ b.load4a((F32*) (pos+idx[0]));
+ v1.load4a((F32*) (pos+idx[1]));
+ v2.load4a((F32*) (pos+idx[2]));
+
+ //calculate triangle normal
+ LLVector4a a;
+
+ a.setSub(b, v1);
+ b.sub(v2);
+
+
+ LLQuad& vector1 = *((LLQuad*) &v1);
+ LLQuad& vector2 = *((LLQuad*) &v2);
+
+ LLQuad& amQ = *((LLQuad*) &a);
+ LLQuad& bmQ = *((LLQuad*) &b);
+
+ //v1.setCross3(t,v0);
+ //setCross3(const LLVector4a& a, const LLVector4a& b)
+ // Vectors are stored in memory in w, z, y, x order from high to low
+ // Set vector1 = { a[W], a[X], a[Z], a[Y] }
+ vector1 = _mm_shuffle_ps( amQ, amQ, _MM_SHUFFLE( 3, 0, 2, 1 ));
+ // Set vector2 = { b[W], b[Y], b[X], b[Z] }
+ vector2 = _mm_shuffle_ps( bmQ, bmQ, _MM_SHUFFLE( 3, 1, 0, 2 ));
+ // mQ = { a[W]*b[W], a[X]*b[Y], a[Z]*b[X], a[Y]*b[Z] }
+ vector2 = _mm_mul_ps( vector1, vector2 );
+ // vector3 = { a[W], a[Y], a[X], a[Z] }
+ amQ = _mm_shuffle_ps( amQ, amQ, _MM_SHUFFLE( 3, 1, 0, 2 ));
+ // vector4 = { b[W], b[X], b[Z], b[Y] }
+ bmQ = _mm_shuffle_ps( bmQ, bmQ, _MM_SHUFFLE( 3, 0, 2, 1 ));
+ // mQ = { 0, a[X]*b[Y] - a[Y]*b[X], a[Z]*b[X] - a[X]*b[Z], a[Y]*b[Z] - a[Z]*b[Y] }
+ vector1 = _mm_sub_ps( vector2, _mm_mul_ps( amQ, bmQ ));
+
+ llassert(v1.isFinite3());
+
+ v1.store4a((F32*) output);
+
+
+ output++;
+ idx += 3;
+ }
+
+ idx = mIndices;
+
+ LLVector4a* src = triangle_normals.mArray;
+
+ for (U32 i = 0; i < count; i++) //for each triangle
+ {
+ LLVector4a c;
+ c.load4a((F32*) (src++));
+
+ LLVector4a* n0p = norm+idx[0];
+ LLVector4a* n1p = norm+idx[1];
+ LLVector4a* n2p = norm+idx[2];
+
+ idx += 3;
+
+ LLVector4a n0,n1,n2;
+ n0.load4a((F32*) n0p);
+ n1.load4a((F32*) n1p);
+ n2.load4a((F32*) n2p);
+
+ n0.add(c);
+ n1.add(c);
+ n2.add(c);
+
+ llassert(c.isFinite3());
+
+ //even out quad contributions
+ switch (i%2+1)
+ {
+ case 0: n0.add(c); break;
+ case 1: n1.add(c); break;
+ case 2: n2.add(c); break;
+ };
+
+ n0.store4a((F32*) n0p);
+ n1.store4a((F32*) n1p);
+ n2.store4a((F32*) n2p);
+ }
+
+ LL_CHECK_MEMORY
+
+ // adjust normals based on wrapping and stitching
+
+ LLVector4a top;
+ top.setSub(pos[0], pos[mNumS*(mNumT-2)]);
+ bool s_bottom_converges = (top.dot3(top) < 0.000001f);
+
+ top.setSub(pos[mNumS-1], pos[mNumS*(mNumT-2)+mNumS-1]);
+ bool s_top_converges = (top.dot3(top) < 0.000001f);
+
+ if (sculpt_stitching == LL_SCULPT_TYPE_NONE) // logic for non-sculpt volumes
+ {
+ if (!volume->getPath().isOpen())
+ { //wrap normals on T
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ LLVector4a n;
+ n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
+ norm[i] = n;
+ norm[mNumS*(mNumT-1)+i] = n;
+ }
+ }
+
+ if (!volume->getProfile().isOpen() && !s_bottom_converges)
+ { //wrap normals on S
+ for (S32 i = 0; i < mNumT; i++)
+ {
+ LLVector4a n;
+ n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
+ norm[mNumS * i] = n;
+ norm[mNumS * i+mNumS-1] = n;
+ }
+ }
+
+ if (volume->getPathType() == LL_PCODE_PATH_CIRCLE &&
+ ((volume->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF))
+ {
+ if (s_bottom_converges)
+ { //all lower S have same normal
+ for (S32 i = 0; i < mNumT; i++)
+ {
+ norm[mNumS*i].set(1,0,0);
+ }
+ }
+
+ if (s_top_converges)
+ { //all upper S have same normal
+ for (S32 i = 0; i < mNumT; i++)
+ {
+ norm[mNumS*i+mNumS-1].set(-1,0,0);
+ }
+ }
+ }
+ }
+ else // logic for sculpt volumes
+ {
+ bool average_poles = false;
+ bool wrap_s = false;
+ bool wrap_t = false;
+
+ if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+ average_poles = true;
+
+ if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
+ (sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
+ (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
+ wrap_s = true;
+
+ if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
+ wrap_t = true;
+
+
+ if (average_poles)
+ {
+ // average normals for north pole
+
+ LLVector4a average;
+ average.clear();
+
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ average.add(norm[i]);
+ }
+
+ // set average
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ norm[i] = average;
+ }
+
+ // average normals for south pole
+
+ average.clear();
+
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ average.add(norm[i + mNumS * (mNumT - 1)]);
+ }
+
+ // set average
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ norm[i + mNumS * (mNumT - 1)] = average;
+ }
+
+ }
+
+
+ if (wrap_s)
+ {
+ for (S32 i = 0; i < mNumT; i++)
+ {
+ LLVector4a n;
+ n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
+ norm[mNumS * i] = n;
+ norm[mNumS * i+mNumS-1] = n;
+ }
+ }
+
+ if (wrap_t)
+ {
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ LLVector4a n;
+ n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
+ norm[i] = n;
+ norm[mNumS*(mNumT-1)+i] = n;
+ }
+ }
+
+ }
+
+ LL_CHECK_MEMORY
+
+ return true;
+}
+
+//adapted from Lengyel, Eric. "Computing Tangent Space Basis Vectors for an Arbitrary Mesh". Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html
+void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
+ const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ //LLVector4a *tan1 = new LLVector4a[vertexCount * 2];
+ LLVector4a* tan1 = (LLVector4a*) ll_aligned_malloc_16(vertexCount*2*sizeof(LLVector4a));
+ // new(tan1) LLVector4a;
+
+ LLVector4a* tan2 = tan1 + vertexCount;
+
+ U32 count = vertexCount * 2;
+ for (U32 i = 0; i < count; i++)
+ {
+ tan1[i].clear();
+ }
+
+ for (U32 a = 0; a < triangleCount; a++)
+ {
+ U32 i1 = *index_array++;
+ U32 i2 = *index_array++;
+ U32 i3 = *index_array++;
+
+ const LLVector4a& v1 = vertex[i1];
+ const LLVector4a& v2 = vertex[i2];
+ const LLVector4a& v3 = vertex[i3];
+
+ const LLVector2& w1 = texcoord[i1];
+ const LLVector2& w2 = texcoord[i2];
+ const LLVector2& w3 = texcoord[i3];
+
+ const F32* v1ptr = v1.getF32ptr();
+ const F32* v2ptr = v2.getF32ptr();
+ const F32* v3ptr = v3.getF32ptr();
+
+ float x1 = v2ptr[0] - v1ptr[0];
+ float x2 = v3ptr[0] - v1ptr[0];
+ float y1 = v2ptr[1] - v1ptr[1];
+ float y2 = v3ptr[1] - v1ptr[1];
+ float z1 = v2ptr[2] - v1ptr[2];
+ float z2 = v3ptr[2] - v1ptr[2];
+
+ float s1 = w2.mV[0] - w1.mV[0];
+ float s2 = w3.mV[0] - w1.mV[0];
+ float t1 = w2.mV[1] - w1.mV[1];
+ float t2 = w3.mV[1] - w1.mV[1];
+
+ F32 rd = s1*t2-s2*t1;
+
+ float r = ((rd*rd) > FLT_EPSILON) ? (1.0f / rd)
+ : ((rd > 0.0f) ? 1024.f : -1024.f); //some made up large ratio for division by zero
+
+ llassert(llfinite(r));
+ llassert(!llisnan(r));
+
+ LLVector4a sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
+ (t2 * z1 - t1 * z2) * r);
+ LLVector4a tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
+ (s1 * z2 - s2 * z1) * r);
+
+ tan1[i1].add(sdir);
+ tan1[i2].add(sdir);
+ tan1[i3].add(sdir);
+
+ tan2[i1].add(tdir);
+ tan2[i2].add(tdir);
+ tan2[i3].add(tdir);
+ }
+
+ for (U32 a = 0; a < vertexCount; a++)
+ {
+ LLVector4a n = normal[a];
+
+ const LLVector4a& t = tan1[a];
+
+ LLVector4a ncrosst;
+ ncrosst.setCross3(n,t);
+
+ // Gram-Schmidt orthogonalize
+ n.mul(n.dot3(t).getF32());
+
+ LLVector4a tsubn;
+ tsubn.setSub(t,n);
+
+ if (tsubn.dot3(tsubn).getF32() > F_APPROXIMATELY_ZERO)
+ {
+ tsubn.normalize3fast();
+
+ // Calculate handedness
+ F32 handedness = ncrosst.dot3(tan2[a]).getF32() < 0.f ? -1.f : 1.f;
+
+ tsubn.getF32ptr()[3] = handedness;
+
+ tangent[a] = tsubn;
+ }
+ else
+ { //degenerate, make up a value
+ tangent[a].set(0,0,1,1);
+ }
+ }
+
+ ll_aligned_free_16(tan1);
+}
+
+
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index d4099b6366..4f8d9bef84 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -1,1152 +1,1152 @@ -/** - * @file llvolume.h - * @brief LLVolume base class. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLVOLUME_H -#define LL_LLVOLUME_H - -#include <iostream> - -class LLProfileParams; -class LLPathParams; -class LLVolumeParams; -class LLProfile; -class LLPath; - -template<class T> class LLPointer; -template <class T, typename T_PTR> class LLOctreeNode; - -class LLVolumeFace; -class LLVolume; -class LLVolumeTriangle; - -#include "lluuid.h" -#include "v4color.h" -//#include "vmath.h" -#include "v2math.h" -#include "v3math.h" -#include "v3dmath.h" -#include "v4math.h" -#include "llvector4a.h" -#include "llmatrix4a.h" -#include "llquaternion.h" -#include "llstrider.h" -#include "v4coloru.h" -#include "llrefcount.h" -#include "llpointer.h" -#include "llfile.h" -#include "llalignedarray.h" -#include "llrigginginfo.h" - -//============================================================================ - -constexpr S32 MIN_DETAIL_FACES = 6; -constexpr S32 MIN_LOD = 0; -constexpr S32 MAX_LOD = 3; - -// These are defined here but are not enforced at this level, -// rather they are here for the convenience of code that uses -// the LLVolume class. -constexpr F32 MIN_VOLUME_PROFILE_WIDTH = 0.05f; -constexpr F32 MIN_VOLUME_PATH_WIDTH = 0.05f; - -constexpr F32 CUT_QUANTA = 0.00002f; -constexpr F32 SCALE_QUANTA = 0.01f; -constexpr F32 SHEAR_QUANTA = 0.01f; -constexpr F32 TAPER_QUANTA = 0.01f; -constexpr F32 REV_QUANTA = 0.015f; -constexpr F32 HOLLOW_QUANTA = 0.00002f; - -constexpr S32 MAX_VOLUME_TRIANGLE_INDICES = 10000; - -//============================================================================ - -// useful masks -constexpr LLPCode LL_PCODE_HOLLOW_MASK = 0x80; // has a thickness -constexpr LLPCode LL_PCODE_SEGMENT_MASK = 0x40; // segments (1 angle) -constexpr LLPCode LL_PCODE_PATCH_MASK = 0x20; // segmented segments (2 angles) -constexpr LLPCode LL_PCODE_HEMI_MASK = 0x10; // half-primitives get their own type per PR's dictum -constexpr LLPCode LL_PCODE_BASE_MASK = 0x0F; - - // primitive shapes -constexpr LLPCode LL_PCODE_CUBE = 1; -constexpr LLPCode LL_PCODE_PRISM = 2; -constexpr LLPCode LL_PCODE_TETRAHEDRON = 3; -constexpr LLPCode LL_PCODE_PYRAMID = 4; -constexpr LLPCode LL_PCODE_CYLINDER = 5; -constexpr LLPCode LL_PCODE_CONE = 6; -constexpr LLPCode LL_PCODE_SPHERE = 7; -constexpr LLPCode LL_PCODE_TORUS = 8; -constexpr LLPCode LL_PCODE_VOLUME = 9; - - // surfaces -//constexpr LLPCode LL_PCODE_SURFACE_TRIANGLE = 10; -//constexpr LLPCode LL_PCODE_SURFACE_SQUARE = 11; -//constexpr LLPCode LL_PCODE_SURFACE_DISC = 12; - -constexpr LLPCode LL_PCODE_APP = 14; // App specific pcode (for viewer/sim side only objects) -constexpr LLPCode LL_PCODE_LEGACY = 15; - -// Pcodes for legacy objects -//constexpr LLPCode LL_PCODE_LEGACY_ATOR = 0x10 | LL_PCODE_LEGACY; // ATOR -constexpr LLPCode LL_PCODE_LEGACY_AVATAR = 0x20 | LL_PCODE_LEGACY; // PLAYER -//constexpr LLPCode LL_PCODE_LEGACY_BIRD = 0x30 | LL_PCODE_LEGACY; // BIRD -//constexpr LLPCode LL_PCODE_LEGACY_DEMON = 0x40 | LL_PCODE_LEGACY; // DEMON -constexpr LLPCode LL_PCODE_LEGACY_GRASS = 0x50 | LL_PCODE_LEGACY; // GRASS -constexpr LLPCode LL_PCODE_TREE_NEW = 0x60 | LL_PCODE_LEGACY; // new trees -//constexpr LLPCode LL_PCODE_LEGACY_ORACLE = 0x70 | LL_PCODE_LEGACY; // ORACLE -constexpr LLPCode LL_PCODE_LEGACY_PART_SYS = 0x80 | LL_PCODE_LEGACY; // PART_SYS -constexpr LLPCode LL_PCODE_LEGACY_ROCK = 0x90 | LL_PCODE_LEGACY; // ROCK -//constexpr LLPCode LL_PCODE_LEGACY_SHOT = 0xA0 | LL_PCODE_LEGACY; // BASIC_SHOT -//constexpr LLPCode LL_PCODE_LEGACY_SHOT_BIG = 0xB0 | LL_PCODE_LEGACY; -//constexpr LLPCode LL_PCODE_LEGACY_SMOKE = 0xC0 | LL_PCODE_LEGACY; // SMOKE -//constexpr LLPCode LL_PCODE_LEGACY_SPARK = 0xD0 | LL_PCODE_LEGACY;// SPARK -constexpr LLPCode LL_PCODE_LEGACY_TEXT_BUBBLE = 0xE0 | LL_PCODE_LEGACY; // TEXTBUBBLE -constexpr LLPCode LL_PCODE_LEGACY_TREE = 0xF0 | LL_PCODE_LEGACY; // TREE - - // hemis -constexpr LLPCode LL_PCODE_CYLINDER_HEMI = LL_PCODE_CYLINDER | LL_PCODE_HEMI_MASK; -constexpr LLPCode LL_PCODE_CONE_HEMI = LL_PCODE_CONE | LL_PCODE_HEMI_MASK; -constexpr LLPCode LL_PCODE_SPHERE_HEMI = LL_PCODE_SPHERE | LL_PCODE_HEMI_MASK; -constexpr LLPCode LL_PCODE_TORUS_HEMI = LL_PCODE_TORUS | LL_PCODE_HEMI_MASK; - - -// Volumes consist of a profile at the base that is swept around -// a path to make a volume. -// The profile code -constexpr U8 LL_PCODE_PROFILE_MASK = 0x0f; -constexpr U8 LL_PCODE_PROFILE_MIN = 0x00; -constexpr U8 LL_PCODE_PROFILE_CIRCLE = 0x00; -constexpr U8 LL_PCODE_PROFILE_SQUARE = 0x01; -constexpr U8 LL_PCODE_PROFILE_ISOTRI = 0x02; -constexpr U8 LL_PCODE_PROFILE_EQUALTRI = 0x03; -constexpr U8 LL_PCODE_PROFILE_RIGHTTRI = 0x04; -constexpr U8 LL_PCODE_PROFILE_CIRCLE_HALF = 0x05; -constexpr U8 LL_PCODE_PROFILE_MAX = 0x05; - -// Stored in the profile byte -constexpr U8 LL_PCODE_HOLE_MASK = 0xf0; -constexpr U8 LL_PCODE_HOLE_MIN = 0x00; -constexpr U8 LL_PCODE_HOLE_SAME = 0x00; // same as outside profile -constexpr U8 LL_PCODE_HOLE_CIRCLE = 0x10; -constexpr U8 LL_PCODE_HOLE_SQUARE = 0x20; -constexpr U8 LL_PCODE_HOLE_TRIANGLE = 0x30; -constexpr U8 LL_PCODE_HOLE_MAX = 0x03; // min/max needs to be >> 4 of real min/max - -constexpr U8 LL_PCODE_PATH_IGNORE = 0x00; -constexpr U8 LL_PCODE_PATH_MIN = 0x01; // min/max needs to be >> 4 of real min/max -constexpr U8 LL_PCODE_PATH_LINE = 0x10; -constexpr U8 LL_PCODE_PATH_CIRCLE = 0x20; -constexpr U8 LL_PCODE_PATH_CIRCLE2 = 0x30; -constexpr U8 LL_PCODE_PATH_TEST = 0x40; -constexpr U8 LL_PCODE_PATH_FLEXIBLE = 0x80; -constexpr U8 LL_PCODE_PATH_MAX = 0x08; - -//============================================================================ - -// face identifiers -typedef U16 LLFaceID; - -constexpr LLFaceID LL_FACE_PATH_BEGIN = 0x1 << 0; -constexpr LLFaceID LL_FACE_PATH_END = 0x1 << 1; -constexpr LLFaceID LL_FACE_INNER_SIDE = 0x1 << 2; -constexpr LLFaceID LL_FACE_PROFILE_BEGIN = 0x1 << 3; -constexpr LLFaceID LL_FACE_PROFILE_END = 0x1 << 4; -constexpr LLFaceID LL_FACE_OUTER_SIDE_0 = 0x1 << 5; -constexpr LLFaceID LL_FACE_OUTER_SIDE_1 = 0x1 << 6; -constexpr LLFaceID LL_FACE_OUTER_SIDE_2 = 0x1 << 7; -constexpr LLFaceID LL_FACE_OUTER_SIDE_3 = 0x1 << 8; - -//============================================================================ - -// sculpt types + flags - -constexpr U8 LL_SCULPT_TYPE_NONE = 0; -constexpr U8 LL_SCULPT_TYPE_SPHERE = 1; -constexpr U8 LL_SCULPT_TYPE_TORUS = 2; -constexpr U8 LL_SCULPT_TYPE_PLANE = 3; -constexpr U8 LL_SCULPT_TYPE_CYLINDER = 4; -constexpr U8 LL_SCULPT_TYPE_MESH = 5; -constexpr U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE | - LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH; - -// for value checks, assign new value after adding new types -constexpr U8 LL_SCULPT_TYPE_MAX = LL_SCULPT_TYPE_MESH; - -constexpr U8 LL_SCULPT_FLAG_INVERT = 64; -constexpr U8 LL_SCULPT_FLAG_MIRROR = 128; -constexpr U8 LL_SCULPT_FLAG_MASK = LL_SCULPT_FLAG_INVERT | LL_SCULPT_FLAG_MIRROR; - -constexpr S32 LL_SCULPT_MESH_MAX_FACES = 8; - -extern bool gDebugGL; - -class LLProfileParams -{ -public: - LLProfileParams() - : mCurveType(LL_PCODE_PROFILE_SQUARE), - mBegin(0.f), - mEnd(1.f), - mHollow(0.f), - mCRC(0) - { - } - - LLProfileParams(U8 curve, F32 begin, F32 end, F32 hollow) - : mCurveType(curve), - mBegin(begin), - mEnd(end), - mHollow(hollow), - mCRC(0) - { - } - - LLProfileParams(U8 curve, U16 begin, U16 end, U16 hollow) - { - mCurveType = curve; - F32 temp_f32 = begin * CUT_QUANTA; - if (temp_f32 > 1.f) - { - temp_f32 = 1.f; - } - mBegin = temp_f32; - temp_f32 = end * CUT_QUANTA; - if (temp_f32 > 1.f) - { - temp_f32 = 1.f; - } - mEnd = 1.f - temp_f32; - temp_f32 = hollow * HOLLOW_QUANTA; - if (temp_f32 > 1.f) - { - temp_f32 = 1.f; - } - mHollow = temp_f32; - mCRC = 0; - } - - bool operator==(const LLProfileParams ¶ms) const; - bool operator!=(const LLProfileParams ¶ms) const; - bool operator<(const LLProfileParams ¶ms) const; - - void copyParams(const LLProfileParams ¶ms); - - bool importFile(LLFILE *fp); - bool exportFile(LLFILE *fp) const; - - bool importLegacyStream(std::istream& input_stream); - bool exportLegacyStream(std::ostream& output_stream) const; - - LLSD asLLSD() const; - operator LLSD() const { return asLLSD(); } - bool fromLLSD(LLSD& sd); - - const F32& getBegin () const { return mBegin; } - const F32& getEnd () const { return mEnd; } - const F32& getHollow() const { return mHollow; } - const U8& getCurveType () const { return mCurveType; } - - void setCurveType(const U32 type) { mCurveType = type;} - void setBegin(const F32 begin) { mBegin = (begin >= 1.0f) ? 0.0f : ((int) (begin * 100000))/100000.0f;} - void setEnd(const F32 end) { mEnd = (end <= 0.0f) ? 1.0f : ((int) (end * 100000))/100000.0f;} - void setHollow(const F32 hollow) { mHollow = ((int) (hollow * 100000))/100000.0f;} - - friend std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params); - -protected: - // Profile params - U8 mCurveType; - F32 mBegin; - F32 mEnd; - F32 mHollow; - - U32 mCRC; -}; - -inline bool LLProfileParams::operator==(const LLProfileParams ¶ms) const -{ - return - (getCurveType() == params.getCurveType()) && - (getBegin() == params.getBegin()) && - (getEnd() == params.getEnd()) && - (getHollow() == params.getHollow()); -} - -inline bool LLProfileParams::operator!=(const LLProfileParams ¶ms) const -{ - return - (getCurveType() != params.getCurveType()) || - (getBegin() != params.getBegin()) || - (getEnd() != params.getEnd()) || - (getHollow() != params.getHollow()); -} - - -inline bool LLProfileParams::operator<(const LLProfileParams ¶ms) const -{ - if (getCurveType() != params.getCurveType()) - { - return getCurveType() < params.getCurveType(); - } - else - if (getBegin() != params.getBegin()) - { - return getBegin() < params.getBegin(); - } - else - if (getEnd() != params.getEnd()) - { - return getEnd() < params.getEnd(); - } - else - { - return getHollow() < params.getHollow(); - } -} - -#define U8_TO_F32(x) (F32)(*((S8 *)&x)) - -class LLPathParams -{ -public: - LLPathParams() - : - mCurveType(LL_PCODE_PATH_LINE), - mBegin(0.f), - mEnd(1.f), - mScale(1.f,1.f), - mShear(0.f,0.f), - mTwistBegin(0.f), - mTwistEnd(0.f), - mRadiusOffset(0.f), - mTaper(0.f,0.f), - mRevolutions(1.f), - mSkew(0.f), - mCRC(0) - { - } - - LLPathParams(U8 curve, F32 begin, F32 end, F32 scx, F32 scy, F32 shx, F32 shy, F32 twistend, F32 twistbegin, F32 radiusoffset, F32 tx, F32 ty, F32 revolutions, F32 skew) - : mCurveType(curve), - mBegin(begin), - mEnd(end), - mScale(scx,scy), - mShear(shx,shy), - mTwistBegin(twistbegin), - mTwistEnd(twistend), - mRadiusOffset(radiusoffset), - mTaper(tx,ty), - mRevolutions(revolutions), - mSkew(skew), - mCRC(0) - { - } - - LLPathParams(U8 curve, U16 begin, U16 end, U8 scx, U8 scy, U8 shx, U8 shy, U8 twistend, U8 twistbegin, U8 radiusoffset, U8 tx, U8 ty, U8 revolutions, U8 skew) - { - mCurveType = curve; - mBegin = (F32)(begin * CUT_QUANTA); - mEnd = (F32)(100.f - end) * CUT_QUANTA; - if (mEnd > 1.f) - mEnd = 1.f; - mScale.setVec((F32) (200 - scx) * SCALE_QUANTA,(F32) (200 - scy) * SCALE_QUANTA); - mShear.setVec(U8_TO_F32(shx) * SHEAR_QUANTA,U8_TO_F32(shy) * SHEAR_QUANTA); - mTwistBegin = U8_TO_F32(twistbegin) * SCALE_QUANTA; - mTwistEnd = U8_TO_F32(twistend) * SCALE_QUANTA; - mRadiusOffset = U8_TO_F32(radiusoffset) * SCALE_QUANTA; - mTaper.setVec(U8_TO_F32(tx) * TAPER_QUANTA,U8_TO_F32(ty) * TAPER_QUANTA); - mRevolutions = ((F32)revolutions) * REV_QUANTA + 1.0f; - mSkew = U8_TO_F32(skew) * SCALE_QUANTA; - - mCRC = 0; - } - - bool operator==(const LLPathParams ¶ms) const; - bool operator!=(const LLPathParams ¶ms) const; - bool operator<(const LLPathParams ¶ms) const; - - void copyParams(const LLPathParams ¶ms); - - bool importFile(LLFILE *fp); - bool exportFile(LLFILE *fp) const; - - bool importLegacyStream(std::istream& input_stream); - bool exportLegacyStream(std::ostream& output_stream) const; - - LLSD asLLSD() const; - operator LLSD() const { return asLLSD(); } - bool fromLLSD(LLSD& sd); - - const F32& getBegin() const { return mBegin; } - const F32& getEnd() const { return mEnd; } - const LLVector2 &getScale() const { return mScale; } - const F32& getScaleX() const { return mScale.mV[0]; } - const F32& getScaleY() const { return mScale.mV[1]; } - const LLVector2 getBeginScale() const; - const LLVector2 getEndScale() const; - const LLVector2 &getShear() const { return mShear; } - const F32& getShearX() const { return mShear.mV[0]; } - const F32& getShearY() const { return mShear.mV[1]; } - const U8& getCurveType () const { return mCurveType; } - - const F32& getTwistBegin() const { return mTwistBegin; } - const F32& getTwistEnd() const { return mTwistEnd; } - const F32& getTwist() const { return mTwistEnd; } // deprecated - const F32& getRadiusOffset() const { return mRadiusOffset; } - const LLVector2 &getTaper() const { return mTaper; } - const F32& getTaperX() const { return mTaper.mV[0]; } - const F32& getTaperY() const { return mTaper.mV[1]; } - const F32& getRevolutions() const { return mRevolutions; } - const F32& getSkew() const { return mSkew; } - - void setCurveType(const U8 type) { mCurveType = type; } - void setBegin(const F32 begin) { mBegin = begin; } - void setEnd(const F32 end) { mEnd = end; } - - void setScale(const F32 x, const F32 y) { mScale.setVec(x,y); } - void setScaleX(const F32 v) { mScale.mV[VX] = v; } - void setScaleY(const F32 v) { mScale.mV[VY] = v; } - void setShear(const F32 x, const F32 y) { mShear.setVec(x,y); } - void setShearX(const F32 v) { mShear.mV[VX] = v; } - void setShearY(const F32 v) { mShear.mV[VY] = v; } - - void setTwistBegin(const F32 twist_begin) { mTwistBegin = twist_begin; } - void setTwistEnd(const F32 twist_end) { mTwistEnd = twist_end; } - void setTwist(const F32 twist) { setTwistEnd(twist); } // deprecated - void setRadiusOffset(const F32 radius_offset){ mRadiusOffset = radius_offset; } - void setTaper(const F32 x, const F32 y) { mTaper.setVec(x,y); } - void setTaperX(const F32 v) { mTaper.mV[VX] = v; } - void setTaperY(const F32 v) { mTaper.mV[VY] = v; } - void setRevolutions(const F32 revolutions) { mRevolutions = revolutions; } - void setSkew(const F32 skew) { mSkew = skew; } - - friend std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params); - -protected: - // Path params - U8 mCurveType; - F32 mBegin; - F32 mEnd; - LLVector2 mScale; - LLVector2 mShear; - - F32 mTwistBegin; - F32 mTwistEnd; - F32 mRadiusOffset; - LLVector2 mTaper; - F32 mRevolutions; - F32 mSkew; - - U32 mCRC; -}; - -inline bool LLPathParams::operator==(const LLPathParams ¶ms) const -{ - return - (getCurveType() == params.getCurveType()) && - (getScale() == params.getScale()) && - (getBegin() == params.getBegin()) && - (getEnd() == params.getEnd()) && - (getShear() == params.getShear()) && - (getTwist() == params.getTwist()) && - (getTwistBegin() == params.getTwistBegin()) && - (getRadiusOffset() == params.getRadiusOffset()) && - (getTaper() == params.getTaper()) && - (getRevolutions() == params.getRevolutions()) && - (getSkew() == params.getSkew()); -} - -inline bool LLPathParams::operator!=(const LLPathParams ¶ms) const -{ - return - (getCurveType() != params.getCurveType()) || - (getScale() != params.getScale()) || - (getBegin() != params.getBegin()) || - (getEnd() != params.getEnd()) || - (getShear() != params.getShear()) || - (getTwist() != params.getTwist()) || - (getTwistBegin() !=params.getTwistBegin()) || - (getRadiusOffset() != params.getRadiusOffset()) || - (getTaper() != params.getTaper()) || - (getRevolutions() != params.getRevolutions()) || - (getSkew() != params.getSkew()); -} - - -inline bool LLPathParams::operator<(const LLPathParams ¶ms) const -{ - if( getCurveType() != params.getCurveType()) - { - return getCurveType() < params.getCurveType(); - } - else - if( getScale() != params.getScale()) - { - return getScale() < params.getScale(); - } - else - if( getBegin() != params.getBegin()) - { - return getBegin() < params.getBegin(); - } - else - if( getEnd() != params.getEnd()) - { - return getEnd() < params.getEnd(); - } - else - if( getShear() != params.getShear()) - { - return getShear() < params.getShear(); - } - else - if( getTwist() != params.getTwist()) - { - return getTwist() < params.getTwist(); - } - else - if( getTwistBegin() != params.getTwistBegin()) - { - return getTwistBegin() < params.getTwistBegin(); - } - else - if( getRadiusOffset() != params.getRadiusOffset()) - { - return getRadiusOffset() < params.getRadiusOffset(); - } - else - if( getTaper() != params.getTaper()) - { - return getTaper() < params.getTaper(); - } - else - if( getRevolutions() != params.getRevolutions()) - { - return getRevolutions() < params.getRevolutions(); - } - else - { - return getSkew() < params.getSkew(); - } -} - -typedef LLVolumeParams* LLVolumeParamsPtr; -typedef const LLVolumeParams* const_LLVolumeParamsPtr; - -class LLVolumeParams -{ -public: - LLVolumeParams() - : mSculptType(LL_SCULPT_TYPE_NONE) - { - } - - LLVolumeParams(LLProfileParams &profile, LLPathParams &path, - LLUUID sculpt_id = LLUUID::null, U8 sculpt_type = LL_SCULPT_TYPE_NONE) - : mProfileParams(profile), mPathParams(path), mSculptID(sculpt_id), mSculptType(sculpt_type) - { - } - - bool operator==(const LLVolumeParams ¶ms) const; - bool operator!=(const LLVolumeParams ¶ms) const; - bool operator<(const LLVolumeParams ¶ms) const; - - - void copyParams(const LLVolumeParams ¶ms); - - const LLProfileParams &getProfileParams() const {return mProfileParams;} - LLProfileParams &getProfileParams() {return mProfileParams;} - const LLPathParams &getPathParams() const {return mPathParams;} - LLPathParams &getPathParams() {return mPathParams;} - - bool importFile(LLFILE *fp); - bool exportFile(LLFILE *fp) const; - - bool importLegacyStream(std::istream& input_stream); - bool exportLegacyStream(std::ostream& output_stream) const; - - LLSD sculptAsLLSD() const; - bool sculptFromLLSD(LLSD& sd); - - LLSD asLLSD() const; - operator LLSD() const { return asLLSD(); } - bool fromLLSD(LLSD& sd); - - bool setType(U8 profile, U8 path); - - //void setBeginS(const F32 beginS) { mProfileParams.setBegin(beginS); } // range 0 to 1 - //void setBeginT(const F32 beginT) { mPathParams.setBegin(beginT); } // range 0 to 1 - //void setEndS(const F32 endS) { mProfileParams.setEnd(endS); } // range 0 to 1, must be greater than begin - //void setEndT(const F32 endT) { mPathParams.setEnd(endT); } // range 0 to 1, must be greater than begin - - bool setBeginAndEndS(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end - bool setBeginAndEndT(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end - - bool setHollow(const F32 hollow); // range 0 to 1 - bool setRatio(const F32 x) { return setRatio(x,x); } // 0 = point, 1 = same as base - bool setShear(const F32 x) { return setShear(x,x); } // 0 = no movement, - bool setRatio(const F32 x, const F32 y); // 0 = point, 1 = same as base - bool setShear(const F32 x, const F32 y); // 0 = no movement - - bool setTwistBegin(const F32 twist_begin); // range -1 to 1 - bool setTwistEnd(const F32 twist_end); // range -1 to 1 - bool setTwist(const F32 twist) { return setTwistEnd(twist); } // deprecated - bool setTaper(const F32 x, const F32 y) { bool pass_x = setTaperX(x); bool pass_y = setTaperY(y); return pass_x && pass_y; } - bool setTaperX(const F32 v); // -1 to 1 - bool setTaperY(const F32 v); // -1 to 1 - bool setRevolutions(const F32 revolutions); // 1 to 4 - bool setRadiusOffset(const F32 radius_offset); - bool setSkew(const F32 skew); - bool setSculptID(const LLUUID& sculpt_id, U8 sculpt_type); - - static bool validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow, - U8 path_curve, F32 path_begin, F32 path_end, - F32 scx, F32 scy, F32 shx, F32 shy, - F32 twistend, F32 twistbegin, F32 radiusoffset, - F32 tx, F32 ty, F32 revolutions, F32 skew); - - const F32& getBeginS() const { return mProfileParams.getBegin(); } - const F32& getBeginT() const { return mPathParams.getBegin(); } - const F32& getEndS() const { return mProfileParams.getEnd(); } - const F32& getEndT() const { return mPathParams.getEnd(); } - - const F32& getHollow() const { return mProfileParams.getHollow(); } - const F32& getTwist() const { return mPathParams.getTwist(); } - const F32& getRatio() const { return mPathParams.getScaleX(); } - const F32& getRatioX() const { return mPathParams.getScaleX(); } - const F32& getRatioY() const { return mPathParams.getScaleY(); } - const F32& getShearX() const { return mPathParams.getShearX(); } - const F32& getShearY() const { return mPathParams.getShearY(); } - - const F32& getTwistBegin()const { return mPathParams.getTwistBegin(); } - const F32& getRadiusOffset() const { return mPathParams.getRadiusOffset(); } - const F32& getTaper() const { return mPathParams.getTaperX(); } - const F32& getTaperX() const { return mPathParams.getTaperX(); } - const F32& getTaperY() const { return mPathParams.getTaperY(); } - const F32& getRevolutions() const { return mPathParams.getRevolutions(); } - const F32& getSkew() const { return mPathParams.getSkew(); } - const LLUUID& getSculptID() const { return mSculptID; } - const U8& getSculptType() const { return mSculptType; } - bool isSculpt() const; - bool isMeshSculpt() const; - bool isConvex() const; - - // 'begin' and 'end' should be in range [0, 1] (they will be clamped) - // (begin, end) = (0, 1) will not change the volume - // (begin, end) = (0, 0.5) will reduce the volume to the first half of its profile/path (S/T) - void reduceS(F32 begin, F32 end); - void reduceT(F32 begin, F32 end); - - struct compare - { - bool operator()( const const_LLVolumeParamsPtr& first, const const_LLVolumeParamsPtr& second) const - { - return (*first < *second); - } - }; - - friend std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params); - - // debug helper functions - void setCube(); - -protected: - LLProfileParams mProfileParams; - LLPathParams mPathParams; - LLUUID mSculptID; - U8 mSculptType; -}; - - -class LLProfile -{ - friend class LLVolume; - -public: - LLProfile() - : mOpen(false), - mConcave(false), - mDirty(true), - mTotalOut(0), - mTotal(2) - { - } - - S32 getTotal() const { return mTotal; } - S32 getTotalOut() const { return mTotalOut; } // Total number of outside points - bool isFlat(S32 face) const { return (mFaces[face].mCount == 2); } - bool isOpen() const { return mOpen; } - void setDirty() { mDirty = true; } - - static S32 getNumPoints(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0, - bool is_sculpted = false, S32 sculpt_size = 0); - bool generate(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0, - bool is_sculpted = false, S32 sculpt_size = 0); - bool isConcave() const { return mConcave; } -public: - struct Face - { - S32 mIndex; - S32 mCount; - F32 mScaleU; - bool mCap; - bool mFlat; - LLFaceID mFaceID; - }; - - LLAlignedArray<LLVector4a, 64> mProfile; - //LLAlignedArray<LLVector4a, 64> mNormals; - std::vector<Face> mFaces; - - //LLAlignedArray<LLVector4a, 64> mEdgeNormals; - //LLAlignedArray<LLVector4a, 64> mEdgeCenters; - - friend std::ostream& operator<<(std::ostream &s, const LLProfile &profile); - -protected: - ~LLProfile(); - - static S32 getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0); - void genNGon(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0); - - Face* addHole(const LLProfileParams& params, bool flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split = 0); - Face* addCap (S16 faceID); - Face* addFace(S32 index, S32 count, F32 scaleU, S16 faceID, bool flat); - -protected: - bool mOpen; - bool mConcave; - bool mDirty; - - S32 mTotalOut; - S32 mTotal; -}; - -//------------------------------------------------------------------- -// SWEEP/EXTRUDE PATHS -//------------------------------------------------------------------- - -class LLPath -{ -public: - class PathPt - { - public: - LLMatrix4a mRot; - LLVector4a mPos; - - LLVector4a mScale; - F32 mTexT; - F32 pad[3]; //for alignment - PathPt() - { - mPos.clear(); - mTexT = 0; - mScale.clear(); - mRot.setRows(LLVector4a(1,0,0,0), - LLVector4a(0,1,0,0), - LLVector4a(0,0,1,0)); - - //distinguished data in the pad for debugging - pad[0] = 3.14159f; - pad[1] = -3.14159f; - pad[2] = 0.585f; - } - }; - -public: - LLPath() - : mOpen(false), - mTotal(0), - mDirty(true), - mStep(1) - { - } - - virtual ~LLPath(); - - static S32 getNumPoints(const LLPathParams& params, F32 detail); - static S32 getNumNGonPoints(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f); - - void genNGon(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f); - virtual bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0, - bool is_sculpted = false, S32 sculpt_size = 0); - - bool isOpen() const { return mOpen; } - F32 getStep() const { return mStep; } - void setDirty() { mDirty = true; } - - S32 getPathLength() const { return (S32)mPath.size(); } - - void resizePath(S32 length) { mPath.resize(length); } - - friend std::ostream& operator<<(std::ostream &s, const LLPath &path); - -public: - LLAlignedArray<PathPt, 64> mPath; - -protected: - bool mOpen; - S32 mTotal; - bool mDirty; - F32 mStep; -}; - -class LLDynamicPath : public LLPath -{ -public: - LLDynamicPath() : LLPath() { } - /*virtual*/ bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0, - bool is_sculpted = false, S32 sculpt_size = 0); -}; - -// Yet another "face" class - caches volume-specific, but not instance-specific data for faces) -class LLVolumeFace -{ -public: - class VertexData - { - enum - { - POSITION = 0, - NORMAL = 1 - }; - - private: - void init(); - public: - VertexData(); - VertexData(const VertexData& rhs); - const VertexData& operator=(const VertexData& rhs); - - ~VertexData(); - LLVector4a& getPosition(); - LLVector4a& getNormal(); - const LLVector4a& getPosition() const; - const LLVector4a& getNormal() const; - void setPosition(const LLVector4a& pos); - void setNormal(const LLVector4a& norm); - - - LLVector2 mTexCoord; - - bool operator<(const VertexData& rhs) const; - bool operator==(const VertexData& rhs) const; - bool compareNormal(const VertexData& rhs, F32 angle_cutoff) const; - - private: - LLVector4a* mData; - }; - - LLVolumeFace(); - LLVolumeFace(const LLVolumeFace& src); - LLVolumeFace& operator=(const LLVolumeFace& rhs); - - ~LLVolumeFace(); -private: - void freeData(); -public: - - bool create(LLVolume* volume, bool partial_build = false); - void createTangents(); - - void resizeVertices(S32 num_verts); - void allocateTangents(S32 num_verts); - void allocateWeights(S32 num_verts); - void allocateJointIndices(S32 num_verts); - void resizeIndices(S32 num_indices); - void fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx); - - void pushVertex(const VertexData& cv); - void pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc); - void pushIndex(const U16& idx); - - void swapData(LLVolumeFace& rhs); - - void getVertexData(U16 indx, LLVolumeFace::VertexData& cv); - - class VertexMapData : public LLVolumeFace::VertexData - { - public: - U16 mIndex; - - bool operator==(const LLVolumeFace::VertexData& rhs) const; - - struct ComparePosition - { - bool operator()(const LLVector3& a, const LLVector3& b) const; - }; - - typedef std::map<LLVector3, std::vector<VertexMapData>, VertexMapData::ComparePosition > PointMap; - }; - - // Eliminates non unique triangles, takes positions, - // normals and texture coordinates into account. - void remap(); - - void optimize(F32 angle_cutoff = 2.f); - bool cacheOptimize(bool gen_tangents = false); - - void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f)); - void destroyOctree(); - // Get a reference to the octree, which may be null - const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const; - - enum - { - SINGLE_MASK = 0x0001, - CAP_MASK = 0x0002, - END_MASK = 0x0004, - SIDE_MASK = 0x0008, - INNER_MASK = 0x0010, - OUTER_MASK = 0x0020, - HOLLOW_MASK = 0x0040, - OPEN_MASK = 0x0080, - FLAT_MASK = 0x0100, - TOP_MASK = 0x0200, - BOTTOM_MASK = 0x0400 - }; - -public: - S32 mID; - U32 mTypeMask; - - // Only used for INNER/OUTER faces - S32 mBeginS; - S32 mBeginT; - S32 mNumS; - S32 mNumT; - - LLVector4a* mExtents; //minimum and maximum point of face - LLVector4a* mCenter; - LLVector2 mTexCoordExtents[2]; //minimum and maximum of texture coordinates of the face. - - S32 mNumVertices; // num vertices == num normals == num texcoords - S32 mNumAllocatedVertices; - S32 mNumIndices; - - LLVector4a* mPositions; // Contains vertices, nortmals and texcoords - LLVector4a* mNormals; // pointer into mPositions - LLVector4a* mTangents; - LLVector2* mTexCoords; // pointer into mPositions - - // mIndices contains mNumIndices amount of elements. - // It contains triangles, each 3 indices describe one triangle. - // If mIndices contains {0, 2, 3, 1, 2, 4}, it means there - // are two triangles {0, 2, 3} and {1, 2, 4} with values being - // indexes for mPositions/mNormals/mTexCoords - U16* mIndices; - - std::vector<S32> mEdge; - - //list of skin weights for rigged volumes - // format is mWeights[vertex_index].mV[influence] = <joint_index>.<weight> - // mWeights.size() should be empty or match mVertices.size() - LLVector4a* mWeights; - -#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS - LLVector4a* mJustWeights; - U8* mJointIndices; -#endif - - mutable bool mWeightsScrubbed; - - // Which joints are rigged to, and the bounding box of any rigged - // vertices per joint. - LLJointRiggingInfoTab mJointRiggingInfoTab; - - //whether or not face has been cache optimized - bool mOptimized; - - // if this is a mesh asset, scale and translation that were applied - // when encoding the source mesh into a unit cube - // used for regenerating tangents - LLVector3 mNormalizedScale = LLVector3(1,1,1); - -private: - LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree; - LLVolumeTriangle* mOctreeTriangles; - - bool createUnCutCubeCap(LLVolume* volume, bool partial_build = false); - bool createCap(LLVolume* volume, bool partial_build = false); - bool createSide(LLVolume* volume, bool partial_build = false); -}; - -class LLVolume : public LLRefCount -{ - friend class LLVolumeLODGroup; - -protected: - virtual ~LLVolume(); // use unref - -public: - typedef std::vector<LLVolumeFace> face_list_t; - - struct FaceParams - { - LLFaceID mFaceID; - S32 mBeginS; - S32 mCountS; - S32 mBeginT; - S32 mCountT; - }; - - LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool generate_single_face = false, const bool is_unique = false); - - U8 getProfileType() const { return mParams.getProfileParams().getCurveType(); } - U8 getPathType() const { return mParams.getPathParams().getCurveType(); } - S32 getNumFaces() const; - S32 getNumVolumeFaces() const { return mVolumeFaces.size(); } - F32 getDetail() const { return mDetail; } - F32 getSurfaceArea() const { return mSurfaceArea; } - const LLVolumeParams& getParams() const { return mParams; } - LLVolumeParams getCopyOfParams() const { return mParams; } - const LLProfile& getProfile() const { return *mProfilep; } - LLPath& getPath() const { return *mPathp; } - void resizePath(S32 length); - const LLAlignedArray<LLVector4a,64>& getMesh() const { return mMesh; } - const LLVector4a& getMeshPt(const U32 i) const { return mMesh[i]; } - - - void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); } - - void regen(); - void genTangents(S32 face); - - bool isConvex() const; - bool isCap(S32 face); - bool isFlat(S32 face); - bool isUnique() const { return mUnique; } - - S32 getSculptLevel() const { return mSculptLevel; } - void setSculptLevel(S32 level) { mSculptLevel = level; } - - - static void getLoDTriangleCounts(const LLVolumeParams& params, S32* counts); - - S32 getNumTriangles(S32* vcount = nullptr) const; - - void generateSilhouetteVertices(std::vector<LLVector3> &vertices, - std::vector<LLVector3> &normals, - const LLVector3& view_vec, - const LLMatrix4& mat, - const LLMatrix3& norm_mat, - S32 face_index); - - //get the face index of the face that intersects with the given line segment at the point - //closest to start. Moves end to the point of intersection. Returns -1 if no intersection. - //Line segment must be in volume space. - S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, - S32 face = -1, // which face to check, -1 = ALL_SIDES - LLVector4a* intersection = nullptr, // return the intersection point - LLVector2* tex_coord = nullptr, // return the texture coordinates of the intersection point - LLVector4a* normal = nullptr, // return the surface normal at the intersection point - LLVector4a* tangent = nullptr // return the surface tangent at the intersection point - ); - - LLFaceID generateFaceMask(); - - bool isFaceMaskValid(LLFaceID face_mask); - static S32 sNumMeshPoints; - - friend std::ostream& operator<<(std::ostream &s, const LLVolume &volume); - friend std::ostream& operator<<(std::ostream &s, const LLVolume *volumep); // HACK to bypass Windoze confusion over - // conversion if *(LLVolume*) to LLVolume& - const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE - - LLVolumeFace &getVolumeFace(const S32 f) {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE - - face_list_t& getVolumeFaces() { return mVolumeFaces; } - - U32 mFaceMask; // bit array of which faces exist in this volume - LLVector3 mLODScaleBias; // vector for biasing LOD based on scale - - void sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level, bool visible_placeholder); - void copyVolumeFaces(const LLVolume* volume); - void copyFacesTo(std::vector<LLVolumeFace> &faces) const; - void copyFacesFrom(const std::vector<LLVolumeFace> &faces); - - // use meshoptimizer to optimize index buffer for vertex shader cache - // gen_tangents - if true, generate MikkTSpace tangents if needed before optimizing index buffer - bool cacheOptimize(bool gen_tangents = false); - -private: - void sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type); - F32 sculptGetSurfaceArea(); - void sculptGenerateEmptyPlaceholder(); - void sculptGenerateSpherePlaceholder(); - -protected: - bool generate(); - void createVolumeFaces(); -public: - bool unpackVolumeFaces(std::istream& is, S32 size); - bool unpackVolumeFaces(U8* in_data, S32 size); -private: - bool unpackVolumeFacesInternal(const LLSD& mdl); - -public: - virtual void setMeshAssetLoaded(bool loaded); - virtual bool isMeshAssetLoaded(); - virtual void setMeshAssetUnavaliable(bool unavaliable); - virtual bool isMeshAssetUnavaliable(); - - protected: - bool mUnique; - F32 mDetail; - S32 mSculptLevel; - F32 mSurfaceArea; //unscaled surface area - bool mIsMeshAssetLoaded; - bool mIsMeshAssetUnavaliable; - - const LLVolumeParams mParams; - LLPath *mPathp; - LLProfile *mProfilep; - LLAlignedArray<LLVector4a,64> mMesh; - - - bool mGenerateSingleFace; - face_list_t mVolumeFaces; - -public: - LLVector4a* mHullPoints; - U16* mHullIndices; - S32 mNumHullPoints; - S32 mNumHullIndices; -}; - -std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params); - -bool LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size); -bool LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size); -bool LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size); - -bool LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, - F32& intersection_a, F32& intersection_b, F32& intersection_t); -bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, - F32& intersection_a, F32& intersection_b, F32& intersection_t); - -#endif +/**
+ * @file llvolume.h
+ * @brief LLVolume base class.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLVOLUME_H
+#define LL_LLVOLUME_H
+
+#include <iostream>
+
+class LLProfileParams;
+class LLPathParams;
+class LLVolumeParams;
+class LLProfile;
+class LLPath;
+
+template<class T> class LLPointer;
+template <class T, typename T_PTR> class LLOctreeNode;
+
+class LLVolumeFace;
+class LLVolume;
+class LLVolumeTriangle;
+
+#include "lluuid.h"
+#include "v4color.h"
+//#include "vmath.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
+#include "llvector4a.h"
+#include "llmatrix4a.h"
+#include "llquaternion.h"
+#include "llstrider.h"
+#include "v4coloru.h"
+#include "llrefcount.h"
+#include "llpointer.h"
+#include "llfile.h"
+#include "llalignedarray.h"
+#include "llrigginginfo.h"
+
+//============================================================================
+
+constexpr S32 MIN_DETAIL_FACES = 6;
+constexpr S32 MIN_LOD = 0;
+constexpr S32 MAX_LOD = 3;
+
+// These are defined here but are not enforced at this level,
+// rather they are here for the convenience of code that uses
+// the LLVolume class.
+constexpr F32 MIN_VOLUME_PROFILE_WIDTH = 0.05f;
+constexpr F32 MIN_VOLUME_PATH_WIDTH = 0.05f;
+
+constexpr F32 CUT_QUANTA = 0.00002f;
+constexpr F32 SCALE_QUANTA = 0.01f;
+constexpr F32 SHEAR_QUANTA = 0.01f;
+constexpr F32 TAPER_QUANTA = 0.01f;
+constexpr F32 REV_QUANTA = 0.015f;
+constexpr F32 HOLLOW_QUANTA = 0.00002f;
+
+constexpr S32 MAX_VOLUME_TRIANGLE_INDICES = 10000;
+
+//============================================================================
+
+// useful masks
+constexpr LLPCode LL_PCODE_HOLLOW_MASK = 0x80; // has a thickness
+constexpr LLPCode LL_PCODE_SEGMENT_MASK = 0x40; // segments (1 angle)
+constexpr LLPCode LL_PCODE_PATCH_MASK = 0x20; // segmented segments (2 angles)
+constexpr LLPCode LL_PCODE_HEMI_MASK = 0x10; // half-primitives get their own type per PR's dictum
+constexpr LLPCode LL_PCODE_BASE_MASK = 0x0F;
+
+ // primitive shapes
+constexpr LLPCode LL_PCODE_CUBE = 1;
+constexpr LLPCode LL_PCODE_PRISM = 2;
+constexpr LLPCode LL_PCODE_TETRAHEDRON = 3;
+constexpr LLPCode LL_PCODE_PYRAMID = 4;
+constexpr LLPCode LL_PCODE_CYLINDER = 5;
+constexpr LLPCode LL_PCODE_CONE = 6;
+constexpr LLPCode LL_PCODE_SPHERE = 7;
+constexpr LLPCode LL_PCODE_TORUS = 8;
+constexpr LLPCode LL_PCODE_VOLUME = 9;
+
+ // surfaces
+//constexpr LLPCode LL_PCODE_SURFACE_TRIANGLE = 10;
+//constexpr LLPCode LL_PCODE_SURFACE_SQUARE = 11;
+//constexpr LLPCode LL_PCODE_SURFACE_DISC = 12;
+
+constexpr LLPCode LL_PCODE_APP = 14; // App specific pcode (for viewer/sim side only objects)
+constexpr LLPCode LL_PCODE_LEGACY = 15;
+
+// Pcodes for legacy objects
+//constexpr LLPCode LL_PCODE_LEGACY_ATOR = 0x10 | LL_PCODE_LEGACY; // ATOR
+constexpr LLPCode LL_PCODE_LEGACY_AVATAR = 0x20 | LL_PCODE_LEGACY; // PLAYER
+//constexpr LLPCode LL_PCODE_LEGACY_BIRD = 0x30 | LL_PCODE_LEGACY; // BIRD
+//constexpr LLPCode LL_PCODE_LEGACY_DEMON = 0x40 | LL_PCODE_LEGACY; // DEMON
+constexpr LLPCode LL_PCODE_LEGACY_GRASS = 0x50 | LL_PCODE_LEGACY; // GRASS
+constexpr LLPCode LL_PCODE_TREE_NEW = 0x60 | LL_PCODE_LEGACY; // new trees
+//constexpr LLPCode LL_PCODE_LEGACY_ORACLE = 0x70 | LL_PCODE_LEGACY; // ORACLE
+constexpr LLPCode LL_PCODE_LEGACY_PART_SYS = 0x80 | LL_PCODE_LEGACY; // PART_SYS
+constexpr LLPCode LL_PCODE_LEGACY_ROCK = 0x90 | LL_PCODE_LEGACY; // ROCK
+//constexpr LLPCode LL_PCODE_LEGACY_SHOT = 0xA0 | LL_PCODE_LEGACY; // BASIC_SHOT
+//constexpr LLPCode LL_PCODE_LEGACY_SHOT_BIG = 0xB0 | LL_PCODE_LEGACY;
+//constexpr LLPCode LL_PCODE_LEGACY_SMOKE = 0xC0 | LL_PCODE_LEGACY; // SMOKE
+//constexpr LLPCode LL_PCODE_LEGACY_SPARK = 0xD0 | LL_PCODE_LEGACY;// SPARK
+constexpr LLPCode LL_PCODE_LEGACY_TEXT_BUBBLE = 0xE0 | LL_PCODE_LEGACY; // TEXTBUBBLE
+constexpr LLPCode LL_PCODE_LEGACY_TREE = 0xF0 | LL_PCODE_LEGACY; // TREE
+
+ // hemis
+constexpr LLPCode LL_PCODE_CYLINDER_HEMI = LL_PCODE_CYLINDER | LL_PCODE_HEMI_MASK;
+constexpr LLPCode LL_PCODE_CONE_HEMI = LL_PCODE_CONE | LL_PCODE_HEMI_MASK;
+constexpr LLPCode LL_PCODE_SPHERE_HEMI = LL_PCODE_SPHERE | LL_PCODE_HEMI_MASK;
+constexpr LLPCode LL_PCODE_TORUS_HEMI = LL_PCODE_TORUS | LL_PCODE_HEMI_MASK;
+
+
+// Volumes consist of a profile at the base that is swept around
+// a path to make a volume.
+// The profile code
+constexpr U8 LL_PCODE_PROFILE_MASK = 0x0f;
+constexpr U8 LL_PCODE_PROFILE_MIN = 0x00;
+constexpr U8 LL_PCODE_PROFILE_CIRCLE = 0x00;
+constexpr U8 LL_PCODE_PROFILE_SQUARE = 0x01;
+constexpr U8 LL_PCODE_PROFILE_ISOTRI = 0x02;
+constexpr U8 LL_PCODE_PROFILE_EQUALTRI = 0x03;
+constexpr U8 LL_PCODE_PROFILE_RIGHTTRI = 0x04;
+constexpr U8 LL_PCODE_PROFILE_CIRCLE_HALF = 0x05;
+constexpr U8 LL_PCODE_PROFILE_MAX = 0x05;
+
+// Stored in the profile byte
+constexpr U8 LL_PCODE_HOLE_MASK = 0xf0;
+constexpr U8 LL_PCODE_HOLE_MIN = 0x00;
+constexpr U8 LL_PCODE_HOLE_SAME = 0x00; // same as outside profile
+constexpr U8 LL_PCODE_HOLE_CIRCLE = 0x10;
+constexpr U8 LL_PCODE_HOLE_SQUARE = 0x20;
+constexpr U8 LL_PCODE_HOLE_TRIANGLE = 0x30;
+constexpr U8 LL_PCODE_HOLE_MAX = 0x03; // min/max needs to be >> 4 of real min/max
+
+constexpr U8 LL_PCODE_PATH_IGNORE = 0x00;
+constexpr U8 LL_PCODE_PATH_MIN = 0x01; // min/max needs to be >> 4 of real min/max
+constexpr U8 LL_PCODE_PATH_LINE = 0x10;
+constexpr U8 LL_PCODE_PATH_CIRCLE = 0x20;
+constexpr U8 LL_PCODE_PATH_CIRCLE2 = 0x30;
+constexpr U8 LL_PCODE_PATH_TEST = 0x40;
+constexpr U8 LL_PCODE_PATH_FLEXIBLE = 0x80;
+constexpr U8 LL_PCODE_PATH_MAX = 0x08;
+
+//============================================================================
+
+// face identifiers
+typedef U16 LLFaceID;
+
+constexpr LLFaceID LL_FACE_PATH_BEGIN = 0x1 << 0;
+constexpr LLFaceID LL_FACE_PATH_END = 0x1 << 1;
+constexpr LLFaceID LL_FACE_INNER_SIDE = 0x1 << 2;
+constexpr LLFaceID LL_FACE_PROFILE_BEGIN = 0x1 << 3;
+constexpr LLFaceID LL_FACE_PROFILE_END = 0x1 << 4;
+constexpr LLFaceID LL_FACE_OUTER_SIDE_0 = 0x1 << 5;
+constexpr LLFaceID LL_FACE_OUTER_SIDE_1 = 0x1 << 6;
+constexpr LLFaceID LL_FACE_OUTER_SIDE_2 = 0x1 << 7;
+constexpr LLFaceID LL_FACE_OUTER_SIDE_3 = 0x1 << 8;
+
+//============================================================================
+
+// sculpt types + flags
+
+constexpr U8 LL_SCULPT_TYPE_NONE = 0;
+constexpr U8 LL_SCULPT_TYPE_SPHERE = 1;
+constexpr U8 LL_SCULPT_TYPE_TORUS = 2;
+constexpr U8 LL_SCULPT_TYPE_PLANE = 3;
+constexpr U8 LL_SCULPT_TYPE_CYLINDER = 4;
+constexpr U8 LL_SCULPT_TYPE_MESH = 5;
+constexpr U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE |
+ LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH;
+
+// for value checks, assign new value after adding new types
+constexpr U8 LL_SCULPT_TYPE_MAX = LL_SCULPT_TYPE_MESH;
+
+constexpr U8 LL_SCULPT_FLAG_INVERT = 64;
+constexpr U8 LL_SCULPT_FLAG_MIRROR = 128;
+constexpr U8 LL_SCULPT_FLAG_MASK = LL_SCULPT_FLAG_INVERT | LL_SCULPT_FLAG_MIRROR;
+
+constexpr S32 LL_SCULPT_MESH_MAX_FACES = 8;
+
+extern bool gDebugGL;
+
+class LLProfileParams
+{
+public:
+ LLProfileParams()
+ : mCurveType(LL_PCODE_PROFILE_SQUARE),
+ mBegin(0.f),
+ mEnd(1.f),
+ mHollow(0.f),
+ mCRC(0)
+ {
+ }
+
+ LLProfileParams(U8 curve, F32 begin, F32 end, F32 hollow)
+ : mCurveType(curve),
+ mBegin(begin),
+ mEnd(end),
+ mHollow(hollow),
+ mCRC(0)
+ {
+ }
+
+ LLProfileParams(U8 curve, U16 begin, U16 end, U16 hollow)
+ {
+ mCurveType = curve;
+ F32 temp_f32 = begin * CUT_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ temp_f32 = 1.f;
+ }
+ mBegin = temp_f32;
+ temp_f32 = end * CUT_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ temp_f32 = 1.f;
+ }
+ mEnd = 1.f - temp_f32;
+ temp_f32 = hollow * HOLLOW_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ temp_f32 = 1.f;
+ }
+ mHollow = temp_f32;
+ mCRC = 0;
+ }
+
+ bool operator==(const LLProfileParams ¶ms) const;
+ bool operator!=(const LLProfileParams ¶ms) const;
+ bool operator<(const LLProfileParams ¶ms) const;
+
+ void copyParams(const LLProfileParams ¶ms);
+
+ bool importFile(LLFILE *fp);
+ bool exportFile(LLFILE *fp) const;
+
+ bool importLegacyStream(std::istream& input_stream);
+ bool exportLegacyStream(std::ostream& output_stream) const;
+
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ const F32& getBegin () const { return mBegin; }
+ const F32& getEnd () const { return mEnd; }
+ const F32& getHollow() const { return mHollow; }
+ const U8& getCurveType () const { return mCurveType; }
+
+ void setCurveType(const U32 type) { mCurveType = type;}
+ void setBegin(const F32 begin) { mBegin = (begin >= 1.0f) ? 0.0f : ((int) (begin * 100000))/100000.0f;}
+ void setEnd(const F32 end) { mEnd = (end <= 0.0f) ? 1.0f : ((int) (end * 100000))/100000.0f;}
+ void setHollow(const F32 hollow) { mHollow = ((int) (hollow * 100000))/100000.0f;}
+
+ friend std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params);
+
+protected:
+ // Profile params
+ U8 mCurveType;
+ F32 mBegin;
+ F32 mEnd;
+ F32 mHollow;
+
+ U32 mCRC;
+};
+
+inline bool LLProfileParams::operator==(const LLProfileParams ¶ms) const
+{
+ return
+ (getCurveType() == params.getCurveType()) &&
+ (getBegin() == params.getBegin()) &&
+ (getEnd() == params.getEnd()) &&
+ (getHollow() == params.getHollow());
+}
+
+inline bool LLProfileParams::operator!=(const LLProfileParams ¶ms) const
+{
+ return
+ (getCurveType() != params.getCurveType()) ||
+ (getBegin() != params.getBegin()) ||
+ (getEnd() != params.getEnd()) ||
+ (getHollow() != params.getHollow());
+}
+
+
+inline bool LLProfileParams::operator<(const LLProfileParams ¶ms) const
+{
+ if (getCurveType() != params.getCurveType())
+ {
+ return getCurveType() < params.getCurveType();
+ }
+ else
+ if (getBegin() != params.getBegin())
+ {
+ return getBegin() < params.getBegin();
+ }
+ else
+ if (getEnd() != params.getEnd())
+ {
+ return getEnd() < params.getEnd();
+ }
+ else
+ {
+ return getHollow() < params.getHollow();
+ }
+}
+
+#define U8_TO_F32(x) (F32)(*((S8 *)&x))
+
+class LLPathParams
+{
+public:
+ LLPathParams()
+ :
+ mCurveType(LL_PCODE_PATH_LINE),
+ mBegin(0.f),
+ mEnd(1.f),
+ mScale(1.f,1.f),
+ mShear(0.f,0.f),
+ mTwistBegin(0.f),
+ mTwistEnd(0.f),
+ mRadiusOffset(0.f),
+ mTaper(0.f,0.f),
+ mRevolutions(1.f),
+ mSkew(0.f),
+ mCRC(0)
+ {
+ }
+
+ LLPathParams(U8 curve, F32 begin, F32 end, F32 scx, F32 scy, F32 shx, F32 shy, F32 twistend, F32 twistbegin, F32 radiusoffset, F32 tx, F32 ty, F32 revolutions, F32 skew)
+ : mCurveType(curve),
+ mBegin(begin),
+ mEnd(end),
+ mScale(scx,scy),
+ mShear(shx,shy),
+ mTwistBegin(twistbegin),
+ mTwistEnd(twistend),
+ mRadiusOffset(radiusoffset),
+ mTaper(tx,ty),
+ mRevolutions(revolutions),
+ mSkew(skew),
+ mCRC(0)
+ {
+ }
+
+ LLPathParams(U8 curve, U16 begin, U16 end, U8 scx, U8 scy, U8 shx, U8 shy, U8 twistend, U8 twistbegin, U8 radiusoffset, U8 tx, U8 ty, U8 revolutions, U8 skew)
+ {
+ mCurveType = curve;
+ mBegin = (F32)(begin * CUT_QUANTA);
+ mEnd = (F32)(100.f - end) * CUT_QUANTA;
+ if (mEnd > 1.f)
+ mEnd = 1.f;
+ mScale.setVec((F32) (200 - scx) * SCALE_QUANTA,(F32) (200 - scy) * SCALE_QUANTA);
+ mShear.setVec(U8_TO_F32(shx) * SHEAR_QUANTA,U8_TO_F32(shy) * SHEAR_QUANTA);
+ mTwistBegin = U8_TO_F32(twistbegin) * SCALE_QUANTA;
+ mTwistEnd = U8_TO_F32(twistend) * SCALE_QUANTA;
+ mRadiusOffset = U8_TO_F32(radiusoffset) * SCALE_QUANTA;
+ mTaper.setVec(U8_TO_F32(tx) * TAPER_QUANTA,U8_TO_F32(ty) * TAPER_QUANTA);
+ mRevolutions = ((F32)revolutions) * REV_QUANTA + 1.0f;
+ mSkew = U8_TO_F32(skew) * SCALE_QUANTA;
+
+ mCRC = 0;
+ }
+
+ bool operator==(const LLPathParams ¶ms) const;
+ bool operator!=(const LLPathParams ¶ms) const;
+ bool operator<(const LLPathParams ¶ms) const;
+
+ void copyParams(const LLPathParams ¶ms);
+
+ bool importFile(LLFILE *fp);
+ bool exportFile(LLFILE *fp) const;
+
+ bool importLegacyStream(std::istream& input_stream);
+ bool exportLegacyStream(std::ostream& output_stream) const;
+
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ const F32& getBegin() const { return mBegin; }
+ const F32& getEnd() const { return mEnd; }
+ const LLVector2 &getScale() const { return mScale; }
+ const F32& getScaleX() const { return mScale.mV[0]; }
+ const F32& getScaleY() const { return mScale.mV[1]; }
+ const LLVector2 getBeginScale() const;
+ const LLVector2 getEndScale() const;
+ const LLVector2 &getShear() const { return mShear; }
+ const F32& getShearX() const { return mShear.mV[0]; }
+ const F32& getShearY() const { return mShear.mV[1]; }
+ const U8& getCurveType () const { return mCurveType; }
+
+ const F32& getTwistBegin() const { return mTwistBegin; }
+ const F32& getTwistEnd() const { return mTwistEnd; }
+ const F32& getTwist() const { return mTwistEnd; } // deprecated
+ const F32& getRadiusOffset() const { return mRadiusOffset; }
+ const LLVector2 &getTaper() const { return mTaper; }
+ const F32& getTaperX() const { return mTaper.mV[0]; }
+ const F32& getTaperY() const { return mTaper.mV[1]; }
+ const F32& getRevolutions() const { return mRevolutions; }
+ const F32& getSkew() const { return mSkew; }
+
+ void setCurveType(const U8 type) { mCurveType = type; }
+ void setBegin(const F32 begin) { mBegin = begin; }
+ void setEnd(const F32 end) { mEnd = end; }
+
+ void setScale(const F32 x, const F32 y) { mScale.setVec(x,y); }
+ void setScaleX(const F32 v) { mScale.mV[VX] = v; }
+ void setScaleY(const F32 v) { mScale.mV[VY] = v; }
+ void setShear(const F32 x, const F32 y) { mShear.setVec(x,y); }
+ void setShearX(const F32 v) { mShear.mV[VX] = v; }
+ void setShearY(const F32 v) { mShear.mV[VY] = v; }
+
+ void setTwistBegin(const F32 twist_begin) { mTwistBegin = twist_begin; }
+ void setTwistEnd(const F32 twist_end) { mTwistEnd = twist_end; }
+ void setTwist(const F32 twist) { setTwistEnd(twist); } // deprecated
+ void setRadiusOffset(const F32 radius_offset){ mRadiusOffset = radius_offset; }
+ void setTaper(const F32 x, const F32 y) { mTaper.setVec(x,y); }
+ void setTaperX(const F32 v) { mTaper.mV[VX] = v; }
+ void setTaperY(const F32 v) { mTaper.mV[VY] = v; }
+ void setRevolutions(const F32 revolutions) { mRevolutions = revolutions; }
+ void setSkew(const F32 skew) { mSkew = skew; }
+
+ friend std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params);
+
+protected:
+ // Path params
+ U8 mCurveType;
+ F32 mBegin;
+ F32 mEnd;
+ LLVector2 mScale;
+ LLVector2 mShear;
+
+ F32 mTwistBegin;
+ F32 mTwistEnd;
+ F32 mRadiusOffset;
+ LLVector2 mTaper;
+ F32 mRevolutions;
+ F32 mSkew;
+
+ U32 mCRC;
+};
+
+inline bool LLPathParams::operator==(const LLPathParams ¶ms) const
+{
+ return
+ (getCurveType() == params.getCurveType()) &&
+ (getScale() == params.getScale()) &&
+ (getBegin() == params.getBegin()) &&
+ (getEnd() == params.getEnd()) &&
+ (getShear() == params.getShear()) &&
+ (getTwist() == params.getTwist()) &&
+ (getTwistBegin() == params.getTwistBegin()) &&
+ (getRadiusOffset() == params.getRadiusOffset()) &&
+ (getTaper() == params.getTaper()) &&
+ (getRevolutions() == params.getRevolutions()) &&
+ (getSkew() == params.getSkew());
+}
+
+inline bool LLPathParams::operator!=(const LLPathParams ¶ms) const
+{
+ return
+ (getCurveType() != params.getCurveType()) ||
+ (getScale() != params.getScale()) ||
+ (getBegin() != params.getBegin()) ||
+ (getEnd() != params.getEnd()) ||
+ (getShear() != params.getShear()) ||
+ (getTwist() != params.getTwist()) ||
+ (getTwistBegin() !=params.getTwistBegin()) ||
+ (getRadiusOffset() != params.getRadiusOffset()) ||
+ (getTaper() != params.getTaper()) ||
+ (getRevolutions() != params.getRevolutions()) ||
+ (getSkew() != params.getSkew());
+}
+
+
+inline bool LLPathParams::operator<(const LLPathParams ¶ms) const
+{
+ if( getCurveType() != params.getCurveType())
+ {
+ return getCurveType() < params.getCurveType();
+ }
+ else
+ if( getScale() != params.getScale())
+ {
+ return getScale() < params.getScale();
+ }
+ else
+ if( getBegin() != params.getBegin())
+ {
+ return getBegin() < params.getBegin();
+ }
+ else
+ if( getEnd() != params.getEnd())
+ {
+ return getEnd() < params.getEnd();
+ }
+ else
+ if( getShear() != params.getShear())
+ {
+ return getShear() < params.getShear();
+ }
+ else
+ if( getTwist() != params.getTwist())
+ {
+ return getTwist() < params.getTwist();
+ }
+ else
+ if( getTwistBegin() != params.getTwistBegin())
+ {
+ return getTwistBegin() < params.getTwistBegin();
+ }
+ else
+ if( getRadiusOffset() != params.getRadiusOffset())
+ {
+ return getRadiusOffset() < params.getRadiusOffset();
+ }
+ else
+ if( getTaper() != params.getTaper())
+ {
+ return getTaper() < params.getTaper();
+ }
+ else
+ if( getRevolutions() != params.getRevolutions())
+ {
+ return getRevolutions() < params.getRevolutions();
+ }
+ else
+ {
+ return getSkew() < params.getSkew();
+ }
+}
+
+typedef LLVolumeParams* LLVolumeParamsPtr;
+typedef const LLVolumeParams* const_LLVolumeParamsPtr;
+
+class LLVolumeParams
+{
+public:
+ LLVolumeParams()
+ : mSculptType(LL_SCULPT_TYPE_NONE)
+ {
+ }
+
+ LLVolumeParams(LLProfileParams &profile, LLPathParams &path,
+ LLUUID sculpt_id = LLUUID::null, U8 sculpt_type = LL_SCULPT_TYPE_NONE)
+ : mProfileParams(profile), mPathParams(path), mSculptID(sculpt_id), mSculptType(sculpt_type)
+ {
+ }
+
+ bool operator==(const LLVolumeParams ¶ms) const;
+ bool operator!=(const LLVolumeParams ¶ms) const;
+ bool operator<(const LLVolumeParams ¶ms) const;
+
+
+ void copyParams(const LLVolumeParams ¶ms);
+
+ const LLProfileParams &getProfileParams() const {return mProfileParams;}
+ LLProfileParams &getProfileParams() {return mProfileParams;}
+ const LLPathParams &getPathParams() const {return mPathParams;}
+ LLPathParams &getPathParams() {return mPathParams;}
+
+ bool importFile(LLFILE *fp);
+ bool exportFile(LLFILE *fp) const;
+
+ bool importLegacyStream(std::istream& input_stream);
+ bool exportLegacyStream(std::ostream& output_stream) const;
+
+ LLSD sculptAsLLSD() const;
+ bool sculptFromLLSD(LLSD& sd);
+
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ bool setType(U8 profile, U8 path);
+
+ //void setBeginS(const F32 beginS) { mProfileParams.setBegin(beginS); } // range 0 to 1
+ //void setBeginT(const F32 beginT) { mPathParams.setBegin(beginT); } // range 0 to 1
+ //void setEndS(const F32 endS) { mProfileParams.setEnd(endS); } // range 0 to 1, must be greater than begin
+ //void setEndT(const F32 endT) { mPathParams.setEnd(endT); } // range 0 to 1, must be greater than begin
+
+ bool setBeginAndEndS(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end
+ bool setBeginAndEndT(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end
+
+ bool setHollow(const F32 hollow); // range 0 to 1
+ bool setRatio(const F32 x) { return setRatio(x,x); } // 0 = point, 1 = same as base
+ bool setShear(const F32 x) { return setShear(x,x); } // 0 = no movement,
+ bool setRatio(const F32 x, const F32 y); // 0 = point, 1 = same as base
+ bool setShear(const F32 x, const F32 y); // 0 = no movement
+
+ bool setTwistBegin(const F32 twist_begin); // range -1 to 1
+ bool setTwistEnd(const F32 twist_end); // range -1 to 1
+ bool setTwist(const F32 twist) { return setTwistEnd(twist); } // deprecated
+ bool setTaper(const F32 x, const F32 y) { bool pass_x = setTaperX(x); bool pass_y = setTaperY(y); return pass_x && pass_y; }
+ bool setTaperX(const F32 v); // -1 to 1
+ bool setTaperY(const F32 v); // -1 to 1
+ bool setRevolutions(const F32 revolutions); // 1 to 4
+ bool setRadiusOffset(const F32 radius_offset);
+ bool setSkew(const F32 skew);
+ bool setSculptID(const LLUUID& sculpt_id, U8 sculpt_type);
+
+ static bool validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
+ U8 path_curve, F32 path_begin, F32 path_end,
+ F32 scx, F32 scy, F32 shx, F32 shy,
+ F32 twistend, F32 twistbegin, F32 radiusoffset,
+ F32 tx, F32 ty, F32 revolutions, F32 skew);
+
+ const F32& getBeginS() const { return mProfileParams.getBegin(); }
+ const F32& getBeginT() const { return mPathParams.getBegin(); }
+ const F32& getEndS() const { return mProfileParams.getEnd(); }
+ const F32& getEndT() const { return mPathParams.getEnd(); }
+
+ const F32& getHollow() const { return mProfileParams.getHollow(); }
+ const F32& getTwist() const { return mPathParams.getTwist(); }
+ const F32& getRatio() const { return mPathParams.getScaleX(); }
+ const F32& getRatioX() const { return mPathParams.getScaleX(); }
+ const F32& getRatioY() const { return mPathParams.getScaleY(); }
+ const F32& getShearX() const { return mPathParams.getShearX(); }
+ const F32& getShearY() const { return mPathParams.getShearY(); }
+
+ const F32& getTwistBegin()const { return mPathParams.getTwistBegin(); }
+ const F32& getRadiusOffset() const { return mPathParams.getRadiusOffset(); }
+ const F32& getTaper() const { return mPathParams.getTaperX(); }
+ const F32& getTaperX() const { return mPathParams.getTaperX(); }
+ const F32& getTaperY() const { return mPathParams.getTaperY(); }
+ const F32& getRevolutions() const { return mPathParams.getRevolutions(); }
+ const F32& getSkew() const { return mPathParams.getSkew(); }
+ const LLUUID& getSculptID() const { return mSculptID; }
+ const U8& getSculptType() const { return mSculptType; }
+ bool isSculpt() const;
+ bool isMeshSculpt() const;
+ bool isConvex() const;
+
+ // 'begin' and 'end' should be in range [0, 1] (they will be clamped)
+ // (begin, end) = (0, 1) will not change the volume
+ // (begin, end) = (0, 0.5) will reduce the volume to the first half of its profile/path (S/T)
+ void reduceS(F32 begin, F32 end);
+ void reduceT(F32 begin, F32 end);
+
+ struct compare
+ {
+ bool operator()( const const_LLVolumeParamsPtr& first, const const_LLVolumeParamsPtr& second) const
+ {
+ return (*first < *second);
+ }
+ };
+
+ friend std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
+
+ // debug helper functions
+ void setCube();
+
+protected:
+ LLProfileParams mProfileParams;
+ LLPathParams mPathParams;
+ LLUUID mSculptID;
+ U8 mSculptType;
+};
+
+
+class LLProfile
+{
+ friend class LLVolume;
+
+public:
+ LLProfile()
+ : mOpen(false),
+ mConcave(false),
+ mDirty(true),
+ mTotalOut(0),
+ mTotal(2)
+ {
+ }
+
+ S32 getTotal() const { return mTotal; }
+ S32 getTotalOut() const { return mTotalOut; } // Total number of outside points
+ bool isFlat(S32 face) const { return (mFaces[face].mCount == 2); }
+ bool isOpen() const { return mOpen; }
+ void setDirty() { mDirty = true; }
+
+ static S32 getNumPoints(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0,
+ bool is_sculpted = false, S32 sculpt_size = 0);
+ bool generate(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0,
+ bool is_sculpted = false, S32 sculpt_size = 0);
+ bool isConcave() const { return mConcave; }
+public:
+ struct Face
+ {
+ S32 mIndex;
+ S32 mCount;
+ F32 mScaleU;
+ bool mCap;
+ bool mFlat;
+ LLFaceID mFaceID;
+ };
+
+ LLAlignedArray<LLVector4a, 64> mProfile;
+ //LLAlignedArray<LLVector4a, 64> mNormals;
+ std::vector<Face> mFaces;
+
+ //LLAlignedArray<LLVector4a, 64> mEdgeNormals;
+ //LLAlignedArray<LLVector4a, 64> mEdgeCenters;
+
+ friend std::ostream& operator<<(std::ostream &s, const LLProfile &profile);
+
+protected:
+ ~LLProfile();
+
+ static S32 getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0);
+ void genNGon(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0);
+
+ Face* addHole(const LLProfileParams& params, bool flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split = 0);
+ Face* addCap (S16 faceID);
+ Face* addFace(S32 index, S32 count, F32 scaleU, S16 faceID, bool flat);
+
+protected:
+ bool mOpen;
+ bool mConcave;
+ bool mDirty;
+
+ S32 mTotalOut;
+ S32 mTotal;
+};
+
+//-------------------------------------------------------------------
+// SWEEP/EXTRUDE PATHS
+//-------------------------------------------------------------------
+
+class LLPath
+{
+public:
+ class PathPt
+ {
+ public:
+ LLMatrix4a mRot;
+ LLVector4a mPos;
+
+ LLVector4a mScale;
+ F32 mTexT;
+ F32 pad[3]; //for alignment
+ PathPt()
+ {
+ mPos.clear();
+ mTexT = 0;
+ mScale.clear();
+ mRot.setRows(LLVector4a(1,0,0,0),
+ LLVector4a(0,1,0,0),
+ LLVector4a(0,0,1,0));
+
+ //distinguished data in the pad for debugging
+ pad[0] = 3.14159f;
+ pad[1] = -3.14159f;
+ pad[2] = 0.585f;
+ }
+ };
+
+public:
+ LLPath()
+ : mOpen(false),
+ mTotal(0),
+ mDirty(true),
+ mStep(1)
+ {
+ }
+
+ virtual ~LLPath();
+
+ static S32 getNumPoints(const LLPathParams& params, F32 detail);
+ static S32 getNumNGonPoints(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f);
+
+ void genNGon(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f);
+ virtual bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0,
+ bool is_sculpted = false, S32 sculpt_size = 0);
+
+ bool isOpen() const { return mOpen; }
+ F32 getStep() const { return mStep; }
+ void setDirty() { mDirty = true; }
+
+ S32 getPathLength() const { return (S32)mPath.size(); }
+
+ void resizePath(S32 length) { mPath.resize(length); }
+
+ friend std::ostream& operator<<(std::ostream &s, const LLPath &path);
+
+public:
+ LLAlignedArray<PathPt, 64> mPath;
+
+protected:
+ bool mOpen;
+ S32 mTotal;
+ bool mDirty;
+ F32 mStep;
+};
+
+class LLDynamicPath : public LLPath
+{
+public:
+ LLDynamicPath() : LLPath() { }
+ /*virtual*/ bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0,
+ bool is_sculpted = false, S32 sculpt_size = 0);
+};
+
+// Yet another "face" class - caches volume-specific, but not instance-specific data for faces)
+class LLVolumeFace
+{
+public:
+ class VertexData
+ {
+ enum
+ {
+ POSITION = 0,
+ NORMAL = 1
+ };
+
+ private:
+ void init();
+ public:
+ VertexData();
+ VertexData(const VertexData& rhs);
+ const VertexData& operator=(const VertexData& rhs);
+
+ ~VertexData();
+ LLVector4a& getPosition();
+ LLVector4a& getNormal();
+ const LLVector4a& getPosition() const;
+ const LLVector4a& getNormal() const;
+ void setPosition(const LLVector4a& pos);
+ void setNormal(const LLVector4a& norm);
+
+
+ LLVector2 mTexCoord;
+
+ bool operator<(const VertexData& rhs) const;
+ bool operator==(const VertexData& rhs) const;
+ bool compareNormal(const VertexData& rhs, F32 angle_cutoff) const;
+
+ private:
+ LLVector4a* mData;
+ };
+
+ LLVolumeFace();
+ LLVolumeFace(const LLVolumeFace& src);
+ LLVolumeFace& operator=(const LLVolumeFace& rhs);
+
+ ~LLVolumeFace();
+private:
+ void freeData();
+public:
+
+ bool create(LLVolume* volume, bool partial_build = false);
+ void createTangents();
+
+ void resizeVertices(S32 num_verts);
+ void allocateTangents(S32 num_verts);
+ void allocateWeights(S32 num_verts);
+ void allocateJointIndices(S32 num_verts);
+ void resizeIndices(S32 num_indices);
+ void fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx);
+
+ void pushVertex(const VertexData& cv);
+ void pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc);
+ void pushIndex(const U16& idx);
+
+ void swapData(LLVolumeFace& rhs);
+
+ void getVertexData(U16 indx, LLVolumeFace::VertexData& cv);
+
+ class VertexMapData : public LLVolumeFace::VertexData
+ {
+ public:
+ U16 mIndex;
+
+ bool operator==(const LLVolumeFace::VertexData& rhs) const;
+
+ struct ComparePosition
+ {
+ bool operator()(const LLVector3& a, const LLVector3& b) const;
+ };
+
+ typedef std::map<LLVector3, std::vector<VertexMapData>, VertexMapData::ComparePosition > PointMap;
+ };
+
+ // Eliminates non unique triangles, takes positions,
+ // normals and texture coordinates into account.
+ void remap();
+
+ void optimize(F32 angle_cutoff = 2.f);
+ bool cacheOptimize(bool gen_tangents = false);
+
+ void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f));
+ void destroyOctree();
+ // Get a reference to the octree, which may be null
+ const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const;
+
+ enum
+ {
+ SINGLE_MASK = 0x0001,
+ CAP_MASK = 0x0002,
+ END_MASK = 0x0004,
+ SIDE_MASK = 0x0008,
+ INNER_MASK = 0x0010,
+ OUTER_MASK = 0x0020,
+ HOLLOW_MASK = 0x0040,
+ OPEN_MASK = 0x0080,
+ FLAT_MASK = 0x0100,
+ TOP_MASK = 0x0200,
+ BOTTOM_MASK = 0x0400
+ };
+
+public:
+ S32 mID;
+ U32 mTypeMask;
+
+ // Only used for INNER/OUTER faces
+ S32 mBeginS;
+ S32 mBeginT;
+ S32 mNumS;
+ S32 mNumT;
+
+ LLVector4a* mExtents; //minimum and maximum point of face
+ LLVector4a* mCenter;
+ LLVector2 mTexCoordExtents[2]; //minimum and maximum of texture coordinates of the face.
+
+ S32 mNumVertices; // num vertices == num normals == num texcoords
+ S32 mNumAllocatedVertices;
+ S32 mNumIndices;
+
+ LLVector4a* mPositions; // Contains vertices, nortmals and texcoords
+ LLVector4a* mNormals; // pointer into mPositions
+ LLVector4a* mTangents;
+ LLVector2* mTexCoords; // pointer into mPositions
+
+ // mIndices contains mNumIndices amount of elements.
+ // It contains triangles, each 3 indices describe one triangle.
+ // If mIndices contains {0, 2, 3, 1, 2, 4}, it means there
+ // are two triangles {0, 2, 3} and {1, 2, 4} with values being
+ // indexes for mPositions/mNormals/mTexCoords
+ U16* mIndices;
+
+ std::vector<S32> mEdge;
+
+ //list of skin weights for rigged volumes
+ // format is mWeights[vertex_index].mV[influence] = <joint_index>.<weight>
+ // mWeights.size() should be empty or match mVertices.size()
+ LLVector4a* mWeights;
+
+#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
+ LLVector4a* mJustWeights;
+ U8* mJointIndices;
+#endif
+
+ mutable bool mWeightsScrubbed;
+
+ // Which joints are rigged to, and the bounding box of any rigged
+ // vertices per joint.
+ LLJointRiggingInfoTab mJointRiggingInfoTab;
+
+ //whether or not face has been cache optimized
+ bool mOptimized;
+
+ // if this is a mesh asset, scale and translation that were applied
+ // when encoding the source mesh into a unit cube
+ // used for regenerating tangents
+ LLVector3 mNormalizedScale = LLVector3(1,1,1);
+
+private:
+ LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree;
+ LLVolumeTriangle* mOctreeTriangles;
+
+ bool createUnCutCubeCap(LLVolume* volume, bool partial_build = false);
+ bool createCap(LLVolume* volume, bool partial_build = false);
+ bool createSide(LLVolume* volume, bool partial_build = false);
+};
+
+class LLVolume : public LLRefCount
+{
+ friend class LLVolumeLODGroup;
+
+protected:
+ virtual ~LLVolume(); // use unref
+
+public:
+ typedef std::vector<LLVolumeFace> face_list_t;
+
+ struct FaceParams
+ {
+ LLFaceID mFaceID;
+ S32 mBeginS;
+ S32 mCountS;
+ S32 mBeginT;
+ S32 mCountT;
+ };
+
+ LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool generate_single_face = false, const bool is_unique = false);
+
+ U8 getProfileType() const { return mParams.getProfileParams().getCurveType(); }
+ U8 getPathType() const { return mParams.getPathParams().getCurveType(); }
+ S32 getNumFaces() const;
+ S32 getNumVolumeFaces() const { return mVolumeFaces.size(); }
+ F32 getDetail() const { return mDetail; }
+ F32 getSurfaceArea() const { return mSurfaceArea; }
+ const LLVolumeParams& getParams() const { return mParams; }
+ LLVolumeParams getCopyOfParams() const { return mParams; }
+ const LLProfile& getProfile() const { return *mProfilep; }
+ LLPath& getPath() const { return *mPathp; }
+ void resizePath(S32 length);
+ const LLAlignedArray<LLVector4a,64>& getMesh() const { return mMesh; }
+ const LLVector4a& getMeshPt(const U32 i) const { return mMesh[i]; }
+
+
+ void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); }
+
+ void regen();
+ void genTangents(S32 face);
+
+ bool isConvex() const;
+ bool isCap(S32 face);
+ bool isFlat(S32 face);
+ bool isUnique() const { return mUnique; }
+
+ S32 getSculptLevel() const { return mSculptLevel; }
+ void setSculptLevel(S32 level) { mSculptLevel = level; }
+
+
+ static void getLoDTriangleCounts(const LLVolumeParams& params, S32* counts);
+
+ S32 getNumTriangles(S32* vcount = nullptr) const;
+
+ void generateSilhouetteVertices(std::vector<LLVector3> &vertices,
+ std::vector<LLVector3> &normals,
+ const LLVector3& view_vec,
+ const LLMatrix4& mat,
+ const LLMatrix3& norm_mat,
+ S32 face_index);
+
+ //get the face index of the face that intersects with the given line segment at the point
+ //closest to start. Moves end to the point of intersection. Returns -1 if no intersection.
+ //Line segment must be in volume space.
+ S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ S32 face = -1, // which face to check, -1 = ALL_SIDES
+ LLVector4a* intersection = nullptr, // return the intersection point
+ LLVector2* tex_coord = nullptr, // return the texture coordinates of the intersection point
+ LLVector4a* normal = nullptr, // return the surface normal at the intersection point
+ LLVector4a* tangent = nullptr // return the surface tangent at the intersection point
+ );
+
+ LLFaceID generateFaceMask();
+
+ bool isFaceMaskValid(LLFaceID face_mask);
+ static S32 sNumMeshPoints;
+
+ friend std::ostream& operator<<(std::ostream &s, const LLVolume &volume);
+ friend std::ostream& operator<<(std::ostream &s, const LLVolume *volumep); // HACK to bypass Windoze confusion over
+ // conversion if *(LLVolume*) to LLVolume&
+ const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE
+
+ LLVolumeFace &getVolumeFace(const S32 f) {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE
+
+ face_list_t& getVolumeFaces() { return mVolumeFaces; }
+
+ U32 mFaceMask; // bit array of which faces exist in this volume
+ LLVector3 mLODScaleBias; // vector for biasing LOD based on scale
+
+ void sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level, bool visible_placeholder);
+ void copyVolumeFaces(const LLVolume* volume);
+ void copyFacesTo(std::vector<LLVolumeFace> &faces) const;
+ void copyFacesFrom(const std::vector<LLVolumeFace> &faces);
+
+ // use meshoptimizer to optimize index buffer for vertex shader cache
+ // gen_tangents - if true, generate MikkTSpace tangents if needed before optimizing index buffer
+ bool cacheOptimize(bool gen_tangents = false);
+
+private:
+ void sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type);
+ F32 sculptGetSurfaceArea();
+ void sculptGenerateEmptyPlaceholder();
+ void sculptGenerateSpherePlaceholder();
+
+protected:
+ bool generate();
+ void createVolumeFaces();
+public:
+ bool unpackVolumeFaces(std::istream& is, S32 size);
+ bool unpackVolumeFaces(U8* in_data, S32 size);
+private:
+ bool unpackVolumeFacesInternal(const LLSD& mdl);
+
+public:
+ virtual void setMeshAssetLoaded(bool loaded);
+ virtual bool isMeshAssetLoaded();
+ virtual void setMeshAssetUnavaliable(bool unavaliable);
+ virtual bool isMeshAssetUnavaliable();
+
+ protected:
+ bool mUnique;
+ F32 mDetail;
+ S32 mSculptLevel;
+ F32 mSurfaceArea; //unscaled surface area
+ bool mIsMeshAssetLoaded;
+ bool mIsMeshAssetUnavaliable;
+
+ const LLVolumeParams mParams;
+ LLPath *mPathp;
+ LLProfile *mProfilep;
+ LLAlignedArray<LLVector4a,64> mMesh;
+
+
+ bool mGenerateSingleFace;
+ face_list_t mVolumeFaces;
+
+public:
+ LLVector4a* mHullPoints;
+ U16* mHullIndices;
+ S32 mNumHullPoints;
+ S32 mNumHullIndices;
+};
+
+std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
+
+bool LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size);
+bool LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
+bool LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size);
+
+bool LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+ F32& intersection_a, F32& intersection_b, F32& intersection_t);
+bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+ F32& intersection_a, F32& intersection_b, F32& intersection_t);
+
+#endif
diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp index 06794fd23f..f796fbf551 100644 --- a/indra/llmath/llvolumemgr.cpp +++ b/indra/llmath/llvolumemgr.cpp @@ -1,408 +1,408 @@ -/** - * @file llvolumemgr.cpp - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llvolumemgr.h" -#include "llvolume.h" - - -const F32 BASE_THRESHOLD = 0.03f; - -//static -F32 LLVolumeLODGroup::mDetailThresholds[NUM_LODS] = {BASE_THRESHOLD, - 2*BASE_THRESHOLD, - 8*BASE_THRESHOLD, - 100*BASE_THRESHOLD}; - -//static -F32 LLVolumeLODGroup::mDetailScales[NUM_LODS] = {1.f, 1.5f, 2.5f, 4.f}; - - -//============================================================================ - -LLVolumeMgr::LLVolumeMgr() -: mDataMutex(NULL) -{ - // the LLMutex magic interferes with easy unit testing, - // so you now must manually call useMutex() to use it - //mDataMutex = new LLMutex(); -} - -LLVolumeMgr::~LLVolumeMgr() -{ - cleanup(); - - delete mDataMutex; - mDataMutex = NULL; -} - -bool LLVolumeMgr::cleanup() -{ - bool no_refs = true; - if (mDataMutex) - { - mDataMutex->lock(); - } - for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(), - end = mVolumeLODGroups.end(); - iter != end; iter++) - { - LLVolumeLODGroup *volgroupp = iter->second; - if (!volgroupp->cleanupRefs()) - { - no_refs = false; - } - delete volgroupp; - } - mVolumeLODGroups.clear(); - if (mDataMutex) - { - mDataMutex->unlock(); - } - return no_refs; -} - -// Always only ever store the results of refVolume in a LLPointer -// Note however that LLVolumeLODGroup that contains the volume -// also holds a LLPointer so the volume will only go away after -// anything holding the volume and the LODGroup are destroyed -LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 lod) -{ - LLVolumeLODGroup* volgroupp; - if (mDataMutex) - { - mDataMutex->lock(); - } - volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(&volume_params); - if( iter == mVolumeLODGroups.end() ) - { - volgroupp = createNewGroup(volume_params); - } - else - { - volgroupp = iter->second; - } - if (mDataMutex) - { - mDataMutex->unlock(); - } - return volgroupp->refLOD(lod); -} - -// virtual -LLVolumeLODGroup* LLVolumeMgr::getGroup( const LLVolumeParams& volume_params ) const -{ - LLVolumeLODGroup* volgroupp = NULL; - if (mDataMutex) - { - mDataMutex->lock(); - } - volume_lod_group_map_t::const_iterator iter = mVolumeLODGroups.find(&volume_params); - if( iter != mVolumeLODGroups.end() ) - { - volgroupp = iter->second; - } - if (mDataMutex) - { - mDataMutex->unlock(); - } - return volgroupp; -} - -void LLVolumeMgr::unrefVolume(LLVolume *volumep) -{ - if (volumep->isUnique()) - { - // TomY: Don't need to manage this volume. It is a unique instance. - return; - } - const LLVolumeParams* params = &(volumep->getParams()); - if (mDataMutex) - { - mDataMutex->lock(); - } - volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(params); - if( iter == mVolumeLODGroups.end() ) - { - LL_ERRS() << "Warning! Tried to cleanup unknown volume type! " << *params << LL_ENDL; - if (mDataMutex) - { - mDataMutex->unlock(); - } - return; - } - else - { - LLVolumeLODGroup* volgroupp = iter->second; - - volgroupp->derefLOD(volumep); - if (volgroupp->getNumRefs() == 0) - { - mVolumeLODGroups.erase(params); - delete volgroupp; - } - } - if (mDataMutex) - { - mDataMutex->unlock(); - } - -} - -// protected -void LLVolumeMgr::insertGroup(LLVolumeLODGroup* volgroup) -{ - mVolumeLODGroups[volgroup->getVolumeParams()] = volgroup; -} - -// protected -LLVolumeLODGroup* LLVolumeMgr::createNewGroup(const LLVolumeParams& volume_params) -{ - LLVolumeLODGroup* volgroup = new LLVolumeLODGroup(volume_params); - insertGroup(volgroup); - return volgroup; -} - -// virtual -void LLVolumeMgr::dump() -{ - F32 avg = 0.f; - if (mDataMutex) - { - mDataMutex->lock(); - } - for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(), - end = mVolumeLODGroups.end(); - iter != end; iter++) - { - LLVolumeLODGroup *volgroupp = iter->second; - avg += volgroupp->dump(); - } - int count = (int)mVolumeLODGroups.size(); - avg = count ? avg / (F32)count : 0.0f; - if (mDataMutex) - { - mDataMutex->unlock(); - } - LL_INFOS() << "Average usage of LODs " << avg << LL_ENDL; -} - -void LLVolumeMgr::useMutex() -{ - if (!mDataMutex) - { - mDataMutex = new LLMutex(); - } -} - -std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr) -{ - s << "{ numLODgroups=" << volume_mgr.mVolumeLODGroups.size() << ", "; - - S32 total_refs = 0; - if (volume_mgr.mDataMutex) - { - volume_mgr.mDataMutex->lock(); - } - - for (LLVolumeMgr::volume_lod_group_map_t::const_iterator iter = volume_mgr.mVolumeLODGroups.begin(); - iter != volume_mgr.mVolumeLODGroups.end(); ++iter) - { - LLVolumeLODGroup *volgroupp = iter->second; - total_refs += volgroupp->getNumRefs(); - s << ", " << (*volgroupp); - } - - if (volume_mgr.mDataMutex) - { - volume_mgr.mDataMutex->unlock(); - } - - s << ", total_refs=" << total_refs << " }"; - return s; -} - -LLVolumeLODGroup::LLVolumeLODGroup(const LLVolumeParams ¶ms) - : mVolumeParams(params), - mRefs(0) -{ - for (S32 i = 0; i < NUM_LODS; i++) - { - mLODRefs[i] = 0; - mAccessCount[i] = 0; - } -} - -LLVolumeLODGroup::~LLVolumeLODGroup() -{ - for (S32 i = 0; i < NUM_LODS; i++) - { - llassert_always(mLODRefs[i] == 0); - } -} - -// Called from LLVolumeMgr::cleanup -bool LLVolumeLODGroup::cleanupRefs() -{ - bool res = true; - if (mRefs != 0) - { - LL_WARNS() << "Volume group has remaining refs:" << getNumRefs() << LL_ENDL; - mRefs = 0; - for (S32 i = 0; i < NUM_LODS; i++) - { - if (mLODRefs[i] > 0) - { - LL_WARNS() << " LOD " << i << " refs = " << mLODRefs[i] << LL_ENDL; - mLODRefs[i] = 0; - mVolumeLODs[i] = NULL; - } - } - LL_WARNS() << *getVolumeParams() << LL_ENDL; - res = false; - } - return res; -} - -LLVolume* LLVolumeLODGroup::refLOD(const S32 lod) -{ - llassert(lod >=0 && lod < NUM_LODS); - mAccessCount[lod]++; - - mRefs++; - if (mVolumeLODs[lod].isNull()) - { - mVolumeLODs[lod] = new LLVolume(mVolumeParams, mDetailScales[lod]); - } - mLODRefs[lod]++; - return mVolumeLODs[lod]; -} - -bool LLVolumeLODGroup::derefLOD(LLVolume *volumep) -{ - llassert_always(mRefs > 0); - mRefs--; - for (S32 i = 0; i < NUM_LODS; i++) - { - if (mVolumeLODs[i] == volumep) - { - llassert_always(mLODRefs[i] > 0); - mLODRefs[i]--; -#if 0 // SJB: Possible opt: keep other lods around - if (!mLODRefs[i]) - { - mVolumeLODs[i] = NULL; - } -#endif - return true; - } - } - LL_ERRS() << "Deref of non-matching LOD in volume LOD group" << LL_ENDL; - return false; -} - -S32 LLVolumeLODGroup::getDetailFromTan(const F32 tan_angle) -{ - S32 i = 0; - while (i < (NUM_LODS - 1)) - { - if (tan_angle <= mDetailThresholds[i]) - { - return i; - } - i++; - } - return NUM_LODS - 1; -} - -void LLVolumeLODGroup::getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher) -{ - S32 detail = getDetailFromTan(tan_angle); - - if (detail > 0) - { - to_lower = tan_angle - mDetailThresholds[detail]; - } - else - { - to_lower = 1024.f*1024.f; - } - - if (detail < NUM_LODS-1) - { - to_higher = mDetailThresholds[detail+1] - tan_angle; - } - else - { - to_higher = 1024.f*1024.f; - } -} - -F32 LLVolumeLODGroup::getVolumeScaleFromDetail(const S32 detail) -{ - return mDetailScales[detail]; -} - -S32 LLVolumeLODGroup::getVolumeDetailFromScale(const F32 detail) -{ - for (S32 i = 1; i < 4; i++) - { - if (mDetailScales[i] > detail) - { - return i-1; - } - } - - return 3; -} - -F32 LLVolumeLODGroup::dump() -{ - F32 usage = 0.f; - for (S32 i = 0; i < NUM_LODS; i++) - { - if (mAccessCount[i] > 0) - { - usage += 1.f; - } - } - usage = usage / (F32)NUM_LODS; - - std::string dump_str = llformat("%.3f %d %d %d %d", usage, mAccessCount[0], mAccessCount[1], mAccessCount[2], mAccessCount[3]); - - LL_INFOS() << dump_str << LL_ENDL; - return usage; -} - -std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup) -{ - s << "{ numRefs=" << volgroup.getNumRefs(); - s << ", mParams=" << volgroup.getVolumeParams(); - s << " }"; - - return s; -} - +/**
+ * @file llvolumemgr.cpp
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llvolumemgr.h"
+#include "llvolume.h"
+
+
+const F32 BASE_THRESHOLD = 0.03f;
+
+//static
+F32 LLVolumeLODGroup::mDetailThresholds[NUM_LODS] = {BASE_THRESHOLD,
+ 2*BASE_THRESHOLD,
+ 8*BASE_THRESHOLD,
+ 100*BASE_THRESHOLD};
+
+//static
+F32 LLVolumeLODGroup::mDetailScales[NUM_LODS] = {1.f, 1.5f, 2.5f, 4.f};
+
+
+//============================================================================
+
+LLVolumeMgr::LLVolumeMgr()
+: mDataMutex(NULL)
+{
+ // the LLMutex magic interferes with easy unit testing,
+ // so you now must manually call useMutex() to use it
+ //mDataMutex = new LLMutex();
+}
+
+LLVolumeMgr::~LLVolumeMgr()
+{
+ cleanup();
+
+ delete mDataMutex;
+ mDataMutex = NULL;
+}
+
+bool LLVolumeMgr::cleanup()
+{
+ bool no_refs = true;
+ if (mDataMutex)
+ {
+ mDataMutex->lock();
+ }
+ for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(),
+ end = mVolumeLODGroups.end();
+ iter != end; iter++)
+ {
+ LLVolumeLODGroup *volgroupp = iter->second;
+ if (!volgroupp->cleanupRefs())
+ {
+ no_refs = false;
+ }
+ delete volgroupp;
+ }
+ mVolumeLODGroups.clear();
+ if (mDataMutex)
+ {
+ mDataMutex->unlock();
+ }
+ return no_refs;
+}
+
+// Always only ever store the results of refVolume in a LLPointer
+// Note however that LLVolumeLODGroup that contains the volume
+// also holds a LLPointer so the volume will only go away after
+// anything holding the volume and the LODGroup are destroyed
+LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 lod)
+{
+ LLVolumeLODGroup* volgroupp;
+ if (mDataMutex)
+ {
+ mDataMutex->lock();
+ }
+ volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(&volume_params);
+ if( iter == mVolumeLODGroups.end() )
+ {
+ volgroupp = createNewGroup(volume_params);
+ }
+ else
+ {
+ volgroupp = iter->second;
+ }
+ if (mDataMutex)
+ {
+ mDataMutex->unlock();
+ }
+ return volgroupp->refLOD(lod);
+}
+
+// virtual
+LLVolumeLODGroup* LLVolumeMgr::getGroup( const LLVolumeParams& volume_params ) const
+{
+ LLVolumeLODGroup* volgroupp = NULL;
+ if (mDataMutex)
+ {
+ mDataMutex->lock();
+ }
+ volume_lod_group_map_t::const_iterator iter = mVolumeLODGroups.find(&volume_params);
+ if( iter != mVolumeLODGroups.end() )
+ {
+ volgroupp = iter->second;
+ }
+ if (mDataMutex)
+ {
+ mDataMutex->unlock();
+ }
+ return volgroupp;
+}
+
+void LLVolumeMgr::unrefVolume(LLVolume *volumep)
+{
+ if (volumep->isUnique())
+ {
+ // TomY: Don't need to manage this volume. It is a unique instance.
+ return;
+ }
+ const LLVolumeParams* params = &(volumep->getParams());
+ if (mDataMutex)
+ {
+ mDataMutex->lock();
+ }
+ volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(params);
+ if( iter == mVolumeLODGroups.end() )
+ {
+ LL_ERRS() << "Warning! Tried to cleanup unknown volume type! " << *params << LL_ENDL;
+ if (mDataMutex)
+ {
+ mDataMutex->unlock();
+ }
+ return;
+ }
+ else
+ {
+ LLVolumeLODGroup* volgroupp = iter->second;
+
+ volgroupp->derefLOD(volumep);
+ if (volgroupp->getNumRefs() == 0)
+ {
+ mVolumeLODGroups.erase(params);
+ delete volgroupp;
+ }
+ }
+ if (mDataMutex)
+ {
+ mDataMutex->unlock();
+ }
+
+}
+
+// protected
+void LLVolumeMgr::insertGroup(LLVolumeLODGroup* volgroup)
+{
+ mVolumeLODGroups[volgroup->getVolumeParams()] = volgroup;
+}
+
+// protected
+LLVolumeLODGroup* LLVolumeMgr::createNewGroup(const LLVolumeParams& volume_params)
+{
+ LLVolumeLODGroup* volgroup = new LLVolumeLODGroup(volume_params);
+ insertGroup(volgroup);
+ return volgroup;
+}
+
+// virtual
+void LLVolumeMgr::dump()
+{
+ F32 avg = 0.f;
+ if (mDataMutex)
+ {
+ mDataMutex->lock();
+ }
+ for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(),
+ end = mVolumeLODGroups.end();
+ iter != end; iter++)
+ {
+ LLVolumeLODGroup *volgroupp = iter->second;
+ avg += volgroupp->dump();
+ }
+ int count = (int)mVolumeLODGroups.size();
+ avg = count ? avg / (F32)count : 0.0f;
+ if (mDataMutex)
+ {
+ mDataMutex->unlock();
+ }
+ LL_INFOS() << "Average usage of LODs " << avg << LL_ENDL;
+}
+
+void LLVolumeMgr::useMutex()
+{
+ if (!mDataMutex)
+ {
+ mDataMutex = new LLMutex();
+ }
+}
+
+std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr)
+{
+ s << "{ numLODgroups=" << volume_mgr.mVolumeLODGroups.size() << ", ";
+
+ S32 total_refs = 0;
+ if (volume_mgr.mDataMutex)
+ {
+ volume_mgr.mDataMutex->lock();
+ }
+
+ for (LLVolumeMgr::volume_lod_group_map_t::const_iterator iter = volume_mgr.mVolumeLODGroups.begin();
+ iter != volume_mgr.mVolumeLODGroups.end(); ++iter)
+ {
+ LLVolumeLODGroup *volgroupp = iter->second;
+ total_refs += volgroupp->getNumRefs();
+ s << ", " << (*volgroupp);
+ }
+
+ if (volume_mgr.mDataMutex)
+ {
+ volume_mgr.mDataMutex->unlock();
+ }
+
+ s << ", total_refs=" << total_refs << " }";
+ return s;
+}
+
+LLVolumeLODGroup::LLVolumeLODGroup(const LLVolumeParams ¶ms)
+ : mVolumeParams(params),
+ mRefs(0)
+{
+ for (S32 i = 0; i < NUM_LODS; i++)
+ {
+ mLODRefs[i] = 0;
+ mAccessCount[i] = 0;
+ }
+}
+
+LLVolumeLODGroup::~LLVolumeLODGroup()
+{
+ for (S32 i = 0; i < NUM_LODS; i++)
+ {
+ llassert_always(mLODRefs[i] == 0);
+ }
+}
+
+// Called from LLVolumeMgr::cleanup
+bool LLVolumeLODGroup::cleanupRefs()
+{
+ bool res = true;
+ if (mRefs != 0)
+ {
+ LL_WARNS() << "Volume group has remaining refs:" << getNumRefs() << LL_ENDL;
+ mRefs = 0;
+ for (S32 i = 0; i < NUM_LODS; i++)
+ {
+ if (mLODRefs[i] > 0)
+ {
+ LL_WARNS() << " LOD " << i << " refs = " << mLODRefs[i] << LL_ENDL;
+ mLODRefs[i] = 0;
+ mVolumeLODs[i] = NULL;
+ }
+ }
+ LL_WARNS() << *getVolumeParams() << LL_ENDL;
+ res = false;
+ }
+ return res;
+}
+
+LLVolume* LLVolumeLODGroup::refLOD(const S32 lod)
+{
+ llassert(lod >=0 && lod < NUM_LODS);
+ mAccessCount[lod]++;
+
+ mRefs++;
+ if (mVolumeLODs[lod].isNull())
+ {
+ mVolumeLODs[lod] = new LLVolume(mVolumeParams, mDetailScales[lod]);
+ }
+ mLODRefs[lod]++;
+ return mVolumeLODs[lod];
+}
+
+bool LLVolumeLODGroup::derefLOD(LLVolume *volumep)
+{
+ llassert_always(mRefs > 0);
+ mRefs--;
+ for (S32 i = 0; i < NUM_LODS; i++)
+ {
+ if (mVolumeLODs[i] == volumep)
+ {
+ llassert_always(mLODRefs[i] > 0);
+ mLODRefs[i]--;
+#if 0 // SJB: Possible opt: keep other lods around
+ if (!mLODRefs[i])
+ {
+ mVolumeLODs[i] = NULL;
+ }
+#endif
+ return true;
+ }
+ }
+ LL_ERRS() << "Deref of non-matching LOD in volume LOD group" << LL_ENDL;
+ return false;
+}
+
+S32 LLVolumeLODGroup::getDetailFromTan(const F32 tan_angle)
+{
+ S32 i = 0;
+ while (i < (NUM_LODS - 1))
+ {
+ if (tan_angle <= mDetailThresholds[i])
+ {
+ return i;
+ }
+ i++;
+ }
+ return NUM_LODS - 1;
+}
+
+void LLVolumeLODGroup::getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher)
+{
+ S32 detail = getDetailFromTan(tan_angle);
+
+ if (detail > 0)
+ {
+ to_lower = tan_angle - mDetailThresholds[detail];
+ }
+ else
+ {
+ to_lower = 1024.f*1024.f;
+ }
+
+ if (detail < NUM_LODS-1)
+ {
+ to_higher = mDetailThresholds[detail+1] - tan_angle;
+ }
+ else
+ {
+ to_higher = 1024.f*1024.f;
+ }
+}
+
+F32 LLVolumeLODGroup::getVolumeScaleFromDetail(const S32 detail)
+{
+ return mDetailScales[detail];
+}
+
+S32 LLVolumeLODGroup::getVolumeDetailFromScale(const F32 detail)
+{
+ for (S32 i = 1; i < 4; i++)
+ {
+ if (mDetailScales[i] > detail)
+ {
+ return i-1;
+ }
+ }
+
+ return 3;
+}
+
+F32 LLVolumeLODGroup::dump()
+{
+ F32 usage = 0.f;
+ for (S32 i = 0; i < NUM_LODS; i++)
+ {
+ if (mAccessCount[i] > 0)
+ {
+ usage += 1.f;
+ }
+ }
+ usage = usage / (F32)NUM_LODS;
+
+ std::string dump_str = llformat("%.3f %d %d %d %d", usage, mAccessCount[0], mAccessCount[1], mAccessCount[2], mAccessCount[3]);
+
+ LL_INFOS() << dump_str << LL_ENDL;
+ return usage;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup)
+{
+ s << "{ numRefs=" << volgroup.getNumRefs();
+ s << ", mParams=" << volgroup.getVolumeParams();
+ s << " }";
+
+ return s;
+}
+
diff --git a/indra/llmath/llvolumemgr.h b/indra/llmath/llvolumemgr.h index b0baf7054d..daeaf378d2 100644 --- a/indra/llmath/llvolumemgr.h +++ b/indra/llmath/llvolumemgr.h @@ -1,112 +1,112 @@ -/** - * @file llvolumemgr.h - * @brief LLVolumeMgr class. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLVOLUMEMGR_H -#define LL_LLVOLUMEMGR_H - -#include <map> - -#include "llvolume.h" -#include "llpointer.h" -#include "llthread.h" - -class LLVolumeParams; -class LLVolumeLODGroup; - -class LLVolumeLODGroup -{ - LOG_CLASS(LLVolumeLODGroup); - -public: - enum - { - NUM_LODS = 4 - }; - - LLVolumeLODGroup(const LLVolumeParams ¶ms); - ~LLVolumeLODGroup(); - bool cleanupRefs(); - - static S32 getDetailFromTan(const F32 tan_angle); - static void getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher); - static F32 getVolumeScaleFromDetail(const S32 detail); - static S32 getVolumeDetailFromScale(F32 scale); - - LLVolume* refLOD(const S32 detail); - bool derefLOD(LLVolume *volumep); - S32 getNumRefs() const { return mRefs; } - - const LLVolumeParams* getVolumeParams() const { return &mVolumeParams; }; - - F32 dump(); - friend std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup); - -protected: - LLVolumeParams mVolumeParams; - - S32 mRefs; - S32 mLODRefs[NUM_LODS]; - LLPointer<LLVolume> mVolumeLODs[NUM_LODS]; - static F32 mDetailThresholds[NUM_LODS]; - static F32 mDetailScales[NUM_LODS]; - S32 mAccessCount[NUM_LODS]; -}; - -class LLVolumeMgr -{ -public: - LLVolumeMgr(); - virtual ~LLVolumeMgr(); - bool cleanup(); // Cleanup all volumes being managed, returns true if no dangling references - - virtual LLVolumeLODGroup* getGroup( const LLVolumeParams& volume_params ) const; - - // whatever calls getVolume() never owns the LLVolume* and - // cannot keep references for long since it may be deleted - // later. For best results hold it in an LLPointer<LLVolume>. - virtual LLVolume *refVolume(const LLVolumeParams &volume_params, const S32 detail); - virtual void unrefVolume(LLVolume *volumep); - - void dump(); - - // manually call this for mutex magic - void useMutex(); - - friend std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr); - -protected: - void insertGroup(LLVolumeLODGroup* volgroup); - // Overridden in llphysics/abstract/utils/llphysicsvolumemanager.h - virtual LLVolumeLODGroup* createNewGroup(const LLVolumeParams& volume_params); - -protected: - typedef std::map<const LLVolumeParams*, LLVolumeLODGroup*, LLVolumeParams::compare> volume_lod_group_map_t; - volume_lod_group_map_t mVolumeLODGroups; - - LLMutex* mDataMutex; -}; - -#endif // LL_LLVOLUMEMGR_H +/**
+ * @file llvolumemgr.h
+ * @brief LLVolumeMgr class.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLVOLUMEMGR_H
+#define LL_LLVOLUMEMGR_H
+
+#include <map>
+
+#include "llvolume.h"
+#include "llpointer.h"
+#include "llthread.h"
+
+class LLVolumeParams;
+class LLVolumeLODGroup;
+
+class LLVolumeLODGroup
+{
+ LOG_CLASS(LLVolumeLODGroup);
+
+public:
+ enum
+ {
+ NUM_LODS = 4
+ };
+
+ LLVolumeLODGroup(const LLVolumeParams ¶ms);
+ ~LLVolumeLODGroup();
+ bool cleanupRefs();
+
+ static S32 getDetailFromTan(const F32 tan_angle);
+ static void getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher);
+ static F32 getVolumeScaleFromDetail(const S32 detail);
+ static S32 getVolumeDetailFromScale(F32 scale);
+
+ LLVolume* refLOD(const S32 detail);
+ bool derefLOD(LLVolume *volumep);
+ S32 getNumRefs() const { return mRefs; }
+
+ const LLVolumeParams* getVolumeParams() const { return &mVolumeParams; };
+
+ F32 dump();
+ friend std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup);
+
+protected:
+ LLVolumeParams mVolumeParams;
+
+ S32 mRefs;
+ S32 mLODRefs[NUM_LODS];
+ LLPointer<LLVolume> mVolumeLODs[NUM_LODS];
+ static F32 mDetailThresholds[NUM_LODS];
+ static F32 mDetailScales[NUM_LODS];
+ S32 mAccessCount[NUM_LODS];
+};
+
+class LLVolumeMgr
+{
+public:
+ LLVolumeMgr();
+ virtual ~LLVolumeMgr();
+ bool cleanup(); // Cleanup all volumes being managed, returns true if no dangling references
+
+ virtual LLVolumeLODGroup* getGroup( const LLVolumeParams& volume_params ) const;
+
+ // whatever calls getVolume() never owns the LLVolume* and
+ // cannot keep references for long since it may be deleted
+ // later. For best results hold it in an LLPointer<LLVolume>.
+ virtual LLVolume *refVolume(const LLVolumeParams &volume_params, const S32 detail);
+ virtual void unrefVolume(LLVolume *volumep);
+
+ void dump();
+
+ // manually call this for mutex magic
+ void useMutex();
+
+ friend std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr);
+
+protected:
+ void insertGroup(LLVolumeLODGroup* volgroup);
+ // Overridden in llphysics/abstract/utils/llphysicsvolumemanager.h
+ virtual LLVolumeLODGroup* createNewGroup(const LLVolumeParams& volume_params);
+
+protected:
+ typedef std::map<const LLVolumeParams*, LLVolumeLODGroup*, LLVolumeParams::compare> volume_lod_group_map_t;
+ volume_lod_group_map_t mVolumeLODGroups;
+
+ LLMutex* mDataMutex;
+};
+
+#endif // LL_LLVOLUMEMGR_H
diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp index 61455affec..ebac5522e7 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/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h index d65bca5e52..96918912ed 100644 --- a/indra/llmath/llvolumeoctree.h +++ b/indra/llmath/llvolumeoctree.h @@ -1,25 +1,25 @@ -/** +/** * @file llvolumeoctree.h * @brief LLVolume octree classes. * * $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$ */ @@ -38,41 +38,41 @@ class alignas(16) LLVolumeTriangle : public LLRefCount { LL_ALIGN_NEW public: - LLVolumeTriangle() - { - mBinIndex = -1; - } + LLVolumeTriangle() + { + mBinIndex = -1; + } + + LLVolumeTriangle(const LLVolumeTriangle& rhs) + { + *this = rhs; + } - LLVolumeTriangle(const LLVolumeTriangle& rhs) - { - *this = rhs; - } + const LLVolumeTriangle& operator=(const LLVolumeTriangle& rhs) + { + LL_ERRS() << "Illegal operation!" << LL_ENDL; + return *this; + } - const LLVolumeTriangle& operator=(const LLVolumeTriangle& rhs) - { - LL_ERRS() << "Illegal operation!" << LL_ENDL; - return *this; - } + ~LLVolumeTriangle() + { - ~LLVolumeTriangle() - { - - } + } - LL_ALIGN_16(LLVector4a mPositionGroup); + LL_ALIGN_16(LLVector4a mPositionGroup); - const LLVector4a* mV[3]; - U16 mIndex[3]; + const LLVector4a* mV[3]; + U16 mIndex[3]; - F32 mRadius; - mutable S32 mBinIndex; + F32 mRadius; + mutable S32 mBinIndex; - virtual const LLVector4a& getPositionGroup() const; - virtual const F32& getBinRadius() const; - - S32 getBinIndex() const { return mBinIndex; } - void setBinIndex(S32 idx) const { mBinIndex = idx; } + virtual const LLVector4a& getPositionGroup() const; + virtual const F32& getBinRadius() const; + + S32 getBinIndex() const { return mBinIndex; } + void setBinIndex(S32 idx) const { mBinIndex = idx; } }; @@ -82,50 +82,50 @@ class alignas(16) LLVolumeOctreeListener : public LLOctreeListener<LLVolumeTrian LL_ALIGN_NEW public: LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node); - ~LLVolumeOctreeListener(); - - LLVolumeOctreeListener(const LLVolumeOctreeListener& rhs) - { - *this = rhs; - } - - const LLVolumeOctreeListener& operator=(const LLVolumeOctreeListener& rhs) - { - LL_ERRS() << "Illegal operation!" << LL_ENDL; - return *this; - } - - //LISTENER FUNCTIONS + ~LLVolumeOctreeListener(); + + LLVolumeOctreeListener(const LLVolumeOctreeListener& rhs) + { + *this = rhs; + } + + const LLVolumeOctreeListener& operator=(const LLVolumeOctreeListener& rhs) + { + LL_ERRS() << "Illegal operation!" << LL_ENDL; + return *this; + } + + //LISTENER FUNCTIONS virtual void handleChildAddition(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child); - virtual void handleStateChange(const LLTreeNode<LLVolumeTriangle>* node) { } + virtual void handleStateChange(const LLTreeNode<LLVolumeTriangle>* node) { } virtual void handleChildRemoval(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child) { } - virtual void handleInsertion(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { } - virtual void handleRemoval(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { } - virtual void handleDestruction(const LLTreeNode<LLVolumeTriangle>* node) { } - + virtual void handleInsertion(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { } + virtual void handleRemoval(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { } + virtual void handleDestruction(const LLTreeNode<LLVolumeTriangle>* node) { } + public: - LL_ALIGN_16(LLVector4a mBounds[2]); // bounding box (center, size) of this node and all its children (tight fit to objects) - LL_ALIGN_16(LLVector4a mExtents[2]); // extents (min, max) of this node and all its children + LL_ALIGN_16(LLVector4a mBounds[2]); // bounding box (center, size) of this node and all its children (tight fit to objects) + LL_ALIGN_16(LLVector4a mExtents[2]); // extents (min, max) of this node and all its children }; class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*> { public: - const LLVolumeFace* mFace; - LLVector4a mStart; - LLVector4a mDir; - LLVector4a mEnd; - LLVector4a* mIntersection; - LLVector2* mTexCoord; - LLVector4a* mNormal; - LLVector4a* mTangent; - F32* mClosestT; - bool mHitFace; - - LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, - const LLVolumeFace* face, F32* closest_t, - LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent); + const LLVolumeFace* mFace; + LLVector4a mStart; + LLVector4a mDir; + LLVector4a mEnd; + LLVector4a* mIntersection; + LLVector2* mTexCoord; + LLVector4a* mNormal; + LLVector4a* mTangent; + F32* mClosestT; + bool mHitFace; + + LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, + const LLVolumeFace* face, F32* closest_t, + LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent); void traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node); diff --git a/indra/llmath/m3math.cpp b/indra/llmath/m3math.cpp index c7777164c0..e48c47d1ef 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/m3math.h b/indra/llmath/m3math.h index bf38895855..cd14290246 100644 --- a/indra/llmath/m3math.h +++ b/indra/llmath/m3math.h @@ -1,25 +1,25 @@ -/** +/** * @file m3math.h * @brief LLMatrix3 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$ */ @@ -37,139 +37,139 @@ class LLQuaternion; // NOTA BENE: Currently assuming a right-handed, z-up universe -// ji +// ji // LLMatrix3 = | 00 01 02 | -// | 10 11 12 | -// | 20 21 22 | +// | 10 11 12 | +// | 20 21 22 | -// LLMatrix3 = | fx fy fz | forward-axis -// | lx ly lz | left-axis -// | ux uy uz | up-axis +// LLMatrix3 = | fx fy fz | forward-axis +// | lx ly lz | left-axis +// | ux uy uz | up-axis -// NOTE: The world of computer graphics uses column-vectors and matricies that -// "operate to the left". +// NOTE: The world of computer graphics uses column-vectors and matricies that +// "operate to the left". -static const U32 NUM_VALUES_IN_MAT3 = 3; +static const U32 NUM_VALUES_IN_MAT3 = 3; class LLMatrix3 { - public: - F32 mMatrix[NUM_VALUES_IN_MAT3][NUM_VALUES_IN_MAT3]; - - LLMatrix3(void); // Initializes Matrix to identity matrix - explicit LLMatrix3(const F32 *mat); // Initializes Matrix to values in mat - explicit LLMatrix3(const LLQuaternion &q); // Initializes Matrix with rotation q - - LLMatrix3(const F32 angle, const LLVector3 &vec); // Initializes Matrix with axis angle - LLMatrix3(const F32 angle, const LLVector3d &vec); // Initializes Matrix with axis angle - LLMatrix3(const F32 angle, const LLVector4 &vec); // Initializes Matrix with axis angle - LLMatrix3(const F32 roll, const F32 pitch, const F32 yaw); // Initializes Matrix with Euler angles - - ////////////////////////////// - // - // Matrix initializers - these replace any existing values in the matrix - // - - // various useful matrix functions - const LLMatrix3& setIdentity(); // Load identity matrix - const LLMatrix3& clear(); // Clears Matrix to zero - const LLMatrix3& setZero(); // Clears Matrix to zero - - /////////////////////////// - // - // Matrix setters - set some properties without modifying others - // - - // These functions take Rotation arguments - const LLMatrix3& setRot(const F32 angle, const LLVector3 &vec); // Calculate rotation matrix for rotating angle radians about vec - const LLMatrix3& setRot(const F32 roll, const F32 pitch, const F32 yaw); // Calculate rotation matrix from Euler angles - const LLMatrix3& setRot(const LLQuaternion &q); // Transform matrix by Euler angles and translating by pos - - const LLMatrix3& setRows(const LLVector3 &x_axis, const LLVector3 &y_axis, const LLVector3 &z_axis); - const LLMatrix3& setRow( U32 rowIndex, const LLVector3& row ); - const LLMatrix3& setCol( U32 colIndex, const LLVector3& col ); - - - /////////////////////////// - // - // Get properties of a matrix - // - LLQuaternion quaternion() const; // Returns quaternion from mat - void getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const; // Returns Euler angles, in radians - - // Axis extraction routines - LLVector3 getFwdRow() const; - LLVector3 getLeftRow() const; - LLVector3 getUpRow() const; - F32 determinant() const; // Return determinant - - - /////////////////////////// - // - // Operations on an existing matrix - // - const LLMatrix3& transpose(); // Transpose MAT4 - const LLMatrix3& orthogonalize(); // Orthogonalizes X, then Y, then Z - void invert(); // Invert MAT4 - const LLMatrix3& adjointTranspose();// returns transpose of matrix adjoint, for multiplying normals - - - // Rotate existing matrix - // Note: the two lines below are equivalent: - // foo.rotate(bar) - // foo = foo * bar - // That is, foo.rotate(bar) multiplies foo by bar FROM THE RIGHT - const LLMatrix3& rotate(const F32 angle, const F32 x, const F32 y, const F32 z); // Rotate matrix by rotating angle radians about (x, y, z) - const LLMatrix3& rotate(const F32 angle, const LLVector3 &vec); // Rotate matrix by rotating angle radians about vec - const LLMatrix3& rotate(const F32 roll, const F32 pitch, const F32 yaw); // Rotate matrix by roll (about x), pitch (about y), and yaw (about z) - const LLMatrix3& rotate(const LLQuaternion &q); // Transform matrix by Euler angles and translating by pos - - void add(const LLMatrix3& other_matrix); // add other_matrix to this one + public: + F32 mMatrix[NUM_VALUES_IN_MAT3][NUM_VALUES_IN_MAT3]; + + LLMatrix3(void); // Initializes Matrix to identity matrix + explicit LLMatrix3(const F32 *mat); // Initializes Matrix to values in mat + explicit LLMatrix3(const LLQuaternion &q); // Initializes Matrix with rotation q + + LLMatrix3(const F32 angle, const LLVector3 &vec); // Initializes Matrix with axis angle + LLMatrix3(const F32 angle, const LLVector3d &vec); // Initializes Matrix with axis angle + LLMatrix3(const F32 angle, const LLVector4 &vec); // Initializes Matrix with axis angle + LLMatrix3(const F32 roll, const F32 pitch, const F32 yaw); // Initializes Matrix with Euler angles + + ////////////////////////////// + // + // Matrix initializers - these replace any existing values in the matrix + // + + // various useful matrix functions + const LLMatrix3& setIdentity(); // Load identity matrix + const LLMatrix3& clear(); // Clears Matrix to zero + const LLMatrix3& setZero(); // Clears Matrix to zero + + /////////////////////////// + // + // Matrix setters - set some properties without modifying others + // + + // These functions take Rotation arguments + const LLMatrix3& setRot(const F32 angle, const LLVector3 &vec); // Calculate rotation matrix for rotating angle radians about vec + const LLMatrix3& setRot(const F32 roll, const F32 pitch, const F32 yaw); // Calculate rotation matrix from Euler angles + const LLMatrix3& setRot(const LLQuaternion &q); // Transform matrix by Euler angles and translating by pos + + const LLMatrix3& setRows(const LLVector3 &x_axis, const LLVector3 &y_axis, const LLVector3 &z_axis); + const LLMatrix3& setRow( U32 rowIndex, const LLVector3& row ); + const LLMatrix3& setCol( U32 colIndex, const LLVector3& col ); + + + /////////////////////////// + // + // Get properties of a matrix + // + LLQuaternion quaternion() const; // Returns quaternion from mat + void getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const; // Returns Euler angles, in radians + + // Axis extraction routines + LLVector3 getFwdRow() const; + LLVector3 getLeftRow() const; + LLVector3 getUpRow() const; + F32 determinant() const; // Return determinant + + + /////////////////////////// + // + // Operations on an existing matrix + // + const LLMatrix3& transpose(); // Transpose MAT4 + const LLMatrix3& orthogonalize(); // Orthogonalizes X, then Y, then Z + void invert(); // Invert MAT4 + const LLMatrix3& adjointTranspose();// returns transpose of matrix adjoint, for multiplying normals + + + // Rotate existing matrix + // Note: the two lines below are equivalent: + // foo.rotate(bar) + // foo = foo * bar + // That is, foo.rotate(bar) multiplies foo by bar FROM THE RIGHT + const LLMatrix3& rotate(const F32 angle, const F32 x, const F32 y, const F32 z); // Rotate matrix by rotating angle radians about (x, y, z) + const LLMatrix3& rotate(const F32 angle, const LLVector3 &vec); // Rotate matrix by rotating angle radians about vec + const LLMatrix3& rotate(const F32 roll, const F32 pitch, const F32 yaw); // Rotate matrix by roll (about x), pitch (about y), and yaw (about z) + const LLMatrix3& rotate(const LLQuaternion &q); // Transform matrix by Euler angles and translating by pos + + void add(const LLMatrix3& other_matrix); // add other_matrix to this one // This operator is misleading as to operation direction -// friend LLVector3 operator*(const LLMatrix3 &a, const LLVector3 &b); // Apply rotation a to vector b +// friend LLVector3 operator*(const LLMatrix3 &a, const LLVector3 &b); // Apply rotation a to vector b - friend LLVector3 operator*(const LLVector3 &a, const LLMatrix3 &b); // Apply rotation b to vector a - friend LLVector3d operator*(const LLVector3d &a, const LLMatrix3 &b); // Apply rotation b to vector a - friend LLMatrix3 operator*(const LLMatrix3 &a, const LLMatrix3 &b); // Return a * b + friend LLVector3 operator*(const LLVector3 &a, const LLMatrix3 &b); // Apply rotation b to vector a + friend LLVector3d operator*(const LLVector3d &a, const LLMatrix3 &b); // Apply rotation b to vector a + friend LLMatrix3 operator*(const LLMatrix3 &a, const LLMatrix3 &b); // Return a * b - friend bool operator==(const LLMatrix3 &a, const LLMatrix3 &b); // Return a == b - friend bool operator!=(const LLMatrix3 &a, const LLMatrix3 &b); // Return a != b + friend bool operator==(const LLMatrix3 &a, const LLMatrix3 &b); // Return a == b + friend bool operator!=(const LLMatrix3 &a, const LLMatrix3 &b); // Return a != b - friend const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b); // Return a * b - friend const LLMatrix3& operator*=(LLMatrix3 &a, F32 scalar ); // Return a * scalar + friend const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b); // Return a * b + friend const LLMatrix3& operator*=(LLMatrix3 &a, F32 scalar ); // Return a * scalar - friend std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a); // Stream a + friend std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a); // Stream a }; inline LLMatrix3::LLMatrix3(void) { - mMatrix[0][0] = 1.f; - mMatrix[0][1] = 0.f; - mMatrix[0][2] = 0.f; + 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[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; + mMatrix[2][0] = 0.f; + mMatrix[2][1] = 0.f; + mMatrix[2][2] = 1.f; } inline LLMatrix3::LLMatrix3(const F32 *mat) { - mMatrix[0][0] = mat[0]; - mMatrix[0][1] = mat[1]; - mMatrix[0][2] = mat[2]; + mMatrix[0][0] = mat[0]; + mMatrix[0][1] = mat[1]; + mMatrix[0][2] = mat[2]; - mMatrix[1][0] = mat[3]; - mMatrix[1][1] = mat[4]; - mMatrix[1][2] = mat[5]; + mMatrix[1][0] = mat[3]; + mMatrix[1][1] = mat[4]; + mMatrix[1][2] = mat[5]; - mMatrix[2][0] = mat[6]; - mMatrix[2][1] = mat[7]; - mMatrix[2][2] = mat[8]; + mMatrix[2][0] = mat[6]; + mMatrix[2][1] = mat[7]; + mMatrix[2][2] = mat[8]; } @@ -187,7 +187,7 @@ inline LLMatrix3::LLMatrix3(const F32 *mat) // Creating Rotation Matricies From Object Axes // -------------------------------------------- // Suppose you know the three axes of some object in some "absolute-frame". -// If you take those three vectors and throw them into the rows of +// If you take those three vectors and throw them into the rows of // a rotation matrix what do you get? // // R = | X0 X1 X2 | @@ -198,11 +198,11 @@ inline LLMatrix3::LLMatrix3(const F32 *mat) // // Transpose the matrix and have it operate on a vector... // -// V * R_transpose = [ V0 V1 V2 ] * | X0 Y0 Z0 | -// | X1 Y1 Z1 | +// V * R_transpose = [ V0 V1 V2 ] * | X0 Y0 Z0 | +// | X1 Y1 Z1 | // | X2 Y2 Z2 | -// -// = [ V*X V*Y V*Z ] +// +// = [ V*X V*Y V*Z ] // // = components of V that are parallel to the three object axes // diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp index ee4f607442..976535dd84 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/m4math.h b/indra/llmath/m4math.h index b9da970cde..b0f8c90cdf 100644 --- a/indra/llmath/m4math.h +++ b/indra/llmath/m4math.h @@ -1,25 +1,25 @@ -/** +/** * @file m4math.h * @brief LLMatrix4 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$ */ @@ -58,16 +58,16 @@ class LLMatrix4a; // One way to think about it is a matrix that takes the origin frame A // and rotates it into B': i.e. A*M = B // -// Vectors: -// f - forward axis of B expressed in A -// l - left axis of B expressed in A -// u - up axis of B expressed in A +// Vectors: +// f - forward axis of B expressed in A +// l - left axis of B expressed in A +// u - up axis of B expressed in A // -// | 0: fx 1: fy 2: fz 3:0 | +// | 0: fx 1: fy 2: fz 3:0 | // M = | 4: lx 5: ly 6: lz 7:0 | // | 8: ux 9: uy 10: uz 11:0 | // | 12: 0 13: 0 14: 0 15:1 | -// +// // // // @@ -76,18 +76,18 @@ class LLMatrix4a; // // so p*M = p' // -// Vectors: +// Vectors: // f - forward of frame B in frame A // l - left of frame B in frame A // u - up of frame B in frame A // o - origin of frame frame B in frame A // -// | 0: fx 1: lx 2: ux 3:0 | +// | 0: fx 1: lx 2: ux 3:0 | // M = | 4: fy 5: ly 6: uy 7:0 | // | 8: fz 9: lz 10: uz 11:0 | // | 12:-of 13:-ol 14:-ou 15:1 | // -// of, ol, and ou mean the component of the "global" origin o in the f axis, l axis, and u axis. +// of, ol, and ou mean the component of the "global" origin o in the f axis, l axis, and u axis. // static const U32 NUM_VALUES_IN_MAT4 = 4; @@ -95,283 +95,283 @@ static const U32 NUM_VALUES_IN_MAT4 = 4; class LLMatrix4 { public: - F32 mMatrix[NUM_VALUES_IN_MAT4][NUM_VALUES_IN_MAT4]; - - // Initializes Matrix to identity matrix - LLMatrix4() - { - setIdentity(); - } - explicit LLMatrix4(const F32 *mat); // Initializes Matrix to values in mat - explicit LLMatrix4(const LLMatrix3 &mat); // Initializes Matrix to values in mat and sets position to (0,0,0) - explicit LLMatrix4(const LLQuaternion &q); // Initializes Matrix with rotation q and sets position to (0,0,0) + F32 mMatrix[NUM_VALUES_IN_MAT4][NUM_VALUES_IN_MAT4]; + + // Initializes Matrix to identity matrix + LLMatrix4() + { + setIdentity(); + } + explicit LLMatrix4(const F32 *mat); // Initializes Matrix to values in mat + explicit LLMatrix4(const LLMatrix3 &mat); // Initializes Matrix to values in mat and sets position to (0,0,0) + explicit LLMatrix4(const LLQuaternion &q); // Initializes Matrix with rotation q and sets position to (0,0,0) explicit LLMatrix4(const LLMatrix4a& mat); - LLMatrix4(const LLMatrix3 &mat, const LLVector4 &pos); // Initializes Matrix to values in mat and pos + LLMatrix4(const LLMatrix3 &mat, const LLVector4 &pos); // Initializes Matrix to values in mat and pos + + // These are really, really, inefficient as implemented! - djs + LLMatrix4(const LLQuaternion &q, const LLVector4 &pos); // Initializes Matrix with rotation q and position pos + LLMatrix4(F32 angle, + const LLVector4 &vec, + const LLVector4 &pos); // Initializes Matrix with axis-angle and position + LLMatrix4(F32 angle, const LLVector4 &vec); // Initializes Matrix with axis-angle and sets position to (0,0,0) + LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw, + const LLVector4 &pos); // Initializes Matrix with Euler angles + LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw); // Initializes Matrix with Euler angles + + ~LLMatrix4(void); // Destructor + + LLSD getValue() const; + void setValue(const LLSD&); - // These are really, really, inefficient as implemented! - djs - LLMatrix4(const LLQuaternion &q, const LLVector4 &pos); // Initializes Matrix with rotation q and position pos - LLMatrix4(F32 angle, - const LLVector4 &vec, - const LLVector4 &pos); // Initializes Matrix with axis-angle and position - LLMatrix4(F32 angle, const LLVector4 &vec); // Initializes Matrix with axis-angle and sets position to (0,0,0) - LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw, - const LLVector4 &pos); // Initializes Matrix with Euler angles - LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw); // Initializes Matrix with Euler angles + ////////////////////////////// + // + // Matrix initializers - these replace any existing values in the matrix + // - ~LLMatrix4(void); // Destructor + void initRows(const LLVector4 &row0, + const LLVector4 &row1, + const LLVector4 &row2, + const LLVector4 &row3); - LLSD getValue() const; - void setValue(const LLSD&); + // various useful matrix functions + const LLMatrix4& setIdentity(); // Load identity matrix + bool isIdentity() const; + const LLMatrix4& setZero(); // Clears matrix to all zeros. - ////////////////////////////// - // - // Matrix initializers - these replace any existing values in the matrix - // + const LLMatrix4& initRotation(const F32 angle, const LLVector4 &axis); // Calculate rotation matrix for rotating angle radians about vec + const LLMatrix4& initRotation(const F32 roll, const F32 pitch, const F32 yaw); // Calculate rotation matrix from Euler angles + const LLMatrix4& initRotation(const LLQuaternion &q); // Set with Quaternion and position - void initRows(const LLVector4 &row0, - const LLVector4 &row1, - const LLVector4 &row2, - const LLVector4 &row3); + // Position Only + const LLMatrix4& initMatrix(const LLMatrix3 &mat); // + const LLMatrix4& initMatrix(const LLMatrix3 &mat, const LLVector4 &translation); - // various useful matrix functions - const LLMatrix4& setIdentity(); // Load identity matrix - bool isIdentity() const; - const LLMatrix4& setZero(); // Clears matrix to all zeros. + // These operation create a matrix that will rotate and translate by the + // specified amounts. + const LLMatrix4& initRotTrans(const F32 angle, const LLVector3 &axis, const LLVector3 &translation); // Rotation from axis angle + translation + const LLMatrix4& initRotTrans(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &pos); // Rotation from Euler + translation + const LLMatrix4& initRotTrans(const LLQuaternion &q, const LLVector4 &pos); // Set with Quaternion and position - const LLMatrix4& initRotation(const F32 angle, const LLVector4 &axis); // Calculate rotation matrix for rotating angle radians about vec - const LLMatrix4& initRotation(const F32 roll, const F32 pitch, const F32 yaw); // Calculate rotation matrix from Euler angles - const LLMatrix4& initRotation(const LLQuaternion &q); // Set with Quaternion and position - - // Position Only - const LLMatrix4& initMatrix(const LLMatrix3 &mat); // - const LLMatrix4& initMatrix(const LLMatrix3 &mat, const LLVector4 &translation); + const LLMatrix4& initScale(const LLVector3 &scale); - // These operation create a matrix that will rotate and translate by the - // specified amounts. - const LLMatrix4& initRotTrans(const F32 angle, const LLVector3 &axis, const LLVector3 &translation); // Rotation from axis angle + translation - const LLMatrix4& initRotTrans(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &pos); // Rotation from Euler + translation - const LLMatrix4& initRotTrans(const LLQuaternion &q, const LLVector4 &pos); // Set with Quaternion and position + // Set all + const LLMatrix4& initAll(const LLVector3 &scale, const LLQuaternion &q, const LLVector3 &pos); - const LLMatrix4& initScale(const LLVector3 &scale); - // Set all - const LLMatrix4& initAll(const LLVector3 &scale, const LLQuaternion &q, const LLVector3 &pos); + /////////////////////////// + // + // Matrix setters - set some properties without modifying others + // + const LLMatrix4& setTranslation(const F32 x, const F32 y, const F32 z); // Sets matrix to translate by (x,y,z) - /////////////////////////// - // - // Matrix setters - set some properties without modifying others - // + void setFwdRow(const LLVector3 &row); + void setLeftRow(const LLVector3 &row); + void setUpRow(const LLVector3 &row); - const LLMatrix4& setTranslation(const F32 x, const F32 y, const F32 z); // Sets matrix to translate by (x,y,z) + void setFwdCol(const LLVector3 &col); + void setLeftCol(const LLVector3 &col); + void setUpCol(const LLVector3 &col); - void setFwdRow(const LLVector3 &row); - void setLeftRow(const LLVector3 &row); - void setUpRow(const LLVector3 &row); + const LLMatrix4& setTranslation(const LLVector4 &translation); + const LLMatrix4& setTranslation(const LLVector3 &translation); - void setFwdCol(const LLVector3 &col); - void setLeftCol(const LLVector3 &col); - void setUpCol(const LLVector3 &col); + // Convenience func for simplifying comparison-heavy code by + // intentionally stomping values [-FLT_EPS,FLT_EPS] to 0.0 + // + void condition(void); - const LLMatrix4& setTranslation(const LLVector4 &translation); - const LLMatrix4& setTranslation(const LLVector3 &translation); + /////////////////////////// + // + // Get properties of a matrix + // - // Convenience func for simplifying comparison-heavy code by - // intentionally stomping values [-FLT_EPS,FLT_EPS] to 0.0 - // - void condition(void); + F32 determinant(void) const; // Return determinant + LLQuaternion quaternion(void) const; // Returns quaternion - /////////////////////////// - // - // Get properties of a matrix - // + LLVector4 getFwdRow4() const; + LLVector4 getLeftRow4() const; + LLVector4 getUpRow4() const; - F32 determinant(void) const; // Return determinant - LLQuaternion quaternion(void) const; // Returns quaternion + LLMatrix3 getMat3() const; - LLVector4 getFwdRow4() const; - LLVector4 getLeftRow4() const; - LLVector4 getUpRow4() const; + const LLVector3& getTranslation() const { return *(LLVector3*)&mMatrix[3][0]; } - LLMatrix3 getMat3() const; + /////////////////////////// + // + // Operations on an existing matrix + // - const LLVector3& getTranslation() const { return *(LLVector3*)&mMatrix[3][0]; } + const LLMatrix4& transpose(); // Transpose LLMatrix4 + const LLMatrix4& invert(); // Invert LLMatrix4 - /////////////////////////// - // - // Operations on an existing matrix - // + // Rotate existing matrix + // These are really, really, inefficient as implemented! - djs + const LLMatrix4& rotate(const F32 angle, const LLVector4 &vec); // Rotate matrix by rotating angle radians about vec + const LLMatrix4& rotate(const F32 roll, const F32 pitch, const F32 yaw); // Rotate matrix by Euler angles + const LLMatrix4& rotate(const LLQuaternion &q); // Rotate matrix by Quaternion - const LLMatrix4& transpose(); // Transpose LLMatrix4 - const LLMatrix4& invert(); // Invert LLMatrix4 - // Rotate existing matrix - // These are really, really, inefficient as implemented! - djs - const LLMatrix4& rotate(const F32 angle, const LLVector4 &vec); // Rotate matrix by rotating angle radians about vec - const LLMatrix4& rotate(const F32 roll, const F32 pitch, const F32 yaw); // Rotate matrix by Euler angles - const LLMatrix4& rotate(const LLQuaternion &q); // Rotate matrix by Quaternion + // Translate existing matrix + const LLMatrix4& translate(const LLVector3 &vec); // Translate matrix by (vec[VX], vec[VY], vec[VZ]) - - // Translate existing matrix - const LLMatrix4& translate(const LLVector3 &vec); // Translate matrix by (vec[VX], vec[VY], vec[VZ]) - - /////////////////////// - // - // Operators - // + /////////////////////// + // + // Operators + // - // friend inline LLMatrix4 operator*(const LLMatrix4 &a, const LLMatrix4 &b); // Return a * b - friend LLVector4 operator*(const LLVector4 &a, const LLMatrix4 &b); // Return transform of vector a by matrix b - friend const LLVector3 operator*(const LLVector3 &a, const LLMatrix4 &b); // Return full transform of a by matrix b - friend LLVector4 rotate_vector(const LLVector4 &a, const LLMatrix4 &b); // Rotates a but does not translate - friend LLVector3 rotate_vector(const LLVector3 &a, const LLMatrix4 &b); // Rotates a but does not translate + // friend inline LLMatrix4 operator*(const LLMatrix4 &a, const LLMatrix4 &b); // Return a * b + friend LLVector4 operator*(const LLVector4 &a, const LLMatrix4 &b); // Return transform of vector a by matrix b + friend const LLVector3 operator*(const LLVector3 &a, const LLMatrix4 &b); // Return full transform of a by matrix b + friend LLVector4 rotate_vector(const LLVector4 &a, const LLMatrix4 &b); // Rotates a but does not translate + friend LLVector3 rotate_vector(const LLVector3 &a, const LLMatrix4 &b); // Rotates a but does not translate - friend bool operator==(const LLMatrix4 &a, const LLMatrix4 &b); // Return a == b - friend bool operator!=(const LLMatrix4 &a, const LLMatrix4 &b); // Return a != b - friend bool operator<(const LLMatrix4 &a, const LLMatrix4& b); // Return a < b + friend bool operator==(const LLMatrix4 &a, const LLMatrix4 &b); // Return a == b + friend bool operator!=(const LLMatrix4 &a, const LLMatrix4 &b); // Return a != b + friend bool operator<(const LLMatrix4 &a, const LLMatrix4& b); // Return a < b - friend const LLMatrix4& operator+=(LLMatrix4 &a, const LLMatrix4 &b); // Return a + b - friend const LLMatrix4& operator-=(LLMatrix4 &a, const LLMatrix4 &b); // Return a - b - friend const LLMatrix4& operator*=(LLMatrix4 &a, const LLMatrix4 &b); // Return a * b - friend const LLMatrix4& operator*=(LLMatrix4 &a, const F32 &b); // Return a * b + friend const LLMatrix4& operator+=(LLMatrix4 &a, const LLMatrix4 &b); // Return a + b + friend const LLMatrix4& operator-=(LLMatrix4 &a, const LLMatrix4 &b); // Return a - b + friend const LLMatrix4& operator*=(LLMatrix4 &a, const LLMatrix4 &b); // Return a * b + friend const LLMatrix4& operator*=(LLMatrix4 &a, const F32 &b); // Return a * b - friend std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a); // Stream a + friend std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a); // Stream a }; -inline const LLMatrix4& LLMatrix4::setIdentity() +inline const LLMatrix4& LLMatrix4::setIdentity() { - mMatrix[0][0] = 1.f; - mMatrix[0][1] = 0.f; - mMatrix[0][2] = 0.f; - mMatrix[0][3] = 0.f; - - mMatrix[1][0] = 0.f; - mMatrix[1][1] = 1.f; - mMatrix[1][2] = 0.f; - mMatrix[1][3] = 0.f; - - mMatrix[2][0] = 0.f; - mMatrix[2][1] = 0.f; - mMatrix[2][2] = 1.f; - 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); + mMatrix[0][0] = 1.f; + mMatrix[0][1] = 0.f; + mMatrix[0][2] = 0.f; + mMatrix[0][3] = 0.f; + + mMatrix[1][0] = 0.f; + mMatrix[1][1] = 1.f; + mMatrix[1][2] = 0.f; + mMatrix[1][3] = 0.f; + + mMatrix[2][0] = 0.f; + mMatrix[2][1] = 0.f; + mMatrix[2][2] = 1.f; + 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); } inline bool LLMatrix4::isIdentity() const { - return - mMatrix[0][0] == 1.f && - mMatrix[0][1] == 0.f && - mMatrix[0][2] == 0.f && - mMatrix[0][3] == 0.f && - - mMatrix[1][0] == 0.f && - mMatrix[1][1] == 1.f && - mMatrix[1][2] == 0.f && - mMatrix[1][3] == 0.f && - - mMatrix[2][0] == 0.f && - mMatrix[2][1] == 0.f && - mMatrix[2][2] == 1.f && - 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 + mMatrix[0][0] == 1.f && + mMatrix[0][1] == 0.f && + mMatrix[0][2] == 0.f && + mMatrix[0][3] == 0.f && + + mMatrix[1][0] == 0.f && + mMatrix[1][1] == 1.f && + mMatrix[1][2] == 0.f && + mMatrix[1][3] == 0.f && + + mMatrix[2][0] == 0.f && + mMatrix[2][1] == 0.f && + mMatrix[2][2] == 1.f && + 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; } /* inline LLMatrix4 operator*(const LLMatrix4 &a, const LLMatrix4 &b) { - U32 i, j; - LLMatrix4 mat; - for (i = 0; i < NUM_VALUES_IN_MAT4; i++) - { - for (j = 0; j < NUM_VALUES_IN_MAT4; 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.mMatrix[j][3] * b.mMatrix[3][i]; - } - } - return mat; + U32 i, j; + LLMatrix4 mat; + for (i = 0; i < NUM_VALUES_IN_MAT4; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT4; 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.mMatrix[j][3] * b.mMatrix[3][i]; + } + } + return mat; } */ inline const LLMatrix4& operator*=(LLMatrix4 &a, const LLMatrix4 &b) { - U32 i, j; - LLMatrix4 mat; - for (i = 0; i < NUM_VALUES_IN_MAT4; i++) - { - for (j = 0; j < NUM_VALUES_IN_MAT4; 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.mMatrix[j][3] * b.mMatrix[3][i]; - } - } - a = mat; - return a; + U32 i, j; + LLMatrix4 mat; + for (i = 0; i < NUM_VALUES_IN_MAT4; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT4; 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.mMatrix[j][3] * b.mMatrix[3][i]; + } + } + a = mat; + return a; } inline const LLMatrix4& operator*=(LLMatrix4 &a, const F32 &b) { - U32 i, j; - LLMatrix4 mat; - for (i = 0; i < NUM_VALUES_IN_MAT4; i++) - { - for (j = 0; j < NUM_VALUES_IN_MAT4; j++) - { - mat.mMatrix[j][i] = a.mMatrix[j][i] * b; - } - } - a = mat; - return a; + U32 i, j; + LLMatrix4 mat; + for (i = 0; i < NUM_VALUES_IN_MAT4; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT4; j++) + { + mat.mMatrix[j][i] = a.mMatrix[j][i] * b; + } + } + a = mat; + return a; } inline const LLMatrix4& operator+=(LLMatrix4 &a, const LLMatrix4 &b) { - LLMatrix4 mat; - U32 i, j; - for (i = 0; i < NUM_VALUES_IN_MAT4; i++) - { - for (j = 0; j < NUM_VALUES_IN_MAT4; j++) - { - mat.mMatrix[j][i] = a.mMatrix[j][i] + b.mMatrix[j][i]; - } - } - a = mat; - return a; + LLMatrix4 mat; + U32 i, j; + for (i = 0; i < NUM_VALUES_IN_MAT4; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT4; j++) + { + mat.mMatrix[j][i] = a.mMatrix[j][i] + b.mMatrix[j][i]; + } + } + a = mat; + return a; } inline const LLMatrix4& operator-=(LLMatrix4 &a, const LLMatrix4 &b) { - LLMatrix4 mat; - U32 i, j; - for (i = 0; i < NUM_VALUES_IN_MAT4; i++) - { - for (j = 0; j < NUM_VALUES_IN_MAT4; j++) - { - mat.mMatrix[j][i] = a.mMatrix[j][i] - b.mMatrix[j][i]; - } - } - a = mat; - return a; + LLMatrix4 mat; + U32 i, j; + for (i = 0; i < NUM_VALUES_IN_MAT4; i++) + { + for (j = 0; j < NUM_VALUES_IN_MAT4; j++) + { + mat.mMatrix[j][i] = a.mMatrix[j][i] - b.mMatrix[j][i]; + } + } + a = mat; + return a; } // Operates "to the left" on row-vector a @@ -380,24 +380,24 @@ inline const LLMatrix4& operator-=(LLMatrix4 &a, const LLMatrix4 &b) // due to software skinning in LLViewerJointMesh::updateGeometry(). JC inline const LLVector3 operator*(const LLVector3 &a, const LLMatrix4 &b) { - // This is better than making a temporary LLVector3. This eliminates an - // unnecessary LLVector3() constructor and also helps the compiler to - // realize that the output floats do not alias the input floats, hence - // eliminating redundant loads of a.mV[0], etc. JC - return LLVector3(a.mV[VX] * b.mMatrix[VX][VX] + - a.mV[VY] * b.mMatrix[VY][VX] + - a.mV[VZ] * b.mMatrix[VZ][VX] + - 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] + - 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] + - b.mMatrix[VW][VZ]); + // This is better than making a temporary LLVector3. This eliminates an + // unnecessary LLVector3() constructor and also helps the compiler to + // realize that the output floats do not alias the input floats, hence + // eliminating redundant loads of a.mV[0], etc. JC + return LLVector3(a.mV[VX] * b.mMatrix[VX][VX] + + a.mV[VY] * b.mMatrix[VY][VX] + + a.mV[VZ] * b.mMatrix[VZ][VX] + + 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] + + 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] + + b.mMatrix[VW][VZ]); } #endif diff --git a/indra/llmath/raytrace.cpp b/indra/llmath/raytrace.cpp index 117ba2369e..6bdb1280ba 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 2352790b1f..8892347f9b 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/alignment_test.cpp b/indra/llmath/tests/alignment_test.cpp index 5ee3c45502..eb6fa4a3b8 100644 --- a/indra/llmath/tests/alignment_test.cpp +++ b/indra/llmath/tests/alignment_test.cpp @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2011&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2011, 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$ */ @@ -50,27 +50,27 @@ LL_ALIGN_PREFIX(16) class MyVector4a { public: - void* operator new(size_t size) - { - return ll_aligned_malloc_16(size); - } - - void operator delete(void *p) - { - ll_aligned_free_16(p); - } - - void* operator new[](size_t count) - { // try to allocate count bytes for an array - return ll_aligned_malloc_16(count); - } - - void operator delete[](void *p) - { - ll_aligned_free_16(p); - } - - LLQuad mQ; + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void *p) + { + ll_aligned_free_16(p); + } + + void* operator new[](size_t count) + { // try to allocate count bytes for an array + return ll_aligned_malloc_16(count); + } + + void operator delete[](void *p) + { + ll_aligned_free_16(p); + } + + LLQuad mQ; } LL_ALIGN_POSTFIX(16); @@ -79,36 +79,36 @@ template<> template<> void alignment_test_object_t::test<1>() { # ifdef LL_DEBUG -// skip("This test fails on Windows when compiled in debug mode."); +// skip("This test fails on Windows when compiled in debug mode."); # endif - - const int num_tests = 7; - void *align_ptr; - for (int i=0; i<num_tests; i++) - { - align_ptr = ll_aligned_malloc_16(sizeof(MyVector4a)); - ensure("ll_aligned_malloc_16 failed", is_aligned(align_ptr,16)); - - align_ptr = ll_aligned_realloc_16(align_ptr,2*sizeof(MyVector4a), sizeof(MyVector4a)); - ensure("ll_aligned_realloc_16 failed", is_aligned(align_ptr,16)); - - ll_aligned_free_16(align_ptr); - - align_ptr = ll_aligned_malloc_32(sizeof(MyVector4a)); - ensure("ll_aligned_malloc_32 failed", is_aligned(align_ptr,32)); - ll_aligned_free_32(align_ptr); - } + + const int num_tests = 7; + void *align_ptr; + for (int i=0; i<num_tests; i++) + { + align_ptr = ll_aligned_malloc_16(sizeof(MyVector4a)); + ensure("ll_aligned_malloc_16 failed", is_aligned(align_ptr,16)); + + align_ptr = ll_aligned_realloc_16(align_ptr,2*sizeof(MyVector4a), sizeof(MyVector4a)); + ensure("ll_aligned_realloc_16 failed", is_aligned(align_ptr,16)); + + ll_aligned_free_16(align_ptr); + + align_ptr = ll_aligned_malloc_32(sizeof(MyVector4a)); + ensure("ll_aligned_malloc_32 failed", is_aligned(align_ptr,32)); + ll_aligned_free_32(align_ptr); + } } // In-place allocation of objects and arrays. template<> template<> void alignment_test_object_t::test<2>() { - MyVector4a vec1; - ensure("LLAlignment vec1 unaligned", is_aligned(&vec1,16)); - - MyVector4a veca[12]; - ensure("LLAlignment veca unaligned", is_aligned(veca,16)); + MyVector4a vec1; + ensure("LLAlignment vec1 unaligned", is_aligned(&vec1,16)); + + MyVector4a veca[12]; + ensure("LLAlignment veca unaligned", is_aligned(veca,16)); } // Heap allocation of objects and arrays. @@ -116,26 +116,26 @@ template<> template<> void alignment_test_object_t::test<3>() { # ifdef LL_DEBUG -// skip("This test fails on Windows when compiled in debug mode."); +// skip("This test fails on Windows when compiled in debug mode."); # endif - - const int ARR_SIZE = 7; - for(int i=0; i<ARR_SIZE; i++) - { - MyVector4a *vecp = new MyVector4a; - ensure("LLAlignment vecp unaligned", is_aligned(vecp,16)); - delete vecp; - } - - MyVector4a *veca = new MyVector4a[ARR_SIZE]; - //std::cout << "veca base is " << (S32) veca << std::endl; - ensure("LLAligment veca base", is_aligned(veca,16)); - for(int i=0; i<ARR_SIZE; i++) - { - std::cout << "veca[" << i << "]" << std::endl; - ensure("LLAlignment veca member unaligned", is_aligned(&veca[i],16)); - } - delete [] veca; + + const int ARR_SIZE = 7; + for(int i=0; i<ARR_SIZE; i++) + { + MyVector4a *vecp = new MyVector4a; + ensure("LLAlignment vecp unaligned", is_aligned(vecp,16)); + delete vecp; + } + + MyVector4a *veca = new MyVector4a[ARR_SIZE]; + //std::cout << "veca base is " << (S32) veca << std::endl; + ensure("LLAligment veca base", is_aligned(veca,16)); + for(int i=0; i<ARR_SIZE; i++) + { + std::cout << "veca[" << i << "]" << std::endl; + ensure("LLAlignment veca member unaligned", is_aligned(&veca[i],16)); + } + delete [] veca; } } diff --git a/indra/llmath/tests/llbbox_test.cpp b/indra/llmath/tests/llbbox_test.cpp index 373e2d02a0..0847420a3e 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/llbboxlocal_test.cpp b/indra/llmath/tests/llbboxlocal_test.cpp index f31e4126c4..120e18cadf 100644 --- a/indra/llmath/tests/llbboxlocal_test.cpp +++ b/indra/llmath/tests/llbboxlocal_test.cpp @@ -3,25 +3,25 @@ * @author Martin Reddy * @date 2009-06-25 * @brief Test for llbboxlocal.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$ */ @@ -32,201 +32,201 @@ namespace tut { - struct LLBBoxLocalData - { - }; + struct LLBBoxLocalData + { + }; - typedef test_group<LLBBoxLocalData> factory; - typedef factory::object object; + typedef test_group<LLBBoxLocalData> factory; + typedef factory::object object; } namespace { - tut::factory llbboxlocal_test_factory("LLBBoxLocal"); + tut::factory llbboxlocal_test_factory("LLBBoxLocal"); } namespace tut { - template<> template<> - void object::test<1>() - { - // - // test the default constructor - // - - LLBBoxLocal bbox1; - - ensure_equals("Default bbox min", bbox1.getMin(), LLVector3(0.0f, 0.0f, 0.0f)); - ensure_equals("Default bbox max", bbox1.getMax(), LLVector3(0.0f, 0.0f, 0.0f)); - } - - template<> template<> - void object::test<2>() - { - // - // test the non-default constructor - // - - LLBBoxLocal bbox2(LLVector3(-1.0f, -2.0f, 0.0f), LLVector3(1.0f, 2.0f, 3.0f)); - - ensure_equals("Custom bbox min", bbox2.getMin(), LLVector3(-1.0f, -2.0f, 0.0f)); - ensure_equals("Custom bbox max", bbox2.getMax(), LLVector3(1.0f, 2.0f, 3.0f)); - } - - template<> template<> - void object::test<3>() - { - // - // test the setMin() - // - // N.B. no validation is currently performed to ensure that the min - // and max vectors are actually the min/max values. - // - - LLBBoxLocal bbox2; - bbox2.setMin(LLVector3(1.0f, 2.0f, 3.0f)); - - ensure_equals("Custom bbox min (2)", bbox2.getMin(), LLVector3(1.0f, 2.0f, 3.0f)); - } - - template<> template<> - void object::test<4>() - { - // - // test the setMax() - // - // N.B. no validation is currently performed to ensure that the min - // and max vectors are actually the min/max values. - // - - LLBBoxLocal bbox2; - bbox2.setMax(LLVector3(10.0f, 20.0f, 30.0f)); - - ensure_equals("Custom bbox max (2)", bbox2.getMax(), LLVector3(10.0f, 20.0f, 30.0f)); - } - - template<> template<> - void object::test<5>() - { - // - // test the getCenter() method - // - - ensure_equals("Default bbox center", LLBBoxLocal().getCenter(), LLVector3(0.0f, 0.0f, 0.0f)); - - LLBBoxLocal bbox1(LLVector3(-1.0f, -1.0f, -1.0f), LLVector3(0.0f, 0.0f, 0.0f)); - - ensure_equals("Custom bbox center", bbox1.getCenter(), LLVector3(-0.5f, -0.5f, -0.5f)); - - LLBBoxLocal bbox2(LLVector3(0.0f, 0.0f, 0.0f), LLVector3(-1.0f, -1.0f, -1.0f)); - - ensure_equals("Invalid bbox center", bbox2.getCenter(), LLVector3(-0.5f, -0.5f, -0.5f)); - } - - template<> template<> - void object::test<6>() - { - // - // test the getExtent() method - // - - LLBBoxLocal bbox2(LLVector3(0.0f, 0.0f, 0.0f), LLVector3(-1.0f, -1.0f, -1.0f)); - - ensure_equals("Default bbox extent", LLBBoxLocal().getExtent(), LLVector3(0.0f, 0.0f, 0.0f)); - - LLBBoxLocal bbox3(LLVector3(-1.0f, -1.0f, -1.0f), LLVector3(1.0f, 2.0f, 0.0f)); - - ensure_equals("Custom bbox extent", bbox3.getExtent(), LLVector3(2.0f, 3.0f, 1.0f)); - } - - template<> template<> - void object::test<7>() - { - // - // test the addPoint() method - // - // N.B. if you create an empty bbox and then add points, - // the vector (0, 0, 0) will always be part of the bbox. - // (Fixing this would require adding a bool to the class size). - // - - LLBBoxLocal bbox1; - bbox1.addPoint(LLVector3(-1.0f, -2.0f, -3.0f)); - bbox1.addPoint(LLVector3(3.0f, 4.0f, 5.0f)); - - ensure_equals("Custom BBox center (1)", bbox1.getCenter(), LLVector3(1.0f, 1.0f, 1.0f)); - ensure_equals("Custom BBox min (1)", bbox1.getMin(), LLVector3(-1.0f, -2.0f, -3.0f)); - ensure_equals("Custom BBox max (1)", bbox1.getMax(), LLVector3(3.0f, 4.0f, 5.0f)); - - bbox1.addPoint(LLVector3(0.0f, 0.0f, 0.0f)); - bbox1.addPoint(LLVector3(1.0f, 2.0f, 3.0f)); - bbox1.addPoint(LLVector3(2.0f, 2.0f, 2.0f)); - - ensure_equals("Custom BBox center (2)", bbox1.getCenter(), LLVector3(1.0f, 1.0f, 1.0f)); - ensure_equals("Custom BBox min (2)", bbox1.getMin(), LLVector3(-1.0f, -2.0f, -3.0f)); - ensure_equals("Custom BBox max (2)", bbox1.getMax(), LLVector3(3.0f, 4.0f, 5.0f)); - - bbox1.addPoint(LLVector3(5.0f, 5.0f, 5.0f)); - - ensure_equals("Custom BBox center (3)", bbox1.getCenter(), LLVector3(2.0f, 1.5f, 1.0f)); - ensure_equals("Custom BBox min (3)", bbox1.getMin(), LLVector3(-1.0f, -2.0f, -3.0f)); - ensure_equals("Custom BBox max (3)", bbox1.getMax(), LLVector3(5.0f, 5.0f, 5.0f)); - } - - template<> template<> - void object::test<8>() - { - // - // test the addBBox() methods - // - // N.B. if you create an empty bbox and then add points, - // the vector (0, 0, 0) will always be part of the bbox. - // (Fixing this would require adding a bool to the class size). - // - - LLBBoxLocal bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLVector3(2.0f, 2.0f, 2.0f)); - bbox2.addBBox(LLBBoxLocal(LLVector3(1.5f, 1.5f, 1.5f), LLVector3(3.0f, 3.0f, 3.0f))); - - ensure_equals("Custom BBox center (4)", bbox2.getCenter(), LLVector3(2.0f, 2.0f, 2.0f)); - ensure_equals("Custom BBox min (4)", bbox2.getMin(), LLVector3(1.0f, 1.0f, 1.0f)); - ensure_equals("Custom BBox max (4)", bbox2.getMax(), LLVector3(3.0f, 3.0f, 3.0f)); - - bbox2.addBBox(LLBBoxLocal(LLVector3(-1.0f, -1.0f, -1.0f), LLVector3(0.0f, 0.0f, 0.0f))); - - ensure_equals("Custom BBox center (5)", bbox2.getCenter(), LLVector3(1.0f, 1.0f, 1.0f)); - ensure_equals("Custom BBox min (5)", bbox2.getMin(), LLVector3(-1.0f, -1.0f, -1.0f)); - ensure_equals("Custom BBox max (5)", bbox2.getMax(), LLVector3(3.0f, 3.0f, 3.0f)); - } - - template<> template<> - void object::test<9>() - { - // - // test the expand() method - // - - LLBBoxLocal bbox1; - bbox1.expand(0.0f); - - ensure_equals("Zero-expanded Default BBox center", bbox1.getCenter(), LLVector3(0.0f, 0.0f, 0.0f)); - - LLBBoxLocal bbox2(LLVector3(1.0f, 2.0f, 3.0f), LLVector3(3.0f, 4.0f, 5.0f)); - bbox2.expand(0.0f); - - ensure_equals("Zero-expanded BBox center", bbox2.getCenter(), LLVector3(2.0f, 3.0f, 4.0f)); - ensure_equals("Zero-expanded BBox min", bbox2.getMin(), LLVector3(1.0f, 2.0f, 3.0f)); - ensure_equals("Zero-expanded BBox max", bbox2.getMax(), LLVector3(3.0f, 4.0f, 5.0f)); - - bbox2.expand(0.5f); - - ensure_equals("Positive-expanded BBox center", bbox2.getCenter(), LLVector3(2.0f, 3.0f, 4.0f)); - ensure_equals("Positive-expanded BBox min", bbox2.getMin(), LLVector3(0.5f, 1.5f, 2.5f)); - ensure_equals("Positive-expanded BBox max", bbox2.getMax(), LLVector3(3.5f, 4.5f, 5.5f)); - - bbox2.expand(-1.0f); - - ensure_equals("Negative-expanded BBox center", bbox2.getCenter(), LLVector3(2.0f, 3.0f, 4.0f)); - ensure_equals("Negative-expanded BBox min", bbox2.getMin(), LLVector3(1.5f, 2.5f, 3.5f)); - ensure_equals("Negative-expanded BBox max", bbox2.getMax(), LLVector3(2.5f, 3.5f, 4.5f)); - } + template<> template<> + void object::test<1>() + { + // + // test the default constructor + // + + LLBBoxLocal bbox1; + + ensure_equals("Default bbox min", bbox1.getMin(), LLVector3(0.0f, 0.0f, 0.0f)); + ensure_equals("Default bbox max", bbox1.getMax(), LLVector3(0.0f, 0.0f, 0.0f)); + } + + template<> template<> + void object::test<2>() + { + // + // test the non-default constructor + // + + LLBBoxLocal bbox2(LLVector3(-1.0f, -2.0f, 0.0f), LLVector3(1.0f, 2.0f, 3.0f)); + + ensure_equals("Custom bbox min", bbox2.getMin(), LLVector3(-1.0f, -2.0f, 0.0f)); + ensure_equals("Custom bbox max", bbox2.getMax(), LLVector3(1.0f, 2.0f, 3.0f)); + } + + template<> template<> + void object::test<3>() + { + // + // test the setMin() + // + // N.B. no validation is currently performed to ensure that the min + // and max vectors are actually the min/max values. + // + + LLBBoxLocal bbox2; + bbox2.setMin(LLVector3(1.0f, 2.0f, 3.0f)); + + ensure_equals("Custom bbox min (2)", bbox2.getMin(), LLVector3(1.0f, 2.0f, 3.0f)); + } + + template<> template<> + void object::test<4>() + { + // + // test the setMax() + // + // N.B. no validation is currently performed to ensure that the min + // and max vectors are actually the min/max values. + // + + LLBBoxLocal bbox2; + bbox2.setMax(LLVector3(10.0f, 20.0f, 30.0f)); + + ensure_equals("Custom bbox max (2)", bbox2.getMax(), LLVector3(10.0f, 20.0f, 30.0f)); + } + + template<> template<> + void object::test<5>() + { + // + // test the getCenter() method + // + + ensure_equals("Default bbox center", LLBBoxLocal().getCenter(), LLVector3(0.0f, 0.0f, 0.0f)); + + LLBBoxLocal bbox1(LLVector3(-1.0f, -1.0f, -1.0f), LLVector3(0.0f, 0.0f, 0.0f)); + + ensure_equals("Custom bbox center", bbox1.getCenter(), LLVector3(-0.5f, -0.5f, -0.5f)); + + LLBBoxLocal bbox2(LLVector3(0.0f, 0.0f, 0.0f), LLVector3(-1.0f, -1.0f, -1.0f)); + + ensure_equals("Invalid bbox center", bbox2.getCenter(), LLVector3(-0.5f, -0.5f, -0.5f)); + } + + template<> template<> + void object::test<6>() + { + // + // test the getExtent() method + // + + LLBBoxLocal bbox2(LLVector3(0.0f, 0.0f, 0.0f), LLVector3(-1.0f, -1.0f, -1.0f)); + + ensure_equals("Default bbox extent", LLBBoxLocal().getExtent(), LLVector3(0.0f, 0.0f, 0.0f)); + + LLBBoxLocal bbox3(LLVector3(-1.0f, -1.0f, -1.0f), LLVector3(1.0f, 2.0f, 0.0f)); + + ensure_equals("Custom bbox extent", bbox3.getExtent(), LLVector3(2.0f, 3.0f, 1.0f)); + } + + template<> template<> + void object::test<7>() + { + // + // test the addPoint() method + // + // N.B. if you create an empty bbox and then add points, + // the vector (0, 0, 0) will always be part of the bbox. + // (Fixing this would require adding a bool to the class size). + // + + LLBBoxLocal bbox1; + bbox1.addPoint(LLVector3(-1.0f, -2.0f, -3.0f)); + bbox1.addPoint(LLVector3(3.0f, 4.0f, 5.0f)); + + ensure_equals("Custom BBox center (1)", bbox1.getCenter(), LLVector3(1.0f, 1.0f, 1.0f)); + ensure_equals("Custom BBox min (1)", bbox1.getMin(), LLVector3(-1.0f, -2.0f, -3.0f)); + ensure_equals("Custom BBox max (1)", bbox1.getMax(), LLVector3(3.0f, 4.0f, 5.0f)); + + bbox1.addPoint(LLVector3(0.0f, 0.0f, 0.0f)); + bbox1.addPoint(LLVector3(1.0f, 2.0f, 3.0f)); + bbox1.addPoint(LLVector3(2.0f, 2.0f, 2.0f)); + + ensure_equals("Custom BBox center (2)", bbox1.getCenter(), LLVector3(1.0f, 1.0f, 1.0f)); + ensure_equals("Custom BBox min (2)", bbox1.getMin(), LLVector3(-1.0f, -2.0f, -3.0f)); + ensure_equals("Custom BBox max (2)", bbox1.getMax(), LLVector3(3.0f, 4.0f, 5.0f)); + + bbox1.addPoint(LLVector3(5.0f, 5.0f, 5.0f)); + + ensure_equals("Custom BBox center (3)", bbox1.getCenter(), LLVector3(2.0f, 1.5f, 1.0f)); + ensure_equals("Custom BBox min (3)", bbox1.getMin(), LLVector3(-1.0f, -2.0f, -3.0f)); + ensure_equals("Custom BBox max (3)", bbox1.getMax(), LLVector3(5.0f, 5.0f, 5.0f)); + } + + template<> template<> + void object::test<8>() + { + // + // test the addBBox() methods + // + // N.B. if you create an empty bbox and then add points, + // the vector (0, 0, 0) will always be part of the bbox. + // (Fixing this would require adding a bool to the class size). + // + + LLBBoxLocal bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLVector3(2.0f, 2.0f, 2.0f)); + bbox2.addBBox(LLBBoxLocal(LLVector3(1.5f, 1.5f, 1.5f), LLVector3(3.0f, 3.0f, 3.0f))); + + ensure_equals("Custom BBox center (4)", bbox2.getCenter(), LLVector3(2.0f, 2.0f, 2.0f)); + ensure_equals("Custom BBox min (4)", bbox2.getMin(), LLVector3(1.0f, 1.0f, 1.0f)); + ensure_equals("Custom BBox max (4)", bbox2.getMax(), LLVector3(3.0f, 3.0f, 3.0f)); + + bbox2.addBBox(LLBBoxLocal(LLVector3(-1.0f, -1.0f, -1.0f), LLVector3(0.0f, 0.0f, 0.0f))); + + ensure_equals("Custom BBox center (5)", bbox2.getCenter(), LLVector3(1.0f, 1.0f, 1.0f)); + ensure_equals("Custom BBox min (5)", bbox2.getMin(), LLVector3(-1.0f, -1.0f, -1.0f)); + ensure_equals("Custom BBox max (5)", bbox2.getMax(), LLVector3(3.0f, 3.0f, 3.0f)); + } + + template<> template<> + void object::test<9>() + { + // + // test the expand() method + // + + LLBBoxLocal bbox1; + bbox1.expand(0.0f); + + ensure_equals("Zero-expanded Default BBox center", bbox1.getCenter(), LLVector3(0.0f, 0.0f, 0.0f)); + + LLBBoxLocal bbox2(LLVector3(1.0f, 2.0f, 3.0f), LLVector3(3.0f, 4.0f, 5.0f)); + bbox2.expand(0.0f); + + ensure_equals("Zero-expanded BBox center", bbox2.getCenter(), LLVector3(2.0f, 3.0f, 4.0f)); + ensure_equals("Zero-expanded BBox min", bbox2.getMin(), LLVector3(1.0f, 2.0f, 3.0f)); + ensure_equals("Zero-expanded BBox max", bbox2.getMax(), LLVector3(3.0f, 4.0f, 5.0f)); + + bbox2.expand(0.5f); + + ensure_equals("Positive-expanded BBox center", bbox2.getCenter(), LLVector3(2.0f, 3.0f, 4.0f)); + ensure_equals("Positive-expanded BBox min", bbox2.getMin(), LLVector3(0.5f, 1.5f, 2.5f)); + ensure_equals("Positive-expanded BBox max", bbox2.getMax(), LLVector3(3.5f, 4.5f, 5.5f)); + + bbox2.expand(-1.0f); + + ensure_equals("Negative-expanded BBox center", bbox2.getCenter(), LLVector3(2.0f, 3.0f, 4.0f)); + ensure_equals("Negative-expanded BBox min", bbox2.getMin(), LLVector3(1.5f, 2.5f, 3.5f)); + ensure_equals("Negative-expanded BBox max", bbox2.getMax(), LLVector3(2.5f, 3.5f, 4.5f)); + } } diff --git a/indra/llmath/tests/llmodularmath_test.cpp b/indra/llmath/tests/llmodularmath_test.cpp index 063d3ef79f..399b3af05d 100644 --- a/indra/llmath/tests/llmodularmath_test.cpp +++ b/indra/llmath/tests/llmodularmath_test.cpp @@ -7,25 +7,25 @@ * $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 "../llmodularmath.h" @@ -34,43 +34,43 @@ namespace tut { - struct modularmath_data - { - }; - typedef test_group<modularmath_data> modularmath_test; - typedef modularmath_test::object modularmath_object; - tut::modularmath_test modularmath_testcase("LLModularMath"); + struct modularmath_data + { + }; + typedef test_group<modularmath_data> modularmath_test; + typedef modularmath_test::object modularmath_object; + tut::modularmath_test modularmath_testcase("LLModularMath"); - template<> template<> - void modularmath_object::test<1>() - { - // lhs < rhs - const U32 lhs = 0x000001; - const U32 rhs = 0xFFFFFF; - const U32 width = 24; - U32 result = LLModularMath::subtract<width>(lhs, rhs); - ensure_equals("diff(0x000001, 0xFFFFFF, 24)", result, 2); - } + template<> template<> + void modularmath_object::test<1>() + { + // lhs < rhs + const U32 lhs = 0x000001; + const U32 rhs = 0xFFFFFF; + const U32 width = 24; + U32 result = LLModularMath::subtract<width>(lhs, rhs); + ensure_equals("diff(0x000001, 0xFFFFFF, 24)", result, 2); + } - template<> template<> - void modularmath_object::test<2>() - { - // lhs > rhs - const U32 lhs = 0x000002; - const U32 rhs = 0x000001; - const U32 width = 24; - U32 result = LLModularMath::subtract<width>(lhs, rhs); - ensure_equals("diff(0x000002, 0x000001, 24)", result, 1); - } + template<> template<> + void modularmath_object::test<2>() + { + // lhs > rhs + const U32 lhs = 0x000002; + const U32 rhs = 0x000001; + const U32 width = 24; + U32 result = LLModularMath::subtract<width>(lhs, rhs); + ensure_equals("diff(0x000002, 0x000001, 24)", result, 1); + } - template<> template<> - void modularmath_object::test<3>() - { - // lhs == rhs - const U32 lhs = 0xABCDEF; - const U32 rhs = 0xABCDEF; - const U32 width = 24; - U32 result = LLModularMath::subtract<width>(lhs, rhs); - ensure_equals("diff(0xABCDEF, 0xABCDEF, 24)", result, 0); - } + template<> template<> + void modularmath_object::test<3>() + { + // lhs == rhs + const U32 lhs = 0xABCDEF; + const U32 rhs = 0xABCDEF; + const U32 width = 24; + U32 result = LLModularMath::subtract<width>(lhs, rhs); + ensure_equals("diff(0xABCDEF, 0xABCDEF, 24)", result, 0); + } } diff --git a/indra/llmath/tests/llquaternion_test.cpp b/indra/llmath/tests/llquaternion_test.cpp index 3490829743..aa3c0ad843 100644 --- a/indra/llmath/tests/llquaternion_test.cpp +++ b/indra/llmath/tests/llquaternion_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llquaternion_test.cpp * @author Adroit * @date 2007-03 @@ -7,21 +7,21 @@ * $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$ */ @@ -38,623 +38,623 @@ namespace tut { - struct llquat_test - { - }; - typedef test_group<llquat_test> llquat_test_t; - typedef llquat_test_t::object llquat_test_object_t; - tut::llquat_test_t tut_llquat_test("LLQuaternion"); - - //test case for LLQuaternion::LLQuaternion(void) fn. - template<> template<> - void llquat_test_object_t::test<1>() - { - LLQuaternion llquat; - ensure("LLQuaternion::LLQuaternion() failed", 0.f == llquat.mQ[0] && - 0.f == llquat.mQ[1] && - 0.f == llquat.mQ[2] && - 1.f == llquat.mQ[3]); - } - - //test case for explicit LLQuaternion(const LLMatrix4 &mat) fn. - template<> template<> - void llquat_test_object_t::test<2>() - { - LLMatrix4 llmat; - LLVector4 vector1(2.0f, 1.0f, 3.0f, 6.0f); - LLVector4 vector2(5.0f, 6.0f, 0.0f, 1.0f); - LLVector4 vector3(2.0f, 1.0f, 2.0f, 9.0f); - LLVector4 vector4(3.0f, 8.0f, 1.0f, 5.0f); - - llmat.initRows(vector1, vector2, vector3, vector4); - ensure("explicit LLQuaternion(const LLMatrix4 &mat) failed", 2.0f == llmat.mMatrix[0][0] && - 1.0f == llmat.mMatrix[0][1] && - 3.0f == llmat.mMatrix[0][2] && - 6.0f == llmat.mMatrix[0][3] && - 5.0f == llmat.mMatrix[1][0] && - 6.0f == llmat.mMatrix[1][1] && - 0.0f == llmat.mMatrix[1][2] && - 1.0f == llmat.mMatrix[1][3] && - 2.0f == llmat.mMatrix[2][0] && - 1.0f == llmat.mMatrix[2][1] && - 2.0f == llmat.mMatrix[2][2] && - 9.0f == llmat.mMatrix[2][3] && - 3.0f == llmat.mMatrix[3][0] && - 8.0f == llmat.mMatrix[3][1] && - 1.0f == llmat.mMatrix[3][2] && - 5.0f == llmat.mMatrix[3][3]); - } - - template<> template<> - void llquat_test_object_t::test<3>() - { - LLMatrix3 llmat; - - LLVector3 vect1(3.4028234660000000f , 234.56f, 4234.442234f); - LLVector3 vect2(741.434f, 23.00034f, 6567.223423f); - LLVector3 vect3(566.003034f, 12.98705f, 234.764423f); - llmat.setRows(vect1, vect2, vect3); - - ensure("LLMatrix3::setRows fn failed.", 3.4028234660000000f == llmat.mMatrix[0][0] && - 234.56f == llmat.mMatrix[0][1] && - 4234.442234f == llmat.mMatrix[0][2] && - 741.434f == llmat.mMatrix[1][0] && - 23.00034f == llmat.mMatrix[1][1] && - 6567.223423f == llmat.mMatrix[1][2] && - 566.003034f == llmat.mMatrix[2][0] && - 12.98705f == llmat.mMatrix[2][1] && - 234.764423f == llmat.mMatrix[2][2]); - } - - //test case for LLQuaternion(F32 x, F32 y, F32 z, F32 w), setQuatInit() and normQuat() fns. - template<> template<> - void llquat_test_object_t::test<4>() - { - F32 x_val = 3.0f; - F32 y_val = 2.0f; - F32 z_val = 6.0f; - F32 w_val = 1.0f; - - LLQuaternion res_quat; - res_quat.setQuatInit(x_val, y_val, z_val, w_val); - res_quat.normQuat(); - - ensure("LLQuaternion::normQuat() fn failed", - is_approx_equal(0.42426407f, res_quat.mQ[0]) && - is_approx_equal(0.28284273f, res_quat.mQ[1]) && - is_approx_equal(0.84852815f, res_quat.mQ[2]) && - is_approx_equal(0.14142136f, res_quat.mQ[3])); - - x_val = 0.0f; - y_val = 0.0f; - z_val = 0.0f; - w_val = 0.0f; - - res_quat.setQuatInit(x_val, y_val, z_val, w_val); - res_quat.normQuat(); - - ensure("LLQuaternion::normQuat() fn. failed.", - is_approx_equal(0.0f, res_quat.mQ[0]) && - is_approx_equal(0.0f, res_quat.mQ[1]) && - is_approx_equal(0.0f, res_quat.mQ[2]) && - is_approx_equal(1.0f, res_quat.mQ[3])); - - - ensure("LLQuaternion::normQuat() fn. failed.", - is_approx_equal(0.0f, res_quat.mQ[0]) && - is_approx_equal(0.0f, res_quat.mQ[1]) && - is_approx_equal(0.0f, res_quat.mQ[2]) && - is_approx_equal(1.0f, res_quat.mQ[3])); - } - - //test case for conjQuat() and transQuat() fns. - template<> template<> - void llquat_test_object_t::test<5>() - { - F32 x_val = 3.0f; - F32 y_val = 2.0f; - F32 z_val = 6.0f; - F32 w_val = 1.0f; - - LLQuaternion res_quat; - LLQuaternion result, result1; - result1 = result = res_quat.setQuatInit(x_val, y_val, z_val, w_val); - - result.conjQuat(); - result1.transQuat(); - - ensure("LLQuaternion::conjQuat and LLQuaternion::transQuat failed ", - is_approx_equal(result1.mQ[0], result.mQ[0]) && - is_approx_equal(result1.mQ[1], result.mQ[1]) && - is_approx_equal(result1.mQ[2], result.mQ[2])); - - } - - //test case for dot(const LLQuaternion &a, const LLQuaternion &b) fn. - template<> template<> - void llquat_test_object_t::test<6>() - { - LLQuaternion quat1(3.0f, 2.0f, 6.0f, 0.0f), quat2(1.0f, 1.0f, 1.0f, 1.0f); - ensure("1. The two values are different", ll_round(12.000000f, 2) == ll_round(dot(quat1, quat2), 2)); - - LLQuaternion quat0(3.0f, 9.334f, 34.5f, 23.0f), quat(34.5f, 23.23f, 2.0f, 45.5f); - ensure("2. The two values are different", ll_round(1435.828807f, 2) == ll_round(dot(quat0, quat), 2)); - } - - //test case for LLQuaternion &LLQuaternion::constrain(F32 radians) fn. - template<> template<> - void llquat_test_object_t::test<7>() - { - F32 radian = 60.0f; - LLQuaternion quat(3.0f, 2.0f, 6.0f, 0.0f); - LLQuaternion quat1; - quat1 = quat.constrain(radian); - ensure("1. LLQuaternion::constrain(F32 radians) failed", - is_approx_equal_fraction(-0.423442f, quat1.mQ[0], 8) && - is_approx_equal_fraction(-0.282295f, quat1.mQ[1], 8) && - is_approx_equal_fraction(-0.846884f, quat1.mQ[2], 8) && - is_approx_equal_fraction(0.154251f, quat1.mQ[3], 8)); - - - radian = 30.0f; - LLQuaternion quat0(37.50f, 12.0f, 86.023f, 40.32f); - quat1 = quat0.constrain(radian); - - ensure("2. LLQuaternion::constrain(F32 radians) failed", - is_approx_equal_fraction(37.500000f, quat1.mQ[0], 8) && - is_approx_equal_fraction(12.0000f, quat1.mQ[1], 8) && - is_approx_equal_fraction(86.0230f, quat1.mQ[2], 8) && - is_approx_equal_fraction(40.320000f, quat1.mQ[3], 8)); - } - - template<> template<> - void llquat_test_object_t::test<8>() - { - F32 value1 = 15.0f; - LLQuaternion quat1(1.0f, 2.0f, 4.0f, 1.0f); - LLQuaternion quat2(4.0f, 3.0f, 6.5f, 9.7f); - LLQuaternion res_lerp, res_slerp, res_nlerp; - - //test case for lerp(F32 t, const LLQuaternion &q) fn. - res_lerp = lerp(value1, quat1); - ensure("1. LLQuaternion lerp(F32 t, const LLQuaternion &q) failed", - is_approx_equal_fraction(0.181355f, res_lerp.mQ[0], 16) && - is_approx_equal_fraction(0.362711f, res_lerp.mQ[1], 16) && - is_approx_equal_fraction(0.725423f, res_lerp.mQ[2], 16) && - is_approx_equal_fraction(0.556158f, res_lerp.mQ[3], 16)); - - //test case for lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q) fn. - res_lerp = lerp(value1, quat1, quat2); - ensure("2. LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q) failed", - is_approx_equal_fraction(0.314306f, res_lerp.mQ[0], 16) && - is_approx_equal_fraction(0.116156f, res_lerp.mQ[1], 16) && - is_approx_equal_fraction(0.283559f, res_lerp.mQ[2], 16) && - is_approx_equal_fraction(0.898506f, res_lerp.mQ[3], 16)); - - //test case for slerp( F32 u, const LLQuaternion &a, const LLQuaternion &b ) fn. - res_slerp = slerp(value1, quat1, quat2); - ensure("3. LLQuaternion slerp( F32 u, const LLQuaternion &a, const LLQuaternion &b) failed", - is_approx_equal_fraction(46.000f, res_slerp.mQ[0], 16) && - is_approx_equal_fraction(17.00f, res_slerp.mQ[1], 16) && - is_approx_equal_fraction(41.5f, res_slerp.mQ[2], 16) && - is_approx_equal_fraction(131.5f, res_slerp.mQ[3], 16)); - - //test case for nlerp(F32 t, const LLQuaternion &a, const LLQuaternion &b) fn. - res_nlerp = nlerp(value1, quat1, quat2); - ensure("4. LLQuaternion nlerp(F32 t, const LLQuaternion &a, const LLQuaternion &b) failed", - is_approx_equal_fraction(0.314306f, res_nlerp.mQ[0], 16) && - is_approx_equal_fraction(0.116157f, res_nlerp.mQ[1], 16) && - is_approx_equal_fraction(0.283559f, res_nlerp.mQ[2], 16) && - is_approx_equal_fraction(0.898506f, res_nlerp.mQ[3], 16)); - - //test case for nlerp(F32 t, const LLQuaternion &q) fn. - res_slerp = slerp(value1, quat1); - ensure("5. LLQuaternion slerp(F32 t, const LLQuaternion &q) failed", - is_approx_equal_fraction(1.0f, res_slerp.mQ[0], 16) && - is_approx_equal_fraction(2.0f, res_slerp.mQ[1], 16) && - is_approx_equal_fraction(4.0000f, res_slerp.mQ[2], 16) && - is_approx_equal_fraction(1.000f, res_slerp.mQ[3], 16)); - - LLQuaternion quat3(2.0f, 1.0f, 5.5f, 10.5f); - LLQuaternion res_nlerp1; - value1 = 100.0f; - res_nlerp1 = nlerp(value1, quat3); - ensure("6. LLQuaternion nlerp(F32 t, const LLQuaternion &q) failed", - is_approx_equal_fraction(0.268245f, res_nlerp1.mQ[0], 16) && is_approx_equal_fraction(0.134122f, res_nlerp1.mQ[1], 2) && - is_approx_equal_fraction(0.737673f, res_nlerp1.mQ[2], 16) && - is_approx_equal_fraction(0.604892f, res_nlerp1.mQ[3], 16)); - - //test case for lerp(F32 t, const LLQuaternion &q) fn. - res_lerp = lerp(value1, quat2); - ensure("7. LLQuaternion lerp(F32 t, const LLQuaternion &q) failed", - is_approx_equal_fraction(0.404867f, res_lerp.mQ[0], 16) && - is_approx_equal_fraction(0.303650f, res_lerp.mQ[1], 16) && - is_approx_equal_fraction(0.657909f, res_lerp.mQ[2], 16) && - is_approx_equal_fraction(0.557704f, res_lerp.mQ[3], 16)); - - } - - template<> template<> - void llquat_test_object_t::test<9>() - { - //test case for LLQuaternion operator*(const LLQuaternion &a, const LLQuaternion &b) fn - LLQuaternion quat1(1.0f, 2.5f, 3.5f, 5.5f); - LLQuaternion quat2(4.0f, 3.0f, 5.0f, 1.0f); - LLQuaternion result = quat1 * quat2; - ensure("1. LLQuaternion Operator* failed", (21.0f == result.mQ[0]) && - (10.0f == result.mQ[1]) && - (38.0f == result.mQ[2]) && - (-23.5f == result.mQ[3])); - - LLQuaternion quat3(2341.340f, 2352.345f, 233.25f, 7645.5f); - LLQuaternion quat4(674.067f, 893.0897f, 578.0f, 231.0f); - result = quat3 * quat4; - ensure("2. LLQuaternion Operator* failed", (4543086.5f == result.mQ[0]) && - (8567578.0f == result.mQ[1]) && - (3967591.25f == result.mQ[2]) && - is_approx_equal(-2047783.25f, result.mQ[3])); - - //inline LLQuaternion operator+(const LLQuaternion &a, const LLQuaternion &b)fn. - result = quat1 + quat2; - ensure("3. LLQuaternion operator+ failed", (5.0f == result.mQ[0]) && - (5.5f == result.mQ[1]) && - (8.5f == result.mQ[2]) && - (6.5f == result.mQ[3])); - - result = quat3 + quat4; - ensure( - "4. LLQuaternion operator+ failed", - is_approx_equal(3015.407227f, result.mQ[0]) && - is_approx_equal(3245.434570f, result.mQ[1]) && - (811.25f == result.mQ[2]) && - (7876.5f == result.mQ[3])); - - //inline LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b) fn - result = quat1 - quat2; - ensure( - "5. LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b) failed", - (-3.0f == result.mQ[0]) && - (-0.5f == result.mQ[1]) && - (-1.5f == result.mQ[2]) && - (4.5f == result.mQ[3])); - - result = quat3 - quat4; - ensure( - "6. LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b) failed", - is_approx_equal(1667.273071f, result.mQ[0]) && - is_approx_equal(1459.255249f, result.mQ[1]) && - (-344.75f == result.mQ[2]) && - (7414.50f == result.mQ[3])); - } - - //test case for LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot) fn. - template<> template<> - void llquat_test_object_t::test<10>() - { - LLVector4 vect(12.0f, 5.0f, 60.0f, 75.1f); - LLQuaternion quat(2323.034f, 23.5f, 673.23f, 57667.5f); - LLVector4 result = vect * quat; - ensure( - "1. LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot) failed", - is_approx_equal(39928406016.0f, result.mV[0]) && - // gcc on x86 actually gives us more precision than we were expecting, verified with -ffloat-store - we forgive this - (1457802240.0f >= result.mV[1]) && // gcc+x86+linux - (1457800960.0f <= result.mV[1]) && // elsewhere - is_approx_equal(200580612096.0f, result.mV[2]) && - (75.099998f == result.mV[3])); - - LLVector4 vect1(22.0f, 45.0f, 40.0f, 78.1f); - LLQuaternion quat1(2.034f, 45.5f, 37.23f, 7.5f); - result = vect1 * quat1; - ensure( - "2. LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot) failed", - is_approx_equal(-58153.5390f, result.mV[0]) && - (183787.8125f == result.mV[1]) && - (116864.164063f == result.mV[2]) && - (78.099998f == result.mV[3])); - } - - //test case for LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot) fn. - template<> template<> - void llquat_test_object_t::test<11>() - { - LLVector3 vect(12.0f, 5.0f, 60.0f); - LLQuaternion quat(23.5f, 6.5f, 3.23f, 56.5f); - LLVector3 result = vect * quat; - ensure( - "1. LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot) failed", - is_approx_equal(97182.953125f,result.mV[0]) && - is_approx_equal(-135405.640625f, result.mV[1]) && - is_approx_equal(162986.140f, result.mV[2])); - - LLVector3 vect1(5.0f, 40.0f, 78.1f); - LLQuaternion quat1(2.034f, 45.5f, 37.23f, 7.5f); - result = vect1 * quat1; - ensure( - "2. LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot) failed", - is_approx_equal(33217.703f, result.mV[0]) && - is_approx_equal(295383.8125f, result.mV[1]) && - is_approx_equal(84718.140f, result.mV[2])); - } - - //test case for LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot) fn. - template<> template<> - void llquat_test_object_t::test<12>() - { - LLVector3d vect(-2.0f, 5.0f, -6.0f); - LLQuaternion quat(-3.5f, 4.5f, 3.5f, 6.5f); - LLVector3d result = vect * quat; - ensure( - "1. LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot) failed ", - (-633.0f == result.mdV[0]) && - (-300.0f == result.mdV[1]) && - (-36.0f == result.mdV[2])); - - LLVector3d vect1(5.0f, -4.5f, 8.21f); - LLQuaternion quat1(2.0f, 4.5f, -7.2f, 9.5f); - result = vect1 * quat1; - ensure( - "2. LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot) failed", - is_approx_equal_fraction(-120.29f, (F32) result.mdV[0], 8) && - is_approx_equal_fraction(-1683.958f, (F32) result.mdV[1], 8) && - is_approx_equal_fraction(516.56f, (F32) result.mdV[2], 8)); - - LLVector3d vect2(2.0f, 3.5f, 1.1f); - LLQuaternion quat2(1.0f, 4.0f, 2.0f, 5.0f); - result = vect2 * quat2; - ensure( - "3. LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot) failed", - is_approx_equal_fraction(18.400001f, (F32) result.mdV[0], 8) && - is_approx_equal_fraction(188.6f, (F32) result.mdV[1], 8) && - is_approx_equal_fraction(32.20f, (F32) result.mdV[2], 8)); - } - - //test case for inline LLQuaternion operator-(const LLQuaternion &a) fn. - template<> template<> - void llquat_test_object_t::test<13>() - { - LLQuaternion quat(23.5f, 34.5f, 16723.4f, 324.7f); - LLQuaternion result = -quat; - ensure( - "1. LLQuaternion operator-(const LLQuaternion &a) failed", - (-23.5f == result.mQ[0]) && - (-34.5f == result.mQ[1]) && - (-16723.4f == result.mQ[2]) && - (-324.7f == result.mQ[3])); - - LLQuaternion quat1(-3.5f, -34.5f, -16.4f, -154.7f); - result = -quat1; - ensure( - "2. LLQuaternion operator-(const LLQuaternion &a) failed.", - (3.5f == result.mQ[0]) && - (34.5f == result.mQ[1]) && - (16.4f == result.mQ[2]) && - (154.7f == result.mQ[3])); - } - - //test case for inline LLQuaternion operator*(F32 a, const LLQuaternion &q) and - //inline LLQuaternion operator*(F32 a, const LLQuaternion &q) fns. - template<> template<> - void llquat_test_object_t::test<14>() - { - LLQuaternion quat_value(9.0f, 8.0f, 7.0f, 6.0f); - F32 a =3.5f; - LLQuaternion result = a * quat_value; - LLQuaternion result1 = quat_value * a; - - ensure( - "1. LLQuaternion operator* failed", - (result.mQ[0] == result1.mQ[0]) && - (result.mQ[1] == result1.mQ[1]) && - (result.mQ[2] == result1.mQ[2]) && - (result.mQ[3] == result1.mQ[3])); - - - LLQuaternion quat_val(9454.0f, 43568.3450f, 456343247.0343f, 2346.03434f); - a =-3324.3445f; - result = a * quat_val; - result1 = quat_val * a; - - ensure( - "2. LLQuaternion operator* failed", - (result.mQ[0] == result1.mQ[0]) && - (result.mQ[1] == result1.mQ[1]) && - (result.mQ[2] == result1.mQ[2]) && - (result.mQ[3] == result1.mQ[3])); - } - - template<> template<> - void llquat_test_object_t::test<15>() - { - // test cases for inline LLQuaternion operator~(const LLQuaternion &a) - LLQuaternion quat_val(2323.634f, -43535.4f, 3455.88f, -32232.45f); - LLQuaternion result = ~quat_val; - ensure( - "1. LLQuaternion operator~(const LLQuaternion &a) failed ", - (-2323.634f == result.mQ[0]) && - (43535.4f == result.mQ[1]) && - (-3455.88f == result.mQ[2]) && - (-32232.45f == result.mQ[3])); - - //test case for inline bool LLQuaternion::operator==(const LLQuaternion &b) const - LLQuaternion quat_val1(2323.634f, -43535.4f, 3455.88f, -32232.45f); - LLQuaternion quat_val2(2323.634f, -43535.4f, 3455.88f, -32232.45f); - ensure( - "2. LLQuaternion::operator==(const LLQuaternion &b) failed", - quat_val1 == quat_val2); - } - - template<> template<> - void llquat_test_object_t::test<16>() - { - //test case for inline bool LLQuaternion::operator!=(const LLQuaternion &b) const - LLQuaternion quat_val1(2323.634f, -43535.4f, 3455.88f, -32232.45f); - LLQuaternion quat_val2(0, -43535.4f, 3455.88f, -32232.45f); - ensure("LLQuaternion::operator!=(const LLQuaternion &b) failed", quat_val1 != quat_val2); - } - - template<> template<> - void llquat_test_object_t::test<17>() - { - //test case for LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) - F32 x = 2.0f; - F32 y = 1.0f; - F32 z = 3.0f; - - LLQuaternion result = mayaQ(x, y, z, LLQuaternion::XYZ); - ensure( - "1. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for XYZ", - is_approx_equal_fraction(0.0172174f, result.mQ[0], 16) && - is_approx_equal_fraction(0.009179f, result.mQ[1], 16) && - is_approx_equal_fraction(0.026020f, result.mQ[2], 16) && - is_approx_equal_fraction(0.999471f, result.mQ[3], 16)); - - LLQuaternion result1 = mayaQ(x, y, z, LLQuaternion::YZX); - ensure( - "2. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for XYZ", - is_approx_equal_fraction(0.017217f, result1.mQ[0], 16) && - is_approx_equal_fraction(0.008265f, result1.mQ[1], 16) && - is_approx_equal_fraction(0.026324f, result1.mQ[2], 16) && - is_approx_equal_fraction(0.999471f, result1.mQ[3], 16)); - - LLQuaternion result2 = mayaQ(x, y, z, LLQuaternion::ZXY); - ensure( - "3. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for ZXY", - is_approx_equal_fraction(0.017674f, result2.mQ[0], 16) && - is_approx_equal_fraction(0.008265f, result2.mQ[1], 16) && - is_approx_equal_fraction(0.026020f, result2.mQ[2], 16) && - is_approx_equal_fraction(0.999471f, result2.mQ[3], 16)); - - LLQuaternion result3 = mayaQ(x, y, z, LLQuaternion::XZY); - ensure( - "4. TLLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for XZY", - is_approx_equal_fraction(0.017674f, result3.mQ[0], 16) && - is_approx_equal_fraction(0.009179f, result3.mQ[1], 16) && - is_approx_equal_fraction(0.026020f, result3.mQ[2], 16) && - is_approx_equal_fraction(0.999463f, result3.mQ[3], 16)); - - LLQuaternion result4 = mayaQ(x, y, z, LLQuaternion::YXZ); - ensure( - "5. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for YXZ", - is_approx_equal_fraction(0.017217f, result4.mQ[0], 16) && - is_approx_equal_fraction(0.009179f, result4.mQ[1], 16) && - is_approx_equal_fraction(0.026324f, result4.mQ[2], 16) && - is_approx_equal_fraction(0.999463f, result4.mQ[3], 16)); - - LLQuaternion result5 = mayaQ(x, y, z, LLQuaternion::ZYX); - ensure( - "6. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for ZYX", - is_approx_equal_fraction(0.017674f, result5.mQ[0], 16) && - is_approx_equal_fraction(0.008265f, result5.mQ[1], 16) && - is_approx_equal_fraction(0.026324f, result5.mQ[2], 16) && - is_approx_equal_fraction(0.999463f, result5.mQ[3], 16)); - } - - template<> template<> - void llquat_test_object_t::test<18>() - { - // test case for friend std::ostream& operator<<(std::ostream &s, const LLQuaternion &a) fn - LLQuaternion a(1.0f, 1.0f, 1.0f, 1.0f); - std::ostringstream result_value; - result_value << a; - ensure_equals("1. Operator << failed", result_value.str(), "{ 1, 1, 1, 1 }"); - - LLQuaternion b(-31.034f, 231.2340f, 3451.344320f, -341.0f); - std::ostringstream result_value1; - result_value1 << b; - ensure_equals("2. Operator << failed", result_value1.str(), "{ -31.034, 231.234, 3451.34, -341 }"); - - LLQuaternion c(1.0f, 2.2f, 3.3f, 4.4f); - result_value << c; - ensure_equals("3. Operator << failed", result_value.str(), "{ 1, 1, 1, 1 }{ 1, 2.2, 3.3, 4.4 }"); - - } - - template<> template<> - void llquat_test_object_t::test<19>() - { - //test case for const char *OrderToString( const LLQuaternion::Order order ) fn - const char* result = OrderToString(LLQuaternion::XYZ); - ensure("1. OrderToString failed for XYZ", (0 == strcmp("XYZ", result))); - - result = OrderToString(LLQuaternion::YZX); - ensure("2. OrderToString failed for YZX", (0 == strcmp("YZX", result))); - - result = OrderToString(LLQuaternion::ZXY); - ensure( - "3. OrderToString failed for ZXY", - (0 == strcmp("ZXY", result)) && - (0 != strcmp("XYZ", result)) && - (0 != strcmp("YXZ", result)) && - (0 != strcmp("ZYX", result)) && - (0 != strcmp("XYZ", result))); - - result = OrderToString(LLQuaternion::XZY); - ensure("4. OrderToString failed for XZY", (0 == strcmp("XZY", result))); - - result = OrderToString(LLQuaternion::ZYX); - ensure("5. OrderToString failed for ZYX", (0 == strcmp("ZYX", result))); - - result = OrderToString(LLQuaternion::YXZ); - ensure("6.OrderToString failed for YXZ", (0 == strcmp("YXZ", result))); - } - - template<> template<> - void llquat_test_object_t::test<20>() - { - //test case for LLQuaternion::Order StringToOrder( const char *str ) fn - int result = StringToOrder("XYZ"); - ensure("1. LLQuaternion::Order StringToOrder(const char *str ) failed for XYZ", 0 == result); - - result = StringToOrder("YZX"); - ensure("2. LLQuaternion::Order StringToOrder(const char *str) failed for YZX", 1 == result); - - result = StringToOrder("ZXY"); - ensure("3. LLQuaternion::Order StringToOrder(const char *str) failed for ZXY", 2 == result); - - result = StringToOrder("XZY"); - ensure("4. LLQuaternion::Order StringToOrder(const char *str) failed for XZY", 3 == result); - - result = StringToOrder("YXZ"); - ensure("5. LLQuaternion::Order StringToOrder(const char *str) failed for YXZ", 4 == result); - - result = StringToOrder("ZYX"); - ensure("6. LLQuaternion::Order StringToOrder(const char *str) failed for ZYX", 5 == result); - - } - - template<> template<> - void llquat_test_object_t::test<21>() - { - //void LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) const fn - F32 angle_value = 90.0f; - LLVector3 vect(12.0f, 4.0f, 1.0f); - LLQuaternion llquat(angle_value, vect); - llquat.getAngleAxis(&angle_value, vect); - ensure( - "LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) failed", - is_approx_equal_fraction(2.035406f, angle_value, 16) && - is_approx_equal_fraction(0.315244f, vect.mV[1], 16) && - is_approx_equal_fraction(0.078811f, vect.mV[2], 16) && - is_approx_equal_fraction(0.945733f, vect.mV[0], 16)); - } - - template<> template<> - void llquat_test_object_t::test<22>() - { - //test case for void LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const fn - F32 roll = -12.0f; - F32 pitch = -22.43f; - F32 yaw = 11.0f; - - LLQuaternion llquat; - llquat.getEulerAngles(&roll, &pitch, &yaw); - ensure( - "LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) failed", - is_approx_equal(0.000f, llquat.mQ[0]) && - is_approx_equal(0.000f, llquat.mQ[1]) && - is_approx_equal(0.000f, llquat.mQ[2]) && - is_approx_equal(1.000f, llquat.mQ[3])); - } + struct llquat_test + { + }; + typedef test_group<llquat_test> llquat_test_t; + typedef llquat_test_t::object llquat_test_object_t; + tut::llquat_test_t tut_llquat_test("LLQuaternion"); + + //test case for LLQuaternion::LLQuaternion(void) fn. + template<> template<> + void llquat_test_object_t::test<1>() + { + LLQuaternion llquat; + ensure("LLQuaternion::LLQuaternion() failed", 0.f == llquat.mQ[0] && + 0.f == llquat.mQ[1] && + 0.f == llquat.mQ[2] && + 1.f == llquat.mQ[3]); + } + + //test case for explicit LLQuaternion(const LLMatrix4 &mat) fn. + template<> template<> + void llquat_test_object_t::test<2>() + { + LLMatrix4 llmat; + LLVector4 vector1(2.0f, 1.0f, 3.0f, 6.0f); + LLVector4 vector2(5.0f, 6.0f, 0.0f, 1.0f); + LLVector4 vector3(2.0f, 1.0f, 2.0f, 9.0f); + LLVector4 vector4(3.0f, 8.0f, 1.0f, 5.0f); + + llmat.initRows(vector1, vector2, vector3, vector4); + ensure("explicit LLQuaternion(const LLMatrix4 &mat) failed", 2.0f == llmat.mMatrix[0][0] && + 1.0f == llmat.mMatrix[0][1] && + 3.0f == llmat.mMatrix[0][2] && + 6.0f == llmat.mMatrix[0][3] && + 5.0f == llmat.mMatrix[1][0] && + 6.0f == llmat.mMatrix[1][1] && + 0.0f == llmat.mMatrix[1][2] && + 1.0f == llmat.mMatrix[1][3] && + 2.0f == llmat.mMatrix[2][0] && + 1.0f == llmat.mMatrix[2][1] && + 2.0f == llmat.mMatrix[2][2] && + 9.0f == llmat.mMatrix[2][3] && + 3.0f == llmat.mMatrix[3][0] && + 8.0f == llmat.mMatrix[3][1] && + 1.0f == llmat.mMatrix[3][2] && + 5.0f == llmat.mMatrix[3][3]); + } + + template<> template<> + void llquat_test_object_t::test<3>() + { + LLMatrix3 llmat; + + LLVector3 vect1(3.4028234660000000f , 234.56f, 4234.442234f); + LLVector3 vect2(741.434f, 23.00034f, 6567.223423f); + LLVector3 vect3(566.003034f, 12.98705f, 234.764423f); + llmat.setRows(vect1, vect2, vect3); + + ensure("LLMatrix3::setRows fn failed.", 3.4028234660000000f == llmat.mMatrix[0][0] && + 234.56f == llmat.mMatrix[0][1] && + 4234.442234f == llmat.mMatrix[0][2] && + 741.434f == llmat.mMatrix[1][0] && + 23.00034f == llmat.mMatrix[1][1] && + 6567.223423f == llmat.mMatrix[1][2] && + 566.003034f == llmat.mMatrix[2][0] && + 12.98705f == llmat.mMatrix[2][1] && + 234.764423f == llmat.mMatrix[2][2]); + } + + //test case for LLQuaternion(F32 x, F32 y, F32 z, F32 w), setQuatInit() and normQuat() fns. + template<> template<> + void llquat_test_object_t::test<4>() + { + F32 x_val = 3.0f; + F32 y_val = 2.0f; + F32 z_val = 6.0f; + F32 w_val = 1.0f; + + LLQuaternion res_quat; + res_quat.setQuatInit(x_val, y_val, z_val, w_val); + res_quat.normQuat(); + + ensure("LLQuaternion::normQuat() fn failed", + is_approx_equal(0.42426407f, res_quat.mQ[0]) && + is_approx_equal(0.28284273f, res_quat.mQ[1]) && + is_approx_equal(0.84852815f, res_quat.mQ[2]) && + is_approx_equal(0.14142136f, res_quat.mQ[3])); + + x_val = 0.0f; + y_val = 0.0f; + z_val = 0.0f; + w_val = 0.0f; + + res_quat.setQuatInit(x_val, y_val, z_val, w_val); + res_quat.normQuat(); + + ensure("LLQuaternion::normQuat() fn. failed.", + is_approx_equal(0.0f, res_quat.mQ[0]) && + is_approx_equal(0.0f, res_quat.mQ[1]) && + is_approx_equal(0.0f, res_quat.mQ[2]) && + is_approx_equal(1.0f, res_quat.mQ[3])); + + + ensure("LLQuaternion::normQuat() fn. failed.", + is_approx_equal(0.0f, res_quat.mQ[0]) && + is_approx_equal(0.0f, res_quat.mQ[1]) && + is_approx_equal(0.0f, res_quat.mQ[2]) && + is_approx_equal(1.0f, res_quat.mQ[3])); + } + + //test case for conjQuat() and transQuat() fns. + template<> template<> + void llquat_test_object_t::test<5>() + { + F32 x_val = 3.0f; + F32 y_val = 2.0f; + F32 z_val = 6.0f; + F32 w_val = 1.0f; + + LLQuaternion res_quat; + LLQuaternion result, result1; + result1 = result = res_quat.setQuatInit(x_val, y_val, z_val, w_val); + + result.conjQuat(); + result1.transQuat(); + + ensure("LLQuaternion::conjQuat and LLQuaternion::transQuat failed ", + is_approx_equal(result1.mQ[0], result.mQ[0]) && + is_approx_equal(result1.mQ[1], result.mQ[1]) && + is_approx_equal(result1.mQ[2], result.mQ[2])); + + } + + //test case for dot(const LLQuaternion &a, const LLQuaternion &b) fn. + template<> template<> + void llquat_test_object_t::test<6>() + { + LLQuaternion quat1(3.0f, 2.0f, 6.0f, 0.0f), quat2(1.0f, 1.0f, 1.0f, 1.0f); + ensure("1. The two values are different", ll_round(12.000000f, 2) == ll_round(dot(quat1, quat2), 2)); + + LLQuaternion quat0(3.0f, 9.334f, 34.5f, 23.0f), quat(34.5f, 23.23f, 2.0f, 45.5f); + ensure("2. The two values are different", ll_round(1435.828807f, 2) == ll_round(dot(quat0, quat), 2)); + } + + //test case for LLQuaternion &LLQuaternion::constrain(F32 radians) fn. + template<> template<> + void llquat_test_object_t::test<7>() + { + F32 radian = 60.0f; + LLQuaternion quat(3.0f, 2.0f, 6.0f, 0.0f); + LLQuaternion quat1; + quat1 = quat.constrain(radian); + ensure("1. LLQuaternion::constrain(F32 radians) failed", + is_approx_equal_fraction(-0.423442f, quat1.mQ[0], 8) && + is_approx_equal_fraction(-0.282295f, quat1.mQ[1], 8) && + is_approx_equal_fraction(-0.846884f, quat1.mQ[2], 8) && + is_approx_equal_fraction(0.154251f, quat1.mQ[3], 8)); + + + radian = 30.0f; + LLQuaternion quat0(37.50f, 12.0f, 86.023f, 40.32f); + quat1 = quat0.constrain(radian); + + ensure("2. LLQuaternion::constrain(F32 radians) failed", + is_approx_equal_fraction(37.500000f, quat1.mQ[0], 8) && + is_approx_equal_fraction(12.0000f, quat1.mQ[1], 8) && + is_approx_equal_fraction(86.0230f, quat1.mQ[2], 8) && + is_approx_equal_fraction(40.320000f, quat1.mQ[3], 8)); + } + + template<> template<> + void llquat_test_object_t::test<8>() + { + F32 value1 = 15.0f; + LLQuaternion quat1(1.0f, 2.0f, 4.0f, 1.0f); + LLQuaternion quat2(4.0f, 3.0f, 6.5f, 9.7f); + LLQuaternion res_lerp, res_slerp, res_nlerp; + + //test case for lerp(F32 t, const LLQuaternion &q) fn. + res_lerp = lerp(value1, quat1); + ensure("1. LLQuaternion lerp(F32 t, const LLQuaternion &q) failed", + is_approx_equal_fraction(0.181355f, res_lerp.mQ[0], 16) && + is_approx_equal_fraction(0.362711f, res_lerp.mQ[1], 16) && + is_approx_equal_fraction(0.725423f, res_lerp.mQ[2], 16) && + is_approx_equal_fraction(0.556158f, res_lerp.mQ[3], 16)); + + //test case for lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q) fn. + res_lerp = lerp(value1, quat1, quat2); + ensure("2. LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q) failed", + is_approx_equal_fraction(0.314306f, res_lerp.mQ[0], 16) && + is_approx_equal_fraction(0.116156f, res_lerp.mQ[1], 16) && + is_approx_equal_fraction(0.283559f, res_lerp.mQ[2], 16) && + is_approx_equal_fraction(0.898506f, res_lerp.mQ[3], 16)); + + //test case for slerp( F32 u, const LLQuaternion &a, const LLQuaternion &b ) fn. + res_slerp = slerp(value1, quat1, quat2); + ensure("3. LLQuaternion slerp( F32 u, const LLQuaternion &a, const LLQuaternion &b) failed", + is_approx_equal_fraction(46.000f, res_slerp.mQ[0], 16) && + is_approx_equal_fraction(17.00f, res_slerp.mQ[1], 16) && + is_approx_equal_fraction(41.5f, res_slerp.mQ[2], 16) && + is_approx_equal_fraction(131.5f, res_slerp.mQ[3], 16)); + + //test case for nlerp(F32 t, const LLQuaternion &a, const LLQuaternion &b) fn. + res_nlerp = nlerp(value1, quat1, quat2); + ensure("4. LLQuaternion nlerp(F32 t, const LLQuaternion &a, const LLQuaternion &b) failed", + is_approx_equal_fraction(0.314306f, res_nlerp.mQ[0], 16) && + is_approx_equal_fraction(0.116157f, res_nlerp.mQ[1], 16) && + is_approx_equal_fraction(0.283559f, res_nlerp.mQ[2], 16) && + is_approx_equal_fraction(0.898506f, res_nlerp.mQ[3], 16)); + + //test case for nlerp(F32 t, const LLQuaternion &q) fn. + res_slerp = slerp(value1, quat1); + ensure("5. LLQuaternion slerp(F32 t, const LLQuaternion &q) failed", + is_approx_equal_fraction(1.0f, res_slerp.mQ[0], 16) && + is_approx_equal_fraction(2.0f, res_slerp.mQ[1], 16) && + is_approx_equal_fraction(4.0000f, res_slerp.mQ[2], 16) && + is_approx_equal_fraction(1.000f, res_slerp.mQ[3], 16)); + + LLQuaternion quat3(2.0f, 1.0f, 5.5f, 10.5f); + LLQuaternion res_nlerp1; + value1 = 100.0f; + res_nlerp1 = nlerp(value1, quat3); + ensure("6. LLQuaternion nlerp(F32 t, const LLQuaternion &q) failed", + is_approx_equal_fraction(0.268245f, res_nlerp1.mQ[0], 16) && is_approx_equal_fraction(0.134122f, res_nlerp1.mQ[1], 2) && + is_approx_equal_fraction(0.737673f, res_nlerp1.mQ[2], 16) && + is_approx_equal_fraction(0.604892f, res_nlerp1.mQ[3], 16)); + + //test case for lerp(F32 t, const LLQuaternion &q) fn. + res_lerp = lerp(value1, quat2); + ensure("7. LLQuaternion lerp(F32 t, const LLQuaternion &q) failed", + is_approx_equal_fraction(0.404867f, res_lerp.mQ[0], 16) && + is_approx_equal_fraction(0.303650f, res_lerp.mQ[1], 16) && + is_approx_equal_fraction(0.657909f, res_lerp.mQ[2], 16) && + is_approx_equal_fraction(0.557704f, res_lerp.mQ[3], 16)); + + } + + template<> template<> + void llquat_test_object_t::test<9>() + { + //test case for LLQuaternion operator*(const LLQuaternion &a, const LLQuaternion &b) fn + LLQuaternion quat1(1.0f, 2.5f, 3.5f, 5.5f); + LLQuaternion quat2(4.0f, 3.0f, 5.0f, 1.0f); + LLQuaternion result = quat1 * quat2; + ensure("1. LLQuaternion Operator* failed", (21.0f == result.mQ[0]) && + (10.0f == result.mQ[1]) && + (38.0f == result.mQ[2]) && + (-23.5f == result.mQ[3])); + + LLQuaternion quat3(2341.340f, 2352.345f, 233.25f, 7645.5f); + LLQuaternion quat4(674.067f, 893.0897f, 578.0f, 231.0f); + result = quat3 * quat4; + ensure("2. LLQuaternion Operator* failed", (4543086.5f == result.mQ[0]) && + (8567578.0f == result.mQ[1]) && + (3967591.25f == result.mQ[2]) && + is_approx_equal(-2047783.25f, result.mQ[3])); + + //inline LLQuaternion operator+(const LLQuaternion &a, const LLQuaternion &b)fn. + result = quat1 + quat2; + ensure("3. LLQuaternion operator+ failed", (5.0f == result.mQ[0]) && + (5.5f == result.mQ[1]) && + (8.5f == result.mQ[2]) && + (6.5f == result.mQ[3])); + + result = quat3 + quat4; + ensure( + "4. LLQuaternion operator+ failed", + is_approx_equal(3015.407227f, result.mQ[0]) && + is_approx_equal(3245.434570f, result.mQ[1]) && + (811.25f == result.mQ[2]) && + (7876.5f == result.mQ[3])); + + //inline LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b) fn + result = quat1 - quat2; + ensure( + "5. LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b) failed", + (-3.0f == result.mQ[0]) && + (-0.5f == result.mQ[1]) && + (-1.5f == result.mQ[2]) && + (4.5f == result.mQ[3])); + + result = quat3 - quat4; + ensure( + "6. LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b) failed", + is_approx_equal(1667.273071f, result.mQ[0]) && + is_approx_equal(1459.255249f, result.mQ[1]) && + (-344.75f == result.mQ[2]) && + (7414.50f == result.mQ[3])); + } + + //test case for LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot) fn. + template<> template<> + void llquat_test_object_t::test<10>() + { + LLVector4 vect(12.0f, 5.0f, 60.0f, 75.1f); + LLQuaternion quat(2323.034f, 23.5f, 673.23f, 57667.5f); + LLVector4 result = vect * quat; + ensure( + "1. LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot) failed", + is_approx_equal(39928406016.0f, result.mV[0]) && + // gcc on x86 actually gives us more precision than we were expecting, verified with -ffloat-store - we forgive this + (1457802240.0f >= result.mV[1]) && // gcc+x86+linux + (1457800960.0f <= result.mV[1]) && // elsewhere + is_approx_equal(200580612096.0f, result.mV[2]) && + (75.099998f == result.mV[3])); + + LLVector4 vect1(22.0f, 45.0f, 40.0f, 78.1f); + LLQuaternion quat1(2.034f, 45.5f, 37.23f, 7.5f); + result = vect1 * quat1; + ensure( + "2. LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot) failed", + is_approx_equal(-58153.5390f, result.mV[0]) && + (183787.8125f == result.mV[1]) && + (116864.164063f == result.mV[2]) && + (78.099998f == result.mV[3])); + } + + //test case for LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot) fn. + template<> template<> + void llquat_test_object_t::test<11>() + { + LLVector3 vect(12.0f, 5.0f, 60.0f); + LLQuaternion quat(23.5f, 6.5f, 3.23f, 56.5f); + LLVector3 result = vect * quat; + ensure( + "1. LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot) failed", + is_approx_equal(97182.953125f,result.mV[0]) && + is_approx_equal(-135405.640625f, result.mV[1]) && + is_approx_equal(162986.140f, result.mV[2])); + + LLVector3 vect1(5.0f, 40.0f, 78.1f); + LLQuaternion quat1(2.034f, 45.5f, 37.23f, 7.5f); + result = vect1 * quat1; + ensure( + "2. LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot) failed", + is_approx_equal(33217.703f, result.mV[0]) && + is_approx_equal(295383.8125f, result.mV[1]) && + is_approx_equal(84718.140f, result.mV[2])); + } + + //test case for LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot) fn. + template<> template<> + void llquat_test_object_t::test<12>() + { + LLVector3d vect(-2.0f, 5.0f, -6.0f); + LLQuaternion quat(-3.5f, 4.5f, 3.5f, 6.5f); + LLVector3d result = vect * quat; + ensure( + "1. LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot) failed ", + (-633.0f == result.mdV[0]) && + (-300.0f == result.mdV[1]) && + (-36.0f == result.mdV[2])); + + LLVector3d vect1(5.0f, -4.5f, 8.21f); + LLQuaternion quat1(2.0f, 4.5f, -7.2f, 9.5f); + result = vect1 * quat1; + ensure( + "2. LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot) failed", + is_approx_equal_fraction(-120.29f, (F32) result.mdV[0], 8) && + is_approx_equal_fraction(-1683.958f, (F32) result.mdV[1], 8) && + is_approx_equal_fraction(516.56f, (F32) result.mdV[2], 8)); + + LLVector3d vect2(2.0f, 3.5f, 1.1f); + LLQuaternion quat2(1.0f, 4.0f, 2.0f, 5.0f); + result = vect2 * quat2; + ensure( + "3. LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot) failed", + is_approx_equal_fraction(18.400001f, (F32) result.mdV[0], 8) && + is_approx_equal_fraction(188.6f, (F32) result.mdV[1], 8) && + is_approx_equal_fraction(32.20f, (F32) result.mdV[2], 8)); + } + + //test case for inline LLQuaternion operator-(const LLQuaternion &a) fn. + template<> template<> + void llquat_test_object_t::test<13>() + { + LLQuaternion quat(23.5f, 34.5f, 16723.4f, 324.7f); + LLQuaternion result = -quat; + ensure( + "1. LLQuaternion operator-(const LLQuaternion &a) failed", + (-23.5f == result.mQ[0]) && + (-34.5f == result.mQ[1]) && + (-16723.4f == result.mQ[2]) && + (-324.7f == result.mQ[3])); + + LLQuaternion quat1(-3.5f, -34.5f, -16.4f, -154.7f); + result = -quat1; + ensure( + "2. LLQuaternion operator-(const LLQuaternion &a) failed.", + (3.5f == result.mQ[0]) && + (34.5f == result.mQ[1]) && + (16.4f == result.mQ[2]) && + (154.7f == result.mQ[3])); + } + + //test case for inline LLQuaternion operator*(F32 a, const LLQuaternion &q) and + //inline LLQuaternion operator*(F32 a, const LLQuaternion &q) fns. + template<> template<> + void llquat_test_object_t::test<14>() + { + LLQuaternion quat_value(9.0f, 8.0f, 7.0f, 6.0f); + F32 a =3.5f; + LLQuaternion result = a * quat_value; + LLQuaternion result1 = quat_value * a; + + ensure( + "1. LLQuaternion operator* failed", + (result.mQ[0] == result1.mQ[0]) && + (result.mQ[1] == result1.mQ[1]) && + (result.mQ[2] == result1.mQ[2]) && + (result.mQ[3] == result1.mQ[3])); + + + LLQuaternion quat_val(9454.0f, 43568.3450f, 456343247.0343f, 2346.03434f); + a =-3324.3445f; + result = a * quat_val; + result1 = quat_val * a; + + ensure( + "2. LLQuaternion operator* failed", + (result.mQ[0] == result1.mQ[0]) && + (result.mQ[1] == result1.mQ[1]) && + (result.mQ[2] == result1.mQ[2]) && + (result.mQ[3] == result1.mQ[3])); + } + + template<> template<> + void llquat_test_object_t::test<15>() + { + // test cases for inline LLQuaternion operator~(const LLQuaternion &a) + LLQuaternion quat_val(2323.634f, -43535.4f, 3455.88f, -32232.45f); + LLQuaternion result = ~quat_val; + ensure( + "1. LLQuaternion operator~(const LLQuaternion &a) failed ", + (-2323.634f == result.mQ[0]) && + (43535.4f == result.mQ[1]) && + (-3455.88f == result.mQ[2]) && + (-32232.45f == result.mQ[3])); + + //test case for inline bool LLQuaternion::operator==(const LLQuaternion &b) const + LLQuaternion quat_val1(2323.634f, -43535.4f, 3455.88f, -32232.45f); + LLQuaternion quat_val2(2323.634f, -43535.4f, 3455.88f, -32232.45f); + ensure( + "2. LLQuaternion::operator==(const LLQuaternion &b) failed", + quat_val1 == quat_val2); + } + + template<> template<> + void llquat_test_object_t::test<16>() + { + //test case for inline bool LLQuaternion::operator!=(const LLQuaternion &b) const + LLQuaternion quat_val1(2323.634f, -43535.4f, 3455.88f, -32232.45f); + LLQuaternion quat_val2(0, -43535.4f, 3455.88f, -32232.45f); + ensure("LLQuaternion::operator!=(const LLQuaternion &b) failed", quat_val1 != quat_val2); + } + + template<> template<> + void llquat_test_object_t::test<17>() + { + //test case for LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) + F32 x = 2.0f; + F32 y = 1.0f; + F32 z = 3.0f; + + LLQuaternion result = mayaQ(x, y, z, LLQuaternion::XYZ); + ensure( + "1. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for XYZ", + is_approx_equal_fraction(0.0172174f, result.mQ[0], 16) && + is_approx_equal_fraction(0.009179f, result.mQ[1], 16) && + is_approx_equal_fraction(0.026020f, result.mQ[2], 16) && + is_approx_equal_fraction(0.999471f, result.mQ[3], 16)); + + LLQuaternion result1 = mayaQ(x, y, z, LLQuaternion::YZX); + ensure( + "2. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for XYZ", + is_approx_equal_fraction(0.017217f, result1.mQ[0], 16) && + is_approx_equal_fraction(0.008265f, result1.mQ[1], 16) && + is_approx_equal_fraction(0.026324f, result1.mQ[2], 16) && + is_approx_equal_fraction(0.999471f, result1.mQ[3], 16)); + + LLQuaternion result2 = mayaQ(x, y, z, LLQuaternion::ZXY); + ensure( + "3. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for ZXY", + is_approx_equal_fraction(0.017674f, result2.mQ[0], 16) && + is_approx_equal_fraction(0.008265f, result2.mQ[1], 16) && + is_approx_equal_fraction(0.026020f, result2.mQ[2], 16) && + is_approx_equal_fraction(0.999471f, result2.mQ[3], 16)); + + LLQuaternion result3 = mayaQ(x, y, z, LLQuaternion::XZY); + ensure( + "4. TLLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for XZY", + is_approx_equal_fraction(0.017674f, result3.mQ[0], 16) && + is_approx_equal_fraction(0.009179f, result3.mQ[1], 16) && + is_approx_equal_fraction(0.026020f, result3.mQ[2], 16) && + is_approx_equal_fraction(0.999463f, result3.mQ[3], 16)); + + LLQuaternion result4 = mayaQ(x, y, z, LLQuaternion::YXZ); + ensure( + "5. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for YXZ", + is_approx_equal_fraction(0.017217f, result4.mQ[0], 16) && + is_approx_equal_fraction(0.009179f, result4.mQ[1], 16) && + is_approx_equal_fraction(0.026324f, result4.mQ[2], 16) && + is_approx_equal_fraction(0.999463f, result4.mQ[3], 16)); + + LLQuaternion result5 = mayaQ(x, y, z, LLQuaternion::ZYX); + ensure( + "6. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for ZYX", + is_approx_equal_fraction(0.017674f, result5.mQ[0], 16) && + is_approx_equal_fraction(0.008265f, result5.mQ[1], 16) && + is_approx_equal_fraction(0.026324f, result5.mQ[2], 16) && + is_approx_equal_fraction(0.999463f, result5.mQ[3], 16)); + } + + template<> template<> + void llquat_test_object_t::test<18>() + { + // test case for friend std::ostream& operator<<(std::ostream &s, const LLQuaternion &a) fn + LLQuaternion a(1.0f, 1.0f, 1.0f, 1.0f); + std::ostringstream result_value; + result_value << a; + ensure_equals("1. Operator << failed", result_value.str(), "{ 1, 1, 1, 1 }"); + + LLQuaternion b(-31.034f, 231.2340f, 3451.344320f, -341.0f); + std::ostringstream result_value1; + result_value1 << b; + ensure_equals("2. Operator << failed", result_value1.str(), "{ -31.034, 231.234, 3451.34, -341 }"); + + LLQuaternion c(1.0f, 2.2f, 3.3f, 4.4f); + result_value << c; + ensure_equals("3. Operator << failed", result_value.str(), "{ 1, 1, 1, 1 }{ 1, 2.2, 3.3, 4.4 }"); + + } + + template<> template<> + void llquat_test_object_t::test<19>() + { + //test case for const char *OrderToString( const LLQuaternion::Order order ) fn + const char* result = OrderToString(LLQuaternion::XYZ); + ensure("1. OrderToString failed for XYZ", (0 == strcmp("XYZ", result))); + + result = OrderToString(LLQuaternion::YZX); + ensure("2. OrderToString failed for YZX", (0 == strcmp("YZX", result))); + + result = OrderToString(LLQuaternion::ZXY); + ensure( + "3. OrderToString failed for ZXY", + (0 == strcmp("ZXY", result)) && + (0 != strcmp("XYZ", result)) && + (0 != strcmp("YXZ", result)) && + (0 != strcmp("ZYX", result)) && + (0 != strcmp("XYZ", result))); + + result = OrderToString(LLQuaternion::XZY); + ensure("4. OrderToString failed for XZY", (0 == strcmp("XZY", result))); + + result = OrderToString(LLQuaternion::ZYX); + ensure("5. OrderToString failed for ZYX", (0 == strcmp("ZYX", result))); + + result = OrderToString(LLQuaternion::YXZ); + ensure("6.OrderToString failed for YXZ", (0 == strcmp("YXZ", result))); + } + + template<> template<> + void llquat_test_object_t::test<20>() + { + //test case for LLQuaternion::Order StringToOrder( const char *str ) fn + int result = StringToOrder("XYZ"); + ensure("1. LLQuaternion::Order StringToOrder(const char *str ) failed for XYZ", 0 == result); + + result = StringToOrder("YZX"); + ensure("2. LLQuaternion::Order StringToOrder(const char *str) failed for YZX", 1 == result); + + result = StringToOrder("ZXY"); + ensure("3. LLQuaternion::Order StringToOrder(const char *str) failed for ZXY", 2 == result); + + result = StringToOrder("XZY"); + ensure("4. LLQuaternion::Order StringToOrder(const char *str) failed for XZY", 3 == result); + + result = StringToOrder("YXZ"); + ensure("5. LLQuaternion::Order StringToOrder(const char *str) failed for YXZ", 4 == result); + + result = StringToOrder("ZYX"); + ensure("6. LLQuaternion::Order StringToOrder(const char *str) failed for ZYX", 5 == result); + + } + + template<> template<> + void llquat_test_object_t::test<21>() + { + //void LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) const fn + F32 angle_value = 90.0f; + LLVector3 vect(12.0f, 4.0f, 1.0f); + LLQuaternion llquat(angle_value, vect); + llquat.getAngleAxis(&angle_value, vect); + ensure( + "LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) failed", + is_approx_equal_fraction(2.035406f, angle_value, 16) && + is_approx_equal_fraction(0.315244f, vect.mV[1], 16) && + is_approx_equal_fraction(0.078811f, vect.mV[2], 16) && + is_approx_equal_fraction(0.945733f, vect.mV[0], 16)); + } + + template<> template<> + void llquat_test_object_t::test<22>() + { + //test case for void LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const fn + F32 roll = -12.0f; + F32 pitch = -22.43f; + F32 yaw = 11.0f; + + LLQuaternion llquat; + llquat.getEulerAngles(&roll, &pitch, &yaw); + ensure( + "LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) failed", + is_approx_equal(0.000f, llquat.mQ[0]) && + is_approx_equal(0.000f, llquat.mQ[1]) && + is_approx_equal(0.000f, llquat.mQ[2]) && + is_approx_equal(1.000f, llquat.mQ[3])); + } } diff --git a/indra/llmath/tests/llrect_test.cpp b/indra/llmath/tests/llrect_test.cpp index 365f298636..5e3104e293 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/m3math_test.cpp b/indra/llmath/tests/m3math_test.cpp index 2a0fe76aa7..b023c85ffd 100644 --- a/indra/llmath/tests/m3math_test.cpp +++ b/indra/llmath/tests/m3math_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file m3math_test.cpp * @author Adroit * @date 2007-03 @@ -7,21 +7,21 @@ * $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$ */ @@ -49,284 +49,284 @@ namespace tut { - struct m3math_test - { - }; - typedef test_group<m3math_test> m3math_test_t; - typedef m3math_test_t::object m3math_test_object_t; - tut::m3math_test_t tut_m3math_test("m3math_h"); - - //test case for setIdentity() fn. - template<> template<> - void m3math_test_object_t::test<1>() - { - LLMatrix3 llmat3_obj; - llmat3_obj.setIdentity(); - ensure("LLMatrix3::setIdentity failed", 1.f == llmat3_obj.mMatrix[0][0] && - 0.f == llmat3_obj.mMatrix[0][1] && - 0.f == llmat3_obj.mMatrix[0][2] && - 0.f == llmat3_obj.mMatrix[1][0] && - 1.f == llmat3_obj.mMatrix[1][1] && - 0.f == llmat3_obj.mMatrix[1][2] && - 0.f == llmat3_obj.mMatrix[2][0] && - 0.f == llmat3_obj.mMatrix[2][1] && - 1.f == llmat3_obj.mMatrix[2][2]); - } - - //test case for LLMatrix3& setZero() fn. - template<> template<> - void m3math_test_object_t::test<2>() - { - LLMatrix3 llmat3_obj; - llmat3_obj.setZero(); - - ensure("LLMatrix3::setZero failed", 0.f == llmat3_obj.setZero().mMatrix[0][0] && - 0.f == llmat3_obj.setZero().mMatrix[0][1] && - 0.f == llmat3_obj.setZero().mMatrix[0][2] && - 0.f == llmat3_obj.setZero().mMatrix[1][0] && - 0.f == llmat3_obj.setZero().mMatrix[1][1] && - 0.f == llmat3_obj.setZero().mMatrix[1][2] && - 0.f == llmat3_obj.setZero().mMatrix[2][0] && - 0.f == llmat3_obj.setZero().mMatrix[2][1] && - 0.f == llmat3_obj.setZero().mMatrix[2][2]); - } - - //test case for setRows(const LLVector3 &x_axis, const LLVector3 &y_axis, const LLVector3 &z_axis) fns. - template<> template<> - void m3math_test_object_t::test<3>() - { - LLMatrix3 llmat3_obj; - LLVector3 vect1(2, 1, 4); - LLVector3 vect2(3, 5, 7); - LLVector3 vect3(6, 9, 7); - llmat3_obj.setRows(vect1, vect2, vect3); - ensure("LLVector3::setRows failed ", 2 == llmat3_obj.mMatrix[0][0] && - 1 == llmat3_obj.mMatrix[0][1] && - 4 == llmat3_obj.mMatrix[0][2] && - 3 == llmat3_obj.mMatrix[1][0] && - 5 == llmat3_obj.mMatrix[1][1] && - 7 == llmat3_obj.mMatrix[1][2] && - 6 == llmat3_obj.mMatrix[2][0] && - 9 == llmat3_obj.mMatrix[2][1] && - 7 == llmat3_obj.mMatrix[2][2]); - } - - //test case for getFwdRow(), getLeftRow(), getUpRow() fns. - template<> template<> - void m3math_test_object_t::test<4>() - { - LLMatrix3 llmat3_obj; - LLVector3 vect1(2, 1, 4); - LLVector3 vect2(3, 5, 7); - LLVector3 vect3(6, 9, 7); - llmat3_obj.setRows(vect1, vect2, vect3); - - ensure("LLVector3::getFwdRow failed ", vect1 == llmat3_obj.getFwdRow()); - ensure("LLVector3::getLeftRow failed ", vect2 == llmat3_obj.getLeftRow()); - ensure("LLVector3::getUpRow failed ", vect3 == llmat3_obj.getUpRow()); - } - - //test case for operator*(const LLMatrix3 &a, const LLMatrix3 &b) - template<> template<> - void m3math_test_object_t::test<5>() - { - LLMatrix3 llmat_obj1; - LLMatrix3 llmat_obj2; - LLMatrix3 llmat_obj3; - - LLVector3 llvec1(1, 3, 5); - LLVector3 llvec2(3, 6, 1); - LLVector3 llvec3(4, 6, 9); - - LLVector3 llvec4(1, 1, 5); - LLVector3 llvec5(3, 6, 8); - LLVector3 llvec6(8, 6, 2); - - LLVector3 llvec7(0, 0, 0); - LLVector3 llvec8(0, 0, 0); - LLVector3 llvec9(0, 0, 0); - - llmat_obj1.setRows(llvec1, llvec2, llvec3); - llmat_obj2.setRows(llvec4, llvec5, llvec6); - llmat_obj3.setRows(llvec7, llvec8, llvec9); - llmat_obj3 = llmat_obj1 * llmat_obj2; - ensure("LLMatrix3::operator*(const LLMatrix3 &a, const LLMatrix3 &b) failed", - 50 == llmat_obj3.mMatrix[0][0] && - 49 == llmat_obj3.mMatrix[0][1] && - 39 == llmat_obj3.mMatrix[0][2] && - 29 == llmat_obj3.mMatrix[1][0] && - 45 == llmat_obj3.mMatrix[1][1] && - 65 == llmat_obj3.mMatrix[1][2] && - 94 == llmat_obj3.mMatrix[2][0] && - 94 == llmat_obj3.mMatrix[2][1] && - 86 == llmat_obj3.mMatrix[2][2]); - } - - - //test case for operator*(const LLVector3 &a, const LLMatrix3 &b) - template<> template<> - void m3math_test_object_t::test<6>() - { - - LLMatrix3 llmat_obj1; - - LLVector3 llvec(1, 3, 5); - LLVector3 res_vec(0, 0, 0); - LLVector3 llvec1(1, 3, 5); - LLVector3 llvec2(3, 6, 1); - LLVector3 llvec3(4, 6, 9); - - llmat_obj1.setRows(llvec1, llvec2, llvec3); - res_vec = llvec * llmat_obj1; - - LLVector3 expected_result(30, 51, 53); - - ensure("LLMatrix3::operator*(const LLVector3 &a, const LLMatrix3 &b) failed", res_vec == expected_result); - } - - //test case for operator*(const LLVector3d &a, const LLMatrix3 &b) - template<> template<> - void m3math_test_object_t::test<7>() - { - LLMatrix3 llmat_obj1; - LLVector3d llvec3d1; - LLVector3d llvec3d2(0, 3, 4); - - LLVector3 llvec1(1, 3, 5); - LLVector3 llvec2(3, 2, 1); - LLVector3 llvec3(4, 6, 0); - - llmat_obj1.setRows(llvec1, llvec2, llvec3); - llvec3d1 = llvec3d2 * llmat_obj1; - - LLVector3d expected_result(25, 30, 3); - - ensure("LLMatrix3::operator*(const LLVector3 &a, const LLMatrix3 &b) failed", llvec3d1 == expected_result); - } - - // test case for operator==(const LLMatrix3 &a, const LLMatrix3 &b) - template<> template<> - void m3math_test_object_t::test<8>() - { - LLMatrix3 llmat_obj1; - LLMatrix3 llmat_obj2; - - LLVector3 llvec1(1, 3, 5); - LLVector3 llvec2(3, 6, 1); - LLVector3 llvec3(4, 6, 9); - - llmat_obj1.setRows(llvec1, llvec2, llvec3); - llmat_obj2.setRows(llvec1, llvec2, llvec3); - ensure("LLMatrix3::operator==(const LLMatrix3 &a, const LLMatrix3 &b) failed", llmat_obj1 == llmat_obj2); - - llmat_obj2.setRows(llvec2, llvec2, llvec3); - ensure("LLMatrix3::operator!=(const LLMatrix3 &a, const LLMatrix3 &b) failed", llmat_obj1 != llmat_obj2); - } - - //test case for quaternion() fn. - template<> template<> - void m3math_test_object_t::test<9>() - { - LLMatrix3 llmat_obj1; - LLQuaternion llmat_quat; - - LLVector3 llmat1(2.0f, 1.0f, 6.0f); - LLVector3 llmat2(1.0f, 1.0f, 3.0f); - LLVector3 llmat3(1.0f, 7.0f, 5.0f); - - llmat_obj1.setRows(llmat1, llmat2, llmat3); - llmat_quat = llmat_obj1.quaternion(); - ensure("LLMatrix3::quaternion failed ", is_approx_equal(-0.66666669f, llmat_quat.mQ[0]) && - is_approx_equal(-0.83333337f, llmat_quat.mQ[1]) && - is_approx_equal(0.0f, llmat_quat.mQ[2]) && - is_approx_equal(1.5f, llmat_quat.mQ[3])); - } - - //test case for transpose() fn. - template<> template<> - void m3math_test_object_t::test<10>() - { - LLMatrix3 llmat_obj; - - LLVector3 llvec1(1, 2, 3); - LLVector3 llvec2(3, 2, 1); - LLVector3 llvec3(2, 2, 2); - - llmat_obj.setRows(llvec1, llvec2, llvec3); - llmat_obj.transpose(); - - LLVector3 resllvec1(1, 3, 2); - LLVector3 resllvec2(2, 2, 2); - LLVector3 resllvec3(3, 1, 2); - LLMatrix3 expectedllmat_obj; - expectedllmat_obj.setRows(resllvec1, resllvec2, resllvec3); - - ensure("LLMatrix3::transpose failed ", llmat_obj == expectedllmat_obj); - } - - //test case for determinant() fn. - template<> template<> - void m3math_test_object_t::test<11>() - { - LLMatrix3 llmat_obj1; - - LLVector3 llvec1(1, 2, 3); - LLVector3 llvec2(3, 2, 1); - LLVector3 llvec3(2, 2, 2); - llmat_obj1.setRows(llvec1, llvec2, llvec3); - ensure("LLMatrix3::determinant failed ", 0.0f == llmat_obj1.determinant()); - } - - //test case for orthogonalize() fn. - template<> template<> - void m3math_test_object_t::test<12>() - { - LLMatrix3 llmat_obj; - - LLVector3 llvec1(1, 4, 3); - LLVector3 llvec2(1, 2, 0); - LLVector3 llvec3(2, 4, 2); + struct m3math_test + { + }; + typedef test_group<m3math_test> m3math_test_t; + typedef m3math_test_t::object m3math_test_object_t; + tut::m3math_test_t tut_m3math_test("m3math_h"); + + //test case for setIdentity() fn. + template<> template<> + void m3math_test_object_t::test<1>() + { + LLMatrix3 llmat3_obj; + llmat3_obj.setIdentity(); + ensure("LLMatrix3::setIdentity failed", 1.f == llmat3_obj.mMatrix[0][0] && + 0.f == llmat3_obj.mMatrix[0][1] && + 0.f == llmat3_obj.mMatrix[0][2] && + 0.f == llmat3_obj.mMatrix[1][0] && + 1.f == llmat3_obj.mMatrix[1][1] && + 0.f == llmat3_obj.mMatrix[1][2] && + 0.f == llmat3_obj.mMatrix[2][0] && + 0.f == llmat3_obj.mMatrix[2][1] && + 1.f == llmat3_obj.mMatrix[2][2]); + } + + //test case for LLMatrix3& setZero() fn. + template<> template<> + void m3math_test_object_t::test<2>() + { + LLMatrix3 llmat3_obj; + llmat3_obj.setZero(); + + ensure("LLMatrix3::setZero failed", 0.f == llmat3_obj.setZero().mMatrix[0][0] && + 0.f == llmat3_obj.setZero().mMatrix[0][1] && + 0.f == llmat3_obj.setZero().mMatrix[0][2] && + 0.f == llmat3_obj.setZero().mMatrix[1][0] && + 0.f == llmat3_obj.setZero().mMatrix[1][1] && + 0.f == llmat3_obj.setZero().mMatrix[1][2] && + 0.f == llmat3_obj.setZero().mMatrix[2][0] && + 0.f == llmat3_obj.setZero().mMatrix[2][1] && + 0.f == llmat3_obj.setZero().mMatrix[2][2]); + } + + //test case for setRows(const LLVector3 &x_axis, const LLVector3 &y_axis, const LLVector3 &z_axis) fns. + template<> template<> + void m3math_test_object_t::test<3>() + { + LLMatrix3 llmat3_obj; + LLVector3 vect1(2, 1, 4); + LLVector3 vect2(3, 5, 7); + LLVector3 vect3(6, 9, 7); + llmat3_obj.setRows(vect1, vect2, vect3); + ensure("LLVector3::setRows failed ", 2 == llmat3_obj.mMatrix[0][0] && + 1 == llmat3_obj.mMatrix[0][1] && + 4 == llmat3_obj.mMatrix[0][2] && + 3 == llmat3_obj.mMatrix[1][0] && + 5 == llmat3_obj.mMatrix[1][1] && + 7 == llmat3_obj.mMatrix[1][2] && + 6 == llmat3_obj.mMatrix[2][0] && + 9 == llmat3_obj.mMatrix[2][1] && + 7 == llmat3_obj.mMatrix[2][2]); + } + + //test case for getFwdRow(), getLeftRow(), getUpRow() fns. + template<> template<> + void m3math_test_object_t::test<4>() + { + LLMatrix3 llmat3_obj; + LLVector3 vect1(2, 1, 4); + LLVector3 vect2(3, 5, 7); + LLVector3 vect3(6, 9, 7); + llmat3_obj.setRows(vect1, vect2, vect3); + + ensure("LLVector3::getFwdRow failed ", vect1 == llmat3_obj.getFwdRow()); + ensure("LLVector3::getLeftRow failed ", vect2 == llmat3_obj.getLeftRow()); + ensure("LLVector3::getUpRow failed ", vect3 == llmat3_obj.getUpRow()); + } + + //test case for operator*(const LLMatrix3 &a, const LLMatrix3 &b) + template<> template<> + void m3math_test_object_t::test<5>() + { + LLMatrix3 llmat_obj1; + LLMatrix3 llmat_obj2; + LLMatrix3 llmat_obj3; + + LLVector3 llvec1(1, 3, 5); + LLVector3 llvec2(3, 6, 1); + LLVector3 llvec3(4, 6, 9); + + LLVector3 llvec4(1, 1, 5); + LLVector3 llvec5(3, 6, 8); + LLVector3 llvec6(8, 6, 2); + + LLVector3 llvec7(0, 0, 0); + LLVector3 llvec8(0, 0, 0); + LLVector3 llvec9(0, 0, 0); + + llmat_obj1.setRows(llvec1, llvec2, llvec3); + llmat_obj2.setRows(llvec4, llvec5, llvec6); + llmat_obj3.setRows(llvec7, llvec8, llvec9); + llmat_obj3 = llmat_obj1 * llmat_obj2; + ensure("LLMatrix3::operator*(const LLMatrix3 &a, const LLMatrix3 &b) failed", + 50 == llmat_obj3.mMatrix[0][0] && + 49 == llmat_obj3.mMatrix[0][1] && + 39 == llmat_obj3.mMatrix[0][2] && + 29 == llmat_obj3.mMatrix[1][0] && + 45 == llmat_obj3.mMatrix[1][1] && + 65 == llmat_obj3.mMatrix[1][2] && + 94 == llmat_obj3.mMatrix[2][0] && + 94 == llmat_obj3.mMatrix[2][1] && + 86 == llmat_obj3.mMatrix[2][2]); + } + + + //test case for operator*(const LLVector3 &a, const LLMatrix3 &b) + template<> template<> + void m3math_test_object_t::test<6>() + { + + LLMatrix3 llmat_obj1; + + LLVector3 llvec(1, 3, 5); + LLVector3 res_vec(0, 0, 0); + LLVector3 llvec1(1, 3, 5); + LLVector3 llvec2(3, 6, 1); + LLVector3 llvec3(4, 6, 9); + + llmat_obj1.setRows(llvec1, llvec2, llvec3); + res_vec = llvec * llmat_obj1; + + LLVector3 expected_result(30, 51, 53); + + ensure("LLMatrix3::operator*(const LLVector3 &a, const LLMatrix3 &b) failed", res_vec == expected_result); + } + + //test case for operator*(const LLVector3d &a, const LLMatrix3 &b) + template<> template<> + void m3math_test_object_t::test<7>() + { + LLMatrix3 llmat_obj1; + LLVector3d llvec3d1; + LLVector3d llvec3d2(0, 3, 4); + + LLVector3 llvec1(1, 3, 5); + LLVector3 llvec2(3, 2, 1); + LLVector3 llvec3(4, 6, 0); + + llmat_obj1.setRows(llvec1, llvec2, llvec3); + llvec3d1 = llvec3d2 * llmat_obj1; + + LLVector3d expected_result(25, 30, 3); + + ensure("LLMatrix3::operator*(const LLVector3 &a, const LLMatrix3 &b) failed", llvec3d1 == expected_result); + } + + // test case for operator==(const LLMatrix3 &a, const LLMatrix3 &b) + template<> template<> + void m3math_test_object_t::test<8>() + { + LLMatrix3 llmat_obj1; + LLMatrix3 llmat_obj2; + + LLVector3 llvec1(1, 3, 5); + LLVector3 llvec2(3, 6, 1); + LLVector3 llvec3(4, 6, 9); + + llmat_obj1.setRows(llvec1, llvec2, llvec3); + llmat_obj2.setRows(llvec1, llvec2, llvec3); + ensure("LLMatrix3::operator==(const LLMatrix3 &a, const LLMatrix3 &b) failed", llmat_obj1 == llmat_obj2); + + llmat_obj2.setRows(llvec2, llvec2, llvec3); + ensure("LLMatrix3::operator!=(const LLMatrix3 &a, const LLMatrix3 &b) failed", llmat_obj1 != llmat_obj2); + } + + //test case for quaternion() fn. + template<> template<> + void m3math_test_object_t::test<9>() + { + LLMatrix3 llmat_obj1; + LLQuaternion llmat_quat; + + LLVector3 llmat1(2.0f, 1.0f, 6.0f); + LLVector3 llmat2(1.0f, 1.0f, 3.0f); + LLVector3 llmat3(1.0f, 7.0f, 5.0f); + + llmat_obj1.setRows(llmat1, llmat2, llmat3); + llmat_quat = llmat_obj1.quaternion(); + ensure("LLMatrix3::quaternion failed ", is_approx_equal(-0.66666669f, llmat_quat.mQ[0]) && + is_approx_equal(-0.83333337f, llmat_quat.mQ[1]) && + is_approx_equal(0.0f, llmat_quat.mQ[2]) && + is_approx_equal(1.5f, llmat_quat.mQ[3])); + } + + //test case for transpose() fn. + template<> template<> + void m3math_test_object_t::test<10>() + { + LLMatrix3 llmat_obj; + + LLVector3 llvec1(1, 2, 3); + LLVector3 llvec2(3, 2, 1); + LLVector3 llvec3(2, 2, 2); + + llmat_obj.setRows(llvec1, llvec2, llvec3); + llmat_obj.transpose(); + + LLVector3 resllvec1(1, 3, 2); + LLVector3 resllvec2(2, 2, 2); + LLVector3 resllvec3(3, 1, 2); + LLMatrix3 expectedllmat_obj; + expectedllmat_obj.setRows(resllvec1, resllvec2, resllvec3); + + ensure("LLMatrix3::transpose failed ", llmat_obj == expectedllmat_obj); + } + + //test case for determinant() fn. + template<> template<> + void m3math_test_object_t::test<11>() + { + LLMatrix3 llmat_obj1; + + LLVector3 llvec1(1, 2, 3); + LLVector3 llvec2(3, 2, 1); + LLVector3 llvec3(2, 2, 2); + llmat_obj1.setRows(llvec1, llvec2, llvec3); + ensure("LLMatrix3::determinant failed ", 0.0f == llmat_obj1.determinant()); + } + + //test case for orthogonalize() fn. + template<> template<> + void m3math_test_object_t::test<12>() + { + LLMatrix3 llmat_obj; + + LLVector3 llvec1(1, 4, 3); + LLVector3 llvec2(1, 2, 0); + LLVector3 llvec3(2, 4, 2); skip("This test fails depending on architecture. Need to fix comparison operation, is_approx_equal, to work on more than one platform."); - llmat_obj.setRows(llvec1, llvec2, llvec3); - llmat_obj.orthogonalize(); - - ensure("LLMatrix3::orthogonalize failed ", - is_approx_equal(0.19611614f, llmat_obj.mMatrix[0][0]) && - is_approx_equal(0.78446454f, llmat_obj.mMatrix[0][1]) && - is_approx_equal(0.58834841f, llmat_obj.mMatrix[0][2]) && - is_approx_equal(0.47628204f, llmat_obj.mMatrix[1][0]) && - is_approx_equal(0.44826545f, llmat_obj.mMatrix[1][1]) && - is_approx_equal(-0.75644795f, llmat_obj.mMatrix[1][2]) && - is_approx_equal(-0.85714286f, llmat_obj.mMatrix[2][0]) && - is_approx_equal(0.42857143f, llmat_obj.mMatrix[2][1]) && - is_approx_equal(-0.28571429f, llmat_obj.mMatrix[2][2])); - } - - //test case for adjointTranspose() fn. - template<> template<> - void m3math_test_object_t::test<13>() - { - LLMatrix3 llmat_obj; - - LLVector3 llvec1(3, 2, 1); - LLVector3 llvec2(6, 2, 1); - LLVector3 llvec3(3, 6, 8); - - llmat_obj.setRows(llvec1, llvec2, llvec3); - llmat_obj.adjointTranspose(); - - ensure("LLMatrix3::adjointTranspose failed ", 10 == llmat_obj.mMatrix[0][0] && - -45 == llmat_obj.mMatrix[1][0] && - 30 == llmat_obj.mMatrix[2][0] && - -10 == llmat_obj.mMatrix[0][1] && - 21 == llmat_obj.mMatrix[1][1] && - -12 == llmat_obj.mMatrix[2][1] && - 0 == llmat_obj.mMatrix[0][2] && - 3 == llmat_obj.mMatrix[1][2] && - -6 == llmat_obj.mMatrix[2][2]); - } - - /* TBD: Need to add test cases for getEulerAngles() and setRot() functions */ + llmat_obj.setRows(llvec1, llvec2, llvec3); + llmat_obj.orthogonalize(); + + ensure("LLMatrix3::orthogonalize failed ", + is_approx_equal(0.19611614f, llmat_obj.mMatrix[0][0]) && + is_approx_equal(0.78446454f, llmat_obj.mMatrix[0][1]) && + is_approx_equal(0.58834841f, llmat_obj.mMatrix[0][2]) && + is_approx_equal(0.47628204f, llmat_obj.mMatrix[1][0]) && + is_approx_equal(0.44826545f, llmat_obj.mMatrix[1][1]) && + is_approx_equal(-0.75644795f, llmat_obj.mMatrix[1][2]) && + is_approx_equal(-0.85714286f, llmat_obj.mMatrix[2][0]) && + is_approx_equal(0.42857143f, llmat_obj.mMatrix[2][1]) && + is_approx_equal(-0.28571429f, llmat_obj.mMatrix[2][2])); + } + + //test case for adjointTranspose() fn. + template<> template<> + void m3math_test_object_t::test<13>() + { + LLMatrix3 llmat_obj; + + LLVector3 llvec1(3, 2, 1); + LLVector3 llvec2(6, 2, 1); + LLVector3 llvec3(3, 6, 8); + + llmat_obj.setRows(llvec1, llvec2, llvec3); + llmat_obj.adjointTranspose(); + + ensure("LLMatrix3::adjointTranspose failed ", 10 == llmat_obj.mMatrix[0][0] && + -45 == llmat_obj.mMatrix[1][0] && + 30 == llmat_obj.mMatrix[2][0] && + -10 == llmat_obj.mMatrix[0][1] && + 21 == llmat_obj.mMatrix[1][1] && + -12 == llmat_obj.mMatrix[2][1] && + 0 == llmat_obj.mMatrix[0][2] && + 3 == llmat_obj.mMatrix[1][2] && + -6 == llmat_obj.mMatrix[2][2]); + } + + /* TBD: Need to add test cases for getEulerAngles() and setRot() functions */ } diff --git a/indra/llmath/tests/mathmisc_test.cpp b/indra/llmath/tests/mathmisc_test.cpp index 46bdc1cf95..63d35bee96 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 06e1292941..229edc56f5 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 0fb52394a5..11298bebd2 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 0c8c01a77a..fc9e7d9dc1 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 090f399e62..93d9f4a006 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/v4color_test.cpp b/indra/llmath/tests/v4color_test.cpp index d7eec3c87f..3b3adbda0d 100644 --- a/indra/llmath/tests/v4color_test.cpp +++ b/indra/llmath/tests/v4color_test.cpp @@ -7,21 +7,21 @@ * $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$ */ @@ -38,322 +38,322 @@ namespace tut { - struct v4color_data - { - }; - typedef test_group<v4color_data> v4color_test; - typedef v4color_test::object v4color_object; - tut::v4color_test v4color_testcase("v4color_h"); - - template<> template<> - void v4color_object::test<1>() - { - LLColor4 llcolor4; - ensure("1:LLColor4:Fail to initialize ", ((0 == llcolor4.mV[VX]) && (0 == llcolor4.mV[VY]) && (0 == llcolor4.mV[VZ])&& (1.0f == llcolor4.mV[VW]))); - - F32 r = 0x20, g = 0xFFFF, b = 0xFF, a = 0xAF; - LLColor4 llcolor4a(r,g,b); - ensure("2:LLColor4:Fail to initialize ", ((r == llcolor4a.mV[VX]) && (g == llcolor4a.mV[VY]) && (b == llcolor4a.mV[VZ])&& (1.0f == llcolor4a.mV[VW]))); - - LLColor4 llcolor4b(r,g,b,a); - ensure("3:LLColor4:Fail to initialize ", ((r == llcolor4b.mV[VX]) && (g == llcolor4b.mV[VY]) && (b == llcolor4b.mV[VZ])&& (a == llcolor4b.mV[VW]))); - - const F32 vec[4] = {.112f ,23.2f, -4.2f, -.0001f}; - LLColor4 llcolor4c(vec); - ensure("4:LLColor4:Fail to initialize ", ((vec[0] == llcolor4c.mV[VX]) && (vec[1] == llcolor4c.mV[VY]) && (vec[2] == llcolor4c.mV[VZ])&& (vec[3] == llcolor4c.mV[VW]))); - - LLColor3 llcolor3(-2.23f,1.01f,42.3f); - F32 val = -.1f; - LLColor4 llcolor4d(llcolor3,val); - ensure("5:LLColor4:Fail to initialize ", ((llcolor3.mV[VX] == llcolor4d.mV[VX]) && (llcolor3.mV[VY] == llcolor4d.mV[VY]) && (llcolor3.mV[VZ] == llcolor4d.mV[VZ])&& (val == llcolor4d.mV[VW]))); - - LLSD sd = llcolor4d.getValue(); - LLColor4 llcolor4e(sd); - ensure_equals("6:LLColor4:(LLSD) failed ", llcolor4d, llcolor4e); - - U8 r1 = 0xF2, g1 = 0xFA, b1 = 0xBF; - LLColor4U color4u(r1,g1,b1); - LLColor4 llcolor4g(color4u); - const F32 SCALE = 1.f/255.f; - F32 r2 = r1*SCALE, g2 = g1* SCALE, b2 = b1* SCALE; - ensure("7:LLColor4:Fail to initialize ", ((r2 == llcolor4g.mV[VX]) && (g2 == llcolor4g.mV[VY]) && (b2 == llcolor4g.mV[VZ]))); - } - - template<> template<> - void v4color_object::test<2>() - { - LLColor4 llcolor(1.0, 2.0, 3.0, 4.0); - LLSD llsd = llcolor.getValue(); - LLColor4 llcolor4(llsd), llcolor4a; - llcolor4a.setValue(llsd); - ensure("setValue: failed", (llcolor4 == llcolor4a)); - LLSD sd = llcolor4a.getValue(); - LLColor4 llcolor4b(sd); - ensure("getValue: Failed ", (llcolor4b == llcolor4a)); - } - - template<> template<> - void v4color_object::test<3>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF,a = 0xAF; - LLColor4 llcolor4(r,g,b,a); - llcolor4.setToBlack(); - ensure("setToBlack:Fail to set the black ", ((0 == llcolor4.mV[VX]) && (0 == llcolor4.mV[VY]) && (0 == llcolor4.mV[VZ])&& (1.0f == llcolor4.mV[VW]))); - - llcolor4.setToWhite(); - ensure("setToWhite:Fail to set the white ", ((1.f == llcolor4.mV[VX]) && (1.f == llcolor4.mV[VY]) && (1.f == llcolor4.mV[VZ])&& (1.0f == llcolor4.mV[VW]))); - } - - template<> template<> - void v4color_object::test<4>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF, a = 0xAF; - LLColor4 llcolor4; - llcolor4.setVec(r,g,b); - ensure("1:setVec:Fail to set the values ", ((r == llcolor4.mV[VX]) && (g == llcolor4.mV[VY]) && (b == llcolor4.mV[VZ])&& (1.f == llcolor4.mV[VW]))); - - llcolor4.setVec(r,g,b,a); - ensure("2:setVec:Fail to set the values ", ((r == llcolor4.mV[VX]) && (g == llcolor4.mV[VY]) && (b == llcolor4.mV[VZ])&& (a == llcolor4.mV[VW]))); - - LLColor4 llcolor4a; - llcolor4a.setVec(llcolor4); - ensure_equals("3:setVec:Fail to set the values ", llcolor4a,llcolor4); - - LLColor3 llcolor3(-2.23f,1.01f,42.3f); - llcolor4a.setVec(llcolor3); - ensure("4:setVec:Fail to set the values ", ((llcolor3.mV[VX] == llcolor4a.mV[VX]) && (llcolor3.mV[VY] == llcolor4a.mV[VY]) && (llcolor3.mV[VZ] == llcolor4a.mV[VZ]))); - - F32 val = -.33f; - llcolor4a.setVec(llcolor3,val); - ensure("4:setVec:Fail to set the values ", ((llcolor3.mV[VX] == llcolor4a.mV[VX]) && (llcolor3.mV[VY] == llcolor4a.mV[VY]) && (llcolor3.mV[VZ] == llcolor4a.mV[VZ]) && (val == llcolor4a.mV[VW]))); - - const F32 vec[4] = {.112f ,23.2f, -4.2f, -.0001f}; - LLColor4 llcolor4c; - llcolor4c.setVec(vec); - ensure("5:setVec:Fail to initialize ", ((vec[0] == llcolor4c.mV[VX]) && (vec[1] == llcolor4c.mV[VY]) && (vec[2] == llcolor4c.mV[VZ])&& (vec[3] == llcolor4c.mV[VW]))); - - U8 r1 = 0xF2, g1 = 0xFA, b1= 0xBF; - LLColor4U color4u(r1,g1,b1); - llcolor4.setVec(color4u); - const F32 SCALE = 1.f/255.f; - F32 r2 = r1*SCALE, g2 = g1* SCALE, b2 = b1* SCALE; - ensure("6:setVec:Fail to initialize ", ((r2 == llcolor4.mV[VX]) && (g2 == llcolor4.mV[VY]) && (b2 == llcolor4.mV[VZ]))); - } - - template<> template<> - void v4color_object::test<5>() - { - F32 alpha = 0xAF; - LLColor4 llcolor4; - llcolor4.setAlpha(alpha); - ensure("setAlpha:Fail to initialize ", (alpha == llcolor4.mV[VW])); - } - - template<> template<> - void v4color_object::test<6>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF; - LLColor4 llcolor4(r,g,b); - ensure("magVecSquared:Fail ", is_approx_equal(llcolor4.magVecSquared(), (r*r + g*g + b*b))); - ensure("magVec:Fail ", is_approx_equal(llcolor4.magVec(), (F32) sqrt(r*r + g*g + b*b))); - } - - template<> template<> - void v4color_object::test<7>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF; - LLColor4 llcolor4(r,g,b); - F32 vecMag = llcolor4.normVec(); - F32 mag = (F32) sqrt(r*r + g*g + b*b); - F32 oomag = 1.f / mag; - F32 val1 = r * oomag, val2 = g * oomag, val3 = b * oomag; - ensure("1:normVec failed ", (is_approx_equal(val1, llcolor4.mV[0]) && is_approx_equal(val2, llcolor4.mV[1]) && is_approx_equal(val3, llcolor4.mV[2]) && is_approx_equal(vecMag, mag))); - } - - template<> template<> - void v4color_object::test<8>() - { - LLColor4 llcolor4; - ensure("1:isOpaque failed ",(1 == llcolor4.isOpaque())); - F32 r = 0x20, g = 0xFFFF, b = 0xFF,a = 1.f; - llcolor4.setVec(r,g,b,a); - ensure("2:isOpaque failed ",(1 == llcolor4.isOpaque())); - a = 2.f; - llcolor4.setVec(r,g,b,a); - ensure("3:isOpaque failed ",(0 == llcolor4.isOpaque())); - } - - template<> template<> - void v4color_object::test<9>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF; - LLColor4 llcolor4(r,g,b); - ensure("1:operator [] failed",( r == llcolor4[0])); - ensure("2:operator [] failed",( g == llcolor4[1])); - ensure("3:operator [] failed",( b == llcolor4[2])); - - r = 0xA20, g = 0xFBFF, b = 0xFFF; - llcolor4.setVec(r,g,b); - F32 &ref1 = llcolor4[0]; - ensure("4:operator [] failed",( ref1 == llcolor4[0])); - F32 &ref2 = llcolor4[1]; - ensure("5:operator [] failed",( ref2 == llcolor4[1])); - F32 &ref3 = llcolor4[2]; - ensure("6:operator [] failed",( ref3 == llcolor4[2])); - } - - template<> template<> - void v4color_object::test<10>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF; - LLColor3 llcolor3(r,g,b); - LLColor4 llcolor4a,llcolor4b; - llcolor4a = llcolor3; - ensure("Operator=:Fail to initialize ", ((llcolor3.mV[0] == llcolor4a.mV[VX]) && (llcolor3.mV[1] == llcolor4a.mV[VY]) && (llcolor3.mV[2] == llcolor4a.mV[VZ]))); - LLSD sd = llcolor4a.getValue(); - llcolor4b = LLColor4(sd); - ensure_equals("Operator= LLSD:Fail ", llcolor4a, llcolor4b); - } - - template<> template<> - void v4color_object::test<11>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF; - std::ostringstream stream1, stream2; - LLColor4 llcolor4a(r,g,b),llcolor4b; - stream1 << llcolor4a; - llcolor4b.setVec(r,g,b); - stream2 << llcolor4b; - ensure("operator << failed ", (stream1.str() == stream2.str())); - } - - template<> template<> - void v4color_object::test<12>() - { - F32 r1 = 0x20, g1 = 0xFFFF, b1 = 0xFF; - F32 r2 = 0xABF, g2 = 0xFB, b2 = 0xFFF; - LLColor4 llcolor4a(r1,g1,b1),llcolor4b(r2,g2,b2),llcolor4c; - llcolor4c = llcolor4b + llcolor4a; - ensure("operator+:Fail to Add the values ", (is_approx_equal(r1+r2,llcolor4c.mV[VX]) && is_approx_equal(g1+g2,llcolor4c.mV[VY]) && is_approx_equal(b1+b2,llcolor4c.mV[VZ]))); - - llcolor4b += llcolor4a; - ensure("operator+=:Fail to Add the values ", (is_approx_equal(r1+r2,llcolor4b.mV[VX]) && is_approx_equal(g1+g2,llcolor4b.mV[VY]) && is_approx_equal(b1+b2,llcolor4b.mV[VZ]))); - } - - template<> template<> - void v4color_object::test<13>() - { - F32 r1 = 0x20, g1 = 0xFFFF, b1 = 0xFF; - F32 r2 = 0xABF, g2 = 0xFB, b2 = 0xFFF; - LLColor4 llcolor4a(r1,g1,b1),llcolor4b(r2,g2,b2),llcolor4c; - llcolor4c = llcolor4a - llcolor4b; - ensure("operator-:Fail to subtract the values ", (is_approx_equal(r1-r2,llcolor4c.mV[VX]) && is_approx_equal(g1-g2,llcolor4c.mV[VY]) && is_approx_equal(b1-b2,llcolor4c.mV[VZ]))); - - llcolor4a -= llcolor4b; - ensure("operator-=:Fail to subtract the values ", (is_approx_equal(r1-r2,llcolor4a.mV[VX]) && is_approx_equal(g1-g2,llcolor4a.mV[VY]) && is_approx_equal(b1-b2,llcolor4a.mV[VZ]))); - } - - template<> template<> - void v4color_object::test<14>() - { - F32 r1 = 0x20, g1 = 0xFFFF, b1 = 0xFF; - F32 r2 = 0xABF, g2 = 0xFB, b2 = 0xFFF; - LLColor4 llcolor4a(r1,g1,b1),llcolor4b(r2,g2,b2),llcolor4c; - llcolor4c = llcolor4a * llcolor4b; - ensure("1:operator*:Fail to multiply the values", (is_approx_equal(r1*r2,llcolor4c.mV[VX]) && is_approx_equal(g1*g2,llcolor4c.mV[VY]) && is_approx_equal(b1*b2,llcolor4c.mV[VZ]))); - - F32 mulVal = 3.33f; - llcolor4c = llcolor4a * mulVal; - ensure("2:operator*:Fail ", (is_approx_equal(r1*mulVal,llcolor4c.mV[VX]) && is_approx_equal(g1*mulVal,llcolor4c.mV[VY]) && is_approx_equal(b1*mulVal,llcolor4c.mV[VZ]))); - llcolor4c = mulVal * llcolor4a; - ensure("3:operator*:Fail to multiply the values", (is_approx_equal(r1*mulVal,llcolor4c.mV[VX]) && is_approx_equal(g1*mulVal,llcolor4c.mV[VY]) && is_approx_equal(b1*mulVal,llcolor4c.mV[VZ]))); - - llcolor4a *= mulVal; - ensure("4:operator*=:Fail to multiply the values ", (is_approx_equal(r1*mulVal,llcolor4a.mV[VX]) && is_approx_equal(g1*mulVal,llcolor4a.mV[VY]) && is_approx_equal(b1*mulVal,llcolor4a.mV[VZ]))); - - LLColor4 llcolor4d(r1,g1,b1),llcolor4e(r2,g2,b2); - llcolor4e *= llcolor4d; - ensure("5:operator*=:Fail to multiply the values ", (is_approx_equal(r1*r2,llcolor4e.mV[VX]) && is_approx_equal(g1*g2,llcolor4e.mV[VY]) && is_approx_equal(b1*b2,llcolor4e.mV[VZ]))); - } - - template<> template<> - void v4color_object::test<15>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF,a = 0x30; - F32 div = 12.345f; - LLColor4 llcolor4a(r,g,b,a),llcolor4b; - llcolor4b = llcolor4a % div;//chnage only alpha value nor r,g,b; - ensure("1operator%:Fail ", (is_approx_equal(r,llcolor4b.mV[VX]) && is_approx_equal(g,llcolor4b.mV[VY]) && is_approx_equal(b,llcolor4b.mV[VZ])&& is_approx_equal(div*a,llcolor4b.mV[VW]))); - - llcolor4b = div % llcolor4a; - ensure("2operator%:Fail ", (is_approx_equal(r,llcolor4b.mV[VX]) && is_approx_equal(g,llcolor4b.mV[VY]) && is_approx_equal(b,llcolor4b.mV[VZ])&& is_approx_equal(div*a,llcolor4b.mV[VW]))); - - llcolor4a %= div; - ensure("operator%=:Fail ", (is_approx_equal(a*div,llcolor4a.mV[VW]))); - } - - template<> template<> - void v4color_object::test<16>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF,a = 0x30; - LLColor4 llcolor4a(r,g,b,a),llcolor4b; - llcolor4b = llcolor4a; - ensure("1:operator== failed to ensure the equality ", (llcolor4b == llcolor4a)); - F32 r1 = 0x2, g1 = 0xFF, b1 = 0xFA; - LLColor3 llcolor3(r1,g1,b1); - llcolor4b = llcolor3; - ensure("2:operator== failed to ensure the equality ", (llcolor4b == llcolor3)); - ensure("2:operator!= failed to ensure the equality ", (llcolor4a != llcolor3)); - } - - template<> template<> - void v4color_object::test<17>() - { - F32 r = 0x20, g = 0xFFFF, b = 0xFF; - LLColor4 llcolor4a(r,g,b),llcolor4b; - LLColor3 llcolor3 = vec4to3(llcolor4a); - ensure("vec4to3:Fail to convert vec4 to vec3 ", (is_approx_equal(llcolor3.mV[VX],llcolor4a.mV[VX]) && is_approx_equal(llcolor3.mV[VY],llcolor4a.mV[VY]) && is_approx_equal(llcolor3.mV[VZ],llcolor4a.mV[VZ]))); - llcolor4b = vec3to4(llcolor3); - ensure_equals("vec3to4:Fail to convert vec3 to vec4 ", llcolor4b, llcolor4a); - } - - template<> template<> - void v4color_object::test<18>() - { - F32 r1 = 0x20, g1 = 0xFFFF, b1 = 0xFF, val = 0x20; - F32 r2 = 0xABF, g2 = 0xFB, b2 = 0xFFF; - LLColor4 llcolor4a(r1,g1,b1),llcolor4b(r2,g2,b2),llcolor4c; - llcolor4c = lerp(llcolor4a,llcolor4b,val); - ensure("lerp:Fail ", (is_approx_equal(r1 + (r2 - r1)* val,llcolor4c.mV[VX]) && is_approx_equal(g1 + (g2 - g1)* val,llcolor4c.mV[VY]) && is_approx_equal(b1 + (b2 - b1)* val,llcolor4c.mV[VZ]))); - } - - template<> template<> - void v4color_object::test<19>() - { - F32 r = 12.0f, g = -2.3f, b = 1.32f, a = 5.0f; - LLColor4 llcolor4a(r,g,b,a),llcolor4b; - std::string color("red"); - LLColor4::parseColor(color, &llcolor4b); - ensure_equals("1:parseColor() failed to parse the color value ", llcolor4b, LLColor4::red); - - color = "12.0, -2.3, 1.32, 5.0"; - LLColor4::parseColor(color, &llcolor4b); - llcolor4a = llcolor4a * (1.f / 255.f); - ensure_equals("2:parseColor() failed to parse the color value ", llcolor4a,llcolor4b); - - color = "yellow5"; - llcolor4a.setVec(r,g,b); - LLColor4::parseColor(color, &llcolor4a); - ensure_equals("3:parseColor() failed to parse the color value ", llcolor4a, LLColor4::yellow5); - } - - template<> template<> - void v4color_object::test<20>() - { - F32 r = 12.0f, g = -2.3f, b = 1.32f, a = 5.0f; - LLColor4 llcolor4a(r,g,b,a),llcolor4b; - std::string color("12.0, -2.3, 1.32, 5.0"); - LLColor4::parseColor4(color, &llcolor4b); - ensure_equals("parseColor4() failed to parse the color value ", llcolor4a, llcolor4b); - } + struct v4color_data + { + }; + typedef test_group<v4color_data> v4color_test; + typedef v4color_test::object v4color_object; + tut::v4color_test v4color_testcase("v4color_h"); + + template<> template<> + void v4color_object::test<1>() + { + LLColor4 llcolor4; + ensure("1:LLColor4:Fail to initialize ", ((0 == llcolor4.mV[VX]) && (0 == llcolor4.mV[VY]) && (0 == llcolor4.mV[VZ])&& (1.0f == llcolor4.mV[VW]))); + + F32 r = 0x20, g = 0xFFFF, b = 0xFF, a = 0xAF; + LLColor4 llcolor4a(r,g,b); + ensure("2:LLColor4:Fail to initialize ", ((r == llcolor4a.mV[VX]) && (g == llcolor4a.mV[VY]) && (b == llcolor4a.mV[VZ])&& (1.0f == llcolor4a.mV[VW]))); + + LLColor4 llcolor4b(r,g,b,a); + ensure("3:LLColor4:Fail to initialize ", ((r == llcolor4b.mV[VX]) && (g == llcolor4b.mV[VY]) && (b == llcolor4b.mV[VZ])&& (a == llcolor4b.mV[VW]))); + + const F32 vec[4] = {.112f ,23.2f, -4.2f, -.0001f}; + LLColor4 llcolor4c(vec); + ensure("4:LLColor4:Fail to initialize ", ((vec[0] == llcolor4c.mV[VX]) && (vec[1] == llcolor4c.mV[VY]) && (vec[2] == llcolor4c.mV[VZ])&& (vec[3] == llcolor4c.mV[VW]))); + + LLColor3 llcolor3(-2.23f,1.01f,42.3f); + F32 val = -.1f; + LLColor4 llcolor4d(llcolor3,val); + ensure("5:LLColor4:Fail to initialize ", ((llcolor3.mV[VX] == llcolor4d.mV[VX]) && (llcolor3.mV[VY] == llcolor4d.mV[VY]) && (llcolor3.mV[VZ] == llcolor4d.mV[VZ])&& (val == llcolor4d.mV[VW]))); + + LLSD sd = llcolor4d.getValue(); + LLColor4 llcolor4e(sd); + ensure_equals("6:LLColor4:(LLSD) failed ", llcolor4d, llcolor4e); + + U8 r1 = 0xF2, g1 = 0xFA, b1 = 0xBF; + LLColor4U color4u(r1,g1,b1); + LLColor4 llcolor4g(color4u); + const F32 SCALE = 1.f/255.f; + F32 r2 = r1*SCALE, g2 = g1* SCALE, b2 = b1* SCALE; + ensure("7:LLColor4:Fail to initialize ", ((r2 == llcolor4g.mV[VX]) && (g2 == llcolor4g.mV[VY]) && (b2 == llcolor4g.mV[VZ]))); + } + + template<> template<> + void v4color_object::test<2>() + { + LLColor4 llcolor(1.0, 2.0, 3.0, 4.0); + LLSD llsd = llcolor.getValue(); + LLColor4 llcolor4(llsd), llcolor4a; + llcolor4a.setValue(llsd); + ensure("setValue: failed", (llcolor4 == llcolor4a)); + LLSD sd = llcolor4a.getValue(); + LLColor4 llcolor4b(sd); + ensure("getValue: Failed ", (llcolor4b == llcolor4a)); + } + + template<> template<> + void v4color_object::test<3>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF,a = 0xAF; + LLColor4 llcolor4(r,g,b,a); + llcolor4.setToBlack(); + ensure("setToBlack:Fail to set the black ", ((0 == llcolor4.mV[VX]) && (0 == llcolor4.mV[VY]) && (0 == llcolor4.mV[VZ])&& (1.0f == llcolor4.mV[VW]))); + + llcolor4.setToWhite(); + ensure("setToWhite:Fail to set the white ", ((1.f == llcolor4.mV[VX]) && (1.f == llcolor4.mV[VY]) && (1.f == llcolor4.mV[VZ])&& (1.0f == llcolor4.mV[VW]))); + } + + template<> template<> + void v4color_object::test<4>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF, a = 0xAF; + LLColor4 llcolor4; + llcolor4.setVec(r,g,b); + ensure("1:setVec:Fail to set the values ", ((r == llcolor4.mV[VX]) && (g == llcolor4.mV[VY]) && (b == llcolor4.mV[VZ])&& (1.f == llcolor4.mV[VW]))); + + llcolor4.setVec(r,g,b,a); + ensure("2:setVec:Fail to set the values ", ((r == llcolor4.mV[VX]) && (g == llcolor4.mV[VY]) && (b == llcolor4.mV[VZ])&& (a == llcolor4.mV[VW]))); + + LLColor4 llcolor4a; + llcolor4a.setVec(llcolor4); + ensure_equals("3:setVec:Fail to set the values ", llcolor4a,llcolor4); + + LLColor3 llcolor3(-2.23f,1.01f,42.3f); + llcolor4a.setVec(llcolor3); + ensure("4:setVec:Fail to set the values ", ((llcolor3.mV[VX] == llcolor4a.mV[VX]) && (llcolor3.mV[VY] == llcolor4a.mV[VY]) && (llcolor3.mV[VZ] == llcolor4a.mV[VZ]))); + + F32 val = -.33f; + llcolor4a.setVec(llcolor3,val); + ensure("4:setVec:Fail to set the values ", ((llcolor3.mV[VX] == llcolor4a.mV[VX]) && (llcolor3.mV[VY] == llcolor4a.mV[VY]) && (llcolor3.mV[VZ] == llcolor4a.mV[VZ]) && (val == llcolor4a.mV[VW]))); + + const F32 vec[4] = {.112f ,23.2f, -4.2f, -.0001f}; + LLColor4 llcolor4c; + llcolor4c.setVec(vec); + ensure("5:setVec:Fail to initialize ", ((vec[0] == llcolor4c.mV[VX]) && (vec[1] == llcolor4c.mV[VY]) && (vec[2] == llcolor4c.mV[VZ])&& (vec[3] == llcolor4c.mV[VW]))); + + U8 r1 = 0xF2, g1 = 0xFA, b1= 0xBF; + LLColor4U color4u(r1,g1,b1); + llcolor4.setVec(color4u); + const F32 SCALE = 1.f/255.f; + F32 r2 = r1*SCALE, g2 = g1* SCALE, b2 = b1* SCALE; + ensure("6:setVec:Fail to initialize ", ((r2 == llcolor4.mV[VX]) && (g2 == llcolor4.mV[VY]) && (b2 == llcolor4.mV[VZ]))); + } + + template<> template<> + void v4color_object::test<5>() + { + F32 alpha = 0xAF; + LLColor4 llcolor4; + llcolor4.setAlpha(alpha); + ensure("setAlpha:Fail to initialize ", (alpha == llcolor4.mV[VW])); + } + + template<> template<> + void v4color_object::test<6>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF; + LLColor4 llcolor4(r,g,b); + ensure("magVecSquared:Fail ", is_approx_equal(llcolor4.magVecSquared(), (r*r + g*g + b*b))); + ensure("magVec:Fail ", is_approx_equal(llcolor4.magVec(), (F32) sqrt(r*r + g*g + b*b))); + } + + template<> template<> + void v4color_object::test<7>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF; + LLColor4 llcolor4(r,g,b); + F32 vecMag = llcolor4.normVec(); + F32 mag = (F32) sqrt(r*r + g*g + b*b); + F32 oomag = 1.f / mag; + F32 val1 = r * oomag, val2 = g * oomag, val3 = b * oomag; + ensure("1:normVec failed ", (is_approx_equal(val1, llcolor4.mV[0]) && is_approx_equal(val2, llcolor4.mV[1]) && is_approx_equal(val3, llcolor4.mV[2]) && is_approx_equal(vecMag, mag))); + } + + template<> template<> + void v4color_object::test<8>() + { + LLColor4 llcolor4; + ensure("1:isOpaque failed ",(1 == llcolor4.isOpaque())); + F32 r = 0x20, g = 0xFFFF, b = 0xFF,a = 1.f; + llcolor4.setVec(r,g,b,a); + ensure("2:isOpaque failed ",(1 == llcolor4.isOpaque())); + a = 2.f; + llcolor4.setVec(r,g,b,a); + ensure("3:isOpaque failed ",(0 == llcolor4.isOpaque())); + } + + template<> template<> + void v4color_object::test<9>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF; + LLColor4 llcolor4(r,g,b); + ensure("1:operator [] failed",( r == llcolor4[0])); + ensure("2:operator [] failed",( g == llcolor4[1])); + ensure("3:operator [] failed",( b == llcolor4[2])); + + r = 0xA20, g = 0xFBFF, b = 0xFFF; + llcolor4.setVec(r,g,b); + F32 &ref1 = llcolor4[0]; + ensure("4:operator [] failed",( ref1 == llcolor4[0])); + F32 &ref2 = llcolor4[1]; + ensure("5:operator [] failed",( ref2 == llcolor4[1])); + F32 &ref3 = llcolor4[2]; + ensure("6:operator [] failed",( ref3 == llcolor4[2])); + } + + template<> template<> + void v4color_object::test<10>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF; + LLColor3 llcolor3(r,g,b); + LLColor4 llcolor4a,llcolor4b; + llcolor4a = llcolor3; + ensure("Operator=:Fail to initialize ", ((llcolor3.mV[0] == llcolor4a.mV[VX]) && (llcolor3.mV[1] == llcolor4a.mV[VY]) && (llcolor3.mV[2] == llcolor4a.mV[VZ]))); + LLSD sd = llcolor4a.getValue(); + llcolor4b = LLColor4(sd); + ensure_equals("Operator= LLSD:Fail ", llcolor4a, llcolor4b); + } + + template<> template<> + void v4color_object::test<11>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF; + std::ostringstream stream1, stream2; + LLColor4 llcolor4a(r,g,b),llcolor4b; + stream1 << llcolor4a; + llcolor4b.setVec(r,g,b); + stream2 << llcolor4b; + ensure("operator << failed ", (stream1.str() == stream2.str())); + } + + template<> template<> + void v4color_object::test<12>() + { + F32 r1 = 0x20, g1 = 0xFFFF, b1 = 0xFF; + F32 r2 = 0xABF, g2 = 0xFB, b2 = 0xFFF; + LLColor4 llcolor4a(r1,g1,b1),llcolor4b(r2,g2,b2),llcolor4c; + llcolor4c = llcolor4b + llcolor4a; + ensure("operator+:Fail to Add the values ", (is_approx_equal(r1+r2,llcolor4c.mV[VX]) && is_approx_equal(g1+g2,llcolor4c.mV[VY]) && is_approx_equal(b1+b2,llcolor4c.mV[VZ]))); + + llcolor4b += llcolor4a; + ensure("operator+=:Fail to Add the values ", (is_approx_equal(r1+r2,llcolor4b.mV[VX]) && is_approx_equal(g1+g2,llcolor4b.mV[VY]) && is_approx_equal(b1+b2,llcolor4b.mV[VZ]))); + } + + template<> template<> + void v4color_object::test<13>() + { + F32 r1 = 0x20, g1 = 0xFFFF, b1 = 0xFF; + F32 r2 = 0xABF, g2 = 0xFB, b2 = 0xFFF; + LLColor4 llcolor4a(r1,g1,b1),llcolor4b(r2,g2,b2),llcolor4c; + llcolor4c = llcolor4a - llcolor4b; + ensure("operator-:Fail to subtract the values ", (is_approx_equal(r1-r2,llcolor4c.mV[VX]) && is_approx_equal(g1-g2,llcolor4c.mV[VY]) && is_approx_equal(b1-b2,llcolor4c.mV[VZ]))); + + llcolor4a -= llcolor4b; + ensure("operator-=:Fail to subtract the values ", (is_approx_equal(r1-r2,llcolor4a.mV[VX]) && is_approx_equal(g1-g2,llcolor4a.mV[VY]) && is_approx_equal(b1-b2,llcolor4a.mV[VZ]))); + } + + template<> template<> + void v4color_object::test<14>() + { + F32 r1 = 0x20, g1 = 0xFFFF, b1 = 0xFF; + F32 r2 = 0xABF, g2 = 0xFB, b2 = 0xFFF; + LLColor4 llcolor4a(r1,g1,b1),llcolor4b(r2,g2,b2),llcolor4c; + llcolor4c = llcolor4a * llcolor4b; + ensure("1:operator*:Fail to multiply the values", (is_approx_equal(r1*r2,llcolor4c.mV[VX]) && is_approx_equal(g1*g2,llcolor4c.mV[VY]) && is_approx_equal(b1*b2,llcolor4c.mV[VZ]))); + + F32 mulVal = 3.33f; + llcolor4c = llcolor4a * mulVal; + ensure("2:operator*:Fail ", (is_approx_equal(r1*mulVal,llcolor4c.mV[VX]) && is_approx_equal(g1*mulVal,llcolor4c.mV[VY]) && is_approx_equal(b1*mulVal,llcolor4c.mV[VZ]))); + llcolor4c = mulVal * llcolor4a; + ensure("3:operator*:Fail to multiply the values", (is_approx_equal(r1*mulVal,llcolor4c.mV[VX]) && is_approx_equal(g1*mulVal,llcolor4c.mV[VY]) && is_approx_equal(b1*mulVal,llcolor4c.mV[VZ]))); + + llcolor4a *= mulVal; + ensure("4:operator*=:Fail to multiply the values ", (is_approx_equal(r1*mulVal,llcolor4a.mV[VX]) && is_approx_equal(g1*mulVal,llcolor4a.mV[VY]) && is_approx_equal(b1*mulVal,llcolor4a.mV[VZ]))); + + LLColor4 llcolor4d(r1,g1,b1),llcolor4e(r2,g2,b2); + llcolor4e *= llcolor4d; + ensure("5:operator*=:Fail to multiply the values ", (is_approx_equal(r1*r2,llcolor4e.mV[VX]) && is_approx_equal(g1*g2,llcolor4e.mV[VY]) && is_approx_equal(b1*b2,llcolor4e.mV[VZ]))); + } + + template<> template<> + void v4color_object::test<15>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF,a = 0x30; + F32 div = 12.345f; + LLColor4 llcolor4a(r,g,b,a),llcolor4b; + llcolor4b = llcolor4a % div;//chnage only alpha value nor r,g,b; + ensure("1operator%:Fail ", (is_approx_equal(r,llcolor4b.mV[VX]) && is_approx_equal(g,llcolor4b.mV[VY]) && is_approx_equal(b,llcolor4b.mV[VZ])&& is_approx_equal(div*a,llcolor4b.mV[VW]))); + + llcolor4b = div % llcolor4a; + ensure("2operator%:Fail ", (is_approx_equal(r,llcolor4b.mV[VX]) && is_approx_equal(g,llcolor4b.mV[VY]) && is_approx_equal(b,llcolor4b.mV[VZ])&& is_approx_equal(div*a,llcolor4b.mV[VW]))); + + llcolor4a %= div; + ensure("operator%=:Fail ", (is_approx_equal(a*div,llcolor4a.mV[VW]))); + } + + template<> template<> + void v4color_object::test<16>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF,a = 0x30; + LLColor4 llcolor4a(r,g,b,a),llcolor4b; + llcolor4b = llcolor4a; + ensure("1:operator== failed to ensure the equality ", (llcolor4b == llcolor4a)); + F32 r1 = 0x2, g1 = 0xFF, b1 = 0xFA; + LLColor3 llcolor3(r1,g1,b1); + llcolor4b = llcolor3; + ensure("2:operator== failed to ensure the equality ", (llcolor4b == llcolor3)); + ensure("2:operator!= failed to ensure the equality ", (llcolor4a != llcolor3)); + } + + template<> template<> + void v4color_object::test<17>() + { + F32 r = 0x20, g = 0xFFFF, b = 0xFF; + LLColor4 llcolor4a(r,g,b),llcolor4b; + LLColor3 llcolor3 = vec4to3(llcolor4a); + ensure("vec4to3:Fail to convert vec4 to vec3 ", (is_approx_equal(llcolor3.mV[VX],llcolor4a.mV[VX]) && is_approx_equal(llcolor3.mV[VY],llcolor4a.mV[VY]) && is_approx_equal(llcolor3.mV[VZ],llcolor4a.mV[VZ]))); + llcolor4b = vec3to4(llcolor3); + ensure_equals("vec3to4:Fail to convert vec3 to vec4 ", llcolor4b, llcolor4a); + } + + template<> template<> + void v4color_object::test<18>() + { + F32 r1 = 0x20, g1 = 0xFFFF, b1 = 0xFF, val = 0x20; + F32 r2 = 0xABF, g2 = 0xFB, b2 = 0xFFF; + LLColor4 llcolor4a(r1,g1,b1),llcolor4b(r2,g2,b2),llcolor4c; + llcolor4c = lerp(llcolor4a,llcolor4b,val); + ensure("lerp:Fail ", (is_approx_equal(r1 + (r2 - r1)* val,llcolor4c.mV[VX]) && is_approx_equal(g1 + (g2 - g1)* val,llcolor4c.mV[VY]) && is_approx_equal(b1 + (b2 - b1)* val,llcolor4c.mV[VZ]))); + } + + template<> template<> + void v4color_object::test<19>() + { + F32 r = 12.0f, g = -2.3f, b = 1.32f, a = 5.0f; + LLColor4 llcolor4a(r,g,b,a),llcolor4b; + std::string color("red"); + LLColor4::parseColor(color, &llcolor4b); + ensure_equals("1:parseColor() failed to parse the color value ", llcolor4b, LLColor4::red); + + color = "12.0, -2.3, 1.32, 5.0"; + LLColor4::parseColor(color, &llcolor4b); + llcolor4a = llcolor4a * (1.f / 255.f); + ensure_equals("2:parseColor() failed to parse the color value ", llcolor4a,llcolor4b); + + color = "yellow5"; + llcolor4a.setVec(r,g,b); + LLColor4::parseColor(color, &llcolor4a); + ensure_equals("3:parseColor() failed to parse the color value ", llcolor4a, LLColor4::yellow5); + } + + template<> template<> + void v4color_object::test<20>() + { + F32 r = 12.0f, g = -2.3f, b = 1.32f, a = 5.0f; + LLColor4 llcolor4a(r,g,b,a),llcolor4b; + std::string color("12.0, -2.3, 1.32, 5.0"); + LLColor4::parseColor4(color, &llcolor4b); + ensure_equals("parseColor4() failed to parse the color value ", llcolor4a, llcolor4b); + } } diff --git a/indra/llmath/tests/v4coloru_test.cpp b/indra/llmath/tests/v4coloru_test.cpp index 1d3aa4c63d..b9968d0cd0 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 5308e7efd4..5e443663de 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 6348b3225c..983ac37574 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 22b37628d8..fd71c337fa 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 b1ac0b3340..327e937675 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/v3color.cpp b/indra/llmath/v3color.cpp index d38f48b11e..9fe9c8d5e5 100644 --- a/indra/llmath/v3color.cpp +++ b/indra/llmath/v3color.cpp @@ -1,25 +1,25 @@ -/** +/** * @file v3color.cpp * @brief LLColor3 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$ */ @@ -36,118 +36,118 @@ LLColor3 LLColor3::grey (0.5f, 0.5f, 0.5f); LLColor3::LLColor3(const LLColor4 &a) { - mV[0] = a.mV[0]; - mV[1] = a.mV[1]; - mV[2] = a.mV[2]; + mV[0] = a.mV[0]; + mV[1] = a.mV[1]; + mV[2] = a.mV[2]; } LLColor3::LLColor3(const LLVector4 &a) { - mV[0] = a.mV[0]; - mV[1] = a.mV[1]; - mV[2] = a.mV[2]; + mV[0] = a.mV[0]; + mV[1] = a.mV[1]; + mV[2] = a.mV[2]; } LLColor3::LLColor3(const LLSD &sd) { - setValue(sd); + setValue(sd); } -const LLColor3& LLColor3::operator=(const LLColor4 &a) +const LLColor3& LLColor3::operator=(const LLColor4 &a) { - mV[0] = a.mV[0]; - mV[1] = a.mV[1]; - mV[2] = a.mV[2]; - return (*this); + mV[0] = a.mV[0]; + mV[1] = a.mV[1]; + mV[2] = a.mV[2]; + return (*this); } -std::ostream& operator<<(std::ostream& s, const LLColor3 &a) +std::ostream& operator<<(std::ostream& s, const LLColor3 &a) { - s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }"; - return s; + s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }"; + return s; } 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 ); + 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 LLColor3::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 ) ); - } + 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 LLColor3::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; + 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; } diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h index d925f56e97..ea26e9eb76 100644 --- a/indra/llmath/v3color.h +++ b/indra/llmath/v3color.h @@ -1,25 +1,25 @@ -/** +/** * @file v3color.h * @brief LLColor3 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$ */ @@ -43,52 +43,52 @@ static const U32 LENGTHOFCOLOR3 = 3; class LLColor3 { public: - F32 mV[LENGTHOFCOLOR3]; + F32 mV[LENGTHOFCOLOR3]; - static LLColor3 white; - static LLColor3 black; - static LLColor3 grey; + static LLColor3 white; + static LLColor3 black; + static LLColor3 grey; public: - LLColor3(); // Initializes LLColor3 to (0, 0, 0) - LLColor3(F32 r, F32 g, F32 b); // Initializes LLColor3 to (r, g, b) - LLColor3(const F32 *vec); // Initializes LLColor3 to (vec[0]. vec[1], vec[2]) - LLColor3(const char *color_string); // html format color ie "#FFDDEE" - explicit LLColor3(const LLColor4& color4); // "explicit" to avoid automatic conversion - explicit LLColor3(const LLVector4& vector4); // "explicit" to avoid automatic conversion - LLColor3(const LLSD& sd); - - - LLSD getValue() const - { - LLSD ret; - ret[0] = mV[0]; - ret[1] = mV[1]; - ret[2] = mV[2]; - return ret; - } - - void setValue(const LLSD& sd) - { - mV[0] = (F32) sd[0].asReal();; - mV[1] = (F32) sd[1].asReal();; - mV[2] = (F32) sd[2].asReal();; - } - - void setHSL(F32 hue, F32 saturation, F32 luminance); - void calcHSL(F32* hue, F32* saturation, F32* luminance) const; - - const LLColor3& setToBlack(); // Clears LLColor3 to (0, 0, 0) - const LLColor3& setToWhite(); // Zero LLColor3 to (0, 0, 0) - - const LLColor3& setVec(F32 x, F32 y, F32 z); // deprecated - const LLColor3& setVec(const LLColor3 &vec); // deprecated - const LLColor3& setVec(const F32 *vec); // deprecated - - const LLColor3& set(F32 x, F32 y, F32 z); // Sets LLColor3 to (x, y, z) - const LLColor3& set(const LLColor3 &vec); // Sets LLColor3 to vec - const LLColor3& set(const F32 *vec); // Sets LLColor3 to vec - + LLColor3(); // Initializes LLColor3 to (0, 0, 0) + LLColor3(F32 r, F32 g, F32 b); // Initializes LLColor3 to (r, g, b) + LLColor3(const F32 *vec); // Initializes LLColor3 to (vec[0]. vec[1], vec[2]) + LLColor3(const char *color_string); // html format color ie "#FFDDEE" + explicit LLColor3(const LLColor4& color4); // "explicit" to avoid automatic conversion + explicit LLColor3(const LLVector4& vector4); // "explicit" to avoid automatic conversion + LLColor3(const LLSD& sd); + + + LLSD getValue() const + { + LLSD ret; + ret[0] = mV[0]; + ret[1] = mV[1]; + ret[2] = mV[2]; + return ret; + } + + void setValue(const LLSD& sd) + { + mV[0] = (F32) sd[0].asReal();; + mV[1] = (F32) sd[1].asReal();; + mV[2] = (F32) sd[2].asReal();; + } + + void setHSL(F32 hue, F32 saturation, F32 luminance); + void calcHSL(F32* hue, F32* saturation, F32* luminance) const; + + const LLColor3& setToBlack(); // Clears LLColor3 to (0, 0, 0) + const LLColor3& setToWhite(); // Zero LLColor3 to (0, 0, 0) + + const LLColor3& setVec(F32 x, F32 y, F32 z); // deprecated + const LLColor3& setVec(const LLColor3 &vec); // deprecated + const LLColor3& setVec(const F32 *vec); // deprecated + + const LLColor3& set(F32 x, F32 y, F32 z); // Sets LLColor3 to (x, y, z) + const LLColor3& set(const LLColor3 &vec); // Sets LLColor3 to vec + const LLColor3& set(const F32 *vec); // Sets LLColor3 to vec + // set from a vector of unknown type and size // may leave some data unmodified template<typename T> @@ -99,18 +99,18 @@ public: template<typename T> void write(std::vector<T>& v) const; - F32 magVec() const; // deprecated - F32 magVecSquared() const; // deprecated - F32 normVec(); // deprecated + F32 magVec() const; // deprecated + F32 magVecSquared() const; // deprecated + F32 normVec(); // deprecated + + F32 length() const; // Returns magnitude of LLColor3 + F32 lengthSquared() const; // Returns magnitude squared of LLColor3 + F32 normalize(); // Normalizes and returns the magnitude of LLColor3 - F32 length() const; // Returns magnitude of LLColor3 - F32 lengthSquared() const; // Returns magnitude squared of LLColor3 - F32 normalize(); // Normalizes and returns the magnitude of LLColor3 + F32 brightness() const; // Returns brightness of LLColor3 - F32 brightness() const; // Returns brightness of LLColor3 + const LLColor3& operator=(const LLColor4 &a); - const LLColor3& operator=(const LLColor4 &a); - LL_FORCE_INLINE LLColor3 divide(const LLColor3 &col2) { return LLColor3( @@ -128,27 +128,27 @@ public: mV[2] / l ); } - friend std::ostream& operator<<(std::ostream& s, const LLColor3 &a); // Print a - friend LLColor3 operator+(const LLColor3 &a, const LLColor3 &b); // Return vector a + b - friend LLColor3 operator-(const LLColor3 &a, const LLColor3 &b); // Return vector a minus b + friend std::ostream& operator<<(std::ostream& s, const LLColor3 &a); // Print a + friend LLColor3 operator+(const LLColor3 &a, const LLColor3 &b); // Return vector a + b + friend LLColor3 operator-(const LLColor3 &a, const LLColor3 &b); // Return vector a minus b - friend const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b); // Return vector a + b - friend const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b); // Return vector a minus b - friend const LLColor3& operator*=(LLColor3 &a, const LLColor3 &b); + friend const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b); // Return vector a + b + friend const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b); // Return vector a minus b + friend const LLColor3& operator*=(LLColor3 &a, const LLColor3 &b); - friend LLColor3 operator*(const LLColor3 &a, const LLColor3 &b); // Return component wise a * b - friend LLColor3 operator*(const LLColor3 &a, F32 k); // Return a times scaler k - friend LLColor3 operator*(F32 k, const LLColor3 &a); // Return a times scaler k + friend LLColor3 operator*(const LLColor3 &a, const LLColor3 &b); // Return component wise a * b + friend LLColor3 operator*(const LLColor3 &a, F32 k); // Return a times scaler k + friend LLColor3 operator*(F32 k, const LLColor3 &a); // Return a times scaler k - friend bool operator==(const LLColor3 &a, const LLColor3 &b); // Return a == b - friend bool operator!=(const LLColor3 &a, const LLColor3 &b); // Return a != b + friend bool operator==(const LLColor3 &a, const LLColor3 &b); // Return a == b + friend bool operator!=(const LLColor3 &a, const LLColor3 &b); // Return a != b - friend const LLColor3& operator*=(LLColor3 &a, F32 k); // Return a times scaler k + friend const LLColor3& operator*=(LLColor3 &a, F32 k); // Return a times scaler k - friend LLColor3 operator-(const LLColor3 &a); // Return vector 1-rgb (inverse) + friend LLColor3 operator-(const LLColor3 &a); // Return vector 1-rgb (inverse) - inline void clamp(); - inline void exp(); // Do an exponential on the color + inline void clamp(); + inline void exp(); // Do an exponential on the color }; LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u); @@ -156,343 +156,343 @@ LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u); void LLColor3::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; - } -} - -// Non-member functions -F32 distVec(const LLColor3 &a, const LLColor3 &b); // Returns distance between a and b -F32 distVec_squared(const LLColor3 &a, const LLColor3 &b);// Returns distance squared between a and b + // 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; + } +} + +// Non-member functions +F32 distVec(const LLColor3 &a, const LLColor3 &b); // Returns distance between a and b +F32 distVec_squared(const LLColor3 &a, const LLColor3 &b);// Returns distance squared between a and b inline LLColor3::LLColor3(void) { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; } inline LLColor3::LLColor3(F32 r, F32 g, F32 b) { - mV[VX] = r; - mV[VY] = g; - mV[VZ] = b; + mV[VX] = r; + mV[VY] = g; + mV[VZ] = b; } inline LLColor3::LLColor3(const F32 *vec) { - mV[VX] = vec[VX]; - mV[VY] = vec[VY]; - mV[VZ] = vec[VZ]; + mV[VX] = vec[VX]; + mV[VY] = vec[VY]; + mV[VZ] = vec[VZ]; } #if LL_WINDOWS # pragma warning( disable : 4996 ) // strncpy teh sux0r #endif -inline LLColor3::LLColor3(const char* color_string) // takes a string of format "RRGGBB" where RR is hex 00..FF +inline LLColor3::LLColor3(const char* color_string) // takes a string of format "RRGGBB" where RR is hex 00..FF { - if (strlen(color_string) < 6) /* Flawfinder: ignore */ - { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; - return; - } + if (strlen(color_string) < 6) /* Flawfinder: ignore */ + { + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; + return; + } - char tempstr[7]; - strncpy(tempstr,color_string,6); /* Flawfinder: ignore */ - tempstr[6] = '\0'; - mV[VZ] = (F32)strtol(&tempstr[4],NULL,16)/255.f; - tempstr[4] = '\0'; - mV[VY] = (F32)strtol(&tempstr[2],NULL,16)/255.f; - tempstr[2] = '\0'; - mV[VX] = (F32)strtol(&tempstr[0],NULL,16)/255.f; + char tempstr[7]; + strncpy(tempstr,color_string,6); /* Flawfinder: ignore */ + tempstr[6] = '\0'; + mV[VZ] = (F32)strtol(&tempstr[4],NULL,16)/255.f; + tempstr[4] = '\0'; + mV[VY] = (F32)strtol(&tempstr[2],NULL,16)/255.f; + tempstr[2] = '\0'; + mV[VX] = (F32)strtol(&tempstr[0],NULL,16)/255.f; } -inline const LLColor3& LLColor3::setToBlack(void) +inline const LLColor3& LLColor3::setToBlack(void) { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; - return (*this); + mV[0] = 0.f; + mV[1] = 0.f; + mV[2] = 0.f; + return (*this); } -inline const LLColor3& LLColor3::setToWhite(void) +inline const LLColor3& LLColor3::setToWhite(void) { - mV[0] = 1.f; - mV[1] = 1.f; - mV[2] = 1.f; - return (*this); + mV[0] = 1.f; + mV[1] = 1.f; + mV[2] = 1.f; + return (*this); } -inline const LLColor3& LLColor3::set(F32 r, F32 g, F32 b) +inline const LLColor3& LLColor3::set(F32 r, F32 g, F32 b) { - mV[0] = r; - mV[1] = g; - mV[2] = b; - return (*this); + mV[0] = r; + mV[1] = g; + mV[2] = b; + return (*this); } -inline const LLColor3& LLColor3::set(const LLColor3 &vec) +inline const LLColor3& LLColor3::set(const LLColor3 &vec) { - mV[0] = vec.mV[0]; - mV[1] = vec.mV[1]; - mV[2] = vec.mV[2]; - return (*this); + mV[0] = vec.mV[0]; + mV[1] = vec.mV[1]; + mV[2] = vec.mV[2]; + return (*this); } -inline const LLColor3& LLColor3::set(const F32 *vec) +inline const LLColor3& LLColor3::set(const F32 *vec) { - mV[0] = vec[0]; - mV[1] = vec[1]; - mV[2] = vec[2]; - return (*this); + mV[0] = vec[0]; + mV[1] = vec[1]; + mV[2] = vec[2]; + return (*this); } // deprecated -inline const LLColor3& LLColor3::setVec(F32 r, F32 g, F32 b) +inline const LLColor3& LLColor3::setVec(F32 r, F32 g, F32 b) { - mV[0] = r; - mV[1] = g; - mV[2] = b; - return (*this); + mV[0] = r; + mV[1] = g; + mV[2] = b; + return (*this); } // deprecated -inline const LLColor3& LLColor3::setVec(const LLColor3 &vec) +inline const LLColor3& LLColor3::setVec(const LLColor3 &vec) { - mV[0] = vec.mV[0]; - mV[1] = vec.mV[1]; - mV[2] = vec.mV[2]; - return (*this); + mV[0] = vec.mV[0]; + mV[1] = vec.mV[1]; + mV[2] = vec.mV[2]; + return (*this); } // deprecated -inline const LLColor3& LLColor3::setVec(const F32 *vec) +inline const LLColor3& LLColor3::setVec(const F32 *vec) { - mV[0] = vec[0]; - mV[1] = vec[1]; - mV[2] = vec[2]; - return (*this); + mV[0] = vec[0]; + mV[1] = vec[1]; + mV[2] = vec[2]; + return (*this); } -inline F32 LLColor3::brightness(void) const +inline F32 LLColor3::brightness(void) const { - return (mV[0] + mV[1] + mV[2]) / 3.0f; + return (mV[0] + mV[1] + mV[2]) / 3.0f; } -inline F32 LLColor3::length(void) const +inline F32 LLColor3::length(void) const { - return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); } -inline F32 LLColor3::lengthSquared(void) const +inline F32 LLColor3::lengthSquared(void) const { - return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; + return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; } -inline F32 LLColor3::normalize(void) +inline F32 LLColor3::normalize(void) { - F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); - F32 oomag; + F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + F32 oomag; - if (mag) - { - oomag = 1.f/mag; - mV[0] *= oomag; - mV[1] *= oomag; - mV[2] *= oomag; - } - return (mag); + if (mag) + { + oomag = 1.f/mag; + mV[0] *= oomag; + mV[1] *= oomag; + mV[2] *= oomag; + } + return (mag); } // deprecated -inline F32 LLColor3::magVec(void) const +inline F32 LLColor3::magVec(void) const { - return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); } // deprecated -inline F32 LLColor3::magVecSquared(void) const +inline F32 LLColor3::magVecSquared(void) const { - return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; + return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; } // deprecated -inline F32 LLColor3::normVec(void) +inline F32 LLColor3::normVec(void) { - F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); - F32 oomag; + F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + F32 oomag; - if (mag) - { - oomag = 1.f/mag; - mV[0] *= oomag; - mV[1] *= oomag; - mV[2] *= oomag; - } - return (mag); + if (mag) + { + oomag = 1.f/mag; + mV[0] *= oomag; + mV[1] *= oomag; + mV[2] *= oomag; + } + return (mag); } inline void LLColor3::exp() { #if 0 - mV[0] = ::exp(mV[0]); - mV[1] = ::exp(mV[1]); - mV[2] = ::exp(mV[2]); + mV[0] = ::exp(mV[0]); + mV[1] = ::exp(mV[1]); + mV[2] = ::exp(mV[2]); #else - mV[0] = (F32)LL_FAST_EXP(mV[0]); - mV[1] = (F32)LL_FAST_EXP(mV[1]); - mV[2] = (F32)LL_FAST_EXP(mV[2]); + mV[0] = (F32)LL_FAST_EXP(mV[0]); + mV[1] = (F32)LL_FAST_EXP(mV[1]); + mV[2] = (F32)LL_FAST_EXP(mV[2]); #endif } inline LLColor3 operator+(const LLColor3 &a, const LLColor3 &b) { - return LLColor3( - a.mV[0] + b.mV[0], - a.mV[1] + b.mV[1], - a.mV[2] + b.mV[2]); + return LLColor3( + a.mV[0] + b.mV[0], + a.mV[1] + b.mV[1], + a.mV[2] + b.mV[2]); } inline LLColor3 operator-(const LLColor3 &a, const LLColor3 &b) { - return LLColor3( - a.mV[0] - b.mV[0], - a.mV[1] - b.mV[1], - a.mV[2] - b.mV[2]); + return LLColor3( + a.mV[0] - b.mV[0], + a.mV[1] - b.mV[1], + a.mV[2] - b.mV[2]); } inline LLColor3 operator*(const LLColor3 &a, const LLColor3 &b) { - return LLColor3( - a.mV[0] * b.mV[0], - a.mV[1] * b.mV[1], - a.mV[2] * b.mV[2]); + return LLColor3( + a.mV[0] * b.mV[0], + a.mV[1] * b.mV[1], + a.mV[2] * b.mV[2]); } inline LLColor3 operator*(const LLColor3 &a, F32 k) { - return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); + return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); } inline LLColor3 operator*(F32 k, const LLColor3 &a) { - return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); + return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); } inline bool operator==(const LLColor3 &a, const LLColor3 &b) { - return ( (a.mV[0] == b.mV[0]) - &&(a.mV[1] == b.mV[1]) - &&(a.mV[2] == b.mV[2])); + return ( (a.mV[0] == b.mV[0]) + &&(a.mV[1] == b.mV[1]) + &&(a.mV[2] == b.mV[2])); } inline bool operator!=(const LLColor3 &a, const LLColor3 &b) { - return ( (a.mV[0] != b.mV[0]) - ||(a.mV[1] != b.mV[1]) - ||(a.mV[2] != b.mV[2])); + return ( (a.mV[0] != b.mV[0]) + ||(a.mV[1] != b.mV[1]) + ||(a.mV[2] != b.mV[2])); } inline const LLColor3 &operator*=(LLColor3 &a, const LLColor3 &b) { - a.mV[0] *= b.mV[0]; - a.mV[1] *= b.mV[1]; - a.mV[2] *= b.mV[2]; - return a; + a.mV[0] *= b.mV[0]; + a.mV[1] *= b.mV[1]; + a.mV[2] *= b.mV[2]; + return a; } inline const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b) { - a.mV[0] += b.mV[0]; - a.mV[1] += b.mV[1]; - a.mV[2] += b.mV[2]; - return a; + a.mV[0] += b.mV[0]; + a.mV[1] += b.mV[1]; + a.mV[2] += b.mV[2]; + return a; } inline const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b) { - a.mV[0] -= b.mV[0]; - a.mV[1] -= b.mV[1]; - a.mV[2] -= b.mV[2]; - return a; + a.mV[0] -= b.mV[0]; + a.mV[1] -= b.mV[1]; + a.mV[2] -= b.mV[2]; + return a; } inline const LLColor3& operator*=(LLColor3 &a, F32 k) { - a.mV[0] *= k; - a.mV[1] *= k; - a.mV[2] *= k; - return a; + a.mV[0] *= k; + a.mV[1] *= k; + a.mV[2] *= k; + return a; } inline LLColor3 operator-(const LLColor3 &a) { - return LLColor3( - 1.f - a.mV[0], - 1.f - a.mV[1], - 1.f - a.mV[2] ); + return LLColor3( + 1.f - a.mV[0], + 1.f - a.mV[1], + 1.f - a.mV[2] ); } // Non-member functions -inline F32 distVec(const LLColor3 &a, const LLColor3 &b) +inline F32 distVec(const LLColor3 &a, const LLColor3 &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 ); + 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 distVec_squared(const LLColor3 &a, const LLColor3 &b) +inline F32 distVec_squared(const LLColor3 &a, const LLColor3 &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; + 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 LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u) { - return LLColor3( - 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); + return LLColor3( + 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 const LLColor3 srgbColor3(const LLColor3 &a) { - LLColor3 srgbColor; - srgbColor.mV[0] = linearTosRGB(a.mV[0]); - srgbColor.mV[1] = linearTosRGB(a.mV[1]); - srgbColor.mV[2] = linearTosRGB(a.mV[2]); + LLColor3 srgbColor; + srgbColor.mV[0] = linearTosRGB(a.mV[0]); + srgbColor.mV[1] = linearTosRGB(a.mV[1]); + srgbColor.mV[2] = linearTosRGB(a.mV[2]); - return srgbColor; + return srgbColor; } inline const LLColor3 linearColor3p(const F32* v) { diff --git a/indra/llmath/v3colorutil.h b/indra/llmath/v3colorutil.h index 6d8cd9329b..62005f76a0 100644 --- a/indra/llmath/v3colorutil.h +++ b/indra/llmath/v3colorutil.h @@ -1,25 +1,25 @@ -/** +/** * @file v3color.h * @brief LLColor3 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$ */ diff --git a/indra/llmath/v3dmath.cpp b/indra/llmath/v3dmath.cpp index cc7121da38..faa877a5cb 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 751d6d6228..2968bcd511 100644 --- a/indra/llmath/v3dmath.h +++ b/indra/llmath/v3dmath.h @@ -1,530 +1,530 @@ -/** - * @file v3dmath.h - * @brief High precision 3 dimensional vector. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_V3DMATH_H -#define LL_V3DMATH_H - -#include "llerror.h" -#include "v3math.h" - -class LLVector3d -{ - public: - F64 mdV[3]; - - const static LLVector3d zero; - const static LLVector3d x_axis; - const static LLVector3d y_axis; - const static LLVector3d z_axis; - const static LLVector3d x_axis_neg; - const static LLVector3d y_axis_neg; - const static LLVector3d z_axis_neg; - - inline LLVector3d(); // Initializes LLVector3d to (0, 0, 0) - inline LLVector3d(const F64 x, const F64 y, const F64 z); // Initializes LLVector3d to (x. y, z) - inline explicit LLVector3d(const F64 *vec); // Initializes LLVector3d to (vec[0]. vec[1], vec[2]) - inline explicit LLVector3d(const LLVector3 &vec); - explicit LLVector3d(const LLSD& sd) - { - setValue(sd); - } - - void setValue(const LLSD& sd) - { - mdV[0] = sd[0].asReal(); - mdV[1] = sd[1].asReal(); - mdV[2] = sd[2].asReal(); - } - - LLSD getValue() const - { - LLSD ret; - ret[0] = mdV[0]; - ret[1] = mdV[1]; - ret[2] = mdV[2]; - return ret; - } - - inline bool isFinite() const; // checks to see if all values of LLVector3d are finite - bool clamp(const F64 min, const F64 max); // Clamps all values to (min,max), returns true if data changed - bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed - - inline const LLVector3d& clear(); // Clears LLVector3d to (0, 0, 0, 1) - inline const LLVector3d& clearVec(); // deprecated - inline const LLVector3d& setZero(); // Zero LLVector3d to (0, 0, 0, 0) - inline const LLVector3d& zeroVec(); // deprecated - inline const LLVector3d& set(const F64 x, const F64 y, const F64 z); // Sets LLVector3d to (x, y, z, 1) - inline const LLVector3d& set(const LLVector3d &vec); // Sets LLVector3d to vec - inline const LLVector3d& set(const F64 *vec); // Sets LLVector3d to vec - inline const LLVector3d& set(const LLVector3 &vec); - inline const LLVector3d& setVec(const F64 x, const F64 y, const F64 z); // deprecated - inline const LLVector3d& setVec(const LLVector3d &vec); // deprecated - inline const LLVector3d& setVec(const F64 *vec); // deprecated - inline const LLVector3d& setVec(const LLVector3 &vec); // deprecated - - F64 magVec() const; // deprecated - F64 magVecSquared() const; // deprecated - inline F64 normVec(); // deprecated - - F64 length() const; // Returns magnitude of LLVector3d - F64 lengthSquared() const; // Returns magnitude squared of LLVector3d - inline F64 normalize(); // Normalizes and returns the magnitude of LLVector3d - - const LLVector3d& rotVec(const F64 angle, const LLVector3d &vec); // Rotates about vec by angle radians - const LLVector3d& rotVec(const F64 angle, const F64 x, const F64 y, const F64 z); // Rotates about x,y,z by angle radians - const LLVector3d& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat - const LLVector3d& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q - - bool isNull() const; // Returns true if vector has a _very_small_ length - bool isExactlyZero() const { return !mdV[VX] && !mdV[VY] && !mdV[VZ]; } - - const LLVector3d& operator=(const LLVector4 &a); - - F64 operator[](int idx) const { return mdV[idx]; } - F64 &operator[](int idx) { return mdV[idx]; } - - friend LLVector3d operator+(const LLVector3d& a, const LLVector3d& b); // Return vector a + b - friend LLVector3d operator-(const LLVector3d& a, const LLVector3d& b); // Return vector a minus b - friend F64 operator*(const LLVector3d& a, const LLVector3d& b); // Return a dot b - friend LLVector3d operator%(const LLVector3d& a, const LLVector3d& b); // Return a cross b - friend LLVector3d operator*(const LLVector3d& a, const F64 k); // Return a times scaler k - friend LLVector3d operator/(const LLVector3d& a, const F64 k); // Return a divided by scaler k - friend LLVector3d operator*(const F64 k, const LLVector3d& a); // Return a times scaler k - friend bool operator==(const LLVector3d& a, const LLVector3d& b); // Return a == b - friend bool operator!=(const LLVector3d& a, const LLVector3d& b); // Return a != b - - friend const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b); // Return vector a + b - friend const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b); // Return vector a minus b - friend const LLVector3d& operator%=(LLVector3d& a, const LLVector3d& b); // Return a cross b - friend const LLVector3d& operator*=(LLVector3d& a, const F64 k); // Return a times scaler k - friend const LLVector3d& operator/=(LLVector3d& a, const F64 k); // Return a divided by scaler k - - friend LLVector3d operator-(const LLVector3d& a); // Return vector -a - - friend std::ostream& operator<<(std::ostream& s, const LLVector3d& a); // Stream a - - static bool parseVector3d(const std::string& buf, LLVector3d* value); - -}; - -typedef LLVector3d LLGlobalVec; - -inline const LLVector3d &LLVector3d::set(const LLVector3 &vec) -{ - mdV[0] = vec.mV[0]; - mdV[1] = vec.mV[1]; - mdV[2] = vec.mV[2]; - return *this; -} - -inline const LLVector3d &LLVector3d::setVec(const LLVector3 &vec) -{ - mdV[0] = vec.mV[0]; - mdV[1] = vec.mV[1]; - mdV[2] = vec.mV[2]; - return *this; -} - - -inline LLVector3d::LLVector3d(void) -{ - mdV[0] = 0.f; - mdV[1] = 0.f; - mdV[2] = 0.f; -} - -inline LLVector3d::LLVector3d(const F64 x, const F64 y, const F64 z) -{ - mdV[VX] = x; - mdV[VY] = y; - mdV[VZ] = z; -} - -inline LLVector3d::LLVector3d(const F64 *vec) -{ - mdV[VX] = vec[VX]; - mdV[VY] = vec[VY]; - mdV[VZ] = vec[VZ]; -} - -inline LLVector3d::LLVector3d(const LLVector3 &vec) -{ - mdV[VX] = vec.mV[VX]; - mdV[VY] = vec.mV[VY]; - mdV[VZ] = vec.mV[VZ]; -} - -/* -inline LLVector3d::LLVector3d(const LLVector3d ©) -{ - mdV[VX] = copy.mdV[VX]; - mdV[VY] = copy.mdV[VY]; - mdV[VZ] = copy.mdV[VZ]; -} -*/ - -// Destructors - -// checker -inline bool LLVector3d::isFinite() const -{ - return (llfinite(mdV[VX]) && llfinite(mdV[VY]) && llfinite(mdV[VZ])); -} - - -// Clear and Assignment Functions - -inline const LLVector3d& LLVector3d::clear(void) -{ - mdV[0] = 0.f; - mdV[1] = 0.f; - mdV[2]= 0.f; - return (*this); -} - -inline const LLVector3d& LLVector3d::clearVec(void) -{ - mdV[0] = 0.f; - mdV[1] = 0.f; - mdV[2]= 0.f; - return (*this); -} - -inline const LLVector3d& LLVector3d::setZero(void) -{ - mdV[0] = 0.f; - mdV[1] = 0.f; - mdV[2] = 0.f; - return (*this); -} - -inline const LLVector3d& LLVector3d::zeroVec(void) -{ - mdV[0] = 0.f; - mdV[1] = 0.f; - mdV[2] = 0.f; - return (*this); -} - -inline const LLVector3d& LLVector3d::set(const F64 x, const F64 y, const F64 z) -{ - mdV[VX] = x; - mdV[VY] = y; - mdV[VZ] = z; - return (*this); -} - -inline const LLVector3d& LLVector3d::set(const LLVector3d &vec) -{ - mdV[0] = vec.mdV[0]; - mdV[1] = vec.mdV[1]; - mdV[2] = vec.mdV[2]; - return (*this); -} - -inline const LLVector3d& LLVector3d::set(const F64 *vec) -{ - mdV[0] = vec[0]; - mdV[1] = vec[1]; - mdV[2] = vec[2]; - return (*this); -} - -inline const LLVector3d& LLVector3d::setVec(const F64 x, const F64 y, const F64 z) -{ - mdV[VX] = x; - mdV[VY] = y; - mdV[VZ] = z; - return (*this); -} - -inline const LLVector3d& LLVector3d::setVec(const LLVector3d &vec) -{ - mdV[0] = vec.mdV[0]; - mdV[1] = vec.mdV[1]; - mdV[2] = vec.mdV[2]; - return (*this); -} - -inline const LLVector3d& LLVector3d::setVec(const F64 *vec) -{ - mdV[0] = vec[0]; - mdV[1] = vec[1]; - mdV[2] = vec[2]; - return (*this); -} - -inline F64 LLVector3d::normVec(void) -{ - F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]); - F64 oomag; - - if (mag > FP_MAG_THRESHOLD) - { - oomag = 1.f/mag; - mdV[0] *= oomag; - mdV[1] *= oomag; - mdV[2] *= oomag; - } - else - { - mdV[0] = 0.f; - mdV[1] = 0.f; - mdV[2] = 0.f; - mag = 0; - } - return (mag); -} - -inline F64 LLVector3d::normalize(void) -{ - F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]); - F64 oomag; - - if (mag > FP_MAG_THRESHOLD) - { - oomag = 1.f/mag; - mdV[0] *= oomag; - mdV[1] *= oomag; - mdV[2] *= oomag; - } - else - { - mdV[0] = 0.f; - mdV[1] = 0.f; - mdV[2] = 0.f; - mag = 0; - } - return (mag); -} - -// LLVector3d Magnitude and Normalization Functions - -inline F64 LLVector3d::magVec(void) const -{ - return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]); -} - -inline F64 LLVector3d::magVecSquared(void) const -{ - return mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]; -} - -inline F64 LLVector3d::length(void) const -{ - return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]); -} - -inline F64 LLVector3d::lengthSquared(void) const -{ - return mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]; -} - -inline LLVector3d operator+(const LLVector3d& a, const LLVector3d& b) -{ - LLVector3d c(a); - return c += b; -} - -inline LLVector3d operator-(const LLVector3d& a, const LLVector3d& b) -{ - LLVector3d c(a); - return c -= b; -} - -inline F64 operator*(const LLVector3d& a, const LLVector3d& b) -{ - return (a.mdV[0]*b.mdV[0] + a.mdV[1]*b.mdV[1] + a.mdV[2]*b.mdV[2]); -} - -inline LLVector3d operator%(const LLVector3d& a, const LLVector3d& b) -{ - return LLVector3d( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1] ); -} - -inline LLVector3d operator/(const LLVector3d& a, const F64 k) -{ - F64 t = 1.f / k; - return LLVector3d( a.mdV[0] * t, a.mdV[1] * t, a.mdV[2] * t ); -} - -inline LLVector3d operator*(const LLVector3d& a, const F64 k) -{ - return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k ); -} - -inline LLVector3d operator*(F64 k, const LLVector3d& a) -{ - return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k ); -} - -inline bool operator==(const LLVector3d& a, const LLVector3d& b) -{ - return ( (a.mdV[0] == b.mdV[0]) - &&(a.mdV[1] == b.mdV[1]) - &&(a.mdV[2] == b.mdV[2])); -} - -inline bool operator!=(const LLVector3d& a, const LLVector3d& b) -{ - return ( (a.mdV[0] != b.mdV[0]) - ||(a.mdV[1] != b.mdV[1]) - ||(a.mdV[2] != b.mdV[2])); -} - -inline const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b) -{ - a.mdV[0] += b.mdV[0]; - a.mdV[1] += b.mdV[1]; - a.mdV[2] += b.mdV[2]; - return a; -} - -inline const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b) -{ - a.mdV[0] -= b.mdV[0]; - a.mdV[1] -= b.mdV[1]; - a.mdV[2] -= b.mdV[2]; - return a; -} - -inline const LLVector3d& operator%=(LLVector3d& a, const LLVector3d& b) -{ - LLVector3d ret( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1]); - a = ret; - return a; -} - -inline const LLVector3d& operator*=(LLVector3d& a, const F64 k) -{ - a.mdV[0] *= k; - a.mdV[1] *= k; - a.mdV[2] *= k; - return a; -} - -inline const LLVector3d& operator/=(LLVector3d& a, const F64 k) -{ - F64 t = 1.f / k; - a.mdV[0] *= t; - a.mdV[1] *= t; - a.mdV[2] *= t; - return a; -} - -inline LLVector3d operator-(const LLVector3d& a) -{ - return LLVector3d( -a.mdV[0], -a.mdV[1], -a.mdV[2] ); -} - -inline F64 dist_vec(const LLVector3d& a, const LLVector3d& b) -{ - F64 x = a.mdV[0] - b.mdV[0]; - F64 y = a.mdV[1] - b.mdV[1]; - F64 z = a.mdV[2] - b.mdV[2]; - return (F32) sqrt( x*x + y*y + z*z ); -} - -inline F64 dist_vec_squared(const LLVector3d& a, const LLVector3d& b) -{ - F64 x = a.mdV[0] - b.mdV[0]; - F64 y = a.mdV[1] - b.mdV[1]; - F64 z = a.mdV[2] - b.mdV[2]; - return x*x + y*y + z*z; -} - -inline F64 dist_vec_squared2D(const LLVector3d& a, const LLVector3d& b) -{ - F64 x = a.mdV[0] - b.mdV[0]; - F64 y = a.mdV[1] - b.mdV[1]; - return x*x + y*y; -} - -inline LLVector3d lerp(const LLVector3d& a, const LLVector3d& b, const F64 u) -{ - return LLVector3d( - a.mdV[VX] + (b.mdV[VX] - a.mdV[VX]) * u, - a.mdV[VY] + (b.mdV[VY] - a.mdV[VY]) * u, - a.mdV[VZ] + (b.mdV[VZ] - a.mdV[VZ]) * u); -} - - -inline bool LLVector3d::isNull() const -{ - if ( F_APPROXIMATELY_ZERO > mdV[VX]*mdV[VX] + mdV[VY]*mdV[VY] + mdV[VZ]*mdV[VZ] ) - { - return true; - } - return false; -} - - -inline F64 angle_between(const LLVector3d& a, const LLVector3d& b) -{ - LLVector3d an = a; - LLVector3d bn = b; - an.normalize(); - bn.normalize(); - F64 cosine = an * bn; - F64 angle = (cosine >= 1.0f) ? 0.0f : - (cosine <= -1.0f) ? F_PI : - acos(cosine); - return angle; -} - -inline bool are_parallel(const LLVector3d& a, const LLVector3d& b, const F64 epsilon) -{ - LLVector3d an = a; - LLVector3d bn = b; - an.normalize(); - bn.normalize(); - F64 dot = an * bn; - if ( (1.0f - fabs(dot)) < epsilon) - { - return true; - } - return false; -} - -inline LLVector3d projected_vec(const LLVector3d& a, const LLVector3d& b) -{ - LLVector3d project_axis = b; - project_axis.normalize(); - return project_axis * (a * project_axis); -} - -inline LLVector3d inverse_projected_vec(const LLVector3d& a, const LLVector3d& b) -{ - LLVector3d normalized_a = a; - normalized_a.normalize(); - LLVector3d normalized_b = b; - F64 b_length = normalized_b.normalize(); - - F64 dot_product = normalized_a * normalized_b; - return normalized_a * (b_length / dot_product); -} - -#endif // LL_V3DMATH_H +/**
+ * @file v3dmath.h
+ * @brief High precision 3 dimensional vector.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_V3DMATH_H
+#define LL_V3DMATH_H
+
+#include "llerror.h"
+#include "v3math.h"
+
+class LLVector3d
+{
+ public:
+ F64 mdV[3];
+
+ const static LLVector3d zero;
+ const static LLVector3d x_axis;
+ const static LLVector3d y_axis;
+ const static LLVector3d z_axis;
+ const static LLVector3d x_axis_neg;
+ const static LLVector3d y_axis_neg;
+ const static LLVector3d z_axis_neg;
+
+ inline LLVector3d(); // Initializes LLVector3d to (0, 0, 0)
+ inline LLVector3d(const F64 x, const F64 y, const F64 z); // Initializes LLVector3d to (x. y, z)
+ inline explicit LLVector3d(const F64 *vec); // Initializes LLVector3d to (vec[0]. vec[1], vec[2])
+ inline explicit LLVector3d(const LLVector3 &vec);
+ explicit LLVector3d(const LLSD& sd)
+ {
+ setValue(sd);
+ }
+
+ void setValue(const LLSD& sd)
+ {
+ mdV[0] = sd[0].asReal();
+ mdV[1] = sd[1].asReal();
+ mdV[2] = sd[2].asReal();
+ }
+
+ LLSD getValue() const
+ {
+ LLSD ret;
+ ret[0] = mdV[0];
+ ret[1] = mdV[1];
+ ret[2] = mdV[2];
+ return ret;
+ }
+
+ inline bool isFinite() const; // checks to see if all values of LLVector3d are finite
+ bool clamp(const F64 min, const F64 max); // Clamps all values to (min,max), returns true if data changed
+ bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed
+
+ inline const LLVector3d& clear(); // Clears LLVector3d to (0, 0, 0, 1)
+ inline const LLVector3d& clearVec(); // deprecated
+ inline const LLVector3d& setZero(); // Zero LLVector3d to (0, 0, 0, 0)
+ inline const LLVector3d& zeroVec(); // deprecated
+ inline const LLVector3d& set(const F64 x, const F64 y, const F64 z); // Sets LLVector3d to (x, y, z, 1)
+ inline const LLVector3d& set(const LLVector3d &vec); // Sets LLVector3d to vec
+ inline const LLVector3d& set(const F64 *vec); // Sets LLVector3d to vec
+ inline const LLVector3d& set(const LLVector3 &vec);
+ inline const LLVector3d& setVec(const F64 x, const F64 y, const F64 z); // deprecated
+ inline const LLVector3d& setVec(const LLVector3d &vec); // deprecated
+ inline const LLVector3d& setVec(const F64 *vec); // deprecated
+ inline const LLVector3d& setVec(const LLVector3 &vec); // deprecated
+
+ F64 magVec() const; // deprecated
+ F64 magVecSquared() const; // deprecated
+ inline F64 normVec(); // deprecated
+
+ F64 length() const; // Returns magnitude of LLVector3d
+ F64 lengthSquared() const; // Returns magnitude squared of LLVector3d
+ inline F64 normalize(); // Normalizes and returns the magnitude of LLVector3d
+
+ const LLVector3d& rotVec(const F64 angle, const LLVector3d &vec); // Rotates about vec by angle radians
+ const LLVector3d& rotVec(const F64 angle, const F64 x, const F64 y, const F64 z); // Rotates about x,y,z by angle radians
+ const LLVector3d& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat
+ const LLVector3d& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q
+
+ bool isNull() const; // Returns true if vector has a _very_small_ length
+ bool isExactlyZero() const { return !mdV[VX] && !mdV[VY] && !mdV[VZ]; }
+
+ const LLVector3d& operator=(const LLVector4 &a);
+
+ F64 operator[](int idx) const { return mdV[idx]; }
+ F64 &operator[](int idx) { return mdV[idx]; }
+
+ friend LLVector3d operator+(const LLVector3d& a, const LLVector3d& b); // Return vector a + b
+ friend LLVector3d operator-(const LLVector3d& a, const LLVector3d& b); // Return vector a minus b
+ friend F64 operator*(const LLVector3d& a, const LLVector3d& b); // Return a dot b
+ friend LLVector3d operator%(const LLVector3d& a, const LLVector3d& b); // Return a cross b
+ friend LLVector3d operator*(const LLVector3d& a, const F64 k); // Return a times scaler k
+ friend LLVector3d operator/(const LLVector3d& a, const F64 k); // Return a divided by scaler k
+ friend LLVector3d operator*(const F64 k, const LLVector3d& a); // Return a times scaler k
+ friend bool operator==(const LLVector3d& a, const LLVector3d& b); // Return a == b
+ friend bool operator!=(const LLVector3d& a, const LLVector3d& b); // Return a != b
+
+ friend const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b); // Return vector a + b
+ friend const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b); // Return vector a minus b
+ friend const LLVector3d& operator%=(LLVector3d& a, const LLVector3d& b); // Return a cross b
+ friend const LLVector3d& operator*=(LLVector3d& a, const F64 k); // Return a times scaler k
+ friend const LLVector3d& operator/=(LLVector3d& a, const F64 k); // Return a divided by scaler k
+
+ friend LLVector3d operator-(const LLVector3d& a); // Return vector -a
+
+ friend std::ostream& operator<<(std::ostream& s, const LLVector3d& a); // Stream a
+
+ static bool parseVector3d(const std::string& buf, LLVector3d* value);
+
+};
+
+typedef LLVector3d LLGlobalVec;
+
+inline const LLVector3d &LLVector3d::set(const LLVector3 &vec)
+{
+ mdV[0] = vec.mV[0];
+ mdV[1] = vec.mV[1];
+ mdV[2] = vec.mV[2];
+ return *this;
+}
+
+inline const LLVector3d &LLVector3d::setVec(const LLVector3 &vec)
+{
+ mdV[0] = vec.mV[0];
+ mdV[1] = vec.mV[1];
+ mdV[2] = vec.mV[2];
+ return *this;
+}
+
+
+inline LLVector3d::LLVector3d(void)
+{
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2] = 0.f;
+}
+
+inline LLVector3d::LLVector3d(const F64 x, const F64 y, const F64 z)
+{
+ mdV[VX] = x;
+ mdV[VY] = y;
+ mdV[VZ] = z;
+}
+
+inline LLVector3d::LLVector3d(const F64 *vec)
+{
+ mdV[VX] = vec[VX];
+ mdV[VY] = vec[VY];
+ mdV[VZ] = vec[VZ];
+}
+
+inline LLVector3d::LLVector3d(const LLVector3 &vec)
+{
+ mdV[VX] = vec.mV[VX];
+ mdV[VY] = vec.mV[VY];
+ mdV[VZ] = vec.mV[VZ];
+}
+
+/*
+inline LLVector3d::LLVector3d(const LLVector3d ©)
+{
+ mdV[VX] = copy.mdV[VX];
+ mdV[VY] = copy.mdV[VY];
+ mdV[VZ] = copy.mdV[VZ];
+}
+*/
+
+// Destructors
+
+// checker
+inline bool LLVector3d::isFinite() const
+{
+ return (llfinite(mdV[VX]) && llfinite(mdV[VY]) && llfinite(mdV[VZ]));
+}
+
+
+// Clear and Assignment Functions
+
+inline const LLVector3d& LLVector3d::clear(void)
+{
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2]= 0.f;
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::clearVec(void)
+{
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2]= 0.f;
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::setZero(void)
+{
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2] = 0.f;
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::zeroVec(void)
+{
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2] = 0.f;
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::set(const F64 x, const F64 y, const F64 z)
+{
+ mdV[VX] = x;
+ mdV[VY] = y;
+ mdV[VZ] = z;
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::set(const LLVector3d &vec)
+{
+ mdV[0] = vec.mdV[0];
+ mdV[1] = vec.mdV[1];
+ mdV[2] = vec.mdV[2];
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::set(const F64 *vec)
+{
+ mdV[0] = vec[0];
+ mdV[1] = vec[1];
+ mdV[2] = vec[2];
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::setVec(const F64 x, const F64 y, const F64 z)
+{
+ mdV[VX] = x;
+ mdV[VY] = y;
+ mdV[VZ] = z;
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::setVec(const LLVector3d &vec)
+{
+ mdV[0] = vec.mdV[0];
+ mdV[1] = vec.mdV[1];
+ mdV[2] = vec.mdV[2];
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::setVec(const F64 *vec)
+{
+ mdV[0] = vec[0];
+ mdV[1] = vec[1];
+ mdV[2] = vec[2];
+ return (*this);
+}
+
+inline F64 LLVector3d::normVec(void)
+{
+ F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+ F64 oomag;
+
+ if (mag > FP_MAG_THRESHOLD)
+ {
+ oomag = 1.f/mag;
+ mdV[0] *= oomag;
+ mdV[1] *= oomag;
+ mdV[2] *= oomag;
+ }
+ else
+ {
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2] = 0.f;
+ mag = 0;
+ }
+ return (mag);
+}
+
+inline F64 LLVector3d::normalize(void)
+{
+ F64 mag = (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+ F64 oomag;
+
+ if (mag > FP_MAG_THRESHOLD)
+ {
+ oomag = 1.f/mag;
+ mdV[0] *= oomag;
+ mdV[1] *= oomag;
+ mdV[2] *= oomag;
+ }
+ else
+ {
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2] = 0.f;
+ mag = 0;
+ }
+ return (mag);
+}
+
+// LLVector3d Magnitude and Normalization Functions
+
+inline F64 LLVector3d::magVec(void) const
+{
+ return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+}
+
+inline F64 LLVector3d::magVecSquared(void) const
+{
+ return mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2];
+}
+
+inline F64 LLVector3d::length(void) const
+{
+ return (F32) sqrt(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+}
+
+inline F64 LLVector3d::lengthSquared(void) const
+{
+ return mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2];
+}
+
+inline LLVector3d operator+(const LLVector3d& a, const LLVector3d& b)
+{
+ LLVector3d c(a);
+ return c += b;
+}
+
+inline LLVector3d operator-(const LLVector3d& a, const LLVector3d& b)
+{
+ LLVector3d c(a);
+ return c -= b;
+}
+
+inline F64 operator*(const LLVector3d& a, const LLVector3d& b)
+{
+ return (a.mdV[0]*b.mdV[0] + a.mdV[1]*b.mdV[1] + a.mdV[2]*b.mdV[2]);
+}
+
+inline LLVector3d operator%(const LLVector3d& a, const LLVector3d& b)
+{
+ return LLVector3d( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1] );
+}
+
+inline LLVector3d operator/(const LLVector3d& a, const F64 k)
+{
+ F64 t = 1.f / k;
+ return LLVector3d( a.mdV[0] * t, a.mdV[1] * t, a.mdV[2] * t );
+}
+
+inline LLVector3d operator*(const LLVector3d& a, const F64 k)
+{
+ return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k );
+}
+
+inline LLVector3d operator*(F64 k, const LLVector3d& a)
+{
+ return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k );
+}
+
+inline bool operator==(const LLVector3d& a, const LLVector3d& b)
+{
+ return ( (a.mdV[0] == b.mdV[0])
+ &&(a.mdV[1] == b.mdV[1])
+ &&(a.mdV[2] == b.mdV[2]));
+}
+
+inline bool operator!=(const LLVector3d& a, const LLVector3d& b)
+{
+ return ( (a.mdV[0] != b.mdV[0])
+ ||(a.mdV[1] != b.mdV[1])
+ ||(a.mdV[2] != b.mdV[2]));
+}
+
+inline const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b)
+{
+ a.mdV[0] += b.mdV[0];
+ a.mdV[1] += b.mdV[1];
+ a.mdV[2] += b.mdV[2];
+ return a;
+}
+
+inline const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b)
+{
+ a.mdV[0] -= b.mdV[0];
+ a.mdV[1] -= b.mdV[1];
+ a.mdV[2] -= b.mdV[2];
+ return a;
+}
+
+inline const LLVector3d& operator%=(LLVector3d& a, const LLVector3d& b)
+{
+ LLVector3d ret( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1]);
+ a = ret;
+ return a;
+}
+
+inline const LLVector3d& operator*=(LLVector3d& a, const F64 k)
+{
+ a.mdV[0] *= k;
+ a.mdV[1] *= k;
+ a.mdV[2] *= k;
+ return a;
+}
+
+inline const LLVector3d& operator/=(LLVector3d& a, const F64 k)
+{
+ F64 t = 1.f / k;
+ a.mdV[0] *= t;
+ a.mdV[1] *= t;
+ a.mdV[2] *= t;
+ return a;
+}
+
+inline LLVector3d operator-(const LLVector3d& a)
+{
+ return LLVector3d( -a.mdV[0], -a.mdV[1], -a.mdV[2] );
+}
+
+inline F64 dist_vec(const LLVector3d& a, const LLVector3d& b)
+{
+ F64 x = a.mdV[0] - b.mdV[0];
+ F64 y = a.mdV[1] - b.mdV[1];
+ F64 z = a.mdV[2] - b.mdV[2];
+ return (F32) sqrt( x*x + y*y + z*z );
+}
+
+inline F64 dist_vec_squared(const LLVector3d& a, const LLVector3d& b)
+{
+ F64 x = a.mdV[0] - b.mdV[0];
+ F64 y = a.mdV[1] - b.mdV[1];
+ F64 z = a.mdV[2] - b.mdV[2];
+ return x*x + y*y + z*z;
+}
+
+inline F64 dist_vec_squared2D(const LLVector3d& a, const LLVector3d& b)
+{
+ F64 x = a.mdV[0] - b.mdV[0];
+ F64 y = a.mdV[1] - b.mdV[1];
+ return x*x + y*y;
+}
+
+inline LLVector3d lerp(const LLVector3d& a, const LLVector3d& b, const F64 u)
+{
+ return LLVector3d(
+ a.mdV[VX] + (b.mdV[VX] - a.mdV[VX]) * u,
+ a.mdV[VY] + (b.mdV[VY] - a.mdV[VY]) * u,
+ a.mdV[VZ] + (b.mdV[VZ] - a.mdV[VZ]) * u);
+}
+
+
+inline bool LLVector3d::isNull() const
+{
+ if ( F_APPROXIMATELY_ZERO > mdV[VX]*mdV[VX] + mdV[VY]*mdV[VY] + mdV[VZ]*mdV[VZ] )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+inline F64 angle_between(const LLVector3d& a, const LLVector3d& b)
+{
+ LLVector3d an = a;
+ LLVector3d bn = b;
+ an.normalize();
+ bn.normalize();
+ F64 cosine = an * bn;
+ F64 angle = (cosine >= 1.0f) ? 0.0f :
+ (cosine <= -1.0f) ? F_PI :
+ acos(cosine);
+ return angle;
+}
+
+inline bool are_parallel(const LLVector3d& a, const LLVector3d& b, const F64 epsilon)
+{
+ LLVector3d an = a;
+ LLVector3d bn = b;
+ an.normalize();
+ bn.normalize();
+ F64 dot = an * bn;
+ if ( (1.0f - fabs(dot)) < epsilon)
+ {
+ return true;
+ }
+ return false;
+}
+
+inline LLVector3d projected_vec(const LLVector3d& a, const LLVector3d& b)
+{
+ LLVector3d project_axis = b;
+ project_axis.normalize();
+ return project_axis * (a * project_axis);
+}
+
+inline LLVector3d inverse_projected_vec(const LLVector3d& a, const LLVector3d& b)
+{
+ LLVector3d normalized_a = a;
+ normalized_a.normalize();
+ LLVector3d normalized_b = b;
+ F64 b_length = normalized_b.normalize();
+
+ F64 dot_product = normalized_a * normalized_b;
+ return normalized_a * (b_length / dot_product);
+}
+
+#endif // LL_V3DMATH_H
diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp index 72e73a79ec..089d4ce6db 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 fa6ad06008..e43c756fe7 100644 --- a/indra/llmath/v3math.h +++ b/indra/llmath/v3math.h @@ -1,612 +1,612 @@ -/** - * @file v3math.h - * @brief LLVector3 class header file. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_V3MATH_H -#define LL_V3MATH_H - -#include "llerror.h" -#include "llmath.h" - -#include "llsd.h" -class LLVector2; -class LLVector4; -class LLVector4a; -class LLMatrix3; -class LLMatrix4; -class LLVector3d; -class LLQuaternion; - -// LLvector3 = |x y z w| - -static const U32 LENGTHOFVECTOR3 = 3; - -class LLVector3 -{ - public: - F32 mV[LENGTHOFVECTOR3]; - - static const LLVector3 zero; - static const LLVector3 x_axis; - static const LLVector3 y_axis; - static const LLVector3 z_axis; - static const LLVector3 x_axis_neg; - static const LLVector3 y_axis_neg; - static const LLVector3 z_axis_neg; - static const LLVector3 all_one; - - inline LLVector3(); // Initializes LLVector3 to (0, 0, 0) - inline LLVector3(const F32 x, const F32 y, const F32 z); // Initializes LLVector3 to (x. y, z) - inline explicit LLVector3(const F32 *vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2]) - explicit LLVector3(const LLVector2 &vec); // Initializes LLVector3 to (vec[0]. vec[1], 0) - explicit LLVector3(const LLVector3d &vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2]) - explicit LLVector3(const LLVector4 &vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2]) - explicit LLVector3(const LLVector4a& vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2]) - explicit LLVector3(const LLSD& sd); - - - LLSD getValue() const; - - void setValue(const LLSD& sd); - - inline bool isFinite() const; // checks to see if all values of LLVector3 are finite - bool clamp(F32 min, F32 max); // Clamps all values to (min,max), returns true if data changed - bool clamp(const LLVector3 &min_vec, const LLVector3 &max_vec); // Scales vector by another vector - bool clampLength( F32 length_limit ); // Scales vector to limit length to a value - - void quantize16(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization - void quantize8(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization - void snap(S32 sig_digits); // snaps x,y,z to sig_digits decimal places - - bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed - - inline void clear(); // Clears LLVector3 to (0, 0, 0) - inline void setZero(); // Clears LLVector3 to (0, 0, 0) - inline void clearVec(); // deprecated - inline void zeroVec(); // deprecated - - inline void set(F32 x, F32 y, F32 z); // Sets LLVector3 to (x, y, z, 1) - inline void set(const LLVector3 &vec); // Sets LLVector3 to vec - inline void set(const F32 *vec); // Sets LLVector3 to vec - const LLVector3& set(const LLVector4 &vec); - const LLVector3& set(const LLVector3d &vec);// Sets LLVector3 to vec - - inline void setVec(F32 x, F32 y, F32 z); // deprecated - inline void setVec(const LLVector3 &vec); // deprecated - inline void setVec(const F32 *vec); // deprecated - - const LLVector3& setVec(const LLVector4 &vec); // deprecated - const LLVector3& setVec(const LLVector3d &vec); // deprecated - - F32 length() const; // Returns magnitude of LLVector3 - F32 lengthSquared() const; // Returns magnitude squared of LLVector3 - F32 magVec() const; // deprecated - F32 magVecSquared() const; // deprecated - - inline F32 normalize(); // Normalizes and returns the magnitude of LLVector3 - inline F32 normVec(); // deprecated - - inline bool inRange( F32 min, F32 max ) const; // Returns true if all values of the vector are between min and max - - const LLVector3& rotVec(F32 angle, const LLVector3 &vec); // Rotates about vec by angle radians - const LLVector3& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians - const LLVector3& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat - const LLVector3& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q - const LLVector3& transVec(const LLMatrix4& mat); // Transforms by LLMatrix4 mat (mat * v) - - const LLVector3& scaleVec(const LLVector3& vec); // scales per component by vec - LLVector3 scaledVec(const LLVector3& vec) const; // get a copy of this vector scaled by vec - - bool isNull() const; // Returns true if vector has a _very_small_ length - bool isExactlyZero() const { return !mV[VX] && !mV[VY] && !mV[VZ]; } - - F32 operator[](int idx) const { return mV[idx]; } - F32 &operator[](int idx) { return mV[idx]; } - - friend LLVector3 operator+(const LLVector3 &a, const LLVector3 &b); // Return vector a + b - friend LLVector3 operator-(const LLVector3 &a, const LLVector3 &b); // Return vector a minus b - friend F32 operator*(const LLVector3 &a, const LLVector3 &b); // Return a dot b - friend LLVector3 operator%(const LLVector3 &a, const LLVector3 &b); // Return a cross b - friend LLVector3 operator*(const LLVector3 &a, F32 k); // Return a times scaler k - friend LLVector3 operator/(const LLVector3 &a, F32 k); // Return a divided by scaler k - friend LLVector3 operator*(F32 k, const LLVector3 &a); // Return a times scaler k - friend bool operator==(const LLVector3 &a, const LLVector3 &b); // Return a == b - friend bool operator!=(const LLVector3 &a, const LLVector3 &b); // Return a != b - // less than operator useful for using vectors as std::map keys - friend bool operator<(const LLVector3 &a, const LLVector3 &b); // Return a < b - - friend const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b); // Return vector a + b - friend const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b); // Return vector a minus b - friend const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b); // Return a cross b - friend const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b); // Returns a * b; - friend const LLVector3& operator*=(LLVector3 &a, F32 k); // Return a times scaler k - friend const LLVector3& operator/=(LLVector3 &a, F32 k); // Return a divided by scaler k - friend const LLVector3& operator*=(LLVector3 &a, const LLQuaternion &b); // Returns a * b; - - friend LLVector3 operator-(const LLVector3 &a); // Return vector -a - - friend std::ostream& operator<<(std::ostream& s, const LLVector3 &a); // Stream a - - static bool parseVector3(const std::string& buf, LLVector3* value); -}; - -typedef LLVector3 LLSimLocalVec; - -// Non-member functions - -F32 angle_between(const LLVector3 &a, const LLVector3 &b); // Returns angle (radians) between a and b -bool are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon=F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel -F32 dist_vec(const LLVector3 &a, const LLVector3 &b); // Returns distance between a and b -F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b);// Returns distance squared between a and b -F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b);// Returns distance squared between a and b ignoring Z component -LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b -LLVector3 inverse_projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a scaled such that projected_vec(inverse_projected_vec(a, b), b) == b; -LLVector3 parallel_component(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b (same as projected_vec) -LLVector3 orthogonal_component(const LLVector3 &a, const LLVector3 &b); // Returns component of vector a not parallel to vector b (same as projected_vec) -LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u); // Returns a vector that is a linear interpolation between a and b -LLVector3 point_to_box_offset(LLVector3& pos, const LLVector3* box); // Displacement from query point to nearest point on bounding box. -bool box_valid_and_non_zero(const LLVector3* box); - -inline LLVector3::LLVector3(void) -{ - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; -} - -inline LLVector3::LLVector3(const F32 x, const F32 y, const F32 z) -{ - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; -} - -inline LLVector3::LLVector3(const F32 *vec) -{ - mV[VX] = vec[VX]; - mV[VY] = vec[VY]; - mV[VZ] = vec[VZ]; -} - -/* -inline LLVector3::LLVector3(const LLVector3 ©) -{ - mV[VX] = copy.mV[VX]; - mV[VY] = copy.mV[VY]; - mV[VZ] = copy.mV[VZ]; -} -*/ - -// Destructors - -// checker -inline bool LLVector3::isFinite() const -{ - return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ])); -} - - -// Clear and Assignment Functions - -inline void LLVector3::clear(void) -{ - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; -} - -inline void LLVector3::setZero(void) -{ - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; -} - -inline void LLVector3::clearVec(void) -{ - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; -} - -inline void LLVector3::zeroVec(void) -{ - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; -} - -inline void LLVector3::set(F32 x, F32 y, F32 z) -{ - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; -} - -inline void LLVector3::set(const LLVector3 &vec) -{ - mV[0] = vec.mV[0]; - mV[1] = vec.mV[1]; - mV[2] = vec.mV[2]; -} - -inline void LLVector3::set(const F32 *vec) -{ - mV[0] = vec[0]; - mV[1] = vec[1]; - mV[2] = vec[2]; -} - -// deprecated -inline void LLVector3::setVec(F32 x, F32 y, F32 z) -{ - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; -} - -// deprecated -inline void LLVector3::setVec(const LLVector3 &vec) -{ - mV[0] = vec.mV[0]; - mV[1] = vec.mV[1]; - mV[2] = vec.mV[2]; -} - -// deprecated -inline void LLVector3::setVec(const F32 *vec) -{ - mV[0] = vec[0]; - mV[1] = vec[1]; - mV[2] = vec[2]; -} - -inline F32 LLVector3::normalize(void) -{ - F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); - F32 oomag; - - if (mag > FP_MAG_THRESHOLD) - { - oomag = 1.f/mag; - mV[0] *= oomag; - mV[1] *= oomag; - mV[2] *= oomag; - } - else - { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; - mag = 0; - } - return (mag); -} - -// deprecated -inline F32 LLVector3::normVec(void) -{ - F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); - F32 oomag; - - if (mag > FP_MAG_THRESHOLD) - { - oomag = 1.f/mag; - mV[0] *= oomag; - mV[1] *= oomag; - mV[2] *= oomag; - } - else - { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; - mag = 0; - } - return (mag); -} - -// LLVector3 Magnitude and Normalization Functions - -inline F32 LLVector3::length(void) const -{ - return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); -} - -inline F32 LLVector3::lengthSquared(void) const -{ - return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; -} - -inline F32 LLVector3::magVec(void) const -{ - return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); -} - -inline F32 LLVector3::magVecSquared(void) const -{ - return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; -} - -inline bool LLVector3::inRange( F32 min, F32 max ) const -{ - return mV[0] >= min && mV[0] <= max && - mV[1] >= min && mV[1] <= max && - mV[2] >= min && mV[2] <= max; -} - -inline LLVector3 operator+(const LLVector3 &a, const LLVector3 &b) -{ - LLVector3 c(a); - return c += b; -} - -inline LLVector3 operator-(const LLVector3 &a, const LLVector3 &b) -{ - LLVector3 c(a); - return c -= b; -} - -inline F32 operator*(const LLVector3 &a, const LLVector3 &b) -{ - return (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1] + a.mV[2]*b.mV[2]); -} - -inline LLVector3 operator%(const LLVector3 &a, const LLVector3 &b) -{ - return LLVector3( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1] ); -} - -inline LLVector3 operator/(const LLVector3 &a, F32 k) -{ - F32 t = 1.f / k; - return LLVector3( a.mV[0] * t, a.mV[1] * t, a.mV[2] * t ); -} - -inline LLVector3 operator*(const LLVector3 &a, F32 k) -{ - return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); -} - -inline LLVector3 operator*(F32 k, const LLVector3 &a) -{ - return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); -} - -inline bool operator==(const LLVector3 &a, const LLVector3 &b) -{ - return ( (a.mV[0] == b.mV[0]) - &&(a.mV[1] == b.mV[1]) - &&(a.mV[2] == b.mV[2])); -} - -inline bool operator!=(const LLVector3 &a, const LLVector3 &b) -{ - return ( (a.mV[0] != b.mV[0]) - ||(a.mV[1] != b.mV[1]) - ||(a.mV[2] != b.mV[2])); -} - -inline bool operator<(const LLVector3 &a, const LLVector3 &b) -{ - return (a.mV[0] < b.mV[0] - || (a.mV[0] == b.mV[0] - && (a.mV[1] < b.mV[1] - || ((a.mV[1] == b.mV[1]) - && a.mV[2] < b.mV[2])))); -} - -inline const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b) -{ - a.mV[0] += b.mV[0]; - a.mV[1] += b.mV[1]; - a.mV[2] += b.mV[2]; - return a; -} - -inline const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b) -{ - a.mV[0] -= b.mV[0]; - a.mV[1] -= b.mV[1]; - a.mV[2] -= b.mV[2]; - return a; -} - -inline const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b) -{ - LLVector3 ret( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1]); - a = ret; - return a; -} - -inline const LLVector3& operator*=(LLVector3 &a, F32 k) -{ - a.mV[0] *= k; - a.mV[1] *= k; - a.mV[2] *= k; - return a; -} - -inline const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b) -{ - a.mV[0] *= b.mV[0]; - a.mV[1] *= b.mV[1]; - a.mV[2] *= b.mV[2]; - return a; -} - -inline const LLVector3& operator/=(LLVector3 &a, F32 k) -{ - F32 t = 1.f / k; - a.mV[0] *= t; - a.mV[1] *= t; - a.mV[2] *= t; - return a; -} - -inline LLVector3 operator-(const LLVector3 &a) -{ - return LLVector3( -a.mV[0], -a.mV[1], -a.mV[2] ); -} - -inline F32 dist_vec(const LLVector3 &a, const LLVector3 &b) -{ - F32 x = a.mV[0] - b.mV[0]; - F32 y = a.mV[1] - b.mV[1]; - F32 z = a.mV[2] - b.mV[2]; - return (F32) sqrt( x*x + y*y + z*z ); -} - -inline F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b) -{ - F32 x = a.mV[0] - b.mV[0]; - F32 y = a.mV[1] - b.mV[1]; - F32 z = a.mV[2] - b.mV[2]; - return x*x + y*y + z*z; -} - -inline F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b) -{ - F32 x = a.mV[0] - b.mV[0]; - F32 y = a.mV[1] - b.mV[1]; - return x*x + y*y; -} - -inline LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b) -{ - F32 bb = b * b; - if (bb > FP_MAG_THRESHOLD * FP_MAG_THRESHOLD) - { - return ((a * b) / bb) * b; - } - else - { - return b.zero; - } -} - -inline LLVector3 inverse_projected_vec(const LLVector3& a, const LLVector3& b) -{ - LLVector3 normalized_a = a; - normalized_a.normalize(); - LLVector3 normalized_b = b; - F32 b_length = normalized_b.normalize(); - - F32 dot_product = normalized_a * normalized_b; - //NB: if a _|_ b, then returns an infinite vector - return normalized_a * (b_length / dot_product); -} - -inline LLVector3 parallel_component(const LLVector3 &a, const LLVector3 &b) -{ - return projected_vec(a, b); -} - -inline LLVector3 orthogonal_component(const LLVector3 &a, const LLVector3 &b) -{ - return a - projected_vec(a, b); -} - - -inline LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u) -{ - return LLVector3( - a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u, - a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u, - a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u); -} - - -inline bool LLVector3::isNull() const -{ - if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ] ) - { - return true; - } - return false; -} - -inline void update_min_max(LLVector3& min, LLVector3& max, const LLVector3& pos) -{ - for (U32 i = 0; i < 3; i++) - { - if (min.mV[i] > pos.mV[i]) - { - min.mV[i] = pos.mV[i]; - } - if (max.mV[i] < pos.mV[i]) - { - max.mV[i] = pos.mV[i]; - } - } -} - -inline void update_min_max(LLVector3& min, LLVector3& max, const F32* pos) -{ - for (U32 i = 0; i < 3; i++) - { - if (min.mV[i] > pos[i]) - { - min.mV[i] = pos[i]; - } - if (max.mV[i] < pos[i]) - { - max.mV[i] = pos[i]; - } - } -} - -inline F32 angle_between(const LLVector3& a, const LLVector3& b) -{ - F32 ab = a * b; // dotproduct - if (ab == -0.0f) - { - ab = 0.0f; // get rid of negative zero - } - LLVector3 c = a % b; // crossproduct - return atan2f(sqrtf(c * c), ab); // return the angle -} - -inline bool are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon) -{ - LLVector3 an = a; - LLVector3 bn = b; - an.normalize(); - bn.normalize(); - F32 dot = an * bn; - if ( (1.0f - fabs(dot)) < epsilon) - { - return true; - } - return false; -} - -inline std::ostream& operator<<(std::ostream& s, const LLVector3 &a) -{ - s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }"; - return s; -} - -#endif +/**
+ * @file v3math.h
+ * @brief LLVector3 class header file.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_V3MATH_H
+#define LL_V3MATH_H
+
+#include "llerror.h"
+#include "llmath.h"
+
+#include "llsd.h"
+class LLVector2;
+class LLVector4;
+class LLVector4a;
+class LLMatrix3;
+class LLMatrix4;
+class LLVector3d;
+class LLQuaternion;
+
+// LLvector3 = |x y z w|
+
+static const U32 LENGTHOFVECTOR3 = 3;
+
+class LLVector3
+{
+ public:
+ F32 mV[LENGTHOFVECTOR3];
+
+ static const LLVector3 zero;
+ static const LLVector3 x_axis;
+ static const LLVector3 y_axis;
+ static const LLVector3 z_axis;
+ static const LLVector3 x_axis_neg;
+ static const LLVector3 y_axis_neg;
+ static const LLVector3 z_axis_neg;
+ static const LLVector3 all_one;
+
+ inline LLVector3(); // Initializes LLVector3 to (0, 0, 0)
+ inline LLVector3(const F32 x, const F32 y, const F32 z); // Initializes LLVector3 to (x. y, z)
+ inline explicit LLVector3(const F32 *vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2])
+ explicit LLVector3(const LLVector2 &vec); // Initializes LLVector3 to (vec[0]. vec[1], 0)
+ explicit LLVector3(const LLVector3d &vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2])
+ explicit LLVector3(const LLVector4 &vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2])
+ explicit LLVector3(const LLVector4a& vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2])
+ explicit LLVector3(const LLSD& sd);
+
+
+ LLSD getValue() const;
+
+ void setValue(const LLSD& sd);
+
+ inline bool isFinite() const; // checks to see if all values of LLVector3 are finite
+ bool clamp(F32 min, F32 max); // Clamps all values to (min,max), returns true if data changed
+ bool clamp(const LLVector3 &min_vec, const LLVector3 &max_vec); // Scales vector by another vector
+ bool clampLength( F32 length_limit ); // Scales vector to limit length to a value
+
+ void quantize16(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization
+ void quantize8(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization
+ void snap(S32 sig_digits); // snaps x,y,z to sig_digits decimal places
+
+ bool abs(); // sets all values to absolute value of original value (first octant), returns true if changed
+
+ inline void clear(); // Clears LLVector3 to (0, 0, 0)
+ inline void setZero(); // Clears LLVector3 to (0, 0, 0)
+ inline void clearVec(); // deprecated
+ inline void zeroVec(); // deprecated
+
+ inline void set(F32 x, F32 y, F32 z); // Sets LLVector3 to (x, y, z, 1)
+ inline void set(const LLVector3 &vec); // Sets LLVector3 to vec
+ inline void set(const F32 *vec); // Sets LLVector3 to vec
+ const LLVector3& set(const LLVector4 &vec);
+ const LLVector3& set(const LLVector3d &vec);// Sets LLVector3 to vec
+
+ inline void setVec(F32 x, F32 y, F32 z); // deprecated
+ inline void setVec(const LLVector3 &vec); // deprecated
+ inline void setVec(const F32 *vec); // deprecated
+
+ const LLVector3& setVec(const LLVector4 &vec); // deprecated
+ const LLVector3& setVec(const LLVector3d &vec); // deprecated
+
+ F32 length() const; // Returns magnitude of LLVector3
+ F32 lengthSquared() const; // Returns magnitude squared of LLVector3
+ F32 magVec() const; // deprecated
+ F32 magVecSquared() const; // deprecated
+
+ inline F32 normalize(); // Normalizes and returns the magnitude of LLVector3
+ inline F32 normVec(); // deprecated
+
+ inline bool inRange( F32 min, F32 max ) const; // Returns true if all values of the vector are between min and max
+
+ const LLVector3& rotVec(F32 angle, const LLVector3 &vec); // Rotates about vec by angle radians
+ const LLVector3& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians
+ const LLVector3& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat
+ const LLVector3& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q
+ const LLVector3& transVec(const LLMatrix4& mat); // Transforms by LLMatrix4 mat (mat * v)
+
+ const LLVector3& scaleVec(const LLVector3& vec); // scales per component by vec
+ LLVector3 scaledVec(const LLVector3& vec) const; // get a copy of this vector scaled by vec
+
+ bool isNull() const; // Returns true if vector has a _very_small_ length
+ bool isExactlyZero() const { return !mV[VX] && !mV[VY] && !mV[VZ]; }
+
+ F32 operator[](int idx) const { return mV[idx]; }
+ F32 &operator[](int idx) { return mV[idx]; }
+
+ friend LLVector3 operator+(const LLVector3 &a, const LLVector3 &b); // Return vector a + b
+ friend LLVector3 operator-(const LLVector3 &a, const LLVector3 &b); // Return vector a minus b
+ friend F32 operator*(const LLVector3 &a, const LLVector3 &b); // Return a dot b
+ friend LLVector3 operator%(const LLVector3 &a, const LLVector3 &b); // Return a cross b
+ friend LLVector3 operator*(const LLVector3 &a, F32 k); // Return a times scaler k
+ friend LLVector3 operator/(const LLVector3 &a, F32 k); // Return a divided by scaler k
+ friend LLVector3 operator*(F32 k, const LLVector3 &a); // Return a times scaler k
+ friend bool operator==(const LLVector3 &a, const LLVector3 &b); // Return a == b
+ friend bool operator!=(const LLVector3 &a, const LLVector3 &b); // Return a != b
+ // less than operator useful for using vectors as std::map keys
+ friend bool operator<(const LLVector3 &a, const LLVector3 &b); // Return a < b
+
+ friend const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b); // Return vector a + b
+ friend const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b); // Return vector a minus b
+ friend const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b); // Return a cross b
+ friend const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b); // Returns a * b;
+ friend const LLVector3& operator*=(LLVector3 &a, F32 k); // Return a times scaler k
+ friend const LLVector3& operator/=(LLVector3 &a, F32 k); // Return a divided by scaler k
+ friend const LLVector3& operator*=(LLVector3 &a, const LLQuaternion &b); // Returns a * b;
+
+ friend LLVector3 operator-(const LLVector3 &a); // Return vector -a
+
+ friend std::ostream& operator<<(std::ostream& s, const LLVector3 &a); // Stream a
+
+ static bool parseVector3(const std::string& buf, LLVector3* value);
+};
+
+typedef LLVector3 LLSimLocalVec;
+
+// Non-member functions
+
+F32 angle_between(const LLVector3 &a, const LLVector3 &b); // Returns angle (radians) between a and b
+bool are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon=F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel
+F32 dist_vec(const LLVector3 &a, const LLVector3 &b); // Returns distance between a and b
+F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b);// Returns distance squared between a and b
+F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b);// Returns distance squared between a and b ignoring Z component
+LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b
+LLVector3 inverse_projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a scaled such that projected_vec(inverse_projected_vec(a, b), b) == b;
+LLVector3 parallel_component(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b (same as projected_vec)
+LLVector3 orthogonal_component(const LLVector3 &a, const LLVector3 &b); // Returns component of vector a not parallel to vector b (same as projected_vec)
+LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u); // Returns a vector that is a linear interpolation between a and b
+LLVector3 point_to_box_offset(LLVector3& pos, const LLVector3* box); // Displacement from query point to nearest point on bounding box.
+bool box_valid_and_non_zero(const LLVector3* box);
+
+inline LLVector3::LLVector3(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline LLVector3::LLVector3(const F32 x, const F32 y, const F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+}
+
+inline LLVector3::LLVector3(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+}
+
+/*
+inline LLVector3::LLVector3(const LLVector3 ©)
+{
+ mV[VX] = copy.mV[VX];
+ mV[VY] = copy.mV[VY];
+ mV[VZ] = copy.mV[VZ];
+}
+*/
+
+// Destructors
+
+// checker
+inline bool LLVector3::isFinite() const
+{
+ return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]));
+}
+
+
+// Clear and Assignment Functions
+
+inline void LLVector3::clear(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline void LLVector3::setZero(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline void LLVector3::clearVec(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline void LLVector3::zeroVec(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline void LLVector3::set(F32 x, F32 y, F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+}
+
+inline void LLVector3::set(const LLVector3 &vec)
+{
+ mV[0] = vec.mV[0];
+ mV[1] = vec.mV[1];
+ mV[2] = vec.mV[2];
+}
+
+inline void LLVector3::set(const F32 *vec)
+{
+ mV[0] = vec[0];
+ mV[1] = vec[1];
+ mV[2] = vec[2];
+}
+
+// deprecated
+inline void LLVector3::setVec(F32 x, F32 y, F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+}
+
+// deprecated
+inline void LLVector3::setVec(const LLVector3 &vec)
+{
+ mV[0] = vec.mV[0];
+ mV[1] = vec.mV[1];
+ mV[2] = vec.mV[2];
+}
+
+// deprecated
+inline void LLVector3::setVec(const F32 *vec)
+{
+ mV[0] = vec[0];
+ mV[1] = vec[1];
+ mV[2] = vec[2];
+}
+
+inline F32 LLVector3::normalize(void)
+{
+ F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+ F32 oomag;
+
+ if (mag > FP_MAG_THRESHOLD)
+ {
+ oomag = 1.f/mag;
+ mV[0] *= oomag;
+ mV[1] *= oomag;
+ mV[2] *= oomag;
+ }
+ else
+ {
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+ mag = 0;
+ }
+ return (mag);
+}
+
+// deprecated
+inline F32 LLVector3::normVec(void)
+{
+ F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+ F32 oomag;
+
+ if (mag > FP_MAG_THRESHOLD)
+ {
+ oomag = 1.f/mag;
+ mV[0] *= oomag;
+ mV[1] *= oomag;
+ mV[2] *= oomag;
+ }
+ else
+ {
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+ mag = 0;
+ }
+ return (mag);
+}
+
+// LLVector3 Magnitude and Normalization Functions
+
+inline F32 LLVector3::length(void) const
+{
+ return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+}
+
+inline F32 LLVector3::lengthSquared(void) const
+{
+ return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
+}
+
+inline F32 LLVector3::magVec(void) const
+{
+ return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+}
+
+inline F32 LLVector3::magVecSquared(void) const
+{
+ return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
+}
+
+inline bool LLVector3::inRange( F32 min, F32 max ) const
+{
+ return mV[0] >= min && mV[0] <= max &&
+ mV[1] >= min && mV[1] <= max &&
+ mV[2] >= min && mV[2] <= max;
+}
+
+inline LLVector3 operator+(const LLVector3 &a, const LLVector3 &b)
+{
+ LLVector3 c(a);
+ return c += b;
+}
+
+inline LLVector3 operator-(const LLVector3 &a, const LLVector3 &b)
+{
+ LLVector3 c(a);
+ return c -= b;
+}
+
+inline F32 operator*(const LLVector3 &a, const LLVector3 &b)
+{
+ return (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1] + a.mV[2]*b.mV[2]);
+}
+
+inline LLVector3 operator%(const LLVector3 &a, const LLVector3 &b)
+{
+ return LLVector3( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1] );
+}
+
+inline LLVector3 operator/(const LLVector3 &a, F32 k)
+{
+ F32 t = 1.f / k;
+ return LLVector3( a.mV[0] * t, a.mV[1] * t, a.mV[2] * t );
+}
+
+inline LLVector3 operator*(const LLVector3 &a, F32 k)
+{
+ return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
+}
+
+inline LLVector3 operator*(F32 k, const LLVector3 &a)
+{
+ return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
+}
+
+inline bool operator==(const LLVector3 &a, const LLVector3 &b)
+{
+ return ( (a.mV[0] == b.mV[0])
+ &&(a.mV[1] == b.mV[1])
+ &&(a.mV[2] == b.mV[2]));
+}
+
+inline bool operator!=(const LLVector3 &a, const LLVector3 &b)
+{
+ return ( (a.mV[0] != b.mV[0])
+ ||(a.mV[1] != b.mV[1])
+ ||(a.mV[2] != b.mV[2]));
+}
+
+inline bool operator<(const LLVector3 &a, const LLVector3 &b)
+{
+ return (a.mV[0] < b.mV[0]
+ || (a.mV[0] == b.mV[0]
+ && (a.mV[1] < b.mV[1]
+ || ((a.mV[1] == b.mV[1])
+ && a.mV[2] < b.mV[2]))));
+}
+
+inline const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b)
+{
+ a.mV[0] += b.mV[0];
+ a.mV[1] += b.mV[1];
+ a.mV[2] += b.mV[2];
+ return a;
+}
+
+inline const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b)
+{
+ a.mV[0] -= b.mV[0];
+ a.mV[1] -= b.mV[1];
+ a.mV[2] -= b.mV[2];
+ return a;
+}
+
+inline const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b)
+{
+ LLVector3 ret( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1]);
+ a = ret;
+ return a;
+}
+
+inline const LLVector3& operator*=(LLVector3 &a, F32 k)
+{
+ a.mV[0] *= k;
+ a.mV[1] *= k;
+ a.mV[2] *= k;
+ return a;
+}
+
+inline const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b)
+{
+ a.mV[0] *= b.mV[0];
+ a.mV[1] *= b.mV[1];
+ a.mV[2] *= b.mV[2];
+ return a;
+}
+
+inline const LLVector3& operator/=(LLVector3 &a, F32 k)
+{
+ F32 t = 1.f / k;
+ a.mV[0] *= t;
+ a.mV[1] *= t;
+ a.mV[2] *= t;
+ return a;
+}
+
+inline LLVector3 operator-(const LLVector3 &a)
+{
+ return LLVector3( -a.mV[0], -a.mV[1], -a.mV[2] );
+}
+
+inline F32 dist_vec(const LLVector3 &a, const LLVector3 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ F32 z = a.mV[2] - b.mV[2];
+ return (F32) sqrt( x*x + y*y + z*z );
+}
+
+inline F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ F32 z = a.mV[2] - b.mV[2];
+ return x*x + y*y + z*z;
+}
+
+inline F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ return x*x + y*y;
+}
+
+inline LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b)
+{
+ F32 bb = b * b;
+ if (bb > FP_MAG_THRESHOLD * FP_MAG_THRESHOLD)
+ {
+ return ((a * b) / bb) * b;
+ }
+ else
+ {
+ return b.zero;
+ }
+}
+
+inline LLVector3 inverse_projected_vec(const LLVector3& a, const LLVector3& b)
+{
+ LLVector3 normalized_a = a;
+ normalized_a.normalize();
+ LLVector3 normalized_b = b;
+ F32 b_length = normalized_b.normalize();
+
+ F32 dot_product = normalized_a * normalized_b;
+ //NB: if a _|_ b, then returns an infinite vector
+ return normalized_a * (b_length / dot_product);
+}
+
+inline LLVector3 parallel_component(const LLVector3 &a, const LLVector3 &b)
+{
+ return projected_vec(a, b);
+}
+
+inline LLVector3 orthogonal_component(const LLVector3 &a, const LLVector3 &b)
+{
+ return a - projected_vec(a, b);
+}
+
+
+inline LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u)
+{
+ return LLVector3(
+ a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
+ a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u,
+ a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u);
+}
+
+
+inline bool LLVector3::isNull() const
+{
+ if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ] )
+ {
+ return true;
+ }
+ return false;
+}
+
+inline void update_min_max(LLVector3& min, LLVector3& max, const LLVector3& pos)
+{
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (min.mV[i] > pos.mV[i])
+ {
+ min.mV[i] = pos.mV[i];
+ }
+ if (max.mV[i] < pos.mV[i])
+ {
+ max.mV[i] = pos.mV[i];
+ }
+ }
+}
+
+inline void update_min_max(LLVector3& min, LLVector3& max, const F32* pos)
+{
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (min.mV[i] > pos[i])
+ {
+ min.mV[i] = pos[i];
+ }
+ if (max.mV[i] < pos[i])
+ {
+ max.mV[i] = pos[i];
+ }
+ }
+}
+
+inline F32 angle_between(const LLVector3& a, const LLVector3& b)
+{
+ F32 ab = a * b; // dotproduct
+ if (ab == -0.0f)
+ {
+ ab = 0.0f; // get rid of negative zero
+ }
+ LLVector3 c = a % b; // crossproduct
+ return atan2f(sqrtf(c * c), ab); // return the angle
+}
+
+inline bool are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon)
+{
+ LLVector3 an = a;
+ LLVector3 bn = b;
+ an.normalize();
+ bn.normalize();
+ F32 dot = an * bn;
+ if ( (1.0f - fabs(dot)) < epsilon)
+ {
+ return true;
+ }
+ return false;
+}
+
+inline std::ostream& operator<<(std::ostream& s, const LLVector3 &a)
+{
+ s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }";
+ return s;
+}
+
+#endif
diff --git a/indra/llmath/v4color.cpp b/indra/llmath/v4color.cpp index 81d18285b9..63ac1e9088 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 498d4f7734..b92522a5db 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 d238d609b4..500d8ed8ab 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 ca6a5425f9..ce15540866 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 a68028d74a..bee6fc3ee8 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 f46033db11..2a21edf198 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 838dee0a54..239c1ca195 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 a301e4ca47..0b583ce189 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
|