/** * @file xform.h * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_XFORM_H #define LL_XFORM_H #include "v3math.h" #include "m4math.h" #include "llquaternion.h" constexpr F32 MAX_OBJECT_Z = 4096.f; // should match REGION_HEIGHT_METERS, Pre-havok4: 768.f constexpr F32 MIN_OBJECT_Z = -256.f; constexpr F32 DEFAULT_MAX_PRIM_SCALE = 64.f; constexpr F32 DEFAULT_MAX_PRIM_SCALE_NO_MESH = 10.f; constexpr F32 MIN_PRIM_SCALE = 0.01f; constexpr F32 MAX_PRIM_SCALE = 65536.f; // something very high but not near FLT_MAX class LLXform { protected: LLVector3 mPosition; LLQuaternion mRotation; LLVector3 mScale; //RN: TODO: move these world transform members to LLXformMatrix // as they are *never* updated or accessed in the base class LLVector3 mWorldPosition; LLQuaternion mWorldRotation; LLXform* mParent; U32 mChanged; bool mScaleChildOffset; public: typedef enum e_changed_flags { UNCHANGED = 0x00, TRANSLATED = 0x01, ROTATED = 0x02, SCALED = 0x04, SHIFTED = 0x08, GEOMETRY = 0x10, TEXTURE = 0x20, MOVED = TRANSLATED|ROTATED|SCALED, SILHOUETTE = 0x40, ALL_CHANGED = 0x7f }EChangedFlags; void init() { mParent = NULL; mChanged = UNCHANGED; mPosition.setVec(0,0,0); mRotation.loadIdentity(); mScale. setVec(1,1,1); mWorldPosition.clearVec(); mWorldRotation.loadIdentity(); mScaleChildOffset = false; } LLXform(); virtual ~LLXform(); void getLocalMat4(LLMatrix4 &mat) const { mat.initAll(mScale, mRotation, mPosition); } inline bool setParent(LLXform *parent); inline void setPosition(const LLVector3& pos); inline void setPosition(const F32 x, const F32 y, const F32 z); inline void setPositionX(const F32 x); inline void setPositionY(const F32 y); inline void setPositionZ(const F32 z); inline void addPosition(const LLVector3& pos); inline void setScale(const LLVector3& scale); inline void setScale(const F32 x, const F32 y, const F32 z); inline void setRotation(const LLQuaternion& rot); inline void setRotation(const F32 x, const F32 y, const F32 z); inline void setRotation(const F32 x, const F32 y, const F32 z, const F32 s); // Above functions must be inline for speed, but also // need to emit warnings. LL_WARNS() causes inline LLError::CallSite // static objects that make more work for the linker. // Avoid inline LL_WARNS() by calling this function. void warn(const char* const msg); void setChanged(const U32 bits) { mChanged |= bits; } bool isChanged() const { return mChanged; } bool isChanged(const U32 bits) const { return mChanged & bits; } void clearChanged() { mChanged = 0; } void clearChanged(U32 bits) { mChanged &= ~bits; } void setScaleChildOffset(bool scale) { mScaleChildOffset = scale; } bool getScaleChildOffset() { return mScaleChildOffset; } LLXform* getParent() const { return mParent; } LLXform* getRoot() const; virtual bool isRoot() const; virtual bool isRootEdit() const; const LLVector3& getPosition() const { return mPosition; } const LLVector3& getScale() const { return mScale; } const LLQuaternion& getRotation() const { return mRotation; } const LLVector3& getPositionW() const { return mWorldPosition; } const LLQuaternion& getWorldRotation() const { return mWorldRotation; } const LLVector3& getWorldPosition() const { return mWorldPosition; } }; class LLXformMatrix : public LLXform { public: LLXformMatrix() : LLXform() {}; virtual ~LLXformMatrix(); const LLMatrix4& getWorldMatrix() const { return mWorldMatrix; } void setWorldMatrix (const LLMatrix4& mat) { mWorldMatrix = mat; } void init() { mWorldMatrix.setIdentity(); mMin.clearVec(); mMax.clearVec(); LLXform::init(); } void update(); void updateMatrix(bool update_bounds = true); void getMinMax(LLVector3& min,LLVector3& max) const; protected: LLMatrix4 mWorldMatrix; LLVector3 mMin; LLVector3 mMax; }; bool LLXform::setParent(LLXform* parent) { // Validate and make sure we're not creating a loop if (parent == mParent) { return true; } if (parent) { LLXform *cur_par = parent->mParent; while (cur_par) { if (cur_par == this) { //warn("LLXform::setParent Creating loop when setting parent!"); return false; } cur_par = cur_par->mParent; } } mParent = parent; return true; } void LLXform::setPosition(const LLVector3& pos) { setChanged(TRANSLATED); if (pos.isFinite()) mPosition = pos; else { mPosition.clearVec(); warn("Non Finite in LLXform::setPosition(LLVector3)"); } } void LLXform::setPosition(const F32 x, const F32 y, const F32 z) { setChanged(TRANSLATED); if (llfinite(x) && llfinite(y) && llfinite(z)) mPosition.setVec(x,y,z); else { mPosition.clearVec(); warn("Non Finite in LLXform::setPosition(F32,F32,F32)"); } } void LLXform::setPositionX(const F32 x) { setChanged(TRANSLATED); if (llfinite(x)) mPosition.mV[VX] = x; else { mPosition.mV[VX] = 0.f; warn("Non Finite in LLXform::setPositionX"); } } void LLXform::setPositionY(const F32 y) { setChanged(TRANSLATED); if (llfinite(y)) mPosition.mV[VY] = y; else { mPosition.mV[VY] = 0.f; warn("Non Finite in LLXform::setPositionY"); } } void LLXform::setPositionZ(const F32 z) { setChanged(TRANSLATED); if (llfinite(z)) mPosition.mV[VZ] = z; else { mPosition.mV[VZ] = 0.f; warn("Non Finite in LLXform::setPositionZ"); } } void LLXform::addPosition(const LLVector3& pos) { setChanged(TRANSLATED); if (pos.isFinite()) mPosition += pos; else warn("Non Finite in LLXform::addPosition"); } void LLXform::setScale(const LLVector3& scale) { setChanged(SCALED); if (scale.isFinite()) mScale = scale; else { mScale.setVec(1.f, 1.f, 1.f); warn("Non Finite in LLXform::setScale"); } } void LLXform::setScale(const F32 x, const F32 y, const F32 z) { setChanged(SCALED); if (llfinite(x) && llfinite(y) && llfinite(z)) mScale.setVec(x,y,z); else { mScale.setVec(1.f, 1.f, 1.f); warn("Non Finite in LLXform::setScale"); } } void LLXform::setRotation(const LLQuaternion& rot) { setChanged(ROTATED); if (rot.isFinite()) mRotation = rot; else { mRotation.loadIdentity(); warn("Non Finite in LLXform::setRotation"); } } void LLXform::setRotation(const F32 x, const F32 y, const F32 z) { setChanged(ROTATED); if (llfinite(x) && llfinite(y) && llfinite(z)) { mRotation.setQuat(x,y,z); } else { mRotation.loadIdentity(); warn("Non Finite in LLXform::setRotation"); } } void LLXform::setRotation(const F32 x, const F32 y, const F32 z, const F32 s) { setChanged(ROTATED); if (llfinite(x) && llfinite(y) && llfinite(z) && llfinite(s)) { mRotation.mQ[VX] = x; mRotation.mQ[VY] = y; mRotation.mQ[VZ] = z; mRotation.mQ[VS] = s; } else { mRotation.loadIdentity(); warn("Non Finite in LLXform::setRotation"); } } #endif