From 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:44:46 -0500 Subject: Second Life viewer sources 1.13.2.12 --- linden/indra/llmath/camera.h | 28 + linden/indra/llmath/coordframe.h | 28 + linden/indra/llmath/files.lst | 23 + linden/indra/llmath/llbboxlocal.cpp | 56 + linden/indra/llmath/llbboxlocal.h | 69 + linden/indra/llmath/llcamera.cpp | 610 +++++ linden/indra/llmath/llcamera.h | 188 ++ linden/indra/llmath/llcoord.h | 97 + linden/indra/llmath/llcoordframe.cpp | 748 ++++++ linden/indra/llmath/llcoordframe.h | 175 ++ linden/indra/llmath/llcrc.cpp | 206 ++ linden/indra/llmath/llcrc.h | 69 + linden/indra/llmath/llinterp.h | 419 ++++ linden/indra/llmath/llmath.h | 421 ++++ linden/indra/llmath/llmath.vcproj | 322 +++ linden/indra/llmath/llmd5.cpp | 574 +++++ linden/indra/llmath/llmd5.h | 145 ++ linden/indra/llmath/lloctree.h | 712 ++++++ linden/indra/llmath/llperlin.cpp | 295 +++ linden/indra/llmath/llperlin.h | 47 + linden/indra/llmath/llplane.h | 68 + linden/indra/llmath/llquantize.h | 122 + linden/indra/llmath/llquaternion.cpp | 849 +++++++ linden/indra/llmath/llquaternion.h | 461 ++++ linden/indra/llmath/llrand.cpp | 74 + linden/indra/llmath/llrand.h | 88 + linden/indra/llmath/llrect.cpp | 29 + linden/indra/llmath/llrect.h | 289 +++ linden/indra/llmath/lltreenode.h | 180 ++ linden/indra/llmath/lluuid.cpp | 864 +++++++ linden/indra/llmath/lluuid.h | 321 +++ linden/indra/llmath/llvolume.cpp | 4576 ++++++++++++++++++++++++++++++++++ linden/indra/llmath/llvolume.h | 901 +++++++ linden/indra/llmath/llvolumemgr.cpp | 314 +++ linden/indra/llmath/llvolumemgr.h | 101 + linden/indra/llmath/m3math.cpp | 549 ++++ linden/indra/llmath/m3math.h | 217 ++ linden/indra/llmath/m4math.cpp | 813 ++++++ linden/indra/llmath/m4math.h | 384 +++ linden/indra/llmath/raytrace.cpp | 1275 ++++++++++ linden/indra/llmath/raytrace.h | 233 ++ linden/indra/llmath/v2math.cpp | 111 + linden/indra/llmath/v2math.h | 326 +++ linden/indra/llmath/v3color.cpp | 63 + linden/indra/llmath/v3color.h | 381 +++ linden/indra/llmath/v3dmath.cpp | 148 ++ linden/indra/llmath/v3dmath.h | 437 ++++ linden/indra/llmath/v3math.cpp | 232 ++ linden/indra/llmath/v3math.h | 465 ++++ linden/indra/llmath/v4color.cpp | 580 +++++ linden/indra/llmath/v4color.h | 558 +++++ linden/indra/llmath/v4coloru.cpp | 121 + linden/indra/llmath/v4coloru.h | 520 ++++ linden/indra/llmath/v4math.cpp | 143 ++ linden/indra/llmath/v4math.h | 404 +++ linden/indra/llmath/xform.cpp | 115 + linden/indra/llmath/xform.h | 288 +++ 57 files changed, 22832 insertions(+) create mode 100644 linden/indra/llmath/camera.h create mode 100644 linden/indra/llmath/coordframe.h create mode 100644 linden/indra/llmath/files.lst create mode 100644 linden/indra/llmath/llbboxlocal.cpp create mode 100644 linden/indra/llmath/llbboxlocal.h create mode 100644 linden/indra/llmath/llcamera.cpp create mode 100644 linden/indra/llmath/llcamera.h create mode 100644 linden/indra/llmath/llcoord.h create mode 100644 linden/indra/llmath/llcoordframe.cpp create mode 100644 linden/indra/llmath/llcoordframe.h create mode 100644 linden/indra/llmath/llcrc.cpp create mode 100644 linden/indra/llmath/llcrc.h create mode 100644 linden/indra/llmath/llinterp.h create mode 100644 linden/indra/llmath/llmath.h create mode 100644 linden/indra/llmath/llmath.vcproj create mode 100644 linden/indra/llmath/llmd5.cpp create mode 100644 linden/indra/llmath/llmd5.h create mode 100644 linden/indra/llmath/lloctree.h create mode 100644 linden/indra/llmath/llperlin.cpp create mode 100644 linden/indra/llmath/llperlin.h create mode 100644 linden/indra/llmath/llplane.h create mode 100644 linden/indra/llmath/llquantize.h create mode 100644 linden/indra/llmath/llquaternion.cpp create mode 100644 linden/indra/llmath/llquaternion.h create mode 100644 linden/indra/llmath/llrand.cpp create mode 100644 linden/indra/llmath/llrand.h create mode 100644 linden/indra/llmath/llrect.cpp create mode 100644 linden/indra/llmath/llrect.h create mode 100644 linden/indra/llmath/lltreenode.h create mode 100644 linden/indra/llmath/lluuid.cpp create mode 100644 linden/indra/llmath/lluuid.h create mode 100644 linden/indra/llmath/llvolume.cpp create mode 100644 linden/indra/llmath/llvolume.h create mode 100644 linden/indra/llmath/llvolumemgr.cpp create mode 100644 linden/indra/llmath/llvolumemgr.h create mode 100644 linden/indra/llmath/m3math.cpp create mode 100644 linden/indra/llmath/m3math.h create mode 100644 linden/indra/llmath/m4math.cpp create mode 100644 linden/indra/llmath/m4math.h create mode 100644 linden/indra/llmath/raytrace.cpp create mode 100644 linden/indra/llmath/raytrace.h create mode 100644 linden/indra/llmath/v2math.cpp create mode 100644 linden/indra/llmath/v2math.h create mode 100644 linden/indra/llmath/v3color.cpp create mode 100644 linden/indra/llmath/v3color.h create mode 100644 linden/indra/llmath/v3dmath.cpp create mode 100644 linden/indra/llmath/v3dmath.h create mode 100644 linden/indra/llmath/v3math.cpp create mode 100644 linden/indra/llmath/v3math.h create mode 100644 linden/indra/llmath/v4color.cpp create mode 100644 linden/indra/llmath/v4color.h create mode 100644 linden/indra/llmath/v4coloru.cpp create mode 100644 linden/indra/llmath/v4coloru.h create mode 100644 linden/indra/llmath/v4math.cpp create mode 100644 linden/indra/llmath/v4math.h create mode 100644 linden/indra/llmath/xform.cpp create mode 100644 linden/indra/llmath/xform.h (limited to 'linden/indra/llmath') diff --git a/linden/indra/llmath/camera.h b/linden/indra/llmath/camera.h new file mode 100644 index 0000000..e9963e9 --- /dev/null +++ b/linden/indra/llmath/camera.h @@ -0,0 +1,28 @@ +/** + * @file camera.h + * @brief Legacy wrapper header. + * + * Copyright (c) 2000-2007 Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "llcamera.h" diff --git a/linden/indra/llmath/coordframe.h b/linden/indra/llmath/coordframe.h new file mode 100644 index 0000000..81d630e --- /dev/null +++ b/linden/indra/llmath/coordframe.h @@ -0,0 +1,28 @@ +/** + * @file coordframe.h + * @brief Legacy wrapper header. + * + * Copyright (c) 2000-2007 Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "llcoordframe.h" diff --git a/linden/indra/llmath/files.lst b/linden/indra/llmath/files.lst new file mode 100644 index 0000000..cfd635a --- /dev/null +++ b/linden/indra/llmath/files.lst @@ -0,0 +1,23 @@ +llmath/llbboxlocal.cpp +llmath/llcamera.cpp +llmath/llcoordframe.cpp +llmath/llcrc.cpp +llmath/llmd5.cpp +llmath/llperlin.cpp +llmath/llquaternion.cpp +llmath/llrand.cpp +llmath/llrect.cpp +llmath/lluuid.cpp +llmath/llvolume.cpp +llmath/llvolumemgr.cpp +llmath/m3math.cpp +llmath/m4math.cpp +llmath/raytrace.cpp +llmath/v2math.cpp +llmath/v3color.cpp +llmath/v3dmath.cpp +llmath/v3math.cpp +llmath/v4color.cpp +llmath/v4coloru.cpp +llmath/v4math.cpp +llmath/xform.cpp diff --git a/linden/indra/llmath/llbboxlocal.cpp b/linden/indra/llmath/llbboxlocal.cpp new file mode 100644 index 0000000..b197241 --- /dev/null +++ b/linden/indra/llmath/llbboxlocal.cpp @@ -0,0 +1,56 @@ +/** + * @file llbboxlocal.cpp + * @brief General purpose bounding box class (Not axis aligned). + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/llbboxlocal.h b/linden/indra/llmath/llbboxlocal.h new file mode 100644 index 0000000..cb71a4f --- /dev/null +++ b/linden/indra/llmath/llbboxlocal.h @@ -0,0 +1,69 @@ +/** + * @file llbboxlocal.h + * @brief General purpose bounding box class. + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/llcamera.cpp b/linden/indra/llmath/llcamera.cpp new file mode 100644 index 0000000..13666ba --- /dev/null +++ b/linden/indra/llmath/llcamera.cpp @@ -0,0 +1,610 @@ +/** + * @file llcamera.cpp + * @brief Implementation of the LLCamera class. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 ¢er, 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 ¢er, 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 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 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/linden/indra/llmath/llcamera.h b/linden/indra/llmath/llcamera.h new file mode 100644 index 0000000..82b6130 --- /dev/null +++ b/linden/indra/llmath/llcamera.h @@ -0,0 +1,188 @@ +/** + * @file llcamera.h + * @brief Header file for the LLCamera class. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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< +// (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/linden/indra/llmath/llcoordframe.h b/linden/indra/llmath/llcoordframe.h new file mode 100644 index 0000000..d15f3e5 --- /dev/null +++ b/linden/indra/llmath/llcoordframe.h @@ -0,0 +1,175 @@ +/** + * @file llcoordframe.h + * @brief LLCoordFrame class header file. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/llcrc.cpp b/linden/indra/llmath/llcrc.cpp new file mode 100644 index 0000000..4194432 --- /dev/null +++ b/linden/indra/llmath/llcrc.cpp @@ -0,0 +1,206 @@ +/** + * @file llcrc.cpp + * @brief implementation of the crc class. + * + * Copyright (c) 2002-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" + +#include "llcrc.h" +#include "llerror.h" + +#include + +/* Copyright (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction.*/ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* 1. The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ +/* be they sixteen or thirty-two bits wide. You simply choose the */ +/* appropriate table. Alternatively, because the table can be */ +/* generated at runtime, you can start by generating the table for */ +/* the polynomial in question and use exactly the same "updcrc", */ +/* if your application needn't simultaneously handle two CRC */ +/* polynomials. (Note, however, that XMODEM is strange.) */ +/* */ +/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ +/* of course, 32-bit entries work OK if the high 16 bits are zero. */ +/* */ +/* 4. The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +///---------------------------------------------------------------------------- +/// Local function declarations, constants, enums, and typedefs +///---------------------------------------------------------------------------- + +#define UPDC32(octet,crc) (crc_32_tab[((crc) \ + ^ ((U8)octet)) & 0xff] ^ ((crc) >> 8)) + + +static U32 crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ +0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, +0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, +0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, +0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, +0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, +0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, +0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, +0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, +0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, +0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, +0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, +0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, +0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, +0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, +0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, +0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, +0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, +0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, +0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, +0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, +0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, +0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, +0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, +0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, +0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, +0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, +0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, +0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, +0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, +0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, +0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, +0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, +0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, +0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, +0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, +0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, +0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, +0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, +0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, +0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, +0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, +0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, +0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + + +///---------------------------------------------------------------------------- +/// Class llcrc +///---------------------------------------------------------------------------- + +// Default constructor +LLCRC::LLCRC() : mCurrent(0xffffffff) +{ +} + + +U32 LLCRC::getCRC() const +{ + return ~mCurrent; +} + +void LLCRC::update(U8 next_byte) +{ + mCurrent = UPDC32(next_byte, mCurrent); +} + +void LLCRC::update(const U8* buffer, U32 buffer_size) +{ + for ( ; buffer_size; --buffer_size, ++buffer) + { + mCurrent = UPDC32(*buffer, mCurrent); + } +} + +void LLCRC::update(const char *filename) +{ + FILE *fp = LLFile::fopen(filename, "rb"); + + if (fp) + { + fseek(fp, 0, SEEK_END); + U32 size = ftell(fp); + + fseek(fp, 0, SEEK_SET); + + U8 *data = new U8[size]; + fread(data, size, 1, fp); + fclose(fp); + + update(data, size); + delete[] data; + } +} + + +#ifdef _DEBUG +BOOL LLCRC::testHarness() +{ + const S32 TEST_BUFFER_SIZE = 16; + const char TEST_BUFFER[TEST_BUFFER_SIZE] = "hello &#$)$&Nd0"; + LLCRC c1, c2; + c1.update((U8*)TEST_BUFFER, TEST_BUFFER_SIZE - 1); + char* rh = (char*)TEST_BUFFER; + while(*rh != '\0') + { + c2.update(*rh); + ++rh; + } + return(c1.getCRC() == c2.getCRC()); +} +#endif + + + +///---------------------------------------------------------------------------- +/// Local function definitions +///---------------------------------------------------------------------------- diff --git a/linden/indra/llmath/llcrc.h b/linden/indra/llmath/llcrc.h new file mode 100644 index 0000000..7c267d6 --- /dev/null +++ b/linden/indra/llmath/llcrc.h @@ -0,0 +1,69 @@ +/** + * @file llcrc.h + * @brief LLCRC class header file. + * + * Copyright (c) 2002-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLCRC_H +#define LL_LLCRC_H + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class llcrc +// +// Simple 32 bit crc. To use, instantiate an LLCRC instance and feed +// it the bytes you want to check. It will update the internal crc as +// you go, and you can qery it at the end. As a horribly inefficient +// example (don't try this at work kids): +// +// LLCRC crc; +// FILE* fp = LLFile::fopen(filename,"rb"); +// while(!feof(fp)) { +// crc.update(fgetc(fp)); +// } +// fclose(fp); +// llinfos << "File crc: " << crc.getCRC() << llendl; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLCRC +{ +protected: + U32 mCurrent; + +public: + LLCRC(); + + U32 getCRC() const; + void update(U8 next_byte); + void update(const U8* buffer, U32 buffer_size); + void update(const char *filename); + +#ifdef _DEBUG + // This function runs tests to make sure the crc is + // working. Returns TRUE if it is. + static BOOL testHarness(); +#endif +}; + + +#endif // LL_LLCRC_H diff --git a/linden/indra/llmath/llinterp.h b/linden/indra/llmath/llinterp.h new file mode 100644 index 0000000..3cc0b18 --- /dev/null +++ b/linden/indra/llmath/llinterp.h @@ -0,0 +1,419 @@ +/** + * @file llinterp.h + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 +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 +class LLInterpLinear : public LLInterp +{ +public: + /*virtual*/ void start(); + void update(const F32 time); + F32 getCurFrac() const; +protected: + F32 mCurFrac; +}; + +template +class LLInterpExp : public LLInterpLinear +{ +public: + void update(const F32 time); +protected: +}; + +template +class LLInterpAttractor : public LLInterp +{ +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 +class LLInterpFunc : public LLInterp +{ +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 +LLInterp::LLInterp() +{ + mStartTime = 0.f; + mEndTime = 1.f; + mDuration = 1.f; + mCurTime = 0.f; + mDone = FALSE; + mActive = FALSE; +} + +template +void LLInterp::setStartVal(const Type &start_val) +{ + mStartVal = start_val; +} + +template +void LLInterp::start() +{ + mCurVal = mStartVal; + mCurTime = mStartTime; + mDone = FALSE; + mActive = FALSE; +} + +template +const Type &LLInterp::getStartVal() const +{ + return mStartVal; +} + +template +void LLInterp::setEndVal(const Type &end_val) +{ + mEndVal = end_val; +} + +template +const Type &LLInterp::getEndVal() const +{ + return mEndVal; +} + +template +const Type &LLInterp::getCurVal() const +{ + return mCurVal; +} + + +template +void LLInterp::setStartTime(const F32 start_time) +{ + mStartTime = start_time; + mDuration = mEndTime - mStartTime; +} + +template +F32 LLInterp::getStartTime() const +{ + return mStartTime; +} + + +template +void LLInterp::setEndTime(const F32 end_time) +{ + mEndTime = end_time; + mDuration = mEndTime - mStartTime; +} + + +template +F32 LLInterp::getEndTime() const +{ + return mEndTime; +} + + +template +BOOL LLInterp::isDone() const +{ + return mDone; +} + +template +BOOL LLInterp::isActive() const +{ + return mActive; +} + +////////////////////////////// +// +// LLInterpLinear derived class implementation. +// +template +void LLInterpLinear::start() +{ + LLInterp::start(); + mCurFrac = 0.f; +} + +template +void LLInterpLinear::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 +F32 LLInterpLinear::getCurFrac() const +{ + return mCurFrac; +} + + +////////////////////////////// +// +// LLInterpAttractor derived class implementation. +// + + +template +LLInterpAttractor::LLInterpAttractor() : LLInterp() +{ + mForce = 0.1f; + mVelocity *= 0.f; + mStartVel *= 0.f; +} + +template +void LLInterpAttractor::start() +{ + LLInterp::start(); + mVelocity = mStartVel; +} + + +template +void LLInterpAttractor::setStartVel(const Type &vel) +{ + mStartVel = vel; +} + +template +void LLInterpAttractor::setForce(const F32 force) +{ + mForce = force; +} + +template +void LLInterpAttractor::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 +LLInterpFunc::LLInterpFunc() : LLInterp() +{ + mFunc = NULL; + mData = NULL; +} + +template +void LLInterpFunc::setFunc(Type (*func)(const F32, void *data), void *data) +{ + mFunc = func; + mData = data; +} + +template +void LLInterpFunc::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 +void LLInterpExp::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/linden/indra/llmath/llmath.h b/linden/indra/llmath/llmath.h new file mode 100644 index 0000000..7009557 --- /dev/null +++ b/linden/indra/llmath/llmath.h @@ -0,0 +1,421 @@ +/** + * @file llmath.h + * @brief Useful math constants and macros. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLMATH_H +#define LLMATH_H + +#include +#include +#include + +#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/linden/indra/llmath/llmath.vcproj b/linden/indra/llmath/llmath.vcproj new file mode 100644 index 0000000..9b6047d --- /dev/null +++ b/linden/indra/llmath/llmath.vcproj @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linden/indra/llmath/llmd5.cpp b/linden/indra/llmath/llmd5.cpp new file mode 100644 index 0000000..19174af --- /dev/null +++ b/linden/indra/llmath/llmd5.cpp @@ -0,0 +1,574 @@ +/** + * @file llmd5.cpp + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +// llMD5.CC - source code for the C++/object oriented translation and +// modification of MD5. +// +// Adapted to Linden Lab by Frank Filipanits, 6/25/2002 +// Fixed potential memory leak, James Cook, 6/27/2002 + +// Translation and modification (c) 1995 by Mordechai T. Abzug + +// This translation/ modification is provided "as is," without express or +// implied warranty of any kind. + +// The translator/ modifier does not claim (1) that MD5 will do what you think +// it does; (2) that this translation/ modification is accurate; or (3) that +// this software is "merchantible." (Language for this disclaimer partially +// copied from the disclaimer below). + +/* based on: + + MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + MDDRIVER.C - test driver for MD2, MD4 and MD5 + + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + + */ + + + + + +#include "linden_common.h" + +#include "llmd5.h" + +#include +#include +#include +#include + + +// LLMD5 simple initialization method + +LLMD5::LLMD5() +{ + init(); +} + + + + +// MD5 block update operation. Continues an MD5 message-digest +// operation, processing another message block, and updating the +// context. + +void LLMD5::update (const uint1 *input, const uint4 input_length) { + + uint4 input_index, buffer_index; + uint4 buffer_space; // how much space is left in buffer + + if (finalized){ // so we can't update! + std::cerr << "LLMD5::update: Can't update a finalized digest!" << std::endl; + return; + } + + // Compute number of bytes mod 64 + buffer_index = (unsigned int)((count[0] >> 3) & 0x3F); + + // Update number of bits + if ( (count[0] += ((uint4) input_length << 3))<((uint4) input_length << 3) ) + count[1]++; + + count[1] += ((uint4)input_length >> 29); + + + buffer_space = 64 - buffer_index; // how much space is left in buffer + + // Transform as many times as possible. + if (input_length >= buffer_space) { // ie. we have enough to fill the buffer + // fill the rest of the buffer and transform + memcpy (buffer + buffer_index, input, buffer_space); + transform (buffer); + + // now, transform each 64-byte piece of the input, bypassing the buffer + for (input_index = buffer_space; input_index + 63 < input_length; + input_index += 64) + transform (input+input_index); + + buffer_index = 0; // so we can buffer remaining + } + else + input_index=0; // so we can buffer the whole input + + + // and here we do the buffering: + memcpy(buffer+buffer_index, input+input_index, input_length-input_index); +} + + + +// MD5 update for files. +// Like above, except that it works on files (and uses above as a primitive.) + +void LLMD5::update(FILE *file){ + + unsigned char buffer[1024]; + int len; + + while ( (len=(int)fread(buffer, 1, 1024, file)) ) + update(buffer, len); + + fclose (file); + +} + + + + + + +// MD5 update for istreams. +// Like update for files; see above. + +void LLMD5::update(std::istream& stream){ + + unsigned char buffer[1024]; + int len; + + while (stream.good()){ + stream.read( (char*)buffer, 1024); // note that return value of read is unusable. + len=stream.gcount(); + update(buffer, len); + } + +} + + + + + + +// MD5 update for ifstreams. +// Like update for files; see above. + +void LLMD5::update(llifstream& stream){ + + unsigned char buffer[1024]; + int len; + + while (stream.good()){ + stream.read( (char*)buffer, 1024); // note that return value of read is unusable. + len=stream.gcount(); + update(buffer, len); + } + +} + + + + + + +// MD5 finalization. Ends an MD5 message-digest operation, writing the +// the message digest and zeroizing the context. + + +void LLMD5::finalize (){ + + unsigned char bits[8]; + unsigned int index, padLen; + static uint1 PADDING[64]={ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + if (finalized){ + std::cerr << "LLMD5::finalize: Already finalized this digest!" << std::endl; + return; + } + + // Save number of bits + encode (bits, count, 8); + + // Pad out to 56 mod 64. + index = (uint4) ((count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + update (PADDING, padLen); + + // Append length (before padding) + update (bits, 8); + + // Store state in digest + encode (digest, state, 16); + + // Zeroize sensitive information + memset (buffer, 0, sizeof(*buffer)); + + finalized=1; + +} + + + + +LLMD5::LLMD5(FILE *file){ + + init(); // must be called be all constructors + update(file); + finalize (); +} + + + + +LLMD5::LLMD5(std::istream& stream){ + + init(); // must called by all constructors + update (stream); + finalize(); +} + + + +LLMD5::LLMD5(llifstream& stream) +{ + init(); // must called by all constructors + update (stream); + finalize(); +} + +// Digest a string of the format ("%s:%i" % (s, number)) +LLMD5::LLMD5(const unsigned char *string, const unsigned int number) +{ + const char *colon = ":"; + char tbuf[16]; + init(); + update(string, (U32)strlen((const char *) string)); + update((const unsigned char *) colon, (U32)strlen(colon)); + sprintf(tbuf, "%i", number); + update((const unsigned char *) tbuf, (U32)strlen(tbuf)); + finalize(); +} + +// Digest a string +LLMD5::LLMD5(const unsigned char *s) +{ + init(); + update(s, (U32)strlen((const char *) s)); + finalize(); +} + +void LLMD5::raw_digest(unsigned char *s) +{ + if (!finalized) + { + std::cerr << "LLMD5::raw_digest: Can't get digest if you haven't "<< + "finalized the digest!" << std::endl; + s[0] = '\0'; + return; + } + + memcpy(s, digest, 16); + return; +} + + + +void LLMD5::hex_digest(char *s) +{ + int i; + + if (!finalized) + { + std::cerr << "LLMD5::hex_digest: Can't get digest if you haven't "<< + "finalized the digest!" <> 8) & 0xff); + output[j+2] = (uint1) ((input[i] >> 16) & 0xff); + output[j+3] = (uint1) ((input[i] >> 24) & 0xff); + } +} + + + + +// Decodes input (unsigned char) into output (UINT4). Assumes len is +// a multiple of 4. +void LLMD5::decode (uint4 *output, const uint1 *input, const uint4 len){ + + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | + (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); +} + + + + + +// ROTATE_LEFT rotates x left n bits. + +inline unsigned int LLMD5::rotate_left (uint4 x, uint4 n){ + return (x << n) | (x >> (32-n)) ; +} + + + + +// F, G, H and I are basic MD5 functions. + +inline unsigned int LLMD5::F (uint4 x, uint4 y, uint4 z){ + return (x & y) | (~x & z); +} + +inline unsigned int LLMD5::G (uint4 x, uint4 y, uint4 z){ + return (x & z) | (y & ~z); +} + +inline unsigned int LLMD5::H (uint4 x, uint4 y, uint4 z){ + return x ^ y ^ z; +} + +inline unsigned int LLMD5::I (uint4 x, uint4 y, uint4 z){ + return y ^ (x | ~z); +} + + + +// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +// Rotation is separate from addition to prevent recomputation. + + +inline void LLMD5::FF(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += F(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} + +inline void LLMD5::GG(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += G(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} + +inline void LLMD5::HH(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += H(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} + +inline void LLMD5::II(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += I(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} diff --git a/linden/indra/llmath/llmd5.h b/linden/indra/llmath/llmd5.h new file mode 100644 index 0000000..1874b45 --- /dev/null +++ b/linden/indra/llmath/llmd5.h @@ -0,0 +1,145 @@ +/** + * @file llmd5.h + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLMD5_H +#define LL_LLMD5_H + +// LLMD5.CC - source code for the C++/object oriented translation and +// modification of MD5. + +// Translation and modification (c) 1995 by Mordechai T. Abzug + +// This translation/ modification is provided "as is," without express or +// implied warranty of any kind. + +// The translator/ modifier does not claim (1) that MD5 will do what you think +// it does; (2) that this translation/ modification is accurate; or (3) that +// this software is "merchantible." (Language for this disclaimer partially +// copied from the disclaimer below). + +/* based on: + + MD5.H - header file for MD5C.C + MDDRIVER.C - test driver for MD2, MD4 and MD5 + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +#include +#include + +// use for the raw digest output +const int MD5RAW_BYTES = 16; + +// use for outputting hex digests +const int MD5HEX_STR_SIZE = 33; // char hex[MD5HEX_STR_SIZE]; with null +const int MD5HEX_STR_BYTES = 32; // message system fixed size + +class LLMD5 { +// first, some types: + typedef unsigned int uint4; // assumes integer is 4 words long + typedef unsigned short int uint2; // assumes short integer is 2 words long + typedef unsigned char uint1; // assumes char is 1 word long + +public: +// methods for controlled operation: + LLMD5 (); // simple initializer + void update (const uint1 *input, const uint4 input_length); + void update (std::istream& stream); + void update (FILE *file); + void update (llifstream& stream); + void finalize (); + +// constructors for special circumstances. All these constructors finalize +// the MD5 context. + LLMD5 (const unsigned char *string); // digest string, finalize + LLMD5 (std::istream& stream); // digest stream, finalize + LLMD5 (FILE *file); // digest file, close, finalize + LLMD5 (llifstream& stream); // digest stream, close, finalize + LLMD5 (const unsigned char *string, const unsigned int number); + +// methods to acquire finalized result + void raw_digest(unsigned char *array); // provide 16-byte array for binary data + void hex_digest(char *string); // provide 33-byte array for ascii-hex string + friend std::ostream& operator<< (std::ostream&, LLMD5 context); + + + +private: + + +// next, the private data: + uint4 state[4]; + uint4 count[2]; // number of *bits*, mod 2^64 + uint1 buffer[64]; // input buffer + uint1 digest[16]; + uint1 finalized; + +// last, the private methods, mostly static: + void init (); // called by all constructors + void transform (const uint1 *buffer); // does the real update work. Note + // that length is implied to be 64. + + static void encode (uint1 *dest, const uint4 *src, const uint4 length); + static void decode (uint4 *dest, const uint1 *src, const uint4 length); + + static inline uint4 rotate_left (uint4 x, uint4 n); + static inline uint4 F (uint4 x, uint4 y, uint4 z); + static inline uint4 G (uint4 x, uint4 y, uint4 z); + static inline uint4 H (uint4 x, uint4 y, uint4 z); + static inline uint4 I (uint4 x, uint4 y, uint4 z); + static inline void FF (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + static inline void GG (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + static inline void HH (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + static inline void II (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + +}; + +#endif // LL_LLMD5_H diff --git a/linden/indra/llmath/lloctree.h b/linden/indra/llmath/lloctree.h new file mode 100644 index 0000000..6e068cd --- /dev/null +++ b/linden/indra/llmath/lloctree.h @@ -0,0 +1,712 @@ +/** + * @file lloctree.h + * @brief Octree declaration. + * + * Copyright (c) 2005-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLOCTREE_H +#define LL_LLOCTREE_H + +#include "lltreenode.h" +#include "v3math.h" +#include +#include + +#ifdef LL_RELEASE_FOR_DOWNLOAD +#define OCT_ERRS llwarns +#else +#define OCT_ERRS llerrs +#endif + +#define LL_OCTREE_PARANOIA_CHECK 0 + +template class LLOctreeState; +template class LLOctreeNode; + +template +class LLOctreeListener: public LLTreeListener +{ +public: + typedef LLTreeListener BaseType; + typedef LLOctreeNode 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 LLOctreeNode : public LLTreeNode +{ +public: + + typedef LLTreeNode BaseType; + typedef LLTreeState tree_state; + typedef LLOctreeState oct_state; + typedef LLOctreeNode oct_node; + typedef LLOctreeListener 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 ¢er, 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 LLOctreeTraveler : public LLTreeTraveler +{ +public: + virtual void traverse(const LLTreeNode* node); + virtual void visit(const LLTreeState* state) { } + virtual void visit(const LLOctreeState* branch) = 0; +}; + +//will pass requests to a child, might make a new child +template +class LLOctreeState : public LLTreeState +{ +public: + typedef LLTreeState BaseType; + typedef LLOctreeTraveler oct_traveler; + typedef LLOctreeNode oct_node; + typedef LLOctreeListener oct_listener; + typedef LLTreeTraveler tree_traveler; + typedef typename std::set > element_list; + typedef typename std::set >::iterator element_iter; + typedef typename std::set >::const_iterator const_element_iter; + typedef typename std::vector*>::iterator tree_listener_iter; + typedef typename std::vector* > 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* 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*) 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* 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::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* newstate = new LLOctreeState(); + oct_node* child = new LLOctreeNode(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* child = (LLOctreeNode*) getChild(i); + child->removeByAddress(data); + } + } + + virtual void clearChildren() + { + mChild.clear(); + } + + virtual void validate() + { +#if LL_OCTREE_PARANOIA_CHECK + LLOctreeNode* 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 LLOctreeRoot : public LLOctreeState +{ +public: + typedef LLOctreeState BaseType; + typedef LLOctreeNode 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* node = getOctNode(); + if (node->getSize().mdV[0] > data->getBinRadius() && node->isInside(data->getPositionGroup())) + { + //we got it, just act like a branch + LLOctreeState::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::pushCenter(center, size, data); + node->setCenter(center); + node->setSize(size*2); + node->updateMinMax(); + } + LLOctreeState::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::pushCenter(newcenter, size, data); + node->setCenter(newcenter); + node->setSize(size*2); + node->updateMinMax(); + + //copy our children to a new branch + LLOctreeState* newstate = new LLOctreeState(); + LLOctreeNode* newnode = new LLOctreeNode(center, size, newstate, node); + + for (U32 i = 0; i < this->getChildCount(); i++) + { + LLOctreeNode* 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 +void LLOctreeTraveler::traverse(const LLTreeNode* node) +{ + const LLOctreeState* state = (const LLOctreeState*) node->getState(); + state->accept(this); + for (U32 i = 0; i < state->getChildCount(); i++) + { + traverse(state->getChild(i)); + } +} + +#endif diff --git a/linden/indra/llmath/llperlin.cpp b/linden/indra/llmath/llperlin.cpp new file mode 100644 index 0000000..553b6a1 --- /dev/null +++ b/linden/indra/llmath/llperlin.cpp @@ -0,0 +1,295 @@ +/** + * @file llperlin.cpp + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/llperlin.h b/linden/indra/llmath/llperlin.h new file mode 100644 index 0000000..dcd6506 --- /dev/null +++ b/linden/indra/llmath/llperlin.h @@ -0,0 +1,47 @@ +/** + * @file llperlin.h + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/llplane.h b/linden/indra/llmath/llplane.h new file mode 100644 index 0000000..44721f3 --- /dev/null +++ b/linden/indra/llmath/llplane.h @@ -0,0 +1,68 @@ +/** + * @file llplane.h + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/llquantize.h b/linden/indra/llmath/llquantize.h new file mode 100644 index 0000000..6045911 --- /dev/null +++ b/linden/indra/llmath/llquantize.h @@ -0,0 +1,122 @@ +/** + * @file llquantize.h + * @brief useful routines for quantizing floats to various length ints + * and back out again + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/llquaternion.cpp b/linden/indra/llmath/llquaternion.cpp new file mode 100644 index 0000000..1111ca5 --- /dev/null +++ b/linden/indra/llmath/llquaternion.cpp @@ -0,0 +1,849 @@ +/** + * @file qmath.cpp + * @brief LLQuaternion class implementation. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/llquaternion.h b/linden/indra/llmath/llquaternion.h new file mode 100644 index 0000000..1e3f2b6 --- /dev/null +++ b/linden/indra/llmath/llquaternion.h @@ -0,0 +1,461 @@ +/** + * @file llquaternion.h + * @brief LLQuaternion class header file. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/llrand.cpp b/linden/indra/llmath/llrand.cpp new file mode 100644 index 0000000..38e0639 --- /dev/null +++ b/linden/indra/llmath/llrand.cpp @@ -0,0 +1,74 @@ +/** + * @file llrand.cpp + * @brief a few useful math functions. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" + +#include "llrand.h" +#include "lluuid.h" + +/* Put this back if you re-enabled slamFPCW +#if LL_WINDOWS +#include +#else +#include +#endif +*/ + +void slamFPCW( void ) +{ +/* +#if LL_WINDOWS + // for Intel based CPUs, slam the FP control word directly + WORD wTemp, wSave; + + __asm fstcw wSave + if ( (wSave & 0x300) // Not single mode + ||(0x3f != (wSave & 0x3f)) // Exceptions enabled + ||(wSave & 0xC00)) // Not round to nearest mode + { + __asm + { + mov ax, wSave + and ax, not 300h ;; single mode + or ax, 3fh ;; disable all exceptions + and ax, not 0xC00 ;; round to nearest mode + mov wTemp, ax + fldcw wTemp + } + } +#endif +*/ +} + +LLRand gLindenLabRandomNumber(LLUUID::getRandomSeed()); + +F32 frand(F32 val) +{ +// return (val * (F32)rand()/(F32)RAND_MAX); + return gLindenLabRandomNumber.llfrand(val); +} + diff --git a/linden/indra/llmath/llrand.h b/linden/indra/llmath/llrand.h new file mode 100644 index 0000000..db9f353 --- /dev/null +++ b/linden/indra/llmath/llrand.h @@ -0,0 +1,88 @@ +/** + * @file llrand.h + * @brief Some useful math functions. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLRAND_H +#define LL_LLRAND_H + +// As long as you #include "llviewerprecompiledheaders.h", +// you can use "gLindenLabRandomNumber.llfrand( range );" which returns a +// random number F32 ranging from 0.0f to range. +// -Ventrella - Sept 30, 2005 + +// Slams Intel processors into Single Precision FP mode +// (which is not any faster on modern hardware) +void slamFPCW( void ); + +class LLRand +{ +public: + LLRand(U32 seed) : mSeed(seed) {} + ~LLRand() {} + + void seed(U32 seed) { mSeed = seed; } + + U32 llrand() + { + mSeed = U64L(1664525) * mSeed + U64L(1013904223); + return (U32)mSeed; + } + + U32 llrand(U32 val) + { + mSeed = U64L(1664525) * mSeed + U64L(1013904223); + return (U32)(mSeed) % val; + } + + // val is the maximum + F32 llfrand(F32 val) + { + const U32 FP_ONE = 0x3f800000; + const U32 FP_MASK = 0x007fffff; + U32 ir = llrand(); + + ir = FP_ONE | (FP_MASK & ir); + + // generate random float + F32 fr = (*(F32 *)&ir); + + // correct to [0..1) + fr -= 1.f; + + fr *= val; + + return fr; + } + +public: + U64 mSeed; +}; + +F32 frand(F32 val); + +extern LLRand gLindenLabRandomNumber; + +#endif diff --git a/linden/indra/llmath/llrect.cpp b/linden/indra/llmath/llrect.cpp new file mode 100644 index 0000000..390573e --- /dev/null +++ b/linden/indra/llmath/llrect.cpp @@ -0,0 +1,29 @@ +/** + * @file llrect.cpp + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" + +#include "llrect.h" diff --git a/linden/indra/llmath/llrect.h b/linden/indra/llmath/llrect.h new file mode 100644 index 0000000..fd45d3c --- /dev/null +++ b/linden/indra/llmath/llrect.h @@ -0,0 +1,289 @@ +/** + * @file llrect.h + * @brief A rectangle in GL coordinates, with bottom,left = 0,0 + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + + +#ifndef LL_LLRECT_H +#define LL_LLRECT_H + +#include +#include "llmath.h" +#include "llsd.h" + +// Top > Bottom due to GL coords +template 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 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 null; +}; + +template LLRectBase LLRectBase::null(0,0,0,0); + +typedef LLRectBase LLRect; +typedef LLRectBase LLRectf; + +#endif diff --git a/linden/indra/llmath/lltreenode.h b/linden/indra/llmath/lltreenode.h new file mode 100644 index 0000000..78ff759 --- /dev/null +++ b/linden/indra/llmath/lltreenode.h @@ -0,0 +1,180 @@ +/** + * @file lltreenode.h + * + * Copyright (c) 2005-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLTREENODE_H +#define LL_LLTREENODE_H + +#include "stdtypes.h" +#include "xform.h" +#include + +template class LLTreeNode; +template class LLTreeTraveler; +template class LLTreeListener; + +template +class LLTreeState +{ +public: + LLTreeState(LLTreeNode* node) { setNode(node); } + virtual ~LLTreeState() { }; + + virtual bool insert(T* data) = 0; + virtual bool remove(T* data) = 0; + virtual void setNode(LLTreeNode* node); + virtual const LLTreeNode* getNode() const { return mNode; } + virtual LLTreeNode* getNode() { return mNode; } + virtual void accept(LLTreeTraveler* traveler) const = 0; + virtual LLTreeListener* getListener(U32 index) const; +private: + LLTreeNode* mNode; +}; + +template +class LLTreeListener +{ +public: + virtual ~LLTreeListener() { }; + virtual void handleInsertion(const LLTreeNode* node, T* data) = 0; + virtual void handleRemoval(const LLTreeNode* node, T* data) = 0; + virtual void handleDestruction(const LLTreeNode* node) = 0; + virtual void handleStateChange(const LLTreeNode* node) = 0; +}; + +template +class LLTreeNode +{ +public: + LLTreeNode(LLTreeState* state) { setState(state); } + virtual ~LLTreeNode(); + virtual LLTreeState* getState() { return mState; } + virtual const LLTreeState* getState() const { return mState; } + + virtual void setState(LLTreeState* state); + virtual void insert(T* data); + virtual bool remove(T* data); + virtual void notifyRemoval(T* data); + virtual U32 getListenerCount() { return mListeners.size(); } + virtual LLTreeListener* getListener(U32 index) const { return mListeners[index]; } + virtual void addListener(LLTreeListener* 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* mState; +public: + std::vector*> mListeners; +}; + +template +class LLTreeTraveler +{ +public: + virtual ~LLTreeTraveler() { }; + virtual void traverse(const LLTreeNode* node) = 0; + virtual void visit(const LLTreeState* state) = 0; +}; + +template +LLTreeNode::~LLTreeNode() +{ + destroyListeners(); +}; + +template +void LLTreeNode::insert(T* data) +{ + if (mState->insert(data)) + { + for (U32 i = 0; i < mListeners.size(); i++) + { + mListeners[i]->handleInsertion(this, data); + } + } +}; + +template +bool LLTreeNode::remove(T* data) +{ + if (mState->remove(data)) + { + return true; + } + return false; +}; + +template +void LLTreeNode::notifyRemoval(T* data) +{ + for (U32 i = 0; i < mListeners.size(); i++) + { + mListeners[i]->handleRemoval(this, data); + } +} + +template +void LLTreeNode::setState(LLTreeState* 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 +void LLTreeState::setNode(LLTreeNode* node) +{ + mNode = node; + if (node && node->getState() != this) + { + node->setState(this); + } +}; + +template +LLTreeListener* LLTreeState::getListener(U32 index) const +{ + return mNode->getListener(index); +} + +#endif diff --git a/linden/indra/llmath/lluuid.cpp b/linden/indra/llmath/lluuid.cpp new file mode 100644 index 0000000..216dac9 --- /dev/null +++ b/linden/indra/llmath/lluuid.cpp @@ -0,0 +1,864 @@ +/** + * @file lluuid.cpp + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" + +// We can't use WIN32_LEAN_AND_MEAN here, needs lots of includes. +#if LL_WINDOWS +# undef WIN32_LEAN_AND_MEAN +# include +# include +#endif + +#include "stdtypes.h" +#include "lldefs.h" +#include "llerror.h" + +#include "lluuid.h" +#include "llerror.h" +#include "llrand.h" +#include "llmd5.h" +#include "llstring.h" +#include "lltimer.h" + +const LLUUID LLUUID::null; +const LLTransactionID LLTransactionID::tnull; + +/* + +NOT DONE YET!!! + +static char BASE85_TABLE[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '!', '#', '$', '%', '&', '(', ')', '*', + '+', '-', ';', '[', '=', '>', '?', '@', '^', '_', + '`', '{', '|', '}', '~', '\0' +}; + + +void encode( char * fiveChars, unsigned int word ) throw( ) +{ +for( int ix = 0; ix < 5; ++ix ) { +fiveChars[4-ix] = encodeTable[ word % 85]; +word /= 85; +} +} + +To decode: +unsigned int decode( char const * fiveChars ) throw( bad_input_data ) +{ +unsigned int ret = 0; +for( int ix = 0; ix < 5; ++ix ) { +char * s = strchr( encodeTable, fiveChars[ ix ] ); +if( s == 0 ) throw bad_input_data(); +ret = ret * 85 + (s-encodeTable); +} +return ret; +} + +void LLUUID::toBase85(char* out) +{ + U32* me = (U32*)&(mData[0]); + for(S32 i = 0; i < 4; ++i) + { + char* o = &out[i*i]; + for(S32 j = 0; j < 5; ++j) + { + o[4-j] = BASE85_TABLE[ me[i] % 85]; + word /= 85; + } + } +} + +unsigned int decode( char const * fiveChars ) throw( bad_input_data ) +{ + unsigned int ret = 0; + for( S32 ix = 0; ix < 5; ++ix ) + { + char * s = strchr( encodeTable, fiveChars[ ix ] ); + ret = ret * 85 + (s-encodeTable); + } + return ret; +} +*/ + + +// Common to all UUID implementations +void LLUUID::toString(char *out) const +{ + sprintf(out, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (U8)(mData[0]), + (U8)(mData[1]), + (U8)(mData[2]), + (U8)(mData[3]), + (U8)(mData[4]), + (U8)(mData[5]), + (U8)(mData[6]), + (U8)(mData[7]), + (U8)(mData[8]), + (U8)(mData[9]), + (U8)(mData[10]), + (U8)(mData[11]), + (U8)(mData[12]), + (U8)(mData[13]), + (U8)(mData[14]), + (U8)(mData[15])); +} + +void LLUUID::toCompressedString(char *out) const +{ + memcpy(out, mData, UUID_BYTES); + out[UUID_BYTES] = '\0'; +} + +LLString LLUUID::getString() const +{ + char str[UUID_STR_SIZE]; + toString(str); + return LLString(str); +} + +BOOL LLUUID::set(const std::string& in_string, BOOL emit) +{ + return set(in_string.c_str(), emit); +} + +BOOL LLUUID::set(const char *in_string, BOOL emit) +{ + BOOL broken_format = FALSE; + if (!in_string) + { + llerrs << "No string pointer in LLUUID::set!" << llendl; + setNull(); + return FALSE; + } + + // empty strings should make NULL uuid + if (!in_string[0]) + { + setNull(); + return TRUE; + } + + if (strlen(in_string) != (UUID_STR_LENGTH - 1)) + { + // I'm a moron. First implementation didn't have the right UUID format. + // Shouldn't see any of these any more + if (strlen(in_string) == (UUID_STR_LENGTH - 2)) + { + if(emit) + { + llinfos << "Warning! Using broken UUID string format" << llendl; + } + broken_format = TRUE; + } + else + { + // Bad UUID string. Spam as INFO, as most cases we don't care. + if(emit) + { + llinfos << "Bad UUID string: " << in_string << llendl; + } + setNull(); + return FALSE; + } + } + + U8 cur_pos = 0; + S32 i; + for (i = 0; i < UUID_BYTES; i++) + { + if ((i == 4) || (i == 6) || (i == 8) || (i == 10)) + { + cur_pos++; + if (broken_format && (i==10)) + { + // Missing - in the broken format + cur_pos--; + } + } + + mData[i] = 0; + + if ((*(in_string + cur_pos) >= '0') && (*(in_string+cur_pos) <= '9')) + { + mData[i] += (U8)(*(in_string + cur_pos) - '0'); + } + else if ((*(in_string + cur_pos) >= 'a') && (*(in_string+cur_pos) <='f')) + { + mData[i] += (U8)(10 + *(in_string + cur_pos) - 'a'); + } + else if ((*(in_string + cur_pos) >= 'A') && (*(in_string+cur_pos) <='F')) + { + mData[i] += (U8)(10 + *(in_string + cur_pos) - 'A'); + } + else + { + if(emit) + { + llwarns << "Invalid UUID string character" << llendl; + } + setNull(); + return FALSE; + } + + mData[i] = mData[i] << 4; + cur_pos++; + + if ((*(in_string + cur_pos) >= '0') && (*(in_string+cur_pos) <= '9')) + { + mData[i] += (U8)(*(in_string + cur_pos) - '0'); + } + else if ((*(in_string + cur_pos) >= 'a') && (*(in_string+cur_pos) <='f')) + { + mData[i] += (U8)(10 + *(in_string + cur_pos) - 'a'); + } + else if ((*(in_string + cur_pos) >= 'A') && (*(in_string+cur_pos) <='F')) + { + mData[i] += (U8)(10 + *(in_string + cur_pos) - 'A'); + } + else + { + if(emit) + { + llwarns << "Invalid UUID string character" << llendl; + } + setNull(); + return FALSE; + } + cur_pos++; + } + + return TRUE; +} + +BOOL LLUUID::validate(const std::string& in_string) +{ + return validate(in_string.c_str()); +} + +BOOL LLUUID::validate(const char *in_string) +{ + BOOL broken_format = FALSE; + if (!in_string) + { + return FALSE; + } + if (strlen(in_string) != (UUID_STR_LENGTH - 1)) + { + // I'm a moron. First implementation didn't have the right UUID format. + if (strlen(in_string) == (UUID_STR_LENGTH - 2)) + { + broken_format = TRUE; + } + else + { + return FALSE; + } + } + + U8 cur_pos = 0; + U32 i; + for (i = 0; i < 16; i++) + { + if ((i == 4) || (i == 6) || (i == 8) || (i == 10)) + { + cur_pos++; + if (broken_format && (i==10)) + { + // Missing - in the broken format + cur_pos--; + } + } + + if ((*(in_string + cur_pos) >= '0') && (*(in_string+cur_pos) <= '9')) + { + } + else if ((*(in_string + cur_pos) >= 'a') && (*(in_string+cur_pos) <='f')) + { + } + else if ((*(in_string + cur_pos) >= 'A') && (*(in_string+cur_pos) <='F')) + { + } + else + { + return FALSE; + } + + cur_pos++; + + if ((*(in_string + cur_pos) >= '0') && (*(in_string+cur_pos) <= '9')) + { + } + else if ((*(in_string + cur_pos) >= 'a') && (*(in_string+cur_pos) <='f')) + { + } + else if ((*(in_string + cur_pos) >= 'A') && (*(in_string+cur_pos) <='F')) + { + } + else + { + return FALSE; + } + cur_pos++; + } + return TRUE; +} + +const LLUUID& LLUUID::operator^=(const LLUUID& rhs) +{ + U32* me = (U32*)&(mData[0]); + const U32* other = (U32*)&(rhs.mData[0]); + for(S32 i = 0; i < 4; ++i) + { + me[i] = me[i] ^ other[i]; + } + return *this; +} + +LLUUID LLUUID::operator^(const LLUUID& rhs) const +{ + LLUUID id(*this); + id ^= rhs; + return id; +} + +void LLUUID::combine(const LLUUID& other, LLUUID& result) const +{ + LLMD5 md5_uuid; + md5_uuid.update((unsigned char*)mData, 16); + md5_uuid.update((unsigned char*)other.mData, 16); + md5_uuid.finalize(); + md5_uuid.raw_digest(result.mData); +} + +LLUUID LLUUID::combine(const LLUUID &other) const +{ + LLUUID combination; + combine(other, combination); + return combination; +} + +std::ostream& operator<<(std::ostream& s, const LLUUID &uuid) +{ + char uuid_str[UUID_STR_LENGTH]; + + uuid.toString(uuid_str); + s << uuid_str; + return s; +} + +std::istream& operator>>(std::istream &s, LLUUID &uuid) +{ + U32 i; + char uuid_str[UUID_STR_LENGTH]; + for (i = 0; i < UUID_STR_LENGTH-1; i++) + { + s >> uuid_str[i]; + } + uuid_str[i] = '\0'; + uuid.set(uuid_str); + return s; +} + +static void get_random_bytes(void *buf, int nbytes) +{ + int i; + char *cp = (char *) buf; + + for (i=0; i < nbytes; i++) + *cp++ = gLindenLabRandomNumber.llrand() & 0xFF; + return; +} + +#if LL_WINDOWS +typedef struct _ASTAT_ +{ + ADAPTER_STATUS adapt; + NAME_BUFFER NameBuff [30]; +}ASTAT, * PASTAT; + +// static +S32 LLUUID::getNodeID(unsigned char * node_id) +{ + ASTAT Adapter; + NCB Ncb; + UCHAR uRetCode; + LANA_ENUM lenum; + int i; + int retval = 0; + + memset( &Ncb, 0, sizeof(Ncb) ); + Ncb.ncb_command = NCBENUM; + Ncb.ncb_buffer = (UCHAR *)&lenum; + Ncb.ncb_length = sizeof(lenum); + uRetCode = Netbios( &Ncb ); + // printf( "The NCBENUM return code is: 0x%x \n", uRetCode ); + + for(i=0; i < lenum.length ;i++) + { + memset( &Ncb, 0, sizeof(Ncb) ); + Ncb.ncb_command = NCBRESET; + Ncb.ncb_lana_num = lenum.lana[i]; + + uRetCode = Netbios( &Ncb ); + // printf( "The NCBRESET on LANA %d return code is: 0x%x \n", + // lenum.lana[i], uRetCode ); + + memset( &Ncb, 0, sizeof (Ncb) ); + Ncb.ncb_command = NCBASTAT; + Ncb.ncb_lana_num = lenum.lana[i]; + + strcpy( (char *)Ncb.ncb_callname, "* " ); + Ncb.ncb_buffer = (unsigned char *)&Adapter; + Ncb.ncb_length = sizeof(Adapter); + + uRetCode = Netbios( &Ncb ); +// printf( "The NCBASTAT on LANA %d return code is: 0x%x \n", +// lenum.lana[i], uRetCode ); + if ( uRetCode == 0 ) + { +// printf( "The Ethernet Number on LANA %d is: %02x%02x%02x%02x%02x%02x\n", +// lenum.lana[i], +// Adapter.adapt.adapter_address[0], +// Adapter.adapt.adapter_address[1], +// Adapter.adapt.adapter_address[2], +// Adapter.adapt.adapter_address[3], +// Adapter.adapt.adapter_address[4], +// Adapter.adapt.adapter_address[5] ); + memcpy(node_id,Adapter.adapt.adapter_address,6); + retval = 1; + + } + } + return retval; +} + +#elif LL_DARWIN +// Mac OS X version of the UUID generation code... +/* + * Get an ethernet hardware address, if we can find it... + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// static +S32 LLUUID::getNodeID(unsigned char *node_id) +{ + int i; + unsigned char *a = NULL; + struct ifaddrs *ifap, *ifa; + int rv; + S32 result = 0; + + if ((rv=getifaddrs(&ifap))==-1) + { + return -1; + } + if (ifap == NULL) + { + return -1; + } + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) + { +// printf("Interface %s, address family %d, ", ifa->ifa_name, ifa->ifa_addr->sa_family); + for(i=0; i< ifa->ifa_addr->sa_len; i++) + { +// printf("%02X ", (unsigned char)ifa->ifa_addr->sa_data[i]); + } +// printf("\n"); + + if(ifa->ifa_addr->sa_family == AF_LINK) + { + // This is a link-level address + struct sockaddr_dl *lla = (struct sockaddr_dl *)ifa->ifa_addr; + +// printf("\tLink level address, type %02X\n", lla->sdl_type); + + if(lla->sdl_type == IFT_ETHER) + { + // Use the first ethernet MAC in the list. + // For some reason, the macro LLADDR() defined in net/if_dl.h doesn't expand correctly. This is what it would do. + a = (unsigned char *)&((lla)->sdl_data); + a += (lla)->sdl_nlen; + + if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) + { + continue; + } + + if (node_id) + { + memcpy(node_id, a, 6); + result = 1; + } + + // We found one. + break; + } + } + } + freeifaddrs(ifap); + + return result; +} + +#else + +// Linux version of the UUID generation code... +/* + * Get the ethernet hardware address, if we can find it... + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define HAVE_NETINET_IN_H +#ifdef HAVE_NETINET_IN_H +#include +#if !LL_DARWIN +#include +#endif +#endif + +// static +S32 LLUUID::getNodeID(unsigned char *node_id) +{ + int sd; + struct ifreq ifr, *ifrp; + struct ifconf ifc; + char buf[1024]; + int n, i; + unsigned char *a; + +/* + * BSD 4.4 defines the size of an ifreq to be + * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len + * However, under earlier systems, sa_len isn't present, so the size is + * just sizeof(struct ifreq) + */ +#ifdef HAVE_SA_LEN +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#define ifreq_size(i) max(sizeof(struct ifreq),\ + sizeof((i).ifr_name)+(i).ifr_addr.sa_len) +#else +#define ifreq_size(i) sizeof(struct ifreq) +#endif /* HAVE_SA_LEN*/ + + sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sd < 0) { + return -1; + } + memset(buf, 0, sizeof(buf)); + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { + close(sd); + return -1; + } + n = ifc.ifc_len; + for (i = 0; i < n; i+= ifreq_size(*ifr) ) { + ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); + strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); +#ifdef SIOCGIFHWADDR + if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) + continue; + a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; +#else +#ifdef SIOCGENADDR + if (ioctl(sd, SIOCGENADDR, &ifr) < 0) + continue; + a = (unsigned char *) ifr.ifr_enaddr; +#else + /* + * XXX we don't have a way of getting the hardware + * address + */ + close(sd); + return 0; +#endif /* SIOCGENADDR */ +#endif /* SIOCGIFHWADDR */ + if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) + continue; + if (node_id) { + memcpy(node_id, a, 6); + close(sd); + return 1; + } + } + close(sd); + return 0; +} + +#endif + +S32 LLUUID::cmpTime(uuid_time_t *t1, uuid_time_t *t2) +{ + // Compare two time values. + + if (t1->high < t2->high) return -1; + if (t1->high > t2->high) return 1; + if (t1->low < t2->low) return -1; + if (t1->low > t2->low) return 1; + return 0; +} + +void LLUUID::getSystemTime(uuid_time_t *timestamp) +{ + // Get system time with 100ns precision. Time is since Oct 15, 1582. +#if LL_WINDOWS + ULARGE_INTEGER time; + GetSystemTimeAsFileTime((FILETIME *)&time); + // NT keeps time in FILETIME format which is 100ns ticks since + // Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582. + // The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec) + // + 18 years and 5 leap days. + time.QuadPart += + (unsigned __int64) (1000*1000*10) // seconds + * (unsigned __int64) (60 * 60 * 24) // days + * (unsigned __int64) (17+30+31+365*18+5); // # of days + + timestamp->high = time.HighPart; + timestamp->low = time.LowPart; +#else + struct timeval tp; + gettimeofday(&tp, 0); + + // Offset between UUID formatted times and Unix formatted times. + // UUID UTC base time is October 15, 1582. + // Unix base time is January 1, 1970. + U64 uuid_time = ((U64)tp.tv_sec * 10000000) + (tp.tv_usec * 10) + + U64L(0x01B21DD213814000); + timestamp->high = (U32) (uuid_time >> 32); + timestamp->low = (U32) (uuid_time & 0xFFFFFFFF); +#endif +} + +void LLUUID::getCurrentTime(uuid_time_t *timestamp) +{ + // Get current time as 60 bit 100ns ticks since whenever. + // Compensate for the fact that real clock resolution is less + // than 100ns. + + const U32 uuids_per_tick = 1024; + + static uuid_time_t time_last; + static U32 uuids_this_tick; + static BOOL init = FALSE; + + if (!init) { + getSystemTime(&time_last); + uuids_this_tick = uuids_per_tick; + init = TRUE; + } + + uuid_time_t time_now = {0,0}; + + while (1) { + getSystemTime(&time_now); + + // if clock reading changed since last UUID generated + if (cmpTime(&time_last, &time_now)) { + // reset count of uuid's generated with this clock reading + uuids_this_tick = 0; + break; + } + if (uuids_this_tick < uuids_per_tick) { + uuids_this_tick++; + break; + } + // going too fast for our clock; spin + } + + time_last = time_now; + + if (uuids_this_tick != 0) { + if (time_now.low & 0x80000000) { + time_now.low += uuids_this_tick; + if (!(time_now.low & 0x80000000)) + time_now.high++; + } else + time_now.low += uuids_this_tick; + } + + timestamp->high = time_now.high; + timestamp->low = time_now.low; +} + +void LLUUID::generate() +{ + // Create a UUID. + + + uuid_time_t timestamp; + + static unsigned char node_id[6]; + static int has_init = 0; + + // Create a UUID. + static uuid_time_t time_last = {0,0}; + static U16 clock_seq = 0; + static LLRand random_generator(0); // dummy seed. reset it below + if (!has_init) + { + if (getNodeID(node_id) <= 0) + { + get_random_bytes(node_id, 6); + /* + * Set multicast bit, to prevent conflicts + * with IEEE 802 addresses obtained from + * network cards + */ + node_id[0] |= 0x80; + } + + getCurrentTime(&time_last); + random_generator.seed(time_last.low); + clock_seq = (U16) random_generator.llrand(65536); + has_init = 1; + } + + // get current time + getCurrentTime(×tamp); + + // if clock went backward change clockseq + if (cmpTime(×tamp, &time_last) == -1) { + clock_seq = (clock_seq + 1) & 0x3FFF; + if (clock_seq == 0) clock_seq++; + } + + memcpy(mData+10, node_id, 6); + U32 tmp; + tmp = timestamp.low; + mData[3] = (unsigned char) tmp; + tmp >>= 8; + mData[2] = (unsigned char) tmp; + tmp >>= 8; + mData[1] = (unsigned char) tmp; + tmp >>= 8; + mData[0] = (unsigned char) tmp; + + tmp = (U16) timestamp.high; + mData[5] = (unsigned char) tmp; + tmp >>= 8; + mData[4] = (unsigned char) tmp; + + tmp = (timestamp.high >> 16) | 0x1000; + mData[7] = (unsigned char) tmp; + tmp >>= 8; + mData[6] = (unsigned char) tmp; + + tmp = clock_seq; + mData[9] = (unsigned char) tmp; + tmp >>= 8; + mData[8] = (unsigned char) tmp; + + LLMD5 md5_uuid; + + md5_uuid.update(mData,16); + md5_uuid.finalize(); + md5_uuid.raw_digest(mData); + + time_last = timestamp; +} + + +U32 LLUUID::getRandomSeed() +{ + static unsigned char seed[16]; + + getNodeID(&seed[0]); + seed[6]='\0'; + seed[7]='\0'; + getSystemTime((uuid_time_t *)(&seed[8])); + + LLMD5 md5_seed; + + md5_seed.update(seed,16); + md5_seed.finalize(); + md5_seed.raw_digest(seed); + + return(*(U32 *)seed); +} + +BOOL LLUUID::parseUUID(const char* buf, LLUUID* value) +{ + if( buf == NULL || buf[0] == '\0' || value == NULL) + { + return FALSE; + } + + LLString temp( buf ); + LLString::trim(temp); + if( LLUUID::validate( temp ) ) + { + value->set( temp ); + return TRUE; + } + return FALSE; +} + +LLAssetID LLTransactionID::makeAssetID(const LLUUID& session) const +{ + LLAssetID result; + if (isNull()) + { + result.setNull(); + } + else + { + combine(session, result); + } + return result; +} diff --git a/linden/indra/llmath/lluuid.h b/linden/indra/llmath/lluuid.h new file mode 100644 index 0000000..b8288eb --- /dev/null +++ b/linden/indra/llmath/lluuid.h @@ -0,0 +1,321 @@ +/** + * @file lluuid.h + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLUUID_H +#define LL_LLUUID_H + +#include +#include + +#include "llstring.h" + +const S32 UUID_BYTES = 16; +const S32 UUID_WORDS = 4; +const S32 UUID_STR_LENGTH = 37; // actually wrong, should be 36 and use size below +const S32 UUID_STR_SIZE = 37; +const S32 UUID_BASE85_LENGTH = 21; // including the trailing NULL. + +struct uuid_time_t { + U32 high; + U32 low; + }; + +class LLUUID +{ +public: + // + // CREATORS + // + LLUUID(); + explicit LLUUID(const char *in_string); // Convert from string. + explicit LLUUID(const std::string& in_string); // Convert from string. + LLUUID(const LLUUID &in); + LLUUID &operator=(const LLUUID &rhs); + + ~LLUUID(); + + // + // MANIPULATORS + // + void generate(); // Generate a new UUID + BOOL set(const char *in_string, BOOL emit = TRUE); // Convert from string, if emit is FALSE, do not emit warnings + BOOL set(const std::string& in_string, BOOL emit = TRUE); // Convert from string, if emit is FALSE, do not emit warnings + void setNull(); // Faster than setting to LLUUID::null. + + S32 cmpTime(uuid_time_t *t1, uuid_time_t *t2); + static void getSystemTime(uuid_time_t *timestamp); + void getCurrentTime(uuid_time_t *timestamp); + + // + // ACCESSORS + // + BOOL isNull() const; // Faster than comparing to LLUUID::null. + BOOL notNull() const; // Faster than comparing to LLUUID::null. + // JC: This is dangerous. It allows UUIDs to be cast automatically + // to integers, among other things. Use isNull() or notNull(). + // operator bool() const; + + // JC: These must return real bool's (not BOOLs) or else use of the STL + // will generate bool-to-int performance warnings. + bool operator==(const LLUUID &rhs) const; + bool operator!=(const LLUUID &rhs) const; + bool operator<(const LLUUID &rhs) const; + bool operator>(const LLUUID &rhs) const; + + // xor functions. Useful since any two random uuids xored together + // will yield a determinate third random unique id that can be + // used as a key in a single uuid that represents 2. + const LLUUID& operator^=(const LLUUID& rhs); + LLUUID operator^(const LLUUID& rhs) const; + + // similar to functions above, but not invertible + // yields a third random UUID that can be reproduced from the two inputs + // but which, given the result and one of the inputs can't be used to + // deduce the other input + LLUUID combine(const LLUUID& other) const; + void combine(const LLUUID& other, LLUUID& result) const; + + friend std::ostream& operator<<(std::ostream& s, const LLUUID &uuid); + friend std::istream& operator>>(std::istream& s, LLUUID &uuid); + + void toString(char *out) const; // Does not allocate memory, needs 36 characters (including \0) + void toCompressedString(char *out) const; // Does not allocate memory, needs 17 characters (including \0) + LLString getString() const; + U16 getCRC16() const; + U32 getCRC32() const; + + static BOOL validate(const std::string& in_string); // Validate that the UUID string is legal. + static BOOL validate(const char *in_string); // Validate that the UUID string is legal. + + static const LLUUID null; + + static U32 getRandomSeed(); + static S32 getNodeID(unsigned char * node_id); + + static BOOL parseUUID(const char* buf, LLUUID* value); + + U8 mData[UUID_BYTES]; +}; + + +// Construct +inline LLUUID::LLUUID() +{ + setNull(); +} + + +// Faster than copying from memory +inline void LLUUID::setNull() +{ + U32 *word = (U32 *)mData; + word[0] = 0; + word[1] = 0; + word[2] = 0; + word[3] = 0; +} + + +// Compare +inline bool LLUUID::operator==(const LLUUID& rhs) const +{ + U32 *tmp = (U32 *)mData; + U32 *rhstmp = (U32 *)rhs.mData; + // Note: binary & to avoid branching + return + (tmp[0] == rhstmp[0]) & + (tmp[1] == rhstmp[1]) & + (tmp[2] == rhstmp[2]) & + (tmp[3] == rhstmp[3]); +} + + +inline bool LLUUID::operator!=(const LLUUID& rhs) const +{ + U32 *tmp = (U32 *)mData; + U32 *rhstmp = (U32 *)rhs.mData; + // Note: binary | to avoid branching + return + (tmp[0] != rhstmp[0]) | + (tmp[1] != rhstmp[1]) | + (tmp[2] != rhstmp[2]) | + (tmp[3] != rhstmp[3]); +} + +/* +// JC: This is dangerous. It allows UUIDs to be cast automatically +// to integers, among other things. Use isNull() or notNull(). +inline LLUUID::operator bool() const +{ + U32 *word = (U32 *)mData; + return (word[0] | word[1] | word[2] | word[3]) > 0; +} +*/ + +inline BOOL LLUUID::notNull() const +{ + U32 *word = (U32 *)mData; + return (word[0] | word[1] | word[2] | word[3]) > 0; +} + +// Faster than == LLUUID::null because doesn't require +// as much memory access. +inline BOOL LLUUID::isNull() const +{ + U32 *word = (U32 *)mData; + // If all bits are zero, return !0 == TRUE + return !(word[0] | word[1] | word[2] | word[3]); +} + +// Copy constructor +inline LLUUID::LLUUID(const LLUUID& rhs) +{ + U32 *tmp = (U32 *)mData; + U32 *rhstmp = (U32 *)rhs.mData; + tmp[0] = rhstmp[0]; + tmp[1] = rhstmp[1]; + tmp[2] = rhstmp[2]; + tmp[3] = rhstmp[3]; +} + +inline LLUUID::~LLUUID() +{ +} + +// Assignment +inline LLUUID& LLUUID::operator=(const LLUUID& rhs) +{ + // No need to check the case where this==&rhs. The branch is slower than the write. + U32 *tmp = (U32 *)mData; + U32 *rhstmp = (U32 *)rhs.mData; + tmp[0] = rhstmp[0]; + tmp[1] = rhstmp[1]; + tmp[2] = rhstmp[2]; + tmp[3] = rhstmp[3]; + + return *this; +} + + +inline LLUUID::LLUUID(const char *in_string) +{ + if (!in_string || in_string[0] == 0) + { + setNull(); + return; + } + + set(in_string); +} + +inline LLUUID::LLUUID(const std::string& in_string) +{ + if (in_string.empty()) + { + setNull(); + return; + } + + set(in_string); +} + +// IW: DON'T "optimize" these w/ U32s or you'll scoogie the sort order +// IW: this will make me very sad +inline bool LLUUID::operator<(const LLUUID &rhs) const +{ + U32 i; + for( i = 0; i < (UUID_BYTES - 1); i++ ) + { + if( mData[i] != rhs.mData[i] ) + { + return (mData[i] < rhs.mData[i]); + } + } + return (mData[UUID_BYTES - 1] < rhs.mData[UUID_BYTES - 1]); +} + +inline bool LLUUID::operator>(const LLUUID &rhs) const +{ + U32 i; + for( i = 0; i < (UUID_BYTES - 1); i++ ) + { + if( mData[i] != rhs.mData[i] ) + { + return (mData[i] > rhs.mData[i]); + } + } + return (mData[UUID_BYTES - 1] > rhs.mData[UUID_BYTES - 1]); +} + +inline U16 LLUUID::getCRC16() const +{ + // A UUID is 16 bytes, or 8 shorts. + U16 *short_data = (U16*)mData; + U16 out = 0; + out += short_data[0]; + out += short_data[1]; + out += short_data[2]; + out += short_data[3]; + out += short_data[4]; + out += short_data[5]; + out += short_data[6]; + out += short_data[7]; + return out; +} + +inline U32 LLUUID::getCRC32() const +{ + U32 *tmp = (U32*)mData; + return tmp[0] + tmp[1] + tmp[2] + tmp[3]; +} + + +// Helper structure for ordering lluuids in stl containers. +// eg: std::map widget_map; +struct lluuid_less +{ + bool operator()(const LLUUID& lhs, const LLUUID& rhs) const + { + return (lhs < rhs) ? true : false; + } +}; + +typedef std::set uuid_list_t; + +/* + * Sub-classes for keeping transaction IDs and asset IDs + * straight. + */ +typedef LLUUID LLAssetID; + +class LLTransactionID : public LLUUID +{ +public: + static const LLTransactionID tnull; + LLAssetID makeAssetID(const LLUUID& session) const; +}; + +#endif diff --git a/linden/indra/llmath/llvolume.cpp b/linden/indra/llmath/llvolume.cpp new file mode 100644 index 0000000..14d4cdf --- /dev/null +++ b/linden/indra/llmath/llvolume.cpp @@ -0,0 +1,4576 @@ +/** + * @file llvolume.cpp + * + * Copyright (c) 2002-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" +#include "llmath.h" + +#include + +#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 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 ¶ms) +{ + 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= 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;imParams.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 ¶ms) const +{ + return (getPathParams() == params.getPathParams()) && + (getProfileParams() == params.getProfileParams()); +} + +bool LLVolumeParams::operator!=(const LLVolumeParams ¶ms) const +{ + return (getPathParams() != params.getPathParams()) || + (getProfileParams() != params.getProfileParams()); +} + +bool LLVolumeParams::operator<(const LLVolumeParams ¶ms) const +{ + if( getPathParams() != params.getPathParams() ) + { + return getPathParams() < params.getPathParams(); + } + else + { + return getProfileParams() < params.getProfileParams(); + } +} + +void LLVolumeParams::copyParams(const LLVolumeParams ¶ms) +{ + 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 &vertices, + std::vector &normals, + std::vector &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 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) + // *FIX IF NEEDED: this does not deal with neighboring degenerate faces + for (S32 k = 0; k < 3; k++) + { + S32 index = face.mEdge[j*3+k]; + if (index != -1) + { + fFacing[j] = fFacing[index]; + break; + } + } + continue; //skip degenerate face + } + + //for each edge + for (S32 k = 0; k < 3; k++) { + S32 index = face.mEdge[j*3+k]; + if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) { + //our neighbor is degenerate, make him face our direction + fFacing[face.mEdge[j*3+k]] = fFacing[j]; + continue; + } + + if (index == -1 || //edge has no neighbor, MUST be a silhouette edge + (fFacing[index] & fFacing[j]) == 0) { //we found a silhouette edge + + S32 v1 = face.mIndices[j*3+k]; + S32 v2 = face.mIndices[j*3+((k+1)%3)]; + + 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& 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 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 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& mesh = mVolumep->getMesh(); + const std::vector& 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=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& mesh = mVolumep->getMesh(); + const std::vector& 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& mesh = mVolumep->getMesh(); + const std::vector& profile = mVolumep->getProfile().mProfile; + const std::vector& 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 &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/linden/indra/llmath/llvolume.h b/linden/indra/llmath/llvolume.h new file mode 100644 index 0000000..63981da --- /dev/null +++ b/linden/indra/llmath/llvolume.h @@ -0,0 +1,901 @@ +/** + * @file llvolume.h + * @brief LLVolume base class. + * + * Copyright (c) 2002-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLVOLUME_H +#define LL_LLVOLUME_H + +#include + +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 ¶ms) const; + bool operator!=(const LLProfileParams ¶ms) const; + bool operator<(const LLProfileParams ¶ms) const; + + void copyParams(const LLProfileParams ¶ms); + + 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 ¶ms) const +{ + return + (getCurveType() == params.getCurveType()) && + (getBegin() == params.getBegin()) && + (getEnd() == params.getEnd()) && + (getHollow() == params.getHollow()); +} + +inline bool LLProfileParams::operator!=(const LLProfileParams ¶ms) const +{ + return + (getCurveType() != params.getCurveType()) || + (getBegin() != params.getBegin()) || + (getEnd() != params.getEnd()) || + (getHollow() != params.getHollow()); +} + + +inline bool LLProfileParams::operator<(const LLProfileParams ¶ms) const +{ + if (getCurveType() != params.getCurveType()) + { + return getCurveType() < params.getCurveType(); + } + else + if (getBegin() != params.getBegin()) + { + return getBegin() < params.getBegin(); + } + else + if (getEnd() != params.getEnd()) + { + return getEnd() < params.getEnd(); + } + else + { + return getHollow() < params.getHollow(); + } +} + +#define U8_TO_F32(x) (F32)(*((S8 *)&x)) + +class LLPathParams +{ +public: + LLPathParams() + { + 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 ¶ms) const; + bool operator!=(const LLPathParams ¶ms) const; + bool operator<(const LLPathParams ¶ms) const; + + void copyParams(const LLPathParams ¶ms); + + 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 ¶ms) const +{ + return + (getCurveType() == params.getCurveType()) && + (getScale() == params.getScale()) && + (getBegin() == params.getBegin()) && + (getEnd() == params.getEnd()) && + (getShear() == params.getShear()) && + (getTwist() == params.getTwist()) && + (getTwistBegin() == params.getTwistBegin()) && + (getRadiusOffset() == params.getRadiusOffset()) && + (getTaper() == params.getTaper()) && + (getRevolutions() == params.getRevolutions()) && + (getSkew() == params.getSkew()); +} + +inline bool LLPathParams::operator!=(const LLPathParams ¶ms) const +{ + return + (getCurveType() != params.getCurveType()) || + (getScale() != params.getScale()) || + (getBegin() != params.getBegin()) || + (getEnd() != params.getEnd()) || + (getShear() != params.getShear()) || + (getTwist() != params.getTwist()) || + (getTwistBegin() !=params.getTwistBegin()) || + (getRadiusOffset() != params.getRadiusOffset()) || + (getTaper() != params.getTaper()) || + (getRevolutions() != params.getRevolutions()) || + (getSkew() != params.getSkew()); +} + + +inline bool LLPathParams::operator<(const LLPathParams ¶ms) const +{ + if( getCurveType() != params.getCurveType()) + { + return getCurveType() < params.getCurveType(); + } + else + if( getScale() != params.getScale()) + { + return getScale() < params.getScale(); + } + else + if( getBegin() != params.getBegin()) + { + return getBegin() < params.getBegin(); + } + else + if( getEnd() != params.getEnd()) + { + return getEnd() < params.getEnd(); + } + else + if( getShear() != params.getShear()) + { + return getShear() < params.getShear(); + } + else + if( getTwist() != params.getTwist()) + { + return getTwist() < params.getTwist(); + } + else + if( getTwistBegin() != params.getTwistBegin()) + { + return getTwistBegin() < params.getTwistBegin(); + } + else + if( getRadiusOffset() != params.getRadiusOffset()) + { + return getRadiusOffset() < params.getRadiusOffset(); + } + else + if( getTaper() != params.getTaper()) + { + return getTaper() < params.getTaper(); + } + else + if( getRevolutions() != params.getRevolutions()) + { + return getRevolutions() < params.getRevolutions(); + } + else + { + return getSkew() < params.getSkew(); + } +} + +typedef LLVolumeParams* LLVolumeParamsPtr; +typedef const LLVolumeParams* const_LLVolumeParamsPtr; + +class LLVolumeParams +{ +public: + LLVolumeParams() + { + } + + LLVolumeParams(LLProfileParams &profile, LLPathParams &path) + : mProfileParams(profile), mPathParams(path) + { + } + + bool operator==(const LLVolumeParams ¶ms) const; + bool operator!=(const LLVolumeParams ¶ms) const; + bool operator<(const LLVolumeParams ¶ms) const; + + + void copyParams(const LLVolumeParams ¶ms); + + const LLProfileParams &getProfileParams() const {return mProfileParams;} + LLProfileParams &getProfileParams() {return mProfileParams;} + const LLPathParams &getPathParams() const {return mPathParams;} + LLPathParams &getPathParams() {return mPathParams;} + + BOOL importFile(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 ¶ms) : 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 mProfile; + std::vector mNormals; + std::vector mFaces; + std::vector mEdgeNormals; + std::vector 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 ¶ms) : 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 mPath; + +protected: + BOOL mOpen; + S32 mTotal; + BOOL mDirty; + F32 mStep; +}; + +class LLDynamicPath : public LLPath +{ +public: + LLDynamicPath(const LLPathParams ¶ms) : 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 mVertices; + std::vector mIndices; + std::vector 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 &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 ¶ms, 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& 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 &vertices, std::vector &normals, std::vector &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 &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 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/linden/indra/llmath/llvolumemgr.cpp b/linden/indra/llmath/llvolumemgr.cpp new file mode 100644 index 0000000..6b6182a --- /dev/null +++ b/linden/indra/llmath/llvolumemgr.cpp @@ -0,0 +1,314 @@ +/** + * @file llvolumemgr.cpp + * + * Copyright (c) 2002-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 ¶ms) +{ + 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/linden/indra/llmath/llvolumemgr.h b/linden/indra/llmath/llvolumemgr.h new file mode 100644 index 0000000..9cb1f63 --- /dev/null +++ b/linden/indra/llmath/llvolumemgr.h @@ -0,0 +1,101 @@ +/** + * @file llvolumemgr.h + * @brief LLVolumeMgr class. + * + * Copyright (c) 2002-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLVOLUMEMGR_H +#define LL_LLVOLUMEMGR_H + +#include + +#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 ¶ms); + + 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 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/linden/indra/llmath/m3math.cpp b/linden/indra/llmath/m3math.cpp new file mode 100644 index 0000000..c5d2c2d --- /dev/null +++ b/linden/indra/llmath/m3math.cpp @@ -0,0 +1,549 @@ +/** + * @file m3math.cpp + * @brief LLMatrix3 class implementation. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/m3math.h b/linden/indra/llmath/m3math.h new file mode 100644 index 0000000..74c5203 --- /dev/null +++ b/linden/indra/llmath/m3math.h @@ -0,0 +1,217 @@ +/** + * @file m3math.h + * @brief LLMatrix3 class header file. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/m4math.cpp b/linden/indra/llmath/m4math.cpp new file mode 100644 index 0000000..ee7e6f3 --- /dev/null +++ b/linden/indra/llmath/m4math.cpp @@ -0,0 +1,813 @@ +/** + * @file m4math.cpp + * @brief LLMatrix4 class implementation. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/m4math.h b/linden/indra/llmath/m4math.h new file mode 100644 index 0000000..fe9b26a --- /dev/null +++ b/linden/indra/llmath/m4math.h @@ -0,0 +1,384 @@ +/** + * @file m4math.h + * @brief LLMatrix3 class header file. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/raytrace.cpp b/linden/indra/llmath/raytrace.cpp new file mode 100644 index 0000000..64bf616 --- /dev/null +++ b/linden/indra/llmath/raytrace.cpp @@ -0,0 +1,1275 @@ +/** + * @file raytrace.cpp + * @brief Functions called by box object scripts. + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/raytrace.h b/linden/indra/llmath/raytrace.h new file mode 100644 index 0000000..58cca69 --- /dev/null +++ b/linden/indra/llmath/raytrace.h @@ -0,0 +1,233 @@ +/** + * @file raytrace.h + * @brief Ray intersection tests for primitives. + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/v2math.cpp b/linden/indra/llmath/v2math.cpp new file mode 100644 index 0000000..cee0f73 --- /dev/null +++ b/linden/indra/llmath/v2math.cpp @@ -0,0 +1,111 @@ +/** + * @file v2math.cpp + * @brief LLVector2 class implementation. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/v2math.h b/linden/indra/llmath/v2math.h new file mode 100644 index 0000000..1832403 --- /dev/null +++ b/linden/indra/llmath/v2math.h @@ -0,0 +1,326 @@ +/** + * @file v2math.h + * @brief LLVector2 class header file. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_V2MATH_H +#define LL_V2MATH_H + +#include + +#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/linden/indra/llmath/v3color.cpp b/linden/indra/llmath/v3color.cpp new file mode 100644 index 0000000..9428093 --- /dev/null +++ b/linden/indra/llmath/v3color.cpp @@ -0,0 +1,63 @@ +/** + * @file v3color.cpp + * @brief LLColor3 class implementation. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/v3color.h b/linden/indra/llmath/v3color.h new file mode 100644 index 0000000..606a810 --- /dev/null +++ b/linden/indra/llmath/v3color.h @@ -0,0 +1,381 @@ +/** + * @file v3color.h + * @brief LLColor3 class header file. + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/v3dmath.cpp b/linden/indra/llmath/v3dmath.cpp new file mode 100644 index 0000000..5d3aad0 --- /dev/null +++ b/linden/indra/llmath/v3dmath.cpp @@ -0,0 +1,148 @@ +/** + * @file v3dmath.cpp + * @brief LLVector3d class implementation. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" + +//#include // 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/linden/indra/llmath/v3dmath.h b/linden/indra/llmath/v3dmath.h new file mode 100644 index 0000000..da13cc3 --- /dev/null +++ b/linden/indra/llmath/v3dmath.h @@ -0,0 +1,437 @@ +/** + * @file v3dmath.h + * @brief High precision 3 dimensional vector. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 ©) +{ + 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; + +} + +inline LLVector3d projected_vec(const LLVector3d &a, const LLVector3d &b) +{ + LLVector3d project_axis = b; + project_axis.normVec(); + return project_axis * (a * project_axis); +} + +#endif // LL_V3DMATH_H diff --git a/linden/indra/llmath/v3math.cpp b/linden/indra/llmath/v3math.cpp new file mode 100644 index 0000000..39d3b70 --- /dev/null +++ b/linden/indra/llmath/v3math.cpp @@ -0,0 +1,232 @@ +/** + * @file v3math.cpp + * @brief LLVector3 class implementation. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/v3math.h b/linden/indra/llmath/v3math.h new file mode 100644 index 0000000..68e60de --- /dev/null +++ b/linden/indra/llmath/v3math.h @@ -0,0 +1,465 @@ +/** + * @file v3math.h + * @brief LLVector3 class header file. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 ©) +{ + 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/linden/indra/llmath/v4color.cpp b/linden/indra/llmath/v4color.cpp new file mode 100644 index 0000000..2732a60 --- /dev/null +++ b/linden/indra/llmath/v4color.cpp @@ -0,0 +1,580 @@ +/** + * @file v4color.cpp + * @brief LLColor4 class implementation. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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(", ")); + 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/linden/indra/llmath/v4color.h b/linden/indra/llmath/v4color.h new file mode 100644 index 0000000..d4130d6 --- /dev/null +++ b/linden/indra/llmath/v4color.h @@ -0,0 +1,558 @@ +/** + * @file v4color.h + * @brief LLColor4 class header file. + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/v4coloru.cpp b/linden/indra/llmath/v4coloru.cpp new file mode 100644 index 0000000..848a082 --- /dev/null +++ b/linden/indra/llmath/v4coloru.cpp @@ -0,0 +1,121 @@ +/** + * @file v4coloru.cpp + * @brief LLColor4U class implementation. + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/v4coloru.h b/linden/indra/llmath/v4coloru.h new file mode 100644 index 0000000..a5124d0 --- /dev/null +++ b/linden/indra/llmath/v4coloru.h @@ -0,0 +1,520 @@ +/** + * @file v4coloru.h + * @brief The LLColor4U class. + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/v4math.cpp b/linden/indra/llmath/v4math.cpp new file mode 100644 index 0000000..a4af47d --- /dev/null +++ b/linden/indra/llmath/v4math.cpp @@ -0,0 +1,143 @@ +/** + * @file v4math.cpp + * @brief LLVector4 class implementation. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/v4math.h b/linden/indra/llmath/v4math.h new file mode 100644 index 0000000..06ac777 --- /dev/null +++ b/linden/indra/llmath/v4math.h @@ -0,0 +1,404 @@ +/** + * @file v4math.h + * @brief LLVector4 class header file. + * + * Copyright (c) 2000-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/xform.cpp b/linden/indra/llmath/xform.cpp new file mode 100644 index 0000000..fc2e060 --- /dev/null +++ b/linden/indra/llmath/xform.cpp @@ -0,0 +1,115 @@ +/** + * @file xform.cpp + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llmath/xform.h b/linden/indra/llmath/xform.h new file mode 100644 index 0000000..af28e43 --- /dev/null +++ b/linden/indra/llmath/xform.h @@ -0,0 +1,288 @@ +/** + * @file xform.h + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 -- cgit v1.1