/** * @file llvolume.h * @brief LLVolume base class. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_LLVOLUME_H #define LL_LLVOLUME_H #include class LLProfileParams; class LLPathParams; class LLVolumeParams; class LLProfile; class LLPath; template class LLPointer; template class LLOctreeNode; class LLVolumeFace; class LLVolume; class LLVolumeTriangle; class LLVolumeOctree; #include "lluuid.h" #include "v4color.h" //#include "vmath.h" #include "v2math.h" #include "v3math.h" #include "v3dmath.h" #include "v4math.h" #include "llvector4a.h" #include "llmatrix4a.h" #include "llquaternion.h" #include "llstrider.h" #include "v4coloru.h" #include "llrefcount.h" #include "llpointer.h" #include "llfile.h" #include "llalignedarray.h" #include "llrigginginfo.h" //============================================================================ constexpr S32 MIN_DETAIL_FACES = 6; constexpr S32 MIN_LOD = 0; constexpr S32 MAX_LOD = 3; // These are defined here but are not enforced at this level, // rather they are here for the convenience of code that uses // the LLVolume class. constexpr F32 MIN_VOLUME_PROFILE_WIDTH = 0.05f; constexpr F32 MIN_VOLUME_PATH_WIDTH = 0.05f; constexpr F32 CUT_QUANTA = 0.00002f; constexpr F32 SCALE_QUANTA = 0.01f; constexpr F32 SHEAR_QUANTA = 0.01f; constexpr F32 TAPER_QUANTA = 0.01f; constexpr F32 REV_QUANTA = 0.015f; constexpr F32 HOLLOW_QUANTA = 0.00002f; constexpr S32 MAX_VOLUME_TRIANGLE_INDICES = 10000; //============================================================================ // useful masks constexpr LLPCode LL_PCODE_HOLLOW_MASK = 0x80; // has a thickness constexpr LLPCode LL_PCODE_SEGMENT_MASK = 0x40; // segments (1 angle) constexpr LLPCode LL_PCODE_PATCH_MASK = 0x20; // segmented segments (2 angles) constexpr LLPCode LL_PCODE_HEMI_MASK = 0x10; // half-primitives get their own type per PR's dictum constexpr LLPCode LL_PCODE_BASE_MASK = 0x0F; // primitive shapes constexpr LLPCode LL_PCODE_CUBE = 1; constexpr LLPCode LL_PCODE_PRISM = 2; constexpr LLPCode LL_PCODE_TETRAHEDRON = 3; constexpr LLPCode LL_PCODE_PYRAMID = 4; constexpr LLPCode LL_PCODE_CYLINDER = 5; constexpr LLPCode LL_PCODE_CONE = 6; constexpr LLPCode LL_PCODE_SPHERE = 7; constexpr LLPCode LL_PCODE_TORUS = 8; constexpr LLPCode LL_PCODE_VOLUME = 9; // surfaces //constexpr LLPCode LL_PCODE_SURFACE_TRIANGLE = 10; //constexpr LLPCode LL_PCODE_SURFACE_SQUARE = 11; //constexpr LLPCode LL_PCODE_SURFACE_DISC = 12; constexpr LLPCode LL_PCODE_APP = 14; // App specific pcode (for viewer/sim side only objects) constexpr LLPCode LL_PCODE_LEGACY = 15; // Pcodes for legacy objects //constexpr LLPCode LL_PCODE_LEGACY_ATOR = 0x10 | LL_PCODE_LEGACY; // ATOR constexpr LLPCode LL_PCODE_LEGACY_AVATAR = 0x20 | LL_PCODE_LEGACY; // PLAYER //constexpr LLPCode LL_PCODE_LEGACY_BIRD = 0x30 | LL_PCODE_LEGACY; // BIRD //constexpr LLPCode LL_PCODE_LEGACY_DEMON = 0x40 | LL_PCODE_LEGACY; // DEMON constexpr LLPCode LL_PCODE_LEGACY_GRASS = 0x50 | LL_PCODE_LEGACY; // GRASS constexpr LLPCode LL_PCODE_TREE_NEW = 0x60 | LL_PCODE_LEGACY; // new trees //constexpr LLPCode LL_PCODE_LEGACY_ORACLE = 0x70 | LL_PCODE_LEGACY; // ORACLE constexpr LLPCode LL_PCODE_LEGACY_PART_SYS = 0x80 | LL_PCODE_LEGACY; // PART_SYS constexpr LLPCode LL_PCODE_LEGACY_ROCK = 0x90 | LL_PCODE_LEGACY; // ROCK //constexpr LLPCode LL_PCODE_LEGACY_SHOT = 0xA0 | LL_PCODE_LEGACY; // BASIC_SHOT //constexpr LLPCode LL_PCODE_LEGACY_SHOT_BIG = 0xB0 | LL_PCODE_LEGACY; //constexpr LLPCode LL_PCODE_LEGACY_SMOKE = 0xC0 | LL_PCODE_LEGACY; // SMOKE //constexpr LLPCode LL_PCODE_LEGACY_SPARK = 0xD0 | LL_PCODE_LEGACY;// SPARK constexpr LLPCode LL_PCODE_LEGACY_TEXT_BUBBLE = 0xE0 | LL_PCODE_LEGACY; // TEXTBUBBLE constexpr LLPCode LL_PCODE_LEGACY_TREE = 0xF0 | LL_PCODE_LEGACY; // TREE // hemis constexpr LLPCode LL_PCODE_CYLINDER_HEMI = LL_PCODE_CYLINDER | LL_PCODE_HEMI_MASK; constexpr LLPCode LL_PCODE_CONE_HEMI = LL_PCODE_CONE | LL_PCODE_HEMI_MASK; constexpr LLPCode LL_PCODE_SPHERE_HEMI = LL_PCODE_SPHERE | LL_PCODE_HEMI_MASK; constexpr LLPCode LL_PCODE_TORUS_HEMI = LL_PCODE_TORUS | LL_PCODE_HEMI_MASK; // Volumes consist of a profile at the base that is swept around // a path to make a volume. // The profile code constexpr U8 LL_PCODE_PROFILE_MASK = 0x0f; constexpr U8 LL_PCODE_PROFILE_MIN = 0x00; constexpr U8 LL_PCODE_PROFILE_CIRCLE = 0x00; constexpr U8 LL_PCODE_PROFILE_SQUARE = 0x01; constexpr U8 LL_PCODE_PROFILE_ISOTRI = 0x02; constexpr U8 LL_PCODE_PROFILE_EQUALTRI = 0x03; constexpr U8 LL_PCODE_PROFILE_RIGHTTRI = 0x04; constexpr U8 LL_PCODE_PROFILE_CIRCLE_HALF = 0x05; constexpr U8 LL_PCODE_PROFILE_MAX = 0x05; // Stored in the profile byte constexpr U8 LL_PCODE_HOLE_MASK = 0xf0; constexpr U8 LL_PCODE_HOLE_MIN = 0x00; constexpr U8 LL_PCODE_HOLE_SAME = 0x00; // same as outside profile constexpr U8 LL_PCODE_HOLE_CIRCLE = 0x10; constexpr U8 LL_PCODE_HOLE_SQUARE = 0x20; constexpr U8 LL_PCODE_HOLE_TRIANGLE = 0x30; constexpr U8 LL_PCODE_HOLE_MAX = 0x03; // min/max needs to be >> 4 of real min/max constexpr U8 LL_PCODE_PATH_IGNORE = 0x00; constexpr U8 LL_PCODE_PATH_MIN = 0x01; // min/max needs to be >> 4 of real min/max constexpr U8 LL_PCODE_PATH_LINE = 0x10; constexpr U8 LL_PCODE_PATH_CIRCLE = 0x20; constexpr U8 LL_PCODE_PATH_CIRCLE2 = 0x30; constexpr U8 LL_PCODE_PATH_TEST = 0x40; constexpr U8 LL_PCODE_PATH_FLEXIBLE = 0x80; constexpr U8 LL_PCODE_PATH_MAX = 0x08; //============================================================================ // face identifiers typedef U16 LLFaceID; constexpr LLFaceID LL_FACE_PATH_BEGIN = 0x1 << 0; constexpr LLFaceID LL_FACE_PATH_END = 0x1 << 1; constexpr LLFaceID LL_FACE_INNER_SIDE = 0x1 << 2; constexpr LLFaceID LL_FACE_PROFILE_BEGIN = 0x1 << 3; constexpr LLFaceID LL_FACE_PROFILE_END = 0x1 << 4; constexpr LLFaceID LL_FACE_OUTER_SIDE_0 = 0x1 << 5; constexpr LLFaceID LL_FACE_OUTER_SIDE_1 = 0x1 << 6; constexpr LLFaceID LL_FACE_OUTER_SIDE_2 = 0x1 << 7; constexpr LLFaceID LL_FACE_OUTER_SIDE_3 = 0x1 << 8; //============================================================================ // sculpt types + flags constexpr U8 LL_SCULPT_TYPE_NONE = 0; constexpr U8 LL_SCULPT_TYPE_SPHERE = 1; constexpr U8 LL_SCULPT_TYPE_TORUS = 2; constexpr U8 LL_SCULPT_TYPE_PLANE = 3; constexpr U8 LL_SCULPT_TYPE_CYLINDER = 4; constexpr U8 LL_SCULPT_TYPE_MESH = 5; constexpr U8 LL_SCULPT_TYPE_GLTF = 6; constexpr U8 LL_SCULPT_TYPE_MAX = LL_SCULPT_TYPE_GLTF; constexpr U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE | LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH | LL_SCULPT_TYPE_GLTF; // for value checks, assign new value after adding new types constexpr U8 LL_SCULPT_FLAG_INVERT = 64; constexpr U8 LL_SCULPT_FLAG_MIRROR = 128; constexpr U8 LL_SCULPT_FLAG_MASK = LL_SCULPT_FLAG_INVERT | LL_SCULPT_FLAG_MIRROR; constexpr S32 LL_SCULPT_MESH_MAX_FACES = 8; extern bool gDebugGL; class LLProfileParams { public: LLProfileParams() : mCurveType(LL_PCODE_PROFILE_SQUARE), mBegin(0.f), mEnd(1.f), mHollow(0.f), mCRC(0) { } LLProfileParams(U8 curve, F32 begin, F32 end, F32 hollow) : mCurveType(curve), mBegin(begin), mEnd(end), mHollow(hollow), mCRC(0) { } LLProfileParams(U8 curve, U16 begin, U16 end, U16 hollow) { mCurveType = curve; F32 temp_f32 = begin * CUT_QUANTA; if (temp_f32 > 1.f) { temp_f32 = 1.f; } mBegin = temp_f32; temp_f32 = end * CUT_QUANTA; if (temp_f32 > 1.f) { temp_f32 = 1.f; } mEnd = 1.f - temp_f32; temp_f32 = hollow * HOLLOW_QUANTA; if (temp_f32 > 1.f) { temp_f32 = 1.f; } mHollow = temp_f32; mCRC = 0; } bool operator==(const LLProfileParams ¶ms) const; bool operator!=(const LLProfileParams ¶ms) const; bool operator<(const LLProfileParams ¶ms) const; void copyParams(const LLProfileParams ¶ms); bool importFile(LLFILE *fp); bool exportFile(LLFILE *fp) const; bool importLegacyStream(std::istream& input_stream); bool exportLegacyStream(std::ostream& output_stream) const; LLSD asLLSD() const; operator LLSD() const { return asLLSD(); } bool fromLLSD(LLSD& sd); const F32& getBegin () const { return mBegin; } const F32& getEnd () const { return mEnd; } const F32& getHollow() const { return mHollow; } const U8& getCurveType () const { return mCurveType; } void setCurveType(const U32 type) { mCurveType = type;} void setBegin(const F32 begin) { mBegin = (begin >= 1.0f) ? 0.0f : ((int) (begin * 100000))/100000.0f;} void setEnd(const F32 end) { mEnd = (end <= 0.0f) ? 1.0f : ((int) (end * 100000))/100000.0f;} void setHollow(const F32 hollow) { mHollow = ((int) (hollow * 100000))/100000.0f;} friend std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params); protected: // Profile params U8 mCurveType; F32 mBegin; F32 mEnd; F32 mHollow; U32 mCRC; }; inline bool LLProfileParams::operator==(const LLProfileParams ¶ms) const { return (getCurveType() == params.getCurveType()) && (getBegin() == params.getBegin()) && (getEnd() == params.getEnd()) && (getHollow() == params.getHollow()); } inline bool LLProfileParams::operator!=(const LLProfileParams ¶ms) const { return (getCurveType() != params.getCurveType()) || (getBegin() != params.getBegin()) || (getEnd() != params.getEnd()) || (getHollow() != params.getHollow()); } inline bool LLProfileParams::operator<(const LLProfileParams ¶ms) const { if (getCurveType() != params.getCurveType()) { return getCurveType() < params.getCurveType(); } else if (getBegin() != params.getBegin()) { return getBegin() < params.getBegin(); } else if (getEnd() != params.getEnd()) { return getEnd() < params.getEnd(); } else { return getHollow() < params.getHollow(); } } #define U8_TO_F32(x) (F32)(*((S8 *)&x)) class LLPathParams { public: LLPathParams() : mCurveType(LL_PCODE_PATH_LINE), mBegin(0.f), mEnd(1.f), mScale(1.f,1.f), mShear(0.f,0.f), mTwistBegin(0.f), mTwistEnd(0.f), mRadiusOffset(0.f), mTaper(0.f,0.f), mRevolutions(1.f), mSkew(0.f), mCRC(0) { } LLPathParams(U8 curve, F32 begin, F32 end, F32 scx, F32 scy, F32 shx, F32 shy, F32 twistend, F32 twistbegin, F32 radiusoffset, F32 tx, F32 ty, F32 revolutions, F32 skew) : mCurveType(curve), mBegin(begin), mEnd(end), mScale(scx,scy), mShear(shx,shy), mTwistBegin(twistbegin), mTwistEnd(twistend), mRadiusOffset(radiusoffset), mTaper(tx,ty), mRevolutions(revolutions), mSkew(skew), mCRC(0) { } LLPathParams(U8 curve, U16 begin, U16 end, U8 scx, U8 scy, U8 shx, U8 shy, U8 twistend, U8 twistbegin, U8 radiusoffset, U8 tx, U8 ty, U8 revolutions, U8 skew) { mCurveType = curve; mBegin = (F32)(begin * CUT_QUANTA); mEnd = (F32)(100.f - end) * CUT_QUANTA; if (mEnd > 1.f) mEnd = 1.f; mScale.setVec((F32) (200 - scx) * SCALE_QUANTA,(F32) (200 - scy) * SCALE_QUANTA); mShear.setVec(U8_TO_F32(shx) * SHEAR_QUANTA,U8_TO_F32(shy) * SHEAR_QUANTA); mTwistBegin = U8_TO_F32(twistbegin) * SCALE_QUANTA; mTwistEnd = U8_TO_F32(twistend) * SCALE_QUANTA; mRadiusOffset = U8_TO_F32(radiusoffset) * SCALE_QUANTA; mTaper.setVec(U8_TO_F32(tx) * TAPER_QUANTA,U8_TO_F32(ty) * TAPER_QUANTA); mRevolutions = ((F32)revolutions) * REV_QUANTA + 1.0f; mSkew = U8_TO_F32(skew) * SCALE_QUANTA; mCRC = 0; } bool operator==(const LLPathParams ¶ms) const; bool operator!=(const LLPathParams ¶ms) const; bool operator<(const LLPathParams ¶ms) const; void copyParams(const LLPathParams ¶ms); bool importFile(LLFILE *fp); bool exportFile(LLFILE *fp) const; bool importLegacyStream(std::istream& input_stream); bool exportLegacyStream(std::ostream& output_stream) const; LLSD asLLSD() const; operator LLSD() const { return asLLSD(); } bool fromLLSD(LLSD& sd); const F32& getBegin() const { return mBegin; } const F32& getEnd() const { return mEnd; } const LLVector2 &getScale() const { return mScale; } const F32& getScaleX() const { return mScale.mV[0]; } const F32& getScaleY() const { return mScale.mV[1]; } const LLVector2 getBeginScale() const; const LLVector2 getEndScale() const; const LLVector2 &getShear() const { return mShear; } const F32& getShearX() const { return mShear.mV[0]; } const F32& getShearY() const { return mShear.mV[1]; } const U8& getCurveType () const { return mCurveType; } const F32& getTwistBegin() const { return mTwistBegin; } const F32& getTwistEnd() const { return mTwistEnd; } const F32& getTwist() const { return mTwistEnd; } // deprecated const F32& getRadiusOffset() const { return mRadiusOffset; } const LLVector2 &getTaper() const { return mTaper; } const F32& getTaperX() const { return mTaper.mV[0]; } const F32& getTaperY() const { return mTaper.mV[1]; } const F32& getRevolutions() const { return mRevolutions; } const F32& getSkew() const { return mSkew; } void setCurveType(const U8 type) { mCurveType = type; } void setBegin(const F32 begin) { mBegin = begin; } void setEnd(const F32 end) { mEnd = end; } void setScale(const F32 x, const F32 y) { mScale.setVec(x,y); } void setScaleX(const F32 v) { mScale.mV[VX] = v; } void setScaleY(const F32 v) { mScale.mV[VY] = v; } void setShear(const F32 x, const F32 y) { mShear.setVec(x,y); } void setShearX(const F32 v) { mShear.mV[VX] = v; } void setShearY(const F32 v) { mShear.mV[VY] = v; } void setTwistBegin(const F32 twist_begin) { mTwistBegin = twist_begin; } void setTwistEnd(const F32 twist_end) { mTwistEnd = twist_end; } void setTwist(const F32 twist) { setTwistEnd(twist); } // deprecated void setRadiusOffset(const F32 radius_offset){ mRadiusOffset = radius_offset; } void setTaper(const F32 x, const F32 y) { mTaper.setVec(x,y); } void setTaperX(const F32 v) { mTaper.mV[VX] = v; } void setTaperY(const F32 v) { mTaper.mV[VY] = v; } void setRevolutions(const F32 revolutions) { mRevolutions = revolutions; } void setSkew(const F32 skew) { mSkew = skew; } friend std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params); protected: // Path params U8 mCurveType; F32 mBegin; F32 mEnd; LLVector2 mScale; LLVector2 mShear; F32 mTwistBegin; F32 mTwistEnd; F32 mRadiusOffset; LLVector2 mTaper; F32 mRevolutions; F32 mSkew; U32 mCRC; }; inline bool LLPathParams::operator==(const LLPathParams ¶ms) const { return (getCurveType() == params.getCurveType()) && (getScale() == params.getScale()) && (getBegin() == params.getBegin()) && (getEnd() == params.getEnd()) && (getShear() == params.getShear()) && (getTwist() == params.getTwist()) && (getTwistBegin() == params.getTwistBegin()) && (getRadiusOffset() == params.getRadiusOffset()) && (getTaper() == params.getTaper()) && (getRevolutions() == params.getRevolutions()) && (getSkew() == params.getSkew()); } inline bool LLPathParams::operator!=(const LLPathParams ¶ms) const { return (getCurveType() != params.getCurveType()) || (getScale() != params.getScale()) || (getBegin() != params.getBegin()) || (getEnd() != params.getEnd()) || (getShear() != params.getShear()) || (getTwist() != params.getTwist()) || (getTwistBegin() !=params.getTwistBegin()) || (getRadiusOffset() != params.getRadiusOffset()) || (getTaper() != params.getTaper()) || (getRevolutions() != params.getRevolutions()) || (getSkew() != params.getSkew()); } inline bool LLPathParams::operator<(const LLPathParams ¶ms) const { if( getCurveType() != params.getCurveType()) { return getCurveType() < params.getCurveType(); } else if( getScale() != params.getScale()) { return getScale() < params.getScale(); } else if( getBegin() != params.getBegin()) { return getBegin() < params.getBegin(); } else if( getEnd() != params.getEnd()) { return getEnd() < params.getEnd(); } else if( getShear() != params.getShear()) { return getShear() < params.getShear(); } else if( getTwist() != params.getTwist()) { return getTwist() < params.getTwist(); } else if( getTwistBegin() != params.getTwistBegin()) { return getTwistBegin() < params.getTwistBegin(); } else if( getRadiusOffset() != params.getRadiusOffset()) { return getRadiusOffset() < params.getRadiusOffset(); } else if( getTaper() != params.getTaper()) { return getTaper() < params.getTaper(); } else if( getRevolutions() != params.getRevolutions()) { return getRevolutions() < params.getRevolutions(); } else { return getSkew() < params.getSkew(); } } typedef LLVolumeParams* LLVolumeParamsPtr; typedef const LLVolumeParams* const_LLVolumeParamsPtr; class LLVolumeParams { public: LLVolumeParams() : mSculptType(LL_SCULPT_TYPE_NONE) { } LLVolumeParams(LLProfileParams &profile, LLPathParams &path, LLUUID sculpt_id = LLUUID::null, U8 sculpt_type = LL_SCULPT_TYPE_NONE) : mProfileParams(profile), mPathParams(path), mSculptID(sculpt_id), mSculptType(sculpt_type) { } bool operator==(const LLVolumeParams ¶ms) const; bool operator!=(const LLVolumeParams ¶ms) const; bool operator<(const LLVolumeParams ¶ms) const; void copyParams(const LLVolumeParams ¶ms); const LLProfileParams &getProfileParams() const {return mProfileParams;} LLProfileParams &getProfileParams() {return mProfileParams;} const LLPathParams &getPathParams() const {return mPathParams;} LLPathParams &getPathParams() {return mPathParams;} bool importFile(LLFILE *fp); bool exportFile(LLFILE *fp) const; bool importLegacyStream(std::istream& input_stream); bool exportLegacyStream(std::ostream& output_stream) const; LLSD sculptAsLLSD() const; bool sculptFromLLSD(LLSD& sd); LLSD asLLSD() const; operator LLSD() const { return asLLSD(); } bool fromLLSD(LLSD& sd); bool setType(U8 profile, U8 path); //void setBeginS(const F32 beginS) { mProfileParams.setBegin(beginS); } // range 0 to 1 //void setBeginT(const F32 beginT) { mPathParams.setBegin(beginT); } // range 0 to 1 //void setEndS(const F32 endS) { mProfileParams.setEnd(endS); } // range 0 to 1, must be greater than begin //void setEndT(const F32 endT) { mPathParams.setEnd(endT); } // range 0 to 1, must be greater than begin bool setBeginAndEndS(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end bool setBeginAndEndT(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end bool setHollow(const F32 hollow); // range 0 to 1 bool setRatio(const F32 x) { return setRatio(x,x); } // 0 = point, 1 = same as base bool setShear(const F32 x) { return setShear(x,x); } // 0 = no movement, bool setRatio(const F32 x, const F32 y); // 0 = point, 1 = same as base bool setShear(const F32 x, const F32 y); // 0 = no movement bool setTwistBegin(const F32 twist_begin); // range -1 to 1 bool setTwistEnd(const F32 twist_end); // range -1 to 1 bool setTwist(const F32 twist) { return setTwistEnd(twist); } // deprecated bool setTaper(const F32 x, const F32 y) { bool pass_x = setTaperX(x); bool pass_y = setTaperY(y); return pass_x && pass_y; } bool setTaperX(const F32 v); // -1 to 1 bool setTaperY(const F32 v); // -1 to 1 bool setRevolutions(const F32 revolutions); // 1 to 4 bool setRadiusOffset(const F32 radius_offset); bool setSkew(const F32 skew); bool setSculptID(const LLUUID& sculpt_id, U8 sculpt_type); static bool validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow, U8 path_curve, F32 path_begin, F32 path_end, F32 scx, F32 scy, F32 shx, F32 shy, F32 twistend, F32 twistbegin, F32 radiusoffset, F32 tx, F32 ty, F32 revolutions, F32 skew); const F32& getBeginS() const { return mProfileParams.getBegin(); } const F32& getBeginT() const { return mPathParams.getBegin(); } const F32& getEndS() const { return mProfileParams.getEnd(); } const F32& getEndT() const { return mPathParams.getEnd(); } const F32& getHollow() const { return mProfileParams.getHollow(); } const F32& getTwist() const { return mPathParams.getTwist(); } const F32& getRatio() const { return mPathParams.getScaleX(); } const F32& getRatioX() const { return mPathParams.getScaleX(); } const F32& getRatioY() const { return mPathParams.getScaleY(); } const F32& getShearX() const { return mPathParams.getShearX(); } const F32& getShearY() const { return mPathParams.getShearY(); } const F32& getTwistBegin()const { return mPathParams.getTwistBegin(); } const F32& getRadiusOffset() const { return mPathParams.getRadiusOffset(); } const F32& getTaper() const { return mPathParams.getTaperX(); } const F32& getTaperX() const { return mPathParams.getTaperX(); } const F32& getTaperY() const { return mPathParams.getTaperY(); } const F32& getRevolutions() const { return mPathParams.getRevolutions(); } const F32& getSkew() const { return mPathParams.getSkew(); } const LLUUID& getSculptID() const { return mSculptID; } const U8& getSculptType() const { return mSculptType; } bool isSculpt() const; bool isMeshSculpt() const; bool isConvex() const; // 'begin' and 'end' should be in range [0, 1] (they will be clamped) // (begin, end) = (0, 1) will not change the volume // (begin, end) = (0, 0.5) will reduce the volume to the first half of its profile/path (S/T) void reduceS(F32 begin, F32 end); void reduceT(F32 begin, F32 end); struct compare { bool operator()( const const_LLVolumeParamsPtr& first, const const_LLVolumeParamsPtr& second) const { return (*first < *second); } }; friend std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params); // debug helper functions void setCube(); protected: LLProfileParams mProfileParams; LLPathParams mPathParams; LLUUID mSculptID; U8 mSculptType; }; class LLProfile { friend class LLVolume; public: LLProfile() : mOpen(false), mConcave(false), mDirty(true), mTotalOut(0), mTotal(2) { } S32 getTotal() const { return mTotal; } S32 getTotalOut() const { return mTotalOut; } // Total number of outside points bool isFlat(S32 face) const { return (mFaces[face].mCount == 2); } bool isOpen() const { return mOpen; } void setDirty() { mDirty = true; } static S32 getNumPoints(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0, bool is_sculpted = false, S32 sculpt_size = 0); bool generate(const LLProfileParams& params, bool path_open, F32 detail = 1.0f, S32 split = 0, bool is_sculpted = false, S32 sculpt_size = 0); bool isConcave() const { return mConcave; } public: struct Face { S32 mIndex; S32 mCount; F32 mScaleU; bool mCap; bool mFlat; LLFaceID mFaceID; }; LLAlignedArray mProfile; //LLAlignedArray mNormals; std::vector mFaces; //LLAlignedArray mEdgeNormals; //LLAlignedArray mEdgeCenters; friend std::ostream& operator<<(std::ostream &s, const LLProfile &profile); protected: ~LLProfile(); static S32 getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0); void genNGon(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0); Face* addHole(const LLProfileParams& params, bool flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split = 0); Face* addCap (S16 faceID); Face* addFace(S32 index, S32 count, F32 scaleU, S16 faceID, bool flat); protected: bool mOpen; bool mConcave; bool mDirty; S32 mTotalOut; S32 mTotal; }; //------------------------------------------------------------------- // SWEEP/EXTRUDE PATHS //------------------------------------------------------------------- class LLPath { public: class PathPt { public: LLMatrix4a mRot; LLVector4a mPos; LLVector4a mScale; F32 mTexT; F32 pad[3]; //for alignment PathPt() { mPos.clear(); mTexT = 0; mScale.clear(); mRot.setRows(LLVector4a(1,0,0,0), LLVector4a(0,1,0,0), LLVector4a(0,0,1,0)); //distinguished data in the pad for debugging pad[0] = 3.14159f; pad[1] = -3.14159f; pad[2] = 0.585f; } }; public: LLPath() : mOpen(false), mTotal(0), mDirty(true), mStep(1) { } virtual ~LLPath(); static S32 getNumPoints(const LLPathParams& params, F32 detail); static S32 getNumNGonPoints(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f); void genNGon(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f); virtual bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0, bool is_sculpted = false, S32 sculpt_size = 0); bool isOpen() const { return mOpen; } F32 getStep() const { return mStep; } void setDirty() { mDirty = true; } S32 getPathLength() const { return (S32)mPath.size(); } void resizePath(S32 length) { mPath.resize(length); } friend std::ostream& operator<<(std::ostream &s, const LLPath &path); public: LLAlignedArray mPath; protected: bool mOpen; S32 mTotal; bool mDirty; F32 mStep; }; class LLDynamicPath : public LLPath { public: LLDynamicPath() : LLPath() { } /*virtual*/ bool generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0, bool is_sculpted = false, S32 sculpt_size = 0); }; // Yet another "face" class - caches volume-specific, but not instance-specific data for faces) class LLVolumeFace { public: class VertexData { enum { POSITION = 0, NORMAL = 1 }; private: void init(); public: VertexData(); VertexData(const VertexData& rhs); const VertexData& operator=(const VertexData& rhs); ~VertexData(); LLVector4a& getPosition(); LLVector4a& getNormal(); const LLVector4a& getPosition() const; const LLVector4a& getNormal() const; void setPosition(const LLVector4a& pos); void setNormal(const LLVector4a& norm); LLVector2 mTexCoord; bool operator<(const VertexData& rhs) const; bool operator==(const VertexData& rhs) const; bool compareNormal(const VertexData& rhs, F32 angle_cutoff) const; private: LLVector4a* mData; }; LLVolumeFace(); LLVolumeFace(const LLVolumeFace& src); LLVolumeFace& operator=(const LLVolumeFace& rhs); ~LLVolumeFace(); private: void freeData(); public: bool create(LLVolume* volume, bool partial_build = false); void createTangents(); void resizeVertices(S32 num_verts); void allocateTangents(S32 num_verts); void allocateWeights(S32 num_verts); void allocateJointIndices(S32 num_verts); void resizeIndices(S32 num_indices); void fillFromLegacyData(std::vector& v, std::vector& idx); void pushVertex(const VertexData& cv); void pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc); void pushIndex(const U16& idx); void swapData(LLVolumeFace& rhs); void getVertexData(U16 indx, LLVolumeFace::VertexData& cv); class VertexMapData : public LLVolumeFace::VertexData { public: U16 mIndex; bool operator==(const LLVolumeFace::VertexData& rhs) const; struct ComparePosition { bool operator()(const LLVector3& a, const LLVector3& b) const; }; typedef std::map, VertexMapData::ComparePosition > PointMap; }; // Eliminates non unique triangles, takes positions, // normals and texture coordinates into account. void remap(); void optimize(F32 angle_cutoff = 2.f); bool cacheOptimize(bool gen_tangents = false); void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f)); void destroyOctree(); // Get a reference to the octree, which may be null const LLVolumeOctree* getOctree() const; // Part of silhouette generation (used by selection outlines) // Populates the provided edge array with numbers corresponding to // *partial* logic of whether a particular index should be rendered // as a silhouette edge. -1 indicates the index should be rendered as a // silhouette edge. See generateSilhouetteVertices for the full logic. // Silhouette edges can only be generated for some types of prims. If a // silhouette edge cannot be generated, the edge array will be left empty. void generateSilhouetteEdge(const LLVolume* volume, std::vector& edge) const; enum { SINGLE_MASK = 0x0001, CAP_MASK = 0x0002, END_MASK = 0x0004, SIDE_MASK = 0x0008, INNER_MASK = 0x0010, OUTER_MASK = 0x0020, HOLLOW_MASK = 0x0040, OPEN_MASK = 0x0080, FLAT_MASK = 0x0100, TOP_MASK = 0x0200, BOTTOM_MASK = 0x0400 }; public: S32 mID; U32 mTypeMask; // Only used for INNER/OUTER faces S32 mBeginS; S32 mBeginT; S32 mNumS; S32 mNumT; LLVector4a* mExtents; //minimum and maximum point of face LLVector4a* mCenter; LLVector2 mTexCoordExtents[2]; //minimum and maximum of texture coordinates of the face. S32 mNumVertices; // num vertices == num normals == num texcoords S32 mNumAllocatedVertices; S32 mNumIndices; LLVector4a* mPositions; // Contains vertices, nortmals and texcoords LLVector4a* mNormals; // pointer into mPositions LLVector4a* mTangents; LLVector2* mTexCoords; // pointer into mPositions // mIndices contains mNumIndices amount of elements. // It contains triangles, each 3 indices describe one triangle. // If mIndices contains {0, 2, 3, 1, 2, 4}, it means there // are two triangles {0, 2, 3} and {1, 2, 4} with values being // indexes for mPositions/mNormals/mTexCoords U16* mIndices; //list of skin weights for rigged volumes // format is mWeights[vertex_index].mV[influence] = . // mWeights.size() should be empty or match mVertices.size() LLVector4a* mWeights; #if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS LLVector4a* mJustWeights; U8* mJointIndices; #endif mutable bool mWeightsScrubbed; // Which joints are rigged to, and the bounding box of any rigged // vertices per joint. LLJointRiggingInfoTab mJointRiggingInfoTab; //whether or not face has been cache optimized bool mOptimized; // if this is a mesh asset, scale and translation that were applied // when encoding the source mesh into a unit cube // used for regenerating tangents LLVector3 mNormalizedScale = LLVector3(1,1,1); private: LLVolumeOctree* mOctree; LLVolumeTriangle* mOctreeTriangles; bool createUnCutCubeCap(LLVolume* volume, bool partial_build = false); bool createCap(LLVolume* volume, bool partial_build = false); bool createSide(LLVolume* volume, bool partial_build = false); }; class LLVolume : public LLRefCount { friend class LLVolumeLODGroup; protected: virtual ~LLVolume(); // use unref public: typedef std::vector face_list_t; struct FaceParams { LLFaceID mFaceID; S32 mBeginS; S32 mCountS; S32 mBeginT; S32 mCountT; }; LLVolume(const LLVolumeParams ¶ms, const F32 detail, const bool generate_single_face = false, const bool is_unique = false); U8 getProfileType() const { return mParams.getProfileParams().getCurveType(); } U8 getPathType() const { return mParams.getPathParams().getCurveType(); } S32 getNumFaces() const; S32 getNumVolumeFaces() const { return static_cast(mVolumeFaces.size()); } F32 getDetail() const { return mDetail; } F32 getSurfaceArea() const { return mSurfaceArea; } const LLVolumeParams& getParams() const { return mParams; } LLVolumeParams getCopyOfParams() const { return mParams; } const LLProfile& getProfile() const { return *mProfilep; } LLPath& getPath() const { return *mPathp; } void resizePath(S32 length); const LLAlignedArray& getMesh() const { return mMesh; } const LLVector4a& getMeshPt(const U32 i) const { return mMesh[i]; } void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); } void regen(); void genTangents(S32 face); bool isConvex() const; bool isCap(S32 face); bool isFlat(S32 face); bool isUnique() const { return mUnique; } S32 getSculptLevel() const { return mSculptLevel; } void setSculptLevel(S32 level) { mSculptLevel = level; } static void getLoDTriangleCounts(const LLVolumeParams& params, S32* counts); S32 getNumTriangles(S32* vcount = nullptr) const; void generateSilhouetteVertices(std::vector &vertices, std::vector &normals, const LLVector3& view_vec, const LLMatrix4& mat, const LLMatrix3& norm_mat, S32 face_index); //get the face index of the face that intersects with the given line segment at the point //closest to start. Moves end to the point of intersection. Returns -1 if no intersection. //Line segment must be in volume space. S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face = -1, // which face to check, -1 = ALL_SIDES LLVector4a* intersection = nullptr, // return the intersection point LLVector2* tex_coord = nullptr, // return the texture coordinates of the intersection point LLVector4a* normal = nullptr, // return the surface normal at the intersection point LLVector4a* tangent = nullptr // return the surface tangent at the intersection point ); LLFaceID generateFaceMask(); bool isFaceMaskValid(LLFaceID face_mask); static S32 sNumMeshPoints; friend std::ostream& operator<<(std::ostream &s, const LLVolume &volume); friend std::ostream& operator<<(std::ostream &s, const LLVolume *volumep); // HACK to bypass Windoze confusion over // conversion if *(LLVolume*) to LLVolume& const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE LLVolumeFace &getVolumeFace(const S32 f) {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE face_list_t& getVolumeFaces() { return mVolumeFaces; } U32 mFaceMask; // bit array of which faces exist in this volume LLVector3 mLODScaleBias; // vector for biasing LOD based on scale void sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level, bool visible_placeholder); void copyVolumeFaces(const LLVolume* volume); void copyFacesTo(std::vector &faces) const; void copyFacesFrom(const std::vector &faces); // use meshoptimizer to optimize index buffer for vertex shader cache // gen_tangents - if true, generate MikkTSpace tangents if needed before optimizing index buffer bool cacheOptimize(bool gen_tangents = false); private: void sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type); F32 sculptGetSurfaceArea(); void sculptGenerateEmptyPlaceholder(); void sculptGenerateSpherePlaceholder(); protected: bool generate(); void createVolumeFaces(); public: bool unpackVolumeFaces(std::istream& is, S32 size); bool unpackVolumeFaces(U8* in_data, S32 size); private: bool unpackVolumeFacesInternal(const LLSD& mdl); public: virtual void setMeshAssetLoaded(bool loaded); virtual bool isMeshAssetLoaded() const; virtual void setMeshAssetUnavaliable(bool unavaliable); virtual bool isMeshAssetUnavaliable() const; protected: bool mUnique; F32 mDetail; S32 mSculptLevel; F32 mSurfaceArea; //unscaled surface area bool mIsMeshAssetLoaded; bool mIsMeshAssetUnavaliable; const LLVolumeParams mParams; LLPath *mPathp; LLProfile *mProfilep; LLAlignedArray mMesh; bool mGenerateSingleFace; face_list_t mVolumeFaces; public: LLVector4a* mHullPoints; U16* mHullIndices; S32 mNumHullPoints; S32 mNumHullIndices; }; std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params); void LLCalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent); bool LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size); bool LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size); bool LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size); bool LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, F32& intersection_a, F32& intersection_b, F32& intersection_t); bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, F32& intersection_a, F32& intersection_b, F32& intersection_t); #endif