summaryrefslogtreecommitdiff
path: root/indra/llmath
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llmath')
-rw-r--r--indra/llmath/camera.h9
-rw-r--r--indra/llmath/coordframe.h9
-rw-r--r--indra/llmath/llbboxlocal.cpp37
-rw-r--r--indra/llmath/llbboxlocal.h50
-rw-r--r--indra/llmath/llcamera.cpp591
-rw-r--r--indra/llmath/llcamera.h169
-rw-r--r--indra/llmath/llcoord.h78
-rw-r--r--indra/llmath/llcoordframe.cpp729
-rw-r--r--indra/llmath/llcoordframe.h156
-rw-r--r--indra/llmath/llinterp.h400
-rw-r--r--indra/llmath/llmath.h402
-rw-r--r--indra/llmath/lloctree.h693
-rw-r--r--indra/llmath/llperlin.cpp276
-rw-r--r--indra/llmath/llperlin.h28
-rw-r--r--indra/llmath/llplane.h49
-rw-r--r--indra/llmath/llquantize.h103
-rw-r--r--indra/llmath/llquaternion.cpp830
-rw-r--r--indra/llmath/llquaternion.h442
-rw-r--r--indra/llmath/llrect.cpp10
-rw-r--r--indra/llmath/llrect.h270
-rw-r--r--indra/llmath/lltreenode.h161
-rw-r--r--indra/llmath/llvolume.cpp4557
-rw-r--r--indra/llmath/llvolume.h882
-rw-r--r--indra/llmath/llvolumemgr.cpp295
-rw-r--r--indra/llmath/llvolumemgr.h82
-rw-r--r--indra/llmath/m3math.cpp530
-rw-r--r--indra/llmath/m3math.h198
-rw-r--r--indra/llmath/m4math.cpp794
-rw-r--r--indra/llmath/m4math.h365
-rw-r--r--indra/llmath/raytrace.cpp1256
-rw-r--r--indra/llmath/raytrace.h214
-rw-r--r--indra/llmath/v2math.cpp92
-rw-r--r--indra/llmath/v2math.h307
-rw-r--r--indra/llmath/v3color.cpp44
-rw-r--r--indra/llmath/v3color.h362
-rw-r--r--indra/llmath/v3dmath.cpp129
-rw-r--r--indra/llmath/v3dmath.h409
-rw-r--r--indra/llmath/v3math.cpp213
-rw-r--r--indra/llmath/v3math.h446
-rw-r--r--indra/llmath/v4color.cpp561
-rw-r--r--indra/llmath/v4color.h539
-rw-r--r--indra/llmath/v4coloru.cpp102
-rw-r--r--indra/llmath/v4coloru.h501
-rw-r--r--indra/llmath/v4math.cpp124
-rw-r--r--indra/llmath/v4math.h385
-rw-r--r--indra/llmath/xform.cpp96
-rw-r--r--indra/llmath/xform.h269
47 files changed, 19244 insertions, 0 deletions
diff --git a/indra/llmath/camera.h b/indra/llmath/camera.h
new file mode 100644
index 0000000000..f130a036a1
--- /dev/null
+++ b/indra/llmath/camera.h
@@ -0,0 +1,9 @@
+/**
+ * @file camera.h
+ * @brief Legacy wrapper header.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
+
+#include "llcamera.h"
diff --git a/indra/llmath/coordframe.h b/indra/llmath/coordframe.h
new file mode 100644
index 0000000000..5efab4b63e
--- /dev/null
+++ b/indra/llmath/coordframe.h
@@ -0,0 +1,9 @@
+/**
+ * @file coordframe.h
+ * @brief Legacy wrapper header.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
+
+#include "llcoordframe.h"
diff --git a/indra/llmath/llbboxlocal.cpp b/indra/llmath/llbboxlocal.cpp
new file mode 100644
index 0000000000..ba0d4f38ed
--- /dev/null
+++ b/indra/llmath/llbboxlocal.cpp
@@ -0,0 +1,37 @@
+/**
+ * @file llbboxlocal.cpp
+ * @brief General purpose bounding box class (Not axis aligned).
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llbboxlocal.h"
+#include "m4math.h"
+
+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] );
+}
+
+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;
+}
+
+LLBBoxLocal operator*(const LLBBoxLocal &a, const LLMatrix4 &b)
+{
+ return LLBBoxLocal( a.mMin * b, a.mMax * b );
+}
diff --git a/indra/llmath/llbboxlocal.h b/indra/llmath/llbboxlocal.h
new file mode 100644
index 0000000000..c8b7fbbbc8
--- /dev/null
+++ b/indra/llmath/llbboxlocal.h
@@ -0,0 +1,50 @@
+/**
+ * @file llbboxlocal.h
+ * @brief General purpose bounding box class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_BBOXLOCAL_H
+#define LL_BBOXLOCAL_H
+
+#include "v3math.h"
+
+class LLMatrix4;
+
+class LLBBoxLocal
+{
+public:
+ 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& 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; }
+
+ BOOL containsPoint(const LLVector3& p) const;
+ BOOL intersects(const LLBBoxLocal& b) const;
+
+ void addPoint(const LLVector3& p);
+ void addBBox(const LLBBoxLocal& b) { addPoint( b.mMin ); addPoint( b.mMax ); }
+
+ void expand( F32 delta );
+
+ friend LLBBoxLocal operator*(const LLBBoxLocal& a, const LLMatrix4& b);
+
+private:
+ LLVector3 mMin;
+ LLVector3 mMax;
+};
+
+LLBBoxLocal operator*(const LLBBoxLocal &a, const LLMatrix4 &b);
+
+
+#endif // LL_BBOXLOCAL_H
diff --git a/indra/llmath/llcamera.cpp b/indra/llmath/llcamera.cpp
new file mode 100644
index 0000000000..675659c68a
--- /dev/null
+++ b/indra/llmath/llcamera.cpp
@@ -0,0 +1,591 @@
+/**
+ * @file llcamera.cpp
+ * @brief Implementation of the LLCamera class.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+#include "llcamera.h"
+
+// ---------------- 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)
+{
+ calculateFrustumPlanes();
+}
+
+
+LLCamera::LLCamera(F32 z_field_of_view, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane) :
+ LLCoordFrame(),
+ mView(z_field_of_view),
+ mAspect(aspect_ratio),
+ mViewHeightInPixels(view_height_in_pixels),
+ mNearPlane(near_plane),
+ mFarPlane(far_plane),
+ mFixedDistance(-1.f)
+{
+ if (mView < MIN_FIELD_OF_VIEW) { mView = MIN_FIELD_OF_VIEW; }
+ else if (mView > MAX_FIELD_OF_VIEW) { mView = MAX_FIELD_OF_VIEW; }
+
+ if (mAspect < MIN_ASPECT_RATIO) { mAspect = MIN_ASPECT_RATIO; }
+ else if (mAspect > MAX_ASPECT_RATIO) { mAspect = MAX_ASPECT_RATIO; }
+
+ if (mNearPlane < MIN_NEAR_PLANE) { mNearPlane = MIN_NEAR_PLANE; }
+ else if (mNearPlane > MAX_NEAR_PLANE) { mNearPlane = MAX_NEAR_PLANE; }
+
+ if (mFarPlane < 0) { mFarPlane = DEFAULT_FAR_PLANE; }
+ else if (mFarPlane < MIN_FAR_PLANE) { mFarPlane = MIN_FAR_PLANE; }
+ else if (mFarPlane > MAX_FAR_PLANE) { mFarPlane = MAX_FAR_PLANE; }
+
+ calculateFrustumPlanes();
+}
+
+
+
+// ---------------- LLCamera::setFoo() member functions ----------------
+
+void LLCamera::setView(F32 field_of_view)
+{
+ mView = field_of_view;
+ if (mView < MIN_FIELD_OF_VIEW) { mView = MIN_FIELD_OF_VIEW; }
+ else if (mView > MAX_FIELD_OF_VIEW) { mView = MAX_FIELD_OF_VIEW; }
+ calculateFrustumPlanes();
+}
+
+void LLCamera::setViewHeightInPixels(S32 height)
+{
+ mViewHeightInPixels = height;
+
+ // Don't really need to do this, but update the pixel meter ratio with it.
+ calculateFrustumPlanes();
+}
+
+void LLCamera::setAspect(F32 aspect_ratio)
+{
+ mAspect = aspect_ratio;
+ if (mAspect < MIN_ASPECT_RATIO) { mAspect = MIN_ASPECT_RATIO; }
+ else if (mAspect > MAX_ASPECT_RATIO) { mAspect = MAX_ASPECT_RATIO; }
+ calculateFrustumPlanes();
+}
+
+
+void LLCamera::setNear(F32 near_plane)
+{
+ mNearPlane = near_plane;
+ if (mNearPlane < MIN_NEAR_PLANE) { mNearPlane = MIN_NEAR_PLANE; }
+ else if (mNearPlane > MAX_NEAR_PLANE) { mNearPlane = MAX_NEAR_PLANE; }
+ calculateFrustumPlanes();
+}
+
+
+void LLCamera::setFar(F32 far_plane)
+{
+ mFarPlane = far_plane;
+ if (mFarPlane < MIN_FAR_PLANE) { mFarPlane = MIN_FAR_PLANE; }
+ else if (mFarPlane > MAX_FAR_PLANE) { mFarPlane = MAX_FAR_PLANE; }
+ calculateFrustumPlanes();
+}
+
+
+// ---------------- read/write to buffer ----------------
+
+size_t LLCamera::writeFrustumToBuffer(char *buffer) const
+{
+ memcpy(buffer, &mView, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(buffer, &mAspect, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(buffer, &mNearPlane, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(buffer, &mFarPlane, sizeof(F32));
+ return 4*sizeof(F32);
+}
+
+size_t LLCamera::readFrustumFromBuffer(const char *buffer)
+{
+ memcpy(&mView, buffer, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(&mAspect, buffer, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(&mNearPlane, buffer, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(&mFarPlane, buffer, sizeof(F32));
+ return 4*sizeof(F32);
+}
+
+
+// ---------------- test methods ----------------
+
+int LLCamera::AABBInFrustum(const LLVector3 &center, const LLVector3& radius)
+{
+ static const LLVector3 scaler[] = {
+ LLVector3(-1,-1,-1),
+ LLVector3( 1,-1,-1),
+ LLVector3(-1, 1,-1),
+ LLVector3( 1, 1,-1),
+ LLVector3(-1,-1, 1),
+ LLVector3( 1,-1, 1),
+ LLVector3(-1, 1, 1),
+ LLVector3( 1, 1, 1)
+ };
+
+ U8 mask = 0;
+ S32 result = 2;
+
+ for (int i = 0; i < 6; i++)
+ {
+ mask = mAgentPlaneMask[i];
+ LLPlane p = mAgentPlanes[i];
+ LLVector3 n = LLVector3(p);
+ float d = p.mV[3];
+ LLVector3 rscale = radius.scaledVec(scaler[mask]);
+
+ LLVector3 minp = center - rscale;
+ LLVector3 maxp = center + rscale;
+
+ if (n * minp > -d)
+ {
+ return 0;
+ }
+
+ if (n * maxp > -d)
+ {
+ result = 1;
+ }
+ }
+
+ return result;
+}
+
+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;
+
+ if (dsq < rsq)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+// HACK: This version is still around because the version below doesn't work
+// unless the agent planes are initialized.
+// Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
+// NOTE: 'center' is in absolute frame.
+int LLCamera::sphereInFrustumOld(const LLVector3 &sphere_center, const F32 radius) const
+{
+ // Returns 1 if sphere is in frustum, 0 if not.
+ // modified so that default view frust is along X with Z vertical
+ F32 x, y, z, rightDist, leftDist, topDist, bottomDist;
+
+ // Subtract the view position
+ //LLVector3 relative_center;
+ //relative_center = sphere_center - getOrigin();
+ LLVector3 rel_center(sphere_center);
+ rel_center -= mOrigin;
+
+ bool all_in = TRUE;
+
+ // Transform relative_center.x to camera frame
+ x = mXAxis * rel_center;
+ if (x < MIN_NEAR_PLANE - radius)
+ {
+ return 0;
+ }
+ else if (x < MIN_NEAR_PLANE + radius)
+ {
+ all_in = FALSE;
+ }
+
+ if (x > mFarPlane + radius)
+ {
+ return 0;
+ }
+ else if (x > mFarPlane - radius)
+ {
+ all_in = FALSE;
+ }
+
+ // Transform relative_center.y to camera frame
+ y = mYAxis * rel_center;
+
+ // distance to plane is the dot product of (x, y, 0) * plane_normal
+ rightDist = x * mLocalPlanes[PLANE_RIGHT][VX] + y * mLocalPlanes[PLANE_RIGHT][VY];
+ if (rightDist < -radius)
+ {
+ return 0;
+ }
+ else if (rightDist < radius)
+ {
+ all_in = FALSE;
+ }
+
+ leftDist = x * mLocalPlanes[PLANE_LEFT][VX] + y * mLocalPlanes[PLANE_LEFT][VY];
+ if (leftDist < -radius)
+ {
+ return 0;
+ }
+ else if (leftDist < radius)
+ {
+ all_in = FALSE;
+ }
+
+ // Transform relative_center.y to camera frame
+ z = mZAxis * rel_center;
+
+ topDist = x * mLocalPlanes[PLANE_TOP][VX] + z * mLocalPlanes[PLANE_TOP][VZ];
+ if (topDist < -radius)
+ {
+ return 0;
+ }
+ else if (topDist < radius)
+ {
+ all_in = FALSE;
+ }
+
+ bottomDist = x * mLocalPlanes[PLANE_BOTTOM][VX] + z * mLocalPlanes[PLANE_BOTTOM][VZ];
+ if (bottomDist < -radius)
+ {
+ return 0;
+ }
+ else if (bottomDist < radius)
+ {
+ all_in = FALSE;
+ }
+
+ if (all_in)
+ {
+ return 2;
+ }
+
+ return 1;
+}
+
+
+// HACK: This (presumably faster) version only currently works if you set up the
+// frustum planes using GL. At some point we should get those planes through another
+// mechanism, and then we can get rid of the "old" version above.
+
+// 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.
+ int res = 2;
+ for (int i = 0; i < 6; i++)
+ {
+ float d = mAgentPlanes[i].dist(sphere_center);
+
+ if (d > radius)
+ {
+ return 0;
+ }
+
+ if (d > -radius)
+ {
+ res = 1;
+ }
+ }
+
+ return res;
+}
+
+
+// return height of a sphere of given radius, located at center, in pixels
+F32 LLCamera::heightInPixels(const LLVector3 &center, F32 radius ) const
+{
+ 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;
+
+ // Compute distance to sphere
+ F32 dist = vec.magVec();
+
+ // 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;
+
+ // Compute number of pixels tall, based on vertical field of view
+ return (fraction_of_fov * mViewHeightInPixels);
+ }
+ else
+ {
+ // return invalid height
+ return -1.0f;
+ }
+}
+
+// If pos is visible, return the distance from pos to the camera.
+// Use fudge distance to scale rad against top/bot/left/right planes
+// Otherwise, return -distance
+F32 LLCamera::visibleDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
+{
+ if (mFixedDistance > 0)
+ {
+ return mFixedDistance;
+ }
+ LLVector3 dvec = pos - mOrigin;
+ // Check visibility
+ F32 dist = dvec.magVec();
+ if (dist > rad)
+ {
+ F32 dp,tdist;
+ dp = dvec * mXAxis;
+ if (dp < -rad)
+ return -dist;
+
+ rad *= fudgedist;
+ LLVector3 tvec(pos);
+ for (int p=0; p<PLANE_NUM; p++)
+ {
+ if (!(planemask & (1<<p)))
+ continue;
+ tdist = -(mWorldPlanes[p].dist(tvec));
+ if (tdist > rad)
+ return -dist;
+ }
+ }
+ return dist;
+}
+
+// Like visibleDistance, except uses mHorizPlanes[], which are left and right
+// planes perpindicular to (0,0,1) in world space
+F32 LLCamera::visibleHorizDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
+{
+ if (mFixedDistance > 0)
+ {
+ return mFixedDistance;
+ }
+ LLVector3 dvec = pos - mOrigin;
+ // Check visibility
+ F32 dist = dvec.magVec();
+ if (dist > rad)
+ {
+ rad *= fudgedist;
+ LLVector3 tvec(pos);
+ for (int p=0; p<HORIZ_PLANE_NUM; p++)
+ {
+ if (!(planemask & (1<<p)))
+ continue;
+ F32 tdist = -(mHorizPlanes[p].dist(tvec));
+ if (tdist > rad)
+ return -dist;
+ }
+ }
+ return dist;
+}
+
+// ---------------- friends and operators ----------------
+
+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 << " TopPlane = " << C.mLocalPlanes[LLCamera::PLANE_TOP][VX] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_TOP][VY] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_TOP][VZ] << "\n";
+ s << " BottomPlane = " << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VX] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VY] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VZ] << "\n";
+ s << " LeftPlane = " << C.mLocalPlanes[LLCamera::PLANE_LEFT][VX] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_LEFT][VY] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_LEFT][VZ] << "\n";
+ s << " RightPlane = " << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VX] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VY] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VZ] << "\n";
+ s << "}";
+ return s;
+}
+
+
+
+// ---------------- private member functions ----------------
+
+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.
+
+ F32 left,right,top,bottom;
+ top = mFarPlane * (F32)tanf(0.5f * mView);
+ bottom = -top;
+ left = top * mAspect;
+ right = -left;
+
+ calculateFrustumPlanes(left, right, top, bottom);
+}
+
+LLPlane planeFromPoints(LLVector3 p1, LLVector3 p2, LLVector3 p3)
+{
+ LLVector3 n = ((p2-p1)%(p3-p1));
+ n.normVec();
+
+ return LLPlane(p1, n);
+}
+
+
+void LLCamera::calcAgentFrustumPlanes(LLVector3* frust)
+{
+
+ for (int i = 0; i < 8; i++)
+ {
+ mAgentFrustum[i] = frust[i];
+ }
+
+ //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
+
+ //near - frust[0], frust[1], frust[2]
+ mAgentPlanes[2] = planeFromPoints(frust[0], frust[1], frust[2]);
+
+ //far
+ mAgentPlanes[5] = planeFromPoints(frust[5], frust[4], frust[6]);
+
+ //left
+ mAgentPlanes[0] = planeFromPoints(frust[4], frust[0], frust[7]);
+
+ //right
+ mAgentPlanes[1] = planeFromPoints(frust[1], frust[5], frust[6]);
+
+ //top
+ mAgentPlanes[4] = planeFromPoints(frust[3], frust[2], frust[6]);
+
+ //bottom
+ mAgentPlanes[3] = planeFromPoints(frust[1], frust[0], frust[4]);
+
+ //cache plane octant facing mask for use in AABBInFrustum
+ for (int i = 0; i < 8; i++)
+ {
+ U8 mask = 0;
+ LLPlane p = mAgentPlanes[i];
+ LLVector3 n = LLVector3(p);
+
+ if (n.mV[0] >= 0)
+ {
+ mask |= 1;
+ }
+ if (n.mV[1] >= 0)
+ {
+ mask |= 2;
+ }
+ if (n.mV[2] >= 0)
+ {
+ mask |= 4;
+ }
+ mAgentPlaneMask[i] = mask;
+ }
+}
+
+void LLCamera::calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom)
+{
+ LLVector3 a, b, c;
+
+ // For each plane we need to define 3 points (LLVector3's) in camera view space.
+ // The order in which we pass the points to planeFromPoints() matters, because the
+ // plane normal has a degeneracy of 2; we want it pointing _into_ the frustum.
+
+ a.setVec(0.0f, 0.0f, 0.0f);
+ b.setVec(mFarPlane, right, top);
+ c.setVec(mFarPlane, right, bottom);
+ mLocalPlanes[PLANE_RIGHT].setVec(a, b, c);
+
+ c.setVec(mFarPlane, left, top);
+ mLocalPlanes[PLANE_TOP].setVec(a, c, b);
+
+ b.setVec(mFarPlane, left, bottom);
+ mLocalPlanes[PLANE_LEFT].setVec(a, b, c);
+
+ c.setVec(mFarPlane, right, bottom);
+ mLocalPlanes[PLANE_BOTTOM].setVec( a, c, b);
+
+ //calculate center and radius squared of frustum in world absolute coordinates
+ 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;
+
+ calculateFrustumPlanes(left, right, top, bottom);
+}
+
+void LLCamera::calculateWorldFrustumPlanes()
+{
+ F32 d;
+ LLVector3 center = mOrigin - mXAxis*mNearPlane;
+ mWorldPlanePos = center;
+ for (int p=0; p<4; p++)
+ {
+ LLVector3 pnorm = LLVector3(mLocalPlanes[p]);
+ LLVector3 norm = rotateToAbsolute(pnorm);
+ norm.normVec();
+ d = -(center * norm);
+ mWorldPlanes[p] = LLPlane(norm, d);
+ }
+ // horizontal planes, perpindicular to (0,0,1);
+ LLVector3 zaxis(0, 0, 1.0f);
+ F32 yaw = getYaw();
+ {
+ LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_LEFT]);
+ tnorm.rotVec(yaw, zaxis);
+ d = -(mOrigin * tnorm);
+ mHorizPlanes[HORIZ_PLANE_LEFT] = LLPlane(tnorm, d);
+ }
+ {
+ LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_RIGHT]);
+ tnorm.rotVec(yaw, zaxis);
+ d = -(mOrigin * tnorm);
+ mHorizPlanes[HORIZ_PLANE_RIGHT] = LLPlane(tnorm, d);
+ }
+}
+
+// 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 };
diff --git a/indra/llmath/llcamera.h b/indra/llmath/llcamera.h
new file mode 100644
index 0000000000..685f919ac4
--- /dev/null
+++ b/indra/llmath/llcamera.h
@@ -0,0 +1,169 @@
+/**
+ * @file llcamera.h
+ * @brief Header file for the LLCamera class.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_CAMERA_H
+#define LL_CAMERA_H
+
+
+#include "llmath.h"
+#include "llcoordframe.h"
+#include "llplane.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 MAX_FIELD_OF_VIEW = F_PI;
+const F32 MAX_ASPECT_RATIO = 50.0f;
+const F32 MAX_NEAR_PLANE = 10.f;
+const F32 MAX_FAR_PLANE = 100000.0f; //1000000.0f; // Max allowed. Not good Z precision though.
+const F32 MAX_FAR_CLIP = 1024.0f;
+
+const F32 MIN_FIELD_OF_VIEW = 0.1f;
+const F32 MIN_ASPECT_RATIO = 0.02f;
+const F32 MIN_NEAR_PLANE = 0.1f;
+const F32 MIN_FAR_PLANE = 0.2f;
+
+static const LLVector3 X_AXIS(1.f,0.f,0.f);
+static const LLVector3 Y_AXIS(0.f,1.f,0.f);
+static const LLVector3 Z_AXIS(0.f,0.f,1.f);
+
+static const LLVector3 NEG_X_AXIS(-1.f,0.f,0.f);
+static const LLVector3 NEG_Y_AXIS(0.f,-1.f,0.f);
+static const LLVector3 NEG_Z_AXIS(0.f,0.f,-1.f);
+
+
+// An LLCamera is an LLCoorFrame with a view frustum.
+// This means that it has several methods for moving it around
+// that are inherited from the LLCoordFrame() class :
+//
+// setOrigin(), setAxes()
+// translate(), rotate()
+// roll(), pitch(), yaw()
+// etc...
+
+
+class LLCamera
+: public LLCoordFrame
+{
+public:
+ enum {
+ PLANE_LEFT = 0,
+ PLANE_RIGHT = 1,
+ PLANE_BOTTOM = 2,
+ PLANE_TOP = 3,
+ PLANE_NUM = 4
+ };
+ 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 {
+ 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
+ };
+
+protected:
+ F32 mView; // angle between top and bottom frustum planes in radians.
+ F32 mAspect; // width/height
+ S32 mViewHeightInPixels; // for ViewHeightInPixels() only
+ F32 mNearPlane;
+ F32 mFarPlane;
+ LLPlane mLocalPlanes[4];
+ F32 mFixedDistance; // Always return this distance, unless < 0
+ LLVector3 mFrustCenter; // center of frustum and radius squared for ultra-quick exclusion test
+ F32 mFrustRadiusSquared;
+
+ LLPlane mWorldPlanes[PLANE_NUM];
+ LLPlane mHorizPlanes[HORIZ_PLANE_NUM];
+ LLPlane mAgentPlanes[6]; //frustum in agent space a la gluUnproject (I'm a bastard, I know) - DaveP
+ U8 mAgentPlaneMask[6];
+ LLVector3 mWorldPlanePos; // Position of World Planes (may be offset from camera)
+public:
+ LLVector3 mAgentFrustum[8];
+
+public:
+ LLCamera();
+ LLCamera(F32 z_field_of_view, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane);
+
+ void setView(F32 new_view);
+ 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
+
+ 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 LLPlane& getWorldPlane(S32 index) const { return mWorldPlanes[index]; }
+ 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);
+ // Returns 1 if partly in, 2 if fully in.
+ // NOTE: 'center' is in absolute frame.
+ S32 sphereInFrustumOld(const LLVector3 &center, const F32 radius) const;
+ S32 sphereInFrustum(const LLVector3 &center, const F32 radius) const;
+ S32 pointInFrustum(const LLVector3 &point) const { return sphereInFrustum(point, 0.0f); }
+ S32 sphereInFrustumFull(const LLVector3 &center, const F32 radius) const { return sphereInFrustum(center, radius); }
+ S32 AABBInFrustum(const LLVector3 &center, const LLVector3& 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 &center, F32 radius ) const;
+
+ // return the distance from pos to camera if visible (-distance if not visible)
+ F32 visibleDistance(const LLVector3 &pos, F32 rad, F32 fudgescale = 1.0f, U32 planemask = PLANE_ALL_MASK) const;
+ F32 visibleHorizDistance(const LLVector3 &pos, F32 rad, F32 fudgescale = 1.0f, U32 planemask = HORIZ_PLANE_ALL_MASK) const;
+ 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 calculateWorldFrustumPlanes();
+};
+
+
+#endif
+
+
+
diff --git a/indra/llmath/llcoord.h b/indra/llmath/llcoord.h
new file mode 100644
index 0000000000..4eaf86e8b1
--- /dev/null
+++ b/indra/llmath/llcoord.h
@@ -0,0 +1,78 @@
+/**
+ * @file llcoord.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCOORD_H
+#define LL_LLCOORD_H
+
+// A two-dimensional pixel value
+class LLCoord
+{
+public:
+ S32 mX;
+ S32 mY;
+
+ LLCoord(): mX(0), mY(0)
+ {}
+ LLCoord(S32 x, S32 y): mX(x), mY(y)
+ {}
+ virtual ~LLCoord()
+ {}
+
+ virtual void set(S32 x, S32 y) { mX = x; mY = y; }
+};
+
+
+// GL coordinates start in the client region of a window,
+// with left, bottom = 0, 0
+class LLCoordGL : public LLCoord
+{
+public:
+ LLCoordGL() : LLCoord()
+ {}
+ LLCoordGL(S32 x, S32 y) : LLCoord(x, y)
+ {}
+};
+
+
+// Window coords include things like window borders,
+// menu regions, etc.
+class LLCoordWindow : public LLCoord
+{
+public:
+ LLCoordWindow() : LLCoord()
+ {}
+ LLCoordWindow(S32 x, S32 y) : LLCoord(x, y)
+ {}
+};
+
+
+// Screen coords start at left, top = 0, 0
+class LLCoordScreen : public LLCoord
+{
+public:
+ LLCoordScreen() : LLCoord()
+ {}
+ LLCoordScreen(S32 x, S32 y) : LLCoord(x, y)
+ {}
+};
+
+class LLCoordFont : public LLCoord
+{
+public:
+ F32 mZ;
+
+ LLCoordFont() : LLCoord(), mZ(0.f)
+ {}
+ LLCoordFont(S32 x, S32 y, F32 z = 0) : LLCoord(x,y), mZ(z)
+ {}
+
+ void set(S32 x, S32 y) { LLCoord::set(x,y); mZ = 0.f; }
+ void set(S32 x, S32 y, F32 z) { mX = x; mY = y; mZ = z; }
+};
+
+
+#endif
diff --git a/indra/llmath/llcoordframe.cpp b/indra/llmath/llcoordframe.cpp
new file mode 100644
index 0000000000..c8b69e57cd
--- /dev/null
+++ b/indra/llmath/llcoordframe.cpp
@@ -0,0 +1,729 @@
+/**
+ * @file llcoordframe.cpp
+ * @brief LLCoordFrame class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include "vmath.h"
+#include "v3math.h"
+#include "m3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+#include "llcoordframe.h"
+
+#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
+#endif
+
+// Constructors
+
+LLCoordFrame::LLCoordFrame() :
+ 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)
+{
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLVector3 &direction) :
+ mOrigin(origin)
+{
+ lookDir(direction);
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+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)
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+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)
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+
+LLCoordFrame::LLCoordFrame(const LLVector3 &origin,
+ const LLMatrix3 &rotation) :
+ mOrigin(origin),
+ mXAxis(rotation.mMatrix[VX]),
+ mYAxis(rotation.mMatrix[VY]),
+ mZAxis(rotation.mMatrix[VZ])
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLQuaternion &q) :
+ 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]);
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLQuaternion &q) :
+ mOrigin(origin)
+{
+ LLMatrix3 rotation_matrix(q);
+ mXAxis.setVec(rotation_matrix.mMatrix[VX]);
+ mYAxis.setVec(rotation_matrix.mMatrix[VY]);
+ mZAxis.setVec(rotation_matrix.mMatrix[VZ]);
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLMatrix4 &mat) :
+ mOrigin(mat.mMatrix[VW]),
+ mXAxis(mat.mMatrix[VX]),
+ mYAxis(mat.mMatrix[VY]),
+ mZAxis(mat.mMatrix[VZ])
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+
+// 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)
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+*/
+
+/*
+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))
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+*/
+
+
+void LLCoordFrame::reset()
+{
+ 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);
+}
+
+// setOrigin() member functions set mOrigin
+
+void LLCoordFrame::setOrigin(F32 x, F32 y, F32 z)
+{
+ mOrigin.setVec(x, y, z);
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setOrigin()" << llendl;
+ }
+}
+
+void LLCoordFrame::setOrigin(const LLVector3 &new_origin)
+{
+ mOrigin = new_origin;
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setOrigin()" << llendl;
+ }
+}
+
+void LLCoordFrame::setOrigin(const F32 *origin)
+{
+ mOrigin.mV[VX] = *(origin + VX);
+ mOrigin.mV[VY] = *(origin + VY);
+ mOrigin.mV[VZ] = *(origin + VZ);
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setOrigin()" << llendl;
+ }
+}
+
+void LLCoordFrame::setOrigin(const LLCoordFrame &frame)
+{
+ mOrigin = frame.getOrigin();
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setOrigin()" << llendl;
+ }
+}
+
+// setAxes() member functions set the axes, and assume that
+// the arguments are orthogonal and normalized.
+
+void LLCoordFrame::setAxes(const LLVector3 &x_axis,
+ const LLVector3 &y_axis,
+ const LLVector3 &z_axis)
+{
+ mXAxis = x_axis;
+ mYAxis = y_axis;
+ mZAxis = z_axis;
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+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]);
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::setAxes(const LLQuaternion &q )
+{
+ LLMatrix3 rotation_matrix(q);
+ setAxes(rotation_matrix);
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+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);
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::setAxes(const LLCoordFrame &frame)
+{
+ mXAxis = frame.getXAxis();
+ mYAxis = frame.getYAxis();
+ mZAxis = frame.getZAxis();
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+// 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;
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::translate()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::translate(const LLVector3 &v)
+{
+ mOrigin += v;
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::translate()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::translate(const F32 *origin)
+{
+ mOrigin.mV[VX] += *(origin + VX);
+ mOrigin.mV[VY] += *(origin + VY);
+ mOrigin.mV[VZ] += *(origin + VZ);
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::translate()" << llendl;
+ }
+}
+
+
+// Rotate move the axes to a relative rotation
+
+void LLCoordFrame::rotate(F32 angle, F32 x, F32 y, F32 z)
+{
+ LLQuaternion q(angle, LLVector3(x,y,z));
+ rotate(q);
+}
+
+
+void LLCoordFrame::rotate(F32 angle, const LLVector3 &rotation_axis)
+{
+ LLQuaternion q(angle, rotation_axis);
+ rotate(q);
+}
+
+
+void LLCoordFrame::rotate(const LLQuaternion &q)
+{
+ LLMatrix3 rotation_matrix(q);
+ rotate(rotation_matrix);
+}
+
+
+void LLCoordFrame::rotate(const LLMatrix3 &rotation_matrix)
+{
+ mXAxis.rotVec(rotation_matrix);
+ mYAxis.rotVec(rotation_matrix);
+ orthonormalize();
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::rotate()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::roll(F32 angle)
+{
+ LLQuaternion q(angle, mXAxis);
+ LLMatrix3 rotation_matrix(q);
+ rotate(rotation_matrix);
+
+ if( !mYAxis.isFinite() || !mZAxis.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::roll()" << llendl;
+ }
+}
+
+void LLCoordFrame::pitch(F32 angle)
+{
+ LLQuaternion q(angle, mYAxis);
+ LLMatrix3 rotation_matrix(q);
+ rotate(rotation_matrix);
+
+ if( !mXAxis.isFinite() || !mZAxis.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::pitch()" << llendl;
+ }
+}
+
+void LLCoordFrame::yaw(F32 angle)
+{
+ LLQuaternion q(angle, mZAxis);
+ LLMatrix3 rotation_matrix(q);
+ rotate(rotation_matrix);
+
+ if( !mXAxis.isFinite() || !mYAxis.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::yaw()" << llendl;
+ }
+}
+
+// get*() routines
+
+
+LLQuaternion LLCoordFrame::getQuaternion() const
+{
+ LLQuaternion quat(mXAxis, mYAxis, mZAxis);
+ return quat;
+}
+
+
+void LLCoordFrame::getMatrixToLocal(LLMatrix4& mat) const
+{
+ 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]));
+}
+
+
+void LLCoordFrame::getRotMatrixToParent(LLMatrix4& mat) const
+{
+ // 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));
+ buffer += 3*sizeof(F32);
+ memcpy(buffer, mXAxis.mV, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(buffer, mYAxis.mV, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(buffer, mZAxis.mV, 3*sizeof(F32));
+ return 12*sizeof(F32);
+}
+
+
+size_t LLCoordFrame::readOrientation(const char *buffer)
+{
+ memcpy(mOrigin.mV, buffer, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(mXAxis.mV, buffer, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(mYAxis.mV, buffer, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(mZAxis.mV, buffer, 3*sizeof(F32));
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::readOrientation()" << llendl;
+ }
+
+ return 12*sizeof(F32);
+}
+
+
+// rotation and transform vectors between reference frames
+
+LLVector3 LLCoordFrame::rotateToLocal(const LLVector3 &absolute_vector) const
+{
+ 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;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+
+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
+}
+
+
+LLVector3 LLCoordFrame::transformToLocal(const LLVector3 &absolute_vector) const
+{
+ 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);
+}
+
+
+LLVector3 LLCoordFrame::transformToAbsolute(const LLVector3 &local_vector) const
+{
+ 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;
+}
+
+
+// This is how you combine a translation and rotation of a
+// coordinate frame to get an OpenGL transformation matrix:
+//
+// translation * rotation = transformation matrix
+//
+// (i)->
+// (j)| 1 0 0 0 | | a d g 0 | | a d g 0 |
+// | | 0 1 0 0 | * | b e h 0 | = | b e h 0 |
+// 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
+// {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 + 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 + 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 + 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 + 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;
+}
+
+
+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 + 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 + 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();
+
+ LLVector3 up = at % left;
+ setAxes(at, left, up);
+}
+
+void LLCoordFrame::lookDir(const LLVector3 &xuv)
+{
+ 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);
+}
+
+void LLCoordFrame::lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest)
+{
+ static LLVector3 up_direction(0.0f, 0.0f, 1.0f);
+
+ setOrigin(origin);
+ LLVector3 at(point_of_interest - origin);
+ at.normVec();
+ lookDir(at, up_direction);
+}
+
+
+// Operators and friends
+
+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;
+}
+
+
+
+// Private member functions
+
+
+//EOF
diff --git a/indra/llmath/llcoordframe.h b/indra/llmath/llcoordframe.h
new file mode 100644
index 0000000000..ddd20c4491
--- /dev/null
+++ b/indra/llmath/llcoordframe.h
@@ -0,0 +1,156 @@
+/**
+ * @file llcoordframe.h
+ * @brief LLCoordFrame class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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
new file mode 100644
index 0000000000..61bf1029fb
--- /dev/null
+++ b/indra/llmath/llinterp.h
@@ -0,0 +1,400 @@
+/**
+ * @file llinterp.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINTERP_H
+#define LL_LLINTERP_H
+
+#include "math.h"
+
+// Class from which different types of interpolators can be derived
+
+class LLInterpVal
+{
+public:
+ virtual ~LLInterpVal() {}
+ virtual void interp(LLInterpVal &target, const F32 frac); // Linear interpolation for each type
+};
+
+template <typename Type>
+class LLInterp
+{
+public:
+ LLInterp();
+ virtual ~LLInterp() {}
+
+ virtual void start();
+ void update(const F32 time);
+ 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:
+ /*virtual*/ void start();
+ void update(const F32 time);
+ 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();
+ /*virtual*/ void start();
+ void setStartVel(const Type &vel);
+ void setForce(const F32 force);
+ void update(const F32 time);
+protected:
+ F32 mForce;
+ Type mStartVel;
+ Type mVelocity;
+};
+
+template <typename Type>
+class LLInterpFunc : public LLInterp<Type>
+{
+public:
+ LLInterpFunc();
+ void update(const F32 time);
+
+ 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()
+{
+ 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 = NULL;
+ mData = NULL;
+}
+
+template <class Type>
+void LLInterpFunc<Type>::setFunc(Type (*func)(const F32, void *data), void *data)
+{
+ mFunc = func;
+ mData = data;
+}
+
+template <class Type>
+void LLInterpFunc<Type>::update(const F32 time)
+{
+ if (time > this->mStartTime)
+ {
+ this->mActive = TRUE;
+ }
+ else
+ {
+ return;
+ }
+ if (time > this->mEndTime)
+ {
+ this->mDone = TRUE;
+ return;
+ }
+
+ this->mCurVal = (*mFunc)(time - this->mStartTime, mData);
+ this->mCurTime = time;
+}
+
+//////////////////////////////
+//
+// LLInterpExp derived class implementation.
+//
+
+template <class Type>
+void LLInterpExp<Type>::update(const F32 time)
+{
+ F32 target_frac = (time - this->mStartTime) / this->mDuration;
+ if (target_frac >= 0.f)
+ {
+ this->mActive = TRUE;
+ }
+
+ if (target_frac > 1.f)
+ {
+ this->mCurVal = this->mEndVal;
+ this->mCurFrac = 1.f;
+ this->mCurTime = time;
+ this->mDone = TRUE;
+ return;
+ }
+
+ this->mCurFrac = 1.f - (F32)(exp(-2.f*target_frac));
+ this->mCurVal = this->mStartVal + this->mCurFrac * (this->mEndVal - this->mStartVal);
+ this->mCurTime = time;
+}
+
+#endif // LL_LLINTERP_H
+
diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h
new file mode 100644
index 0000000000..ad8ced9e1a
--- /dev/null
+++ b/indra/llmath/llmath.h
@@ -0,0 +1,402 @@
+/**
+ * @file llmath.h
+ * @brief Useful math constants and macros.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLMATH_H
+#define LLMATH_H
+
+#include <cmath>
+#include <math.h>
+#include <stdlib.h>
+
+#include "lldefs.h"
+
+// work around for Windows & older gcc non-standard function names.
+#if LL_WINDOWS
+#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
+#ifndef fsqrtf
+#define fsqrtf(x) ((F32)sqrt((F64)(x)))
+#endif
+#ifndef sqrtf
+#define sqrtf(x) ((F32)sqrt((F64)(x)))
+#endif
+
+#ifndef cosf
+#define cosf(x) ((F32)cos((F64)(x)))
+#endif
+#ifndef sinf
+#define sinf(x) ((F32)sin((F64)(x)))
+#endif
+#ifndef tanf
+#define tanf(x) ((F32)tan((F64)(x)))
+#endif
+#ifndef acosf
+#define acosf(x) ((F32)acos((F64)(x)))
+#endif
+
+#ifndef powf
+#define powf(x,y) ((F32)pow((F64)(x),(F64)(y)))
+#endif
+
+const F32 GRAVITY = -9.8f;
+
+// mathematical constants
+const F32 F_PI = 3.1415926535897932384626433832795f;
+const F32 F_TWO_PI = 6.283185307179586476925286766559f;
+const F32 F_PI_BY_TWO = 1.5707963267948966192313216916398f;
+const F32 F_SQRT2 = 1.4142135623730950488016887242097f;
+const F32 F_SQRT3 = 1.73205080756888288657986402541f;
+const F32 OO_SQRT2 = 0.7071067811865475244008443621049f;
+const F32 DEG_TO_RAD = 0.017453292519943295769236907684886f;
+const F32 RAD_TO_DEG = 57.295779513082320876798154814105f;
+const F32 F_APPROXIMATELY_ZERO = 0.00001f;
+const F32 F_LN2 = 0.69314718056f;
+const F32 OO_LN2 = 1.4426950408889634073599246810019f;
+
+// BUG: Eliminate in favor of F_APPROXIMATELY_ZERO above?
+const 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); }
+
+inline BOOL is_approx_equal(F32 x, F32 y)
+{
+ const S32 COMPARE_MANTISSA_UP_TO_BIT = 0x02;
+ return (abs((S32) ((U32&)x - (U32&)y) ) < COMPARE_MANTISSA_UP_TO_BIT);
+}
+
+inline S32 llabs(const S32 a)
+{
+ return S32(labs(a));
+}
+
+inline F32 llabs(const F32 a)
+{
+ return F32(fabs(a));
+}
+
+inline F64 llabs(const F64 a)
+{
+ return F64(fabs(a));
+}
+
+inline S32 lltrunc( F32 f )
+{
+#if LL_WINDOWS && !defined( __INTEL_COMPILER )
+ // 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 )
+ // 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 llround(const F32 val)
+{
+ return llfloor(val + 0.5f);
+}
+
+#else // BOGUS_ROUND
+// Old llround 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 llround(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 F32 llround( F32 val, F32 nearest )
+{
+ return F32(floor(val * (1.0f / nearest) + 0.5f)) * nearest;
+}
+
+inline F64 llround( 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
+
+const F32 FAST_MAG_ALPHA = 0.960433870103f;
+const 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
+//
+//const F32 FAST_MAG_ALPHA = 0.948059448969f;
+//const 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
+
+const F64 LL_DOUBLE_TO_FIX_MAGIC = 68719476736.0*1.5; //2^36 * 1.5, (52-_shiftamt=36) uses limited precisicion to floor
+const 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
+
+/* Deprecated: use llround(), lltrunc(), or llfloor() instead
+// ================================================================================================
+// Real2Int
+// ================================================================================================
+inline S32 F64toS32(F64 val)
+{
+ val = val + LL_DOUBLE_TO_FIX_MAGIC;
+ return ((S32*)&val)[LL_MAN_INDEX] >> LL_SHIFT_AMOUNT;
+}
+
+// ================================================================================================
+// Real2Int
+// ================================================================================================
+inline S32 F32toS32(F32 val)
+{
+ return F64toS32 ((F64)val);
+}
+*/
+
+////////////////////////////////////////////////
+//
+// 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 = llround(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;
+ }
+
+ foo = (F32)llround(foo * bar);
+
+ // shift back
+ foo /= bar;
+ return 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;
+}
+
+#endif
diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h
new file mode 100644
index 0000000000..69ff43452c
--- /dev/null
+++ b/indra/llmath/lloctree.h
@@ -0,0 +1,693 @@
+/**
+ * @file lloctree.h
+ * @brief Octree declaration.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLOCTREE_H
+#define LL_LLOCTREE_H
+
+#include "lltreenode.h"
+#include "v3math.h"
+#include <vector>
+#include <set>
+
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+#define OCT_ERRS llwarns
+#else
+#define OCT_ERRS llerrs
+#endif
+
+#define LL_OCTREE_PARANOIA_CHECK 0
+
+template <class T> class LLOctreeState;
+template <class T> class LLOctreeNode;
+
+template <class T>
+class LLOctreeListener: public LLTreeListener<T>
+{
+public:
+ typedef LLTreeListener<T> BaseType;
+ typedef LLOctreeNode<T> oct_node;
+
+ virtual ~LLOctreeListener() { };
+ 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>
+class LLOctreeNode : public LLTreeNode<T>
+{
+public:
+
+ typedef LLTreeNode<T> BaseType;
+ typedef LLTreeState<T> tree_state;
+ typedef LLOctreeState<T> oct_state;
+ typedef LLOctreeNode<T> oct_node;
+ typedef LLOctreeListener<T> oct_listener;
+
+ static const U8 OCTANT_POSITIVE_X = 0x01;
+ static const U8 OCTANT_POSITIVE_Y = 0x02;
+ static const U8 OCTANT_POSITIVE_Z = 0x04;
+
+ LLOctreeNode( LLVector3d center,
+ LLVector3d size,
+ tree_state* state,
+ BaseType* parent,
+ U8 octant = 255)
+ : BaseType(state),
+ mParent((oct_node*)parent),
+ mCenter(center),
+ mSize(size),
+ mOctant(octant)
+ {
+ updateMinMax();
+ if ((mOctant == 255) && mParent)
+ {
+ mOctant = ((oct_node*) mParent)->getOctant(mCenter.mdV);
+ }
+ }
+
+ virtual ~LLOctreeNode() { BaseType::destroyListeners(); delete this->mState; }
+
+ virtual const BaseType* getParent() const { return mParent; }
+ virtual void setParent(BaseType* parent) { mParent = (oct_node*) parent; }
+ virtual const LLVector3d& getCenter() const { return mCenter; }
+ virtual const LLVector3d& getSize() const { return mSize; }
+ virtual void setCenter(LLVector3d center) { mCenter = center; }
+ virtual void setSize(LLVector3d size) { mSize = size; }
+ virtual bool balance() { return getOctState()->balance(); }
+ virtual void validate() { getOctState()->validate(); }
+ virtual U32 getChildCount() const { return getOctState()->getChildCount(); }
+ virtual oct_node* getChild(U32 index) { return getOctState()->getChild(index); }
+ virtual const oct_node* getChild(U32 index) const { return getOctState()->getChild(index); }
+ virtual U32 getElementCount() const { return getOctState()->getElementCount(); }
+ virtual void removeByAddress(T* data) { getOctState()->removeByAddress(data); }
+ virtual bool hasLeafState() const { return getOctState()->isLeaf(); }
+ virtual void destroy() { getOctState()->destroy(); }
+ virtual oct_node* getNodeAt(T* data) { return getOctState()->getNodeAt(data); }
+ virtual U8 getOctant() const { return mOctant; }
+ virtual void setOctant(U8 octant) { mOctant = octant; }
+ virtual const oct_state* getOctState() const { return (const oct_state*) BaseType::mState; }
+ virtual oct_state* getOctState() { return (oct_state*) BaseType::mState; }
+ virtual const oct_node* getOctParent() const { return (const oct_node*) getParent(); }
+ virtual oct_node* getOctParent() { return (oct_node*) getParent(); }
+ virtual void deleteChild(oct_node* child) { getOctState()->deleteChild(child); }
+
+ virtual U8 getOctant(const F64 pos[]) const //get the octant pos is in
+ {
+ U8 ret = 0;
+
+ if (pos[0] > mCenter.mdV[0])
+ {
+ ret |= OCTANT_POSITIVE_X;
+ }
+ if (pos[1] > mCenter.mdV[1])
+ {
+ ret |= OCTANT_POSITIVE_Y;
+ }
+ if (pos[2] > mCenter.mdV[2])
+ {
+ ret |= OCTANT_POSITIVE_Z;
+ }
+
+ return ret;
+ }
+
+ virtual bool isInside(T* data) const
+ {
+ return data->getBinRadius() <= mSize.mdV[0]*2.0 && isInside(data->getPositionGroup());
+ }
+
+ virtual bool isInside(const LLVector3d& pos) const
+ {
+ const F64& x = pos.mdV[0];
+ const F64& y = pos.mdV[1];
+ const F64& z = pos.mdV[2];
+
+ if (x > mMax.mdV[0] || x <= mMin.mdV[0] ||
+ y > mMax.mdV[1] || y <= mMin.mdV[1] ||
+ z > mMax.mdV[2] || z <= mMin.mdV[2])
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ virtual void updateMinMax()
+ {
+ for (U32 i = 0; i < 3; i++)
+ {
+ mMax.mdV[i] = mCenter.mdV[i] + mSize.mdV[i];
+ mMin.mdV[i] = mCenter.mdV[i] - mSize.mdV[i];
+ mCenter.mdV[i] = mCenter.mdV[i];
+ }
+ }
+
+ virtual oct_listener* getOctListener(U32 index)
+ {
+ return (oct_listener*) BaseType::getListener(index);
+ }
+
+ bool contains(T* xform)
+ {
+ if (mParent == NULL)
+ { //root node contains nothing
+ return false;
+ }
+
+ F64 size = mSize.mdV[0];
+ F64 p_size = size * 2.0;
+ F64 radius = xform->getBinRadius();
+
+ return (radius <= 0.001 && size <= 0.001) ||
+ (radius <= p_size && radius > size);
+ }
+
+ static void pushCenter(LLVector3d &center, LLVector3d &size, T* data)
+ {
+ LLVector3 pos(data->getPositionGroup());
+ F64 p[] =
+ {
+ (F64) pos.mV[0],
+ (F64) pos.mV[1],
+ (F64) pos.mV[2]
+ };
+
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (p[i] > center.mdV[i])
+ {
+ center.mdV[i] += size.mdV[i];
+ }
+ else
+ {
+ center.mdV[i] -= size.mdV[i];
+ }
+ }
+ }
+
+protected:
+ oct_node* mParent;
+ LLVector3d mCenter;
+ LLVector3d mSize;
+ LLVector3d mMax;
+ LLVector3d mMin;
+ U8 mOctant;
+};
+
+template <class T>
+class LLOctreeTraveler : public LLTreeTraveler<T>
+{
+public:
+ virtual void traverse(const LLTreeNode<T>* node);
+ virtual void visit(const LLTreeState<T>* state) { }
+ virtual void visit(const LLOctreeState<T>* branch) = 0;
+};
+
+//will pass requests to a child, might make a new child
+template <class T>
+class LLOctreeState : public LLTreeState<T>
+{
+public:
+ typedef LLTreeState<T> BaseType;
+ typedef LLOctreeTraveler<T> oct_traveler;
+ typedef LLOctreeNode<T> oct_node;
+ typedef LLOctreeListener<T> oct_listener;
+ typedef LLTreeTraveler<T> tree_traveler;
+ typedef typename std::set<LLPointer<T> > element_list;
+ typedef typename std::set<LLPointer<T> >::iterator element_iter;
+ typedef typename std::set<LLPointer<T> >::const_iterator const_element_iter;
+ typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter;
+ typedef typename std::vector<LLOctreeNode<T>* > child_list;
+
+ LLOctreeState(oct_node* node = NULL): BaseType(node) { this->clearChildren(); }
+ virtual ~LLOctreeState()
+ {
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ delete getChild(i);
+ }
+ }
+
+
+ virtual void accept(oct_traveler* visitor) { visitor->visit(this); }
+ virtual bool isLeaf() const { return mChild.empty(); }
+
+ virtual U32 getElementCount() const { return mData.size(); }
+ virtual element_list& getData() { return mData; }
+ virtual const element_list& getData() const { return mData; }
+
+ virtual U32 getChildCount() const { return mChild.size(); }
+ virtual oct_node* getChild(U32 index) { return mChild[index]; }
+ virtual const oct_node* getChild(U32 index) const { return mChild[index]; }
+ virtual child_list& getChildren() { return mChild; }
+ virtual const child_list& getChildren() const { return mChild; }
+
+ virtual void accept(tree_traveler* visitor) const { visitor->visit(this); }
+ virtual void accept(oct_traveler* visitor) const { visitor->visit(this); }
+ const oct_node* getOctNode() const { return (const oct_node*) BaseType::getNode(); }
+ oct_node* getOctNode() { return (oct_node*) BaseType::getNode(); }
+
+ virtual oct_node* getNodeAt(T* data)
+ {
+ const LLVector3d& pos = data->getPositionGroup();
+ LLOctreeNode<T>* node = getOctNode();
+
+ if (node->isInside(data))
+ {
+ //do a quick search by octant
+ U8 octant = node->getOctant(pos.mdV);
+ BOOL keep_going = TRUE;
+
+ //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
+ while (keep_going && node->getSize().mdV[0] >= data->getBinRadius())
+ {
+ keep_going = FALSE;
+ for (U32 i = 0; i < node->getChildCount() && !keep_going; i++)
+ {
+ if (node->getChild(i)->getOctant() == octant)
+ {
+ node = node->getChild(i);
+ octant = node->getOctant(pos.mdV);
+ keep_going = TRUE;
+ }
+ }
+ }
+ }
+ else if (!node->contains(data) && node->getParent())
+ { //if we got here, data does not exist in this node
+ return ((LLOctreeNode<T>*) node->getParent())->getNodeAt(data);
+ }
+
+ return node;
+ }
+
+ virtual bool insert(T* data)
+ {
+ if (data == NULL)
+ {
+ OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << llendl;
+ return false;
+ }
+ LLOctreeNode<T>* node = getOctNode();
+
+ if (data->getBinRadius() <= node->getSize().mdV[0])
+ {
+ oct_node* dest = getNodeAt(data);
+
+ if (dest != node)
+ {
+ dest->insert(data);
+ return false;
+ }
+ }
+
+ //no kid found, is it even here?
+ if (node->isInside(data))
+ {
+ if (node->contains(data))
+ { //it belongs here
+ if (data == NULL)
+ {
+ OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE LEAF !!!" << llendl;
+ return false;
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ //if this is a redundant insertion, error out (should never happen)
+ if (mData.find(data) != mData.end())
+ {
+ llwarns << "Redundant octree insertion detected. " << data << llendl;
+ return false;
+ }
+#endif
+
+ mData.insert(data);
+ return true;
+ }
+ else
+ {
+ //it's here, but no kids are in the right place, make a new kid
+ LLVector3d center(node->getCenter());
+ LLVector3d size(node->getSize()*0.5);
+
+ //push center in direction of data
+ LLOctreeNode<T>::pushCenter(center, size, data);
+
+#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." << llendl;
+ //bool check = node->isInside(data);
+ return false;
+ }
+
+ //make sure no existing node matches this position
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ if (mChild[i]->getCenter() == center)
+ {
+ OCT_ERRS << "Octree detected duplicate child center and gave up." << llendl;
+ //bool check = node->isInside(data);
+ //check = getChild(i)->isInside(data);
+ return false;
+ }
+ }
+#endif
+
+ //make the new kid
+ LLOctreeState<T>* newstate = new LLOctreeState<T>();
+ oct_node* child = new LLOctreeNode<T>(center, size, newstate, node);
+ addChild(child);
+
+ child->insert(data);
+ }
+ }
+ else
+ {
+ //it's not in here, give it to the parent
+ node->getOctParent()->insert(data);
+ }
+
+ return false;
+ }
+
+ virtual bool remove(T* data)
+ {
+ oct_node* node = getOctNode();
+
+ if (mData.find(data) != mData.end())
+ { //we have data
+ mData.erase(data);
+ node->notifyRemoval(data);
+ checkAlive();
+ return true;
+ }
+ else if (node->isInside(data))
+ {
+ oct_node* dest = getNodeAt(data);
+
+ if (dest != node)
+ {
+ return dest->remove(data);
+ }
+ }
+
+ //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 = node->getOctParent();
+ while (parent != NULL)
+ {
+ node = parent;
+ parent = node->getOctParent();
+ }
+
+ //node is now root
+ llwarns << "!!! OCTREE REMOVING FACE BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << llendl;
+ node->removeByAddress(data);
+ return true;
+ }
+
+ virtual void removeByAddress(T* data)
+ {
+ if (mData.find(data) != mData.end())
+ {
+ mData.erase(data);
+ getOctNode()->notifyRemoval(data);
+ llwarns << "FOUND!" << llendl;
+ checkAlive();
+ return;
+ }
+
+ for (U32 i = 0; i < getChildCount(); i++)
+ { //we don't contain data, so pass this guy down
+ LLOctreeNode<T>* child = (LLOctreeNode<T>*) getChild(i);
+ child->removeByAddress(data);
+ }
+ }
+
+ virtual void clearChildren()
+ {
+ mChild.clear();
+ }
+
+ virtual void validate()
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+ LLOctreeNode<T>* node = this->getOctNode();
+
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ mChild[i]->validate();
+ if (mChild[i]->getParent() != node)
+ {
+ llerrs << "Octree child has invalid parent." << llendl;
+ }
+ }
+#endif
+ }
+
+ virtual bool balance()
+ {
+ return false;
+ }
+
+ virtual void destroy()
+ {
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ mChild[i]->destroy();
+ delete mChild[i];
+ }
+ }
+
+ virtual void addChild(oct_node* child, BOOL silent = FALSE)
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ if(mChild[i]->getSize() != child->getSize())
+ {
+ OCT_ERRS <<"Invalid octree child size." << llendl;
+ }
+ if (mChild[i]->getCenter() == child->getCenter())
+ {
+ OCT_ERRS <<"Duplicate octree child position." << llendl;
+ }
+ }
+
+ if (mChild.size() >= 8)
+ {
+ OCT_ERRS <<"Octree node has too many children... why?" << llendl;
+ }
+#endif
+
+ mChild.push_back(child);
+ child->setParent(getOctNode());
+
+ if (!silent)
+ {
+ oct_node* node = getOctNode();
+
+ for (U32 i = 0; i < node->getListenerCount(); i++)
+ {
+ oct_listener* listener = node->getOctListener(i);
+ listener->handleChildAddition(node, child);
+ }
+ }
+ }
+
+ virtual void removeChild(U8 index, BOOL destroy = FALSE)
+ {
+ oct_node* node = getOctNode();
+ for (U32 i = 0; i < node->getListenerCount(); i++)
+ {
+ oct_listener* listener = node->getOctListener(i);
+ listener->handleChildRemoval(node, getChild(index));
+ }
+
+ if (destroy)
+ {
+ mChild[index]->destroy();
+ delete mChild[index];
+ }
+ mChild.erase(mChild.begin() + index);
+
+ checkAlive();
+ }
+
+ virtual void checkAlive()
+ {
+ if (getChildCount() == 0 && getElementCount() == 0)
+ {
+ oct_node* node = getOctNode();
+ oct_node* parent = node->getOctParent();
+ if (parent)
+ {
+ parent->deleteChild(node);
+ }
+ }
+ }
+
+ virtual 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." << llendl;
+ }
+
+protected:
+ child_list mChild;
+ element_list mData;
+};
+
+//just like a branch, except it might expand the node it points to
+template <class T>
+class LLOctreeRoot : public LLOctreeState<T>
+{
+public:
+ typedef LLOctreeState<T> BaseType;
+ typedef LLOctreeNode<T> oct_node;
+
+ LLOctreeRoot(oct_node* node = NULL) : BaseType(node) { }
+
+ oct_node* getOctNode() { return BaseType::getOctNode(); }
+ virtual bool isLeaf() { return false; }
+
+ virtual bool balance()
+ {
+ //the cached node might be invalid, so don't reference it
+ if (this->getChildCount() == 1 &&
+ !(this->mChild[0]->hasLeafState()) &&
+ this->mChild[0]->getElementCount() == 0)
+ { //if we have only one child and that child is an empty branch, make that child the root
+ BaseType* state = this->mChild[0]->getOctState();
+ oct_node* child = this->mChild[0];
+ oct_node* root = getOctNode();
+
+ //make the root node look like the child
+ root->setCenter(this->mChild[0]->getCenter());
+ root->setSize(this->mChild[0]->getSize());
+ root->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 < state->getChildCount(); i++)
+ {
+ addChild(state->getChild(i), TRUE);
+ }
+
+ //destroy child
+ state->clearChildren();
+ delete child;
+ }
+
+ return true;
+ }
+
+ // LLOctreeRoot::insert
+ virtual bool insert(T* data)
+ {
+ if (data == NULL)
+ {
+ OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << llendl;
+ return false;
+ }
+
+ if (data->getBinRadius() > 4096.0)
+ {
+ OCT_ERRS << "!!! ELEMENT EXCEDES MAXIMUM SIZE IN OCTREE ROOT !!!" << llendl;
+ }
+
+ LLOctreeNode<T>* node = getOctNode();
+ if (node->getSize().mdV[0] > data->getBinRadius() && node->isInside(data->getPositionGroup()))
+ {
+ //we got it, just act like a branch
+ LLOctreeState<T>::insert(data);
+ }
+ else if (this->getChildCount() == 0)
+ {
+ //first object being added, just wrap it up
+ while (!(node->getSize().mdV[0] > data->getBinRadius() && node->isInside(data->getPositionGroup())))
+ {
+ LLVector3d center, size;
+ center = node->getCenter();
+ size = node->getSize();
+ LLOctreeNode<T>::pushCenter(center, size, data);
+ node->setCenter(center);
+ node->setSize(size*2);
+ node->updateMinMax();
+ }
+ LLOctreeState<T>::insert(data);
+ }
+ else
+ {
+ //the data is outside the root node, we need to grow
+ LLVector3d center(node->getCenter());
+ LLVector3d size(node->getSize());
+
+ //expand this node
+ LLVector3d newcenter(center);
+ LLOctreeNode<T>::pushCenter(newcenter, size, data);
+ node->setCenter(newcenter);
+ node->setSize(size*2);
+ node->updateMinMax();
+
+ //copy our children to a new branch
+ LLOctreeState<T>* newstate = new LLOctreeState<T>();
+ LLOctreeNode<T>* newnode = new LLOctreeNode<T>(center, size, newstate, node);
+
+ for (U32 i = 0; i < this->getChildCount(); i++)
+ {
+ LLOctreeNode<T>* child = this->getChild(i);
+ newstate->addChild(child);
+ }
+
+ //clear our children and add the root copy
+ this->clearChildren();
+ addChild(newnode);
+
+ //insert the data
+ node->insert(data);
+ }
+
+ return false;
+ }
+};
+
+
+//========================
+// LLOctreeTraveler
+//========================
+template <class T>
+void LLOctreeTraveler<T>::traverse(const LLTreeNode<T>* node)
+{
+ const LLOctreeState<T>* state = (const LLOctreeState<T>*) node->getState();
+ state->accept(this);
+ for (U32 i = 0; i < state->getChildCount(); i++)
+ {
+ traverse(state->getChild(i));
+ }
+}
+
+#endif
diff --git a/indra/llmath/llperlin.cpp b/indra/llmath/llperlin.cpp
new file mode 100644
index 0000000000..770d80a845
--- /dev/null
+++ b/indra/llmath/llperlin.cpp
@@ -0,0 +1,276 @@
+/**
+ * @file llperlin.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmath.h"
+
+#include "llperlin.h"
+
+#define B 0x100
+#define BM 0xff
+#define N 0x1000
+#define NF32 (4096.f)
+#define NP 12 /* 2^N */
+#define NM 0xfff
+
+static S32 p[B + B + 2];
+static F32 g3[B + B + 2][3];
+static F32 g2[B + B + 2][2];
+static F32 g1[B + B + 2];
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+
+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;
+}
+
+
+//============================================================================
+// Noise functions
+
+#define s_curve(t) ( t * t * (3.f - 2.f * t) )
+
+#define lerp_m(t, a, b) ( a + t * (b - a) )
+
+F32 LLPerlinNoise::noise1(F32 x)
+{
+ int bx0, bx1;
+ F32 rx0, rx1, sx, t, u, v;
+
+ if (!sInitialized)
+ init();
+
+ t = x + N;
+ bx0 = (lltrunc(t)) & BM;
+ bx1 = (bx0+1) & BM;
+ rx0 = t - lltrunc(t);
+ rx1 = rx0 - 1.f;
+
+ sx = s_curve(rx0);
+
+ u = rx0 * g1[ p[ bx0 ] ];
+ v = rx1 * g1[ p[ bx1 ] ];
+
+ return lerp_m(sx, u, v);
+}
+
+static F32 fast_at2(F32 rx, F32 ry, F32 *q)
+{
+ 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;
+
+ if (!sInitialized)
+ init();
+
+ fast_setup(x, bx0, bx1, rx0, rx1);
+ fast_setup(y, by0, by1, ry0, ry1);
+
+ i = *(p + bx0);
+ j = *(p + bx1);
+
+ b00 = *(p + i + by0);
+ b10 = *(p + j + by0);
+ b01 = *(p + i + by1);
+ b11 = *(p + j + by1);
+
+ 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 + 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);
+}
+
+static F32 fast_at3(F32 rx, F32 ry, F32 rz, F32 *q)
+{
+ 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);
+}
+
+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 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 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;
+}
diff --git a/indra/llmath/llperlin.h b/indra/llmath/llperlin.h
new file mode 100644
index 0000000000..75e38bb8b6
--- /dev/null
+++ b/indra/llmath/llperlin.h
@@ -0,0 +1,28 @@
+/**
+ * @file llperlin.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PERLIN_H
+#define LL_PERLIN_H
+
+#include "stdtypes.h"
+
+// namespace wrapper
+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);
+private:
+ static bool sInitialized;
+ static void init(void);
+};
+
+#endif // LL_PERLIN_
diff --git a/indra/llmath/llplane.h b/indra/llmath/llplane.h
new file mode 100644
index 0000000000..0db1d31d80
--- /dev/null
+++ b/indra/llmath/llplane.h
@@ -0,0 +1,49 @@
+/**
+ * @file llplane.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPLANE_H
+#define LL_LLPLANE_H
+
+#include "v3math.h"
+#include "v4math.h"
+
+// 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)
+
+class LLPlane : public LLVector4
+{
+public:
+ LLPlane() {}; // no default constructor
+ LLPlane(const LLVector3 &p0, F32 d) { setVec(p0, d); }
+ LLPlane(const LLVector3 &p0, const LLVector3 &n) { setVec(p0, n); }
+ void setVec(const LLVector3 &p0, F32 d) { LLVector4::setVec(p0[0], p0[1], p0[2], d); }
+ void setVec(const LLVector3 &p0, const LLVector3 &n)
+ {
+ F32 d = -(p0 * n);
+ setVec(n, d);
+ }
+ 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);
+ }
+ LLPlane& operator=(const LLVector4& v2) { LLVector4::setVec(v2[0],v2[1],v2[2],v2[3]); return *this;}
+ F32 dist(const LLVector3 &v2) const { return mV[0]*v2[0] + mV[1]*v2[1] + mV[2]*v2[2] + mV[3]; }
+};
+
+
+
+#endif // LL_LLPLANE_H
diff --git a/indra/llmath/llquantize.h b/indra/llmath/llquantize.h
new file mode 100644
index 0000000000..8aa03628f2
--- /dev/null
+++ b/indra/llmath/llquantize.h
@@ -0,0 +1,103 @@
+/**
+ * @file llquantize.h
+ * @brief useful routines for quantizing floats to various length ints
+ * and back out again
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLQUANTIZE_H
+#define LL_LLQUANTIZE_H
+
+const U16 U16MAX = 65535;
+const F32 OOU16MAX = 1.f/(F32)(U16MAX);
+
+const U8 U8MAX = 255;
+const F32 OOU8MAX = 1.f/(F32)(U8MAX);
+
+const U8 FIRSTVALIDCHAR = 54;
+const U8 MAXSTRINGVAL = U8MAX - FIRSTVALIDCHAR; //we don't allow newline or null
+
+
+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);
+
+ // 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 max_error = delta*OOU16MAX;
+
+ // make sure that zero's come through as zero
+ if (fabsf(val) < max_error)
+ val = 0.f;
+
+ return val;
+}
+
+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);
+
+ // 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 max_error = delta*OOU8MAX;
+
+ // make sure that zero's come through as zero
+ if (fabsf(val) < max_error)
+ val = 0.f;
+
+ 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;
+}
+
+inline F32 STRING_TO_F32(U8 ival, F32 lower, F32 upper)
+{
+ // 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]
+
+ return val;
+}
+
+#endif
diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp
new file mode 100644
index 0000000000..56a3830bb3
--- /dev/null
+++ b/indra/llmath/llquaternion.cpp
@@ -0,0 +1,830 @@
+/**
+ * @file qmath.cpp
+ * @brief LLQuaternion class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llquaternion.h"
+
+#include "llmath.h" // for F_PI
+//#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();
+ normQuat();
+}
+
+LLQuaternion::LLQuaternion(const LLMatrix3 &mat)
+{
+ *this = mat.quaternion();
+ normQuat();
+}
+
+LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec)
+{
+ LLVector3 v(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
+ v.normVec();
+
+ F32 c, s;
+ c = cosf(angle*0.5f);
+ s = sinf(angle*0.5f);
+
+ mQ[VX] = v.mV[VX] * s;
+ mQ[VY] = v.mV[VY] * s;
+ mQ[VZ] = v.mV[VZ] * s;
+ mQ[VW] = c;
+ normQuat();
+}
+
+LLQuaternion::LLQuaternion(F32 angle, const LLVector3 &vec)
+{
+ LLVector3 v(vec);
+ v.normVec();
+
+ F32 c, s;
+ c = cosf(angle*0.5f);
+ s = sinf(angle*0.5f);
+
+ mQ[VX] = v.mV[VX] * s;
+ mQ[VY] = v.mV[VY] * s;
+ mQ[VZ] = v.mV[VZ] * s;
+ mQ[VW] = c;
+ normQuat();
+}
+
+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();
+ normQuat();
+}
+
+// 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(x, lower, upper), lower, upper);
+ y = U16_to_F32(F32_to_U16(y, lower, upper), lower, upper);
+ z = U16_to_F32(F32_to_U16(z, lower, upper), lower, upper);
+ s = U16_to_F32(F32_to_U16(s, lower, upper), lower, upper);
+
+ mQ[VX] = x;
+ mQ[VY] = y;
+ mQ[VZ] = z;
+ mQ[VS] = s;
+}
+
+void LLQuaternion::quantize8(F32 lower, F32 upper)
+{
+ mQ[VX] = U8_to_F32(F32_to_U8(mQ[VX], lower, upper), lower, upper);
+ mQ[VY] = U8_to_F32(F32_to_U8(mQ[VY], lower, upper), lower, upper);
+ mQ[VZ] = U8_to_F32(F32_to_U8(mQ[VZ], lower, upper), lower, upper);
+ mQ[VS] = U8_to_F32(F32_to_U8(mQ[VS], lower, upper), lower, upper);
+}
+
+// LLVector3 Magnitude and Normalization Functions
+
+
+// Set LLQuaternion routines
+
+const LLQuaternion& LLQuaternion::setQuat(F32 angle, F32 x, F32 y, F32 z)
+{
+ LLVector3 vec(x, y, z);
+ vec.normVec();
+
+ angle *= 0.5f;
+ F32 c, s;
+ c = cosf(angle);
+ s = sinf(angle);
+
+ mQ[VX] = vec.mV[VX]*s;
+ mQ[VY] = vec.mV[VY]*s;
+ mQ[VZ] = vec.mV[VZ]*s;
+ mQ[VW] = c;
+
+ normQuat();
+ return (*this);
+}
+
+const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector3 &vec)
+{
+ LLVector3 v(vec);
+ v.normVec();
+
+ angle *= 0.5f;
+ F32 c, s;
+ c = cosf(angle);
+ s = sinf(angle);
+
+ mQ[VX] = v.mV[VX]*s;
+ mQ[VY] = v.mV[VY]*s;
+ mQ[VZ] = v.mV[VZ]*s;
+ mQ[VW] = c;
+
+ normQuat();
+ return (*this);
+}
+
+const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector4 &vec)
+{
+ LLVector3 v(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
+ v.normVec();
+
+ F32 c, s;
+ c = cosf(angle*0.5f);
+ s = sinf(angle*0.5f);
+
+ mQ[VX] = v.mV[VX]*s;
+ mQ[VY] = v.mV[VY]*s;
+ mQ[VZ] = v.mV[VZ]*s;
+ mQ[VW] = c;
+
+ normQuat();
+ return (*this);
+}
+
+const LLQuaternion& LLQuaternion::setQuat(F32 roll, F32 pitch, F32 yaw)
+{
+ LLMatrix3 rot_mat(roll, pitch, yaw);
+ rot_mat.orthogonalize();
+ *this = rot_mat.quaternion();
+
+ normQuat();
+ 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
+//
+// normQuat();
+// 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)
+{
+ // Make a local copy of both vectors.
+ LLVector3 vec_a = a;
+ LLVector3 vec_b = b;
+
+ // Make sure neither vector is zero length. Also normalize
+ // the vectors while we are at it.
+ F32 vec_a_mag = vec_a.normVec();
+ F32 vec_b_mag = vec_b.normVec();
+ if (vec_a_mag < F_APPROXIMATELY_ZERO ||
+ vec_b_mag < F_APPROXIMATELY_ZERO)
+ {
+ // Can't calculate a rotation from this.
+ // Just return ZERO_ROTATION instead.
+ loadIdentity();
+ return;
+ }
+
+ // Create an axis to rotate around, and the cos of the angle to rotate.
+ LLVector3 axis = vec_a % vec_b;
+ F32 cos_theta = vec_a * vec_b;
+
+ // Check the angle between the vectors to see if they are parallel or anti-parallel.
+ if (cos_theta > 1.0 - F_APPROXIMATELY_ZERO)
+ {
+ // a and b are parallel. No rotation is necessary.
+ loadIdentity();
+ }
+ else if (cos_theta < -1.0 + F_APPROXIMATELY_ZERO)
+ {
+ // a and b are anti-parallel.
+ // Rotate 180 degrees around some orthogonal axis.
+ // Find the projection of the x-axis onto a, and try
+ // using the vector between the projection and the x-axis
+ // as the orthogonal axis.
+ LLVector3 proj = vec_a.mV[VX] / (vec_a * vec_a) * vec_a;
+ LLVector3 ortho_axis(1.f, 0.f, 0.f);
+ ortho_axis -= proj;
+
+ // Turn this into an orthonormal axis.
+ F32 ortho_length = ortho_axis.normVec();
+ // If the axis' length is 0, then our guess at an orthogonal axis
+ // was wrong (a is parallel to the x-axis).
+ if (ortho_length < F_APPROXIMATELY_ZERO)
+ {
+ // Use the z-axis instead.
+ ortho_axis.setVec(0.f, 0.f, 1.f);
+ }
+
+ // Construct a quaternion from this orthonormal axis.
+ mQ[VX] = ortho_axis.mV[VX];
+ mQ[VY] = ortho_axis.mV[VY];
+ mQ[VZ] = ortho_axis.mV[VZ];
+ mQ[VW] = 0.f;
+ }
+ else
+ {
+ // a and b are NOT parallel or anti-parallel.
+ // Return the rotation between these vectors.
+ F32 theta = (F32)acos(cos_theta);
+
+ setQuat(theta, axis);
+ }
+}
+
+// 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.normQuat();
+ 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.normQuat();
+ 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.normQuat();
+ 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
+ int 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 )
+{
+ 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;
+}
+
+const LLQuaternion& LLQuaternion::setQuat(const LLMatrix3 &mat)
+{
+ *this = mat.quaternion();
+ normQuat();
+ return (*this);
+}
+
+const LLQuaternion& LLQuaternion::setQuat(const LLMatrix4 &mat)
+{
+ *this = mat.quaternion();
+ normQuat();
+ return (*this);
+}
+
+void LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) const
+{
+ F32 cos_a = mQ[VW];
+ if (cos_a > 1.0f) cos_a = 1.0f;
+ if (cos_a < -1.0f) cos_a = -1.0f;
+
+ F32 sin_a = (F32) sqrt( 1.0f - cos_a * cos_a );
+
+ if ( fabs( sin_a ) < 0.0005f )
+ sin_a = 1.0f;
+ else
+ sin_a = 1.f/sin_a;
+
+ *angle = 2.0f * (F32) acos( cos_a );
+ vec.mV[VX] = mQ[VX] * sin_a;
+ vec.mV[VY] = mQ[VY] * sin_a;
+ vec.mV[VZ] = mQ[VZ] * sin_a;
+}
+
+
+// quaternion does not need to be normalized
+void LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const
+{
+ LLMatrix3 rot_mat(*this);
+ rot_mat.orthogonalize();
+ rot_mat.getEulerAngles(roll, pitch, yaw);
+
+// // 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).
+// F32 x = -mQ[VX], y = -mQ[VY], z = -mQ[VZ], w = mQ[VW];
+// F64 m20 = 2.0*(x*z-y*w);
+// if (1.0f - fabsf(m20) < F_APPROXIMATELY_ZERO)
+// {
+// *roll = 0.0f;
+// *pitch = (F32)asin(m20);
+// *yaw = (F32)atan2(2.0*(x*y-z*w), 1.0 - 2.0*(x*x+z*z));
+// }
+// else
+// {
+// *roll = (F32)atan2(-2.0*(y*z+x*w), 1.0-2.0*(x*x+y*y));
+// *pitch = (F32)asin(m20);
+// *yaw = (F32)atan2(-2.0*(x*y+z*w), 1.0-2.0*(y*y+z*z));
+// }
+}
+
+// Saves space by using the fact that our quaternions are normalized
+LLVector3 LLQuaternion::packToVector3() const
+{
+ if( mQ[VW] >= 0 )
+ {
+ return LLVector3( mQ[VX], mQ[VY], mQ[VZ] );
+ }
+ else
+ {
+ return LLVector3( -mQ[VX], -mQ[VY], -mQ[VZ] );
+ }
+}
+
+// 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 char* buf, LLQuaternion* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ LLQuaternion quat;
+ S32 count = sscanf( buf, "%f %f %f %f", quat.mQ + 0, quat.mQ + 1, quat.mQ + 2, quat.mQ + 3 );
+ if( 4 == count )
+ {
+ value->setQuat( quat );
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+// End
diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h
new file mode 100644
index 0000000000..558eeec341
--- /dev/null
+++ b/indra/llmath/llquaternion.h
@@ -0,0 +1,442 @@
+/**
+ * @file llquaternion.h
+ * @brief LLQuaternion class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLQUATERNION_H
+#define LLQUATERNION_H
+
+#include "llmath.h"
+
+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 normQuat(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 normQuat(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]
+
+ 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
+ const LLQuaternion& setQuatInit(F32 x, F32 y, F32 z, F32 w); // Sets Quaternion to normQuat(x, y, z, w)
+ const LLQuaternion& setQuat(const LLQuaternion &quat); // Copies Quaternion
+ const LLQuaternion& setQuat(const F32 *q); // Sets Quaternion to normQuat(quat[VX], quat[VY], quat[VZ], quat[VW])
+ const LLQuaternion& setQuat(const LLMatrix3 &mat); // Sets Quaternion to mat2quat(mat)
+ const LLQuaternion& setQuat(const LLMatrix4 &mat); // Sets Quaternion to mat2quat(mat)
+ const LLQuaternion& setQuat(F32 angle, F32 x, F32 y, F32 z); // Sets Quaternion to axis_angle2quat(angle, x, y, z)
+ const LLQuaternion& setQuat(F32 angle, const LLVector3 &vec); // Sets Quaternion to axis_angle2quat(angle, vec)
+ const LLQuaternion& setQuat(F32 angle, const LLVector4 &vec); // Sets Quaternion to axis_angle2quat(angle, vec)
+ const LLQuaternion& setQuat(F32 roll, F32 pitch, F32 yaw); // Sets Quaternion to euler2quat(pitch, yaw, roll)
+
+ 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;
+
+ F32 normQuat(); // Normalizes Quaternion and returns magnitude
+ const LLQuaternion& conjQuat(void); // Conjugates Quaternion and returns result
+
+ // Other useful methods
+ const LLQuaternion& transQuat(); // Transpose
+ 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 char* buf, LLQuaternion* value);
+
+ // For debugging, only
+ //static U32 mMultCount;
+};
+
+// 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
+ //normQuat();
+ /*
+ 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];
+
+ normQuat();
+ /*
+ 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 const LLQuaternion& LLQuaternion::setQuatInit(F32 x, F32 y, F32 z, F32 w)
+{
+ mQ[VX] = x;
+ mQ[VY] = y;
+ mQ[VZ] = z;
+ mQ[VS] = w;
+ normQuat();
+ return (*this);
+}
+
+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];
+ normQuat();
+ return (*this);
+}
+
+inline const LLQuaternion& LLQuaternion::setQuat(const F32 *q)
+{
+ mQ[VX] = q[VX];
+ mQ[VY] = q[VY];
+ mQ[VZ] = q[VZ];
+ mQ[VS] = q[VW];
+ normQuat();
+ return (*this);
+}
+
+// There may be a cheaper way that avoids the sqrt.
+// Does sin_a = VX*VX + VY*VY + VZ*VZ?
+// Copied from Matrix and Quaternion FAQ 1.12
+inline void LLQuaternion::getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const
+{
+ F32 cos_a = mQ[VW];
+ if (cos_a > 1.0f) cos_a = 1.0f;
+ if (cos_a < -1.0f) cos_a = -1.0f;
+
+ F32 sin_a = (F32) sqrt( 1.0f - cos_a * cos_a );
+
+ if ( fabs( sin_a ) < 0.0005f )
+ sin_a = 1.0f;
+ else
+ sin_a = 1.f/sin_a;
+
+ *angle = 2.0f * (F32) acos( cos_a );
+ *x = mQ[VX] * sin_a;
+ *y = mQ[VY] * sin_a;
+ *z = mQ[VZ] * sin_a;
+}
+
+inline const LLQuaternion& LLQuaternion::conjQuat()
+{
+ mQ[VX] *= -1.f;
+ mQ[VY] *= -1.f;
+ mQ[VZ] *= -1.f;
+ return (*this);
+}
+
+// Transpose
+inline const LLQuaternion& LLQuaternion::transQuat()
+{
+ mQ[VX] = -mQ[VX];
+ mQ[VY] = -mQ[VY];
+ mQ[VZ] = -mQ[VZ];
+ 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;
+}
+
+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)
+ {
+ F32 oomag = 1.f/mag;
+ mQ[VX] *= oomag;
+ mQ[VY] *= oomag;
+ mQ[VZ] *= oomag;
+ mQ[VS] *= oomag;
+ }
+ else
+ {
+ mQ[VX] = 0.f;
+ mQ[VY] = 0.f;
+ mQ[VZ] = 0.f;
+ mQ[VS] = 1.f;
+ }
+
+ return mag;
+}
+
+LLQuaternion::Order StringToOrder( const char *str );
+
+// Some notes about Quaternions
+
+// What is a Quaternion?
+// ---------------------
+// A quaternion is a point in 4-dimensional complex space.
+// Q = { Qx, Qy, Qz, Qw }
+//
+//
+// Why Quaternions?
+// ----------------
+// The set of quaternions that make up the the 4-D unit sphere
+// can be mapped to the set of all rotations in 3-D space. Sometimes
+// it is easier to describe/manipulate rotations in quaternion space
+// than rotation-matrix space.
+//
+//
+// How Quaternions?
+// ----------------
+// In order to take advantage of quaternions we need to know how to
+// go from rotation-matricies to quaternions and back. We also have
+// to agree what variety of rotations we're generating.
+//
+// Consider the equation... v' = v * R
+//
+// There are two ways to think about rotations of vectors.
+// 1) v' is the same vector in a different reference frame
+// 2) v' is a new vector in the same reference frame
+//
+// bookmark -- which way are we using?
+//
+//
+// Quaternion from Angle-Axis:
+// ---------------------------
+// Suppose we wanted to represent a rotation of some angle (theta)
+// about some axis ({Ax, Ay, Az})...
+//
+// axis of rotation = {Ax, Ay, Az}
+// angle_of_rotation = theta
+//
+// s = sin(0.5 * theta)
+// c = cos(0.5 * theta)
+// Q = { s * Ax, s * Ay, s * Az, c }
+//
+//
+// 3x3 Matrix from Quaternion
+// --------------------------
+//
+// | |
+// | 1 - 2 * (y^2 + z^2) 2 * (x * y + z * w) 2 * (y * w - x * z) |
+// | |
+// M = | 2 * (x * y - z * w) 1 - 2 * (x^2 + z^2) 2 * (y * z + x * w) |
+// | |
+// | 2 * (x * z + y * w) 2 * (y * z - x * w) 1 - 2 * (x^2 + y^2) |
+// | |
+
+#endif
diff --git a/indra/llmath/llrect.cpp b/indra/llmath/llrect.cpp
new file mode 100644
index 0000000000..60280536eb
--- /dev/null
+++ b/indra/llmath/llrect.cpp
@@ -0,0 +1,10 @@
+/**
+ * @file llrect.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llrect.h"
diff --git a/indra/llmath/llrect.h b/indra/llmath/llrect.h
new file mode 100644
index 0000000000..92e415155b
--- /dev/null
+++ b/indra/llmath/llrect.h
@@ -0,0 +1,270 @@
+/**
+ * @file llrect.h
+ * @brief A rectangle in GL coordinates, with bottom,left = 0,0
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#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:
+ 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)
+ {}
+
+ LLRectBase(const LLSD& sd)
+ {
+ setValue(sd);
+ }
+
+ const LLRectBase& operator=(const LLSD& sd)
+ {
+ setValue(sd);
+ return *this;
+ }
+
+ void setValue(const LLSD& sd)
+ {
+ mLeft = sd[0].asInteger();
+ mTop = sd[1].asInteger();
+ mRight = sd[2].asInteger();
+ mBottom = 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 = ((F32)clip_x / (F32)delta_x);
+ F32 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 rectInRect(const LLRectBase* rect) const
+ {
+ return mLeft <= rect->mRight && rect->mLeft <= mRight &&
+ mBottom <= rect->mTop && rect->mBottom <= mTop ;
+ }
+
+ void set(Type left, Type top, Type right, Type bottom)
+ {
+ mLeft = left;
+ mTop = top;
+ mRight = right;
+ mBottom = bottom;
+ }
+
+ // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
+ void setOriginAndSize( Type left, Type bottom, Type width, Type height)
+ {
+ mLeft = left;
+ mTop = bottom + height;
+ mRight = left + width;
+ mBottom = bottom;
+ }
+
+ // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
+ void setLeftTopAndSize( Type left, Type top, Type width, Type height)
+ {
+ mLeft = left;
+ mTop = top;
+ mRight = left + width;
+ mBottom = top - height;
+ }
+
+ void setCenterAndSize(Type x, Type y, Type width, Type height)
+ {
+ mLeft = x - width/2;
+ mTop = y + height/2;
+ mRight = x + width/2;
+ mBottom = y - height/2;
+ }
+
+
+ void translate(Type horiz, Type vertical)
+ {
+ mLeft += horiz;
+ mRight += horiz;
+ mTop += vertical;
+ mBottom += vertical;
+ }
+
+ void stretch( Type dx, Type dy)
+ {
+ mLeft -= dx;
+ mRight += dx;
+ mTop += dy;
+ mBottom -= dy;
+ makeValid();
+ }
+
+ void stretch( Type delta )
+ {
+ stretch(delta, delta);
+
+ }
+
+ void makeValid()
+ {
+ mLeft = llmin(mLeft, mRight);
+ mBottom = llmin(mBottom, mTop);
+ }
+
+ friend const LLRectBase& operator|=(LLRectBase &a, const LLRectBase &b) // Return rect including a & b
+ {
+ a.mLeft = llmin(a.mLeft, b.mLeft);
+ a.mRight = llmax(a.mRight, b.mRight);
+ a.mBottom = llmin(a.mBottom, b.mBottom);
+ a.mTop = llmax(a.mTop, b.mTop);
+ return a;
+ }
+
+ friend LLRectBase operator|(const LLRectBase &a, const LLRectBase &b) // Return rect including a & b
+ {
+ LLRectBase<Type> result;
+ result.mLeft = llmin(a.mLeft, b.mLeft);
+ result.mRight = llmax(a.mRight, b.mRight);
+ result.mBottom = llmin(a.mBottom, b.mBottom);
+ result.mTop = llmax(a.mTop, b.mTop);
+ return result;
+ }
+
+ friend const LLRectBase& operator&=(LLRectBase &a, const LLRectBase &b) // set a to rect where a intersects b
+ {
+ a.mLeft = llmax(a.mLeft, b.mLeft);
+ a.mRight = llmin(a.mRight, b.mRight);
+ a.mBottom = llmax(a.mBottom, b.mBottom);
+ a.mTop = llmin(a.mTop, b.mTop);
+ if (a.mLeft > a.mRight)
+ {
+ a.mLeft = a.mRight;
+ }
+ if (a.mBottom > a.mTop)
+ {
+ a.mBottom = a.mTop;
+ }
+ return a;
+ }
+
+ friend LLRectBase operator&(const LLRectBase &a, const LLRectBase &b) // Return rect where a intersects b
+ {
+ LLRectBase result = a;
+ result &= b;
+ return result;
+ }
+
+ 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)
+ {
+ return ((mLeft == b.mLeft) &&
+ (mTop == b.mTop) &&
+ (mRight == b.mRight) &&
+ (mBottom == b.mBottom));
+ }
+
+ bool operator!=(const LLRectBase &b)
+ {
+ 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/lltreenode.h b/indra/llmath/lltreenode.h
new file mode 100644
index 0000000000..dd0c73c00c
--- /dev/null
+++ b/indra/llmath/lltreenode.h
@@ -0,0 +1,161 @@
+/**
+ * @file lltreenode.h
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTREENODE_H
+#define LL_LLTREENODE_H
+
+#include "stdtypes.h"
+#include "xform.h"
+#include <vector>
+
+template <class T> class LLTreeNode;
+template <class T> class LLTreeTraveler;
+template <class T> class LLTreeListener;
+
+template <class T>
+class LLTreeState
+{
+public:
+ LLTreeState(LLTreeNode<T>* node) { setNode(node); }
+ virtual ~LLTreeState() { };
+
+ virtual bool insert(T* data) = 0;
+ virtual bool remove(T* data) = 0;
+ virtual void setNode(LLTreeNode<T>* node);
+ virtual const LLTreeNode<T>* getNode() const { return mNode; }
+ virtual LLTreeNode<T>* getNode() { return mNode; }
+ virtual void accept(LLTreeTraveler<T>* traveler) const = 0;
+ virtual LLTreeListener<T>* getListener(U32 index) const;
+private:
+ LLTreeNode<T>* mNode;
+};
+
+template <class T>
+class LLTreeListener
+{
+public:
+ virtual ~LLTreeListener() { };
+ 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:
+ LLTreeNode(LLTreeState<T>* state) { setState(state); }
+ virtual ~LLTreeNode();
+ virtual LLTreeState<T>* getState() { return mState; }
+ virtual const LLTreeState<T>* getState() const { return mState; }
+
+ virtual void setState(LLTreeState<T>* state);
+ virtual void insert(T* data);
+ virtual bool remove(T* data);
+ virtual void notifyRemoval(T* data);
+ virtual U32 getListenerCount() { return mListeners.size(); }
+ virtual LLTreeListener<T>* getListener(U32 index) const { return mListeners[index]; }
+ virtual void addListener(LLTreeListener<T>* listener) { mListeners.push_back(listener); }
+ virtual void removeListener(U32 index) { mListeners.erase(mListeners.begin()+index); }
+
+protected:
+ void destroyListeners()
+ {
+ for (U32 i = 0; i < mListeners.size(); i++)
+ {
+ mListeners[i]->handleDestruction(this);
+ }
+ mListeners.clear();
+ }
+
+ LLTreeState<T>* mState;
+public:
+ std::vector<LLTreeListener<T>*> mListeners;
+};
+
+template <class T>
+class LLTreeTraveler
+{
+public:
+ virtual ~LLTreeTraveler() { };
+ virtual void traverse(const LLTreeNode<T>* node) = 0;
+ virtual void visit(const LLTreeState<T>* state) = 0;
+};
+
+template <class T>
+LLTreeNode<T>::~LLTreeNode()
+{
+ destroyListeners();
+};
+
+template <class T>
+void LLTreeNode<T>::insert(T* data)
+{
+ if (mState->insert(data))
+ {
+ for (U32 i = 0; i < mListeners.size(); i++)
+ {
+ mListeners[i]->handleInsertion(this, data);
+ }
+ }
+};
+
+template <class T>
+bool LLTreeNode<T>::remove(T* data)
+{
+ if (mState->remove(data))
+ {
+ return true;
+ }
+ return false;
+};
+
+template <class T>
+void LLTreeNode<T>::notifyRemoval(T* data)
+{
+ for (U32 i = 0; i < mListeners.size(); i++)
+ {
+ mListeners[i]->handleRemoval(this, data);
+ }
+}
+
+template <class T>
+void LLTreeNode<T>::setState(LLTreeState<T>* state)
+{
+ mState = state;
+ if (state)
+ {
+ if (state->getNode() != this)
+ {
+ state->setNode(this);
+ }
+
+ for (U32 i = 0; i < mListeners.size(); i++)
+ {
+ mListeners[i]->handleStateChange(this);
+ }
+ }
+};
+
+template <class T>
+void LLTreeState<T>::setNode(LLTreeNode<T>* node)
+{
+ mNode = node;
+ if (node && node->getState() != this)
+ {
+ node->setState(this);
+ }
+};
+
+template <class T>
+LLTreeListener<T>* LLTreeState<T>::getListener(U32 index) const
+{
+ return mNode->getListener(index);
+}
+
+#endif
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
new file mode 100644
index 0000000000..41e01b5193
--- /dev/null
+++ b/indra/llmath/llvolume.cpp
@@ -0,0 +1,4557 @@
+/**
+ * @file llvolume.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmath.h"
+
+#include <set>
+
+#include "llerror.h"
+
+#include "llvolumemgr.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "lldarray.h"
+#include "llvolume.h"
+#include "llstl.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
+
+const F32 CUT_MIN = 0.f;
+const F32 CUT_MAX = 1.f;
+const F32 MIN_CUT_DELTA = 0.02f;
+
+const F32 HOLLOW_MIN = 0.f;
+const F32 HOLLOW_MAX = 0.95f;
+const F32 HOLLOW_MAX_SQUARE = 0.7f;
+
+const F32 TWIST_MIN = -1.f;
+const F32 TWIST_MAX = 1.f;
+
+const F32 RATIO_MIN = 0.f;
+const F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper
+
+const F32 HOLE_X_MIN= 0.05f;
+const F32 HOLE_X_MAX= 1.0f;
+
+const F32 HOLE_Y_MIN= 0.05f;
+const F32 HOLE_Y_MAX= 0.5f;
+
+const F32 SHEAR_MIN = -0.5f;
+const F32 SHEAR_MAX = 0.5f;
+
+const F32 REV_MIN = 1.f;
+const F32 REV_MAX = 4.f;
+
+const F32 TAPER_MIN = -1.f;
+const F32 TAPER_MAX = 1.f;
+
+const F32 SKEW_MIN = -0.95f;
+const F32 SKEW_MAX = 0.95f;
+
+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;
+ }
+}
+
+// intersect test between triangle pt1,pt2,pt3 and line from linept to linept+vect
+//returns TRUE if intersecting and moves linept to the point of intersection
+BOOL LLTriangleLineSegmentIntersect( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, LLVector3& linept, const LLVector3& vect)
+{
+ LLVector3 V1 = pt2-pt1;
+ LLVector3 V2 = pt3-pt2;
+
+ LLVector3 norm = V1 % V2;
+
+ F32 dotprod = norm * vect;
+
+ if(dotprod < 0)
+ {
+ //Find point of intersect to triangle plane.
+ //find t to intersect point
+ F32 t = -(norm * (linept-pt1))/dotprod;
+
+ // if ds is neg line started past triangle so can't hit triangle.
+ if (t > 0)
+ {
+ return FALSE;
+ }
+
+ LLVector3 pt_int = linept + (vect*t);
+
+ if(check_same_clock_dir(pt1, pt2, pt_int, norm))
+ {
+ if(check_same_clock_dir(pt2, pt3, pt_int, norm))
+ {
+ if(check_same_clock_dir(pt3, pt1, pt_int, norm))
+ {
+ // answer in pt_int is insde triangle
+ linept.setVec(pt_int);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+//-------------------------------------------------------------------
+// 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;
+}
+
+// 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(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.
+ const 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;
+ LLVector3 pt1,pt2;
+
+ mMaxX = 0.f;
+ mMinX = 0.f;
+
+ F32 begin = mParams.getBegin();
+ F32 end = mParams.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 = llround(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.setVec(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.setVec(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.99f)
+ {
+ LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
+ F32 pt_x = new_pt.mV[VX];
+ if (pt_x < mMinX)
+ {
+ mMinX = pt_x;
+ }
+ else if (pt_x > mMaxX)
+ {
+ mMaxX = pt_x;
+ }
+ 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.setVec(cos(ang)*scale,sin(ang)*scale,t);
+
+ F32 pt_x = pt1.mV[VX];
+ if (pt_x < mMinX)
+ {
+ mMinX = pt_x;
+ }
+ else if (pt_x > mMaxX)
+ {
+ mMaxX = pt_x;
+ }
+
+ if (mProfile.size() > 0) {
+ LLVector3 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));
+ }
+ }
+ 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.setVec(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.01f)
+ {
+ LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
+ F32 pt_x = new_pt.mV[VX];
+ if (pt_x < mMinX)
+ {
+ mMinX = pt_x;
+ }
+ else if (pt_x > mMaxX)
+ {
+ mMaxX = pt_x;
+ }
+
+ if (mProfile.size() > 0) {
+ LLVector3 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));
+ }
+ }
+ 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 (!isHollow())
+ {
+ // put center point if not hollow.
+ mProfile.push_back(LLVector3(0,0,0));
+ }
+ }
+ else
+ {
+ // The profile isn't open.
+ mOpen = FALSE;
+ mConcave = FALSE;
+ }
+
+ mTotal = mProfile.size();
+}
+
+void LLProfile::genNormals()
+{
+ S32 count = mProfile.size();
+
+ S32 outer_count;
+ if (mTotalOut)
+ {
+ outer_count = mTotalOut;
+ }
+ else
+ {
+ outer_count = mTotal / 2;
+ }
+
+ mEdgeNormals.resize(count * 2);
+ mEdgeCenters.resize(count * 2);
+ mNormals.resize(count);
+
+ LLVector2 pt0,pt1;
+
+ BOOL hollow;
+ hollow = isHollow();
+
+ S32 i0, i1, i2, i3, i4;
+
+ // Parametrically generate normal
+ for (i2 = 0; i2 < count; i2++)
+ {
+ mNormals[i2].mV[0] = mProfile[i2].mV[0];
+ mNormals[i2].mV[1] = mProfile[i2].mV[1];
+ if (hollow && (i2 >= outer_count))
+ {
+ mNormals[i2] *= -1.f;
+ }
+ if (mNormals[i2].magVec() < 0.001)
+ {
+ // Special case for point at center, get adjacent points.
+ i1 = (i2 - 1) >= 0 ? i2 - 1 : count - 1;
+ i0 = (i1 - 1) >= 0 ? i1 - 1 : count - 1;
+ i3 = (i2 + 1) < count ? i2 + 1 : 0;
+ i4 = (i3 + 1) < count ? i3 + 1 : 0;
+
+ pt0.setVec(mProfile[i1].mV[VX] + mProfile[i1].mV[VX] - mProfile[i0].mV[VX],
+ mProfile[i1].mV[VY] + mProfile[i1].mV[VY] - mProfile[i0].mV[VY]);
+ pt1.setVec(mProfile[i3].mV[VX] + mProfile[i3].mV[VX] - mProfile[i4].mV[VX],
+ mProfile[i3].mV[VY] + mProfile[i3].mV[VY] - mProfile[i4].mV[VY]);
+
+ mNormals[i2] = pt0 + pt1;
+ mNormals[i2] *= 0.5f;
+ }
+ mNormals[i2].normVec();
+ }
+
+ S32 num_normal_sets = isConcave() ? 2 : 1;
+ for (S32 normal_set = 0; normal_set < num_normal_sets; normal_set++)
+ {
+ S32 point_num;
+ for (point_num = 0; point_num < mTotal; point_num++)
+ {
+ LLVector3 point_1 = mProfile[point_num];
+ point_1.mV[VZ] = 0.f;
+
+ LLVector3 point_2;
+
+ if (isConcave() && normal_set == 0 && point_num == (mTotal - 1) / 2)
+ {
+ point_2 = mProfile[mTotal - 1];
+ }
+ else if (isConcave() && normal_set == 1 && point_num == mTotal - 1)
+ {
+ point_2 = mProfile[(mTotal - 1) / 2];
+ }
+ else
+ {
+ LLVector3 delta_pos;
+ S32 neighbor_point = (point_num + 1) % mTotal;
+ while(delta_pos.magVecSquared() < 0.01f * 0.01f)
+ {
+ point_2 = mProfile[neighbor_point];
+ delta_pos = point_2 - point_1;
+ neighbor_point = (neighbor_point + 1) % mTotal;
+ if (neighbor_point == point_num)
+ {
+ break;
+ }
+ }
+ }
+
+ point_2.mV[VZ] = 0.f;
+ LLVector3 face_normal = (point_2 - point_1) % LLVector3::z_axis;
+ face_normal.normVec();
+ mEdgeNormals[normal_set * count + point_num] = face_normal;
+ mEdgeCenters[normal_set * count + point_num] = lerp(point_1, point_2, 0.5f);
+ }
+ }
+}
+
+
+// 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(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(llfloor(sides),offset,-1, ang_scale, split);
+
+ Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
+
+ LLVector3 pt[128];
+
+ for (S32 i=mTotalOut;i<mTotal;i++)
+ {
+ pt[i] = mProfile[i] * 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;
+}
+
+BOOL LLProfile::generate(BOOL path_open,F32 detail, S32 split)
+{
+ if (!mDirty)
+ {
+ return FALSE;
+ }
+ mDirty = FALSE;
+
+ if (detail < MIN_LOD)
+ {
+ llinfos << "Generating profile with LOD < MIN_LOD. CLAMPING" << llendl;
+ detail = MIN_LOD;
+ }
+
+ mProfile.clear();
+ mFaces.clear();
+
+ // Generate the face data
+ S32 i;
+ F32 begin = mParams.getBegin();
+ F32 end = mParams.getEnd();
+ F32 hollow = mParams.getHollow();
+
+ // Quick validation to eliminate some server crashes.
+ if (begin > end - 0.01f)
+ {
+ llwarns << "LLProfile::generate() assertion failed (begin >= end)" << llendl;
+ return FALSE;
+ }
+
+ S32 face_num = 0;
+
+ switch (mParams.getCurveType() & LL_PCODE_PROFILE_MASK)
+ {
+ case LL_PCODE_PROFILE_SQUARE:
+ {
+ genNGon(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);
+ }
+
+ for (i = 0; i <(S32) mProfile.size(); i++)
+ {
+ // Scale by 4 to generate proper tex coords.
+ mProfile[i].mV[2] *= 4.f;
+ }
+
+ if (hollow)
+ {
+ switch (mParams.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(TRUE, 3, -0.375f, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ // TODO: Compute actual detail levels for cubes
+ addHole(FALSE, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f);
+ break;
+ case LL_PCODE_HOLE_SAME:
+ case LL_PCODE_HOLE_SQUARE:
+ default:
+ addHole(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(3,0, 0, 1, split);
+ for (i = 0; i <(S32) mProfile.size(); i++)
+ {
+ // Scale by 3 to generate proper tex coords.
+ mProfile[i].mV[2] *= 3.f;
+ }
+
+ 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 (mParams.getCurveType() & LL_PCODE_HOLE_MASK)
+ {
+ case LL_PCODE_HOLE_CIRCLE:
+ // TODO: Actually generate level of detail for triangles
+ addHole(FALSE, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f);
+ break;
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(TRUE, 4, 0, triangle_hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_SAME:
+ case LL_PCODE_HOLE_TRIANGLE:
+ default:
+ addHole(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 = mParams.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;
+ }
+ }
+
+ //llinfos << "(CIRCLE) detail: " << detail << "; genNGon("
+ // << llfloor(circle_detail) << ")" << llendl;
+ genNGon(llfloor(circle_detail));
+ 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(TRUE, 4, 0, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ addHole(TRUE, 3, 0, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ case LL_PCODE_HOLE_SAME:
+ default:
+ addHole(FALSE, 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 = mParams.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(llfloor(circle_detail), 0.5f, 0.f, 0.5f);
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_BEGIN);
+ }
+ if (mOpen && !mParams.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(TRUE, 2, 0.5f, hollow, 0.5f, split);
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ addHole(TRUE, 3, 0.5f, hollow, 0.5f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ case LL_PCODE_HOLE_SAME:
+ default:
+ addHole(FALSE, circle_detail, 0.5f, hollow, 0.5f);
+ break;
+ }
+ }
+
+ // Special case for openness of sphere
+ if ((mParams.getEnd() - mParams.getBegin()) < 1.f)
+ {
+ mOpen = TRUE;
+ }
+ else if (!hollow)
+ {
+ mOpen = FALSE;
+ mProfile.push_back(mProfile[0]);
+ mTotal++;
+ }
+ }
+ break;
+ default:
+ llerrs << "Unknown profile: getCurveType()=" << mParams.getCurveType() << llendl;
+ 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);
+ }
+ }
+
+ //genNormals();
+
+ return TRUE;
+}
+
+
+
+BOOL LLProfileParams::importFile(FILE *fp)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ char valuestr[256];
+ keyword[0] = 0;
+ valuestr[0] = 0;
+ F32 tempF32;
+ U32 tempU32;
+
+ while (!feof(fp))
+ {
+ fgets(buffer, BUFSIZE, fp);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ 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
+ {
+ llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLProfileParams::exportFile(FILE *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];
+ char keyword[256];
+ char valuestr[256];
+ keyword[0] = 0;
+ valuestr[0] = 0;
+ F32 tempF32;
+ U32 tempU32;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ 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
+ {
+ llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLProfileParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream <<"\t\tprofile 0\n";
+ output_stream <<"\t\t{\n";
+ output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n";
+ output_stream <<"\t\t\tbegin\t" << getBegin() << "\n";
+ output_stream <<"\t\t\tend\t" << getEnd() << "\n";
+ output_stream <<"\t\t\thollow\t" << getHollow() << "\n";
+ output_stream << "\t\t}\n";
+ return TRUE;
+}
+
+LLSD LLProfileParams::asLLSD() const
+{
+ LLSD sd;
+
+ sd["curve"] = getCurveType();
+ sd["begin"] = getBegin();
+ sd["end"] = getEnd();
+ sd["hollow"] = getHollow();
+ return sd;
+}
+
+bool LLProfileParams::fromLLSD(LLSD& sd)
+{
+ setCurveType(sd["curve"].asInteger());
+ setBegin((F32)sd["begin"].asReal());
+ setEnd((F32)sd["end"].asReal());
+ setHollow((F32)sd["hollow"].asReal());
+ return true;
+}
+
+void LLProfileParams::copyParams(const LLProfileParams &params)
+{
+ setCurveType(params.getCurveType());
+ setBegin(params.getBegin());
+ setEnd(params.getEnd());
+ setHollow(params.getHollow());
+}
+
+
+LLPath::~LLPath()
+{
+}
+
+void LLPath::genNGon(S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
+{
+ // Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane.
+ const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
+
+ F32 revolutions = mParams.getRevolutions();
+ F32 skew = mParams.getSkew();
+ F32 skew_mag = fabs(skew);
+ F32 hole_x = mParams.getScaleX() * (1.0f - skew_mag);
+ F32 hole_y = mParams.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 - mParams.getTaperX();
+ F32 taper_y_begin = 1.0f;
+ F32 taper_y_end = 1.0f - mParams.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 = mParams.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 = ( (mParams.getEnd()*end_scale - mParams.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 = mParams.getTwistBegin() * twist_scale;
+ F32 twist_end = mParams.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 = mParams.getBegin();
+ pt = vector_append(mPath, 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.setVec(0 + lerp(0,mParams.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,mParams.getShear().mV[1],s),
+ s);
+ pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
+ pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
+ 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);
+ pt->mRot = twist * qang;
+
+ 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 < mParams.getEnd())
+ {
+ pt = vector_append(mPath, 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.setVec(0 + lerp(0,mParams.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,mParams.getShear().mV[1],s),
+ s);
+
+ pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
+ pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
+ 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);
+ pt->mRot = twist * qang;
+
+ t+=step;
+ }
+
+ // Make one final pass for the end cut.
+ t = mParams.getEnd();
+ pt = vector_append(mPath, 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.setVec(0 + lerp(0,mParams.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,mParams.getShear().mV[1],s),
+ s);
+ pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
+ pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
+ 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);
+ pt->mRot = twist * qang;
+
+ 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;
+}
+
+BOOL LLPath::generate(F32 detail, S32 split)
+{
+ if (!mDirty)
+ {
+ return FALSE;
+ }
+
+ if (detail < MIN_LOD)
+ {
+ llinfos << "Generating path with LOD < MIN! Clamping to 1" << llendl;
+ detail = MIN_LOD;
+ }
+
+ mDirty = FALSE;
+ S32 np = 2; // hardcode for line
+
+ mPath.clear();
+ mOpen = TRUE;
+
+ // Is this 0xf0 mask really necessary? DK 03/02/05
+ switch (mParams.getCurveType() & 0xf0)
+ {
+ default:
+ case LL_PCODE_PATH_LINE:
+ {
+ // Take the begin/end twist into account for detail.
+ np = llfloor(fabs(mParams.getTwistBegin() - mParams.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 = mParams.getBeginScale();
+ LLVector2 end_scale = mParams.getEndScale();
+
+ for (S32 i=0;i<np;i++)
+ {
+ F32 t = lerp(mParams.getBegin(),mParams.getEnd(),(F32)i * mStep);
+ mPath[i].mPos.setVec(lerp(0,mParams.getShear().mV[0],t),
+ lerp(0,mParams.getShear().mV[1],t),
+ t - 0.5f);
+ mPath[i].mRot.setQuat(lerp(F_PI * mParams.getTwistBegin(),F_PI * mParams.getTwist(),t),0,0,1);
+ mPath[i].mScale.mV[0] = lerp(start_scale.mV[0],end_scale.mV[0],t);
+ mPath[i].mScale.mV[1] = lerp(start_scale.mV[1],end_scale.mV[1],t);
+ mPath[i].mTexT = t;
+ }
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE:
+ {
+ // Increase the detail as the revolutions and twist increase.
+ F32 twist_mag = fabs(mParams.getTwistBegin() - mParams.getTwist());
+ genNGon(llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * mParams.getRevolutions()));
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE2:
+ {
+ if (mParams.getEnd() - mParams.getBegin() >= 0.99f &&
+ mParams.getScaleX() >= .99f)
+ {
+ mOpen = FALSE;
+ }
+
+ //genNGon(llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
+ genNGon(llfloor(MIN_DETAIL_FACES * detail));
+
+ F32 t = 0.f;
+ F32 tStep = 1.0f / mPath.size();
+
+ F32 toggle = 0.5f;
+ for (S32 i=0;i<(S32)mPath.size();i++)
+ {
+ mPath[i].mPos.mV[0] = toggle;
+ if (toggle == 0.5f)
+ toggle = -0.5f;
+ else
+ toggle = 0.5f;
+ t += tStep;
+ }
+ }
+
+ 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.setVec(0,
+ lerp(0, -sin(F_PI*mParams.getTwist()*t)*0.5f,t),
+ lerp(-0.5, cos(F_PI*mParams.getTwist()*t)*0.5f,t));
+ mPath[i].mScale.mV[0] = lerp(1,mParams.getScale().mV[0],t);
+ mPath[i].mScale.mV[1] = lerp(1,mParams.getScale().mV[1],t);
+ mPath[i].mTexT = t;
+ mPath[i].mRot.setQuat(F_PI * mParams.getTwist() * t,1,0,0);
+ }
+
+ break;
+ };
+
+ if (mParams.getTwist() != mParams.getTwistBegin()) mOpen = TRUE;
+
+ //if ((int(fabsf(mParams.getTwist() - mParams.getTwistBegin())*100))%100 != 0) {
+ // mOpen = TRUE;
+ //}
+
+ return TRUE;
+}
+
+BOOL LLDynamicPath::generate(F32 detail, S32 split)
+{
+ 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);
+ for (U32 i = 0; i < 2; i++)
+ {
+ mPath[i].mPos.setVec(0, 0, 0);
+ mPath[i].mRot.setQuat(0, 0, 0);
+ mPath[i].mScale.setVec(1, 1);
+ mPath[i].mTexT = 0;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLPathParams::importFile(FILE *fp)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ char valuestr[256];
+ keyword[0] = 0;
+ valuestr[0] = 0;
+
+ F32 tempF32;
+ F32 x, y;
+ U32 tempU32;
+
+ while (!feof(fp))
+ {
+ fgets(buffer, BUFSIZE, fp);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ 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
+ {
+ llwarns << "unknown keyword " << " in path import" << llendl;
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLPathParams::exportFile(FILE *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];
+ char keyword[256];
+ char valuestr[256];
+ keyword[0] = 0;
+ valuestr[0] = 0;
+
+ F32 tempF32;
+ F32 x, y;
+ U32 tempU32;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ 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
+ {
+ llwarns << "unknown keyword " << " in path import" << llendl;
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLPathParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream << "\t\tpath 0\n";
+ output_stream << "\t\t{\n";
+ output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n";
+ output_stream << "\t\t\tbegin\t" << getBegin() << "\n";
+ output_stream << "\t\t\tend\t" << getEnd() << "\n";
+ output_stream << "\t\t\tscale_x\t" << getScaleX() << "\n";
+ output_stream << "\t\t\tscale_y\t" << getScaleY() << "\n";
+ output_stream << "\t\t\tshear_x\t" << getShearX() << "\n";
+ output_stream << "\t\t\tshear_y\t" << getShearY() << "\n";
+ output_stream <<"\t\t\ttwist\t" << getTwist() << "\n";
+
+ output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n";
+ output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n";
+ output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n";
+ output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n";
+ output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n";
+ output_stream <<"\t\t\tskew\t" << getSkew() << "\n";
+
+ output_stream << "\t\t}\n";
+ return TRUE;
+}
+
+LLSD LLPathParams::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["curve"] = getCurveType();
+ sd["begin"] = getBegin();
+ sd["end"] = getEnd();
+ sd["scale_x"] = getScaleX();
+ sd["scale_y"] = getScaleY();
+ sd["shear_x"] = getShearX();
+ sd["shear_y"] = getShearY();
+ sd["twist"] = getTwist();
+ sd["twist_begin"] = getTwistBegin();
+ sd["radius_offset"] = getRadiusOffset();
+ sd["taper_x"] = getTaperX();
+ sd["taper_y"] = getTaperY();
+ sd["revolutions"] = getRevolutions();
+ sd["skew"] = getSkew();
+
+ return sd;
+}
+
+bool LLPathParams::fromLLSD(LLSD& sd)
+{
+ setCurveType(sd["curve"].asInteger());
+ setBegin((F32)sd["begin"].asReal());
+ setEnd((F32)sd["end"].asReal());
+ setScaleX((F32)sd["scale_x"].asReal());
+ setScaleY((F32)sd["scale_y"].asReal());
+ setShearX((F32)sd["shear_x"].asReal());
+ setShearY((F32)sd["shear_y"].asReal());
+ setTwist((F32)sd["twist"].asReal());
+ setTwistBegin((F32)sd["twist_begin"].asReal());
+ setRadiusOffset((F32)sd["radius_offset"].asReal());
+ setTaperX((F32)sd["taper_x"].asReal());
+ setTaperY((F32)sd["taper_y"].asReal());
+ setRevolutions((F32)sd["revolutions"].asReal());
+ setSkew((F32)sd["skew"].asReal());
+ return true;
+}
+
+void LLPathParams::copyParams(const LLPathParams &params)
+{
+ setCurveType(params.getCurveType());
+ setBegin(params.getBegin());
+ setEnd(params.getEnd());
+ setScale(params.getScaleX(), params.getScaleY() );
+ setShear(params.getShearX(), params.getShearY() );
+ setTwist(params.getTwist());
+ setTwistBegin(params.getTwistBegin());
+ setRadiusOffset(params.getRadiusOffset());
+ setTaper( params.getTaperX(), params.getTaperY() );
+ setRevolutions(params.getRevolutions());
+ setSkew(params.getSkew());
+}
+
+LLProfile::~LLProfile()
+{
+
+}
+
+
+S32 LLVolume::mNumMeshPoints = 0;
+
+LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL generate_single_face, const BOOL is_unique) : mParams(params)
+{
+ mUnique = is_unique;
+ mFaceMask = 0x0;
+ mDetail = detail;
+ // set defaults
+ if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
+ {
+ mPathp = new LLDynamicPath(mParams.getPathParams());
+ }
+ else
+ {
+ mPathp = new LLPath(mParams.getPathParams());
+ }
+ mProfilep = new LLProfile(mParams.getProfileParams());
+
+ mNumVolumeFaces = 0;
+ mVolumeFaces = NULL;
+ mGenerateSingleFace = generate_single_face;
+
+ generate();
+ createVolumeFaces();
+}
+
+void LLVolume::regen()
+{
+ generate();
+ createVolumeFaces();
+}
+
+LLVolume::~LLVolume()
+{
+ mNumMeshPoints -= mMesh.size();
+ delete mPathp;
+ delete mProfilep;
+ delete[] mVolumeFaces;
+
+ mPathp = NULL;
+ mProfilep = NULL;
+ mVolumeFaces = NULL;
+}
+
+BOOL LLVolume::generate()
+{
+ //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 (mPathp->mParams.getCurveType() == LL_PCODE_PATH_LINE &&
+ (mPathp->mParams.getScale().mV[0] != 1.0f ||
+ mPathp->mParams.getScale().mV[1] != 1.0f) &&
+ (mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_SQUARE ||
+ mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_ISOTRI ||
+ mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_EQUALTRI ||
+ mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_RIGHTTRI))
+ {
+ split = 0;
+ }
+
+ mLODScaleBias.setVec(0.5f, 0.5f, 0.5f);
+
+ F32 profile_detail = mDetail;
+ F32 path_detail = mDetail;
+
+ U8 path_type = mPathp->mParams.getCurveType();
+ U8 profile_type = mProfilep->mParams.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(path_detail, split);
+ BOOL regenProf = mProfilep->generate(mPathp->isOpen(),profile_detail, split);
+
+ if (regenPath || regenProf )
+ {
+ mNumMeshPoints -= mMesh.size();
+ mMesh.resize(mProfilep->mProfile.size() * mPathp->mPath.size());
+ mNumMeshPoints += mMesh.size();
+
+ S32 s = 0, t=0;
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+ S32 line = 0;
+
+ //generate vertex positions
+
+ // Run along the path.
+ while (s < sizeS)
+ {
+ LLVector2 scale = mPathp->mPath[s].mScale;
+ LLQuaternion rot = mPathp->mPath[s].mRot;
+
+ t = 0;
+ // Run along the profile.
+ while (t < sizeT)
+ {
+ S32 i = t + line;
+ Point& pt = mMesh[i];
+
+ pt.mPos.mV[0] = mProfilep->mProfile[t].mV[0] * scale.mV[0];
+ pt.mPos.mV[1] = mProfilep->mProfile[t].mV[1] * scale.mV[1];
+ pt.mPos.mV[2] = 0.0f;
+ pt.mPos = pt.mPos * rot;
+ pt.mPos += mPathp->mPath[s].mPos;
+ t++;
+ }
+ line += sizeT;
+ s++;
+ }
+
+ for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
+ {
+ mFaceMask |= mProfilep->mFaces[i].mFaceID;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void LLVolume::createVolumeFaces()
+{
+ S32 i;
+
+ if (mVolumeFaces != NULL)
+ {
+ delete[] mVolumeFaces;
+ mVolumeFaces = NULL;
+ }
+
+ if (mGenerateSingleFace)
+ {
+ mNumVolumeFaces = 0;
+ }
+ else
+ {
+ S32 num_faces = getNumFaces();
+ mNumVolumeFaces = num_faces;
+ mVolumeFaces = new LLVolumeFace[num_faces];
+ // Initialize volume faces with parameter data
+ for (i = 0; i < num_faces; i++)
+ {
+ LLVolumeFace &vf = mVolumeFaces[i];
+ LLProfile::Face &face = mProfilep->mFaces[i];
+ vf.mVolumep = this;
+ vf.mBeginS = face.mIndex;
+ vf.mNumS = face.mCount;
+ vf.mBeginT = 0;
+ vf.mNumT= getPath().mPath.size();
+ vf.mID = i;
+
+ // Set the type mask bits correctly
+ if (mProfilep->isHollow())
+ {
+ 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;
+ }
+ }
+ else
+ {
+ vf.mTypeMask |= LLVolumeFace::OUTER_MASK;
+ }
+ }
+ }
+
+ for (i = 0; i < mNumVolumeFaces; i++)
+ {
+ mVolumeFaces[i].create();
+ }
+ }
+
+ mBounds[1] = LLVector3(0,0,0);
+ mBounds[0] = LLVector3(512,512,512);
+}
+
+
+BOOL LLVolume::isCap(S32 face)
+{
+ return mProfilep->mFaces[face].mCap;
+}
+
+BOOL LLVolume::isFlat(S32 face)
+{
+ return mProfilep->mFaces[face].mFlat;
+}
+
+
+bool LLVolumeParams::operator==(const LLVolumeParams &params) const
+{
+ return (getPathParams() == params.getPathParams()) &&
+ (getProfileParams() == params.getProfileParams());
+}
+
+bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
+{
+ return (getPathParams() != params.getPathParams()) ||
+ (getProfileParams() != params.getProfileParams());
+}
+
+bool LLVolumeParams::operator<(const LLVolumeParams &params) const
+{
+ if( getPathParams() != params.getPathParams() )
+ {
+ return getPathParams() < params.getPathParams();
+ }
+ else
+ {
+ return getProfileParams() < params.getProfileParams();
+ }
+}
+
+void LLVolumeParams::copyParams(const LLVolumeParams &params)
+{
+ mProfileParams.copyParams(params.mProfileParams);
+ mPathParams.copyParams(params.mPathParams);
+}
+
+// return true if in range (or nearly so)
+static bool limit_range(F32& v, F32 min, F32 max)
+{
+ F32 min_delta = v - min;
+ if (min_delta < 0.f)
+ {
+ v = min;
+ if (!is_approx_zero(min_delta))
+ return false;
+ }
+ F32 max_delta = max - v;
+ if (max_delta < 0.f)
+ {
+ v = max;
+ if (!is_approx_zero(max_delta))
+ 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;
+ valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+
+ valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA);
+
+ // 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);
+
+ // 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 = is_approx_zero(delta);
+ }
+
+ 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 = is_approx_zero(delta);
+ }
+
+ mPathParams.setSkew(skew);
+ return valid;
+}
+
+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;
+ llwarns << "LLVolumeParams::setType changing bad profile type (" << profile_type
+ << ") to be LL_PCODE_PROFILE_SQUARE" << llendl;
+ }
+ else if (hole_type > LL_PCODE_HOLE_MAX)
+ {
+ // Bad hole. Make it the same.
+ profile = profile_type;
+ result = false;
+ llwarns << "LLVolumeParams::setType changing bad hole type (" << hole_type
+ << ") to be LL_PCODE_HOLE_SAME" << llendl;
+ }
+
+ if (path_type < LL_PCODE_PATH_MIN ||
+ path_type > LL_PCODE_PATH_MAX)
+ {
+ // Bad path. Make it linear.
+ result = false;
+ llwarns << "LLVolumeParams::setType changing bad path (" << path
+ << ") to be LL_PCODE_PATH_LINE" << llendl;
+ 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;
+}
+
+#define MAX_INDEX 10000
+S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
+{
+ S32 index[MAX_INDEX];
+ S32 count = 0;
+ S32 *indices = NULL;
+
+ // Let's do this totally diffently, as we don't care about faces...
+ // Counter-clockwise triangles are forward facing...
+
+ BOOL open = getProfile().isOpen();
+ BOOL hollow = getProfile().isHollow();
+ BOOL path_open = getPath().isOpen();
+ S32 size_s, size_s_out, size_t;
+ S32 s, t, i;
+ size_s = getProfile().getTotal();
+ size_s_out = getProfile().getTotalOut();
+ size_t = getPath().mPath.size();
+
+ if (open)
+ {
+ if (hollow)
+ {
+ // Open hollow -- much like the closed solid, except we
+ // we need to stitch up the gap between s=0 and s=size_s-1
+
+ if ( (size_t - 1) * (((size_s -1) * 6) + 6) >= MAX_INDEX)
+ goto noindices;
+
+ for (t = 0; t < size_t - 1; t++)
+ {
+ // The outer face, first cut, and inner face
+ for (s = 0; s < size_s - 1; s++)
+ {
+ i = s + t*size_s;
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s + 1; // x+1,y+1
+ }
+
+ // The other cut face
+ index[count++] = s + t*size_s; // x,y
+ index[count++] = 0 + t*size_s; // x+1,y
+ index[count++] = s + (t+1)*size_s; // x,y+1
+
+ index[count++] = s + (t+1)*size_s; // x,y+1
+ index[count++] = 0 + t*size_s; // x+1,y
+ index[count++] = 0 + (t+1)*size_s; // x+1,y+1
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ // Top cap
+ S32 pt1 = 0;
+ S32 pt2 = size_s-1;
+ S32 i = (size_t - 1)*size_s;
+
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ 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 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[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
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1 + i;
+ index[count++] = pt1 + 1 + i;
+ index[count++] = pt2 + i;
+ pt1++;
+ }
+ else
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1 + i;
+ index[count++] = pt2 - 1 + i;
+ index[count++] = pt2 + i;
+ pt2--;
+ }
+ }
+
+ // Bottom cap
+ pt1 = 0;
+ pt2 = size_s-1;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ 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 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[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
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt1 + 1;
+ pt1++;
+ }
+ else
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt2 - 1;
+ pt2--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Open solid
+
+ if ( (size_t - 1) * (((size_s -1) * 6) + 6) >= MAX_INDEX)
+ goto noindices;
+
+ for (t = 0; t < size_t - 1; t++)
+ {
+ // Outer face + 1 cut face
+ for (s = 0; s < size_s - 1; s++)
+ {
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s + 1; // x+1,y+1
+ }
+
+ // The other cut face
+ index[count++] = (size_s - 1) + (t*size_s); // x,y
+ index[count++] = 0 + t*size_s; // x+1,y
+ index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
+
+ index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
+ index[count++] = 0 + (t*size_s); // x+1,y
+ index[count++] = 0 + (t+1)*size_s; // x+1,y+1
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ if ( count + (size_s - 2) * 3 >= MAX_INDEX)
+ goto noindices;
+ for (s = 0; s < size_s - 2; s++)
+ {
+ index[count++] = s+1;
+ index[count++] = s;
+ index[count++] = size_s - 1;
+ }
+
+ // We've got a top cap
+ S32 offset = (size_t - 1)*size_s;
+ if ( count + (size_s - 2) * 3 >= MAX_INDEX)
+ goto noindices;
+ for (s = 0; s < size_s - 2; s++)
+ {
+ // Inverted ordering from bottom cap.
+ index[count++] = offset + size_s - 1;
+ index[count++] = offset + s;
+ index[count++] = offset + s + 1;
+ }
+ }
+ }
+ }
+ else if (hollow)
+ {
+ // Closed hollow
+ // Outer face
+
+ if ( (size_t - 1) * (size_s_out - 1) * 6 >= MAX_INDEX)
+ goto noindices;
+ for (t = 0; t < size_t - 1; t++)
+ {
+ for (s = 0; s < size_s_out - 1; s++)
+ {
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + 1 + size_s; // x+1,y+1
+ }
+ }
+
+ // Inner face
+ // Invert facing from outer face
+ if ( count + (size_t - 1) * ((size_s - 1) - size_s_out) * 6 >= MAX_INDEX)
+ goto noindices;
+ for (t = 0; t < size_t - 1; t++)
+ {
+ for (s = size_s_out; s < size_s - 1; s++)
+ {
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + 1 + size_s; // x+1,y+1
+ }
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ // Top cap
+ S32 pt1 = 0;
+ S32 pt2 = size_s-1;
+ S32 i = (size_t - 1)*size_s;
+
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ 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 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[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
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1 + i;
+ index[count++] = pt1 + 1 + i;
+ index[count++] = pt2 + i;
+ pt1++;
+ }
+ else
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1 + i;
+ index[count++] = pt2 - 1 + i;
+ index[count++] = pt2 + i;
+ pt2--;
+ }
+ }
+
+ // Bottom cap
+ pt1 = 0;
+ pt2 = size_s-1;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ 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 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[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
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt1 + 1;
+ pt1++;
+ }
+ else
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt2 - 1;
+ pt2--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Closed solid. Easy case.
+ if ( (size_t - 1) * (size_s - 1) * 6 > MAX_INDEX)
+ goto noindices;
+ for (t = 0; t < size_t - 1; t++)
+ {
+ for (s = 0; s < size_s - 1; s++)
+ {
+ // Should wrap properly, but for now...
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s + 1; // x+1,y+1
+ }
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ // bottom cap
+ if ( count + (size_s - 2 - 1) * 3 >= MAX_INDEX)
+ goto noindices;
+ for (s = 1; s < size_s - 2; s++)
+ {
+ index[count++] = s+1;
+ index[count++] = s;
+ index[count++] = 0;
+ }
+
+ // top cap
+ S32 offset = (size_t - 1)*size_s;
+ if ( count + (size_s - 2 - 1) * 3 >= MAX_INDEX)
+ goto noindices;
+ for (s = 1; s < size_s - 2; s++)
+ {
+ // Inverted ordering from bottom cap.
+ index[count++] = offset;
+ index[count++] = offset + s;
+ index[count++] = offset + s + 1;
+ }
+ }
+ }
+
+#if 0
+ S32 num_vertices = mMesh.size();
+ for (i = 0; i < count; i+=3)
+ {
+ llinfos << index[i] << ":" << index[i+1] << ":" << index[i+2] << llendl;
+ llassert(index[i] < num_vertices);
+ llassert(index[i+1] < num_vertices);
+ llassert(index[i+2] < num_vertices);
+ }
+#endif
+
+ indices = new S32[count];
+noindices:
+ if (!indices)
+ {
+ llwarns << "Couldn't allocate triangle indices" << llendl;
+ num_indices = 0;
+ return NULL;
+ }
+ num_indices = count;
+ memcpy(indices, index, count * sizeof(S32));
+ return indices;
+}
+
+//-----------------------------------------------------------------------------
+// generateSilhouetteVertices()
+//-----------------------------------------------------------------------------
+void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
+ std::vector<LLVector3> &normals,
+ std::vector<S32> &segments,
+ const LLVector3& obj_cam_vec,
+ const LLMatrix4& mat,
+ const LLMatrix3& norm_mat)
+{
+ vertices.clear();
+ normals.clear();
+ segments.clear();
+
+ //for each face
+ for (S32 i = 0; i < getNumFaces(); i++) {
+ LLVolumeFace face = this->getVolumeFace(i);
+
+ if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) {
+
+ }
+ else {
+
+ //==============================================
+ //DEBUG draw edge map instead of silhouette edge
+ //==============================================
+
+#if DEBUG_SILHOUETTE_EDGE_MAP
+
+ //for each triangle
+ U32 count = face.mIndices.size();
+ for (U32 j = 0; j < count/3; 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].mPosition +
+ face.mVertices[v2].mPosition +
+ face.mVertices[v3].mPosition) / 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) count/3) {
+ 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].mPosition +
+ face.mVertices[v2].mPosition +
+ face.mVertices[v3].mPosition) / 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.mVertices.size(); j++) {
+ vertices.push_back(face.mVertices[j].mPosition);
+ vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mNormal*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].mPosition);
+ vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mBinormal*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
+ //==============================================
+
+ static const U8 AWAY = 0x01,
+ TOWARDS = 0x02;
+
+ //for each triangle
+ std::vector<U8> fFacing;
+ vector_append(fFacing, face.mIndices.size()/3);
+ for (U32 j = 0; j < face.mIndices.size()/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];
+
+ LLVector3 norm = (face.mVertices[v1].mPosition - face.mVertices[v2].mPosition) %
+ (face.mVertices[v2].mPosition - face.mVertices[v3].mPosition);
+
+ if (norm.magVecSquared() < 0.00000001f)
+ {
+ fFacing[j] = AWAY | TOWARDS;
+ }
+ else
+ {
+ //get view vector
+ LLVector3 view = (obj_cam_vec-face.mVertices[v1].mPosition);
+ bool away = view * norm > 0.0f;
+ if (away)
+ {
+ fFacing[j] = AWAY;
+ }
+ else
+ {
+ fFacing[j] = TOWARDS;
+ }
+ }
+ }
+
+ //for each triangle
+ for (U32 j = 0; j < face.mIndices.size()/3; j++)
+ {
+ if (fFacing[j] == (AWAY | TOWARDS))
+ { //this is a degenerate triangle
+ //take neighbor facing (degenerate faces get facing of one of their neighbors)
+ // FIXME 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)];
+
+ vertices.push_back(face.mVertices[v1].mPosition*mat);
+ normals.push_back(face.mVertices[v1].mNormal*norm_mat);
+
+ vertices.push_back(face.mVertices[v2].mPosition*mat);
+ normals.push_back(face.mVertices[v2].mNormal*norm_mat);
+ segments.push_back(vertices.size());
+ }
+ }
+ }
+#endif
+ }
+ }
+}
+
+S32 LLVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+{
+ S32 ret = -1;
+
+ LLVector3 vec = end - start;
+
+ for (U32 i = 0; i < (U32)getNumFaces(); i++)
+ {
+ LLVolumeFace face = getVolumeFace(i);
+
+ for (U32 j = 0; j < face.mIndices.size()/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];
+
+ LLVector3 norm = (face.mVertices[v2].mPosition - face.mVertices[v1].mPosition) %
+ (face.mVertices[v3].mPosition - face.mVertices[v2].mPosition);
+
+ if (norm.magVecSquared() >= 0.00000001f)
+ {
+ //get view vector
+ //LLVector3 view = (start-face.mVertices[v1].mPosition);
+ //if (view * norm < 0.0f)
+ {
+ if (LLTriangleLineSegmentIntersect( face.mVertices[v1].mPosition,
+ face.mVertices[v2].mPosition,
+ face.mVertices[v3].mPosition,
+ end,
+ vec))
+ {
+ vec = end-start;
+ ret = (S32) i;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+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;
+}
+
+const F32 VERTEX_SLOP = 0.00001f;
+const F32 VERTEX_SLOP_SQRD = VERTEX_SLOP * VERTEX_SLOP;
+
+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 LLVolume::cleanupTriangleData( const S32 num_input_vertices,
+ const std::vector<Point>& input_vertices,
+ const S32 num_input_triangles,
+ S32 *input_triangles,
+ S32 &num_output_vertices,
+ LLVector3 **output_vertices,
+ S32 &num_output_triangles,
+ S32 **output_triangles)
+{
+ // Here's how we do this:
+ // Create a structure which contains the original vertex index and the
+ // LLVector3 data.
+ // "Sort" the data by the vectors
+ // Create an array the size of the old vertex list, with a mapping of
+ // old indices to new indices.
+ // Go through triangles, shift so the lowest index is first
+ // Sort triangles by first index
+ // Remove duplicate triangles
+ // Allocate and pack new triangle data.
+
+ //LLTimer cleanupTimer;
+ //llinfos << "In vertices: " << num_input_vertices << llendl;
+ //llinfos << "In triangles: " << num_input_triangles << llendl;
+
+ S32 i;
+ typedef std::multiset<LLVertexIndexPair*, lessVertex> vertex_set_t;
+ vertex_set_t vertex_list;
+
+ LLVertexIndexPair *pairp = NULL;
+ for (i = 0; i < num_input_vertices; i++)
+ {
+ LLVertexIndexPair *new_pairp = new LLVertexIndexPair(input_vertices[i].mPos, i);
+ vertex_list.insert(new_pairp);
+ }
+
+ // Generate the vertex mapping and the list of vertices without
+ // duplicates. This will crash if there are no vertices.
+ S32 *vertex_mapping = new S32[num_input_vertices];
+ LLVector3 *new_vertices = new LLVector3[num_input_vertices];
+ LLVertexIndexPair *prev_pairp = NULL;
+
+ S32 new_num_vertices;
+
+ new_num_vertices = 0;
+ for (vertex_set_t::iterator iter = vertex_list.begin(),
+ end = vertex_list.end();
+ iter != end; iter++)
+ {
+ pairp = *iter;
+ if (!prev_pairp || ((pairp->mVertex - prev_pairp->mVertex).magVecSquared() >= VERTEX_SLOP_SQRD))
+ {
+ new_vertices[new_num_vertices] = pairp->mVertex;
+ //llinfos << "Added vertex " << new_num_vertices << " : " << pairp->mVertex << llendl;
+ new_num_vertices++;
+ // Update the previous
+ prev_pairp = pairp;
+ }
+ else
+ {
+ //llinfos << "Removed duplicate vertex " << pairp->mVertex << llendl;
+ }
+ vertex_mapping[pairp->mIndex] = new_num_vertices - 1;
+ }
+
+ // Iterate through triangles and remove degenerates, re-ordering vertices
+ // along the way.
+ S32 *new_triangles = new S32[num_input_triangles * 3];
+ S32 new_num_triangles = 0;
+
+ for (i = 0; i < num_input_triangles; i++)
+ {
+ //llinfos << "Checking triangle " << input_triangles[i*3] << ":" << input_triangles[i*3+1] << ":" << input_triangles[i*3+2] << llendl;
+ input_triangles[i*3] = vertex_mapping[input_triangles[i*3]];
+ input_triangles[i*3+1] = vertex_mapping[input_triangles[i*3+1]];
+ input_triangles[i*3+2] = vertex_mapping[input_triangles[i*3+2]];
+
+ if ((input_triangles[i*3] == input_triangles[i*3+1])
+ || (input_triangles[i*3] == input_triangles[i*3+2])
+ || (input_triangles[i*3+1] == input_triangles[i*3+2]))
+ {
+ //llinfos << "Removing degenerate triangle " << input_triangles[i*3] << ":" << input_triangles[i*3+1] << ":" << input_triangles[i*3+2] << llendl;
+ // Degenerate triangle, skip
+ continue;
+ }
+
+ if (input_triangles[i*3] < input_triangles[i*3+1])
+ {
+ if (input_triangles[i*3] < input_triangles[i*3+2])
+ {
+ // (0 < 1) && (0 < 2)
+ new_triangles[new_num_triangles*3] = input_triangles[i*3];
+ new_triangles[new_num_triangles*3+1] = input_triangles[i*3+1];
+ new_triangles[new_num_triangles*3+2] = input_triangles[i*3+2];
+ }
+ else
+ {
+ // (0 < 1) && (2 < 0)
+ new_triangles[new_num_triangles*3] = input_triangles[i*3+2];
+ new_triangles[new_num_triangles*3+1] = input_triangles[i*3];
+ new_triangles[new_num_triangles*3+2] = input_triangles[i*3+1];
+ }
+ }
+ else if (input_triangles[i*3+1] < input_triangles[i*3+2])
+ {
+ // (1 < 0) && (1 < 2)
+ new_triangles[new_num_triangles*3] = input_triangles[i*3+1];
+ new_triangles[new_num_triangles*3+1] = input_triangles[i*3+2];
+ new_triangles[new_num_triangles*3+2] = input_triangles[i*3];
+ }
+ else
+ {
+ // (1 < 0) && (2 < 1)
+ new_triangles[new_num_triangles*3] = input_triangles[i*3+2];
+ new_triangles[new_num_triangles*3+1] = input_triangles[i*3];
+ new_triangles[new_num_triangles*3+2] = input_triangles[i*3+1];
+ }
+ new_num_triangles++;
+ }
+
+ if (new_num_triangles == 0)
+ {
+ llwarns << "Created volume object with 0 faces." << llendl;
+ return FALSE;
+ }
+
+ typedef std::set<S32*, lessTriangle> triangle_set_t;
+ triangle_set_t triangle_list;
+
+ for (i = 0; i < new_num_triangles; i++)
+ {
+ triangle_list.insert(&new_triangles[i*3]);
+ }
+
+ // Sort through the triangle list, and delete duplicates
+
+ S32 *prevp = NULL;
+ S32 *curp = NULL;
+
+ S32 *sorted_tris = new S32[new_num_triangles*3];
+ S32 cur_tri = 0;
+ for (triangle_set_t::iterator iter = triangle_list.begin(),
+ end = triangle_list.end();
+ iter != end; iter++)
+ {
+ curp = *iter;
+ if (!prevp || !equalTriangle(prevp, curp))
+ {
+ //llinfos << "Added triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
+ sorted_tris[cur_tri*3] = *curp;
+ sorted_tris[cur_tri*3+1] = *(curp+1);
+ sorted_tris[cur_tri*3+2] = *(curp+2);
+ cur_tri++;
+ prevp = curp;
+ }
+ else
+ {
+ //llinfos << "Skipped triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
+ }
+ }
+
+ *output_vertices = new LLVector3[new_num_vertices];
+ num_output_vertices = new_num_vertices;
+ for (i = 0; i < new_num_vertices; i++)
+ {
+ (*output_vertices)[i] = new_vertices[i];
+ }
+
+ *output_triangles = new S32[cur_tri*3];
+ num_output_triangles = cur_tri;
+ memcpy(*output_triangles, sorted_tris, 3*cur_tri*sizeof(S32));
+
+ /*
+ llinfos << "Out vertices: " << num_output_vertices << llendl;
+ llinfos << "Out triangles: " << num_output_triangles << llendl;
+ for (i = 0; i < num_output_vertices; i++)
+ {
+ llinfos << i << ":" << (*output_vertices)[i] << llendl;
+ }
+ for (i = 0; i < num_output_triangles; i++)
+ {
+ llinfos << i << ":" << (*output_triangles)[i*3] << ":" << (*output_triangles)[i*3+1] << ":" << (*output_triangles)[i*3+2] << llendl;
+ }
+ */
+
+ //llinfos << "Out vertices: " << num_output_vertices << llendl;
+ //llinfos << "Out triangles: " << num_output_triangles << llendl;
+ delete[] vertex_mapping;
+ vertex_mapping = NULL;
+ delete[] new_vertices;
+ new_vertices = NULL;
+ delete[] new_triangles;
+ new_triangles = NULL;
+ delete[] sorted_tris;
+ sorted_tris = NULL;
+ triangle_list.clear();
+ std::for_each(vertex_list.begin(), vertex_list.end(), DeletePointer());
+ vertex_list.clear();
+
+ return TRUE;
+}
+
+
+BOOL LLVolumeParams::importFile(FILE *fp)
+{
+ //llinfos << "importing volume" << llendl;
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ keyword[0] = 0;
+
+ while (!feof(fp))
+ {
+ fgets(buffer, BUFSIZE, fp);
+ sscanf(buffer, " %s", keyword);
+ if (!keyword)
+ {
+ continue;
+ }
+ 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
+ {
+ llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLVolumeParams::exportFile(FILE *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)
+{
+ //llinfos << "importing volume" << llendl;
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ keyword[0] = 0;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf(buffer, " %s", keyword);
+ if (!keyword)
+ {
+ continue;
+ }
+ 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
+ {
+ llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
+ }
+ }
+
+ 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::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["path"] = mPathParams;
+ sd["profile"] = mProfileParams;
+ return sd;
+}
+
+bool LLVolumeParams::fromLLSD(LLSD& sd)
+{
+ mPathParams.fromLLSD(sd["path"]);
+ mProfileParams.fromLLSD(sd["profile"]);
+ 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));
+}
+
+BOOL LLVolumeParams::isConvex() const
+{
+ // The logic for determining convexity is a little convoluted.
+
+ // Do we need to take getTwistBegin into account? DK 08/12/04
+ if ( mProfileParams.getHollow() != 0.0f
+ || mPathParams.getTwist() != mPathParams.getTwistBegin() )
+ {
+ // hollow or twist gaurantees concavity
+ return FALSE;
+ }
+
+ F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
+ BOOL concave_profile = (profile_length < 1.0f) && (profile_length > 0.5f);
+ if (concave_profile)
+ {
+ // concave profile
+ return FALSE;
+ }
+
+ U8 path_type = mPathParams.getCurveType();
+ if ( LL_PCODE_PATH_LINE == path_type )
+ {
+ // straight paths with convex profile
+ return TRUE;
+ }
+
+ F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
+ BOOL concave_path = (path_length < 1.0f) && (path_length > 0.5f);
+ if (concave_path)
+ {
+ return FALSE;
+ }
+
+ // we're left with spheres, toroids and tubes
+ // only the spheres can be convex
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
+ {
+ return TRUE;
+ }
+
+ // it's a toroid or tube
+ return FALSE;
+}
+
+LLFaceID LLVolume::generateFaceMask()
+{
+ LLFaceID new_mask = 0x0000;
+
+ switch(mProfilep->mParams.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)(mProfilep->mParams.getBegin() * 4.f); side < llceil(mProfilep->mParams.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)(mProfilep->mParams.getBegin() * 3.f); side < llceil(mProfilep->mParams.getEnd() * 3.f); side++)
+ {
+ new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+ }
+ }
+ break;
+ default:
+ llerrs << "Unknown profile!" << llendl
+ break;
+ }
+
+ // handle hollow objects
+ if (mProfilep->isHollow())
+ {
+ 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.mParams;
+ s << ", path = " << *volume.mPathp;
+ s << ", profile = " << *volume.mProfilep;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
+{
+ s << "{params = " << volumep->mParams;
+ s << ", path = " << *(volumep->mPathp);
+ s << ", profile = " << *(volumep->mProfilep);
+ s << "}";
+ return s;
+}
+
+
+LLVolumeFace::LLVolumeFace()
+{
+ mTypeMask = 0;
+ mID = 0;
+ mBeginS = 0;
+ mBeginT = 0;
+ mNumS = 0;
+ mNumT = 0;
+}
+
+
+BOOL LLVolumeFace::create()
+{
+ if (mTypeMask & CAP_MASK)
+ {
+ return createCap();
+ }
+ else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
+ {
+ return createSide();
+ }
+ else
+ {
+ llerrs << "Unknown/uninitialized face type!" << llendl;
+ return FALSE;
+ }
+}
+
+void LerpPlanarVertex(LLVolumeFace::VertexData& v0,
+ LLVolumeFace::VertexData& v1,
+ LLVolumeFace::VertexData& v2,
+ LLVolumeFace::VertexData& vout,
+ F32 coef01,
+ F32 coef02)
+{
+ vout.mPosition = v0.mPosition + ((v1.mPosition-v0.mPosition)*coef01)+((v2.mPosition-v0.mPosition)*coef02);
+ vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02);
+ vout.mNormal = v0.mNormal;
+ vout.mBinormal = v0.mBinormal;
+}
+
+BOOL LLVolumeFace::createUnCutCubeCap()
+{
+ const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
+ const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
+ S32 max_s = mVolumep->getProfile().getTotal();
+ S32 max_t = mVolumep->getPath().mPath.size();
+
+ // S32 i;
+ S32 num_vertices = 0, num_indices = 0;
+ S32 grid_size = (profile.size()-1)/4;
+ S32 quad_count = (grid_size * grid_size);
+
+ num_vertices = (grid_size+1)*(grid_size+1);
+ num_indices = quad_count * 4;
+
+ S32 offset = 0;
+ if (mTypeMask & TOP_MASK)
+ offset = (max_t-1) * max_s;
+ else
+ offset = mBeginS;
+
+ VertexData corners[4];
+ VertexData baseVert;
+ for(int t = 0; t < 4; t++){
+ corners[t].mPosition = mesh[offset + (grid_size*t)].mPos;
+ corners[t].mTexCoord.mV[0] = profile[grid_size*t].mV[0]+0.5f;
+ corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t].mV[1];
+ }
+ baseVert.mNormal =
+ ((corners[1].mPosition-corners[0].mPosition) %
+ (corners[2].mPosition-corners[1].mPosition));
+ baseVert.mNormal.normVec();
+ if(!(mTypeMask & TOP_MASK)){
+ baseVert.mNormal *= -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;
+ }
+ baseVert.mBinormal = calc_binormal_from_triangle(
+ corners[0].mPosition, corners[0].mTexCoord,
+ corners[1].mPosition, corners[1].mTexCoord,
+ corners[2].mPosition, corners[2].mTexCoord);
+ for(int t = 0; t < 4; t++){
+ corners[t].mBinormal = baseVert.mBinormal;
+ corners[t].mNormal = baseVert.mNormal;
+ }
+
+ S32 vtop = mVertices.size();
+// S32 itop = mIndices.size();
+/// vector_append(mVertices,4);
+// vector_append(mIndices,4);
+// LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
+#if 0
+ for(int t=0;t<4;t++){
+ VertexData vd;
+ vd.mPosition = corners[t].mPosition;
+ vd.mNormal =
+ ((corners[(t+1)%4].mPosition-corners[t].mPosition)%
+ (corners[(t+2)%4].mPosition-corners[(t+1)%4].mPosition));
+ vd.mNormal.normVec();
+
+ if (mTypeMask & TOP_MASK)
+ vd.mNormal *= -1.0f;
+ vd.mBinormal = vd.mNormal;
+ vd.mTexCoord = corners[t].mTexCoord;
+ mVertices.push_back(vd);
+ }
+ int idxs[] = {0,1,2,2,3,0};
+ if (mTypeMask & TOP_MASK){
+ for(int i=0;i<6;i++)mIndices.push_back(vtop+idxs[i]);
+ }else{
+ for(int i=5;i>=0;i--)mIndices.push_back(vtop+idxs[i]);
+ }
+#else
+ 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);
+ mVertices.push_back(newVert);
+ }
+ }
+ int idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
+ for(int gx = 0;gx<grid_size;gx++){
+ for(int gy = 0;gy<grid_size;gy++){
+ if (mTypeMask & TOP_MASK){
+ for(int i=5;i>=0;i--)mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
+ }else{
+ for(int i=0;i<6;i++)mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
+ }
+ }
+ }
+#endif
+ return TRUE;
+}
+
+
+BOOL LLVolumeFace::createCap()
+{
+ if (!(mTypeMask & HOLLOW_MASK) &&
+ !(mTypeMask & OPEN_MASK) &&
+ ((this->mVolumep->getParams().getPathParams().getBegin()==0.0f)&&
+ (this->mVolumep->getParams().getPathParams().getEnd()==1.0f))&&
+ (mVolumep->getProfile().mParams.getCurveType()==LL_PCODE_PROFILE_SQUARE &&
+ mVolumep->getPath().mParams.getCurveType()==LL_PCODE_PATH_LINE)
+ ){
+ return createUnCutCubeCap();
+ }
+
+ S32 i;
+ S32 num_vertices = 0, num_indices = 0;
+
+ const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
+ const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
+
+ // All types of caps have the same number of vertices and indices
+ num_vertices = profile.size();
+ num_indices = (profile.size() - 2)*3;
+ vector_append(mVertices,num_vertices);
+ vector_append(mIndices,num_indices);
+
+ S32 max_s = mVolumep->getProfile().getTotal();
+ S32 max_t = mVolumep->getPath().mPath.size();
+
+ mCenter.clearVec();
+
+ 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(0,0);
+
+ // Copy the vertices into the array
+ for (i = 0; i < num_vertices; i++)
+ {
+
+ if (mTypeMask & TOP_MASK)
+ {
+ mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f;
+ mVertices[i].mTexCoord.mV[1] = profile[i].mV[1]+0.5f;
+ }
+ else
+ {
+ // Mirror for underside.
+ mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f;
+ mVertices[i].mTexCoord.mV[1] = 0.5f - profile[i].mV[1];
+ }
+
+ if(i){
+ //Dont include the first point of the profile in the average
+ cuv += mVertices[i].mTexCoord;
+ mCenter += mVertices[i].mPosition = mesh[i + offset].mPos;
+ }
+ else mVertices[i].mPosition = mesh[i + offset].mPos;
+ //mVertices[i].mNormal = normal;
+ }
+
+ mCenter /= (F32)(num_vertices-1);
+ cuv /= (F32)(num_vertices-1);
+
+ LLVector3 binormal = calc_binormal_from_triangle(
+ mCenter, cuv,
+ mVertices[0].mPosition, mVertices[0].mTexCoord,
+ mVertices[1].mPosition, mVertices[1].mTexCoord);
+ binormal.normVec();
+
+ LLVector3 d0;
+ LLVector3 d1;
+ LLVector3 normal;
+
+ d0 = mCenter-mVertices[0].mPosition;
+ d1 = mCenter-mVertices[1].mPosition;
+
+ normal = (mTypeMask & TOP_MASK) ? (d0%d1) : (d1%d0);
+ normal.normVec();
+
+ VertexData vd;
+ vd.mPosition = mCenter;
+ vd.mNormal = normal;
+ vd.mBinormal = binormal;
+ vd.mTexCoord = cuv;
+
+ if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
+ {
+ mVertices.push_back(vd);
+ num_vertices++;
+ vector_append(mIndices, 3);
+ }
+
+
+ for (i = 0; i < num_vertices; i++)
+ {
+ mVertices[i].mBinormal = binormal;
+ mVertices[i].mNormal = normal;
+ }
+
+ 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;
+ i = 0;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = profile[pt1];
+ LLVector3 p2 = profile[pt2];
+ LLVector3 pa = profile[pt1+1];
+ LLVector3 pb = profile[pt2-1];
+
+ 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 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[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
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ 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;
+
+ i = 0;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = profile[pt1];
+ LLVector3 p2 = profile[pt2];
+ LLVector3 pa = profile[pt1+1];
+ LLVector3 pb = profile[pt2-1];
+
+ 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 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[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
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ 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.
+ if (mTypeMask & TOP_MASK)
+ {
+ if (mTypeMask & OPEN_MASK)
+ {
+ // SOLID OPEN TOP
+ // Generate indices
+ // This is a tri-fan, so we reuse the same first point for all triangles.
+ for (i = 0; i < (num_vertices - 2); i++)
+ {
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+1] = i;
+ mIndices[3*i+2] = i + 1;
+ }
+ }
+ else
+ {
+ // SOLID CLOSED TOP
+ for (i = 0; i < (num_vertices - 2); i++)
+ {
+ //MSMSM fix these caps but only for the un-cut case
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+1] = i;
+ mIndices[3*i+2] = i + 1;
+ }
+ }
+ }
+ else
+ {
+ if (mTypeMask & OPEN_MASK)
+ {
+ // SOLID OPEN BOTTOM
+ // Generate indices
+ // This is a tri-fan, so we reuse the same first point for all triangles.
+ for (i = 0; i < (num_vertices - 2); i++)
+ {
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+1] = i + 1;
+ mIndices[3*i+2] = i;
+ }
+ }
+ else
+ {
+ // SOLID CLOSED BOTTOM
+ for (i = 0; i < (num_vertices - 2); i++)
+ {
+ //MSMSM fix these caps but only for the un-cut case
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+1] = i + 1;
+ mIndices[3*i+2] = i;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLVolumeFace::createSide()
+{
+ BOOL flat = mTypeMask & FLAT_MASK;
+ S32 num_vertices, num_indices;
+
+
+ const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
+ const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
+ const std::vector<LLPath::PathPt>& path_data = mVolumep->getPath().mPath;
+
+ S32 max_s = mVolumep->getProfile().getTotal();
+
+ S32 s, t, i;
+ F32 ss, tt;
+
+ num_vertices = mNumS*mNumT;
+ num_indices = (mNumS-1)*(mNumT-1)*6;
+ vector_append(mVertices,num_vertices);
+ vector_append(mIndices,num_indices);
+ vector_append(mEdge, num_indices);
+
+ mCenter.clearVec();
+
+ S32 begin_stex = llfloor( profile[mBeginS].mV[2] );
+ S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS;
+
+ S32 cur_vertex = 0;
+ // Copy the vertices into the array
+ for (t = mBeginT; t < mBeginT + mNumT; 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.
+ if (!flat)
+ {
+ ss = profile[mBeginS + s].mV[2];
+ }
+ else
+ {
+ ss = profile[mBeginS + s].mV[2] - begin_stex;
+ }
+ }
+
+ // 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;
+ }
+
+ mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
+ mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
+
+ mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
+ mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
+
+ cur_vertex++;
+
+ if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2 && s > 0)
+ {
+ mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
+ mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
+
+ mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
+ mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
+ 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].mV[2] - begin_stex;
+ mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
+ mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
+
+ mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
+ mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
+ cur_vertex++;
+ }
+ }
+ mCenter /= (F32)num_vertices;
+
+ S32 cur_index = 0;
+ S32 cur_edge = 0;
+ BOOL flat_face = mTypeMask & FLAT_MASK;
+
+ // 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
+
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1; //bottom left/top right neighbor face
+ 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 || mVolumep->getPath().isOpen() == TRUE) { //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 || mVolumep->getProfile().isOpen() == TRUE) { //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 || mVolumep->getPath().isOpen() == TRUE) { //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 || mVolumep->getProfile().isOpen() == TRUE) { //no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else { //wrap on S
+ mEdge[cur_edge++] = (mNumS-1)*2*t;
+ }
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2; //top right/bottom left neighbor face
+ }
+ }
+
+
+ //generate normals
+ for (U32 i = 0; i < mIndices.size()/3; i++) { //for each triangle
+ const VertexData& v0 = mVertices[mIndices[i*3+0]];
+ const VertexData& v1 = mVertices[mIndices[i*3+1]];
+ const VertexData& v2 = mVertices[mIndices[i*3+2]];
+
+ //calculate triangle normal
+ LLVector3 norm = (v0.mPosition-v1.mPosition)%
+ (v0.mPosition-v2.mPosition);
+
+ //calculate binormal
+ LLVector3 binorm = calc_binormal_from_triangle(v0.mPosition, v0.mTexCoord,
+ v1.mPosition, v1.mTexCoord,
+ v2.mPosition, v2.mTexCoord);
+
+ for (U32 j = 0; j < 3; j++) { //add triangle normal to vertices
+ mVertices[mIndices[i*3+j]].mNormal += norm; // * (weight_sum - d[j])/weight_sum;
+ mVertices[mIndices[i*3+j]].mBinormal += binorm; // * (weight_sum - d[j])/weight_sum;
+ }
+
+ //even out quad contributions
+ if (i % 2 == 0) {
+ mVertices[mIndices[i*3+2]].mNormal += norm;
+ mVertices[mIndices[i*3+2]].mBinormal += binorm;
+ }
+ else {
+ mVertices[mIndices[i*3+1]].mNormal += norm;
+ mVertices[mIndices[i*3+1]].mBinormal += binorm;
+ }
+ }
+
+ if (mVolumep->getPath().isOpen() == FALSE) { //wrap normals on T
+ for (S32 i = 0; i < mNumS; i++) {
+ LLVector3 norm = mVertices[i].mNormal + mVertices[mNumS*(mNumT-1)+i].mNormal;
+ mVertices[i].mNormal = norm;
+ mVertices[mNumS*(mNumT-1)+i].mNormal = norm;
+ }
+ }
+
+ if (mVolumep->getProfile().isOpen() == FALSE) { //wrap normals on S
+ for (S32 i = 0; i < mNumT; i++) {
+ LLVector3 norm = mVertices[mNumS*i].mNormal + mVertices[mNumS*i+mNumS-1].mNormal;
+ mVertices[mNumS * i].mNormal = norm;
+ mVertices[mNumS * i+mNumS-1].mNormal = norm;
+ }
+ }
+
+ if (mVolumep->getPathType() == LL_PCODE_PATH_CIRCLE && ((mVolumep->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF)) {
+ if ((mVertices[0].mPosition - mVertices[mNumS*(mNumT-2)].mPosition).magVecSquared() < 0.000001f)
+ { //all lower S have same normal
+ for (S32 i = 0; i < mNumT; i++) {
+ mVertices[mNumS*i].mNormal = LLVector3(1,0,0);
+ }
+ }
+
+ if ((mVertices[mNumS-1].mPosition - mVertices[mNumS*(mNumT-2)+mNumS-1].mPosition).magVecSquared() < 0.000001f)
+ { //all upper T have same normal
+ for (S32 i = 0; i < mNumT; i++) {
+ mVertices[mNumS*i+mNumS-1].mNormal = LLVector3(-1,0,0);
+ }
+ }
+ }
+
+ //this loop would LOVE OpenMP
+ LLVector3 min = mVolumep->mBounds[0] - mVolumep->mBounds[1];
+ LLVector3 max = mVolumep->mBounds[0] + mVolumep->mBounds[1];
+
+ if (min == max && min == LLVector3(512,512,512))
+ {
+ min = max = mVertices[0].mPosition;
+ }
+
+ for (U32 i = 0; i < mVertices.size(); i++) {
+ mVertices[i].mNormal.normVec();
+ mVertices[i].mBinormal.normVec();
+
+ for (U32 j = 0; j < 3; j++) {
+ if (mVertices[i].mPosition.mV[j] > max.mV[j]) {
+ max.mV[j] = mVertices[i].mPosition.mV[j];
+ }
+ if (mVertices[i].mPosition.mV[j] < min.mV[j]) {
+ min.mV[j] = mVertices[i].mPosition.mV[j];
+ }
+ }
+ }
+
+ mVolumep->mBounds[0] = (min + max) * 0.5f; //center
+ mVolumep->mBounds[1] = (max - min) * 0.5f; //half-height
+
+ return TRUE;
+}
+
+// Static
+BOOL LLVolumeFace::updateColors(LLColor4U *old_colors, const S32 num_old, const LLVolumeFace &old_vf,
+ LLStrider<LLColor4U> &new_colors, const S32 num_new, const LLVolumeFace &new_vf)
+{
+ if (new_vf.mTypeMask & CAP_MASK)
+ {
+ // These aren't interpolated correctly. Need to fix when shadows go in...
+ F32 ratio = (F32)num_old / (F32)num_new;
+ S32 v = 0;
+ for (v = 0; v < num_new; v++)
+ {
+ new_colors[v] = old_colors[(S32)(v*ratio)];
+ }
+ return FALSE;
+ }
+ else if (new_vf.mTypeMask & END_MASK)
+ {
+ // These aren't interpolated correctly. Need to fix when shadows go in...
+ F32 ratio = (F32)num_old / (F32)num_new;
+ S32 v = 0;
+ for (v = 0; v < num_new; v++)
+ {
+ new_colors[v] = old_colors[(S32)(v*ratio)];
+ }
+ return FALSE;
+ }
+ else if (new_vf.mTypeMask & SIDE_MASK)
+ {
+ S32 s, t;
+ F32 s_ratio = (F32)old_vf.mNumS / (F32)new_vf.mNumS;
+ F32 t_ratio = (F32)old_vf.mNumT / (F32)new_vf.mNumT;
+
+ S32 v = 0;
+ for (t = 0; t < new_vf.mNumT; t++)
+ {
+ F32 t_frac = t * t_ratio;
+ S32 old_t = (S32)t_frac;
+ S32 t_target = llmin(old_t + 1, (old_vf.mNumT - 1));
+ t_frac -= old_t;
+ for (s = 0; s < new_vf.mNumS; s++)
+ {
+ F32 s_frac = s * s_ratio;
+ S32 old_s = (S32)s_frac;
+ S32 s_target = llmin(old_s + 1, (old_vf.mNumS - 1));
+ s_frac -= old_s;
+
+ // Interpolate along s, then along t.
+ LLColor4U s_interp0 = old_colors[old_t * old_vf.mNumS + old_s].multAll(1.f - s_frac).addClampMax(old_colors[old_t * old_vf.mNumS + s_target].multAll(s_frac));
+ LLColor4U s_interp1 = old_colors[t_target * old_vf.mNumS + old_s].multAll(1.f - s_frac).addClampMax(old_colors[t_target * old_vf.mNumS + s_target].multAll(s_frac));
+ new_colors[v] = s_interp0.multAll(1.f - t_frac).addClampMax(s_interp1.multAll(t_frac));
+ v++;
+ }
+ }
+ }
+ else
+ {
+ llerrs << "Unknown/uninitialized face type!" << llendl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+// Finds binormal based on three vertices with texture coordinates.
+// Fills in dummy values if the triangle has degenerate texture coordinates.
+LLVector3 calc_binormal_from_triangle(
+ const LLVector3& pos0,
+ const LLVector2& tex0,
+ const LLVector3& pos1,
+ const LLVector2& tex1,
+ const LLVector3& pos2,
+ const LLVector2& tex2)
+{
+ LLVector3 rx0( pos0.mV[VX], tex0.mV[VX], tex0.mV[VY] );
+ LLVector3 rx1( pos1.mV[VX], tex1.mV[VX], tex1.mV[VY] );
+ LLVector3 rx2( pos2.mV[VX], tex2.mV[VX], tex2.mV[VY] );
+
+ LLVector3 ry0( pos0.mV[VY], tex0.mV[VX], tex0.mV[VY] );
+ LLVector3 ry1( pos1.mV[VY], tex1.mV[VX], tex1.mV[VY] );
+ LLVector3 ry2( pos2.mV[VY], tex2.mV[VX], tex2.mV[VY] );
+
+ LLVector3 rz0( pos0.mV[VZ], tex0.mV[VX], tex0.mV[VY] );
+ LLVector3 rz1( pos1.mV[VZ], tex1.mV[VX], tex1.mV[VY] );
+ LLVector3 rz2( pos2.mV[VZ], tex2.mV[VX], tex2.mV[VY] );
+
+ LLVector3 r0 = (rx0 - rx1) % (rx0 - rx2);
+ LLVector3 r1 = (ry0 - ry1) % (ry0 - ry2);
+ LLVector3 r2 = (rz0 - rz1) % (rz0 - rz2);
+
+ if( r0.mV[VX] && r1.mV[VX] && r2.mV[VX] )
+ {
+ LLVector3 binormal(
+ -r0.mV[VZ] / r0.mV[VX],
+ -r1.mV[VZ] / r1.mV[VX],
+ -r2.mV[VZ] / r2.mV[VX]);
+ //binormal.normVec();
+ return binormal;
+ }
+ else
+ {
+ return LLVector3( 0, 1 , 0 );
+ }
+}
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
new file mode 100644
index 0000000000..cf9ae00f21
--- /dev/null
+++ b/indra/llmath/llvolume.h
@@ -0,0 +1,882 @@
+/**
+ * @file llvolume.h
+ * @brief LLVolume base class.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOLUME_H
+#define LL_LLVOLUME_H
+
+#include <iostream>
+
+class LLProfileParams;
+class LLPathParams;
+class LLVolumeParams;
+class LLProfile;
+class LLPath;
+class LLVolumeFace;
+class LLVolume;
+
+#include "lldarray.h"
+#include "lluuid.h"
+#include "v4color.h"
+//#include "vmath.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "llstrider.h"
+#include "v4coloru.h"
+#include "llmemory.h"
+
+//============================================================================
+
+const S32 MIN_DETAIL_FACES = 6;
+const S32 MIN_LOD = 0;
+const 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.
+const F32 MIN_VOLUME_PROFILE_WIDTH = 0.05f;
+const F32 MIN_VOLUME_PATH_WIDTH = 0.05f;
+
+const F32 CUT_QUANTA = 0.005f;
+const F32 SCALE_QUANTA = 0.01f;
+const F32 SHEAR_QUANTA = 0.01f;
+const F32 TAPER_QUANTA = 0.01f;
+const F32 REV_QUANTA = 0.015f;
+
+
+//============================================================================
+
+// useful masks
+const LLPCode LL_PCODE_HOLLOW_MASK = 0x80; // has a thickness
+const LLPCode LL_PCODE_SEGMENT_MASK = 0x40; // segments (1 angle)
+const LLPCode LL_PCODE_PATCH_MASK = 0x20; // segmented segments (2 angles)
+const LLPCode LL_PCODE_HEMI_MASK = 0x10; // half-primitives get their own type per PR's dictum
+const LLPCode LL_PCODE_BASE_MASK = 0x0F;
+
+ // primitive shapes
+const LLPCode LL_PCODE_CUBE = 1;
+const LLPCode LL_PCODE_PRISM = 2;
+const LLPCode LL_PCODE_TETRAHEDRON = 3;
+const LLPCode LL_PCODE_PYRAMID = 4;
+const LLPCode LL_PCODE_CYLINDER = 5;
+const LLPCode LL_PCODE_CONE = 6;
+const LLPCode LL_PCODE_SPHERE = 7;
+const LLPCode LL_PCODE_TORUS = 8;
+const LLPCode LL_PCODE_VOLUME = 9;
+
+ // surfaces
+//const LLPCode LL_PCODE_SURFACE_TRIANGLE = 10;
+//const LLPCode LL_PCODE_SURFACE_SQUARE = 11;
+//const LLPCode LL_PCODE_SURFACE_DISC = 12;
+
+const LLPCode LL_PCODE_APP = 14; // App specific pcode (for viewer/sim side only objects)
+const LLPCode LL_PCODE_LEGACY = 15;
+
+// Pcodes for legacy objects
+//const LLPCode LL_PCODE_LEGACY_ATOR = 0x10 | LL_PCODE_LEGACY; // ATOR
+const LLPCode LL_PCODE_LEGACY_AVATAR = 0x20 | LL_PCODE_LEGACY; // PLAYER
+//const LLPCode LL_PCODE_LEGACY_BIRD = 0x30 | LL_PCODE_LEGACY; // BIRD
+//const LLPCode LL_PCODE_LEGACY_DEMON = 0x40 | LL_PCODE_LEGACY; // DEMON
+const LLPCode LL_PCODE_LEGACY_GRASS = 0x50 | LL_PCODE_LEGACY; // GRASS
+const LLPCode LL_PCODE_TREE_NEW = 0x60 | LL_PCODE_LEGACY; // new trees
+//const LLPCode LL_PCODE_LEGACY_ORACLE = 0x70 | LL_PCODE_LEGACY; // ORACLE
+const LLPCode LL_PCODE_LEGACY_PART_SYS = 0x80 | LL_PCODE_LEGACY; // PART_SYS
+const LLPCode LL_PCODE_LEGACY_ROCK = 0x90 | LL_PCODE_LEGACY; // ROCK
+//const LLPCode LL_PCODE_LEGACY_SHOT = 0xA0 | LL_PCODE_LEGACY; // BASIC_SHOT
+//const LLPCode LL_PCODE_LEGACY_SHOT_BIG = 0xB0 | LL_PCODE_LEGACY;
+//const LLPCode LL_PCODE_LEGACY_SMOKE = 0xC0 | LL_PCODE_LEGACY; // SMOKE
+//const LLPCode LL_PCODE_LEGACY_SPARK = 0xD0 | LL_PCODE_LEGACY;// SPARK
+const LLPCode LL_PCODE_LEGACY_TEXT_BUBBLE = 0xE0 | LL_PCODE_LEGACY; // TEXTBUBBLE
+const LLPCode LL_PCODE_LEGACY_TREE = 0xF0 | LL_PCODE_LEGACY; // TREE
+
+ // hemis
+const LLPCode LL_PCODE_CYLINDER_HEMI = LL_PCODE_CYLINDER | LL_PCODE_HEMI_MASK;
+const LLPCode LL_PCODE_CONE_HEMI = LL_PCODE_CONE | LL_PCODE_HEMI_MASK;
+const LLPCode LL_PCODE_SPHERE_HEMI = LL_PCODE_SPHERE | LL_PCODE_HEMI_MASK;
+const 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
+const U8 LL_PCODE_PROFILE_MASK = 0x0f;
+const U8 LL_PCODE_PROFILE_MIN = 0x00;
+const U8 LL_PCODE_PROFILE_CIRCLE = 0x00;
+const U8 LL_PCODE_PROFILE_SQUARE = 0x01;
+const U8 LL_PCODE_PROFILE_ISOTRI = 0x02;
+const U8 LL_PCODE_PROFILE_EQUALTRI = 0x03;
+const U8 LL_PCODE_PROFILE_RIGHTTRI = 0x04;
+const U8 LL_PCODE_PROFILE_CIRCLE_HALF = 0x05;
+const U8 LL_PCODE_PROFILE_MAX = 0x05;
+
+// Stored in the profile byte
+const U8 LL_PCODE_HOLE_MASK = 0xf0;
+const U8 LL_PCODE_HOLE_MIN = 0x00;
+const U8 LL_PCODE_HOLE_SAME = 0x00; // same as outside profile
+const U8 LL_PCODE_HOLE_CIRCLE = 0x10;
+const U8 LL_PCODE_HOLE_SQUARE = 0x20;
+const U8 LL_PCODE_HOLE_TRIANGLE = 0x30;
+const U8 LL_PCODE_HOLE_MAX = 0x03; // min/max needs to be >> 4 of real min/max
+
+const U8 LL_PCODE_PATH_IGNORE = 0x00;
+const U8 LL_PCODE_PATH_MIN = 0x01; // min/max needs to be >> 4 of real min/max
+const U8 LL_PCODE_PATH_LINE = 0x10;
+const U8 LL_PCODE_PATH_CIRCLE = 0x20;
+const U8 LL_PCODE_PATH_CIRCLE2 = 0x30;
+const U8 LL_PCODE_PATH_TEST = 0x40;
+const U8 LL_PCODE_PATH_FLEXIBLE = 0x80;
+const U8 LL_PCODE_PATH_MAX = 0x08;
+
+//============================================================================
+
+// face identifiers
+typedef U16 LLFaceID;
+
+const LLFaceID LL_FACE_PATH_BEGIN = 0x1 << 0;
+const LLFaceID LL_FACE_PATH_END = 0x1 << 1;
+const LLFaceID LL_FACE_INNER_SIDE = 0x1 << 2;
+const LLFaceID LL_FACE_PROFILE_BEGIN = 0x1 << 3;
+const LLFaceID LL_FACE_PROFILE_END = 0x1 << 4;
+const LLFaceID LL_FACE_OUTER_SIDE_0 = 0x1 << 5;
+const LLFaceID LL_FACE_OUTER_SIDE_1 = 0x1 << 6;
+const LLFaceID LL_FACE_OUTER_SIDE_2 = 0x1 << 7;
+const LLFaceID LL_FACE_OUTER_SIDE_3 = 0x1 << 8;
+
+//============================================================================
+
+class LLProfileParams
+{
+public:
+ LLProfileParams()
+ {
+ mBegin = 0;
+ mEnd = 1;
+ mHollow = 0;
+ mCurveType = LL_PCODE_PROFILE_SQUARE;
+ }
+
+ LLProfileParams(U8 curve, F32 begin, F32 end, F32 hollow)
+ : mCurveType(curve), mBegin(begin), mEnd(end), mHollow(hollow)
+ {
+ }
+
+ LLProfileParams(U8 curve, U8 begin, U8 end, U8 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 * SCALE_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ temp_f32 = 1.f;
+ }
+ mHollow = temp_f32;
+ }
+
+ bool operator==(const LLProfileParams &params) const;
+ bool operator!=(const LLProfileParams &params) const;
+ bool operator<(const LLProfileParams &params) const;
+
+ void copyParams(const LLProfileParams &params);
+
+ BOOL importFile(FILE *fp);
+ BOOL exportFile(FILE *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 * 1000))/1000.0f;}
+ void setEnd(const F32 end) { mEnd = (end <= 0.0f) ? 1.0f : ((int) (end * 1000))/1000.0f;}
+ void setHollow(const F32 hollow) { mHollow = ((int) (hollow * 1000))/1000.0f;}
+
+ friend std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params);
+
+protected:
+ // Profile params
+ U8 mCurveType;
+ F32 mBegin;
+ F32 mEnd;
+ F32 mHollow;
+
+ U32 mCRC;
+};
+
+inline bool LLProfileParams::operator==(const LLProfileParams &params) const
+{
+ return
+ (getCurveType() == params.getCurveType()) &&
+ (getBegin() == params.getBegin()) &&
+ (getEnd() == params.getEnd()) &&
+ (getHollow() == params.getHollow());
+}
+
+inline bool LLProfileParams::operator!=(const LLProfileParams &params) const
+{
+ return
+ (getCurveType() != params.getCurveType()) ||
+ (getBegin() != params.getBegin()) ||
+ (getEnd() != params.getEnd()) ||
+ (getHollow() != params.getHollow());
+}
+
+
+inline bool LLProfileParams::operator<(const LLProfileParams &params) const
+{
+ if (getCurveType() != params.getCurveType())
+ {
+ return getCurveType() < params.getCurveType();
+ }
+ else
+ if (getBegin() != params.getBegin())
+ {
+ return getBegin() < params.getBegin();
+ }
+ else
+ if (getEnd() != params.getEnd())
+ {
+ return getEnd() < params.getEnd();
+ }
+ else
+ {
+ return getHollow() < params.getHollow();
+ }
+}
+
+#define U8_TO_F32(x) (F32)(*((S8 *)&x))
+
+class LLPathParams
+{
+public:
+ LLPathParams()
+ {
+ mBegin = 0;
+ mEnd = 1;
+ mScale.setVec(1,1);
+ mShear.setVec(0,0);
+ mCurveType = LL_PCODE_PATH_LINE;
+ mTwistBegin = 0;
+ mTwistEnd = 0;
+ mRadiusOffset = 0;
+ mTaper.setVec(0,0);
+ mRevolutions = 1;
+ mSkew = 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), mTwistBegin(twistbegin), mTwistEnd(twistend),
+ mRadiusOffset(radiusoffset), mRevolutions(revolutions), mSkew(skew)
+ {
+ mScale.setVec(scx,scy);
+ mShear.setVec(shx,shy);
+ mTaper.setVec(tx,ty);
+ }
+
+ LLPathParams(U8 curve, U8 begin, U8 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 * SCALE_QUANTA);
+ mEnd = (F32)(100.f - end) * SCALE_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;
+ }
+
+ bool operator==(const LLPathParams &params) const;
+ bool operator!=(const LLPathParams &params) const;
+ bool operator<(const LLPathParams &params) const;
+
+ void copyParams(const LLPathParams &params);
+
+ BOOL importFile(FILE *fp);
+ BOOL exportFile(FILE *fp) const;
+
+ BOOL importLegacyStream(std::istream& input_stream);
+ BOOL exportLegacyStream(std::ostream& output_stream) const;
+
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ const F32& getBegin() const { return mBegin; }
+ const F32& getEnd() const { return mEnd; }
+ const LLVector2 &getScale() const { return mScale; }
+ const F32& getScaleX() const { return mScale.mV[0]; }
+ const F32& getScaleY() const { return mScale.mV[1]; }
+ const LLVector2 getBeginScale() const;
+ const LLVector2 getEndScale() const;
+ const LLVector2 &getShear() const { return mShear; }
+ const F32& getShearX() const { return mShear.mV[0]; }
+ const F32& getShearY() const { return mShear.mV[1]; }
+ const U8& getCurveType () const { return mCurveType; }
+
+ const F32& getTwistBegin() const { return mTwistBegin; }
+ const F32& getTwistEnd() const { return mTwistEnd; }
+ const F32& getTwist() const { return mTwistEnd; } // deprecated
+ const F32& getRadiusOffset() const { return mRadiusOffset; }
+ const LLVector2 &getTaper() const { return mTaper; }
+ const F32& getTaperX() const { return mTaper.mV[0]; }
+ const F32& getTaperY() const { return mTaper.mV[1]; }
+ const F32& getRevolutions() const { return mRevolutions; }
+ const F32& getSkew() const { return mSkew; }
+
+ void setCurveType(const U8 type) { mCurveType = type; }
+ void setBegin(const F32 begin) { mBegin = begin; }
+ void setEnd(const F32 end) { mEnd = end; }
+
+ void setScale(const F32 x, const F32 y) { mScale.setVec(x,y); }
+ void setScaleX(const F32 v) { mScale.mV[VX] = v; }
+ void setScaleY(const F32 v) { mScale.mV[VY] = v; }
+ void setShear(const F32 x, const F32 y) { mShear.setVec(x,y); }
+ void setShearX(const F32 v) { mShear.mV[VX] = v; }
+ void setShearY(const F32 v) { mShear.mV[VY] = v; }
+
+ void setTwistBegin(const F32 twist_begin) { mTwistBegin = twist_begin; }
+ void setTwistEnd(const F32 twist_end) { mTwistEnd = twist_end; }
+ void setTwist(const F32 twist) { setTwistEnd(twist); } // deprecated
+ void setRadiusOffset(const F32 radius_offset){ mRadiusOffset = radius_offset; }
+ void setTaper(const F32 x, const F32 y) { mTaper.setVec(x,y); }
+ void setTaperX(const F32 v) { mTaper.mV[VX] = v; }
+ void setTaperY(const F32 v) { mTaper.mV[VY] = v; }
+ void setRevolutions(const F32 revolutions) { mRevolutions = revolutions; }
+ void setSkew(const F32 skew) { mSkew = skew; }
+
+ friend std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params);
+
+protected:
+ // Path params
+ U8 mCurveType;
+ F32 mBegin;
+ F32 mEnd;
+ LLVector2 mScale;
+ LLVector2 mShear;
+
+ F32 mTwistBegin;
+ F32 mTwistEnd;
+ F32 mRadiusOffset;
+ LLVector2 mTaper;
+ F32 mRevolutions;
+ F32 mSkew;
+
+ U32 mCRC;
+};
+
+inline bool LLPathParams::operator==(const LLPathParams &params) const
+{
+ return
+ (getCurveType() == params.getCurveType()) &&
+ (getScale() == params.getScale()) &&
+ (getBegin() == params.getBegin()) &&
+ (getEnd() == params.getEnd()) &&
+ (getShear() == params.getShear()) &&
+ (getTwist() == params.getTwist()) &&
+ (getTwistBegin() == params.getTwistBegin()) &&
+ (getRadiusOffset() == params.getRadiusOffset()) &&
+ (getTaper() == params.getTaper()) &&
+ (getRevolutions() == params.getRevolutions()) &&
+ (getSkew() == params.getSkew());
+}
+
+inline bool LLPathParams::operator!=(const LLPathParams &params) const
+{
+ return
+ (getCurveType() != params.getCurveType()) ||
+ (getScale() != params.getScale()) ||
+ (getBegin() != params.getBegin()) ||
+ (getEnd() != params.getEnd()) ||
+ (getShear() != params.getShear()) ||
+ (getTwist() != params.getTwist()) ||
+ (getTwistBegin() !=params.getTwistBegin()) ||
+ (getRadiusOffset() != params.getRadiusOffset()) ||
+ (getTaper() != params.getTaper()) ||
+ (getRevolutions() != params.getRevolutions()) ||
+ (getSkew() != params.getSkew());
+}
+
+
+inline bool LLPathParams::operator<(const LLPathParams &params) const
+{
+ if( getCurveType() != params.getCurveType())
+ {
+ return getCurveType() < params.getCurveType();
+ }
+ else
+ if( getScale() != params.getScale())
+ {
+ return getScale() < params.getScale();
+ }
+ else
+ if( getBegin() != params.getBegin())
+ {
+ return getBegin() < params.getBegin();
+ }
+ else
+ if( getEnd() != params.getEnd())
+ {
+ return getEnd() < params.getEnd();
+ }
+ else
+ if( getShear() != params.getShear())
+ {
+ return getShear() < params.getShear();
+ }
+ else
+ if( getTwist() != params.getTwist())
+ {
+ return getTwist() < params.getTwist();
+ }
+ else
+ if( getTwistBegin() != params.getTwistBegin())
+ {
+ return getTwistBegin() < params.getTwistBegin();
+ }
+ else
+ if( getRadiusOffset() != params.getRadiusOffset())
+ {
+ return getRadiusOffset() < params.getRadiusOffset();
+ }
+ else
+ if( getTaper() != params.getTaper())
+ {
+ return getTaper() < params.getTaper();
+ }
+ else
+ if( getRevolutions() != params.getRevolutions())
+ {
+ return getRevolutions() < params.getRevolutions();
+ }
+ else
+ {
+ return getSkew() < params.getSkew();
+ }
+}
+
+typedef LLVolumeParams* LLVolumeParamsPtr;
+typedef const LLVolumeParams* const_LLVolumeParamsPtr;
+
+class LLVolumeParams
+{
+public:
+ LLVolumeParams()
+ {
+ }
+
+ LLVolumeParams(LLProfileParams &profile, LLPathParams &path)
+ : mProfileParams(profile), mPathParams(path)
+ {
+ }
+
+ bool operator==(const LLVolumeParams &params) const;
+ bool operator!=(const LLVolumeParams &params) const;
+ bool operator<(const LLVolumeParams &params) const;
+
+
+ void copyParams(const LLVolumeParams &params);
+
+ const LLProfileParams &getProfileParams() const {return mProfileParams;}
+ LLProfileParams &getProfileParams() {return mProfileParams;}
+ const LLPathParams &getPathParams() const {return mPathParams;}
+ LLPathParams &getPathParams() {return mPathParams;}
+
+ BOOL importFile(FILE *fp);
+ BOOL exportFile(FILE *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);
+
+ 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);
+
+ 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(); }
+
+ 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);
+
+protected:
+ LLProfileParams mProfileParams;
+ LLPathParams mPathParams;
+};
+
+
+class LLProfile
+{
+public:
+ LLProfile(const LLProfileParams &params) : mParams(params)
+ {
+ mTotal = 2;
+ mTotalOut = 0;
+ mDirty = TRUE;
+ mConcave = FALSE;
+ }
+
+ ~LLProfile();
+
+ S32 getTotal() const { return mTotal; }
+ S32 getTotalOut() const { return mTotalOut; } // Total number of outside points
+ BOOL isHollow() const { return (mParams.getHollow() > 0); }
+ BOOL isFlat(S32 face) const { return (mFaces[face].mCount == 2); }
+ BOOL isOpen() const { return mOpen; }
+ void setDirty() { mDirty = TRUE; }
+ BOOL generate(BOOL path_open, F32 detail = 1.0f, S32 split = 0);
+ BOOL isConcave() const { return mConcave; }
+public:
+ const LLProfileParams &mParams;
+
+ struct Face
+ {
+ S32 mIndex;
+ S32 mCount;
+ F32 mScaleU;
+ BOOL mCap;
+ BOOL mFlat;
+ LLFaceID mFaceID;
+ };
+
+ std::vector<LLVector3> mProfile;
+ std::vector<LLVector2> mNormals;
+ std::vector<Face> mFaces;
+ std::vector<LLVector3> mEdgeNormals;
+ std::vector<LLVector3> mEdgeCenters;
+ F32 mMaxX;
+ F32 mMinX;
+
+ friend std::ostream& operator<<(std::ostream &s, const LLProfile &profile);
+
+protected:
+ void genNormals();
+ void genNGon(S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0);
+
+ Face* addHole(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:
+ struct PathPt
+ {
+ LLVector3 mPos;
+ LLVector2 mScale;
+ LLQuaternion mRot;
+ F32 mTexT;
+ PathPt() { mPos.setVec(0,0,0); mTexT = 0; mScale.setVec(0,0); mRot.loadIdentity(); }
+ };
+
+public:
+ LLPath(const LLPathParams &params) : mParams(params)
+ {
+ mOpen = FALSE;
+ mDirty = TRUE;
+ mStep = 1;
+ }
+
+ virtual ~LLPath();
+
+ void genNGon(S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f);
+ virtual BOOL generate(F32 detail=1.0f, S32 split = 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:
+ const LLPathParams &mParams;
+ std::vector<PathPt> mPath;
+
+protected:
+ BOOL mOpen;
+ S32 mTotal;
+ BOOL mDirty;
+ F32 mStep;
+};
+
+class LLDynamicPath : public LLPath
+{
+public:
+ LLDynamicPath(const LLPathParams &params) : LLPath(params) { }
+ BOOL generate(F32 detail=1.0f, S32 split = 0);
+};
+
+// Yet another "face" class - caches volume-specific, but not instance-specific data for faces)
+class LLVolumeFace
+{
+public:
+ LLVolumeFace();
+ BOOL create();
+
+ class VertexData
+ {
+ public:
+ LLVector3 mPosition;
+ LLVector3 mNormal;
+ LLVector3 mBinormal;
+ LLVector2 mTexCoord;
+ };
+
+ 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
+ } TypeMask;
+
+public:
+ S32 mID;
+ U32 mTypeMask;
+ LLVector3 mCenter;
+
+ // Only used for INNER/OUTER faces
+ S32 mBeginS;
+ S32 mBeginT;
+ S32 mNumS;
+ S32 mNumT;
+
+ std::vector<VertexData> mVertices;
+ std::vector<S32> mIndices;
+ std::vector<S32> mEdge;
+ LLVolume *mVolumep; // Deliberately NOT reference counted - djs 11/20/03 - otherwise would make an annoying circular reference
+
+ // Shouldn't need num_old and num_new, really - djs
+ static BOOL updateColors(LLColor4U *old_colors, const S32 num_old, const LLVolumeFace &old_face,
+ LLStrider<LLColor4U> &new_colors, const S32 num_new, const LLVolumeFace &new_face);
+
+protected:
+ BOOL createUnCutCubeCap();
+ BOOL createCap();
+ BOOL createSide();
+};
+
+class LLVolume : public LLRefCount
+{
+ friend class LLVolumeLODGroup;
+
+private:
+ LLVolume(const LLVolume&); // Don't implement
+ ~LLVolume(); // use unref
+
+public:
+ struct Point
+ {
+ LLVector3 mPos;
+ };
+
+ struct FaceParams
+ {
+ LLFaceID mFaceID;
+ S32 mBeginS;
+ S32 mCountS;
+ S32 mBeginT;
+ S32 mCountT;
+ };
+
+ LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL generate_single_face = FALSE, const BOOL is_unique = FALSE);
+
+ U8 getProfileType() const { return mProfilep->mParams.getCurveType(); }
+ U8 getPathType() const { return mPathp->mParams.getCurveType(); }
+ S32 getNumFaces() const { return (S32)mProfilep->mFaces.size(); }
+
+ const F32 getDetail() const { return mDetail; }
+ const LLVolumeParams & getParams() const { return mParams; }
+ LLVolumeParams getCopyOfParams() const { return mParams; }
+ const LLProfile& getProfile() const { return *mProfilep; }
+ LLPath& getPath() const { return *mPathp; }
+ const std::vector<Point>& getMesh() const { return mMesh; }
+ const LLVector3& getMeshPt(const U32 i) const { return mMesh[i].mPos; }
+
+ void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); }
+
+ void regen();
+
+ BOOL isConvex() const;
+ BOOL isCap(S32 face);
+ BOOL isFlat(S32 face);
+ BOOL isUnique() const { return mUnique; }
+
+ S32 *getTriangleIndices(U32 &num_indices) const;
+ void generateSilhouetteVertices(std::vector<LLVector3> &vertices, std::vector<LLVector3> &normals, std::vector<S32> &segments, const LLVector3& view_vec,
+ const LLMatrix4& mat,
+ const LLMatrix3& norm_mat);
+
+ //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 LLVector3& start, LLVector3& end) const;
+
+ // The following cleans up vertices and triangles,
+ // getting rid of degenerate triangles and duplicate vertices,
+ // and allocates new arrays with the clean data.
+ static BOOL cleanupTriangleData( const S32 num_input_vertices,
+ const std::vector<Point> &input_vertices,
+ const S32 num_input_triangles,
+ S32 *input_triangles,
+ S32 &num_output_vertices,
+ LLVector3 **output_vertices,
+ S32 &num_output_triangles,
+ S32 **output_triangles);
+ LLFaceID generateFaceMask();
+
+ BOOL isFaceMaskValid(LLFaceID face_mask);
+ static S32 mNumMeshPoints;
+
+ 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
+
+ U32 mFaceMask; // bit array of which faces exist in this volume
+ LLVector3 mBounds[2]; // bounding box (center, half-height)
+ LLVector3 mLODScaleBias; // vector for biasing LOD based on scale
+
+protected:
+ BOOL generate();
+ void createVolumeFaces();
+
+protected:
+ BOOL mUnique;
+ F32 mDetail;
+ LLVolumeParams mParams;
+ LLPath *mPathp;
+ LLProfile *mProfilep;
+ std::vector<Point> mMesh;
+
+ BOOL mGenerateSingleFace;
+ S32 mNumVolumeFaces;
+ LLVolumeFace *mVolumeFaces;
+};
+
+std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
+
+LLVector3 calc_binormal_from_triangle(
+ const LLVector3& pos0,
+ const LLVector2& tex0,
+ const LLVector3& pos1,
+ const LLVector2& tex1,
+ const LLVector3& pos2,
+ const LLVector2& tex2);
+
+#endif
diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp
new file mode 100644
index 0000000000..54be916c12
--- /dev/null
+++ b/indra/llmath/llvolumemgr.cpp
@@ -0,0 +1,295 @@
+/**
+ * @file llvolumemgr.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llvolumemgr.h"
+#include "llvolume.h"
+
+
+//#define DEBUG_VOLUME
+
+LLVolumeMgr* gVolumeMgr = 0;
+
+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};
+
+//============================================================================
+//static
+void LLVolumeMgr::initClass()
+{
+ gVolumeMgr = new LLVolumeMgr();
+}
+
+//static
+BOOL LLVolumeMgr::cleanupClass()
+{
+ BOOL res = FALSE;
+ if (gVolumeMgr) {
+ res = gVolumeMgr->cleanup();
+ delete gVolumeMgr;
+ gVolumeMgr = 0;
+ }
+ return res;
+}
+
+//============================================================================
+
+LLVolumeMgr::LLVolumeMgr()
+{
+ mDataMutex = new LLMutex(gAPRPoolp);
+// mNumVolumes = 0;
+}
+
+LLVolumeMgr::~LLVolumeMgr()
+{
+ cleanup();
+ delete mDataMutex;
+}
+
+BOOL LLVolumeMgr::cleanup()
+{
+ #ifdef DEBUG_VOLUME
+ {
+ lldebugs << "LLVolumeMgr::cleanup()" << llendl;
+ }
+ #endif
+ BOOL no_refs = TRUE;
+ mDataMutex->lock();
+ for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(),
+ end = mVolumeLODGroups.end();
+ iter != end; iter++)
+ {
+ LLVolumeLODGroup *volgroupp = iter->second;
+ if (volgroupp->getNumRefs() != 1)
+ {
+ llwarns << "Volume group " << volgroupp << " has "
+ << volgroupp->getNumRefs() << " remaining refs" << llendl;
+ llwarns << volgroupp->getParams() << llendl;
+ no_refs = FALSE;
+ }
+ volgroupp->unref();// this );
+ }
+ mVolumeLODGroups.clear();
+ mDataMutex->unlock();
+ return no_refs;
+}
+
+LLVolume *LLVolumeMgr::getVolume(const LLVolumeParams &volume_params, const S32 detail)
+{
+ LLVolumeLODGroup* volgroupp;
+ mDataMutex->lock();
+ volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(&volume_params);
+ if( iter == mVolumeLODGroups.end() )
+ {
+ volgroupp = new LLVolumeLODGroup(volume_params);
+ const LLVolumeParams* params = &(volgroupp->getParams());
+ mVolumeLODGroups[params] = volgroupp;
+ volgroupp->ref(); // initial reference
+ }
+ else
+ {
+ volgroupp = iter->second;
+ }
+ volgroupp->ref();// this );
+ mDataMutex->unlock();
+ // mNumVolumes++;
+ #ifdef DEBUG_VOLUME
+ {
+ lldebugs << "LLVolumeMgr::getVolume() " << (*this) << llendl;
+ }
+ #endif
+ return volgroupp->getLOD(detail);
+}
+
+void LLVolumeMgr::cleanupVolume(LLVolume *volumep)
+{
+ if (volumep->isUnique())
+ {
+ // TomY: Don't need to manage this volume. It is a unique instance.
+ return;
+ }
+ LLVolumeParams* params = (LLVolumeParams*) &(volumep->getParams());
+ mDataMutex->lock();
+ volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(params);
+ if( iter == mVolumeLODGroups.end() )
+ {
+ llerrs << "Warning! Tried to cleanup unknown volume type! " << *params << llendl;
+ mDataMutex->unlock();
+ return;
+ }
+ else
+ {
+ LLVolumeLODGroup* volgroupp = iter->second;
+
+ volgroupp->derefLOD(volumep);
+ volgroupp->unref();// this );
+ if (volgroupp->getNumRefs() == 1)
+ {
+ mVolumeLODGroups.erase(params);
+ volgroupp->unref();// this );
+ }
+ // mNumVolumes--;
+ }
+ mDataMutex->unlock();
+
+ #ifdef DEBUG_VOLUME
+ {
+ lldebugs << "LLVolumeMgr::cleanupVolume() " << (*this) << llendl;
+ }
+ #endif
+}
+
+void LLVolumeMgr::dump()
+{
+ F32 avg = 0.f;
+ 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;
+ mDataMutex->unlock();
+ llinfos << "Average usage of LODs " << avg << llendl;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr)
+{
+ s << "{ numLODgroups=" << volume_mgr.mVolumeLODGroups.size() << ", ";
+
+ S32 total_refs = 0;
+ volume_mgr.mDataMutex->lock();
+
+ LLVolumeMgr::volume_lod_group_map_iter iter = volume_mgr.mVolumeLODGroups.begin();
+ LLVolumeMgr::volume_lod_group_map_iter end = volume_mgr.mVolumeLODGroups.end();
+ for ( ; iter != end; ++iter)
+ {
+ LLVolumeLODGroup *volgroupp = iter->second;
+ total_refs += volgroupp->getNumRefs();
+ s << ", " << (*volgroupp);
+ }
+
+ volume_mgr.mDataMutex->unlock();
+
+ s << ", total_refs=" << total_refs << " }";
+ return s;
+}
+
+LLVolumeLODGroup::LLVolumeLODGroup(const LLVolumeParams &params)
+{
+ S32 i;
+ mParams = params;
+
+ for (i = 0; i < NUM_LODS; i++)
+ {
+ mLODRefs[i] = 0;
+ mVolumeLODs[i] = NULL;
+ mAccessCount[i] = 0;
+ }
+}
+
+LLVolumeLODGroup::~LLVolumeLODGroup()
+{
+ S32 i;
+ for (i = 0; i < NUM_LODS; i++)
+ {
+ delete mVolumeLODs[i];
+ mVolumeLODs[i] = NULL;
+ }
+}
+
+
+LLVolume * LLVolumeLODGroup::getLOD(const S32 detail)
+{
+ llassert(detail >=0 && detail < NUM_LODS);
+ mAccessCount[detail]++;
+ mLODRefs[detail]++;
+ if (!mVolumeLODs[detail])
+ {
+ mVolumeLODs[detail] = new LLVolume(mParams, mDetailScales[detail]);
+ }
+ return mVolumeLODs[detail];
+}
+
+BOOL LLVolumeLODGroup::derefLOD(LLVolume *volumep)
+{
+ S32 i;
+ for (i = 0; i < NUM_LODS; i++)
+ {
+ if (mVolumeLODs[i] == volumep)
+ {
+ mLODRefs[i]--;
+ if (!mLODRefs[i])
+ {
+ mVolumeLODs[i] = NULL;
+ }
+ return TRUE;
+ }
+ }
+ llerrs << "Deref of non-matching LOD in volume LOD group" << llendl;
+ 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;
+}
+
+F32 LLVolumeLODGroup::getVolumeScaleFromDetail(const S32 detail)
+{
+ return mDetailScales[detail];
+}
+
+F32 LLVolumeLODGroup::dump()
+{
+ char dump_str[255];
+ F32 usage = 0.f;
+ for (S32 i = 0; i < NUM_LODS; i++)
+ {
+ if (mAccessCount[i] > 0)
+ {
+ usage += 1.f;
+ }
+ }
+ usage = usage / (F32)NUM_LODS;
+
+ sprintf(dump_str, "%.3f %d %d %d %d", usage, mAccessCount[0], mAccessCount[1], mAccessCount[2], mAccessCount[3]);
+
+ llinfos << dump_str << llendl;
+ return usage;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup)
+{
+ s << "{ numRefs=" << volgroup.getNumRefs();
+ s << ", mParams=" << volgroup.mParams;
+ s << " }";
+
+ return s;
+}
+
diff --git a/indra/llmath/llvolumemgr.h b/indra/llmath/llvolumemgr.h
new file mode 100644
index 0000000000..675c8f640c
--- /dev/null
+++ b/indra/llmath/llvolumemgr.h
@@ -0,0 +1,82 @@
+/**
+ * @file llvolumemgr.h
+ * @brief LLVolumeMgr class.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOLUMEMGR_H
+#define LL_LLVOLUMEMGR_H
+
+#include <map>
+
+#include "llvolume.h"
+#include "llmemory.h"
+#include "llthread.h"
+
+class LLVolumeParams;
+class LLVolumeLODGroup;
+
+class LLVolumeLODGroup : public LLThreadSafeRefCount
+{
+protected:
+ ~LLVolumeLODGroup();
+
+public:
+ enum
+ {
+ NUM_LODS = 4
+ };
+
+ LLVolumeLODGroup(const LLVolumeParams &params);
+
+ BOOL derefLOD(LLVolume *volumep);
+ static S32 getDetailFromTan(const F32 tan_angle);
+ static F32 getVolumeScaleFromDetail(const S32 detail);
+
+ LLVolume *getLOD(const S32 detail);
+ const LLVolumeParams &getParams() const { return mParams; };
+
+ F32 dump();
+ friend std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup);
+
+protected:
+ LLVolumeParams mParams;
+
+ S32 mLODRefs[NUM_LODS];
+ LLVolume *mVolumeLODs[NUM_LODS];
+ static F32 mDetailThresholds[NUM_LODS];
+ static F32 mDetailScales[NUM_LODS];
+ S32 mAccessCount[NUM_LODS];
+};
+
+class LLVolumeMgr
+{
+public:
+ static void initClass();
+ static BOOL cleanupClass();
+
+public:
+ LLVolumeMgr();
+ ~LLVolumeMgr();
+ BOOL cleanup(); // Cleanup all volumes being managed, returns TRUE if no dangling references
+ LLVolume *getVolume(const LLVolumeParams &volume_params, const S32 detail);
+ void cleanupVolume(LLVolume *volumep);
+
+ void dump();
+ friend std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr);
+
+protected:
+ typedef std::map<const LLVolumeParams*, LLVolumeLODGroup*, LLVolumeParams::compare> volume_lod_group_map_t;
+ typedef volume_lod_group_map_t::const_iterator volume_lod_group_map_iter;
+ volume_lod_group_map_t mVolumeLODGroups;
+
+ LLMutex* mDataMutex;
+
+// S32 mNumVolumes;
+};
+
+extern LLVolumeMgr* gVolumeMgr;
+
+#endif // LL_LLVOLUMEMGR_H
diff --git a/indra/llmath/m3math.cpp b/indra/llmath/m3math.cpp
new file mode 100644
index 0000000000..523acb4dcf
--- /dev/null
+++ b/indra/llmath/m3math.cpp
@@ -0,0 +1,530 @@
+/**
+ * @file m3math.cpp
+ * @brief LLMatrix3 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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)
+{
+ *this = q.getMatrix3();
+}
+
+
+LLMatrix3::LLMatrix3(const F32 angle, const LLVector3 &vec)
+{
+ LLQuaternion quat(angle, vec);
+ *this = setRot(quat);
+}
+
+LLMatrix3::LLMatrix3(const F32 angle, const LLVector3d &vec)
+{
+ LLVector3 vec_f;
+ vec_f.setVec(vec);
+ LLQuaternion quat(angle, vec_f);
+ *this = setRot(quat);
+}
+
+LLMatrix3::LLMatrix3(const F32 angle, const LLVector4 &vec)
+{
+ LLQuaternion quat(angle, vec);
+ *this = setRot(quat);
+}
+
+LLMatrix3::LLMatrix3(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLVector3 vec(x, y, z);
+ LLQuaternion quat(angle, vec);
+ *this = setRot(quat);
+}
+
+LLMatrix3::LLMatrix3(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;
+}
+
+// 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::identity()
+{
+ 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::zero()
+{
+ 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]);
+}
+
+// This is identical to the transMat3() method because we assume a rotation matrix
+const LLMatrix3& LLMatrix3::invert()
+{
+ // 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;
+}
+
+// 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;
+}
+
+
+// These functions take Rotation arguments
+const LLMatrix3& LLMatrix3::setRot(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLMatrix3 mat(angle, x, y, z);
+ *this = mat;
+ return *this;
+}
+
+const LLMatrix3& LLMatrix3::setRot(const F32 angle, const LLVector3 &vec)
+{
+ LLMatrix3 mat(angle, vec);
+ *this = mat;
+ return *this;
+}
+
+const LLMatrix3& LLMatrix3::setRot(const F32 roll, const F32 pitch, const F32 yaw)
+{
+ LLMatrix3 mat(roll, pitch, yaw);
+ *this = mat;
+ return *this;
+}
+
+
+const LLMatrix3& LLMatrix3::setRot(const LLQuaternion &q)
+{
+ LLMatrix3 mat(q);
+ *this = mat;
+ 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;
+}
+
+
+// Rotate exisitng mMatrix
+const LLMatrix3& LLMatrix3::rotate(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLMatrix3 mat(angle, x, y, z);
+ *this *= mat;
+ 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;
+}
+
+
+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;
+}
+
+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
new file mode 100644
index 0000000000..ac93ed86fb
--- /dev/null
+++ b/indra/llmath/m3math.h
@@ -0,0 +1,198 @@
+/**
+ * @file m3math.h
+ * @brief LLMatrix3 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_M3MATH_H
+#define LL_M3MATH_H
+
+#include "llerror.h"
+
+class LLVector4;
+class LLVector3;
+class LLVector3d;
+class LLQuaternion;
+
+// NOTA BENE: Currently assuming a right-handed, z-up universe
+
+// 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
+
+// NOTE: The world of computer graphics uses column-vectors and matricies that
+// "operate to the left".
+
+
+static const U32 NUM_VALUES_IN_MAT3 = 3;
+#if LL_WINDOWS
+__declspec( align(16) )
+#endif
+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 F32 x, const F32 y, const F32 z); // Initializes Matrix with axis angle
+ 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& identity(); // Load identity matrix
+ const LLMatrix3& zero(); // Clears Matrix to zero
+
+ ///////////////////////////
+ //
+ // Matrix setters - set some properties without modifying others
+ //
+
+ // These functions take Rotation arguments
+ const LLMatrix3& setRot(const F32 angle, const F32 x, const F32 y, const F32 z); // Calculate rotation matrix for rotating angle radians about (x, y, z)
+ 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);
+
+ ///////////////////////////
+ //
+ // 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& invert(); // Invert MAT4
+ const LLMatrix3& orthogonalize(); // Orthogonalizes X, then Y, then Z
+ 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.rotMat3(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
+
+// 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 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 const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b); // Return a * b
+
+ friend std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a); // Stream a
+}
+#if LL_DARWIN
+__attribute__ ((aligned (16)))
+#endif
+;
+
+inline LLMatrix3::LLMatrix3(void)
+{
+ 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;
+}
+
+inline LLMatrix3::LLMatrix3(const F32 *mat)
+{
+ 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[2][0] = mat[6];
+ mMatrix[2][1] = mat[7];
+ mMatrix[2][2] = mat[8];
+}
+
+
+#endif
+
+
+// Rotation matrix hints...
+
+// Inverse of Rotation Matrices
+// ----------------------------
+// If R is a rotation matrix that rotate vectors from Frame-A to Frame-B,
+// then the transpose of R will rotate vectors from Frame-B to Frame-A.
+
+
+// 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
+// a rotation matrix what do you get?
+//
+// R = | X0 X1 X2 |
+// | Y0 Y1 Y2 |
+// | Z0 Z1 Z2 |
+//
+// Yeah, but what does it mean?
+//
+// Transpose the matrix and have it operate on a vector...
+//
+// V * R_transpose = [ V0 V1 V2 ] * | X0 Y0 Z0 |
+// | X1 Y1 Z1 |
+// | X2 Y2 Z2 |
+//
+// = [ V*X V*Y V*Z ]
+//
+// = components of V that are parallel to the three object axes
+//
+// = transformation of V into object frame
+//
+// Since the transformation of a rotation matrix is its inverse, then
+// R must rotate vectors from the object-frame into the absolute-frame.
+
+
+
diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp
new file mode 100644
index 0000000000..969c3663e6
--- /dev/null
+++ b/indra/llmath/m4math.cpp
@@ -0,0 +1,794 @@
+/**
+ * @file m4math.cpp
+ * @brief LLMatrix4 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include "vmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llquaternion.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 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::zero()
+{
+ 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
+{
+ llerrs << "Not implemented!" << llendl;
+ return 0.f;
+}
+
+// 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;
+}
+
+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(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLMatrix3 mat(angle, x, y, z);
+ return initMatrix(mat);
+}
+
+
+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);
+}
+
+
+// Position and Rotation
+const LLMatrix4& LLMatrix4::initRotTrans(const F32 angle, const F32 rx, const F32 ry, const F32 rz,
+ const F32 tx, const F32 ty, const F32 tz)
+{
+ LLMatrix3 mat(angle, rx, ry, rz);
+ LLVector3 translation(tx, ty, tz);
+ initMatrix(mat);
+ setTranslation(translation);
+ return (*this);
+}
+
+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::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);
+}
+
+// Rotate exisitng mMatrix
+const LLMatrix4& LLMatrix4::rotate(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLVector4 vec4(x, y, z);
+ LLMatrix4 mat(angle, vec4);
+ *this *= mat;
+ 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
+
+
+/* Not implemented to help enforce code consistency with the syntax of
+ row-major notation. This is a Good Thing.
+LLVector4 operator*(const LLMatrix4 &a, const LLVector4 &b)
+{
+ // Operate "to the right" on column-vector b
+ LLVector4 vec;
+ vec.mV[VX] = a.mMatrix[VX][VX] * b.mV[VX] +
+ a.mMatrix[VY][VX] * b.mV[VY] +
+ a.mMatrix[VZ][VX] * b.mV[VZ] +
+ a.mMatrix[VW][VX] * b.mV[VW];
+
+ vec.mV[VY] = a.mMatrix[VX][VY] * b.mV[VX] +
+ a.mMatrix[VY][VY] * b.mV[VY] +
+ a.mMatrix[VZ][VY] * b.mV[VZ] +
+ a.mMatrix[VW][VY] * b.mV[VW];
+
+ vec.mV[VZ] = a.mMatrix[VX][VZ] * b.mV[VX] +
+ a.mMatrix[VY][VZ] * b.mV[VY] +
+ a.mMatrix[VZ][VZ] * b.mV[VZ] +
+ a.mMatrix[VW][VZ] * b.mV[VW];
+
+ vec.mV[VW] = a.mMatrix[VX][VW] * b.mV[VX] +
+ a.mMatrix[VY][VW] * b.mV[VY] +
+ a.mMatrix[VZ][VW] * b.mV[VZ] +
+ a.mMatrix[VW][VW] * b.mV[VW];
+ return vec;
+}
+*/
+
+
+LLVector4 operator*(const LLVector4 &a, const LLMatrix4 &b)
+{
+ // 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] +
+ a.mV[VW] * b.mMatrix[VW][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] +
+ a.mV[VW] * b.mMatrix[VW][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] +
+ a.mV[VW] * b.mMatrix[VW][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] +
+ a.mV[VW] * b.mMatrix[VW][VW];
+ return vec;
+}
+
+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;
+}
+
+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;
+}
+
+
diff --git a/indra/llmath/m4math.h b/indra/llmath/m4math.h
new file mode 100644
index 0000000000..4f26d875ff
--- /dev/null
+++ b/indra/llmath/m4math.h
@@ -0,0 +1,365 @@
+/**
+ * @file m4math.h
+ * @brief LLMatrix3 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_M4MATH_H
+#define LL_M4MATH_H
+
+#include "v3math.h"
+
+class LLVector4;
+class LLMatrix3;
+class LLQuaternion;
+
+// NOTA BENE: Currently assuming a right-handed, x-forward, y-left, z-up universe
+
+// Us versus OpenGL:
+
+// Even though OpenGL uses column vectors and we use row vectors, we can plug our matrices
+// directly into OpenGL. This is because OpenGL numbers its matrices going columnwise:
+//
+// OpenGL indexing: Our indexing:
+// 0 4 8 12 [0][0] [0][1] [0][2] [0][3]
+// 1 5 9 13 [1][0] [1][1] [1][2] [1][3]
+// 2 6 10 14 [2][0] [2][1] [2][2] [2][3]
+// 3 7 11 15 [3][0] [3][1] [3][2] [3][3]
+//
+// So when you're looking at OpenGL related matrices online, our matrices will be
+// "transposed". But our matrices can be plugged directly into OpenGL and work fine!
+//
+
+// We're using row vectors - [vx, vy, vz, vw]
+//
+// There are several different ways of thinking of matrices, if you mix them up, you'll get very confused.
+//
+// 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
+//
+// | 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 |
+//
+//
+//
+//
+// Another way to think of matrices is matrix that takes a point p in frame A, and puts it into frame B:
+// This is used most commonly for the modelview matrix.
+//
+// so p*M = p'
+//
+// 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 |
+// 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.
+//
+
+static const U32 NUM_VALUES_IN_MAT4 = 4;
+
+#if LL_WINDOWS
+__declspec(align(16))
+#endif
+class LLMatrix4
+{
+public:
+ F32 mMatrix[NUM_VALUES_IN_MAT4][NUM_VALUES_IN_MAT4];
+
+ LLMatrix4(); // Initializes Matrix to identity matrix
+ explicit LLMatrix4(const F32 *mat); // Initializes Matrix to values in mat
+ explicit LLMatrix4(const LLMatrix3 &mat); // Initializes Matrix to valuee 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)
+
+ 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
+
+
+ //////////////////////////////
+ //
+ // Matrix initializers - these replace any existing values in the matrix
+ //
+
+ void initRows(const LLVector4 &row0,
+ const LLVector4 &row1,
+ const LLVector4 &row2,
+ const LLVector4 &row3);
+
+ // various useful matrix functions
+ const LLMatrix4& identity(); // Load identity matrix
+ const LLMatrix4& zero(); // Clears matrix to all zeros.
+
+ const LLMatrix4& initRotation(const F32 angle, const F32 x, const F32 y, const F32 z); // Calculate rotation matrix by rotating angle radians about (x, y, z)
+ 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);
+
+ // These operation create a matrix that will rotate and translate by the
+ // specified amounts.
+ const LLMatrix4& initRotTrans(const F32 angle,
+ const F32 rx, const F32 ry, const F32 rz,
+ const F32 px, const F32 py, const F32 pz);
+
+ 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);
+
+
+ ///////////////////////////
+ //
+ // 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)
+
+ void setFwdRow(const LLVector3 &row);
+ void setLeftRow(const LLVector3 &row);
+ void setUpRow(const LLVector3 &row);
+
+ void setFwdCol(const LLVector3 &col);
+ void setLeftCol(const LLVector3 &col);
+ void setUpCol(const LLVector3 &col);
+
+ const LLMatrix4& setTranslation(const LLVector4 &translation);
+ const LLMatrix4& setTranslation(const LLVector3 &translation);
+
+ ///////////////////////////
+ //
+ // Get properties of a matrix
+ //
+
+ F32 determinant(void) const; // Return determinant
+ LLQuaternion quaternion(void) const; // Returns quaternion
+
+ LLVector4 getFwdRow4() const;
+ LLVector4 getLeftRow4() const;
+ LLVector4 getUpRow4() const;
+
+ LLMatrix3 getMat3() const;
+
+ const LLVector3& getTranslation() const { return *(LLVector3*)&mMatrix[3][0]; }
+
+ ///////////////////////////
+ //
+ // Operations on an existing matrix
+ //
+
+ 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 F32 x, const F32 y, const F32 z); // Rotate matrix by rotating angle radians about (x, y, z)
+ 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])
+
+
+
+
+ ///////////////////////
+ //
+ // Operators
+ //
+
+// Not implemented to enforce code that agrees with symbolic syntax
+// friend LLVector4 operator*(const LLMatrix4 &a, const LLVector4 &b); // Apply rotation a to vector b
+
+// 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 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 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
+}
+#if LL_DARWIN
+__attribute__ ((aligned (16)))
+#endif
+;
+
+
+inline LLMatrix4::LLMatrix4()
+{
+ identity();
+}
+
+inline const LLMatrix4& LLMatrix4::identity()
+{
+ 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 LLVector3 operator*(const LLVector3 &a, const LLMatrix4 &b)
+{
+ // Converts a to LLVector4 and applies full transformation
+ // Operates "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] +
+ b.mMatrix[VW][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] +
+ b.mMatrix[VW][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] +
+ b.mMatrix[VW][VZ];
+ return vec;
+}
+
+/*
+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;
+}
+*/
+
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+#endif
+
+
+
diff --git a/indra/llmath/raytrace.cpp b/indra/llmath/raytrace.cpp
new file mode 100644
index 0000000000..dfee5d09fd
--- /dev/null
+++ b/indra/llmath/raytrace.cpp
@@ -0,0 +1,1256 @@
+/**
+ * @file raytrace.cpp
+ * @brief Functions called by box object scripts.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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 (TRUE == 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 (TRUE == 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 (TRUE == 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 (TRUE == 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 (TRUE == 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 (TRUE == 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 (TRUE == 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 (TRUE == 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 (TRUE == 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 (TRUE == 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 (TRUE == 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
new file mode 100644
index 0000000000..d4f93647b8
--- /dev/null
+++ b/indra/llmath/raytrace.h
@@ -0,0 +1,214 @@
+/**
+ * @file raytrace.h
+ * @brief Ray intersection tests for primitives.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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 iff 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 iff 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/v2math.cpp b/indra/llmath/v2math.cpp
new file mode 100644
index 0000000000..d4cff60f62
--- /dev/null
+++ b/indra/llmath/v2math.cpp
@@ -0,0 +1,92 @@
+/**
+ * @file v2math.cpp
+ * @brief LLVector2 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include "vmath.h"
+#include "v2math.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 fsqrtf( 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 );
+}
diff --git a/indra/llmath/v2math.h b/indra/llmath/v2math.h
new file mode 100644
index 0000000000..c94333e2a9
--- /dev/null
+++ b/indra/llmath/v2math.h
@@ -0,0 +1,307 @@
+/**
+ * @file v2math.h
+ * @brief LLVector2 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V2MATH_H
+#define LL_V2MATH_H
+
+#include <math.h>
+
+#include "llmath.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])
+
+ // Clears LLVector2 to (0, 0). DEPRECATED - prefer zeroVec.
+ void clearVec();
+
+ // Zero LLVector2 to (0, 0)
+ void zeroVec();
+
+ void setVec(F32 x, F32 y); // Sets LLVector2 to (x, y)
+ void setVec(const LLVector2 &vec); // Sets LLVector2 to vec
+ void setVec(const F32 *vec); // Sets LLVector2 to vec
+
+ F32 magVec() const; // Returns magnitude of LLVector2
+ F32 magVecSquared() const; // Returns magnitude squared of LLVector2
+ F32 normVec(); // Normalizes and returns the magnitude of LLVector2
+
+ 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 sqaured between a and b
+F32 dist_vec_squared2D(const LLVector2 &a, const LLVector2 &b);// Returns distance sqaured 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];
+}
+
+
+// Clear and Assignment Functions
+
+inline void LLVector2::clearVec(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+}
+
+inline void LLVector2::zeroVec(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+}
+
+inline void LLVector2::setVec(F32 x, F32 y)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+}
+
+inline void LLVector2::setVec(const LLVector2 &vec)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+}
+
+inline void LLVector2::setVec(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+}
+
+// LLVector2 Magnitude and Normalization Functions
+
+inline F32 LLVector2::magVec(void) const
+{
+ return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
+}
+
+inline F32 LLVector2::magVecSquared(void) const
+{
+ return mV[0]*mV[0] + mV[1]*mV[1];
+}
+
+inline F32 LLVector2::normVec(void)
+{
+ F32 mag = fsqrtf(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 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
new file mode 100644
index 0000000000..2dbc368d98
--- /dev/null
+++ b/indra/llmath/v3color.cpp
@@ -0,0 +1,44 @@
+/**
+ * @file v3color.cpp
+ * @brief LLColor3 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "v3color.h"
+#include "v4color.h"
+
+LLColor3 LLColor3::white(1.0f, 1.0f, 1.0f);
+LLColor3 LLColor3::black(0.0f, 0.0f, 0.0f);
+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];
+}
+
+LLColor3::LLColor3(const LLSD &sd)
+{
+ mV[0] = (F32) sd[0].asReal();
+ mV[1] = (F32) sd[1].asReal();
+ mV[2] = (F32) sd[2].asReal();
+}
+
+const LLColor3& LLColor3::operator=(const LLColor4 &a)
+{
+ 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)
+{
+ s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }";
+ return s;
+}
diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h
new file mode 100644
index 0000000000..3777c00054
--- /dev/null
+++ b/indra/llmath/v3color.h
@@ -0,0 +1,362 @@
+/**
+ * @file v3color.h
+ * @brief LLColor3 class header file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V3COLOR_H
+#define LL_V3COLOR_H
+
+class LLColor4;
+
+#include "llerror.h"
+#include "llmath.h"
+#include "llsd.h"
+
+// LLColor3 = |r g b|
+
+static const U32 LENGTHOFCOLOR3 = 3;
+
+class LLColor3
+{
+public:
+ F32 mV[LENGTHOFCOLOR3];
+
+ 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(char *color_string); // html format color ie "#FFDDEE"
+ explicit LLColor3(const LLColor4& color4); // "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();;
+ }
+
+ 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); // Sets LLColor3 to (x, y, z)
+ const LLColor3& setVec(const LLColor3 &vec); // Sets LLColor3 to vec
+ const LLColor3& setVec(const F32 *vec); // Sets LLColor3 to vec
+
+ F32 magVec() const; // Returns magnitude of LLColor3
+ F32 magVecSquared() const; // Returns magnitude squared of LLColor3
+ F32 normVec(); // Normalizes and returns the magnitude of LLColor3
+
+ const LLColor3& operator=(const LLColor4 &a);
+
+ 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 LLColor3 operator*(const LLColor3 &a, const LLColor3 &b); // Return a dot 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 const LLColor3& operator*=(LLColor3 &a, F32 k); // Return a times scaler k
+
+ friend LLColor3 operator-(const LLColor3 &a); // Return vector 1-rgb (inverse)
+
+ inline void clamp();
+ inline void exp(); // Do an exponential on the color
+};
+
+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 sqaured between a and b
+
+inline LLColor3::LLColor3(void)
+{
+ 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;
+}
+
+inline LLColor3::LLColor3(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+}
+
+inline LLColor3::LLColor3(char* color_string) // takes a string of format "RRGGBB" where RR is hex 00..FF
+{
+ if (strlen(color_string) < 6)
+ {
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+ return;
+ }
+
+ static char tempstr[7];
+ strncpy(tempstr,color_string,6);
+ 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)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+ return (*this);
+}
+
+inline const LLColor3& LLColor3::setToWhite(void)
+{
+ mV[0] = 1.f;
+ mV[1] = 1.f;
+ mV[2] = 1.f;
+ return (*this);
+}
+
+inline const LLColor3& LLColor3::setVec(F32 r, F32 g, F32 b)
+{
+ mV[0] = r;
+ mV[1] = g;
+ mV[2] = b;
+ return (*this);
+}
+
+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);
+}
+
+inline const LLColor3& LLColor3::setVec(const F32 *vec)
+{
+ mV[0] = vec[0];
+ mV[1] = vec[1];
+ mV[2] = vec[2];
+ return (*this);
+}
+
+inline F32 LLColor3::magVec(void) const
+{
+ return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+}
+
+inline F32 LLColor3::magVecSquared(void) const
+{
+ return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
+}
+
+inline F32 LLColor3::normVec(void)
+{
+ F32 mag = fsqrtf(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);
+}
+
+inline void LLColor3::exp()
+{
+#if 0
+ 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]);
+#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]);
+}
+
+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]);
+}
+
+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]);
+}
+
+inline LLColor3 operator*(const LLColor3 &a, F32 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 );
+}
+
+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]));
+}
+
+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]));
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+inline const LLColor3& operator*=(LLColor3 &a, F32 k)
+{
+ 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] );
+}
+
+// Non-member functions
+
+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 fsqrtf( x*x + y*y + z*z );
+}
+
+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;
+}
+
+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);
+}
+
+
+#endif
diff --git a/indra/llmath/v3dmath.cpp b/indra/llmath/v3dmath.cpp
new file mode 100644
index 0000000000..0e12389acd
--- /dev/null
+++ b/indra/llmath/v3dmath.cpp
@@ -0,0 +1,129 @@
+/**
+ * @file v3dmath.cpp
+ * @brief LLVector3d class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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 char* buf, LLVector3d* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ LLVector3d v;
+ S32 count = sscanf( buf, "%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
new file mode 100644
index 0000000000..d8feb10757
--- /dev/null
+++ b/indra/llmath/v3dmath.h
@@ -0,0 +1,409 @@
+/**
+ * @file v3dmath.h
+ * @brief High precision 3 dimensional vector.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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);
+ 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();
+ }
+
+ const LLVector3d& operator=(const LLSD& sd)
+ {
+ setValue(sd);
+ return *this;
+ }
+
+ 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& clearVec(); // Clears LLVector3d to (0, 0, 0, 1)
+ inline const LLVector3d& zeroVec(); // Zero LLVector3d to (0, 0, 0, 0)
+ inline const LLVector3d& setVec(const F64 x, const F64 y, const F64 z); // Sets LLVector3d to (x, y, z, 1)
+ inline const LLVector3d& setVec(const LLVector3d &vec); // Sets LLVector3d to vec
+ inline const LLVector3d& setVec(const F64 *vec); // Sets LLVector3d to vec
+ inline const LLVector3d& setVec(const LLVector3 &vec);
+
+ F64 magVec() const; // Returns magnitude of LLVector3d
+ F64 magVecSquared() const; // Returns magnitude squared of LLVector3d
+ inline F64 normVec(); // 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 char* buf, LLVector3d* value);
+
+};
+
+typedef LLVector3d LLGlobalVec;
+
+const LLVector3d &LLVector3d::setVec(const LLVector3 &vec)
+{
+ mdV[0] = vec.mV[0];
+ mdV[1] = vec.mV[1];
+ mdV[2] = vec.mV[2];
+ return *this;
+}
+
+
+inline LLVector3d::LLVector3d(void)
+{
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2] = 0.f;
+}
+
+inline LLVector3d::LLVector3d(const F64 x, const F64 y, const F64 z)
+{
+ mdV[VX] = x;
+ mdV[VY] = y;
+ mdV[VZ] = z;
+}
+
+inline LLVector3d::LLVector3d(const F64 *vec)
+{
+ mdV[VX] = vec[VX];
+ mdV[VY] = vec[VY];
+ mdV[VZ] = vec[VZ];
+}
+
+inline LLVector3d::LLVector3d(const LLVector3 &vec)
+{
+ mdV[VX] = vec.mV[VX];
+ mdV[VY] = vec.mV[VY];
+ mdV[VZ] = vec.mV[VZ];
+}
+
+/*
+inline LLVector3d::LLVector3d(const LLVector3d &copy)
+{
+ mdV[VX] = copy.mdV[VX];
+ mdV[VY] = copy.mdV[VY];
+ mdV[VZ] = copy.mdV[VZ];
+}
+*/
+
+// Destructors
+
+// checker
+inline BOOL LLVector3d::isFinite() const
+{
+ return (llfinite(mdV[VX]) && llfinite(mdV[VY]) && llfinite(mdV[VZ]));
+}
+
+
+// Clear and Assignment Functions
+
+inline const LLVector3d& LLVector3d::clearVec(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::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 = fsqrtf(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 fsqrtf(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 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 fsqrtf( 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.normVec();
+ bn.normVec();
+ 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.normVec();
+ bn.normVec();
+ F64 dot = an * bn;
+ if ( (1.0f - fabs(dot)) < epsilon)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif // LL_V3DMATH_H
diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp
new file mode 100644
index 0000000000..f254f4112e
--- /dev/null
+++ b/indra/llmath/v3math.cpp
@@ -0,0 +1,213 @@
+/**
+ * @file v3math.cpp
+ * @brief LLVector3 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "v3math.h"
+
+//#include "vmath.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;
+}
+
+// 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);
+}
+
+
+std::ostream& operator<<(std::ostream& s, const LLVector3 &a)
+{
+ s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }";
+ return s;
+}
+
+
+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::rotVec(F32 angle, const LLVector3 &vec)
+{
+ if ( !vec.isExactlyZero() && angle )
+ {
+ *this = *this * LLMatrix3(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 * LLMatrix3(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::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 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];
+}
+
+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 char* buf, LLVector3* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ LLVector3 v;
+ S32 count = sscanf( buf, "%f %f %f", v.mV + 0, v.mV + 1, v.mV + 2 );
+ if( 3 == count )
+ {
+ value->setVec( v );
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h
new file mode 100644
index 0000000000..09042b6dc3
--- /dev/null
+++ b/indra/llmath/v3math.h
@@ -0,0 +1,446 @@
+/**
+ * @file v3math.h
+ * @brief LLVector3 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V3MATH_H
+#define LL_V3MATH_H
+
+#include "llerror.h"
+#include "llmath.h"
+
+#include "llsd.h"
+class LLVector4;
+class LLMatrix3;
+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 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])
+ LLVector3(const LLSD& sd)
+ {
+ setValue(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();
+ }
+
+ const LLVector3& operator=(const LLSD& sd)
+ {
+ setValue(sd);
+ return *this;
+ }
+
+ 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
+
+ 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 clearVec(); // Clears LLVector3 to (0, 0, 0, 1)
+ inline void zeroVec(); // Zero LLVector3 to (0, 0, 0, 0)
+ inline void setVec(F32 x, F32 y, F32 z); // Sets LLVector3 to (x, y, z, 1)
+ inline void setVec(const LLVector3 &vec); // Sets LLVector3 to vec
+ inline void setVec(const F32 *vec); // Sets LLVector3 to vec
+
+ const LLVector3& setVec(const LLVector4 &vec);
+ const LLVector3& setVec(const LLVector3d &vec); // Sets LLVector3 to vec
+
+ F32 magVec() const; // Returns magnitude of LLVector3
+ F32 magVecSquared() const; // Returns magnitude squared of LLVector3
+ inline F32 normVec(); // Normalizes and returns the magnitude of LLVector3
+
+ 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& 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 char* 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 sqaured between a and b
+F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b);// Returns distance sqaured between a and b ignoring Z component
+LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b
+LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u); // Returns a vector that is a linear interpolation between a and b
+
+inline LLVector3::LLVector3(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline LLVector3::LLVector3(const F32 x, const F32 y, const F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+}
+
+inline LLVector3::LLVector3(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+}
+
+/*
+inline LLVector3::LLVector3(const LLVector3 &copy)
+{
+ mV[VX] = copy.mV[VX];
+ mV[VY] = copy.mV[VY];
+ mV[VZ] = copy.mV[VZ];
+}
+*/
+
+// Destructors
+
+// checker
+inline BOOL LLVector3::isFinite() const
+{
+ return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]));
+}
+
+
+// Clear and Assignment Functions
+
+inline void LLVector3::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::setVec(F32 x, F32 y, F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+}
+
+inline void LLVector3::setVec(const LLVector3 &vec)
+{
+ mV[0] = vec.mV[0];
+ mV[1] = vec.mV[1];
+ mV[2] = vec.mV[2];
+}
+
+inline void LLVector3::setVec(const F32 *vec)
+{
+ mV[0] = vec[0];
+ mV[1] = vec[1];
+ mV[2] = vec[2];
+}
+
+inline F32 LLVector3::normVec(void)
+{
+ F32 mag = fsqrtf(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::magVec(void) const
+{
+ return fsqrtf(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 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 fsqrtf( 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)
+{
+ LLVector3 project_axis = b;
+ project_axis.normVec();
+ return project_axis * (a * project_axis);
+}
+
+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 F32 angle_between(const LLVector3& a, const LLVector3& b)
+{
+ LLVector3 an = a;
+ LLVector3 bn = b;
+ an.normVec();
+ bn.normVec();
+ F32 cosine = an * bn;
+ F32 angle = (cosine >= 1.0f) ? 0.0f :
+ (cosine <= -1.0f) ? F_PI :
+ (F32)acos(cosine);
+ return angle;
+}
+
+inline BOOL are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon)
+{
+ LLVector3 an = a;
+ LLVector3 bn = b;
+ an.normVec();
+ bn.normVec();
+ F32 dot = an * bn;
+ if ( (1.0f - fabs(dot)) < epsilon)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#endif
diff --git a/indra/llmath/v4color.cpp b/indra/llmath/v4color.cpp
new file mode 100644
index 0000000000..83870941df
--- /dev/null
+++ b/indra/llmath/v4color.cpp
@@ -0,0 +1,561 @@
+/**
+ * @file v4color.cpp
+ * @brief LLColor4 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llboost.h"
+
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v3color.h"
+//#include "vmath.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 const LLColor4U() const
+{
+ return LLColor4U(
+ (U8)llclampb(llround(mV[VRED]*255.f)),
+ (U8)llclampb(llround(mV[VGREEN]*255.f)),
+ (U8)llclampb(llround(mV[VBLUE]*255.f)),
+ (U8)llclampb(llround(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;
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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
+BOOL LLColor4::parseColor(const char* buf, LLColor4* color)
+{
+ if( buf == NULL || buf[0] == '\0' || color == NULL)
+ {
+ return FALSE;
+ }
+
+ LLString full_string(buf);
+
+ boost_tokenizer tokens(full_string, 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.
+ LLString color_name( (*token_iter) );
+ ++token_iter;
+
+ if (token_iter != tokens.end())
+ {
+ // There are more tokens to read. This must be a vector.
+ LLColor4 v;
+ LLString::convertToF32( color_name, v.mV[VX] );
+ LLString::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.
+ llwarns << "LLColor4::parseColor() malformed color " << full_string << llendl;
+ }
+ else
+ {
+ // There is a z-component.
+ LLString::convertToF32( *token_iter, v.mV[VZ] );
+
+ ++token_iter;
+ if (token_iter != tokens.end())
+ {
+ // There is an alpha component.
+ LLString::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->setVec( v );
+ }
+ else // Single value. Read as a named color.
+ {
+ // We have a color name
+ if ( "red" == color_name )
+ {
+ color->setVec(LLColor4::red);
+ }
+ else if ( "red1" == color_name )
+ {
+ color->setVec(LLColor4::red1);
+ }
+ else if ( "red2" == color_name )
+ {
+ color->setVec(LLColor4::red2);
+ }
+ else if ( "red3" == color_name )
+ {
+ color->setVec(LLColor4::red3);
+ }
+ else if ( "red4" == color_name )
+ {
+ color->setVec(LLColor4::red4);
+ }
+ else if ( "red5" == color_name )
+ {
+ color->setVec(LLColor4::red5);
+ }
+ else if( "green" == color_name )
+ {
+ color->setVec(LLColor4::green);
+ }
+ else if( "green1" == color_name )
+ {
+ color->setVec(LLColor4::green1);
+ }
+ else if( "green2" == color_name )
+ {
+ color->setVec(LLColor4::green2);
+ }
+ else if( "green3" == color_name )
+ {
+ color->setVec(LLColor4::green3);
+ }
+ else if( "green4" == color_name )
+ {
+ color->setVec(LLColor4::green4);
+ }
+ else if( "green5" == color_name )
+ {
+ color->setVec(LLColor4::green5);
+ }
+ else if( "green6" == color_name )
+ {
+ color->setVec(LLColor4::green6);
+ }
+ else if( "blue" == color_name )
+ {
+ color->setVec(LLColor4::blue);
+ }
+ else if( "blue1" == color_name )
+ {
+ color->setVec(LLColor4::blue1);
+ }
+ else if( "blue2" == color_name )
+ {
+ color->setVec(LLColor4::blue2);
+ }
+ else if( "blue3" == color_name )
+ {
+ color->setVec(LLColor4::blue3);
+ }
+ else if( "blue4" == color_name )
+ {
+ color->setVec(LLColor4::blue4);
+ }
+ else if( "blue5" == color_name )
+ {
+ color->setVec(LLColor4::blue5);
+ }
+ else if( "blue6" == color_name )
+ {
+ color->setVec(LLColor4::blue6);
+ }
+ else if( "black" == color_name )
+ {
+ color->setVec(LLColor4::black);
+ }
+ else if( "white" == color_name )
+ {
+ color->setVec(LLColor4::white);
+ }
+ else if( "yellow" == color_name )
+ {
+ color->setVec(LLColor4::yellow);
+ }
+ else if( "yellow1" == color_name )
+ {
+ color->setVec(LLColor4::yellow1);
+ }
+ else if( "yellow2" == color_name )
+ {
+ color->setVec(LLColor4::yellow2);
+ }
+ else if( "yellow3" == color_name )
+ {
+ color->setVec(LLColor4::yellow3);
+ }
+ else if( "yellow4" == color_name )
+ {
+ color->setVec(LLColor4::yellow4);
+ }
+ else if( "yellow5" == color_name )
+ {
+ color->setVec(LLColor4::yellow5);
+ }
+ else if( "yellow6" == color_name )
+ {
+ color->setVec(LLColor4::yellow6);
+ }
+ else if( "magenta" == color_name )
+ {
+ color->setVec(LLColor4::magenta);
+ }
+ else if( "magenta1" == color_name )
+ {
+ color->setVec(LLColor4::magenta1);
+ }
+ else if( "magenta2" == color_name )
+ {
+ color->setVec(LLColor4::magenta2);
+ }
+ else if( "magenta3" == color_name )
+ {
+ color->setVec(LLColor4::magenta3);
+ }
+ else if( "magenta4" == color_name )
+ {
+ color->setVec(LLColor4::magenta4);
+ }
+ else if( "purple" == color_name )
+ {
+ color->setVec(LLColor4::purple);
+ }
+ else if( "purple1" == color_name )
+ {
+ color->setVec(LLColor4::purple1);
+ }
+ else if( "purple2" == color_name )
+ {
+ color->setVec(LLColor4::purple2);
+ }
+ else if( "purple3" == color_name )
+ {
+ color->setVec(LLColor4::purple3);
+ }
+ else if( "purple4" == color_name )
+ {
+ color->setVec(LLColor4::purple4);
+ }
+ else if( "purple5" == color_name )
+ {
+ color->setVec(LLColor4::purple5);
+ }
+ else if( "purple6" == color_name )
+ {
+ color->setVec(LLColor4::purple6);
+ }
+ else if( "pink" == color_name )
+ {
+ color->setVec(LLColor4::pink);
+ }
+ else if( "pink1" == color_name )
+ {
+ color->setVec(LLColor4::pink1);
+ }
+ else if( "pink2" == color_name )
+ {
+ color->setVec(LLColor4::pink2);
+ }
+ else if( "cyan" == color_name )
+ {
+ color->setVec(LLColor4::cyan);
+ }
+ else if( "cyan1" == color_name )
+ {
+ color->setVec(LLColor4::cyan1);
+ }
+ else if( "cyan2" == color_name )
+ {
+ color->setVec(LLColor4::cyan2);
+ }
+ else if( "cyan3" == color_name )
+ {
+ color->setVec(LLColor4::cyan3);
+ }
+ else if( "cyan4" == color_name )
+ {
+ color->setVec(LLColor4::cyan4);
+ }
+ else if( "cyan5" == color_name )
+ {
+ color->setVec(LLColor4::cyan5);
+ }
+ else if( "cyan6" == color_name )
+ {
+ color->setVec(LLColor4::cyan6);
+ }
+ else if( "smoke" == color_name )
+ {
+ color->setVec(LLColor4::smoke);
+ }
+ else if( "grey" == color_name )
+ {
+ color->setVec(LLColor4::grey);
+ }
+ else if( "grey1" == color_name )
+ {
+ color->setVec(LLColor4::grey1);
+ }
+ else if( "grey2" == color_name )
+ {
+ color->setVec(LLColor4::grey2);
+ }
+ else if( "grey3" == color_name )
+ {
+ color->setVec(LLColor4::grey3);
+ }
+ else if( "grey4" == color_name )
+ {
+ color->setVec(LLColor4::grey4);
+ }
+ else if( "orange" == color_name )
+ {
+ color->setVec(LLColor4::orange);
+ }
+ else if( "orange1" == color_name )
+ {
+ color->setVec(LLColor4::orange1);
+ }
+ else if( "orange2" == color_name )
+ {
+ color->setVec(LLColor4::orange2);
+ }
+ else if( "orange3" == color_name )
+ {
+ color->setVec(LLColor4::orange3);
+ }
+ else if( "orange4" == color_name )
+ {
+ color->setVec(LLColor4::orange4);
+ }
+ else if( "orange5" == color_name )
+ {
+ color->setVec(LLColor4::orange5);
+ }
+ else if( "orange6" == color_name )
+ {
+ color->setVec(LLColor4::orange6);
+ }
+ else if ( "clear" == color_name )
+ {
+ color->setVec(0.f, 0.f, 0.f, 0.f);
+ }
+ else
+ {
+ llwarns << "invalid color " << color_name << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+// static
+BOOL LLColor4::parseColor4(const char* buf, LLColor4* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ LLColor4 v;
+ S32 count = sscanf( buf, "%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, "%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
new file mode 100644
index 0000000000..8fe5846e26
--- /dev/null
+++ b/indra/llmath/v4color.h
@@ -0,0 +1,539 @@
+/**
+ * @file v4color.h
+ * @brief LLColor4 class header file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V4COLOR_H
+#define LL_V4COLOR_H
+
+#include "llerror.h"
+//#include "vmath.h"
+#include "llmath.h"
+#include "llsd.h"
+
+class LLColor3;
+class LLColor4U;
+
+// 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(U32 clr); // Initializes LLColor4 to (r=clr>>24, etc))
+ LLColor4(const F32 *vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], 1)
+ LLColor4(const LLColor3 &vec, F32 a = 1.f); // Initializes LLColor4 to (vec, a)
+ LLColor4(const LLSD& sd);
+ explicit LLColor4(const LLColor4U& color4u); // "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)
+ {
+ mV[0] = (F32) sd[0].asReal();
+ mV[1] = (F32) sd[1].asReal();
+ mV[2] = (F32) sd[2].asReal();
+ mV[3] = (F32) sd[3].asReal();
+ }
+
+ 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); // Sets LLColor4 to (r, g, b, a)
+ const LLColor4& setVec(F32 r, F32 g, F32 b); // Sets LLColor4 to (r, g, b) (no change in a)
+ const LLColor4& setVec(const LLColor4 &vec); // Sets LLColor4 to vec
+ const LLColor4& setVec(const LLColor3 &vec); // Sets LLColor4 to LLColor3 vec (no change in alpha)
+ const LLColor4& setVec(const LLColor3 &vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified
+ const LLColor4& setVec(const F32 *vec); // Sets LLColor4 to vec
+ const LLColor4& setVec(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled.
+
+
+ const LLColor4& setAlpha(F32 a);
+
+ F32 magVec() const; // Returns magnitude of LLColor4
+ F32 magVecSquared() const; // Returns magnitude squared of LLColor4
+ F32 normVec(); // Normalizes and returns the magnitude of LLColor4
+ const 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
+ const LLColor4& operator=(const LLSD& sd);
+
+ 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 a * b
+ friend LLColor4 operator*(const LLColor4 &a, F32 k); // Return rgb times scaler 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 const 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 char* buf, LLColor4* color);
+ static BOOL parseColor4(const char* 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 = 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::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);
+}
+
+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);
+}
+
+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);
+}
+
+
+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::magVec(void) const
+{
+ return fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+}
+
+inline F32 LLColor4::magVecSquared(void) const
+{
+ return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
+}
+
+inline F32 LLColor4::normVec(void)
+{
+ F32 mag = fsqrtf(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*(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.magVec());
+}
+
+inline F32 distVec_squared(const LLColor4 &a, const LLColor4 &b)
+{
+ LLColor4 vec = a - b;
+ return (vec.magVecSquared());
+}
+
+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);
+}
+
+
+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;
+ }
+}
+
+inline const LLColor4& LLColor4::operator=(const LLSD& sd)
+{
+ mV[0] = (F32) sd[0].asReal();
+ mV[1] = (F32) sd[1].asReal();
+ mV[2] = (F32) sd[2].asReal();
+ mV[3] = (F32) sd[3].asReal();
+
+ return *this;
+}
+
+#endif
+
diff --git a/indra/llmath/v4coloru.cpp b/indra/llmath/v4coloru.cpp
new file mode 100644
index 0000000000..e5a17e1a97
--- /dev/null
+++ b/indra/llmath/v4coloru.cpp
@@ -0,0 +1,102 @@
+/**
+ * @file v4coloru.cpp
+ * @brief LLColor4U class implementation.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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 char* buf, LLColor4U* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ U32 v[4];
+ S32 count = sscanf( buf, "%u, %u, %u, %u", v + 0, v + 1, v + 2, v + 3 );
+ if (1 == count )
+ {
+ // try this format
+ count = sscanf( buf, "%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->setVec( 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
new file mode 100644
index 0000000000..e9cc9b2ab3
--- /dev/null
+++ b/indra/llmath/v4coloru.h
@@ -0,0 +1,501 @@
+/**
+ * @file v4coloru.h
+ * @brief The LLColor4U class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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:
+
+ union
+ {
+ U8 mV[LENGTHOFCOLOR4U];
+ U32 mAll;
+ LLColor4* mSources;
+ LLColor4U* mSourcesU;
+ };
+
+
+ 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)
+ 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();
+ }
+
+ const LLColor4U& operator=(const LLSD& sd)
+ {
+ setValue(sd);
+ return *this;
+ }
+
+ 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& setVec(U8 r, U8 g, U8 b, U8 a); // Sets LLColor4U to (r, g, b, a)
+ const LLColor4U& setVec(U8 r, U8 g, U8 b); // Sets LLColor4U to (r, g, b) (no change in a)
+ const LLColor4U& setVec(const LLColor4U &vec); // Sets LLColor4U to vec
+ const LLColor4U& setVec(const U8 *vec); // Sets LLColor4U to vec
+
+ const LLColor4U& setAlpha(U8 a);
+
+ F32 magVec() const; // Returns magnitude of LLColor4U
+ F32 magVecSquared() 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
+ const LLColor4U& combine();
+
+ inline void setVecScaleClamp(const LLColor3 &color);
+ inline void setVecScaleClamp(const LLColor4 &color);
+
+ static BOOL parseColor4U(const char* buf, LLColor4U* value);
+
+ 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::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);
+}
+
+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);
+}
+
+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);
+}
+
+/*
+inline const LLColor4U& LLColor4U::setVec(const LLColor4 &vec)
+{
+ mV[VX] = (U8) (llmin(1.f, vec.mV[VX]) * 255.f);
+ mV[VY] = (U8) (llmin(1.f, vec.mV[VY]) * 255.f);
+ mV[VZ] = (U8) (llmin(1.f, vec.mV[VZ]) * 255.f);
+ mV[VW] = (U8) (llmin(1.f, vec.mV[VW]) * 255.f);
+ return (*this);
+}
+*/
+
+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
+// bookmark
+
+inline F32 LLColor4U::magVec(void) const
+{
+ return fsqrtf( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] );
+}
+
+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)llround(mV[VX] * k),
+ (U8)llround(mV[VY] * k),
+ (U8)llround(mV[VZ] * k),
+ (U8)llround(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.magVec());
+}
+
+inline F32 distVec_squared(const LLColor4U &a, const LLColor4U &b)
+{
+ LLColor4U vec = a - b;
+ return (vec.magVecSquared());
+}
+
+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 = llround(color.mV[0] * color_scale_factor);
+ if (r > MAX_COLOR)
+ {
+ r = MAX_COLOR;
+ }
+ else if (r < 0)
+ {
+ r = 0;
+ }
+ mV[0] = r;
+
+ S32 g = llround(color.mV[1] * color_scale_factor);
+ if (g > MAX_COLOR)
+ {
+ g = MAX_COLOR;
+ }
+ else if (g < 0)
+ {
+ g = 0;
+ }
+ mV[1] = g;
+
+ S32 b = llround(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 = llround(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 = llround(color.mV[0] * color_scale_factor);
+ if (r > MAX_COLOR)
+ {
+ r = MAX_COLOR;
+ }
+ else
+ if (r < 0)
+ {
+ r = 0;
+ }
+ mV[0] = r;
+
+ S32 g = llround(color.mV[1] * color_scale_factor);
+ if (g > MAX_COLOR)
+ {
+ g = MAX_COLOR;
+ }
+ else
+ if (g < 0)
+ {
+ g = 0;
+ }
+ mV[1] = g;
+
+ S32 b = llround(color.mV[2] * color_scale_factor);
+ if (b > MAX_COLOR)
+ {
+ b = MAX_COLOR;
+ }
+ if (b < 0)
+ {
+ b = 0;
+ }
+ mV[2] = b;
+
+ mV[3] = 255;
+}
+
+
+#endif
+
diff --git a/indra/llmath/v4math.cpp b/indra/llmath/v4math.cpp
new file mode 100644
index 0000000000..16fbdd24a1
--- /dev/null
+++ b/indra/llmath/v4math.cpp
@@ -0,0 +1,124 @@
+/**
+ * @file v4math.cpp
+ * @brief LLVector4 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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(F32 angle, const LLVector4 &vec)
+{
+ if ( !vec.isExactlyZero() && angle )
+ {
+ *this = *this * LLMatrix4(angle, vec);
+ }
+ return *this;
+}
+
+const LLVector4& LLVector4::rotVec(F32 angle, F32 x, F32 y, F32 z)
+{
+ LLVector3 vec(x, y, z);
+ if ( !vec.isExactlyZero() && angle )
+ {
+ *this = *this * LLMatrix4(angle, vec);
+ }
+ return *this;
+}
+*/
+
+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.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 LLVector4 &a, const LLVector4 &b, F32 epsilon)
+{
+ LLVector4 an = a;
+ LLVector4 bn = b;
+ an.normVec();
+ bn.normVec();
+ 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
new file mode 100644
index 0000000000..abdc66e0b1
--- /dev/null
+++ b/indra/llmath/v4math.h
@@ -0,0 +1,385 @@
+/**
+ * @file v4math.h
+ * @brief LLVector4 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V4MATH_H
+#define LL_V4MATH_H
+
+#include "llerror.h"
+#include "llmath.h"
+#include "v3math.h"
+
+class LLMatrix3;
+class LLMatrix4;
+class LLQuaternion;
+
+// LLVector4 = |x y z w|
+
+static const U32 LENGTHOFVECTOR4 = 4;
+
+#if LL_WINDOWS
+__declspec( align(16) )
+#endif
+
+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], 1)
+ explicit LLVector4(const LLVector3 &vec); // Initializes LLVector4 to (vec, 1)
+ explicit LLVector4(const LLVector3 &vec, F32 w); // Initializes LLVector4 to (vec, w)
+ 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;
+ }
+
+ inline BOOL isFinite() const; // checks to see if all values of LLVector3 are finite
+
+ inline void clearVec(); // Clears LLVector4 to (0, 0, 0, 1)
+ inline void zeroVec(); // zero LLVector4 to (0, 0, 0, 0)
+ inline void setVec(F32 x, F32 y, F32 z); // Sets LLVector4 to (x, y, z, 1)
+ inline void setVec(F32 x, F32 y, F32 z, F32 w); // Sets LLVector4 to (x, y, z, w)
+ inline void setVec(const LLVector4 &vec); // Sets LLVector4 to vec
+ inline void setVec(const LLVector3 &vec, F32 w = 1.f); // Sets LLVector4 to LLVector3 vec
+ inline void setVec(const F32 *vec); // Sets LLVector4 to vec
+
+ F32 magVec() const; // Returns magnitude of LLVector4
+ F32 magVecSquared() const; // Returns magnitude squared of LLVector4
+ F32 normVec(); // Normalizes and returns the magnitude of LLVector4
+
+ // 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(F32 angle, const LLVector4 &vec); // Rotates about vec by angle radians
+ const LLVector4& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians
+ 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
+}
+#if LL_DARWIN
+__attribute__ ((aligned (16)))
+#endif
+;
+
+
+// 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 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 BOOL LLVector4::isFinite() const
+{
+ return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]) && llfinite(mV[VW]));
+}
+
+// Clear and Assignment Functions
+
+inline void LLVector4::clearVec(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+ mV[VZ] = 0.f;
+ mV[VW] = 1.f;
+}
+
+inline void LLVector4::zeroVec(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+ mV[VZ] = 0.f;
+ mV[VW] = 0.f;
+}
+
+inline void LLVector4::setVec(F32 x, F32 y, F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+ mV[VW] = 1.f;
+}
+
+inline void LLVector4::setVec(F32 x, F32 y, F32 z, F32 w)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+ mV[VW] = w;
+}
+
+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];
+}
+
+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;
+}
+
+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::magVec(void) const
+{
+ return fsqrtf(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]));
+}
+
+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.magVec());
+}
+
+inline F32 dist_vec_squared(const LLVector4 &a, const LLVector4 &b)
+{
+ LLVector4 vec = a - b;
+ return (vec.magVecSquared());
+}
+
+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::normVec(void)
+{
+ F32 mag = fsqrtf(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);
+}
+
+
+#endif
+
diff --git a/indra/llmath/xform.cpp b/indra/llmath/xform.cpp
new file mode 100644
index 0000000000..715b993b2b
--- /dev/null
+++ b/indra/llmath/xform.cpp
@@ -0,0 +1,96 @@
+/**
+ * @file xform.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "xform.h"
+
+LLXform::LLXform()
+{
+ init();
+}
+
+LLXform::~LLXform()
+{
+}
+
+
+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
new file mode 100644
index 0000000000..bd4bc74c79
--- /dev/null
+++ b/indra/llmath/xform.h
@@ -0,0 +1,269 @@
+/**
+ * @file xform.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_XFORM_H
+#define LL_XFORM_H
+
+#include "v3math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+
+#define CHECK_FOR_FINITE
+
+
+const F32 MAX_OBJECT_Z = 768.f;
+const F32 MIN_OBJECT_Z = -256.f;
+const F32 MIN_OBJECT_SCALE = 0.01f;
+const F32 MAX_OBJECT_SCALE = 10.f;
+
+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);
+
+ 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.identity();
+ 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)
+ {
+ llwarns << "LLXform::setParent Creating loop when setting parent!" << llendl;
+ return FALSE;
+ }
+ cur_par = cur_par->mParent;
+ }
+ }
+ mParent = parent;
+ return TRUE;
+}
+
+#ifdef CHECK_FOR_FINITE
+void LLXform::setPosition(const LLVector3& pos)
+{
+ setChanged(TRANSLATED);
+ if (pos.isFinite())
+ mPosition = pos;
+ else
+ llerror("Non Finite in LLXform::setPosition(LLVector3)", 0);
+}
+
+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
+ llerror("Non Finite in LLXform::setPosition(F32,F32,F32)", 0);
+}
+
+void LLXform::setPositionX(const F32 x)
+{
+ setChanged(TRANSLATED);
+ if (llfinite(x))
+ mPosition.mV[VX] = x;
+ else
+ llerror("Non Finite in LLXform::setPositionX", 0);
+}
+
+void LLXform::setPositionY(const F32 y)
+{
+ setChanged(TRANSLATED);
+ if (llfinite(y))
+ mPosition.mV[VY] = y;
+ else
+ llerror("Non Finite in LLXform::setPositionY", 0);
+}
+
+void LLXform::setPositionZ(const F32 z)
+{
+ setChanged(TRANSLATED);
+ if (llfinite(z))
+ mPosition.mV[VZ] = z;
+ else
+ llerror("Non Finite in LLXform::setPositionZ", 0);
+}
+
+void LLXform::addPosition(const LLVector3& pos)
+{
+ setChanged(TRANSLATED);
+ if (pos.isFinite())
+ mPosition += pos;
+ else
+ llerror("Non Finite in LLXform::addPosition", 0);
+}
+
+void LLXform::setScale(const LLVector3& scale)
+{
+ setChanged(SCALED);
+ if (scale.isFinite())
+ mScale = scale;
+ else
+ llerror("Non Finite in LLXform::setScale", 0);
+}
+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
+ llerror("Non Finite in LLXform::setScale", 0);
+}
+void LLXform::setRotation(const LLQuaternion& rot)
+{
+ setChanged(ROTATED);
+ if (rot.isFinite())
+ mRotation = rot;
+ else
+ llerror("Non Finite in LLXform::setRotation", 0);
+}
+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
+ {
+ llerror("Non Finite in LLXform::setRotation", 0);
+ }
+}
+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
+ {
+ llerror("Non Finite in LLXform::setRotation", 0);
+ }
+}
+
+#endif
+
+#endif